Implemented jump-to breakpoints
This commit is contained in:
parent
19f42d5a3a
commit
05cd81b77c
311
Core/debugger.c
311
Core/debugger.c
@ -34,6 +34,7 @@ struct GB_breakpoint_s {
|
|||||||
uint32_t key; /* For sorting and comparing */
|
uint32_t key; /* For sorting and comparing */
|
||||||
};
|
};
|
||||||
char *condition;
|
char *condition;
|
||||||
|
bool is_jump_to;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define BP_KEY(x) (((struct GB_breakpoint_s){.addr = ((x).value), .bank = (x).has_bank? (x).bank : -1 }).key)
|
#define BP_KEY(x) (((struct GB_breakpoint_s){.addr = ((x).value), .bank = (x).has_bank? (x).bank : -1 }).key)
|
||||||
@ -845,7 +846,15 @@ static uint16_t find_breakpoint(GB_gameboy_t *gb, value_t addr)
|
|||||||
|
|
||||||
static bool breakpoint(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
|
static bool breakpoint(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
|
||||||
{
|
{
|
||||||
NO_MODIFIERS
|
bool is_jump_to = true;
|
||||||
|
if (!modifiers) {
|
||||||
|
is_jump_to = false;
|
||||||
|
}
|
||||||
|
else if (strcmp(modifiers, "j") != 0) {
|
||||||
|
print_usage(gb, command);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (strlen(lstrip(arguments)) == 0) {
|
if (strlen(lstrip(arguments)) == 0) {
|
||||||
print_usage(gb, command);
|
print_usage(gb, command);
|
||||||
return true;
|
return true;
|
||||||
@ -904,6 +913,12 @@ static bool breakpoint(GB_gameboy_t *gb, char *arguments, char *modifiers, const
|
|||||||
gb->breakpoints[index].condition = NULL;
|
gb->breakpoints[index].condition = NULL;
|
||||||
}
|
}
|
||||||
gb->n_breakpoints++;
|
gb->n_breakpoints++;
|
||||||
|
|
||||||
|
gb->breakpoints[index].is_jump_to = is_jump_to;
|
||||||
|
|
||||||
|
if (is_jump_to) {
|
||||||
|
gb->has_jump_to_breakpoints = true;
|
||||||
|
}
|
||||||
|
|
||||||
GB_log(gb, "Breakpoint set at %s\n", debugger_value_to_string(gb, result, true));
|
GB_log(gb, "Breakpoint set at %s\n", debugger_value_to_string(gb, result, true));
|
||||||
return true;
|
return true;
|
||||||
@ -955,6 +970,16 @@ static bool delete(GB_gameboy_t *gb, char *arguments, char *modifiers, const deb
|
|||||||
free(gb->breakpoints[index].condition);
|
free(gb->breakpoints[index].condition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gb->breakpoints[index].is_jump_to) {
|
||||||
|
gb->has_jump_to_breakpoints = false;
|
||||||
|
for (unsigned i = 0; i < gb->n_breakpoints; i++) {
|
||||||
|
if (i == index) continue;
|
||||||
|
if (gb->breakpoints[i].is_jump_to) {
|
||||||
|
gb->has_jump_to_breakpoints = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
memmove(&gb->breakpoints[index], &gb->breakpoints[index + 1], (gb->n_breakpoints - index - 1) * sizeof(gb->breakpoints[0]));
|
memmove(&gb->breakpoints[index], &gb->breakpoints[index + 1], (gb->n_breakpoints - index - 1) * sizeof(gb->breakpoints[0]));
|
||||||
gb->n_breakpoints--;
|
gb->n_breakpoints--;
|
||||||
@ -1152,12 +1177,15 @@ static bool list(GB_gameboy_t *gb, char *arguments, char *modifiers, const debug
|
|||||||
for (uint16_t i = 0; i < gb->n_breakpoints; i++) {
|
for (uint16_t i = 0; i < gb->n_breakpoints; i++) {
|
||||||
value_t addr = (value_t){gb->breakpoints[i].bank != (uint16_t)-1, gb->breakpoints[i].bank, gb->breakpoints[i].addr};
|
value_t addr = (value_t){gb->breakpoints[i].bank != (uint16_t)-1, gb->breakpoints[i].bank, gb->breakpoints[i].addr};
|
||||||
if (gb->breakpoints[i].condition) {
|
if (gb->breakpoints[i].condition) {
|
||||||
GB_log(gb, " %d. %s (Condition: %s)\n", i + 1,
|
GB_log(gb, " %d. %s (%sCondition: %s)\n", i + 1,
|
||||||
debugger_value_to_string(gb, addr, addr.has_bank),
|
debugger_value_to_string(gb, addr, addr.has_bank),
|
||||||
|
gb->breakpoints[i].is_jump_to? "Jump to, ": "",
|
||||||
gb->breakpoints[i].condition);
|
gb->breakpoints[i].condition);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
GB_log(gb, " %d. %s\n", i + 1, debugger_value_to_string(gb, addr, addr.has_bank));
|
GB_log(gb, " %d. %s%s\n", i + 1,
|
||||||
|
debugger_value_to_string(gb, addr, addr.has_bank),
|
||||||
|
gb->breakpoints[i].is_jump_to? " (Jump to)" : "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1186,12 +1214,12 @@ static bool list(GB_gameboy_t *gb, char *arguments, char *modifiers, const debug
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool _should_break(GB_gameboy_t *gb, value_t addr)
|
static bool _should_break(GB_gameboy_t *gb, value_t addr, bool jump_to)
|
||||||
{
|
{
|
||||||
uint16_t index = find_breakpoint(gb, addr);
|
uint16_t index = find_breakpoint(gb, addr);
|
||||||
uint32_t key = BP_KEY(addr);
|
uint32_t key = BP_KEY(addr);
|
||||||
|
|
||||||
if (index < gb->n_breakpoints && gb->breakpoints[index].key == key) {
|
if (index < gb->n_breakpoints && gb->breakpoints[index].key == key && gb->breakpoints[index].is_jump_to == jump_to) {
|
||||||
if (!gb->breakpoints[index].condition) {
|
if (!gb->breakpoints[index].condition) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1208,16 +1236,16 @@ static bool _should_break(GB_gameboy_t *gb, value_t addr)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool should_break(GB_gameboy_t *gb, uint16_t addr)
|
static bool should_break(GB_gameboy_t *gb, uint16_t addr, bool jump_to)
|
||||||
{
|
{
|
||||||
/* Try any-bank breakpoint */
|
/* Try any-bank breakpoint */
|
||||||
value_t full_addr = (VALUE_16(addr));
|
value_t full_addr = (VALUE_16(addr));
|
||||||
if (_should_break(gb, full_addr)) return true;
|
if (_should_break(gb, full_addr, jump_to)) return true;
|
||||||
|
|
||||||
/* Try bank-specific breakpoint */
|
/* Try bank-specific breakpoint */
|
||||||
full_addr.has_bank = true;
|
full_addr.has_bank = true;
|
||||||
full_addr.bank = bank_for_addr(gb, addr);
|
full_addr.bank = bank_for_addr(gb, addr);
|
||||||
return _should_break(gb, full_addr);
|
return _should_break(gb, full_addr, jump_to);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool print(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
|
static bool print(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
|
||||||
@ -1562,8 +1590,10 @@ static const debugger_command_t commands[] = {
|
|||||||
{"lcd", 3, lcd, "Displays information about the current state of the LCD controller"},
|
{"lcd", 3, lcd, "Displays information about the current state of the LCD controller"},
|
||||||
{"palettes", 3, palettes, "Displays the current CGB palettes"},
|
{"palettes", 3, palettes, "Displays the current CGB palettes"},
|
||||||
{"breakpoint", 1, breakpoint, "Add a new breakpoint at the specified address/expression" HELP_NEWLINE
|
{"breakpoint", 1, breakpoint, "Add a new breakpoint at the specified address/expression" HELP_NEWLINE
|
||||||
"Can also modify the condition of existing breakpoints.",
|
"Can also modify the condition of existing breakpoints." HELP_NEWLINE
|
||||||
"<expression>[ if <condition expression>]"},
|
"If the j modifier is used, the breakpoint will occur just" HELP_NEWLINE
|
||||||
|
"before jumping to the target.",
|
||||||
|
"<expression>[ if <condition expression>]", "(j)"},
|
||||||
{"delete", 2, delete, "Delete a breakpoint by its address, or all breakpoints", "[<expression>]"},
|
{"delete", 2, delete, "Delete a breakpoint by its address, or all breakpoints", "[<expression>]"},
|
||||||
{"watch", 1, watch, "Add a new watchpoint at the specified address/expression." HELP_NEWLINE
|
{"watch", 1, watch, "Add a new watchpoint at the specified address/expression." HELP_NEWLINE
|
||||||
"Can also modify the condition and type of existing watchpoints." HELP_NEWLINE
|
"Can also modify the condition and type of existing watchpoints." HELP_NEWLINE
|
||||||
@ -1834,6 +1864,14 @@ bool GB_debugger_execute_command(GB_gameboy_t *gb, char *input)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
JUMP_TO_NONE,
|
||||||
|
JUMP_TO_BREAK,
|
||||||
|
JUMP_TO_NONTRIVIAL,
|
||||||
|
} jump_to_return_t;
|
||||||
|
|
||||||
|
static jump_to_return_t test_jump_to_breakpoints(GB_gameboy_t *gb, uint16_t *address);
|
||||||
|
|
||||||
void GB_debugger_run(GB_gameboy_t *gb)
|
void GB_debugger_run(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
if (gb->debug_disable) return;
|
if (gb->debug_disable) return;
|
||||||
@ -1852,11 +1890,55 @@ next_command:
|
|||||||
if (input) {
|
if (input) {
|
||||||
free(input);
|
free(input);
|
||||||
}
|
}
|
||||||
if (gb->breakpoints && !gb->debug_stopped && should_break(gb, gb->pc)) {
|
if (gb->breakpoints && !gb->debug_stopped && should_break(gb, gb->pc, false)) {
|
||||||
gb->debug_stopped = true;
|
gb->debug_stopped = true;
|
||||||
GB_log(gb, "Breakpoint: PC = %s\n", value_to_string(gb, gb->pc, true));
|
GB_log(gb, "Breakpoint: PC = %s\n", value_to_string(gb, gb->pc, true));
|
||||||
GB_cpu_disassemble(gb, gb->pc, 5);
|
GB_cpu_disassemble(gb, gb->pc, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gb->breakpoints && !gb->debug_stopped) {
|
||||||
|
uint16_t address = 0;
|
||||||
|
jump_to_return_t jump_to_result = test_jump_to_breakpoints(gb, &address);
|
||||||
|
|
||||||
|
bool should_delete_state = true;
|
||||||
|
if (gb->nontrivial_jump_state && should_break(gb, gb->pc, true)) {
|
||||||
|
if (gb->non_trivial_jump_breakpoint_occured) {
|
||||||
|
gb->non_trivial_jump_breakpoint_occured = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gb->non_trivial_jump_breakpoint_occured = true;
|
||||||
|
GB_log(gb, "Jumping to breakpoint: PC = %s\n", value_to_string(gb, gb->pc, true));
|
||||||
|
GB_cpu_disassemble(gb, gb->pc, 5);
|
||||||
|
GB_load_state_from_buffer(gb, gb->nontrivial_jump_state, -1);
|
||||||
|
gb->debug_stopped = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (jump_to_result == JUMP_TO_BREAK) {
|
||||||
|
gb->debug_stopped = true;
|
||||||
|
GB_log(gb, "Jumping to breakpoint: PC = %s\n", value_to_string(gb, address, true));
|
||||||
|
GB_cpu_disassemble(gb, gb->pc, 5);
|
||||||
|
gb->non_trivial_jump_breakpoint_occured = false;
|
||||||
|
}
|
||||||
|
else if (jump_to_result == JUMP_TO_NONTRIVIAL) {
|
||||||
|
if (!gb->nontrivial_jump_state) {
|
||||||
|
gb->nontrivial_jump_state = malloc(GB_get_save_state_size(gb));
|
||||||
|
}
|
||||||
|
GB_save_state_to_buffer(gb, gb->nontrivial_jump_state);
|
||||||
|
gb->non_trivial_jump_breakpoint_occured = false;
|
||||||
|
should_delete_state = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gb->non_trivial_jump_breakpoint_occured = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (should_delete_state) {
|
||||||
|
if (gb->nontrivial_jump_state) {
|
||||||
|
free(gb->nontrivial_jump_state);
|
||||||
|
gb->nontrivial_jump_state = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (gb->debug_stopped && !gb->debug_disable) {
|
if (gb->debug_stopped && !gb->debug_disable) {
|
||||||
gb->debug_next_command = false;
|
gb->debug_next_command = false;
|
||||||
gb->debug_fin_command = false;
|
gb->debug_fin_command = false;
|
||||||
@ -1986,3 +2068,210 @@ void GB_debugger_set_disabled(GB_gameboy_t *gb, bool disabled)
|
|||||||
{
|
{
|
||||||
gb->debug_disable = disabled;
|
gb->debug_disable = disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Jump-to breakpoints */
|
||||||
|
|
||||||
|
static bool is_in_trivial_memory(uint16_t addr)
|
||||||
|
{
|
||||||
|
/* ROM */
|
||||||
|
if (addr < 0x8000) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* HRAM */
|
||||||
|
if (addr >= 0xFF80 && addr < 0xFFFF) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RAM */
|
||||||
|
if (addr >= 0xC000 && addr < 0xE000) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef uint16_t GB_opcode_address_getter_t(GB_gameboy_t *gb, uint8_t opcode);
|
||||||
|
|
||||||
|
uint16_t trivial_1(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
|
{
|
||||||
|
return gb->pc + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t trivial_2(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
|
{
|
||||||
|
return gb->pc + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t trivial_3(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
|
{
|
||||||
|
return gb->pc + 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t jr_r8(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
|
{
|
||||||
|
return gb->pc + 2 + (int8_t)GB_read_memory(gb, gb->pc + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool condition_code(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
|
{
|
||||||
|
switch ((opcode >> 3) & 0x3) {
|
||||||
|
case 0:
|
||||||
|
return !(gb->registers[GB_REGISTER_AF] & GB_ZERO_FLAG);
|
||||||
|
case 1:
|
||||||
|
return (gb->registers[GB_REGISTER_AF] & GB_ZERO_FLAG);
|
||||||
|
case 2:
|
||||||
|
return !(gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG);
|
||||||
|
case 3:
|
||||||
|
return (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t jr_cc_r8(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
|
{
|
||||||
|
if (!condition_code(gb, opcode)) {
|
||||||
|
return gb->pc + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return gb->pc + 2 + (int8_t)GB_read_memory(gb, gb->pc + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t ret(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
|
{
|
||||||
|
return GB_read_memory(gb, gb->registers[GB_REGISTER_SP]) |
|
||||||
|
(GB_read_memory(gb, gb->registers[GB_REGISTER_SP] + 1) << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint16_t ret_cc(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
|
{
|
||||||
|
if (condition_code(gb, opcode)) {
|
||||||
|
return ret(gb, opcode);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return gb->pc + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t jp_a16(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
|
{
|
||||||
|
return GB_read_memory(gb, gb->pc + 1) |
|
||||||
|
(GB_read_memory(gb, gb->pc + 2) << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t jp_cc_a16(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
|
{
|
||||||
|
if (condition_code(gb, opcode)) {
|
||||||
|
return jp_a16(gb, opcode);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return gb->pc + 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t rst(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
|
{
|
||||||
|
return opcode ^ 0xC7;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t jp_hl(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
|
{
|
||||||
|
return gb->hl;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GB_opcode_address_getter_t *opcodes[256] = {
|
||||||
|
/* X0 X1 X2 X3 X4 X5 X6 X7 */
|
||||||
|
/* X8 X9 Xa Xb Xc Xd Xe Xf */
|
||||||
|
trivial_1, trivial_3, trivial_1, trivial_1, trivial_1, trivial_1, trivial_2, trivial_1, /* 0X */
|
||||||
|
trivial_3, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_2, trivial_1,
|
||||||
|
trivial_2, trivial_3, trivial_1, trivial_1, trivial_1, trivial_1, trivial_2, trivial_1, /* 1X */
|
||||||
|
jr_r8, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_2, trivial_1,
|
||||||
|
jr_cc_r8, trivial_3, trivial_1, trivial_1, trivial_1, trivial_1, trivial_2, trivial_1, /* 2X */
|
||||||
|
jr_cc_r8, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_2, trivial_1,
|
||||||
|
jr_cc_r8, trivial_3, trivial_1, trivial_1, trivial_1, trivial_1, trivial_2, trivial_1, /* 3X */
|
||||||
|
jr_cc_r8, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_2, trivial_1,
|
||||||
|
trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, /* 4X */
|
||||||
|
trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1,
|
||||||
|
trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, /* 5X */
|
||||||
|
trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1,
|
||||||
|
trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, /* 6X */
|
||||||
|
trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1,
|
||||||
|
trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, NULL, trivial_1, /* 7X */
|
||||||
|
trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1,
|
||||||
|
trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, /* 8X */
|
||||||
|
trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1,
|
||||||
|
trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, /* 9X */
|
||||||
|
trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1,
|
||||||
|
trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, /* aX */
|
||||||
|
trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1,
|
||||||
|
trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, /* bX */
|
||||||
|
trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1, trivial_1,
|
||||||
|
ret_cc, trivial_1, jp_cc_a16, jp_a16, jp_cc_a16, trivial_1, trivial_2, rst, /* cX */
|
||||||
|
ret_cc, ret, jp_cc_a16, trivial_2, jp_cc_a16, jp_a16, trivial_2, rst,
|
||||||
|
ret_cc, trivial_1, jp_cc_a16, NULL, jp_cc_a16, trivial_1, trivial_2, rst, /* dX */
|
||||||
|
ret_cc, ret, jp_cc_a16, NULL, jp_cc_a16, NULL, trivial_2, rst,
|
||||||
|
trivial_2, trivial_1, trivial_1, NULL, NULL, trivial_1, trivial_2, rst, /* eX */
|
||||||
|
trivial_2, jp_hl, trivial_3, NULL, NULL, NULL, trivial_2, rst,
|
||||||
|
trivial_2, trivial_1, trivial_1, trivial_1, NULL, trivial_1, trivial_2, rst, /* fX */
|
||||||
|
trivial_2, trivial_1, trivial_3, trivial_1, NULL, NULL, trivial_2, rst,
|
||||||
|
};
|
||||||
|
|
||||||
|
static jump_to_return_t test_jump_to_breakpoints(GB_gameboy_t *gb, uint16_t *address)
|
||||||
|
{
|
||||||
|
if (!gb->has_jump_to_breakpoints) return JUMP_TO_NONE;
|
||||||
|
|
||||||
|
if (!is_in_trivial_memory(gb->pc) || !is_in_trivial_memory(gb->pc + 2) ||
|
||||||
|
!is_in_trivial_memory(gb->registers[GB_REGISTER_SP]) || !is_in_trivial_memory(gb->registers[GB_REGISTER_SP] + 1)) {
|
||||||
|
return JUMP_TO_NONTRIVIAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Interrupts */
|
||||||
|
if (gb->ime) {
|
||||||
|
for (unsigned i = 0; i < 5; i++) {
|
||||||
|
if ((gb->interrupt_enable & (1 << i)) && (gb->io_registers[GB_IO_IF] & (1 << i))) {
|
||||||
|
if (should_break(gb, 0x40 + i * 8, true)) {
|
||||||
|
if (address) {
|
||||||
|
*address = 0x40 + i * 8;
|
||||||
|
}
|
||||||
|
return JUMP_TO_BREAK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t n_watchpoints = gb->n_watchpoints;
|
||||||
|
gb->n_watchpoints = 0;
|
||||||
|
|
||||||
|
uint8_t opcode = GB_read_memory(gb, gb->pc);
|
||||||
|
|
||||||
|
if (opcode == 0x76) {
|
||||||
|
gb->n_watchpoints = n_watchpoints;
|
||||||
|
if (gb->ime) { /* Already handled in above */
|
||||||
|
return JUMP_TO_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gb->interrupt_enable & gb->io_registers[GB_IO_IF] & 0x1F) {
|
||||||
|
return JUMP_TO_NONTRIVIAL; /* HALT bug could occur */
|
||||||
|
}
|
||||||
|
|
||||||
|
return JUMP_TO_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GB_opcode_address_getter_t *getter = opcodes[opcode];
|
||||||
|
if (!getter) {
|
||||||
|
gb->n_watchpoints = n_watchpoints;
|
||||||
|
return JUMP_TO_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t new_pc = getter(gb, opcode);
|
||||||
|
|
||||||
|
gb->n_watchpoints = n_watchpoints;
|
||||||
|
|
||||||
|
if (address) {
|
||||||
|
*address = new_pc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return should_break(gb, new_pc, true) ? JUMP_TO_BREAK : JUMP_TO_NONE;
|
||||||
|
}
|
||||||
|
@ -142,6 +142,9 @@ void GB_free(GB_gameboy_t *gb)
|
|||||||
if (gb->sgb) {
|
if (gb->sgb) {
|
||||||
free(gb->sgb);
|
free(gb->sgb);
|
||||||
}
|
}
|
||||||
|
if (gb->nontrivial_jump_state) {
|
||||||
|
free(gb->nontrivial_jump_state);
|
||||||
|
}
|
||||||
#ifndef DISABLE_DEBUGGER
|
#ifndef DISABLE_DEBUGGER
|
||||||
GB_debugger_clear_symbols(gb);
|
GB_debugger_clear_symbols(gb);
|
||||||
#endif
|
#endif
|
||||||
@ -689,6 +692,11 @@ void GB_reset(GB_gameboy_t *gb)
|
|||||||
|
|
||||||
GB_apu_update_cycles_per_sample(gb);
|
GB_apu_update_cycles_per_sample(gb);
|
||||||
|
|
||||||
|
if (gb->nontrivial_jump_state) {
|
||||||
|
free(gb->nontrivial_jump_state);
|
||||||
|
gb->nontrivial_jump_state = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
gb->magic = (uintptr_t)'SAME';
|
gb->magic = (uintptr_t)'SAME';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -531,6 +531,9 @@ struct GB_gameboy_internal_s {
|
|||||||
/* Breakpoints */
|
/* Breakpoints */
|
||||||
uint16_t n_breakpoints;
|
uint16_t n_breakpoints;
|
||||||
struct GB_breakpoint_s *breakpoints;
|
struct GB_breakpoint_s *breakpoints;
|
||||||
|
bool has_jump_to_breakpoints;
|
||||||
|
void *nontrivial_jump_state;
|
||||||
|
bool non_trivial_jump_breakpoint_occured;
|
||||||
|
|
||||||
/* SLD (Todo: merge with backtrace) */
|
/* SLD (Todo: merge with backtrace) */
|
||||||
bool stack_leak_detection;
|
bool stack_leak_detection;
|
||||||
|
Loading…
Reference in New Issue
Block a user