diff --git a/apps/dvxbasic/runtime/vm.c b/apps/dvxbasic/runtime/vm.c index 1081f6d..24568f5 100644 --- a/apps/dvxbasic/runtime/vm.c +++ b/apps/dvxbasic/runtime/vm.c @@ -20,6 +20,11 @@ // Prototypes // ============================================================ +// Steps to execute between doEvents yields during subroutine calls. +// This keeps the GUI responsive without excessive overhead from +// calling doEvents on every instruction. +#define SUB_YIELD_INTERVAL 500 + static BasCallFrameT *currentFrame(BasVmT *vm); static void defaultPrint(void *ctx, const char *text, bool newline); static BasVmResultE execArith(BasVmT *vm, uint8_t op); @@ -37,6 +42,51 @@ static uint16_t readUint16(BasVmT *vm); static void runtimeError(BasVmT *vm, int32_t errNum, const char *msg); +// ============================================================ +// runSubLoop -- shared inner loop for basVmCallSub variants +// ============================================================ +// +// Executes instructions until callDepth drops back to savedCallDepth +// (meaning the subroutine returned). Periodically yields via the +// doEvents callback to keep the GUI responsive during long-running +// event handlers. + +static bool runSubLoop(BasVmT *vm, int32_t savedPc, int32_t savedCallDepth, bool savedRunning) { + int32_t stepsSinceYield = 0; + + while (vm->running && vm->callDepth > savedCallDepth) { + BasVmResultE result = basVmStep(vm); + + if (result == BAS_VM_HALTED) { + break; + } + + if (result != BAS_VM_OK) { + vm->pc = savedPc; + vm->callDepth = savedCallDepth; + vm->running = savedRunning; + return false; + } + + // Yield periodically to keep the GUI responsive + if (++stepsSinceYield >= SUB_YIELD_INTERVAL) { + stepsSinceYield = 0; + + if (vm->doEventsFn) { + if (!vm->doEventsFn(vm->doEventsCtx)) { + vm->running = false; + break; + } + } + } + } + + vm->pc = savedPc; + vm->running = savedRunning; + return true; +} + + // ============================================================ // basVmCallSub // ============================================================ @@ -70,26 +120,7 @@ bool basVmCallSub(BasVmT *vm, int32_t codeAddr) { vm->pc = codeAddr; vm->running = true; - // Run until the SUB returns (callDepth drops back). - // No step limit -- event handlers must run to completion. - while (vm->running && vm->callDepth > savedCallDepth) { - BasVmResultE result = basVmStep(vm); - - if (result == BAS_VM_HALTED) { - break; - } - - if (result != BAS_VM_OK) { - vm->pc = savedPc; - vm->callDepth = savedCallDepth; - vm->running = savedRunning; - return false; - } - } - - vm->pc = savedPc; - vm->running = savedRunning; - return true; + return runSubLoop(vm, savedPc, savedCallDepth, savedRunning); } @@ -127,24 +158,7 @@ bool basVmCallSubWithArgs(BasVmT *vm, int32_t codeAddr, const BasValueT *args, i vm->pc = codeAddr; vm->running = true; - while (vm->running && vm->callDepth > savedCallDepth) { - BasVmResultE result = basVmStep(vm); - - if (result == BAS_VM_HALTED) { - break; - } - - if (result != BAS_VM_OK) { - vm->pc = savedPc; - vm->callDepth = savedCallDepth; - vm->running = savedRunning; - return false; - } - } - - vm->pc = savedPc; - vm->running = savedRunning; - return true; + return runSubLoop(vm, savedPc, savedCallDepth, savedRunning); } @@ -181,25 +195,12 @@ bool basVmCallSubWithArgsOut(BasVmT *vm, int32_t codeAddr, const BasValueT *args vm->pc = codeAddr; vm->running = true; - while (vm->running && vm->callDepth > savedCallDepth) { - BasVmResultE result = basVmStep(vm); + bool ok = runSubLoop(vm, savedPc, savedCallDepth, savedRunning); - if (result == BAS_VM_HALTED) { - break; - } - - if (result != BAS_VM_OK) { - vm->pc = savedPc; - vm->callDepth = savedCallDepth; - vm->running = savedRunning; - return false; - } - } - - // Read back modified locals before restoring state. + // Read back modified locals before the frame is reused. // The frame at savedCallDepth still has the data even // though callDepth was decremented by RET. - if (outArgs && outCount > 0) { + if (ok && outArgs && outCount > 0) { BasCallFrameT *doneFrame = &vm->callStack[savedCallDepth]; for (int32_t i = 0; i < outCount && i < BAS_VM_MAX_LOCALS; i++) { @@ -207,9 +208,7 @@ bool basVmCallSubWithArgsOut(BasVmT *vm, int32_t codeAddr, const BasValueT *args } } - vm->pc = savedPc; - vm->running = savedRunning; - return true; + return ok; }