Mercurial > repos > blastem
diff z80_to_x86.c @ 2400:c97609fe8315
Implement watchpoints in Z80 debugger
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 23 Dec 2023 23:03:31 -0800 |
parents | 3e591869d135 |
children | ed7b4d869989 |
line wrap: on
line diff
--- a/z80_to_x86.c Sat Dec 23 22:11:43 2023 -0800 +++ b/z80_to_x86.c Sat Dec 23 23:03:31 2023 -0800 @@ -3201,6 +3201,7 @@ options->gen.max_address = 0x10000; options->gen.bus_cycles = 3; options->gen.clock_divider = clock_divider; + options->gen.watchpoint_range_off = offsetof(z80_context, watchpoint_min); options->gen.mem_ptr_off = offsetof(z80_context, mem_pointers); options->gen.ram_flags_off = offsetof(z80_context, ram_code_flags); options->gen.ram_flags_shift = 7; @@ -3866,7 +3867,6 @@ } } } - void zremove_breakpoint(z80_context * context, uint16_t address) { context->breakpoint_flags[address / 8] &= ~(1 << (address % 8)); @@ -3881,6 +3881,101 @@ } } +static void *z80_watchpoint_check(uint32_t address, void *vcontext, uint8_t value) +{ + z80_context *context = vcontext; + z80_watchpoint *watch = NULL; + address &= 0xFFFF; + for (uint32_t i = 0; i < context->num_watchpoints; i++) + { + if (address >= context->watchpoints[i].start && address <= context->watchpoints[i].end) { + watch = context->watchpoints + i; + break; + } + } + if (!watch) { + return vcontext; + } + if (watch->check_change) { + uint8_t old = read_byte(address, (void **)context->mem_pointers, &context->options->gen, context); + if (old == value) { + return vcontext; + } + context->wp_old_value = old; + } else { + context->wp_old_value = value; + } + context->wp_hit_address = address; + context->wp_hit_value = value; + context->wp_hit = 1; + context->target_cycle = context->sync_cycle = context->current_cycle; + system_header *system = context->system; + system->enter_debugger = 1; + return vcontext; +} + +static void z80_enable_watchpoints(z80_context *context) +{ + if (context->options->gen.check_watchpoints_8) { + //already enabled + return; + } + context->watchpoint_min = 0xFFFF; + context->watchpoint_max = 0; + context->options->gen.check_watchpoints_8 = z80_watchpoint_check; + //re-generate write handlers with watchpoints enabled + code_ptr new_write8 = gen_mem_fun(&context->options->gen, context->options->gen.memmap, context->options->gen.memmap_chunks, WRITE_8, NULL); + + //patch old write handlers to point to the new ones + code_info code = { + .cur = context->options->write_8, + .last = context->options->write_8 + 256 + }; + jmp(&code, new_write8); + context->options->write_8 = new_write8; +} + +void z80_add_watchpoint(z80_context *context, uint16_t address, uint16_t size) +{ + uint32_t end = address + size - 1; + for (uint32_t i = 0; i < context->num_watchpoints; i++) + { + if (context->watchpoints[i].start == address && context->watchpoints[i].end == end) { + return; + } + } + z80_enable_watchpoints(context); + if (context->wp_storage == context->num_watchpoints) { + context->wp_storage = context->wp_storage ? context->wp_storage * 2 : 4; + context->watchpoints = realloc(context->watchpoints, context->wp_storage * sizeof(z80_watchpoint)); + } + const memmap_chunk *chunk = find_map_chunk(address, &context->options->gen, 0, NULL); + context->watchpoints[context->num_watchpoints++] = (z80_watchpoint){ + .start = address, + .end = end, + .check_change = chunk && (chunk->flags & MMAP_READ) + }; + if (context->watchpoint_min > address) { + context->watchpoint_min = address; + } + if (context->watchpoint_max < end) { + context->watchpoint_max = end; + } +} + +void z80_remove_watchpoint(z80_context *context, uint32_t address, uint32_t size) +{ + uint32_t end = address + size - 1; + for (uint32_t i = 0; i < context->num_watchpoints; i++) + { + if (context->watchpoints[i].start == address && context->watchpoints[i].end == end) { + context->watchpoints[i] = context->watchpoints[context->num_watchpoints-1]; + context->num_watchpoints--; + return; + } + } +} + void z80_serialize(z80_context *context, serialize_buffer *buf) { for (int i = 0; i <= Z80_A; i++)