diff --git a/src/apps/gbdb.c b/src/apps/gbdb.c index 5baa02d..f842a79 100644 --- a/src/apps/gbdb.c +++ b/src/apps/gbdb.c @@ -25,8 +25,9 @@ typedef void (gbdb_cmd)(char *arg_string); -static bool cpu_at_breakpoint(void); static void breakpoint_addr_hit(uint16_t id); +static void breakpoint_cb(struct lr35902_state *lr); +static void reset_breakpoints(void); static uint8_t ram[MAX_RAM_LEN]; static const char prompt[] = "gbdb >"; @@ -40,12 +41,16 @@ static const char usage[] = "peek: view the next instruction to run\n" "exit: quit the program\n"; +static struct lr35902_ops cpu_ops; static struct lr35902_state cpu; static struct gb_video video; static unsigned char bootrom[0x100] = {0}; static int bootrom_mapped = 1; + +static int paused_breakpoint = 0; +static volatile sig_atomic_t paused_signal = 0; static volatile sig_atomic_t paused = 0; static struct { @@ -151,7 +156,11 @@ static void mem_dump(char *filename) static void init(void) { - lr35902_init(&cpu, mem_read, mem_write); + cpu_ops.mem_read = mem_read; + cpu_ops.mem_write = mem_write; + cpu_ops.undef_d3 = breakpoint_cb; + + lr35902_init(&cpu, &cpu_ops); gb_video_init(&video); memset(&ram, 0, MAX_RAM_LEN); bootrom_mapped = 1; @@ -184,6 +193,17 @@ static void echo(char *arg_list) } } +static void breakpoint_cb(struct lr35902_state *lr) +{ + int i; + + paused_breakpoint = 1; + paused = 1; + + breakpoint_addr_hit(cpu.pc); +} + + static void step(char *arg_list) { uint64_t steps, end_steps; @@ -199,22 +219,27 @@ static void step(char *arg_list) paused = 0; signal(SIGINT, break_execution_handler); end_steps = cpu.metrics.retired_instrs + steps; - do { - cycle(); - } while (end_steps > cpu.metrics.retired_instrs && - !cpu_at_breakpoint() && - !paused); + cycle(); + reset_breakpoints(); + + while(end_steps > cpu.metrics.retired_instrs && + !paused) { + cycle(); + } + if (end_steps <= cpu.metrics.retired_instrs) { gb_log("CPU stopped after %" PRId64 " instructions\n", steps); - } else if (cpu_at_breakpoint()) { + } else if (paused_breakpoint) { breakpoint_addr_hit(cpu.pc); gb_log("Breakpoint hit\n"); - } else if (cpu.halted) { - gb_log("CPU halted\n"); - } else { + } else if (paused_signal){ gb_log("Interrupted\n"); } + + paused = 0; + paused_signal = 0; + paused_breakpoint = 0; } static void regs(char *arg_list) @@ -447,18 +472,24 @@ static void quit(char *arg_list) static void do_run(void) { paused = 0; signal(SIGINT, break_execution_handler); - while(!cpu.halted && !cpu_at_breakpoint() && !paused) { + + cycle(); + reset_breakpoints(); + + while(!paused) { cycle(); } - - if (cpu.halted) { - gb_log("CPU halted after %ld cycles\n", cpu.metrics.cycles); - } else if (paused) { + + if (paused_signal) { gb_log("Interrupted.\n"); - } else { + } else if (paused_breakpoint) { breakpoint_addr_hit(cpu.pc); gb_log("Breakpoint hit\n"); } + + paused = 0; + paused_breakpoint = 0; + paused_signal = 0; } static void run(char *arg_list) @@ -470,6 +501,8 @@ static struct breakpoint { uint16_t addr; int id; bool temp; + uint8_t op; + uint8_t *ptr; } breakpoints[MAX_BREAKPTS]; static int num_breakpoints = 0; @@ -487,11 +520,29 @@ static struct breakpoint *get_breakpoint(int id) return NULL; } +static void reset_breakpoints(void) +{ + int i; + + for (i = 0; i < num_breakpoints; i++) { + struct breakpoint *bkpt = &breakpoints[i]; + *bkpt->ptr = 0xd3; + } +} + static int do_set_breakpoint(uint16_t addr, bool temp) { static int id = 0; + if (num_breakpoints < ARRAY_SIZE(breakpoints)) { breakpoints[num_breakpoints].addr = addr; + breakpoints[num_breakpoints].op = mem_read(NULL, addr); + if (bootrom_mapped && addr < 0x100) { + breakpoints[num_breakpoints].ptr = &bootrom[addr]; + } else { + breakpoints[num_breakpoints].ptr = &ram[addr]; + } + *breakpoints[num_breakpoints].ptr = 0xd3; breakpoints[num_breakpoints].id = id++; breakpoints[num_breakpoints].temp = temp; num_breakpoints++; @@ -543,6 +594,8 @@ static int do_delete_breakpoint(int id) return -1; } + *bkpt->ptr = bkpt->op; + index = bkpt - breakpoints; memmove(&breakpoints[index], &breakpoints[index + 1], num_breakpoints - index - 1); @@ -659,11 +712,6 @@ static bool breakpoint_is_at_addr(uint16_t addr) { return false; } -static bool cpu_at_breakpoint(void) -{ - return breakpoint_is_at_addr(cpu.pc); -} - static void process_cmd(const char *cmd_str, struct tri *commands) { char cmd_buffer[INPUT_MAX_LEN]; /* Buffer for command parsing */ diff --git a/src/gbemu/cpu.c b/src/gbemu/cpu.c index 9a6db48..0a66a42 100644 --- a/src/gbemu/cpu.c +++ b/src/gbemu/cpu.c @@ -72,13 +72,13 @@ void lr35902_set_reg_8(struct lr35902_state *cpu, lr35902_regs_8 reg, } void lr35902_init(struct lr35902_state *cpu, - lr35902_mem_read_fn mem_read, - lr35902_mem_write_fn mem_write) + const struct lr35902_ops *ops) { int i; - cpu->mem_read = mem_read; - cpu->mem_write = mem_write; + cpu->mem_read = ops->mem_read; + cpu->mem_write = ops->mem_write; + cpu->undef_d3 = ops->undef_d3; cpu->int_state = LR35902_INT_OFF; cpu->stall_cycles = 0; @@ -1353,7 +1353,16 @@ void lr35902_cycle(struct lr35902_state *cpu) POP_16(cpu, cpu->de); break; case 0xd2: + break; case 0xd3: /* UNDEF */ + if (cpu->undef_d3) { + /* Undo the incremented PC so the op is called + with the PC at the D3 instruction */ + cpu->metrics.cycles--; + cpu->stall_cycles = 0; + cpu->pc--; + cpu->undef_d3(cpu); + } break; case 0xd4: /* CALL NC */ if (!cpu->nf) { diff --git a/src/gbemu/cpu.h b/src/gbemu/cpu.h index 536007e..e4ab46d 100644 --- a/src/gbemu/cpu.h +++ b/src/gbemu/cpu.h @@ -94,8 +94,11 @@ struct lr35902_state { int stall_cycles; int halted; + lr35902_mem_read_fn mem_read; lr35902_mem_write_fn mem_write; + void (*undef_d3)(struct lr35902_state *reg); + lr35902_interrupt_state int_state; struct lr35902_event events[LR35902_MAX_EVENTS]; @@ -108,9 +111,15 @@ struct lr35902_state { } metrics; }; +struct lr35902_ops { + lr35902_mem_read_fn mem_read; + lr35902_mem_write_fn mem_write; + + void (*undef_d3)(struct lr35902_state *reg); +}; + void lr35902_init(struct lr35902_state *cpu, - lr35902_mem_read_fn mem_read, - lr35902_mem_write_fn mem_write); + const struct lr35902_ops *ops); uint16_t lr35902_get_reg_16(const struct lr35902_state *cpu, lr35902_regs_16 reg);