git.y1.nz

SameBoy

Accurate GB/GBC emulator
download: https://git.y1.nz/archives/sameboy.tar.gz
README | Files | Log | Refs | LICENSE

Core/debugger.c

      1 #include <stdio.h>
      2 #include <string.h>
      3 #include <stdlib.h>
      4 #include "gb.h"
      5 
      6 typedef struct {
      7     bool has_bank;
      8     uint16_t bank:9;
      9     uint16_t value;
     10 } value_t;
     11 
     12 typedef struct {
     13     enum {
     14         LVALUE_MEMORY,
     15         LVALUE_MEMORY16,
     16         LVALUE_REG16,
     17         LVALUE_REG_H,
     18         LVALUE_REG_L,
     19     } kind;
     20     union {
     21         uint16_t *register_address;
     22         value_t memory_address;
     23     };
     24 } lvalue_t;
     25 
     26 #define VALUE_16(x) ((value_t){false, 0, (x)})
     27 
     28 struct GB_breakpoint_s {
     29     unsigned id;
     30     union {
     31         struct {
     32             uint16_t addr;
     33             uint16_t bank; /* -1 = any bank*/
     34         };
     35         uint32_t key; /* For sorting and comparing */
     36     };
     37     char *condition;
     38     bool is_jump_to;
     39     uint16_t length;
     40     bool inclusive;
     41 };
     42 
     43 #define BP_KEY(x) (((struct GB_breakpoint_s){.addr = ((x).value), .bank = (x).has_bank? (x).bank : -1 }).key)
     44 
     45 #define WATCHPOINT_READ (1)
     46 #define WATCHPOINT_WRITE (2)
     47 
     48 struct GB_watchpoint_s {
     49     unsigned id;
     50     union {
     51         struct {
     52             uint16_t addr;
     53             uint16_t bank; /* -1 = any bank*/
     54         };
     55         uint32_t key; /* For sorting and comparing */
     56     };
     57     char *condition;
     58     uint8_t flags;
     59     uint16_t length;
     60     bool inclusive;
     61 };
     62 
     63 #define WP_KEY(x) (((struct GB_watchpoint_s){.addr = ((x).value), .bank = (x).has_bank? (x).bank : -1 }).key)
     64 
     65 static uint16_t bank_for_addr(GB_gameboy_t *gb, uint16_t addr)
     66 {
     67     if (addr < 0x4000) {
     68         return gb->mbc_rom0_bank;
     69     }
     70 
     71     if (addr < 0x8000) {
     72         return gb->mbc_rom_bank;
     73     }
     74 
     75     if (addr < 0xD000) {
     76         return 0;
     77     }
     78 
     79     if (addr < 0xE000) {
     80         return gb->cgb_ram_bank;
     81     }
     82 
     83     return 0;
     84 }
     85 
     86 typedef struct {
     87     uint16_t rom0_bank;
     88     uint16_t rom_bank;
     89     uint8_t mbc_ram_bank;
     90     bool mbc_ram_enable;
     91     uint8_t ram_bank;
     92     uint8_t vram_bank;
     93 } banking_state_t;
     94 
     95 static inline void save_banking_state(GB_gameboy_t *gb, banking_state_t *state)
     96 {
     97     state->rom0_bank = gb->mbc_rom0_bank;
     98     state->rom_bank = gb->mbc_rom_bank;
     99     state->mbc_ram_bank = gb->mbc_ram_bank;
    100     state->mbc_ram_enable = gb->mbc_ram_enable;
    101     state->ram_bank = gb->cgb_ram_bank;
    102     state->vram_bank = gb->cgb_vram_bank;
    103 }
    104 
    105 static inline void restore_banking_state(GB_gameboy_t *gb, banking_state_t *state)
    106 {
    107 
    108     gb->mbc_rom0_bank = state->rom0_bank;
    109     gb->mbc_rom_bank = state->rom_bank;
    110     gb->mbc_ram_bank = state->mbc_ram_bank;
    111     gb->mbc_ram_enable = state->mbc_ram_enable;
    112     gb->cgb_ram_bank = state->ram_bank;
    113     gb->cgb_vram_bank = state->vram_bank;
    114 }
    115 
    116 static inline void switch_banking_state(GB_gameboy_t *gb, uint16_t bank)
    117 {
    118     gb->mbc_rom0_bank = bank;
    119     gb->mbc_rom_bank = bank;
    120     gb->mbc_ram_bank = bank;
    121     gb->mbc_ram_enable = true;
    122     if (GB_is_cgb(gb)) {
    123         gb->cgb_ram_bank = bank & 7;
    124         gb->cgb_vram_bank = bank & 1;
    125         if (gb->cgb_ram_bank == 0) {
    126             gb->cgb_ram_bank = 1;
    127         }
    128     }
    129 }
    130 
    131 static const char *value_to_string(GB_gameboy_t *gb, uint16_t value, bool prefer_name, bool prefer_local, bool prefer_no_padding)
    132 {
    133     static __thread char output[256];
    134     const GB_bank_symbol_t *symbol = GB_debugger_find_symbol(gb, value, prefer_local);
    135 
    136     if (symbol && (value - symbol->addr > 0x1000 || symbol->addr == 0) ) {
    137         symbol = NULL;
    138     }
    139 
    140     if (!symbol) {
    141         if (prefer_no_padding) {
    142             snprintf(output, sizeof(output), "$%x", value);
    143         }
    144         else {
    145             snprintf(output, sizeof(output), "$%04x", value);
    146         }
    147     }
    148     else if (symbol->addr == value) {
    149         if (prefer_name) {
    150             snprintf(output, sizeof(output), "%s ($%04x)", symbol->name, value);
    151         }
    152         else {
    153             snprintf(output, sizeof(output), "$%04x (%s)", value, symbol->name);
    154         }
    155     }
    156 
    157     else {
    158         if (prefer_name) {
    159             snprintf(output, sizeof(output), "%s+$%03x ($%04x)", symbol->name, value - symbol->addr, value);
    160         }
    161         else {
    162             snprintf(output, sizeof(output), "$%04x (%s+$%03x)", value, symbol->name, value - symbol->addr);
    163         }
    164     }
    165     return output;
    166 }
    167 
    168 static GB_symbol_map_t *get_symbol_map(GB_gameboy_t *gb, uint16_t bank)
    169 {
    170     if (bank >= gb->n_symbol_maps) {
    171         return NULL;
    172     }
    173     return gb->bank_symbols[bank];
    174 }
    175 
    176 static const char *debugger_value_to_string(GB_gameboy_t *gb, value_t value, bool prefer_name, bool prefer_local)
    177 {
    178     if (!value.has_bank) return value_to_string(gb, value.value, prefer_name, prefer_local, false);
    179 
    180     static __thread char output[256];
    181     const GB_bank_symbol_t *symbol = GB_map_find_symbol(get_symbol_map(gb, value.bank), value.value, prefer_local);
    182 
    183     if (symbol && (value.value - symbol->addr > 0x1000 || symbol->addr == 0) ) {
    184         symbol = NULL;
    185     }
    186 
    187     if (!symbol) {
    188         snprintf(output, sizeof(output), "$%02x:$%04x", value.bank, value.value);
    189     }
    190 
    191     else if (symbol->addr == value.value) {
    192         if (prefer_name) {
    193             snprintf(output, sizeof(output), "%s ($%02x:$%04x)", symbol->name, value.bank, value.value);
    194         }
    195         else {
    196             snprintf(output, sizeof(output), "$%02x:$%04x (%s)", value.bank, value.value, symbol->name);
    197         }
    198     }
    199 
    200     else {
    201         if (prefer_name) {
    202             snprintf(output, sizeof(output), "%s+$%03x ($%02x:$%04x)", symbol->name, value.value - symbol->addr, value.bank, value.value);
    203         }
    204         else {
    205             snprintf(output, sizeof(output), "$%02x:$%04x (%s+$%03x)", value.bank, value.value, symbol->name, value.value - symbol->addr);
    206         }
    207     }
    208     return output;
    209 }
    210 
    211 static value_t read_lvalue(GB_gameboy_t *gb, lvalue_t lvalue)
    212 {
    213     /* Not used until we add support for operators like += */
    214     switch (lvalue.kind) {
    215         case LVALUE_MEMORY:
    216             if (lvalue.memory_address.has_bank) {
    217                 banking_state_t state;
    218                 save_banking_state(gb, &state);
    219                 switch_banking_state(gb, lvalue.memory_address.bank);
    220                 value_t r = VALUE_16(GB_read_memory(gb, lvalue.memory_address.value));
    221                 restore_banking_state(gb, &state);
    222                 return r;
    223             }
    224             return VALUE_16(GB_read_memory(gb, lvalue.memory_address.value));
    225 
    226         case LVALUE_MEMORY16:
    227             if (lvalue.memory_address.has_bank) {
    228                 banking_state_t state;
    229                 save_banking_state(gb, &state);
    230                 switch_banking_state(gb, lvalue.memory_address.bank);
    231                 value_t r = VALUE_16(GB_read_memory(gb, lvalue.memory_address.value) |
    232                                    (GB_read_memory(gb, lvalue.memory_address.value + 1) * 0x100));
    233                 restore_banking_state(gb, &state);
    234                 return r;
    235             }
    236             return VALUE_16(GB_read_memory(gb, lvalue.memory_address.value) |
    237                             (GB_read_memory(gb, lvalue.memory_address.value + 1) * 0x100));
    238 
    239         case LVALUE_REG16:
    240             return VALUE_16(*lvalue.register_address);
    241 
    242         case LVALUE_REG_L:
    243             return VALUE_16(*lvalue.register_address & 0x00FF);
    244 
    245         case LVALUE_REG_H:
    246             return VALUE_16(*lvalue.register_address >> 8);
    247     }
    248 
    249     return VALUE_16(0);
    250 }
    251 
    252 static void write_lvalue(GB_gameboy_t *gb, lvalue_t lvalue, uint16_t value)
    253 {
    254     switch (lvalue.kind) {
    255         case LVALUE_MEMORY:
    256             if (lvalue.memory_address.has_bank) {
    257                 banking_state_t state;
    258                 save_banking_state(gb, &state);
    259                 switch_banking_state(gb, lvalue.memory_address.bank);
    260                 GB_write_memory(gb, lvalue.memory_address.value, value);
    261                 restore_banking_state(gb, &state);
    262                 return;
    263             }
    264             GB_write_memory(gb, lvalue.memory_address.value, value);
    265             return;
    266 
    267         case LVALUE_MEMORY16:
    268             if (lvalue.memory_address.has_bank) {
    269                 banking_state_t state;
    270                 save_banking_state(gb, &state);
    271                 switch_banking_state(gb, lvalue.memory_address.bank);
    272                 GB_write_memory(gb, lvalue.memory_address.value, value);
    273                 GB_write_memory(gb, lvalue.memory_address.value + 1, value >> 8);
    274                 restore_banking_state(gb, &state);
    275                 return;
    276             }
    277             GB_write_memory(gb, lvalue.memory_address.value, value);
    278             GB_write_memory(gb, lvalue.memory_address.value + 1, value >> 8);
    279             return;
    280 
    281         case LVALUE_REG16:
    282             *lvalue.register_address = value;
    283             return;
    284 
    285         case LVALUE_REG_L:
    286             *lvalue.register_address &= 0xFF00;
    287             *lvalue.register_address |= value & 0xFF;
    288             return;
    289 
    290         case LVALUE_REG_H:
    291             *lvalue.register_address &= 0x00FF;
    292             *lvalue.register_address |= value << 8;
    293             return;
    294     }
    295 }
    296 
    297 /* 16 bit value   <op> 16 bit value   = 16 bit value
    298    25 bit address <op> 16 bit value   = 25 bit address
    299    16 bit value   <op> 25 bit address = 25 bit address
    300    25 bit address <op> 25 bit address = 16 bit value (since adding pointers, for examples, makes no sense)
    301 
    302    Boolean operators always return a 16-bit value
    303    */
    304 #define FIX_BANK(x) ((value_t){a.has_bank ^ b.has_bank, a.has_bank? a.bank : b.bank, (x)})
    305 
    306 static value_t add(value_t a, value_t b) {return FIX_BANK(a.value + b.value);}
    307 static value_t sub(value_t a, value_t b) {return FIX_BANK(a.value - b.value);}
    308 static value_t mul(value_t a, value_t b) {return FIX_BANK(a.value * b.value);}
    309 static value_t _div(value_t a, value_t b) 
    310 {
    311     if (b.value == 0) {
    312         return FIX_BANK(0);
    313     }
    314     return FIX_BANK(a.value / b.value);
    315 };
    316 static value_t mod(value_t a, value_t b) 
    317 {
    318     if (b.value == 0) {
    319         return FIX_BANK(0);
    320     }
    321     return FIX_BANK(a.value % b.value);
    322 };
    323 static value_t and(value_t a, value_t b) {return FIX_BANK(a.value & b.value);}
    324 static value_t or(value_t a, value_t b) {return FIX_BANK(a.value | b.value);}
    325 static value_t xor(value_t a, value_t b) {return FIX_BANK(a.value ^ b.value);}
    326 static value_t shleft(value_t a, value_t b) {return FIX_BANK(a.value << b.value);}
    327 static value_t shright(value_t a, value_t b) {return FIX_BANK(a.value >> b.value);}
    328 static value_t assign(GB_gameboy_t *gb, lvalue_t a, uint16_t b)
    329 {
    330     write_lvalue(gb, a, b);
    331     return read_lvalue(gb, a);
    332 }
    333 
    334 static value_t bool_and(value_t a, value_t b) {return VALUE_16(a.value && b.value);}
    335 static value_t bool_or(value_t a, value_t b) {return VALUE_16(a.value || b.value);}
    336 static value_t equals(value_t a, value_t b) {return VALUE_16(a.value == b.value);}
    337 static value_t different(value_t a, value_t b) {return VALUE_16(a.value != b.value);}
    338 static value_t lower(value_t a, value_t b) {return VALUE_16(a.value < b.value);}
    339 static value_t greater(value_t a, value_t b) {return VALUE_16(a.value > b.value);}
    340 static value_t lower_equals(value_t a, value_t b) {return VALUE_16(a.value <= b.value);}
    341 static value_t greater_equals(value_t a, value_t b) {return VALUE_16(a.value >= b.value);}
    342 static value_t bank(value_t a, value_t b) {return (value_t) {true, a.value, b.value};}
    343 
    344 
    345 static struct {
    346     const char *string;
    347     int8_t priority;
    348     value_t (*operator)(value_t, value_t);
    349     value_t (*lvalue_operator)(GB_gameboy_t *, lvalue_t, uint16_t);
    350 } operators[] =
    351 {
    352     // Yes. This is not C-like. But it makes much more sense.
    353     // Deal with it.
    354     {"+", 0, add},
    355     {"-", 0, sub},
    356     {"||", 0, bool_or},
    357     {"|", 0, or},
    358     {"*", 1, mul},
    359     {"/", 1, _div},
    360     {"%", 1, mod},
    361     {"&&", 1, bool_and},
    362     {"&", 1, and},
    363     {"^", 1, xor},
    364     {"<<", 2, shleft},
    365     {"<=", -2, lower_equals},
    366     {"<", -2, lower},
    367     {">>", 2, shright},
    368     {">=", -2, greater_equals},
    369     {">", -2, greater},
    370     {"==", -2, equals},
    371     {"=", -1, NULL, assign},
    372     {"!=", -2, different},
    373     {":", 3, bank},
    374 };
    375 
    376 typedef struct {
    377     union {
    378         uint16_t old_address;
    379         uint16_t old_value;
    380     };
    381     uint16_t new_value;
    382     bool old_as_value;
    383 } evaluate_conf_t;
    384 
    385 static value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
    386                                  size_t length, bool *error,
    387                                  const evaluate_conf_t *conf);
    388 
    389 static lvalue_t debugger_evaluate_lvalue(GB_gameboy_t *gb, const char *string,
    390                                          size_t length, bool *error,
    391                                          const evaluate_conf_t *conf)
    392 {
    393     *error = false;
    394     // Strip whitespace
    395     while (length && (string[0] == ' ' || string[0] == '\n' || string[0] == '\r' || string[0] == '\t')) {
    396         string++;
    397         length--;
    398     }
    399     while (length && (string[length-1] == ' ' || string[length-1] == '\n' || string[length-1] == '\r' || string[length-1] == '\t')) {
    400         length--;
    401     }
    402     if (length == 0) { 
    403         GB_log(gb, "Expected expression.\n");
    404         *error = true;
    405         return (lvalue_t){0,};
    406     }
    407     if (string[0] == '(' && string[length - 1] == ')') {
    408         // Attempt to strip parentheses
    409         signed depth = 0;
    410         for (unsigned i = 0; i < length; i++) {
    411             if (string[i] == '(') depth++;
    412             if (depth == 0) {
    413                 // First and last are not matching
    414                 depth = 1;
    415                 break;
    416             }
    417             if (string[i] == ')') depth--;
    418         }
    419         if (depth == 0) return debugger_evaluate_lvalue(gb, string + 1, length - 2, error, conf);
    420     }
    421     else if (string[0] == '[' && string[length - 1] == ']') {
    422         // Attempt to strip square parentheses (memory dereference)
    423         signed depth = 0;
    424         for (unsigned i = 0; i < length; i++) {
    425             if (string[i] == '[') depth++;
    426             if (depth == 0) {
    427                 // First and last are not matching
    428                 depth = 1;
    429                 break;
    430             }
    431             if (string[i] == ']') depth--;
    432         }
    433         if (depth == 0) {
    434             return (lvalue_t){LVALUE_MEMORY, .memory_address = debugger_evaluate(gb, string + 1, length - 2, error, conf)};
    435         }
    436     }
    437     else if (string[0] == '{' && string[length - 1] == '}') {
    438         // Attempt to strip curly parentheses (memory dereference)
    439         signed depth = 0;
    440         for (unsigned i = 0; i < length; i++) {
    441             if (string[i] == '{') depth++;
    442             if (depth == 0) {
    443                 // First and last are not matching
    444                 depth = 1;
    445                 break;
    446             }
    447             if (string[i] == '}') depth--;
    448         }
    449         if (depth == 0) {
    450             return (lvalue_t){LVALUE_MEMORY16, .memory_address = debugger_evaluate(gb, string + 1, length - 2, error, conf)};
    451         }
    452     }
    453 
    454     // Registers
    455     if (string[0] != '$' && (string[0] < '0' || string[0] > '9')) {
    456         if (length == 1) {
    457             switch (string[0]) {
    458                 case 'a': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->af};
    459                 case 'f': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->af};
    460                 case 'b': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->bc};
    461                 case 'c': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->bc};
    462                 case 'd': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->de};
    463                 case 'e': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->de};
    464                 case 'h': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->hl};
    465                 case 'l': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->hl};
    466             }
    467         }
    468         else if (length == 2) {
    469             switch (string[0]) {
    470                 case 'a': if (string[1] == 'f') return (lvalue_t){LVALUE_REG16, .register_address = &gb->af};
    471                 case 'b': if (string[1] == 'c') return (lvalue_t){LVALUE_REG16, .register_address = &gb->bc};
    472                 case 'd': if (string[1] == 'e') return (lvalue_t){LVALUE_REG16, .register_address = &gb->de};
    473                 case 'h': if (string[1] == 'l') return (lvalue_t){LVALUE_REG16, .register_address = &gb->hl};
    474                 case 's': if (string[1] == 'p') return (lvalue_t){LVALUE_REG16, .register_address = &gb->sp};
    475                 case 'p': if (string[1] == 'c') return (lvalue_t){LVALUE_REG16, .register_address = &gb->pc};
    476             }
    477         }
    478         GB_log(gb, "Unknown register: %.*s\n", (unsigned) length, string);
    479         *error = true;
    480         return (lvalue_t){0,};
    481     }
    482 
    483     GB_log(gb, "Expression is not an lvalue: %.*s\n", (unsigned) length, string);
    484     *error = true;
    485     return (lvalue_t){0,};
    486 }
    487 
    488 #define ERROR ((value_t){0,})
    489 static value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
    490                                  size_t length, bool *error,
    491                                  const evaluate_conf_t *conf)
    492 {
    493     /* Disable watchpoints while evaluating expressions */
    494     uint16_t n_watchpoints = gb->n_watchpoints;
    495     gb->n_watchpoints = 0;
    496 
    497     value_t ret = ERROR;
    498 
    499     *error = false;
    500     // Strip whitespace
    501     while (length && (string[0] == ' ' || string[0] == '\n' || string[0] == '\r' || string[0] == '\t')) {
    502         string++;
    503         length--;
    504     }
    505     while (length && (string[length-1] == ' ' || string[length-1] == '\n' || string[length-1] == '\r' || string[length-1] == '\t')) {
    506         length--;
    507     }
    508     if (length == 0) { 
    509         GB_log(gb, "Expected expression.\n");
    510         *error = true;
    511         goto exit;
    512     }
    513     if (string[0] == '(' && string[length - 1] == ')') {
    514         // Attempt to strip parentheses
    515         signed depth = 0;
    516         for (unsigned i = 0; i < length; i++) {
    517             if (string[i] == '(') depth++;
    518             if (depth == 0) {
    519                 // First and last are not matching
    520                 depth = 1;
    521                 break;
    522             }
    523             if (string[i] == ')') depth--;
    524         }
    525         if (depth == 0) {
    526             ret = debugger_evaluate(gb, string + 1, length - 2, error, conf);
    527             goto exit;
    528         }
    529     }
    530     else if (string[0] == '[' && string[length - 1] == ']') {
    531         // Attempt to strip square parentheses (memory dereference)
    532         signed depth = 0;
    533         for (unsigned i = 0; i < length; i++) {
    534             if (string[i] == '[') depth++;
    535             if (depth == 0) {
    536                 // First and last are not matching
    537                 depth = 1;
    538                 break;
    539             }
    540             if (string[i] == ']') depth--;
    541         }
    542 
    543         if (depth == 0) {
    544             value_t addr = debugger_evaluate(gb, string + 1, length - 2, error, conf);
    545             banking_state_t state;
    546             if (addr.bank) {
    547                 save_banking_state(gb, &state);
    548                 switch_banking_state(gb, addr.bank);
    549             }
    550             ret = VALUE_16(GB_read_memory(gb, addr.value));
    551             if (addr.bank) {
    552                 restore_banking_state(gb, &state);
    553             }
    554             goto exit;
    555         }
    556     }
    557     else if (string[0] == '{' && string[length - 1] == '}') {
    558         // Attempt to strip curly parentheses (memory dereference)
    559         signed depth = 0;
    560         for (unsigned i = 0; i < length; i++) {
    561             if (string[i] == '{') depth++;
    562             if (depth == 0) {
    563                 // First and last are not matching
    564                 depth = 1;
    565                 break;
    566             }
    567             if (string[i] == '}') depth--;
    568         }
    569 
    570         if (depth == 0) {
    571             value_t addr = debugger_evaluate(gb, string + 1, length - 2, error, conf);
    572             banking_state_t state;
    573             if (addr.bank) {
    574                 save_banking_state(gb, &state);
    575                 switch_banking_state(gb, addr.bank);
    576             }
    577             ret = VALUE_16(GB_read_memory(gb, addr.value) | (GB_read_memory(gb, addr.value + 1) * 0x100));
    578             if (addr.bank) {
    579                 restore_banking_state(gb, &state);
    580             }
    581             goto exit;
    582         }
    583     }
    584     // Search for lowest priority operator
    585     signed depth = 0;
    586     unsigned operator_index = -1;
    587     unsigned operator_pos = 0;
    588     for (unsigned i = 0; i < length; i++) {
    589         if (string[i] == '(') depth++;
    590         else if (string[i] == ')') depth--;
    591         else if (string[i] == '[') depth++;
    592         else if (string[i] == ']') depth--;
    593         else if (depth == 0) {
    594             for (unsigned j = 0; j < sizeof(operators) / sizeof(operators[0]); j++) {
    595                 unsigned operator_length = strlen(operators[j].string);
    596                 if (operator_length > length - i) continue; // Operator too long
    597                 
    598                 if (memcmp(string + i, operators[j].string, operator_length) == 0) {
    599                     if (operator_index != -1 && operators[operator_index].priority < operators[j].priority) {
    600                         /* for supporting = vs ==, etc*/
    601                         i += operator_length - 1;
    602                         break;
    603                     }
    604                     // Found an operator!
    605                     operator_pos = i;
    606                     operator_index = j;
    607                     /* for supporting = vs ==, etc*/
    608                     i += operator_length - 1;
    609                     break;
    610                 }
    611             }
    612         }
    613     }
    614     if (operator_index != -1) {
    615         unsigned right_start = (unsigned)(operator_pos + strlen(operators[operator_index].string));
    616         value_t right = debugger_evaluate(gb, string + right_start, length - right_start, error, conf);
    617         if (*error) goto exit;
    618         if (operators[operator_index].lvalue_operator) {
    619             lvalue_t left = debugger_evaluate_lvalue(gb, string, operator_pos, error, conf);
    620             if (*error) goto exit;
    621             ret = operators[operator_index].lvalue_operator(gb, left, right.value);
    622             goto exit;
    623         }
    624         value_t left = debugger_evaluate(gb, string, operator_pos, error, conf);
    625         if (*error) goto exit;
    626         ret = operators[operator_index].operator(left, right);
    627         goto exit;
    628     }
    629 
    630     // Not an expression - must be a register or a literal
    631 
    632     // Registers
    633     if (string[0] != '$' && (string[0] < '0' || string[0] > '9')) {
    634         if (length == 1) {
    635             switch (string[0]) {
    636                 case 'a': ret = VALUE_16(gb->af >> 8); goto exit;
    637                 case 'f': ret = VALUE_16(gb->af & 0xFF); goto exit;
    638                 case 'b': ret = VALUE_16(gb->bc >> 8); goto exit;
    639                 case 'c': ret = VALUE_16(gb->bc & 0xFF); goto exit;
    640                 case 'd': ret = VALUE_16(gb->de >> 8); goto exit;
    641                 case 'e': ret = VALUE_16(gb->de & 0xFF); goto exit;
    642                 case 'h': ret = VALUE_16(gb->hl >> 8); goto exit;
    643                 case 'l': ret = VALUE_16(gb->hl & 0xFF); goto exit;
    644             }
    645         }
    646         else if (length == 2) {
    647             switch (string[0]) {
    648                 case 'a': if (string[1] == 'f') {ret = VALUE_16(gb->af); goto exit;}
    649                 case 'b': if (string[1] == 'c') {ret = VALUE_16(gb->bc); goto exit;}
    650                 case 'd': if (string[1] == 'e') {ret = VALUE_16(gb->de); goto exit;}
    651                 case 'h': if (string[1] == 'l') {ret = VALUE_16(gb->hl); goto exit;}
    652                 case 's': if (string[1] == 'p') {ret = VALUE_16(gb->sp); goto exit;}
    653                 case 'p': if (string[1] == 'c') {ret = (value_t){true, bank_for_addr(gb, gb->pc), gb->pc};  goto exit;}
    654             }
    655         }
    656         else if (length == 3 && conf) {
    657             if (memcmp(string, "old", 3) == 0) {
    658                 if (conf->old_as_value) {
    659                     ret = VALUE_16(conf->old_value);
    660                 }
    661                 else {
    662                     ret = VALUE_16(GB_read_memory(gb, conf->old_address));
    663                 }
    664                 goto exit;
    665             }
    666 
    667             if (memcmp(string, "new", 3) == 0) {
    668                 ret = VALUE_16(conf->new_value);
    669                 goto exit;
    670             }
    671 
    672         }
    673 
    674         char symbol_name[length + 1];
    675         memcpy(symbol_name, string, length);
    676         symbol_name[length] = 0;
    677         const GB_symbol_t *symbol = GB_reversed_map_find_symbol(&gb->reversed_symbol_map, symbol_name);
    678         if (symbol) {
    679             ret = (value_t){true, symbol->bank, symbol->addr};
    680             goto exit;
    681         }
    682 
    683         GB_log(gb, "Unknown register or symbol: %.*s\n", (unsigned) length, string);
    684         *error = true;
    685         goto exit;
    686     }
    687 
    688     char *end;
    689     unsigned base = 10;
    690     if (string[0] == '$') {
    691         string++;
    692         base = 16;
    693         length--;
    694     }
    695     uint16_t literal = (uint16_t) (strtol(string, &end, base));
    696     if (end != string + length) {
    697         GB_log(gb, "Failed to parse: %.*s\n", (unsigned) length, string);
    698         *error = true;
    699         goto exit;
    700     }
    701     ret = VALUE_16(literal);
    702 exit:
    703     gb->n_watchpoints = n_watchpoints;
    704     return ret;
    705 }
    706 
    707 static void update_debug_active(GB_gameboy_t *gb)
    708 {
    709     gb->debug_active = !gb->debug_disable && (gb->debug_stopped || gb->debug_fin_command || gb->debug_next_command || gb->breakpoints);
    710 }
    711 
    712 struct debugger_command_s;
    713 typedef bool debugger_command_imp_t(GB_gameboy_t *gb, char *arguments, char *modifiers, const struct debugger_command_s *command);
    714 typedef char *debugger_completer_imp_t(GB_gameboy_t *gb, const char *string, uintptr_t *context);
    715 
    716 typedef struct debugger_command_s {
    717     const char *command;
    718     uint8_t min_length;
    719     debugger_command_imp_t *implementation;
    720     const char *help_string; // Null if should not appear in help
    721     const char *arguments_format; // For usage message
    722     const char *modifiers_format; // For usage message
    723     debugger_completer_imp_t *argument_completer;
    724     debugger_completer_imp_t *modifiers_completer;
    725 } debugger_command_t;
    726 
    727 static const char *lstrip(const char *str)
    728 {
    729     while (*str == ' ' || *str == '\t') {
    730         str++;
    731     }
    732     return str;
    733 }
    734 
    735 #define STOPPED_ONLY \
    736 if (!gb->debug_stopped) { \
    737 GB_log(gb, "Program is running, use 'interrupt' to stop execution.\n"); \
    738 return false; \
    739 }
    740 
    741 #define NO_MODIFIERS \
    742 if (modifiers) { \
    743 print_usage(gb, command); \
    744 return true; \
    745 }
    746 
    747 static void print_usage(GB_gameboy_t *gb, const debugger_command_t *command)
    748 {
    749     GB_log(gb, "Usage: %s", command->command);
    750 
    751     if (command->modifiers_format) {
    752         GB_log(gb, "[/%s]", command->modifiers_format);
    753     }
    754 
    755     if (command->arguments_format) {
    756         GB_log(gb, " %s", command->arguments_format);
    757     }
    758 
    759     GB_log(gb, "\n");
    760 }
    761 
    762 static bool cont(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
    763 {
    764     NO_MODIFIERS
    765     STOPPED_ONLY
    766 
    767     if (strlen(lstrip(arguments))) {
    768         print_usage(gb, command);
    769         return true;
    770     }
    771 
    772     gb->debug_stopped = false;
    773     return false;
    774 }
    775 
    776 static bool interrupt(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
    777 {
    778     NO_MODIFIERS
    779     
    780     if (strlen(lstrip(arguments))) {
    781         print_usage(gb, command);
    782         return true;
    783     }
    784     
    785     if (gb->debug_stopped) {
    786         GB_log(gb, "Program already stopped.\n");
    787         return true;
    788     }
    789     
    790     GB_debugger_break(gb);
    791     return true;
    792 }
    793 
    794 static char *reset_completer(GB_gameboy_t *gb, const char *string, uintptr_t *context)
    795 {
    796     size_t length = strlen(string);
    797     const char *suggestions[] = {"quick", "reload"};
    798     while (*context < sizeof(suggestions) / sizeof(suggestions[0])) {
    799         if (strncmp(string, suggestions[*context], length) == 0) {
    800             return strdup(suggestions[(*context)++] + length);
    801         }
    802         (*context)++;
    803     }
    804     return NULL;
    805 }
    806 
    807 static bool reset(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
    808 {
    809     NO_MODIFIERS
    810     
    811     const char *stripped_argument = lstrip(arguments);
    812     
    813     if (stripped_argument[0] == 0) {
    814         GB_reset(gb);
    815         if (gb->debug_stopped) {
    816             GB_cpu_disassemble(gb, gb->pc, 5);
    817         }
    818         return true;
    819     }
    820     
    821     if (strcmp(stripped_argument, "quick") == 0) {
    822         GB_quick_reset(gb);
    823         if (gb->debug_stopped) {
    824             GB_cpu_disassemble(gb, gb->pc, 5);
    825         }
    826         return true;
    827     }
    828     
    829     if (strcmp(stripped_argument, "reload") == 0) {
    830         if (gb->debugger_reload_callback) {
    831             gb->debugger_reload_callback(gb);
    832             if (gb->undo_state) {
    833                 free(gb->undo_state);
    834                 gb->undo_state = NULL;
    835             }
    836             if (gb->debug_stopped) {
    837                 GB_cpu_disassemble(gb, gb->pc, 5);
    838             }
    839             return true;
    840         }
    841         GB_log(gb, "ROM reloading via the debugger is not supported in this frontend.\n");
    842         return true;
    843     }
    844     
    845     print_usage(gb, command);
    846     return true;
    847 }
    848 
    849 static bool next(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
    850 {
    851     NO_MODIFIERS
    852     STOPPED_ONLY
    853 
    854     if (strlen(lstrip(arguments))) {
    855         print_usage(gb, command);
    856         return true;
    857     }
    858 
    859     gb->debug_stopped = false;
    860     gb->debug_next_command = true;
    861     gb->debug_call_depth = 0;
    862     return false;
    863 }
    864 
    865 static bool step(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
    866 {
    867     NO_MODIFIERS
    868     STOPPED_ONLY
    869 
    870     if (strlen(lstrip(arguments))) {
    871         print_usage(gb, command);
    872         return true;
    873     }
    874 
    875     return false;
    876 }
    877 
    878 static bool finish(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
    879 {
    880     NO_MODIFIERS
    881     STOPPED_ONLY
    882 
    883     if (strlen(lstrip(arguments))) {
    884         print_usage(gb, command);
    885         return true;
    886     }
    887 
    888     gb->debug_stopped = false;
    889     gb->debug_fin_command = true;
    890     gb->debug_call_depth = 0;
    891     return false;
    892 }
    893 
    894 static bool registers(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
    895 {
    896     NO_MODIFIERS
    897     if (strlen(lstrip(arguments))) {
    898         print_usage(gb, command);
    899         return true;
    900     }
    901 
    902 
    903     GB_log(gb, "AF  = $%04x (%c%c%c%c)\n", gb->af, /* AF can't really be an address */
    904            (gb->f & GB_CARRY_FLAG)?      'C' : '-',
    905            (gb->f & GB_HALF_CARRY_FLAG)? 'H' : '-',
    906            (gb->f & GB_SUBTRACT_FLAG)?   'N' : '-',
    907            (gb->f & GB_ZERO_FLAG)?       'Z' : '-');
    908     GB_log(gb, "BC  = %s\n", value_to_string(gb, gb->bc, false, false, false));
    909     GB_log(gb, "DE  = %s\n", value_to_string(gb, gb->de, false, false, false));
    910     GB_log(gb, "HL  = %s\n", value_to_string(gb, gb->hl, false, false, false));
    911     GB_log(gb, "SP  = %s\n", value_to_string(gb, gb->sp, false, false, false));
    912     GB_log(gb, "PC  = %s\n", value_to_string(gb, gb->pc, false, false, false));
    913     GB_log(gb, "IME = %s\n", gb->ime? "Enabled" : "Disabled");
    914     return true;
    915 }
    916 
    917 static char *on_off_completer(GB_gameboy_t *gb, const char *string, uintptr_t *context)
    918 {
    919     size_t length = strlen(string);
    920     const char *suggestions[] = {"on", "off"};
    921     while (*context < sizeof(suggestions) / sizeof(suggestions[0])) {
    922         if (strncmp(string, suggestions[*context], length) == 0) {
    923             return strdup(suggestions[(*context)++] + length);
    924         }
    925         (*context)++;
    926     }
    927     return NULL;
    928 }
    929 
    930 /* Enable or disable software breakpoints */
    931 static bool softbreak(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
    932 {
    933     NO_MODIFIERS
    934     if (strcmp(lstrip(arguments), "on") == 0 || !strlen(lstrip(arguments))) {
    935         gb->has_software_breakpoints = true;
    936     }
    937     else if (strcmp(lstrip(arguments), "off") == 0) {
    938         gb->has_software_breakpoints = false;
    939     }
    940     else {
    941         print_usage(gb, command);
    942     }
    943 
    944     return true;
    945 }
    946 
    947 static inline bool is_legal_symbol_char(char c)
    948 {
    949     if (c >= '0' && c <= '9') return true;
    950     if (c >= 'A' && c <= 'Z') return true;
    951     if (c >= 'a' && c <= 'z') return true;
    952     if (c == '_') return true;
    953     if (c == '.') return true;
    954     return false;
    955 }
    956 
    957 static char *symbol_completer(GB_gameboy_t *gb, const char *string, uintptr_t *_context)
    958 {
    959     const char *symbol_prefix = string;
    960     while (*string) {
    961         if (!is_legal_symbol_char(*string)) {
    962             symbol_prefix = string + 1;
    963         }
    964         string++;
    965     }
    966     
    967     if (*symbol_prefix == '$') {
    968         return NULL;
    969     }
    970     
    971     struct {
    972         uint16_t bank;
    973         uint32_t symbol;
    974     } *context = (void *)_context;
    975     
    976     
    977     size_t length = strlen(symbol_prefix);
    978     while (context->bank < 0x200) {
    979         GB_symbol_map_t *map = get_symbol_map(gb, context->bank);
    980         if (map == NULL ||
    981             context->symbol >= map->n_symbols) {
    982             context->bank++;
    983             context->symbol = 0;
    984             continue;
    985         }
    986         const char *candidate = map->symbols[context->symbol++].name;
    987         if (strncmp(symbol_prefix, candidate, length) == 0) {
    988             return strdup(candidate + length);
    989         }
    990     }
    991     return NULL;
    992 }
    993 
    994 static char *j_completer(GB_gameboy_t *gb, const char *string, uintptr_t *context)
    995 {
    996     size_t length = strlen(string);
    997     const char *suggestions[] = {"j"};
    998     while (*context < sizeof(suggestions) / sizeof(suggestions[0])) {
    999         if (strncmp(string, suggestions[*context], length) == 0) {
   1000             return strdup(suggestions[(*context)++] + length);
   1001         }
   1002         (*context)++;
   1003     }
   1004     return NULL;
   1005 }
   1006 
   1007 static bool check_inclusive(char *to)
   1008 {
   1009     size_t length = strlen(to);
   1010     while (length > strlen("inclusive")) {
   1011         if (to[length - 1] == ' ') {
   1012             to[length - 1] = 0;
   1013             length--;
   1014             continue;
   1015         }
   1016         if (strcmp(to + length - strlen("inclusive"), "inclusive") == 0) {
   1017             to[length - strlen("inclusive")] = 0;
   1018             return true;
   1019         }
   1020         return false;
   1021     }
   1022     return false;
   1023 }
   1024 
   1025 static bool breakpoint(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
   1026 {
   1027     bool is_jump_to = true;
   1028     if (!modifiers) {
   1029         is_jump_to = false;
   1030     }
   1031     else if (strcmp(modifiers, "j") != 0) {
   1032         print_usage(gb, command);
   1033         return true;
   1034     }
   1035 
   1036     if (strlen(lstrip(arguments)) == 0) {
   1037         print_usage(gb, command);
   1038         return true;
   1039     }
   1040 
   1041     if (gb->n_breakpoints == (typeof(gb->n_breakpoints)) -1) {
   1042         GB_log(gb, "Too many breakpoints set\n");
   1043         return true;
   1044     }
   1045 
   1046     char *condition = NULL;
   1047     if ((condition = strstr(arguments, " if "))) {
   1048         *condition = 0;
   1049         condition += strlen(" if ");
   1050         /* Verify condition is sane (Todo: This might have side effects!) */
   1051         bool error;
   1052         debugger_evaluate(gb, condition, (unsigned)strlen(condition), &error, NULL);
   1053         if (error) return true;
   1054 
   1055     }
   1056     
   1057     char *to = NULL;
   1058     bool inclusive = false;
   1059     if ((to = strstr(arguments, " to "))) {
   1060         *to = 0;
   1061         to += strlen(" to ");
   1062         inclusive = check_inclusive(to);
   1063     }
   1064 
   1065     bool error;
   1066     value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL);
   1067     if (error) return true;
   1068 
   1069     uint16_t length = 0;
   1070     value_t end = result;
   1071     if (to) {
   1072         end = debugger_evaluate(gb, to, (unsigned)strlen(to), &error, NULL);
   1073         if (error) return true;
   1074         if (end.has_bank && result.has_bank && end.bank != result.bank) {
   1075             GB_log(gb, "Breakpoint range start and end points have different banks\n");
   1076             return true;
   1077         }
   1078         if (end.value <= result.value) {
   1079             GB_log(gb, "Breakpoint range end point must be grater than the start point\n");
   1080             return true;
   1081         }
   1082         length = end.value - result.value - 1;
   1083     }
   1084 
   1085     uint32_t key = BP_KEY(result);
   1086     
   1087     gb->breakpoints = realloc(gb->breakpoints, (gb->n_breakpoints + 1) * sizeof(gb->breakpoints[0]));
   1088     unsigned id = 1;
   1089     if (gb->n_breakpoints) {
   1090         id = gb->breakpoints[gb->n_breakpoints - 1].id + 1;
   1091     }
   1092     
   1093     gb->breakpoints[gb->n_breakpoints++] = (struct GB_breakpoint_s){
   1094         .id = id,
   1095         .key = key,
   1096         .condition = condition? strdup(condition) : NULL,
   1097         .is_jump_to = is_jump_to,
   1098         .length = length,
   1099         .inclusive = inclusive,
   1100     };
   1101 
   1102     if (is_jump_to) {
   1103         gb->has_jump_to_breakpoints = true;
   1104     }
   1105 
   1106     GB_log(gb, "Breakpoint %u set at %s", id, debugger_value_to_string(gb, result, true, false));
   1107     if (length) {
   1108         GB_log(gb, " - %s%s\n", debugger_value_to_string(gb, end, true, true), inclusive? " (inclusive)" : "");
   1109     }
   1110     else {
   1111         GB_log(gb, "\n");
   1112     }
   1113     return true;
   1114 }
   1115 
   1116 static bool delete(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
   1117 {
   1118     NO_MODIFIERS
   1119     if (strlen(lstrip(arguments)) == 0) {
   1120         for (unsigned i = gb->n_breakpoints; i--;) {
   1121             if (gb->breakpoints[i].condition) {
   1122                 free(gb->breakpoints[i].condition);
   1123             }
   1124         }
   1125         free(gb->breakpoints);
   1126         gb->breakpoints = NULL;
   1127         gb->n_breakpoints = 0;
   1128         gb->has_jump_to_breakpoints = false;
   1129         return true;
   1130     }
   1131 
   1132     char *end;
   1133     unsigned id = strtol(arguments, &end, 10);
   1134     if (*end) {
   1135         print_usage(gb, command);
   1136         return true;
   1137     }
   1138     
   1139     for (unsigned i = 0; i < gb->n_breakpoints; i++) {
   1140         if (gb->breakpoints[i].id != id) continue;
   1141         value_t addr = (value_t){gb->breakpoints[i].bank != (uint16_t)-1, gb->breakpoints[i].bank, gb->breakpoints[i].addr};
   1142         GB_log(gb, "Breakpoint %u removed from %s\n", id, debugger_value_to_string(gb, addr, addr.has_bank, false));
   1143 
   1144         if (gb->breakpoints[i].condition) {
   1145             free(gb->breakpoints[i].condition);
   1146         }
   1147         
   1148         if (gb->breakpoints[i].is_jump_to) {
   1149             gb->has_jump_to_breakpoints = false;
   1150             for (unsigned j = 0; j < gb->n_breakpoints; j++) {
   1151                 if (j == i) continue;
   1152                 if (gb->breakpoints[j].is_jump_to) {
   1153                     gb->has_jump_to_breakpoints = true;
   1154                     break;
   1155                 }
   1156             }
   1157         }
   1158         
   1159         memmove(&gb->breakpoints[i], &gb->breakpoints[i + 1], (gb->n_breakpoints - i - 1) * sizeof(gb->breakpoints[0]));
   1160         gb->n_breakpoints--;
   1161         gb->breakpoints = realloc(gb->breakpoints, gb->n_breakpoints * sizeof(gb->breakpoints[0]));
   1162         
   1163         return true;
   1164     }
   1165 
   1166     GB_log(gb, "Breakpoint %u was not found\n", id);
   1167     return true;
   1168 }
   1169 
   1170 static char *rw_completer(GB_gameboy_t *gb, const char *string, uintptr_t *context)
   1171 {
   1172     size_t length = strlen(string);
   1173     const char *suggestions[] = {"r", "rw", "w"};
   1174     while (*context < sizeof(suggestions) / sizeof(suggestions[0])) {
   1175         if (strncmp(string, suggestions[*context], length) == 0) {
   1176             return strdup(suggestions[(*context)++] + length);
   1177         }
   1178         (*context)++;
   1179     }
   1180     return NULL;
   1181 }
   1182 
   1183 static bool watch(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
   1184 {
   1185     if (strlen(lstrip(arguments)) == 0) {
   1186         print_usage(gb, command);
   1187         return true;
   1188     }
   1189 
   1190     if (gb->n_watchpoints == (typeof(gb->n_watchpoints)) -1) {
   1191         GB_log(gb, "Too many watchpoints set\n");
   1192         return true;
   1193     }
   1194 
   1195     if (!modifiers) {
   1196         modifiers = "w";
   1197     }
   1198 
   1199     uint8_t flags = 0;
   1200     while (*modifiers) {
   1201         switch (*modifiers) {
   1202             case 'r':
   1203                 flags |= WATCHPOINT_READ;
   1204                 break;
   1205             case 'w':
   1206                 flags |= WATCHPOINT_WRITE;
   1207                 break;
   1208             default:
   1209                 print_usage(gb, command);
   1210                 return true;
   1211         }
   1212         modifiers++;
   1213     }
   1214 
   1215     if (!flags) {
   1216         print_usage(gb, command);
   1217         return true;
   1218     }
   1219 
   1220     char *condition = NULL;
   1221     if ((condition = strstr(arguments, " if "))) {
   1222         *condition = 0;
   1223         condition += strlen(" if ");
   1224         /* Verify condition is sane (Todo: This might have side effects!) */
   1225         bool error;
   1226         /* To make new and old legal */
   1227         static const evaluate_conf_t conf = {
   1228             .old_as_value = true,
   1229             .old_value = 0,
   1230             .new_value = 0,
   1231         };
   1232         debugger_evaluate(gb, condition, (unsigned)strlen(condition), &error, &conf);
   1233         if (error) return true;
   1234     }
   1235     
   1236     char *to = NULL;
   1237     bool inclusive = false;
   1238     if ((to = strstr(arguments, " to "))) {
   1239         *to = 0;
   1240         to += strlen(" to ");
   1241         inclusive = check_inclusive(to);
   1242     }
   1243 
   1244     bool error;
   1245     value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL);
   1246     uint32_t key = WP_KEY(result);
   1247     
   1248     uint16_t length = 0;
   1249     value_t end = result;
   1250     if (to) {
   1251         end = debugger_evaluate(gb, to, (unsigned)strlen(to), &error, NULL);
   1252         if (error) return true;
   1253         if (end.has_bank && result.has_bank && end.bank != result.bank) {
   1254             GB_log(gb, "Watchpoint range start and end points have different banks\n");
   1255             return true;
   1256         }
   1257         if (end.value <= result.value) {
   1258             GB_log(gb, "Watchpoint range end point must be grater than the start point\n");
   1259             return true;
   1260         }
   1261         length = end.value - result.value - 1;
   1262     }
   1263 
   1264     if (error) return true;
   1265     
   1266     unsigned id = 1;
   1267     if (gb->n_watchpoints) {
   1268         id = gb->watchpoints[gb->n_watchpoints - 1].id + 1;
   1269     }
   1270     
   1271 
   1272     gb->watchpoints = realloc(gb->watchpoints, (gb->n_watchpoints + 1) * sizeof(gb->watchpoints[0]));
   1273     
   1274     gb->watchpoints[gb->n_watchpoints++] = (struct GB_watchpoint_s){
   1275         .id = id,
   1276         .key = key,
   1277         .condition = condition? strdup(condition) : NULL,
   1278         .flags = flags,
   1279         .length = length,
   1280         .inclusive = inclusive,
   1281     };
   1282 
   1283     const char *flags_string = inline_const(const char *[], {
   1284         [WATCHPOINT_READ] = "read-only",
   1285         [WATCHPOINT_WRITE] = "write-only",
   1286         [WATCHPOINT_READ | WATCHPOINT_WRITE] = "read-write",
   1287     })[flags];
   1288     
   1289     GB_log(gb, "Watchpoint %u set at %s", id, debugger_value_to_string(gb, result, true, false));
   1290     if (length) {
   1291         GB_log(gb, " - %s%s, %s\n", debugger_value_to_string(gb, end, true, true), inclusive? " (inclusive)" : "", flags_string);
   1292     }
   1293     else {
   1294         GB_log(gb, ", %s\n", flags_string);
   1295     }
   1296     return true;
   1297 }
   1298 
   1299 static bool unwatch(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
   1300 {
   1301     NO_MODIFIERS
   1302     if (strlen(lstrip(arguments)) == 0) {
   1303         for (unsigned i = gb->n_watchpoints; i--;) {
   1304             if (gb->watchpoints[i].condition) {
   1305                 free(gb->watchpoints[i].condition);
   1306             }
   1307         }
   1308         free(gb->watchpoints);
   1309         gb->watchpoints = NULL;
   1310         gb->n_watchpoints = 0;
   1311         return true;
   1312     }
   1313     
   1314     char *end;
   1315     unsigned id = strtol(arguments, &end, 10);
   1316     if (*end) {
   1317         print_usage(gb, command);
   1318         return true;
   1319     }
   1320     
   1321     for (unsigned i = 0; i < gb->n_watchpoints; i++) {
   1322         if (gb->watchpoints[i].id != id) continue;
   1323         
   1324         value_t addr = (value_t){gb->watchpoints[i].bank != (uint16_t)-1, gb->watchpoints[i].bank, gb->watchpoints[i].addr};
   1325         GB_log(gb, "Watchpoint %u removed from %s\n", id, debugger_value_to_string(gb, addr, addr.has_bank, false));
   1326         
   1327         if (gb->watchpoints[i].condition) {
   1328             free(gb->watchpoints[i].condition);
   1329         }
   1330         
   1331         memmove(&gb->watchpoints[i], &gb->watchpoints[i + 1], (gb->n_watchpoints - i - 1) * sizeof(gb->watchpoints[0]));
   1332         gb->n_watchpoints--;
   1333         gb->watchpoints = realloc(gb->watchpoints, gb->n_watchpoints * sizeof(gb->watchpoints[0]));
   1334         
   1335         return true;
   1336     }
   1337     
   1338     GB_log(gb, "Watchpoint %u was not found\n", id);
   1339     return true;
   1340 }
   1341 
   1342 static bool list(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
   1343 {
   1344     NO_MODIFIERS
   1345     if (strlen(lstrip(arguments))) {
   1346         print_usage(gb, command);
   1347         return true;
   1348     }
   1349 
   1350     if (gb->n_breakpoints == 0) {
   1351         GB_log(gb, "No breakpoints set.\n");
   1352     }
   1353     else {
   1354         GB_log(gb, "%d breakpoint(s) set:\n", gb->n_breakpoints);
   1355         for (uint16_t i = 0; i < gb->n_breakpoints; i++) {
   1356             value_t addr = (value_t){gb->breakpoints[i].bank != (uint16_t)-1, gb->breakpoints[i].bank, gb->breakpoints[i].addr};
   1357             char *end_string = NULL;
   1358             if (gb->breakpoints[i].length) {
   1359                 value_t end = addr;
   1360                 end.value += gb->breakpoints[i].length + 1;
   1361                 end_string = strdup(debugger_value_to_string(gb, end, addr.has_bank, true));
   1362             }
   1363             if (gb->breakpoints[i].condition) {
   1364                 GB_log(gb, " %d. %s%s%s%s (%sCondition: %s)\n", gb->breakpoints[i].id,
   1365                                                         debugger_value_to_string(gb, addr, addr.has_bank, false),
   1366                                                         end_string? " - " : "",
   1367                                                         end_string ?: "",
   1368                                                         gb->breakpoints[i].inclusive? " (inclusive)" : "",
   1369                                                         gb->breakpoints[i].is_jump_to? "Jump to, ": "",
   1370                                                         gb->breakpoints[i].condition);
   1371             }
   1372             else {
   1373                 GB_log(gb, " %d. %s%s%s%s%s\n", gb->breakpoints[i].id,
   1374                                           debugger_value_to_string(gb, addr, addr.has_bank, false),
   1375                                           end_string? " - " : "",
   1376                                           end_string ?: "",
   1377                                           gb->breakpoints[i].inclusive? " (inclusive)" : "",
   1378                                           gb->breakpoints[i].is_jump_to? " (Jump to)" : "");
   1379             }
   1380             if (end_string) {
   1381                 free(end_string);
   1382             }
   1383         }
   1384     }
   1385 
   1386     if (gb->n_watchpoints == 0) {
   1387         GB_log(gb, "No watchpoints set.\n");
   1388     }
   1389     else {
   1390         GB_log(gb, "%d watchpoint(s) set:\n", gb->n_watchpoints);
   1391         for (uint16_t i = 0; i < gb->n_watchpoints; i++) {
   1392             value_t addr = (value_t){gb->watchpoints[i].bank != (uint16_t)-1, gb->watchpoints[i].bank, gb->watchpoints[i].addr};
   1393             char *end_string = NULL;
   1394             if (gb->watchpoints[i].length) {
   1395                 value_t end = addr;
   1396                 end.value += gb->watchpoints[i].length + 1;
   1397                 end_string = strdup(debugger_value_to_string(gb, end, addr.has_bank, true));
   1398             }
   1399             if (gb->watchpoints[i].condition) {
   1400                 GB_log(gb, " %d. %s%s%s%s (%c%c, Condition: %s)\n", gb->watchpoints[i].id, debugger_value_to_string(gb, addr, addr.has_bank, false),
   1401                                                                   end_string? " - " : "", end_string ?: "",
   1402                                                                   gb->watchpoints[i].inclusive? " (inclusive)" : "",
   1403                                                                   (gb->watchpoints[i].flags & WATCHPOINT_READ)? 'r' : '-',
   1404                                                                   (gb->watchpoints[i].flags & WATCHPOINT_WRITE)? 'w' : '-',
   1405                                                                   gb->watchpoints[i].condition);
   1406             }
   1407             else {
   1408                 GB_log(gb, " %d. %s%s%s%s (%c%c)\n", gb->watchpoints[i].id, debugger_value_to_string(gb, addr, addr.has_bank, false),
   1409                                                    end_string? " - " : "", end_string ?: "",
   1410                                                    gb->watchpoints[i].inclusive? " (inclusive)" : "",
   1411                                                    (gb->watchpoints[i].flags & WATCHPOINT_READ)? 'r' : '-',
   1412                                                    (gb->watchpoints[i].flags & WATCHPOINT_WRITE)? 'w' : '-');
   1413             }
   1414         }
   1415     }
   1416 
   1417     return true;
   1418 }
   1419 
   1420 // Returns the id or 0
   1421 static unsigned should_break(GB_gameboy_t *gb, uint16_t addr, bool jump_to)
   1422 {
   1423     if (unlikely(gb->backstep_instructions)) return false;
   1424     uint16_t bank = bank_for_addr(gb, addr);
   1425     for (unsigned i = 0; i < gb->n_breakpoints; i++) {
   1426         struct GB_breakpoint_s *breakpoint = &gb->breakpoints[i];
   1427         if (breakpoint->bank != (uint16_t)-1) {
   1428             if (breakpoint->bank != bank) continue;
   1429             if (!gb->boot_rom_finished) continue;
   1430         }
   1431         if (breakpoint->is_jump_to != jump_to) continue;
   1432         if (addr < breakpoint->addr) continue;
   1433         if (addr > (uint32_t)breakpoint->addr + breakpoint->length + breakpoint->inclusive) continue;
   1434         if (!breakpoint->condition) return breakpoint->id;
   1435         bool error;
   1436         bool condition = debugger_evaluate(gb, breakpoint->condition,
   1437                                            (unsigned)strlen(breakpoint->condition),
   1438                                            &error, NULL).value;
   1439         if (error) {
   1440             GB_log(gb, "The condition for breakpoint %u is no longer a valid expression\n", breakpoint->id);
   1441             return breakpoint->id;
   1442         }
   1443         if (condition) return breakpoint->id;
   1444     }
   1445     
   1446     return 0;
   1447 }
   1448 
   1449 static char *format_completer(GB_gameboy_t *gb, const char *string, uintptr_t *context)
   1450 {
   1451     size_t length = strlen(string);
   1452     const char *suggestions[] = {"a", "b", "d", "o", "x"};
   1453     while (*context < sizeof(suggestions) / sizeof(suggestions[0])) {
   1454         if (strncmp(string, suggestions[*context], length) == 0) {
   1455             return strdup(suggestions[(*context)++] + length);
   1456         }
   1457         (*context)++;
   1458     }
   1459     return NULL;
   1460 }
   1461 
   1462 static bool print(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
   1463 {
   1464     if (strlen(lstrip(arguments)) == 0) {
   1465         print_usage(gb, command);
   1466         return true;
   1467     }
   1468 
   1469     if (!modifiers) {
   1470         modifiers = "";
   1471     }
   1472     else if (modifiers[0] && modifiers[1]) {
   1473         print_usage(gb, command);
   1474         return true;
   1475     }
   1476 
   1477     bool error;
   1478     value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL);
   1479     if (!error) {
   1480         switch (modifiers[0]) {
   1481             case '\0':
   1482                 if (!result.has_bank) {
   1483                     GB_log(gb, "=%s\n", value_to_string(gb, result.value, false, false, true));
   1484                     break;
   1485                 }
   1486                 // fallthrough
   1487             case 'a':
   1488                 GB_log(gb, "=%s\n", debugger_value_to_string(gb, result, false, false));
   1489                 break;
   1490             case 'd':
   1491                 GB_log(gb, "=%d\n", result.value);
   1492                 break;
   1493             case 'x':
   1494                 GB_log(gb, "=$%x\n", result.value);
   1495                 break;
   1496             case 'o':
   1497                 GB_log(gb, "=0%o\n", result.value);
   1498                 break;
   1499             case 'b':
   1500             {
   1501                 if (!result.value) {
   1502                     GB_log(gb, "=%%0\n");
   1503                     break;
   1504                 }
   1505                 char binary[17];
   1506                 binary[16] = 0;
   1507                 char *ptr = &binary[16];
   1508                 while (result.value) {
   1509                     *(--ptr) = (result.value & 1)? '1' : '0';
   1510                     result.value >>= 1;
   1511                 }
   1512                 GB_log(gb, "=%%%s\n", ptr);
   1513                 break;
   1514             }
   1515             default:
   1516                 print_usage(gb, command);
   1517                 break;
   1518         }
   1519     }
   1520     return true;
   1521 }
   1522 
   1523 static bool examine(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
   1524 {
   1525     if (strlen(lstrip(arguments)) == 0) {
   1526         print_usage(gb, command);
   1527         return true;
   1528     }
   1529 
   1530     bool error;
   1531     value_t addr = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL);
   1532     uint16_t count = 32;
   1533     bool assembler_syntax = false;
   1534     if (modifiers) {
   1535         size_t modifiers_length = strlen(modifiers);
   1536         if (modifiers_length && modifiers[modifiers_length - 1] == 's') {
   1537             assembler_syntax = true;
   1538             modifiers[modifiers_length - 1] = 0;
   1539             modifiers_length--;
   1540         }
   1541         if (modifiers_length) {
   1542             char *end;
   1543             if (modifiers[0] == '$' && modifiers_length > 1) {
   1544                 count = (uint16_t) (strtol(modifiers + 1, &end, 16));
   1545             }
   1546             else {
   1547                 count = (uint16_t) (strtol(modifiers, &end, 10));
   1548             }
   1549             if (*end) {
   1550                 print_usage(gb, command);
   1551                 return true;
   1552             }
   1553         }
   1554     }
   1555 
   1556     if (!error) {
   1557         if (addr.has_bank) {
   1558             banking_state_t old_state;
   1559             save_banking_state(gb, &old_state);
   1560             switch_banking_state(gb, addr.bank);
   1561 
   1562             while (count) {
   1563                 if (assembler_syntax) {
   1564                     GB_log(gb, "db ");
   1565                 }
   1566                 else {
   1567                     GB_log(gb, "%02x:%04x: ", addr.bank, addr.value);
   1568                 }
   1569                 for (unsigned i = 0; i < (assembler_syntax? 8 : 16) && count; i++) {
   1570                     GB_log(gb, "%s%02x%s ", assembler_syntax? "$": "", GB_safe_read_memory(gb, addr.value + i), (assembler_syntax && count && i != 7)? ",": "");
   1571                     count--;
   1572                 }
   1573                 if (assembler_syntax)  {
   1574                     GB_log(gb, "; %02x:%04x", addr.bank, addr.value);
   1575                 }
   1576                 addr.value += assembler_syntax? 8 : 16;
   1577                 GB_log(gb, "\n");
   1578             }
   1579 
   1580             restore_banking_state(gb, &old_state);
   1581         }
   1582         else {
   1583             while (count) {
   1584                 if (assembler_syntax) {
   1585                     GB_log(gb, "db ");
   1586                 }
   1587                 else {
   1588                     GB_log(gb, "%04x: ", addr.value);
   1589                 }
   1590                 for (unsigned i = 0; i < (assembler_syntax? 8 : 16) && count; i++) {
   1591                     GB_log(gb, "%s%02x%s ", assembler_syntax? "$": "", GB_safe_read_memory(gb, addr.value + i), (assembler_syntax && count && i != 7)? ",": "");
   1592                     count--;
   1593                 }
   1594                 if (assembler_syntax) {
   1595                     GB_log(gb, "; %04x", addr.value);
   1596                 }
   1597                 addr.value += assembler_syntax? 8 : 16;
   1598                 GB_log(gb, "\n");
   1599             }
   1600         }
   1601     }
   1602     return true;
   1603 }
   1604 
   1605 static bool disassemble(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
   1606 {
   1607     if (strlen(lstrip(arguments)) == 0) {
   1608         arguments = "pc";
   1609     }
   1610 
   1611     bool error;
   1612     value_t addr = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL);
   1613     uint16_t count = 5;
   1614 
   1615     if (modifiers) {
   1616         char *end;
   1617         count = (uint16_t) (strtol(modifiers, &end, 10));
   1618         if (*end) {
   1619             print_usage(gb, command);
   1620             return true;
   1621         }
   1622     }
   1623 
   1624     if (!error) {
   1625         if (addr.has_bank) {
   1626             banking_state_t old_state;
   1627             save_banking_state(gb, &old_state);
   1628             switch_banking_state(gb, addr.bank);
   1629 
   1630             GB_cpu_disassemble(gb, addr.value, count);
   1631 
   1632             restore_banking_state(gb, &old_state);
   1633         }
   1634         else {
   1635             GB_cpu_disassemble(gb, addr.value, count);
   1636         }
   1637     }
   1638     return true;
   1639 }
   1640 
   1641 static bool mbc(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
   1642 {
   1643     NO_MODIFIERS
   1644 
   1645     if (strlen(lstrip(arguments))) {
   1646         print_usage(gb, command);
   1647         return true;
   1648     }
   1649 
   1650     const GB_cartridge_t *cartridge = gb->cartridge_type;
   1651 
   1652     if (cartridge->has_ram) {
   1653         bool has_battery = gb->cartridge_type->has_battery &&
   1654                            (gb->cartridge_type->mbc_type != GB_TPP1 || (gb->rom[0x153] & 8));
   1655         GB_log(gb, "Cartridge includes%s RAM: $%x bytes\n", has_battery? " battery-backed": "", gb->mbc_ram_size);
   1656     }
   1657     else {
   1658         GB_log(gb, "No cartridge RAM\n");
   1659     }
   1660 
   1661     if (cartridge->mbc_type) {
   1662         if (gb->is_mbc30) {
   1663             GB_log(gb, "MBC30\n");
   1664         }
   1665         else {
   1666             static const char *const mapper_names[] = {
   1667                 [GB_MBC1]   = "MBC1",
   1668                 [GB_MBC2]   = "MBC2",
   1669                 [GB_MBC3]   = "MBC3",
   1670                 [GB_MBC5]   = "MBC5",
   1671                 [GB_MBC7]   = "MBC7",
   1672                 [GB_MMM01]  = "MMM01",
   1673                 [GB_HUC1]   = "HUC-1",
   1674                 [GB_HUC3]   = "HUC-3",
   1675                 [GB_CAMERA] = "MAC-GBD",
   1676 
   1677             };
   1678             GB_log(gb, "%s\n", mapper_names[cartridge->mbc_type]);
   1679         }
   1680         if (cartridge->mbc_type == GB_MMM01 || cartridge->mbc_type == GB_MBC1) {
   1681             GB_log(gb, "Current mapped ROM0 bank: %x\n", gb->mbc_rom0_bank);
   1682         }
   1683         GB_log(gb, "Current mapped ROM bank: %x\n", gb->mbc_rom_bank);
   1684         if (cartridge->has_ram) {
   1685             GB_log(gb, "Current mapped RAM bank: %x\n", gb->mbc_ram_bank);
   1686             if (gb->cartridge_type->mbc_type != GB_HUC1) {
   1687                 GB_log(gb, "RAM is currently %s\n", gb->mbc_ram_enable? "enabled" : "disabled");
   1688             }
   1689         }
   1690         if (cartridge->mbc_type == GB_MBC1 && gb->mbc1_wiring == GB_STANDARD_MBC1_WIRING) {
   1691             GB_log(gb, "MBC1 banking mode is %s\n", gb->mbc1.mode == 1 ? "RAM" : "ROM");
   1692         }
   1693         if (cartridge->mbc_type == GB_MBC1 && gb->mbc1_wiring == GB_MBC1M_WIRING) {
   1694             GB_log(gb, "MBC1 uses MBC1M wiring. \n");
   1695             GB_log(gb, "Current mapped ROM0 bank: %x\n", gb->mbc_rom0_bank);
   1696             GB_log(gb, "MBC1 multicart banking mode is %s\n", gb->mbc1.mode == 1 ? "enabled" : "disabled");
   1697         }
   1698 
   1699     }
   1700     else {
   1701         GB_log(gb, "No MBC\n");
   1702     }
   1703 
   1704     if (gb->cartridge_type->has_rumble &&
   1705        (gb->cartridge_type->mbc_type != GB_TPP1 || (gb->rom[0x153] & 1))) {
   1706         GB_log(gb, "Cart contains a Rumble Pak\n");
   1707     }
   1708 
   1709     if (cartridge->has_rtc) {
   1710         GB_log(gb, "Cart contains a real time clock\n");
   1711     }
   1712 
   1713     return true;
   1714 }
   1715 
   1716 static bool backtrace(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
   1717 {
   1718     NO_MODIFIERS
   1719 
   1720     if (strlen(lstrip(arguments))) {
   1721         print_usage(gb, command);
   1722         return true;
   1723     }
   1724 
   1725     GB_log(gb, "  1. %s\n", debugger_value_to_string(gb, (value_t){true, bank_for_addr(gb, gb->pc), gb->pc}, true, false));
   1726     for (unsigned i = gb->backtrace_size; i--;) {
   1727         GB_log(gb, "%3d. %s\n", gb->backtrace_size - i + 1, debugger_value_to_string(gb, (value_t){true, gb->backtrace_returns[i].bank, gb->backtrace_returns[i].addr}, true, false));
   1728     }
   1729 
   1730     return true;
   1731 }
   1732 
   1733 static char *keep_completer(GB_gameboy_t *gb, const char *string, uintptr_t *context)
   1734 {
   1735     size_t length = strlen(string);
   1736     const char *suggestions[] = {"keep"};
   1737     while (*context < sizeof(suggestions) / sizeof(suggestions[0])) {
   1738         if (strncmp(string, suggestions[*context], length) == 0) {
   1739             return strdup(suggestions[(*context)++] + length);
   1740         }
   1741         (*context)++;
   1742     }
   1743     return NULL;
   1744 }
   1745 
   1746 static bool ticks(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
   1747 {
   1748     NO_MODIFIERS
   1749     STOPPED_ONLY
   1750     bool keep = false;
   1751     if (strcmp(lstrip(arguments), "keep") == 0) {
   1752         keep = true;
   1753     }
   1754     else if (lstrip(arguments)[0]) {
   1755         print_usage(gb, command);
   1756         return true;
   1757     }
   1758 
   1759     GB_log(gb, "T-cycles: %llu\n", (unsigned long long)gb->debugger_ticks);
   1760     GB_log(gb, "M-cycles: %llu\n", (unsigned long long)gb->debugger_ticks / 4);
   1761     GB_log(gb, "Absolute 8MHz ticks: %llu\n", (unsigned long long)gb->absolute_debugger_ticks);
   1762     if (!keep) {
   1763         GB_log(gb, "Tick count reset.\n");
   1764         gb->debugger_ticks = 0;
   1765         gb->absolute_debugger_ticks = 0;
   1766     }
   1767 
   1768     return true;
   1769 }
   1770 
   1771 double GB_debugger_get_frame_cpu_usage(GB_gameboy_t *gb)
   1772 {
   1773     if (gb->last_frame_busy_cycles || gb->last_frame_idle_cycles) {
   1774         return (double)gb->last_frame_busy_cycles / (gb->last_frame_busy_cycles + gb->last_frame_idle_cycles);
   1775     }
   1776     return 0;
   1777 }
   1778 
   1779 double GB_debugger_get_second_cpu_usage(GB_gameboy_t *gb)
   1780 {
   1781     if (gb->last_second_busy_cycles || gb->last_second_idle_cycles) {
   1782         return (double)gb->last_second_busy_cycles / (gb->last_second_busy_cycles + gb->last_second_idle_cycles);
   1783     }
   1784     return 0;
   1785 }
   1786 
   1787 double GB_debugger_get_second_cpu_usage(GB_gameboy_t *gb);
   1788 
   1789 static bool usage(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
   1790 {
   1791     NO_MODIFIERS
   1792     
   1793     if (strlen(lstrip(arguments))) {
   1794         print_usage(gb, command);
   1795         return true;
   1796     }
   1797     
   1798     if (gb->last_frame_busy_cycles || gb->last_frame_idle_cycles) {
   1799         GB_log(gb, "CPU usage (last frame): %.2f%%\n", (double)gb->last_frame_busy_cycles / (gb->last_frame_busy_cycles + gb->last_frame_idle_cycles) * 100);
   1800     }
   1801     else {
   1802         GB_log(gb, "CPU usage (last frame): N/A\n");
   1803     }
   1804     
   1805     if (gb->last_second_busy_cycles || gb->last_second_idle_cycles) {
   1806         GB_log(gb, "CPU usage (last 60 frames): %.2f%%\n", (double)gb->last_second_busy_cycles / (gb->last_second_busy_cycles + gb->last_second_idle_cycles) * 100);
   1807     }
   1808     else {
   1809         GB_log(gb, "CPU usage (last 60 frames): N/A\n");
   1810     }
   1811     
   1812     return true;
   1813 }
   1814 
   1815 static bool palettes(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
   1816 {
   1817     NO_MODIFIERS
   1818     if (strlen(lstrip(arguments))) {
   1819         print_usage(gb, command);
   1820         return true;
   1821     }
   1822 
   1823     if (!GB_is_cgb(gb)) {
   1824         GB_log(gb, "Not available on a DMG.\n");
   1825         return true;
   1826     }
   1827 
   1828     GB_log(gb, "Background palettes: \n");
   1829     for (unsigned i = 0; i < 32; i++) {
   1830         GB_log(gb, "%04x ", ((uint16_t *)&gb->background_palettes_data)[i]);
   1831         if (i % 4 == 3) {
   1832             GB_log(gb, "\n");
   1833         }
   1834     }
   1835 
   1836     GB_log(gb, "Object palettes: \n");
   1837     for (unsigned i = 0; i < 32; i++) {
   1838         GB_log(gb, "%04x ", ((uint16_t *)&gb->object_palettes_data)[i]);
   1839         if (i % 4 == 3) {
   1840             GB_log(gb, "\n");
   1841         }
   1842     }
   1843 
   1844     return true;
   1845 }
   1846 
   1847 static bool dma(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
   1848 {
   1849     NO_MODIFIERS
   1850     if (strlen(lstrip(arguments))) {
   1851         print_usage(gb, command);
   1852         return true;
   1853     }
   1854     
   1855     if (!GB_is_dma_active(gb)) {
   1856         GB_log(gb, "DMA is inactive\n");
   1857         return true;
   1858     }
   1859     
   1860     if (gb->dma_current_dest == 0xFF) {
   1861         GB_log(gb, "DMA warming up\n"); // Shouldn't actually happen, as it only lasts 2 T-cycles
   1862         return true;
   1863     }
   1864     
   1865     GB_log(gb, "Next DMA write: [$FE%02X] = [$%04X]\n", gb->dma_current_dest, gb->dma_current_src);
   1866     
   1867     return true;
   1868 }
   1869 
   1870 static bool lcd(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
   1871 {
   1872     NO_MODIFIERS
   1873     if (strlen(lstrip(arguments))) {
   1874         print_usage(gb, command);
   1875         return true;
   1876     }
   1877     GB_log(gb, "LCDC:\n");
   1878     GB_log(gb, "    LCD enabled: %s\n",(gb->io_registers[GB_IO_LCDC] & GB_LCDC_ENABLE)? "Enabled" : "Disabled");
   1879     GB_log(gb, "    %s: %s\n", (gb->cgb_mode? "Object priority flags" : "Background and Window"),
   1880                                (gb->io_registers[GB_IO_LCDC] & GB_LCDC_BG_EN)? "Enabled" : "Disabled");
   1881     GB_log(gb, "    Objects: %s\n", (gb->io_registers[GB_IO_LCDC] & GB_LCDC_OBJ_EN)? "Enabled" : "Disabled");
   1882     GB_log(gb, "    Object size: %s\n", (gb->io_registers[GB_IO_LCDC] & GB_LCDC_OBJ_SIZE)? "8x16" : "8x8");
   1883     GB_log(gb, "    Background tilemap: %s\n", (gb->io_registers[GB_IO_LCDC] & GB_LCDC_BG_MAP)? "$9C00" : "$9800");
   1884     GB_log(gb, "    Background and Window Tileset: %s\n", (gb->io_registers[GB_IO_LCDC] & GB_LCDC_TILE_SEL)? "$8000" : "$8800");
   1885     GB_log(gb, "    Window: %s\n", (gb->io_registers[GB_IO_LCDC] & GB_LCDC_WIN_ENABLE)? "Enabled" : "Disabled");
   1886     GB_log(gb, "    Window tilemap: %s\n", (gb->io_registers[GB_IO_LCDC] & GB_LCDC_WIN_MAP)? "$9C00" : "$9800");
   1887 
   1888     GB_log(gb, "\nSTAT:\n");
   1889     static const char *modes[] = {"Mode 0, H-Blank", "Mode 1, V-Blank", "Mode 2, OAM", "Mode 3, Rendering"};
   1890     GB_log(gb, "    Current mode: %s\n", modes[gb->io_registers[GB_IO_STAT] & 3]);
   1891     GB_log(gb, "    LYC flag: %s\n", (gb->io_registers[GB_IO_STAT] & 4)? "On" : "Off");
   1892     GB_log(gb, "    H-Blank interrupt: %s\n", (gb->io_registers[GB_IO_STAT] & 8)? "Enabled" : "Disabled");
   1893     GB_log(gb, "    V-Blank interrupt: %s\n", (gb->io_registers[GB_IO_STAT] & 16)? "Enabled" : "Disabled");
   1894     GB_log(gb, "    OAM interrupt: %s\n", (gb->io_registers[GB_IO_STAT] & 32)? "Enabled" : "Disabled");
   1895     GB_log(gb, "    LYC interrupt: %s\n", (gb->io_registers[GB_IO_STAT] & 64)? "Enabled" : "Disabled");
   1896 
   1897 
   1898 
   1899     GB_log(gb, "\nCurrent line: %d\n", gb->current_line);
   1900     GB_log(gb, "Current state: ");
   1901     if (!(gb->io_registers[GB_IO_LCDC] & GB_LCDC_ENABLE)) {
   1902         GB_log(gb, "Off\n");
   1903     }
   1904     else if (gb->display_state == 7 || gb->display_state == 8) {
   1905         GB_log(gb, "Reading OAM data (%d/40)\n", gb->display_state == 8? gb->oam_search_index : 0);
   1906     }
   1907     else if (gb->display_state <= 3 || gb->display_state == 24 || gb->display_state == 31) {
   1908         GB_log(gb, "Glitched line 0 OAM mode (%d cycles to next event)\n", -gb->display_cycles / 2);
   1909     }
   1910     else if (gb->mode_for_interrupt == 3) {
   1911         if (((uint8_t)(gb->position_in_line + 16) < 8)) {
   1912             GB_log(gb, "Adjusting for scrolling (%d/%d)\n", gb->position_in_line & 7, gb->io_registers[GB_IO_SCX] & 7);
   1913         }
   1914         else {
   1915             signed pixel = gb->position_in_line > 160? (int8_t) gb->position_in_line : gb->position_in_line;
   1916             GB_log(gb, "Rendering pixel (%d/160)\n", pixel);
   1917         }
   1918     }
   1919     else {
   1920         GB_log(gb, "Sleeping (%d cycles to next event)\n", -gb->display_cycles / 2);
   1921     }
   1922     GB_log(gb, "LY: %d\n", gb->io_registers[GB_IO_LY]);
   1923     GB_log(gb, "LYC: %d\n", gb->io_registers[GB_IO_LYC]);
   1924     GB_log(gb, "Window position: %d, %d\n", (signed) gb->io_registers[GB_IO_WX] - 7, gb->io_registers[GB_IO_WY]);
   1925     GB_log(gb, "Interrupt line: %s\n", gb->stat_interrupt_line? "On" : "Off");
   1926     GB_log(gb, "Background shifter size: %d\n", gb->bg_fifo.size);
   1927     GB_log(gb, "Background fetcher state: %s\n", inline_const(const char *[], {
   1928         "Tile (1/2)",
   1929         "Tile (2/2)",
   1930         "Low data (1/2)",
   1931         "Low data (2/2)",
   1932         "High data (1/2)",
   1933         "High data (2/2)",
   1934         "Push (1/2)",
   1935         "Push (2/2)",
   1936     })[gb->fetcher_state & 7]);
   1937 
   1938     return true;
   1939 }
   1940 
   1941 static bool apu(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
   1942 {
   1943     NO_MODIFIERS
   1944     const char *stripped = lstrip(arguments);
   1945     if (strlen(stripped)) {
   1946         if (stripped[0] != 0 && (stripped[0] < '1' || stripped[0] > '5')) {
   1947             print_usage(gb, command);
   1948             return true;
   1949         }
   1950     }
   1951 
   1952     if (stripped[0] == 0 || stripped[0] == '5') {
   1953         GB_log(gb, "Current state: ");
   1954         if (!gb->apu.global_enable) {
   1955             GB_log(gb, "Disabled\n");
   1956         }
   1957         else {
   1958             GB_log(gb, "Enabled\n");
   1959             for (uint8_t channel = 0; channel < GB_N_CHANNELS; channel++) {
   1960                 GB_log(gb, "CH%u is %s, DAC %s; current sample = 0x%x\n", channel + 1,
   1961                     gb->apu.is_active[channel] ? "active  " : "inactive",
   1962                     GB_apu_is_DAC_enabled(gb, channel) ? "active  " : "inactive",
   1963                     gb->apu.samples[channel]);
   1964             }
   1965         }
   1966 
   1967         GB_log(gb, "SO1 (left output):  volume %u,", gb->io_registers[GB_IO_NR50] & 0x07);
   1968         if (gb->io_registers[GB_IO_NR51] & 0x0F) {
   1969             for (uint8_t channel = 0, mask = 0x01; channel < GB_N_CHANNELS; channel++, mask <<= 1) {
   1970                 if (gb->io_registers[GB_IO_NR51] & mask) {
   1971                     GB_log(gb, " CH%u", channel + 1);
   1972                 }
   1973             }
   1974         }
   1975         else {
   1976             GB_log(gb, " no channels");
   1977         }
   1978         GB_log(gb, "%s\n", gb->io_registers[GB_IO_NR50] & 0x80 ? " VIN": "");
   1979 
   1980         GB_log(gb, "SO2 (right output): volume %u,", gb->io_registers[GB_IO_NR50] & 0x70 >> 4);
   1981         if (gb->io_registers[GB_IO_NR51] & 0xF0) {
   1982             for (uint8_t channel = 0, mask = 0x10; channel < GB_N_CHANNELS; channel++, mask <<= 1) {
   1983                 if (gb->io_registers[GB_IO_NR51] & mask) {
   1984                     GB_log(gb, " CH%u", channel + 1);
   1985                 }
   1986             }
   1987         }
   1988         else {
   1989             GB_log(gb, " no channels");
   1990         }
   1991         GB_log(gb, "%s\n", gb->io_registers[GB_IO_NR50] & 0x80 ? " VIN": "");
   1992     }
   1993 
   1994 
   1995     for (uint8_t channel = GB_SQUARE_1; channel <= GB_SQUARE_2; channel++) {
   1996         if (stripped[0] != 0 && stripped[0] != ('1') + channel) continue;
   1997         
   1998         GB_log(gb, "\nCH%u:\n", channel + 1);
   1999         GB_log(gb, "    Current volume: %u, current sample length: %u APU ticks (next in %u ticks)\n",
   2000              gb->apu.square_channels[channel].current_volume,
   2001             (gb->apu.square_channels[channel].sample_length ^ 0x7FF) * 2 + 1,
   2002              gb->apu.square_channels[channel].sample_countdown);
   2003 
   2004         uint8_t nrx2 = gb->io_registers[channel == GB_SQUARE_1? GB_IO_NR12 : GB_IO_NR22];
   2005         GB_log(gb, "    %u 256 Hz ticks till next volume %screase (out of %u)\n",
   2006             gb->apu.square_channels[channel].volume_countdown,
   2007             nrx2 & 8 ? "in" : "de",
   2008             nrx2 & 7);
   2009 
   2010         uint8_t duty = gb->io_registers[channel == GB_SQUARE_1? GB_IO_NR11 :GB_IO_NR21] >> 6;
   2011         GB_log(gb, "    Duty cycle %s%% (%s), current index %u/8%s\n",
   2012                duty > 3? "" : inline_const(const char *[], {"12.5", "  25", "  50", "  75"})[duty],
   2013                duty > 3? "" : inline_const(const char *[], {"_______-", "-______-", "-____---", "_------_"})[duty],
   2014                gb->apu.square_channels[channel].current_sample_index,
   2015                gb->apu.square_channels[channel].sample_surpressed ? " (suppressed)" : "");
   2016 
   2017         if (channel == GB_SQUARE_1) {
   2018             GB_log(gb, "    Frequency sweep %s and %s\n",
   2019                    ((gb->io_registers[GB_IO_NR10] & 0x7) && (gb->io_registers[GB_IO_NR10] & 0x70))? "active" : "inactive",
   2020                    (gb->io_registers[GB_IO_NR10] & 0x8) ? "decreasing" : "increasing");
   2021             if (gb->apu.square_sweep_calculate_countdown) {
   2022                 GB_log(gb, "    On-going frequency calculation will be ready in %u APU ticks\n",
   2023                        gb->apu.square_sweep_calculate_countdown * 2 + 1 - gb->apu.lf_div);
   2024             }
   2025             else {
   2026                 GB_log(gb, "    Shadow frequency register: 0x%03x\n", gb->apu.shadow_sweep_sample_length);
   2027                 GB_log(gb, "    Sweep addend register: 0x%03x\n", gb->apu.sweep_length_addend);
   2028             }
   2029         }
   2030 
   2031         if (gb->apu.square_channels[channel].length_enabled) {
   2032             GB_log(gb, "    Channel will end in %u 256 Hz ticks\n",
   2033                 gb->apu.square_channels[channel].pulse_length);
   2034         }
   2035     }
   2036 
   2037     if (stripped[0] == 0 || stripped[0] == '3') {
   2038         GB_log(gb, "\nCH3:\n");
   2039         GB_log(gb, "    Wave:");
   2040         for (uint8_t i = 0; i < 16; i++) {
   2041             GB_log(gb, "%s%X", i % 2? "" : " ", gb->io_registers[GB_IO_WAV_START + i] >> 4);
   2042             GB_log(gb, "%X", gb->io_registers[GB_IO_WAV_START + i] & 0xF);
   2043         }
   2044         GB_log(gb, "\n");
   2045         GB_log(gb, "    Current position: %u\n", gb->apu.wave_channel.current_sample_index);
   2046 
   2047         GB_log(gb, "    Volume %s (right-shifted %u times)\n",
   2048                gb->apu.wave_channel.shift > 4? "" : inline_const(const char *[], {"100%", "50%", "25%", "", "muted"})[gb->apu.wave_channel.shift],
   2049                gb->apu.wave_channel.shift);
   2050 
   2051         GB_log(gb, "    Current sample length: %u APU ticks (next in %u ticks)\n",
   2052             gb->apu.wave_channel.sample_length ^ 0x7FF,
   2053             gb->apu.wave_channel.sample_countdown);
   2054 
   2055         if (gb->apu.wave_channel.length_enabled) {
   2056             GB_log(gb, "    Channel will end in %u 256 Hz ticks\n",
   2057                 gb->apu.wave_channel.pulse_length);
   2058         }
   2059     }
   2060 
   2061 
   2062     if (stripped[0] == 0 || stripped[0] == '4') {
   2063         GB_log(gb, "\nCH4:\n");
   2064         GB_log(gb, "    Current volume: %u, current internal counter: 0x%04x (next increase in %u ticks)\n",
   2065             gb->apu.noise_channel.current_volume,
   2066             gb->apu.noise_channel.counter,
   2067             gb->apu.noise_channel.counter_countdown);
   2068 
   2069         GB_log(gb, "    %u 256 Hz ticks till next volume %screase (out of %u)\n",
   2070             gb->apu.noise_channel.volume_countdown,
   2071             gb->io_registers[GB_IO_NR42] & 8 ? "in" : "de",
   2072             gb->io_registers[GB_IO_NR42] & 7);
   2073 
   2074         GB_log(gb, "    LFSR in %u-step mode, current value ",
   2075             gb->apu.noise_channel.narrow? 7 : 15);
   2076         nounroll for (uint16_t lfsr = gb->apu.noise_channel.lfsr, i = 15; i--; lfsr <<= 1) {
   2077             GB_log(gb, "%u%s", (lfsr >> 14) & 1, i%4 ? "" : " ");
   2078         }
   2079 
   2080         if (gb->apu.noise_channel.length_enabled) {
   2081             GB_log(gb, "    Channel will end in %u 256 Hz ticks\n",
   2082                 gb->apu.noise_channel.pulse_length);
   2083         }
   2084     }
   2085 
   2086 
   2087     GB_log(gb, "\n\nReminder: APU ticks are @ 2 MiHz\n");
   2088 
   2089     return true;
   2090 }
   2091 
   2092 static char *wave_completer(GB_gameboy_t *gb, const char *string, uintptr_t *context)
   2093 {
   2094     size_t length = strlen(string);
   2095     const char *suggestions[] = {"c", "f", "l"};
   2096     while (*context < sizeof(suggestions) / sizeof(suggestions[0])) {
   2097         if (strncmp(string, suggestions[*context], length) == 0) {
   2098             return strdup(suggestions[(*context)++] + length);
   2099         }
   2100         (*context)++;
   2101     }
   2102     return NULL;
   2103 }
   2104 
   2105 static bool wave(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
   2106 {
   2107     if (strlen(lstrip(arguments)) || (modifiers && !strchr("fcl", modifiers[0]))) {
   2108         print_usage(gb, command);
   2109         return true;
   2110     }
   2111 
   2112     uint8_t shift_amount = 1, mask;
   2113     if (modifiers) {
   2114         switch (modifiers[0]) {
   2115             case 'c':
   2116                 shift_amount = 2;
   2117                 break;
   2118             case 'l':
   2119                 shift_amount = 8;
   2120                 break;
   2121         }
   2122     }
   2123     mask = (0xF << (shift_amount - 1)) & 0xF;
   2124 
   2125     for (int8_t cur_val = 0xF & mask; cur_val >= 0; cur_val -= shift_amount) {
   2126         for (uint8_t i = 0; i < 32; i++) {
   2127             uint8_t sample = i & 1?
   2128             (gb->io_registers[GB_IO_WAV_START + i / 2] & 0xF) :
   2129             (gb->io_registers[GB_IO_WAV_START + i / 2] >> 4);
   2130             if ((sample & mask) == cur_val) {
   2131                 GB_log(gb, "%X", sample);
   2132             }
   2133             else {
   2134                 GB_log(gb, "%c", i % 4 == 2 ? '-' : ' ');
   2135             }
   2136         }
   2137         GB_log(gb, "\n");
   2138     }
   2139 
   2140     return true;
   2141 }
   2142 
   2143 static bool undo(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
   2144 {
   2145     NO_MODIFIERS
   2146     STOPPED_ONLY
   2147     
   2148     if (strlen(lstrip(arguments))) {
   2149         print_usage(gb, command);
   2150         return true;
   2151     }
   2152     
   2153     if (!gb->undo_label) {
   2154         GB_log(gb, "No undo state available\n");
   2155         return true;
   2156     }
   2157     uint16_t pc = gb->pc;
   2158     GB_load_state_from_buffer(gb, gb->undo_state, GB_get_save_state_size_no_bess(gb));
   2159     GB_log(gb, "Reverted a \"%s\" command.\n", gb->undo_label);
   2160     if (pc != gb->pc) {
   2161         GB_cpu_disassemble(gb, gb->pc, 5);
   2162     }
   2163     gb->undo_label = NULL;
   2164     
   2165     return true;
   2166 }
   2167 
   2168 #ifndef DISABLE_REWIND
   2169 static bool backstep(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
   2170 {
   2171     NO_MODIFIERS
   2172     STOPPED_ONLY
   2173     
   2174     if (strlen(lstrip(arguments))) {
   2175         print_usage(gb, command);
   2176         return true;
   2177     }
   2178     
   2179     bool didPop = false;
   2180 retry:;
   2181     typeof(gb->rewind_sequences[0]) *sequence = &gb->rewind_sequences[gb->rewind_pos];
   2182     if (!gb->rewind_sequences || !sequence->key_state) {
   2183         if (gb->rewind_buffer_length  == 0) {
   2184             GB_log(gb, "Backstepping requires enabling rewinding\n");
   2185         }
   2186         else {
   2187             GB_log(gb, "Reached the end of the rewind buffer\n");
   2188             if (didPop) {
   2189                 GB_rewind_push(gb);
   2190                 sequence = &gb->rewind_sequences[gb->rewind_pos];
   2191                 sequence->instruction_count[sequence->pos] = 1;
   2192             }
   2193         }
   2194         return true;
   2195     }
   2196     
   2197     gb->backstep_instructions = sequence->instruction_count[sequence->pos] - 2;
   2198     if (gb->backstep_instructions == (uint32_t)-1) { // This frame was just pushed, pop it and try again
   2199         GB_rewind_pop(gb);
   2200         gb->backstep_instructions = 0;
   2201         didPop = true;
   2202         goto retry;
   2203     }
   2204     else if (gb->backstep_instructions > 0x20000) {
   2205         GB_log(gb, "Backstepping is currently not available\n");
   2206         gb->backstep_instructions = 0;
   2207         return true;
   2208     }
   2209     GB_rewind_pop(gb);
   2210     GB_rewind_push(gb);
   2211     sequence = &gb->rewind_sequences[gb->rewind_pos];
   2212     sequence->instruction_count[sequence->pos] = 1;
   2213     while (gb->backstep_instructions) {
   2214         GB_run(gb);
   2215     }
   2216     GB_cpu_disassemble(gb, gb->pc, 5);
   2217     return true;
   2218 }
   2219 #endif
   2220 
   2221 static bool help(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command);
   2222 
   2223 
   2224 /* Commands without implementations are aliases of the previous non-alias commands */
   2225 static const debugger_command_t commands[] = {
   2226     {"continue", 1, cont, "Continue running until next stop"},
   2227     {"interrupt", 1, interrupt, "Interrupt the program execution"},
   2228     {"reset", 3, reset, "Reset the program execution. "
   2229                         "Add 'quick' as an argument to perform a quick reset that does not reset RAM. "
   2230                         "Add 'reload' as an argument to reload the ROM and symbols before resetting.",
   2231                         "[quick|reload]", .argument_completer = reset_completer},
   2232     {"next", 1, next, "Run the next instruction, skipping over function calls"},
   2233     {"step", 1, step, "Run the next instruction, stepping into function calls"},
   2234     {"finish", 1, finish, "Run until the current function returns"},
   2235 #ifndef DISABLE_REWIND
   2236     {"backstep", 5, backstep, "Step one instruction backward, assuming constant inputs"},
   2237     {"bs", 2, }, /* Alias */
   2238 #endif
   2239     {"undo", 1, undo, "Revert the last command"},
   2240     {"registers", 1, registers, "Print values of processor registers and other important registers"},
   2241     {"backtrace", 2, backtrace, "Display the current call stack"},
   2242     {"bt", 2, }, /* Alias */
   2243     {"print", 1, print, "Evaluate and print an expression. "
   2244                         "Use modifier to format as an address (a) or as a number in "
   2245                         "decimal (d), hexadecimal (x), octal (o) or binary (b).",
   2246                         "<expression>", "format", .argument_completer = symbol_completer, .modifiers_completer = format_completer},
   2247     {"eval", 2, }, /* Alias */
   2248     {"examine", 2, examine, "Examine values at address. Use the 's' modifier to output in assembeler syntax.", "<expression>", "[$]count[s]", .argument_completer = symbol_completer},
   2249     {"x", 1, }, /* Alias */
   2250     {"disassemble", 1, disassemble, "Disassemble instructions at address", "<expression>", "count", .argument_completer = symbol_completer},
   2251     {"breakpoint", 1, breakpoint, "Add a new breakpoint at the specified address/expression or range. "
   2252                                   "Ranges are exclusive by default, unless \"inclusive\" is used. "
   2253                                   "If the j modifier is used, the breakpoint will occur just before "
   2254                                   "jumping to the target.",
   2255                                   "<expression> [to <end expression> [inclusive]] [if <condition expression>]", "j",
   2256                                   .argument_completer = symbol_completer, .modifiers_completer = j_completer},
   2257     {"delete", 2, delete, "Delete a breakpoint by its identifier, or all breakpoints", "[<breakpoint id>]"},
   2258     {"watch", 1, watch, "Add a new watchpoint at the specified address/expression or range. "
   2259                         "Ranges are exclusive by default, unless \"inclusive\" is used. "
   2260                         "The default watchpoint type is write-only.",
   2261                         "<expression> [to <end expression> [inclusive]] [if <condition expression>]", "(r|w|rw)",
   2262                         .argument_completer = symbol_completer, .modifiers_completer = rw_completer
   2263     },
   2264     {"unwatch", 3, unwatch, "Delete a watchpoint by its identifier, or all watchpoints", "[<watchpoint id>]"},
   2265     {"softbreak", 2, softbreak, "Enable or disable software breakpoints ('ld b, b' opcodes)", "(on|off)", .argument_completer = on_off_completer},
   2266     {"list", 1, list, "List all set breakpoints and watchpoints"},
   2267     {"ticks", 2, ticks, "Display the number of CPU ticks since the last time 'ticks' was "
   2268                         "used. Use 'keep' as an argument to display ticks without reseeting "
   2269                         "the count.", "(keep)", .argument_completer = keep_completer},
   2270     {"usage", 2, usage, "Display CPU usage"},
   2271     {"cartridge", 2, mbc, "Display information about the MBC and cartridge"},
   2272     {"mbc", 3, }, /* Alias */
   2273     {"apu", 3, apu, "Display information about the current state of the audio processing unit",
   2274                     "[channel (1-4, 5 for NR5x)]"},
   2275     {"wave", 3, wave, "Print a visual representation of the wave RAM. "
   2276                       "Modifiers can be used for a (f)ull print (the default), "
   2277                       "a more (c)ompact one, or a one-(l)iner", "", "(f|c|l)", .modifiers_completer = wave_completer},
   2278     {"lcd", 3, lcd, "Display information about the current state of the LCD controller"},
   2279     {"palettes", 3, palettes, "Display the current CGB palettes"},
   2280     {"dma", 3, dma, "Display the current OAM DMA status"},
   2281 
   2282     {"help", 1, help, "List available commands or show help for the specified command", "[<command>]"},
   2283     {NULL,}, /* Null terminator */
   2284 };
   2285 
   2286 static const debugger_command_t *find_command(const char *string)
   2287 {
   2288     size_t length = strlen(string);
   2289     for (const debugger_command_t *command = commands; command->command; command++) {
   2290         if (command->min_length > length) continue;
   2291         if (strncmp(command->command, string, length) == 0) { /* Is a substring? */
   2292             /* Aliases */
   2293             while (!command->implementation) {
   2294                 command--;
   2295             }
   2296             return command;
   2297         }
   2298     }
   2299 
   2300     return NULL;
   2301 }
   2302 
   2303 static void print_command_shortcut(GB_gameboy_t *gb, const debugger_command_t *command)
   2304 {
   2305     GB_attributed_log(gb, GB_LOG_BOLD | GB_LOG_UNDERLINE, "%.*s", command->min_length, command->command);
   2306     GB_attributed_log(gb, GB_LOG_BOLD, "%s", command->command + command->min_length);
   2307 }
   2308 
   2309 static void print_command_description(GB_gameboy_t *gb, const debugger_command_t *command)
   2310 {
   2311     print_command_shortcut(gb, command);
   2312     GB_log(gb, ": ");
   2313     GB_log(gb, "%s", (const char *)&"           " + strlen(command->command));
   2314     
   2315     const char *string = command->help_string;
   2316     const unsigned width = 80 - 13;
   2317     nounroll while (strlen(string) > width) {
   2318         const char *space = string + width;
   2319         nounroll while (*space != ' ') {
   2320             space--;
   2321             if (space == string) {
   2322                 // This help string has some extra long word? Abort line-breaking, it's going to break anyway.
   2323                 GB_log(gb, "%s\n", string);
   2324                 return;
   2325             }
   2326         }
   2327         GB_log(gb, "%.*s\n             ", (unsigned)(space - string), string);
   2328         string = space + 1;
   2329     }
   2330     GB_log(gb, "%s\n", string);
   2331 }
   2332 
   2333 static bool help(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *ignored)
   2334 {
   2335     const debugger_command_t *command = find_command(arguments);
   2336     if (command) {
   2337         print_command_description(gb, command);
   2338         GB_log(gb, "\n");
   2339         print_usage(gb, command);
   2340 
   2341         command++;
   2342         if (command->command && !command->implementation) { /* Command has aliases*/
   2343             GB_log(gb, "\nAliases: ");
   2344             do {
   2345                 print_command_shortcut(gb, command);
   2346                 GB_log(gb, " ");
   2347                 command++;
   2348             } while (command->command && !command->implementation);
   2349             GB_log(gb, "\n");
   2350         }
   2351         return true;
   2352     }
   2353     nounroll for (command = commands; command->command; command++) {
   2354         if (command->help_string) {
   2355             print_command_description(gb, command);
   2356         }
   2357     }
   2358     return true;
   2359 }
   2360 
   2361 void GB_debugger_call_hook(GB_gameboy_t *gb, uint16_t call_addr)
   2362 {
   2363     /* Called just after the CPU calls a function/enters an interrupt/etc... */
   2364 
   2365     if (gb->backtrace_size < sizeof(gb->backtrace_sps) / sizeof(gb->backtrace_sps[0])) {
   2366         while (gb->backtrace_size) {
   2367             if (gb->backtrace_sps[gb->backtrace_size - 1] <= gb->sp) {
   2368                 gb->backtrace_size--;
   2369                 gb->debug_call_depth--;
   2370             }
   2371             else {
   2372                 break;
   2373             }
   2374         }
   2375 
   2376         gb->backtrace_sps[gb->backtrace_size] = gb->sp;
   2377         gb->backtrace_returns[gb->backtrace_size].bank = bank_for_addr(gb, call_addr);
   2378         gb->backtrace_returns[gb->backtrace_size].addr = call_addr;
   2379         gb->backtrace_size++;
   2380         gb->debug_call_depth++;
   2381     }
   2382 }
   2383 
   2384 void GB_debugger_ret_hook(GB_gameboy_t *gb)
   2385 {
   2386     /* Called just before the CPU runs ret/reti */
   2387 
   2388     while (gb->backtrace_size) {
   2389         if (gb->backtrace_sps[gb->backtrace_size - 1] <= gb->sp) {
   2390             gb->backtrace_size--;
   2391             gb->debug_call_depth--;
   2392         }
   2393         else {
   2394             break;
   2395         }
   2396     }
   2397 }
   2398 
   2399 
   2400 // Returns the id or 0
   2401 static void test_watchpoint(GB_gameboy_t *gb, uint16_t addr, uint8_t flags, uint8_t value)
   2402 {
   2403     if (unlikely(gb->backstep_instructions)) return;
   2404     uint16_t bank = bank_for_addr(gb, addr);
   2405     for (unsigned i = 0; i < gb->n_watchpoints; i++) {
   2406         struct GB_watchpoint_s *watchpoint = &gb->watchpoints[i];
   2407         if (watchpoint->bank != (uint16_t)-1) {
   2408             if (watchpoint->bank != bank) continue;
   2409         }
   2410         if (!(watchpoint->flags & flags)) continue;
   2411         if (addr < watchpoint->addr) continue;
   2412         if (addr > (uint32_t)watchpoint->addr + watchpoint->length + watchpoint->inclusive) continue;
   2413         if (!watchpoint->condition) {
   2414         condition_ok:
   2415             GB_debugger_break(gb);
   2416             if (flags == WATCHPOINT_READ) {
   2417                 GB_log(gb, "Watchpoint %u: [%s]\n", watchpoint->id, value_to_string(gb, addr, true, false, false));
   2418             }
   2419             else {
   2420                 GB_log(gb, "Watchpoint %u: [%s] = $%02x\n", watchpoint->id, value_to_string(gb, addr, true, false, false), value);
   2421             }
   2422             return;
   2423         }
   2424         bool error;
   2425         evaluate_conf_t conf = {
   2426             .old_as_value = flags == WATCHPOINT_READ,
   2427             .new_value = value,
   2428         };
   2429         if (flags == WATCHPOINT_READ) {
   2430             conf.old_value = value;
   2431         }
   2432         else {
   2433             conf.old_address = addr;
   2434         }
   2435         bool condition = debugger_evaluate(gb, watchpoint->condition,
   2436                                            (unsigned)strlen(watchpoint->condition),
   2437                                            &error, &conf).value;
   2438         if (error) {
   2439             GB_log(gb, "The condition for watchpoint %u is no longer a valid expression\n", watchpoint->id);
   2440             GB_debugger_break(gb);
   2441         }
   2442         if (condition) {
   2443             goto condition_ok;
   2444         }
   2445     }
   2446 }
   2447 
   2448 void GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
   2449 {
   2450     if (gb->debug_stopped) return;
   2451     test_watchpoint(gb, addr, WATCHPOINT_WRITE, value);
   2452 }
   2453 
   2454 
   2455 void GB_debugger_test_read_watchpoint(GB_gameboy_t *gb, uint16_t addr)
   2456 {
   2457     if (gb->debug_stopped) return;
   2458     test_watchpoint(gb, addr, WATCHPOINT_READ, 0);
   2459 }
   2460 
   2461 /* Returns true if debugger waits for more commands */
   2462 bool GB_debugger_execute_command(GB_gameboy_t *gb, char *input)
   2463 {
   2464     GB_ASSERT_NOT_RUNNING_OTHER_THREAD(gb)
   2465     
   2466     while (*input == ' ') {
   2467         input++;
   2468     }
   2469     if (!input[0]) {
   2470         return true;
   2471     }
   2472     
   2473     GB_display_sync(gb);
   2474     GB_apu_run(gb, true);
   2475 
   2476     char *command_string = input;
   2477     char *arguments = strchr(input, ' ');
   2478     if (arguments) {
   2479         /* Actually "split" the string. */
   2480         arguments[0] = 0;
   2481         arguments++;
   2482     }
   2483     else {
   2484         arguments = "";
   2485     }
   2486 
   2487     char *modifiers = strchr(command_string, '/');
   2488     if (modifiers) {
   2489         /* Actually "split" the string. */
   2490         modifiers[0] = 0;
   2491         modifiers++;
   2492     }
   2493 
   2494     gb->help_shown = true;
   2495     const debugger_command_t *command = find_command(command_string);
   2496     if (command) {
   2497         uint8_t *old_state = malloc(GB_get_save_state_size_no_bess(gb));
   2498         GB_save_state_to_buffer_no_bess(gb, old_state);
   2499         bool ret = command->implementation(gb, arguments, modifiers, command);
   2500         if (!ret) { // Command continues, save state in any case
   2501             free(gb->undo_state);
   2502             gb->undo_state = old_state;
   2503             gb->undo_label = command->command;
   2504         }
   2505         else {
   2506             uint8_t *new_state = malloc(GB_get_save_state_size_no_bess(gb));
   2507             GB_save_state_to_buffer_no_bess(gb, new_state);
   2508             if (memcmp(new_state, old_state, GB_get_save_state_size_no_bess(gb)) != 0) {
   2509                 // State changed, save the old state as the new undo state
   2510                 free(gb->undo_state);
   2511                 gb->undo_state = old_state;
   2512                 gb->undo_label = command->command;
   2513             }
   2514             else {
   2515                 // Nothing changed, just free the old state
   2516                 free(old_state);
   2517             }
   2518             free(new_state);
   2519         }
   2520         return ret;
   2521     }
   2522     else {
   2523         GB_log(gb, "%s: no such command. Type 'help' to list the available debugger commands.\n", command_string);
   2524         return true;
   2525     }
   2526 }
   2527 
   2528 char *GB_debugger_complete_substring(GB_gameboy_t *gb, char *input, uintptr_t *context)
   2529 {   
   2530     char *command_string = input;
   2531     char *arguments = strchr(input, ' ');
   2532     if (arguments) {
   2533         /* Actually "split" the string. */
   2534         arguments[0] = 0;
   2535         arguments++;
   2536     }
   2537     
   2538     char *modifiers = strchr(command_string, '/');
   2539     if (modifiers) {
   2540         /* Actually "split" the string. */
   2541         modifiers[0] = 0;
   2542         modifiers++;
   2543     }
   2544     
   2545     const debugger_command_t *command = find_command(command_string);
   2546     if (command && command->implementation == help && arguments) {
   2547         command_string = arguments;
   2548         arguments = NULL;
   2549     }
   2550     
   2551     /* No commands and no modifiers, complete the command */
   2552     if (!arguments && !modifiers) {
   2553         size_t length = strlen(command_string);
   2554         if (*context >= sizeof(commands) / sizeof(commands[0])) {
   2555             return NULL;
   2556         }
   2557         for (const debugger_command_t *command = &commands[*context]; command->command; command++) {
   2558             (*context)++;
   2559             if (strncmp(command->command, command_string, length) == 0) { /* Is a substring? */
   2560                 return strdup(command->command + length);
   2561             }
   2562         }
   2563         return NULL;
   2564     }
   2565     
   2566     if (command) {
   2567         if (arguments) {
   2568             if (command->argument_completer) {
   2569                 return command->argument_completer(gb, arguments, context);
   2570             }
   2571             return NULL;
   2572         }
   2573         
   2574         if (modifiers) {
   2575             if (command->modifiers_completer) {
   2576                 return command->modifiers_completer(gb, modifiers, context);
   2577             }
   2578             return NULL;
   2579         }
   2580     }
   2581     return NULL;
   2582 }
   2583 
   2584 typedef enum {
   2585     JUMP_TO_NONE,
   2586     JUMP_TO_BREAK,
   2587     JUMP_TO_NONTRIVIAL,
   2588 } jump_to_return_t;
   2589 
   2590 static jump_to_return_t test_jump_to_breakpoints(GB_gameboy_t *gb, uint16_t *address, unsigned *breakpoint_id);
   2591 
   2592 static void noinline debugger_run(GB_gameboy_t *gb)
   2593 {
   2594     if (!gb->undo_state) {
   2595         gb->undo_state = malloc(GB_get_save_state_size_no_bess(gb));
   2596         GB_save_state_to_buffer_no_bess(gb, gb->undo_state);
   2597     }
   2598 
   2599     char *input = NULL;
   2600     if (gb->debug_next_command && gb->debug_call_depth <= 0 && !gb->halted) {
   2601         GB_debugger_break(gb);
   2602     }
   2603     if (gb->debug_fin_command && gb->debug_call_depth <= -1) {
   2604         GB_debugger_break(gb);
   2605     }
   2606     if (gb->debug_stopped) {
   2607         if (!gb->help_shown) {
   2608             gb->help_shown = true;
   2609             GB_log(gb, "Type 'help' to list the available debugger commands.\n");
   2610         }
   2611         GB_cpu_disassemble(gb, gb->pc, 5);
   2612     }
   2613 next_command:
   2614     if (input) {
   2615         free(input);
   2616     }
   2617     unsigned breakpoint_id = 0;
   2618     if (gb->breakpoints && !gb->debug_stopped && (breakpoint_id = should_break(gb, gb->pc, false))) {
   2619         GB_debugger_break(gb);
   2620         GB_log(gb, "Breakpoint %u: PC = %s\n", breakpoint_id, value_to_string(gb, gb->pc, true, false, false));
   2621         GB_cpu_disassemble(gb, gb->pc, 5);
   2622     }
   2623 
   2624     if (gb->breakpoints && !gb->debug_stopped) {
   2625         uint16_t address = 0;
   2626         jump_to_return_t jump_to_result = test_jump_to_breakpoints(gb, &address, &breakpoint_id);
   2627 
   2628         bool should_delete_state = true;
   2629         if (jump_to_result == JUMP_TO_BREAK) {
   2630             GB_debugger_break(gb);
   2631             GB_log(gb, "Jumping to breakpoint %u: %s\n", breakpoint_id, value_to_string(gb, address, true, false, false));
   2632             GB_cpu_disassemble(gb, gb->pc, 5);
   2633             gb->non_trivial_jump_breakpoint_occured = false;
   2634         }
   2635         else if (gb->nontrivial_jump_state && (breakpoint_id = should_break(gb, gb->pc, true))) {
   2636             if (gb->non_trivial_jump_breakpoint_occured) {
   2637                 gb->non_trivial_jump_breakpoint_occured = false;
   2638             }
   2639             else {
   2640                 gb->non_trivial_jump_breakpoint_occured = true;
   2641                 GB_log(gb, "Jumping to breakpoint %u: %s\n", breakpoint_id, value_to_string(gb, gb->pc, true, false, false));
   2642                 GB_load_state_from_buffer(gb, gb->nontrivial_jump_state, -1);
   2643                 GB_rewind_push(gb);
   2644                 GB_cpu_disassemble(gb, gb->pc, 5);
   2645                 GB_debugger_break(gb);
   2646             }
   2647         }
   2648         else if (jump_to_result == JUMP_TO_NONTRIVIAL) {
   2649             if (!gb->nontrivial_jump_state) {
   2650                 gb->nontrivial_jump_state = malloc(GB_get_save_state_size_no_bess(gb));
   2651             }
   2652             GB_save_state_to_buffer_no_bess(gb, gb->nontrivial_jump_state);
   2653             gb->non_trivial_jump_breakpoint_occured = false;
   2654             should_delete_state = false;
   2655         }
   2656         else {
   2657             gb->non_trivial_jump_breakpoint_occured = false;
   2658         }
   2659 
   2660         if (should_delete_state) {
   2661             if (gb->nontrivial_jump_state) {
   2662                 free(gb->nontrivial_jump_state);
   2663                 gb->nontrivial_jump_state = NULL;
   2664             }
   2665         }
   2666     }
   2667 
   2668     if (gb->debug_stopped && !gb->debug_disable) {
   2669         gb->debug_next_command = false;
   2670         gb->debug_fin_command = false;
   2671         input = gb->input_callback(gb);
   2672 
   2673         if (input == NULL) {
   2674             /* Debugging is no currently available, continue running */
   2675             gb->debug_stopped = false;
   2676             update_debug_active(gb);
   2677             return;
   2678         }
   2679 
   2680         if (GB_debugger_execute_command(gb, input)) {
   2681             goto next_command;
   2682         }
   2683 
   2684         free(input);
   2685     }
   2686     update_debug_active(gb);
   2687 }
   2688 void GB_debugger_run(GB_gameboy_t *gb)
   2689 {
   2690 #ifndef DISABLE_REWIND
   2691     if (gb->rewind_sequences && gb->rewind_sequences[gb->rewind_pos].key_state) {
   2692         typeof(gb->rewind_sequences[0]) *sequence = &gb->rewind_sequences[gb->rewind_pos];
   2693         sequence->instruction_count[sequence->pos]++;
   2694     }
   2695     if (unlikely(gb->backstep_instructions)) {
   2696         gb->backstep_instructions--;
   2697         return;
   2698     }
   2699 #endif
   2700     if (likely(!gb->debug_active)) return;
   2701     debugger_run(gb);
   2702 }
   2703 
   2704 void GB_debugger_handle_async_commands(GB_gameboy_t *gb)
   2705 {
   2706     char *input = NULL;
   2707 
   2708     while (gb->async_input_callback && (input = gb->async_input_callback(gb))) {
   2709         GB_debugger_execute_command(gb, input);
   2710         update_debug_active(gb);
   2711         free(input);
   2712     }
   2713 }
   2714 
   2715 void GB_debugger_add_symbol(GB_gameboy_t *gb, uint16_t bank, uint16_t address, const char *symbol)
   2716 {
   2717     if (bank >= gb->n_symbol_maps) {
   2718         gb->bank_symbols = realloc(gb->bank_symbols, (bank + 1) * sizeof(*gb->bank_symbols));
   2719         while (bank >= gb->n_symbol_maps) {
   2720             gb->bank_symbols[gb->n_symbol_maps++] = NULL;
   2721         }
   2722     }
   2723 
   2724     if (!gb->bank_symbols[bank]) {
   2725         gb->bank_symbols[bank] = GB_map_alloc();
   2726     }
   2727     GB_bank_symbol_t *allocated_symbol = GB_map_add_symbol(gb->bank_symbols[bank], address, symbol);
   2728     if (allocated_symbol) {
   2729         GB_reversed_map_add_symbol(&gb->reversed_symbol_map, bank, allocated_symbol);
   2730     }
   2731 }
   2732 
   2733 void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path)
   2734 {
   2735     FILE *f = fopen(path, "r");
   2736     if (!f) return;
   2737 
   2738     char *line = NULL;
   2739     size_t size = 0;
   2740     size_t length = 0;
   2741     while ((length = getline(&line, &size, f)) != -1) {
   2742         for (unsigned i = 0; i < length; i++) {
   2743             if (line[i] == ';' || line[i] == '\n' || line[i] == '\r') {
   2744                 line[i] = 0;
   2745                 length = i;
   2746                 break;
   2747             }
   2748         }
   2749         if (length == 0) continue;
   2750 
   2751         unsigned bank, address;
   2752         char symbol[length];
   2753 
   2754         if (sscanf(line, "%x:%x %s", &bank, &address, symbol) == 3) {
   2755             GB_debugger_add_symbol(gb, bank, address, symbol);
   2756         }
   2757     }
   2758     free(line);
   2759     fclose(f);
   2760 }
   2761 
   2762 void GB_debugger_clear_symbols(GB_gameboy_t *gb)
   2763 {
   2764     for (unsigned i = gb->n_symbol_maps; i--;) {
   2765         if (gb->bank_symbols[i]) {
   2766             GB_map_free(gb->bank_symbols[i]);
   2767             gb->bank_symbols[i] = 0;
   2768         }
   2769     }
   2770     for (unsigned i = sizeof(gb->reversed_symbol_map.buckets) / sizeof(gb->reversed_symbol_map.buckets[0]); i--;) {
   2771         while (gb->reversed_symbol_map.buckets[i]) {
   2772             GB_symbol_t *next = gb->reversed_symbol_map.buckets[i]->next;
   2773             free(gb->reversed_symbol_map.buckets[i]);
   2774             gb->reversed_symbol_map.buckets[i] = next;
   2775         }
   2776     }
   2777     gb->n_symbol_maps = 0;
   2778     if (gb->bank_symbols) {
   2779         free(gb->bank_symbols);
   2780         gb->bank_symbols = NULL;
   2781     }
   2782 }
   2783 
   2784 const GB_bank_symbol_t *GB_debugger_find_symbol(GB_gameboy_t *gb, uint16_t addr, bool prefer_local)
   2785 {
   2786     uint16_t bank = bank_for_addr(gb, addr);
   2787 
   2788     const GB_bank_symbol_t *symbol = GB_map_find_symbol(get_symbol_map(gb, bank), addr, prefer_local);
   2789     if (symbol) return symbol;
   2790     if (bank != 0) return GB_map_find_symbol(get_symbol_map(gb, 0), addr, prefer_local); /* Maybe the symbol incorrectly uses bank 0? */
   2791 
   2792     return NULL;
   2793 }
   2794 
   2795 const char *GB_debugger_name_for_address(GB_gameboy_t *gb, uint16_t addr)
   2796 {
   2797     return GB_debugger_describe_address(gb, addr, -1, true, false);
   2798 }
   2799 
   2800 const char *GB_debugger_describe_address(GB_gameboy_t *gb,
   2801                                          uint16_t addr, uint16_t bank,
   2802                                          bool exact_match, bool prefer_local)
   2803 {
   2804     if (bank == (uint16_t)-1) {
   2805         bank = bank_for_addr(gb, addr);
   2806     }
   2807     if ((addr >> 12) == 0xC) {
   2808         bank = 0;
   2809     }
   2810     if (exact_match) {
   2811         const GB_bank_symbol_t *symbol = GB_map_find_symbol(get_symbol_map(gb, bank), addr, prefer_local);
   2812         if (symbol && symbol->addr == addr) return symbol->name;
   2813         if (bank != 0) symbol = GB_map_find_symbol(get_symbol_map(gb, 0), addr, prefer_local); /* Maybe the symbol incorrectly uses bank 0? */
   2814         if (symbol && symbol->addr == addr) return symbol->name;
   2815         
   2816         return NULL;
   2817     }
   2818     
   2819     return debugger_value_to_string(gb, (value_t){
   2820         .value = addr,
   2821         .bank = bank,
   2822         .has_bank = true,
   2823     }, true, prefer_local);
   2824 }
   2825 
   2826 /* The public version of debugger_evaluate */
   2827 bool GB_debugger_evaluate(GB_gameboy_t *gb, const char *string, uint16_t *result, uint16_t *result_bank)
   2828 {
   2829     GB_ASSERT_NOT_RUNNING_OTHER_THREAD(gb)
   2830     
   2831     bool error = false;
   2832     value_t value = debugger_evaluate(gb, string, strlen(string), &error, NULL);
   2833     if (result) {
   2834         *result = value.value;
   2835     }
   2836     if (result_bank) {
   2837         *result_bank = value.has_bank? value.bank : -1;
   2838     }
   2839     return error;
   2840 }
   2841 
   2842 #ifndef GB_DISABLE_CHEAT_SEARCH
   2843 internal bool GB_debugger_evaluate_cheat_filter(GB_gameboy_t *gb, const char *string, bool *result, uint16_t old, uint16_t new)
   2844 {
   2845     GB_ASSERT_NOT_RUNNING_OTHER_THREAD(gb)
   2846     
   2847     bool error = false;
   2848     evaluate_conf_t conf = {
   2849         .old_as_value = true,
   2850         .old_value = old,
   2851         .new_value = new,
   2852     };
   2853     value_t value = debugger_evaluate(gb, string, strlen(string), &error, &conf);
   2854     if (result) {
   2855         *result = value.value;
   2856     }
   2857 
   2858     return error;
   2859 }
   2860 #endif
   2861 
   2862 void GB_debugger_break(GB_gameboy_t *gb)
   2863 {
   2864     gb->debug_stopped = true;
   2865     update_debug_active(gb);
   2866 }
   2867 
   2868 bool GB_debugger_is_stopped(GB_gameboy_t *gb)
   2869 {
   2870     return gb->debug_stopped;
   2871 }
   2872 
   2873 void GB_debugger_set_disabled(GB_gameboy_t *gb, bool disabled)
   2874 {
   2875     gb->debug_disable = disabled;
   2876     update_debug_active(gb);
   2877 }
   2878 
   2879 void GB_debugger_set_reload_callback(GB_gameboy_t *gb, GB_debugger_reload_callback_t callback)
   2880 {
   2881     gb->debugger_reload_callback = callback;
   2882 }
   2883 
   2884 /* Jump-to breakpoints */
   2885 
   2886 static bool is_in_trivial_memory(uint16_t addr)
   2887 {
   2888     /* ROM */
   2889     if (addr < 0x8000) {
   2890         return true;
   2891     }
   2892 
   2893     /* HRAM */
   2894     if (addr >= 0xFF80 && addr < 0xFFFF) {
   2895         return true;
   2896     }
   2897 
   2898     /* RAM */
   2899     if (addr >= 0xC000 && addr < 0xE000) {
   2900         return true;
   2901     }
   2902 
   2903     return false;
   2904 }
   2905 
   2906 typedef uint16_t opcode_address_getter_t(GB_gameboy_t *gb, uint8_t opcode);
   2907 
   2908 static uint16_t trivial_1(GB_gameboy_t *gb, uint8_t opcode)
   2909 {
   2910     return gb->pc + 1;
   2911 }
   2912 
   2913 static uint16_t trivial_2(GB_gameboy_t *gb, uint8_t opcode)
   2914 {
   2915     return gb->pc + 2;
   2916 }
   2917 
   2918 static uint16_t trivial_3(GB_gameboy_t *gb, uint8_t opcode)
   2919 {
   2920     return gb->pc + 3;
   2921 }
   2922 
   2923 static uint16_t jr_r8(GB_gameboy_t *gb, uint8_t opcode)
   2924 {
   2925     return gb->pc + 2 + (int8_t)GB_read_memory(gb, gb->pc + 1);
   2926 }
   2927 
   2928 static bool condition_code(GB_gameboy_t *gb, uint8_t opcode)
   2929 {
   2930     switch ((opcode >> 3) & 0x3) {
   2931         case 0:
   2932             return !(gb->af & GB_ZERO_FLAG);
   2933         case 1:
   2934             return (gb->af & GB_ZERO_FLAG);
   2935         case 2:
   2936             return !(gb->af & GB_CARRY_FLAG);
   2937         case 3:
   2938             return (gb->af & GB_CARRY_FLAG);
   2939     }
   2940 
   2941     return false;
   2942 }
   2943 
   2944 static uint16_t jr_cc_r8(GB_gameboy_t *gb, uint8_t opcode)
   2945 {
   2946     if (!condition_code(gb, opcode)) {
   2947         return gb->pc + 2;
   2948     }
   2949 
   2950     return gb->pc + 2 + (int8_t)GB_read_memory(gb, gb->pc + 1);
   2951 }
   2952 
   2953 static uint16_t ret(GB_gameboy_t *gb, uint8_t opcode)
   2954 {
   2955     return GB_read_memory(gb, gb->sp) |
   2956            (GB_read_memory(gb, gb->sp + 1) << 8);
   2957 }
   2958 
   2959 
   2960 static uint16_t ret_cc(GB_gameboy_t *gb, uint8_t opcode)
   2961 {
   2962     if (condition_code(gb, opcode)) {
   2963         return ret(gb, opcode);
   2964     }
   2965     else {
   2966         return gb->pc + 1;
   2967     }
   2968 }
   2969 
   2970 static uint16_t jp_a16(GB_gameboy_t *gb, uint8_t opcode)
   2971 {
   2972     return GB_read_memory(gb, gb->pc + 1) |
   2973            (GB_read_memory(gb, gb->pc + 2) << 8);
   2974 }
   2975 
   2976 static uint16_t jp_cc_a16(GB_gameboy_t *gb, uint8_t opcode)
   2977 {
   2978     if (condition_code(gb, opcode)) {
   2979         return jp_a16(gb, opcode);
   2980     }
   2981     else {
   2982         return gb->pc + 3;
   2983     }
   2984 }
   2985 
   2986 static uint16_t rst(GB_gameboy_t *gb, uint8_t opcode)
   2987 {
   2988     return opcode ^ 0xC7;
   2989 }
   2990 
   2991 static uint16_t jp_hl(GB_gameboy_t *gb, uint8_t opcode)
   2992 {
   2993     return gb->hl;
   2994 }
   2995 
   2996 static opcode_address_getter_t *opcodes[256] = {
   2997     /*  X0          X1          X2          X3          X4          X5          X6          X7                */
   2998     /*  X8          X9          Xa          Xb          Xc          Xd          Xe          Xf                */
   2999     trivial_1,  trivial_3,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_2,  trivial_1,   /* 0X */
   3000     trivial_3,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_2,  trivial_1,
   3001     trivial_2,  trivial_3,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_2,  trivial_1,  /* 1X */
   3002     jr_r8,      trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_2,  trivial_1,
   3003     jr_cc_r8,   trivial_3,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_2,  trivial_1,  /* 2X */
   3004     jr_cc_r8,   trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_2,  trivial_1,
   3005     jr_cc_r8,   trivial_3,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_2,  trivial_1,  /* 3X */
   3006     jr_cc_r8,   trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_2,  trivial_1,
   3007     trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  /* 4X */
   3008     trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,
   3009     trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  /* 5X */
   3010     trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,
   3011     trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  /* 6X */
   3012     trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,
   3013     trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  NULL,       trivial_1,  /* 7X */
   3014     trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,
   3015     trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  /* 8X */
   3016     trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,
   3017     trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  /* 9X */
   3018     trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,
   3019     trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  /* aX */
   3020     trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,
   3021     trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  /* bX */
   3022     trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,  trivial_1,
   3023     ret_cc,     trivial_1,  jp_cc_a16,  jp_a16,     jp_cc_a16,  trivial_1,  trivial_2,  rst,        /* cX */
   3024     ret_cc,     ret,        jp_cc_a16,  trivial_2,  jp_cc_a16,  jp_a16,     trivial_2,  rst,
   3025     ret_cc,     trivial_1,  jp_cc_a16,  NULL,       jp_cc_a16,  trivial_1,  trivial_2,  rst,        /* dX */
   3026     ret_cc,     ret,        jp_cc_a16,  NULL,       jp_cc_a16,  NULL,       trivial_2,  rst,
   3027     trivial_2,  trivial_1,  trivial_1,  NULL,       NULL,       trivial_1,  trivial_2,  rst,        /* eX */
   3028     trivial_2,  jp_hl,      trivial_3,  NULL,       NULL,       NULL,       trivial_2,  rst,
   3029     trivial_2,  trivial_1,  trivial_1,  trivial_1,  NULL,       trivial_1,  trivial_2,  rst,        /* fX */
   3030     trivial_2,  trivial_1,  trivial_3,  trivial_1,  NULL,       NULL,       trivial_2,  rst,
   3031 };
   3032 
   3033 static jump_to_return_t test_jump_to_breakpoints(GB_gameboy_t *gb, uint16_t *address, unsigned *breakpoint_id)
   3034 {
   3035     if (!gb->has_jump_to_breakpoints) return JUMP_TO_NONE;
   3036 
   3037     if (!is_in_trivial_memory(gb->pc) || !is_in_trivial_memory(gb->pc + 2) ||
   3038         !is_in_trivial_memory(gb->sp) || !is_in_trivial_memory(gb->sp - 1)) {
   3039         return JUMP_TO_NONTRIVIAL;
   3040     }
   3041 
   3042     /* Interrupts */
   3043     if (gb->ime) {
   3044         for (unsigned i = 0; i < 5; i++) {
   3045             if ((gb->interrupt_enable & (1 << i)) && (gb->io_registers[GB_IO_IF] & (1 << i))) {
   3046                 if ((*breakpoint_id = should_break(gb, 0x40 + i * 8, true))) {
   3047                     if (address) {
   3048                         *address = 0x40 + i * 8;
   3049                     }
   3050                     return JUMP_TO_BREAK;
   3051                 }
   3052             }
   3053         }
   3054     }
   3055 
   3056     uint16_t n_watchpoints = gb->n_watchpoints;
   3057     gb->n_watchpoints = 0;
   3058 
   3059     uint8_t opcode = GB_read_memory(gb, gb->pc);
   3060 
   3061     if (opcode == 0x76) {
   3062         gb->n_watchpoints = n_watchpoints;
   3063         if (gb->ime) { /* Already handled in above */
   3064             return JUMP_TO_NONE;
   3065         }
   3066 
   3067         if (gb->interrupt_enable & gb->io_registers[GB_IO_IF] & 0x1F) {
   3068             return JUMP_TO_NONTRIVIAL; /* HALT bug could occur */
   3069         }
   3070 
   3071         return JUMP_TO_NONE;
   3072     }
   3073 
   3074     opcode_address_getter_t *getter = opcodes[opcode];
   3075     if (!getter) {
   3076         gb->n_watchpoints = n_watchpoints;
   3077         return JUMP_TO_NONE;
   3078     }
   3079 
   3080     uint16_t new_pc = getter(gb, opcode);
   3081 
   3082     gb->n_watchpoints = n_watchpoints;
   3083 
   3084     if (address) {
   3085         *address = new_pc;
   3086     }
   3087 
   3088     return (*breakpoint_id = should_break(gb, new_pc, true)) ? JUMP_TO_BREAK : JUMP_TO_NONE;
   3089 }

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.