// test_vm.c -- Quick test for the DVX BASIC VM // // Hand-assembles a small p-code program and executes it. // Tests: PRINT "Hello, World!", arithmetic, FOR loop, string ops. // // Build (native, not cross-compiled): // gcc -O2 -Wall -o test_vm test_vm.c runtime/vm.c runtime/values.c -lm #include "compiler/opcodes.h" #include "runtime/vm.h" #include "runtime/values.h" #include #include // ============================================================ // Helper: emit bytes into a code buffer // ============================================================ static uint8_t sCode[4096]; static int32_t sCodeLen = 0; static void emit8(uint8_t b) { sCode[sCodeLen++] = b; } static void emit16(int16_t v) { memcpy(&sCode[sCodeLen], &v, 2); sCodeLen += 2; } static void emitU16(uint16_t v) { memcpy(&sCode[sCodeLen], &v, 2); sCodeLen += 2; } // ============================================================ // Test 1: PRINT "Hello, World!" // ============================================================ static void test1(void) { printf("--- Test 1: PRINT \"Hello, World!\" ---\n"); sCodeLen = 0; // String constant pool BasStringT *consts[1]; consts[0] = basStringNew("Hello, World!", 13); // Code: PUSH_STR 0; PRINT; PRINT_NL; HALT emit8(OP_PUSH_STR); emitU16(0); emit8(OP_PRINT); emit8(OP_PRINT_NL); emit8(OP_HALT); BasModuleT module; memset(&module, 0, sizeof(module)); module.code = sCode; module.codeLen = sCodeLen; module.constants = consts; module.constCount = 1; module.entryPoint = 0; BasVmT *vm = basVmCreate(); basVmLoadModule(vm, &module); BasVmResultE result = basVmRun(vm); printf("Result: %d (expected %d = HALTED)\n\n", result, BAS_VM_HALTED); basVmDestroy(vm); basStringUnref(consts[0]); } // ============================================================ // Test 2: Arithmetic: PRINT 2 + 3 * 4 // ============================================================ static void test2(void) { printf("--- Test 2: PRINT 2 + 3 * 4 (expect 14) ---\n"); sCodeLen = 0; // Code: PUSH 3; PUSH 4; MUL; PUSH 2; ADD; PRINT; PRINT_NL; HALT emit8(OP_PUSH_INT16); emit16(3); emit8(OP_PUSH_INT16); emit16(4); emit8(OP_MUL_INT); emit8(OP_PUSH_INT16); emit16(2); emit8(OP_ADD_INT); emit8(OP_PRINT); emit8(OP_PRINT_NL); emit8(OP_HALT); BasModuleT module; memset(&module, 0, sizeof(module)); module.code = sCode; module.codeLen = sCodeLen; module.entryPoint = 0; BasVmT *vm = basVmCreate(); basVmLoadModule(vm, &module); basVmRun(vm); basVmDestroy(vm); printf("\n"); } // ============================================================ // Test 3: String concatenation // ============================================================ static void test3(void) { printf("--- Test 3: PRINT \"Hello\" & \" \" & \"BASIC\" ---\n"); sCodeLen = 0; BasStringT *consts[3]; consts[0] = basStringNew("Hello", 5); consts[1] = basStringNew(" ", 1); consts[2] = basStringNew("BASIC", 5); // Code: PUSH consts[0]; PUSH consts[1]; CONCAT; PUSH consts[2]; CONCAT; PRINT; PRINT_NL; HALT emit8(OP_PUSH_STR); emitU16(0); emit8(OP_PUSH_STR); emitU16(1); emit8(OP_STR_CONCAT); emit8(OP_PUSH_STR); emitU16(2); emit8(OP_STR_CONCAT); emit8(OP_PRINT); emit8(OP_PRINT_NL); emit8(OP_HALT); BasModuleT module; memset(&module, 0, sizeof(module)); module.code = sCode; module.codeLen = sCodeLen; module.constants = consts; module.constCount = 3; module.entryPoint = 0; BasVmT *vm = basVmCreate(); basVmLoadModule(vm, &module); basVmRun(vm); basVmDestroy(vm); printf("\n"); basStringUnref(consts[0]); basStringUnref(consts[1]); basStringUnref(consts[2]); } // ============================================================ // Test 4: FOR loop -- PRINT 1 to 5 // ============================================================ static void test4(void) { printf("--- Test 4: FOR i = 1 TO 5: PRINT i: NEXT ---\n"); sCodeLen = 0; // We need a call frame with at least 1 local (the loop variable) // For module-level code, we use callStack[0] as implicit frame // Setup: store initial value in local 0 // PUSH 1; STORE_LOCAL 0 -- i = 1 emit8(OP_PUSH_INT16); emit16(1); emit8(OP_STORE_LOCAL); emitU16(0); // Push limit and step for FOR_INIT // PUSH 5 (limit); PUSH 1 (step) emit8(OP_PUSH_INT16); emit16(5); emit8(OP_PUSH_INT16); emit16(1); emit8(OP_FOR_INIT); emitU16(0); emit8(1); // isLocal=1 // Loop body start (record PC for FOR_NEXT offset) int32_t loopBody = sCodeLen; // LOAD_LOCAL 0; PRINT; PRINT " " emit8(OP_LOAD_LOCAL); emitU16(0); emit8(OP_PRINT); // FOR_NEXT: increment i, test, jump back emit8(OP_FOR_NEXT); emitU16(0); // local index emit8(1); // isLocal=1 int16_t offset = (int16_t)(loopBody - (sCodeLen + 2)); emit16(offset); // After loop emit8(OP_PRINT_NL); emit8(OP_HALT); BasModuleT module; memset(&module, 0, sizeof(module)); module.code = sCode; module.codeLen = sCodeLen; module.entryPoint = 0; BasVmT *vm = basVmCreate(); // Initialize the implicit main frame with 1 local vm->callStack[0].localCount = 1; vm->callDepth = 1; basVmLoadModule(vm, &module); basVmRun(vm); basVmDestroy(vm); printf("\n"); } // ============================================================ // main // ============================================================ int main(void) { printf("DVX BASIC VM Tests\n"); printf("==================\n\n"); basStringSystemInit(); test1(); test2(); test3(); test4(); printf("All tests complete.\n"); return 0; }