Files
ppsspp/Core/HW/MMIO.cpp
T
Henrik RydgårdandClaude Opus 5.5 53ac2c7c56 IR: Compile kernel loads and stores instead of interpreting them
Kernel code's loads and stores may reach hardware registers, which only the
interpreter handled, so the IR frontend sent every one of them (in kernel
blocks, only run by the VSH boot) to it.

They now become LoadKernel*/StoreKernel* IR ops (one per size, like the
plain loads and stores), which check at run time whether the address is
memory or a hardware register (MMIO). After constant
propagation, ResolveKernelMemory turns the ones at a constant address that's
plain memory into ordinary loads and stores, so only the hardware registers,
and addresses only known at run time, stay checked. The native JITs run the
two ops through their IR fallback. They're barriers, since a register access
has side effects, but can't change the GPRs, so constants stay known across.

The kernel mode check for MMIO moves to the interpreter, the only caller that
can run user code there. TestMMIO covers the new paths and which accesses
stay checked. Under --vsh, the JIT and the interpreter now make the same
unhandled register accesses, in the same numbers.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 13:40:47 +02:00

78 lines
2.2 KiB
C++

// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <vector>
#include "Common/Log.h"
#include "Common/Serialize/Serializer.h"
#include "Core/HW/GpioMMIO.h"
#include "Core/HW/MMIO.h"
#include "Core/MIPS/MIPS.h"
namespace MMIO {
static std::vector<MMIODevice *> g_devices;
void Init() {
g_devices = {
GpioMMIO::Device(),
SysconSerialMMIO::Device(),
};
for (MMIODevice *device : g_devices) {
device->Reset();
}
}
void Shutdown() {
g_devices.clear();
}
void DoState(PointerWrap &p) {
auto s = p.Section("MMIO", 1, 1);
if (!s)
return;
// Every device, whether or not this boot registered it, so the layout doesn't depend on that.
GpioMMIO::Device()->DoState(p);
SysconSerialMMIO::Device()->DoState(p);
}
static MMIODevice *FindDevice(u32 address) {
for (MMIODevice *device : g_devices) {
if (device->Contains(address))
return device;
}
return nullptr;
}
u32 Read(MIPSState *mips, u32 address, int size) {
MMIODevice *device = FindDevice(address);
u32 value;
if (device && device->Read(mips, address - device->Base(), size, &value))
return value;
WARN_LOG(Log::CPU, "Unhandled MMIO Read%d at %08x%s%s", size * 8, address, device ? " in " : "", device ? device->Name() : "");
return UNHANDLED_READ;
}
void Write(MIPSState *mips, u32 address, int size, u32 value) {
MMIODevice *device = FindDevice(address);
if (device && device->Write(mips, address - device->Base(), size, value))
return;
WARN_LOG(Log::CPU, "Unhandled MMIO Write%d at %08x = %08x%s%s", size * 8, address, value, device ? " in " : "", device ? device->Name() : "");
}
} // namespace MMIO