Bus: Handle CPU-internal narrow writes

The full 32-bits of the GPR are used.

Fixes SoundScope in the BIOS Shell.
This commit is contained in:
Connor McLaughlin
2021-04-26 21:57:00 +10:00
parent 7fb5d6908f
commit 40731b49fc
10 changed files with 102 additions and 124 deletions

View File

@ -111,20 +111,17 @@ static void ReleaseMemory();
static void SetCodePageFastmemProtection(u32 page_index, bool writable);
#define FIXUP_WORD_READ_OFFSET(offset) ((offset) & ~u32(3))
#define FIXUP_WORD_READ_VALUE(offset, value) ((value) >> (((offset)&u32(3)) * 8u))
#define FIXUP_HALFWORD_READ_OFFSET(offset) ((offset) & ~u32(1))
#define FIXUP_HALFWORD_READ_VALUE(offset, value) ((value) >> (((offset)&u32(1)) * 8u))
#define FIXUP_HALFWORD_WRITE_VALUE(offset, value) ((value) << (((offset)&u32(1)) * 8u))
#define FIXUP_HALFWORD_OFFSET(size, offset) ((size >= MemoryAccessSize::HalfWord) ? (offset) : ((offset) & ~1u))
#define FIXUP_HALFWORD_READ_VALUE(size, offset, value) \
((size >= MemoryAccessSize::HalfWord) ? (value) : ((value) >> (((offset)&u32(1)) * 8u)))
#define FIXUP_HALFWORD_WRITE_VALUE(size, offset, value) \
((size >= MemoryAccessSize::HalfWord) ? (value) : ((value) << (((offset)&u32(1)) * 8u)))
// Offset and value remapping for (w32) registers from nocash docs.
// TODO: Make template function based on type, and noop for word access
ALWAYS_INLINE static void FixupUnalignedWordAccessW32(u32& offset, u32& value)
{
const u32 byte_offset = offset & u32(3);
offset &= ~u32(3);
value <<= byte_offset * 8;
}
#define FIXUP_WORD_OFFSET(size, offset) ((size == MemoryAccessSize::Word) ? (offset) : ((offset) & ~3u))
#define FIXUP_WORD_READ_VALUE(size, offset, value) \
((size == MemoryAccessSize::Word) ? (value) : ((value) >> (((offset)&3u) * 8)))
#define FIXUP_WORD_WRITE_VALUE(size, offset, value) \
((size == MemoryAccessSize::Word) ? (value) : ((value) << (((offset)&3u) * 8)))
bool Initialize()
{
@ -971,15 +968,15 @@ ALWAYS_INLINE static TickCount DoMemoryControlAccess(u32 offset, u32& value)
{
if constexpr (type == MemoryAccessType::Read)
{
value = m_MEMCTRL.regs[offset / 4];
FixupUnalignedWordAccessW32(offset, value);
value = m_MEMCTRL.regs[FIXUP_WORD_OFFSET(size, offset) / 4];
value = FIXUP_WORD_READ_VALUE(size, offset, value);
return 2;
}
else
{
FixupUnalignedWordAccessW32(offset, value);
const u32 index = FIXUP_WORD_OFFSET(size, offset) / 4;
value = FIXUP_WORD_WRITE_VALUE(size, offset, value);
const u32 index = offset / 4;
const u32 write_mask = (index == 8) ? COMDELAY::WRITE_MASK : MEMDELAY::WRITE_MASK;
const u32 new_value = (m_MEMCTRL.regs[index] & ~write_mask) | (value & write_mask);
if (m_MEMCTRL.regs[index] != new_value)
@ -1027,12 +1024,13 @@ ALWAYS_INLINE static TickCount DoPadAccess(u32 offset, u32& value)
{
if constexpr (type == MemoryAccessType::Read)
{
value = g_pad.ReadRegister(offset);
value = g_pad.ReadRegister(FIXUP_HALFWORD_OFFSET(size, offset));
value = FIXUP_HALFWORD_READ_VALUE(size, offset, value);
return 2;
}
else
{
g_pad.WriteRegister(offset, value);
g_pad.WriteRegister(FIXUP_HALFWORD_OFFSET(size, offset), FIXUP_HALFWORD_WRITE_VALUE(size, offset, value));
return 0;
}
}
@ -1042,12 +1040,13 @@ ALWAYS_INLINE static TickCount DoSIOAccess(u32 offset, u32& value)
{
if constexpr (type == MemoryAccessType::Read)
{
value = g_sio.ReadRegister(offset);
value = g_sio.ReadRegister(FIXUP_HALFWORD_OFFSET(size, offset));
value = FIXUP_HALFWORD_READ_VALUE(size, offset, value);
return 2;
}
else
{
g_sio.WriteRegister(offset, value);
g_sio.WriteRegister(FIXUP_HALFWORD_OFFSET(size, offset), FIXUP_HALFWORD_WRITE_VALUE(size, offset, value));
return 0;
}
}
@ -1117,14 +1116,13 @@ ALWAYS_INLINE static TickCount DoGPUAccess(u32 offset, u32& value)
{
if constexpr (type == MemoryAccessType::Read)
{
value = g_gpu->ReadRegister(offset);
FixupUnalignedWordAccessW32(offset, value);
value = g_gpu->ReadRegister(FIXUP_WORD_OFFSET(size, offset));
value = FIXUP_WORD_READ_VALUE(size, offset, value);
return 2;
}
else
{
FixupUnalignedWordAccessW32(offset, value);
g_gpu->WriteRegister(offset, value);
g_gpu->WriteRegister(FIXUP_WORD_OFFSET(size, offset), FIXUP_WORD_WRITE_VALUE(size, offset, value));
return 0;
}
}
@ -1134,14 +1132,13 @@ ALWAYS_INLINE static TickCount DoMDECAccess(u32 offset, u32& value)
{
if constexpr (type == MemoryAccessType::Read)
{
value = g_mdec.ReadRegister(offset);
FixupUnalignedWordAccessW32(offset, value);
value = g_mdec.ReadRegister(FIXUP_WORD_OFFSET(size, offset));
value = FIXUP_WORD_READ_VALUE(size, offset, value);
return 2;
}
else
{
FixupUnalignedWordAccessW32(offset, value);
g_mdec.WriteRegister(offset, value);
g_mdec.WriteRegister(FIXUP_WORD_OFFSET(size, offset), FIXUP_WORD_WRITE_VALUE(size, offset, value));
return 0;
}
}
@ -1151,14 +1148,13 @@ ALWAYS_INLINE static TickCount DoAccessInterruptController(u32 offset, u32& valu
{
if constexpr (type == MemoryAccessType::Read)
{
value = g_interrupt_controller.ReadRegister(offset);
FixupUnalignedWordAccessW32(offset, value);
value = g_interrupt_controller.ReadRegister(FIXUP_WORD_OFFSET(size, offset));
value = FIXUP_WORD_READ_VALUE(size, offset, value);
return 2;
}
else
{
FixupUnalignedWordAccessW32(offset, value);
g_interrupt_controller.WriteRegister(offset, value);
g_interrupt_controller.WriteRegister(FIXUP_WORD_OFFSET(size, offset), FIXUP_WORD_WRITE_VALUE(size, offset, value));
return 0;
}
}
@ -1168,14 +1164,13 @@ ALWAYS_INLINE static TickCount DoAccessTimers(u32 offset, u32& value)
{
if constexpr (type == MemoryAccessType::Read)
{
value = g_timers.ReadRegister(offset);
FixupUnalignedWordAccessW32(offset, value);
value = g_timers.ReadRegister(FIXUP_WORD_OFFSET(size, offset));
value = FIXUP_WORD_READ_VALUE(size, offset, value);
return 2;
}
else
{
FixupUnalignedWordAccessW32(offset, value);
g_timers.WriteRegister(offset, value);
g_timers.WriteRegister(FIXUP_WORD_OFFSET(size, offset), FIXUP_WORD_WRITE_VALUE(size, offset, value));
return 0;
}
}
@ -1205,8 +1200,8 @@ ALWAYS_INLINE static TickCount DoAccessSPU(u32 offset, u32& value)
case MemoryAccessSize::Byte:
default:
{
const u16 value16 = g_spu.ReadRegister(FIXUP_HALFWORD_READ_OFFSET(offset));
value = FIXUP_HALFWORD_READ_VALUE(offset, value16);
const u16 value16 = g_spu.ReadRegister(FIXUP_HALFWORD_OFFSET(size, offset));
value = FIXUP_HALFWORD_READ_VALUE(size, offset, value16);
}
break;
}
@ -1236,7 +1231,8 @@ ALWAYS_INLINE static TickCount DoAccessSPU(u32 offset, u32& value)
case MemoryAccessSize::Byte:
{
g_spu.WriteRegister(FIXUP_HALFWORD_READ_OFFSET(offset), Truncate16(FIXUP_HALFWORD_READ_VALUE(offset, value)));
g_spu.WriteRegister(FIXUP_HALFWORD_OFFSET(size, offset),
Truncate16(FIXUP_HALFWORD_READ_VALUE(size, offset, value)));
break;
}
}
@ -1250,28 +1246,13 @@ ALWAYS_INLINE static TickCount DoDMAAccess(u32 offset, u32& value)
{
if constexpr (type == MemoryAccessType::Read)
{
value = FIXUP_WORD_READ_VALUE(offset, g_dma.ReadRegister(FIXUP_WORD_READ_OFFSET(offset)));
value = g_dma.ReadRegister(FIXUP_WORD_OFFSET(size, offset));
value = FIXUP_WORD_READ_VALUE(size, offset, value);
return 2;
}
else
{
switch (size)
{
case MemoryAccessSize::Byte:
case MemoryAccessSize::HalfWord:
{
// zero extend length register
if ((offset & u32(0xF0)) < 7 && (offset & u32(0x0F)) == 0x4)
value = ZeroExtend32(value);
else
FixupUnalignedWordAccessW32(offset, value);
}
default:
break;
}
g_dma.WriteRegister(offset, value);
g_dma.WriteRegister(FIXUP_WORD_OFFSET(size, offset), FIXUP_WORD_WRITE_VALUE(size, offset, value));
return 0;
}
}
@ -1782,10 +1763,9 @@ bool ReadMemoryWord(VirtualMemoryAddress addr, u32* value)
return true;
}
bool WriteMemoryByte(VirtualMemoryAddress addr, u8 value)
bool WriteMemoryByte(VirtualMemoryAddress addr, u32 value)
{
u32 temp = ZeroExtend32(value);
const TickCount cycles = DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Byte>(addr, temp);
const TickCount cycles = DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Byte>(addr, value);
if (cycles < 0)
{
RaiseException(Exception::DBE);
@ -1796,13 +1776,12 @@ bool WriteMemoryByte(VirtualMemoryAddress addr, u8 value)
return true;
}
bool WriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value)
bool WriteMemoryHalfWord(VirtualMemoryAddress addr, u32 value)
{
if (!DoAlignmentCheck<MemoryAccessType::Write, MemoryAccessSize::HalfWord>(addr))
return false;
u32 temp = ZeroExtend32(value);
const TickCount cycles = DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::HalfWord>(addr, temp);
const TickCount cycles = DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::HalfWord>(addr, value);
if (cycles < 0)
{
RaiseException(Exception::DBE);
@ -1972,10 +1951,9 @@ u64 ReadMemoryWord(u32 address)
return ZeroExtend64(temp);
}
u32 WriteMemoryByte(u32 address, u8 value)
u32 WriteMemoryByte(u32 address, u32 value)
{
u32 temp = ZeroExtend32(value);
const TickCount cycles = DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Byte>(address, temp);
const TickCount cycles = DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Byte>(address, value);
if (cycles < 0)
return static_cast<u32>(Exception::DBE);
@ -1983,7 +1961,7 @@ u32 WriteMemoryByte(u32 address, u8 value)
return 0;
}
u32 WriteMemoryHalfWord(u32 address, u16 value)
u32 WriteMemoryHalfWord(u32 address, u32 value)
{
if (!Common::IsAlignedPow2(address, 2))
{
@ -1991,8 +1969,7 @@ u32 WriteMemoryHalfWord(u32 address, u16 value)
return static_cast<u32>(Exception::AdES);
}
u32 temp = ZeroExtend32(value);
const TickCount cycles = DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::HalfWord>(address, temp);
const TickCount cycles = DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::HalfWord>(address, value);
if (cycles < 0)
return static_cast<u32>(Exception::DBE);
@ -2037,16 +2014,14 @@ u32 UncheckedReadMemoryWord(u32 address)
return temp;
}
void UncheckedWriteMemoryByte(u32 address, u8 value)
void UncheckedWriteMemoryByte(u32 address, u32 value)
{
u32 temp = ZeroExtend32(value);
g_state.pending_ticks += DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Byte>(address, temp);
g_state.pending_ticks += DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Byte>(address, value);
}
void UncheckedWriteMemoryHalfWord(u32 address, u16 value)
void UncheckedWriteMemoryHalfWord(u32 address, u32 value)
{
u32 temp = ZeroExtend32(value);
g_state.pending_ticks += DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::HalfWord>(address, temp);
g_state.pending_ticks += DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::HalfWord>(address, value);
}
void UncheckedWriteMemoryWord(u32 address, u32 value)