mirror of
https://github.com/n64dev/cen64.git
synced 2025-04-02 10:31:54 -04:00
155 lines
4.7 KiB
C
155 lines
4.7 KiB
C
//
|
|
// vr4300/interface.c: VR4300 interface.
|
|
//
|
|
// 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 "bus/address.h"
|
|
#include "vr4300/cpu.h"
|
|
#include "vr4300/interface.h"
|
|
|
|
#define MI_EBUS_TEST_MODE 0x0080
|
|
#define MI_INIT_MODE 0x0100
|
|
#define MI_RDRAM_REG_MODE 0x0200
|
|
|
|
static void lower_rcp_interrupt(struct vr4300 *vr4300);
|
|
static void raise_rcp_interrupt(struct vr4300 *vr4300);
|
|
|
|
// Checks for interrupts, possibly sets the cause bit.
|
|
static void check_for_interrupts(struct vr4300 *vr4300, uint32_t flags) {
|
|
if (flags & vr4300->mi_regs[MI_INTR_MASK_REG])
|
|
raise_rcp_interrupt(vr4300);
|
|
else
|
|
lower_rcp_interrupt(vr4300);
|
|
}
|
|
|
|
// Callback: An RCP component is clearing an interrupt.
|
|
void clear_rcp_interrupt(struct vr4300 *vr4300, enum rcp_interrupt_mask mask) {
|
|
uint32_t flags = __sync_and_and_fetch(&vr4300->mi_regs[MI_INTR_REG], ~mask);
|
|
check_for_interrupts(vr4300, flags); // TODO/FIXME: ???
|
|
}
|
|
|
|
// Deasserts the interrupt signal from the RCP.
|
|
static void lower_rcp_interrupt(struct vr4300 *vr4300) {
|
|
__sync_and_and_fetch(&vr4300->regs[VR4300_CP0_REGISTER_CAUSE], ~0x400);
|
|
}
|
|
|
|
// Asserts the interrupt signal from the RCP.
|
|
static void raise_rcp_interrupt(struct vr4300 *vr4300) {
|
|
//vr4300->regs[VR4300_CP0_REGISTER_CAUSE] |= 0x400;
|
|
__sync_or_and_fetch(&vr4300->regs[VR4300_CP0_REGISTER_CAUSE], 0x400);
|
|
}
|
|
|
|
// Deasserts the interrupt signal from the 64DD.
|
|
void clear_dd_interrupt(struct vr4300 *vr4300) {
|
|
//vr4300->regs[VR4300_CP0_REGISTER_CAUSE] &= ~0x800;
|
|
__sync_and_and_fetch(&vr4300->regs[VR4300_CP0_REGISTER_CAUSE], ~0x800);
|
|
}
|
|
|
|
// Asserts the interrupt signal from the 64DD.
|
|
void signal_dd_interrupt(struct vr4300 *vr4300) {
|
|
__sync_or_and_fetch(&vr4300->regs[VR4300_CP0_REGISTER_CAUSE], 0x800);
|
|
}
|
|
|
|
// Callback: An RCP component is signaling an interrupt.
|
|
void signal_rcp_interrupt(struct vr4300 *vr4300, enum rcp_interrupt_mask mask) {
|
|
uint32_t flags = __sync_or_and_fetch(&vr4300->mi_regs[MI_INTR_REG], mask);
|
|
check_for_interrupts(vr4300, flags); // TODO/FIXME: ???
|
|
}
|
|
|
|
// Reads a word from the MI MMIO register space.
|
|
int read_mi_regs(void *opaque, uint32_t address, uint32_t *word) {
|
|
struct vr4300 *vr4300 = (struct vr4300 *) opaque;
|
|
uint32_t offset = address - MI_REGS_BASE_ADDRESS;
|
|
enum mi_register reg = (offset >> 2);
|
|
|
|
*word = vr4300->mi_regs[reg];
|
|
debug_mmio_read(mi, mi_register_mnemonics[reg], *word);
|
|
return 0;
|
|
}
|
|
|
|
// Writes a word to the MI MMIO register space.
|
|
int write_mi_regs(void *opaque, uint32_t address, uint32_t word, uint32_t dqm) {
|
|
struct vr4300 *vr4300 = (struct vr4300 *) opaque;
|
|
uint32_t offset = address - MI_REGS_BASE_ADDRESS;
|
|
enum mi_register reg = (offset >> 2);
|
|
uint32_t result;
|
|
uint32_t flags;
|
|
|
|
debug_mmio_write(mi, mi_register_mnemonics[reg], word, dqm);
|
|
|
|
// Change mode settings?
|
|
if (reg == MI_INIT_MODE_REG) {
|
|
result = word & 0x3FF;
|
|
|
|
if (word & 0x0080)
|
|
result &= ~MI_INIT_MODE;
|
|
else if (word & 0x0100)
|
|
result |= MI_INIT_MODE;
|
|
|
|
if (word & 0x0200)
|
|
result &= ~MI_EBUS_TEST_MODE;
|
|
else if (word & 0x0400)
|
|
result |= MI_EBUS_TEST_MODE;
|
|
|
|
if (word & 0x0800) {
|
|
flags = __sync_and_and_fetch(&vr4300->mi_regs[MI_INTR_REG], ~MI_INTR_DP);
|
|
check_for_interrupts(vr4300, flags); // TODO/FIXME: ???
|
|
}
|
|
|
|
if (word & 0x1000)
|
|
result &= ~MI_RDRAM_REG_MODE;
|
|
else if (word & 0x2000)
|
|
result |= MI_RDRAM_REG_MODE;
|
|
|
|
vr4300->mi_regs[MI_INIT_MODE_REG] = result;
|
|
}
|
|
|
|
// Change interrupt mask?
|
|
else if (reg == MI_INTR_MASK_REG) {
|
|
if (word & 0x0001)
|
|
vr4300->mi_regs[MI_INTR_MASK_REG] &= ~MI_INTR_SP;
|
|
else if (word & 0x0002)
|
|
vr4300->mi_regs[MI_INTR_MASK_REG] |= MI_INTR_SP;
|
|
|
|
if (word & 0x0004)
|
|
vr4300->mi_regs[MI_INTR_MASK_REG] &= ~MI_INTR_SI;
|
|
else if (word & 0x0008)
|
|
vr4300->mi_regs[MI_INTR_MASK_REG] |= MI_INTR_SI;
|
|
|
|
if (word & 0x0010)
|
|
vr4300->mi_regs[MI_INTR_MASK_REG] &= ~MI_INTR_AI;
|
|
else if (word & 0x0020)
|
|
vr4300->mi_regs[MI_INTR_MASK_REG] |= MI_INTR_AI;
|
|
|
|
if (word & 0x0040)
|
|
vr4300->mi_regs[MI_INTR_MASK_REG] &= ~MI_INTR_VI;
|
|
else if (word & 0x0080)
|
|
vr4300->mi_regs[MI_INTR_MASK_REG] |= MI_INTR_VI;
|
|
|
|
if (word & 0x0100)
|
|
vr4300->mi_regs[MI_INTR_MASK_REG] &= ~MI_INTR_PI;
|
|
else if (word & 0x0200)
|
|
vr4300->mi_regs[MI_INTR_MASK_REG] |= MI_INTR_PI;
|
|
|
|
if (word & 0x0400)
|
|
vr4300->mi_regs[MI_INTR_MASK_REG] &= ~MI_INTR_DP;
|
|
else if (word & 0x0800)
|
|
vr4300->mi_regs[MI_INTR_MASK_REG] |= MI_INTR_DP;
|
|
|
|
check_for_interrupts(vr4300, vr4300->mi_regs[MI_INTR_REG]); // TODO/FIXME: ???
|
|
}
|
|
|
|
else {
|
|
vr4300->mi_regs[reg] &= ~dqm;
|
|
vr4300->mi_regs[reg] |= word;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|