Initial 25-bit debugger values support

This commit is contained in:
Lior Halphon 2016-07-14 20:46:00 +03:00
parent ea082b777d
commit 65f37bccbd
3 changed files with 151 additions and 79 deletions

View File

@ -20,6 +20,14 @@ typedef struct {
}; };
} lvalue_t; } lvalue_t;
typedef struct {
bool has_bank;
uint16_t bank:9;
uint16_t value;
} value_t;
#define VALUE_16(x) ((value_t){false, 0, (x)})
struct GB_breakpoint_s { struct GB_breakpoint_s {
uint16_t addr; uint16_t addr;
char *condition; char *condition;
@ -73,21 +81,61 @@ static const char *value_to_string(GB_gameboy_t *gb, uint16_t value, bool prefer
return output; return output;
} }
static uint16_t read_lvalue(GB_gameboy_t *gb, lvalue_t lvalue) static const char *debugger_value_to_string(GB_gameboy_t *gb, value_t value, bool prefer_name)
{
if (!value.has_bank) return value_to_string(gb, value.value, prefer_name);
static __thread char output[256];
const GB_bank_symbol_t *symbol = GB_map_find_symbol(gb->bank_symbols[value.bank], value.value);
if (symbol && (value.value - symbol->addr > 0x1000 || symbol->addr == 0) ) {
symbol = NULL;
}
/* Avoid overflow */
if (symbol && strlen(symbol->name) > 240) {
symbol = NULL;
}
if (!symbol) {
sprintf(output, "$%02x:$%04x", value.bank, value.value);
}
else if (symbol->addr == value.value) {
if (prefer_name) {
sprintf(output, "%s ($%02x:$%04x)", symbol->name, value.bank, value.value);
}
else {
sprintf(output, "$%02x:$%04x (%s)", value.bank, value.value, symbol->name);
}
}
else {
if (prefer_name) {
sprintf(output, "%s+$%03x ($%02x:$%04x)", symbol->name, value.value - symbol->addr, value.bank, value.value);
}
else {
sprintf(output, "$%02x:$%04x (%s+$%03x)", value.bank, value.value, symbol->name, value.value - symbol->addr);
}
}
return output;
}
static value_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 += */
switch (lvalue.kind) { switch (lvalue.kind) {
case LVALUE_MEMORY: case LVALUE_MEMORY:
return GB_read_memory(gb, lvalue.memory_address); return VALUE_16(GB_read_memory(gb, lvalue.memory_address));
case LVALUE_REG16: case LVALUE_REG16:
return *lvalue.register_address; return VALUE_16(*lvalue.register_address);
case LVALUE_REG_L: case LVALUE_REG_L:
return *lvalue.register_address & 0x00FF; return VALUE_16(*lvalue.register_address & 0x00FF);
case LVALUE_REG_H: case LVALUE_REG_H:
return *lvalue.register_address >> 8; return VALUE_16(*lvalue.register_address >> 8);
} }
} }
@ -114,46 +162,57 @@ static void write_lvalue(GB_gameboy_t *gb, lvalue_t lvalue, uint16_t value)
} }
} }
static uint16_t add(uint16_t a, uint16_t b) {return a + b;}; /* 16 bit value <op> 16 bit value = 16 bit value
static uint16_t sub(uint16_t a, uint16_t b) {return a - b;}; 25 bit address <op> 16 bit value = 25 bit address
static uint16_t mul(uint16_t a, uint16_t b) {return a * b;}; 16 bit value <op> 25 bit address = 25 bit address
static uint16_t _div(uint16_t a, uint16_t b) { 25 bit address <op> 25 bit address = 16 bit value (since adding pointers, for examples, makes no sense)
if (b == 0) {
return 0; Boolean operators always return a 16-bit value
*/
#define FIX_BANK(x) ((value_t){a.has_bank ^ b.has_bank, a.has_bank? a.bank : b.bank, (x)})
static value_t add(value_t a, value_t b) {return FIX_BANK(a.value + b.value);}
static value_t sub(value_t a, value_t b) {return FIX_BANK(a.value - b.value);}
static value_t mul(value_t a, value_t b) {return FIX_BANK(a.value * b.value);}
static value_t _div(value_t a, value_t b) {
if (b.value == 0) {
return FIX_BANK(0);
} }
return a / b; return FIX_BANK(a.value / b.value);
}; };
static uint16_t mod(uint16_t a, uint16_t b) { static value_t mod(value_t a, value_t b) {
if (b == 0) { if (b.value == 0) {
return 0; return FIX_BANK(0);
} }
return a % b; return FIX_BANK(a.value % b.value);
}; };
static uint16_t and(uint16_t a, uint16_t b) {return a & b;}; static value_t and(value_t a, value_t b) {return FIX_BANK(a.value & b.value);}
static uint16_t or(uint16_t a, uint16_t b) {return a | b;}; static value_t or(value_t a, value_t b) {return FIX_BANK(a.value | b.value);}
static uint16_t xor(uint16_t a, uint16_t b) {return a ^ b;}; static value_t xor(value_t a, value_t b) {return FIX_BANK(a.value ^ b.value);}
static uint16_t shleft(uint16_t a, uint16_t b) {return a << b;}; static value_t shleft(value_t a, value_t b) {return FIX_BANK(a.value << b.value);}
static uint16_t shright(uint16_t a, uint16_t b) {return a >> b;}; static value_t shright(value_t a, value_t b) {return FIX_BANK(a.value >> b.value);}
static uint16_t assign(GB_gameboy_t *gb, lvalue_t a, uint16_t b) static value_t assign(GB_gameboy_t *gb, lvalue_t a, uint16_t b)
{ {
write_lvalue(gb, a, b); write_lvalue(gb, a, b);
return read_lvalue(gb, a); return read_lvalue(gb, a);
} }
static uint16_t bool_and(uint16_t a, uint16_t b) {return a && b;}; static value_t bool_and(value_t a, value_t b) {return VALUE_16(a.value && b.value);}
static uint16_t bool_or(uint16_t a, uint16_t b) {return a || b;}; static value_t bool_or(value_t a, value_t b) {return VALUE_16(a.value || b.value);}
static uint16_t equals(uint16_t a, uint16_t b) {return a == b;}; static value_t equals(value_t a, value_t b) {return VALUE_16(a.value == b.value);}
static uint16_t different(uint16_t a, uint16_t b) {return a != b;}; static value_t different(value_t a, value_t b) {return VALUE_16(a.value != b.value);}
static uint16_t lower(uint16_t a, uint16_t b) {return a < b;}; static value_t lower(value_t a, value_t b) {return VALUE_16(a.value < b.value);}
static uint16_t greater(uint16_t a, uint16_t b) {return a > b;}; static value_t greater(value_t a, value_t b) {return VALUE_16(a.value > b.value);}
static uint16_t lower_equals(uint16_t a, uint16_t b) {return a <= b;}; static value_t lower_equals(value_t a, value_t b) {return VALUE_16(a.value <= b.value);}
static uint16_t greater_equals(uint16_t a, uint16_t b) {return a >= b;}; static value_t greater_equals(value_t a, value_t b) {return VALUE_16(a.value >= b.value);}
static value_t bank(value_t a, value_t b) {return (value_t) {true, a.value, b.value};}
static struct { static struct {
const char *string; const char *string;
char priority; char priority;
uint16_t (*operator)(uint16_t, uint16_t); value_t (*operator)(value_t, value_t);
uint16_t (*lvalue_operator)(GB_gameboy_t *, lvalue_t, uint16_t); value_t (*lvalue_operator)(GB_gameboy_t *, lvalue_t, uint16_t);
} operators[] = } operators[] =
{ {
// Yes. This is not C-like. But it makes much more sense. // Yes. This is not C-like. But it makes much more sense.
@ -177,9 +236,10 @@ static struct {
{"==", 3, equals}, {"==", 3, equals},
{"=", -1, NULL, assign}, {"=", -1, NULL, assign},
{"!=", 3, different}, {"!=", 3, different},
{":", 4, bank},
}; };
uint16_t debugger_evaluate(GB_gameboy_t *gb, const char *string, value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
unsigned int length, bool *error, unsigned int length, bool *error,
uint16_t *watchpoint_address, uint8_t *watchpoint_new_value); uint16_t *watchpoint_address, uint8_t *watchpoint_new_value);
@ -229,7 +289,8 @@ static lvalue_t debugger_evaluate_lvalue(GB_gameboy_t *gb, const char *string,
if (string[i] == ']') depth--; if (string[i] == ']') depth--;
} }
if (depth == 0) { if (depth == 0) {
return (lvalue_t){LVALUE_MEMORY, .memory_address = debugger_evaluate(gb, string + 1, length - 2, error, watchpoint_address, watchpoint_new_value)}; /* Todo: Warn the user when dereferencing a specific bank, but it's not mapped */
return (lvalue_t){LVALUE_MEMORY, .memory_address = debugger_evaluate(gb, string + 1, length - 2, error, watchpoint_address, watchpoint_new_value).value};
} }
} }
@ -267,9 +328,10 @@ static lvalue_t debugger_evaluate_lvalue(GB_gameboy_t *gb, const char *string,
return (lvalue_t){0,}; return (lvalue_t){0,};
} }
uint16_t debugger_evaluate(GB_gameboy_t *gb, const char *string, #define ERROR ((value_t){0,})
unsigned int length, bool *error, value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
uint16_t *watchpoint_address, uint8_t *watchpoint_new_value) unsigned int length, bool *error,
uint16_t *watchpoint_address, uint8_t *watchpoint_new_value)
{ {
*error = false; *error = false;
// Strip whitespace // Strip whitespace
@ -284,7 +346,7 @@ uint16_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
{ {
GB_log(gb, "Expected expression.\n"); GB_log(gb, "Expected expression.\n");
*error = true; *error = true;
return -1; return ERROR;
} }
if (string[0] == '(' && string[length - 1] == ')') { if (string[0] == '(' && string[length - 1] == ')') {
// Attempt to strip parentheses // Attempt to strip parentheses
@ -312,7 +374,13 @@ uint16_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
} }
if (string[i] == ']') depth--; if (string[i] == ']') depth--;
} }
if (depth == 0) return GB_read_memory(gb, debugger_evaluate(gb, string + 1, length - 2, error, watchpoint_address, watchpoint_new_value)); /* Todo: Warn the user when dereferencing a specific bank, but it's not mapped */
if (depth == 0)
return VALUE_16(
GB_read_memory(gb,
debugger_evaluate(gb, string + 1, length - 2, error, watchpoint_address, watchpoint_new_value).value
)
);
} }
// Search for lowest priority operator // Search for lowest priority operator
signed int depth = 0; signed int depth = 0;
@ -342,15 +410,15 @@ uint16_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
} }
if (operator_index != -1) { if (operator_index != -1) {
unsigned int right_start = (unsigned int)(operator_pos + strlen(operators[operator_index].string)); unsigned int right_start = (unsigned int)(operator_pos + strlen(operators[operator_index].string));
uint16_t right = debugger_evaluate(gb, string + right_start, length - right_start, error, watchpoint_address, watchpoint_new_value); value_t right = debugger_evaluate(gb, string + right_start, length - right_start, error, watchpoint_address, watchpoint_new_value);
if (*error) return -1; if (*error) return ERROR;
if (operators[operator_index].lvalue_operator) { if (operators[operator_index].lvalue_operator) {
lvalue_t left = debugger_evaluate_lvalue(gb, string, operator_pos, error, watchpoint_address, watchpoint_new_value); lvalue_t left = debugger_evaluate_lvalue(gb, string, operator_pos, error, watchpoint_address, watchpoint_new_value);
if (*error) return -1; if (*error) return ERROR;
return operators[operator_index].lvalue_operator(gb, left, right); return operators[operator_index].lvalue_operator(gb, left, right.value);
} }
uint16_t left = debugger_evaluate(gb, string, operator_pos, error, watchpoint_address, watchpoint_new_value); value_t left = debugger_evaluate(gb, string, operator_pos, error, watchpoint_address, watchpoint_new_value);
if (*error) return -1; if (*error) return ERROR;
return operators[operator_index].operator(left, right); return operators[operator_index].operator(left, right);
} }
@ -360,43 +428,43 @@ uint16_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
if (string[0] != '$' && (string[0] < '0' || string[0] > '9')) { if (string[0] != '$' && (string[0] < '0' || string[0] > '9')) {
if (length == 1) { if (length == 1) {
switch (string[0]) { switch (string[0]) {
case 'a': return gb->registers[GB_REGISTER_AF] >> 8; case 'a': return VALUE_16(gb->registers[GB_REGISTER_AF] >> 8);
case 'f': return gb->registers[GB_REGISTER_AF] & 0xFF; case 'f': return VALUE_16(gb->registers[GB_REGISTER_AF] & 0xFF);
case 'b': return gb->registers[GB_REGISTER_BC] >> 8; case 'b': return VALUE_16(gb->registers[GB_REGISTER_BC] >> 8);
case 'c': return gb->registers[GB_REGISTER_BC] & 0xFF; case 'c': return VALUE_16(gb->registers[GB_REGISTER_BC] & 0xFF);
case 'd': return gb->registers[GB_REGISTER_DE] >> 8; case 'd': return VALUE_16(gb->registers[GB_REGISTER_DE] >> 8);
case 'e': return gb->registers[GB_REGISTER_DE] & 0xFF; case 'e': return VALUE_16(gb->registers[GB_REGISTER_DE] & 0xFF);
case 'h': return gb->registers[GB_REGISTER_HL] >> 8; case 'h': return VALUE_16(gb->registers[GB_REGISTER_HL] >> 8);
case 'l': return gb->registers[GB_REGISTER_HL] & 0xFF; case 'l': return VALUE_16(gb->registers[GB_REGISTER_HL] & 0xFF);
} }
} }
else if (length == 2) { else if (length == 2) {
switch (string[0]) { switch (string[0]) {
case 'a': if (string[1] == 'f') return gb->registers[GB_REGISTER_AF]; case 'a': if (string[1] == 'f') return VALUE_16(gb->registers[GB_REGISTER_AF]);
case 'b': if (string[1] == 'c') return gb->registers[GB_REGISTER_BC]; case 'b': if (string[1] == 'c') return VALUE_16(gb->registers[GB_REGISTER_BC]);
case 'd': if (string[1] == 'e') return gb->registers[GB_REGISTER_DE]; case 'd': if (string[1] == 'e') return VALUE_16(gb->registers[GB_REGISTER_DE]);
case 'h': if (string[1] == 'l') return gb->registers[GB_REGISTER_HL]; case 'h': if (string[1] == 'l') return VALUE_16(gb->registers[GB_REGISTER_HL]);
case 's': if (string[1] == 'p') return gb->registers[GB_REGISTER_SP]; case 's': if (string[1] == 'p') return VALUE_16(gb->registers[GB_REGISTER_SP]);
case 'p': if (string[1] == 'c') return gb->pc; case 'p': if (string[1] == 'c') return VALUE_16(gb->pc);
} }
} }
else if (length == 3) { else if (length == 3) {
if (watchpoint_address && memcmp(string, "old", 3) == 0) { if (watchpoint_address && memcmp(string, "old", 3) == 0) {
return GB_read_memory(gb, *watchpoint_address); return VALUE_16(GB_read_memory(gb, *watchpoint_address));
} }
if (watchpoint_new_value && memcmp(string, "new", 3) == 0) { if (watchpoint_new_value && memcmp(string, "new", 3) == 0) {
return *watchpoint_new_value; return VALUE_16(*watchpoint_new_value);
} }
/* $new is identical to $old in read conditions */ /* $new is identical to $old in read conditions */
if (watchpoint_address && memcmp(string, "new", 3) == 0) { if (watchpoint_address && memcmp(string, "new", 3) == 0) {
return GB_read_memory(gb, *watchpoint_address); return VALUE_16(GB_read_memory(gb, *watchpoint_address));
} }
} }
GB_log(gb, "Unknown register: %.*s\n", length, string); GB_log(gb, "Unknown register: %.*s\n", length, string);
*error = true; *error = true;
return -1; return ERROR;
} }
char *end; char *end;
@ -410,9 +478,9 @@ uint16_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
if (end != string + length) { if (end != string + length) {
GB_log(gb, "Failed to parse: %.*s\n", length, string); GB_log(gb, "Failed to parse: %.*s\n", length, string);
*error = true; *error = true;
return -1; return ERROR;
} }
return literal; return VALUE_16(literal);
} }
typedef bool debugger_command_imp_t(GB_gameboy_t *gb, char *arguments); typedef bool debugger_command_imp_t(GB_gameboy_t *gb, char *arguments);
@ -550,7 +618,7 @@ static bool breakpoint(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).value;
if (error) return true; if (error) return true;
@ -608,7 +676,7 @@ static bool delete(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).value;
if (error) return true; if (error) return true;
@ -693,7 +761,7 @@ print_usage:
} }
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).value;
if (error) return true; if (error) return true;
@ -756,7 +824,7 @@ static bool unwatch(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).value;
if (error) return true; if (error) return true;
@ -834,7 +902,7 @@ static bool should_break(GB_gameboy_t *gb, uint16_t addr)
} }
bool error; bool error;
bool condition = debugger_evaluate(gb, gb->breakpoints[index].condition, bool condition = debugger_evaluate(gb, gb->breakpoints[index].condition,
(unsigned int)strlen(gb->breakpoints[index].condition), &error, NULL, NULL); (unsigned int)strlen(gb->breakpoints[index].condition), &error, NULL, NULL).value;
if (error) { if (error) {
/* Should never happen */ /* Should never happen */
GB_log(gb, "An internal error has occured\n"); GB_log(gb, "An internal error has occured\n");
@ -853,9 +921,9 @@ 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); value_t result = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL);
if (!error) { if (!error) {
GB_log(gb, "=%s\n", value_to_string(gb, result, false)); GB_log(gb, "=%s\n", debugger_value_to_string(gb, result, false));
} }
return true; return true;
} }
@ -868,7 +936,7 @@ static bool examine(GB_gameboy_t *gb, char *arguments)
} }
bool error; bool error;
uint16_t addr = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL); uint16_t addr = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL).value;
if (!error) { if (!error) {
GB_log(gb, "%4x: ", addr); GB_log(gb, "%4x: ", addr);
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
@ -1030,7 +1098,7 @@ void GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, uint16_t addr, uint8_t
} }
bool error; bool error;
bool condition = debugger_evaluate(gb, gb->watchpoints[index].condition, bool condition = debugger_evaluate(gb, gb->watchpoints[index].condition,
(unsigned int)strlen(gb->watchpoints[index].condition), &error, &addr, &value); (unsigned int)strlen(gb->watchpoints[index].condition), &error, &addr, &value).value;
if (error) { if (error) {
/* Should never happen */ /* Should never happen */
GB_log(gb, "An internal error has occured\n"); GB_log(gb, "An internal error has occured\n");
@ -1057,7 +1125,7 @@ void GB_debugger_test_read_watchpoint(GB_gameboy_t *gb, uint16_t addr)
} }
bool error; bool error;
bool condition = debugger_evaluate(gb, gb->watchpoints[index].condition, bool condition = debugger_evaluate(gb, gb->watchpoints[index].condition,
(unsigned int)strlen(gb->watchpoints[index].condition), &error, &addr, NULL); (unsigned int)strlen(gb->watchpoints[index].condition), &error, &addr, NULL).value;
if (error) { if (error) {
/* Should never happen */ /* Should never happen */
GB_log(gb, "An internal error has occured\n"); GB_log(gb, "An internal error has occured\n");
@ -1150,6 +1218,7 @@ void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path)
char symbol[length]; char symbol[length];
if (sscanf(line, "%02x:%04x %s", &bank, &address, symbol) == 3) { if (sscanf(line, "%02x:%04x %s", &bank, &address, symbol) == 3) {
bank &= 0x1FF;
if (!gb->bank_symbols[bank]) { if (!gb->bank_symbols[bank]) {
gb->bank_symbols[bank] = GB_map_alloc(); gb->bank_symbols[bank] = GB_map_alloc();
} }
@ -1162,7 +1231,7 @@ 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 GB_bank_symbol_t *GB_debugger_find_symbol(GB_gameboy_t *gb, uint16_t addr)
{ {
unsigned char bank = 0; uint16_t bank = 0;
if (addr < 0x4000) { if (addr < 0x4000) {
bank = gb->mbc_rom0_bank; bank = gb->mbc_rom0_bank;
} }
@ -1177,9 +1246,12 @@ const GB_bank_symbol_t *GB_debugger_find_symbol(GB_gameboy_t *gb, uint16_t addr)
bank = gb->cgb_ram_bank; bank = gb->cgb_ram_bank;
} }
if (bank > 0x1FF) return NULL;
const GB_bank_symbol_t *symbol = GB_map_find_symbol(gb->bank_symbols[bank], addr); const GB_bank_symbol_t *symbol = GB_map_find_symbol(gb->bank_symbols[bank], addr);
if (symbol) return symbol; if (symbol) return symbol;
if (bank != 0) GB_map_find_symbol(gb->bank_symbols[0], addr); /* Maybe the symbol incorrectly uses bank 0? */ if (bank != 0) return GB_map_find_symbol(gb->bank_symbols[0], addr); /* Maybe the symbol incorrectly uses bank 0? */
return NULL; return NULL;
} }

View File

@ -137,7 +137,7 @@ 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--;) { for (int i = 0x200; i--;) {
if (gb->bank_symbols[i]) { if (gb->bank_symbols[i]) {
GB_map_free(gb->bank_symbols[i]); GB_map_free(gb->bank_symbols[i]);
} }

View File

@ -361,7 +361,7 @@ typedef struct GB_gameboy_s {
struct GB_watchpoint_s *watchpoints; struct GB_watchpoint_s *watchpoints;
/* Symbol table */ /* Symbol table */
GB_symbol_map_t *bank_symbols[0x100]; GB_symbol_map_t *bank_symbols[0x200];
/* Misc */ /* Misc */
bool turbo; bool turbo;