VM will no longer block cooperative multitasking.

This commit is contained in:
Scott Duensing 2026-04-05 00:09:45 -05:00
parent eb5e4e567e
commit 626befa664

View file

@ -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;
}