Restructure

This commit is contained in:
CocoSimone
2022-12-22 22:33:43 +01:00
parent ba62db8f12
commit 4adb7a46f8
70 changed files with 554 additions and 582 deletions

View File

@@ -0,0 +1,112 @@
#include <core/mmio/AI.hpp>
#include <log.hpp>
#include <core/Mem.hpp>
#include <core/registers/Registers.hpp>
#include <core/Audio.hpp>
namespace n64 {
void AI::Reset() {
dmaEnable = 0;
dacRate = 0;
bitrate = 0;
dmaCount = 0;
dmaAddrCarry = false;
cycles = 0;
memset(dmaLen, 0, 2);
memset(dmaAddr, 0, 2);
dac = {44100, N64_CPU_FREQ / dac.freq, 16};
}
auto AI::Read(u32 addr) const -> u32 {
if(addr == 0x0450000C) {
u32 val = 0;
val |= (dmaCount > 1);
val |= 1 << 20;
val |= 1 << 24;
val |= (dmaEnable << 25);
val |= (dmaCount > 0) << 30;
val |= (dmaCount > 1) << 31;
return val;
}
return dmaLen[0];
}
#define max(x, y) ((x) > (y) ? (x) : (y))
void AI::Write(Mem& mem, Registers& regs, u32 addr, u32 val) {
switch(addr) {
case 0x04500000:
if(dmaCount < 2) {
dmaAddr[dmaCount] = val & 0xFFFFFF & ~7;
}
break;
case 0x04500004: {
u32 len = (val & 0x3FFFF) & ~7;
if((dmaCount < 2) && len) {
// if(dmaCount == 0) InterruptRaise(mem.mmio.mi, regs, Interrupt::AI);
dmaLen[dmaCount] = len;
dmaCount++;
}
} break;
case 0x04500008:
dmaEnable = val & 1;
break;
case 0x0450000C:
InterruptLower(mem.mmio.mi, regs, Interrupt::AI);
break;
case 0x04500010: {
u32 old_dac_freq = dac.freq;
dacRate = val & 0x3FFF;
dac.freq = max(1, N64_CPU_FREQ / 2 / (dacRate + 1)) * 1.037;
dac.period = N64_CPU_FREQ / dac.freq;
if(old_dac_freq != dac.freq) {
AdjustSampleRate(dac.freq);
}
} break;
case 0x04500014:
bitrate = val & 0xF;
dac.precision = bitrate + 1;
break;
default:
util::panic("Unhandled AI write at addr {:08X} with val {:08X}\n", addr, val);
}
}
void AI::Step(Mem& mem, Registers& regs, int cpuCycles, float volumeL, float volumeR) {
cycles += cpuCycles;
while(cycles > dac.period) {
if (dmaCount == 0) {
return;
}
if(dmaLen[0] && dmaEnable) {
if(volumeR > 0 && volumeL > 0) {
u32 addrHi = ((dmaAddr[0] >> 13) + dmaAddrCarry) & 0x7FF;
dmaAddr[0] = (addrHi << 13) | (dmaAddr[0] & 0x1FFF);
u32 data = util::ReadAccess<u32>(mem.mmio.rdp.rdram.data(), dmaAddr[0] & RDRAM_DSIZE);
s16 l = s16(data >> 16);
s16 r = s16(data);
PushSample((float) l / INT16_MAX, volumeL, (float) r / INT16_MAX, volumeR);
u32 addrLo = (dmaAddr[0] + 4) & 0x1FFF;
dmaAddr[0] = (dmaAddr[0] & ~0x1FFF) | addrLo;
dmaAddrCarry = addrLo == 0;
}
dmaLen[0] -= 4;
}
if(!dmaLen[0]) {
if(--dmaCount > 0) {
InterruptRaise(mem.mmio.mi, regs, Interrupt::AI);
dmaAddr[0] = dmaAddr[1];
dmaLen[0] = dmaLen[1];
}
}
cycles -= dac.period;
}
}
}

View File

@@ -0,0 +1,30 @@
#pragma once
#include <common.hpp>
#include <core/mmio/Interrupt.hpp>
namespace n64 {
struct Mem;
struct Registers;
struct AI {
AI() = default;
void Reset();
auto Read(u32) const -> u32;
void Write(Mem&, Registers&, u32, u32);
void Step(Mem&, Registers&, int, float, float);
bool dmaEnable{};
u16 dacRate{};
u8 bitrate{};
int dmaCount{};
u32 dmaLen[2]{};
u32 dmaAddr[2]{};
bool dmaAddrCarry{};
int cycles{};
struct {
u32 freq{44100};
u32 period{N64_CPU_FREQ / freq};
u32 precision{16};
} dac;
};
}

View File

