Lay down initial PI bus latch implementation

This commit is contained in:
SimoneN64
2023-11-06 14:34:17 +01:00
parent e75ea2beb7
commit 4331981d34
12 changed files with 252 additions and 113 deletions

View File

@@ -2,6 +2,18 @@
#include <Scheduler.hpp> #include <Scheduler.hpp>
namespace n64 { namespace n64 {
u32 extraCycles = 0;
void CpuStall(u32 cycles) {
extraCycles += cycles;
}
u32 PopStalledCycles() {
u32 ret = extraCycles;
extraCycles = 0;
return ret;
}
Core::Core() { Core::Core() {
if(SDL_GameControllerAddMappingsFromFile("resources/gamecontrollerdb.txt") < 0) { if(SDL_GameControllerAddMappingsFromFile("resources/gamecontrollerdb.txt") < 0) {
Util::warn("Failed to load game controller DB"); Util::warn("Failed to load game controller DB");
@@ -46,8 +58,9 @@ void Core::Run(float volumeL, float volumeR) {
MMIO& mmio = mem.mmio; MMIO& mmio = mem.mmio;
Registers& regs = cpu->regs; Registers& regs = cpu->regs;
Event event;
for (int field = 0; field < mmio.vi.numFields; field++) { for (int field = 0; field < mmio.vi.numFields; field++) {
int frameCycles = 0; u32 frameCycles = 0;
for (int i = 0; i < mmio.vi.numHalflines; i++) { for (int i = 0; i < mmio.vi.numHalflines; i++) {
mmio.vi.current = (i << 1) + field; mmio.vi.current = (i << 1) + field;
@@ -56,8 +69,9 @@ void Core::Run(float volumeL, float volumeR) {
} }
for(; cycles < mem.mmio.vi.cyclesPerHalfline; cycles++, frameCycles++) { for(; cycles < mem.mmio.vi.cyclesPerHalfline; cycles++, frameCycles++) {
int taken = cpu->Step(); u32 taken = cpu->Step();
static int cpuSteps = 0; taken += PopStalledCycles();
static u32 cpuSteps = 0;
cpuSteps += taken; cpuSteps += taken;
if(mmio.rsp.spStatus.halt) { if(mmio.rsp.spStatus.halt) {
cpuSteps = 0; cpuSteps = 0;

View File

@@ -6,6 +6,7 @@
#include <SDL2/SDL_timer.h> #include <SDL2/SDL_timer.h>
struct Window; struct Window;
struct Event;
namespace n64 { namespace n64 {
struct Core { struct Core {
@@ -17,13 +18,14 @@ struct Core {
void Serialize(); void Serialize();
void Deserialize(); void Deserialize();
void TogglePause() { pause = !pause; } void TogglePause() { pause = !pause; }
void HandleEvents(Event*);
[[nodiscard]] VI& GetVI() { return cpu->mem.mmio.vi; } [[nodiscard]] VI& GetVI() { return cpu->mem.mmio.vi; }
u32 breakpoint = 0; u32 breakpoint = 0;
bool pause = true; bool pause = true;
bool render = true; bool render = true;
int cycles = 0; u32 cycles = 0;
bool romLoaded = false; bool romLoaded = false;
std::string rom; std::string rom;
std::unique_ptr<BaseCPU> cpu; std::unique_ptr<BaseCPU> cpu;
@@ -31,4 +33,8 @@ struct Core {
int memSize, cpuSize, verSize; int memSize, cpuSize, verSize;
int slot = 0; int slot = 0;
}; };
extern u32 extraCycles;
void CpuStall(u32 cycles);
u32 PopStalledCycles();
} }

View File

@@ -51,7 +51,7 @@
#define PI_REGION 0x04600000 ... 0x046FFFFF #define PI_REGION 0x04600000 ... 0x046FFFFF
#define RI_REGION 0x04700000 ... 0x047FFFFF #define RI_REGION 0x04700000 ... 0x047FFFFF
#define SI_REGION 0x04800000 ... 0x048FFFFF #define SI_REGION 0x04800000 ... 0x048FFFFF
#define REGION_CART CART_REGION_START_2_1 ... CART_REGION_END_1_2 #define REGION_CART CART_REGION_START_2_1 ... CART_REGION_END_1_2
#define PIF_ROM_REGION PIF_ROM_REGION_START ... PIF_ROM_REGION_END #define PIF_ROM_REGION PIF_ROM_REGION_START ... PIF_ROM_REGION_END
#define PIF_RAM_REGION PIF_RAM_REGION_START ... PIF_RAM_REGION_END #define PIF_RAM_REGION PIF_RAM_REGION_START ... PIF_RAM_REGION_END

View File

@@ -1,4 +1,60 @@
#include <Scheduler.hpp> #include <Scheduler.hpp>
#include <core/registers/Registers.hpp> #include <core/registers/Registers.hpp>
#include <core/Mem.hpp>
Scheduler scheduler; Scheduler scheduler;
void Scheduler::enqueueRelative(u64 t, const EventType type) {
enqueueAbsolute(t + ticks, type);
}
void Scheduler::enqueueAbsolute(u64 t, const EventType type) {
events.push({t, type});
}
u64 Scheduler::remove(EventType type) {
auto copy = events;
while(!copy.empty()) {
if(copy.top().type == type) {
u64 ret = copy.top().time - ticks;
copy.pop();
events.swap(copy);
return ret;
}
copy.pop();
}
return 0;
}
void Scheduler::tick(u64 t, n64::Mem& mem, n64::Registers& regs) {
ticks += t;
n64::MI& mi = mem.mmio.mi;
n64::SI& si = mem.mmio.si;
n64::PI& pi = mem.mmio.pi;
while(ticks >= events.top().time) {
switch(events.top().type) {
case SI_DMA:
si.status.dmaBusy = false;
si.DMA(mem, regs);
InterruptRaise(mi, regs, n64::Interrupt::SI);
break;
case PI_DMA_COMPLETE:
InterruptRaise(mi, regs, n64::Interrupt::PI);
pi.dmaBusy = false;
break;
case PI_BUS_WRITE_COMPLETE:
pi.ioBusy = false;
break;
case NONE:
break;
case IMPOSSIBLE:
Util::panic("Congratulations on keeping the emulator on for about 5 billion years, I guess, nerd.");
default:
Util::panic("Unknown scheduler event type");
}
events.pop();
}
}

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include <queue> #include <queue>
#include <array> #include <array>
#include <functional>
#include <log.hpp> #include <log.hpp>
namespace n64 { namespace n64 {
@@ -8,9 +9,17 @@ struct Mem;
struct Registers; struct Registers;
} }
enum EventType {
NONE,
PI_BUS_WRITE_COMPLETE,
PI_DMA_COMPLETE,
SI_DMA,
IMPOSSIBLE
};
struct Event { struct Event {
u64 time = 0; u64 time;
void(*handler)(n64::Mem&, n64::Registers&) = nullptr; EventType type;
friend bool operator<(const Event& rhs, const Event& lhs) { friend bool operator<(const Event& rhs, const Event& lhs) {
return rhs.time < lhs.time; return rhs.time < lhs.time;
@@ -27,26 +36,14 @@ struct Event {
struct Scheduler { struct Scheduler {
Scheduler() { Scheduler() {
enqueueAbsolute(Event{std::numeric_limits<u64>::max(), [](n64::Mem&, n64::Registers&) { enqueueAbsolute(std::numeric_limits<u64>::max(), IMPOSSIBLE);
Util::panic("How the fuck did we get here?!");
}});
} }
FORCE_INLINE void enqueueRelative(const Event& event) { void enqueueRelative(u64, const EventType);
enqueueAbsolute({event.time + ticks, event.handler}); void enqueueAbsolute(u64, const EventType);
} u64 remove(const EventType);
void tick(u64 t, n64::Mem&, n64::Registers&);
FORCE_INLINE void enqueueAbsolute(const Event& e) {
events.push(e);
}
FORCE_INLINE void tick(u64 t, n64::Mem& mem, n64::Registers& regs) {
ticks += t;
while(ticks >= events.top().time) {
events.top().handler(mem, regs);
events.pop();
}
}
std::priority_queue<Event, std::vector<Event>, std::greater<>> events; std::priority_queue<Event, std::vector<Event>, std::greater<>> events;
u64 ticks = 0; u64 ticks = 0;
u8 index = 0; u8 index = 0;

View File

@@ -363,11 +363,12 @@ void Mem::Write8(Registers& regs, u32 paddr, u32 val) {
Util::WriteAccess<u32>(mmio.rsp.dmem, mirrAddr, val); Util::WriteAccess<u32>(mmio.rsp.dmem, mirrAddr, val);
} }
} break; } break;
case MMIO_REGION:
Util::panic("MMIO Write8!");
case REGION_CART: case REGION_CART:
Util::debug("BusWrite8 @ {:08X} = {:02X}", paddr, val);
mmio.pi.BusWrite8(*this, paddr, val); mmio.pi.BusWrite8(*this, paddr, val);
break; break;
case MMIO_REGION:
Util::panic("MMIO Write8!");
case PIF_RAM_REGION: case PIF_RAM_REGION:
val = val << (8 * (3 - (paddr & 3))); val = val << (8 * (3 - (paddr & 3)));
paddr = (paddr - PIF_RAM_REGION_START) & ~3; paddr = (paddr - PIF_RAM_REGION_START) & ~3;
@@ -411,6 +412,10 @@ void Mem::Write16(Registers& regs, u32 paddr, u32 val) {
Util::WriteAccess<u32>(mmio.rsp.dmem, mirrAddr, val); Util::WriteAccess<u32>(mmio.rsp.dmem, mirrAddr, val);
} }
} break; } break;
case REGION_CART:
Util::debug("BusWrite8 @ {:08X} = {:04X}", paddr, val);
mmio.pi.BusWrite16(*this, paddr, val);
break;
case MMIO_REGION: case MMIO_REGION:
Util::panic("MMIO Write16!"); Util::panic("MMIO Write16!");
case PIF_RAM_REGION: case PIF_RAM_REGION:
@@ -419,9 +424,6 @@ void Mem::Write16(Registers& regs, u32 paddr, u32 val) {
Util::WriteAccess<u32>(si.pif.ram, paddr - PIF_RAM_REGION_START, htobe32(val)); Util::WriteAccess<u32>(si.pif.ram, paddr - PIF_RAM_REGION_START, htobe32(val));
si.pif.ProcessCommands(*this); si.pif.ProcessCommands(*this);
break; break;
case REGION_CART:
mmio.pi.BusWrite16(*this, paddr, val);
break;
case 0x00800000 ... 0x03FFFFFF: case 0x00800000 ... 0x03FFFFFF:
case 0x04200000 ... 0x042FFFFF: case 0x04200000 ... 0x042FFFFF:
case 0x04900000 ... 0x04FFFFFF: case 0x04900000 ... 0x04FFFFFF:
@@ -457,6 +459,10 @@ void Mem::Write32(Registers& regs, u32 paddr, u32 val) {
Util::WriteAccess<u32>(mmio.rsp.dmem, paddr & DMEM_DSIZE, val); Util::WriteAccess<u32>(mmio.rsp.dmem, paddr & DMEM_DSIZE, val);
} }
} break; } break;
case REGION_CART:
Util::debug("BusWrite8 @ {:08X} = {:08X}", paddr, val);
mmio.pi.BusWrite32(*this, paddr, val);
break;
case MMIO_REGION: case MMIO_REGION:
mmio.Write(*this, regs, paddr, val); mmio.Write(*this, regs, paddr, val);
break; break;
@@ -470,9 +476,6 @@ void Mem::Write32(Registers& regs, u32 paddr, u32 val) {
case PIF_ROM_REGION: case PIF_ROM_REGION:
case 0x1FC00800 ... 0x7FFFFFFF: case 0x1FC00800 ... 0x7FFFFFFF:
case 0x80000000 ... 0xFFFFFFFF: break; case 0x80000000 ... 0xFFFFFFFF: break;
case REGION_CART:
mmio.pi.BusWrite32(*this, paddr, val);
break;
default: Util::panic("Unimplemented 32-bit write at address {:08X} with value {:0X} (PC = {:016X})", paddr, val, (u64)regs.pc); default: Util::panic("Unimplemented 32-bit write at address {:08X} with value {:0X} (PC = {:016X})", paddr, val, (u64)regs.pc);
} }
} }
@@ -500,15 +503,16 @@ void Mem::Write64(Registers& regs, u32 paddr, u64 val) {
Util::WriteAccess<u32>(mmio.rsp.dmem, paddr & DMEM_DSIZE, val); Util::WriteAccess<u32>(mmio.rsp.dmem, paddr & DMEM_DSIZE, val);
} }
} break; } break;
case REGION_CART:
Util::debug("BusWrite8 @ {:08X} = {:016X}", paddr, val);
mmio.pi.BusWrite64(*this, paddr, val);
break;
case MMIO_REGION: case MMIO_REGION:
Util::panic("MMIO Write64!"); Util::panic("MMIO Write64!");
case PIF_RAM_REGION: case PIF_RAM_REGION:
Util::WriteAccess<u64>(si.pif.ram, paddr - PIF_RAM_REGION_START, htobe64(val)); Util::WriteAccess<u64>(si.pif.ram, paddr - PIF_RAM_REGION_START, htobe64(val));
si.pif.ProcessCommands(*this); si.pif.ProcessCommands(*this);
break; break;
case REGION_CART:
mmio.pi.BusWrite64(*this, paddr, val);
break;
case 0x00800000 ... 0x03FFFFFF: case 0x00800000 ... 0x03FFFFFF:
case 0x04200000 ... 0x042FFFFF: case 0x04200000 ... 0x042FFFFF:
case 0x04900000 ... 0x04FFFFFF: case 0x04900000 ... 0x04FFFFFF:

View File

@@ -73,7 +73,7 @@ void AI::Write(Mem& mem, Registers& regs, u32 addr, u32 val) {
} }
} }
void AI::Step(Mem& mem, Registers& regs, int cpuCycles, float volumeL, float volumeR) { void AI::Step(Mem& mem, Registers& regs, u32 cpuCycles, float volumeL, float volumeR) {
cycles += cpuCycles; cycles += cpuCycles;
while(cycles > dac.period) { while(cycles > dac.period) {
if (dmaCount == 0) { if (dmaCount == 0) {

View File

@@ -11,7 +11,7 @@ struct AI {
void Reset(); void Reset();
auto Read(u32) const -> u32; auto Read(u32) const -> u32;
void Write(Mem&, Registers&, u32, u32); void Write(Mem&, Registers&, u32, u32);
void Step(Mem&, Registers&, int, float, float); void Step(Mem&, Registers&, u32, float, float);
bool dmaEnable{}; bool dmaEnable{};
u16 dacRate{}; u16 dacRate{};
u8 bitrate{}; u8 bitrate{};
@@ -19,7 +19,7 @@ struct AI {
u32 dmaLen[2]{}; u32 dmaLen[2]{};
u32 dmaAddr[2]{}; u32 dmaAddr[2]{};
bool dmaAddrCarry{}; bool dmaAddrCarry{};
int cycles{}; u32 cycles{};
struct { struct {
u32 freq{44100}; u32 freq{44100};

View File

@@ -1,8 +1,7 @@
#include <core/mmio/PI.hpp> #include <core/mmio/PI.hpp>
#include <log.hpp> #include <log.hpp>
#include <core/Mem.hpp> #include <Core.hpp>
#include <core/registers/Registers.hpp> #include <Scheduler.hpp>
#include "Scheduler.hpp"
namespace n64 { namespace n64 {
PI::PI() { PI::PI() {
@@ -10,23 +9,59 @@ PI::PI() {
} }
void PI::Reset() { void PI::Reset() {
dmaBusy = false;
ioBusy = false;
latch = 0;
dramAddr = 0; dramAddr = 0;
cartAddr = 0; cartAddr = 0;
dramAddrInternal = 0;
cartAddrInternal = 0;
rdLen = 0; rdLen = 0;
wrLen = 0; wrLen = 0;
pi_bsd_dom1_lat = 0;
pi_bsd_dom2_lat = 0;
pi_bsd_dom1_pwd = 0;
pi_bsd_dom2_pwd = 0;
pi_bsd_dom1_pgs = 0;
pi_bsd_dom2_pgs = 0;
pi_bsd_dom1_rls = 0;
pi_bsd_dom2_rls = 0;
} }
auto PI::BusRead8(Mem& mem, u32 addr) const -> u8 { bool PI::WriteLatch(u32 value) {
// TODO: Latch with CPU stall if (ioBusy) {
return false;
} else {
ioBusy = true;
latch = value;
scheduler.enqueueRelative(100, PI_BUS_WRITE_COMPLETE);
return true;
}
}
bool PI::ReadLatch() {
if (ioBusy) [[unlikely]] {
ioBusy = false;
CpuStall(scheduler.remove(PI_BUS_WRITE_COMPLETE));
return false;
}
return true;
}
auto PI::BusRead8(Mem& mem, u32 addr) -> u8 {
if (!ReadLatch()) [[unlikely]] {
return latch >> 24;
}
switch (addr) { switch (addr) {
case REGION_PI_UNKNOWN: case REGION_PI_UNKNOWN:
Util::warn("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, returning FF because it is not emulated", addr); //Util::warn("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, returning FF because it is not emulated", addr);
return 0xFF; return 0xFF;
case REGION_PI_64DD_REG: case REGION_PI_64DD_REG:
Util::warn("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG - This is the N64DD, returning FF because it is not emulated", addr); //Util::warn("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG - This is the N64DD, returning FF because it is not emulated", addr);
return 0xFF; return 0xFF;
case REGION_PI_64DD_ROM: case REGION_PI_64DD_ROM:
Util::warn("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM - This is the N64DD, returning FF because it is not emulated", addr); //Util::warn("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM - This is the N64DD, returning FF because it is not emulated", addr);
return 0xFF; return 0xFF;
case REGION_PI_SRAM: case REGION_PI_SRAM:
return mem.BackupRead8(addr - SREGION_PI_SRAM); return mem.BackupRead8(addr - SREGION_PI_SRAM);
@@ -35,7 +70,7 @@ auto PI::BusRead8(Mem& mem, u32 addr) const -> u8 {
addr = (addr + 2) & ~2; addr = (addr + 2) & ~2;
u32 index = BYTE_ADDRESS(addr) - SREGION_PI_ROM; u32 index = BYTE_ADDRESS(addr) - SREGION_PI_ROM;
if (index > mem.rom.size) { if (index > mem.rom.size) {
Util::warn("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM! ({}/0x{:016X})", addr, index, index, mem.rom.size, mem.rom.size); //Util::warn("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM! ({}/0x{:016X})", addr, index, index, mem.rom.size, mem.rom.size);
return 0xFF; return 0xFF;
} }
return mem.rom.cart[index]; return mem.rom.cart[index];
@@ -46,51 +81,59 @@ auto PI::BusRead8(Mem& mem, u32 addr) const -> u8 {
} }
void PI::BusWrite8(Mem& mem, u32 addr, u32 val) { void PI::BusWrite8(Mem& mem, u32 addr, u32 val) {
int latch_shift = 24 - (addr & 1) * 8;
if (!WriteLatch(val << latch_shift) && addr != 0x05000020) [[unlikely]] {
return;
}
switch (addr) { switch (addr) {
case REGION_PI_UNKNOWN: case REGION_PI_UNKNOWN:
Util::warn("Writing byte 0x{:02X} to address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", val, addr); //Util::warn("Writing byte 0x{:02X} to address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", val, addr);
return; return;
case REGION_PI_64DD_REG: case REGION_PI_64DD_REG:
if (addr == 0x05000020) { if (addr == 0x05000020) {
fprintf(stderr, "%c", val); fprintf(stderr, "%c", val);
} else { } else {
Util::warn("Writing byte 0x{:02X} to address 0x{:08X} in region: REGION_PI_64DD_ROM, this is the 64DD, ignoring!", val, addr); //Util::warn("Writing byte 0x{:02X} to address 0x{:08X} in region: REGION_PI_64DD_ROM, this is the 64DD, ignoring!", val, addr);
} }
return; return;
case REGION_PI_64DD_ROM: case REGION_PI_64DD_ROM:
Util::warn("Writing byte 0x{:02X} to address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", val, addr); //Util::warn("Writing byte 0x{:02X} to address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", val, addr);
return; return;
case REGION_PI_SRAM: case REGION_PI_SRAM:
mem.BackupWrite8(addr - SREGION_PI_SRAM, val); mem.BackupWrite8(addr - SREGION_PI_SRAM, val);
return; return;
case REGION_PI_ROM: case REGION_PI_ROM:
Util::warn("Writing byte 0x{:02X} to address 0x{:08X} in unsupported region: REGION_PI_ROM", val, addr); //Util::warn("Writing byte 0x{:02X} to address 0x{:08X} in unsupported region: REGION_PI_ROM", val, addr);
return; return;
default: default:
Util::panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr); Util::panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr);
} }
} }
auto PI::BusRead16(Mem& mem, u32 addr) const -> u16 { auto PI::BusRead16(Mem& mem, u32 addr) -> u16 {
// TODO: Latch with CPU stall if (!ReadLatch()) [[unlikely]] {
return latch >> 16;
}
switch (addr) { switch (addr) {
case REGION_PI_UNKNOWN: case REGION_PI_UNKNOWN:
Util::warn("Reading half from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, returning FF because it is not emulated", addr); //Util::warn("Reading half from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, returning FF because it is not emulated", addr);
return 0xFF; return 0xFF;
case REGION_PI_64DD_REG: case REGION_PI_64DD_REG:
Util::warn("Reading half from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG - This is the N64DD, returning FF because it is not emulated", addr); //Util::warn("Reading half from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG - This is the N64DD, returning FF because it is not emulated", addr);
return 0xFF; return 0xFF;
case REGION_PI_64DD_ROM: case REGION_PI_64DD_ROM:
Util::warn("Reading half from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM - This is the N64DD, returning FF because it is not emulated", addr); //Util::warn("Reading half from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM - This is the N64DD, returning FF because it is not emulated", addr);
return 0xFF; return 0xFF;
case REGION_PI_SRAM: case REGION_PI_SRAM:
Util::panic("Reading half from address 0x{:08X} in unsupported region: REGION_PI_SRAM", addr); Util::panic("Reading half from address 0x{:08X} in unsupported region: REGION_PI_SRAM", addr);
case REGION_PI_ROM: { case REGION_PI_ROM: {
// round to nearest 4 byte boundary, keeping old LSB
addr = (addr + 2) & ~3; addr = (addr + 2) & ~3;
u32 index = HALF_ADDRESS(addr) - SREGION_PI_ROM; u32 index = HALF_ADDRESS(addr) - SREGION_PI_ROM;
if (index > mem.rom.size - 1) { if (index > mem.rom.size - 1) {
Util::warn("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM! ({}/0x{:016X})", addr, index, index, mem.rom.size, mem.rom.size); //Util::warn("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM! ({}/0x{:016X})", addr, index, index, mem.rom.size, mem.rom.size);
return 0xFFFF; return 0xFFFF;
} }
return Util::ReadAccess<u16>(mem.rom.cart, index); return Util::ReadAccess<u16>(mem.rom.cart, index);
@@ -101,38 +144,45 @@ auto PI::BusRead16(Mem& mem, u32 addr) const -> u16 {
} }
void PI::BusWrite16(Mem& mem, u32 addr, u16 val) { void PI::BusWrite16(Mem& mem, u32 addr, u16 val) {
if (!WriteLatch(val << 16)) [[unlikely]] {
return;
}
switch (addr) { switch (addr) {
case REGION_PI_UNKNOWN: case REGION_PI_UNKNOWN:
Util::warn("Writing half 0x{:04X} to address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", val, addr); //Util::warn("Writing half 0x{:04X} to address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", val, addr);
return; return;
case REGION_PI_64DD_REG: case REGION_PI_64DD_REG:
Util::warn("Writing half 0x{:04X} to address 0x{:08X} in region: REGION_PI_64DD_ROM, this is the 64DD, ignoring!", val, addr); //Util::warn("Writing half 0x{:04X} to address 0x{:08X} in region: REGION_PI_64DD_ROM, this is the 64DD, ignoring!", val, addr);
return; return;
case REGION_PI_64DD_ROM: case REGION_PI_64DD_ROM:
Util::warn("Writing half 0x{:04X} to address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", val, addr); //Util::warn("Writing half 0x{:04X} to address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", val, addr);
return; return;
case REGION_PI_SRAM: case REGION_PI_SRAM:
Util::warn("Writing half 0x{:04X} to address 0x{:08X} in unsupported region: REGION_PI_SRAM", val, addr); //Util::warn("Writing half 0x{:04X} to address 0x{:08X} in unsupported region: REGION_PI_SRAM", val, addr);
return; return;
case REGION_PI_ROM: case REGION_PI_ROM:
Util::warn("Writing half 0x{:04X} to address 0x{:08X} in unsupported region: REGION_PI_ROM", val, addr); //Util::warn("Writing half 0x{:04X} to address 0x{:08X} in unsupported region: REGION_PI_ROM", val, addr);
return; return;
default: default:
Util::panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr); Util::panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr);
} }
} }
auto PI::BusRead32(Mem& mem, u32 addr) const -> u32 { auto PI::BusRead32(Mem& mem, u32 addr) -> u32 {
// TODO: Latch with CPU stall if (!ReadLatch()) [[unlikely]] {
return latch;
}
switch (addr) { switch (addr) {
case REGION_PI_UNKNOWN: case REGION_PI_UNKNOWN:
Util::warn("Reading word from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, returning FF because it is not emulated", addr); //Util::warn("Reading word from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, returning FF because it is not emulated", addr);
return 0xFF; return 0xFF;
case REGION_PI_64DD_REG: case REGION_PI_64DD_REG:
Util::warn("Reading word from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG - This is the N64DD, returning FF because it is not emulated", addr); //Util::warn("Reading word from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG - This is the N64DD, returning FF because it is not emulated", addr);
return 0xFF; return 0xFF;
case REGION_PI_64DD_ROM: case REGION_PI_64DD_ROM:
Util::warn("Reading word from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM - This is the N64DD, returning FF because it is not emulated", addr); //Util::warn("Reading word from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM - This is the N64DD, returning FF because it is not emulated", addr);
return 0xFF; return 0xFF;
case REGION_PI_SRAM: case REGION_PI_SRAM:
return mem.BackupRead32(addr - SREGION_PI_SRAM); return mem.BackupRead32(addr - SREGION_PI_SRAM);
@@ -145,7 +195,7 @@ auto PI::BusRead32(Mem& mem, u32 addr) const -> u32 {
case CART_ISVIEWER_FLUSH: case CART_ISVIEWER_FLUSH:
Util::panic("Read from ISViewer flush!"); Util::panic("Read from ISViewer flush!");
} }
Util::warn("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM!", addr, index, index); //Util::warn("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM!", addr, index, index);
return 0; return 0;
} else { } else {
return Util::ReadAccess<u32>(mem.rom.cart, index); return Util::ReadAccess<u32>(mem.rom.cart, index);
@@ -159,15 +209,27 @@ auto PI::BusRead32(Mem& mem, u32 addr) const -> u32 {
void PI::BusWrite32(Mem& mem, u32 addr, u32 val) { void PI::BusWrite32(Mem& mem, u32 addr, u32 val) {
switch (addr) { switch (addr) {
case REGION_PI_UNKNOWN: case REGION_PI_UNKNOWN:
Util::warn("Writing word 0x{:08X} to address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", val, addr); if (!WriteLatch(val)) [[unlikely]] {
return;
}
//Util::warn("Writing word 0x{:08X} to address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", val, addr);
return; return;
case REGION_PI_64DD_REG: case REGION_PI_64DD_REG:
Util::warn("Writing word 0x{:08X} to address 0x{:08X} in region: REGION_PI_64DD_ROM, this is the 64DD, ignoring!", val, addr); if (!WriteLatch(val)) [[unlikely]] {
return;
}
//Util::warn("Writing word 0x{:08X} to address 0x{:08X} in region: REGION_PI_64DD_ROM, this is the 64DD, ignoring!", val, addr);
return; return;
case REGION_PI_64DD_ROM: case REGION_PI_64DD_ROM:
Util::warn("Writing word 0x{:08X} to address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", val, addr); if (!WriteLatch(val)) [[unlikely]] {
return;
}
//Util::warn("Writing word 0x{:08X} to address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", val, addr);
return; return;
case REGION_PI_SRAM: case REGION_PI_SRAM:
if (!WriteLatch(val)) [[unlikely]] {
return;
}
mem.BackupWrite32(addr - SREGION_PI_SRAM, val); mem.BackupWrite32(addr - SREGION_PI_SRAM, val);
return; return;
case REGION_PI_ROM: case REGION_PI_ROM:
@@ -188,7 +250,10 @@ void PI::BusWrite32(Mem& mem, u32 addr, u32 val) {
break; break;
} }
default: default:
Util::warn("Writing word 0x{:08X} to address 0x{:08X} in unsupported region: REGION_PI_ROM", val, addr); if (!WriteLatch(val)) [[unlikely]] {
return;
}
//Util::warn("Writing word 0x{:08X} to address 0x{:08X} in unsupported region: REGION_PI_ROM", val, addr);
} }
return; return;
default: default:
@@ -196,19 +261,23 @@ void PI::BusWrite32(Mem& mem, u32 addr, u32 val) {
} }
} }
auto PI::BusRead64(Mem& mem, u32 addr) const -> u64 { auto PI::BusRead64(Mem& mem, u32 addr) -> u64 {
if (!ReadLatch()) [[unlikely]] {
return (u64)latch << 32;
}
switch (addr) { switch (addr) {
case REGION_PI_UNKNOWN: case REGION_PI_UNKNOWN:
Util::warn("Reading dword from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", addr); //Util::warn("Reading dword from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", addr);
return 0xFFFFFFFFFFFFFFFF; return 0xFFFFFFFFFFFFFFFF;
case REGION_PI_64DD_REG: case REGION_PI_64DD_REG:
Util::warn("Reading dword from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG", addr); //Util::warn("Reading dword from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG", addr);
return 0xFFFFFFFFFFFFFFFF; return 0xFFFFFFFFFFFFFFFF;
case REGION_PI_64DD_ROM: case REGION_PI_64DD_ROM:
Util::warn("Reading dword from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", addr); //Util::warn("Reading dword from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", addr);
return 0xFFFFFFFFFFFFFFFF; return 0xFFFFFFFFFFFFFFFF;
case REGION_PI_SRAM: case REGION_PI_SRAM:
Util::warn("Reading dword from address 0x{:08X} in unsupported region: REGION_PI_SRAM", addr); //Util::warn("Reading dword from address 0x{:08X} in unsupported region: REGION_PI_SRAM", addr);
return 0xFFFFFFFFFFFFFFFF; return 0xFFFFFFFFFFFFFFFF;
case REGION_PI_ROM: { case REGION_PI_ROM: {
u32 index = addr - SREGION_PI_ROM; u32 index = addr - SREGION_PI_ROM;
@@ -223,6 +292,10 @@ auto PI::BusRead64(Mem& mem, u32 addr) const -> u64 {
} }
void PI::BusWrite64(Mem& mem, u32 addr, u64 val) { void PI::BusWrite64(Mem& mem, u32 addr, u64 val) {
if (!WriteLatch(val >> 32)) [[unlikely]] {
return;
}
switch (addr) { switch (addr) {
case REGION_PI_UNKNOWN: case REGION_PI_UNKNOWN:
Util::panic("Writing dword 0x{:016X} to address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", val, addr); Util::panic("Writing dword 0x{:016X} to address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", val, addr);
@@ -233,7 +306,7 @@ void PI::BusWrite64(Mem& mem, u32 addr, u64 val) {
case REGION_PI_SRAM: case REGION_PI_SRAM:
Util::panic("Writing dword 0x{:016X} to address 0x{:08X} in unsupported region: REGION_PI_SRAM", val, addr); Util::panic("Writing dword 0x{:016X} to address 0x{:08X} in unsupported region: REGION_PI_SRAM", val, addr);
case REGION_PI_ROM: case REGION_PI_ROM:
Util::warn("Writing dword 0x{:016X} to address 0x{:08X} in unsupported region: REGION_PI_ROM", val, addr); //Util::warn("Writing dword 0x{:016X} to address 0x{:08X} in unsupported region: REGION_PI_ROM", val, addr);
break; break;
default: default:
Util::panic("Should never end up here! Access to address %08X which did not match any PI bus regions!", addr); Util::panic("Should never end up here! Access to address %08X which did not match any PI bus regions!", addr);
@@ -248,8 +321,8 @@ auto PI::Read(MI& mi, u32 addr) const -> u32 {
case 0x0460000C: return wrLen; case 0x0460000C: return wrLen;
case 0x04600010: { case 0x04600010: {
u32 value = 0; u32 value = 0;
value |= (0 << dmaBusy); // Is PI DMA active? No, because it's instant value |= (dmaBusy << 0); // Is PI DMA active? No, because it's instant
value |= (0 << ioBusy); // Is PI IO busy? No, because it's instant value |= (ioBusy << 1); // Is PI IO busy? No, because it's instant
value |= (0 << 2); // PI IO error? value |= (0 << 2); // PI IO error?
value |= (mi.miIntr.pi << 3); // PI interrupt? value |= (mi.miIntr.pi << 3); // PI interrupt?
return value; return value;
@@ -314,23 +387,6 @@ FORCE_INLINE u32 PIAccessTiming(PI& pi, u8 domain, u32 length) {
return cycles * 1.5; // Converting RCP clock speed to CPU clock speed return cycles * 1.5; // Converting RCP clock speed to CPU clock speed
} }
template <bool toCart>
FORCE_INLINE void OnDMAComplete(Mem& mem, Registers& regs) {
PI& pi = mem.mmio.pi;
u32 len;
if constexpr (toCart) {
len = pi.rdLen;
} else {
len = pi.wrLen;
}
pi.dramAddr = pi.dramAddrInternal + len;
pi.cartAddr = pi.cartAddrInternal + len;
pi.dmaBusy = false;
pi.ioBusy = false;
InterruptRaise(mem.mmio.mi, regs, Interrupt::PI);
}
void PI::Write(Mem& mem, Registers& regs, u32 addr, u32 val) { void PI::Write(Mem& mem, Registers& regs, u32 addr, u32 val) {
MI& mi = mem.mmio.mi; MI& mi = mem.mmio.mi;
switch(addr) { switch(addr) {
@@ -349,8 +405,8 @@ void PI::Write(Mem& mem, Registers& regs, u32 addr, u32 val) {
} }
Util::trace("PI DMA from RDRAM to CARTRIDGE (size: {} B, {:08X} to {:08X})", len, dramAddr, cartAddr); Util::trace("PI DMA from RDRAM to CARTRIDGE (size: {} B, {:08X} to {:08X})", len, dramAddr, cartAddr);
dmaBusy = true; dmaBusy = true;
ioBusy = true; toCart = true;
scheduler.enqueueRelative(Event{PIAccessTiming(*this, PIGetDomain(cartAddr), len), OnDMAComplete<true>}); scheduler.enqueueRelative(PIAccessTiming(*this, PIGetDomain(cartAddr), len), PI_DMA_COMPLETE);
} break; } break;
case 0x0460000C: { case 0x0460000C: {
u32 len = (val & 0x00FFFFFF) + 1; u32 len = (val & 0x00FFFFFF) + 1;
@@ -364,9 +420,9 @@ void PI::Write(Mem& mem, Registers& regs, u32 addr, u32 val) {
mem.mmio.rdp.rdram[BYTE_ADDRESS(dramAddrInternal + i) & RDRAM_DSIZE] = mem.rom.cart[BYTE_ADDRESS(cartAddrInternal + i) & mem.rom.mask]; mem.mmio.rdp.rdram[BYTE_ADDRESS(dramAddrInternal + i) & RDRAM_DSIZE] = mem.rom.cart[BYTE_ADDRESS(cartAddrInternal + i) & mem.rom.mask];
} }
dmaBusy = true; dmaBusy = true;
ioBusy = true;
Util::trace("PI DMA from CARTRIDGE to RDRAM (size: {} B, {:08X} to {:08X})", len, cartAddr, dramAddr); Util::trace("PI DMA from CARTRIDGE to RDRAM (size: {} B, {:08X} to {:08X})", len, cartAddr, dramAddr);
scheduler.enqueueRelative(Event{PIAccessTiming(*this, PIGetDomain(cartAddr), len), OnDMAComplete<false>}); toCart = false;
scheduler.enqueueRelative(PIAccessTiming(*this, PIGetDomain(cartAddr), len), PI_DMA_COMPLETE);
} break; } break;
case 0x04600010: case 0x04600010:
if(val & 2) { if(val & 2) {

View File

@@ -12,15 +12,18 @@ struct PI {
void Reset(); void Reset();
auto Read(MI&, u32) const -> u32; auto Read(MI&, u32) const -> u32;
void Write(Mem&, Registers&, u32, u32); void Write(Mem&, Registers&, u32, u32);
auto BusRead8(Mem&, u32) const -> u8; auto BusRead8(Mem&, u32) -> u8;
void BusWrite8(Mem&, u32, u32); void BusWrite8(Mem&, u32, u32);
auto BusRead16(Mem&, u32) const -> u16; auto BusRead16(Mem&, u32) -> u16;
void BusWrite16(Mem&, u32, u16); void BusWrite16(Mem&, u32, u16);
auto BusRead32(Mem&, u32) const -> u32; auto BusRead32(Mem&, u32) -> u32;
void BusWrite32(Mem&, u32, u32); void BusWrite32(Mem&, u32, u32);
auto BusRead64(Mem&, u32) const -> u64; auto BusRead64(Mem&, u32) -> u64;
void BusWrite64(Mem&, u32, u64); void BusWrite64(Mem&, u32, u64);
bool dmaBusy{}, ioBusy{}; bool ReadLatch();
bool WriteLatch(u32 val);
bool dmaBusy{}, ioBusy{}, toCart{};
u32 latch;
u32 dramAddr{}, cartAddr{}, dramAddrInternal{}, cartAddrInternal{}; u32 dramAddr{}, cartAddr{}, dramAddrInternal{}, cartAddrInternal{};
u32 rdLen{}, wrLen{}; u32 rdLen{}, wrLen{};
u32 pi_bsd_dom1_lat{}, pi_bsd_dom2_lat{}; u32 pi_bsd_dom1_lat{}, pi_bsd_dom2_lat{};

View File

@@ -32,11 +32,10 @@ auto SI::Read(MI& mi, u32 addr) const -> u32 {
} }
} }
template <bool toDram> void SI::DMA(Mem& mem, Registers& regs) {
FORCE_INLINE void DMA(Mem& mem, Registers& regs) {
SI& si = mem.mmio.si; SI& si = mem.mmio.si;
si.status.dmaBusy = false; si.status.dmaBusy = false;
if constexpr(toDram) { if (toDram) {
si.pif.ProcessCommands(mem); si.pif.ProcessCommands(mem);
for(int i = 0; i < 64; i++) { for(int i = 0; i < 64; i++) {
mem.mmio.rdp.rdram[BYTE_ADDRESS(si.dramAddr + i)] = si.pif.Read(si.pifAddr + i); mem.mmio.rdp.rdram[BYTE_ADDRESS(si.dramAddr + i)] = si.pif.Read(si.pifAddr + i);
@@ -60,12 +59,14 @@ void SI::Write(Mem& mem, Registers& regs, u32 addr, u32 val) {
case 0x04800004: { case 0x04800004: {
pifAddr = val & 0x1FFFFFFF; pifAddr = val & 0x1FFFFFFF;
status.dmaBusy = true; status.dmaBusy = true;
scheduler.enqueueRelative({SI_DMA_DELAY, DMA<true>}); toDram = true;
scheduler.enqueueRelative(SI_DMA_DELAY, SI_DMA);
} break; } break;
case 0x04800010: { case 0x04800010: {
pifAddr = val & 0x1FFFFFFF; pifAddr = val & 0x1FFFFFFF;
status.dmaBusy = true; status.dmaBusy = true;
scheduler.enqueueRelative({SI_DMA_DELAY, DMA<false>}); toDram = false;
scheduler.enqueueRelative(SI_DMA_DELAY, SI_DMA);
} break; } break;
case 0x04800018: case 0x04800018:
InterruptLower(mem.mmio.mi, regs, Interrupt::SI); InterruptLower(mem.mmio.mi, regs, Interrupt::SI);

View File

@@ -26,9 +26,11 @@ struct SI {
SIStatus status{}; SIStatus status{};
u32 dramAddr{}; u32 dramAddr{};
u32 pifAddr{}; u32 pifAddr{};
bool toDram = false;
auto Read(MI&, u32) const -> u32; auto Read(MI&, u32) const -> u32;
void Write(Mem&, Registers&, u32, u32); void Write(Mem&, Registers&, u32, u32);
void DMA(Mem&, Registers&);
PIF pif; PIF pif;
}; };