// shellMain.c — DVX Shell entry point and main loop // // Initializes the GUI, task system, DXE export table, and loads // the desktop app. Runs the cooperative main loop, yielding to // app tasks and reaping terminated apps each frame. #include "shellApp.h" #include "dvxDialog.h" #include #include #include #include #include #include // ============================================================ // Module state // ============================================================ static AppContextT sCtx; static jmp_buf sCrashJmp; static volatile int sCrashSignal = 0; static FILE *sLogFile = NULL; static void (*sDesktopUpdateFn)(void) = NULL; // ============================================================ // Prototypes // ============================================================ static void crashHandler(int sig); static void desktopUpdate(void); static void idleYield(void *ctx); static void installCrashHandler(void); static void logCrash(int sig); // ============================================================ // crashHandler — catch page faults and other fatal signals // ============================================================ static void crashHandler(int sig) { logCrash(sig); // Re-install handler (DJGPP resets to SIG_DFL after delivery) signal(sig, crashHandler); sCrashSignal = sig; longjmp(sCrashJmp, 1); } // ============================================================ // desktopUpdate — notify desktop app of state change // ============================================================ static void desktopUpdate(void) { if (sDesktopUpdateFn) { sDesktopUpdateFn(); } } // ============================================================ // idleYield — called when no dirty rects need compositing // ============================================================ static void idleYield(void *ctx) { (void)ctx; if (tsActiveCount() > 1) { tsYield(); } } // ============================================================ // installCrashHandler // ============================================================ static void installCrashHandler(void) { signal(SIGSEGV, crashHandler); signal(SIGFPE, crashHandler); signal(SIGILL, crashHandler); } // ============================================================ // logCrash — write exception details to the log // ============================================================ static void logCrash(int sig) { const char *sigName = "UNKNOWN"; if (sig == SIGSEGV) { sigName = "SIGSEGV (page fault)"; } else if (sig == SIGFPE) { sigName = "SIGFPE (floating point exception)"; } else if (sig == SIGILL) { sigName = "SIGILL (illegal instruction)"; } shellLog("=== CRASH ==="); shellLog("Signal: %d (%s)", sig, sigName); shellLog("Current app ID: %ld", (long)sCurrentAppId); if (sCurrentAppId > 0) { ShellAppT *app = shellGetApp(sCurrentAppId); if (app) { shellLog("App name: %s", app->name); shellLog("App path: %s", app->path); shellLog("Has main loop: %s", app->hasMainLoop ? "yes" : "no"); shellLog("Task ID: %lu", (unsigned long)app->mainTaskId); } } else { shellLog("Crashed in shell (task 0)"); } // Dump CPU registers from exception state jmp_buf *estate = __djgpp_exception_state_ptr; if (estate) { struct __jmp_buf *regs = &(*estate)[0]; shellLog("EIP: 0x%08lx CS: 0x%04x", regs->__eip, regs->__cs); shellLog("EAX: 0x%08lx EBX: 0x%08lx ECX: 0x%08lx EDX: 0x%08lx", regs->__eax, regs->__ebx, regs->__ecx, regs->__edx); shellLog("ESI: 0x%08lx EDI: 0x%08lx EBP: 0x%08lx ESP: 0x%08lx", regs->__esi, regs->__edi, regs->__ebp, regs->__esp); shellLog("DS: 0x%04x ES: 0x%04x FS: 0x%04x GS: 0x%04x SS: 0x%04x", regs->__ds, regs->__es, regs->__fs, regs->__gs, regs->__ss); shellLog("EFLAGS: 0x%08lx", regs->__eflags); } } // ============================================================ // shellLog — append a line to SHELL.LOG // ============================================================ void shellLog(const char *fmt, ...) { if (!sLogFile) { return; } va_list ap; va_start(ap, fmt); vfprintf(sLogFile, fmt, ap); va_end(ap); fprintf(sLogFile, "\n"); fflush(sLogFile); } // ============================================================ // shellRegisterDesktopUpdate // ============================================================ void shellRegisterDesktopUpdate(void (*updateFn)(void)) { sDesktopUpdateFn = updateFn; } // ============================================================ // main // ============================================================ int main(void) { sLogFile = fopen("shell.log", "w"); shellLog("DVX Shell starting..."); // Initialize GUI int32_t result = dvxInit(&sCtx, 640, 480, 32); if (result != 0) { shellLog("Failed to initialize DVX GUI (error %ld)", (long)result); if (sLogFile) { fclose(sLogFile); } return 1; } // Initialize task system if (tsInit() != TS_OK) { shellLog("Failed to initialize task system"); dvxShutdown(&sCtx); if (sLogFile) { fclose(sLogFile); } return 1; } // Shell task (task 0) gets high priority for responsive UI tsSetPriority(0, TS_PRIORITY_HIGH); // Register DXE export table shellExportInit(); // Initialize app slot table shellAppInit(); // Set up idle callback for cooperative yielding sCtx.idleCallback = idleYield; sCtx.idleCtx = &sCtx; // Load the desktop app int32_t desktopId = shellLoadApp(&sCtx, SHELL_DESKTOP_APP); if (desktopId < 0) { shellLog("Failed to load desktop app '%s'", SHELL_DESKTOP_APP); tsShutdown(); dvxShutdown(&sCtx); if (sLogFile) { fclose(sLogFile); } return 1; } // Install crash handler after everything is initialized installCrashHandler(); shellLog("DVX Shell ready."); // Set recovery point for crash handler if (setjmp(sCrashJmp) != 0) { // Returned here from crash handler via longjmp. // If the crash was in a non-main task, the task switcher still // thinks that task is running. Fix it before doing anything else. tsRecoverToMain(); shellLog("Recovering from crash, killing app %ld", (long)sCurrentAppId); if (sCurrentAppId > 0) { ShellAppT *app = shellGetApp(sCurrentAppId); if (app) { char msg[256]; snprintf(msg, sizeof(msg), "'%s' has caused a fault and will be terminated.", app->name); shellForceKillApp(&sCtx, app); sCurrentAppId = 0; dvxMessageBox(&sCtx, "Application Error", msg, MB_OK | MB_ICONERROR); } } sCurrentAppId = 0; sCrashSignal = 0; desktopUpdate(); } // Main loop while (sCtx.running) { dvxUpdate(&sCtx); // Give app tasks CPU time even during active frames if (tsActiveCount() > 1) { tsYield(); } // Reap terminated apps shellReapApps(&sCtx); // Notify desktop of any state changes desktopUpdate(); } shellLog("DVX Shell shutting down..."); // Clean shutdown: terminate all apps shellTerminateAllApps(&sCtx); tsShutdown(); dvxShutdown(&sCtx); shellLog("DVX Shell exited."); if (sLogFile) { fclose(sLogFile); sLogFile = NULL; } return 0; }