@@ -0,0 +1,59 @@
#include <core/mmio/Interrupt.hpp>
#include <core/mmio/MI.hpp>
#include <core/registers/Registers.hpp>
namespace n64 {
void InterruptRaise(MI &mi, Registers &regs, Interrupt intr) {
switch(intr) {
case Interrupt::VI:
mi.miIntr.vi = true;
break;
case Interrupt::SI:
mi.miIntr.si = true;
break;
case Interrupt::PI:
mi.miIntr.pi = true;
break;
case Interrupt::AI:
mi.miIntr.ai = true;
break;
case Interrupt::DP:
mi.miIntr.dp = true;
break;
case Interrupt::SP:
mi.miIntr.sp = true;
break;
}
UpdateInterrupt(mi, regs);
}
void InterruptLower(MI &mi, Registers &regs, Interrupt intr) {
switch(intr) {
case Interrupt::VI:
mi.miIntr.vi = false;
break;
case Interrupt::SI:
mi.miIntr.si = false;
break;
case Interrupt::PI:
mi.miIntr.pi = false;
break;
case Interrupt::AI:
mi.miIntr.ai = false;
break;
case Interrupt::DP:
mi.miIntr.dp = false;
break;
case Interrupt::SP:
mi.miIntr.sp = false;
break;
}
UpdateInterrupt(mi, regs);
}
void UpdateInterrupt(MI &mi, Registers &regs) {
bool interrupt = mi.miIntr.raw & mi.miIntrMask.raw;
regs.cop0.cause.ip2 = interrupt;
}
}

View File

@@ -0,0 +1,16 @@
#pragma once
#include <common.hpp>
#include <core/mmio/MI.hpp>
namespace n64 {
struct Registers;
enum class Interrupt : u8 {
VI, SI, PI, AI, DP, SP
};
void InterruptRaise(MI &mi, Registers &regs, Interrupt intr);
void InterruptLower(MI &mi, Registers &regs, Interrupt intr);
void UpdateInterrupt(MI &mi, Registers &regs);
}

View File

@@ -0,0 +1,84 @@
#include <core/mmio/MI.hpp>
#include <core/registers/Registers.hpp>
#include <log.hpp>
#include <core/mmio/Interrupt.hpp>
#define MI_VERSION_REG 0x02020102
namespace n64 {
MI::MI() {
Reset();
}
void MI::Reset() {
miIntrMask.raw = 0;
miIntr.raw = 0;
miMode = 0;
}
auto MI::Read(u32 paddr) const -> u32 {
switch(paddr & 0xF) {
case 0x0: return miMode & 0x3FF;
case 0x4: return MI_VERSION_REG;
case 0x8: return miIntr.raw & 0x3F;
case 0xC: return miIntrMask.raw & 0x3F;
default:
util::panic("Unhandled MI[{:08X}] read\n", paddr);
}
}
void MI::Write(Registers& regs, u32 paddr, u32 val) {
switch(paddr & 0xF) {
case 0x0:
miMode &= 0xFFFFFF80;
miMode |= val & 0x7F;
if (val & (1 << 7)) {
miMode &= ~(1 << 7);
}
if (val & (1 << 8)) {
miMode |= 1 << 7;
}
if (val & (1 << 9)) {
miMode &= ~(1 << 8);
}
if (val & (1 << 10)) {
miMode |= 1 << 8;
}
if (val & (1 << 11)) {
InterruptLower(*this, regs, Interrupt::DP);
}
if (val & (1 << 12)) {
miMode &= ~(1 << 9);
}
if (val & (1 << 13)) {
miMode |= 1 << 9;
}
break;
case 0x4: break;
case 0xC:
for (int bit = 0; bit < 6; bit++) {
int clearbit = bit << 1;
int setbit = (bit << 1) + 1;
if (val & (1 << clearbit)) {
miIntrMask.raw &= ~(1 << bit);
}
if (val & (1 << setbit)) {
miIntrMask.raw |= 1 << bit;
}
}
UpdateInterrupt(*this, regs);
break;
default:
util::panic("Unhandled MI[{:08X}] write ({:08X})\n", val, paddr);
}
}
}

View File

@@ -0,0 +1,30 @@
#pragma once
#include <common.hpp>
namespace n64 {
union MIIntr {
struct {
unsigned sp: 1;
unsigned si: 1;
unsigned ai: 1;
unsigned vi: 1;
unsigned pi: 1;
unsigned dp: 1;
unsigned: 26;
};
u32 raw;
};
struct Registers;
struct MI {
MI();
void Reset();
[[nodiscard]] auto Read(u32) const -> u32;
void Write(Registers& regs, u32, u32);
u32 miMode;
MIIntr miIntr{}, miIntrMask{};
};
}

View File

