DVX_GUI/apps/dvxbasic/test_vm.c

233 lines
5.7 KiB
C

// 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: make -C dvxbasic tests
#include "compiler/opcodes.h"
#include "runtime/vm.h"
#include "runtime/values.h"
#include <stdio.h>
#include <string.h>
// ============================================================
// 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;
}