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.