@@ -0,0 +1,90 @@
#include <core/mmio/PI.hpp>
#include <log.hpp>
#include <core/Mem.hpp>
#include <core/registers/Registers.hpp>
namespace n64 {
PI::PI() {
Reset();
}
void PI::Reset() {
dramAddr = 0;
cartAddr = 0;
rdLen = 0;
wrLen = 0;
memset(stub, 0, 8);
}
auto PI::Read(MI& mi, u32 addr) const -> u32 {
switch(addr) {
case 0x04600000: return dramAddr;
case 0x04600004: return cartAddr;
case 0x04600008: return rdLen;
case 0x0460000C: return wrLen;
case 0x04600010: {
u32 value = 0;
value |= (0 << 0); // Is PI DMA active? No, because it's instant
value |= (0 << 1); // Is PI IO busy? No, because it's instant
value |= (0 << 2); // PI IO error?
value |= (mi.miIntr.pi << 3); // PI interrupt?
return value;
}
case 0x04600014: case 0x04600018: case 0x0460001C: case 0x04600020:
case 0x04600024: case 0x04600028: case 0x0460002C: case 0x04600030:
return stub[(addr & 0xff) - 5];
default:
util::panic("Unhandled PI[{:08X}] read\n", addr);
}
}
void PI::Write(Mem& mem, Registers& regs, u32 addr, u32 val) {
MI& mi = mem.mmio.mi;
switch(addr) {
case 0x04600000: dramAddr = val & 0xFFFFFF; break;
case 0x04600004: cartAddr = val; break;
case 0x04600008: {
u32 len = (val & 0x00FFFFFF) + 1;
u32 cart_addr = cartAddr & 0xFFFFFFFE;
u32 dram_addr = dramAddr & 0x007FFFFE;
if (dram_addr & 0x7) {
len -= dram_addr & 0x7;
}
rdLen = len;
for(int i = 0; i < len; i++) {
mem.cart[BYTE_ADDRESS(cart_addr + i) & mem.romMask] = mem.mmio.rdp.rdram[BYTE_ADDRESS(dram_addr + i) & RDRAM_DSIZE];
}
dramAddr = dram_addr + len;
cartAddr = cart_addr + len;
InterruptRaise(mi, regs, Interrupt::PI);
//util::print("PI DMA from RDRAM to CARTRIDGE (size: {} B, {:08X} to {:08X})\n", len, dramAddr, cartAddr);
} break;
case 0x0460000C: {
u32 len = (val & 0x00FFFFFF) + 1;
u32 cart_addr = cartAddr & 0xFFFFFFFE;
u32 dram_addr = dramAddr & 0x007FFFFE;
if (dram_addr & 0x7) {
len -= (dram_addr & 0x7);
}
wrLen = len;
for(int i = 0; i < len; i++) {
mem.mmio.rdp.rdram[BYTE_ADDRESS(dram_addr + i) & RDRAM_DSIZE] = mem.cart[BYTE_ADDRESS(cart_addr + i) & mem.romMask];
}
dramAddr = dram_addr + len;
cartAddr = cart_addr + len;
InterruptRaise(mi, regs, Interrupt::PI);
//util::print("PI DMA from CARTRIDGE to RDRAM (size: {} B, {:08X} to {:08X})\n", len, cart_addr, dram_addr);
} break;
case 0x04600010:
if(val & 2) {
InterruptLower(mi, regs, Interrupt::PI);
} break;
case 0x04600014: case 0x04600018: case 0x0460001C: case 0x04600020:
case 0x04600024: case 0x04600028: case 0x0460002C: case 0x04600030:
stub[(addr & 0xff) - 5] = val & 0xff;
break;
default:
util::panic("Unhandled PI[{:08X}] write ({:08X})\n", val, addr);
}
}
}

View File

@@ -0,0 +1,19 @@
#pragma once
#include <common.hpp>
#include <core/mmio/Interrupt.hpp>
namespace n64 {
struct Mem;
struct Registers;
struct PI {
PI();
void Reset();
auto Read(MI&, u32) const -> u32;
void Write(Mem&, Registers&, u32, u32);
u32 dramAddr{}, cartAddr{};
u32 rdLen{}, wrLen{};
u32 stub[8]{};
};
}

View File

