ME: Let kernel code on the main CPU reach RAM and VRAM through kseg1

Yabause-PSP's me.prx ORs 0xA0000000 onto its own addresses to share a
buffer with the ME uncached, which is 0xA8xxxxxx for a module in kernel
RAM. The main CPU's space has no views there, so it crashed.

All of kernel segment 1 (0xA0000000-0xBFFFFFFF) now counts as MMIO, and a
device below the hardware registers maps RAM and VRAM onto their uncached
mirrors at 0x40000000. The main CPU's native kernel loads and stores send
the whole segment to that path; the ME keeps 0xBC000000 as the boundary,
since its space maps the rest.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-10-21 12:50:46 +02:00
1 parent e906b9f700
commit 657e99fcbf
4 files changed
+54 -7

No files matched your search

+38
View File
@@ -91,6 +91,43 @@ public:
static BootPageDevice g_bootPage;
// Kernel code reaches RAM and VRAM uncached at 0xA8000000 and 0xA4000000 (Yabause's me.prx does, by
// ORing 0xA0000000 onto its own addresses). The main CPU's space has no views there, so it goes
// through the uncached mirrors at 0x40000000 instead. The Media Engine's space maps them.
class KernelUncachedDevice : public MMIODevice {
public:
KernelUncachedDevice() : MMIODevice("kseg1 memory", 0xA0000000, 0x1C000000) {}
bool Read(MIPSState *mips, u32 offset, int size, u32 *value) override {
const u32 address = 0x40000000 + offset;
if ((mips && mips->IsMediaEngine()) || !Memory::IsValidRange(address, size))
return false;
switch (size) {
case 1: *value = Memory::ReadUnchecked_U8(address); break;
case 2: *value = Memory::ReadUnchecked_U16(address); break;
default: *value = Memory::ReadUnchecked_U32(address); break;
}
return true;
}
bool Write(MIPSState *mips, u32 offset, int size, u32 value) override {
const u32 address = 0x40000000 + offset;
if ((mips && mips->IsMediaEngine()) || !Memory::IsValidRange(address, size))
return false;
switch (size) {
case 1: Memory::WriteUnchecked_U8((u8)value, address); break;
case 2: Memory::WriteUnchecked_U16((u16)value, address); break;
default: Memory::WriteUnchecked_U32(value, address); break;
}
return true;
}
void Reset() override {}
void DoState(PointerWrap &p) override {}
};
static KernelUncachedDevice g_kernelUncached;
static std::vector<MMIODevice *> g_devices;
void Init() {
@@ -106,6 +143,7 @@ void Init() {
MeMMIO::ClockDevice(),
MeMMIO::DdrDevice(),
MeMMIO::AudioDevice(),
&g_kernelUncached,
};
for (MMIODevice *device : g_devices) {
device->Reset();