diff --git a/CMakeLists.txt b/CMakeLists.txt index 587e48c7..1d738a67 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,6 +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 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(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 @@ -60,6 +61,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/opengl.hpp inc include/kernel/config_mem.hpp include/services/service_manager.hpp include/services/apt.hpp include/kernel/handles.hpp include/services/hid.hpp include/services/fs.hpp include/services/gsp_gpu.hpp include/services/gsp_lcd.hpp include/arm_defs.hpp + include/gpu.hpp ) set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp @@ -75,7 +77,9 @@ source_group("Header Files\\Core" FILES ${HEADER_FILES}) source_group("Source Files\\Core" FILES ${SOURCE_FILES}) source_group("Source Files\\Core\\Kernel" FILES ${KERNEL_SOURCE_FILES}) source_group("Source Files\\Core\\Services" FILES ${SERVICE_SOURCE_FILES}) +source_group("Source Files\\Core\\PICA" FILES ${PICA_SOURCE_FILES}) source_group("Source Files\\Third Party" FILES ${THIRD_PARTY_SOURCE_FILES}) -add_executable(Alber ${SOURCE_FILES} ${KERNEL_SOURCE_FILES} ${SERVICE_SOURCE_FILES} ${THIRD_PARTY_SOURCE_FILES} ${HEADER_FILES}) +add_executable(Alber ${SOURCE_FILES} ${KERNEL_SOURCE_FILES} ${SERVICE_SOURCE_FILES} ${PICA_SOURCE_FILES} +${THIRD_PARTY_SOURCE_FILES} ${HEADER_FILES}) target_link_libraries(Alber PRIVATE sfml-system sfml-network sfml-graphics sfml-window dynarmic) \ No newline at end of file diff --git a/include/emulator.hpp b/include/emulator.hpp index 438e2053..a3821d7f 100644 --- a/include/emulator.hpp +++ b/include/emulator.hpp @@ -4,7 +4,8 @@ #include #include "cpu.hpp" -#include "helpers.hpp" +#include "gpu.hpp" +#include "memory.hpp" #include "opengl.hpp" #include "SFML/Window.hpp" #include "SFML/Graphics.hpp" @@ -15,6 +16,7 @@ enum class ROMType { class Emulator { CPU cpu; + GPU gpu; Memory memory; Kernel kernel; @@ -28,7 +30,7 @@ class Emulator { public: Emulator() : window(sf::VideoMode(width, height), "Alber", sf::Style::Default, sf::ContextSettings(0, 0, 0, 4, 3)), - kernel(cpu, memory), cpu(memory, kernel) { + kernel(cpu, memory, gpu), cpu(memory, kernel) { reset(); window.setActive(true); } diff --git a/include/gpu.hpp b/include/gpu.hpp new file mode 100644 index 00000000..c62ff991 --- /dev/null +++ b/include/gpu.hpp @@ -0,0 +1,10 @@ +#pragma once +#include "helpers.hpp" + +class GPU { + +public: + GPU() {} + void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control); + void reset(); +}; \ No newline at end of file diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 39c6e08f..20a28657 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -100,7 +100,7 @@ class Kernel { void waitSynchronization1(); public: - Kernel(CPU& cpu, Memory& mem); + Kernel(CPU& cpu, Memory& mem, GPU& gpu); void serviceSVC(u32 svc); void reset(); }; \ No newline at end of file diff --git a/include/services/gsp_gpu.hpp b/include/services/gsp_gpu.hpp index a456410d..e4b2eb35 100644 --- a/include/services/gsp_gpu.hpp +++ b/include/services/gsp_gpu.hpp @@ -1,8 +1,9 @@ #pragma once +#include +#include "gpu.hpp" #include "helpers.hpp" #include "kernel_types.hpp" #include "memory.hpp" -#include enum class GPUInterrupt : u8 { PSC0 = 0, // Memory fill completed @@ -17,6 +18,7 @@ enum class GPUInterrupt : u8 { class GPUService { Handle handle = KernelHandles::GPU; Memory& mem; + GPU& gpu; u32& currentPID; // Process ID of the current process u8* sharedMem; // Pointer to GSP shared memory @@ -24,16 +26,22 @@ class GPUService { // This is the PID of that process u32 privilegedProcess; + void processCommands(); + // Service commands void acquireRight(u32 messagePointer); void flushDataCache(u32 messagePointer); void registerInterruptRelayQueue(u32 messagePointer); void setLCDForceBlack(u32 messagePointer); + void triggerCmdReqQueue(u32 messagePointer); void writeHwRegs(u32 messagePointer); void writeHwRegsWithMask(u32 messagePointer); + // GPU commands processed via TriggerCmdReqQueue + void memoryFill(u32* cmd); + public: - GPUService(Memory& mem, u32& currentPID) : mem(mem), currentPID(currentPID) {} + GPUService(Memory& mem, GPU& gpu, u32& currentPID) : mem(mem), gpu(gpu), currentPID(currentPID) {} void reset(); void handleSyncRequest(u32 messagePointer); void requestInterrupt(GPUInterrupt type); diff --git a/include/services/service_manager.hpp b/include/services/service_manager.hpp index abf731c1..33c7a9d2 100644 --- a/include/services/service_manager.hpp +++ b/include/services/service_manager.hpp @@ -23,7 +23,7 @@ class ServiceManager { void registerClient(u32 messagePointer); public: - ServiceManager(std::array& regs, Memory& mem, u32& currentPID); + ServiceManager(std::array& regs, Memory& mem, GPU& gpu, u32& currentPID); void reset(); void handleSyncRequest(u32 messagePointer); diff --git a/src/core/PICA/gpu.cpp b/src/core/PICA/gpu.cpp new file mode 100644 index 00000000..85c73384 --- /dev/null +++ b/src/core/PICA/gpu.cpp @@ -0,0 +1,10 @@ +#include "gpu.hpp" +#include + +void GPU::reset() { + +} + +void GPU::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) { + printf("GPU: Clear buffer\nStart: %08X End: %08X\nValue: %08X Control: %08X\n", startAddress, endAddress, value, control); +} \ No newline at end of file diff --git a/src/core/kernel/kernel.cpp b/src/core/kernel/kernel.cpp index 9a1cc09b..74fce4a6 100644 --- a/src/core/kernel/kernel.cpp +++ b/src/core/kernel/kernel.cpp @@ -3,8 +3,8 @@ #include "kernel_types.hpp" #include "cpu.hpp" -Kernel::Kernel(CPU& cpu, Memory& mem) - : cpu(cpu), regs(cpu.regs()), mem(mem), handleCounter(0), serviceManager(regs, mem, currentProcess) { +Kernel::Kernel(CPU& cpu, Memory& mem, GPU& gpu) + : cpu(cpu), regs(cpu.regs()), mem(mem), handleCounter(0), serviceManager(regs, mem, gpu, currentProcess) { objects.reserve(512); // Make room for a few objects to avoid further memory allocs later portHandles.reserve(32); diff --git a/src/core/services/gsp_gpu.cpp b/src/core/services/gsp_gpu.cpp index 3741a536..9d5d9ace 100644 --- a/src/core/services/gsp_gpu.cpp +++ b/src/core/services/gsp_gpu.cpp @@ -1,13 +1,22 @@ #include "services/gsp_gpu.hpp" -namespace GPUCommands { +// Commands used with SendSyncRequest targetted to the GSP::GPU service +namespace ServiceCommands { enum : u32 { AcquireRight = 0x00160042, RegisterInterruptRelayQueue = 0x00130042, WriteHwRegs = 0x00010082, WriteHwRegsWithMask = 0x00020084, FlushDataCache = 0x00080082, - SetLCDForceBlack = 0x000B0040 + SetLCDForceBlack = 0x000B0040, + TriggerCmdReqQueue = 0x000C0000 + }; +} + +// Commands written to shared memory and processed by TriggerCmdReqQueue +namespace GPUCommands { + enum : u32 { + MemoryFill = 2 }; } @@ -26,12 +35,13 @@ void GPUService::reset() { void GPUService::handleSyncRequest(u32 messagePointer) { const u32 command = mem.read32(messagePointer); switch (command) { - case GPUCommands::AcquireRight: acquireRight(messagePointer); break; - case GPUCommands::FlushDataCache: flushDataCache(messagePointer); break; - case GPUCommands::RegisterInterruptRelayQueue: registerInterruptRelayQueue(messagePointer); break; - case GPUCommands::SetLCDForceBlack: setLCDForceBlack(messagePointer); break; - case GPUCommands::WriteHwRegs: writeHwRegs(messagePointer); break; - case GPUCommands::WriteHwRegsWithMask: writeHwRegsWithMask(messagePointer); break; + case ServiceCommands::AcquireRight: acquireRight(messagePointer); break; + case ServiceCommands::FlushDataCache: flushDataCache(messagePointer); break; + case ServiceCommands::RegisterInterruptRelayQueue: registerInterruptRelayQueue(messagePointer); break; + case ServiceCommands::SetLCDForceBlack: setLCDForceBlack(messagePointer); break; + case ServiceCommands::TriggerCmdReqQueue: triggerCmdReqQueue(messagePointer); break; + case ServiceCommands::WriteHwRegs: writeHwRegs(messagePointer); break; + case ServiceCommands::WriteHwRegsWithMask: writeHwRegsWithMask(messagePointer); break; ; default: Helpers::panic("GPU service requested. Command: %08X\n", command); } } @@ -58,6 +68,11 @@ void GPUService::acquireRight(u32 messagePointer) { // What is the "GSP module thread index" meant to be? // How does the shared memory handle thing work? void GPUService::registerInterruptRelayQueue(u32 messagePointer) { + // Detect if this function is called a 2nd time because we'll likely need to impl threads properly for the GSP + static bool beenHere = false; + if (beenHere) Helpers::panic("RegisterInterruptRelayQueue called a second time. Need to implement GSP threads properly"); + beenHere = true; + 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); @@ -73,7 +88,7 @@ void GPUService::requestInterrupt(GPUInterrupt type) { return; } - u8 index = sharedMem[0]; + u8 index = sharedMem[0]; // The interrupt block is normally located at sharedMem + processGSPIndex*0x40 u8& interruptCount = sharedMem[1]; u8 flagIndex = (index + interruptCount) % 0x34; interruptCount++; @@ -168,4 +183,64 @@ void GPUService::setLCDForceBlack(u32 messagePointer) { } mem.write32(messagePointer + 4, Result::Success); +} + +void GPUService::triggerCmdReqQueue(u32 messagePointer) { + processCommands(); + mem.write32(messagePointer + 4, Result::Success); +} + +#include +#include +using namespace std::chrono_literals; +void GPUService::processCommands() { + if (sharedMem == nullptr) [[unlikely]] { // Shared memory hasn't been set up yet + return; + } + + constexpr int threadCount = 1; // TODO: More than 1 thread can have GSP commands at a time + for (int t = 0; t < threadCount; t++) { + u8* cmdBuffer = &sharedMem[0x800 + t * 0x200]; + u8& commandsLeft = cmdBuffer[1]; + // Commands start at byte 0x20 of the command buffer, each being 0x20 bytes long + u32* cmd = reinterpret_cast(&cmdBuffer[0x20]); + + printf("Processing %d GPU commands\n", commandsLeft); + std::this_thread::sleep_for(1000ms); + + while (commandsLeft != 0) { + u32 cmdID = cmd[0] & 0xff; + switch (cmdID) { + case GPUCommands::MemoryFill: memoryFill(cmd); break; + default: Helpers::panic("GSP::GPU::ProcessCommands: Unknown cmd ID %d", cmdID); + } + + commandsLeft--; + } + } +} + +// Fill 2 GPU framebuffers, buf0 and buf1, using a specific word value +void GPUService::memoryFill(u32* cmd) { + u32 control = cmd[7]; + + // buf0 parameters + u32 start0 = cmd[1]; // Start address for the fill. If 0, don't fill anything + u32 value0 = cmd[2]; // Value to fill the framebuffer with + u32 end0 = cmd[3]; // End address for the fill + u32 control0 = control & 0xffff; + + // buf1 parameters + u32 start1 = cmd[4]; + u32 value1 = cmd[5]; + u32 end1 = cmd[6]; + u32 control1 = control >> 16; + + if (start0 != 0) { + gpu.clearBuffer(start0, end0, value0, control0); + } + + if (start1 != 0) { + gpu.clearBuffer(start1, end1, value1, control1); + } } \ No newline at end of file diff --git a/src/core/services/service_manager.cpp b/src/core/services/service_manager.cpp index 3524c8ca..aa4e9a5f 100644 --- a/src/core/services/service_manager.cpp +++ b/src/core/services/service_manager.cpp @@ -1,7 +1,7 @@ #include "services/service_manager.hpp" -ServiceManager::ServiceManager(std::array& regs, Memory& mem, u32& currentPID) - : regs(regs), mem(mem), apt(mem), hid(mem), fs(mem), gsp_gpu(mem, currentPID), gsp_lcd(mem) {} +ServiceManager::ServiceManager(std::array& regs, Memory& mem, GPU& gpu, u32& currentPID) + : regs(regs), mem(mem), apt(mem), hid(mem), fs(mem), gsp_gpu(mem, gpu, currentPID), gsp_lcd(mem) {} void ServiceManager::reset() { apt.reset(); diff --git a/src/emulator.cpp b/src/emulator.cpp index 5eb7fd74..5e383537 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -1,8 +1,10 @@ #include "emulator.hpp" void Emulator::reset() { - memory.reset(); cpu.reset(); + gpu.reset(); + memory.reset(); + // Kernel must be reset last because it depends on CPU/Memory state kernel.reset(); // Reloading r13 and r15 needs to happen after everything has been reset