From be4fae51044f53bcb4858301f7b6fd4f9d4f571e Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Sat, 24 Sep 2022 02:45:57 +0300 Subject: [PATCH] Trongle --- CMakeLists.txt | 3 +- include/PICA/gpu.hpp | 26 +++++ include/PICA/regs.hpp | 2 + include/cpu_dynarmic.hpp | 4 +- include/emulator.hpp | 1 + include/kernel/kernel.hpp | 8 +- include/logger.hpp | 39 +++++++ include/opengl.hpp | 2 +- include/services/apt.hpp | 2 + include/services/fs.hpp | 2 + include/services/gsp_gpu.hpp | 3 + include/services/gsp_lcd.hpp | 2 + include/services/hid.hpp | 2 + include/services/ndm.hpp | 2 + include/services/service_manager.hpp | 2 + src/core/PICA/gpu.cpp | 24 ++-- src/core/PICA/regs.cpp | 8 +- src/core/PICA/renderer_opengl.cpp | 160 ++++++++++++++++++++++++++ src/core/kernel/address_arbiter.cpp | 4 +- src/core/kernel/events.cpp | 8 +- src/core/kernel/kernel.cpp | 10 +- src/core/kernel/memory_management.cpp | 6 +- src/core/kernel/ports.cpp | 4 +- src/core/kernel/resource_limits.cpp | 6 +- src/core/kernel/threads.cpp | 4 +- src/core/services/apt.cpp | 2 +- src/core/services/fs.cpp | 4 +- src/core/services/gsp_gpu.cpp | 36 +++--- src/core/services/hid.cpp | 2 +- src/core/services/ndm.cpp | 4 +- src/core/services/service_manager.cpp | 6 +- src/emulator.cpp | 21 +++- src/main.cpp | 2 + 33 files changed, 341 insertions(+), 70 deletions(-) create mode 100644 include/logger.hpp create mode 100644 src/core/PICA/renderer_opengl.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f0d30c8..84429810 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,7 +55,7 @@ set(SERVICE_SOURCE_FILES src/core/services/service_manager.cpp src/core/services src/core/services/ndm.cpp ) set(PICA_SOURCE_FILES src/core/PICA/gpu.cpp src/core/PICA/regs.cpp src/core/PICA/shader_unit.cpp - src/core/PICA/shader_interpreter.cpp + src/core/PICA/shader_interpreter.cpp src/core/PICA/renderer_opengl.cpp ) set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/opengl.hpp include/termcolor.hpp @@ -66,6 +66,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/opengl.hpp inc include/services/gsp_gpu.hpp include/services/gsp_lcd.hpp include/arm_defs.hpp include/PICA/gpu.hpp include/PICA/regs.hpp include/services/ndm.hpp include/PICA/shader.hpp include/PICA/shader_unit.hpp include/PICA/float_types.hpp + include/logger.hpp ) set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp diff --git a/include/PICA/gpu.hpp b/include/PICA/gpu.hpp index ac4e2f84..330f5f27 100644 --- a/include/PICA/gpu.hpp +++ b/include/PICA/gpu.hpp @@ -1,6 +1,7 @@ #pragma once #include #include "helpers.hpp" +#include "logger.hpp" #include "memory.hpp" #include "opengl.hpp" #include "PICA/float_types.hpp" @@ -12,11 +13,17 @@ class GPU { Memory& mem; ShaderUnit shaderUnit; u8* vram = nullptr; + MAKE_LOG_FUNCTION(log, gpuLogger) static constexpr u32 maxAttribCount = 12; // Up to 12 vertex attributes static constexpr u32 regNum = 0x300; static constexpr u32 vramSize = 6_MB; std::array regs; // GPU internal registers + + struct Vertex { + OpenGL::vec4 position; + OpenGL::vec4 colour; + }; // Read a value of type T from physical address paddr // This is necessary because vertex attribute fetching uses physical addresses @@ -71,8 +78,27 @@ class GPU { u32 fixedAttribCount = 0; // How many attribute components have we written? When we get to 4 the attr will actually get submitted std::array fixedAttrBuff; // Buffer to hold fixed attributes in until they get submitted + OpenGL::Framebuffer fbo; + OpenGL::Texture fboTexture; + OpenGL::Program triangleProgram; + OpenGL::Program displayProgram; + + OpenGL::VertexArray vao; + OpenGL::VertexBuffer vbo; + + // Dummy VAO/VBO for blitting the final output + OpenGL::VertexArray dummyVAO; + OpenGL::VertexBuffer dummyVBO; + + static constexpr u32 vertexBufferSize = 0x1000; + void drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 count); + public: GPU(Memory& mem); + void initGraphicsContext(); // Initialize graphics context + void getGraphicsContext(); // Set up the graphics context for rendering + void display(); // Display the screen contents onto our window + void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control); void reset(); diff --git a/include/PICA/regs.hpp b/include/PICA/regs.hpp index 5faa7502..7707a83a 100644 --- a/include/PICA/regs.hpp +++ b/include/PICA/regs.hpp @@ -47,6 +47,8 @@ namespace PICAInternalRegs { FixedAttribData1 = 0x234, FixedAttribData2 = 0x235, + PrimitiveConfig = 0x25E, + // Vertex shader registers VertexShaderTransferEnd = 0x2BF, VertexFloatUniformIndex = 0x2C0, diff --git a/include/cpu_dynarmic.hpp b/include/cpu_dynarmic.hpp index 0cabbd0e..2329c4a8 100644 --- a/include/cpu_dynarmic.hpp +++ b/include/cpu_dynarmic.hpp @@ -158,6 +158,8 @@ public: void runFrame() { env.ticksLeft = 268111856 / 60; const auto exitReason = jit->Run(); - Helpers::panic("Exit reason: %d\nPC: %08X", (u32)exitReason, getReg(15)); + if (static_cast(exitReason) != 0) [[unlikely]] { + Helpers::panic("Exit reason: %d\nPC: %08X", static_cast(exitReason), getReg(15)); + } } }; \ No newline at end of file diff --git a/include/emulator.hpp b/include/emulator.hpp index 181a178a..ec6c30c9 100644 --- a/include/emulator.hpp +++ b/include/emulator.hpp @@ -43,4 +43,5 @@ public: bool loadELF(std::filesystem::path& path); bool loadELF(std::ifstream& file); + void initGraphicsContext() { gpu.initGraphicsContext(); } }; \ No newline at end of file diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 05f614fd..ba68615e 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -5,6 +5,7 @@ #include #include "kernel_types.hpp" #include "helpers.hpp" +#include "logger.hpp" #include "memory.hpp" #include "resource_limits.hpp" #include "services/service_manager.hpp" @@ -58,7 +59,7 @@ class Kernel { } objects.push_back(KernelObject(handleCounter, type)); - printf("Created %s object with handle %d\n", kernelObjectTypeToString(type), handleCounter); + log("Created %s object with handle %d\n", kernelObjectTypeToString(type), handleCounter); return handleCounter++; } @@ -83,6 +84,9 @@ class Kernel { std::string getProcessName(u32 pid); const char* resetTypeToString(u32 type); + MAKE_LOG_FUNCTION(log, kernelLogger) + MAKE_LOG_FUNCTION(logSVC, svcLogger) + // SVC implementations void arbitrateAddress(); void clearEvent(); @@ -110,4 +114,6 @@ public: void setVersion(u8 major, u8 minor); void serviceSVC(u32 svc); void reset(); + + void sendGPUInterrupt(GPUInterrupt type) { serviceManager.requestGPUInterrupt(type); } }; \ No newline at end of file diff --git a/include/logger.hpp b/include/logger.hpp new file mode 100644 index 00000000..5f569cf2 --- /dev/null +++ b/include/logger.hpp @@ -0,0 +1,39 @@ +#pragma once +#include +#include + +namespace Log { + // Our logger class + template + class Logger { + public: + void log(const char* fmt, ...) { + if constexpr (!enabled) return; + + std::va_list args; + va_start(args, fmt); + std::vprintf(fmt, args); + va_end(args); + } + }; + + // Our loggers here. Enable/disable by toggling the template param + static Logger kernelLogger; + static Logger svcLogger; + static Logger gpuLogger; + + // Service loggers + static Logger aptLogger; + static Logger fsLogger; + static Logger hidLogger; + static Logger gspGPULogger; + static Logger gspLCDLogger; + static Logger ndmLogger; + static Logger srvLogger; + + #define MAKE_LOG_FUNCTION(functionName, logger) \ + template \ + void functionName(const char* fmt, Args... args) { \ + Log::logger.log(fmt, args...); \ + } +} \ No newline at end of file diff --git a/include/opengl.hpp b/include/opengl.hpp index e3424bb1..07ecc9f0 100644 --- a/include/opengl.hpp +++ b/include/opengl.hpp @@ -57,7 +57,7 @@ namespace OpenGL { void bind() { glBindVertexArray(m_handle); } template - void setAttributeFloat(GLuint index, GLint size, GLsizei stride, const void* offset, bool normalized = false) { + void setAttributeFloat(GLuint index, GLint size, GLsizei stride, const void* offset, bool normalized = GL_FALSE) { if constexpr (std::is_same()) { glVertexAttribPointer(index, size, GL_FLOAT, normalized, stride, offset); } diff --git a/include/services/apt.hpp b/include/services/apt.hpp index b39b01f0..c245a090 100644 --- a/include/services/apt.hpp +++ b/include/services/apt.hpp @@ -1,11 +1,13 @@ #pragma once #include "helpers.hpp" #include "kernel_types.hpp" +#include "logger.hpp" #include "memory.hpp" class APTService { Handle handle = KernelHandles::APT; Memory& mem; + MAKE_LOG_FUNCTION(log, aptLogger) // Service commands void getLockHandle(u32 messagePointer); diff --git a/include/services/fs.hpp b/include/services/fs.hpp index 22081b8e..55fa39d2 100644 --- a/include/services/fs.hpp +++ b/include/services/fs.hpp @@ -1,11 +1,13 @@ #pragma once #include "helpers.hpp" #include "kernel_types.hpp" +#include "logger.hpp" #include "memory.hpp" class FSService { Handle handle = KernelHandles::FS; Memory& mem; + MAKE_LOG_FUNCTION(log, fsLogger) // Service commands void initialize(u32 messagePointer); diff --git a/include/services/gsp_gpu.hpp b/include/services/gsp_gpu.hpp index 9759b8b9..647a235d 100644 --- a/include/services/gsp_gpu.hpp +++ b/include/services/gsp_gpu.hpp @@ -3,6 +3,7 @@ #include "PICA/gpu.hpp" #include "helpers.hpp" #include "kernel_types.hpp" +#include "logger.hpp" #include "memory.hpp" enum class GPUInterrupt : u8 { @@ -26,6 +27,7 @@ class GPUService { // This is the PID of that process u32 privilegedProcess; + MAKE_LOG_FUNCTION(log, gspGPULogger) void processCommandBuffer(); // Service commands @@ -40,6 +42,7 @@ class GPUService { // GSP commands processed via TriggerCmdReqQueue void processCommandList(u32* cmd); void memoryFill(u32* cmd); + void triggerDisplayTransfer(u32* cmd); public: GPUService(Memory& mem, GPU& gpu, u32& currentPID) : mem(mem), gpu(gpu), currentPID(currentPID) {} diff --git a/include/services/gsp_lcd.hpp b/include/services/gsp_lcd.hpp index 18a56c7e..c6fcf7ec 100644 --- a/include/services/gsp_lcd.hpp +++ b/include/services/gsp_lcd.hpp @@ -1,11 +1,13 @@ #pragma once #include "helpers.hpp" #include "kernel_types.hpp" +#include "logger.hpp" #include "memory.hpp" class LCDService { Handle handle = KernelHandles::LCD; Memory& mem; + MAKE_LOG_FUNCTION(log, gspLCDLogger) // Service commands diff --git a/include/services/hid.hpp b/include/services/hid.hpp index 09776eec..2f054379 100644 --- a/include/services/hid.hpp +++ b/include/services/hid.hpp @@ -1,11 +1,13 @@ #pragma once #include "helpers.hpp" #include "kernel_types.hpp" +#include "logger.hpp" #include "memory.hpp" class HIDService { Handle handle = KernelHandles::HID; Memory& mem; + MAKE_LOG_FUNCTION(log, hidLogger) // Service commands void getIPCHandles(u32 messagePointer); diff --git a/include/services/ndm.hpp b/include/services/ndm.hpp index e26b71ca..004722e3 100644 --- a/include/services/ndm.hpp +++ b/include/services/ndm.hpp @@ -1,11 +1,13 @@ #pragma once #include "helpers.hpp" #include "kernel_types.hpp" +#include "logger.hpp" #include "memory.hpp" class NDMService { Handle handle = KernelHandles::NDM; Memory& mem; + MAKE_LOG_FUNCTION(log, ndmLogger) // Service commands void overrideDefaultDaemons(u32 messagePointer); diff --git a/include/services/service_manager.hpp b/include/services/service_manager.hpp index b649ea59..8c7950c6 100644 --- a/include/services/service_manager.hpp +++ b/include/services/service_manager.hpp @@ -1,6 +1,7 @@ #pragma once #include #include "helpers.hpp" +#include "logger.hpp" #include "memory.hpp" #include "services/apt.hpp" #include "services/hid.hpp" @@ -12,6 +13,7 @@ class ServiceManager { std::array& regs; Memory& mem; + MAKE_LOG_FUNCTION(log, srvLogger) APTService apt; HIDService hid; diff --git a/src/core/PICA/gpu.cpp b/src/core/PICA/gpu.cpp index 80733b55..f382a8bf 100644 --- a/src/core/PICA/gpu.cpp +++ b/src/core/PICA/gpu.cpp @@ -30,10 +30,6 @@ void GPU::reset() { // TODO: Reset blending, texturing, etc here } -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); -} - void GPU::drawArrays(bool indexed) { if (indexed) drawArrays(); @@ -48,13 +44,22 @@ void GPU::drawArrays() { const u32 vertexBase = ((regs[PICAInternalRegs::VertexAttribLoc] >> 1) & 0xfffffff) * 16; const u32 vertexCount = regs[PICAInternalRegs::VertexCountReg]; // Total # of vertices to transfer + // Configures the type of primitive and the number of vertex shader outputs + const u32 primConfig = regs[PICAInternalRegs::PrimitiveConfig]; + const u32 primType = (primConfig >> 8) & 3; + if (primType != 0) Helpers::panic("[PICA] Tried to draw non-triangle shape %d\n", primType); + if (vertexCount % 3) Helpers::panic("[PICA] Vertex count not a multiple of 3"); + if (vertexCount > vertexBufferSize) Helpers::panic("[PICA] vertexCount > vertexBufferSize"); + + Vertex vertices[vertexBufferSize]; + // Stuff the global attribute config registers in one u64 to make attr parsing easier // TODO: Cache this when the vertex attribute format registers are written to u64 vertexCfg = u64(regs[PICAInternalRegs::AttribFormatLow]) | (u64(regs[PICAInternalRegs::AttribFormatHigh]) << 32); if constexpr (!indexed) { u32 offset = regs[PICAInternalRegs::VertexOffsetReg]; - printf("PICA::DrawArrays(vertex count = %d, vertexOffset = %d)\n", vertexCount, offset); + log("PICA::DrawArrays(vertex count = %d, vertexOffset = %d)\n", vertexCount, offset); } else { Helpers::panic("[PICA] Indexed drawing"); } @@ -96,7 +101,6 @@ void GPU::drawArrays() { for (component = 0; component < componentCount; component++) { float val = *ptr++; attribute[component] = f24::fromFloat32(val); - printf("Component %d: %f\n", component, (double)val); } break; } @@ -111,11 +115,13 @@ void GPU::drawArrays() { attribute[component] = (component == 3) ? f24::fromFloat32(1.0) : f24::fromFloat32(0.0); component++; } - - printf("Attribute %d, type: %d, component count: %d\n", attrCount, attribType, componentCount); } } - shaderUnit.vs.run(); // Run vertex shader for vertex + shaderUnit.vs.run(); + std::memcpy(&vertices[i].position, &shaderUnit.vs.outputs[0], sizeof(vec4f)); + std::memcpy(&vertices[i].colour, &shaderUnit.vs.outputs[1], sizeof(vec4f)); } + + drawVertices(OpenGL::Triangle, vertices, vertexCount); } \ No newline at end of file diff --git a/src/core/PICA/regs.cpp b/src/core/PICA/regs.cpp index 7efa2e88..cc2e6dea 100644 --- a/src/core/PICA/regs.cpp +++ b/src/core/PICA/regs.cpp @@ -4,7 +4,7 @@ using namespace Floats; u32 GPU::readReg(u32 address) { - printf("Ignoring read from GPU register %08X\n", address); + log("Ignoring read from GPU register %08X\n", address); return 0; } @@ -13,13 +13,13 @@ void GPU::writeReg(u32 address, u32 value) { const u32 index = (address - 0x1EF01000) / sizeof(u32); writeInternalReg(index, value, 0xffffffff); } else { - printf("Ignoring write to external GPU register %08X. Value: %08X\n", address, value); + log("Ignoring write to external 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); + Helpers::panic("Tried to read invalid GPU register. Index: %X\n", index); return 0; } @@ -131,7 +131,7 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) { break; } } else { - printf("GPU: Wrote to unimplemented internal reg: %X, value: %08X\n", index, newValue); + log("GPU: Wrote to unimplemented internal reg: %X, value: %08X\n", index, newValue); } break; } diff --git a/src/core/PICA/renderer_opengl.cpp b/src/core/PICA/renderer_opengl.cpp new file mode 100644 index 00000000..33f7ed4f --- /dev/null +++ b/src/core/PICA/renderer_opengl.cpp @@ -0,0 +1,160 @@ +#include "PICA/gpu.hpp" +#include "opengl.hpp" + +// This is all hacked up to display our first triangle + +const char* vertexShader = R"( + #version 420 core + + layout (location = 0) in vec4 coords; + layout (location = 1) in vec4 vertexColour; + + out vec4 colour; + + void main() { + gl_Position = coords; + colour = vertexColour; + } +)"; + +const char* fragmentShader = R"( + #version 420 core + + in vec4 colour; + out vec4 fragColour; + + void main() { + fragColour = colour; + } +)"; + +const char* displayVertexShader = R"( + #version 420 core + out vec2 UV; + + void main() { + const vec4 positions[4] = vec4[]( + vec4(-1.0, 1.0, 1.0, 1.0), // Top-left + vec4(1.0, 1.0, 1.0, 1.0), // Top-right + vec4(-1.0, -1.0, 1.0, 1.0), // Bottom-left + vec4(1.0, -1.0, 1.0, 1.0) // Bottom-right + ); + + // The 3DS displays both screens' framebuffer rotated 90 deg counter clockwise + // So we adjust our texcoords accordingly + const vec2 texcoords[4] = vec2[]( + vec2(1.0, 1.0), // Top-right + vec2(1.0, 0.0), // Bottom-right + vec2(0.0, 1.0), // Top-left + vec2(0.0, 0.0) // Bottom-left + ); + + gl_Position = positions[gl_VertexID]; + UV = texcoords[gl_VertexID]; + } +)"; + +const char* displayFragmentShader = R"( + #version 420 core + in vec2 UV; + out vec4 FragColor; + + uniform sampler2D u_texture; // TODO: Properly init this to 0 when I'm not lazy + void main() { + FragColor = texture(u_texture, UV); + } +)"; + +void GPU::initGraphicsContext() { + // Set up texture for top screen + fboTexture.create(400, 240, GL_RGBA8); + fboTexture.bind(); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, 0); + + fbo.createWithDrawTexture(fboTexture); + fbo.bind(OpenGL::DrawFramebuffer); + + OpenGL::setViewport(400, 240); + OpenGL::setClearColor(0.0, 0.0, 0.0, 1.0); + OpenGL::clearColor(); + + OpenGL::Shader vert(vertexShader, OpenGL::Vertex); + OpenGL::Shader frag(fragmentShader, OpenGL::Fragment); + triangleProgram.create({ vert, frag }); + + OpenGL::Shader vertDisplay(displayVertexShader, OpenGL::Vertex); + OpenGL::Shader fragDisplay(displayFragmentShader, OpenGL::Fragment); + displayProgram.create({ vertDisplay, fragDisplay }); + + vbo.createFixedSize(sizeof(Vertex) * vertexBufferSize, GL_STREAM_DRAW); + vbo.bind(); + vao.create(); + vao.bind(); + + // Position (x, y, z, w) attributes + vao.setAttributeFloat(0, 4, sizeof(Vertex), offsetof(Vertex, position)); + vao.enableAttribute(0); + // Colour attribute + vao.setAttributeFloat(1, 4, sizeof(Vertex), offsetof(Vertex, colour)); + vao.enableAttribute(1); + + dummyVBO.create(); + dummyVAO.create(); +} + +void GPU::getGraphicsContext() { + OpenGL::disableDepth(); + OpenGL::disableScissor(); + OpenGL::setViewport(400, 240); + fbo.bind(OpenGL::DrawAndReadFramebuffer); + + vbo.bind(); + vao.bind(); + triangleProgram.use(); +} + +void GPU::drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 count) { + vbo.bufferVertsSub(vertices, count); + OpenGL::draw(primType, count); +} + +constexpr u32 topScreenBuffer = 0x1f000000; +constexpr u32 bottomScreenBuffer = 0x1f300000; + +// Quick hack to display top screen for now +void GPU::display() { + OpenGL::disableScissor(); + OpenGL::bindScreenFramebuffer(); + fboTexture.bind(); + displayProgram.use(); + + dummyVBO.bind(); + dummyVAO.bind(); + OpenGL::setViewport(0, 240, 400, 240); + + OpenGL::draw(OpenGL::TriangleStrip, 4); +} + +void GPU::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) { + log("GPU: Clear buffer\nStart: %08X End: %08X\nValue: %08X Control: %08X\n", startAddress, endAddress, value, control); + + const float r = float((value >> 24) & 0xff) / 255.0; + const float g = float((value >> 16) & 0xff) / 255.0; + const float b = float((value >> 8) & 0xff) / 255.0; + const float a = float(value & 0xff) / 255.0; + + if (startAddress == topScreenBuffer) { + log("GPU: Cleared top screen\n"); + } else if (startAddress == bottomScreenBuffer) { + log("GPU: Tried to clear bottom screen\n"); + return; + } else { + log("GPU: Clearing some unknown buffer\n"); + } + + OpenGL::setClearColor(r, g, b, a); + OpenGL::clearColor(); +} \ No newline at end of file diff --git a/src/core/kernel/address_arbiter.cpp b/src/core/kernel/address_arbiter.cpp index ea6237cb..3ac2099e 100644 --- a/src/core/kernel/address_arbiter.cpp +++ b/src/core/kernel/address_arbiter.cpp @@ -37,7 +37,7 @@ void Kernel::arbitrateAddress() { const s32 value = s32(regs[3]); const s64 ns = s64(u64(regs[4]) | (u64(regs[5]) << 32)); - printf("ArbitrateAddress(Handle = %X, address = %08X, type = %s, value = %d, ns = %lld)\n", handle, address, + logSVC("ArbitrateAddress(Handle = %X, address = %08X, type = %s, value = %d, ns = %lld)\n", handle, address, arbitrationTypeToString(type), value, ns); const auto arbiter = getObject(handle, KernelObjectType::AddressArbiter); @@ -68,7 +68,7 @@ void Kernel::arbitrateAddress() { } case ArbitrationType::Signal: - printf("Broken ArbitrateAddress (type == SIGNAL)\n"); + logSVC("Broken ArbitrateAddress (type == SIGNAL)\n"); switchThread(0); break; diff --git a/src/core/kernel/events.cpp b/src/core/kernel/events.cpp index cca30dbd..e2f39564 100644 --- a/src/core/kernel/events.cpp +++ b/src/core/kernel/events.cpp @@ -23,7 +23,7 @@ void Kernel::createEvent() { if (resetType > 2) Helpers::panic("Invalid reset type for event %d", resetType); - printf("CreateEvent(handle pointer = %08X, resetType = %s)\n", outPointer, resetTypeToString(resetType)); + logSVC("CreateEvent(handle pointer = %08X, resetType = %s)\n", outPointer, resetTypeToString(resetType)); regs[0] = SVCResult::Success; regs[1] = makeEvent(static_cast(resetType)); @@ -33,7 +33,7 @@ void Kernel::createEvent() { void Kernel::clearEvent() { const Handle handle = regs[0]; const auto event = getObject(handle, KernelObjectType::Event); - printf("ClearEvent(event handle = %d)\n", handle); + logSVC("ClearEvent(event handle = %d)\n", handle); if (event == nullptr) [[unlikely]] { regs[0] = SVCResult::BadHandle; @@ -56,8 +56,6 @@ void Kernel::waitSynchronization1() { return; } - printf("WaitSynchronization1(handle = %X, ns = %lld) (STUBBED)\n", handle, ns); - serviceManager.requestGPUInterrupt(GPUInterrupt::VBlank0); - serviceManager.requestGPUInterrupt(GPUInterrupt::VBlank1); + logSVC("WaitSynchronization1(handle = %X, ns = %lld) (STUBBED)\n", handle, ns); regs[0] = SVCResult::Success; } \ No newline at end of file diff --git a/src/core/kernel/kernel.cpp b/src/core/kernel/kernel.cpp index 5aed4436..d4eefd44 100644 --- a/src/core/kernel/kernel.cpp +++ b/src/core/kernel/kernel.cpp @@ -118,13 +118,13 @@ u32 Kernel::getTLSPointer() { // Result CloseHandle(Handle handle) void Kernel::svcCloseHandle() { - printf("CloseHandle(handle = %d) (Unimplemented)\n", regs[0]); + logSVC("CloseHandle(handle = %d) (Unimplemented)\n", regs[0]); regs[0] = SVCResult::Success; } // u64 GetSystemTick() void Kernel::getSystemTick() { - printf("GetSystemTick()\n"); + logSVC("GetSystemTick()\n"); u64 ticks = cpu.getTicks(); regs[0] = u32(ticks); @@ -138,7 +138,7 @@ void Kernel::outputDebugString() { const u32 size = regs[1]; std::string message = mem.readString(pointer, size); - printf("[OutputDebugString] %s\n", message.c_str()); + logSVC("[OutputDebugString] %s\n", message.c_str()); regs[0] = SVCResult::Success; } @@ -147,7 +147,7 @@ void Kernel::getProcessInfo() { const auto pid = regs[1]; const auto type = regs[2]; const auto process = getProcessFromPID(pid); - printf("GetProcessInfo(process: %s, type = %d)\n", getProcessName(pid).c_str(), type); + logSVC("GetProcessInfo(process: %s, type = %d)\n", getProcessName(pid).c_str(), type); if (process == nullptr) [[unlikely]] { regs[0] = SVCResult::BadHandle; @@ -170,7 +170,7 @@ void Kernel::getProcessInfo() { // Result GetThreadId(u32* threadId, Handle thread) void Kernel::duplicateHandle() { Handle original = regs[1]; - printf("DuplicateHandle(handle = %X)\n", original); + logSVC("DuplicateHandle(handle = %X)\n", original); if (original == KernelHandles::CurrentThread) { printf("[Warning] Duplicated current thread. This might be horribly broken!\n"); diff --git a/src/core/kernel/memory_management.cpp b/src/core/kernel/memory_management.cpp index 1db536b6..788bd013 100644 --- a/src/core/kernel/memory_management.cpp +++ b/src/core/kernel/memory_management.cpp @@ -49,7 +49,7 @@ void Kernel::controlMemory() { Helpers::panic("ControlMemory: Unaligned parameters\nAddr0: %08X\nAddr1: %08X\nSize: %08X", addr0, addr1, size); } - printf("ControlMemory(addr0 = %08X, addr1 = %08X, size = %08X, operation = %X (%c%c%c)%s\n", + logSVC("ControlMemory(addr0 = %08X, addr1 = %08X, size = %08X, operation = %X (%c%c%c)%s\n", addr0, addr1, size, operation, r ? 'r' : '-', w ? 'w' : '-', x ? 'x' : '-', linear ? ", linear" : "" ); @@ -75,7 +75,7 @@ void Kernel::queryMemory() { const u32 pageInfo = regs[1]; const u32 addr = regs[2]; - printf("QueryMemory(mem info pointer = %08X, page info pointer = %08X, addr = %08X)\n", memInfo, pageInfo, addr); + logSVC("QueryMemory(mem info pointer = %08X, page info pointer = %08X, addr = %08X)\n", memInfo, pageInfo, addr); const auto info = mem.queryMemory(addr); regs[0] = SVCResult::Success; @@ -92,7 +92,7 @@ void Kernel::mapMemoryBlock() { const u32 addr = regs[1]; const u32 myPerms = regs[2]; const u32 otherPerms = regs[3]; - printf("MapMemoryBlock(block = %d, addr = %08X, myPerms = %X, otherPerms = %X\n", block, addr, myPerms, otherPerms); + logSVC("MapMemoryBlock(block = %d, addr = %08X, myPerms = %X, otherPerms = %X\n", block, addr, myPerms, otherPerms); if (!isAligned(addr)) [[unlikely]] { Helpers::panic("MapMemoryBlock: Unaligned address"); diff --git a/src/core/kernel/ports.cpp b/src/core/kernel/ports.cpp index 75900030..4624f173 100644 --- a/src/core/kernel/ports.cpp +++ b/src/core/kernel/ports.cpp @@ -56,7 +56,7 @@ void Kernel::connectToPort() { } Handle portHandle = optionalHandle.value(); - printf("ConnectToPort(handle pointer = %X, port = \"%s\")\n", handlePointer, port.c_str()); + logSVC("ConnectToPort(handle pointer = %X, port = \"%s\")\n", handlePointer, port.c_str()); const auto portData = objects[portHandle].getData(); if (!portData->isPublic) { @@ -76,7 +76,7 @@ void Kernel::sendSyncRequest() { const auto handle = regs[0]; u32 messagePointer = getTLSPointer() + 0x80; // The message is stored starting at TLS+0x80 - printf("SendSyncRequest(session handle = %X)\n", handle); + logSVC("SendSyncRequest(session handle = %X)\n", handle); // The sync request is being sent at a service rather than whatever port, so have the service manager intercept it if (KernelHandles::isServiceHandle(handle)) { diff --git a/src/core/kernel/resource_limits.cpp b/src/core/kernel/resource_limits.cpp index e96691d0..fa4abbc1 100644 --- a/src/core/kernel/resource_limits.cpp +++ b/src/core/kernel/resource_limits.cpp @@ -7,7 +7,7 @@ void Kernel::getResourceLimit() { const auto handlePointer = regs[0]; const auto pid = regs[1]; const auto process = getProcessFromPID(pid); - printf("GetResourceLimit (handle pointer = %08X, process: %s)\n", handlePointer, getProcessName(pid).c_str()); + logSVC("GetResourceLimit (handle pointer = %08X, process: %s)\n", handlePointer, getProcessName(pid).c_str()); if (process == nullptr) [[unlikely]] { regs[0] = SVCResult::BadHandle; @@ -33,7 +33,7 @@ void Kernel::getResourceLimitLimitValues() { return; } - printf("GetResourceLimitLimitValues(values = %08X, handle = %X, names = %08X, count = %d)\n", values, resourceLimit, names, count); + logSVC("GetResourceLimitLimitValues(values = %08X, handle = %X, names = %08X, count = %d)\n", values, resourceLimit, names, count); // printf("[Warning] We do not currently support any resource maximum aside from the application ones"); while (count != 0) { const u32 name = mem.read32(names); @@ -62,7 +62,7 @@ void Kernel::getResourceLimitCurrentValues() { return; } - printf("GetResourceLimitCurrentValues(values = %08X, handle = %X, names = %08X, count = %d)\n", values, resourceLimit, names, count); + logSVC("GetResourceLimitCurrentValues(values = %08X, handle = %X, names = %08X, count = %d)\n", values, resourceLimit, names, count); while (count != 0) { const u32 name = mem.read32(names); // TODO: Unsure if this is supposed to be s32 or u32. Shouldn't matter as the kernel can't create so many resources diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index 812b06a1..ac03905d 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -75,7 +75,7 @@ void Kernel::createThread() { u32 initialSP = regs[3] & ~7; // SP is force-aligned to 8 bytes s32 id = static_cast(regs[4]); - printf("CreateThread(entry = %08X, stacktop = %08X, priority = %X, processor ID = %d)\n", entrypoint, + logSVC("CreateThread(entry = %08X, stacktop = %08X, priority = %X, processor ID = %d)\n", entrypoint, initialSP, priority, id); if (priority > 0x3F) [[unlikely]] { @@ -99,7 +99,7 @@ void Kernel::sleepThreadOnArbiter(u32 waitingAddress) { void Kernel::getThreadID() { Handle handle = regs[1]; - printf("GetThreadID(handle = %X)\n", handle); + logSVC("GetThreadID(handle = %X)\n", handle); if (handle == KernelHandles::CurrentThread) { regs[0] = SVCResult::Success; diff --git a/src/core/services/apt.cpp b/src/core/services/apt.cpp index 66bde4c2..55093945 100644 --- a/src/core/services/apt.cpp +++ b/src/core/services/apt.cpp @@ -24,7 +24,7 @@ void APTService::handleSyncRequest(u32 messagePointer) { } void APTService::getLockHandle(u32 messagePointer) { - printf("APT::GetLockHandle (Failure)\n"); + log("APT::GetLockHandle (Failure)\n"); mem.write32(messagePointer + 4, Result::Failure); // Result code mem.write32(messagePointer + 16, 0); // Translation descriptor } \ No newline at end of file diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 883227ec..3cdb008b 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -26,11 +26,11 @@ void FSService::handleSyncRequest(u32 messagePointer) { } void FSService::initialize(u32 messagePointer) { - printf("FS::Initialize (failure)\n"); + log("FS::Initialize (failure)\n"); mem.write32(messagePointer + 4, Result::Failure); } void FSService::openArchive(u32 messagePointer) { - printf("FS::OpenArchive (failure)\n"); + log("FS::OpenArchive (failure)\n"); mem.write32(messagePointer + 4, Result::Failure); } \ No newline at end of file diff --git a/src/core/services/gsp_gpu.cpp b/src/core/services/gsp_gpu.cpp index 543da71f..c3b52a87 100644 --- a/src/core/services/gsp_gpu.cpp +++ b/src/core/services/gsp_gpu.cpp @@ -14,10 +14,11 @@ namespace ServiceCommands { } // Commands written to shared memory and processed by TriggerCmdReqQueue -namespace GPUCommands { +namespace GXCommands { enum : u32 { ProcessCommandList = 1, - MemoryFill = 2 + MemoryFill = 2, + TriggerDisplayTransfer = 3 }; } @@ -50,7 +51,7 @@ void GPUService::handleSyncRequest(u32 messagePointer) { 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); + log("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"); @@ -76,7 +77,7 @@ 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); + log("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 @@ -101,7 +102,7 @@ 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); + log("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)) { @@ -135,7 +136,7 @@ void GPUService::writeHwRegsWithMask(u32 messagePointer) { 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", + log("GSP::GPU::writeHwRegsWithMask (GPU address = %08X, size = %X, data address = %08X, mask address = %08X)\n", ioAddr, size, dataPointer, maskPointer); // Check for alignment @@ -172,14 +173,14 @@ void GPUService::flushDataCache(u32 messagePointer) { u32 address = mem.read32(messagePointer + 4); u32 size = mem.read32(messagePointer + 8); u32 processHandle = handle = mem.read32(messagePointer + 16); - printf("GSP::GPU::FlushDataCache(address = %08X, size = %X, process = %X\n", address, size, processHandle); + log("GSP::GPU::FlushDataCache(address = %08X, size = %X, process = %X\n", address, size, processHandle); mem.write32(messagePointer + 4, Result::Success); } void GPUService::setLCDForceBlack(u32 messagePointer) { u32 flag = mem.read32(messagePointer + 4); - printf("GSP::GPU::SetLCDForceBlank(flag = %d)\n", flag); + log("GSP::GPU::SetLCDForceBlank(flag = %d)\n", flag); if (flag != 0) { printf("Filled both LCDs with black\n"); @@ -193,9 +194,6 @@ void GPUService::triggerCmdReqQueue(u32 messagePointer) { mem.write32(messagePointer + 4, Result::Success); } -#include -#include -using namespace std::chrono_literals; void GPUService::processCommandBuffer() { if (sharedMem == nullptr) [[unlikely]] { // Shared memory hasn't been set up yet return; @@ -208,14 +206,14 @@ void GPUService::processCommandBuffer() { // 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); + log("Processing %d GPU commands\n", commandsLeft); while (commandsLeft != 0) { u32 cmdID = cmd[0] & 0xff; switch (cmdID) { - case GPUCommands::ProcessCommandList: processCommandList(cmd); break; - case GPUCommands::MemoryFill: memoryFill(cmd); break; + case GXCommands::ProcessCommandList: processCommandList(cmd); break; + case GXCommands::MemoryFill: memoryFill(cmd); break; + case GXCommands::TriggerDisplayTransfer: triggerDisplayTransfer(cmd); break; default: Helpers::panic("GSP::GPU::ProcessCommands: Unknown cmd ID %d", cmdID); } @@ -249,6 +247,11 @@ void GPUService::memoryFill(u32* cmd) { } } +void GPUService::triggerDisplayTransfer(u32* cmd) { + log("GSP::GPU::TriggerDisplayTransfer (Stubbed)\n"); + requestInterrupt(GPUInterrupt::PPF); // Send "Display transfer finished" interrupt +} + // Actually send command list (aka display list) to GPU void GPUService::processCommandList(u32* cmd) { u32 address = cmd[1] & ~7; // Buffer address @@ -305,5 +308,6 @@ void GPUService::processCommandList(u32* cmd) { } } - printf("GPU::GSP::processCommandList. Address: %08X, size in bytes: %08X\n", address, size); + log("GPU::GSP::processCommandList. Address: %08X, size in bytes: %08X\n", address, size); + requestInterrupt(GPUInterrupt::P3D); // Send an IRQ when command list processing is over } \ No newline at end of file diff --git a/src/core/services/hid.cpp b/src/core/services/hid.cpp index d11d72b9..87e38448 100644 --- a/src/core/services/hid.cpp +++ b/src/core/services/hid.cpp @@ -24,7 +24,7 @@ void HIDService::handleSyncRequest(u32 messagePointer) { } void HIDService::getIPCHandles(u32 messagePointer) { - printf("HID::GetIPCHandles (Failure)\n"); + log("HID::GetIPCHandles (Failure)\n"); mem.write32(messagePointer + 4, Result::Failure); // Result code mem.write32(messagePointer + 8, 0x14000000); // Translation descriptor } \ No newline at end of file diff --git a/src/core/services/ndm.cpp b/src/core/services/ndm.cpp index 0bf623f1..f171d0a4 100644 --- a/src/core/services/ndm.cpp +++ b/src/core/services/ndm.cpp @@ -25,11 +25,11 @@ void NDMService::handleSyncRequest(u32 messagePointer) { } void NDMService::overrideDefaultDaemons(u32 messagePointer) { - printf("NDM::OverrideDefaultDaemons(stubbed)\n"); + log("NDM::OverrideDefaultDaemons(stubbed)\n"); mem.write32(messagePointer + 4, Result::Success); } void NDMService::suspendDaemons(u32 messagePointer) { - printf("NDM::SuspendDaemons(stubbed)\n"); + log("NDM::SuspendDaemons(stubbed)\n"); mem.write32(messagePointer + 4, Result::Success); } \ No newline at end of file diff --git a/src/core/services/service_manager.cpp b/src/core/services/service_manager.cpp index 15f9acfa..2eed4b2b 100644 --- a/src/core/services/service_manager.cpp +++ b/src/core/services/service_manager.cpp @@ -54,7 +54,7 @@ void ServiceManager::handleSyncRequest(u32 messagePointer) { // https://www.3dbrew.org/wiki/SRV:RegisterClient void ServiceManager::registerClient(u32 messagePointer) { - printf("srv::registerClient (Stubbed)\n"); + log("srv::registerClient (Stubbed)\n"); mem.write32(messagePointer + 4, Result::Success); } @@ -65,7 +65,7 @@ void ServiceManager::getServiceHandle(u32 messagePointer) { u32 handle = 0; std::string service = mem.readString(messagePointer + 4, 8); - printf("srv::getServiceHandle (Service: %s, nameLength: %d, flags: %d)\n", service.c_str(), nameLength, flags); + log("srv::getServiceHandle (Service: %s, nameLength: %d, flags: %d)\n", service.c_str(), nameLength, flags); if (service == "APT:S") { handle = KernelHandles::APT; @@ -90,7 +90,7 @@ void ServiceManager::getServiceHandle(u32 messagePointer) { } void ServiceManager::enableNotification(u32 messagePointer) { - printf("srv::EnableNotification()\n"); + log("srv::EnableNotification()\n"); mem.write32(messagePointer + 4, Result::Success); // Result code mem.write32(messagePointer + 8, 0); // Translation descriptor diff --git a/src/emulator.cpp b/src/emulator.cpp index 5e383537..e4e3b758 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -23,20 +23,29 @@ void Emulator::render() { void Emulator::run() { while (window.isOpen()) { - runFrame(); - OpenGL::setClearColor(1.0, 0.0, 0.0, 1.0); - OpenGL::setViewport(400, 240 * 2); + // Clear window to black + OpenGL::bindScreenFramebuffer(); OpenGL::disableScissor(); + OpenGL::setClearColor(0.0, 0.0, 0.0, 1.0); OpenGL::clearColor(); + + gpu.getGraphicsContext(); // Give the GPU a rendering context + runFrame(); // Run 1 frame of instructions + gpu.display(); + + // Send VBlank interrupts + kernel.sendGPUInterrupt(GPUInterrupt::VBlank0); + kernel.sendGPUInterrupt(GPUInterrupt::VBlank1); + window.display(); sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) { - Helpers::panic("Bye :("); + printf("Bye :(\n"); + window.close(); + return; } } - - window.display(); } } diff --git a/src/main.cpp b/src/main.cpp index d7bc7ea5..8f1a2b7e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,6 +7,8 @@ int main (int argc, char *argv[]) { Helpers::panic("Failed to initialize OpenGL"); } + emu.initGraphicsContext(); + auto elfPath = std::filesystem::current_path() / (argc > 1 ? argv[1] : "SimplerTri.elf"); if (!emu.loadELF(elfPath)) { // For some reason just .c_str() doesn't show the proper path