diff --git a/CMakeLists.txt b/CMakeLists.txt index 429f979a..18c711a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,7 @@ set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) include_directories(${PROJECT_SOURCE_DIR}/include/) include_directories(${PROJECT_SOURCE_DIR}/include/kernel) include_directories (${FMT_INCLUDE_DIR}) +include_directories(third_party/boost/) include_directories(third_party/elfio/) include_directories(third_party/gl3w/) include_directories(third_party/imgui/) @@ -57,12 +58,18 @@ set(SERVICE_SOURCE_FILES src/core/services/service_manager.cpp src/core/services src/core/services/fs.cpp src/core/services/gsp_gpu.cpp src/core/services/gsp_lcd.cpp src/core/services/ndm.cpp src/core/services/dsp.cpp src/core/services/cfg.cpp src/core/services/ptm.cpp src/core/services/mic.cpp src/core/services/cecd.cpp + src/core/services/ac.cpp src/core/services/am.cpp src/core/services/boss.cpp + src/core/services/frd.cpp src/core/services/nim.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/renderer_opengl.cpp + src/core/PICA/shader_interpreter.cpp ) +set(RENDERER_GL_SOURCE_FILES src/core/renderer_gl/renderer_gl.cpp) + set(LOADER_SOURCE_FILES src/core/loader/elf.cpp src/core/loader/ncsd.cpp src/core/loader/ncch.cpp) -set(FS_SOURCE_FILES src/core/fs/archive_ncch.cpp src/core/fs/archive_save_data.cpp src/core/fs/archive_sdmc.cpp) +set(FS_SOURCE_FILES src/core/fs/archive_ncch.cpp src/core/fs/archive_save_data.cpp src/core/fs/archive_sdmc.cpp + src/core/fs/archive_ext_save_data.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 @@ -76,7 +83,10 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/opengl.hpp inc include/loader/lz77.hpp include/fs/archive_base.hpp include/fs/archive_ncch.hpp include/services/dsp.hpp include/services/cfg.hpp include/services/region_codes.hpp include/fs/archive_save_data.hpp include/fs/archive_sdmc.hpp include/services/ptm.hpp - include/services/mic.hpp include/services/cecd.hpp + include/services/mic.hpp include/services/cecd.hpp include/renderer_gl/renderer_gl.hpp + include/renderer_gl/surfaces.hpp include/renderer_gl/surface_cache.hpp include/services/ac.hpp + include/services/am.hpp include/services/boss.hpp include/services/frd.hpp include/services/nim.hpp + include/fs/archive_ext_save_data.hpp ) set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp @@ -95,8 +105,9 @@ source_group("Source Files\\Core\\Kernel" FILES ${KERNEL_SOURCE_FILES}) source_group("Source Files\\Core\\Loader" FILES ${LOADER_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\\Core\\OpenGL Renderer" FILES ${RENDERER_GL_SOURCE_FILES}) source_group("Source Files\\Third Party" FILES ${THIRD_PARTY_SOURCE_FILES}) add_executable(Alber ${SOURCE_FILES} ${FS_SOURCE_FILES} ${KERNEL_SOURCE_FILES} ${LOADER_SOURCE_FILES} ${SERVICE_SOURCE_FILES} -${PICA_SOURCE_FILES} ${THIRD_PARTY_SOURCE_FILES} ${HEADER_FILES}) +${PICA_SOURCE_FILES} ${RENDERER_GL_SOURCE_FILES} ${THIRD_PARTY_SOURCE_FILES} ${HEADER_FILES}) target_link_libraries(Alber PRIVATE dynarmic SDL2-static) \ No newline at end of file diff --git a/include/PICA/gpu.hpp b/include/PICA/gpu.hpp index 7a80c633..32655035 100644 --- a/include/PICA/gpu.hpp +++ b/include/PICA/gpu.hpp @@ -3,9 +3,9 @@ #include "helpers.hpp" #include "logger.hpp" #include "memory.hpp" -#include "opengl.hpp" #include "PICA/float_types.hpp" #include "PICA/shader_unit.hpp" +#include "renderer_gl/renderer_gl.hpp" class GPU { using vec4f = OpenGL::Vector; @@ -20,11 +20,6 @@ class GPU { 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 template @@ -80,32 +75,15 @@ 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 renderer state - OpenGL::Framebuffer fbo; - OpenGL::Texture fboTexture; - OpenGL::Program triangleProgram; - OpenGL::Program displayProgram; - - OpenGL::VertexArray vao; - OpenGL::VertexBuffer vbo; - GLint alphaControlLoc = -1; - u32 oldAlphaControl = 0; - - // 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); + Renderer renderer; 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 initGraphicsContext() { renderer.initGraphicsContext(); } + void getGraphicsContext() { renderer.getGraphicsContext(); } + void display() { renderer.display(); } void fireDMA(u32 dest, u32 source, u32 size); - void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control); void reset(); // Used by the GSP GPU service for readHwRegs/writeHwRegs/writeHwRegsMasked @@ -115,4 +93,8 @@ public: // Used when processing GPU command lists u32 readInternalReg(u32 index); void writeInternalReg(u32 index, u32 value, u32 mask); + + void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) { + renderer.clearBuffer(startAddress, endAddress, value, control); + } }; \ No newline at end of file diff --git a/include/PICA/regs.hpp b/include/PICA/regs.hpp index 76112d4c..dff9c819 100644 --- a/include/PICA/regs.hpp +++ b/include/PICA/regs.hpp @@ -14,6 +14,10 @@ namespace PICAInternalRegs { // Framebuffer registers AlphaTestConfig = 0x104, DepthAndColorMask = 0x107, + ColourBufferFormat = 0x117, + DepthBufferLoc = 0x11C, + ColourBufferLoc = 0x11D, + FramebufferSize = 0x11E, // Geometry pipeline registers VertexAttribLoc = 0x200, diff --git a/include/PICA/shader.hpp b/include/PICA/shader.hpp index bad37d2e..4f556fe1 100644 --- a/include/PICA/shader.hpp +++ b/include/PICA/shader.hpp @@ -18,6 +18,7 @@ namespace ShaderOpcodes { MUL = 0x08, MAX = 0x0C, MIN = 0x0D, + RCP = 0x0E, RSQ = 0x0F, MOVA = 0x12, MOV = 0x13, @@ -99,6 +100,7 @@ class PICAShader { void mov(u32 instruction); void mova(u32 instruction); void mul(u32 instruction); + void rcp(u32 instruction); void rsq(u32 instruction); // src1, src2 and src3 have different negation & component swizzle bits in the operand descriptor diff --git a/include/arm_defs.hpp b/include/arm_defs.hpp index 03143932..1d7a9bde 100644 --- a/include/arm_defs.hpp +++ b/include/arm_defs.hpp @@ -61,6 +61,7 @@ namespace FPSCR { RoundToZero = 3 << 22, // Default FPSCR value for threads - ThreadDefault = DefaultNan | FlushToZero | RoundToZero | IXC + ThreadDefault = DefaultNan | FlushToZero | RoundToZero, + MainThreadDefault = ThreadDefault | IXC }; } \ No newline at end of file diff --git a/include/fs/archive_ext_save_data.hpp b/include/fs/archive_ext_save_data.hpp new file mode 100644 index 00000000..973b2061 --- /dev/null +++ b/include/fs/archive_ext_save_data.hpp @@ -0,0 +1,16 @@ +#pragma once +#include "archive_base.hpp" + +class ExtSaveDataArchive : public ArchiveBase { +public: + ExtSaveDataArchive(Memory& mem) : ArchiveBase(mem) {} + + u64 getFreeBytes() override { Helpers::panic("ExtSaveData::GetFreeBytes unimplemented"); return 0; } + std::string name() override { return "ExtSaveData"; } + + bool openFile(const FSPath& path) override; + ArchiveBase* openArchive(const FSPath& path) override; + std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override; + + bool isShared = false; +}; \ No newline at end of file diff --git a/include/fs/archive_ncch.hpp b/include/fs/archive_ncch.hpp index 7ece602c..b079e60d 100644 --- a/include/fs/archive_ncch.hpp +++ b/include/fs/archive_ncch.hpp @@ -2,7 +2,6 @@ #include "archive_base.hpp" class SelfNCCHArchive : public ArchiveBase { - public: SelfNCCHArchive(Memory& mem) : ArchiveBase(mem) {} diff --git a/include/fs/archive_save_data.hpp b/include/fs/archive_save_data.hpp index f1f38560..5ff9d8ba 100644 --- a/include/fs/archive_save_data.hpp +++ b/include/fs/archive_save_data.hpp @@ -2,11 +2,10 @@ #include "archive_base.hpp" class SaveDataArchive : public ArchiveBase { - public: SaveDataArchive(Memory& mem) : ArchiveBase(mem) {} - u64 getFreeBytes() override { return 0; } + u64 getFreeBytes() override { Helpers::panic("SaveData::GetFreeBytes unimplemented"); return 0; } std::string name() override { return "SaveData"; } bool openFile(const FSPath& path) override; diff --git a/include/fs/archive_sdmc.hpp b/include/fs/archive_sdmc.hpp index d3cebe25..fc271881 100644 --- a/include/fs/archive_sdmc.hpp +++ b/include/fs/archive_sdmc.hpp @@ -2,11 +2,10 @@ #include "archive_base.hpp" class SDMCArchive : public ArchiveBase { - public: SDMCArchive(Memory& mem) : ArchiveBase(mem) {} - u64 getFreeBytes() override { return 0; } + u64 getFreeBytes() override { Helpers::panic("SDMC::GetFreeBytes unimplemented"); return 0; } std::string name() override { return "SDMC"; } bool openFile(const FSPath& path) override; diff --git a/include/kernel/config_mem.hpp b/include/kernel/config_mem.hpp index 541bf9bd..7533c42c 100644 --- a/include/kernel/config_mem.hpp +++ b/include/kernel/config_mem.hpp @@ -6,9 +6,12 @@ namespace ConfigMem { enum : u32 { KernelVersionMinor = 0x1FF80002, KernelVersionMajor = 0x1FF80003, + SyscoreVer = 0x1FF80010, EnvInfo = 0x1FF80014, AppMemAlloc = 0x1FF80040, Datetime0 = 0x1FF81020, - LedState3D = 0x1FF81084 + LedState3D = 0x1FF81084, + BatteryState = 0x1FF81085, + HeadphonesConnectedMaybe = 0x1FF810C0 // TODO: What is actually stored here? }; } \ No newline at end of file diff --git a/include/kernel/handles.hpp b/include/kernel/handles.hpp index 890d0318..947f6589 100644 --- a/include/kernel/handles.hpp +++ b/include/kernel/handles.hpp @@ -10,19 +10,24 @@ namespace KernelHandles { // Hardcoded handles CurrentThread = 0xFFFF8000, // Used by the original kernel CurrentProcess = 0xFFFF8001, // Used by the original kernel + AC, // Something network related + AM, // Application manager APT, // App Title something service? - CECD, // Streetpass stuff? + BOSS, // Streetpass stuff? + CECD, // More Streetpass stuff? CFG, // CFG service (Console & region info) HID, // HID service (Handles everything input-related including gyro) + FRD, // Friend service (Miiverse friend service) FS, // Filesystem service GPU, // GPU service DSP, // DSP service (Used for audio decoding and output) LCD, // LCD service (Used for configuring the displays) MIC, // MIC service (Controls the microphone) + NIM, // Updates, DLC, etc NDM, // ????? PTM, // PTM service (Used for accessing various console info, such as battery, shell and pedometer state) - MinServiceHandle = APT, + MinServiceHandle = AC, MaxServiceHandle = PTM, GSPSharedMemHandle = MaxServiceHandle + 1, // Handle for the GSP shared memory diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index ac6315b5..6662a722 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -81,6 +81,7 @@ private: MAKE_LOG_FUNCTION(log, kernelLogger) MAKE_LOG_FUNCTION(logSVC, svcLogger) + MAKE_LOG_FUNCTION(logThread, threadLogger) MAKE_LOG_FUNCTION(logDebugString, debugStringLogger) MAKE_LOG_FUNCTION(logError, errorLogger) MAKE_LOG_FUNCTION(logFileIO, fileIOLogger) diff --git a/include/logger.hpp b/include/logger.hpp index 01ac282b..655a83ce 100644 --- a/include/logger.hpp +++ b/include/logger.hpp @@ -18,26 +18,33 @@ namespace Log { }; // Our loggers here. Enable/disable by toggling the template param - static Logger kernelLogger; - static Logger debugStringLogger; // Enables output for the outputDebugString SVC - static Logger errorLogger; - static Logger fileIOLogger; - static Logger svcLogger; - static Logger gpuLogger; + static Logger kernelLogger; + static Logger debugStringLogger; // Enables output for the outputDebugString SVC + static Logger errorLogger; + static Logger fileIOLogger; + static Logger svcLogger; + static Logger threadLogger; + static Logger gpuLogger; + static Logger rendererLogger; // Service loggers - static Logger aptLogger; - static Logger cecdLogger; - static Logger cfgLogger; - static Logger dspServiceLogger; - static Logger fsLogger; - static Logger hidLogger; - static Logger gspGPULogger; - static Logger gspLCDLogger; - static Logger micLogger; - static Logger ndmLogger; - static Logger ptmLogger; - static Logger srvLogger; + static Logger acLogger; + static Logger amLogger; + static Logger aptLogger; + static Logger bossLogger; + static Logger cecdLogger; + static Logger cfgLogger; + static Logger dspServiceLogger; + static Logger frdLogger; + static Logger fsLogger; + static Logger hidLogger; + static Logger gspGPULogger; + static Logger gspLCDLogger; + static Logger micLogger; + static Logger nimLogger; + static Logger ndmLogger; + static Logger ptmLogger; + static Logger srvLogger; #define MAKE_LOG_FUNCTION(functionName, logger) \ template \ diff --git a/include/memory.hpp b/include/memory.hpp index a3815534..1775bf04 100644 --- a/include/memory.hpp +++ b/include/memory.hpp @@ -156,6 +156,17 @@ public: u32 getLinearHeapVaddr(); u8* getFCRAM() { return fcram; } + enum class BatteryLevel { + Empty = 0, AlmostEmpty, OneBar, TwoBars, ThreeBars, FourBars + }; + u8 getBatteryState(bool adapterConnected, bool charging, BatteryLevel batteryLevel) { + u8 value = static_cast(batteryLevel) << 2; // Bits 2:4 are the battery level from 0 to 5 + if (adapterConnected) value |= 1 << 0; // Bit 0 shows if the charger is connected + if (charging) value |= 1 << 1; // Bit 1 shows if we're charging + + return value; + } + NCCH* getCXI() { if (loadedCXI.has_value()) { return &loadedCXI.value(); diff --git a/include/opengl.hpp b/include/opengl.hpp index 07ecc9f0..f91471e8 100644 --- a/include/opengl.hpp +++ b/include/opengl.hpp @@ -30,6 +30,9 @@ #include "gl3w.h" +// Uncomment the following define if you want GL objects to automatically free themselves when their lifetime ends +// #define OPENGL_DESTRUCTORS + namespace OpenGL { // Workaround for using static_assert inside constexpr if @@ -51,7 +54,9 @@ namespace OpenGL { } } - ~VertexArray() { glDeleteVertexArrays(1, &m_handle); } +#ifdef OPENGL_DESTRUCTORS + ~VertexArray() { free(); } +#endif GLuint handle() { return m_handle; } bool exists() { return m_handle != 0; } void bind() { glBindVertexArray(m_handle); } @@ -121,6 +126,10 @@ namespace OpenGL { void enableAttribute(GLuint index) { glEnableVertexAttribArray(index); } void disableAttribute(GLuint index) { glDisableVertexAttribArray(index); } + + void free() { + glDeleteVertexArrays(1, &m_handle); + } }; enum FramebufferTypes { @@ -165,12 +174,16 @@ namespace OpenGL { create(width, height, internalFormat, GL_TEXTURE_2D_MULTISAMPLE, samples); } - ~Texture() { glDeleteTextures(1, &m_handle); } +#ifdef OPENGL_DESTRUCTORS + ~Texture() { free(); } +#endif GLuint handle() { return m_handle; } bool exists() { return m_handle != 0; } void bind() { glBindTexture(m_binding, m_handle); } int width() { return m_width; } int height() { return m_height; } + + void free() { glDeleteTextures(1, &m_handle); } }; struct Framebuffer { @@ -189,11 +202,14 @@ namespace OpenGL { } } - ~Framebuffer() { glDeleteFramebuffers(1, &m_handle); } +#ifdef OPENGL_DESTRUCTORS + ~Framebuffer() { free(); } +#endif GLuint handle() { return m_handle; } bool exists() { return m_handle != 0; } void bind(GLenum target) { glBindFramebuffer(target, m_handle); } void bind(FramebufferTypes target) { bind(static_cast(target)); } + void free() { glDeleteFramebuffers(1, &m_handle); } void createWithTexture(Texture& tex, GLenum mode = GL_FRAMEBUFFER, GLenum textureType = GL_TEXTURE_2D) { m_textureType = textureType; @@ -313,10 +329,13 @@ namespace OpenGL { } } - ~VertexBuffer() { glDeleteBuffers(1, &m_handle); } +#ifdef OPENGL_DESTRUCTORS + ~VertexBuffer() { free(); } +#endif GLuint handle() { return m_handle; } bool exists() { return m_handle != 0; } void bind() { glBindBuffer(GL_ARRAY_BUFFER, m_handle); } + void free() { glDeleteBuffers(1, &m_handle); } // Reallocates the buffer on every call. Prefer the sub version if possible. template diff --git a/include/renderer_gl/renderer_gl.hpp b/include/renderer_gl/renderer_gl.hpp new file mode 100644 index 00000000..072f0d11 --- /dev/null +++ b/include/renderer_gl/renderer_gl.hpp @@ -0,0 +1,69 @@ +#pragma once +#include +#include "helpers.hpp" +#include "logger.hpp" +#include "opengl.hpp" +#include "surface_cache.hpp" + +struct Vertex { + OpenGL::vec4 position; + OpenGL::vec4 colour; +}; + +class Renderer { + OpenGL::Program triangleProgram; + OpenGL::Program displayProgram; + + OpenGL::VertexArray vao; + OpenGL::VertexBuffer vbo; + GLint alphaControlLoc = -1; + u32 oldAlphaControl = 0; + + SurfaceCache depthBufferCache; + SurfaceCache colourBufferCache; + OpenGL::uvec2 fbSize; // The size of the framebuffer (ie both the colour and depth buffer)' + + u32 colourBufferLoc; // Location in 3DS VRAM for the colour buffer + ColourBuffer::Formats colourBufferFormat; // Format of the colours stored in the colour buffer + + // Same for the depth/stencil buffer + u32 depthBufferLoc; + DepthBuffer::Formats depthBufferFormat; + + // Dummy VAO/VBO for blitting the final output + OpenGL::VertexArray dummyVAO; + OpenGL::VertexBuffer dummyVBO; + + static constexpr u32 regNum = 0x300; // Number of internal PICA registers + const std::array& regs; + + OpenGL::Framebuffer getColourFBO(); + + MAKE_LOG_FUNCTION(log, rendererLogger) + +public: + Renderer(const std::array& internalRegs) : regs(internalRegs) {} + + void reset(); + void display(); // Display the 3DS screen contents to the window + void initGraphicsContext(); // Initialize graphics context + void getGraphicsContext(); // Set up graphics context for rendering + void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control); // Clear a GPU buffer in VRAM + void drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 count); // Draw the given vertices + + void setFBSize(u32 width, u32 height) { + fbSize.x() = width; + fbSize.y() = height; + } + + void setColourFormat(ColourBuffer::Formats format) { colourBufferFormat = format; } + void setColourFormat(u32 format) { colourBufferFormat = static_cast(format); } + + void setDepthFormat(DepthBuffer::Formats format) { depthBufferFormat = format; } + void setDepthFormat(u32 format) { depthBufferFormat = static_cast(format); } + + void setColourBufferLoc(u32 loc) { colourBufferLoc = loc; } + void setDepthBufferLoc(u32 loc) { depthBufferLoc = loc; } + + static constexpr u32 vertexBufferSize = 0x1500; +}; \ No newline at end of file diff --git a/include/renderer_gl/surface_cache.hpp b/include/renderer_gl/surface_cache.hpp new file mode 100644 index 00000000..8523e7fb --- /dev/null +++ b/include/renderer_gl/surface_cache.hpp @@ -0,0 +1,65 @@ +#pragma once +#include +#include +#include "surfaces.hpp" + +// Surface cache class that can fit "capacity" instances of the "SurfaceType" class of surfaces +// SurfaceType *must* have all of the following +// - An "allocate" function that allocates GL resources for the surfaces +// - A "free" function that frees up all resources the surface is taking up +// - A "matches" function that, when provided with a SurfaceType object reference +// Will tell us if the 2 surfaces match (Only as far as location in VRAM, format, dimensions, etc) +// Are concerned. We could overload the == operator, but that implies full equality +// Including equality of the allocated OpenGL resources, which we don't want +// - A "valid" member that tells us whether the function is still valid or not +template +class SurfaceCache { + // Vanilla std::optional can't hold actual references + using OptionalRef = std::optional>; + static_assert(std::is_same() || std::is_same(), + "Invalid surface type"); + + size_t size; + std::array buffer; + +public: + void reset() { + size = 0; + for (auto& e : buffer) { // Free the VRAM of all surfaces + e.free(); + } + } + + OptionalRef find(SurfaceType& other) { + for (auto& e : buffer) { + if (e.matches(other) && e.valid) + return e; + } + + return std::nullopt; + } + + // Adds a surface object to the cache and returns it + SurfaceType add(SurfaceType& surface) { + if (size >= capacity) { + Helpers::panic("Surface cache full! Add emptying!"); + } + size++; + + // Find an invalid entry in the cache and overwrite it with the new surface + for (auto& e : buffer) { + if (!e.valid) { + e = surface; + e.allocate(); + return e; + } + } + + // This should be unreachable but helps to panic anyways + Helpers::panic("Couldn't add surface to cache\n"); + } + + SurfaceType& operator[](size_t i) { + return buffer[i]; + } +}; diff --git a/include/renderer_gl/surfaces.hpp b/include/renderer_gl/surfaces.hpp new file mode 100644 index 00000000..93ccd839 --- /dev/null +++ b/include/renderer_gl/surfaces.hpp @@ -0,0 +1,152 @@ +#pragma once +#include "boost/icl/interval.hpp" +#include "helpers.hpp" +#include "opengl.hpp" + +template +using Interval = boost::icl::right_open_interval; + +struct ColourBuffer { + enum class Formats : u32 { + RGBA8 = 0, + BGR8 = 1, + RGB5A1 = 2, + RGB565 = 3, + RGBA4 = 4, + + Trash1 = 5, Trash2 = 6, Trash3 = 7 // Technically selectable, but their function is unknown + }; + + u32 location; + Formats format; + OpenGL::uvec2 size; + bool valid; + + // Range of VRAM taken up by buffer + Interval range; + // OpenGL resources allocated to buffer + OpenGL::Texture texture; + OpenGL::Framebuffer fbo; + + ColourBuffer() : valid(false) {} + + ColourBuffer(u32 loc, Formats format, u32 x, u32 y, bool valid = true) + : location(loc), format(format), size({x, y}), valid(valid) { + + u64 endLoc = (u64)loc + sizeInBytes(); + // Check if start and end are valid here + range = Interval(loc, (u32)endLoc); + } + + void allocate() { + // Create texture for the FBO, setting up filters and the like + // Reading back the current texture is slow, but allocate calls should be and far between. + // If this becomes a bottleneck, we can fix it semi-easily + auto prevTexture = OpenGL::getTex2D(); + texture.create(size.x(), size.y(), GL_RGBA8); + texture.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, prevTexture); + + //Helpers::panic("Creating FBO: %d, %d\n", size.x(), size.y()); + + fbo.createWithDrawTexture(texture); + fbo.bind(OpenGL::DrawAndReadFramebuffer); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + Helpers::panic("Incomplete framebuffer"); + //glBindRenderbuffer(GL_RENDERBUFFER, 0); + + // TODO: This should not clear the framebuffer contents. It should load them from VRAM. + GLint oldViewport[4]; + glGetIntegerv(GL_VIEWPORT, oldViewport); + OpenGL::setViewport(size.x(), size.y()); + OpenGL::setClearColor(0.0, 0.0, 0.0, 1.0); + OpenGL::clearColor(); + OpenGL::setViewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]); + } + + void free() { + valid = false; + + if (texture.exists() || fbo.exists()) + Helpers::panic("Make this buffer free itself"); + } + + bool matches(ColourBuffer& other) { + return location == other.location && format == other.format && + size.x() == other.size.x() && size.y() == other.size.y(); + } + + // Size occupied by each pixel in bytes + // All formats are 16BPP except for RGBA8 (32BPP) and BGR8 (24BPP) + size_t sizePerPixel() { + switch (format) { + case Formats::BGR8: return 3; + case Formats::RGBA8: return 4; + default: return 2; + } + } + + size_t sizeInBytes() { + return (size_t)size.x() * (size_t)size.y() * sizePerPixel(); + } +}; + +struct DepthBuffer { + enum class Formats : u32 { + Depth16 = 0, + Garbage = 1, + Depth24 = 2, + Depth24Stencil8 = 3 + }; + + u32 location; + Formats format; + OpenGL::uvec2 size; // Implicitly set to the size of the framebuffer + bool valid; + + // Range of VRAM taken up by buffer + Interval range; + // OpenGL texture used for storing depth/stencil + OpenGL::Texture texture; + + DepthBuffer() : valid(false) {} + + DepthBuffer(u32 loc, Formats format, u32 x, u32 y, bool valid = true) : + location(loc), format(format), size({x, y}), valid(valid) {} + + bool hasStencil() { + return format == Formats::Depth24Stencil8; + } + + void allocate() { + printf("Make this depth buffer allocate itself\n"); + } + + void free() { + valid = false; + printf("Make this depth buffer free itself\n"); + } + + bool matches(DepthBuffer& other) { + return location == other.location && format == other.format && + size.x() == other.size.x() && size.y() == other.size.y(); + } + + // Size occupied by each pixel in bytes + size_t sizePerPixel() { + switch (format) { + case Formats::Depth16: return 2; + case Formats::Depth24: return 3; + case Formats::Depth24Stencil8: return 4; + + default: return 1; // Invalid format + } + } + + size_t sizeInBytes() { + return (size_t)size.x() * (size_t)size.y() * sizePerPixel(); + } +}; \ No newline at end of file diff --git a/include/services/ac.hpp b/include/services/ac.hpp new file mode 100644 index 00000000..2053ea21 --- /dev/null +++ b/include/services/ac.hpp @@ -0,0 +1,19 @@ +#pragma once +#include "helpers.hpp" +#include "kernel_types.hpp" +#include "logger.hpp" +#include "memory.hpp" + +class ACService { + Handle handle = KernelHandles::AC; + Memory& mem; + MAKE_LOG_FUNCTION(log, acLogger) + + // Service commands + void setClientVersion(u32 messagePointer); + +public: + ACService(Memory& mem) : mem(mem) {} + void reset(); + void handleSyncRequest(u32 messagePointer); +}; \ No newline at end of file diff --git a/include/services/am.hpp b/include/services/am.hpp new file mode 100644 index 00000000..e406b2ef --- /dev/null +++ b/include/services/am.hpp @@ -0,0 +1,19 @@ +#pragma once +#include "helpers.hpp" +#include "kernel_types.hpp" +#include "logger.hpp" +#include "memory.hpp" + +class AMService { + Handle handle = KernelHandles::AM; + Memory& mem; + MAKE_LOG_FUNCTION(log, amLogger) + + // Service commands + void listTitleInfo(u32 messagePointer); + +public: + AMService(Memory& mem) : mem(mem) {} + void reset(); + void handleSyncRequest(u32 messagePointer); +}; \ No newline at end of file diff --git a/include/services/apt.hpp b/include/services/apt.hpp index f5356058..6c6c5fb4 100644 --- a/include/services/apt.hpp +++ b/include/services/apt.hpp @@ -27,14 +27,33 @@ class APTService { void checkNew3DSApp(u32 messagePointer); void enable(u32 messagePointer); void initialize(u32 messagePointer); + void inquireNotification(u32 messagePointer); void notifyToWait(u32 messagePointer); void receiveParameter(u32 messagePointer); void replySleepQuery(u32 messagePointer); void setApplicationCpuTimeLimit(u32 messagePointer); + void setScreencapPostPermission(u32 messagePointer); // Percentage of the syscore available to the application, between 5% and 89% u32 cpuTimeLimit; + enum class NotificationType : u32 { + None = 0, + HomeButton1 = 1, + HomeButton2 = 2, + SleepQuery = 3, + SleepCanceledByOpen = 4, + SleepAccepted = 5, + SleepAwake = 6, + Shutdown = 7, + PowerButtonClick = 8, + PowerButtonClear = 9, + TrySleep = 10, + OrderToClose = 11 + }; + + u32 screencapPostPermission; + public: APTService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {} void reset(); diff --git a/include/services/boss.hpp b/include/services/boss.hpp new file mode 100644 index 00000000..87a72c9f --- /dev/null +++ b/include/services/boss.hpp @@ -0,0 +1,19 @@ +#pragma once +#include "helpers.hpp" +#include "kernel_types.hpp" +#include "logger.hpp" +#include "memory.hpp" + +class BOSSService { + Handle handle = KernelHandles::BOSS; + Memory& mem; + MAKE_LOG_FUNCTION(log, bossLogger) + + // Service commands + void initializeSession(u32 messagePointer); + +public: + BOSSService(Memory& mem) : mem(mem) {} + void reset(); + void handleSyncRequest(u32 messagePointer); +}; \ No newline at end of file diff --git a/include/services/cecd.hpp b/include/services/cecd.hpp index 53c30b59..3a592a32 100644 --- a/include/services/cecd.hpp +++ b/include/services/cecd.hpp @@ -9,7 +9,7 @@ class CECDService { Memory& mem; MAKE_LOG_FUNCTION(log, cecdLogger) - // Service commands + // Service commands public: CECDService(Memory& mem) : mem(mem) {} diff --git a/include/services/dsp.hpp b/include/services/dsp.hpp index 0033bdc6..e32aa314 100644 --- a/include/services/dsp.hpp +++ b/include/services/dsp.hpp @@ -50,6 +50,7 @@ class DSPService { // Service functions void convertProcessAddressFromDspDram(u32 messagePointer); // Nice function name + void flushDataCache(u32 messagePointer); void getHeadphoneStatus(u32 messagePointer); void getSemaphoreHandle(u32 messagePointer); void loadComponent(u32 messagePointer); diff --git a/include/services/frd.hpp b/include/services/frd.hpp new file mode 100644 index 00000000..c33aeb5b --- /dev/null +++ b/include/services/frd.hpp @@ -0,0 +1,21 @@ +#pragma once +#include "helpers.hpp" +#include "kernel_types.hpp" +#include "logger.hpp" +#include "memory.hpp" + +class FRDService { + Handle handle = KernelHandles::FRD; + Memory& mem; + MAKE_LOG_FUNCTION(log, frdLogger) + + // Service commands + void getMyFriendKey(u32 messagePointer); + void getMyPresence(u32 messagePointer); + void setClientSDKVersion(u32 messagePointer); + +public: + FRDService(Memory& mem) : mem(mem) {} + void reset(); + void handleSyncRequest(u32 messagePointer); +}; \ No newline at end of file diff --git a/include/services/fs.hpp b/include/services/fs.hpp index 9e338c91..8dcfc854 100644 --- a/include/services/fs.hpp +++ b/include/services/fs.hpp @@ -1,4 +1,5 @@ #pragma once +#include "fs/archive_ext_save_data.hpp" #include "fs/archive_ncch.hpp" #include "fs/archive_save_data.hpp" #include "fs/archive_sdmc.hpp" @@ -20,6 +21,7 @@ class FSService { // The different filesystem archives (Save data, RomFS+ExeFS, etc) SelfNCCHArchive selfNcch; SaveDataArchive saveData; + ExtSaveDataArchive sharedExtSaveData; SDMCArchive sdmc; ArchiveBase* getArchiveFromID(u32 id); @@ -31,6 +33,7 @@ class FSService { void getPriority(u32 messagePointer); void initialize(u32 messagePointer); void initializeWithSdkVersion(u32 messagePointer); + void isSdmcDetected(u32 messagePointer); void openArchive(u32 messagePointer); void openFile(u32 messagePointer); void openFileDirectly(u32 messagePointer); @@ -40,7 +43,12 @@ class FSService { u32 priority; public: - FSService(Memory& mem, Kernel& kernel) : mem(mem), saveData(mem), sdmc(mem), selfNcch(mem), kernel(kernel) {} + FSService(Memory& mem, Kernel& kernel) : mem(mem), saveData(mem), sharedExtSaveData(mem), sdmc(mem), selfNcch(mem), + kernel(kernel) + { + sharedExtSaveData.isShared = true; // Need to do this here because templates and virtual classes do not mix well + } + void reset(); void handleSyncRequest(u32 messagePointer); }; \ No newline at end of file diff --git a/include/services/gsp_gpu.hpp b/include/services/gsp_gpu.hpp index 3c974b65..789ed211 100644 --- a/include/services/gsp_gpu.hpp +++ b/include/services/gsp_gpu.hpp @@ -37,6 +37,7 @@ class GPUService { void setAxiConfigQoSMode(u32 messagePointer); void setInternalPriorities(u32 messagePointer); void setLCDForceBlack(u32 messagePointer); + void storeDataCache(u32 messagePointer); void triggerCmdReqQueue(u32 messagePointer); void writeHwRegs(u32 messagePointer); void writeHwRegsWithMask(u32 messagePointer); diff --git a/include/services/nim.hpp b/include/services/nim.hpp new file mode 100644 index 00000000..6d45b837 --- /dev/null +++ b/include/services/nim.hpp @@ -0,0 +1,19 @@ +#pragma once +#include "helpers.hpp" +#include "kernel_types.hpp" +#include "logger.hpp" +#include "memory.hpp" + +class NIMService { + Handle handle = KernelHandles::NIM; + Memory& mem; + MAKE_LOG_FUNCTION(log, nimLogger) + + // Service commands + void initialize(u32 messagePointer); + +public: + NIMService(Memory& mem) : mem(mem) {} + void reset(); + void handleSyncRequest(u32 messagePointer); +}; \ No newline at end of file diff --git a/include/services/service_manager.hpp b/include/services/service_manager.hpp index d9903372..a072b88a 100644 --- a/include/services/service_manager.hpp +++ b/include/services/service_manager.hpp @@ -4,15 +4,20 @@ #include "kernel_types.hpp" #include "logger.hpp" #include "memory.hpp" +#include "services/ac.hpp" +#include "services/am.hpp" #include "services/apt.hpp" +#include "services/boss.hpp" #include "services/cecd.hpp" #include "services/cfg.hpp" #include "services/dsp.hpp" #include "services/hid.hpp" +#include "services/frd.hpp" #include "services/fs.hpp" #include "services/gsp_gpu.hpp" #include "services/gsp_lcd.hpp" #include "services/mic.hpp" +#include "services/nim.hpp" #include "services/ndm.hpp" #include "services/ptm.hpp" @@ -28,15 +33,20 @@ class ServiceManager { MAKE_LOG_FUNCTION(log, srvLogger) + ACService ac; + AMService am; APTService apt; + BOSSService boss; CECDService cecd; CFGService cfg; DSPService dsp; HIDService hid; + FRDService frd; FSService fs; GPUService gsp_gpu; LCDService gsp_lcd; MICService mic; + NIMService nim; NDMService ndm; PTMService ptm; @@ -45,6 +55,7 @@ class ServiceManager { void getServiceHandle(u32 messagePointer); void receiveNotification(u32 messagePointer); void registerClient(u32 messagePointer); + void subscribe(u32 messagePointer); public: ServiceManager(std::array& regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel); diff --git a/src/core/CPU/cpu_dynarmic.cpp b/src/core/CPU/cpu_dynarmic.cpp index 9092b7a3..c01d0641 100644 --- a/src/core/CPU/cpu_dynarmic.cpp +++ b/src/core/CPU/cpu_dynarmic.cpp @@ -9,7 +9,7 @@ CPU::CPU(Memory& mem, Kernel& kernel) : mem(mem), env(mem, kernel, *this) { config.arch_version = Dynarmic::A32::ArchVersion::v6K; config.callbacks = &env; config.coprocessors[15] = cp15; - // config.define_unpredictable_behaviour = true; + config.define_unpredictable_behaviour = true; config.global_monitor = &exclusiveMonitor; config.processor_id = 0; @@ -18,7 +18,7 @@ CPU::CPU(Memory& mem, Kernel& kernel) : mem(mem), env(mem, kernel, *this) { void CPU::reset() { setCPSR(CPSR::UserMode); - setFPSCR(FPSCR::ThreadDefault); + setFPSCR(FPSCR::MainThreadDefault); env.totalTicks = 0; cp15->reset(); @@ -26,6 +26,7 @@ void CPU::reset() { jit->Reset(); jit->ClearCache(); jit->Regs().fill(0); + jit->ExtRegs().fill(0); } #endif // CPU_DYNARMIC \ No newline at end of file diff --git a/src/core/PICA/gpu.cpp b/src/core/PICA/gpu.cpp index 3e02280f..c9e22190 100644 --- a/src/core/PICA/gpu.cpp +++ b/src/core/PICA/gpu.cpp @@ -5,7 +5,7 @@ using namespace Floats; -GPU::GPU(Memory& mem) : mem(mem) { +GPU::GPU(Memory& mem) : mem(mem), renderer(regs) { vram = new u8[vramSize]; } @@ -27,7 +27,7 @@ void GPU::reset() { e.config2 = 0; } - // TODO: Reset blending, texturing, etc here + renderer.reset(); } void GPU::drawArrays(bool indexed) { @@ -49,12 +49,13 @@ void GPU::drawArrays() { const u32 primType = (primConfig >> 8) & 3; if (primType != 0 && primType != 1) Helpers::panic("[PICA] Tried to draw unimplemented shape %d\n", primType); if (vertexCount > vertexBufferSize) Helpers::panic("[PICA] vertexCount > vertexBufferSize"); + if (vertexCount > Renderer::vertexBufferSize) Helpers::panic("[PICA] vertexCount > vertexBufferSize"); if ((primType == 0 && vertexCount % 3) || (primType == 1 && vertexCount < 3)) { Helpers::panic("Invalid vertex count for primitive. Type: %d, vert count: %d\n", primType, vertexCount); } - Vertex vertices[vertexBufferSize]; + Vertex vertices[Renderer::vertexBufferSize]; // Get the configuration for the index buffer, used only for indexed drawing u32 indexBufferConfig = regs[PICAInternalRegs::IndexBufferConfig]; @@ -157,7 +158,7 @@ void GPU::drawArrays() { OpenGL::Triangle, OpenGL::TriangleStrip, OpenGL::TriangleFan, OpenGL::LineStrip }; const auto shape = primTypes[primType]; - drawVertices(shape, vertices, vertexCount); + renderer.drawVertices(shape, vertices, vertexCount); } void GPU::fireDMA(u32 dest, u32 source, u32 size) { diff --git a/src/core/PICA/regs.cpp b/src/core/PICA/regs.cpp index 7a2c15c4..cfd0c6d0 100644 --- a/src/core/PICA/regs.cpp +++ b/src/core/PICA/regs.cpp @@ -4,8 +4,13 @@ using namespace Floats; u32 GPU::readReg(u32 address) { - log("Ignoring read from GPU register %08X\n", address); - return 0; + if (address >= 0x1EF01000 && address < 0x1EF01C00) { // Internal registers + const u32 index = (address - 0x1EF01000) / sizeof(u32); + return readInternalReg(index); + } else { + log("Ignoring read to external GPU register %08X.\n", address); + return 0; + } } void GPU::writeReg(u32 address, u32 value) { @@ -54,6 +59,31 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) { fixedAttribMask = (value >> 16) & 0xfff; // Determines which vertex attributes are fixed for all vertices break; + case ColourBufferLoc: { + u32 loc = (value & 0x0fffffff) << 3; + renderer.setColourBufferLoc(loc); + break; + }; + + case ColourBufferFormat: { + u32 format = (value >> 16) & 7; + renderer.setColourFormat(format); + break; + } + + case DepthBufferLoc: { + u32 loc = (value & 0x0fffffff) << 3; + renderer.setDepthBufferLoc(loc); + break; + } + + case FramebufferSize: { + const u32 width = value & 0x7ff; + const u32 height = ((value >> 12) & 0x3ff) + 1; + renderer.setFBSize(width, height); + break; + } + case VertexFloatUniformIndex: shaderUnit.vs.setFloatUniformIndex(value); break; diff --git a/src/core/PICA/shader_interpreter.cpp b/src/core/PICA/shader_interpreter.cpp index 7a7bf45b..122e75cb 100644 --- a/src/core/PICA/shader_interpreter.cpp +++ b/src/core/PICA/shader_interpreter.cpp @@ -30,6 +30,7 @@ void PICAShader::run() { case ShaderOpcodes::MOVA: mova(instruction); break; case ShaderOpcodes::MUL: mul(instruction); break; case ShaderOpcodes::NOP: break; // Do nothing + case ShaderOpcodes::RCP: rcp(instruction); break; case ShaderOpcodes::RSQ: rsq(instruction); break; case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3E: case 0x3F: @@ -292,6 +293,26 @@ void PICAShader::dp4(u32 instruction) { } } +void PICAShader::rcp(u32 instruction) { + const u32 operandDescriptor = operandDescriptors[instruction & 0x7f]; + const u32 src1 = (instruction >> 12) & 0x7f; + const u32 idx = (instruction >> 19) & 3; + const u32 dest = (instruction >> 21) & 0x1f; + + if (idx) Helpers::panic("[PICA] RCP: idx != 0"); + vec4f srcVec1 = getSourceSwizzled<1>(src1, operandDescriptor); + + vec4f& destVector = getDest(dest); + f24 res = f24::fromFloat32(1.0f) / srcVec1[0]; + + u32 componentMask = operandDescriptor & 0xf; + for (int i = 0; i < 4; i++) { + if (componentMask & (1 << i)) { + destVector[3 - i] = res; + } + } +} + void PICAShader::rsq(u32 instruction) { const u32 operandDescriptor = operandDescriptors[instruction & 0x7f]; const u32 src1 = (instruction >> 12) & 0x7f; diff --git a/src/core/fs/archive_ext_save_data.cpp b/src/core/fs/archive_ext_save_data.cpp new file mode 100644 index 00000000..ea7adf61 --- /dev/null +++ b/src/core/fs/archive_ext_save_data.cpp @@ -0,0 +1,29 @@ +#include "fs/archive_ext_save_data.hpp" +#include + +bool ExtSaveDataArchive::openFile(const FSPath& path) { + if (path.type != PathType::Binary) { + Helpers::panic("ExtSaveData accessed with a non-binary path in OpenFile. Type: %d", path.type); + } + + Helpers::panic("ExtSaveDataArchive::OpenFile: Failed"); + return false; +} + +ArchiveBase* ExtSaveDataArchive::openArchive(const FSPath& path) { + if (path.type != PathType::Binary || path.size != 12) { + Helpers::panic("ExtSaveData accessed with an invalid path in OpenArchive"); + } + + u32 mediaType = mem.read32(path.pointer); + u64 saveID = mem.read64(path.pointer + 4); + + Helpers::panic("ExtSaveData: media type = %d\n", mediaType); + + return this; +} + +std::optional ExtSaveDataArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) { + Helpers::panic("ExtSaveDataArchive::ReadFile: Failed"); + return std::nullopt; +} \ No newline at end of file diff --git a/src/core/kernel/events.cpp b/src/core/kernel/events.cpp index 3c12ee2c..b74b5536 100644 --- a/src/core/kernel/events.cpp +++ b/src/core/kernel/events.cpp @@ -37,7 +37,7 @@ void Kernel::clearEvent() { logSVC("ClearEvent(event handle = %X)\n", handle); if (event == nullptr) [[unlikely]] { - Helpers::panic("Tried to clear non-existent event"); + Helpers::panic("Tried to clear non-existent event (handle = %X)", handle); regs[0] = SVCResult::BadHandle; return; } @@ -154,6 +154,7 @@ void Kernel::waitSynchronizationN() { } regs[0] = SVCResult::Success; + regs[1] = waitAll ? handleCount - 1 : 0; // Index of the handle that triggered the exit. STUBBED t.status = ThreadStatus::WaitSyncAll; t.waitAll = waitAll; t.outPointer = outPointer; diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index 7f686783..9198d069 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -11,7 +11,7 @@ void Kernel::switchThread(int newThreadIndex) { auto& oldThread = threads[currentThreadIndex]; auto& newThread = threads[newThreadIndex]; newThread.status = ThreadStatus::Running; - printf("Switching from thread %d to %d\n", currentThreadIndex, newThreadIndex); + logThread("Switching from thread %d to %d\n", currentThreadIndex, newThreadIndex); // Bail early if the new thread is actually the old thread if (currentThreadIndex == newThreadIndex) [[unlikely]] { @@ -70,8 +70,6 @@ std::optional Kernel::getNextThread() { if (canThreadRun(t)) { return index; } - - // TODO: Check timeouts here } // No thread was found @@ -274,7 +272,7 @@ void Kernel::setThreadPriority() { object->getData()->priority = priority; } } - + sortThreads(); rescheduleThreads(); } diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 12ebeeaf..d8de8584 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -74,10 +74,12 @@ u8 Memory::read8(u32 vaddr) { } else { switch (vaddr) { + case ConfigMem::BatteryState: return getBatteryState(true, true, BatteryLevel::FourBars); case ConfigMem::EnvInfo: return envInfo; case ConfigMem::KernelVersionMinor: return u8(kernelVersion & 0xff); case ConfigMem::KernelVersionMajor: return u8(kernelVersion >> 8); case ConfigMem::LedState3D: return 1; // Report the 3D LED as always off (non-zero) for now + case ConfigMem::HeadphonesConnectedMaybe: return 0; default: Helpers::panic("Unimplemented 8-bit read, addr: %08X", vaddr); } } @@ -115,6 +117,7 @@ u32 Memory::read32(u32 vaddr) { return 0; // Set to 0 by PTM case ConfigMem::AppMemAlloc: return appResourceLimits.maxCommit; + case ConfigMem::SyscoreVer: return 2; case 0x1FF81000: return 0; // TODO: Figure out what this config mem address does default: if (vaddr >= VirtualAddrs::VramStart && vaddr < VirtualAddrs::VramStart + VirtualAddrs::VramSize) { diff --git a/src/core/PICA/renderer_opengl.cpp b/src/core/renderer_gl/renderer_gl.cpp similarity index 79% rename from src/core/PICA/renderer_opengl.cpp rename to src/core/renderer_gl/renderer_gl.cpp index 9f57a9f9..48c66ad4 100644 --- a/src/core/PICA/renderer_opengl.cpp +++ b/src/core/renderer_gl/renderer_gl.cpp @@ -1,7 +1,6 @@ +#include "renderer_gl/renderer_gl.hpp" #include "PICA/float_types.hpp" -#include "PICA/gpu.hpp" #include "PICA/regs.hpp" -#include "opengl.hpp" using namespace Floats; @@ -38,7 +37,7 @@ const char* fragmentShader = R"( float alpha = fragColour.a; switch (func) { - case 0: discard; break; // Never pass alpha test + case 0: discard; // Never pass alpha test case 1: break; // Always pass alpha test case 2: // Pass if equal if (alpha != reference) @@ -106,31 +105,19 @@ const char* displayFragmentShader = R"( } )"; -void GPU::initGraphicsContext() { - // Set up texture for top screen - fboTexture.create(400, 240, GL_RGBA8); - fboTexture.bind(); +void Renderer::reset() { + depthBufferCache.reset(); + colourBufferCache.reset(); - 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); + // Init the colour/depth buffer settings to some random defaults on reset + colourBufferLoc = 0; + colourBufferFormat = ColourBuffer::Formats::RGBA8; - fbo.createWithDrawTexture(fboTexture); - fbo.bind(OpenGL::DrawAndReadFramebuffer); - - GLuint rbo; - glGenRenderbuffers(1, &rbo); - glBindRenderbuffer(GL_RENDERBUFFER, rbo); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 400, 240); // use a single renderbuffer object for both a depth AND stencil buffer. - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo); // now actually attach it - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) - Helpers::panic("Incomplete framebuffer"); - //glBindRenderbuffer(GL_RENDERBUFFER, 0); - - OpenGL::setViewport(400, 240); - OpenGL::setClearColor(0.0, 0.0, 0.0, 1.0); - OpenGL::clearColor(); + depthBufferLoc = 0; + depthBufferFormat = DepthBuffer::Formats::Depth16; +} +void Renderer::initGraphicsContext() { OpenGL::Shader vert(vertexShader, OpenGL::Vertex); OpenGL::Shader frag(fragmentShader, OpenGL::Fragment); triangleProgram.create({ vert, frag }); @@ -157,19 +144,19 @@ void GPU::initGraphicsContext() { dummyVBO.create(); dummyVAO.create(); + reset(); } -void GPU::getGraphicsContext() { +void Renderer::getGraphicsContext() { 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) { +void Renderer::drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 count) { // Adjust alpha test if necessary const u32 alphaControl = regs[PICAInternalRegs::AlphaTestConfig]; if (alphaControl != oldAlphaControl) { @@ -177,6 +164,9 @@ void GPU::drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 count) glUniform1ui(alphaControlLoc, alphaControl); } + OpenGL::Framebuffer poop = getColourFBO(); + poop.bind(OpenGL::DrawAndReadFramebuffer); + const u32 depthControl = regs[PICAInternalRegs::DepthAndColorMask]; bool depthEnable = depthControl & 1; bool depthWriteEnable = (depthControl >> 12) & 1; @@ -191,8 +181,8 @@ void GPU::drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 count) f24 depthOffset = f24::fromRaw(regs[PICAInternalRegs::DepthOffset] & 0xffffff); printf("Depth enable: %d, func: %d, writeEnable: %d\n", depthEnable, depthFunc, depthWriteEnable); - if (depthScale.toFloat32() != -1.0 || depthOffset.toFloat32() != 0.0) - Helpers::panic("TODO: Implement depth scale/offset. Remove the depth *= -1.0 from vertex shader"); + //if (depthScale.toFloat32() != -1.0 || depthOffset.toFloat32() != 0.0) + // Helpers::panic("TODO: Implement depth scale/offset. Remove the depth *= -1.0 from vertex shader"); // TODO: Actually use this float viewportWidth = f24::fromRaw(regs[PICAInternalRegs::ViewportWidth] & 0xffffff).toFloat32() * 2.0; @@ -220,21 +210,24 @@ constexpr u32 topScreenBuffer = 0x1f000000; constexpr u32 bottomScreenBuffer = 0x1f05dc00; // Quick hack to display top screen for now -void GPU::display() { +void Renderer::display() { OpenGL::disableDepth(); OpenGL::disableScissor(); + OpenGL::bindScreenFramebuffer(); - fboTexture.bind(); + colourBufferCache[0].texture.bind(); + displayProgram.use(); dummyVAO.bind(); - OpenGL::setClearColor(0.0, 0.0, 0.0, 1.0); // Clear screen colour + OpenGL::setClearColor(0.0, 0.0, 1.0, 1.0); // Clear screen colour OpenGL::clearColor(); OpenGL::setViewport(0, 240, 400, 240); // Actually draw our 3DS screen OpenGL::draw(OpenGL::TriangleStrip, 4); } -void GPU::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) { +void Renderer::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) { + return; 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; @@ -253,4 +246,17 @@ void GPU::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) OpenGL::setClearColor(r, g, b, a); OpenGL::clearColor(); +} + +OpenGL::Framebuffer Renderer::getColourFBO() { + //We construct a colour buffer object and see if our cache has any matching colour buffers in it + // If not, we allocate a texture & FBO for our framebuffer and store it in the cache + ColourBuffer sampleBuffer(colourBufferLoc, colourBufferFormat, fbSize.x(), fbSize.y()); + auto buffer = colourBufferCache.find(sampleBuffer); + + if (buffer.has_value()) { + return buffer.value().get().fbo; + } else { + return colourBufferCache.add(sampleBuffer).fbo; + } } \ No newline at end of file diff --git a/src/core/services/ac.cpp b/src/core/services/ac.cpp new file mode 100644 index 00000000..cc1e3bd5 --- /dev/null +++ b/src/core/services/ac.cpp @@ -0,0 +1,30 @@ +#include "services/ac.hpp" + +namespace ACCommands { + enum : u32 { + SetClientVersion = 0x00400042 + }; +} + +namespace Result { + enum : u32 { + Success = 0, + }; +} + +void ACService::reset() {} + +void ACService::handleSyncRequest(u32 messagePointer) { + const u32 command = mem.read32(messagePointer); + switch (command) { + case ACCommands::SetClientVersion: setClientVersion(messagePointer); break; + default: Helpers::panic("AC service requested. Command: %08X\n", command); + } +} + +void ACService::setClientVersion(u32 messagePointer) { + u32 version = mem.read32(messagePointer + 4); + log("AC::SetClientVersion (version = %d)\n", version); + + mem.write32(messagePointer + 4, Result::Success); +} \ No newline at end of file diff --git a/src/core/services/am.cpp b/src/core/services/am.cpp new file mode 100644 index 00000000..5771f598 --- /dev/null +++ b/src/core/services/am.cpp @@ -0,0 +1,43 @@ +#include "services/am.hpp" + +namespace AMCommands { + enum : u32 { + ListTitleInfo = 0x10070102 + }; +} + +namespace Result { + enum : u32 { + Success = 0, + }; +} + +void AMService::reset() {} + +void AMService::handleSyncRequest(u32 messagePointer) { + const u32 command = mem.read32(messagePointer); + switch (command) { + case AMCommands::ListTitleInfo: listTitleInfo(messagePointer); break; + default: Helpers::panic("AM service requested. Command: %08X\n", command); + } +} + +void AMService::listTitleInfo(u32 messagePointer) { + log("AM::ListDLCOrLicenseTicketInfos\n"); // Yes this is the actual name + u32 ticketCount = mem.read32(messagePointer + 4); + u64 titleID = mem.read64(messagePointer + 8); + u32 pointer = mem.read32(messagePointer + 24); + + for (u32 i = 0; i < ticketCount; i++) { + mem.write64(pointer, titleID); // Title ID + mem.write64(pointer + 8, 0); // Ticket ID + mem.write16(pointer + 16, 0); // Version + mem.write16(pointer + 18, 0); // Padding + mem.write32(pointer + 20, 0); // Size + + pointer += 24; // = sizeof(TicketInfo) + } + + mem.write32(messagePointer + 4, Result::Success); + mem.write32(messagePointer + 8, ticketCount); +} \ No newline at end of file diff --git a/src/core/services/apt.cpp b/src/core/services/apt.cpp index 6cd3ce1e..24e41a17 100644 --- a/src/core/services/apt.cpp +++ b/src/core/services/apt.cpp @@ -6,12 +6,14 @@ namespace APTCommands { GetLockHandle = 0x00010040, Initialize = 0x00020080, Enable = 0x00030040, + InquireNotification = 0x000B0040, ReceiveParameter = 0x000D0080, ReplySleepQuery = 0x003E0080, NotifyToWait = 0x00430040, AppletUtility = 0x004B00C2, SetApplicationCpuTimeLimit = 0x004F0080, GetApplicationCpuTimeLimit = 0x00500040, + SetScreencapPostPermission = 0x00550040, CheckNew3DSApp = 0x01010000, CheckNew3DS = 0x01020000 }; @@ -49,12 +51,14 @@ void APTService::handleSyncRequest(u32 messagePointer) { case APTCommands::CheckNew3DSApp: checkNew3DSApp(messagePointer); break; case APTCommands::Enable: enable(messagePointer); break; case APTCommands::Initialize: initialize(messagePointer); break; + case APTCommands::InquireNotification: inquireNotification(messagePointer); break; case APTCommands::GetApplicationCpuTimeLimit: getApplicationCpuTimeLimit(messagePointer); break; case APTCommands::GetLockHandle: getLockHandle(messagePointer); break; case APTCommands::NotifyToWait: notifyToWait(messagePointer); break; case APTCommands::ReceiveParameter: receiveParameter(messagePointer); break; case APTCommands::ReplySleepQuery: replySleepQuery(messagePointer); break; case APTCommands::SetApplicationCpuTimeLimit: setApplicationCpuTimeLimit(messagePointer); break; + case APTCommands::SetScreencapPostPermission: setScreencapPostPermission(messagePointer); break; default: Helpers::panic("APT service requested. Command: %08X\n", command); } } @@ -96,7 +100,17 @@ void APTService::initialize(u32 messagePointer) { mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 8, 0x04000000); // Translation descriptor mem.write32(messagePointer + 12, notificationEvent.value()); // Notification Event Handle - mem.write32(messagePointer + 12, resumeEvent.value()); // Resume Event Handle + mem.write32(messagePointer + 16, resumeEvent.value()); // Resume Event Handle +} + +void APTService::inquireNotification(u32 messagePointer) { + log("APT::InquireNotification (STUBBED TO FAIL)\n"); + + // Thanks to our silly WaitSynchronization hacks, sometimes games will switch to the APT thread without actually getting a notif + // After REing the APT code, I figured that making InquireNotification fail is one way of making games not crash when this happens + // We should fix this in the future, when the sync object implementation is less hacky. + mem.write32(messagePointer + 4, Result::Failure); + mem.write32(messagePointer + 8, static_cast(NotificationType::None)); } void APTService::getLockHandle(u32 messagePointer) { @@ -159,4 +173,13 @@ void APTService::getApplicationCpuTimeLimit(u32 messagePointer) { log("APT::GetApplicationCpuTimeLimit\n"); mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 8, cpuTimeLimit); +} + +void APTService::setScreencapPostPermission(u32 messagePointer) { + u32 perm = mem.read32(messagePointer + 4); + log("APT::SetScreencapPostPermission (perm = %d)\n"); + + // Apparently only 1-3 are valid values, but I see 0 used in some games like Pokemon Rumble + mem.write32(messagePointer, Result::Success); + screencapPostPermission = perm; } \ No newline at end of file diff --git a/src/core/services/boss.cpp b/src/core/services/boss.cpp new file mode 100644 index 00000000..fb5053f7 --- /dev/null +++ b/src/core/services/boss.cpp @@ -0,0 +1,28 @@ +#include "services/boss.hpp" + +namespace BOSSCommands { + enum : u32 { + InitializeSession = 0x00010082 + }; +} + +namespace Result { + enum : u32 { + Success = 0, + }; +} + +void BOSSService::reset() {} + +void BOSSService::handleSyncRequest(u32 messagePointer) { + const u32 command = mem.read32(messagePointer); + switch (command) { + case BOSSCommands::InitializeSession: initializeSession(messagePointer); break; + default: Helpers::panic("BOSS service requested. Command: %08X\n", command); + } +} + +void BOSSService::initializeSession(u32 messagePointer) { + log("BOSS::InitializeSession (stubbed)\n"); + mem.write32(messagePointer + 4, Result::Success); +} \ No newline at end of file diff --git a/src/core/services/cfg.cpp b/src/core/services/cfg.cpp index 2fb21070..c5f68c15 100644 --- a/src/core/services/cfg.cpp +++ b/src/core/services/cfg.cpp @@ -37,7 +37,12 @@ void CFGService::getConfigInfoBlk2(u32 messagePointer) { mem.write8(output, static_cast(DSPService::SoundOutputMode::Stereo)); } else if (size == 1 && blockID == 0xA0002){ // System language mem.write8(output, static_cast(LanguageCodes::English)); - } else if (size == 0x20 && blockID == 0x50005) { + } else if (size == 4 && blockID == 0xB0000) { // Country info + mem.write8(output, 0); // Unknown + mem.write8(output + 1, 0); // Unknown + mem.write8(output + 2, 2); // Province (Temporarily stubbed to Washington DC like Citra) + mem.write8(output + 3, 49); // Country code (Temporarily stubbed to USA like Citra) + }else if (size == 0x20 && blockID == 0x50005) { printf("[Unimplemented] Read stereo display settings from NAND\n"); } else { Helpers::panic("Unhandled GetConfigInfoBlk2 configuration"); diff --git a/src/core/services/dsp.cpp b/src/core/services/dsp.cpp index cbcf687f..9e10fc17 100644 --- a/src/core/services/dsp.cpp +++ b/src/core/services/dsp.cpp @@ -7,6 +7,7 @@ namespace DSPCommands { WriteProcessPipe = 0x000D0082, ReadPipeIfPossible = 0x001000C0, LoadComponent = 0x001100C2, + FlushDataCache = 0x00130082, RegisterInterruptEvents = 0x00150082, GetSemaphoreHandle = 0x00160000, SetSemaphoreMask = 0x00170040, @@ -30,6 +31,7 @@ void DSPService::handleSyncRequest(u32 messagePointer) { const u32 command = mem.read32(messagePointer); switch (command) { case DSPCommands::ConvertProcessAddressFromDspDram: convertProcessAddressFromDspDram(messagePointer); break; + case DSPCommands::FlushDataCache: flushDataCache(messagePointer); break; case DSPCommands::GetHeadphoneStatus: getHeadphoneStatus(messagePointer); break; case DSPCommands::GetSemaphoreHandle: getSemaphoreHandle(messagePointer); break; case DSPCommands::LoadComponent: loadComponent(messagePointer); break; @@ -133,4 +135,13 @@ void DSPService::writeProcessPipe(u32 messagePointer) { log("DSP::writeProcessPipe (channel = %d, size = %X, buffer = %08X)\n", channel, size, buffer); mem.write32(messagePointer + 4, Result::Success); +} + +void DSPService::flushDataCache(u32 messagePointer) { + u32 address = mem.read32(messagePointer + 4); + u32 size = mem.read32(messagePointer + 8); + u32 process = mem.read32(messagePointer + 16); + + log("DSP::FlushDataCache (addr = %08X, size = %08X, process = %X)\n", address, size, process); + mem.write32(messagePointer + 4, Result::Success); } \ No newline at end of file diff --git a/src/core/services/frd.cpp b/src/core/services/frd.cpp new file mode 100644 index 00000000..cb93b06d --- /dev/null +++ b/src/core/services/frd.cpp @@ -0,0 +1,56 @@ +#include "services/frd.hpp" + +namespace FRDCommands { + enum : u32 { + SetClientSdkVersion = 0x00320042, + GetMyFriendKey = 0x00050000, + GetMyPresence = 0x00080000 + }; +} + +namespace Result { + enum : u32 { + Success = 0, + }; +} + +void FRDService::reset() {} + +void FRDService::handleSyncRequest(u32 messagePointer) { + const u32 command = mem.read32(messagePointer); + switch (command) { + case FRDCommands::GetMyFriendKey: getMyFriendKey(messagePointer); break; + case FRDCommands::GetMyPresence: getMyPresence(messagePointer); break; + case FRDCommands::SetClientSdkVersion: setClientSDKVersion(messagePointer); break; + default: Helpers::panic("FRD service requested. Command: %08X\n", command); + } +} + +void FRDService::getMyFriendKey(u32 messagePointer) { + log("FRD::GetMyFriendKey"); + + mem.write32(messagePointer + 4, Result::Success); + mem.write32(messagePointer + 8, 0); // Principal ID + mem.write32(messagePointer + 12, 0); // Padding (?) + mem.write32(messagePointer + 16, 0); // Local friend code + mem.write32(messagePointer + 20, 0); +} + +void FRDService::getMyPresence(u32 messagePointer) { + static constexpr u32 presenceSize = 0x12C; // A presence seems to be 12C bytes of data, not sure what it contains + log("FRD::GetMyPresence\n"); + u32 buffer = mem.read32(messagePointer + 0x104); // Buffer to write presence info to. + + for (u32 i = 0; i < presenceSize; i += 4) { // Clear presence info with 0s for now + mem.write32(buffer + i, 0); + } + + mem.write32(messagePointer + 4, Result::Success); +} + +void FRDService::setClientSDKVersion(u32 messagePointer) { + u32 version = mem.read32(messagePointer + 4); + log("FRD::SetClientSdkVersion (version = %d)\n", version); + + mem.write32(messagePointer + 4, Result::Success); +} \ No newline at end of file diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 87f42c81..361aa64b 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -8,6 +8,7 @@ namespace FSCommands { OpenFileDirectly = 0x08030204, OpenArchive = 0x080C00C2, CloseArchive = 0x080E0080, + IsSdmcDetected = 0x08170000, InitializeWithSdkVersion = 0x08610042, SetPriority = 0x08620040, GetPriority = 0x08630000 @@ -29,6 +30,7 @@ ArchiveBase* FSService::getArchiveFromID(u32 id) { switch (id) { case ArchiveID::SelfNCCH: return &selfNcch; case ArchiveID::SaveData: return &saveData; + case ArchiveID::SharedExtSaveData: return &sharedExtSaveData; case ArchiveID::SDMC: return &sdmc; default: Helpers::panic("Unknown archive. ID: %d\n", id); @@ -77,6 +79,7 @@ void FSService::handleSyncRequest(u32 messagePointer) { case FSCommands::GetPriority: getPriority(messagePointer); break; case FSCommands::Initialize: initialize(messagePointer); break; case FSCommands::InitializeWithSdkVersion: initializeWithSdkVersion(messagePointer); break; + case FSCommands::IsSdmcDetected: isSdmcDetected(messagePointer); break; case FSCommands::OpenArchive: openArchive(messagePointer); break; case FSCommands::OpenFile: openFile(messagePointer); break; case FSCommands::OpenFileDirectly: openFileDirectly(messagePointer); break; @@ -210,4 +213,10 @@ void FSService::setPriority(u32 messagePointer) { mem.write32(messagePointer + 4, Result::Success); priority = value; +} + +void FSService::isSdmcDetected(u32 messagePointer) { + log("FS::IsSdmcDetected\n"); + mem.write32(messagePointer + 4, Result::Success); + mem.write32(messagePointer + 8, 0); // Whether SD is detected. For now we emulate a 3DS without an SD. } \ No newline at end of file diff --git a/src/core/services/gsp_gpu.cpp b/src/core/services/gsp_gpu.cpp index 910f7aa9..684b3c4f 100644 --- a/src/core/services/gsp_gpu.cpp +++ b/src/core/services/gsp_gpu.cpp @@ -11,7 +11,8 @@ namespace ServiceCommands { FlushDataCache = 0x00080082, SetLCDForceBlack = 0x000B0040, TriggerCmdReqQueue = 0x000C0000, - SetInternalPriorities = 0x001E0080 + SetInternalPriorities = 0x001E0080, + StoreDataCache = 0x001F0082 }; } @@ -47,6 +48,7 @@ void GPUService::handleSyncRequest(u32 messagePointer) { case ServiceCommands::SetAxiConfigQoSMode: setAxiConfigQoSMode(messagePointer); break; case ServiceCommands::SetInternalPriorities: setInternalPriorities(messagePointer); break; case ServiceCommands::SetLCDForceBlack: setLCDForceBlack(messagePointer); break; + case ServiceCommands::StoreDataCache: storeDataCache(messagePointer); break; case ServiceCommands::TriggerCmdReqQueue: [[likely]] triggerCmdReqQueue(messagePointer); break; case ServiceCommands::WriteHwRegs: writeHwRegs(messagePointer); break; case ServiceCommands::WriteHwRegsWithMask: writeHwRegsWithMask(messagePointer); break; @@ -184,6 +186,15 @@ void GPUService::flushDataCache(u32 messagePointer) { mem.write32(messagePointer + 4, Result::Success); } +void GPUService::storeDataCache(u32 messagePointer) { + u32 address = mem.read32(messagePointer + 4); + u32 size = mem.read32(messagePointer + 8); + u32 processHandle = handle = mem.read32(messagePointer + 16); + log("GSP::GPU::StoreDataCache(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); log("GSP::GPU::SetLCDForceBlank(flag = %d)\n", flag); diff --git a/src/core/services/hid.cpp b/src/core/services/hid.cpp index 740b5650..27661954 100644 --- a/src/core/services/hid.cpp +++ b/src/core/services/hid.cpp @@ -1,4 +1,5 @@ #include "services/hid.hpp" +#include namespace HIDCommands { enum : u32 { @@ -49,19 +50,25 @@ void HIDService::enableGyroscopeLow(u32 messagePointer) { void HIDService::getGyroscopeLowCalibrateParam(u32 messagePointer) { log("HID::GetGyroscopeLowCalibrateParam\n"); + constexpr s16 unit = 6700; // Approximately from Citra which took it from hardware mem.write32(messagePointer + 4, Result::Success); - // Fill calibration data with 0s since we don't have gyro - for (int i = 0; i < 5; i++) { - mem.write32(messagePointer + 8 + i * 4, 0); + // Fill calibration data (for x/y/z depending on i) + for (int i = 0; i < 3; i++) { + const u32 pointer = messagePointer + 8 + i * 3 * sizeof(u16); // Pointer to write the calibration info for the current coordinate + + mem.write16(pointer, 0); // Zero point + mem.write16(pointer + 1 * sizeof(u16), unit); // Positive unit point + mem.write16(pointer + 2 * sizeof(u16), -unit); // Negative unit point } } void HIDService::getGyroscopeCoefficient(u32 messagePointer) { log("HID::GetGyroscopeLowRawToDpsCoefficient\n"); + constexpr float gyroscopeCoeff = 14.375f; // Same as retail 3DS mem.write32(messagePointer + 4, Result::Success); - // This should write a coeff value here but we don't have gyro so + mem.write32(messagePointer + 8, std::bit_cast(gyroscopeCoeff)); } void HIDService::getIPCHandles(u32 messagePointer) { diff --git a/src/core/services/nim.cpp b/src/core/services/nim.cpp new file mode 100644 index 00000000..a3c48fb4 --- /dev/null +++ b/src/core/services/nim.cpp @@ -0,0 +1,28 @@ +#include "services/nim.hpp" + +namespace NIMCommands { + enum : u32 { + Initialize = 0x00210000 + }; +} + +namespace Result { + enum : u32 { + Success = 0, + }; +} + +void NIMService::reset() {} + +void NIMService::handleSyncRequest(u32 messagePointer) { + const u32 command = mem.read32(messagePointer); + switch (command) { + case NIMCommands::Initialize: initialize(messagePointer); break; + default: Helpers::panic("NIM service requested. Command: %08X\n", command); + } +} + +void NIMService::initialize(u32 messagePointer) { + log("NIM::Initialize\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 d27e6ce1..c960f461 100644 --- a/src/core/services/service_manager.cpp +++ b/src/core/services/service_manager.cpp @@ -2,21 +2,28 @@ #include "kernel.hpp" ServiceManager::ServiceManager(std::array& regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel) - : regs(regs), mem(mem), kernel(kernel), apt(mem, kernel), cecd(mem), cfg(mem), dsp(mem), hid(mem), - fs(mem, kernel), gsp_gpu(mem, gpu, currentPID), gsp_lcd(mem), mic(mem), ndm(mem), ptm(mem) {} + : regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), apt(mem, kernel), cecd(mem), cfg(mem), + dsp(mem), hid(mem), frd(mem), fs(mem, kernel), gsp_gpu(mem, gpu, currentPID), gsp_lcd(mem), mic(mem), + nim(mem), ndm(mem), ptm(mem) {} static constexpr int MAX_NOTIFICATION_COUNT = 16; +// Reset every single service void ServiceManager::reset() { + ac.reset(); + am.reset(); apt.reset(); + boss.reset(); cecd.reset(); cfg.reset(); dsp.reset(); hid.reset(); + frd.reset(); fs.reset(); gsp_gpu.reset(); gsp_lcd.reset(); mic.reset(); + nim.reset(); ndm.reset(); ptm.reset(); @@ -60,6 +67,7 @@ void ServiceManager::handleSyncRequest(u32 messagePointer) { case Commands::ReceiveNotification: receiveNotification(messagePointer); break; case Commands::RegisterClient: registerClient(messagePointer); break; case Commands::GetServiceHandle: getServiceHandle(messagePointer); break; + case Commands::Subscribe: subscribe(messagePointer); break; default: Helpers::panic("Unknown \"srv:\" command: %08X", header); } } @@ -79,12 +87,18 @@ void ServiceManager::getServiceHandle(u32 messagePointer) { std::string service = mem.readString(messagePointer + 4, 8); log("srv::getServiceHandle (Service: %s, nameLength: %d, flags: %d)\n", service.c_str(), nameLength, flags); - if (service == "APT:S") { // TODO: APT:A, APT:S and APT:U are slightly different + if (service == "ac:u") { + handle = KernelHandles::AC; + } else if (service == "am:app") { + handle = KernelHandles::AM; + } else if (service == "APT:S") { // TODO: APT:A, APT:S and APT:U are slightly different handle = KernelHandles::APT; } else if (service == "APT:A") { handle = KernelHandles::APT; } else if (service == "APT:U") { handle = KernelHandles::APT; + } else if (service == "boss:U") { + handle = KernelHandles::BOSS; } else if (service == "cecd:u") { handle = KernelHandles::CECD; } else if (service == "cfg:u") { @@ -93,7 +107,9 @@ void ServiceManager::getServiceHandle(u32 messagePointer) { handle = KernelHandles::DSP; } else if (service == "hid:USER") { handle = KernelHandles::HID; - } else if (service == "fs:USER") { + } else if (service == "frd:u") { + handle = KernelHandles::FRD; + } else if (service == "fs:USER") { handle = KernelHandles::FS; } else if (service == "gsp::Gpu") { handle = KernelHandles::GPU; @@ -103,6 +119,8 @@ void ServiceManager::getServiceHandle(u32 messagePointer) { handle = KernelHandles::MIC; } else if (service == "ndm:u") { handle = KernelHandles::NDM; + } else if (service == "nim:aoc") { + handle = KernelHandles::NIM; } else if (service == "ptm:u") { handle = KernelHandles::PTM; } else { @@ -134,17 +152,29 @@ void ServiceManager::receiveNotification(u32 messagePointer) { mem.write32(messagePointer + 8, 0); // Notification ID } +void ServiceManager::subscribe(u32 messagePointer) { + u32 id = mem.read32(messagePointer + 4); + log("srv::Subscribe (id = %d) (stubbed)\n", id); + + mem.write32(messagePointer + 4, Result::Success); +} + void ServiceManager::sendCommandToService(u32 messagePointer, Handle handle) { switch (handle) { + case KernelHandles::AC: ac.handleSyncRequest(messagePointer); break; + case KernelHandles::AM: am.handleSyncRequest(messagePointer); break; case KernelHandles::APT: apt.handleSyncRequest(messagePointer); break; + case KernelHandles::BOSS: boss.handleSyncRequest(messagePointer); break; case KernelHandles::CECD: cecd.handleSyncRequest(messagePointer); break; case KernelHandles::CFG: cfg.handleSyncRequest(messagePointer); break; case KernelHandles::DSP: dsp.handleSyncRequest(messagePointer); break; case KernelHandles::HID: hid.handleSyncRequest(messagePointer); break; + case KernelHandles::FRD: frd.handleSyncRequest(messagePointer); break; case KernelHandles::FS: fs.handleSyncRequest(messagePointer); break; case KernelHandles::GPU: [[likely]] gsp_gpu.handleSyncRequest(messagePointer); break; case KernelHandles::LCD: gsp_lcd.handleSyncRequest(messagePointer); break; case KernelHandles::MIC: mic.handleSyncRequest(messagePointer); break; + case KernelHandles::NIM: nim.handleSyncRequest(messagePointer); break; case KernelHandles::NDM: ndm.handleSyncRequest(messagePointer); break; case KernelHandles::PTM: ptm.handleSyncRequest(messagePointer); break; default: Helpers::panic("Sent IPC message to unknown service %08X\n Command: %08X", handle, mem.read32(messagePointer));