// vm.h -- DVX BASIC virtual machine // // Stack-based p-code interpreter. Executes compiled BASIC bytecode. // Embeddable: the host provides I/O callbacks. No DVX dependencies. // // Usage: // BasVmT *vm = basVmCreate(); // basVmSetPrintCallback(vm, myPrintFn, myCtx); // basVmSetInputCallback(vm, myInputFn, myCtx); // basVmLoadModule(vm, compiledCode, codeLen, constants, numConsts); // BasVmResultE result = basVmRun(vm); // basVmDestroy(vm); #ifndef DVXBASIC_VM_H #define DVXBASIC_VM_H #include "values.h" #include #include // ============================================================ // Limits // ============================================================ #define BAS_VM_STACK_SIZE 256 // evaluation stack depth #define BAS_VM_CALL_STACK_SIZE 64 // max call nesting #define BAS_VM_MAX_GLOBALS 512 // global variable slots #define BAS_VM_MAX_LOCALS 64 // locals per stack frame #define BAS_VM_MAX_FOR_DEPTH 32 // nested FOR loops #define BAS_VM_MAX_FILES 16 // open file channels // ============================================================ // Result codes // ============================================================ typedef enum { BAS_VM_OK, // program completed normally BAS_VM_HALTED, // HALT instruction reached BAS_VM_YIELDED, // DoEvents yielded control BAS_VM_ERROR, // runtime error BAS_VM_STACK_OVERFLOW, BAS_VM_STACK_UNDERFLOW, BAS_VM_CALL_OVERFLOW, BAS_VM_DIV_BY_ZERO, BAS_VM_TYPE_MISMATCH, BAS_VM_OUT_OF_MEMORY, BAS_VM_BAD_OPCODE, BAS_VM_FILE_ERROR, BAS_VM_SUBSCRIPT_RANGE, BAS_VM_USER_ERROR, // ON ERROR raised BAS_VM_STEP_LIMIT // step limit reached (not an error) } BasVmResultE; // ============================================================ // I/O callbacks (host-provided) // ============================================================ // Print callback: called for PRINT output. // text is a null-terminated string. newline indicates whether // to advance to the next line after printing. typedef void (*BasPrintFnT)(void *ctx, const char *text, bool newline); // Input callback: called for INPUT statement. // prompt is the text to display. The callback must fill buf // (up to bufSize-1 chars, null-terminated). Returns true on // success, false on cancel/error. typedef bool (*BasInputFnT)(void *ctx, const char *prompt, char *buf, int32_t bufSize); // DoEvents callback: called for DoEvents statement. // The host should process pending events and return. Returns // true to continue execution, false to stop the program. typedef bool (*BasDoEventsFnT)(void *ctx); // ============================================================ // UI callbacks (host-provided, for form/control system) // ============================================================ // // The VM resolves all UI operations through these callbacks. // Control types, property names, and method names are passed // as strings -- the host maps them to its native widget system. // This keeps the VM independent of any specific GUI toolkit. // Get a property from a control. Returns the value. // ctrlRef is an opaque pointer previously returned by createControl/findControl. typedef BasValueT (*BasUiGetPropFnT)(void *ctx, void *ctrlRef, const char *propName); // Set a property on a control. typedef void (*BasUiSetPropFnT)(void *ctx, void *ctrlRef, const char *propName, BasValueT value); // Call a method on a control. args[0..argc-1] are the arguments. // Returns the method's return value (or a zero-value for void methods). typedef BasValueT (*BasUiCallMethodFnT)(void *ctx, void *ctrlRef, const char *methodName, BasValueT *args, int32_t argc); // Create a control on a form. typeName is the widget type ("Button", // "TextBox", etc.). ctrlName is the VB control name ("Command1"). // Returns an opaque control reference. typedef void *(*BasUiCreateCtrlFnT)(void *ctx, void *formRef, const char *typeName, const char *ctrlName); // Find an existing control by name on a form. Returns NULL if not found. typedef void *(*BasUiFindCtrlFnT)(void *ctx, void *formRef, const char *ctrlName); // Load a form by name. Returns an opaque form reference. typedef void *(*BasUiLoadFormFnT)(void *ctx, const char *formName); // Unload a form. typedef void (*BasUiUnloadFormFnT)(void *ctx, void *formRef); // Show a form. modal=true for modal display. typedef void (*BasUiShowFormFnT)(void *ctx, void *formRef, bool modal); // Hide a form (keep in memory). typedef void (*BasUiHideFormFnT)(void *ctx, void *formRef); // Display a message box. Returns the button clicked (1=OK, 6=Yes, 7=No, 2=Cancel). typedef int32_t (*BasUiMsgBoxFnT)(void *ctx, const char *message, int32_t flags); // Display an input box. Returns the entered string (empty on cancel). typedef BasStringT *(*BasUiInputBoxFnT)(void *ctx, const char *prompt, const char *title, const char *defaultText); // Collected UI callbacks typedef struct { BasUiGetPropFnT getProp; BasUiSetPropFnT setProp; BasUiCallMethodFnT callMethod; BasUiCreateCtrlFnT createCtrl; BasUiFindCtrlFnT findCtrl; BasUiLoadFormFnT loadForm; BasUiUnloadFormFnT unloadForm; BasUiShowFormFnT showForm; BasUiHideFormFnT hideForm; BasUiMsgBoxFnT msgBox; BasUiInputBoxFnT inputBox; void *ctx; // passed as first arg to all callbacks } BasUiCallbacksT; // ============================================================ // External library callbacks (host-provided) // ============================================================ // // The VM resolves external functions by library name + function // name at runtime. The host uses dlsym() or equivalent to find // the native function pointer. A second callback marshals the // call -- converting BasValueT arguments to native C types and // the return value back to BasValueT. // Resolve a function by library and symbol name. // Returns an opaque function pointer, or NULL if not found. // The VM caches the result so this is called only once per // unique (library, function) pair. typedef void *(*BasResolveExternFnT)(void *ctx, const char *libName, const char *funcName); // Call a resolved native function. funcPtr is the pointer returned // by resolveExtern. args[0..argc-1] are the BASIC arguments. // retType is the expected return type (BAS_TYPE_*). // Returns the native function's return value as a BasValueT. typedef BasValueT (*BasCallExternFnT)(void *ctx, void *funcPtr, const char *funcName, BasValueT *args, int32_t argc, uint8_t retType); typedef struct { BasResolveExternFnT resolveExtern; BasCallExternFnT callExtern; void *ctx; } BasExternCallbacksT; // ============================================================ // Extern function cache (resolved on first call) // ============================================================ #define BAS_EXTERN_CACHE_SIZE 128 typedef struct { uint16_t libNameIdx; // constant pool index for library name uint16_t funcNameIdx; // constant pool index for function name void *funcPtr; // resolved native function pointer (NULL = not yet resolved) } BasExternCacheEntryT; // ============================================================ // Call stack frame // ============================================================ typedef struct { int32_t returnPc; // instruction to return to int32_t baseSlot; // base index in locals array int32_t localCount; // number of locals in this frame BasValueT locals[BAS_VM_MAX_LOCALS]; } BasCallFrameT; // ============================================================ // FOR loop state // ============================================================ typedef struct { int32_t varIdx; // loop variable slot index bool isLocal; // true = local, false = global BasValueT limit; // upper bound BasValueT step; // step value int32_t loopTop; // PC of the loop body start } BasForStateT; // ============================================================ // File channel // ============================================================ typedef struct { void *handle; // FILE* or platform-specific int32_t mode; // 0=closed, 1=input, 2=output, 3=append, 4=random, 5=binary } BasFileChannelT; // ============================================================ // Procedure table entry (retained from symbol table for runtime) // ============================================================ #define BAS_MAX_PROC_NAME 64 typedef struct { char name[BAS_MAX_PROC_NAME]; // SUB/FUNCTION name (case-preserved) int32_t codeAddr; // entry point in code[] int32_t paramCount; // number of parameters uint8_t returnType; // BAS_TYPE_* (0 for SUB) bool isFunction; // true = FUNCTION, false = SUB } BasProcEntryT; // ============================================================ // Compiled module (output of the compiler) // ============================================================ typedef struct { uint8_t *code; // p-code bytecode int32_t codeLen; BasStringT **constants; // string constant pool int32_t constCount; int32_t globalCount; // number of global variable slots needed int32_t entryPoint; // PC of the first instruction (module-level code) BasValueT *dataPool; // DATA statement value pool int32_t dataCount; // number of values in the data pool BasProcEntryT *procs; // procedure table (SUBs and FUNCTIONs) int32_t procCount; } BasModuleT; // ============================================================ // VM state // ============================================================ typedef struct { // Program BasModuleT *module; // Execution int32_t pc; // program counter bool running; bool yielded; int32_t stepLimit; // max steps per basVmRun (0 = unlimited) int32_t stepCount; // steps executed in last basVmRun // Evaluation stack BasValueT stack[BAS_VM_STACK_SIZE]; int32_t sp; // stack pointer (index of next free slot) // Call stack BasCallFrameT callStack[BAS_VM_CALL_STACK_SIZE]; int32_t callDepth; // FOR loop stack BasForStateT forStack[BAS_VM_MAX_FOR_DEPTH]; int32_t forDepth; // Global variables BasValueT globals[BAS_VM_MAX_GLOBALS]; // File channels (1-based, index 0 unused) BasFileChannelT files[BAS_VM_MAX_FILES]; // DATA/READ pointer int32_t dataPtr; // current READ position in data pool // String comparison mode bool compareTextMode; // true = case-insensitive comparisons // Error handling int32_t errorHandler; // PC of ON ERROR GOTO handler (0 = none) int32_t errorNumber; // current Err number int32_t errorPc; // PC of the instruction that caused the error (for RESUME) int32_t errorNextPc; // PC of the next instruction after error (for RESUME NEXT) bool inErrorHandler; // true when executing error handler code char errorMsg[256]; // current error description // I/O callbacks BasPrintFnT printFn; void *printCtx; BasInputFnT inputFn; void *inputCtx; BasDoEventsFnT doEventsFn; void *doEventsCtx; // UI callbacks (set by host for form/control support) BasUiCallbacksT ui; // External library callbacks (set by host) BasExternCallbacksT ext; // Extern function cache (resolved on first call) BasExternCacheEntryT externCache[BAS_EXTERN_CACHE_SIZE]; int32_t externCacheCount; // Current form reference (set during event dispatch) void *currentForm; } BasVmT; // ============================================================ // API // ============================================================ // Create a new VM instance. BasVmT *basVmCreate(void); // Destroy a VM instance and free all resources. void basVmDestroy(BasVmT *vm); // Load a compiled module into the VM. void basVmLoadModule(BasVmT *vm, BasModuleT *module); // Execute the loaded module. Returns when the program ends, // halts, yields, or hits an error. BasVmResultE basVmRun(BasVmT *vm); // Execute a single instruction. Returns the result. // Useful for stepping/debugging. BasVmResultE basVmStep(BasVmT *vm); // Reset the VM to initial state (clear stack, globals, PC). void basVmReset(BasVmT *vm); // Set I/O callbacks. void basVmSetPrintCallback(BasVmT *vm, BasPrintFnT fn, void *ctx); void basVmSetInputCallback(BasVmT *vm, BasInputFnT fn, void *ctx); void basVmSetDoEventsCallback(BasVmT *vm, BasDoEventsFnT fn, void *ctx); // Set UI callbacks (for form/control system). void basVmSetUiCallbacks(BasVmT *vm, const BasUiCallbacksT *ui); // Set external library callbacks (for DECLARE LIBRARY support). void basVmSetExternCallbacks(BasVmT *vm, const BasExternCallbacksT *ext); // Set the current form context (called by host during event dispatch). void basVmSetCurrentForm(BasVmT *vm, void *formRef); // Set the step limit for basVmRun. 0 = unlimited (default). // When the limit is reached, basVmRun returns BAS_VM_STEP_LIMIT. // The VM remains in a runnable state -- call basVmRun again to continue. void basVmSetStepLimit(BasVmT *vm, int32_t limit); // Push/pop values on the evaluation stack (for host integration). bool basVmPush(BasVmT *vm, BasValueT val); bool basVmPop(BasVmT *vm, BasValueT *val); // Get the current error message. const char *basVmGetError(const BasVmT *vm); // Call a SUB by code address from the host. // Pushes a call frame, runs until the SUB returns, then restores // the previous execution state. Returns true if the SUB was called // and returned normally, false on error or if the VM was not idle. bool basVmCallSub(BasVmT *vm, int32_t codeAddr); #endif // DVXBASIC_VM_H