cen64/vr4300/cpu.c
Giovanni Bajo a56fa4ba41 Fix two bugs in COP0 count
First, since the internal register is kept in CPU cycles (not RCP cycles),
we need to double the value written via MTC0/DMTC0.

Second, writing a count equal to compare would cause an infinite loop
because the fault would be triggered while PC was on the instruction
doing MTC0 itself, which would be then re-executed at the end of the
exception. On real hardware, in general, when COUNT==COMPARE, the
interrupt happens a few cycles later, enough for PC to move to other
opcodes. Instead of trying to implement this, I've simply made sure
that the interrupt happened after the opcode was executed rather than
before. Also, since the internal counter is in CPU cycles, we make
sure to only raise the CAUSE bit once.
2021-06-13 23:19:07 +02:00

176 lines
4.6 KiB
C

//
// vr4300/cpu.c: VR4300 processor container.
//
// 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 "common.h"
#include "vr4300/interface.h"
#include "vr4300/cp0.h"
#include "vr4300/cp1.h"
#include "vr4300/cpu.h"
#include "vr4300/icache.h"
#include "vr4300/pipeline.h"
#ifdef DEBUG_MMIO_REGISTER_ACCESS
const char *mi_register_mnemonics[NUM_MI_REGISTERS] = {
#define X(reg) #reg,
#include "vr4300/registers.md"
#undef X
};
#endif
void vr4300_cycle(struct vr4300 *vr4300) {
struct vr4300_pipeline *pipeline = &vr4300->pipeline;
// Increment counters.
vr4300->regs[VR4300_CP0_REGISTER_COUNT]++;
// We're stalling for something...
if (pipeline->cycles_to_stall > 0)
pipeline->cycles_to_stall--;
else
vr4300_cycle_(vr4300);
if ((vr4300->regs[VR4300_CP0_REGISTER_COUNT] & 1) == 1 &&
(uint32_t) (vr4300->regs[VR4300_CP0_REGISTER_COUNT] >> 1) ==
(uint32_t) vr4300->regs[VR4300_CP0_REGISTER_COMPARE]) {
vr4300->regs[VR4300_CP0_REGISTER_CAUSE] |= 0x8000;
}
}
// Sets the opaque pointer used for external accesses.
static void vr4300_connect_bus(struct vr4300 *vr4300,
struct bus_controller *bus) {
vr4300->bus = bus;
}
// Initializes the VR4300 component.
int vr4300_init(struct vr4300 *vr4300, struct bus_controller *bus, bool profiling) {
vr4300_connect_bus(vr4300, bus);
vr4300_cp0_init(vr4300);
vr4300_cp1_init(vr4300);
vr4300_dcache_init(&vr4300->dcache);
vr4300_icache_init(&vr4300->icache);
vr4300_pipeline_init(&vr4300->pipeline);
vr4300->signals = VR4300_SIGNAL_COLDRESET;
// MESS uses this version, so we will too?
vr4300->mi_regs[MI_VERSION_REG] = 0x01010101;
vr4300->mi_regs[MI_INIT_MODE_REG] = 0x80;
if (profiling)
vr4300->profile_samples = calloc(2 * 8 * 1024 * 1024, sizeof(uint64_t));
else
vr4300->profile_samples = NULL;
vr4300_debug_init(&vr4300->debug);
return 0;
}
// Prints out simulation information to stdout.
void vr4300_print_summary(struct vr4300_stats *stats) {
unsigned i, j;
float secs;
float cpi;
// Print banner.
printf("###############################\n"
" NEC VR4300 Simulation Summary\n"
"###############################\n"
"\n"
);
// Print configuration, summary, whatever.
secs = stats->total_cycles / 93750000.0f;
printf(" %16s: %.1f sec.\n"
"\n\n",
"Actual runtime", secs
);
// Print performance statistics.
cpi = (float) stats->executed_instructions / stats->total_cycles;
printf(" * Performance statistics:\n\n"
" %16s: %lu\n"
" %16s: %lu\n"
" %16s: %1.2f\n"
"\n\n",
"Elapsed pcycles", stats->total_cycles,
"Insns executed", stats->executed_instructions,
"Average CPI", cpi
);
// Print executed opcode counts.
printf(" * Executed instruction counts:\n\n");
for (i = 1; i < NUM_VR4300_OPCODES; i += 2) {
for (j = 0; i + j < NUM_VR4300_OPCODES && j < 2; j++) {
printf(" %16s: %16lu", vr4300_opcode_mnemonics[i + j],
stats->opcode_counts[i + j]);
if (j == 0)
printf("\t");
}
printf("\n");
}
}
uint64_t vr4300_get_register(struct vr4300 *vr4300, size_t i) {
return vr4300->regs[i];
}
uint64_t vr4300_get_pc(struct vr4300 *vr4300) {
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() {
struct vr4300* ptr = (struct vr4300*)malloc(sizeof(struct vr4300));
memset(ptr, 0, sizeof(struct vr4300));
return ptr;
}
cen64_cold void vr4300_free(struct vr4300* ptr) {
vr4300_debug_cleanup(&ptr->debug);
free(ptr);
}
cen64_cold struct vr4300_stats* vr4300_stats_alloc() {
struct vr4300_stats* ptr = (struct vr4300_stats*)malloc(sizeof(struct vr4300_stats));
memset(ptr, 0, sizeof(struct vr4300_stats));
return ptr;
}
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;
}