// basstub.c -- DVX BASIC standalone application stub // // Minimal entry point for compiled BASIC applications. Reads the // MODULE resource (serialized bytecode), FORMn resources (.frm text), // and optional DEBUG resource from its own DXE file. Creates a VM // and form runtime from basrt.lib and runs the program. // // This file is compiled into basstub.app, which is embedded as a // resource in dvxbasic.app. "Make Executable" extracts it, renames // it to the project name, and attaches the compiled resources. #include "dvxApp.h" #include "dvxDlg.h" #include "dvxRes.h" #include "dvxWgt.h" #include "shellApp.h" #include "../runtime/vm.h" #include "../runtime/serialize.h" #include "../formrt/formrt.h" #include #include #include #include // ============================================================ // App descriptor // ============================================================ #define STUB_STACK_SIZE 65536 AppDescriptorT appDescriptor = { .name = "BASIC App", .hasMainLoop = true, .multiInstance = false, .stackSize = STUB_STACK_SIZE, .priority = TS_PRIORITY_NORMAL }; // ============================================================ // Callbacks // ============================================================ static AppContextT *sAc = NULL; static void stubPrint(void *ctx, const char *text, bool newline) { (void)ctx; (void)text; (void)newline; } static bool stubInput(void *ctx, const char *prompt, char *buf, int32_t bufSize) { (void)ctx; buf[0] = '\0'; if (!sAc) { return false; } return dvxInputBox(sAc, "Input", prompt, "", buf, bufSize); } static bool stubDoEvents(void *ctx) { (void)ctx; if (!sAc) { return false; } return dvxUpdate(sAc); } // ============================================================ // appMain // ============================================================ int32_t appMain(DxeAppContextT *ctx) { sAc = ctx->shellCtx; // Open our own resources DvxResHandleT *res = dvxResOpen(ctx->appPath); if (!res) { dvxMessageBox(sAc, "Error", "No compiled module found in this application.", 0); return 1; } // Read app name and update the shell's app record uint32_t nameSize = 0; char *appName = (char *)dvxResRead(res, "APPNAME", &nameSize); if (appName) { ShellAppT *app = shellGetApp(ctx->appId); if (app) { snprintf(app->name, SHELL_APP_NAME_MAX, "%s", appName); } free(appName); } // Load MODULE resource uint32_t modSize = 0; uint8_t *modData = (uint8_t *)dvxResRead(res, "MODULE", &modSize); if (!modData) { dvxMessageBox(sAc, "Error", "MODULE resource not found.", 0); dvxResClose(res); return 1; } BasModuleT *mod = basModuleDeserialize(modData, (int32_t)modSize); free(modData); if (!mod) { dvxMessageBox(sAc, "Error", "Failed to deserialize module.", 0); dvxResClose(res); return 1; } // Load optional DEBUG resource uint32_t dbgSize = 0; uint8_t *dbgData = (uint8_t *)dvxResRead(res, "DEBUG", &dbgSize); if (dbgData) { basDebugDeserialize(mod, dbgData, (int32_t)dbgSize); free(dbgData); } // Create VM BasVmT *vm = basVmCreate(); basVmLoadModule(vm, mod); basVmSetPrintCallback(vm, stubPrint, NULL); basVmSetInputCallback(vm, stubInput, NULL); basVmSetDoEventsCallback(vm, stubDoEvents, NULL); // Set app paths snprintf(vm->appPath, DVX_MAX_PATH, "%s", ctx->appDir); snprintf(vm->appConfig, DVX_MAX_PATH, "%s", ctx->appDir); snprintf(vm->appData, DVX_MAX_PATH, "%s", ctx->appDir); // Set extern call callbacks (required for DECLARE LIBRARY functions) BasExternCallbacksT extCb; memset(&extCb, 0, sizeof(extCb)); extCb.resolveExtern = basExternResolve; extCb.callExtern = basExternCall; basVmSetExternCallbacks(vm, &extCb); // Create form runtime BasFormRtT *rt = basFormRtCreate(sAc, vm, mod); // Register .frm source text for lazy loading for (int32_t i = 0; i < 256; i++) { char resName[16]; snprintf(resName, sizeof(resName), "FORM%ld", (long)i); uint32_t frmSize = 0; char *frmText = (char *)dvxResRead(res, resName, &frmSize); if (!frmText) { break; } // Extract form name from "Begin Form " line char frmName[BAS_MAX_CTRL_NAME] = ""; const char *p = frmText; while (*p) { while (*p == ' ' || *p == '\t') { p++; } if (strncasecmp(p, "Begin Form ", 11) == 0) { p += 11; while (*p == ' ' || *p == '\t') { p++; } int32_t ni = 0; while (*p && *p != ' ' && *p != '\t' && *p != '\r' && *p != '\n' && ni < BAS_MAX_CTRL_NAME - 1) { frmName[ni++] = *p++; } frmName[ni] = '\0'; break; } while (*p && *p != '\n') { p++; } if (*p == '\n') { p++; } } if (frmName[0]) { basFormRtRegisterFrm(rt, frmName, frmText, (int32_t)frmSize); } free(frmText); } dvxResClose(res); // Load all cached forms and show the startup form basFormRtLoadAllForms(rt, NULL); // Run bytecode + VB-style event loop basFormRtRunSimple(rt); // Cleanup basFormRtDestroy(rt); basVmDestroy(vm); basModuleFree(mod); return 0; }