Files
ppsspp/Core/HW/MMIO.cpp
T
Henrik RydgårdandClaude Opus 5.5 b7b4a265b7 MMIO: Register hardware register blocks as devices
The interpreter's MMIO dispatch was a hardcoded chain for GPIO and the syscon
serial port. Devices now register an address range in Core/HW/MMIO and get
each access with its width and the MIPSState making it, for registers that
answer each CPU differently. An access no device handles is logged and reads
as poison, as before.

GPIO and syscon serial become the first two devices. They're reset at boot,
which they never were, and saved in savestates (SaveStart section version 4).

ll/sc, lwl/lwr and lwc1/swc1 reach MMIO in the interpreter too, and kernel
blocks leave lwc1/swc1 to it like the other loads and stores. swl/swr and
lv/sv still treat it as a bad address. TestMMIO runs a kernel-mode access on
each core and the registers through a savestate.

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

96 lines
3.0 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/Core.h"
#include "Core/HW/GpioMMIO.h"
#include "Core/HW/MMIO.h"
#include "Core/MemMap.h"
#include "Core/MIPS/MIPS.h"
namespace MMIO {
// Unhandled reads return a distinctive poison value (not 0) so it's obvious in a live trace when
// some later computation's input traces back to an unimplemented register, rather than looking
// like an ordinary, legitimate zero.
constexpr u32 UNKNOWN_MMIO_POISON = 0x1337BEEF;
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;
}
static bool CheckKernelMode(MIPSState *mips, u32 address, int size, MemoryExceptionType type) {
if (Memory::IsKernelCodeAddress(mips->pc))
return true;
Core_MemoryException(address, size, mips->pc, type, "Kernel mode only");
return false;
}
u32 Read(MIPSState *mips, u32 address, int size) {
if (!CheckKernelMode(mips, address, size, MemoryExceptionType::READ_WORD))
return UNKNOWN_MMIO_POISON;
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 UNKNOWN_MMIO_POISON;
}
void Write(MIPSState *mips, u32 address, int size, u32 value) {
if (!CheckKernelMode(mips, address, size, MemoryExceptionType::WRITE_WORD))
return;
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