diff --git a/CMakeLists.txt b/CMakeLists.txt index 00946f1..1cc93ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -365,6 +365,7 @@ set(VR4300_SOURCES ${PROJECT_SOURCE_DIR}/vr4300/cpu.c ${PROJECT_SOURCE_DIR}/vr4300/dcache.c ${PROJECT_SOURCE_DIR}/vr4300/decoder.c + ${PROJECT_SOURCE_DIR}/vr4300/debug.c ${PROJECT_SOURCE_DIR}/vr4300/fault.c ${PROJECT_SOURCE_DIR}/vr4300/functions.c ${PROJECT_SOURCE_DIR}/vr4300/icache.c diff --git a/bus/controller.h b/bus/controller.h index e7ed702..956d745 100644 --- a/bus/controller.h +++ b/bus/controller.h @@ -55,11 +55,5 @@ cen64_flatten cen64_hot int bus_read_word(const struct bus_controller *bus, cen64_flatten cen64_hot int bus_write_word(struct bus_controller *bus, uint32_t address, uint32_t word, uint32_t dqm); -// For asserting and deasserting RCP interrupts. -enum rcp_interrupt_mask; - -int raise_rcp_interrupt(struct bus_controller *bus, - enum rcp_interrupt_mask mask); - #endif diff --git a/device/device.c b/device/device.c index 89924e8..cd5a046 100644 --- a/device/device.c +++ b/device/device.c @@ -349,3 +349,6 @@ int device_debug_spin(struct cen64_device *device) { return 0; } +cen64_cold void device_connect_debugger(struct cen64_device *device, void* break_handler_data, vr4300_debug_break_handler break_handler) { + vr4300_connect_debugger(device->vr4300, break_handler_data, break_handler); +} \ No newline at end of file diff --git a/device/device.h b/device/device.h index 34003bc..81709c2 100644 --- a/device/device.h +++ b/device/device.h @@ -67,5 +67,7 @@ cen64_cold struct cen64_device *device_create(struct cen64_device *device, cen64_cold void device_exit(struct bus_controller *bus); cen64_cold void device_run(struct cen64_device *device); +cen64_cold void device_connect_debugger(struct cen64_device *device, void* break_handler_data, vr4300_debug_break_handler break_handler); + #endif diff --git a/vr4300/cpu.c b/vr4300/cpu.c index 81a269a..c238ec1 100644 --- a/vr4300/cpu.c +++ b/vr4300/cpu.c @@ -70,6 +70,8 @@ int vr4300_init(struct vr4300 *vr4300, struct bus_controller *bus, bool profilin else vr4300->profile_samples = NULL; + vr4300_debug_init(&vr4300->debug); + return 0; } @@ -126,11 +128,23 @@ void vr4300_print_summary(struct vr4300_stats *stats) { } uint64_t vr4300_get_register(struct vr4300 *vr4300, size_t i) { - return vr4300->regs[i]; + return vr4300->regs[i]; } uint64_t vr4300_get_pc(struct vr4300 *vr4300) { - return vr4300->pipeline.dcwb_latch.common.pc; + return vr4300->pipeline.dcwb_latch.common.pc; +} + +cen64_cold void vr4300_signal_break(struct vr4300 *vr4300) { + vr4300_debug_signal(&vr4300->debug, VR4300_DEBUG_SIGNALS_BREAK); +} + +cen64_cold void vr4300_set_breakpoint(struct vr4300 *vr4300, uint64_t at) { + vr4300_debug_set_breakpoint(&vr4300->debug, at); +} + +cen64_cold void vr4300_remove_breakpoint(struct vr4300 *vr4300, uint64_t at) { + vr4300_debug_remove_breakpoint(&vr4300->debug, at); } struct vr4300* vr4300_alloc() { @@ -140,6 +154,7 @@ struct vr4300* vr4300_alloc() { } cen64_cold void vr4300_free(struct vr4300* ptr) { + vr4300_debug_cleanup(&ptr->debug); free(ptr); } @@ -152,3 +167,8 @@ cen64_cold struct vr4300_stats* vr4300_stats_alloc() { cen64_cold void vr4300_stats_free(struct vr4300_stats* ptr) { free(ptr); } + +cen64_cold void vr4300_connect_debugger(struct vr4300 *vr4300, void* break_handler_data, vr4300_debug_break_handler break_handler) { + vr4300->debug.break_handler = break_handler; + vr4300->debug.break_handler_data = break_handler_data; +} \ No newline at end of file diff --git a/vr4300/cpu.h b/vr4300/cpu.h index 53b73b5..423a29a 100644 --- a/vr4300/cpu.h +++ b/vr4300/cpu.h @@ -14,7 +14,9 @@ #include "vr4300/cp0.h" #include "vr4300/cp1.h" #include "vr4300/dcache.h" +#include "vr4300/debug.h" #include "vr4300/icache.h" +#include "vr4300/interface.h" #include "vr4300/opcodes.h" #include "vr4300/pipeline.h" @@ -23,6 +25,7 @@ struct bus_controller; enum vr4300_signals { VR4300_SIGNAL_FORCEEXIT = 0x000000001, VR4300_SIGNAL_COLDRESET = 0x000000002, + VR4300_SIGNAL_BREAK = 0x000000004, }; enum vr4300_register { @@ -103,6 +106,8 @@ struct vr4300 { struct vr4300_icache icache; uint64_t *profile_samples; + + struct vr4300_debug debug; }; struct vr4300_stats { @@ -112,7 +117,6 @@ struct vr4300_stats { unsigned long opcode_counts[NUM_VR4300_OPCODES]; }; -cen64_cold int vr4300_init(struct vr4300 *vr4300, struct bus_controller *bus, bool profiling); cen64_cold void vr4300_print_summary(struct vr4300_stats *stats); cen64_flatten cen64_hot void vr4300_cycle_(struct vr4300 *vr4300); diff --git a/vr4300/debug.c b/vr4300/debug.c new file mode 100644 index 0000000..19f2ace --- /dev/null +++ b/vr4300/debug.c @@ -0,0 +1,55 @@ +// +// vr4300/debug.c: VR4300 debug hooks. +// +// CEN64: Cycle-Accurate Nintendo 64 Emulator. +// Copyright (C) 2015, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#include "debug.h" + +cen64_cold void vr4300_debug_init(struct vr4300_debug* debug) { + hash_table_init(&debug->breakpoints, 0); + debug->break_handler = NULL; + debug->break_handler_data = NULL; +} + +cen64_cold void vr4300_debug_cleanup(struct vr4300_debug* debug) { + hash_table_free(&debug->breakpoints); +} + +cen64_cold void vr4300_debug_check_breakpoints(struct vr4300_debug* debug, uint64_t pc) { + if (debug->break_handler) { + enum vr4300_debug_break_reason reason = VR4300_DEBUG_BREAK_REASON_NONE; + if (hash_table_get(&debug->breakpoints, (unsigned long)pc, NULL)) { + reason = VR4300_DEBUG_BREAK_REASON_BREAKPOINT; + } else if (debug->signals & VR4300_DEBUG_SIGNALS_BREAK) { + reason = VR4300_DEBUG_BREAK_REASON_PAUSE; + } + + if (reason != VR4300_DEBUG_BREAK_REASON_NONE) { + debug->signals &= ~VR4300_DEBUG_SIGNALS_BREAK; + debug->break_handler(debug->break_handler_data, reason); + } + } +} + +cen64_cold void vr4300_debug_exception(struct vr4300_debug* debug) { + if (debug->break_handler) { + debug->break_handler(debug->break_handler_data, VR4300_DEBUG_BREAK_REASON_EXCEPTION); + } +} + +cen64_cold void vr4300_debug_set_breakpoint(struct vr4300_debug* debug, uint64_t pc) { + hash_table_set(&debug->breakpoints, (unsigned long)pc, 1); +} + +cen64_cold void vr4300_debug_remove_breakpoint(struct vr4300_debug* debug, uint64_t pc) { + hash_table_delete(&debug->breakpoints, (unsigned long)pc); +} + +cen64_cold void vr4300_debug_signal(struct vr4300_debug* debug, enum vr4300_debug_signals signal) { + debug->signals |= signal; +} \ No newline at end of file diff --git a/vr4300/debug.h b/vr4300/debug.h new file mode 100644 index 0000000..f4a613c --- /dev/null +++ b/vr4300/debug.h @@ -0,0 +1,38 @@ +// +// vr4300/debug.h: VR4300 debug hooks. +// +// CEN64: Cycle-Accurate Nintendo 64 Emulator. +// Copyright (C) 2015, Tyler J. Stachecki. +// +// This file is subject to the terms and conditions defined in +// 'LICENSE', which is part of this source code package. +// + +#ifndef __vr4300_debug_h__ +#define __vr4300_debug_h__ +#include "common.h" +#include "common/hash_table.h" +#include "vr4300/interface.h" + +enum vr4300_debug_signals { + VR4300_DEBUG_SIGNALS_BREAK = 0x000000001, +}; + +struct vr4300_debug { + struct hash_table breakpoints; + vr4300_debug_break_handler break_handler; + void* break_handler_data; + unsigned signals; +}; + +cen64_cold void vr4300_debug_init(struct vr4300_debug* debug); + +cen64_cold void vr4300_debug_cleanup(struct vr4300_debug* debug); +cen64_cold void vr4300_debug_check_breakpoints(struct vr4300_debug* debug, uint64_t pc); +cen64_cold void vr4300_debug_exception(struct vr4300_debug* debug); +cen64_cold void vr4300_debug_set_breakpoint(struct vr4300_debug* debug, uint64_t pc); +cen64_cold void vr4300_debug_remove_breakpoint(struct vr4300_debug* debug, uint64_t pc); + +cen64_cold void vr4300_debug_signal(struct vr4300_debug* debug, enum vr4300_debug_signals signal); + +#endif \ No newline at end of file diff --git a/vr4300/fault.c b/vr4300/fault.c index 70c0e51..072710a 100644 --- a/vr4300/fault.c +++ b/vr4300/fault.c @@ -483,6 +483,7 @@ void VR4300_BRPT(struct vr4300 *vr4300) { vr4300_exception_prolog(vr4300, common, &cause, &status, &epc); vr4300_exception_epilogue(vr4300, (cause & ~0xFF) | (9 << 2), status, epc, 0x180); + vr4300_debug_exception(&vr4300->debug); } // TRAP: Trap exception @@ -495,6 +496,7 @@ void VR4300_TRAP(struct vr4300* vr4300) { vr4300_exception_prolog(vr4300, common, &cause, &status, &epc); vr4300_exception_epilogue(vr4300, (cause & ~0xFF) | (13 << 2), status, epc, 0x180); + vr4300_debug_exception(&vr4300->debug); } // RI: Reserved Instruction exception @@ -521,5 +523,6 @@ void VR4300_WAT(struct vr4300 *vr4300) { status, epc, 0x180); vr4300_dc_fault(vr4300, VR4300_FAULT_WAT); + vr4300_debug_exception(&vr4300->debug); } diff --git a/vr4300/interface.c b/vr4300/interface.c index 743a467..9250d2a 100644 --- a/vr4300/interface.c +++ b/vr4300/interface.c @@ -10,6 +10,7 @@ #include "common.h" #include "bus/address.h" +#include "bus/controller.h" #include "vr4300/cpu.h" #include "vr4300/interface.h" #ifdef _WIN32 @@ -190,3 +191,52 @@ uint64_t get_profile_sample(struct vr4300 const *vr4300, size_t i) return vr4300->profile_samples[i]; } +bool vr4300_read_word_vaddr(struct vr4300 *vr4300, uint64_t vaddr, uint32_t* result) { + if (vaddr & 0x3) { + // must be aligned + return false; + } + + const struct segment* segment = get_segment(vaddr, vr4300->regs[VR4300_CP0_REGISTER_STATUS]); + + if (!segment) { + return false; + } + + uint32_t paddr; + bool cached; + + if (segment->mapped) { + unsigned asid = vr4300->regs[VR4300_CP0_REGISTER_ENTRYHI] & 0xFF; + unsigned select, tlb_miss, index; + uint32_t page_mask; + + tlb_miss = tlb_probe(&vr4300->cp0.tlb, vaddr, asid, &index); + page_mask = vr4300->cp0.page_mask[index]; + select = ((page_mask + 1) & vaddr) != 0; + + if (unlikely(tlb_miss || !(vr4300->cp0.state[index][select] & 2))) { + return false; + } + + cached = ((vr4300->cp0.state[index][select] & 0x38) != 0x10); + paddr = (vr4300->cp0.pfn[index][select]) | (vaddr & page_mask); + } else { + paddr = vaddr - segment->offset; + cached = segment->cached; + } + + if (cached) { + struct vr4300_dcache_line* line = vr4300_dcache_probe(&vr4300->dcache, vaddr, paddr); + + if (line) { + memcpy(result, line->data + ((paddr & 0xf) ^ WORD_ADDR_XOR), sizeof(uint32_t)); + } else { + bus_read_word(vr4300->bus, paddr, result); + } + } else { + bus_read_word(vr4300->bus, paddr, result); + } + + return true; +} \ No newline at end of file diff --git a/vr4300/interface.h b/vr4300/interface.h index 745c6d6..e77a4a0 100644 --- a/vr4300/interface.h +++ b/vr4300/interface.h @@ -24,6 +24,15 @@ enum rcp_interrupt_mask { struct vr4300; struct vr4300_stats; +enum vr4300_debug_break_reason { + VR4300_DEBUG_BREAK_REASON_NONE, + VR4300_DEBUG_BREAK_REASON_BREAKPOINT, + VR4300_DEBUG_BREAK_REASON_EXCEPTION, + VR4300_DEBUG_BREAK_REASON_PAUSE, +}; + +typedef void (*vr4300_debug_break_handler)(void* data, enum vr4300_debug_break_reason reason); + cen64_cold struct vr4300* vr4300_alloc(); cen64_cold void vr4300_free(struct vr4300*); @@ -39,6 +48,8 @@ cen64_cold void vr4300_cycle_extra(struct vr4300 *vr4300, struct vr4300_stats *s uint64_t vr4300_get_register(struct vr4300 *vr4300, size_t i); uint64_t vr4300_get_pc(struct vr4300 *vr4300); +bool vr4300_read_word_vaddr(struct vr4300 *vr4300, uint64_t vaddr, uint32_t* result); + int read_mi_regs(struct vr4300 *vr4300, uint32_t address, uint32_t *word); int write_mi_regs(struct vr4300 *vr4300, uint32_t address, uint32_t word, uint32_t dqm); @@ -51,5 +62,10 @@ void signal_dd_interrupt(struct vr4300 *vr4300); uint64_t get_profile_sample(struct vr4300 const *vr4300, size_t i); int has_profile_samples(struct vr4300 const *vr4300); +cen64_cold void vr4300_signal_break(struct vr4300 *vr4300); +cen64_cold void vr4300_set_breakpoint(struct vr4300 *vr4300, uint64_t at); +cen64_cold void vr4300_remove_breakpoint(struct vr4300 *vr4300, uint64_t at); +cen64_cold void vr4300_connect_debugger(struct vr4300 *vr4300, void* break_handler_data, vr4300_debug_break_handler break_handler); + #endif diff --git a/vr4300/pipeline.c b/vr4300/pipeline.c index e085485..4620400 100644 --- a/vr4300/pipeline.c +++ b/vr4300/pipeline.c @@ -522,6 +522,8 @@ void vr4300_cycle_(struct vr4300 *vr4300) { if (vr4300_wb_stage(vr4300)) return; + vr4300_debug_check_breakpoints(&vr4300->debug, vr4300_get_pc(vr4300)); + if (vr4300_dc_stage(vr4300)) return;