// Host-side test for examples/agi/agiVm.c (Phase 1 sub-A). // // Loads a LOGIC resource (default 0), runs the VM until it halts, // and prints the halt reason plus any non-zero vars/flags. Since // only the arithmetic and flag opcodes (0x00-0x11) are implemented // this pass, expectation is one of: // - HALT_RETURN at offset N (logic was entirely sub-A opcodes) // - HALT_UNKNOWN_OP 0xXX at offset N (hit IF / new.room / etc.) // - HALT_TRUNCATED at offset N (malformed resource) // // Usage: // testAgiVm [logic-id] #include "agi.h" #include #include // ----- Prototypes ----- static const char *haltName(AgiVmHaltE r); static void printNonZeroState(const AgiVmT *vm); // ----- Helpers (alphabetical) ----- static const char *haltName(AgiVmHaltE r) { switch (r) { case AGI_VM_HALT_NONE: return "NONE"; case AGI_VM_HALT_RETURN: return "RETURN"; case AGI_VM_HALT_UNKNOWN_OP: return "UNKNOWN_OP"; case AGI_VM_HALT_TRUNCATED: return "TRUNCATED"; case AGI_VM_HALT_NEW_ROOM: return "NEW_ROOM"; default: return "?"; } } static void printNonZeroState(const AgiVmT *vm) { uint16_t i; uint16_t nonZeroVars; uint16_t nonZeroFlags; nonZeroVars = 0u; nonZeroFlags = 0u; for (i = 0u; i < AGI_VM_NUM_VARS; i++) { if (vm->vars[i] != 0u) { if (nonZeroVars == 0u) { printf(" vars:"); } printf(" v%u=%u", (unsigned)i, (unsigned)vm->vars[i]); nonZeroVars++; } } if (nonZeroVars > 0u) { printf("\n"); } for (i = 0u; i < AGI_VM_NUM_FLAGS; i++) { if (vm->flags[i] != 0u) { if (nonZeroFlags == 0u) { printf(" flags:"); } printf(" f%u", (unsigned)i); nonZeroFlags++; } } if (nonZeroFlags > 0u) { printf("\n"); } } // ----- main ----- int main(int argc, char **argv) { AgiGameT game; AgiVmT vm; uint16_t logicId; uint8_t *bytes; uint16_t length; AgiVmHaltE reason; if (argc < 2 || argc > 3) { fprintf(stderr, "usage: %s [logic-id]\n", argv[0]); return 2; } if (!agiResOpen(&game, argv[1])) { fprintf(stderr, "FAIL: agiResOpen rejected '%s'\n", argv[1]); return 1; } logicId = (argc == 3) ? (uint16_t)atoi(argv[2]) : 0u; if (logicId >= game.resCount[AGI_RES_LOGIC]) { fprintf(stderr, "FAIL: no LOGIC at id %u\n", (unsigned)logicId); agiResClose(&game); return 1; } bytes = agiResLoad(&game, AGI_RES_LOGIC, logicId, &length); if (bytes == NULL) { fprintf(stderr, "FAIL: LOGIC %u failed to load\n", (unsigned)logicId); agiResClose(&game); return 1; } agiVmInit(&vm); if (!agiVmLoadLogic(&vm, bytes, length)) { fprintf(stderr, "FAIL: agiVmLoadLogic rejected LOGIC %u (truncated header?)\n", (unsigned)logicId); free(bytes); agiResClose(&game); return 1; } printf("LOGIC %u: %u bytes resource, %u bytes bytecode\n", (unsigned)logicId, (unsigned)length, (unsigned)vm.codeLength); reason = agiVmRun(&vm); printf(" halt=%s at pc=%u/%u", haltName(reason), (unsigned)vm.pc, (unsigned)vm.codeLength); if (reason == AGI_VM_HALT_UNKNOWN_OP) { printf(" opcode=0x%02X", (unsigned)vm.lastUnknownOp); } if (reason == AGI_VM_HALT_NEW_ROOM) { printf(" newRoom=%u", (unsigned)vm.newRoomId); } printf("\n"); printNonZeroState(&vm); free(bytes); agiResClose(&game); return 0; }