CPU: Memory access timings

This commit is contained in:
Connor McLaughlin 2019-10-04 20:23:47 +10:00
parent fd1c4f1457
commit 4422fb0545
6 changed files with 271 additions and 212 deletions

View File

@ -64,6 +64,11 @@ void Bus::Reset()
bool Bus::DoState(StateWrapper& sw) bool Bus::DoState(StateWrapper& sw)
{ {
sw.Do(&m_exp1_access_time);
sw.Do(&m_exp2_access_time);
sw.Do(&m_bios_access_time);
sw.Do(&m_cdrom_access_time);
sw.Do(&m_spu_access_time);
sw.DoBytes(m_ram.data(), m_ram.size()); sw.DoBytes(m_ram.data(), m_ram.size());
sw.DoBytes(m_bios.data(), m_bios.size()); sw.DoBytes(m_bios.data(), m_bios.size());
sw.DoArray(m_MEMCTRL.regs, countof(m_MEMCTRL.regs)); sw.DoArray(m_MEMCTRL.regs, countof(m_MEMCTRL.regs));
@ -223,7 +228,7 @@ void Bus::RecalculateMemoryTimings()
m_spu_access_time[2]); m_spu_access_time[2]);
} }
bool Bus::DoInvalidAccess(MemoryAccessType type, MemoryAccessSize size, PhysicalMemoryAddress address, u32& value) TickCount Bus::DoInvalidAccess(MemoryAccessType type, MemoryAccessSize size, PhysicalMemoryAddress address, u32& value)
{ {
SmallString str; SmallString str;
str.AppendString("Invalid bus "); str.AppendString("Invalid bus ");
@ -247,13 +252,16 @@ bool Bus::DoInvalidAccess(MemoryAccessType type, MemoryAccessSize size, Physical
if (type == MemoryAccessType::Read) if (type == MemoryAccessType::Read)
value = UINT32_C(0xFFFFFFFF); value = UINT32_C(0xFFFFFFFF);
return true; return 1;
} }
bool Bus::DoReadEXP1(MemoryAccessSize size, u32 offset, u32& value) u32 Bus::DoReadEXP1(MemoryAccessSize size, u32 offset)
{ {
if (m_exp1_rom.empty()) if (m_exp1_rom.empty())
return DoInvalidAccess(MemoryAccessType::Read, size, EXP1_BASE | offset, value); {
// EXP1 not present.
return UINT32_C(0xFFFFFFFF);
}
if (offset == 0x20018) if (offset == 0x20018)
{ {
@ -264,10 +272,10 @@ bool Bus::DoReadEXP1(MemoryAccessSize size, u32 offset, u32& value)
const u32 transfer_size = u32(1) << static_cast<u32>(size); const u32 transfer_size = u32(1) << static_cast<u32>(size);
if ((offset + transfer_size) > m_exp1_rom.size()) if ((offset + transfer_size) > m_exp1_rom.size())
{ {
value = UINT32_C(0); return UINT32_C(0);
return true;
} }
u32 value;
if (size == MemoryAccessSize::Byte) if (size == MemoryAccessSize::Byte)
{ {
value = ZeroExtend32(m_exp1_rom[offset]); value = ZeroExtend32(m_exp1_rom[offset]);
@ -284,36 +292,32 @@ bool Bus::DoReadEXP1(MemoryAccessSize size, u32 offset, u32& value)
} }
// Log_DevPrintf("EXP1 read: 0x%08X -> 0x%08X", EXP1_BASE | offset, value); // Log_DevPrintf("EXP1 read: 0x%08X -> 0x%08X", EXP1_BASE | offset, value);
return true; return value;
} }
bool Bus::DoWriteEXP1(MemoryAccessSize size, u32 offset, u32 value) void Bus::DoWriteEXP1(MemoryAccessSize size, u32 offset, u32 value)
{ {
return DoInvalidAccess(MemoryAccessType::Write, size, EXP1_BASE | offset, value); Log_WarningPrintf("EXP1 write: 0x%08X <- 0x%08X", EXP1_BASE | offset, value);
} }
bool Bus::DoReadEXP2(MemoryAccessSize size, u32 offset, u32& value) u32 Bus::DoReadEXP2(MemoryAccessSize size, u32 offset)
{ {
offset &= EXP2_MASK;
// rx/tx buffer empty // rx/tx buffer empty
if (offset == 0x21) if (offset == 0x21)
{ {
value = 0x04 | 0x08; return 0x04 | 0x08;
return true;
} }
return DoInvalidAccess(MemoryAccessType::Read, size, EXP2_BASE | offset, value); Log_WarningPrintf("EXP2 read: 0x%08X", EXP2_BASE | offset);
return UINT32_C(0xFFFFFFFF);
} }
bool Bus::DoWriteEXP2(MemoryAccessSize size, u32 offset, u32 value) void Bus::DoWriteEXP2(MemoryAccessSize size, u32 offset, u32 value)
{ {
offset &= EXP2_MASK;
if (offset == 0x23) if (offset == 0x23)
{ {
if (value == '\r') if (value == '\r')
return true; return;
if (value == '\n') if (value == '\n')
{ {
@ -326,26 +330,26 @@ bool Bus::DoWriteEXP2(MemoryAccessSize size, u32 offset, u32 value)
m_tty_line_buffer.AppendCharacter(Truncate8(value)); m_tty_line_buffer.AppendCharacter(Truncate8(value));
} }
return true; return;
} }
if (offset == 0x41) if (offset == 0x41)
{ {
Log_WarningPrintf("BIOS POST status: %02X", value & UINT32_C(0x0F)); Log_WarningPrintf("BIOS POST status: %02X", value & UINT32_C(0x0F));
return true; return;
} }
return DoInvalidAccess(MemoryAccessType::Write, size, EXP2_BASE | offset, value); Log_WarningPrintf("EXP2 write: 0x%08X <- 0x%08X", EXP2_BASE | offset, value);
} }
bool Bus::DoReadMemoryControl(MemoryAccessSize size, u32 offset, u32& value) u32 Bus::DoReadMemoryControl(MemoryAccessSize size, u32 offset)
{ {
u32 value = m_MEMCTRL.regs[offset / 4];
FixupUnalignedWordAccessW32(offset, value); FixupUnalignedWordAccessW32(offset, value);
value = m_MEMCTRL.regs[offset / 4]; return value;
return true;
} }
bool Bus::DoWriteMemoryControl(MemoryAccessSize size, u32 offset, u32 value) void Bus::DoWriteMemoryControl(MemoryAccessSize size, u32 offset, u32 value)
{ {
FixupUnalignedWordAccessW32(offset, value); FixupUnalignedWordAccessW32(offset, value);
@ -357,165 +361,151 @@ bool Bus::DoWriteMemoryControl(MemoryAccessSize size, u32 offset, u32 value)
m_MEMCTRL.regs[index] = new_value; m_MEMCTRL.regs[index] = new_value;
RecalculateMemoryTimings(); RecalculateMemoryTimings();
} }
return true;
} }
bool Bus::DoReadMemoryControl2(MemoryAccessSize size, u32 offset, u32& value) u32 Bus::DoReadMemoryControl2(MemoryAccessSize size, u32 offset)
{ {
if (offset == 0x00) if (offset == 0x00)
{ return m_ram_size_reg;
value = m_ram_size_reg;
return true;
}
return DoInvalidAccess(MemoryAccessType::Read, size, MEMCTRL2_BASE | offset, value); u32 value = 0;
DoInvalidAccess(MemoryAccessType::Read, size, MEMCTRL2_BASE | offset, value);
return value;
} }
bool Bus::DoWriteMemoryControl2(MemoryAccessSize size, u32 offset, u32 value) void Bus::DoWriteMemoryControl2(MemoryAccessSize size, u32 offset, u32 value)
{ {
if (offset == 0x00) if (offset == 0x00)
{ {
m_ram_size_reg = value; m_ram_size_reg = value;
return true; return;
} }
return DoInvalidAccess(MemoryAccessType::Write, size, MEMCTRL2_BASE | offset, value); DoInvalidAccess(MemoryAccessType::Write, size, MEMCTRL2_BASE | offset, value);
} }
bool Bus::DoReadPad(MemoryAccessSize size, u32 offset, u32& value) u32 Bus::DoReadPad(MemoryAccessSize size, u32 offset)
{ {
value = m_pad->ReadRegister(offset); return m_pad->ReadRegister(offset);
return true;
} }
bool Bus::DoWritePad(MemoryAccessSize size, u32 offset, u32 value) void Bus::DoWritePad(MemoryAccessSize size, u32 offset, u32 value)
{ {
m_pad->WriteRegister(offset, value); m_pad->WriteRegister(offset, value);
return true;
} }
bool Bus::DoReadSIO(MemoryAccessSize size, u32 offset, u32& value) u32 Bus::DoReadSIO(MemoryAccessSize size, u32 offset)
{ {
Log_ErrorPrintf("SIO Read 0x%08X", offset); Log_ErrorPrintf("SIO Read 0x%08X", offset);
value = 0;
if (offset == 0x04) if (offset == 0x04)
value = 0x5; return 0x5;
else
return true; return 0;
} }
bool Bus::DoWriteSIO(MemoryAccessSize size, u32 offset, u32 value) void Bus::DoWriteSIO(MemoryAccessSize size, u32 offset, u32 value)
{ {
Log_ErrorPrintf("SIO Write 0x%08X <- 0x%08X", offset, value); Log_ErrorPrintf("SIO Write 0x%08X <- 0x%08X", offset, value);
return true;
} }
bool Bus::DoReadCDROM(MemoryAccessSize size, u32 offset, u32& value) u32 Bus::DoReadCDROM(MemoryAccessSize size, u32 offset)
{ {
// TODO: Splitting of half/word reads. // TODO: Splitting of half/word reads.
Assert(size == MemoryAccessSize::Byte); Assert(size == MemoryAccessSize::Byte);
value = ZeroExtend32(m_cdrom->ReadRegister(offset)); return ZeroExtend32(m_cdrom->ReadRegister(offset));
return true;
} }
bool Bus::DoWriteCDROM(MemoryAccessSize size, u32 offset, u32 value) void Bus::DoWriteCDROM(MemoryAccessSize size, u32 offset, u32 value)
{ {
// TODO: Splitting of half/word reads. // TODO: Splitting of half/word reads.
Assert(size == MemoryAccessSize::Byte); Assert(size == MemoryAccessSize::Byte);
m_cdrom->WriteRegister(offset, Truncate8(value)); m_cdrom->WriteRegister(offset, Truncate8(value));
return true;
} }
bool Bus::DoReadGPU(MemoryAccessSize size, u32 offset, u32& value) u32 Bus::DoReadGPU(MemoryAccessSize size, u32 offset)
{ {
Assert(size == MemoryAccessSize::Word); Assert(size == MemoryAccessSize::Word);
value = m_gpu->ReadRegister(offset); return m_gpu->ReadRegister(offset);
return true;
} }
bool Bus::DoWriteGPU(MemoryAccessSize size, u32 offset, u32 value) void Bus::DoWriteGPU(MemoryAccessSize size, u32 offset, u32 value)
{ {
Assert(size == MemoryAccessSize::Word); Assert(size == MemoryAccessSize::Word);
m_gpu->WriteRegister(offset, value); m_gpu->WriteRegister(offset, value);
return true;
} }
bool Bus::DoReadMDEC(MemoryAccessSize size, u32 offset, u32& value) u32 Bus::DoReadMDEC(MemoryAccessSize size, u32 offset)
{ {
Assert(size == MemoryAccessSize::Word); Assert(size == MemoryAccessSize::Word);
value = m_mdec->ReadRegister(offset); return m_mdec->ReadRegister(offset);
return true;
} }
bool Bus::DoWriteMDEC(MemoryAccessSize size, u32 offset, u32 value) void Bus::DoWriteMDEC(MemoryAccessSize size, u32 offset, u32 value)
{ {
Assert(size == MemoryAccessSize::Word); Assert(size == MemoryAccessSize::Word);
m_mdec->WriteRegister(offset, value); m_mdec->WriteRegister(offset, value);
return true;
} }
bool Bus::DoReadInterruptController(MemoryAccessSize size, u32 offset, u32& value) u32 Bus::DoReadInterruptController(MemoryAccessSize size, u32 offset)
{ {
u32 value = m_interrupt_controller->ReadRegister(offset);
FixupUnalignedWordAccessW32(offset, value); FixupUnalignedWordAccessW32(offset, value);
value = m_interrupt_controller->ReadRegister(offset); return value;
return true;
} }
bool Bus::DoWriteInterruptController(MemoryAccessSize size, u32 offset, u32 value) void Bus::DoWriteInterruptController(MemoryAccessSize size, u32 offset, u32 value)
{ {
FixupUnalignedWordAccessW32(offset, value); FixupUnalignedWordAccessW32(offset, value);
m_interrupt_controller->WriteRegister(offset, value); m_interrupt_controller->WriteRegister(offset, value);
return true;
} }
bool Bus::DoReadTimers(MemoryAccessSize size, u32 offset, u32& value) u32 Bus::DoReadTimers(MemoryAccessSize size, u32 offset)
{ {
u32 value = m_timers->ReadRegister(offset);
FixupUnalignedWordAccessW32(offset, value); FixupUnalignedWordAccessW32(offset, value);
value = m_timers->ReadRegister(offset); return value;
return true;
} }
bool Bus::DoWriteTimers(MemoryAccessSize size, u32 offset, u32 value) void Bus::DoWriteTimers(MemoryAccessSize size, u32 offset, u32 value)
{ {
FixupUnalignedWordAccessW32(offset, value); FixupUnalignedWordAccessW32(offset, value);
m_timers->WriteRegister(offset, value); m_timers->WriteRegister(offset, value);
return true;
} }
bool Bus::DoReadSPU(MemoryAccessSize size, u32 offset, u32& value) u32 Bus::DoReadSPU(MemoryAccessSize size, u32 offset)
{ {
// 32-bit reads are read as two 16-bit writes. // 32-bit reads are read as two 16-bit accesses.
if (size == MemoryAccessSize::Word) if (size == MemoryAccessSize::Word)
{ {
const u16 lsb = m_spu->ReadRegister(offset); const u16 lsb = m_spu->ReadRegister(offset);
const u16 msb = m_spu->ReadRegister(offset + 2); const u16 msb = m_spu->ReadRegister(offset + 2);
value = ZeroExtend32(lsb) | (ZeroExtend32(msb) << 16); return ZeroExtend32(lsb) | (ZeroExtend32(msb) << 16);
} }
else else
{ {
value = ZeroExtend32(m_spu->ReadRegister(offset)); return ZeroExtend32(m_spu->ReadRegister(offset));
} }
return true;
} }
bool Bus::DoWriteSPU(MemoryAccessSize size, u32 offset, u32 value) void Bus::DoWriteSPU(MemoryAccessSize size, u32 offset, u32 value)
{ {
// 32-bit writes are written as two 16-bit writes. // 32-bit writes are written as two 16-bit writes.
// TODO: Ignore if address is not aligned.
if (size == MemoryAccessSize::Word) if (size == MemoryAccessSize::Word)
{ {
Assert(Common::IsAlignedPow2(offset, 2));
m_spu->WriteRegister(offset, Truncate16(value)); m_spu->WriteRegister(offset, Truncate16(value));
m_spu->WriteRegister(offset + 2, Truncate16(value >> 16)); m_spu->WriteRegister(offset + 2, Truncate16(value >> 16));
return true; return;
} }
Assert(Common::IsAlignedPow2(offset, 2));
m_spu->WriteRegister(offset, Truncate16(value)); m_spu->WriteRegister(offset, Truncate16(value));
return true;
} }
bool Bus::DoReadDMA(MemoryAccessSize size, u32 offset, u32& value) u32 Bus::DoReadDMA(MemoryAccessSize size, u32 offset)
{ {
u32 value = m_dma->ReadRegister(offset);
switch (size) switch (size)
{ {
case MemoryAccessSize::Byte: case MemoryAccessSize::Byte:
@ -529,11 +519,10 @@ bool Bus::DoReadDMA(MemoryAccessSize size, u32 offset, u32& value)
break; break;
} }
value = m_dma->ReadRegister(offset); return value;
return true;
} }
bool Bus::DoWriteDMA(MemoryAccessSize size, u32 offset, u32 value) void Bus::DoWriteDMA(MemoryAccessSize size, u32 offset, u32 value)
{ {
switch (size) switch (size)
{ {
@ -552,5 +541,4 @@ bool Bus::DoWriteDMA(MemoryAccessSize size, u32 offset, u32 value)
} }
m_dma->WriteRegister(offset, value); m_dma->WriteRegister(offset, value);
return true;
} }

View File

@ -40,7 +40,7 @@ public:
bool WriteWord(PhysicalMemoryAddress address, u32 value); bool WriteWord(PhysicalMemoryAddress address, u32 value);
template<MemoryAccessType type, MemoryAccessSize size> template<MemoryAccessType type, MemoryAccessSize size>
bool DispatchAccess(PhysicalMemoryAddress address, u32& value); TickCount DispatchAccess(PhysicalMemoryAddress address, u32& value);
void PatchBIOS(u32 address, u32 value, u32 mask = UINT32_C(0xFFFFFFFF)); void PatchBIOS(u32 address, u32 value, u32 mask = UINT32_C(0xFFFFFFFF));
void SetExpansionROM(std::vector<u8> data); void SetExpansionROM(std::vector<u8> data);
@ -93,6 +93,7 @@ private:
enum : u32 enum : u32
{ {
RAM_ACCESS_DELAY = 6, // Nocash docs say RAM takes 6 cycles to access.
MEMCTRL_REG_COUNT = 9 MEMCTRL_REG_COUNT = 9
}; };
@ -148,51 +149,51 @@ private:
void RecalculateMemoryTimings(); void RecalculateMemoryTimings();
template<MemoryAccessType type, MemoryAccessSize size> template<MemoryAccessType type, MemoryAccessSize size>
bool DoRAMAccess(u32 offset, u32& value); TickCount DoRAMAccess(u32 offset, u32& value);
template<MemoryAccessType type, MemoryAccessSize size> template<MemoryAccessType type, MemoryAccessSize size>
bool DoBIOSAccess(u32 offset, u32& value); TickCount DoBIOSAccess(u32 offset, u32& value);
bool DoInvalidAccess(MemoryAccessType type, MemoryAccessSize size, PhysicalMemoryAddress address, u32& value); TickCount DoInvalidAccess(MemoryAccessType type, MemoryAccessSize size, PhysicalMemoryAddress address, u32& value);
bool DoReadEXP1(MemoryAccessSize size, u32 offset, u32& value); u32 DoReadEXP1(MemoryAccessSize size, u32 offset);
bool DoWriteEXP1(MemoryAccessSize size, u32 offset, u32 value); void DoWriteEXP1(MemoryAccessSize size, u32 offset, u32 value);
bool DoReadEXP2(MemoryAccessSize size, u32 offset, u32& value); u32 DoReadEXP2(MemoryAccessSize size, u32 offset);
bool DoWriteEXP2(MemoryAccessSize size, u32 offset, u32 value); void DoWriteEXP2(MemoryAccessSize size, u32 offset, u32 value);
bool DoReadMemoryControl(MemoryAccessSize size, u32 offset, u32& value); u32 DoReadMemoryControl(MemoryAccessSize size, u32 offset);
bool DoWriteMemoryControl(MemoryAccessSize size, u32 offset, u32 value); void DoWriteMemoryControl(MemoryAccessSize size, u32 offset, u32 value);
bool DoReadMemoryControl2(MemoryAccessSize size, u32 offset, u32& value); u32 DoReadMemoryControl2(MemoryAccessSize size, u32 offset);
bool DoWriteMemoryControl2(MemoryAccessSize size, u32 offset, u32 value); void DoWriteMemoryControl2(MemoryAccessSize size, u32 offset, u32 value);
bool DoReadPad(MemoryAccessSize size, u32 offset, u32& value); u32 DoReadPad(MemoryAccessSize size, u32 offset);
bool DoWritePad(MemoryAccessSize size, u32 offset, u32 value); void DoWritePad(MemoryAccessSize size, u32 offset, u32 value);
bool DoReadSIO(MemoryAccessSize size, u32 offset, u32& value); u32 DoReadSIO(MemoryAccessSize size, u32 offset);
bool DoWriteSIO(MemoryAccessSize size, u32 offset, u32 value); void DoWriteSIO(MemoryAccessSize size, u32 offset, u32 value);
bool DoReadCDROM(MemoryAccessSize size, u32 offset, u32& value); u32 DoReadCDROM(MemoryAccessSize size, u32 offset);
bool DoWriteCDROM(MemoryAccessSize size, u32 offset, u32 value); void DoWriteCDROM(MemoryAccessSize size, u32 offset, u32 value);
bool DoReadGPU(MemoryAccessSize size, u32 offset, u32& value); u32 DoReadGPU(MemoryAccessSize size, u32 offset);
bool DoWriteGPU(MemoryAccessSize size, u32 offset, u32 value); void DoWriteGPU(MemoryAccessSize size, u32 offset, u32 value);
bool DoReadMDEC(MemoryAccessSize size, u32 offset, u32& value); u32 DoReadMDEC(MemoryAccessSize size, u32 offset);
bool DoWriteMDEC(MemoryAccessSize size, u32 offset, u32 value); void DoWriteMDEC(MemoryAccessSize size, u32 offset, u32 value);
bool DoReadInterruptController(MemoryAccessSize size, u32 offset, u32& value); u32 DoReadInterruptController(MemoryAccessSize size, u32 offset);
bool DoWriteInterruptController(MemoryAccessSize size, u32 offset, u32 value); void DoWriteInterruptController(MemoryAccessSize size, u32 offset, u32 value);
bool DoReadDMA(MemoryAccessSize size, u32 offset, u32& value); u32 DoReadDMA(MemoryAccessSize size, u32 offset);
bool DoWriteDMA(MemoryAccessSize size, u32 offset, u32 value); void DoWriteDMA(MemoryAccessSize size, u32 offset, u32 value);
bool DoReadTimers(MemoryAccessSize size, u32 offset, u32& value); u32 DoReadTimers(MemoryAccessSize size, u32 offset);
bool DoWriteTimers(MemoryAccessSize size, u32 offset, u32 value); void DoWriteTimers(MemoryAccessSize size, u32 offset, u32 value);
bool DoReadSPU(MemoryAccessSize size, u32 offset, u32& value); u32 DoReadSPU(MemoryAccessSize size, u32 offset);
bool DoWriteSPU(MemoryAccessSize size, u32 offset, u32 value); void DoWriteSPU(MemoryAccessSize size, u32 offset, u32 value);
CPU::Core* m_cpu = nullptr; CPU::Core* m_cpu = nullptr;
DMA* m_dma = nullptr; DMA* m_dma = nullptr;
@ -204,6 +205,8 @@ private:
SPU* m_spu = nullptr; SPU* m_spu = nullptr;
MDEC* m_mdec = nullptr; MDEC* m_mdec = nullptr;
std::array<TickCount, 3> m_exp1_access_time = {};
std::array<TickCount, 3> m_exp2_access_time = {};
std::array<TickCount, 3> m_bios_access_time = {}; std::array<TickCount, 3> m_bios_access_time = {};
std::array<TickCount, 3> m_cdrom_access_time = {}; std::array<TickCount, 3> m_cdrom_access_time = {};
std::array<TickCount, 3> m_spu_access_time = {}; std::array<TickCount, 3> m_spu_access_time = {};

View File

@ -2,7 +2,7 @@
#include "bus.h" #include "bus.h"
template<MemoryAccessType type, MemoryAccessSize size> template<MemoryAccessType type, MemoryAccessSize size>
bool Bus::DoRAMAccess(u32 offset, u32& value) TickCount Bus::DoRAMAccess(u32 offset, u32& value)
{ {
// TODO: Configurable mirroring. // TODO: Configurable mirroring.
offset &= UINT32_C(0x1FFFFF); offset &= UINT32_C(0x1FFFFF);
@ -40,11 +40,12 @@ bool Bus::DoRAMAccess(u32 offset, u32& value)
} }
} }
return true; // Nocash docs say RAM takes 6 cycles to access.
return RAM_ACCESS_DELAY;
} }
template<MemoryAccessType type, MemoryAccessSize size> template<MemoryAccessType type, MemoryAccessSize size>
bool Bus::DoBIOSAccess(u32 offset, u32& value) TickCount Bus::DoBIOSAccess(u32 offset, u32& value)
{ {
// TODO: Configurable mirroring. // TODO: Configurable mirroring.
if constexpr (type == MemoryAccessType::Read) if constexpr (type == MemoryAccessType::Read)
@ -70,11 +71,11 @@ bool Bus::DoBIOSAccess(u32 offset, u32& value)
// Writes are ignored. // Writes are ignored.
} }
return true; return m_bios_access_time[static_cast<u32>(size)];
} }
template<MemoryAccessType type, MemoryAccessSize size> template<MemoryAccessType type, MemoryAccessSize size>
bool Bus::DispatchAccess(PhysicalMemoryAddress address, u32& value) TickCount Bus::DispatchAccess(PhysicalMemoryAddress address, u32& value)
{ {
if (address < 0x800000) if (address < 0x800000)
{ {
@ -86,8 +87,12 @@ bool Bus::DispatchAccess(PhysicalMemoryAddress address, u32& value)
} }
else if (address < (EXP1_BASE + EXP1_SIZE)) else if (address < (EXP1_BASE + EXP1_SIZE))
{ {
return (type == MemoryAccessType::Read) ? DoReadEXP1(size, address & EXP1_MASK, value) : if constexpr (type == MemoryAccessType::Read)
value = DoReadEXP1(size, address & EXP1_MASK);
else
DoWriteEXP1(size, address & EXP1_MASK, value); DoWriteEXP1(size, address & EXP1_MASK, value);
return m_exp1_access_time[static_cast<u32>(size)];
} }
else if (address < MEMCTRL_BASE) else if (address < MEMCTRL_BASE)
{ {
@ -95,39 +100,66 @@ bool Bus::DispatchAccess(PhysicalMemoryAddress address, u32& value)
} }
else if (address < (MEMCTRL_BASE + MEMCTRL_SIZE)) else if (address < (MEMCTRL_BASE + MEMCTRL_SIZE))
{ {
return (type == MemoryAccessType::Read) ? DoReadMemoryControl(size, address & PAD_MASK, value) : if constexpr (type == MemoryAccessType::Read)
value = DoReadMemoryControl(size, address & PAD_MASK);
else
DoWriteMemoryControl(size, address & PAD_MASK, value); DoWriteMemoryControl(size, address & PAD_MASK, value);
return 1;
} }
else if (address < (PAD_BASE + PAD_SIZE)) else if (address < (PAD_BASE + PAD_SIZE))
{ {
return (type == MemoryAccessType::Read) ? DoReadPad(size, address & PAD_MASK, value) : if constexpr (type == MemoryAccessType::Read)
value = DoReadPad(size, address & PAD_MASK);
else
DoWritePad(size, address & PAD_MASK, value); DoWritePad(size, address & PAD_MASK, value);
return 1;
} }
else if (address < (SIO_BASE + SIO_SIZE)) else if (address < (SIO_BASE + SIO_SIZE))
{ {
return (type == MemoryAccessType::Read) ? DoReadSIO(size, address & SIO_MASK, value) : if constexpr (type == MemoryAccessType::Read)
value = DoReadSIO(size, address & SIO_MASK);
else
DoWriteSIO(size, address & SIO_MASK, value); DoWriteSIO(size, address & SIO_MASK, value);
return 1;
} }
else if (address < (MEMCTRL2_BASE + MEMCTRL2_SIZE)) else if (address < (MEMCTRL2_BASE + MEMCTRL2_SIZE))
{ {
return (type == MemoryAccessType::Read) ? DoReadMemoryControl2(size, address & PAD_MASK, value) : if constexpr (type == MemoryAccessType::Read)
value = DoReadMemoryControl2(size, address & PAD_MASK);
else
DoWriteMemoryControl2(size, address & PAD_MASK, value); DoWriteMemoryControl2(size, address & PAD_MASK, value);
return 1;
} }
else if (address < (INTERRUPT_CONTROLLER_BASE + INTERRUPT_CONTROLLER_SIZE)) else if (address < (INTERRUPT_CONTROLLER_BASE + INTERRUPT_CONTROLLER_SIZE))
{ {
return (type == MemoryAccessType::Read) ? if constexpr (type == MemoryAccessType::Read)
DoReadInterruptController(size, address & INTERRUPT_CONTROLLER_MASK, value) : value = DoReadInterruptController(size, address & INTERRUPT_CONTROLLER_MASK);
else
DoWriteInterruptController(size, address & INTERRUPT_CONTROLLER_MASK, value); DoWriteInterruptController(size, address & INTERRUPT_CONTROLLER_MASK, value);
return 1;
} }
else if (address < (DMA_BASE + DMA_SIZE)) else if (address < (DMA_BASE + DMA_SIZE))
{ {
return (type == MemoryAccessType::Read) ? DoReadDMA(size, address & DMA_MASK, value) : if constexpr (type == MemoryAccessType::Read)
value = DoReadDMA(size, address & DMA_MASK);
else
DoWriteDMA(size, address & DMA_MASK, value); DoWriteDMA(size, address & DMA_MASK, value);
return 1;
} }
else if (address < (TIMERS_BASE + TIMERS_SIZE)) else if (address < (TIMERS_BASE + TIMERS_SIZE))
{ {
return (type == MemoryAccessType::Read) ? DoReadTimers(size, address & TIMERS_MASK, value) : if constexpr (type == MemoryAccessType::Read)
value = DoReadTimers(size, address & TIMERS_MASK);
else
DoWriteTimers(size, address & TIMERS_MASK, value); DoWriteTimers(size, address & TIMERS_MASK, value);
return 1;
} }
else if (address < CDROM_BASE) else if (address < CDROM_BASE)
{ {
@ -135,18 +167,30 @@ bool Bus::DispatchAccess(PhysicalMemoryAddress address, u32& value)
} }
else if (address < (CDROM_BASE + GPU_SIZE)) else if (address < (CDROM_BASE + GPU_SIZE))
{ {
return (type == MemoryAccessType::Read) ? DoReadCDROM(size, address & CDROM_MASK, value) : if constexpr (type == MemoryAccessType::Read)
value = DoReadCDROM(size, address & CDROM_MASK);
else
DoWriteCDROM(size, address & CDROM_MASK, value); DoWriteCDROM(size, address & CDROM_MASK, value);
return m_cdrom_access_time[static_cast<u32>(size)];
} }
else if (address < (GPU_BASE + GPU_SIZE)) else if (address < (GPU_BASE + GPU_SIZE))
{ {
return (type == MemoryAccessType::Read) ? DoReadGPU(size, address & GPU_MASK, value) : if constexpr (type == MemoryAccessType::Read)
value = DoReadGPU(size, address & GPU_MASK);
else
DoWriteGPU(size, address & GPU_MASK, value); DoWriteGPU(size, address & GPU_MASK, value);
return 1;
} }
else if (address < (MDEC_BASE + MDEC_SIZE)) else if (address < (MDEC_BASE + MDEC_SIZE))
{ {
return (type == MemoryAccessType::Read) ? DoReadMDEC(size, address & MDEC_MASK, value) : if constexpr (type == MemoryAccessType::Read)
value = DoReadMDEC(size, address & MDEC_MASK);
else
DoWriteMDEC(size, address & MDEC_MASK, value); DoWriteMDEC(size, address & MDEC_MASK, value);
return 1;
} }
else if (address < SPU_BASE) else if (address < SPU_BASE)
{ {
@ -154,8 +198,12 @@ bool Bus::DispatchAccess(PhysicalMemoryAddress address, u32& value)
} }
else if (address < (SPU_BASE + SPU_SIZE)) else if (address < (SPU_BASE + SPU_SIZE))
{ {
return (type == MemoryAccessType::Read) ? DoReadSPU(size, address & SPU_MASK, value) : if constexpr (type == MemoryAccessType::Read)
value = DoReadSPU(size, address & SPU_MASK);
else
DoWriteSPU(size, address & SPU_MASK, value); DoWriteSPU(size, address & SPU_MASK, value);
return m_spu_access_time[static_cast<u32>(size)];
} }
else if (address < EXP2_BASE) else if (address < EXP2_BASE)
{ {
@ -163,8 +211,12 @@ bool Bus::DispatchAccess(PhysicalMemoryAddress address, u32& value)
} }
else if (address < (EXP2_BASE + EXP2_SIZE)) else if (address < (EXP2_BASE + EXP2_SIZE))
{ {
return (type == MemoryAccessType::Read) ? DoReadEXP2(size, address & EXP2_MASK, value) : if constexpr (type == MemoryAccessType::Read)
value = DoReadEXP2(size, address & EXP2_MASK);
else
DoWriteEXP2(size, address & EXP2_MASK, value); DoWriteEXP2(size, address & EXP2_MASK, value);
return m_exp2_access_time[static_cast<u32>(size)];
} }
else if (address < BIOS_BASE) else if (address < BIOS_BASE)
{ {

View File

@ -95,12 +95,16 @@ void Core::SetPC(u32 new_pc)
bool Core::ReadMemoryByte(VirtualMemoryAddress addr, u8* value) bool Core::ReadMemoryByte(VirtualMemoryAddress addr, u8* value)
{ {
u32 temp = 0; u32 temp = 0;
const bool result = DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::Byte>(addr, temp); const TickCount cycles = DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::Byte>(addr, temp);
*value = Truncate8(temp); *value = Truncate8(temp);
if (!result) if (cycles < 0)
{
RaiseException(Exception::DBE); RaiseException(Exception::DBE);
return false;
}
return result; AddTicks(cycles - 1);
return true;
} }
bool Core::ReadMemoryHalfWord(VirtualMemoryAddress addr, u16* value) bool Core::ReadMemoryHalfWord(VirtualMemoryAddress addr, u16* value)
@ -109,12 +113,16 @@ bool Core::ReadMemoryHalfWord(VirtualMemoryAddress addr, u16* value)
return false; return false;
u32 temp = 0; u32 temp = 0;
const bool result = DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::HalfWord>(addr, temp); const TickCount cycles = DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::HalfWord>(addr, temp);
*value = Truncate16(temp); *value = Truncate16(temp);
if (!result) if (cycles < 0)
{
RaiseException(Exception::DBE); RaiseException(Exception::DBE);
return false;
}
return result; AddTicks(cycles - 1);
return true;
} }
bool Core::ReadMemoryWord(VirtualMemoryAddress addr, u32* value) bool Core::ReadMemoryWord(VirtualMemoryAddress addr, u32* value)
@ -122,21 +130,29 @@ bool Core::ReadMemoryWord(VirtualMemoryAddress addr, u32* value)
if (!DoAlignmentCheck<MemoryAccessType::Read, MemoryAccessSize::Word>(addr)) if (!DoAlignmentCheck<MemoryAccessType::Read, MemoryAccessSize::Word>(addr))
return false; return false;
const bool result = DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::Word>(addr, *value); const TickCount cycles = DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::Word>(addr, *value);
if (!result) if (cycles < 0)
{
RaiseException(Exception::DBE); RaiseException(Exception::DBE);
return false;
}
return result; AddTicks(cycles - 1);
return true;
} }
bool Core::WriteMemoryByte(VirtualMemoryAddress addr, u8 value) bool Core::WriteMemoryByte(VirtualMemoryAddress addr, u8 value)
{ {
u32 temp = ZeroExtend32(value); u32 temp = ZeroExtend32(value);
const bool result = DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Byte>(addr, temp); const TickCount cycles = DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Byte>(addr, temp);
if (!result) if (cycles < 0)
{
RaiseException(Exception::DBE); RaiseException(Exception::DBE);
return false;
}
return result; AddTicks(cycles - 1);
return true;
} }
bool Core::WriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value) bool Core::WriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value)
@ -145,11 +161,15 @@ bool Core::WriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value)
return false; return false;
u32 temp = ZeroExtend32(value); u32 temp = ZeroExtend32(value);
const bool result = DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::HalfWord>(addr, temp); const TickCount cycles = DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::HalfWord>(addr, temp);
if (!result) if (cycles < 0)
{
RaiseException(Exception::DBE); RaiseException(Exception::DBE);
return false;
}
return result; AddTicks(cycles - 1);
return cycles;
} }
bool Core::WriteMemoryWord(VirtualMemoryAddress addr, u32 value) bool Core::WriteMemoryWord(VirtualMemoryAddress addr, u32 value)
@ -157,49 +177,53 @@ bool Core::WriteMemoryWord(VirtualMemoryAddress addr, u32 value)
if (!DoAlignmentCheck<MemoryAccessType::Write, MemoryAccessSize::Word>(addr)) if (!DoAlignmentCheck<MemoryAccessType::Write, MemoryAccessSize::Word>(addr))
return false; return false;
const bool result = DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Word>(addr, value); const TickCount cycles = DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Word>(addr, value);
if (!result) if (cycles < 0)
{
RaiseException(Exception::DBE); RaiseException(Exception::DBE);
return false;
}
return result; AddTicks(cycles - 1);
return true;
} }
bool Core::SafeReadMemoryByte(VirtualMemoryAddress addr, u8* value) bool Core::SafeReadMemoryByte(VirtualMemoryAddress addr, u8* value)
{ {
u32 temp = 0; u32 temp = 0;
const bool result = DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::Byte>(addr, temp); const TickCount cycles = DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::Byte>(addr, temp);
*value = Truncate8(temp); *value = Truncate8(temp);
return result; return (cycles >= 0);
} }
bool Core::SafeReadMemoryHalfWord(VirtualMemoryAddress addr, u16* value) bool Core::SafeReadMemoryHalfWord(VirtualMemoryAddress addr, u16* value)
{ {
u32 temp = 0; u32 temp = 0;
const bool result = DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::HalfWord>(addr, temp); const TickCount cycles = DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::HalfWord>(addr, temp);
*value = Truncate16(temp); *value = Truncate16(temp);
return result; return (cycles >= 0);
} }
bool Core::SafeReadMemoryWord(VirtualMemoryAddress addr, u32* value) bool Core::SafeReadMemoryWord(VirtualMemoryAddress addr, u32* value)
{ {
return DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::Word>(addr, *value); return DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::Word>(addr, *value) >= 0;
} }
bool Core::SafeWriteMemoryByte(VirtualMemoryAddress addr, u8 value) bool Core::SafeWriteMemoryByte(VirtualMemoryAddress addr, u8 value)
{ {
u32 temp = ZeroExtend32(value); u32 temp = ZeroExtend32(value);
return DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Byte>(addr, temp); return DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Byte>(addr, temp) >= 0;
} }
bool Core::SafeWriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value) bool Core::SafeWriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value)
{ {
u32 temp = ZeroExtend32(value); u32 temp = ZeroExtend32(value);
return DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::HalfWord>(addr, temp); return DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::HalfWord>(addr, temp) >= 0;
} }
bool Core::SafeWriteMemoryWord(VirtualMemoryAddress addr, u32 value) bool Core::SafeWriteMemoryWord(VirtualMemoryAddress addr, u32 value)
{ {
return DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Word>(addr, value); return DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Word>(addr, value) >= 0;
} }
void Core::Branch(u32 target) void Core::Branch(u32 target)
@ -235,10 +259,13 @@ void Core::RaiseException(Exception excode)
void Core::RaiseException(Exception excode, u32 EPC, bool BD, bool BT, u8 CE) void Core::RaiseException(Exception excode, u32 EPC, bool BD, bool BT, u8 CE)
{ {
#ifdef Y_BUILD_CONFIG_DEBUG
if (excode != Exception::INT && excode != Exception::Syscall && excode != Exception::BP)
{
Log_DebugPrintf("Exception %u at 0x%08X (epc=0x%08X, BD=%s, CE=%u)", static_cast<u32>(excode), Log_DebugPrintf("Exception %u at 0x%08X (epc=0x%08X, BD=%s, CE=%u)", static_cast<u32>(excode),
m_current_instruction_pc, EPC, BD ? "true" : "false", ZeroExtend32(CE)); m_current_instruction_pc, EPC, BD ? "true" : "false", ZeroExtend32(CE));
#ifdef Y_BUILD_CONFIG_DEBUG
DisassembleAndPrint(m_current_instruction_pc, 4, 0); DisassembleAndPrint(m_current_instruction_pc, 4, 0);
}
#endif #endif
m_cop0_regs.EPC = EPC; m_cop0_regs.EPC = EPC;
@ -499,8 +526,8 @@ void Core::Execute()
{ {
while (m_downcount >= 0) while (m_downcount >= 0)
{ {
m_pending_ticks += 2; m_pending_ticks += 1;
m_downcount -= 2; m_downcount -= 1;
// now executing the instruction we previously fetched // now executing the instruction we previously fetched
m_current_instruction = m_next_instruction; m_current_instruction = m_next_instruction;
@ -542,7 +569,7 @@ bool Core::FetchInstruction()
RaiseException(Exception::AdEL, m_regs.npc, false, false, 0); RaiseException(Exception::AdEL, m_regs.npc, false, false, 0);
return false; return false;
} }
else if (!DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::Word>(m_regs.npc, m_next_instruction.bits)) else if (DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::Word>(m_regs.npc, m_next_instruction.bits) < 0)
{ {
// Bus errors don't set BadVaddr. // Bus errors don't set BadVaddr.
RaiseException(Exception::IBE, m_regs.npc, false, false, 0); RaiseException(Exception::IBE, m_regs.npc, false, false, 0);

View File

@ -56,7 +56,7 @@ public:
private: private:
template<MemoryAccessType type, MemoryAccessSize size> template<MemoryAccessType type, MemoryAccessSize size>
bool DoMemoryAccess(VirtualMemoryAddress address, u32& value); TickCount DoMemoryAccess(VirtualMemoryAddress address, u32& value);
template<MemoryAccessType type, MemoryAccessSize size> template<MemoryAccessType type, MemoryAccessSize size>
bool DoAlignmentCheck(VirtualMemoryAddress address); bool DoAlignmentCheck(VirtualMemoryAddress address);
@ -75,6 +75,13 @@ private:
bool InUserMode() const { return m_cop0_regs.sr.KUc; } bool InUserMode() const { return m_cop0_regs.sr.KUc; }
bool InKernelMode() const { return !m_cop0_regs.sr.KUc; } bool InKernelMode() const { return !m_cop0_regs.sr.KUc; }
// timing
void AddTicks(TickCount ticks)
{
m_pending_ticks += ticks;
m_downcount -= ticks;
}
void DisassembleAndPrint(u32 addr); void DisassembleAndPrint(u32 addr);
void DisassembleAndPrint(u32 addr, u32 instructions_before, u32 instructions_after); void DisassembleAndPrint(u32 addr, u32 instructions_before, u32 instructions_after);

View File

@ -6,7 +6,7 @@
namespace CPU { namespace CPU {
template<MemoryAccessType type, MemoryAccessSize size> template<MemoryAccessType type, MemoryAccessSize size>
bool Core::DoMemoryAccess(VirtualMemoryAddress address, u32& value) TickCount Core::DoMemoryAccess(VirtualMemoryAddress address, u32& value)
{ {
switch (address >> 29) switch (address >> 29)
{ {
@ -15,23 +15,17 @@ bool Core::DoMemoryAccess(VirtualMemoryAddress address, u32& value)
if constexpr (type == MemoryAccessType::Write) if constexpr (type == MemoryAccessType::Write)
{ {
if (m_cop0_regs.sr.Isc) if (m_cop0_regs.sr.Isc)
return true; return 1;
} }
const PhysicalMemoryAddress phys_addr = address & UINT32_C(0x1FFFFFFF); const PhysicalMemoryAddress phys_addr = address & UINT32_C(0x1FFFFFFF);
if ((phys_addr & DCACHE_LOCATION_MASK) == DCACHE_LOCATION) if ((phys_addr & DCACHE_LOCATION_MASK) == DCACHE_LOCATION)
{ {
DoScratchpadAccess<type, size>(phys_addr, value); DoScratchpadAccess<type, size>(phys_addr, value);
return true; return 1;
} }
if (!m_bus->DispatchAccess<type, size>(phys_addr, value)) return m_bus->DispatchAccess<type, size>(phys_addr, value);
{
Panic("Bus error");
return false;
}
return true;
} }
case 0x01: // KUSEG 512M-1024M case 0x01: // KUSEG 512M-1024M
@ -39,7 +33,7 @@ bool Core::DoMemoryAccess(VirtualMemoryAddress address, u32& value)
case 0x03: // KUSEG 1536M-2048M case 0x03: // KUSEG 1536M-2048M
{ {
// Above 512mb raises an exception. // Above 512mb raises an exception.
return false; return -1;
} }
case 0x04: // KSEG0 - physical memory cached case 0x04: // KSEG0 - physical memory cached
@ -47,36 +41,24 @@ bool Core::DoMemoryAccess(VirtualMemoryAddress address, u32& value)
if constexpr (type == MemoryAccessType::Write) if constexpr (type == MemoryAccessType::Write)
{ {
if (m_cop0_regs.sr.Isc) if (m_cop0_regs.sr.Isc)
return true; return 1;
} }
const PhysicalMemoryAddress phys_addr = address & UINT32_C(0x1FFFFFFF); const PhysicalMemoryAddress phys_addr = address & UINT32_C(0x1FFFFFFF);
if ((phys_addr & DCACHE_LOCATION_MASK) == DCACHE_LOCATION) if ((phys_addr & DCACHE_LOCATION_MASK) == DCACHE_LOCATION)
{ {
DoScratchpadAccess<type, size>(phys_addr, value); DoScratchpadAccess<type, size>(phys_addr, value);
return true; return 1;
} }
if (!m_bus->DispatchAccess<type, size>(phys_addr, value)) return m_bus->DispatchAccess<type, size>(phys_addr, value);
{
Panic("Bus error");
return false;
}
return true;
} }
break; break;
case 0x05: // KSEG1 - physical memory uncached case 0x05: // KSEG1 - physical memory uncached
{ {
const PhysicalMemoryAddress phys_addr = address & UINT32_C(0x1FFFFFFF); const PhysicalMemoryAddress phys_addr = address & UINT32_C(0x1FFFFFFF);
if (!m_bus->DispatchAccess<type, size>(phys_addr, value)) return m_bus->DispatchAccess<type, size>(phys_addr, value);
{
Panic("Bus error");
return false;
}
return true;
} }
break; break;
@ -90,11 +72,11 @@ bool Core::DoMemoryAccess(VirtualMemoryAddress address, u32& value)
else else
WriteCacheControl(value); WriteCacheControl(value);
return true; return 1;
} }
else else
{ {
return false; return -1;
} }
} }