@@ -0,0 +1,277 @@
#include <core/mmio/PIF.hpp>
#include <core/Mem.hpp>
#include <core/registers/Registers.hpp>
#include <log.hpp>
#include <MupenMovie.hpp>
namespace n64 {
static int channel = 0;
void ProcessPIFCommands(u8* pifRam, Controller& controller, Mem& mem) {
u8 control = pifRam[63];
if(control & 1) {
channel = 0;
for(int i = 0; i < 63;) {
u8* cmd = &pifRam[i++];
u8 t = cmd[0] & 0x3f;
if(t == 0 || t == 0x3D) {
channel++;
} else if (t == 0x3E) {
break;
} else if (t == 0x3F) {
continue;
} else {
u8 r = pifRam[i++];
r |= (1 << 7);
if(r == 0xFE) {
break;
}
u8 rlen = r & 0x3F;
u8* res = &pifRam[i + t];
switch(cmd[2]) {
case 0xff:
res[0] = 0x05;
res[1] = 0x00;
res[2] = 0x01;
channel++;
break;
case 0:
res[0] = 0x05;
res[1] = 0x00;
res[2] = 0x01;
break;
case 1:
if(tas_movie_loaded()) {
controller = tas_next_inputs();
}
res[0] = controller.byte1;
res[1] = controller.byte2;
res[2] = controller.joy_x;
res[3] = controller.joy_y;
break;
case 2: case 3: res[0] = 0; break;
default: util::panic("Unimplemented PIF command {}", cmd[2]);
}
i += t + rlen;
}
}
}
if(control & 8) {
pifRam[63] &= ~8;
}
if (control & 0x30) {
pifRam[63] = 0x80;
}
}
void DoPIFHLE(Mem& mem, Registers& regs, CartInfo cartInfo) {
u32 cicType = cartInfo.cicType;
bool pal = cartInfo.isPAL;
mem.Write32<false>(regs, 0x1FC007E4, cicSeeds[cicType], regs.pc);
switch(cicType) {
case CIC_NUS_6101:
mem.Write32<false>(regs, 0x318, RDRAM_SIZE, regs.pc);
regs.gpr[2] = (s64)0xFFFFFFFFDF6445CC;
regs.gpr[3] = (s64)0xFFFFFFFFDF6445CC;
regs.gpr[4] = 0x45CC;
regs.gpr[5] = 0x73EE317A;
regs.gpr[6] = (s64)0xFFFFFFFFA4001F0C;
regs.gpr[7] = (s64)0xFFFFFFFFA4001F08;
regs.gpr[8] = 0xC0;
regs.gpr[10] = 0x40;
regs.gpr[11] = (s64)0xFFFFFFFFA4000040;
regs.gpr[12] = (s64)0xFFFFFFFFC7601FAC;
regs.gpr[13] = (s64)0xFFFFFFFFC7601FAC;
regs.gpr[14] = (s64)0xFFFFFFFFB48E2ED6;
regs.gpr[15] = (s64)0xFFFFFFFFBA1A7D4B;
regs.gpr[20] = 0x0000000000000001;
regs.gpr[22] = 0x000000000000003F;
regs.gpr[23] = 0x0000000000000001;
regs.gpr[24] = 0x0000000000000002;
regs.gpr[25] = (s64)0xFFFFFFFF905F4718;
regs.gpr[29] = (s64)0xFFFFFFFFA4001FF0;
regs.gpr[31] = (s64)0xFFFFFFFFA4001550;
regs.lo = (s64)0xFFFFFFFFBA1A7D4B;
regs.hi = (s64)0xFFFFFFFF997EC317;
break;
case CIC_NUS_7102:
mem.Write32<false>(regs, 0x318, RDRAM_SIZE, regs.pc);
regs.gpr[1] = 0x0000000000000001;
regs.gpr[2] = 0x000000001E324416;
regs.gpr[3] = 0x000000001E324416;
regs.gpr[4] = 0x0000000000004416;
regs.gpr[5] = 0x000000000EC5D9AF;
regs.gpr[6] = (s64)0xFFFFFFFFA4001F0C;
regs.gpr[7] = (s64)0xFFFFFFFFA4001F08;
regs.gpr[8] = 0x00000000000000C0;
regs.gpr[10] = 0x0000000000000040;
regs.gpr[11] = (s64)0xFFFFFFFFA4000040;
regs.gpr[12] = 0x00000000495D3D7B;
regs.gpr[13] = (s64)0xFFFFFFFF8B3DFA1E;
regs.gpr[14] = 0x000000004798E4D4;
regs.gpr[15] = (s64)0xFFFFFFFFF1D30682;
regs.gpr[22] = 0x000000000000003F;
regs.gpr[23] = 0x0000000000000007;
regs.gpr[25] = 0x0000000013D05CAB;
regs.gpr[29] = (s64)0xFFFFFFFFA4001FF0;
regs.gpr[31] = (s64)0xFFFFFFFFA4001554;
regs.lo = (s64)0xFFFFFFFFF1D30682;
regs.hi = 0x0000000010054A98;
break;
case CIC_NUS_6102_7101:
mem.Write32<false>(regs, 0x318, RDRAM_SIZE, regs.pc);
regs.gpr[1] = 0x0000000000000001;
regs.gpr[2] = 0x000000000EBDA536;
regs.gpr[3] = 0x000000000EBDA536;
regs.gpr[4] = 0x000000000000A536;
regs.gpr[5] = (s64)0xFFFFFFFFC0F1D859;
regs.gpr[6] = (s64)0xFFFFFFFFA4001F0C;
regs.gpr[7] = (s64)0xFFFFFFFFA4001F08;
regs.gpr[8] = 0x00000000000000C0;
regs.gpr[10] = 0x0000000000000040;
regs.gpr[11] = (s64)0xFFFFFFFFA4000040;
regs.gpr[12] = (s64)0xFFFFFFFFED10D0B3;
regs.gpr[13] = 0x000000001402A4CC;
regs.gpr[14] = 0x000000002DE108EA;
regs.gpr[15] = 0x000000003103E121;
regs.gpr[20] = 0x0000000000000001;
regs.gpr[25] = (s64)0xFFFFFFFF9DEBB54F;
regs.gpr[29] = (s64)0xFFFFFFFFA4001FF0;
regs.gpr[31] = (s64)0xFFFFFFFFA4001550;
regs.hi = 0x000000003FC18657;
regs.lo = 0x000000003103E121;
if (pal) {
regs.gpr[20] = 0x0000000000000000;
regs.gpr[23] = 0x0000000000000006;
regs.gpr[31] = (s64)0xFFFFFFFFA4001554;
}
break;
case CIC_NUS_6103_7103:
mem.Write32<false>(regs, 0x318, RDRAM_SIZE, regs.pc);
regs.gpr[0] = 0x0000000000000000;
regs.gpr[1] = 0x0000000000000001;
regs.gpr[2] = 0x0000000049A5EE96;
regs.gpr[3] = 0x0000000049A5EE96;
regs.gpr[4] = 0x000000000000EE96;
regs.gpr[5] = (s64)0xFFFFFFFFD4646273;
regs.gpr[6] = (s64)0xFFFFFFFFA4001F0C;
regs.gpr[7] = (s64)0xFFFFFFFFA4001F08;
regs.gpr[8] = 0x00000000000000C0;
regs.gpr[9] = 0x0000000000000000;
regs.gpr[10] = 0x0000000000000040;
regs.gpr[11] = (s64)0xFFFFFFFFA4000040;
regs.gpr[12] = (s64)0xFFFFFFFFCE9DFBF7;
regs.gpr[13] = (s64)0xFFFFFFFFCE9DFBF7;
regs.gpr[14] = 0x000000001AF99984;
regs.gpr[15] = 0x0000000018B63D28;
regs.gpr[16] = 0x0000000000000000;
regs.gpr[17] = 0x0000000000000000;
regs.gpr[18] = 0x0000000000000000;
regs.gpr[19] = 0x0000000000000000;
regs.gpr[20] = 0x0000000000000001;
regs.gpr[21] = 0x0000000000000000;
regs.gpr[23] = 0x0000000000000000;
regs.gpr[24] = 0x0000000000000000;
regs.gpr[25] = (s64)0xFFFFFFFF825B21C9;
regs.gpr[26] = 0x0000000000000000;
regs.gpr[27] = 0x0000000000000000;
regs.gpr[28] = 0x0000000000000000;
regs.gpr[29] = (s64)0xFFFFFFFFA4001FF0;
regs.gpr[30] = 0x0000000000000000;
regs.gpr[31] = (s64)0xFFFFFFFFA4001550;
regs.lo = 0x0000000018B63D28;
regs.hi = 0x00000000625C2BBE;
if (pal) {
regs.gpr[20] = 0x0000000000000000;
regs.gpr[23] = 0x0000000000000006;
regs.gpr[31] = (s64)0xFFFFFFFFA4001554;
}
break;
case CIC_NUS_6105_7105:
mem.Write32<false>(regs, 0x3F0, RDRAM_SIZE, regs.pc);
regs.gpr[2] = (s64)0xFFFFFFFFF58B0FBF;
regs.gpr[3] = (s64)0xFFFFFFFFF58B0FBF;
regs.gpr[4] = 0x0000000000000FBF;
regs.gpr[5] = (s64)0xFFFFFFFFDECAAAD1;
regs.gpr[6] = (s64)0xFFFFFFFFA4001F0C;
regs.gpr[7] = (s64)0xFFFFFFFFA4001F08;
regs.gpr[8] = 0x00000000000000C0;
regs.gpr[10] = 0x0000000000000040;
regs.gpr[11] = (s64)0xFFFFFFFFA4000040;
regs.gpr[12] = (s64)0xFFFFFFFF9651F81E;
regs.gpr[13] = 0x000000002D42AAC5;
regs.gpr[14] = 0x00000000489B52CF;
regs.gpr[15] = 0x0000000056584D60;
regs.gpr[20] = 0x0000000000000001;
regs.gpr[24] = 0x0000000000000002;
regs.gpr[25] = (s64)0xFFFFFFFFCDCE565F;
regs.gpr[29] = (s64)0xFFFFFFFFA4001FF0;
regs.gpr[31] = (s64)0xFFFFFFFFA4001550;
regs.lo = 0x0000000056584D60;
regs.hi = 0x000000004BE35D1F;
if (pal) {
regs.gpr[20] = 0x0000000000000000;
regs.gpr[23] = 0x0000000000000006;
regs.gpr[31] = (s64)0xFFFFFFFFA4001554;
}
mem.Write32<false>(regs, 0x04001000, 0x3C0DBFC0, regs.pc);
mem.Write32<false>(regs, 0x04001004, 0x8DA807FC, regs.pc);
mem.Write32<false>(regs, 0x04001008, 0x25AD07C0, regs.pc);
mem.Write32<false>(regs, 0x0400100C, 0x31080080, regs.pc);
mem.Write32<false>(regs, 0x04001000, 0x5500FFFC, regs.pc);
mem.Write32<false>(regs, 0x04001004, 0x3C0DBFC0, regs.pc);
mem.Write32<false>(regs, 0x04001008, 0x8DA80024, regs.pc);
mem.Write32<false>(regs, 0x0400100C, 0x3C0BB000, regs.pc);
break;
case CIC_NUS_6106_7106:
regs.gpr[2] = (s64)0xFFFFFFFFA95930A4;
regs.gpr[3] = (s64)0xFFFFFFFFA95930A4;
regs.gpr[4] = 0x00000000000030A4;
regs.gpr[5] = (s64)0xFFFFFFFFB04DC903;
regs.gpr[6] = (s64)0xFFFFFFFFA4001F0C;
regs.gpr[7] = (s64)0xFFFFFFFFA4001F08;
regs.gpr[8] = 0x00000000000000C0;
regs.gpr[10] = 0x0000000000000040;
regs.gpr[11] = (s64)0xFFFFFFFFA4000040;
regs.gpr[12] = (s64)0xFFFFFFFFBCB59510;
regs.gpr[13] = (s64)0xFFFFFFFFBCB59510;
regs.gpr[14] = 0x000000000CF85C13;
regs.gpr[15] = 0x000000007A3C07F4;
regs.gpr[20] = 0x0000000000000001;
regs.gpr[24] = 0x0000000000000002;
regs.gpr[25] = 0x00000000465E3F72;
regs.gpr[29] = (s64)0xFFFFFFFFA4001FF0;
regs.gpr[30] = 0x0000000000000000;
regs.gpr[31] = (s64)0xFFFFFFFFA4001550;
regs.lo = 0x000000007A3C07F4;
regs.hi = 0x0000000023953898;
if (pal) {
regs.gpr[20] = 0x0000000000000000;
regs.gpr[23] = 0x0000000000000006;
regs.gpr[31] = (s64)0xFFFFFFFFA4001554;
}
break;
}
regs.gpr[22] = (cicSeeds[cicType] >> 8) & 0xFF;
regs.cop0.Reset();
}
}

