diff --git a/Makefile b/Makefile index 7acb90e..8158b44 100644 --- a/Makefile +++ b/Makefile @@ -8,36 +8,32 @@ # Note: I understand this makefile is not as optimal as it could/should be -CC = gcc -CFLAGS ?= -Wall -DEBUG = -g -ggdb -D DEBUG -lreadline -#HEADERS = vm.h -SRCS = main.c -ROM = rom.bin -OUT = hard +CC = gcc +CFLAGS ?= -Wall -Wpedantic -Wextra -std=c99 +ROM = rom.bin +TRACE_SUFFIX = -all: $(OUT) +all: hard -# Useful for some projects -#%.o: %c %.h -# $(CC) $(CLFAGS) -c $^ +trace: CFLAGS += -g -ggdb -DTRACE -lreadline +trace: TRACE_SUFFIX = -trace +trace: hard -$(OUT): ./src/$(SRCS) - ./bin/ass.sh ./src/rom.asm ./src/zeropage.incbin - xxd -i ./src/$(ROM) > ./src/rom.h - $(CC) $(CFLAGS) -o ./bin/$@ $^ - strip ./bin/$@ +# TODO: add rule to make rom.h -debug: ./src/$(SRCS) - ./bin/ass.sh ./src/rom.asm ./src/zeropage.incbin - xxd -i ./src/$(ROM) > ./src/rom.h - $(CC) $(CFLAGS) $(DEBUG) -o ./bin/$(OUT)-$@ $^ +bin/%.o: src/%.c + $(CC) $(CFLAGS) -o $@ -c $^ -disass: ./src/disass.c - $(CC) $(CFLAGS) $(DEBUG) -o ./bin/$@ $^ +bin/vm.o: +bin/main.o: -.PHONY: clean -clean: - rm -f ./bin/$(BIN)* - rm -f ./bin/disass +hard: bin/main.o bin/vm.o + $(CC) $(CFLAGS) -o bin/$@$(TRACE_SUFFIX) $^ + +clean: cleano + rm -f bin/hard bin/hard-trace +cleano: + rm -f bin/main.o bin/vm.o + +.PHONY: all trace clean diff --git a/bin/ass.sh b/ass.sh similarity index 100% rename from bin/ass.sh rename to ass.sh diff --git a/src/main.c b/src/main.c index 60750e9..1c0f5ee 100644 --- a/src/main.c +++ b/src/main.c @@ -33,9 +33,8 @@ int main() for (uint8_t i = 0; i < strlen(flag_input); ++i) memory[128+i] = (uint8_t)flag_input[i]; - #ifdef DEBUG - printf("[DEBUG MODE]: Running interpreter...\n"); // DEBUG - is_being_traced = 1; + #ifdef TRACE + printf("[DEBUG MODE]: Running VM in debugger...\n"); #endif vm_run(&cpu, memory); diff --git a/src/vm.c b/src/vm.c new file mode 100644 index 0000000..2563ae7 --- /dev/null +++ b/src/vm.c @@ -0,0 +1,384 @@ +/* +3ByteBadVM: a bad virtual machine using 3-byte instructions +x1phosura 2021 + +VM specifications + +TODO: document VM stuff here once I write it in Markdown + +use assert()s to make sure pc, sp, etc... are valid +*/ + +#include +#include +#include +#include +#include +#include + +#ifdef TRACE + // readline is L I T + #include + #include +#endif + +#include "vm.h" + + +#ifdef TRACE // TRACE_VARS +struct TRACE_T trace_state; + +char *trace_bad_cmd = "Unrecognized command.\n"; +char *trace_cmd_help = "Supported commands are:\n" + "s: step single instruction\n" + "c: continue code execution until breakpoint or end\n" + "b : set breakpoint at given memory address\n" + "pb: print current breakpoints\n" + "pr: print register contents\n" + "ps: print stack contents\n" + "pm : print bytes of memory starting at \n" + "pz: print zero page (1st 256 bytes)\n" + "h: print VM debugger command help message\n" + "q: quit VM debugger\n"; +#endif // TRACE_VARS + + +static inline void push(struct CPU *cpu, uint8_t val) +{ + cpu->stack[cpu->sp] = val; + if (cpu->sp < STACK_LIM-1) + ++cpu->sp; +} + + +static inline uint8_t pop(struct CPU *cpu) +{ + uint8_t val; + if (cpu->sp > 0) { + val = cpu->stack[cpu->sp-1]; + --cpu->sp; + } else + return 0; + return val; +} + + +/* vm_do_instruction: + */ +uint16_t vm_do_instruction(struct CPU *cpu, uint8_t *mem, uint8_t instr[3]) +{ + uint16_t pc = cpu->pc; + uint16_t sp = cpu->sp; + uint8_t val = 0; + uint8_t operands[2]; + uint8_t opcode = instr[0]; + operands[0] = instr[1]; + operands[1] = instr[2]; + // vvv- this format needed if operands treated as single 16-bit value + uint16_t operand = ((uint16_t)instr[2] * 256) + instr[1]; + + #ifdef TRACE + printf("0x%04x: ", pc); + print_op_decoded(instr, true); + #endif + + /* Note: the below code will likely be very unsafe. */ + switch (opcode) { + case HALT: + cpu->state = STOPPED; + break; + case PUSH: + push(cpu, mem[operand]); + pc += 3; + break; + case POP: + mem[operand] = pop(cpu); + pc += 3; + break; + case PUSHI: + push(cpu, operands[0]); + pc += 3; + break; + case LDLR: // TODO: fix!! This is broken + // read little-endian + cpu->lr = (uint16_t)(mem[operand+1]); // set MSB + cpu->lr = cpu->lr << 8; // set MSB + cpu->lr += (uint16_t)(mem[operand]); // set LSB + pc += 3; + break; + case STLR: + // write little-endian + mem[operand] = (uint8_t)((cpu->lr) & 0x00ff); // set LSB + mem[operand+1] = (uint8_t)((cpu->lr >> 8) & 0x00ff); // set MSB + pc += 3; + break; + case SETI: // peek ToS, write to memory + mem[operand] = cpu->stack[sp-1]; + pc += 3; + break; + case DUP: + val = cpu->stack[sp-1]; + push(cpu, val); + pc += 3; + break; + case ADD: + val = pop(cpu); + val += pop(cpu); + push(cpu, val); + pc += 3; + break; + case SUB: + val = pop(cpu); + val = ~val; + ++val; + val += pop(cpu); + push(cpu, val); // subtract 2nd from top by top of stack + pc += 3; + break; + case XOR: + val = pop(cpu); + val ^= pop(cpu); + push(cpu, val); + pc += 3; + break; + case CALL: + cpu->lr = pc+3; + return operand; + break; + case RET: + return cpu->lr; + break; + case JMP: + return operand; + break; + case BEQ: + if (sp > 1) // if 2 or more items on stack + if (sp > 0 && cpu->stack[sp-1] == cpu->stack[sp-2]) + return operand; + pc += 3; + break; + case BNQ: + if (sp > 1) // if 2 or more items on stack + if (sp > 0 && cpu->stack[sp-1] != cpu->stack[sp-2]) + return operand; + pc += 3; + break; + case NOP: + pc += 3; + break; + default: + /* EVIL idea: have illegal instructions act as NOPs! */ + pc += 3; + } + + return pc; +} + + +static inline void vm_fetch_instruction(uint16_t pc, uint8_t *mem, + uint8_t instr[3]) +{ + assert(pc < RAM_SIZE - 3); + instr[0] = mem[pc]; + instr[1] = mem[pc+1]; + instr[2] = mem[pc+2]; // finish instruction fetch +} + + +#ifdef TRACE // TRACE_FUNCS +void print_op_decoded(uint8_t i[3], bool pargs) +{ + char fmt[] = " 0x%02x 0x%02x"; + uint8_t opcode = i[0]; + + switch(opcode) { + case HALT: printf("HALT"); break; + case PUSH: printf("PUSH"); if (pargs) printf(fmt, i[1], i[2]); break; + case POP: printf("POP"); if (pargs) printf(fmt, i[1], i[2]); break; + case PUSHI: printf("PUSHI"); if (pargs) printf(fmt, i[1], i[2]); break; + case LDLR: printf("LDLR"); if (pargs) printf(fmt, i[1], i[2]); break; + case STLR: printf("STLR"); if (pargs) printf(fmt, i[1], i[2]); break; + case SETI: printf("SETI"); if (pargs) printf(fmt, i[1], i[2]); break; + case DUP: printf("DUP"); break; + case ADD: printf("ADD"); break; + case SUB: printf("SUB"); break; + case XOR: printf("XOR"); break; + case CALL: printf("CALL"); if (pargs) printf(fmt, i[1], i[2]); break; + case RET: printf("RET"); break; + case JMP: printf("JMP"); if (pargs) printf(fmt, i[1], i[2]); break; + case BEQ: printf("BEQ"); if (pargs) printf(fmt, i[1], i[2]); break; + case BNQ: printf("BNQ"); if (pargs) printf(fmt, i[1], i[2]); break; + case NOP: printf("NOP"); break; + default: printf("0x%02x 0x%02x 0x%02x", i[0], i[1], i[2]); + } + + putchar('\n'); +} + + +void print_vm_registers(struct CPU *cpu) +{ + uint16_t pc = cpu->pc; + uint16_t lr = cpu->lr; + uint16_t sp = cpu->sp; + //uint16_t a = cpu->a; + printf("pc = %#x, lr = %#x, sp = %#x\n", pc, lr, sp); +} + + +void print_vm_stack(struct CPU *cpu) +{ + uint16_t sp = cpu->sp; + + for (uint8_t i = 0; i < sp; ++i) { + if (i % 16 == 0) + putchar('\n'); + printf("%02x ", cpu->stack[i]); + } + putchar('\n'); + for (int8_t j = 0; j < sp % 17; ++j) + printf(" "); + printf("^sp = 0x%02x\n", sp); +} + + +void print_vm_memory(uint8_t *mem, uint16_t start_addr, uint16_t num_bytes) +{ + for (uint16_t i = 0; i < num_bytes; ++i) { + if (i % 16 == 0) + printf("\n0x%04x: ", start_addr+i); + printf("%02x ", mem[start_addr + i]); + } + putchar('\n'); +} + + +void vm_trace(struct CPU *cpu, uint8_t *mem, struct TRACE_T *tstate) +{ + char *command; + //char command[24]; + int mem_start = 0; + int num_bytes = 0; + rl_bind_key('\t', rl_insert); // make readline treat tabs and tabs + + if (cpu->pc == tstate->breakpoint) { + printf("Breakpoint reached!\n"); + tstate->mode = STEP; + } + + while(tstate->mode == STEP) { + if ((command = readline("trace> ")) == NULL) { + printf("Error: readline returned NULL. Aborting...\n"); + exit(0); + } + // Note: technically, command should eventually be freed, but + // it's not actually that important here, and the OS will + // reclaim heap space when the program finishes anyway. + if (strlen(command) > 0) + add_history(command); + + // yeah, yeah, I KNOW a switch-case would be better for this + if (command[0] == 's') { + break; + } else if (command[0] == 'b') { + int addr = 0; + if (sscanf(command+1, "%i", &addr) == EOF) + printf("%s\n%s", trace_bad_cmd, trace_cmd_help); + tstate->breakpoint = (uint16_t)addr; + printf("Set breakpoint at address 0x%2x\n", addr); + } else if (command[0] == 'p') { + switch (command[1]) { + case 'b': + printf("Current breakpoint at 0x%2x\n", + tstate->breakpoint); + break; + case 'r': + print_vm_registers(cpu); + break; + case 's': + print_vm_stack(cpu); + break; + case 'z': + print_vm_memory(mem, 0, 0x100); + break; + case 'm': + if (sscanf(command+2,"%i %i", &mem_start, + &num_bytes) == EOF) + ; // So far, sscanf always returns EOF + // (idk why). TODO: fix eventually + printf("Dumping %d bytes of memory starting " + "at 0x%04x\n", num_bytes, mem_start); + print_vm_memory(mem, (uint16_t)mem_start, + (uint16_t)num_bytes); + break; + default: + printf("%s\n%s", trace_bad_cmd, trace_cmd_help); + } + } else if (command[0] == 'd' && command[1] == 'b') { + printf("Deleted breakpoint at 0x%x\n", + tstate->breakpoint); + // vvv - disable breakpoint by not point to instr start + tstate->breakpoint = 0x01; + + } else if (command[0] == 'c') { + tstate->mode = CONT; + } else if (command[0] == 'h') { + printf("%s", trace_cmd_help); + } else if (command[0] == 'q') { + printf("Exiting debugger and virtual machine...\n"); + free(command); + exit(0); + } else { + printf("%s\n%s", trace_bad_cmd, trace_cmd_help); + } + } + // Idea: maybe return value to exit debugger? +} +#endif // TRACE_FUNCS + + +void vm_run(struct CPU *cpu, uint8_t *mem) +{ + uint16_t pc = cpu->pc; + uint8_t curr_instr[3]; // current instruction + cpu->state = RUNNING; + #ifdef TRACE + trace_state.breakpoint = 0xffff; + trace_state.mode = STEP; + #endif + + while (cpu->state == RUNNING) { + #ifdef TRACE + vm_trace(cpu, mem, &trace_state); + #endif + + vm_fetch_instruction(pc, mem, curr_instr); + pc = vm_do_instruction(cpu, mem, curr_instr); + cpu->pc = pc; + } + + // eventually, maybe make it return some kind of CPU status code +} + + +/* vm_init(): initialize CPU/memory starting states + */ +void vm_init(struct CPU *cpu, uint8_t *mem, uint8_t *memimage, uint16_t imgsize) +{ + uint8_t placeholder_memimage[] = {65, 66, 66, 67, 68, 68, 69, 70, 70,0}; + + cpu->pc = 0x100; // default: start code execution after zero page + cpu->lr = 0; + cpu->sp = 0; + //cpu->a = 0; + cpu->state = STOPPED; + memset(cpu->stack, 0, 64); + + if (memimage == NULL) { + imgsize = sizeof(placeholder_memimage); + memimage = (uint8_t *)placeholder_memimage; + } + for (uint16_t i = 0; i < imgsize; ++i) + mem[i] = memimage[i]; +} + diff --git a/src/vm.h b/src/vm.h index 13e399d..99748c0 100644 --- a/src/vm.h +++ b/src/vm.h @@ -1,54 +1,31 @@ -/* -3ByteBadVM: a bad virtual machine using 3-byte instructions -x1phosura 2021 - -VM specifications - -TODO: document VM stuff here once I write it in Markdown - -use assert()s to make sure pc, sp, etc... are valid -*/ +/* x1phosura 2021 */ #pragma once // WAAAAAY better than a dumb header guard ;) -#include -#include -#include -#include -#include -#include - -#ifdef DEBUG - // readline is L I T - #include - #include -#endif - #define RAM_SIZE 16384 // highest it can be given 16-bit addresses #define STACK_LIM 64 -bool is_being_traced = 0; -enum instructions_t { -HALT = 0, -PUSH = 1, -POP = 2, -PUSHI = 3, -LDLR = 4, -STLR = 5, -SETI = 6, -DUP = 7, -ADD = 8, -SUB = 9, -XOR = 10, -CALL = 11, -RET = 12, -JMP = 13, -BEQ = 14, -BNQ = 15, -// every opcode 0x10-0xfe basically acts like a valid NOP just to be annoying -NOP = 0xff -} instructions; +enum instructions { + HALT = 0, + PUSH = 1, + POP = 2, + PUSHI = 3, + LDLR = 4, + STLR = 5, + SETI = 6, + DUP = 7, + ADD = 8, + SUB = 9, + XOR = 10, + CALL = 11, + RET = 12, + JMP = 13, + BEQ = 14, + BNQ = 15, + // every opcode 0x10-0xfe is basically just a NOP just to be annoying + NOP = 0xff +}; struct CPU { @@ -60,373 +37,23 @@ struct CPU { enum state_t {STOPPED, RUNNING, PAUSED} state; }; -#ifdef DEBUG // TRACE_STRUCT_DECODE + +#ifdef TRACE // TRACE struct TRACE_T { uint16_t breakpoint; enum tmode_t {STEP, CONT} mode; -} trace_state; +}; -char *trace_bad_cmd = "Unrecognized command.\n"; -char *trace_cmd_help = "Supported commands are:\n" - "s: step single instruction\n" - "c: continue code execution until breakpoint or end\n" - "b : set breakpoint at given memory address\n" - "pb: print current breakpoints\n" - "pr: print register contents\n" - "ps: print stack contents\n" - "pm : print bytes of memory starting at \n" - "pz: print zero page (1st 256 bytes)\n" - "h: print VM debugger command help message\n" - "q: quit VM debugger\n"; +void print_op_decoded(uint8_t i[3], bool pargs); +void print_vm_registers(struct CPU *cpu); +void print_vm_stack(struct CPU *cpu); +void print_vm_memory(uint8_t *mem, uint16_t start_addr, uint16_t num_bytes); +void vm_trace(struct CPU *cpu, uint8_t *mem, struct TRACE_T *tstate); +#endif // TRACE +uint16_t vm_do_instruction(struct CPU *cpu, uint8_t *mem, uint8_t instr[3]); -void print_op_decoded(uint8_t i[3], bool pargs) -{ - char fmt[] = " 0x%02x 0x%02x"; - uint8_t opcode = i[0]; - - switch(opcode) { - case HALT: printf("HALT"); break; - case PUSH: printf("PUSH"); if (pargs) printf(fmt, i[1], i[2]); break; - case POP: printf("POP"); if (pargs) printf(fmt, i[1], i[2]); break; - case PUSHI: printf("PUSHI"); if (pargs) printf(fmt, i[1], i[2]); break; - case LDLR: printf("LDLR"); if (pargs) printf(fmt, i[1], i[2]); break; - case STLR: printf("STLR"); if (pargs) printf(fmt, i[1], i[2]); break; - case SETI: printf("SETI"); if (pargs) printf(fmt, i[1], i[2]); break; - case DUP: printf("DUP"); break; - case ADD: printf("ADD"); break; - case SUB: printf("SUB"); break; - case XOR: printf("XOR"); break; - case CALL: printf("CALL"); if (pargs) printf(fmt, i[1], i[2]); break; - case RET: printf("RET"); break; - case JMP: printf("JMP"); if (pargs) printf(fmt, i[1], i[2]); break; - case BEQ: printf("BEQ"); if (pargs) printf(fmt, i[1], i[2]); break; - case BNQ: printf("BNQ"); if (pargs) printf(fmt, i[1], i[2]); break; - case NOP: printf("NOP"); break; - default: printf("0x%02x 0x%02x 0x%02x", i[0], i[1], i[2]); - } - - putchar('\n'); -} -#endif // TRACE_STRUCT_DECODE - - -static inline void push(struct CPU *cpu, uint8_t val) -{ - cpu->stack[cpu->sp] = val; - if (cpu->sp < STACK_LIM-1) - ++cpu->sp; -} - - -static inline uint8_t pop(struct CPU *cpu) -{ - uint8_t val; - if (cpu->sp > 0) { - val = cpu->stack[cpu->sp-1]; - --cpu->sp; - } else - return 0; - return val; -} - - -/* vm_do_instruction: - */ -uint16_t vm_do_instruction(struct CPU *cpu, uint8_t *mem, uint8_t instr[3]) -{ - uint16_t pc = cpu->pc; - uint16_t sp = cpu->sp; - uint8_t val = 0; - uint8_t operands[2]; - uint8_t opcode = instr[0]; - operands[0] = instr[1]; - operands[1] = instr[2]; - // vvv- this format needed if operands treated as single 16-bit value - uint16_t operand = ((uint16_t)instr[2] * 256) + instr[1]; - - #ifdef DEBUG - printf("0x%04x: ", pc); - print_op_decoded(instr, true); - #endif - - /* Note: the below code will likely be very unsafe. */ - switch (opcode) { - case HALT: - cpu->state = STOPPED; - break; - case PUSH: - push(cpu, mem[operand]); - pc += 3; - break; - case POP: - mem[operand] = pop(cpu); - pc += 3; - break; - case PUSHI: - push(cpu, operands[0]); - pc += 3; - break; - case LDLR: // TODO: fix!! This is broken - // read little-endian - cpu->lr = (uint16_t)(mem[operand+1]); // set MSB - cpu->lr = cpu->lr << 8; // set MSB - cpu->lr += (uint16_t)(mem[operand]); // set LSB - pc += 3; - break; - case STLR: - // write little-endian - mem[operand] = (uint8_t)((cpu->lr) & 0x00ff); // set LSB - mem[operand+1] = (uint8_t)((cpu->lr >> 8) & 0x00ff); // set MSB - pc += 3; - break; - case SETI: // peek ToS, write to memory - mem[operand] = cpu->stack[sp-1]; - pc += 3; - break; - case DUP: - val = cpu->stack[sp-1]; - push(cpu, val); - pc += 3; - break; - case ADD: - val = pop(cpu); - val += pop(cpu); - push(cpu, val); - pc += 3; - break; - case SUB: - val = pop(cpu); - val = ~val; - ++val; - val += pop(cpu); - push(cpu, val); // subtract 2nd from top by top of stack - pc += 3; - break; - case XOR: - val = pop(cpu); - val ^= pop(cpu); - push(cpu, val); - pc += 3; - break; - case CALL: - cpu->lr = pc+3; - return operand; - break; - case RET: - return cpu->lr; - break; - case JMP: - return operand; - break; - case BEQ: - if (sp > 1) // if 2 or more items on stack - if (sp > 0 && cpu->stack[sp-1] == cpu->stack[sp-2]) - return operand; - pc += 3; - break; - case BNQ: - if (sp > 1) // if 2 or more items on stack - if (sp > 0 && cpu->stack[sp-1] != cpu->stack[sp-2]) - return operand; - pc += 3; - break; - case NOP: - pc += 3; - break; - default: - /* EVIL idea: have illegal instructions act as NOPs! */ - pc += 3; - } - - return pc; -} - - -static inline void vm_fetch_instruction(uint16_t pc, uint8_t *mem, - uint8_t instr[3]) -{ - assert(pc < RAM_SIZE - 3); - instr[0] = mem[pc]; - instr[1] = mem[pc+1]; - instr[2] = mem[pc+2]; // finish instruction fetch -} - - -#ifdef DEBUG // TRACE_FUNCS -void print_vm_registers(struct CPU *cpu) -{ - uint16_t pc = cpu->pc; - uint16_t lr = cpu->lr; - uint16_t sp = cpu->sp; - //uint16_t a = cpu->a; - printf("pc = %#x, lr = %#x, sp = %#x\n", pc, lr, sp); -} - - -void print_vm_stack(struct CPU *cpu) -{ - uint16_t sp = cpu->sp; - - for (uint8_t i = 0; i < sp; ++i) { - if (i % 16 == 0) - putchar('\n'); - printf("%02x ", cpu->stack[i]); - } - putchar('\n'); - for (int8_t j = 0; j < sp % 17; ++j) - printf(" "); - printf("^sp = 0x%02x\n", sp); -} - - -void print_vm_memory(uint8_t *mem, uint16_t start_addr, uint16_t num_bytes) -{ - for (uint16_t i = 0; i < num_bytes; ++i) { - if (i % 16 == 0) - printf("\n0x%04x: ", start_addr+i); - printf("%02x ", mem[start_addr + i]); - } - putchar('\n'); -} -#endif // TRACE_FUNCS - - - -#ifdef DEBUG // VMTRACE -void vm_trace(struct CPU *cpu, uint8_t *mem, struct TRACE_T *tstate) -{ - char *command; - //char command[24]; - int mem_start = 0; - int num_bytes = 0; - rl_bind_key('\t', rl_insert); // make readline treat tabs and tabs - - if (cpu->pc == tstate->breakpoint) { - printf("Breakpoint reached!\n"); - tstate->mode = STEP; - } - - while(tstate->mode == STEP) { - if ((command = readline("trace> ")) == NULL) { - printf("Error: readline returned NULL. Aborting...\n"); - exit(0); - } - // Note: technically, command should eventually be freed, but - // it's not actually that important here, and the OS will - // reclaim heap space when the program finishes anyway. - if (strlen(command) > 0) - add_history(command); - - // yeah, yeah, I KNOW a switch-case would be better for this - if (command[0] == 's') { - break; - } else if (command[0] == 'b') { - int addr = 0; - if (sscanf(command+1, "%i", &addr) == EOF) - printf("%s\n%s", trace_bad_cmd, trace_cmd_help); - tstate->breakpoint = (uint16_t)addr; - printf("Set breakpoint at address 0x%2x\n", addr); - } else if (command[0] == 'p') { - switch (command[1]) { - case 'b': - printf("Current breakpoint at 0x%2x\n", - tstate->breakpoint); - break; - case 'r': - print_vm_registers(cpu); - break; - case 's': - print_vm_stack(cpu); - break; - case 'z': - print_vm_memory(mem, 0, 0x100); - break; - case 'm': - if (sscanf(command+2,"%i %i", &mem_start, - &num_bytes) == EOF) - ; // So far, sscanf always returns EOF - // (idk why). TODO: fix eventually - printf("Dumping %d bytes of memory starting " - "at 0x%04x\n", num_bytes, mem_start); - print_vm_memory(mem, (uint16_t)mem_start, - (uint16_t)num_bytes); - break; - default: - printf("%s\n%s", trace_bad_cmd, trace_cmd_help); - } - } else if (command[0] == 'd' && command[1] == 'b') { - printf("Deleted breakpoint at 0x%x\n", - tstate->breakpoint); - // vvv - disable breakpoint by not point to instr start - tstate->breakpoint = 0x01; - - } else if (command[0] == 'c') { - tstate->mode = CONT; - } else if (command[0] == 'h') { - printf("%s", trace_cmd_help); - } else if (command[0] == 'q') { - printf("Exiting debugger and virtual machine...\n"); - free(command); - exit(0); - } else { - printf("%s\n%s", trace_bad_cmd, trace_cmd_help); - } - } - // Idea: maybe return value to exit debugger? -} -#endif // VMTRACE - - -void vm_run(struct CPU *cpu, uint8_t *mem) -{ - uint16_t pc = cpu->pc; - uint8_t curr_instr[3]; // current instruction - cpu->state = RUNNING; - #ifdef DEBUG - trace_state.mode = STEP; - #endif - - while (cpu->state == RUNNING) { - #ifdef DEBUG - if (is_being_traced) { - vm_trace(cpu, mem, &trace_state); - } - #endif - - vm_fetch_instruction(pc, mem, curr_instr); - pc = vm_do_instruction(cpu, mem, curr_instr); - cpu->pc = pc; - } - - // eventually, make it return some kind of CPU status code -} - - -/* vm_init(): initialize CPU/memory starting states - */ -void vm_init(struct CPU *cpu, uint8_t *mem, uint8_t *memimage, uint16_t imgsize) -{ - uint8_t placeholder_memimage[] = {65, 66, 66, 67, 68, 68, 69, 70, 70,0}; - - cpu->pc = 0x100; // default: start code execution after zero page - cpu->lr = 0; - cpu->sp = 0; - //cpu->a = 0; - cpu->state = STOPPED; - memset(cpu->stack, 0, 64); - - if (memimage == NULL) { - imgsize = sizeof(placeholder_memimage); - memimage = (uint8_t *)placeholder_memimage; - } - for (uint16_t i = 0; i < imgsize; ++i) - mem[i] = memimage[i]; - //mem[0x100+i] = memimage[i]; - - #ifdef DEBUG - trace_state.breakpoint = 0xffff; - trace_state.mode = CONT; - #endif -} +void vm_run(struct CPU *cpu, uint8_t *mem); +void vm_init(struct CPU *cpu, uint8_t *mem, uint8_t *memimage, + uint16_t imgsize);