233 lines
5.7 KiB
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;
|
|
}
|