DVX_GUI/apps/dvxbasic/runtime/vm.h

366 lines
14 KiB
C

// 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 <stdint.h>
#include <stdbool.h>
// ============================================================
// 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);
bool basVmCallSubWithArgs(BasVmT *vm, int32_t codeAddr, const BasValueT *args, int32_t argCount);
#endif // DVXBASIC_VM_H