Skip to content

Commit

Permalink
Implement debugging hooks into vr4300
Browse files Browse the repository at this point in the history
  • Loading branch information
lambertjamesd committed Jan 11, 2021
1 parent 13720b1 commit 2865d10
Show file tree
Hide file tree
Showing 12 changed files with 197 additions and 9 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Expand Up @@ -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
Expand Down
6 changes: 0 additions & 6 deletions bus/controller.h
Expand Up @@ -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

3 changes: 3 additions & 0 deletions device/device.c
Expand Up @@ -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);
}
2 changes: 2 additions & 0 deletions device/device.h
Expand Up @@ -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

24 changes: 22 additions & 2 deletions vr4300/cpu.c
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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() {
Expand All @@ -140,6 +154,7 @@ struct vr4300* vr4300_alloc() {
}

cen64_cold void vr4300_free(struct vr4300* ptr) {
vr4300_debug_cleanup(&ptr->debug);
free(ptr);
}

Expand All @@ -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;
}
6 changes: 5 additions & 1 deletion vr4300/cpu.h
Expand Up @@ -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"

Expand All @@ -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 {
Expand Down Expand Up @@ -103,6 +106,8 @@ struct vr4300 {
struct vr4300_icache icache;

uint64_t *profile_samples;

struct vr4300_debug debug;
};

struct vr4300_stats {
Expand All @@ -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);
Expand Down
55 changes: 55 additions & 0 deletions 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;
}
38 changes: 38 additions & 0 deletions 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
3 changes: 3 additions & 0 deletions vr4300/fault.c
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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);
}

50 changes: 50 additions & 0 deletions vr4300/interface.c
Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
16 changes: 16 additions & 0 deletions vr4300/interface.h
Expand Up @@ -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*);

Expand All @@ -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);

Expand All @@ -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

2 changes: 2 additions & 0 deletions vr4300/pipeline.c
Expand Up @@ -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;

Expand Down

0 comments on commit 2865d10

Please sign in to comment.