View File

@@ -0,0 +1,66 @@
#pragma once
#include <common.hpp>
namespace n64 {
struct Controller {
union {
u8 byte1;
struct {
bool dp_right:1;
bool dp_left:1;
bool dp_down:1;
bool dp_up:1;
bool start:1;
bool z:1;
bool b:1;
bool a:1;
};
};
union {
u8 byte2;
struct {
bool c_right:1;
bool c_left:1;
bool c_down:1;
bool c_up:1;
bool r:1;
bool l:1;
bool zero:1;
bool joy_reset:1;
};
};
s8 joy_x;
s8 joy_y;
};
static_assert(sizeof(Controller) == 4);
struct Mem;
struct Registers;
const u32 cicSeeds[] = {
0x0,
0x00043F3F, // CIC_NUS_6101
0x00043F3F, // CIC_NUS_7102
0x00003F3F, // CIC_NUS_6102_7101
0x0000783F, // CIC_NUS_6103_7103
0x0000913F, // CIC_NUS_6105_7105
0x0000853F, // CIC_NUS_6106_7106
};
enum CICType {
UNKNOWN_CIC_TYPE,
CIC_NUS_6101,
CIC_NUS_7102,
CIC_NUS_6102_7101,
CIC_NUS_6103_7103,
CIC_NUS_6105_7105,
CIC_NUS_6106_7106
};
struct CartInfo;
void ProcessPIFCommands(u8*, Controller&, Mem&);
void DoPIFHLE(Mem& mem, Registers& regs, CartInfo cartInfo);
}

