mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-08 07:05:40 +12:00
[GPU] Get PICA register access working
This commit is contained in:
parent
09000da701
commit
71ca62e2cc
5 changed files with 113 additions and 10 deletions
|
@ -53,7 +53,7 @@ set(KERNEL_SOURCE_FILES src/core/kernel/kernel.cpp src/core/kernel/resource_limi
|
||||||
set(SERVICE_SOURCE_FILES src/core/services/service_manager.cpp src/core/services/apt.cpp src/core/services/hid.cpp
|
set(SERVICE_SOURCE_FILES src/core/services/service_manager.cpp src/core/services/apt.cpp src/core/services/hid.cpp
|
||||||
src/core/services/fs.cpp src/core/services/gsp_gpu.cpp src/core/services/gsp_lcd.cpp
|
src/core/services/fs.cpp src/core/services/gsp_gpu.cpp src/core/services/gsp_lcd.cpp
|
||||||
)
|
)
|
||||||
set(PICA_SOURCE_FILES src/core/PICA/gpu.cpp)
|
set(PICA_SOURCE_FILES src/core/PICA/gpu.cpp src/core/PICA/regs.cpp)
|
||||||
|
|
||||||
set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/opengl.hpp include/termcolor.hpp
|
set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/opengl.hpp include/termcolor.hpp
|
||||||
include/cpu.hpp include/cpu_dynarmic.hpp include/memory.hpp include/kernel/kernel.hpp
|
include/cpu.hpp include/cpu_dynarmic.hpp include/memory.hpp include/kernel/kernel.hpp
|
||||||
|
|
|
@ -1,12 +1,23 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <array>
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
|
||||||
class GPU {
|
class GPU {
|
||||||
Memory& mem;
|
Memory& mem;
|
||||||
|
static constexpr u32 regNum = 0x300;
|
||||||
|
std::array<u32, regNum> regs; // GPU internal registers
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GPU(Memory& mem) : mem(mem) {}
|
GPU(Memory& mem) : mem(mem) {}
|
||||||
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control);
|
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control);
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
// Used by the GSP GPU service for readHwRegs/writeHwRegs/writeHwRegsMasked
|
||||||
|
u32 readReg(u32 address);
|
||||||
|
void writeReg(u32 address, u32 value);
|
||||||
|
|
||||||
|
// Used when processing GPU command lists
|
||||||
|
u32 readInternalReg(u32 index);
|
||||||
|
void writeInternalReg(u32 index, u32 value, u32 mask);
|
||||||
};
|
};
|
|
@ -2,7 +2,8 @@
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
void GPU::reset() {
|
void GPU::reset() {
|
||||||
|
regs.fill(0);
|
||||||
|
// TODO: Reset blending, texturing, etc here
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPU::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) {
|
void GPU::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) {
|
||||||
|
|
40
src/core/PICA/regs.cpp
Normal file
40
src/core/PICA/regs.cpp
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#include "gpu.hpp"
|
||||||
|
|
||||||
|
u32 GPU::readReg(u32 address) {
|
||||||
|
printf("Ignoring read from GPU register %08X\n", address);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPU::writeReg(u32 address, u32 value) {
|
||||||
|
if (address >= 0x1EF01000 && address < 0x1EF01C00) { // Internal registers
|
||||||
|
const u32 index = (address - 0x1EF01000) / sizeof(u32);
|
||||||
|
writeInternalReg(index, value, 0xffffffff);
|
||||||
|
} else {
|
||||||
|
Helpers::panic("Ignoring write to GPU register %08X. Value: %08X\n", address, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GPU::readInternalReg(u32 index) {
|
||||||
|
if (index > regNum) {
|
||||||
|
printf("Tried to read invalid GPU register. Index: %X\n", index);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return regs[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
|
||||||
|
if (index > regNum) {
|
||||||
|
Helpers::panic("Tried to write to invalid GPU register. Index: %X, value: %08X\n", index, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 currentValue = regs[index];
|
||||||
|
u32 newValue = (currentValue & ~mask) | (value & mask); // Only overwrite the bits specified by "mask"
|
||||||
|
|
||||||
|
switch (index) {
|
||||||
|
default:
|
||||||
|
printf("GPU: Wrote to unimplemented internal reg: %X, value: %08X\n", index, newValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
|
@ -116,12 +116,12 @@ void GPUService::writeHwRegs(u32 messagePointer) {
|
||||||
Helpers::panic("GSP::GPU::writeHwRegs offset too big");
|
Helpers::panic("GSP::GPU::writeHwRegs offset too big");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ioAddr += 0x1EB00000;
|
||||||
for (u32 i = 0; i < size; i += 4) {
|
for (u32 i = 0; i < size; i += 4) {
|
||||||
const u32 value = mem.read32(dataPointer);
|
const u32 value = mem.read32(dataPointer);
|
||||||
printf("GSP::GPU: Wrote %08X to GPU register %X\n", value, ioAddr);
|
gpu.writeReg(ioAddr, value);
|
||||||
dataPointer += 4;
|
dataPointer += 4;
|
||||||
ioAddr += 4;
|
ioAddr += 4;
|
||||||
// TODO: Write the value to the register
|
|
||||||
}
|
}
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
}
|
}
|
||||||
|
@ -151,13 +151,15 @@ void GPUService::writeHwRegsWithMask(u32 messagePointer) {
|
||||||
Helpers::panic("GSP::GPU::writeHwRegs offset too big");
|
Helpers::panic("GSP::GPU::writeHwRegs offset too big");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ioAddr += 0x1EB00000;
|
||||||
for (u32 i = 0; i < size; i += 4) {
|
for (u32 i = 0; i < size; i += 4) {
|
||||||
const u32 currentValue = 0; // TODO: Read the actual register value
|
const u32 current = gpu.readReg(ioAddr);
|
||||||
const u32 data = mem.read32(dataPointer);
|
const u32 data = mem.read32(dataPointer);
|
||||||
const u32 mask = mem.read32(maskPointer);
|
const u32 mask = mem.read32(maskPointer);
|
||||||
|
|
||||||
u32 newValue = (currentValue & ~mask) | (data & mask);
|
u32 newValue = (current & ~mask) | (data & mask);
|
||||||
// TODO: write new value
|
|
||||||
|
gpu.writeReg(ioAddr, newValue);
|
||||||
maskPointer += 4;
|
maskPointer += 4;
|
||||||
dataPointer += 4;
|
dataPointer += 4;
|
||||||
ioAddr += 4;
|
ioAddr += 4;
|
||||||
|
@ -250,9 +252,58 @@ void GPUService::memoryFill(u32* cmd) {
|
||||||
// Actually send command list (aka display list) to GPU
|
// Actually send command list (aka display list) to GPU
|
||||||
void GPUService::processCommandList(u32* cmd) {
|
void GPUService::processCommandList(u32* cmd) {
|
||||||
u32 address = cmd[1]; // Buffer address
|
u32 address = cmd[1]; // Buffer address
|
||||||
u32 size = cmd[2]; // Buffer size
|
u32 size = cmd[2] * 8; // Buffer size in bytes
|
||||||
bool updateGas = cmd[3] == 1; // Update gas additive blend results (0 = don't update, 1 = update)
|
bool updateGas = cmd[3] == 1; // Update gas additive blend results (0 = don't update, 1 = update)
|
||||||
bool flushBuffer = cmd[7] == 1; // Flush buffer (0 = don't flush, 1 = flush)
|
bool flushBuffer = cmd[7] == 1; // Flush buffer (0 = don't flush, 1 = flush)
|
||||||
|
|
||||||
Helpers::panic("GPU::GSP::processCommandList. Address: %08X, size: %08X", address, size);
|
u32* bufferStart = static_cast<u32*>(mem.getReadPointer(address));
|
||||||
|
if (!bufferStart) Helpers::panic("Couldn't get buffer for command list");
|
||||||
|
// TODO: This is very memory unsafe. We get a pointer to FCRAM and just keep writing without checking if we're gonna go OoB
|
||||||
|
|
||||||
|
u32* curr = bufferStart;
|
||||||
|
u32* bufferEnd = bufferStart + (size / sizeof(u32));
|
||||||
|
|
||||||
|
// LUT for converting the parameter mask to an actual 32-bit mask
|
||||||
|
// The parameter mask is 4 bits long, each bit corresponding to one byte of the mask
|
||||||
|
// If the bit is 0 then the corresponding mask byte is 0, otherwise the mask byte is 0xff
|
||||||
|
// So for example if the parameter mask is 0b1001, the full mask is 0xff'00'00'ff
|
||||||
|
static constexpr std::array<u32, 16> maskLUT = {
|
||||||
|
0x00000000, 0x000000ff, 0x0000ff00, 0x0000ffff, 0x00ff0000, 0x00ff00ff, 0x00ffff00, 0x00ffffff,
|
||||||
|
0xff000000, 0xff0000ff, 0xff00ff00, 0xff00ffff, 0xffff0000, 0xffff00ff, 0xffffff00, 0xffffffff,
|
||||||
|
};
|
||||||
|
|
||||||
|
while (curr < bufferEnd) {
|
||||||
|
// If the buffer is not aligned to an 8 byte boundary, force align it by moving the pointer up a word
|
||||||
|
// The curr pointer starts out doubleword-aligned and is increased by 4 bytes each time
|
||||||
|
// So to check if it is aligned, we get the number of words it's been incremented by
|
||||||
|
// If that number is an odd value then the buffer is not aligned, otherwise it is
|
||||||
|
if ((curr - bufferStart) % 2 != 0) {
|
||||||
|
curr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first word of a command is the command parameter and the second one is the header
|
||||||
|
u32 param1 = *curr++;
|
||||||
|
u32 header = *curr++;
|
||||||
|
|
||||||
|
u32 id = header & 0xffff;
|
||||||
|
u32 paramMaskIndex = (header >> 16) & 0xf;
|
||||||
|
u32 paramCount = (header >> 20) & 0xff; // Number of additional parameters
|
||||||
|
// Bit 31 tells us whether this command is going to write to multiple sequential registers (if the bit is 1)
|
||||||
|
// Or if all written values will go to the same register (If the bit is 0). It's essentially the value that
|
||||||
|
// gets added to the "id" field after each register write
|
||||||
|
bool consecutiveWritingMode = (header >> 31) != 0;
|
||||||
|
|
||||||
|
u32 mask = maskLUT[paramMaskIndex]; // Actual parameter mask
|
||||||
|
// Increment the ID by 1 after each write if we're in consecutive mode, or 0 otherwise
|
||||||
|
u32 idIncrement = (consecutiveWritingMode) ? 1 : 0;
|
||||||
|
|
||||||
|
gpu.writeInternalReg(id, param1, mask);
|
||||||
|
for (u32 i = 0; i < paramCount; i++) {
|
||||||
|
id += idIncrement;
|
||||||
|
u32 param = *curr++;
|
||||||
|
gpu.writeInternalReg(id, param, mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Helpers::panic("GPU::GSP::processCommandList. Address: %08X, size in bytes: %08X", address, size);
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue