147 lines
4.9 KiB
C++
147 lines
4.9 KiB
C++
#include "services/gsp_gpu.hpp"
|
|
|
|
namespace GPUCommands {
|
|
enum : u32 {
|
|
AcquireRight = 0x00160042,
|
|
RegisterInterruptRelayQueue = 0x00130042,
|
|
WriteHwRegs = 0x00010082,
|
|
WriteHwRegsWithMask = 0x00020084
|
|
};
|
|
}
|
|
|
|
namespace Result {
|
|
enum : u32 {
|
|
Success = 0,
|
|
SuccessRegisterIRQ = 0x2A07 // TODO: Is this a reference to the Ricoh 2A07 used in PAL NES systems?
|
|
};
|
|
}
|
|
|
|
void GPUService::reset() {
|
|
privilegedProcess = 0xFFFFFFFF; // Set the privileged process to an invalid handle
|
|
sharedMem = nullptr;
|
|
}
|
|
|
|
void GPUService::handleSyncRequest(u32 messagePointer) {
|
|
const u32 command = mem.read32(messagePointer);
|
|
switch (command) {
|
|
case GPUCommands::AcquireRight: acquireRight(messagePointer); break;
|
|
case GPUCommands::RegisterInterruptRelayQueue: registerInterruptRelayQueue(messagePointer); break;
|
|
case GPUCommands::WriteHwRegs: writeHwRegs(messagePointer); break;
|
|
case GPUCommands::WriteHwRegsWithMask: writeHwRegsWithMask(messagePointer); break;
|
|
; default: Helpers::panic("GPU service requested. Command: %08X\n", command);
|
|
}
|
|
}
|
|
|
|
void GPUService::acquireRight(u32 messagePointer) {
|
|
const u32 flag = mem.read32(messagePointer + 4);
|
|
const u32 pid = mem.read32(messagePointer + 12);
|
|
printf("GSP::GPU::AcquireRight (flag = %X, pid = %X)\n", flag, pid);
|
|
|
|
if (flag != 0) {
|
|
Helpers::panic("GSP::GPU::acquireRight with flag != 0 needs to perform additional initialization");
|
|
}
|
|
|
|
if (pid == KernelHandles::CurrentProcess) {
|
|
privilegedProcess = currentPID;
|
|
} else {
|
|
privilegedProcess = pid;
|
|
}
|
|
|
|
mem.write32(messagePointer + 4, Result::Success);
|
|
}
|
|
|
|
// TODO: What is the flags field meant to be?
|
|
// What is the "GSP module thread index" meant to be?
|
|
// How does the shared memory handle thing work?
|
|
void GPUService::registerInterruptRelayQueue(u32 messagePointer) {
|
|
const u32 flags = mem.read32(messagePointer + 4);
|
|
const u32 eventHandle = mem.read32(messagePointer + 12);
|
|
printf("GSP::GPU::RegisterInterruptRelayQueue (flags = %X, event handle = %X)\n", flags, eventHandle);
|
|
|
|
mem.write32(messagePointer + 4, Result::SuccessRegisterIRQ);
|
|
mem.write32(messagePointer + 8, 0); // TODO: GSP module thread index
|
|
mem.write32(messagePointer + 12, 0); // Translation descriptor
|
|
mem.write32(messagePointer + 16, KernelHandles::GSPSharedMemHandle);
|
|
}
|
|
|
|
void GPUService::requestInterrupt(GPUInterrupt type) {
|
|
if (sharedMem == nullptr) [[unlikely]] { // Shared memory hasn't been set up yet
|
|
return;
|
|
}
|
|
|
|
u8 index = sharedMem[0];
|
|
u8& interruptCount = sharedMem[1];
|
|
u8 flagIndex = (index + interruptCount) % 0x34;
|
|
interruptCount++;
|
|
|
|
sharedMem[0xC + flagIndex] = static_cast<u8>(type);
|
|
}
|
|
|
|
void GPUService::writeHwRegs(u32 messagePointer) {
|
|
u32 ioAddr = mem.read32(messagePointer + 4); // GPU address based at 0x1EB00000, word aligned
|
|
const u32 size = mem.read32(messagePointer + 8); // Size in bytes
|
|
u32 dataPointer = mem.read32(messagePointer + 16);
|
|
printf("GSP::GPU::writeHwRegs (GPU address = %08X, size = %X, data address = %08X)\n", ioAddr, size, dataPointer);
|
|
|
|
// Check for alignment
|
|
if ((size & 3) || (ioAddr & 3) || (dataPointer & 3)) {
|
|
Helpers::panic("GSP::GPU::writeHwRegs misalignment");
|
|
}
|
|
|
|
if (size > 0x80) {
|
|
Helpers::panic("GSP::GPU::writeHwRegs size too big");
|
|
}
|
|
|
|
if (ioAddr >= 0x420000) {
|
|
Helpers::panic("GSP::GPU::writeHwRegs offset too big");
|
|
}
|
|
|
|
for (u32 i = 0; i < size; i += 4) {
|
|
const u32 value = mem.read32(dataPointer);
|
|
printf("GSP::GPU: Wrote %08X to GPU register %X\n", value, ioAddr);
|
|
dataPointer += 4;
|
|
ioAddr += 4;
|
|
// TODO: Write the value to the register
|
|
}
|
|
mem.write32(messagePointer + 4, Result::Success);
|
|
}
|
|
|
|
// Update sequential GPU registers using an array of data and mask values using this formula
|
|
// GPU register = (register & ~mask) | (data & mask).
|
|
void GPUService::writeHwRegsWithMask(u32 messagePointer) {
|
|
u32 ioAddr = mem.read32(messagePointer + 4); // GPU address based at 0x1EB00000, word aligned
|
|
const u32 size = mem.read32(messagePointer + 8); // Size in bytes
|
|
|
|
u32 dataPointer = mem.read32(messagePointer + 16); // Data pointer
|
|
u32 maskPointer = mem.read32(messagePointer + 24); // Mask pointer
|
|
|
|
printf("GSP::GPU::writeHwRegsWithMask (GPU address = %08X, size = %X, data address = %08X, mask address = %08X)\n",
|
|
ioAddr, size, dataPointer, maskPointer);
|
|
|
|
// Check for alignment
|
|
if ((size & 3) || (ioAddr & 3) || (dataPointer & 3) || (maskPointer & 3)) {
|
|
Helpers::panic("GSP::GPU::writeHwRegs misalignment");
|
|
}
|
|
|
|
if (size > 0x80) {
|
|
Helpers::panic("GSP::GPU::writeHwRegs size too big");
|
|
}
|
|
|
|
if (ioAddr >= 0x420000) {
|
|
Helpers::panic("GSP::GPU::writeHwRegs offset too big");
|
|
}
|
|
|
|
for (u32 i = 0; i < size; i += 4) {
|
|
const u32 currentValue = 0; // TODO: Read the actual register value
|
|
const u32 data = mem.read32(dataPointer);
|
|
const u32 mask = mem.read32(maskPointer);
|
|
|
|
u32 newValue = (currentValue & ~mask) | (data & mask);
|
|
// TODO: write new value
|
|
maskPointer += 4;
|
|
dataPointer += 4;
|
|
ioAddr += 4;
|
|
}
|
|
|
|
mem.write32(messagePointer + 4, Result::Success);
|
|
} |