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)
{
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_bios.data(), m_bios.size());
sw.DoArray(m_MEMCTRL.regs, countof(m_MEMCTRL.regs));
@ -223,7 +228,7 @@ void Bus::RecalculateMemoryTimings()
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;
str.AppendString("Invalid bus ");
@ -247,13 +252,16 @@ bool Bus::DoInvalidAccess(MemoryAccessType type, MemoryAccessSize size, Physical
if (type == MemoryAccessType::Read)
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())
return DoInvalidAccess(MemoryAccessType::Read, size, EXP1_BASE | offset, value);
{
// EXP1 not present.
return UINT32_C(0xFFFFFFFF);
}
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);
if ((offset + transfer_size) > m_exp1_rom.size())
{
value = UINT32_C(0);
return true;
return UINT32_C(0);
}
u32 value;
if (size == MemoryAccessSize::Byte)
{
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);
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
if (offset == 0x21)
{
value = 0x04 | 0x08;
return true;
return 0x04 | 0x08;
}
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 (value == '\r')
return true;
return;
if (value == '\n')
{
@ -326,26 +330,26 @@ bool Bus::DoWriteEXP2(MemoryAccessSize size, u32 offset, u32 value)
m_tty_line_buffer.AppendCharacter(Truncate8(value));
}
return true;
return;
}
if (offset == 0x41)
{
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);
value = m_MEMCTRL.regs[offset / 4];
return true;
return value;
}
bool Bus::DoWriteMemoryControl(MemoryAccessSize size, u32 offset, u32 value)
void Bus::DoWriteMemoryControl(MemoryAccessSize size, u32 offset, u32 value)
{
FixupUnalignedWordAccessW32(offset, value);
@ -357,165 +361,151 @@ bool Bus::DoWriteMemoryControl(MemoryAccessSize size, u32 offset, u32 value)
m_MEMCTRL.regs[index] = new_value;
RecalculateMemoryTimings();
}
return true;
}
bool Bus::DoReadMemoryControl2(MemoryAccessSize size, u32 offset, u32& value)
u32 Bus::DoReadMemoryControl2(MemoryAccessSize size, u32 offset)
{
if (offset == 0x00)
{
value = m_ram_size_reg;
return true;
}
return m_ram_size_reg;
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)
{
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 true;
return m_pad->ReadRegister(offset);
}
bool Bus::DoWritePad(MemoryAccessSize size, u32 offset, u32 value)
void Bus::DoWritePad(MemoryAccessSize size, u32 offset, u32 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);
value = 0;
if (offset == 0x04)
value = 0x5;
return true;
return 0x5;
else
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);
return true;
}
bool Bus::DoReadCDROM(MemoryAccessSize size, u32 offset, u32& value)
u32 Bus::DoReadCDROM(MemoryAccessSize size, u32 offset)
{
// TODO: Splitting of half/word reads.
Assert(size == MemoryAccessSize::Byte);
value = ZeroExtend32(m_cdrom->ReadRegister(offset));
return true;
return ZeroExtend32(m_cdrom->ReadRegister(offset));
}
bool Bus::DoWriteCDROM(MemoryAccessSize size, u32 offset, u32 value)
void Bus::DoWriteCDROM(MemoryAccessSize size, u32 offset, u32 value)
{
// TODO: Splitting of half/word reads.
Assert(size == MemoryAccessSize::Byte);
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);
value = m_gpu->ReadRegister(offset);
return true;
return m_gpu->ReadRegister(offset);
}
bool Bus::DoWriteGPU(MemoryAccessSize size, u32 offset, u32 value)
void Bus::DoWriteGPU(MemoryAccessSize size, u32 offset, u32 value)
{
Assert(size == MemoryAccessSize::Word);
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);
value = m_mdec->ReadRegister(offset);
return true;
return m_mdec->ReadRegister(offset);
}
bool Bus::DoWriteMDEC(MemoryAccessSize size, u32 offset, u32 value)
void Bus::DoWriteMDEC(MemoryAccessSize size, u32 offset, u32 value)
{
Assert(size == MemoryAccessSize::Word);
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);
value = m_interrupt_controller->ReadRegister(offset);
return true;
return value;
}
bool Bus::DoWriteInterruptController(MemoryAccessSize size, u32 offset, u32 value)
void Bus::DoWriteInterruptController(MemoryAccessSize size, u32 offset, u32 value)
{
FixupUnalignedWordAccessW32(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);
value = m_timers->ReadRegister(offset);
return true;
return value;
}
bool Bus::DoWriteTimers(MemoryAccessSize size, u32 offset, u32 value)
void Bus::DoWriteTimers(MemoryAccessSize size, u32 offset, u32 value)
{
FixupUnalignedWordAccessW32(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)
{
const u16 lsb = m_spu->ReadRegister(offset);
const u16 msb = m_spu->ReadRegister(offset + 2);
value = ZeroExtend32(lsb) | (ZeroExtend32(msb) << 16);
return ZeroExtend32(lsb) | (ZeroExtend32(msb) << 16);
}
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.
// TODO: Ignore if address is not aligned.
if (size == MemoryAccessSize::Word)
{
Assert(Common::IsAlignedPow2(offset, 2));
m_spu->WriteRegister(offset, Truncate16(value));
m_spu->WriteRegister(offset + 2, Truncate16(value >> 16));
return true;
return;
}
Assert(Common::IsAlignedPow2(offset, 2));
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)
{
case MemoryAccessSize::Byte:
@ -529,11 +519,10 @@ bool Bus::DoReadDMA(MemoryAccessSize size, u32 offset, u32& value)
break;
}
value = m_dma->ReadRegister(offset);
return true;
return value;
}
bool Bus::DoWriteDMA(MemoryAccessSize size, u32 offset, u32 value)
void Bus::DoWriteDMA(MemoryAccessSize size, u32 offset, u32 value)
{
switch (size)
{
@ -552,5 +541,4 @@ bool Bus::DoWriteDMA(MemoryAccessSize size, u32 offset, u32 value)
}
m_dma->WriteRegister(offset, value);
return true;
}

