mirror of https://github.com/bsnes-emu/bsnes.git
Debugger can now read .sym files, and display them. (No expression support yet)
This commit is contained in:
parent
e9b3a38171
commit
d49404d248
|
@ -3,6 +3,7 @@
|
||||||
#include "Document.h"
|
#include "Document.h"
|
||||||
#include "AppDelegate.h"
|
#include "AppDelegate.h"
|
||||||
#include "gb.h"
|
#include "gb.h"
|
||||||
|
#include "debugger.h"
|
||||||
|
|
||||||
@interface Document ()
|
@interface Document ()
|
||||||
{
|
{
|
||||||
|
@ -204,6 +205,8 @@ static uint32_t rgbEncode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b)
|
||||||
}
|
}
|
||||||
GB_load_rom(&gb, [fileName UTF8String]);
|
GB_load_rom(&gb, [fileName UTF8String]);
|
||||||
GB_load_battery(&gb, [[[fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"sav"] UTF8String]);
|
GB_load_battery(&gb, [[[fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"sav"] UTF8String]);
|
||||||
|
GB_debugger_load_symbol_file(&gb, [[[fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"sym"] UTF8String]);
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
159
Core/debugger.c
159
Core/debugger.c
|
@ -34,6 +34,40 @@ struct GB_watchpoint_s {
|
||||||
uint8_t flags;
|
uint8_t flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static const char *value_to_string(GB_gameboy_t *gb, uint16_t value, bool prefer_name)
|
||||||
|
{
|
||||||
|
static __thread char output[256];
|
||||||
|
const GB_bank_symbol_t *symbol = GB_debugger_find_symbol(gb, value);
|
||||||
|
|
||||||
|
if (symbol && (value - symbol->addr > 0x1000 || symbol->addr == 0) ) {
|
||||||
|
symbol = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!symbol) {
|
||||||
|
sprintf(output, "$%04x", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (symbol->addr == value) {
|
||||||
|
if (prefer_name) {
|
||||||
|
sprintf(output, "%s ($%04x)", symbol->name, value);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sprintf(output, "$%04x (%s)", value, symbol->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
if (prefer_name) {
|
||||||
|
sprintf(output, "%s+$%03x ($%04x)", symbol->name, value - symbol->addr, value);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sprintf(output, "$%04x (%s+$%03x)", value, symbol->name, value - symbol->addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
static uint16_t read_lvalue(GB_gameboy_t *gb, lvalue_t lvalue)
|
static uint16_t read_lvalue(GB_gameboy_t *gb, lvalue_t lvalue)
|
||||||
{
|
{
|
||||||
/* Not used until we add support for operators like += */
|
/* Not used until we add support for operators like += */
|
||||||
|
@ -459,12 +493,13 @@ static bool registers(GB_gameboy_t *gb, char *arguments)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
GB_log(gb, "AF = $%04x\n", gb->registers[GB_REGISTER_AF]);
|
GB_log(gb, "AF = $%04x\n", gb->registers[GB_REGISTER_AF]); /* AF can't really be an address */
|
||||||
GB_log(gb, "BC = $%04x\n", gb->registers[GB_REGISTER_BC]);
|
GB_log(gb, "BC = %s\n", value_to_string(gb, gb->registers[GB_REGISTER_BC], false));
|
||||||
GB_log(gb, "DE = $%04x\n", gb->registers[GB_REGISTER_DE]);
|
GB_log(gb, "DE = %s\n", value_to_string(gb, gb->registers[GB_REGISTER_DE], false));
|
||||||
GB_log(gb, "HL = $%04x\n", gb->registers[GB_REGISTER_HL]);
|
GB_log(gb, "HL = %s\n", value_to_string(gb, gb->registers[GB_REGISTER_HL], false));
|
||||||
GB_log(gb, "SP = $%04x\n", gb->registers[GB_REGISTER_SP]);
|
GB_log(gb, "SP = %s\n", value_to_string(gb, gb->registers[GB_REGISTER_SP], false));
|
||||||
GB_log(gb, "PC = $%04x\n", gb->pc);
|
GB_log(gb, "PC = %s\n", value_to_string(gb, gb->pc, false));
|
||||||
|
|
||||||
GB_log(gb, "TIMA = %d/%u\n", gb->io_registers[GB_IO_TIMA], gb->tima_cycles);
|
GB_log(gb, "TIMA = %d/%u\n", gb->io_registers[GB_IO_TIMA], gb->tima_cycles);
|
||||||
GB_log(gb, "Display Controller: LY = %d/%u\n", gb->io_registers[GB_IO_LY], gb->display_cycles % 456);
|
GB_log(gb, "Display Controller: LY = %d/%u\n", gb->io_registers[GB_IO_LY], gb->display_cycles % 456);
|
||||||
return true;
|
return true;
|
||||||
|
@ -516,7 +551,7 @@ static bool breakpoint(GB_gameboy_t *gb, char *arguments)
|
||||||
|
|
||||||
uint16_t index = find_breakpoint(gb, result);
|
uint16_t index = find_breakpoint(gb, result);
|
||||||
if (index < gb->n_breakpoints && gb->breakpoints[index].addr == result) {
|
if (index < gb->n_breakpoints && gb->breakpoints[index].addr == result) {
|
||||||
GB_log(gb, "Breakpoint already set at $%04x\n", result);
|
GB_log(gb, "Breakpoint already set at %s\n", value_to_string(gb, result, true));
|
||||||
if (!gb->breakpoints[index].condition && condition) {
|
if (!gb->breakpoints[index].condition && condition) {
|
||||||
GB_log(gb, "Added condition to breakpoint\n");
|
GB_log(gb, "Added condition to breakpoint\n");
|
||||||
gb->breakpoints[index].condition = strdup(condition);
|
gb->breakpoints[index].condition = strdup(condition);
|
||||||
|
@ -545,7 +580,7 @@ static bool breakpoint(GB_gameboy_t *gb, char *arguments)
|
||||||
}
|
}
|
||||||
gb->n_breakpoints++;
|
gb->n_breakpoints++;
|
||||||
|
|
||||||
GB_log(gb, "Breakpoint set at $%04x\n", result);
|
GB_log(gb, "Breakpoint set at %s\n", value_to_string(gb, result, true));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -574,7 +609,7 @@ static bool delete(GB_gameboy_t *gb, char *arguments)
|
||||||
|
|
||||||
uint16_t index = find_breakpoint(gb, result);
|
uint16_t index = find_breakpoint(gb, result);
|
||||||
if (index >= gb->n_breakpoints || gb->breakpoints[index].addr != result) {
|
if (index >= gb->n_breakpoints || gb->breakpoints[index].addr != result) {
|
||||||
GB_log(gb, "No breakpoint set at $%04x\n", result);
|
GB_log(gb, "No breakpoint set at %s\n", value_to_string(gb, result, true));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -586,7 +621,7 @@ static bool delete(GB_gameboy_t *gb, char *arguments)
|
||||||
gb->n_breakpoints--;
|
gb->n_breakpoints--;
|
||||||
gb->breakpoints = realloc(gb->breakpoints, gb->n_breakpoints * sizeof(gb->breakpoints[0]));
|
gb->breakpoints = realloc(gb->breakpoints, gb->n_breakpoints * sizeof(gb->breakpoints[0]));
|
||||||
|
|
||||||
GB_log(gb, "Breakpoint removed from $%04x\n", result);
|
GB_log(gb, "Breakpoint removed from %s\n", value_to_string(gb, result, true));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -659,7 +694,7 @@ print_usage:
|
||||||
|
|
||||||
uint16_t index = find_watchpoint(gb, result);
|
uint16_t index = find_watchpoint(gb, result);
|
||||||
if (index < gb->n_watchpoints && gb->watchpoints[index].addr == result) {
|
if (index < gb->n_watchpoints && gb->watchpoints[index].addr == result) {
|
||||||
GB_log(gb, "Watchpoint already set at $%04x\n", result);
|
GB_log(gb, "Watchpoint already set at %s\n", value_to_string(gb, result, true));
|
||||||
if (!gb->watchpoints[index].flags != flags) {
|
if (!gb->watchpoints[index].flags != flags) {
|
||||||
GB_log(gb, "Modified watchpoint type\n");
|
GB_log(gb, "Modified watchpoint type\n");
|
||||||
gb->watchpoints[index].flags = flags;
|
gb->watchpoints[index].flags = flags;
|
||||||
|
@ -693,7 +728,7 @@ print_usage:
|
||||||
}
|
}
|
||||||
gb->n_watchpoints++;
|
gb->n_watchpoints++;
|
||||||
|
|
||||||
GB_log(gb, "Watchpoint set at $%04x\n", result);
|
GB_log(gb, "Watchpoint set at %s\n", value_to_string(gb, result, true));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -722,7 +757,7 @@ static bool unwatch(GB_gameboy_t *gb, char *arguments)
|
||||||
|
|
||||||
uint16_t index = find_watchpoint(gb, result);
|
uint16_t index = find_watchpoint(gb, result);
|
||||||
if (index >= gb->n_watchpoints || gb->watchpoints[index].addr != result) {
|
if (index >= gb->n_watchpoints || gb->watchpoints[index].addr != result) {
|
||||||
GB_log(gb, "No watchpoint set at $%04x\n", result);
|
GB_log(gb, "No watchpoint set at %s\n", value_to_string(gb, result, true));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -734,7 +769,7 @@ static bool unwatch(GB_gameboy_t *gb, char *arguments)
|
||||||
gb->n_watchpoints--;
|
gb->n_watchpoints--;
|
||||||
gb->watchpoints = realloc(gb->watchpoints, gb->n_watchpoints* sizeof(gb->watchpoints[0]));
|
gb->watchpoints = realloc(gb->watchpoints, gb->n_watchpoints* sizeof(gb->watchpoints[0]));
|
||||||
|
|
||||||
GB_log(gb, "Watchpoint removed from $%04x\n", result);
|
GB_log(gb, "Watchpoint removed from %s\n", value_to_string(gb, result, true));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -752,10 +787,12 @@ static bool list(GB_gameboy_t *gb, char *arguments)
|
||||||
GB_log(gb, "%d breakpoint(s) set:\n", gb->n_breakpoints);
|
GB_log(gb, "%d breakpoint(s) set:\n", gb->n_breakpoints);
|
||||||
for (uint16_t i = 0; i < gb->n_breakpoints; i++) {
|
for (uint16_t i = 0; i < gb->n_breakpoints; i++) {
|
||||||
if (gb->breakpoints[i].condition) {
|
if (gb->breakpoints[i].condition) {
|
||||||
GB_log(gb, " %d. $%04x (Condition: %s)\n", i + 1, gb->breakpoints[i].addr, gb->breakpoints[i].condition);
|
GB_log(gb, " %d. %s (Condition: %s)\n", i + 1,
|
||||||
|
value_to_string(gb, gb->breakpoints[i].addr, true),
|
||||||
|
gb->breakpoints[i].condition);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
GB_log(gb, " %d. $%04x\n", i + 1, gb->breakpoints[i].addr);
|
GB_log(gb, " %d. %s\n", i + 1, value_to_string(gb, gb->breakpoints[i].addr, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -767,15 +804,15 @@ static bool list(GB_gameboy_t *gb, char *arguments)
|
||||||
GB_log(gb, "%d watchpoint(s) set:\n", gb->n_watchpoints);
|
GB_log(gb, "%d watchpoint(s) set:\n", gb->n_watchpoints);
|
||||||
for (uint16_t i = 0; i < gb->n_watchpoints; i++) {
|
for (uint16_t i = 0; i < gb->n_watchpoints; i++) {
|
||||||
if (gb->watchpoints[i].condition) {
|
if (gb->watchpoints[i].condition) {
|
||||||
GB_log(gb, " %d. $%04x (%c%c, Condition: %s)\n", i + 1, gb->watchpoints[i].addr,
|
GB_log(gb, " %d. %s (%c%c, Condition: %s)\n", i + 1, value_to_string(gb, gb->watchpoints[i].addr, true),
|
||||||
(gb->watchpoints[i].flags & GB_WATCHPOINT_R)? 'r' : '-',
|
(gb->watchpoints[i].flags & GB_WATCHPOINT_R)? 'r' : '-',
|
||||||
(gb->watchpoints[i].flags & GB_WATCHPOINT_W)? 'w' : '-',
|
(gb->watchpoints[i].flags & GB_WATCHPOINT_W)? 'w' : '-',
|
||||||
gb->watchpoints[i].condition);
|
gb->watchpoints[i].condition);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
GB_log(gb, " %d. $%04x (%c%c)\n", i + 1, gb->watchpoints[i].addr,
|
GB_log(gb, " %d. %s (%c%c)\n", i + 1, value_to_string(gb, gb->watchpoints[i].addr, true),
|
||||||
(gb->watchpoints[i].flags & GB_WATCHPOINT_R)? 'r' : '-',
|
(gb->watchpoints[i].flags & GB_WATCHPOINT_R)? 'r' : '-',
|
||||||
(gb->watchpoints[i].flags & GB_WATCHPOINT_W)? 'w' : '-');
|
(gb->watchpoints[i].flags & GB_WATCHPOINT_W)? 'w' : '-');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -813,7 +850,7 @@ static bool print(GB_gameboy_t *gb, char *arguments)
|
||||||
bool error;
|
bool error;
|
||||||
uint16_t result = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL);
|
uint16_t result = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL);
|
||||||
if (!error) {
|
if (!error) {
|
||||||
GB_log(gb, "=$%04x\n", result);
|
GB_log(gb, "=%s\n", value_to_string(gb, result, false));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -965,7 +1002,7 @@ void GB_debugger_ret_hook(GB_gameboy_t *gb)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (gb->registers[GB_REGISTER_SP] != gb->sp_for_call_depth[gb->debug_call_depth]) {
|
if (gb->registers[GB_REGISTER_SP] != gb->sp_for_call_depth[gb->debug_call_depth]) {
|
||||||
GB_log(gb, "Stack leak detected for function $%04x!\n", gb->addr_for_call_depth[gb->debug_call_depth]);
|
GB_log(gb, "Stack leak detected for function %s!\n", value_to_string(gb, gb->addr_for_call_depth[gb->debug_call_depth], true));
|
||||||
GB_log(gb, "SP is $%04x, should be $%04x.\n", gb->registers[GB_REGISTER_SP],
|
GB_log(gb, "SP is $%04x, should be $%04x.\n", gb->registers[GB_REGISTER_SP],
|
||||||
gb->sp_for_call_depth[gb->debug_call_depth]);
|
gb->sp_for_call_depth[gb->debug_call_depth]);
|
||||||
gb->debug_stopped = true;
|
gb->debug_stopped = true;
|
||||||
|
@ -983,7 +1020,7 @@ void GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, uint16_t addr, uint8_t
|
||||||
}
|
}
|
||||||
if (!gb->watchpoints[index].condition) {
|
if (!gb->watchpoints[index].condition) {
|
||||||
gb->debug_stopped = true;
|
gb->debug_stopped = true;
|
||||||
GB_log(gb, "Watchpoint: [$%04x] = $%02x\n", addr, value);
|
GB_log(gb, "Watchpoint: [%s] = $%02x\n", value_to_string(gb, addr, true), value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bool error;
|
bool error;
|
||||||
|
@ -996,7 +1033,7 @@ void GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, uint16_t addr, uint8_t
|
||||||
}
|
}
|
||||||
if (condition) {
|
if (condition) {
|
||||||
gb->debug_stopped = true;
|
gb->debug_stopped = true;
|
||||||
GB_log(gb, "Watchpoint: [$%04x] = $%02x\n", addr, value);
|
GB_log(gb, "Watchpoint: [%s] = $%02x\n", value_to_string(gb, addr, true), value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1010,7 +1047,7 @@ void GB_debugger_test_read_watchpoint(GB_gameboy_t *gb, uint16_t addr)
|
||||||
}
|
}
|
||||||
if (!gb->watchpoints[index].condition) {
|
if (!gb->watchpoints[index].condition) {
|
||||||
gb->debug_stopped = true;
|
gb->debug_stopped = true;
|
||||||
GB_log(gb, "Watchpoint: [$%04x]\n", addr);
|
GB_log(gb, "Watchpoint: [%s]\n", value_to_string(gb, addr, true));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bool error;
|
bool error;
|
||||||
|
@ -1023,7 +1060,7 @@ void GB_debugger_test_read_watchpoint(GB_gameboy_t *gb, uint16_t addr)
|
||||||
}
|
}
|
||||||
if (condition) {
|
if (condition) {
|
||||||
gb->debug_stopped = true;
|
gb->debug_stopped = true;
|
||||||
GB_log(gb, "Watchpoint: [$%04x]\n", addr);
|
GB_log(gb, "Watchpoint: [%s]\n", value_to_string(gb, addr, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1046,7 +1083,7 @@ next_command:
|
||||||
}
|
}
|
||||||
if (!gb->debug_stopped && should_break(gb, gb->pc)) {
|
if (!gb->debug_stopped && should_break(gb, gb->pc)) {
|
||||||
gb->debug_stopped = true;
|
gb->debug_stopped = true;
|
||||||
GB_log(gb, "Breakpoint: PC = $%04x\n", gb->pc);
|
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->debug_stopped) {
|
if (gb->debug_stopped) {
|
||||||
|
@ -1084,4 +1121,66 @@ next_command:
|
||||||
|
|
||||||
free(input);
|
free(input);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path)
|
||||||
|
{
|
||||||
|
FILE *f = fopen(path, "r");
|
||||||
|
if (!f) return;
|
||||||
|
|
||||||
|
char *line = NULL;
|
||||||
|
size_t size = 0;
|
||||||
|
size_t length = 0;
|
||||||
|
while ((length = getline(&line, &size, f)) != -1) {
|
||||||
|
for (unsigned i = 0; i < length; i++) {
|
||||||
|
if (line[i] == ';' || line[i] == '\n' || line[i] == '\r') {
|
||||||
|
line[i] = 0;
|
||||||
|
length = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (length == 0) continue;
|
||||||
|
|
||||||
|
unsigned int bank, address;
|
||||||
|
char symbol[length];
|
||||||
|
|
||||||
|
if (sscanf(line, "%02x:%04x %s", &bank, &address, symbol) == 3) {
|
||||||
|
if (!gb->bank_symbols[bank]) {
|
||||||
|
gb->bank_symbols[bank] = GB_map_alloc();
|
||||||
|
}
|
||||||
|
GB_map_add_symbol(gb->bank_symbols[bank], address, symbol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(line);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
const GB_bank_symbol_t *GB_debugger_find_symbol(GB_gameboy_t *gb, uint16_t addr)
|
||||||
|
{
|
||||||
|
unsigned char bank = 0;
|
||||||
|
if (addr < 0x4000) {
|
||||||
|
bank = gb->mbc_rom0_bank;
|
||||||
|
}
|
||||||
|
else if (addr < 0x8000) {
|
||||||
|
bank = gb->mbc_rom_bank;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (addr < 0xD000) {
|
||||||
|
bank = 0;
|
||||||
|
}
|
||||||
|
else if (addr < 0xE000) {
|
||||||
|
bank = gb->cgb_ram_bank;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GB_bank_symbol_t *symbol = GB_map_find_symbol(gb->bank_symbols[bank], addr);
|
||||||
|
if (symbol) return symbol;
|
||||||
|
if (bank != 0) GB_map_find_symbol(gb->bank_symbols[0], addr); /* Maybe the symbol incorrectly uses bank 0? */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *GB_debugger_name_for_address(GB_gameboy_t *gb, uint16_t addr)
|
||||||
|
{
|
||||||
|
const GB_bank_symbol_t *symbol = GB_debugger_find_symbol(gb, addr);
|
||||||
|
if (symbol && symbol->addr == addr) return symbol->name;
|
||||||
|
return NULL;
|
||||||
}
|
}
|
|
@ -7,5 +7,7 @@ void GB_debugger_call_hook(GB_gameboy_t *gb);
|
||||||
void GB_debugger_ret_hook(GB_gameboy_t *gb);
|
void GB_debugger_ret_hook(GB_gameboy_t *gb);
|
||||||
void GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, uint16_t addr, uint8_t value);
|
void GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, uint16_t addr, uint8_t value);
|
||||||
void GB_debugger_test_read_watchpoint(GB_gameboy_t *gb, uint16_t addr);
|
void GB_debugger_test_read_watchpoint(GB_gameboy_t *gb, uint16_t addr);
|
||||||
|
void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path);
|
||||||
|
const GB_bank_symbol_t *GB_debugger_find_symbol(GB_gameboy_t *gb, uint16_t addr);
|
||||||
|
const char *GB_debugger_name_for_address(GB_gameboy_t *gb, uint16_t addr);
|
||||||
#endif /* debugger_h */
|
#endif /* debugger_h */
|
||||||
|
|
|
@ -137,6 +137,11 @@ void GB_free(GB_gameboy_t *gb)
|
||||||
if (gb->breakpoints) {
|
if (gb->breakpoints) {
|
||||||
free(gb->breakpoints);
|
free(gb->breakpoints);
|
||||||
}
|
}
|
||||||
|
for (unsigned char i = 0; i--;) {
|
||||||
|
if (gb->bank_symbols[i]) {
|
||||||
|
GB_map_free(gb->bank_symbols[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int GB_load_boot_rom(GB_gameboy_t *gb, const char *path)
|
int GB_load_boot_rom(GB_gameboy_t *gb, const char *path)
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include "apu.h"
|
#include "apu.h"
|
||||||
#include "save_struct.h"
|
#include "save_struct.h"
|
||||||
|
#include "symbol_hash.h"
|
||||||
|
|
||||||
|
|
||||||
#define GB_STRUCT_VERSION 9
|
#define GB_STRUCT_VERSION 9
|
||||||
|
@ -359,6 +360,8 @@ typedef struct GB_gameboy_s {
|
||||||
uint16_t n_watchpoints;
|
uint16_t n_watchpoints;
|
||||||
struct GB_watchpoint_s *watchpoints;
|
struct GB_watchpoint_s *watchpoints;
|
||||||
|
|
||||||
|
/* Symbol table */
|
||||||
|
GB_symbol_map_t *bank_symbols[0x100];
|
||||||
|
|
||||||
/* Misc */
|
/* Misc */
|
||||||
bool turbo;
|
bool turbo;
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
#include "gb.h"
|
||||||
|
|
||||||
|
static size_t GB_map_find_symbol_index(GB_symbol_map_t *map, uint16_t addr)
|
||||||
|
{
|
||||||
|
if (!map->symbols) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ssize_t min = 0;
|
||||||
|
ssize_t max = map->n_symbols;
|
||||||
|
while (min < max) {
|
||||||
|
size_t pivot = (min + max) / 2;
|
||||||
|
if (map->symbols[pivot].addr == addr) return pivot;
|
||||||
|
if (map->symbols[pivot].addr > addr) {
|
||||||
|
max = pivot;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
min = pivot + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (size_t) min;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GB_map_add_symbol(GB_symbol_map_t *map, uint16_t addr, const char *name)
|
||||||
|
{
|
||||||
|
size_t index = GB_map_find_symbol_index(map, addr);
|
||||||
|
|
||||||
|
if (index < map->n_symbols && map->symbols[index].addr == addr) return;
|
||||||
|
|
||||||
|
map->symbols = realloc(map->symbols, (map->n_symbols + 1) * sizeof(map->symbols[0]));
|
||||||
|
memmove(&map->symbols[index + 1], &map->symbols[index], (map->n_symbols - index) * sizeof(map->symbols[0]));
|
||||||
|
map->symbols[index].addr = addr;
|
||||||
|
map->symbols[index].name = strdup(name);
|
||||||
|
map->n_symbols++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GB_bank_symbol_t *GB_map_find_symbol(GB_symbol_map_t *map, uint16_t addr)
|
||||||
|
{
|
||||||
|
size_t index = GB_map_find_symbol_index(map, addr);
|
||||||
|
if (index < map->n_symbols && map->symbols[index].addr != addr) {
|
||||||
|
index--;
|
||||||
|
}
|
||||||
|
if (index < map->n_symbols) {
|
||||||
|
return &map->symbols[index];
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
GB_symbol_map_t *GB_map_alloc(void)
|
||||||
|
{
|
||||||
|
GB_symbol_map_t *map = malloc(sizeof(*map));
|
||||||
|
memset(map, 0, sizeof(*map));
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GB_map_free(GB_symbol_map_t *map)
|
||||||
|
{
|
||||||
|
for (unsigned char i = 0; i < map->n_symbols; i++) {
|
||||||
|
free(map->symbols[i].name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (map->symbols) {
|
||||||
|
free(map->symbols);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(map);
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef symbol_hash_h
|
||||||
|
#define symbol_hash_h
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t addr;
|
||||||
|
char *name;
|
||||||
|
} GB_bank_symbol_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GB_bank_symbol_t *symbols;
|
||||||
|
size_t n_symbols;
|
||||||
|
} GB_symbol_map_t;
|
||||||
|
|
||||||
|
void GB_map_add_symbol(GB_symbol_map_t *map, uint16_t addr, const char *name);
|
||||||
|
const GB_bank_symbol_t *GB_map_find_symbol(GB_symbol_map_t *map, uint16_t addr);
|
||||||
|
GB_symbol_map_t *GB_map_alloc(void);
|
||||||
|
void GB_map_free(GB_symbol_map_t *map);
|
||||||
|
|
||||||
|
#endif /* symbol_hash_h */
|
|
@ -3,6 +3,7 @@
|
||||||
#include "z80_cpu.h"
|
#include "z80_cpu.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "gb.h"
|
#include "gb.h"
|
||||||
|
#include "debugger.h"
|
||||||
|
|
||||||
|
|
||||||
typedef void GB_opcode_t(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc);
|
typedef void GB_opcode_t(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc);
|
||||||
|
@ -34,7 +35,13 @@ static void ld_rr_d16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
|
||||||
register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1;
|
register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1;
|
||||||
value = GB_read_memory(gb, (*pc)++);
|
value = GB_read_memory(gb, (*pc)++);
|
||||||
value |= GB_read_memory(gb, (*pc)++) << 8;
|
value |= GB_read_memory(gb, (*pc)++) << 8;
|
||||||
GB_log(gb, "LD %s, $%04x\n", register_names[register_id], value);
|
const char *symbol = GB_debugger_name_for_address(gb, value);
|
||||||
|
if (symbol) {
|
||||||
|
GB_log(gb, "LD %s, %s ; =$%04x\n", register_names[register_id], symbol, value);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
GB_log(gb, "LD %s, $%04x\n", register_names[register_id], value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ld_drr_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
|
static void ld_drr_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
|
||||||
|
@ -92,7 +99,13 @@ static void ld_da16_sp(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc){
|
||||||
(*pc)++;
|
(*pc)++;
|
||||||
addr = GB_read_memory(gb, (*pc)++);
|
addr = GB_read_memory(gb, (*pc)++);
|
||||||
addr |= GB_read_memory(gb, (*pc)++) << 8;
|
addr |= GB_read_memory(gb, (*pc)++) << 8;
|
||||||
GB_log(gb, "LD [$%04x], sp\n", addr);
|
const char *symbol = GB_debugger_name_for_address(gb, addr);
|
||||||
|
if (symbol) {
|
||||||
|
GB_log(gb, "LD [%s], sp ; =$%04x\n", symbol, addr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
GB_log(gb, "LD [$%04x], sp\n", addr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_hl_rr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
|
static void add_hl_rr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
|
||||||
|
@ -155,7 +168,14 @@ static void rra(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
|
||||||
static void jr_r8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
|
static void jr_r8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
|
||||||
{
|
{
|
||||||
(*pc)++;
|
(*pc)++;
|
||||||
GB_attributed_log(gb, GB_LOG_UNDERLINE, "JR $%04x\n", *pc + (int8_t) GB_read_memory(gb, (*pc)) + 1);
|
uint16_t addr = *pc + (int8_t) GB_read_memory(gb, (*pc)) + 1;
|
||||||
|
const char *symbol = GB_debugger_name_for_address(gb, addr);
|
||||||
|
if (symbol) {
|
||||||
|
GB_attributed_log(gb, GB_LOG_UNDERLINE, "JR %s ; =$%04x\n", symbol, addr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
GB_attributed_log(gb, GB_LOG_UNDERLINE, "JR $%04x\n", addr);
|
||||||
|
}
|
||||||
(*pc)++;
|
(*pc)++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,7 +198,14 @@ static const char *condition_code(uint8_t opcode)
|
||||||
static void jr_cc_r8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
|
static void jr_cc_r8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
|
||||||
{
|
{
|
||||||
(*pc)++;
|
(*pc)++;
|
||||||
GB_attributed_log(gb, GB_LOG_DASHED_UNDERLINE, "JR %s, $%04x\n", condition_code(opcode), *pc + (int8_t)GB_read_memory(gb, (*pc)) + 1);
|
uint16_t addr = *pc + (int8_t) GB_read_memory(gb, (*pc)) + 1;
|
||||||
|
const char *symbol = GB_debugger_name_for_address(gb, addr);
|
||||||
|
if (symbol) {
|
||||||
|
GB_attributed_log(gb, GB_LOG_DASHED_UNDERLINE, "JR %s, %s ; =$%04x\n", condition_code(opcode), symbol, addr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
GB_attributed_log(gb, GB_LOG_DASHED_UNDERLINE, "JR %s, $%04x\n", condition_code(opcode), addr);
|
||||||
|
}
|
||||||
(*pc)++;
|
(*pc)++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,21 +383,42 @@ static void pop_rr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
|
||||||
static void jp_cc_a16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
|
static void jp_cc_a16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
|
||||||
{
|
{
|
||||||
(*pc)++;
|
(*pc)++;
|
||||||
GB_attributed_log(gb, GB_LOG_DASHED_UNDERLINE, "JP %s, $%04x\n", condition_code(opcode), GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8));
|
uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8);
|
||||||
|
const char *symbol = GB_debugger_name_for_address(gb, addr);
|
||||||
|
if (symbol) {
|
||||||
|
GB_attributed_log(gb, GB_LOG_DASHED_UNDERLINE, "JP %s, %s ; =$%04x\n", condition_code(opcode), symbol, addr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
GB_attributed_log(gb, GB_LOG_DASHED_UNDERLINE, "JP %s, $%04x\n", condition_code(opcode), addr);
|
||||||
|
}
|
||||||
(*pc) += 2;
|
(*pc) += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void jp_a16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
|
static void jp_a16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
|
||||||
{
|
{
|
||||||
(*pc)++;
|
(*pc)++;
|
||||||
GB_log(gb, "JP $%04x\n", GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8));
|
uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8);
|
||||||
|
const char *symbol = GB_debugger_name_for_address(gb, addr);
|
||||||
|
if (symbol) {
|
||||||
|
GB_log(gb, "JP %s ; =$%04x\n", symbol, addr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
GB_log(gb, "JP $%04x\n", addr);
|
||||||
|
}
|
||||||
(*pc) += 2;
|
(*pc) += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void call_cc_a16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
|
static void call_cc_a16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
|
||||||
{
|
{
|
||||||
(*pc)++;
|
(*pc)++;
|
||||||
GB_log(gb, "CALL %s, $%04x\n", condition_code(opcode), GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8));
|
uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8);
|
||||||
|
const char *symbol = GB_debugger_name_for_address(gb, addr);
|
||||||
|
if (symbol) {
|
||||||
|
GB_log(gb, "CALL %s, %s ; =$%04x\n", condition_code(opcode), symbol, addr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
GB_log(gb, "CALL %s, $%04x\n", condition_code(opcode), addr);
|
||||||
|
}
|
||||||
(*pc) += 2;
|
(*pc) += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -451,7 +499,14 @@ static void reti(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
|
||||||
static void call_a16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
|
static void call_a16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
|
||||||
{
|
{
|
||||||
(*pc)++;
|
(*pc)++;
|
||||||
GB_log(gb, "CALL $%04x\n", GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8));
|
uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8);
|
||||||
|
const char *symbol = GB_debugger_name_for_address(gb, addr);
|
||||||
|
if (symbol) {
|
||||||
|
GB_log(gb, "CALL %s ; =$%04x\n", symbol, addr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
GB_log(gb, "CALL $%04x\n", addr);
|
||||||
|
}
|
||||||
(*pc) += 2;
|
(*pc) += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -497,14 +552,28 @@ static void jp_hl(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
|
||||||
static void ld_da16_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
|
static void ld_da16_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
|
||||||
{
|
{
|
||||||
(*pc)++;
|
(*pc)++;
|
||||||
GB_log(gb, "LD [$%04x], a\n", GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8));
|
uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8);
|
||||||
|
const char *symbol = GB_debugger_name_for_address(gb, addr);
|
||||||
|
if (symbol) {
|
||||||
|
GB_log(gb, "LD [%s], a ; =$%04x\n", symbol, addr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
GB_log(gb, "LD [$%04x], a\n", addr);
|
||||||
|
}
|
||||||
(*pc) += 2;
|
(*pc) += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ld_a_da16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
|
static void ld_a_da16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc)
|
||||||
{
|
{
|
||||||
(*pc)++;
|
(*pc)++;
|
||||||
GB_log(gb, "LD a, [$%04x]\n", GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8));
|
uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8);
|
||||||
|
const char *symbol = GB_debugger_name_for_address(gb, addr);
|
||||||
|
if (symbol) {
|
||||||
|
GB_log(gb, "LD a, [%s] ; =$%04x\n", symbol, addr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
GB_log(gb, "LD a, [$%04x]\n", addr);
|
||||||
|
}
|
||||||
(*pc) += 2;
|
(*pc) += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -668,10 +737,36 @@ static GB_opcode_t *opcodes[256] = {
|
||||||
ld_hl_sp_r8,ld_sp_hl, ld_a_da16, ei, ill, ill, cp_a_d8, rst,
|
ld_hl_sp_r8,ld_sp_hl, ld_a_da16, ei, ill, ill, cp_a_d8, rst,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void GB_cpu_disassemble(GB_gameboy_t *gb, uint16_t pc, uint16_t count)
|
void GB_cpu_disassemble(GB_gameboy_t *gb, uint16_t pc, uint16_t count)
|
||||||
{
|
{
|
||||||
|
const GB_bank_symbol_t *function_symbol = GB_debugger_find_symbol(gb, pc);
|
||||||
|
|
||||||
|
if (function_symbol && pc - function_symbol->addr > 0x1000) {
|
||||||
|
function_symbol = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (function_symbol && pc != function_symbol->addr) {
|
||||||
|
GB_log(gb, "%s:\n", function_symbol->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t current_function = function_symbol? function_symbol->addr : 0;
|
||||||
|
|
||||||
while (count--) {
|
while (count--) {
|
||||||
GB_log(gb, "%s%04x: ", pc == gb->pc? "-> ": " ", pc);
|
function_symbol = GB_debugger_find_symbol(gb, pc);
|
||||||
|
if (function_symbol && function_symbol->addr == pc) {
|
||||||
|
if (current_function != function_symbol->addr) {
|
||||||
|
GB_log(gb, "\n");
|
||||||
|
}
|
||||||
|
GB_log(gb, "%s:\n", function_symbol->name);
|
||||||
|
}
|
||||||
|
if (function_symbol) {
|
||||||
|
GB_log(gb, "%s%04x <+%03x>: ", pc == gb->pc? " ->": " ", pc, pc - function_symbol->addr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
GB_log(gb, "%s%04x: ", pc == gb->pc? " ->": " ", pc);
|
||||||
|
}
|
||||||
uint8_t opcode = GB_read_memory(gb, pc);
|
uint8_t opcode = GB_read_memory(gb, pc);
|
||||||
opcodes[opcode](gb, opcode, &pc);
|
opcodes[opcode](gb, opcode, &pc);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue