Added support for multiple breakpoints

This commit is contained in:
Lior Halphon 2016-04-07 00:25:41 +03:00
parent eb3e0eaa1e
commit 1069637e45
3 changed files with 104 additions and 6 deletions

View File

@ -396,6 +396,27 @@ static bool registers(GB_gameboy_t *gb, char *arguments)
return true;
}
/* Find the index of the closest breakpoint equal or greater to addr */
static unsigned short find_breakpoint(GB_gameboy_t *gb, unsigned short addr)
{
if (!gb->breakpoints) {
return 0;
}
int min = 0;
int max = gb->n_breakpoints;
while (min < max) {
unsigned short pivot = (min + max) / 2;
if (gb->breakpoints[pivot] == addr) return pivot;
if (gb->breakpoints[pivot] > addr) {
max = pivot - 1;
}
else {
min = pivot + 1;
}
}
return (unsigned short) min;
}
static bool breakpoint(GB_gameboy_t *gb, char *arguments)
{
if (strlen(lstrip(arguments)) == 0) {
@ -405,12 +426,85 @@ static bool breakpoint(GB_gameboy_t *gb, char *arguments)
bool error;
unsigned short result = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error);
if (!error) {
gb_log(gb, "Breakpoint moved to %04x\n", gb->breakpoint = result);
if (error) return true;
unsigned short index = find_breakpoint(gb, result);
if (index < gb->n_breakpoints && gb->breakpoints[index] == result) {
gb_log(gb, "Breakpoint already set at %04x\n", result);
return true;
}
gb->breakpoints = realloc(gb->breakpoints, gb->n_breakpoints * sizeof(gb->breakpoints[0]));
memmove(&gb->breakpoints[index + 1], &gb->breakpoints[index], (gb->n_breakpoints - index) * sizeof(gb->breakpoints[0]));
gb->breakpoints[index] = result;
gb->n_breakpoints++;
gb_log(gb, "Breakpoint set at %04x\n", result);
return true;
}
static bool delete(GB_gameboy_t *gb, char *arguments)
{
if (strlen(lstrip(arguments)) == 0) {
gb_log(gb, "Delete all breakpoints? ");
char *answer = gb->input_callback(gb);
if (answer[0] == 'Y' || answer[0] == 'y') {
free(gb->breakpoints);
gb->breakpoints = NULL;
gb->n_breakpoints = 0;
}
return true;
}
bool error;
unsigned short result = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error);
if (error) return true;
unsigned short index = find_breakpoint(gb, result);
if (index >= gb->n_breakpoints || gb->breakpoints[index] != result) {
gb_log(gb, "No breakpoint set at %04x\n", result);
return true;
}
gb->breakpoints = realloc(gb->breakpoints, gb->n_breakpoints * sizeof(gb->breakpoints[0]));
memmove(&gb->breakpoints[index], &gb->breakpoints[index + 1], (gb->n_breakpoints - index) * sizeof(gb->breakpoints[0]));
gb->n_breakpoints--;
gb_log(gb, "Breakpoint removed from %04x\n", result);
return true;
}
static bool list(GB_gameboy_t *gb, char *arguments)
{
if (strlen(lstrip(arguments))) {
gb_log(gb, "Usage: list\n");
return true;
}
if (gb->n_breakpoints == 0) {
gb_log(gb, "No breakpoints set.\n");
return true;
}
gb_log(gb, "%d breakpoint(s) set:\n", gb->n_breakpoints);
for (unsigned short i = 0; i < gb->n_breakpoints; i++) {
gb_log(gb, " %d. %04x\n", i + 1, gb->breakpoints[i]);
}
return true;
}
static bool should_break(GB_gameboy_t *gb, unsigned short addr)
{
unsigned short index = find_breakpoint(gb, addr);
if (index < gb->n_breakpoints && gb->breakpoints[index] == addr) {
return true;
}
return false;
}
static bool print(GB_gameboy_t *gb, char *arguments)
{
if (strlen(lstrip(arguments)) == 0) {
@ -453,6 +547,8 @@ static const debugger_command_t commands[] = {
{"finish", 1, finish, "Run until the current function returns"},
{"registers", 1, registers, "Print values of processor registers and other important registers"},
{"breakpoint", 1, breakpoint, "Move the breakpoint to a new position"},
{"list", 1, list, "List all set breakpoints"},
{"delete", 2, delete, "Delete a breakpoint by its address, or all breakpoints"},
{"print", 1, print, "Evaluate and print an expression"},
{"eval", 2, print, NULL},
{"examine", 2, examine, "Examine values at address"},
@ -505,7 +601,7 @@ next_command:
if (input) {
free(input);
}
if (gb->pc == gb->breakpoint && !gb->debug_stopped) {
if (!gb->debug_stopped && should_break(gb, gb->pc)) {
gb->debug_stopped = true;
gb_log(gb, "Breakpoint: PC = %04x\n", gb->pc);
cpu_disassemble(gb, gb->pc, 5);

View File

@ -124,7 +124,6 @@ void gb_init(GB_gameboy_t *gb)
gb->mbc_rom_bank = 1;
gb->last_rtc_second = time(NULL);
gb->last_vblank = clock();
gb->breakpoint = 0xFFFF;
gb->cgb_ram_bank = 1;
/* Todo: this bypasses the rgb encoder because it is not set yet. */
@ -152,7 +151,6 @@ void gb_init_cgb(GB_gameboy_t *gb)
gb->mbc_rom_bank = 1;
gb->last_rtc_second = time(NULL);
gb->last_vblank = clock();
gb->breakpoint = 0xFFFF;
gb->cgb_ram_bank = 1;
gb->input_callback = default_input_callback;
gb->cartridge_type = &cart_defs[0]; // Default cartridge type
@ -175,6 +173,9 @@ void gb_free(GB_gameboy_t *gb)
if (gb->audio_buffer) {
free(gb->audio_buffer);
}
if (gb->breakpoints) {
free(gb->breakpoints);
}
}
int gb_load_bios(GB_gameboy_t *gb, const char *path)

View File

@ -270,13 +270,14 @@ typedef struct GB_gameboy_s{
struct {} first_unsaved_data;
bool turbo;
bool debug_stopped;
unsigned short breakpoint;
GB_log_callback_t log_callback;
GB_input_callback_t input_callback;
GB_rgb_encode_callback_t rgb_encode_callback;
void *user_data;
int debug_call_depth;
bool debug_fin_command, debug_next_command;
unsigned short n_breakpoints;
unsigned short *breakpoints;
} GB_gameboy_t;