diff --git a/src/backend/Core.cpp b/src/backend/Core.cpp index 56e8af69..bf143e67 100644 --- a/src/backend/Core.cpp +++ b/src/backend/Core.cpp @@ -2,6 +2,18 @@ #include namespace n64 { +u32 extraCycles = 0; + +void CpuStall(u32 cycles) { + extraCycles += cycles; +} + +u32 PopStalledCycles() { + u32 ret = extraCycles; + extraCycles = 0; + return ret; +} + Core::Core() { if(SDL_GameControllerAddMappingsFromFile("resources/gamecontrollerdb.txt") < 0) { Util::warn("Failed to load game controller DB"); @@ -46,8 +58,9 @@ void Core::Run(float volumeL, float volumeR) { MMIO& mmio = mem.mmio; Registers& regs = cpu->regs; + Event event; for (int field = 0; field < mmio.vi.numFields; field++) { - int frameCycles = 0; + u32 frameCycles = 0; for (int i = 0; i < mmio.vi.numHalflines; i++) { 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++) { - int taken = cpu->Step(); - static int cpuSteps = 0; + u32 taken = cpu->Step(); + taken += PopStalledCycles(); + static u32 cpuSteps = 0; cpuSteps += taken; if(mmio.rsp.spStatus.halt) { cpuSteps = 0; diff --git a/src/backend/Core.hpp b/src/backend/Core.hpp index 5558e1d0..ad4746f8 100644 --- a/src/backend/Core.hpp +++ b/src/backend/Core.hpp @@ -6,6 +6,7 @@ #include struct Window; +struct Event; namespace n64 { struct Core { @@ -17,13 +18,14 @@ struct Core { void Serialize(); void Deserialize(); void TogglePause() { pause = !pause; } + void HandleEvents(Event*); [[nodiscard]] VI& GetVI() { return cpu->mem.mmio.vi; } u32 breakpoint = 0; bool pause = true; bool render = true; - int cycles = 0; + u32 cycles = 0; bool romLoaded = false; std::string rom; std::unique_ptr cpu; @@ -31,4 +33,8 @@ struct Core { int memSize, cpuSize, verSize; int slot = 0; }; + +extern u32 extraCycles; +void CpuStall(u32 cycles); +u32 PopStalledCycles(); } diff --git a/src/backend/MemoryRegions.hpp b/src/backend/MemoryRegions.hpp index 47bf8702..680df821 100644 --- a/src/backend/MemoryRegions.hpp +++ b/src/backend/MemoryRegions.hpp @@ -51,7 +51,7 @@ #define PI_REGION 0x04600000 ... 0x046FFFFF #define RI_REGION 0x04700000 ... 0x047FFFFF #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_RAM_REGION PIF_RAM_REGION_START ... PIF_RAM_REGION_END diff --git a/src/backend/Scheduler.cpp b/src/backend/Scheduler.cpp index 8a2fc05d..ccfce290 100644 --- a/src/backend/Scheduler.cpp +++ b/src/backend/Scheduler.cpp @@ -1,4 +1,60 @@ #include #include +#include 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(); + } +} \ No newline at end of file diff --git a/src/backend/Scheduler.hpp b/src/backend/Scheduler.hpp index 78afc056..6de92d94 100644 --- a/src/backend/Scheduler.hpp +++ b/src/backend/Scheduler.hpp @@ -1,6 +1,7 @@ #pragma once #include #include +#include #include namespace n64 { @@ -8,9 +9,17 @@ struct Mem; struct Registers; } +enum EventType { + NONE, + PI_BUS_WRITE_COMPLETE, + PI_DMA_COMPLETE, + SI_DMA, + IMPOSSIBLE +}; + struct Event { - u64 time = 0; - void(*handler)(n64::Mem&, n64::Registers&) = nullptr; + u64 time; + EventType type; friend bool operator<(const Event& rhs, const Event& lhs) { return rhs.time < lhs.time; @@ -27,26 +36,14 @@ struct Event { struct Scheduler { Scheduler() { - enqueueAbsolute(Event{std::numeric_limits::max(), [](n64::Mem&, n64::Registers&) { - Util::panic("How the fuck did we get here?!"); - }}); + enqueueAbsolute(std::numeric_limits::max(), IMPOSSIBLE); } - FORCE_INLINE void enqueueRelative(const Event& event) { - enqueueAbsolute({event.time + ticks, event.handler}); - } - - 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(); - } - } + void enqueueRelative(u64, const EventType); + void enqueueAbsolute(u64, const EventType); + u64 remove(const EventType); + void tick(u64 t, n64::Mem&, n64::Registers&); + std::priority_queue, std::greater<>> events; u64 ticks = 0; u8 index = 0; diff --git a/src/backend/core/Mem.cpp b/src/backend/core/Mem.cpp index a5035430..86089ac4 100644 --- a/src/backend/core/Mem.cpp +++ b/src/backend/core/Mem.cpp @@ -363,11 +363,12 @@ void Mem::Write8(Registers& regs, u32 paddr, u32 val) { Util::WriteAccess(mmio.rsp.dmem, mirrAddr, val); } } break; - case MMIO_REGION: - Util::panic("MMIO Write8!"); case REGION_CART: + Util::debug("BusWrite8 @ {:08X} = {:02X}", paddr, val); mmio.pi.BusWrite8(*this, paddr, val); break; + case MMIO_REGION: + Util::panic("MMIO Write8!"); case PIF_RAM_REGION: val = val << (8 * (3 - (paddr & 3))); paddr = (paddr - PIF_RAM_REGION_START) & ~3; @@ -411,6 +412,10 @@ void Mem::Write16(Registers& regs, u32 paddr, u32 val) { Util::WriteAccess(mmio.rsp.dmem, mirrAddr, val); } } break; + case REGION_CART: + Util::debug("BusWrite8 @ {:08X} = {:04X}", paddr, val); + mmio.pi.BusWrite16(*this, paddr, val); + break; case MMIO_REGION: Util::panic("MMIO Write16!"); case PIF_RAM_REGION: @@ -419,9 +424,6 @@ void Mem::Write16(Registers& regs, u32 paddr, u32 val) { Util::WriteAccess(si.pif.ram, paddr - PIF_RAM_REGION_START, htobe32(val)); si.pif.ProcessCommands(*this); break; - case REGION_CART: - mmio.pi.BusWrite16(*this, paddr, val); - break; case 0x00800000 ... 0x03FFFFFF: case 0x04200000 ... 0x042FFFFF: case 0x04900000 ... 0x04FFFFFF: @@ -457,6 +459,10 @@ void Mem::Write32(Registers& regs, u32 paddr, u32 val) { Util::WriteAccess(mmio.rsp.dmem, paddr & DMEM_DSIZE, val); } } break; + case REGION_CART: + Util::debug("BusWrite8 @ {:08X} = {:08X}", paddr, val); + mmio.pi.BusWrite32(*this, paddr, val); + break; case MMIO_REGION: mmio.Write(*this, regs, paddr, val); break; @@ -470,9 +476,6 @@ void Mem::Write32(Registers& regs, u32 paddr, u32 val) { case PIF_ROM_REGION: case 0x1FC00800 ... 0x7FFFFFFF: 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); } } @@ -500,15 +503,16 @@ void Mem::Write64(Registers& regs, u32 paddr, u64 val) { Util::WriteAccess(mmio.rsp.dmem, paddr & DMEM_DSIZE, val); } } break; + case REGION_CART: + Util::debug("BusWrite8 @ {:08X} = {:016X}", paddr, val); + mmio.pi.BusWrite64(*this, paddr, val); + break; case MMIO_REGION: Util::panic("MMIO Write64!"); case PIF_RAM_REGION: Util::WriteAccess(si.pif.ram, paddr - PIF_RAM_REGION_START, htobe64(val)); si.pif.ProcessCommands(*this); break; - case REGION_CART: - mmio.pi.BusWrite64(*this, paddr, val); - break; case 0x00800000 ... 0x03FFFFFF: case 0x04200000 ... 0x042FFFFF: case 0x04900000 ... 0x04FFFFFF: diff --git a/src/backend/core/mmio/AI.cpp b/src/backend/core/mmio/AI.cpp index e2b3652e..0cce566a 100644 --- a/src/backend/core/mmio/AI.cpp +++ b/src/backend/core/mmio/AI.cpp @@ -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; while(cycles > dac.period) { if (dmaCount == 0) { diff --git a/src/backend/core/mmio/AI.hpp b/src/backend/core/mmio/AI.hpp index af4bd810..bd9dc4d8 100644 --- a/src/backend/core/mmio/AI.hpp +++ b/src/backend/core/mmio/AI.hpp @@ -11,7 +11,7 @@ struct AI { void Reset(); auto Read(u32) const -> u32; void Write(Mem&, Registers&, u32, u32); - void Step(Mem&, Registers&, int, float, float); + void Step(Mem&, Registers&, u32, float, float); bool dmaEnable{}; u16 dacRate{}; u8 bitrate{}; @@ -19,7 +19,7 @@ struct AI { u32 dmaLen[2]{}; u32 dmaAddr[2]{}; bool dmaAddrCarry{}; - int cycles{}; + u32 cycles{}; struct { u32 freq{44100}; diff --git a/src/backend/core/mmio/PI.cpp b/src/backend/core/mmio/PI.cpp index 5d260e5a..00342bc1 100644 --- a/src/backend/core/mmio/PI.cpp +++ b/src/backend/core/mmio/PI.cpp @@ -1,8 +1,7 @@ #include #include -#include -#include -#include "Scheduler.hpp" +#include +#include namespace n64 { PI::PI() { @@ -10,23 +9,59 @@ PI::PI() { } void PI::Reset() { + dmaBusy = false; + ioBusy = false; + latch = 0; dramAddr = 0; cartAddr = 0; + dramAddrInternal = 0; + cartAddrInternal = 0; rdLen = 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 { - // TODO: Latch with CPU stall +bool PI::WriteLatch(u32 value) { + 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) { 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; 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; 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; case REGION_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; u32 index = BYTE_ADDRESS(addr) - SREGION_PI_ROM; 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 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) { + int latch_shift = 24 - (addr & 1) * 8; + + if (!WriteLatch(val << latch_shift) && addr != 0x05000020) [[unlikely]] { + return; + } + switch (addr) { 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; case REGION_PI_64DD_REG: if (addr == 0x05000020) { fprintf(stderr, "%c", val); } 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; 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; case REGION_PI_SRAM: mem.BackupWrite8(addr - SREGION_PI_SRAM, val); return; 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; default: 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 { - // TODO: Latch with CPU stall +auto PI::BusRead16(Mem& mem, u32 addr) -> u16 { + if (!ReadLatch()) [[unlikely]] { + return latch >> 16; + } + switch (addr) { 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; 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; 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; case REGION_PI_SRAM: Util::panic("Reading half from address 0x{:08X} in unsupported region: REGION_PI_SRAM", addr); case REGION_PI_ROM: { - // round to nearest 4 byte boundary, keeping old LSB addr = (addr + 2) & ~3; u32 index = HALF_ADDRESS(addr) - SREGION_PI_ROM; 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 Util::ReadAccess(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) { + if (!WriteLatch(val << 16)) [[unlikely]] { + return; + } + switch (addr) { 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; 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; 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; 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; 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; default: 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 { - // TODO: Latch with CPU stall +auto PI::BusRead32(Mem& mem, u32 addr) -> u32 { + if (!ReadLatch()) [[unlikely]] { + return latch; + } + switch (addr) { 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; 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; 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; case REGION_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: 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; } else { return Util::ReadAccess(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) { switch (addr) { 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; 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; 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; case REGION_PI_SRAM: + if (!WriteLatch(val)) [[unlikely]] { + return; + } mem.BackupWrite32(addr - SREGION_PI_SRAM, val); return; case REGION_PI_ROM: @@ -188,7 +250,10 @@ void PI::BusWrite32(Mem& mem, u32 addr, u32 val) { break; } 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; 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) { 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; 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; 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; 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; case REGION_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) { + if (!WriteLatch(val >> 32)) [[unlikely]] { + return; + } + switch (addr) { case REGION_PI_UNKNOWN: 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: Util::panic("Writing dword 0x{:016X} to address 0x{:08X} in unsupported region: REGION_PI_SRAM", val, addr); 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; default: 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 0x04600010: { u32 value = 0; - value |= (0 << dmaBusy); // Is PI DMA active? No, because it's instant - value |= (0 << ioBusy); // Is PI IO busy? No, because it's instant + value |= (dmaBusy << 0); // Is PI DMA active? No, because it's instant + value |= (ioBusy << 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; @@ -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 } -template -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) { MI& mi = mem.mmio.mi; 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); dmaBusy = true; - ioBusy = true; - scheduler.enqueueRelative(Event{PIAccessTiming(*this, PIGetDomain(cartAddr), len), OnDMAComplete}); + toCart = true; + scheduler.enqueueRelative(PIAccessTiming(*this, PIGetDomain(cartAddr), len), PI_DMA_COMPLETE); } break; case 0x0460000C: { 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]; } dmaBusy = true; - ioBusy = true; 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}); + toCart = false; + scheduler.enqueueRelative(PIAccessTiming(*this, PIGetDomain(cartAddr), len), PI_DMA_COMPLETE); } break; case 0x04600010: if(val & 2) { diff --git a/src/backend/core/mmio/PI.hpp b/src/backend/core/mmio/PI.hpp index 0e034406..471699ea 100644 --- a/src/backend/core/mmio/PI.hpp +++ b/src/backend/core/mmio/PI.hpp @@ -12,15 +12,18 @@ struct PI { void Reset(); auto Read(MI&, u32) const -> u32; void Write(Mem&, Registers&, u32, u32); - auto BusRead8(Mem&, u32) const -> u8; + auto BusRead8(Mem&, u32) -> u8; void BusWrite8(Mem&, u32, u32); - auto BusRead16(Mem&, u32) const -> u16; + auto BusRead16(Mem&, u32) -> u16; void BusWrite16(Mem&, u32, u16); - auto BusRead32(Mem&, u32) const -> u32; + auto BusRead32(Mem&, u32) -> u32; void BusWrite32(Mem&, u32, u32); - auto BusRead64(Mem&, u32) const -> u64; + auto BusRead64(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 rdLen{}, wrLen{}; u32 pi_bsd_dom1_lat{}, pi_bsd_dom2_lat{}; diff --git a/src/backend/core/mmio/SI.cpp b/src/backend/core/mmio/SI.cpp index e60efa2f..f18491e6 100644 --- a/src/backend/core/mmio/SI.cpp +++ b/src/backend/core/mmio/SI.cpp @@ -32,11 +32,10 @@ auto SI::Read(MI& mi, u32 addr) const -> u32 { } } -template -FORCE_INLINE void DMA(Mem& mem, Registers& regs) { +void SI::DMA(Mem& mem, Registers& regs) { SI& si = mem.mmio.si; si.status.dmaBusy = false; - if constexpr(toDram) { + if (toDram) { si.pif.ProcessCommands(mem); for(int i = 0; i < 64; 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: { pifAddr = val & 0x1FFFFFFF; status.dmaBusy = true; - scheduler.enqueueRelative({SI_DMA_DELAY, DMA}); + toDram = true; + scheduler.enqueueRelative(SI_DMA_DELAY, SI_DMA); } break; case 0x04800010: { pifAddr = val & 0x1FFFFFFF; status.dmaBusy = true; - scheduler.enqueueRelative({SI_DMA_DELAY, DMA}); + toDram = false; + scheduler.enqueueRelative(SI_DMA_DELAY, SI_DMA); } break; case 0x04800018: InterruptLower(mem.mmio.mi, regs, Interrupt::SI); diff --git a/src/backend/core/mmio/SI.hpp b/src/backend/core/mmio/SI.hpp index 37732eb1..cd9a6b2e 100644 --- a/src/backend/core/mmio/SI.hpp +++ b/src/backend/core/mmio/SI.hpp @@ -26,9 +26,11 @@ struct SI { SIStatus status{}; u32 dramAddr{}; u32 pifAddr{}; + bool toDram = false; auto Read(MI&, u32) const -> u32; void Write(Mem&, Registers&, u32, u32); + void DMA(Mem&, Registers&); PIF pif; };