View File

@@ -0,0 +1,37 @@
#include <core/mmio/RI.hpp>
#include <log.hpp>
namespace n64 {
RI::RI() {
Reset();
}
void RI::Reset() {
mode = 0xE;
config = 0x40;
select = 0x14;
refresh = 0x63634;
}
auto RI::Read(u32 addr) const -> u32 {
switch(addr) {
case 0x04700000: return mode;
case 0x04700004: return config;
case 0x0470000C: return select;
case 0x04700010: return refresh;
default:
util::panic("Unhandled RI[{:08X}] read\n", addr);
}
}
void RI::Write(u32 addr, u32 val) {
switch(addr) {
case 0x04700000: mode = val; break;
case 0x04700004: config = val; break;
case 0x0470000C: select = val; break;
case 0x04700010: refresh = val; break;
default:
util::panic("Unhandled RI[{:08X}] write with val {:08X}\n", addr, val);
}
}
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include <common.hpp>
namespace n64 {
struct RI {
RI();
void Reset();
auto Read(u32) const -> u32;
void Write(u32, u32);
u32 mode{0xE}, config{0x40}, select{0x14}, refresh{0x63634};
};
}

View File

@@ -0,0 +1,79 @@
#include <core/mmio/SI.hpp>
#include <core/Mem.hpp>
#include <Scheduler.hpp>
namespace n64 {
SI::SI() {
Reset();
}
void SI::Reset() {
status.raw = 0;
dramAddr = 0;
pifAddr = 0;
memset(&controller, 0, sizeof(Controller));
}
auto SI::Read(MI& mi, u32 addr) const -> u32 {
switch(addr) {
case 0x04800000: return dramAddr;
case 0x04800004: case 0x04800010: return pifAddr;
case 0x0480000C: return 0;
case 0x04800018: {
u32 val = 0;
val |= status.dmaBusy;
val |= (0 << 1);
val |= (0 << 3);
val |= (mi.miIntr.si << 12);
return val;
}
default:
util::panic("Unhandled SI[{:08X}] read\n", addr);
}
}
void DMA(Mem& mem, Registers& regs) {
MMIO& mmio = mem.mmio;
SI& si = mmio.si;
si.status.dmaBusy = false;
if(si.toDram) {
ProcessPIFCommands(mem.pifRam, si.controller, mem);
for(int i = 0; i < 64; i++) {
mem.mmio.rdp.rdram[BYTE_ADDRESS(si.dramAddr + i)] = mem.pifRam[i];
}
} else {
for(int i = 0; i < 64; i++) {
mem.pifRam[i] = mem.mmio.rdp.rdram[BYTE_ADDRESS(si.dramAddr + i)];
}
util::debug("SI DMA from PIF RAM to RDRAM ({:08X} to {:08X})\n", si.pifAddr, si.dramAddr);
ProcessPIFCommands(mem.pifRam, si.controller, mem);
}
InterruptRaise(mem.mmio.mi, regs, Interrupt::SI);
}
void SI::Write(Mem& mem, Registers& regs, u32 addr, u32 val) {
switch(addr) {
case 0x04800000:
dramAddr = val & RDRAM_DSIZE;
break;
case 0x04800004: {
pifAddr = val & 0x1FFFFFFF;
status.dmaBusy = true;
toDram = true;
scheduler.enqueueRelative({SI_DMA_DELAY, DMA});
} break;
case 0x04800010: {
pifAddr = val & 0x1FFFFFFF;
status.dmaBusy = true;
toDram = false;
scheduler.enqueueRelative({SI_DMA_DELAY, DMA});
util::debug("SI DMA from RDRAM to PIF RAM ({:08X} to {:08X})\n", dramAddr, val & 0x1FFFFFFF);
} break;
case 0x04800018:
InterruptLower(mem.mmio.mi, regs, Interrupt::SI);
break;
default:
util::panic("Unhandled SI[%08X] write (%08X)\n", addr, val);
}
}
}

View File

@@ -0,0 +1,38 @@
#pragma once
#include <common.hpp>
#include <core/mmio/Interrupt.hpp>
#include <core/mmio/MI.hpp>
#include <core/mmio/PIF.hpp>
namespace n64 {
union SIStatus {
u32 raw{};
struct {
unsigned dmaBusy:1;
unsigned ioBusy:1;
unsigned reserved:1;
unsigned dmaErr:1;
unsigned:8;
unsigned intr:1;
};
};
struct Mem;
struct SI {
SI();
void Reset();
SIStatus status{};
u32 dramAddr{};
u32 pifAddr{};
Controller controller{};
bool toDram = false;
auto Read(MI&, u32) const -> u32;
void Write(Mem&, Registers&, u32, u32);
};
static void DMA(Mem& mem, Registers& regs);
}

View File

@@ -0,0 +1,87 @@
#include <core/mmio/VI.hpp>
#include <log.hpp>
#include <core/registers/Registers.hpp>
#include <core/mmio/MI.hpp>
#include <core/mmio/Interrupt.hpp>
namespace n64 {
VI::VI () {
Reset();
}
void VI::Reset() {
status.raw = 0xF;
intr = 256;
origin = 0;
width = 320;
current = 0;
vsync = 0;
hsync = 0;
numHalflines = 262;
numFields = 1;
cyclesPerHalfline = 1000;
}
u32 VI::Read(u32 paddr) const {
switch(paddr) {
case 0x04400000: return status.raw;
case 0x04400004: return origin;
case 0x04400008: return width;
case 0x0440000C: return intr;
case 0x04400010: return current << 1;
case 0x04400014: return burst.raw;
case 0x04400018: return vsync;
case 0x0440001C: return hsync;
case 0x04400020: return hsyncLeap.raw;
case 0x04400024: return hstart.raw;
case 0x04400028: return vstart.raw;
case 0x0440002C: return vburst;
case 0x04400030: return xscale.raw;
case 0x04400034: return yscale.raw;
default:
util::panic("Unimplemented VI[%08X] read\n", paddr);
}
}
void VI::Write(MI& mi, Registers& regs, u32 paddr, u32 val) {
switch(paddr) {
case 0x04400000:
status.raw = val;
numFields = status.serrate ? 2 : 1;
break;
case 0x04400004: {
u32 masked = val & 0xFFFFFF;
if(origin != masked) {
swaps++;
}
origin = masked;
} break;
case 0x04400008: {
width = val & 0x7FF;
} break;
case 0x0440000C: {
intr = val & 0x3FF;
} break;
case 0x04400010:
InterruptLower(mi, regs, Interrupt::VI);
break;
case 0x04400014: burst.raw = val; break;
case 0x04400018: {
vsync = val & 0x3FF;
numHalflines = vsync >> 1;
cyclesPerHalfline = N64_CYCLES_PER_FRAME / numHalflines;
} break;
case 0x0440001C: {
hsync = val & 0x3FF;
} break;
case 0x04400020: hsyncLeap.raw = val; break;
case 0x04400024: hstart.raw = val; break;
case 0x04400028: vstart.raw = val; break;
case 0x0440002C: vburst = val; break;
case 0x04400030: xscale.raw = val; break;
case 0x04400034: yscale.raw = val; break;
default:
util::panic("Unimplemented VI[%08X] write (%08X)\n", paddr, val);
}
}
}

View File

@@ -0,0 +1,106 @@
#pragma once
#include <common.hpp>
namespace n64 {
union VIBurst {
/*struct {
unsigned hsyncW:8;
unsigned burstW:8;
unsigned vsyncW:4;
unsigned burstStart:10;
unsigned:2;
};*/
u32 raw;
};
union VIHsyncLeap {
/*struct {
unsigned leapB:10;
unsigned:6;
unsigned leapA:10;
unsigned:6;
};*/
u32 raw;
} ;
union VIVideo {
struct {
unsigned end:10;
unsigned:6;
unsigned start:10;
unsigned:6;
};
u32 raw;
};
union AxisScale {
u32 raw;
struct {
unsigned scaleDecimal:10;
unsigned scaleInteger:2;
unsigned subpixelOffsetDecimal:10;
unsigned subpixelOffsetInteger:2;
unsigned:4;
};
struct {
unsigned scale:12;
unsigned subpixelOffset:12;
unsigned:4;
};
};
enum VIFormat {
blank = 0,
reserved = 1,
f5553 = 2,
f8888 = 3
};
union VIStatus {
struct {
u8 type:2;
bool gamma_dither_enable:1;
bool gamma_enable:1;
bool divot_enable:1;
bool reserved_always_off:1;
bool serrate:1;
bool reserved_diagnostics_only:1;
unsigned antialias_mode:3;
unsigned:21;
};
u32 raw;
};
union AxisStart {
u32 raw;
struct {
unsigned end:10;
unsigned:6;
unsigned start:10;
unsigned:6;
};
};
struct MI;
struct Registers;
struct VI {
VI();
void Reset();
[[nodiscard]] u32 Read(u32) const;
void Write(MI&, Registers&, u32, u32);
AxisScale xscale{}, yscale{};
VIHsyncLeap hsyncLeap{};
VIStatus status{};
VIBurst burst{};
u32 vburst{};
u32 origin, width, current;
u32 vsync, hsync, intr;
AxisStart hstart{}, vstart{};
int swaps{};
int numHalflines;
int numFields;
int cyclesPerHalfline;
};
} // backend