View file

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

View file

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

View file

@ -95,12 +95,16 @@ void Core::SetPC(u32 new_pc)
bool Core::ReadMemoryByte(VirtualMemoryAddress addr, u8* value)
{
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);
if (!result)
if (cycles < 0)
{
RaiseException(Exception::DBE);
return false;
}
return result;
AddTicks(cycles - 1);
return true;
}
bool Core::ReadMemoryHalfWord(VirtualMemoryAddress addr, u16* value)
@ -109,12 +113,16 @@ bool Core::ReadMemoryHalfWord(VirtualMemoryAddress addr, u16* value)
return false;
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);
if (!result)
if (cycles < 0)
{
RaiseException(Exception::DBE);
return false;
}
return result;
AddTicks(cycles - 1);
return true;
}
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))
return false;
const bool result = DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::Word>(addr, *value);
if (!result)
const TickCount cycles = DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::Word>(addr, *value);
if (cycles < 0)
{
RaiseException(Exception::DBE);
return false;
}
return result;
AddTicks(cycles - 1);
return true;
}
bool Core::WriteMemoryByte(VirtualMemoryAddress addr, u8 value)
{
u32 temp = ZeroExtend32(value);
const bool result = DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Byte>(addr, temp);
if (!result)
const TickCount cycles = DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Byte>(addr, temp);
if (cycles < 0)
{
RaiseException(Exception::DBE);
return false;
}
return result;
AddTicks(cycles - 1);
return true;
}
bool Core::WriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value)
@ -145,11 +161,15 @@ bool Core::WriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value)
return false;
u32 temp = ZeroExtend32(value);
const bool result = DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::HalfWord>(addr, temp);
if (!result)
const TickCount cycles = DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::HalfWord>(addr, temp);
if (cycles < 0)
{
RaiseException(Exception::DBE);
return false;
}
return result;
AddTicks(cycles - 1);
return cycles;
}
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))
return false;
const bool result = DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Word>(addr, value);
if (!result)
const TickCount cycles = DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Word>(addr, value);
if (cycles < 0)
{
RaiseException(Exception::DBE);
return false;
}
return result;
AddTicks(cycles - 1);
return true;
}
bool Core::SafeReadMemoryByte(VirtualMemoryAddress addr, u8* value)
{
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);
return result;
return (cycles >= 0);
}
bool Core::SafeReadMemoryHalfWord(VirtualMemoryAddress addr, u16* value)
{
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);
return result;
return (cycles >= 0);
}
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)
{
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)
{
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)
{
return DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Word>(addr, value);
return DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Word>(addr, value) >= 0;
}
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)
{
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));
#ifdef Y_BUILD_CONFIG_DEBUG
DisassembleAndPrint(m_current_instruction_pc, 4, 0);
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),
m_current_instruction_pc, EPC, BD ? "true" : "false", ZeroExtend32(CE));
DisassembleAndPrint(m_current_instruction_pc, 4, 0);
}
#endif
m_cop0_regs.EPC = EPC;
@ -499,8 +526,8 @@ void Core::Execute()
{
while (m_downcount >= 0)
{
m_pending_ticks += 2;
m_downcount -= 2;
m_pending_ticks += 1;
m_downcount -= 1;
// now executing the instruction we previously fetched
m_current_instruction = m_next_instruction;
@ -542,7 +569,7 @@ bool Core::FetchInstruction()
RaiseException(Exception::AdEL, m_regs.npc, false, false, 0);
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.
RaiseException(Exception::IBE, m_regs.npc, false, false, 0);

View file

@ -56,7 +56,7 @@ public:
private:
template<MemoryAccessType type, MemoryAccessSize size>
bool DoMemoryAccess(VirtualMemoryAddress address, u32& value);
TickCount DoMemoryAccess(VirtualMemoryAddress address, u32& value);
template<MemoryAccessType type, MemoryAccessSize size>
bool DoAlignmentCheck(VirtualMemoryAddress address);
@ -75,6 +75,13 @@ private:
bool InUserMode() 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, u32 instructions_before, u32 instructions_after);

View file

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