diff --git a/CMakeLists.txt b/CMakeLists.txt index 0414a944b..02617f157 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -338,7 +338,7 @@ set(LOADER_SOURCE_FILES src/core/loader/elf.cpp src/core/loader/ncsd.cpp src/cor set(FS_SOURCE_FILES src/core/fs/archive_self_ncch.cpp src/core/fs/archive_save_data.cpp src/core/fs/archive_sdmc.cpp src/core/fs/archive_ext_save_data.cpp src/core/fs/archive_ncch.cpp src/core/fs/romfs.cpp src/core/fs/ivfc.cpp src/core/fs/archive_user_save_data.cpp src/core/fs/archive_system_save_data.cpp - src/core/fs/archive_twl_photo.cpp src/core/fs/archive_twl_sound.cpp + src/core/fs/archive_twl_photo.cpp src/core/fs/archive_twl_sound.cpp src/core/fs/archive_card_spi.cpp ) set(APPLET_SOURCE_FILES src/core/applets/applet.cpp src/core/applets/mii_selector.cpp src/core/applets/software_keyboard.cpp src/core/applets/applet_manager.cpp @@ -389,7 +389,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp include/align.hpp include/audio/aac_decoder.hpp include/PICA/pica_simd.hpp include/services/fonts.hpp include/audio/audio_interpolation.hpp include/audio/hle_mixer.hpp include/audio/dsp_simd.hpp include/services/dsp_firmware_db.hpp include/frontend_settings.hpp include/fs/archive_twl_photo.hpp - include/fs/archive_twl_sound.hpp + include/fs/archive_twl_sound.hpp include/fs/archive_card_spi.hpp ) cmrc_add_resource_library( diff --git a/include/fs/archive_base.hpp b/include/fs/archive_base.hpp index 4c42ca021..d899d1a6e 100644 --- a/include/fs/archive_base.hpp +++ b/include/fs/archive_base.hpp @@ -35,6 +35,7 @@ namespace ArchiveID { SDMC = 9, SDMCWriteOnly = 0xA, + CardSPI = 0x12345679, SavedataAndNcch = 0x2345678A, // 3DBrew: This is the same as the regular SaveData archive, except with this the savedata ID and mediatype is loaded from the input archive // lowpath. diff --git a/include/fs/archive_card_spi.hpp b/include/fs/archive_card_spi.hpp new file mode 100644 index 000000000..fefa9933f --- /dev/null +++ b/include/fs/archive_card_spi.hpp @@ -0,0 +1,30 @@ +#pragma once +#include "archive_base.hpp" +#include "result/result.hpp" + +using Result::HorizonResult; + +class CardSPIArchive : public ArchiveBase { + public: + CardSPIArchive(Memory& mem) : ArchiveBase(mem) {} + std::string name() override { return "Card SPI"; } + + u64 getFreeBytes() override { + Helpers::warn("Unimplemented GetFreeBytes for Card SPI archive"); + return 0_MB; + } + + HorizonResult createDirectory(const FSPath& path) override; + HorizonResult createFile(const FSPath& path, u64 size) override; + HorizonResult deleteFile(const FSPath& path) override; + + Rust::Result openArchive(const FSPath& path) override; + Rust::Result openDirectory(const FSPath& path) override; + + FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override; + + std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override { + Helpers::panic("Unimplemented ReadFile for Card SPI archive"); + return {}; + }; +}; \ No newline at end of file diff --git a/include/services/dsp.hpp b/include/services/dsp.hpp index 7eb9b28b0..3013c94d4 100644 --- a/include/services/dsp.hpp +++ b/include/services/dsp.hpp @@ -44,9 +44,12 @@ class DSPService { size_t totalEventCount; std::vector loadedComponent; + bool headphonesInserted = true; + // Service functions void convertProcessAddressFromDspDram(u32 messagePointer); // Nice function name void flushDataCache(u32 messagePointer); + void forceHeadphoneOut(u32 messagePointer); void getHeadphoneStatus(u32 messagePointer); void getSemaphoreEventHandle(u32 messagePointer); void invalidateDCache(u32 messagePointer); diff --git a/include/services/fs.hpp b/include/services/fs.hpp index dd115cc41..82f07077a 100644 --- a/include/services/fs.hpp +++ b/include/services/fs.hpp @@ -1,5 +1,6 @@ #pragma once #include "config.hpp" +#include "fs/archive_card_spi.hpp" #include "fs/archive_ext_save_data.hpp" #include "fs/archive_ncch.hpp" #include "fs/archive_save_data.hpp" @@ -43,6 +44,7 @@ class FSService { TWLPhotoArchive twlPhoto; TWLSoundArchive twlSound; + CardSPIArchive cardSpi; ArchiveBase* getArchiveFromID(u32 id, const FSPath& archivePath); Rust::Result openArchiveHandle(u32 archiveID, const FSPath& path); @@ -92,7 +94,8 @@ class FSService { FSService(Memory& mem, Kernel& kernel, const EmulatorConfig& config) : mem(mem), saveData(mem), sharedExtSaveData_nand(mem, "../SharedFiles/NAND", true), extSaveData_sdmc(mem, "SDMC"), sdmc(mem), sdmcWriteOnly(mem, true), selfNcch(mem), ncch(mem), userSaveData1(mem, ArchiveID::UserSaveData1), - userSaveData2(mem, ArchiveID::UserSaveData2), systemSaveData(mem), twlPhoto(mem), twlSound(mem), kernel(kernel), config(config) {} + userSaveData2(mem, ArchiveID::UserSaveData2), systemSaveData(mem), twlPhoto(mem), twlSound(mem), cardSpi(mem), kernel(kernel), + config(config) {} void reset(); void handleSyncRequest(u32 messagePointer); diff --git a/include/services/gsp_gpu.hpp b/include/services/gsp_gpu.hpp index d72446097..8294565b1 100644 --- a/include/services/gsp_gpu.hpp +++ b/include/services/gsp_gpu.hpp @@ -1,6 +1,7 @@ #pragma once #include #include + #include "PICA/gpu.hpp" #include "helpers.hpp" #include "kernel_types.hpp" @@ -9,12 +10,12 @@ #include "result/result.hpp" enum class GPUInterrupt : u8 { - PSC0 = 0, // Memory fill completed - PSC1 = 1, // ? - VBlank0 = 2, // ? - VBlank1 = 3, // ? - PPF = 4, // Display transfer finished - P3D = 5, // Command list processing finished + PSC0 = 0, // Memory fill completed + PSC1 = 1, // ? + VBlank0 = 2, // ? + VBlank1 = 3, // ? + PPF = 4, // Display transfer finished + P3D = 5, // Command list processing finished DMA = 6 }; @@ -28,8 +29,8 @@ class GPUService { Memory& mem; GPU& gpu; Kernel& kernel; - u32& currentPID; // Process ID of the current process - u8* sharedMem; // Pointer to GSP shared memory + u32& currentPID; // Process ID of the current process + u8* sharedMem; // Pointer to GSP shared memory // At any point in time only 1 process has privileges to use rendering functions // This is the PID of that process @@ -64,8 +65,8 @@ class GPUService { // Used for saving and restoring GPU state via ImportDisplayCaptureInfo struct CaptureInfo { - u32 leftFramebuffer; // Left framebuffer VA - u32 rightFramebuffer; // Right framebuffer VA (Top screen only) + u32 leftFramebuffer; // Left framebuffer VA + u32 rightFramebuffer; // Right framebuffer VA (Top screen only) u32 format; u32 stride; }; @@ -74,6 +75,7 @@ class GPUService { // Service commands void acquireRight(u32 messagePointer); void flushDataCache(u32 messagePointer); + void invalidateDataCache(u32 messagePointer); void importDisplayCaptureInfo(u32 messagePointer); void readHwRegs(u32 messagePointer); void registerInterruptRelayQueue(u32 messagePointer); @@ -108,15 +110,14 @@ class GPUService { FramebufferUpdate* getTopFramebufferInfo() { return getFramebufferInfo(0); } FramebufferUpdate* getBottomFramebufferInfo() { return getFramebufferInfo(1); } -public: - GPUService(Memory& mem, GPU& gpu, Kernel& kernel, u32& currentPID) : mem(mem), gpu(gpu), - kernel(kernel), currentPID(currentPID) {} + public: + GPUService(Memory& mem, GPU& gpu, Kernel& kernel, u32& currentPID) : mem(mem), gpu(gpu), kernel(kernel), currentPID(currentPID) {} void reset(); void handleSyncRequest(u32 messagePointer); void requestInterrupt(GPUInterrupt type); void setSharedMem(u8* ptr) { sharedMem = ptr; - if (ptr != nullptr) { // Zero-fill shared memory in case the process tries to read stale service data or vice versa + if (ptr != nullptr) { // Zero-fill shared memory in case the process tries to read stale service data or vice versa std::memset(ptr, 0, 0x1000); } } diff --git a/include/services/mcu/mcu_hwc.hpp b/include/services/mcu/mcu_hwc.hpp index 4c6a88305..c2fada28e 100644 --- a/include/services/mcu/mcu_hwc.hpp +++ b/include/services/mcu/mcu_hwc.hpp @@ -17,6 +17,7 @@ namespace MCU { // Service commands void getBatteryLevel(u32 messagePointer); + void setInfoLEDPattern(u32 messagePointer); public: HWCService(Memory& mem, const EmulatorConfig& config) : mem(mem), config(config) {} diff --git a/src/core/fs/archive_card_spi.cpp b/src/core/fs/archive_card_spi.cpp new file mode 100644 index 000000000..db1b5fded --- /dev/null +++ b/src/core/fs/archive_card_spi.cpp @@ -0,0 +1,40 @@ +#include +#include + +#include "fs/archive_card_spi.hpp" + +namespace fs = std::filesystem; + +HorizonResult CardSPIArchive::createFile(const FSPath& path, u64 size) { + Helpers::panic("[Card SPI] CreateFile not yet supported"); + return Result::Success; +} + +HorizonResult CardSPIArchive::deleteFile(const FSPath& path) { + Helpers::panic("[Card SPI] Unimplemented DeleteFile"); + return Result::Success; +} + +HorizonResult CardSPIArchive::createDirectory(const FSPath& path) { + Helpers::panic("[Card SPI] CreateDirectory not yet supported"); + return Result::Success; +} + +FileDescriptor CardSPIArchive::openFile(const FSPath& path, const FilePerms& perms) { + Helpers::panic("[Card SPI] OpenFile not yet supported"); + return FileError; +} + +Rust::Result CardSPIArchive::openArchive(const FSPath& path) { + if (path.type != PathType::Empty) { + Helpers::panic("Unimplemented path type for CardSPIArchive::OpenArchive"); + } + + Helpers::warn("Unimplemented: Card SPI archive"); + return Err(Result::FailurePlaceholder); +} + +Rust::Result CardSPIArchive::openDirectory(const FSPath& path) { + Helpers::panic("[Card SPI] OpenDirectory not yet supported"); + return Err(Result::FailurePlaceholder); +} diff --git a/src/core/services/dsp.cpp b/src/core/services/dsp.cpp index 5d7956b51..93de78da1 100644 --- a/src/core/services/dsp.cpp +++ b/src/core/services/dsp.cpp @@ -28,7 +28,8 @@ namespace DSPCommands { RegisterInterruptEvents = 0x00150082, GetSemaphoreEventHandle = 0x00160000, SetSemaphoreMask = 0x00170040, - GetHeadphoneStatus = 0x001F0000 + GetHeadphoneStatus = 0x001F0000, + ForceHeadphoneOut = 0x00200040, }; } @@ -42,6 +43,7 @@ namespace Result { void DSPService::reset() { totalEventCount = 0; semaphoreMask = 0; + headphonesInserted = true; semaphoreEvent = std::nullopt; interrupt0 = std::nullopt; @@ -60,6 +62,7 @@ void DSPService::handleSyncRequest(u32 messagePointer) { case DSPCommands::ConvertProcessAddressFromDspDram: convertProcessAddressFromDspDram(messagePointer); break; case DSPCommands::FlushDataCache: flushDataCache(messagePointer); break; case DSPCommands::InvalidateDataCache: invalidateDCache(messagePointer); break; + case DSPCommands::ForceHeadphoneOut: forceHeadphoneOut(messagePointer); break; case DSPCommands::GetHeadphoneStatus: getHeadphoneStatus(messagePointer); break; case DSPCommands::GetSemaphoreEventHandle: getSemaphoreEventHandle(messagePointer); break; case DSPCommands::LoadComponent: loadComponent(messagePointer); break; @@ -210,7 +213,8 @@ void DSPService::getHeadphoneStatus(u32 messagePointer) { mem.write32(messagePointer, IPC::responseHeader(0x1F, 2, 0)); mem.write32(messagePointer + 4, Result::Success); - mem.write32(messagePointer + 8, Result::HeadphonesInserted); // This should be toggleable for shits and giggles + // This should be toggleable for shits and giggles + mem.write32(messagePointer + 8, headphonesInserted ? Result::HeadphonesInserted : Result::HeadphonesNotInserted); } void DSPService::getSemaphoreEventHandle(u32 messagePointer) { @@ -278,6 +282,14 @@ void DSPService::invalidateDCache(u32 messagePointer) { mem.write32(messagePointer + 4, Result::Success); } +void DSPService::forceHeadphoneOut(u32 messagePointer) { + headphonesInserted = mem.read8(messagePointer + 4) != 0; + + log("DSP::ForceHeadphoneOut\n"); + mem.write32(messagePointer, IPC::responseHeader(0x20, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); +} + DSPService::ComponentDumpResult DSPService::dumpComponent(const std::filesystem::path& path) { if (loadedComponent.empty()) { return ComponentDumpResult::NotLoaded; diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index c6c84ae5f..54a4241dd 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -102,6 +102,7 @@ ArchiveBase* FSService::getArchiveFromID(u32 id, const FSPath& archivePath) { case ArchiveID::TwlPhoto: return &twlPhoto; case ArchiveID::TwlSound: return &twlSound; + case ArchiveID::CardSPI: return &cardSpi; default: Helpers::panic("Unknown archive. ID: %d\n", id); diff --git a/src/core/services/gsp_gpu.cpp b/src/core/services/gsp_gpu.cpp index 8cf77a7e5..96364e736 100644 --- a/src/core/services/gsp_gpu.cpp +++ b/src/core/services/gsp_gpu.cpp @@ -14,6 +14,7 @@ namespace ServiceCommands { WriteHwRegsWithMask = 0x00020084, SetBufferSwap = 0x00050200, FlushDataCache = 0x00080082, + InvalidateDataCache = 0x00090082, SetLCDForceBlack = 0x000B0040, TriggerCmdReqQueue = 0x000C0000, ReleaseRight = 0x00170000, @@ -21,7 +22,7 @@ namespace ServiceCommands { SaveVramSysArea = 0x00190000, RestoreVramSysArea = 0x001A0000, SetInternalPriorities = 0x001E0080, - StoreDataCache = 0x001F0082 + StoreDataCache = 0x001F0082, }; } @@ -63,6 +64,7 @@ void GPUService::handleSyncRequest(u32 messagePointer) { case ServiceCommands::ReadHwRegs: readHwRegs(messagePointer); break; case ServiceCommands::WriteHwRegs: writeHwRegs(messagePointer); break; case ServiceCommands::WriteHwRegsWithMask: writeHwRegsWithMask(messagePointer); break; + case ServiceCommands::InvalidateDataCache: invalidateDataCache(messagePointer); break; default: Helpers::panic("GPU service requested. Command: %08X\n", command); } } @@ -278,6 +280,16 @@ void GPUService::flushDataCache(u32 messagePointer) { mem.write32(messagePointer + 4, Result::Success); } +void GPUService::invalidateDataCache(u32 messagePointer) { + u32 address = mem.read32(messagePointer + 4); + u32 size = mem.read32(messagePointer + 8); + u32 processHandle = handle = mem.read32(messagePointer + 16); + log("GSP::GPU::InvalidateDataCache(address = %08X, size = %X, process = %X)\n", address, size, processHandle); + + mem.write32(messagePointer, IPC::responseHeader(0x9, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); +} + void GPUService::storeDataCache(u32 messagePointer) { u32 address = mem.read32(messagePointer + 4); u32 size = mem.read32(messagePointer + 8); diff --git a/src/core/services/mcu/mcu_hwc.cpp b/src/core/services/mcu/mcu_hwc.cpp index 2873adf5c..0e4e6ed35 100644 --- a/src/core/services/mcu/mcu_hwc.cpp +++ b/src/core/services/mcu/mcu_hwc.cpp @@ -1,10 +1,12 @@ +#include "services/mcu/mcu_hwc.hpp" + #include "ipc.hpp" #include "result/result.hpp" -#include "services/mcu/mcu_hwc.hpp" namespace MCU::HWCCommands { enum : u32 { GetBatteryLevel = 0x00050000, + SetInfoLedPattern = 0x000A0640, }; } @@ -14,6 +16,7 @@ void MCU::HWCService::handleSyncRequest(u32 messagePointer) { const u32 command = mem.read32(messagePointer); switch (command) { case HWCCommands::GetBatteryLevel: getBatteryLevel(messagePointer); break; + case HWCCommands::SetInfoLedPattern: setInfoLEDPattern(messagePointer); break; default: Helpers::panic("MCU::HWC service requested. Command: %08X\n", command); } } @@ -24,4 +27,12 @@ void MCU::HWCService::getBatteryLevel(u32 messagePointer) { mem.write32(messagePointer, IPC::responseHeader(0x5, 2, 0)); mem.write32(messagePointer + 4, Result::Success); mem.write8(messagePointer + 8, config.batteryPercentage); +} + +void MCU::HWCService::setInfoLEDPattern(u32 messagePointer) { + log("MCU::HWC::SetInfoLedPattern\n"); + + // 25 parameters to make some notification LEDs blink... + mem.write32(messagePointer, IPC::responseHeader(0xA, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); } \ No newline at end of file