diff --git a/.gitignore b/.gitignore index cdac1e25..53137e46 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,7 @@ vgcore.* *.dump *.data disasm.txt -*log.txt +*log*.txt .vs/ CMakeSettings.json out/ diff --git a/README.md b/README.md index da13e2b3..c4dd368d 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ Your GPU needs to support Vulkan 1.1+, because the RDP is implemented via [ParaL This list will probably grow with time! ## Special thanks: - +- [ares](https://github.com/ares-emulator/ares) for being the cleanest and most accurate Nintendo 64 emulator out there. It served as a reference time and time again. Especially regarding FPU accuracy. - [Dillonb](https://github.com/Dillonb) and [KieronJ](https://github.com/KieronJ) for bearing with me and my recurring brainfarts, and for the support :heart: - [WhoBrokeTheBuild](https://github.com/WhoBrokeTheBuild) for the shader that allows letterboxing :rocket: - [Kelpsy](https://github.com/kelpsyberry), [fleroviux](https://github.com/fleroviux), [Kim-Dewelski](https://github.com/Kim-Dewelski), [Peach](https://github.com/wheremyfoodat/), diff --git a/external/nativefiledialog-extended b/external/nativefiledialog-extended deleted file mode 160000 index 17b6e8ce..00000000 --- a/external/nativefiledialog-extended +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 17b6e8ce219c0677f94b63636abb9296b28841ca diff --git a/external/parallel-rdp/ParallelRDPWrapper.cpp b/external/parallel-rdp/ParallelRDPWrapper.cpp index c0f94bf6..cb4e52eb 100644 --- a/external/parallel-rdp/ParallelRDPWrapper.cpp +++ b/external/parallel-rdp/ParallelRDPWrapper.cpp @@ -20,21 +20,21 @@ void ParallelRDP::SetFramerateUnlocked(bool unlocked) { Program* fullscreen_quad_program; -void ParallelRDP::LoadWSIPlatform(Vulkan::InstanceFactory* instanceFactory, std::unique_ptr&& wsi_platform, std::unique_ptr&& newWindowInfo) { - wsi = std::make_unique(); +void ParallelRDP::LoadWSIPlatform(const std::shared_ptr& instanceFactory, const std::shared_ptr& wsi_platform, const std::shared_ptr& newWindowInfo) { + wsi = std::make_shared(); wsi->set_backbuffer_srgb(false); wsi->set_platform(wsi_platform.get()); wsi->set_present_mode(PresentMode::SyncToVBlank); Context::SystemHandles handles; - if (!wsi->init_simple(instanceFactory, 1, handles)) { + if (!wsi->init_simple(instanceFactory.get(), 1, handles)) { Util::panic("Failed to initialize WSI!"); } - windowInfo = std::move(newWindowInfo); + windowInfo = newWindowInfo; } -void ParallelRDP::Init(Vulkan::InstanceFactory* factory, std::unique_ptr&& wsiPlatform, std::unique_ptr&& newWindowInfo, const u8* rdram) { - LoadWSIPlatform(factory, std::move(wsiPlatform), std::move(newWindowInfo)); +void ParallelRDP::Init(const std::shared_ptr& factory, const std::shared_ptr& wsiPlatform, const std::shared_ptr& newWindowInfo, const u8* rdram) { + LoadWSIPlatform(factory, wsiPlatform, newWindowInfo); ResourceLayout vertLayout; ResourceLayout fragLayout; @@ -63,8 +63,7 @@ void ParallelRDP::Init(Vulkan::InstanceFactory* factory, std::unique_ptr(rdram); uintptr_t offset = 0; - if (wsi->get_device().get_device_features().supports_external_memory_host) - { + if (wsi->get_device().get_device_features().supports_external_memory_host) { size_t align = wsi->get_device().get_device_features().host_memory_properties.minImportedHostPointerAlignment; offset = aligned_rdram & (align - 1); aligned_rdram -= offset; @@ -72,8 +71,8 @@ void ParallelRDP::Init(Vulkan::InstanceFactory* factory, std::unique_ptr(wsi->get_device(), reinterpret_cast(aligned_rdram), - offset, 8 * 1024 * 1024, 4 * 1024 * 1024, flags); + command_processor = std::make_shared(wsi->get_device(), reinterpret_cast(aligned_rdram), + offset, 8 * 1024 * 1024, 4 * 1024 * 1024, flags); if (!command_processor->device_is_supported()) { Util::panic("This device probably does not support 8/16-bit storage. Make sure you're using up-to-date drivers!"); @@ -101,11 +100,11 @@ void ParallelRDP::DrawFullscreenTexturedQuad(Util::IntrusivePtr image, Ut float height = (wsi->get_platform().get_surface_height() / windowSize.y) * zoom; float uniform_data[] = { - // Size - width, height, - // Offset - (1.0f - width) * 0.5f, - (1.0f - height) * 0.5f}; + // Size + width, height, + // Offset + (1.0f - width) * 0.5f, + (1.0f - height) * 0.5f}; cmd->push_constants(uniform_data, 0, sizeof(uniform_data)); @@ -122,7 +121,7 @@ void ParallelRDP::UpdateScreen(Util::IntrusivePtr image) { if (!image) { auto info = Vulkan::ImageCreateInfo::immutable_2d_image(800, 600, VK_FORMAT_R8G8B8A8_UNORM); info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | - VK_IMAGE_USAGE_TRANSFER_DST_BIT; + VK_IMAGE_USAGE_TRANSFER_DST_BIT; info.misc = IMAGE_MISC_MUTABLE_SRGB_BIT; info.initial_layout = VK_IMAGE_LAYOUT_UNDEFINED; image = wsi->get_device().create_image(info); @@ -179,8 +178,7 @@ void ParallelRDP::UpdateScreen(n64::VI& vi, bool noGame) { Util::IntrusivePtr image = command_processor->scanout(opts); UpdateScreen(image); command_processor->begin_frame_context(); - } - else { + } else { UpdateScreen(static_cast>(nullptr)); command_processor->begin_frame_context(); } diff --git a/external/parallel-rdp/ParallelRDPWrapper.hpp b/external/parallel-rdp/ParallelRDPWrapper.hpp index 04fd1924..b8a5b696 100644 --- a/external/parallel-rdp/ParallelRDPWrapper.hpp +++ b/external/parallel-rdp/ParallelRDPWrapper.hpp @@ -15,7 +15,7 @@ public: virtual ~WindowInfo() = default; }; - void Init(Vulkan::InstanceFactory*, std::unique_ptr&&, std::unique_ptr&&, const u8*); + void Init(const std::shared_ptr&, const std::shared_ptr&, const std::shared_ptr&, const u8*); ParallelRDP() = default; void UpdateScreen(n64::VI&, bool = false); @@ -24,11 +24,11 @@ public: bool IsFramerateUnlocked(); void SetFramerateUnlocked(bool); private: - void LoadWSIPlatform(Vulkan::InstanceFactory*, std::unique_ptr&&, std::unique_ptr&&); + void LoadWSIPlatform(const std::shared_ptr&, const std::shared_ptr&, const std::shared_ptr&); void DrawFullscreenTexturedQuad(Util::IntrusivePtr, Util::IntrusivePtr); void UpdateScreen(Util::IntrusivePtr); - std::unique_ptr wsi; - std::unique_ptr command_processor; - std::unique_ptr windowInfo; + std::shared_ptr wsi; + std::shared_ptr command_processor; + std::shared_ptr windowInfo; }; diff --git a/external/parallel-rdp/parallel-rdp-standalone b/external/parallel-rdp/parallel-rdp-standalone index 1504f300..896f8ca2 160000 --- a/external/parallel-rdp/parallel-rdp-standalone +++ b/external/parallel-rdp/parallel-rdp-standalone @@ -1 +1 @@ -Subproject commit 1504f3007e91258d4cd25869eceefc47f4be521f +Subproject commit 896f8ca226bf83eea2194056b08fa661c47933dd diff --git a/src/backend/Core.cpp b/src/backend/Core.cpp index 0f62bf39..be7018a5 100644 --- a/src/backend/Core.cpp +++ b/src/backend/Core.cpp @@ -12,7 +12,7 @@ void Core::Stop() { cpu->Reset(); } -bool Core::LoadTAS(const fs::path &path) { +bool Core::LoadTAS(const fs::path &path) const { return cpu->GetMem().mmio.si.pif.movie.Load(path); } @@ -31,6 +31,9 @@ void Core::LoadROM(const std::string& rom_) { cpu->GetMem().LoadROM(isArchive, rom); GameDB::match(cpu->GetMem()); + if(cpu->GetMem().rom.gameNameDB.empty()) { + cpu->GetMem().rom.gameNameDB = fs::path(rom).stem(); + } cpu->GetMem().mmio.vi.isPal = cpu->GetMem().IsROMPAL(); cpu->GetMem().mmio.si.pif.InitDevices(cpu->GetMem().saveType); cpu->GetMem().mmio.si.pif.mempakPath = rom; diff --git a/src/backend/Core.hpp b/src/backend/Core.hpp index 9fab9386..52394ab8 100644 --- a/src/backend/Core.hpp +++ b/src/backend/Core.hpp @@ -11,7 +11,7 @@ struct Core { Core(ParallelRDP&); void Stop(); void LoadROM(const std::string&); - bool LoadTAS(const fs::path&); + bool LoadTAS(const fs::path&) const; void Run(float volumeL, float volumeR); void Serialize(); void Deserialize(); diff --git a/src/backend/core/Interpreter.hpp b/src/backend/core/Interpreter.hpp index 461558cb..daf4218b 100644 --- a/src/backend/core/Interpreter.hpp +++ b/src/backend/core/Interpreter.hpp @@ -12,6 +12,7 @@ struct Interpreter : BaseCPU { void Reset() override { regs.Reset(); mem.Reset(); + cop2Latch = {}; } Mem& GetMem() override { diff --git a/src/backend/core/MMIO.hpp b/src/backend/core/MMIO.hpp index 32864f1b..ccb00339 100644 --- a/src/backend/core/MMIO.hpp +++ b/src/backend/core/MMIO.hpp @@ -15,7 +15,9 @@ struct Mem; struct Registers; struct MMIO { - MMIO(Mem& mem, Registers& regs, ParallelRDP& parallel) : vi(mem, regs), mi(regs), ai(mem, regs), pi(mem, regs), si(mem, regs), rsp(mem, regs), rdp(mem, parallel) {} + MMIO(Mem& mem, Registers& regs, ParallelRDP& parallel) : vi(mem, regs), mi(regs), ai(mem, regs), pi(mem, regs), si(mem, regs), rsp(mem, regs), rdp(mem, parallel) { + Reset(); + } void Reset(); VI vi; diff --git a/src/backend/core/Mem.cpp b/src/backend/core/Mem.cpp index 7e54003b..2c0c21c1 100644 --- a/src/backend/core/Mem.cpp +++ b/src/backend/core/Mem.cpp @@ -9,16 +9,6 @@ namespace n64 { Mem::Mem(Registers& regs, ParallelRDP& parallel) : flash(saveData), mmio(*this, regs, parallel) { - memset(readPages, 0, PAGE_COUNT); - memset(writePages, 0, PAGE_COUNT); - - for(u64 i = 0; i < RDRAM_SIZE / PAGE_SIZE; i++) { - const auto addr = (i * PAGE_SIZE) & RDRAM_DSIZE; - const auto pointer = (uintptr_t) &mmio.rdp.rdram[addr]; - readPages[i] = pointer; - writePages[i] = pointer; - } - rom.cart.resize(CART_SIZE); std::fill(rom.cart.begin(), rom.cart.end(), 0); } @@ -178,317 +168,261 @@ void Mem::LoadROM(bool isArchive, const std::string& filename) { } template<> u8 Mem::Read(n64::Registers ®s, u32 paddr) { - const auto page = paddr >> 12; - const auto offset = paddr & 0xFFF; - const auto pointer = readPages[page]; SI& si = mmio.si; - if(pointer) { - return ((u8*)pointer)[BYTE_ADDRESS(offset)]; - } else { - switch (paddr) { - case RDRAM_REGION: - return mmio.rdp.rdram[BYTE_ADDRESS(paddr)]; - case RSP_MEM_REGION: { - auto& src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; - return src[BYTE_ADDRESS(paddr & 0xfff)]; - } - case REGION_CART: - return mmio.pi.BusRead(paddr); - case 0x04040000 ... 0x040FFFFF: - case 0x04100000 ... 0x041FFFFF: - case 0x04600000 ... 0x048FFFFF: - case 0x04300000 ... 0x044FFFFF: - Util::panic("MMIO Read!\n"); - case AI_REGION: { - u32 w = mmio.ai.Read(paddr & ~3); - int offs = 3 - (paddr & 3); - return (w >> (offs * 8)) & 0xff; - } - case PIF_ROM_REGION: - return si.pif.bootrom[BYTE_ADDRESS(paddr) - PIF_ROM_REGION_START]; - case PIF_RAM_REGION: - return si.pif.ram[paddr - PIF_RAM_REGION_START]; - case 0x00800000 ... 0x03EFFFFF: // unused - case 0x04200000 ... 0x042FFFFF: // unused - case 0x04900000 ... 0x04FFFFFF: // unused - case 0x1FC00800 ... 0xFFFFFFFF: // unused - return 0; - default: - Util::panic("Unimplemented 8-bit read at address {:08X} (PC = {:016X})", paddr, (u64) regs.pc); + switch (paddr) { + case RDRAM_REGION: + return mmio.rdp.ReadRDRAM(paddr); + case RSP_MEM_REGION: { + auto& src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; + return src[BYTE_ADDRESS(paddr & 0xfff)]; } + case REGION_CART: + return mmio.pi.BusRead(paddr); + case 0x04040000 ... 0x040FFFFF: + case 0x04100000 ... 0x041FFFFF: + case 0x04600000 ... 0x048FFFFF: + case 0x04300000 ... 0x044FFFFF: + Util::panic("MMIO Read!\n"); + case AI_REGION: { + u32 w = mmio.ai.Read(paddr & ~3); + int offs = 3 - (paddr & 3); + return (w >> (offs * 8)) & 0xff; + } + case PIF_ROM_REGION: + return si.pif.bootrom[BYTE_ADDRESS(paddr) - PIF_ROM_REGION_START]; + case PIF_RAM_REGION: + return si.pif.ram[paddr - PIF_RAM_REGION_START]; + case 0x00800000 ... 0x03EFFFFF: // unused + case 0x04200000 ... 0x042FFFFF: // unused + case 0x04900000 ... 0x04FFFFFF: // unused + case 0x1FC00800 ... 0xFFFFFFFF: // unused + return 0; + default: + Util::panic("Unimplemented 8-bit read at address {:08X} (PC = {:016X})", paddr, (u64) regs.pc); } } template<> u16 Mem::Read(n64::Registers ®s, u32 paddr) { - const auto page = paddr >> 12; - const auto offset = paddr & 0xFFF; - const auto pointer = readPages[page]; SI& si = mmio.si; - if(pointer) { - return Util::ReadAccess((u8*)pointer, HALF_ADDRESS(offset)); - } else { - switch (paddr) { - case RDRAM_REGION: - return Util::ReadAccess(mmio.rdp.rdram, HALF_ADDRESS(paddr)); - case RSP_MEM_REGION: { - auto& src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; - return Util::ReadAccess(src, HALF_ADDRESS(paddr & 0xfff)); - } - case MMIO_REGION: - return mmio.Read(paddr); - case REGION_CART: - return mmio.pi.BusRead(paddr); - case PIF_ROM_REGION: - return Util::ReadAccess(si.pif.bootrom, HALF_ADDRESS(paddr) - PIF_ROM_REGION_START); - case PIF_RAM_REGION: - return be16toh(Util::ReadAccess(si.pif.ram, paddr - PIF_RAM_REGION_START)); - case 0x00800000 ... 0x03EFFFFF: - case 0x04200000 ... 0x042FFFFF: - case 0x04900000 ... 0x04FFFFFF: - case 0x1FC00800 ... 0xFFFFFFFF: - return 0; - default: - Util::panic("Unimplemented 16-bit read at address {:08X} (PC = {:016X})", paddr, (u64) regs.pc); + switch (paddr) { + case RDRAM_REGION: + return mmio.rdp.ReadRDRAM(paddr); + case RSP_MEM_REGION: { + auto& src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; + return Util::ReadAccess(src, HALF_ADDRESS(paddr & 0xfff)); } + case MMIO_REGION: + return mmio.Read(paddr); + case REGION_CART: + return mmio.pi.BusRead(paddr); + case PIF_ROM_REGION: + return Util::ReadAccess(si.pif.bootrom, HALF_ADDRESS(paddr) - PIF_ROM_REGION_START); + case PIF_RAM_REGION: + return be16toh(Util::ReadAccess(si.pif.ram, paddr - PIF_RAM_REGION_START)); + case 0x00800000 ... 0x03EFFFFF: + case 0x04200000 ... 0x042FFFFF: + case 0x04900000 ... 0x04FFFFFF: + case 0x1FC00800 ... 0xFFFFFFFF: + return 0; + default: + Util::panic("Unimplemented 16-bit read at address {:08X} (PC = {:016X})", paddr, (u64) regs.pc); } } template<> u32 Mem::Read(n64::Registers ®s, u32 paddr) { - const auto page = paddr >> 12; - const auto offset = paddr & 0xFFF; - const auto pointer = readPages[page]; SI& si = mmio.si; - if(pointer) { - return Util::ReadAccess((u8*)pointer, offset); - } else { - switch(paddr) { - case RDRAM_REGION: - return Util::ReadAccess(mmio.rdp.rdram, paddr); - case RSP_MEM_REGION: { - auto& src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; - return Util::ReadAccess(src, paddr & 0xfff); - } - case MMIO_REGION: - return mmio.Read(paddr); - case REGION_CART: - return mmio.pi.BusRead(paddr); - case PIF_ROM_REGION: - return Util::ReadAccess(si.pif.bootrom, paddr - PIF_ROM_REGION_START); - case PIF_RAM_REGION: - return be32toh(Util::ReadAccess(si.pif.ram, paddr - PIF_RAM_REGION_START)); - case 0x00800000 ... 0x03EFFFFF: case 0x04200000 ... 0x042FFFFF: - case 0x04900000 ... 0x04FFFFFF: case 0x1FC00800 ... 0xFFFFFFFF: return 0; - default: - Util::panic("Unimplemented 32-bit read at address {:08X} (PC = {:016X})", paddr, (u64) regs.pc); + switch(paddr) { + case RDRAM_REGION: + return mmio.rdp.ReadRDRAM(paddr); + case RSP_MEM_REGION: { + auto& src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; + return Util::ReadAccess(src, paddr & 0xfff); } + case MMIO_REGION: + return mmio.Read(paddr); + case REGION_CART: + return mmio.pi.BusRead(paddr); + case PIF_ROM_REGION: + return Util::ReadAccess(si.pif.bootrom, paddr - PIF_ROM_REGION_START); + case PIF_RAM_REGION: + return be32toh(Util::ReadAccess(si.pif.ram, paddr - PIF_RAM_REGION_START)); + case 0x00800000 ... 0x03FFFFFF: case 0x04200000 ... 0x042FFFFF: + case 0x04900000 ... 0x04FFFFFF: case 0x1FC00800 ... 0xFFFFFFFF: return 0; + default: + Util::panic("Unimplemented 32-bit read at address {:08X} (PC = {:016X})", paddr, (u64) regs.pc); } } template<> u64 Mem::Read(n64::Registers ®s, u32 paddr) { - const auto page = paddr >> 12; - const auto offset = paddr & 0xFFF; - const auto pointer = readPages[page]; SI& si = mmio.si; - if(pointer) { - return Util::ReadAccess((u8*)pointer, offset); - } else { - switch (paddr) { - case RDRAM_REGION: - return Util::ReadAccess(mmio.rdp.rdram, paddr); - case RSP_MEM_REGION: { - auto& src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; - return Util::ReadAccess(src, paddr & 0xfff); - } - case MMIO_REGION: - return mmio.Read(paddr); - case REGION_CART: - return mmio.pi.BusRead(paddr); - case PIF_ROM_REGION: - return Util::ReadAccess(si.pif.bootrom, paddr - PIF_ROM_REGION_START); - case PIF_RAM_REGION: - return be64toh(Util::ReadAccess(si.pif.ram, paddr - PIF_RAM_REGION_START)); - case 0x00800000 ... 0x03EFFFFF: - case 0x04200000 ... 0x042FFFFF: - case 0x04900000 ... 0x04FFFFFF: - case 0x1FC00800 ... 0xFFFFFFFF: - return 0; - default: - Util::panic("Unimplemented 32-bit read at address {:08X} (PC = {:016X})", paddr, (u64) regs.pc); + switch (paddr) { + case RDRAM_REGION: + return mmio.rdp.ReadRDRAM(paddr); + case RSP_MEM_REGION: { + auto& src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; + return Util::ReadAccess(src, paddr & 0xfff); } + case MMIO_REGION: + return mmio.Read(paddr); + case REGION_CART: + return mmio.pi.BusRead(paddr); + case PIF_ROM_REGION: + return Util::ReadAccess(si.pif.bootrom, paddr - PIF_ROM_REGION_START); + case PIF_RAM_REGION: + return be64toh(Util::ReadAccess(si.pif.ram, paddr - PIF_RAM_REGION_START)); + case 0x00800000 ... 0x03EFFFFF: + case 0x04200000 ... 0x042FFFFF: + case 0x04900000 ... 0x04FFFFFF: + case 0x1FC00800 ... 0xFFFFFFFF: + return 0; + default: + Util::panic("Unimplemented 32-bit read at address {:08X} (PC = {:016X})", paddr, (u64) regs.pc); } } template<> void Mem::Write(Registers& regs, u32 paddr, u32 val) { - const auto page = paddr >> 12; - const auto offset = paddr & 0xFFF; - const auto pointer = writePages[page]; SI& si = mmio.si; - if(pointer) { - ((u8*)pointer)[BYTE_ADDRESS(offset)] = val; - } else { - switch (paddr) { - case RDRAM_REGION: - mmio.rdp.rdram[BYTE_ADDRESS(paddr)] = val; - break; - case RSP_MEM_REGION: { - val = val << (8 * (3 - (paddr & 3))); - auto& dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; - paddr = (paddr & 0xFFF) & ~3; - Util::WriteAccess(dest, paddr, val); - } break; - case REGION_CART: - Util::trace("BusWrite @ {:08X} = {:02X}", paddr, val); - mmio.pi.BusWrite(paddr, val); - break; - case MMIO_REGION: - Util::panic("MMIO Write!"); - case PIF_RAM_REGION: - val = val << (8 * (3 - (paddr & 3))); - paddr = (paddr - PIF_RAM_REGION_START) & ~3; - Util::WriteAccess(si.pif.ram, paddr, htobe32(val)); - si.pif.ProcessCommands(*this); - break; - case 0x00800000 ... 0x03EFFFFF: - case 0x04200000 ... 0x042FFFFF: - case 0x04900000 ... 0x04FFFFFF: - case PIF_ROM_REGION: - case 0x1FC00800 ... 0x7FFFFFFF: - case 0x80000000 ... 0xFFFFFFFF: - break; - default: - Util::panic("Unimplemented 8-bit write at address {:08X} with value {:02X} (PC = {:016X})", paddr, val, - (u64) regs.pc); - } + switch (paddr) { + case RDRAM_REGION: + mmio.rdp.WriteRDRAM(paddr, val); + break; + case RSP_MEM_REGION: { + val = val << (8 * (3 - (paddr & 3))); + auto& dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; + paddr = (paddr & 0xFFF) & ~3; + Util::WriteAccess(dest, paddr, val); + } break; + case REGION_CART: + Util::trace("BusWrite @ {:08X} = {:02X}", paddr, val); + mmio.pi.BusWrite(paddr, val); + break; + case MMIO_REGION: + Util::panic("MMIO Write!"); + case PIF_RAM_REGION: + val = val << (8 * (3 - (paddr & 3))); + paddr = (paddr - PIF_RAM_REGION_START) & ~3; + Util::WriteAccess(si.pif.ram, paddr, htobe32(val)); + si.pif.ProcessCommands(*this); + break; + case 0x00800000 ... 0x03EFFFFF: + case 0x04200000 ... 0x042FFFFF: + case 0x04900000 ... 0x04FFFFFF: + case PIF_ROM_REGION: + case 0x1FC00800 ... 0x7FFFFFFF: + case 0x80000000 ... 0xFFFFFFFF: + break; + default: + Util::panic("Unimplemented 8-bit write at address {:08X} with value {:02X} (PC = {:016X})", paddr, val, + (u64) regs.pc); } } template<> void Mem::Write(Registers& regs, u32 paddr, u32 val) { - const auto page = paddr >> 12; - const auto offset = paddr & 0xFFF; - const auto pointer = writePages[page]; SI& si = mmio.si; - if(pointer) { - Util::WriteAccess((u8*)pointer, HALF_ADDRESS(offset), val); - } else { - switch (paddr) { - case RDRAM_REGION: - Util::WriteAccess(mmio.rdp.rdram, HALF_ADDRESS(paddr), val); - break; - case RSP_MEM_REGION: { - val = val << (16 * !(paddr & 2)); - auto& dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; - paddr = (paddr & 0xFFF) & ~3; - Util::WriteAccess(dest, paddr, val); - } break; - case REGION_CART: - Util::trace("BusWrite @ {:08X} = {:04X}", paddr, val); - mmio.pi.BusWrite(paddr, val); - break; - case MMIO_REGION: - Util::panic("MMIO Write!"); - case PIF_RAM_REGION: - val = val << (16 * !(paddr & 2)); - paddr &= ~3; - Util::WriteAccess(si.pif.ram, paddr - PIF_RAM_REGION_START, htobe32(val)); - si.pif.ProcessCommands(*this); - break; - case 0x00800000 ... 0x03EFFFFF: - case 0x04200000 ... 0x042FFFFF: - case 0x04900000 ... 0x04FFFFFF: - case PIF_ROM_REGION: - case 0x1FC00800 ... 0x7FFFFFFF: - case 0x80000000 ... 0xFFFFFFFF: - break; - default: - Util::panic("Unimplemented 16-bit write at address {:08X} with value {:04X} (PC = {:016X})", paddr, val, - (u64) regs.pc); - } + switch (paddr) { + case RDRAM_REGION: + mmio.rdp.WriteRDRAM(paddr, val); + break; + case RSP_MEM_REGION: { + val = val << (16 * !(paddr & 2)); + auto& dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; + paddr = (paddr & 0xFFF) & ~3; + Util::WriteAccess(dest, paddr, val); + } break; + case REGION_CART: + Util::trace("BusWrite @ {:08X} = {:04X}", paddr, val); + mmio.pi.BusWrite(paddr, val); + break; + case MMIO_REGION: + Util::panic("MMIO Write!"); + case PIF_RAM_REGION: + val = val << (16 * !(paddr & 2)); + paddr &= ~3; + Util::WriteAccess(si.pif.ram, paddr - PIF_RAM_REGION_START, htobe32(val)); + si.pif.ProcessCommands(*this); + break; + case 0x00800000 ... 0x03EFFFFF: + case 0x04200000 ... 0x042FFFFF: + case 0x04900000 ... 0x04FFFFFF: + case PIF_ROM_REGION: + case 0x1FC00800 ... 0x7FFFFFFF: + case 0x80000000 ... 0xFFFFFFFF: + break; + default: + Util::panic("Unimplemented 16-bit write at address {:08X} with value {:04X} (PC = {:016X})", paddr, val, + (u64) regs.pc); } } template<> void Mem::Write(Registers& regs, u32 paddr, u32 val) { - const auto page = paddr >> 12; - const auto offset = paddr & 0xFFF; - const auto pointer = writePages[page]; SI& si = mmio.si; - if(pointer) { - Util::WriteAccess((u8*)pointer, offset, val); - } else { - switch(paddr) { - case RDRAM_REGION: - Util::WriteAccess(mmio.rdp.rdram, paddr, val); - break; - case RSP_MEM_REGION: { - auto& dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; - Util::WriteAccess(dest, paddr & 0xfff, val); - } break; - case REGION_CART: - Util::trace("BusWrite @ {:08X} = {:08X}", paddr, val); - mmio.pi.BusWrite(paddr, val); - break; - case MMIO_REGION: - mmio.Write(paddr, val); - break; - case PIF_RAM_REGION: - Util::WriteAccess(si.pif.ram, paddr - PIF_RAM_REGION_START, htobe32(val)); - si.pif.ProcessCommands(*this); - break; - case 0x00800000 ... 0x03EFFFFF: - case 0x04200000 ... 0x042FFFFF: - case 0x04900000 ... 0x04FFFFFF: - case PIF_ROM_REGION: - case 0x1FC00800 ... 0x7FFFFFFF: - case 0x80000000 ... 0xFFFFFFFF: break; - default: Util::panic("Unimplemented 32-bit write at address {:08X} with value {:0X} (PC = {:016X})", paddr, val, (u64)regs.pc); - } + switch(paddr) { + case RDRAM_REGION: + mmio.rdp.WriteRDRAM(paddr, val); + break; + case RSP_MEM_REGION: { + auto& dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; + Util::WriteAccess(dest, paddr & 0xfff, val); + } break; + case REGION_CART: + Util::trace("BusWrite @ {:08X} = {:08X}", paddr, val); + mmio.pi.BusWrite(paddr, val); + break; + case MMIO_REGION: + mmio.Write(paddr, val); + break; + case PIF_RAM_REGION: + Util::WriteAccess(si.pif.ram, paddr - PIF_RAM_REGION_START, htobe32(val)); + si.pif.ProcessCommands(*this); + break; + case 0x00800000 ... 0x03EFFFFF: + case 0x04200000 ... 0x042FFFFF: + case 0x04900000 ... 0x04FFFFFF: + case PIF_ROM_REGION: + case 0x1FC00800 ... 0x7FFFFFFF: + case 0x80000000 ... 0xFFFFFFFF: break; + default: Util::panic("Unimplemented 32-bit write at address {:08X} with value {:0X} (PC = {:016X})", paddr, val, (u64)regs.pc); } } void Mem::Write(Registers& regs, u32 paddr, u64 val) { - const auto page = paddr >> 12; - const auto offset = paddr & 0xFFF; - const auto pointer = writePages[page]; SI& si = mmio.si; - if(pointer) { - Util::WriteAccess((u8*)pointer, offset, val); - } else { - switch (paddr) { - case RDRAM_REGION: - Util::WriteAccess(mmio.rdp.rdram, paddr, val); - break; - case RSP_MEM_REGION: { - auto& dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; - val >>= 32; - Util::WriteAccess(dest, paddr & 0xfff, val); - } break; - case REGION_CART: - Util::trace("BusWrite @ {:08X} = {:016X}", paddr, val); - mmio.pi.BusWrite(paddr, val); - break; - case MMIO_REGION: - Util::panic("MMIO Write!"); - case PIF_RAM_REGION: - Util::WriteAccess(si.pif.ram, paddr - PIF_RAM_REGION_START, htobe64(val)); - si.pif.ProcessCommands(*this); - break; - case 0x00800000 ... 0x03EFFFFF: - case 0x04200000 ... 0x042FFFFF: - case 0x04900000 ... 0x04FFFFFF: - case 0x1FC00000 ... 0x1FC007BF: - case 0x1FC00800 ... 0x7FFFFFFF: - case 0x80000000 ... 0xFFFFFFFF: break; - default: - Util::panic("Unimplemented 64-bit write at address {:08X} with value {:0X} (PC = {:016X})", paddr, val, - (u64) regs.pc); - } + switch (paddr) { + case RDRAM_REGION: + mmio.rdp.WriteRDRAM(paddr, val); + break; + case RSP_MEM_REGION: { + auto& dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; + val >>= 32; + Util::WriteAccess(dest, paddr & 0xfff, val); + } break; + case REGION_CART: + Util::trace("BusWrite @ {:08X} = {:016X}", paddr, val); + mmio.pi.BusWrite(paddr, val); + break; + case MMIO_REGION: + Util::panic("MMIO Write!"); + case PIF_RAM_REGION: + Util::WriteAccess(si.pif.ram, paddr - PIF_RAM_REGION_START, htobe64(val)); + si.pif.ProcessCommands(*this); + break; + case 0x00800000 ... 0x03EFFFFF: + case 0x04200000 ... 0x042FFFFF: + case 0x04900000 ... 0x04FFFFFF: + case 0x1FC00000 ... 0x1FC007BF: + case 0x1FC00800 ... 0x7FFFFFFF: + case 0x80000000 ... 0xFFFFFFFF: break; + default: + Util::panic("Unimplemented 64-bit write at address {:08X} with value {:0X} (PC = {:016X})", paddr, val, + (u64) regs.pc); } } diff --git a/src/backend/core/Mem.hpp b/src/backend/core/Mem.hpp index 122c50c3..6acbf691 100644 --- a/src/backend/core/Mem.hpp +++ b/src/backend/core/Mem.hpp @@ -132,7 +132,6 @@ struct Mem { Util::SwapBuffer32(temp); Util::WriteFileBinary(temp, "dmem.bin"); } - uintptr_t writePages[PAGE_COUNT]{}, readPages[PAGE_COUNT]{}; ROM rom; SaveType saveType = SAVE_NONE; Flash flash; diff --git a/src/backend/core/RDP.cpp b/src/backend/core/RDP.cpp index a445011d..b3c7406f 100644 --- a/src/backend/core/RDP.cpp +++ b/src/backend/core/RDP.cpp @@ -2,7 +2,6 @@ #include #include #include -#include namespace n64 { RDP::RDP(Mem& mem, ParallelRDP& parallel) : mem(mem), parallel(parallel) { @@ -13,11 +12,60 @@ RDP::RDP(Mem& mem, ParallelRDP& parallel) : mem(mem), parallel(parallel) { } void RDP::Reset() { + dpc = {}; dpc.status.raw = 0x80; std::fill(rdram.begin(), rdram.end(), 0); memset(cmd_buf, 0, 0x100000); } +template<> void RDP::WriteRDRAM(size_t idx, u8 v) { + size_t real = BYTE_ADDRESS(idx); + if(real < RDRAM_SIZE) { + rdram[real] = v; + } +} + +template<> void RDP::WriteRDRAM(size_t idx, u16 v) { + size_t real = HALF_ADDRESS(idx); + if(real < RDRAM_SIZE) { + Util::WriteAccess(rdram, real, v); + } +} + +template<> void RDP::WriteRDRAM(size_t idx, u32 v) { + if(idx < RDRAM_SIZE) { + Util::WriteAccess(rdram, idx, v); + } +} + +template<> void RDP::WriteRDRAM(size_t idx, u64 v) { + if(idx < RDRAM_SIZE) { + Util::WriteAccess(rdram, idx, v); + } +} + +template<> u8 RDP::ReadRDRAM(size_t idx) { + size_t real = BYTE_ADDRESS(idx); + if(real >= RDRAM_SIZE) return 0; + return rdram[real]; +} + +template<> u16 RDP::ReadRDRAM(size_t idx) { + size_t real = HALF_ADDRESS(idx); + if(real >= RDRAM_SIZE) return 0; + return Util::ReadAccess(rdram, real); +} + +template<> u32 RDP::ReadRDRAM(size_t idx) { + if(idx >= RDRAM_SIZE) return 0; + return Util::ReadAccess(rdram, idx); +} + +template<> u64 RDP::ReadRDRAM(size_t idx) { + if(idx >= RDRAM_SIZE) return 0; + return Util::ReadAccess(rdram, idx); +} + static const int cmd_lens[64] = { 2, 2, 2, 2, 2, 2, 2, 2, 8, 12, 24, 28, 24, 28, 40, 44, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, diff --git a/src/backend/core/RDP.hpp b/src/backend/core/RDP.hpp index 8cafac93..e3f56dd7 100644 --- a/src/backend/core/RDP.hpp +++ b/src/backend/core/RDP.hpp @@ -59,7 +59,6 @@ struct RDP { RDP(Mem&, ParallelRDP&); void Reset(); - std::vector rdram{}; [[nodiscard]] auto Read(u32 addr) const -> u32; void Write(u32 addr, u32 val); void WriteStatus(u32 val); @@ -81,7 +80,16 @@ struct RDP { } RunCommand(); } + + template + void WriteRDRAM(size_t, T); + template + T ReadRDRAM(size_t); private: + friend struct Mem; + friend struct MMIO; + std::vector rdram{}; + Mem& mem; ParallelRDP& parallel; }; diff --git a/src/backend/core/RSP.cpp b/src/backend/core/RSP.cpp index 99175f55..e70a4f39 100644 --- a/src/backend/core/RSP.cpp +++ b/src/backend/core/RSP.cpp @@ -31,6 +31,7 @@ void RSP::Reset() { divIn = 0; divOut = 0; divInLoaded = false; + steps = 0; } /* @@ -66,10 +67,10 @@ FORCE_INLINE void logRSP(const RSP& rsp, const u32 instr) { } */ -auto RSP::Read(u32 addr) -> u32{ +auto RSP::Read(u32 addr) -> u32 { switch (addr) { case 0x04040000: return lastSuccessfulSPAddr.raw & 0x1FF8; - case 0x04040004: return lastSuccessfulDRAMAddr.raw & 0xFFFFF8; + case 0x04040004: return lastSuccessfulDRAMAddr.raw & 0xFFFFFC; case 0x04040008: case 0x0404000C: return spDMALen.raw; case 0x04040010: return spStatus.raw; @@ -110,17 +111,79 @@ void RSP::WriteStatus(u32 value) { CLEAR_SET(spStatus.signal7, write.clearSignal7, write.setSignal7); } +template <> void RSP::DMA() { + u32 length = spDMALen.len + 1; + + length = (length + 0x7) & ~0x7; + + std::array& src = spDMASPAddr.bank ? imem : dmem; + + u32 mem_address = spDMASPAddr.address & 0xFF8; + u32 dram_address = spDMADRAMAddr.address & 0xFFFFFC; + Util::trace("SP DMA from RSP to RDRAM (size: {} B, {:08X} to {:08X})", length, mem_address, dram_address); + + for (u32 i = 0; i < spDMALen.count + 1; i++) { + for(u32 j = 0; j < length; j++) { + mem.mmio.rdp.WriteRDRAM(BYTE_ADDRESS(dram_address + j), src[(mem_address + j) & DMEM_DSIZE]); + } + + int skip = i == spDMALen.count ? 0 : spDMALen.skip; + + dram_address += (length + skip); + dram_address &= 0xFFFFFC; + mem_address += length; + mem_address &= 0xFF8; + } + Util::trace("Addresses after: RSP: 0x{:08X}, Dram: 0x{:08X}", mem_address, dram_address); + + lastSuccessfulSPAddr.address = mem_address; + lastSuccessfulSPAddr.bank = spDMASPAddr.bank; + lastSuccessfulDRAMAddr.address = dram_address; + spDMALen.raw = 0xFF8 | (spDMALen.skip << 20); +} + +template <> void RSP::DMA() { + u32 length = spDMALen.len + 1; + + length = (length + 0x7) & ~0x7; + + std::array& dst = spDMASPAddr.bank ? imem : dmem; + + u32 mem_address = spDMASPAddr.address & 0xFF8; + u32 dram_address = spDMADRAMAddr.address & 0xFFFFFC; + Util::trace("SP DMA from RDRAM to RSP (size: {} B, {:08X} to {:08X})", length, dram_address, mem_address); + + for (u32 i = 0; i < spDMALen.count + 1; i++) { + for(u32 j = 0; j < length; j++) { + dst[(mem_address + j) & DMEM_DSIZE] = mem.mmio.rdp.ReadRDRAM(BYTE_ADDRESS(dram_address + j)); + } + + int skip = i == spDMALen.count ? 0 : spDMALen.skip; + + dram_address += (length + skip); + dram_address &= 0xFFFFFC; + mem_address += length; + mem_address &= 0xFF8; + } + Util::trace("Addresses after: RSP: 0x{:08X}, Dram: 0x{:08X}", mem_address, dram_address); + + lastSuccessfulSPAddr.address = mem_address; + lastSuccessfulSPAddr.bank = spDMASPAddr.bank; + lastSuccessfulDRAMAddr.address = dram_address; + spDMALen.raw = 0xFF8 | (spDMALen.skip << 20); +} + void RSP::Write(u32 addr, u32 val) { switch (addr) { case 0x04040000: spDMASPAddr.raw = val & 0x1FF8; break; - case 0x04040004: spDMADRAMAddr.raw = val & 0xFFFFF8; break; + case 0x04040004: spDMADRAMAddr.raw = val & 0xFFFFFC; break; case 0x04040008: { spDMALen.raw = val; - DMAtoRSP(mem.GetRDRAM()); + DMA(); } break; case 0x0404000C: { spDMALen.raw = val; - DMAtoRDRAM(mem.GetRDRAM()); + DMA(); } break; case 0x04040010: WriteStatus(val); break; case 0x0404001C: ReleaseSemaphore(); break; @@ -132,70 +195,4 @@ void RSP::Write(u32 addr, u32 val) { Util::panic("Unimplemented SP register write {:08X}, val: {:08X}", addr, val); } } - -void RSP::DMAtoRDRAM(std::vector& rdram) { - u32 length = spDMALen.len + 1; - - length = (length + 0x7) & ~0x7; - - std::vector& dst = rdram; - std::array& src = spDMASPAddr.bank ? imem : dmem; - - u32 mem_address = spDMASPAddr.address & 0xFF8; - u32 dram_address = spDMADRAMAddr.address & 0xFFFFF8; - - for (u32 i = 0; i < spDMALen.count + 1; i++) { - for(u32 j = 0; j < length; j++) { - if((dram_address + j) < RDRAM_SIZE) { - dst[dram_address + j] = src[(mem_address + j) & DMEM_DSIZE]; - } - } - - int skip = i == spDMALen.count ? 0 : spDMALen.skip; - - dram_address += (length + skip); - dram_address &= 0xFFFFF8; - mem_address += length; - mem_address &= 0xFF8; - } - - lastSuccessfulSPAddr.address = mem_address; - lastSuccessfulSPAddr.bank = spDMASPAddr.bank; - lastSuccessfulDRAMAddr.address = dram_address; - spDMALen.raw = 0xFF8 | (spDMALen.skip << 20); -} - -void RSP::DMAtoRSP(std::vector& rdram) { - u32 length = spDMALen.len + 1; - - length = (length + 0x7) & ~0x7; - - std::vector& src = rdram; - std::array& dst = spDMASPAddr.bank ? imem : dmem; - - u32 mem_address = spDMASPAddr.address & 0xFF8; - u32 dram_address = spDMADRAMAddr.address & 0xFFFFF8; - - for (u32 i = 0; i < spDMALen.count + 1; i++) { - for(u32 j = 0; j < length; j++) { - if((dram_address + j) < RDRAM_SIZE) { - dst[(mem_address + j) & DMEM_DSIZE] = src[dram_address + j]; - } else { - dst[(mem_address + j) & DMEM_DSIZE] = 0; - } - } - - int skip = i == spDMALen.count ? 0 : spDMALen.skip; - - dram_address += (length + skip); - dram_address &= 0xFFFFF8; - mem_address += length; - mem_address &= 0xFF8; - } - - lastSuccessfulSPAddr.address = mem_address; - lastSuccessfulSPAddr.bank = spDMASPAddr.bank; - lastSuccessfulDRAMAddr.address = dram_address; - spDMALen.raw = 0xFF8 | (spDMALen.skip << 20); -} } diff --git a/src/backend/core/RSP.hpp b/src/backend/core/RSP.hpp index 1c4c9e46..692d8f0f 100644 --- a/src/backend/core/RSP.hpp +++ b/src/backend/core/RSP.hpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #define RSP_BYTE(addr) (dmem[BYTE_ADDRESS(addr) & 0xFFF]) @@ -358,8 +357,8 @@ struct RSP { void mfc2(u32 instr); void mtc2(u32 instr); - void DMAtoRDRAM(std::vector& rdram); - void DMAtoRSP(std::vector& rdram); + template + void DMA(); void WriteStatus(u32 value); private: Registers& regs; diff --git a/src/backend/core/interpreter/decode.cpp b/src/backend/core/interpreter/decode.cpp index d27cdbc7..878c18ba 100644 --- a/src/backend/core/interpreter/decode.cpp +++ b/src/backend/core/interpreter/decode.cpp @@ -51,12 +51,12 @@ void Interpreter::special(u32 instr) { case DADDU: daddu(instr); break; case DSUB: dsub(instr); break; case DSUBU: dsubu(instr); break; - case TGE: trap(regs.gpr[RS(instr)] >= regs.gpr[RT(instr)]); break; - case TGEU: trap((u64)regs.gpr[RS(instr)] >= (u64)regs.gpr[RT(instr)]); break; - case TLT: trap(regs.gpr[RS(instr)] < regs.gpr[RT(instr)]); break; - case TLTU: trap((u64)regs.gpr[RS(instr)] < (u64)regs.gpr[RT(instr)]); break; - case TEQ: trap(regs.gpr[RS(instr)] == regs.gpr[RT(instr)]); break; - case TNE: trap(regs.gpr[RS(instr)] != regs.gpr[RT(instr)]); break; + case TGE: trap(regs.Read(RS(instr)) >= regs.Read(RT(instr))); break; + case TGEU: trap(regs.Read(RS(instr)) >= regs.Read(RT(instr))); break; + case TLT: trap(regs.Read(RS(instr)) < regs.Read(RT(instr))); break; + case TLTU: trap(regs.Read(RS(instr)) < regs.Read(RT(instr))); break; + case TEQ: trap(regs.Read(RS(instr)) == regs.Read(RT(instr))); break; + case TNE: trap(regs.Read(RS(instr)) != regs.Read(RT(instr))); break; case DSLL: dsll(instr); break; case DSRL: dsrl(instr); break; case DSRA: dsra(instr); break; @@ -72,20 +72,20 @@ void Interpreter::regimm(u32 instr) { u8 mask = ((instr >> 16) & 0x1F); // 000r_rccc switch (mask) { // TODO: named constants for clearer code - case BLTZ: b(instr, regs.gpr[RS(instr)] < 0); break; - case BGEZ: b(instr, regs.gpr[RS(instr)] >= 0); break; - case BLTZL: bl(instr, regs.gpr[RS(instr)] < 0); break; - case BGEZL: bl(instr, regs.gpr[RS(instr)] >= 0); break; - case TGEI: trap(regs.gpr[RS(instr)] >= s64(s16(instr))); break; - case TGEIU: trap(u64(regs.gpr[RS(instr)]) >= u64(s64(s16(instr)))); break; - case TLTI: trap(regs.gpr[RS(instr)] < s64(s16(instr))); break; - case TLTIU: trap(u64(regs.gpr[RS(instr)]) < u64(s64(s16(instr)))); break; - case TEQI: trap(regs.gpr[RS(instr)] == s64(s16(instr))); break; - case TNEI: trap(regs.gpr[RS(instr)] != s64(s16(instr))); break; - case BLTZAL: blink(instr, regs.gpr[RS(instr)] < 0); break; - case BGEZAL: blink(instr, regs.gpr[RS(instr)] >= 0); break; - case BLTZALL: bllink(instr, regs.gpr[RS(instr)] < 0); break; - case BGEZALL: bllink(instr, regs.gpr[RS(instr)] >= 0); break; + case BLTZ: b(instr, regs.Read(RS(instr)) < 0); break; + case BGEZ: b(instr, regs.Read(RS(instr)) >= 0); break; + case BLTZL: bl(instr, regs.Read(RS(instr)) < 0); break; + case BGEZL: bl(instr, regs.Read(RS(instr)) >= 0); break; + case TGEI: trap(regs.Read(RS(instr)) >= s64(s16(instr))); break; + case TGEIU: trap(regs.Read(RS(instr)) >= u64(s64(s16(instr)))); break; + case TLTI: trap(regs.Read(RS(instr)) < s64(s16(instr))); break; + case TLTIU: trap(regs.Read(RS(instr)) < u64(s64(s16(instr)))); break; + case TEQI: trap(regs.Read(RS(instr)) == s64(s16(instr))); break; + case TNEI: trap(regs.Read(RS(instr)) != s64(s16(instr))); break; + case BLTZAL: blink(instr, regs.Read(RS(instr)) < 0); break; + case BGEZAL: blink(instr, regs.Read(RS(instr)) >= 0); break; + case BLTZALL: bllink(instr, regs.Read(RS(instr)) < 0); break; + case BGEZALL: bllink(instr, regs.Read(RS(instr)) >= 0); break; default: Util::panic("Unimplemented regimm {} {} ({:08X}) (pc: {:016X})", (mask >> 3) & 3, mask & 7, instr, (u64)regs.oldPC); } @@ -116,13 +116,10 @@ void Interpreter::Exec(u32 instr) { case REGIMM: regimm(instr); break; case J: j(instr); break; case JAL: jal(instr); break; - case BEQ: b(instr, regs.gpr[RS(instr)] == regs.gpr[RT(instr)]); break; - case BNE: { - //fmt::print("RS: {:016X}, RT: {:016X}", (u64)regs.gpr[RS(instr)], (u64)regs.gpr[RT(instr)]); - b(instr, regs.gpr[RS(instr)] != regs.gpr[RT(instr)]); - } break; - case BLEZ: b(instr, regs.gpr[RS(instr)] <= 0); break; - case BGTZ: b(instr, regs.gpr[RS(instr)] > 0); break; + case BEQ: b(instr, regs.Read(RS(instr)) == regs.Read(RT(instr))); break; + case BNE: b(instr, regs.Read(RS(instr)) != regs.Read(RT(instr))); break; + case BLEZ: b(instr, regs.Read(RS(instr)) <= 0); break; + case BGTZ: b(instr, regs.Read(RS(instr)) > 0); break; case ADDI: addi(instr); break; case ADDIU: addiu(instr); break; case SLTI: slti(instr); break; @@ -134,10 +131,10 @@ void Interpreter::Exec(u32 instr) { case COP0: regs.cop0.decode(*this, instr); break; case COP1: regs.cop1.decode(*this, instr); break; case COP2: cop2Decode(instr); break; - case BEQL: bl(instr, regs.gpr[RS(instr)] == regs.gpr[RT(instr)]); break; - case BNEL: bl(instr, regs.gpr[RS(instr)] != regs.gpr[RT(instr)]); break; - case BLEZL: bl(instr, regs.gpr[RS(instr)] <= 0); break; - case BGTZL: bl(instr, regs.gpr[RS(instr)] > 0); break; + case BEQL: bl(instr, regs.Read(RS(instr)) == regs.Read(RT(instr))); break; + case BNEL: bl(instr, regs.Read(RS(instr)) != regs.Read(RT(instr))); break; + case BLEZL: bl(instr, regs.Read(RS(instr)) <= 0); break; + case BGTZL: bl(instr, regs.Read(RS(instr)) > 0); break; case DADDI: daddi(instr); break; case DADDIU: daddiu(instr); break; case LDL: ldl(instr); break; diff --git a/src/backend/core/interpreter/instructions.cpp b/src/backend/core/interpreter/instructions.cpp index f38b42e3..b31a2272 100644 --- a/src/backend/core/interpreter/instructions.cpp +++ b/src/backend/core/interpreter/instructions.cpp @@ -5,94 +5,78 @@ namespace n64 { void Interpreter::add(u32 instr) { - u32 rs = (s32)regs.gpr[RS(instr)]; - u32 rt = (s32)regs.gpr[RT(instr)]; + u32 rs = regs.Read(RS(instr)); + u32 rt = regs.Read(RT(instr)); u32 result = rs + rt; if(check_signed_overflow(rs, rt, result)) { regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); } else { - if (RD(instr) != 0) [[likely]] { - regs.gpr[RD(instr)] = s32(result); - } + regs.Write(RD(instr), s32(result)); } } void Interpreter::addu(u32 instr) { - if (RD(instr) != 0) [[likely]] { - s32 rs = (s32)regs.gpr[RS(instr)]; - s32 rt = (s32)regs.gpr[RT(instr)]; - s32 result = rs + rt; - regs.gpr[RD(instr)] = result; - } + s32 rs = (s32)regs.Read(RS(instr)); + s32 rt = (s32)regs.Read(RT(instr)); + s32 result = rs + rt; + regs.Write(RD(instr), result); } void Interpreter::addi(u32 instr) { - u32 rs = regs.gpr[RS(instr)]; + u32 rs = regs.Read(RS(instr)); u32 imm = s32(s16(instr)); u32 result = rs + imm; if(check_signed_overflow(rs, imm, result)) { regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); } else { - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = s32(result); - } + regs.Write(RT(instr), s32(result)); } } void Interpreter::addiu(u32 instr) { - s32 rs = (s32)regs.gpr[RS(instr)]; + s32 rs = regs.Read(RS(instr)); s16 imm = (s16)(instr); s32 result = rs + imm; - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = result; - } + regs.Write(RT(instr), result); } void Interpreter::dadd(u32 instr) { - u64 rs = regs.gpr[RS(instr)]; - u64 rt = regs.gpr[RT(instr)]; + u64 rs = regs.Read(RS(instr)); + u64 rt = regs.Read(RT(instr)); u64 result = rt + rs; if(check_signed_overflow(rs, rt, result)) { regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); } else { - if (RD(instr) != 0) [[likely]] { - regs.gpr[RD(instr)] = result; - } + regs.Write(RD(instr), result); } } void Interpreter::daddu(u32 instr) { - if (RD(instr) != 0) [[likely]] { - s64 rs = regs.gpr[RS(instr)]; - s64 rt = regs.gpr[RT(instr)]; - regs.gpr[RD(instr)] = rs + rt; - } + s64 rs = regs.Read(RS(instr)); + s64 rt = regs.Read(RT(instr)); + regs.Write(RD(instr), rs + rt); } void Interpreter::daddi(u32 instr) { u64 imm = s64(s16(instr)); - u64 rs = regs.gpr[RS(instr)]; + u64 rs = regs.Read(RS(instr)); u64 result = imm + rs; if(check_signed_overflow(rs, imm, result)) { regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); } else { - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = result; - } + regs.Write(RT(instr), result); } } void Interpreter::daddiu(u32 instr) { s16 imm = (s16)(instr); - s64 rs = regs.gpr[RS(instr)]; - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = rs + imm; - } + s64 rs = regs.Read(RS(instr)); + regs.Write(RT(instr), rs + imm); } void Interpreter::div(u32 instr) { - s64 dividend = (s32)regs.gpr[RS(instr)]; - s64 divisor = (s32)regs.gpr[RT(instr)]; + s64 dividend = regs.Read(RS(instr)); + s64 divisor = regs.Read(RT(instr)); if(divisor == 0) { regs.hi = dividend; @@ -110,8 +94,8 @@ void Interpreter::div(u32 instr) { } void Interpreter::divu(u32 instr) { - u32 dividend = regs.gpr[RS(instr)]; - u32 divisor = regs.gpr[RT(instr)]; + u32 dividend = regs.Read(RS(instr)); + u32 divisor = regs.Read(RT(instr)); if(divisor == 0) { regs.lo = -1; regs.hi = (s32)dividend; @@ -124,8 +108,8 @@ void Interpreter::divu(u32 instr) { } void Interpreter::ddiv(u32 instr) { - s64 dividend = regs.gpr[RS(instr)]; - s64 divisor = regs.gpr[RT(instr)]; + s64 dividend = regs.Read(RS(instr)); + s64 divisor = regs.Read(RT(instr)); if (dividend == 0x8000000000000000 && divisor == 0xFFFFFFFFFFFFFFFF) { regs.lo = dividend; regs.hi = 0; @@ -145,8 +129,8 @@ void Interpreter::ddiv(u32 instr) { } void Interpreter::ddivu(u32 instr) { - u64 dividend = regs.gpr[RS(instr)]; - u64 divisor = regs.gpr[RT(instr)]; + u64 dividend = regs.Read(RS(instr)); + u64 divisor = regs.Read(RT(instr)); if(divisor == 0) { regs.lo = -1; regs.hi = (s64)dividend; @@ -182,7 +166,7 @@ void Interpreter::b(u32 instr, bool cond) { } void Interpreter::blink(u32 instr, bool cond) { - regs.gpr[31] = regs.nextPC; + regs.Write(31, regs.nextPC); s16 imm = instr; s64 offset = u64((s64)imm) << 2; s64 address = regs.pc + offset; @@ -197,7 +181,7 @@ void Interpreter::bl(u32 instr, bool cond) { } void Interpreter::bllink(u32 instr, bool cond) { - regs.gpr[31] = regs.nextPC; + regs.Write(31, regs.nextPC); s16 imm = instr; s64 offset = u64((s64)imm) << 2; s64 address = regs.pc + offset; @@ -207,26 +191,22 @@ void Interpreter::bllink(u32 instr, bool cond) { void Interpreter::lui(u32 instr) { u64 val = s64((s16)instr); val <<= 16; - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = val; - } + regs.Write(RT(instr), val); } void Interpreter::lb(u32 instr) { - u64 address = regs.gpr[RS(instr)] + (s16)instr; + u64 address = regs.Read(RS(instr)) + (s16)instr; u32 paddr = 0; if(!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); } else { - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = (s8) mem.Read(regs, paddr); - } + regs.Write(RT(instr), (s8) mem.Read(regs, paddr)); } } void Interpreter::lh(u32 instr) { - u64 address = regs.gpr[RS(instr)] + (s16)instr; + u64 address = regs.Read(RS(instr)) + (s16)instr; if (check_address_error(0b1, address)) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); @@ -238,15 +218,13 @@ void Interpreter::lh(u32 instr) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); } else { - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = (s16) mem.Read(regs, paddr); - } + regs.Write(RT(instr), (s16) mem.Read(regs, paddr)); } } void Interpreter::lw(u32 instr) { s16 offset = instr; - u64 address = regs.gpr[RS(instr)] + offset; + u64 address = regs.Read(RS(instr)) + offset; if (check_address_error(0b11, address)) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); @@ -258,14 +236,12 @@ void Interpreter::lw(u32 instr) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); } else { - if(RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = (s32) mem.Read(regs, physical); - } + regs.Write(RT(instr), (s32) mem.Read(regs, physical)); } } void Interpreter::ll(u32 instr) { - u64 address = regs.gpr[RS(instr)] + (s16)instr; + u64 address = regs.Read(RS(instr)) + (s16)instr; u32 physical; if (!regs.cop0.MapVAddr(Cop0::LOAD, address, physical)) { regs.cop0.HandleTLBException(address); @@ -276,10 +252,8 @@ void Interpreter::ll(u32 instr) { regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); return; } - - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = result; - } + + regs.Write(RT(instr), result); regs.cop0.llbit = true; regs.cop0.LLAddr = physical >> 4; @@ -287,7 +261,7 @@ void Interpreter::ll(u32 instr) { } void Interpreter::lwl(u32 instr) { - u64 address = regs.gpr[RS(instr)] + (s16)instr; + u64 address = regs.Read(RS(instr)) + (s16)instr; u32 paddr = 0; if(!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { regs.cop0.HandleTLBException(address); @@ -296,15 +270,13 @@ void Interpreter::lwl(u32 instr) { u32 shift = 8 * ((address ^ 0) & 3); u32 mask = 0xFFFFFFFF << shift; u32 data = mem.Read(regs, paddr & ~3); - s32 result = s32((regs.gpr[RT(instr)] & ~mask) | (data << shift)); - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = result; - } + s32 result = s32((regs.Read(RT(instr)) & ~mask) | (data << shift)); + regs.Write(RT(instr), result); } } void Interpreter::lwr(u32 instr) { - u64 address = regs.gpr[RS(instr)] + (s16)instr; + u64 address = regs.Read(RS(instr)) + (s16)instr; u32 paddr = 0; if(!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { regs.cop0.HandleTLBException(address); @@ -313,15 +285,13 @@ void Interpreter::lwr(u32 instr) { u32 shift = 8 * ((address ^ 3) & 3); u32 mask = 0xFFFFFFFF >> shift; u32 data = mem.Read(regs, paddr & ~3); - s32 result = s32((regs.gpr[RT(instr)] & ~mask) | (data >> shift)); - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = result; - } + s32 result = s32((regs.Read(RT(instr)) & ~mask) | (data >> shift)); + regs.Write(RT(instr), result); } } void Interpreter::ld(u32 instr) { - s64 address = regs.gpr[RS(instr)] + (s16)instr; + s64 address = regs.Read(RS(instr)) + (s16)instr; if (check_address_error(0b111, address)) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); @@ -334,9 +304,7 @@ void Interpreter::ld(u32 instr) { regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); } else { s64 value = mem.Read(regs, paddr); - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = value; - } + regs.Write(RT(instr), value); } } @@ -346,7 +314,7 @@ void Interpreter::lld(u32 instr) { return; } - u64 address = regs.gpr[RS(instr)] + (s16)instr; + u64 address = regs.Read(RS(instr)) + (s16)instr; u32 paddr; if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { regs.cop0.HandleTLBException(address); @@ -355,9 +323,7 @@ void Interpreter::lld(u32 instr) { if (check_address_error(0b111, address)) { regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); } else { - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = mem.Read(regs, paddr); - } + regs.Write(RT(instr), mem.Read(regs, paddr)); regs.cop0.llbit = true; regs.cop0.LLAddr = paddr >> 4; } @@ -365,7 +331,7 @@ void Interpreter::lld(u32 instr) { } void Interpreter::ldl(u32 instr) { - u64 address = regs.gpr[RS(instr)] + (s16)instr; + u64 address = regs.Read(RS(instr)) + (s16)instr; u32 paddr = 0; if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { regs.cop0.HandleTLBException(address); @@ -374,15 +340,13 @@ void Interpreter::ldl(u32 instr) { s32 shift = 8 * ((address ^ 0) & 7); u64 mask = 0xFFFFFFFFFFFFFFFF << shift; u64 data = mem.Read(regs, paddr & ~7); - s64 result = (s64) ((regs.gpr[RT(instr)] & ~mask) | (data << shift)); - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = result; - } + s64 result = (s64) ((regs.Read(RT(instr)) & ~mask) | (data << shift)); + regs.Write(RT(instr), result); } } void Interpreter::ldr(u32 instr) { - u64 address = regs.gpr[RS(instr)] + (s16)instr; + u64 address = regs.Read(RS(instr)) + (s16)instr; u32 paddr; if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { regs.cop0.HandleTLBException(address); @@ -391,29 +355,25 @@ void Interpreter::ldr(u32 instr) { s32 shift = 8 * ((address ^ 7) & 7); u64 mask = 0xFFFFFFFFFFFFFFFF >> shift; u64 data = mem.Read(regs, paddr & ~7); - s64 result = (s64) ((regs.gpr[RT(instr)] & ~mask) | (data >> shift)); - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = result; - } + s64 result = (s64) ((regs.Read(RT(instr)) & ~mask) | (data >> shift)); + regs.Write(RT(instr), result); } } void Interpreter::lbu(u32 instr) { - u64 address = regs.gpr[RS(instr)] + (s16)instr; + u64 address = regs.Read(RS(instr)) + (s16)instr; u32 paddr; if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); } else { u8 value = mem.Read(regs, paddr); - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = value; - } + regs.Write(RT(instr), value); } } void Interpreter::lhu(u32 instr) { - s64 address = regs.gpr[RS(instr)] + (s16)instr; + s64 address = regs.Read(RS(instr)) + (s16)instr; if (check_address_error(0b1, address)) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); @@ -425,14 +385,12 @@ void Interpreter::lhu(u32 instr) { regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); } else { u16 value = mem.Read(regs, paddr); - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = value; - } + regs.Write(RT(instr), value); } } void Interpreter::lwu(u32 instr) { - s64 address = regs.gpr[RS(instr)] + (s16)instr; + s64 address = regs.Read(RS(instr)) + (s16)instr; if (check_address_error(0b11, address)) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); @@ -445,47 +403,45 @@ void Interpreter::lwu(u32 instr) { regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); } else { u32 value = mem.Read(regs, paddr); - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = value; - } + regs.Write(RT(instr), value); } } void Interpreter::sb(u32 instr) { - u64 address = regs.gpr[RS(instr)] + (s16)instr; + u64 address = regs.Read(RS(instr)) + (s16)instr; u32 paddr; if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); } else { - mem.Write(regs, paddr, regs.gpr[RT(instr)]); + mem.Write(regs, paddr, regs.Read(RT(instr))); } } void Interpreter::sc(u32 instr) { - u64 address = regs.gpr[RS(instr)] + (s16)instr; - - if (check_address_error(0b11, address)) { - regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC); - return; - } + u64 address = regs.Read(RS(instr)) + (s16)instr; if(regs.cop0.llbit) { regs.cop0.llbit = false; + + if (check_address_error(0b11, address)) { + regs.Write(RT(instr), 0); + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC); + return; + } + u32 paddr = 0; if(!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) { + regs.Write(RT(instr), 0); regs.cop0.HandleTLBException(address); regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); } else { - mem.Write(regs, paddr, regs.gpr[RT(instr)]); - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = 1; - } + mem.Write(regs, paddr, regs.Read(RT(instr))); + regs.Write(RT(instr), 1); } } else { - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = 0; - } + regs.Write(RT(instr), 0); } } @@ -495,47 +451,47 @@ void Interpreter::scd(u32 instr) { return; } - s64 address = regs.gpr[RS(instr)] + (s16)instr; - if (check_address_error(0b111, address)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC); - return; - } + s64 address = regs.Read(RS(instr)) + (s16)instr; if(regs.cop0.llbit) { regs.cop0.llbit = false; + + if (check_address_error(0b111, address)) { + regs.Write(RT(instr), 0); + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC); + return; + } + u32 paddr = 0; if(!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) { + regs.Write(RT(instr), 0); regs.cop0.HandleTLBException(address); regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); } else { - mem.Write(regs, paddr, regs.gpr[RT(instr)]); - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = 1; - } + mem.Write(regs, paddr, regs.Read(RT(instr))); + regs.Write(RT(instr), 1); } } else { - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = 0; - } + regs.Write(RT(instr), 0); } } void Interpreter::sh(u32 instr) { - s64 address = regs.gpr[RS(instr)] + (s16)instr; + s64 address = regs.Read(RS(instr)) + (s16)instr; u32 physical; if(!regs.cop0.MapVAddr(Cop0::STORE, address, physical)) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); } else { - mem.Write(regs, physical, regs.gpr[RT(instr)]); + mem.Write(regs, physical, regs.Read(RT(instr))); } } void Interpreter::sw(u32 instr) { s16 offset = instr; - u64 address = regs.gpr[RS(instr)] + offset; + u64 address = regs.Read(RS(instr)) + offset; if (check_address_error(0b11, address)) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC); @@ -547,12 +503,12 @@ void Interpreter::sw(u32 instr) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); } else { - mem.Write(regs, physical, regs.gpr[RT(instr)]); + mem.Write(regs, physical, regs.Read(RT(instr))); } } void Interpreter::sd(u32 instr) { - s64 address = regs.gpr[RS(instr)] + (s16)instr; + s64 address = regs.Read(RS(instr)) + (s16)instr; if (check_address_error(0b111, address)) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC); @@ -564,12 +520,12 @@ void Interpreter::sd(u32 instr) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); } else { - mem.Write(regs, physical, regs.gpr[RT(instr)]); + mem.Write(regs, physical, regs.Read(RT(instr))); } } void Interpreter::sdl(u32 instr) { - u64 address = regs.gpr[RS(instr)] + (s16)instr; + u64 address = regs.Read(RS(instr)) + (s16)instr; u32 paddr; if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) { regs.cop0.HandleTLBException(address); @@ -578,13 +534,13 @@ void Interpreter::sdl(u32 instr) { s32 shift = 8 * ((address ^ 0) & 7); u64 mask = 0xFFFFFFFFFFFFFFFF >> shift; u64 data = mem.Read(regs, paddr & ~7); - u64 rt = regs.gpr[RT(instr)]; + u64 rt = regs.Read(RT(instr)); mem.Write(regs, paddr & ~7, (data & ~mask) | (rt >> shift)); } } void Interpreter::sdr(u32 instr) { - u64 address = regs.gpr[RS(instr)] + (s16)instr; + u64 address = regs.Read(RS(instr)) + (s16)instr; u32 paddr; if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) { regs.cop0.HandleTLBException(address); @@ -593,13 +549,13 @@ void Interpreter::sdr(u32 instr) { s32 shift = 8 * ((address ^ 7) & 7); u64 mask = 0xFFFFFFFFFFFFFFFF << shift; u64 data = mem.Read(regs, paddr & ~7); - u64 rt = regs.gpr[RT(instr)]; + u64 rt = regs.Read(RT(instr)); mem.Write(regs, paddr & ~7, (data & ~mask) | (rt << shift)); } } void Interpreter::swl(u32 instr) { - u64 address = regs.gpr[RS(instr)] + (s16)instr; + u64 address = regs.Read(RS(instr)) + (s16)instr; u32 paddr; if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) { regs.cop0.HandleTLBException(address); @@ -608,13 +564,13 @@ void Interpreter::swl(u32 instr) { u32 shift = 8 * ((address ^ 0) & 3); u32 mask = 0xFFFFFFFF >> shift; u32 data = mem.Read(regs, paddr & ~3); - u32 rt = regs.gpr[RT(instr)]; + u32 rt = regs.Read(RT(instr)); mem.Write(regs, paddr & ~3, (data & ~mask) | (rt >> shift)); } } void Interpreter::swr(u32 instr) { - u64 address = regs.gpr[RS(instr)] + (s16)instr; + u64 address = regs.Read(RS(instr)) + (s16)instr; u32 paddr; if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) { regs.cop0.HandleTLBException(address); @@ -623,29 +579,23 @@ void Interpreter::swr(u32 instr) { u32 shift = 8 * ((address ^ 3) & 3); u32 mask = 0xFFFFFFFF << shift; u32 data = mem.Read(regs, paddr & ~3); - u32 rt = regs.gpr[RT(instr)]; + u32 rt = regs.Read(RT(instr)); mem.Write(regs, paddr & ~3, (data & ~mask) | (rt << shift)); } } void Interpreter::ori(u32 instr) { s64 imm = (u16)instr; - s64 result = imm | regs.gpr[RS(instr)]; - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = result; - } + s64 result = imm | regs.Read(RS(instr)); + regs.Write(RT(instr), result); } void Interpreter::or_(u32 instr) { - if (RD(instr) != 0) [[likely]] { - regs.gpr[RD(instr)] = regs.gpr[RS(instr)] | regs.gpr[RT(instr)]; - } + regs.Write(RD(instr), regs.Read(RS(instr)) | regs.Read(RT(instr))); } void Interpreter::nor(u32 instr) { - if (RD(instr) != 0) [[likely]] { - regs.gpr[RD(instr)] = ~(regs.gpr[RS(instr)] | regs.gpr[RT(instr)]); - } + regs.Write(RD(instr), ~(regs.Read(RS(instr)) | regs.Read(RT(instr)))); } void Interpreter::j(u32 instr) { @@ -656,302 +606,243 @@ void Interpreter::j(u32 instr) { } void Interpreter::jal(u32 instr) { - regs.gpr[31] = regs.nextPC; + regs.Write(31, regs.nextPC); j(instr); } void Interpreter::jalr(u32 instr) { - u64 addr = regs.gpr[RS(instr)]; + u64 addr = regs.Read(RS(instr)); + s64 currentNextPC = regs.nextPC; branch(true, addr); - if (RD(instr) != 0) [[likely]] { - regs.gpr[RD(instr)] = regs.pc + 4; - } + regs.Write(RD(instr), currentNextPC); } void Interpreter::jr(u32 instr) { - u64 address = regs.gpr[RS(instr)]; + u64 address = regs.Read(RS(instr)); branch(true, address); } void Interpreter::slti(u32 instr) { s16 imm = instr; - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = regs.gpr[RS(instr)] < imm; - } + regs.Write(RT(instr), regs.Read(RS(instr)) < imm); } void Interpreter::sltiu(u32 instr) { s16 imm = instr; - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = (u64) regs.gpr[RS(instr)] < imm; - } + regs.Write(RT(instr), regs.Read(RS(instr)) < imm); } void Interpreter::slt(u32 instr) { - if (RD(instr) != 0) [[likely]] { - regs.gpr[RD(instr)] = regs.gpr[RS(instr)] < regs.gpr[RT(instr)]; - } + regs.Write(RD(instr), regs.Read(RS(instr)) < regs.Read(RT(instr))); } void Interpreter::sltu(u32 instr) { - if (RD(instr) != 0) [[likely]] { - regs.gpr[RD(instr)] = (u64) regs.gpr[RS(instr)] < (u64) regs.gpr[RT(instr)]; - } + regs.Write(RD(instr), regs.Read(RS(instr)) < regs.Read(RT(instr))); } void Interpreter::xori(u32 instr) { s64 imm = (u16)instr; - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = regs.gpr[RS(instr)] ^ imm; - } + regs.Write(RT(instr), regs.Read(RS(instr)) ^ imm); } void Interpreter::xor_(u32 instr) { - if (RD(instr) != 0) [[likely]] { - regs.gpr[RD(instr)] = regs.gpr[RT(instr)] ^ regs.gpr[RS(instr)]; - } + regs.Write(RD(instr), regs.Read(RT(instr)) ^ regs.Read(RS(instr))); } void Interpreter::andi(u32 instr) { s64 imm = (u16)instr; - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = regs.gpr[RS(instr)] & imm; - } + regs.Write(RT(instr), regs.Read(RS(instr)) & imm); } void Interpreter::and_(u32 instr) { - if (RD(instr) != 0) [[likely]] { - regs.gpr[RD(instr)] = regs.gpr[RS(instr)] & regs.gpr[RT(instr)]; - } + regs.Write(RD(instr), regs.Read(RS(instr)) & regs.Read(RT(instr))); } void Interpreter::sll(u32 instr) { - if (RD(instr) != 0) [[likely]] { - u8 sa = ((instr >> 6) & 0x1f); - s32 result = regs.gpr[RT(instr)] << sa; - regs.gpr[RD(instr)] = (s64) result; - } + u8 sa = ((instr >> 6) & 0x1f); + s32 result = regs.Read(RT(instr)) << sa; + regs.Write(RD(instr), (s64) result); } void Interpreter::sllv(u32 instr) { - if (RD(instr) != 0) [[likely]] { - u8 sa = (regs.gpr[RS(instr)]) & 0x1F; - u32 rt = regs.gpr[RT(instr)]; - s32 result = rt << sa; - regs.gpr[RD(instr)] = (s64) result; - } + u8 sa = (regs.Read(RS(instr))) & 0x1F; + u32 rt = regs.Read(RT(instr)); + s32 result = rt << sa; + regs.Write(RD(instr), (s64) result); } void Interpreter::dsll32(u32 instr) { - if (RD(instr) != 0) [[likely]] { - u8 sa = ((instr >> 6) & 0x1f); - s64 result = regs.gpr[RT(instr)] << (sa + 32); - regs.gpr[RD(instr)] = result; - } + u8 sa = ((instr >> 6) & 0x1f); + s64 result = regs.Read(RT(instr)) << (sa + 32); + regs.Write(RD(instr), result); } void Interpreter::dsll(u32 instr) { - if (RD(instr) != 0) [[likely]] { - u8 sa = ((instr >> 6) & 0x1f); - s64 result = regs.gpr[RT(instr)] << sa; - regs.gpr[RD(instr)] = result; - } + u8 sa = ((instr >> 6) & 0x1f); + s64 result = regs.Read(RT(instr)) << sa; + regs.Write(RD(instr), result); } void Interpreter::dsllv(u32 instr) { - if (RD(instr) != 0) [[likely]] { - s64 sa = regs.gpr[RS(instr)] & 63; - s64 result = regs.gpr[RT(instr)] << sa; - regs.gpr[RD(instr)] = result; - } + s64 sa = regs.Read(RS(instr)) & 63; + s64 result = regs.Read(RT(instr)) << sa; + regs.Write(RD(instr), result); } void Interpreter::srl(u32 instr) { - if (RD(instr) != 0) [[likely]] { - u32 rt = regs.gpr[RT(instr)]; - u8 sa = ((instr >> 6) & 0x1f); - u32 result = rt >> sa; - regs.gpr[RD(instr)] = (s32) result; - } + u32 rt = regs.Read(RT(instr)); + u8 sa = ((instr >> 6) & 0x1f); + u32 result = rt >> sa; + regs.Write(RD(instr), (s32) result); } void Interpreter::srlv(u32 instr) { - if (RD(instr) != 0) [[likely]] { - u8 sa = (regs.gpr[RS(instr)] & 0x1F); - u32 rt = regs.gpr[RT(instr)]; - s32 result = rt >> sa; - regs.gpr[RD(instr)] = (s64) result; - } + u8 sa = (regs.Read(RS(instr)) & 0x1F); + u32 rt = regs.Read(RT(instr)); + s32 result = rt >> sa; + regs.Write(RD(instr), (s64) result); } void Interpreter::dsrl(u32 instr) { - if (RD(instr) != 0) [[likely]] { - u64 rt = regs.gpr[RT(instr)]; - u8 sa = ((instr >> 6) & 0x1f); - u64 result = rt >> sa; - regs.gpr[RD(instr)] = s64(result); - } + u64 rt = regs.Read(RT(instr)); + u8 sa = ((instr >> 6) & 0x1f); + u64 result = rt >> sa; + regs.Write(RD(instr), s64(result)); } void Interpreter::dsrlv(u32 instr) { - if (RD(instr) != 0) [[likely]] { - u8 amount = (regs.gpr[RS(instr)] & 63); - u64 rt = regs.gpr[RT(instr)]; - u64 result = rt >> amount; - regs.gpr[RD(instr)] = s64(result); - } + u8 amount = (regs.Read(RS(instr)) & 63); + u64 rt = regs.Read(RT(instr)); + u64 result = rt >> amount; + regs.Write(RD(instr), s64(result)); } void Interpreter::dsrl32(u32 instr) { - if (RD(instr) != 0) [[likely]] { - u64 rt = regs.gpr[RT(instr)]; - u8 sa = ((instr >> 6) & 0x1f); - u64 result = rt >> (sa + 32); - regs.gpr[RD(instr)] = s64(result); - } + u64 rt = regs.Read(RT(instr)); + u8 sa = ((instr >> 6) & 0x1f); + u64 result = rt >> (sa + 32); + regs.Write(RD(instr), s64(result)); } void Interpreter::sra(u32 instr) { - if (RD(instr) != 0) [[likely]] { - s64 rt = regs.gpr[RT(instr)]; - u8 sa = ((instr >> 6) & 0x1f); - s32 result = rt >> sa; - regs.gpr[RD(instr)] = result; - } + s64 rt = regs.Read(RT(instr)); + u8 sa = ((instr >> 6) & 0x1f); + s32 result = rt >> sa; + regs.Write(RD(instr), result); } void Interpreter::srav(u32 instr) { - if (RD(instr) != 0) [[likely]] { - s64 rs = regs.gpr[RS(instr)]; - s64 rt = regs.gpr[RT(instr)]; - u8 sa = rs & 0x1f; - s32 result = rt >> sa; - regs.gpr[RD(instr)] = result; - } + s64 rs = regs.Read(RS(instr)); + s64 rt = regs.Read(RT(instr)); + u8 sa = rs & 0x1f; + s32 result = rt >> sa; + regs.Write(RD(instr), result); } void Interpreter::dsra(u32 instr) { - if (RD(instr) != 0) [[likely]] { - s64 rt = regs.gpr[RT(instr)]; - u8 sa = ((instr >> 6) & 0x1f); - s64 result = rt >> sa; - regs.gpr[RD(instr)] = result; - } + s64 rt = regs.Read(RT(instr)); + u8 sa = ((instr >> 6) & 0x1f); + s64 result = rt >> sa; + regs.Write(RD(instr), result); } void Interpreter::dsrav(u32 instr) { - if (RD(instr) != 0) [[likely]] { - s64 rt = regs.gpr[RT(instr)]; - s64 rs = regs.gpr[RS(instr)]; - s64 sa = rs & 63; - s64 result = rt >> sa; - regs.gpr[RD(instr)] = result; - } + s64 rt = regs.Read(RT(instr)); + s64 rs = regs.Read(RS(instr)); + s64 sa = rs & 63; + s64 result = rt >> sa; + regs.Write(RD(instr), result); } void Interpreter::dsra32(u32 instr) { - if (RD(instr) != 0) [[likely]] { - s64 rt = regs.gpr[RT(instr)]; - u8 sa = ((instr >> 6) & 0x1f); - s64 result = rt >> (sa + 32); - regs.gpr[RD(instr)] = result; - } + s64 rt = regs.Read(RT(instr)); + u8 sa = ((instr >> 6) & 0x1f); + s64 result = rt >> (sa + 32); + regs.Write(RD(instr), result); } void Interpreter::dsub(u32 instr) { - s64 rt = regs.gpr[RT(instr)]; - s64 rs = regs.gpr[RS(instr)]; + s64 rt = regs.Read(RT(instr)); + s64 rs = regs.Read(RS(instr)); s64 result = rs - rt; if(check_signed_underflow(rs, rt, result)) { regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); } else { - if (RD(instr) != 0) [[likely]] { - regs.gpr[RD(instr)] = result; - } + regs.Write(RD(instr), result); } } void Interpreter::dsubu(u32 instr) { - if (RD(instr) != 0) [[likely]] { - u64 rt = regs.gpr[RT(instr)]; - u64 rs = regs.gpr[RS(instr)]; - u64 result = rs - rt; - regs.gpr[RD(instr)] = s64(result); - } + u64 rt = regs.Read(RT(instr)); + u64 rs = regs.Read(RS(instr)); + u64 result = rs - rt; + regs.Write(RD(instr), s64(result)); } void Interpreter::sub(u32 instr) { - s32 rt = regs.gpr[RT(instr)]; - s32 rs = regs.gpr[RS(instr)]; + s32 rt = regs.Read(RT(instr)); + s32 rs = regs.Read(RS(instr)); s32 result = rs - rt; if(check_signed_underflow(rs, rt, result)) { regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); } else { - if (RD(instr) != 0) [[likely]] { - regs.gpr[RD(instr)] = result; - } + regs.Write(RD(instr), result); } } void Interpreter::subu(u32 instr) { - if (RD(instr) != 0) [[likely]] { - u32 rt = regs.gpr[RT(instr)]; - u32 rs = regs.gpr[RS(instr)]; - u32 result = rs - rt; - regs.gpr[RD(instr)] = (s64) ((s32) result); - } + u32 rt = regs.Read(RT(instr)); + u32 rs = regs.Read(RS(instr)); + u32 result = rs - rt; + regs.Write(RD(instr), (s64) ((s32) result)); } void Interpreter::dmultu(u32 instr) { - u64 rt = regs.gpr[RT(instr)]; - u64 rs = regs.gpr[RS(instr)]; + u64 rt = regs.Read(RT(instr)); + u64 rs = regs.Read(RS(instr)); u128 result = (u128)rt * (u128)rs; regs.lo = (s64)(result & 0xFFFFFFFFFFFFFFFF); regs.hi = (s64)(result >> 64); } void Interpreter::dmult(u32 instr) { - s64 rt = regs.gpr[RT(instr)]; - s64 rs = regs.gpr[RS(instr)]; + s64 rt = regs.Read(RT(instr)); + s64 rs = regs.Read(RS(instr)); s128 result = (s128)rt * (s128)rs; regs.lo = result & 0xFFFFFFFFFFFFFFFF; regs.hi = result >> 64; } void Interpreter::multu(u32 instr) { - u32 rt = regs.gpr[RT(instr)]; - u32 rs = regs.gpr[RS(instr)]; + u32 rt = regs.Read(RT(instr)); + u32 rs = regs.Read(RS(instr)); u64 result = (u64)rt * (u64)rs; regs.lo = (s64)((s32)result); regs.hi = (s64)((s32)(result >> 32)); } void Interpreter::mult(u32 instr) { - s32 rt = regs.gpr[RT(instr)]; - s32 rs = regs.gpr[RS(instr)]; + s32 rt = regs.Read(RT(instr)); + s32 rs = regs.Read(RS(instr)); s64 result = (s64)rt * (s64)rs; regs.lo = (s64)((s32)result); regs.hi = (s64)((s32)(result >> 32)); } void Interpreter::mflo(u32 instr) { - if (RD(instr) != 0) [[likely]] { - regs.gpr[RD(instr)] = regs.lo; - } + regs.Write(RD(instr), regs.lo); } void Interpreter::mfhi(u32 instr) { - if (RD(instr) != 0) [[likely]] { - regs.gpr[RD(instr)] = regs.hi; - } + regs.Write(RD(instr), regs.hi); } void Interpreter::mtlo(u32 instr) { - regs.lo = regs.gpr[RS(instr)]; + regs.lo = regs.Read(RS(instr)); } void Interpreter::mthi(u32 instr) { - regs.hi = regs.gpr[RS(instr)]; + regs.hi = regs.Read(RS(instr)); } void Interpreter::trap(bool cond) { @@ -961,24 +852,20 @@ void Interpreter::trap(bool cond) { } void Interpreter::mtc2(u32 instr) { - cop2Latch = regs.gpr[RT(instr)]; + cop2Latch = regs.Read(RT(instr)); } void Interpreter::mfc2(u32 instr) { s32 value = cop2Latch; - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = value; - } + regs.Write(RT(instr), value); } void Interpreter::dmtc2(u32 instr) { - cop2Latch = regs.gpr[RT(instr)]; + cop2Latch = regs.Read(RT(instr)); } void Interpreter::dmfc2(u32 instr) { - if (RT(instr) != 0) [[likely]] { - regs.gpr[RT(instr)] = cop2Latch; - } + regs.Write(RT(instr), cop2Latch); } void Interpreter::ctc2(u32) { diff --git a/src/backend/core/mem/Flash.cpp b/src/backend/core/mem/Flash.cpp index 1e652c0f..30590496 100644 --- a/src/backend/core/mem/Flash.cpp +++ b/src/backend/core/mem/Flash.cpp @@ -8,6 +8,11 @@ Flash::Flash(mio::mmap_sink &saveData) : saveData(saveData) {} void Flash::Reset() { state = FlashState::Idle; + writeOffs = {}; + state = {}; + status = {}; + eraseOffs = {}; + writeBuf = {}; } void Flash::Load(SaveType saveType, const std::string& path) { diff --git a/src/backend/core/mmio/AI.cpp b/src/backend/core/mmio/AI.cpp index 06eac4da..dd977d3b 100644 --- a/src/backend/core/mmio/AI.cpp +++ b/src/backend/core/mmio/AI.cpp @@ -13,8 +13,8 @@ void AI::Reset() { dmaCount = 0; dmaAddrCarry = false; cycles = 0; - memset(dmaLen, 0, 2); - memset(dmaAddr, 0, 2); + dmaLen = {}; + dmaAddr = {}; dac = {44100, N64_CPU_FREQ / dac.freq, 16}; } @@ -84,7 +84,7 @@ void AI::Step(u32 cpuCycles, float volumeL, float volumeR) { if(dmaLen[0] && dmaEnable) { u32 addrHi = ((dmaAddr[0] >> 13) + dmaAddrCarry) & 0x7FF; dmaAddr[0] = (addrHi << 13) | (dmaAddr[0] & 0x1FFF); - u32 data = Util::ReadAccess(mem.mmio.rdp.rdram, dmaAddr[0] & RDRAM_DSIZE); + u32 data = mem.mmio.rdp.ReadRDRAM(dmaAddr[0]); s16 l = s16(data >> 16); s16 r = s16(data); diff --git a/src/backend/core/mmio/AI.hpp b/src/backend/core/mmio/AI.hpp index 3f522216..e441a2ed 100644 --- a/src/backend/core/mmio/AI.hpp +++ b/src/backend/core/mmio/AI.hpp @@ -1,6 +1,5 @@ #pragma once #include -#include #include namespace n64 { @@ -17,8 +16,8 @@ struct AI { u16 dacRate{}; u8 bitrate{}; int dmaCount{}; - u32 dmaLen[2]{}; - u32 dmaAddr[2]{}; + std::array dmaLen{}; + std::array dmaAddr{}; bool dmaAddrCarry{}; u32 cycles{}; AudioDevice device; diff --git a/src/backend/core/mmio/Audio.cpp b/src/backend/core/mmio/Audio.cpp index 06fff418..215ff80d 100644 --- a/src/backend/core/mmio/Audio.cpp +++ b/src/backend/core/mmio/Audio.cpp @@ -11,19 +11,13 @@ namespace n64 { void audioCallback(void* user, Uint8* stream, int length) { auto audioDevice = (AudioDevice*)user; int gotten = 0, available = 0; + if (audioDevice) { audioDevice->LockMutex(); - } - - if (audioDevice) { - available = SDL_AudioStreamAvailable(audioDevice->GetStream().get()); - } - - if (available > 0 && audioDevice) { - gotten = SDL_AudioStreamGet(audioDevice->GetStream().get(), stream, length); - } - - if (audioDevice) { + available = SDL_AudioStreamAvailable(audioDevice->GetStream()); + if (available > 0) { + gotten = SDL_AudioStreamGet(audioDevice->GetStream(), stream, length); + } audioDevice->UnlockMutex(); } @@ -37,11 +31,18 @@ void audioCallback(void* user, Uint8* stream, int length) { } } -AudioDevice::AudioDevice() : audioStream(SDL_NewAudioStream, SDL_FreeAudioStream, "audioStream", SYSTEM_SAMPLE_FORMAT, 2, AUDIO_SAMPLE_RATE, SYSTEM_SAMPLE_FORMAT, 2, AUDIO_SAMPLE_RATE), - audioStreamMutex(SDL_CreateMutex, SDL_DestroyMutex, "audioStreamMutex") { +AudioDevice::~AudioDevice() { + LockMutex(); + SDL_FreeAudioStream(GetStream()); + UnlockMutex(); + SDL_DestroyMutex(audioStreamMutex); +} + +AudioDevice::AudioDevice() : audioStream(SDL_NewAudioStream(SYSTEM_SAMPLE_FORMAT, 2, AUDIO_SAMPLE_RATE, SYSTEM_SAMPLE_FORMAT, 2, AUDIO_SAMPLE_RATE)), + audioStreamMutex(SDL_CreateMutex()) { SDL_InitSubSystem(SDL_INIT_AUDIO); - if(!audioStreamMutex.get()) { + if(!audioStreamMutex) { Util::panic("Unable to initialize audio mutex: {}", SDL_GetError()); } @@ -66,15 +67,16 @@ void AudioDevice::PushSample(float left, float volumeL, float right, float volum float adjustedR = right * volumeR; float samples[2]{ adjustedL, adjustedR }; - auto availableBytes = (float)SDL_AudioStreamAvailable(audioStream.get()); + auto availableBytes = (float)SDL_AudioStreamAvailable(audioStream); if(availableBytes <= BYTES_PER_HALF_SECOND) { - SDL_AudioStreamPut(audioStream.get(), samples, 2 * sizeof(float)); + SDL_AudioStreamPut(audioStream, samples, 2 * sizeof(float)); } } void AudioDevice::AdjustSampleRate(int sampleRate) { LockMutex(); - audioStream.Construct(SDL_NewAudioStream, SYSTEM_SAMPLE_FORMAT, 2, sampleRate, SYSTEM_SAMPLE_FORMAT, 2, AUDIO_SAMPLE_RATE); + SDL_FreeAudioStream(audioStream); + audioStream = SDL_NewAudioStream(SYSTEM_SAMPLE_FORMAT, 2, sampleRate, SYSTEM_SAMPLE_FORMAT, 2, AUDIO_SAMPLE_RATE); UnlockMutex(); } } \ No newline at end of file diff --git a/src/backend/core/mmio/Audio.hpp b/src/backend/core/mmio/Audio.hpp index 487a221c..53868557 100644 --- a/src/backend/core/mmio/Audio.hpp +++ b/src/backend/core/mmio/Audio.hpp @@ -5,24 +5,23 @@ namespace n64 { struct AudioDevice { AudioDevice(); + ~AudioDevice(); void PushSample(float, float, float, float); void AdjustSampleRate(int); void LockMutex() { - if(audioStreamMutex.get()) - SDL_LockMutex(audioStreamMutex.get()); + if(audioStreamMutex) + SDL_LockMutex(audioStreamMutex); } void UnlockMutex() { - if (audioStreamMutex.get()) - SDL_UnlockMutex(audioStreamMutex.get()); + if (audioStreamMutex) + SDL_UnlockMutex(audioStreamMutex); } - Util::AutoRelease& GetStream() { return audioStream; } + SDL_AudioStream* GetStream() { return audioStream; } private: - Util::AutoRelease audioStream; - Util::AutoRelease audioStreamMutex; + SDL_AudioStream* audioStream; + SDL_mutex* audioStreamMutex; SDL_AudioSpec audioSpec{}; SDL_AudioSpec request{}; SDL_AudioDeviceID handle{}; diff --git a/src/backend/core/mmio/Interrupt.cpp b/src/backend/core/mmio/Interrupt.cpp index ef91d770..4ea044c2 100644 --- a/src/backend/core/mmio/Interrupt.cpp +++ b/src/backend/core/mmio/Interrupt.cpp @@ -1,4 +1,3 @@ -#include #include #include diff --git a/src/backend/core/mmio/Interrupt.hpp b/src/backend/core/mmio/Interrupt.hpp deleted file mode 100644 index addfbdc7..00000000 --- a/src/backend/core/mmio/Interrupt.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include -#include - -namespace n64 { - -struct Registers; - -} \ No newline at end of file diff --git a/src/backend/core/mmio/MI.cpp b/src/backend/core/mmio/MI.cpp index 9140c17c..bd819bb5 100644 --- a/src/backend/core/mmio/MI.cpp +++ b/src/backend/core/mmio/MI.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #define MI_VERSION_REG 0x02020102 @@ -60,7 +59,7 @@ void MI::Write(u32 paddr, u32 val) { miMode |= 1 << 9; } break; - case 0x4: break; + case 0x4: case 0x8: break; case 0xC: for (int bit = 0; bit < 6; bit++) { int clearbit = bit << 1; diff --git a/src/backend/core/mmio/PI.cpp b/src/backend/core/mmio/PI.cpp index 083f1669..fb115c1d 100644 --- a/src/backend/core/mmio/PI.cpp +++ b/src/backend/core/mmio/PI.cpp @@ -14,8 +14,6 @@ void PI::Reset() { latch = 0; dramAddr = 0; cartAddr = 0; - dramAddrInternal = 0; - cartAddrInternal = 0; rdLen = 0; wrLen = 0; piBsdDom1Lat = 0; @@ -271,7 +269,7 @@ template <> void PI::BusWrite(u32 addr, u32 val) { if (val < CART_ISVIEWER_SIZE) { std::string message(val + 1, 0); std::copy(mem.isviewer.begin(), mem.isviewer.begin() + val, message.begin()); - Util::always("{}", message); + Util::print("{}", message); } else { Util::panic("ISViewer buffer size is emulated at {} bytes, but received a flush command for {} bytes!", CART_ISVIEWER_SIZE, val); } @@ -353,8 +351,8 @@ template <> void PI::BusWrite(u32 addr, u64 val) { auto PI::Read(u32 addr) const -> u32 { switch(addr) { - case 0x04600000: return dramAddr; - case 0x04600004: return cartAddr; + case 0x04600000: return dramAddr & 0x00FFFFFE; + case 0x04600004: return cartAddr & 0xFFFFFFFE; case 0x04600008: return rdLen; case 0x0460000C: return wrLen; case 0x04600010: { @@ -425,57 +423,60 @@ u32 PI::AccessTiming(u8 domain, u32 length) const { return cycles * 1.5; // Converting RCP clock speed to CPU clock speed } +// rdram -> cart +template <> void PI::DMA() { + s32 len = rdLen + 1; + Util::trace("PI DMA from RDRAM to CARTRIDGE (size: {} B, {:08X} to {:08X})", len, dramAddr, cartAddr); + + if(mem.saveType == SAVE_FLASH_1m && cartAddr >= SREGION_PI_SRAM && cartAddr < 0x08010000) { + cartAddr = SREGION_PI_SRAM | ((cartAddr & 0xFFFFF) << 1); + } + + for (int i = 0; i < len; i++) { + BusWrite(cartAddr + i, mem.mmio.rdp.ReadRDRAM(dramAddr + i)); + } + dramAddr += len; + dramAddr = (dramAddr + 7) & ~7; + cartAddr += len; + if(cartAddr & 1) cartAddr += 1; + + dmaBusy = true; + scheduler.EnqueueRelative(AccessTiming(GetDomain(cartAddr), rdLen), PI_DMA_COMPLETE); +} + +// cart -> rdram +template <> void PI::DMA() { + s32 len = wrLen + 1; + Util::trace("PI DMA from CARTRIDGE to RDRAM (size: {} B, {:08X} to {:08X})", len, cartAddr, dramAddr); + + if(mem.saveType == SAVE_FLASH_1m && cartAddr >= SREGION_PI_SRAM && cartAddr < 0x08010000) { + cartAddr = SREGION_PI_SRAM | ((cartAddr & 0xFFFFF) << 1); + } + + for(u32 i = 0; i < len; i++) { + mem.mmio.rdp.WriteRDRAM(dramAddr + i, BusRead(cartAddr + i)); + } + dramAddr += len; + dramAddr = (dramAddr + 7) & ~7; + cartAddr += len; + if(cartAddr & 1) cartAddr += 1; + + dmaBusy = true; + scheduler.EnqueueRelative(AccessTiming(GetDomain(cartAddr), len), PI_DMA_COMPLETE); +} + void PI::Write(u32 addr, u32 val) { MI& mi = mem.mmio.mi; switch(addr) { - case 0x04600000: dramAddr = val & 0xFFFFFF; break; - case 0x04600004: cartAddr = val; break; + case 0x04600000: dramAddr = val & 0x00FFFFFE; break; + case 0x04600004: cartAddr = val & 0xFFFFFFFE; break; case 0x04600008: { - u32 len = (val & 0x00FFFFFF) + 1; - cartAddrInternal = cartAddr & 0xFFFFFFFE; - dramAddrInternal = dramAddr & 0x007FFFFE; - if (dramAddrInternal & 0x7) { - len -= dramAddrInternal & 0x7; - } - rdLen = len; - - for (int i = 0; i < len; i++) { - u32 addr = BYTE_ADDRESS(dramAddrInternal + i) & RDRAM_DSIZE; - if (addr < RDRAM_SIZE) { - BusWrite(cartAddrInternal + i, mem.mmio.rdp.rdram[addr]); - } - else { - BusWrite(cartAddrInternal + i, 0); - } - } - Util::trace("PI DMA from RDRAM to CARTRIDGE (size: {} B, {:08X} to {:08X})", len, dramAddr, cartAddr); - dmaBusy = true; - toCart = true; - scheduler.EnqueueRelative(AccessTiming(GetDomain(cartAddr), len), PI_DMA_COMPLETE); + rdLen = val & 0x00FFFFFF; + DMA(); } break; case 0x0460000C: { - u32 len = (val & 0x00FFFFFF) + 1; - cartAddrInternal = cartAddr & 0xFFFFFFFE; - dramAddrInternal = dramAddr & 0x007FFFFE; - if (dramAddrInternal & 0x7) { - len -= (dramAddrInternal & 0x7); - } - wrLen = len; - - if(mem.saveType == SAVE_FLASH_1m && cartAddrInternal >= SREGION_PI_SRAM && cartAddrInternal < 0x08010000) { - cartAddrInternal = SREGION_PI_SRAM | ((cartAddrInternal & 0xFFFFF) << 1); - } - - for(u32 i = 0; i < len; i++) { - u32 addr = BYTE_ADDRESS(dramAddrInternal + i) & RDRAM_DSIZE; - if (addr < RDRAM_SIZE) { - mem.mmio.rdp.rdram[addr] = BusRead(cartAddrInternal + i); - } - } - dmaBusy = true; - Util::trace("PI DMA from CARTRIDGE to RDRAM (size: {} B, {:08X} to {:08X})", len, cartAddr, dramAddr); - toCart = false; - scheduler.EnqueueRelative(AccessTiming(GetDomain(cartAddr), len), PI_DMA_COMPLETE); + wrLen = val & 0x00FFFFFF; + DMA(); } break; case 0x04600010: if(val & 2) { diff --git a/src/backend/core/mmio/PI.hpp b/src/backend/core/mmio/PI.hpp index d1132cb0..d39b9b2e 100644 --- a/src/backend/core/mmio/PI.hpp +++ b/src/backend/core/mmio/PI.hpp @@ -1,6 +1,5 @@ #pragma once #include -#include namespace n64 { @@ -26,15 +25,18 @@ struct PI { static u8 GetDomain(u32 address); [[nodiscard]] u32 AccessTiming(u8 domain, u32 length) const; - bool dmaBusy{}, ioBusy{}, toCart{}; + bool dmaBusy{}, ioBusy{}; u32 latch{}; - u32 dramAddr{}, cartAddr{}, dramAddrInternal{}, cartAddrInternal{}; + u32 dramAddr{}, cartAddr{}; u32 rdLen{}, wrLen{}; u32 piBsdDom1Lat{}, piBsdDom2Lat{}; u32 piBsdDom1Pwd{}, piBsdDom2Pwd{}; u32 piBsdDom1Pgs{}, piBsdDom2Pgs{}; u32 piBsdDom1Rls{}, piBsdDom2Rls{}; private: + template + void DMA(); + Mem& mem; Registers& regs; }; diff --git a/src/backend/core/mmio/PIF.cpp b/src/backend/core/mmio/PIF.cpp index 7b92782c..fae33f99 100644 --- a/src/backend/core/mmio/PIF.cpp +++ b/src/backend/core/mmio/PIF.cpp @@ -10,9 +10,10 @@ namespace n64 { void PIF::Reset() { - memset(joybusDevices, 0, sizeof(JoybusDevice) * 6); - memset(bootrom, 0, PIF_BOOTROM_SIZE); - memset(ram, 0, PIF_RAM_SIZE); + movie.Reset(); + joybusDevices = {}; + bootrom = {}; + ram = {}; std::error_code error; if(mempak.is_mapped()) { mempak.sync(error); @@ -26,6 +27,7 @@ void PIF::Reset() { } mempakOpen = false; + channel = 0; } void PIF::MaybeLoadMempak() { @@ -341,202 +343,202 @@ void PIF::HLE(bool pal, CICType cicType) { Util::warn("Unknown CIC type!"); break; case CIC_NUS_6101: - regs.gpr[0] = 0x0000000000000000; - regs.gpr[1] = 0x0000000000000000; - regs.gpr[2] = 0xFFFFFFFFDF6445CCll; - regs.gpr[3] = 0xFFFFFFFFDF6445CCll; - regs.gpr[4] = 0x00000000000045CC; - regs.gpr[5] = 0x0000000073EE317A; - regs.gpr[6] = 0xFFFFFFFFA4001F0Cll; - regs.gpr[7] = 0xFFFFFFFFA4001F08ll; - regs.gpr[8] = 0x00000000000000C0; - regs.gpr[9] = 0x0000000000000000; - regs.gpr[10] = 0x0000000000000040; - regs.gpr[11] = 0xFFFFFFFFA4000040ll; - regs.gpr[12] = 0xFFFFFFFFC7601FACll; - regs.gpr[13] = 0xFFFFFFFFC7601FACll; - regs.gpr[14] = 0xFFFFFFFFB48E2ED6ll; - regs.gpr[15] = 0xFFFFFFFFBA1A7D4Bll; - regs.gpr[16] = 0x0000000000000000; - regs.gpr[17] = 0x0000000000000000; - regs.gpr[18] = 0x0000000000000000; - regs.gpr[19] = 0x0000000000000000; - regs.gpr[20] = 0x0000000000000001; - regs.gpr[21] = 0x0000000000000000; - regs.gpr[23] = 0x0000000000000001; - regs.gpr[24] = 0x0000000000000002; - regs.gpr[25] = 0xFFFFFFFF905F4718ll; - regs.gpr[26] = 0x0000000000000000; - regs.gpr[27] = 0x0000000000000000; - regs.gpr[28] = 0x0000000000000000; - regs.gpr[29] = 0xFFFFFFFFA4001FF0ll; - regs.gpr[30] = 0x0000000000000000; - regs.gpr[31] = 0xFFFFFFFFA4001550ll; + regs.Write(0, 0x0000000000000000); + regs.Write(1, 0x0000000000000000); + regs.Write(2, 0xFFFFFFFFDF6445CC); + regs.Write(3, 0xFFFFFFFFDF6445CC); + regs.Write(4, 0x00000000000045CC); + regs.Write(5, 0x0000000073EE317A); + regs.Write(6, 0xFFFFFFFFA4001F0C); + regs.Write(7, 0xFFFFFFFFA4001F08); + regs.Write(8, 0x00000000000000C0); + regs.Write(9, 0x0000000000000000); + regs.Write(10, 0x0000000000000040); + regs.Write(11, 0xFFFFFFFFA4000040); + regs.Write(12, 0xFFFFFFFFC7601FAC); + regs.Write(13, 0xFFFFFFFFC7601FAC); + regs.Write(14, 0xFFFFFFFFB48E2ED6); + regs.Write(15, 0xFFFFFFFFBA1A7D4B); + regs.Write(16, 0x0000000000000000); + regs.Write(17, 0x0000000000000000); + regs.Write(18, 0x0000000000000000); + regs.Write(19, 0x0000000000000000); + regs.Write(20, 0x0000000000000001); + regs.Write(21, 0x0000000000000000); + regs.Write(23, 0x0000000000000001); + regs.Write(24, 0x0000000000000002); + regs.Write(25, 0xFFFFFFFF905F4718); + regs.Write(26, 0x0000000000000000); + regs.Write(27, 0x0000000000000000); + regs.Write(28, 0x0000000000000000); + regs.Write(29, 0xFFFFFFFFA4001FF0); + regs.Write(30, 0x0000000000000000); + regs.Write(31, 0xFFFFFFFFA4001550); regs.lo = 0xFFFFFFFFBA1A7D4Bll; regs.hi = 0xFFFFFFFF997EC317ll; break; case CIC_NUS_7102: - regs.gpr[0] = 0x0000000000000000; - regs.gpr[1] = 0x0000000000000001; - regs.gpr[2] = 0x000000001E324416; - regs.gpr[3] = 0x000000001E324416; - regs.gpr[4] = 0x0000000000004416; - regs.gpr[5] = 0x000000000EC5D9AF; - regs.gpr[6] = 0xFFFFFFFFA4001F0Cll; - regs.gpr[7] = 0xFFFFFFFFA4001F08ll; - regs.gpr[8] = 0x00000000000000C0; - regs.gpr[9] = 0x0000000000000000; - regs.gpr[10] = 0x0000000000000040; - regs.gpr[11] = 0xFFFFFFFFA4000040ll; - regs.gpr[12] = 0x00000000495D3D7B; - regs.gpr[13] = 0xFFFFFFFF8B3DFA1Ell; - regs.gpr[14] = 0x000000004798E4D4; - regs.gpr[15] = 0xFFFFFFFFF1D30682ll; - regs.gpr[16] = 0x0000000000000000; - regs.gpr[17] = 0x0000000000000000; - regs.gpr[18] = 0x0000000000000000; - regs.gpr[19] = 0x0000000000000000; - regs.gpr[20] = 0x0000000000000000; - regs.gpr[21] = 0x0000000000000000; - regs.gpr[22] = 0x000000000000003F; - regs.gpr[23] = 0x0000000000000007; - regs.gpr[24] = 0x0000000000000000; - regs.gpr[25] = 0x0000000013D05CAB; - regs.gpr[26] = 0x0000000000000000; - regs.gpr[27] = 0x0000000000000000; - regs.gpr[28] = 0x0000000000000000; - regs.gpr[29] = 0xFFFFFFFFA4001FF0ll; - regs.gpr[30] = 0x0000000000000000; - regs.gpr[31] = 0xFFFFFFFFA4001554ll; + regs.Write(0, 0x0000000000000000); + regs.Write(1, 0x0000000000000001); + regs.Write(2, 0x000000001E324416); + regs.Write(3, 0x000000001E324416); + regs.Write(4, 0x0000000000004416); + regs.Write(5, 0x000000000EC5D9AF); + regs.Write(6, 0xFFFFFFFFA4001F0C); + regs.Write(7, 0xFFFFFFFFA4001F08); + regs.Write(8, 0x00000000000000C0); + regs.Write(9, 0x0000000000000000); + regs.Write(10, 0x0000000000000040); + regs.Write(11, 0xFFFFFFFFA4000040); + regs.Write(12, 0x00000000495D3D7B); + regs.Write(13, 0xFFFFFFFF8B3DFA1E); + regs.Write(14, 0x000000004798E4D4); + regs.Write(15, 0xFFFFFFFFF1D30682); + regs.Write(16, 0x0000000000000000); + regs.Write(17, 0x0000000000000000); + regs.Write(18, 0x0000000000000000); + regs.Write(19, 0x0000000000000000); + regs.Write(20, 0x0000000000000000); + regs.Write(21, 0x0000000000000000); + regs.Write(22, 0x000000000000003F); + regs.Write(23, 0x0000000000000007); + regs.Write(24, 0x0000000000000000); + regs.Write(25, 0x0000000013D05CAB); + regs.Write(26, 0x0000000000000000); + regs.Write(27, 0x0000000000000000); + regs.Write(28, 0x0000000000000000); + regs.Write(29, 0xFFFFFFFFA4001FF0); + regs.Write(30, 0x0000000000000000); + regs.Write(31, 0xFFFFFFFFA4001554); regs.lo = 0xFFFFFFFFF1D30682ll; regs.hi = 0x0000000010054A98; break; case CIC_NUS_6102_7101: - regs.gpr[0] = 0x0000000000000000; - regs.gpr[1] = 0x0000000000000001; - regs.gpr[2] = 0x000000000EBDA536; - regs.gpr[3] = 0x000000000EBDA536; - regs.gpr[4] = 0x000000000000A536; - regs.gpr[5] = 0xFFFFFFFFC0F1D859ll; - regs.gpr[6] = 0xFFFFFFFFA4001F0Cll; - regs.gpr[7] = 0xFFFFFFFFA4001F08ll; - regs.gpr[8] = 0x00000000000000C0; - regs.gpr[9] = 0x0000000000000000; - regs.gpr[10] = 0x0000000000000040; - regs.gpr[11] = 0xFFFFFFFFA4000040ll; - regs.gpr[12] = 0xFFFFFFFFED10D0B3ll; - regs.gpr[13] = 0x000000001402A4CC; - regs.gpr[14] = 0x000000002DE108EA; - regs.gpr[15] = 0x000000003103E121; - regs.gpr[16] = 0x0000000000000000; - regs.gpr[17] = 0x0000000000000000; - regs.gpr[18] = 0x0000000000000000; - regs.gpr[19] = 0x0000000000000000; - regs.gpr[20] = 0x0000000000000001; - regs.gpr[21] = 0x0000000000000000; - regs.gpr[23] = 0x0000000000000000; - regs.gpr[24] = 0x0000000000000000; - regs.gpr[25] = 0xFFFFFFFF9DEBB54Fll; - regs.gpr[26] = 0x0000000000000000; - regs.gpr[27] = 0x0000000000000000; - regs.gpr[28] = 0x0000000000000000; - regs.gpr[29] = 0xFFFFFFFFA4001FF0ll; - regs.gpr[30] = 0x0000000000000000; - regs.gpr[31] = 0xFFFFFFFFA4001550ll; + regs.Write(0, 0x0000000000000000); + regs.Write(1, 0x0000000000000001); + regs.Write(2, 0x000000000EBDA536); + regs.Write(3, 0x000000000EBDA536); + regs.Write(4, 0x000000000000A536); + regs.Write(5, 0xFFFFFFFFC0F1D859); + regs.Write(6, 0xFFFFFFFFA4001F0C); + regs.Write(7, 0xFFFFFFFFA4001F08); + regs.Write(8, 0x00000000000000C0); + regs.Write(9, 0x0000000000000000); + regs.Write(10, 0x0000000000000040); + regs.Write(11, 0xFFFFFFFFA4000040); + regs.Write(12, 0xFFFFFFFFED10D0B3); + regs.Write(13, 0x000000001402A4CC); + regs.Write(14, 0x000000002DE108EA); + regs.Write(15, 0x000000003103E121); + regs.Write(16, 0x0000000000000000); + regs.Write(17, 0x0000000000000000); + regs.Write(18, 0x0000000000000000); + regs.Write(19, 0x0000000000000000); + regs.Write(20, 0x0000000000000001); + regs.Write(21, 0x0000000000000000); + regs.Write(23, 0x0000000000000000); + regs.Write(24, 0x0000000000000000); + regs.Write(25, 0xFFFFFFFF9DEBB54F); + regs.Write(26, 0x0000000000000000); + regs.Write(27, 0x0000000000000000); + regs.Write(28, 0x0000000000000000); + regs.Write(29, 0xFFFFFFFFA4001FF0); + regs.Write(30, 0x0000000000000000); + regs.Write(31, 0xFFFFFFFFA4001550); regs.hi = 0x000000003FC18657; regs.lo = 0x000000003103E121; if (pal) { - regs.gpr[20] = 0x0000000000000000; - regs.gpr[23] = 0x0000000000000006; - regs.gpr[31] = 0xFFFFFFFFA4001554ll; + regs.Write(20, 0x0000000000000000); + regs.Write(23, 0x0000000000000006); + regs.Write(31, 0xFFFFFFFFA4001554); } break; case CIC_NUS_6103_7103: - regs.gpr[0] = 0x0000000000000000; - regs.gpr[1] = 0x0000000000000001; - regs.gpr[2] = 0x0000000049A5EE96; - regs.gpr[3] = 0x0000000049A5EE96; - regs.gpr[4] = 0x000000000000EE96; - regs.gpr[5] = 0xFFFFFFFFD4646273ll; - regs.gpr[6] = 0xFFFFFFFFA4001F0Cll; - regs.gpr[7] = 0xFFFFFFFFA4001F08ll; - regs.gpr[8] = 0x00000000000000C0; - regs.gpr[9] = 0x0000000000000000; - regs.gpr[10] = 0x0000000000000040; - regs.gpr[11] = 0xFFFFFFFFA4000040ll; - regs.gpr[12] = 0xFFFFFFFFCE9DFBF7ll; - regs.gpr[13] = 0xFFFFFFFFCE9DFBF7ll; - regs.gpr[14] = 0x000000001AF99984; - regs.gpr[15] = 0x0000000018B63D28; - regs.gpr[16] = 0x0000000000000000; - regs.gpr[17] = 0x0000000000000000; - regs.gpr[18] = 0x0000000000000000; - regs.gpr[19] = 0x0000000000000000; - regs.gpr[20] = 0x0000000000000001; - regs.gpr[21] = 0x0000000000000000; - regs.gpr[23] = 0x0000000000000000; - regs.gpr[24] = 0x0000000000000000; - regs.gpr[25] = 0xFFFFFFFF825B21C9ll; - regs.gpr[26] = 0x0000000000000000; - regs.gpr[27] = 0x0000000000000000; - regs.gpr[28] = 0x0000000000000000; - regs.gpr[29] = 0xFFFFFFFFA4001FF0ll; - regs.gpr[30] = 0x0000000000000000; - regs.gpr[31] = 0xFFFFFFFFA4001550ll; + regs.Write(0, 0x0000000000000000); + regs.Write(1, 0x0000000000000001); + regs.Write(2, 0x0000000049A5EE96); + regs.Write(3, 0x0000000049A5EE96); + regs.Write(4, 0x000000000000EE96); + regs.Write(5, 0xFFFFFFFFD4646273); + regs.Write(6, 0xFFFFFFFFA4001F0C); + regs.Write(7, 0xFFFFFFFFA4001F08); + regs.Write(8, 0x00000000000000C0); + regs.Write(9, 0x0000000000000000); + regs.Write(10, 0x0000000000000040); + regs.Write(11, 0xFFFFFFFFA4000040); + regs.Write(12, 0xFFFFFFFFCE9DFBF7); + regs.Write(13, 0xFFFFFFFFCE9DFBF7); + regs.Write(14, 0x000000001AF99984); + regs.Write(15, 0x0000000018B63D28); + regs.Write(16, 0x0000000000000000); + regs.Write(17, 0x0000000000000000); + regs.Write(18, 0x0000000000000000); + regs.Write(19, 0x0000000000000000); + regs.Write(20, 0x0000000000000001); + regs.Write(21, 0x0000000000000000); + regs.Write(23, 0x0000000000000000); + regs.Write(24, 0x0000000000000000); + regs.Write(25, 0xFFFFFFFF825B21C9); + regs.Write(26, 0x0000000000000000); + regs.Write(27, 0x0000000000000000); + regs.Write(28, 0x0000000000000000); + regs.Write(29, 0xFFFFFFFFA4001FF0); + regs.Write(30, 0x0000000000000000); + regs.Write(31, 0xFFFFFFFFA4001550); regs.lo = 0x0000000018B63D28; regs.hi = 0x00000000625C2BBE; if (pal) { - regs.gpr[20] = 0x0000000000000000; - regs.gpr[23] = 0x0000000000000006; - regs.gpr[31] = 0xFFFFFFFFA4001554ll; + regs.Write(20, 0x0000000000000000); + regs.Write(23, 0x0000000000000006); + regs.Write(31, 0xFFFFFFFFA4001554); } break; case CIC_NUS_6105_7105: - regs.gpr[0] = 0x0000000000000000; - regs.gpr[1] = 0x0000000000000000; - regs.gpr[2] = 0xFFFFFFFFF58B0FBFll; - regs.gpr[3] = 0xFFFFFFFFF58B0FBFll; - regs.gpr[4] = 0x0000000000000FBF; - regs.gpr[5] = 0xFFFFFFFFDECAAAD1ll; - regs.gpr[6] = 0xFFFFFFFFA4001F0Cll; - regs.gpr[7] = 0xFFFFFFFFA4001F08ll; - regs.gpr[8] = 0x00000000000000C0; - regs.gpr[9] = 0x0000000000000000; - regs.gpr[10] = 0x0000000000000040; - regs.gpr[11] = 0xFFFFFFFFA4000040ll; - regs.gpr[12] = 0xFFFFFFFF9651F81Ell; - regs.gpr[13] = 0x000000002D42AAC5; - regs.gpr[14] = 0x00000000489B52CF; - regs.gpr[15] = 0x0000000056584D60; - regs.gpr[16] = 0x0000000000000000; - regs.gpr[17] = 0x0000000000000000; - regs.gpr[18] = 0x0000000000000000; - regs.gpr[19] = 0x0000000000000000; - regs.gpr[20] = 0x0000000000000001; - regs.gpr[21] = 0x0000000000000000; - regs.gpr[23] = 0x0000000000000000; - regs.gpr[24] = 0x0000000000000002; - regs.gpr[25] = 0xFFFFFFFFCDCE565Fll; - regs.gpr[26] = 0x0000000000000000; - regs.gpr[27] = 0x0000000000000000; - regs.gpr[28] = 0x0000000000000000; - regs.gpr[29] = 0xFFFFFFFFA4001FF0ll; - regs.gpr[30] = 0x0000000000000000; - regs.gpr[31] = 0xFFFFFFFFA4001550ll; + regs.Write(0, 0x0000000000000000); + regs.Write(1, 0x0000000000000000); + regs.Write(2, 0xFFFFFFFFF58B0FBF); + regs.Write(3, 0xFFFFFFFFF58B0FBF); + regs.Write(4, 0x0000000000000FBF); + regs.Write(5, 0xFFFFFFFFDECAAAD1); + regs.Write(6, 0xFFFFFFFFA4001F0C); + regs.Write(7, 0xFFFFFFFFA4001F08); + regs.Write(8, 0x00000000000000C0); + regs.Write(9, 0x0000000000000000); + regs.Write(10, 0x0000000000000040); + regs.Write(11, 0xFFFFFFFFA4000040); + regs.Write(12, 0xFFFFFFFF9651F81E); + regs.Write(13, 0x000000002D42AAC5); + regs.Write(14, 0x00000000489B52CF); + regs.Write(15, 0x0000000056584D60); + regs.Write(16, 0x0000000000000000); + regs.Write(17, 0x0000000000000000); + regs.Write(18, 0x0000000000000000); + regs.Write(19, 0x0000000000000000); + regs.Write(20, 0x0000000000000001); + regs.Write(21, 0x0000000000000000); + regs.Write(23, 0x0000000000000000); + regs.Write(24, 0x0000000000000002); + regs.Write(25, 0xFFFFFFFFCDCE565F); + regs.Write(26, 0x0000000000000000); + regs.Write(27, 0x0000000000000000); + regs.Write(28, 0x0000000000000000); + regs.Write(29, 0xFFFFFFFFA4001FF0); + regs.Write(30, 0x0000000000000000); + regs.Write(31, 0xFFFFFFFFA4001550); regs.lo = 0x0000000056584D60; regs.hi = 0x000000004BE35D1F; if (pal) { - regs.gpr[20] = 0x0000000000000000; - regs.gpr[23] = 0x0000000000000006; - regs.gpr[31] = 0xFFFFFFFFA4001554ll; + regs.Write(20, 0x0000000000000000); + regs.Write(23, 0x0000000000000006); + regs.Write(31, 0xFFFFFFFFA4001554); } mem.Write(regs, IMEM_REGION_START + 0x00, 0x3C0DBFC0); @@ -549,49 +551,49 @@ void PIF::HLE(bool pal, CICType cicType) { mem.Write(regs, IMEM_REGION_START + 0x1C, 0x3C0BB000); break; case CIC_NUS_6106_7106: - regs.gpr[0] = 0x0000000000000000; - regs.gpr[1] = 0x0000000000000000; - regs.gpr[2] = 0xFFFFFFFFA95930A4ll; - regs.gpr[3] = 0xFFFFFFFFA95930A4ll; - regs.gpr[4] = 0x00000000000030A4; - regs.gpr[5] = 0xFFFFFFFFB04DC903ll; - regs.gpr[6] = 0xFFFFFFFFA4001F0Cll; - regs.gpr[7] = 0xFFFFFFFFA4001F08ll; - regs.gpr[8] = 0x00000000000000C0; - regs.gpr[9] = 0x0000000000000000; - regs.gpr[10] = 0x0000000000000040; - regs.gpr[11] = 0xFFFFFFFFA4000040ll; - regs.gpr[12] = 0xFFFFFFFFBCB59510ll; - regs.gpr[13] = 0xFFFFFFFFBCB59510ll; - regs.gpr[14] = 0x000000000CF85C13; - regs.gpr[15] = 0x000000007A3C07F4; - regs.gpr[16] = 0x0000000000000000; - regs.gpr[17] = 0x0000000000000000; - regs.gpr[18] = 0x0000000000000000; - regs.gpr[19] = 0x0000000000000000; - regs.gpr[20] = 0x0000000000000001; - regs.gpr[21] = 0x0000000000000000; - regs.gpr[23] = 0x0000000000000000; - regs.gpr[24] = 0x0000000000000002; - regs.gpr[25] = 0x00000000465E3F72; - regs.gpr[26] = 0x0000000000000000; - regs.gpr[27] = 0x0000000000000000; - regs.gpr[28] = 0x0000000000000000; - regs.gpr[29] = 0xFFFFFFFFA4001FF0ll; - regs.gpr[30] = 0x0000000000000000; - regs.gpr[31] = 0xFFFFFFFFA4001550ll; + regs.Write(0, 0x0000000000000000); + regs.Write(1, 0x0000000000000000); + regs.Write(2, 0xFFFFFFFFA95930A4); + regs.Write(3, 0xFFFFFFFFA95930A4); + regs.Write(4, 0x00000000000030A4); + regs.Write(5, 0xFFFFFFFFB04DC903); + regs.Write(6, 0xFFFFFFFFA4001F0C); + regs.Write(7, 0xFFFFFFFFA4001F08); + regs.Write(8, 0x00000000000000C0); + regs.Write(9, 0x0000000000000000); + regs.Write(10, 0x0000000000000040); + regs.Write(11, 0xFFFFFFFFA4000040); + regs.Write(12, 0xFFFFFFFFBCB59510); + regs.Write(13, 0xFFFFFFFFBCB59510); + regs.Write(14, 0x000000000CF85C13); + regs.Write(15, 0x000000007A3C07F4); + regs.Write(16, 0x0000000000000000); + regs.Write(17, 0x0000000000000000); + regs.Write(18, 0x0000000000000000); + regs.Write(19, 0x0000000000000000); + regs.Write(20, 0x0000000000000001); + regs.Write(21, 0x0000000000000000); + regs.Write(23, 0x0000000000000000); + regs.Write(24, 0x0000000000000002); + regs.Write(25, 0x00000000465E3F72); + regs.Write(26, 0x0000000000000000); + regs.Write(27, 0x0000000000000000); + regs.Write(28, 0x0000000000000000); + regs.Write(29, 0xFFFFFFFFA4001FF0); + regs.Write(30, 0x0000000000000000); + regs.Write(31, 0xFFFFFFFFA4001550); regs.lo = 0x000000007A3C07F4; regs.hi = 0x0000000023953898; if (pal) { - regs.gpr[20] = 0x0000000000000000; - regs.gpr[23] = 0x0000000000000006; - regs.gpr[31] = 0xFFFFFFFFA4001554ll; + regs.Write(20, 0x0000000000000000); + regs.Write(23, 0x0000000000000006); + regs.Write(31, 0xFFFFFFFFA4001554); } break; } - regs.gpr[22] = (cicSeeds[cicType] >> 8) & 0xFF; + regs.Write(22, (cicSeeds[cicType] >> 8) & 0xFF); regs.cop0.Reset(); mem.Write(regs, 0x04300004, 0x01010101); std::copy(mem.rom.cart.begin(), mem.rom.cart.begin() + 0x1000, mem.mmio.rsp.dmem.begin()); @@ -630,11 +632,11 @@ std::vector PIF::Serialize() { sizeof(int)); u32 index = 0; - memcpy(res.data() + index, joybusDevices, 6*sizeof(JoybusDevice)); + memcpy(res.data() + index, joybusDevices.data(), 6*sizeof(JoybusDevice)); index += 6*sizeof(JoybusDevice); - memcpy(res.data() + index, bootrom, PIF_BOOTROM_SIZE); + memcpy(res.data() + index, bootrom.data(), PIF_BOOTROM_SIZE); index += PIF_BOOTROM_SIZE; - memcpy(res.data() + index, ram, PIF_RAM_SIZE); + memcpy(res.data() + index, ram.data(), PIF_RAM_SIZE); index += PIF_RAM_SIZE; memcpy(res.data() + index, mempak.data(), mempak.size()); index += mempak.size(); diff --git a/src/backend/core/mmio/PIF.hpp b/src/backend/core/mmio/PIF.hpp index 5945c274..0833e8bc 100644 --- a/src/backend/core/mmio/PIF.hpp +++ b/src/backend/core/mmio/PIF.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "MupenMovie.hpp" namespace fs = std::filesystem; @@ -178,8 +179,9 @@ struct PIF { } bool mempakOpen = false; - JoybusDevice joybusDevices[6]{}; - u8 bootrom[PIF_BOOTROM_SIZE]{}, ram[PIF_RAM_SIZE]{}; + std::array joybusDevices{}; + std::array bootrom{}; + std::array ram{}; mio::mmap_sink mempak, eeprom; int channel = 0; std::string mempakPath{}, eepromPath{}; diff --git a/src/backend/core/mmio/PIF/MupenMovie.cpp b/src/backend/core/mmio/PIF/MupenMovie.cpp index 2ce74620..fa9ce1ae 100644 --- a/src/backend/core/mmio/PIF/MupenMovie.cpp +++ b/src/backend/core/mmio/PIF/MupenMovie.cpp @@ -35,7 +35,7 @@ bool MupenMovie::Load(const fs::path &path) { return false; } - memcpy(&loadedTasMovieHeader, loadedTasMovie.data(), loadedTasMovie.size()); + memcpy(&loadedTasMovieHeader, loadedTasMovie.data(), sizeof(TASMovieHeader)); if (loadedTasMovieHeader.signature[0] != 0x4D || loadedTasMovieHeader.signature[1] != 0x36 || loadedTasMovieHeader.signature[2] != 0x34 || loadedTasMovieHeader.signature[3] != 0x1A) { Util::error("Failed to load movie: incorrect signature. Are you sure this is a valid movie?"); @@ -71,6 +71,12 @@ MupenMovie::MupenMovie(const fs::path &path) { } } +void MupenMovie::Reset() { + if(!IsLoaded()) return; + + loadedTasMovieIndex = sizeof(TASMovieHeader) - 4; // skip header +} + FORCE_INLINE void LogController(const n64::Controller& controller) { Util::debug("c_right: {}", controller.cRight); Util::debug("c_left: {}", controller.cLeft); diff --git a/src/backend/core/mmio/PIF/MupenMovie.hpp b/src/backend/core/mmio/PIF/MupenMovie.hpp index acdc7b60..9cbcc9b5 100644 --- a/src/backend/core/mmio/PIF/MupenMovie.hpp +++ b/src/backend/core/mmio/PIF/MupenMovie.hpp @@ -49,11 +49,10 @@ struct MupenMovie { MupenMovie() = default; MupenMovie(const fs::path&); bool Load(const fs::path&); + void Reset(); n64::Controller NextInputs(); bool IsLoaded() const { return !loadedTasMovie.empty(); } private: - std::string filename = ""; - std::string game = ""; std::vector loadedTasMovie = {}; TASMovieHeader loadedTasMovieHeader = {}; uint32_t loadedTasMovieIndex = 0; diff --git a/src/backend/core/mmio/SI.cpp b/src/backend/core/mmio/SI.cpp index db8d9a15..8ad33b9b 100644 --- a/src/backend/core/mmio/SI.cpp +++ b/src/backend/core/mmio/SI.cpp @@ -11,6 +11,7 @@ void SI::Reset() { status.raw = 0; dramAddr = 0; pifAddr = 0; + toDram = false; pif.Reset(); } @@ -32,21 +33,27 @@ auto SI::Read(u32 addr) const -> u32 { } } +// pif -> rdram +template <> void SI::DMA() { + pif.ProcessCommands(mem); + for(int i = 0; i < 64; i++) { + mem.mmio.rdp.WriteRDRAM(dramAddr + i, pif.Read(pifAddr + i)); + } + Util::trace("SI DMA from PIF RAM to RDRAM ({:08X} to {:08X})", pifAddr, dramAddr); +} + +// rdram -> pif +template <> void SI::DMA() { + for(int i = 0; i < 64; i++) { + pif.Write(pifAddr + i, mem.mmio.rdp.ReadRDRAM(dramAddr + i)); + } + Util::trace("SI DMA from RDRAM to PIF RAM ({:08X} to {:08X})", dramAddr, pifAddr); +} + void SI::DMA() { status.dmaBusy = false; - if (toDram) { - pif.ProcessCommands(mem); - for(int i = 0; i < 64; i++) { - mem.mmio.rdp.rdram[BYTE_ADDRESS(dramAddr + i)] = pif.Read(pifAddr + i); - } - Util::trace("SI DMA from PIF RAM to RDRAM ({:08X} to {:08X})", pifAddr, dramAddr); - } else { - for(int i = 0; i < 64; i++) { - pif.Write(pifAddr + i, mem.mmio.rdp.rdram[BYTE_ADDRESS(dramAddr + i)]); - } - Util::trace("SI DMA from RDRAM to PIF RAM ({:08X} to {:08X})", dramAddr, pifAddr); - pif.ProcessCommands(mem); - } + if (toDram) DMA(); + else DMA(); mem.mmio.mi.InterruptRaise(MI::Interrupt::SI); } @@ -74,4 +81,4 @@ void SI::Write(u32 addr, u32 val) { Util::panic("Unhandled SI[{:08X}] write ({:08X})", addr, val); } } -} \ No newline at end of file +} diff --git a/src/backend/core/mmio/SI.hpp b/src/backend/core/mmio/SI.hpp index e58f25bb..d0d82a23 100644 --- a/src/backend/core/mmio/SI.hpp +++ b/src/backend/core/mmio/SI.hpp @@ -1,6 +1,5 @@ #pragma once #include -#include #include #include @@ -30,6 +29,8 @@ struct SI { auto Read(u32) const -> u32; void Write(u32, u32); + template + void DMA(); void DMA(); PIF pif; private: diff --git a/src/backend/core/mmio/VI.cpp b/src/backend/core/mmio/VI.cpp index f1230ac2..47dc3b60 100644 --- a/src/backend/core/mmio/VI.cpp +++ b/src/backend/core/mmio/VI.cpp @@ -2,7 +2,6 @@ #include #include #include -#include namespace n64 { VI::VI (Mem& mem, Registers& regs) : mem(mem), regs(regs) { @@ -20,6 +19,11 @@ void VI::Reset() { numHalflines = 262; numFields = 1; cyclesPerHalfline = 1000; + xscale = {}, yscale = {}; + hsyncLeap = {}, burst = {}, vburst = {}; + hstart = {}, vstart = {}; + isPal = false; + swaps = {}; } u32 VI::Read(u32 paddr) const { diff --git a/src/backend/core/registers/Cop0.cpp b/src/backend/core/registers/Cop0.cpp index c20f2cb1..5023da95 100644 --- a/src/backend/core/registers/Cop0.cpp +++ b/src/backend/core/registers/Cop0.cpp @@ -20,6 +20,29 @@ void Cop0::Reset() { wired = 0; index.raw = 63; badVaddr = 0xFFFFFFFFFFFFFFFF; + + kernelMode = {true}; + supervisorMode = {false}; + userMode = {false}; + is64BitAddressing = {false}; + llbit = {}; + + pageMask = {}; + entryHi = {}; + entryLo0 = {}, entryLo1 = {}; + context = {}; + wired = {}, r7 = {}; + count = {}; + compare = {}; + LLAddr = {}, WatchLo = {}, WatchHi = {}; + xcontext = {}; + r21 = {}, r22 = {}, r23 = {}, r24 = {}, r25 = {}; + ParityError = {}, CacheError = {}, TagLo = {}, TagHi = {}; + ErrorEPC = {}; + r31 = {}; + memset(tlb, 0, sizeof(TLBEntry)*32); + tlbError = NONE; + openbus = {}; } u32 Cop0::GetReg32(u8 addr) { diff --git a/src/backend/core/registers/Cop1.cpp b/src/backend/core/registers/Cop1.cpp index 5e56e937..89ffc166 100644 --- a/src/backend/core/registers/Cop1.cpp +++ b/src/backend/core/registers/Cop1.cpp @@ -43,10 +43,10 @@ void Cop1::decodeInterp(Interpreter &cpu, u32 instr) { case 0x07: unimplemented(); break; case 0x08: switch(mask_branch) { - case 0: CheckFPUUsable(); cpu.b(instr, !fcr31.compare); break; - case 1: CheckFPUUsable(); cpu.b(instr, fcr31.compare); break; - case 2: CheckFPUUsable(); cpu.bl(instr, !fcr31.compare); break; - case 3: CheckFPUUsable(); cpu.bl(instr, fcr31.compare); break; + case 0: if(!CheckFPUUsable()) return; cpu.b(instr, !fcr31.compare); break; + case 1: if(!CheckFPUUsable()) return; cpu.b(instr, fcr31.compare); break; + case 2: if(!CheckFPUUsable()) return; cpu.bl(instr, !fcr31.compare); break; + case 3: if(!CheckFPUUsable()) return; cpu.bl(instr, fcr31.compare); break; default: Util::panic("Undefined BC COP1 {:02X}", mask_branch); } break; diff --git a/src/backend/core/registers/Cop1.hpp b/src/backend/core/registers/Cop1.hpp index 88c56034..f5ef8c6e 100644 --- a/src/backend/core/registers/Cop1.hpp +++ b/src/backend/core/registers/Cop1.hpp @@ -9,46 +9,78 @@ union FCR31 { FCR31() = default; struct { unsigned rounding_mode:2; - unsigned flag_inexact_operation:1; - unsigned flag_underflow:1; - unsigned flag_overflow:1; - unsigned flag_division_by_zero:1; - unsigned flag_invalid_operation:1; - unsigned enable_inexact_operation:1; - unsigned enable_underflow:1; - unsigned enable_overflow:1; - unsigned enable_division_by_zero:1; - unsigned enable_invalid_operation:1; - unsigned cause_inexact_operation:1; - unsigned cause_underflow:1; - unsigned cause_overflow:1; - unsigned cause_division_by_zero:1; - unsigned cause_invalid_operation:1; - unsigned cause_unimplemented_operation:1; + struct { + unsigned inexact_operation:1; + unsigned underflow:1; + unsigned overflow:1; + unsigned division_by_zero:1; + unsigned invalid_operation:1; + } flag; + struct { + unsigned inexact_operation:1; + unsigned underflow:1; + unsigned overflow:1; + unsigned division_by_zero:1; + unsigned invalid_operation:1; + } enable; + struct { + unsigned inexact_operation:1; + unsigned underflow:1; + unsigned overflow:1; + unsigned division_by_zero:1; + unsigned invalid_operation:1; + unsigned unimplemented_operation:1; + } cause; unsigned:5; unsigned compare:1; unsigned fs:1; unsigned:7; } __attribute__((__packed__)); - struct { - unsigned:2; - unsigned flag:5; - unsigned enable:5; - unsigned cause:6; - unsigned:14; - } __attribute__((__packed__)); - [[nodiscard]] u32 read() const { - return (fs << 24) | (compare << 23) | (cause << 12) | (enable << 7) | (flag << 2) | rounding_mode; + u32 ret = 0; + ret |= (u32(fs) << 24); + ret |= (u32(compare) << 23); + ret |= (u32(cause.unimplemented_operation) << 17); + ret |= (u32(cause.invalid_operation) << 16); + ret |= (u32(cause.division_by_zero) << 15); + ret |= (u32(cause.overflow) << 14); + ret |= (u32(cause.underflow) << 13); + ret |= (u32(cause.inexact_operation) << 12); + ret |= (u32(enable.invalid_operation) << 11); + ret |= (u32(enable.division_by_zero) << 10); + ret |= (u32(enable.overflow) << 9); + ret |= (u32(enable.underflow) << 8); + ret |= (u32(enable.inexact_operation) << 7); + ret |= (u32(flag.invalid_operation) << 6); + ret |= (u32(flag.division_by_zero) << 5); + ret |= (u32(flag.overflow) << 4); + ret |= (u32(flag.underflow) << 3); + ret |= (u32(flag.inexact_operation) << 2); + ret |= (u32(rounding_mode) & 3); + + return ret; } void write(u32 val) { - fs = (val & 0x01000000) >> 24; - compare = (val & 0x00800000) >> 23; - cause = (val & 0x0003f000) >> 12; - enable = (val & 0x00000f80) >> 7; - flag = (val & 0x0000007c) >> 2; + fs = val >> 24; + compare = val >> 23; + cause.unimplemented_operation = val >> 17; + cause.invalid_operation = val >> 16; + cause.division_by_zero = val >> 15; + cause.overflow = val >> 14; + cause.underflow = val >> 13; + cause.inexact_operation = val >> 12; + enable.invalid_operation = val >> 11; + enable.division_by_zero = val >> 10; + enable.overflow = val >> 9; + enable.underflow = val >> 8; + enable.inexact_operation = val >> 7; + flag.invalid_operation = val >> 6; + flag.division_by_zero = val >> 5; + flag.overflow = val >> 4; + flag.underflow = val >> 3; + flag.inexact_operation = val >> 2; rounding_mode = val & 3; } }; @@ -89,38 +121,50 @@ struct JIT; struct Registers; struct Cop1 { -#define CheckFPUUsable_PreserveCause() do { if(!regs.cop0.status.cu1) { regs.cop0.FireException(ExceptionCode::CoprocessorUnusable, 1, regs.oldPC); return; } } while(0) -#define CheckFPUUsable() do { CheckFPUUsable_PreserveCause(); fcr31.cause = 0; } while(0) - Cop1(Registers&); + explicit Cop1(Registers&); u32 fcr0{}; FCR31 fcr31{}; FloatingPointReg fgr[32]{}; + void Reset(); template // either JIT or Interpreter void decode(T&, u32); friend struct Interpreter; - bool FireException(); + template + bool CheckFPUUsable(); + template + bool CheckResult(T&); + template + bool CheckArg(T&); + template + bool CheckArgs(T&, T&); + template + bool isqnan(T); + + template + bool XORDERED(T fs, T ft); template - void SetCauseOnResult(T& d); + bool CheckCVTArg(float &f); template - void SetCauseByArg(T f); - template - void SetCauseByArgLCVT(T f); - template - void SetCauseByArgWCVT(T f); - void SetCauseRaised(int); - void SetCauseRaisedCVT(int); + bool CheckCVTArg(double &f); + + template + bool TestExceptions(); void SetCauseUnimplemented(); - void SetCauseUnderflow(); - void SetCauseInexact(); - void SetCauseDivisionByZero(); - void SetCauseOverflow(); - void SetCauseInvalid(); + bool SetCauseUnderflow(); + bool SetCauseInexact(); + bool SetCauseDivisionByZero(); + bool SetCauseOverflow(); + bool SetCauseInvalid(); private: template - auto FGR(Cop0Status&, u32) -> T&; + auto FGR_T(Cop0Status&, u32) -> T&; + template + auto FGR_S(Cop0Status&, u32) -> T&; + template + auto FGR_D(Cop0Status&, u32) -> T&; void decodeInterp(Interpreter&, u32); void decodeJIT(JIT&, u32); void absd(u32 instr); @@ -133,7 +177,7 @@ private: void ceilws(u32 instr); void ceilld(u32 instr); void ceilwd(u32 instr); - void cfc1(u32 instr) const; + void cfc1(u32 instr); void ctc1(u32 instr); void unimplemented(); void roundls(u32 instr); diff --git a/src/backend/core/registers/Registers.cpp b/src/backend/core/registers/Registers.cpp index 60e8e064..ea168393 100644 --- a/src/backend/core/registers/Registers.cpp +++ b/src/backend/core/registers/Registers.cpp @@ -11,6 +11,12 @@ void Registers::Reset() { delaySlot = false; prevDelaySlot = false; memset(gpr, 0, 32*sizeof(s64)); + + cop0.Reset(); + cop1.Reset(); + + steps = 0; + extraCycles = 0; } void Registers::SetPC64(s64 val) { @@ -24,4 +30,80 @@ void Registers::SetPC32(s32 val) { pc = s64(val); nextPC = pc + 4; } + +template <> u64 Registers::Read(size_t idx) { + return idx == 0 ? 0 : gpr[idx]; +} + +template <> s64 Registers::Read(size_t idx) { + return s64(Read(idx)); +} + +template <> u32 Registers::Read(size_t idx) { + return idx == 0 ? 0 : gpr[idx]; +} + +template <> s32 Registers::Read(size_t idx) { + return s32(Read(idx)); +} + +template <> u16 Registers::Read(size_t idx) { + return idx == 0 ? 0 : gpr[idx]; +} + +template <> s16 Registers::Read(size_t idx) { + return s16(Read(idx)); +} + +template <> u8 Registers::Read(size_t idx) { + return idx == 0 ? 0 : gpr[idx]; +} + +template <> s8 Registers::Read(size_t idx) { + return s8(Read(idx)); +} + +template <> void Registers::Write(size_t idx, bool v) { + if(idx == 0) return; + gpr[idx] = v; +} + +template <> void Registers::Write(size_t idx, u64 v) { + if(idx == 0) return; + gpr[idx] = v; +} + +template <> void Registers::Write(size_t idx, s64 v) { + Write(idx, v); +} + +template <> void Registers::Write(size_t idx, u32 v) { + if(idx == 0) return; + gpr[idx] = (u32)v; +} + +template <> void Registers::Write(size_t idx, s32 v) { + if(idx == 0) return; + gpr[idx] = v; +} + +template <> void Registers::Write(size_t idx, u16 v) { + if(idx == 0) return; + gpr[idx] = (u16)v; +} + +template <> void Registers::Write(size_t idx, s16 v) { + if(idx == 0) return; + gpr[idx] = v; +} + +template <> void Registers::Write(size_t idx, u8 v) { + if(idx == 0) return; + gpr[idx] = (u8)v; +} + +template <> void Registers::Write(size_t idx, s8 v) { + if(idx == 0) return; + gpr[idx] = v; +} } diff --git a/src/backend/core/registers/Registers.hpp b/src/backend/core/registers/Registers.hpp index 0921b61b..c15701a9 100644 --- a/src/backend/core/registers/Registers.hpp +++ b/src/backend/core/registers/Registers.hpp @@ -17,7 +17,6 @@ struct Registers { return IsRegConstant(first) && IsRegConstant(second); } - s64 gpr[32]{}; bool gprIsConstant[32]{}; bool loIsConstant = false, hiIsConstant = false; Cop0 cop0; @@ -37,5 +36,12 @@ struct Registers { extraCycles = 0; return ret; } + + template + T Read(size_t); + template + void Write(size_t, T); +private: + s64 gpr[32]{}; }; } diff --git a/src/backend/core/registers/cop/cop0instructions.cpp b/src/backend/core/registers/cop/cop0instructions.cpp index 2bc1da5d..ee0fb578 100644 --- a/src/backend/core/registers/cop/cop0instructions.cpp +++ b/src/backend/core/registers/cop/cop0instructions.cpp @@ -4,19 +4,19 @@ namespace n64 { void Cop0::mtc0(u32 instr) { - SetReg32(RD(instr), regs.gpr[RT(instr)]); + SetReg32(RD(instr), regs.Read(RT(instr))); } void Cop0::dmtc0(u32 instr) { - SetReg64(RD(instr), regs.gpr[RT(instr)]); + SetReg64(RD(instr), regs.Read(RT(instr))); } void Cop0::mfc0(u32 instr) { - regs.gpr[RT(instr)] = s32(GetReg32(RD(instr))); + regs.Write(RT(instr), s32(GetReg32(RD(instr)))); } void Cop0::dmfc0(u32 instr) const { - regs.gpr[RT(instr)] = s64(GetReg64(RD(instr))); + regs.Write(RT(instr), s64(GetReg64(RD(instr)))); } void Cop0::eret() { diff --git a/src/backend/core/registers/cop/cop1instructions.cpp b/src/backend/core/registers/cop/cop1instructions.cpp index feccac3a..bbe584d3 100644 --- a/src/backend/core/registers/cop/cop1instructions.cpp +++ b/src/backend/core/registers/cop/cop1instructions.cpp @@ -1,40 +1,30 @@ #include #include #include -#include #include #include +#include namespace n64 { -template<> auto Cop1::FGR(Cop0Status& status, u32 index) -> s32& { +template<> auto Cop1::FGR_T(Cop0Status& status, u32 index) -> s32& { if (status.fr) { return fgr[index].int32; + } else if (index & 1) { + return fgr[index & ~1].int32h; } else { - if (index & 1) { - return fgr[index & ~1].int32h; - } else { - return fgr[index].int32; - } + return fgr[index & ~1].int32; } } -template<> auto Cop1::FGR(Cop0Status& status, u32 index) -> u32& { - return (u32&)FGR(status, index); +template<> auto Cop1::FGR_T(Cop0Status& status, u32 index) -> u32& { + return (u32&)FGR_T(status, index); } -template<> auto Cop1::FGR(Cop0Status& status, u32 index) -> float& { - if (status.fr) { - return fgr[index].float32; - } else { - if (index & 1) { - return fgr[index & ~1].float32h; - } else { - return fgr[index].float32; - } - } +template<> auto Cop1::FGR_T(Cop0Status&, u32 index) -> float& { + return fgr[index].float32; } -template<> auto Cop1::FGR(Cop0Status& status, u32 index) -> s64& { +template<> auto Cop1::FGR_T(Cop0Status& status, u32 index) -> s64& { if (status.fr) { return fgr[index].int64; } else { @@ -42,11 +32,43 @@ template<> auto Cop1::FGR(Cop0Status& status, u32 index) -> s64& { } } -template<> auto Cop1::FGR(Cop0Status& status, u32 index) -> u64& { - return (u64&)FGR(status, index); +template<> auto Cop1::FGR_T(Cop0Status& status, u32 index) -> u64& { + return (u64&)FGR_T(status, index); } -template<> auto Cop1::FGR(Cop0Status& status, u32 index) -> double& { +template<> auto Cop1::FGR_T(Cop0Status&, u32 index) -> double& { + return fgr[index].float64; +} + +template<> auto Cop1::FGR_S(Cop0Status& status, u32 index) -> s32& { + if (status.fr) { + return fgr[index].int32; + } else { + return fgr[index & ~1].int32; + } +} + +template<> auto Cop1::FGR_S(Cop0Status& status, u32 index) -> u32& { + return (u32&)FGR_S(status, index); +} + +template<> auto Cop1::FGR_S(Cop0Status& status, u32 index) -> float& { + if (status.fr) { + return fgr[index].float32; + } else { + return fgr[index & ~1].float32; + } +} + +template<> auto Cop1::FGR_S(Cop0Status& status, u32 index) -> s64& { + return FGR_T(status, index); +} + +template<> auto Cop1::FGR_S(Cop0Status& status, u32 index) -> u64& { + return (u64&)FGR_S(status, index); +} + +template<> auto Cop1::FGR_S(Cop0Status& status, u32 index) -> double& { if (status.fr) { return fgr[index].float64; } else { @@ -54,367 +76,392 @@ template<> auto Cop1::FGR(Cop0Status& status, u32 index) -> double& { } } -bool Cop1::FireException() { - u32 enable = fcr31.enable | (1 << 5); - if(fcr31.cause & enable) { +template<> auto Cop1::FGR_D(Cop0Status&, u32 index) -> s32& { + fgr[index].int32h = 0; + return fgr[index].int32; +} + +template<> auto Cop1::FGR_D(Cop0Status& status, u32 index) -> u32& { + return (u32&)FGR_D(status, index); +} + +template<> auto Cop1::FGR_D(Cop0Status&, u32 index) -> float& { + fgr[index].float32h = 0; + return fgr[index].float32; +} + +template<> auto Cop1::FGR_D(Cop0Status&, u32 index) -> s64& { + return fgr[index].int64; +} + +template<> auto Cop1::FGR_D(Cop0Status& status, u32 index) -> u64& { + return (u64&)FGR_D(status, index); +} + +template<> auto Cop1::FGR_D(Cop0Status& status, u32 index) -> double& { + return FGR_T(status, index); +} + +u32 floatIntegerCast(float f) { + u32 v; + memcpy(&v, &f, 4); + return v; +} + +u64 doubleIntegerCast(double f) { + u64 v; + memcpy(&v, &f, 8); + return v; +} + +template<> bool Cop1::isqnan(float f) { + return (floatIntegerCast(f) >> 22) & 1; +} + +template<> bool Cop1::isqnan(double f) { + return (doubleIntegerCast(f) >> 51) & 1; +} + +template <> bool Cop1::CheckCVTArg(float& f) { + switch (fpclassify(f)) { + case FP_SUBNORMAL: case FP_INFINITE: case FP_NAN: + SetCauseUnimplemented(); regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return true; + return false; } - return false; -} - -#define CheckFPUException() do { if(FireException()) { return; } } while(0) - -FORCE_INLINE int PushRoundingMode(const FCR31& fcr31) { - int og = fegetround(); - switch (fcr31.rounding_mode) { - case 0: fesetround(FE_TONEAREST); break; - case 1: fesetround(FE_TOWARDZERO); break; - case 2: fesetround(FE_UPWARD); break; - case 3: fesetround(FE_DOWNWARD); break; + if(f >= 0x1p+31f || f < -0x1p+31f) { + SetCauseUnimplemented(); + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; } - return og; + return true; } -void Cop1::SetCauseUnimplemented() { - fcr31.cause_unimplemented_operation = true; -} - -void Cop1::SetCauseUnderflow() { - fcr31.cause_underflow = true; - if(!fcr31.enable_underflow) { - fcr31.flag_underflow = true; +template <> bool Cop1::CheckCVTArg(double& f) { + switch (fpclassify(f)) { + case FP_SUBNORMAL: case FP_INFINITE: case FP_NAN: + SetCauseUnimplemented(); + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; } + + if((f >= 0x1p+31 || f < -0x1p+31)) { + SetCauseUnimplemented(); + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } + + return true; } -void Cop1::SetCauseInexact() { - fcr31.cause_inexact_operation = true; - if(!fcr31.enable_inexact_operation) { - fcr31.flag_inexact_operation = true; +template <> bool Cop1::CheckCVTArg(float& f) { + switch (fpclassify(f)) { + case FP_SUBNORMAL: case FP_INFINITE: case FP_NAN: + SetCauseUnimplemented(); + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; } + + if((f >= 0x1p+53f || f <= -0x1p+53f)) { + SetCauseUnimplemented(); + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } + + return true; } -void Cop1::SetCauseDivisionByZero() { - fcr31.cause_division_by_zero = true; - if(!fcr31.enable_division_by_zero) { - fcr31.flag_division_by_zero = true; +template <> bool Cop1::CheckCVTArg(double& f) { + switch (fpclassify(f)) { + case FP_SUBNORMAL: case FP_INFINITE: case FP_NAN: + SetCauseUnimplemented(); + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; } -} -void Cop1::SetCauseOverflow() { - fcr31.cause_overflow = true; - if(!fcr31.enable_overflow) { - fcr31.flag_overflow = true; + if((f >= 0x1p+53 || f <= -0x1p+53)) { + SetCauseUnimplemented(); + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; } -} -void Cop1::SetCauseInvalid() { - fcr31.cause_invalid_operation = true; - if(!fcr31.enable_invalid_operation) { - fcr31.flag_invalid_operation = true; - } -} - -#define PUSHROUNDING int orig_round = PushRoundingMode(fcr31) -#define POPROUNDING fesetround(orig_round) -#define OP_CheckExcept(op) do { feclearexcept(FE_ALL_EXCEPT); PUSHROUNDING; op; SetCauseRaised(fetestexcept(FE_ALL_EXCEPT)); POPROUNDING; } while(0) -#define CVT_OP_CheckExcept(op) do { feclearexcept(FE_ALL_EXCEPT); op; SetCauseRaisedCVT(fetestexcept(FE_ALL_EXCEPT)); CheckFPUException(); } while(0) - -#define OP(T, op) do { \ - CheckFPUUsable(); \ - auto fs = FGR(regs.cop0.status, FS(instr)); \ - auto ft = FGR(regs.cop0.status, FT(instr)); \ - CheckArg(fs); \ - CheckArg(ft); \ - T result; \ - OP_CheckExcept({result = (op);}); \ - CheckResult(result); \ - FGR(regs.cop0.status, FD(instr)) = result; \ -} while(0) - -template -void Cop1::SetCauseByArgWCVT(T f) { - switch (std::fpclassify(f)) { - case FP_NAN: - case FP_INFINITE: - case FP_SUBNORMAL: - SetCauseUnimplemented(); - CheckFPUException(); - break; - - case FP_NORMAL: - // Check overflow - if (f >= 2147483648.0f || f < -2147483648.0f) { - SetCauseUnimplemented(); - CheckFPUException(); - } - break; - - case FP_ZERO: - break; // Fine - } + return true; } template -void Cop1::SetCauseByArgLCVT(T f) { - switch (std::fpclassify(f)) { - case FP_NAN: - case FP_INFINITE: +bool Cop1::CheckArg(T& f) { + switch(fpclassify(f)) { case FP_SUBNORMAL: SetCauseUnimplemented(); - CheckFPUException(); - break; - - case FP_NORMAL: - // Check overflow - if (f >= 9007199254740992.000000 || f <= -9007199254740992.000000) { - SetCauseUnimplemented(); - CheckFPUException(); + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + case FP_NAN: + if(isqnan(f) ? SetCauseInvalid() : (SetCauseUnimplemented(), true)) { + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; } - break; - - case FP_ZERO: - break; // Fine + return true; } + return true; } -#define CheckWCVTArg(f) do { SetCauseByArgWCVT(f); CheckFPUException(); } while(0) -#define CheckLCVTArg(f) do { SetCauseByArgLCVT(f); CheckFPUException(); } while(0) - -void Cop1::SetCauseRaised(int raised) { - if (raised == 0) { - return; +template +bool Cop1::CheckArgs(T& f1, T& f2) { + auto class1 = std::fpclassify(f1), class2 = std::fpclassify(f2); + if((class1 == FP_NAN && !isqnan(f1)) || (class2 == FP_NAN && !isqnan(f2))) { + SetCauseUnimplemented(); + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; } - if (raised & FE_UNDERFLOW) { - if (!fcr31.fs || fcr31.enable_underflow || fcr31.enable_inexact_operation) { - SetCauseUnimplemented(); - return; - } else { - SetCauseUnderflow(); + if(class1 == FP_SUBNORMAL || class2 == FP_SUBNORMAL) { + SetCauseUnimplemented(); + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } + + if((class1 == FP_NAN && isqnan(f1)) || (class2 == FP_NAN && isqnan(f2))) { + if(SetCauseInvalid()) { + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; } } - if (raised & FE_INEXACT) { - SetCauseInexact(); - } - - if (raised & FE_DIVBYZERO) { - SetCauseDivisionByZero(); - } - - if (raised & FE_OVERFLOW) { - SetCauseOverflow(); - } - - if (raised & FE_INVALID) { - SetCauseInvalid(); - } + return true; } -void Cop1::SetCauseRaisedCVT(int raised) { - if(raised & FE_INVALID) { - SetCauseUnimplemented(); - return; - } - - SetCauseRaised(raised); -} - -#define F_TO_U32(f) (*((u32*)(&(f)))) -#define D_TO_U64(d) (*((u64*)(&(d)))) -#define U64_TO_D(d) (*((double*)(&(d)))) -#define U32_TO_F(f) (*((float*)(&(f)))) - -template -FORCE_INLINE bool isqnan(T f) { - if constexpr(std::is_same_v) { - u32 v = F_TO_U32(f); - return (v & 0x7FC00000) == 0x7FC00000; - } else if constexpr(std::is_same_v) { - u64 v = D_TO_U64(f); - return (v & 0x7FF8000000000000) == 0x7FF8000000000000; +template +bool Cop1::CheckFPUUsable() { + if constexpr (preserveCause) { + if(!regs.cop0.status.cu1) { + regs.cop0.FireException(ExceptionCode::CoprocessorUnusable, 1, regs.oldPC); + return false; + } } else { - Util::panic("Invalid float type in isqnan"); + if(!CheckFPUUsable()) return false; + fcr31.cause = {}; + } + + return true; +} + +template bool Cop1::CheckFPUUsable(); +template bool Cop1::CheckFPUUsable(); + +template +FORCE_INLINE T FlushResult(T f, u32 round) { + switch (round) { + case FE_TONEAREST: case FE_TOWARDZERO: return std::copysign(T(), f); + case FE_UPWARD: return std::signbit(f) ? -T() : std::numeric_limits::min(); + case FE_DOWNWARD: return std::signbit(f) ? -std::numeric_limits::min() : T(); + default: + __builtin_unreachable(); } } -template -void Cop1::SetCauseByArg(T f) { - auto fp_class = std::fpclassify(f); - switch(fp_class) { - case FP_NAN: - if(isqnan(f)) { - SetCauseInvalid(); - CheckFPUException(); - } else { - SetCauseUnimplemented(); - CheckFPUException(); - } - break; +template <> +bool Cop1::CheckResult(float& f) { + switch (std::fpclassify(f)) { case FP_SUBNORMAL: + if(!fcr31.fs || fcr31.enable.underflow || fcr31.enable.inexact_operation) { + SetCauseUnimplemented(); + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } + SetCauseUnderflow(); + SetCauseInexact(); + f = FlushResult(f, std::fegetround()); + return true; + case FP_NAN: { + uint32_t v = 0x7fbf'ffff; + memcpy(&f, &v, 4); + return true; + } + } + return true; +} + +template <> +bool Cop1::CheckResult(double& f) { + switch (std::fpclassify(f)) { + case FP_SUBNORMAL: + if(!fcr31.fs || fcr31.enable.underflow || fcr31.enable.inexact_operation) { + SetCauseUnimplemented(); + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } + SetCauseUnderflow(); + SetCauseInexact(); + f = FlushResult(f, fegetround()); + return true; + case FP_NAN: { + uint64_t v = 0x7ff7'ffff'ffff'ffff; + memcpy(&f, &v, 8); + return true; + } + } + return true; +} + +template +bool Cop1::TestExceptions() { + u32 exc = std::fetestexcept(FE_ALL_EXCEPT); + + if(!exc) return false; + + if constexpr (cvt) { + if(exc & FE_INVALID) { SetCauseUnimplemented(); - CheckFPUException(); - break; - case FP_INFINITE: - case FP_ZERO: - case FP_NORMAL: - break; // No-op, these are fine. - default: - Util::panic("Unknown floating point classification: {}", fp_class); + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return true; + } } + + if(exc & FE_UNDERFLOW) { + if(!fcr31.fs || fcr31.enable.underflow || fcr31.enable.inexact_operation) { + SetCauseUnimplemented(); + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return true; + } + } + + bool raise = false; + if(exc & FE_DIVBYZERO) raise |= SetCauseDivisionByZero(); + if(exc & FE_INEXACT) { + raise |= SetCauseInexact(); + } + if(exc & FE_UNDERFLOW) raise |= SetCauseUnderflow(); + if(exc & FE_OVERFLOW) raise |= SetCauseOverflow(); + if(exc & FE_INVALID) raise |= SetCauseInvalid(); + if(raise) regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return raise; } -#define CheckArg(f) do { SetCauseByArg(f); CheckFPUException(); } while(0) +template bool Cop1::TestExceptions(); +template bool Cop1::TestExceptions(); -template -void Cop1::SetCauseOnResult(T& d) { - auto fp_class = std::fpclassify(d); - T magic, min; - if constexpr(std::is_same_v) { - u32 c = 0x7FBFFFFF; - magic = U32_TO_F(c); - min = std::numeric_limits::min(); - } else if constexpr(std::is_same_v) { - u64 c = 0x7FF7FFFFFFFFFFFF; - magic = U64_TO_D(c); - min = std::numeric_limits::min(); - } - switch (fp_class) { - case FP_NAN: - d = magic; // set result to sNAN - break; - case FP_SUBNORMAL: - if (!fcr31.fs || fcr31.enable_underflow || fcr31.enable_inexact_operation) { - SetCauseUnimplemented(); - CheckFPUException(); - } else { - // Since the if statement checks for the corresponding enable bits, it's safe to turn these cause bits on here. - SetCauseUnderflow(); - SetCauseInexact(); - switch (fcr31.rounding_mode) { - case 0: - case 1: - d = std::copysign(0, d); - break; - case 2: - if (std::signbit(d)) { - d = -(T)0; - } else { - d = min; - } - break; - case 3: - if (std::signbit(d)) { - d = -min; - } else { - d = 0; - } - break; - } - } - break; - case FP_INFINITE: - case FP_ZERO: - case FP_NORMAL: - break; // No-op, these are fine. - default: - Util::panic("Unknown FP classification: {}", fp_class); - } +void Cop1::SetCauseUnimplemented() { + fcr31.cause.unimplemented_operation = true; } -#define CheckResult(f) do { SetCauseOnResult((f)); CheckFPUException(); } while(0) - -#define any_unordered(fs, ft) (std::isnan(fs) || std::isnan(ft)) -#define CheckRound(a, b) do { if ((a) != (b)) { SetCauseInexact(); } CheckFPUException(); } while(0) - -template -FORCE_INLINE bool is_nan(T f) { - if constexpr(std::is_same_v) { - u32 v = F_TO_U32(f); - return ((v & 0x7F800000) == 0x7F800000) && ((v & 0x7FFFFF) != 0); - } else if constexpr(std::is_same_v) { - u64 v = D_TO_U64(f); - return ((v & 0x7FF0000000000000) == 0x7FF0000000000000) && ((v & 0xFFFFFFFFFFFFF) != 0); - } else { - Util::panic("Invalid float type in is_nan"); - } +bool Cop1::SetCauseUnderflow() { + fcr31.cause.underflow = true; + if(fcr31.enable.underflow) return true; + fcr31.flag.underflow = true; + return false; } -#define checknanregs(fs, ft) do { \ - if(is_nan(fs) || is_nan(ft)) { \ - SetCauseInvalid(); \ - CheckFPUException(); \ - } \ -} while(0) +bool Cop1::SetCauseInexact() { + fcr31.cause.inexact_operation = true; + if(fcr31.enable.inexact_operation) return true; + fcr31.flag.inexact_operation = true; + return false; +} -#define checkqnanregs(fs, ft) do { \ - if(isqnan(fs) || isqnan(ft)) { \ - SetCauseInvalid(); \ - CheckFPUException(); \ - } \ -} while(0) +bool Cop1::SetCauseDivisionByZero() { + fcr31.cause.division_by_zero = true; + if(fcr31.enable.division_by_zero) return true; + fcr31.flag.division_by_zero = true; + return false; +} + +bool Cop1::SetCauseOverflow() { + fcr31.cause.overflow = true; + if(fcr31.enable.overflow) return true; + fcr31.flag.overflow = true; + return false; +} + +bool Cop1::SetCauseInvalid() { + fcr31.cause.invalid_operation = true; + if(fcr31.enable.invalid_operation) return true; + fcr31.flag.invalid_operation = true; + return false; +} + +#define CHECK_FPE_IMPL(type, res, operation, convert) \ + feclearexcept(FE_ALL_EXCEPT); \ + volatile type v##res = [&]() -> type { return operation; }(); \ + if (TestExceptions()) return; \ + type res = v##res; + +#define CHECK_FPE(type, res, operation) CHECK_FPE_IMPL(type, res, operation, false) +#define CHECK_FPE_CONV(type, res, operation) CHECK_FPE_IMPL(type, res, operation, true) void Cop1::absd(u32 instr) { - OP(double, std::fabs(fs)); + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckArg(fs)) return; + auto fd = std::abs(fs); + if(!CheckResult(fd)) return; + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::abss(u32 instr) { - OP(float, std::fabs(fs)); + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckArg(fs)) return; + auto fd = std::abs(fs); + if(!CheckResult(fd)) return; + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::adds(u32 instr) { - OP(float, fs + ft); + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + auto ft = FGR_T(regs.cop0.status, FT(instr)); + if(!CheckArgs(fs, ft)) return; + CHECK_FPE(float, fd, fs + ft) + if(!CheckResult(fd)) return; + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::addd(u32 instr) { - OP(double, fs + ft); + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + auto ft = FGR_T(regs.cop0.status, FT(instr)); + if(!CheckArgs(fs, ft)) return; + CHECK_FPE(double, fd, fs + ft) + if(!CheckResult(fd)) return; + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::ceills(u32 instr) { - CheckFPUUsable(); - auto fs = FGR(regs.cop0.status, FS(instr)); - CheckLCVTArg(fs); - s64 result; - CVT_OP_CheckExcept({ result = std::ceil(fs); }); - CheckRound(fs, result); - FGR(regs.cop0.status, FD(instr)) = result; -} - -void Cop1::ceilws(u32 instr) { - CheckFPUUsable(); - auto fs = FGR(regs.cop0.status, FS(instr)); - CheckWCVTArg(fs); - s32 result; - CVT_OP_CheckExcept({ result = std::ceil(fs); }); - CheckRound(fs, result); - FGR(regs.cop0.status, FD(instr)) = result; + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckCVTArg(fs)) return; + CHECK_FPE(s64, fd, Util::roundCeil(fs)) + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::ceilld(u32 instr) { - CheckFPUUsable(); - auto fs = FGR(regs.cop0.status, FS(instr)); - CheckLCVTArg(fs); - s64 result; - CVT_OP_CheckExcept({ result = std::ceil(fs); }); - CheckRound(fs, result); - FGR(regs.cop0.status, FD(instr)) = result; + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckCVTArg(fs)) return; + CHECK_FPE(s64, fd, Util::roundCeil(fs)) + FGR_D(regs.cop0.status, FD(instr)) = fd; +} + +void Cop1::ceilws(u32 instr) { + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckCVTArg(fs)) return; + CHECK_FPE_CONV(s32, fd, Util::roundCeil(fs)) + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::ceilwd(u32 instr) { - CheckFPUUsable(); - auto fs = FGR(regs.cop0.status, FS(instr)); - CheckWCVTArg(fs); - s32 result; - CVT_OP_CheckExcept({ result = std::ceil(fs); }); - CheckRound(fs, result); - FGR(regs.cop0.status, FD(instr)) = result; + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckCVTArg(fs)) return; + CHECK_FPE_CONV(s32, fd, Util::roundCeil(fs)) + FGR_D(regs.cop0.status, FD(instr)) = fd; } -void Cop1::cfc1(u32 instr) const { - CheckFPUUsable_PreserveCause(); +void Cop1::cfc1(u32 instr) { + if(!CheckFPUUsable()) return; u8 fd = RD(instr); s32 val = 0; switch(fd) { @@ -424,13 +471,13 @@ void Cop1::cfc1(u32 instr) const { break; default: Util::panic("Undefined CFC1 with rd != 0 or 31"); } - regs.gpr[RT(instr)] = val; + regs.Write(RT(instr), val); } void Cop1::ctc1(u32 instr) { - CheckFPUUsable_PreserveCause(); + if(!CheckFPUUsable()) return; u8 fs = RD(instr); - u32 val = regs.gpr[RT(instr)]; + u32 val = regs.Read(RT(instr)); switch(fs) { case 0: break; case 31: { @@ -444,267 +491,273 @@ void Cop1::ctc1(u32 instr) { case 3: fesetround(FE_DOWNWARD); break; } } - CheckFPUException(); + if(fcr31.cause.inexact_operation && fcr31.enable.inexact_operation) regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + if(fcr31.cause.underflow && fcr31.enable.underflow) regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + if(fcr31.cause.overflow && fcr31.enable.overflow) regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + if(fcr31.cause.division_by_zero && fcr31.enable.division_by_zero) regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + if(fcr31.cause.invalid_operation && fcr31.enable.invalid_operation) regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + if(fcr31.cause.unimplemented_operation) regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); } break; default: Util::panic("Undefined CTC1 with rd != 0 or 31"); } } void Cop1::cvtds(u32 instr) { - CheckFPUUsable(); - auto fs = FGR(regs.cop0.status, FS(instr)); - CheckArg(fs); - double result; - OP_CheckExcept({ result = double(fs); }); - CheckResult(result); - FGR(regs.cop0.status, FD(instr)) = result; + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckArg(fs)) return; + CHECK_FPE(double, fd, fs) + if(!CheckResult(fd)) return; + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::cvtsd(u32 instr) { - CheckFPUUsable(); - auto fs = FGR(regs.cop0.status, FS(instr)); - CheckArg(fs); - float result; - OP_CheckExcept({ result = float(fs); }); - CheckResult(result); - FGR(regs.cop0.status, FD(instr)) = result; + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckArg(fs)) return; + CHECK_FPE(float, fd, (float)fs) + if(!CheckResult(fd)) return; + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::cvtsw(u32 instr) { - CheckFPUUsable(); - auto fs = FGR(regs.cop0.status, FS(instr)); - float result; - OP_CheckExcept({ result = float(fs); }); - CheckResult(result); - FGR(regs.cop0.status, FD(instr)) = result; + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + CHECK_FPE(float, fd, fs) + if(!CheckResult(fd)) return; + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::cvtsl(u32 instr) { - CheckFPUUsable(); - auto fs = FGR(regs.cop0.status, FS(instr)); + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); if (fs >= s64(0x0080000000000000) || fs < s64(0xff80000000000000)) { SetCauseUnimplemented(); - CheckFPUException(); + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; } - float result; - OP_CheckExcept({ result = float(fs); }); - CheckResult(result); - FGR(regs.cop0.status, FD(instr)) = result; + CHECK_FPE(float, fd, fs) + if(!CheckResult(fd)) return; + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::cvtwd(u32 instr) { - CheckFPUUsable(); - auto fs = FGR(regs.cop0.status, FS(instr)); - CheckWCVTArg(fs); - s32 result; - PUSHROUNDING; - CVT_OP_CheckExcept({ result = std::rint(fs); }); - POPROUNDING; - CheckRound(fs, result); - FGR(regs.cop0.status, FD(instr)) = result; + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckCVTArg(fs)) return; + CHECK_FPE_CONV(s32, fd, Util::roundCurrent(fs)) + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::cvtws(u32 instr) { - CheckFPUUsable(); - auto fs = FGR(regs.cop0.status, FS(instr)); - CheckWCVTArg(fs); - s32 result; - PUSHROUNDING; - CVT_OP_CheckExcept({ result = std::rint(fs); }); - POPROUNDING; - CheckRound(fs, result); - FGR(regs.cop0.status, FD(instr)) = result; + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckCVTArg(fs)) return; + CHECK_FPE_CONV(s32, fd, Util::roundCurrent(fs)) + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::cvtls(u32 instr) { - CheckFPUUsable(); - auto fs = FGR(regs.cop0.status, FS(instr)); - CheckLCVTArg(fs); - s64 result; - PUSHROUNDING; - CVT_OP_CheckExcept({ result = std::rint(fs); }); - POPROUNDING; - CheckRound(fs, result); - FGR(regs.cop0.status, FD(instr)) = result; + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckCVTArg(fs)) return; + CHECK_FPE(s64, fd, Util::roundCurrent(fs)) + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::cvtdw(u32 instr) { - CheckFPUUsable(); - auto fs = FGR(regs.cop0.status, FS(instr)); - double result; - OP_CheckExcept({ result = double(fs); }); - CheckResult(result); - FGR(regs.cop0.status, FD(instr)) = result; + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + CHECK_FPE(double, fd, fs) + if(!CheckResult(fd)) return; + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::cvtdl(u32 instr) { - CheckFPUUsable(); - auto fs = FGR(regs.cop0.status, FS(instr)); + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); if (fs >= s64(0x0080000000000000) || fs < s64(0xff80000000000000)) { SetCauseUnimplemented(); - CheckFPUException(); + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; } - double result; - OP_CheckExcept({ result = double(fs); }); - CheckResult(result); - FGR(regs.cop0.status, FD(instr)) = result; + CHECK_FPE(double, fd, fs) + if(!CheckResult(fd)) return; + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::cvtld(u32 instr) { - CheckFPUUsable(); - auto fs = FGR(regs.cop0.status, FS(instr)); - CheckLCVTArg(fs); - s64 result; - PUSHROUNDING; - CVT_OP_CheckExcept({ result = std::rint(fs); }); - POPROUNDING; - CheckRound(fs, result); - FGR(regs.cop0.status, FD(instr)) = result; + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckCVTArg(fs)) return; + CHECK_FPE(s64, fd, Util::roundCurrent(fs)) + FGR_D(regs.cop0.status, FD(instr)) = fd; } +template +bool Cop1::XORDERED(T fs, T ft) { + if(std::isnan(fs) || std::isnan(ft)) { + if(std::isnan(fs) && (!quiet || isqnan(fs)) && SetCauseInvalid()) { + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } + if(std::isnan(ft) && (!quiet || isqnan(ft)) && SetCauseInvalid()) { + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } + fcr31.compare = cf; + return false; + } + + return true; +} + +#define ORDERED(type, cf) XORDERED +#define UNORDERED(type, cf) XORDERED + template void Cop1::cf(u32 instr) { - CheckFPUUsable(); - T fs = FGR(regs.cop0.status, FS(instr)); - T ft = FGR(regs.cop0.status, FT(instr)); - checkqnanregs(fs, ft); - fcr31.compare = false; + if(!CheckFPUUsable()) return; + T fs = FGR_S(regs.cop0.status, FS(instr)); + T ft = FGR_T(regs.cop0.status, FT(instr)); + if(!UNORDERED(T, 0)(fs, ft)) return; + fcr31.compare = 0; } template void Cop1::cun(u32 instr) { - CheckFPUUsable(); - T fs = FGR(regs.cop0.status, FS(instr)); - T ft = FGR(regs.cop0.status, FT(instr)); - checkqnanregs(fs, ft); - fcr31.compare = any_unordered(fs, ft); + if(!CheckFPUUsable()) return; + T fs = FGR_S(regs.cop0.status, FS(instr)); + T ft = FGR_T(regs.cop0.status, FT(instr)); + if(!UNORDERED(T, 1)(fs, ft)) return; + fcr31.compare = 0; } template void Cop1::ceq(u32 instr) { - CheckFPUUsable(); - T fs = FGR(regs.cop0.status, FS(instr)); - T ft = FGR(regs.cop0.status, FT(instr)); - checkqnanregs(fs, ft); + if(!CheckFPUUsable()) return; + T fs = FGR_S(regs.cop0.status, FS(instr)); + T ft = FGR_T(regs.cop0.status, FT(instr)); + if(!UNORDERED(T, 0)(fs, ft)) return; fcr31.compare = fs == ft; } template void Cop1::cueq(u32 instr) { - CheckFPUUsable(); - T fs = FGR(regs.cop0.status, FS(instr)); - T ft = FGR(regs.cop0.status, FT(instr)); - checkqnanregs(fs, ft); - fcr31.compare = fs == ft || any_unordered(fs, ft); + if(!CheckFPUUsable()) return; + T fs = FGR_S(regs.cop0.status, FS(instr)); + T ft = FGR_T(regs.cop0.status, FT(instr)); + if(!UNORDERED(T, 1)(fs, ft)) return; + fcr31.compare = fs == ft; } template void Cop1::colt(u32 instr) { - CheckFPUUsable(); - T fs = FGR(regs.cop0.status, FS(instr)); - T ft = FGR(regs.cop0.status, FT(instr)); - checkqnanregs(fs, ft); + if(!CheckFPUUsable()) return; + T fs = FGR_S(regs.cop0.status, FS(instr)); + T ft = FGR_T(regs.cop0.status, FT(instr)); + if(!UNORDERED(T, 0)(fs, ft)) return; fcr31.compare = fs < ft; } template void Cop1::cult(u32 instr) { - CheckFPUUsable(); - T fs = FGR(regs.cop0.status, FS(instr)); - T ft = FGR(regs.cop0.status, FT(instr)); - checkqnanregs(fs, ft); - fcr31.compare = fs < ft || any_unordered(fs, ft); + if(!CheckFPUUsable()) return; + T fs = FGR_S(regs.cop0.status, FS(instr)); + T ft = FGR_T(regs.cop0.status, FT(instr)); + if(!UNORDERED(T, 1)(fs, ft)) return; + fcr31.compare = fs < ft; } template void Cop1::cole(u32 instr) { - CheckFPUUsable(); - T fs = FGR(regs.cop0.status, FS(instr)); - T ft = FGR(regs.cop0.status, FT(instr)); - checkqnanregs(fs, ft); + if(!CheckFPUUsable()) return; + T fs = FGR_S(regs.cop0.status, FS(instr)); + T ft = FGR_T(regs.cop0.status, FT(instr)); + if(!UNORDERED(T, 0)(fs, ft)) return; fcr31.compare = fs <= ft; } template void Cop1::cule(u32 instr) { - CheckFPUUsable(); - T fs = FGR(regs.cop0.status, FS(instr)); - T ft = FGR(regs.cop0.status, FT(instr)); - checkqnanregs(fs, ft); - fcr31.compare = fs <= ft || any_unordered(fs, ft); + if(!CheckFPUUsable()) return; + T fs = FGR_S(regs.cop0.status, FS(instr)); + T ft = FGR_T(regs.cop0.status, FT(instr)); + if(!UNORDERED(T, 1)(fs, ft)) return; + fcr31.compare = fs <= ft; } template void Cop1::csf(u32 instr) { - CheckFPUUsable(); - T fs = FGR(regs.cop0.status, FS(instr)); - T ft = FGR(regs.cop0.status, FT(instr)); - checknanregs(fs, ft); - fcr31.compare = false; + if(!CheckFPUUsable()) return; + T fs = FGR_S(regs.cop0.status, FS(instr)); + T ft = FGR_T(regs.cop0.status, FT(instr)); + if(!ORDERED(T, 0)(fs, ft)) return; + fcr31.compare = 0; } template void Cop1::cngle(u32 instr) { - CheckFPUUsable(); - T fs = FGR(regs.cop0.status, FS(instr)); - T ft = FGR(regs.cop0.status, FT(instr)); - checknanregs(fs, ft); - fcr31.compare = any_unordered(fs, ft); + if(!CheckFPUUsable()) return; + T fs = FGR_S(regs.cop0.status, FS(instr)); + T ft = FGR_T(regs.cop0.status, FT(instr)); + if(!ORDERED(T, 1)(fs, ft)) return; + fcr31.compare = 0; } template void Cop1::cseq(u32 instr) { - CheckFPUUsable(); - T fs = FGR(regs.cop0.status, FS(instr)); - T ft = FGR(regs.cop0.status, FT(instr)); - checknanregs(fs, ft); + if(!CheckFPUUsable()) return; + T fs = FGR_S(regs.cop0.status, FS(instr)); + T ft = FGR_T(regs.cop0.status, FT(instr)); + if(!ORDERED(T, 0)(fs, ft)) return; fcr31.compare = fs == ft; } template void Cop1::cngl(u32 instr) { - CheckFPUUsable(); - T fs = FGR(regs.cop0.status, FS(instr)); - T ft = FGR(regs.cop0.status, FT(instr)); - checknanregs(fs, ft); - fcr31.compare = fs == ft || any_unordered(fs, ft); + if(!CheckFPUUsable()) return; + T fs = FGR_S(regs.cop0.status, FS(instr)); + T ft = FGR_T(regs.cop0.status, FT(instr)); + if(!ORDERED(T, 1)(fs, ft)) return; + fcr31.compare = fs == ft; } template void Cop1::clt(u32 instr) { - CheckFPUUsable(); - T fs = FGR(regs.cop0.status, FS(instr)); - T ft = FGR(regs.cop0.status, FT(instr)); - checknanregs(fs, ft); + if(!CheckFPUUsable()) return; + T fs = FGR_S(regs.cop0.status, FS(instr)); + T ft = FGR_T(regs.cop0.status, FT(instr)); + if(!ORDERED(T, 0)(fs, ft)) return; fcr31.compare = fs < ft; } template void Cop1::cnge(u32 instr) { - CheckFPUUsable(); - T fs = FGR(regs.cop0.status, FS(instr)); - T ft = FGR(regs.cop0.status, FT(instr)); - checknanregs(fs, ft); - fcr31.compare = fs < ft || any_unordered(fs, ft); + if(!CheckFPUUsable()) return; + T fs = FGR_S(regs.cop0.status, FS(instr)); + T ft = FGR_T(regs.cop0.status, FT(instr)); + if(!ORDERED(T, 1)(fs, ft)) return; + fcr31.compare = fs < ft; } template void Cop1::cle(u32 instr) { - CheckFPUUsable(); - T fs = FGR(regs.cop0.status, FS(instr)); - T ft = FGR(regs.cop0.status, FT(instr)); - checknanregs(fs, ft); + if(!CheckFPUUsable()) return; + T fs = FGR_S(regs.cop0.status, FS(instr)); + T ft = FGR_T(regs.cop0.status, FT(instr)); + if(!ORDERED(T, 0)(fs, ft)) return; fcr31.compare = fs <= ft; } template void Cop1::cngt(u32 instr) { - CheckFPUUsable(); - T fs = FGR(regs.cop0.status, FS(instr)); - T ft = FGR(regs.cop0.status, FT(instr)); - checknanregs(fs, ft); - fcr31.compare = fs <= ft || any_unordered(fs, ft); + if(!CheckFPUUsable()) return; + T fs = FGR_S(regs.cop0.status, FS(instr)); + T ft = FGR_T(regs.cop0.status, FT(instr)); + if(!ORDERED(T, 1)(fs, ft)) return; + fcr31.compare = fs <= ft; } template void Cop1::cf(u32 instr); @@ -741,181 +794,242 @@ template void Cop1::cle(u32 instr); template void Cop1::cngt(u32 instr); void Cop1::divs(u32 instr) { - OP(float, fs / ft); + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + auto ft = FGR_T(regs.cop0.status, FT(instr)); + if(!CheckArgs(fs, ft)) return; + CHECK_FPE(float, fd, fs / ft) + if(!CheckResult(fd)) return; + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::divd(u32 instr) { - OP(double, fs / ft); + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + auto ft = FGR_T(regs.cop0.status, FT(instr)); + if(!CheckArgs(fs, ft)) return; + CHECK_FPE(double, fd, fs / ft) + if(!CheckResult(fd)) return; + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::muls(u32 instr) { - OP(float, fs * ft); + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + auto ft = FGR_T(regs.cop0.status, FT(instr)); + if(!CheckArgs(fs, ft)) return; + CHECK_FPE(float, fd, fs * ft) + if(!CheckResult(fd)) return; + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::muld(u32 instr) { - OP(double, fs * ft); + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + auto ft = FGR_T(regs.cop0.status, FT(instr)); + if(!CheckArgs(fs, ft)) return; + CHECK_FPE(double, fd, fs * ft) + if(!CheckResult(fd)) return; + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::subs(u32 instr) { - OP(float, fs - ft); + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + auto ft = FGR_T(regs.cop0.status, FT(instr)); + if(!CheckArgs(fs, ft)) return; + CHECK_FPE(float, fd, fs - ft) + if(!CheckResult(fd)) return; + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::subd(u32 instr) { - OP(double, fs - ft); + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + auto ft = FGR_T(regs.cop0.status, FT(instr)); + if(!CheckArgs(fs, ft)) return; + CHECK_FPE(double, fd, fs - ft) + if(!CheckResult(fd)) return; + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::movs(u32 instr) { - CheckFPUUsable_PreserveCause(); - auto val = FGR(regs.cop0.status, FS(instr)); - FGR(regs.cop0.status, FD(instr)) = val; + movd(instr); } void Cop1::movd(u32 instr) { - CheckFPUUsable_PreserveCause(); - auto val = FGR(regs.cop0.status, FS(instr)); - FGR(regs.cop0.status, FD(instr)) = val; + if(!CheckFPUUsable()) return; + FGR_D(regs.cop0.status, FD(instr)) = FGR_S(regs.cop0.status, FS(instr)); } void Cop1::negs(u32 instr) { - OP(float, -fs); + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckArg(fs)) return; + CHECK_FPE(float, fd, -fs) + if(!CheckResult(fd)) return; + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::negd(u32 instr) { - OP(double, -fs); + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckArg(fs)) return; + CHECK_FPE(double, fd, -fs) + if(!CheckResult(fd)) return; + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::sqrts(u32 instr) { - OP(float, std::sqrt(fs)); + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckArg(fs)) return; + CHECK_FPE(float, fd, sqrtf(fs)) + if(!CheckResult(fd)) return; + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::sqrtd(u32 instr) { - OP(double, std::sqrt(fs)); + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckArg(fs)) return; + CHECK_FPE(double, fd, sqrt(fs)) + if(!CheckResult(fd)) return; + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::roundls(u32 instr) { - CheckFPUUsable(); - auto fs = FGR(regs.cop0.status, FS(instr)); - CheckLCVTArg(fs); - s64 result; - CVT_OP_CheckExcept({ result = std::nearbyint(fs); }); - CheckRound(fs, result); - FGR(regs.cop0.status, FD(instr)) = result; + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckCVTArg(fs)) return; + CHECK_FPE(s64, fd, Util::roundNearest(fs)) + if(fd != fs && SetCauseInexact()) { + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::roundld(u32 instr) { - CheckFPUUsable(); - auto fs = FGR(regs.cop0.status, FS(instr)); - CheckLCVTArg(fs); - s64 result; - CVT_OP_CheckExcept({ result = std::nearbyint(fs); }); - CheckRound(fs, result); - FGR(regs.cop0.status, FD(instr)) = result; + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckCVTArg(fs)) return; + CHECK_FPE(s64, fd, Util::roundNearest(fs)) + if(fd != fs && SetCauseInexact()) { + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::roundws(u32 instr) { - CheckFPUUsable(); - auto fs = FGR(regs.cop0.status, FS(instr)); - CheckWCVTArg(fs); - s32 result; - CVT_OP_CheckExcept({ result = std::nearbyint(fs); }); - CheckRound(fs, result); - FGR(regs.cop0.status, FD(instr)) = result; + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckCVTArg(fs)) return; + CHECK_FPE_CONV(s32, fd, Util::roundNearest(fs)) + if(fd != fs && SetCauseInexact()) { + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::roundwd(u32 instr) { - CheckFPUUsable(); - auto fs = FGR(regs.cop0.status, FS(instr)); - CheckWCVTArg(fs); - s32 result; - CVT_OP_CheckExcept({ result = std::nearbyint(fs); }); - CheckRound(fs, result); - FGR(regs.cop0.status, FD(instr)) = result; + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckCVTArg(fs)) return; + CHECK_FPE_CONV(s32, fd, Util::roundNearest(fs)) + if(fd != fs && SetCauseInexact()) { + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::floorls(u32 instr) { - CheckFPUUsable(); - auto fs = FGR(regs.cop0.status, FS(instr)); - CheckLCVTArg(fs); - s64 result; - CVT_OP_CheckExcept({ result = std::floor(fs); }); - CheckRound(fs, result); - FGR(regs.cop0.status, FD(instr)) = result; + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckCVTArg(fs)) return; + CHECK_FPE(s64, fd, Util::roundFloor(fs)) + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::floorld(u32 instr) { - CheckFPUUsable(); - auto fs = FGR(regs.cop0.status, FS(instr)); - CheckLCVTArg(fs); - s64 result; - CVT_OP_CheckExcept({ result = std::floor(fs); }); - CheckRound(fs, result); - FGR(regs.cop0.status, FD(instr)) = result; + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckCVTArg(fs)) return; + CHECK_FPE(s64, fd, Util::roundFloor(fs)) + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::floorws(u32 instr) { - CheckFPUUsable(); - auto fs = FGR(regs.cop0.status, FS(instr)); - CheckWCVTArg(fs); - s32 result; - CVT_OP_CheckExcept({ result = std::floor(fs); }); - CheckRound(fs, result); - FGR(regs.cop0.status, FD(instr)) = result; + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckCVTArg(fs)) return; + CHECK_FPE_CONV(s32, fd, Util::roundFloor(fs)) + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::floorwd(u32 instr) { - CheckFPUUsable(); - auto fs = FGR(regs.cop0.status, FS(instr)); - CheckWCVTArg(fs); - s32 result; - CVT_OP_CheckExcept({ result = std::floor(fs); }); - CheckRound(fs, result); - FGR(regs.cop0.status, FD(instr)) = result; + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckCVTArg(fs)) return; + CHECK_FPE_CONV(s32, fd, Util::roundFloor(fs)) + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::truncws(u32 instr) { - CheckFPUUsable(); - auto fs = FGR(regs.cop0.status, FS(instr)); - CheckWCVTArg(fs); - s32 result; - CVT_OP_CheckExcept({ result = std::trunc(fs); }); - CheckRound(fs, result); - FGR(regs.cop0.status, FD(instr)) = result; + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckCVTArg(fs)) return; + CHECK_FPE_CONV(s32, fd, Util::roundTrunc(fs)) + if((float)fd != fs && SetCauseInexact()) { + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::truncwd(u32 instr) { - CheckFPUUsable(); - auto fs = FGR(regs.cop0.status, FS(instr)); - CheckWCVTArg(fs); - s32 result; - CVT_OP_CheckExcept({ result = std::trunc(fs); }); - CheckRound(fs, result); - FGR(regs.cop0.status, FD(instr)) = result; + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckCVTArg(fs)) return; + CHECK_FPE_CONV(s32, fd, Util::roundTrunc(fs)) + if((double)fd != fs && SetCauseInexact()) { + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::truncls(u32 instr) { - CheckFPUUsable(); - auto fs = FGR(regs.cop0.status, FS(instr)); - CheckLCVTArg(fs); - s64 result; - CVT_OP_CheckExcept({ result = std::trunc(fs); }); - CheckRound(fs, result); - FGR(regs.cop0.status, FD(instr)) = result; + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckCVTArg(fs)) return; + CHECK_FPE(s64, fd, Util::roundTrunc(fs)) + if((float)fd != fs && SetCauseInexact()) { + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + FGR_D(regs.cop0.status, FD(instr)) = fd; } void Cop1::truncld(u32 instr) { - CheckFPUUsable(); - auto fs = FGR(regs.cop0.status, FS(instr)); - CheckLCVTArg(fs); - s64 result; - CVT_OP_CheckExcept({ result = std::trunc(fs); }); - CheckRound(fs, result); - FGR(regs.cop0.status, FD(instr)) = result; + if(!CheckFPUUsable()) return; + auto fs = FGR_S(regs.cop0.status, FS(instr)); + if(!CheckCVTArg(fs)) return; + CHECK_FPE(s64, fd, Util::roundTrunc(fs)) + if((double)fd != fs && SetCauseInexact()) { + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + FGR_D(regs.cop0.status, FD(instr)) = fd; } template void Cop1::lwc1(T &cpu, Mem &mem, u32 instr) { if constexpr(std::is_same_v) { - CheckFPUUsable_PreserveCause(); + if(!CheckFPUUsable()) return; lwc1Interp(mem, instr); } else if constexpr (std::is_same_v) { lwc1JIT(cpu, mem, instr); @@ -930,7 +1044,7 @@ template void Cop1::lwc1(JIT&, Mem&, u32); template void Cop1::swc1(T &cpu, Mem &mem, u32 instr) { if constexpr(std::is_same_v) { - CheckFPUUsable_PreserveCause(); + if(!CheckFPUUsable()) return; swc1Interp(mem, instr); } else if constexpr (std::is_same_v) { swc1JIT(cpu, mem, instr); @@ -945,7 +1059,7 @@ template void Cop1::swc1(JIT&, Mem&, u32); template void Cop1::ldc1(T &cpu, Mem &mem, u32 instr) { if constexpr(std::is_same_v) { - CheckFPUUsable_PreserveCause(); + if(!CheckFPUUsable()) return; ldc1Interp(mem, instr); } else if constexpr (std::is_same_v) { ldc1JIT(cpu, mem, instr); @@ -960,7 +1074,7 @@ template void Cop1::ldc1(JIT&, Mem&, u32); template void Cop1::sdc1(T &cpu, Mem &mem, u32 instr) { if constexpr(std::is_same_v) { - CheckFPUUsable_PreserveCause(); + if(!CheckFPUUsable()) return; sdc1Interp(mem, instr); } else if constexpr (std::is_same_v) { sdc1JIT(cpu, mem, instr); @@ -973,7 +1087,7 @@ template void Cop1::sdc1(Interpreter&, Mem&, u32); template void Cop1::sdc1(JIT&, Mem&, u32); void Cop1::lwc1Interp(Mem& mem, u32 instr) { - u64 addr = (s64)(s16)instr + regs.gpr[BASE(instr)]; + u64 addr = (s64)(s16)instr + regs.Read(BASE(instr)); u32 physical; if(!regs.cop0.MapVAddr(Cop0::LOAD, addr, physical)) { @@ -981,30 +1095,30 @@ void Cop1::lwc1Interp(Mem& mem, u32 instr) { regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); } else { u32 data = mem.Read(regs, physical); - FGR(regs.cop0.status, FT(instr)) = data; + FGR_T(regs.cop0.status, FT(instr)) = data; } } void Cop1::swc1Interp(Mem& mem, u32 instr) { - u64 addr = (s64)(s16)instr + regs.gpr[BASE(instr)]; + u64 addr = (s64)(s16)instr + regs.Read(BASE(instr)); u32 physical; if(!regs.cop0.MapVAddr(Cop0::STORE, addr, physical)) { regs.cop0.HandleTLBException(addr); regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); } else { - mem.Write(regs, physical, FGR(regs.cop0.status, FT(instr))); + mem.Write(regs, physical, FGR_T(regs.cop0.status, FT(instr))); } } void Cop1::unimplemented() { - CheckFPUUsable(); - fcr31.cause_unimplemented_operation = true; - FireException(); + if(!CheckFPUUsable()) return; + SetCauseUnimplemented(); + regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); } void Cop1::ldc1Interp(Mem& mem, u32 instr) { - u64 addr = (s64)(s16)instr + regs.gpr[BASE(instr)]; + u64 addr = (s64)(s16)instr + regs.Read(BASE(instr)); u32 physical; if(!regs.cop0.MapVAddr(Cop0::LOAD, addr, physical)) { @@ -1012,40 +1126,39 @@ void Cop1::ldc1Interp(Mem& mem, u32 instr) { regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); } else { u64 data = mem.Read(regs, physical); - FGR(regs.cop0.status, FT(instr)) = data; + FGR_T(regs.cop0.status, FT(instr)) = data; } } void Cop1::sdc1Interp(Mem& mem, u32 instr) { - u64 addr = (s64)(s16)instr + regs.gpr[BASE(instr)]; + u64 addr = (s64)(s16)instr + regs.Read(BASE(instr)); u32 physical; if(!regs.cop0.MapVAddr(Cop0::STORE, addr, physical)) { regs.cop0.HandleTLBException(addr); regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); } else { - mem.Write(regs, physical, FGR(regs.cop0.status, FT(instr))); + mem.Write(regs, physical, FGR_T(regs.cop0.status, FT(instr))); } } void Cop1::mfc1(u32 instr) { - CheckFPUUsable_PreserveCause(); - regs.gpr[RT(instr)] = FGR(regs.cop0.status, FS(instr)); + if(!CheckFPUUsable()) return; + regs.Write(RT(instr), FGR_T(regs.cop0.status, FS(instr))); } void Cop1::dmfc1(u32 instr) { - CheckFPUUsable_PreserveCause(); - regs.gpr[RT(instr)] = FGR(regs.cop0.status, FS(instr)); + if(!CheckFPUUsable()) return; + regs.Write(RT(instr), FGR_S(regs.cop0.status, FS(instr))); } void Cop1::mtc1(u32 instr) { - CheckFPUUsable_PreserveCause(); - FGR(regs.cop0.status, FS(instr)) = regs.gpr[RT(instr)]; + if(!CheckFPUUsable()) return; + FGR_T(regs.cop0.status, FS(instr)) = regs.Read(RT(instr)); } void Cop1::dmtc1(u32 instr) { - CheckFPUUsable_PreserveCause(); - FGR(regs.cop0.status, FS(instr)) = regs.gpr[RT(instr)]; + if(!CheckFPUUsable()) return; + FGR_S(regs.cop0.status, FS(instr)) = regs.Read(RT(instr)); } - } diff --git a/src/backend/core/rsp/decode.cpp b/src/backend/core/rsp/decode.cpp index 224e6d17..737da901 100644 --- a/src/backend/core/rsp/decode.cpp +++ b/src/backend/core/rsp/decode.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include namespace n64 { diff --git a/src/backend/core/rsp/instructions.cpp b/src/backend/core/rsp/instructions.cpp index 7f95fe85..f69623d7 100644 --- a/src/backend/core/rsp/instructions.cpp +++ b/src/backend/core/rsp/instructions.cpp @@ -60,11 +60,11 @@ FORCE_INLINE void SetCop0Reg(Registers& regs, Mem& mem, u8 index, u32 val) { case 1: rsp.spDMADRAMAddr.raw = val; break; case 2: rsp.spDMALen.raw = val; - rsp.DMAtoRSP(mem.GetRDRAM()); + rsp.DMA(); break; case 3: rsp.spDMALen.raw = val; - rsp.DMAtoRDRAM(mem.GetRDRAM()); + rsp.DMA(); break; case 4: rsp.WriteStatus(val); break; case 7: diff --git a/src/frontend/CMakeLists.txt b/src/frontend/CMakeLists.txt index 67245770..c7ef344d 100644 --- a/src/frontend/CMakeLists.txt +++ b/src/frontend/CMakeLists.txt @@ -28,7 +28,6 @@ include_directories( ../../external/parallel-rdp/parallel-rdp-standalone/vulkan ../../external/parallel-rdp/parallel-rdp-standalone/vulkan-headers/include ../../external/parallel-rdp/parallel-rdp-standalone/util - ../../external/nativefiledialog-extended/src/include ../../external/imgui/imgui ../../external/imgui/imgui/backends ../../external/unarr @@ -43,7 +42,6 @@ add_subdirectory(../../external/json json) add_subdirectory(../../external/fmt fmt) add_subdirectory(../../external/mio mio) add_subdirectory(../backend backend) -add_subdirectory(../../external/nativefiledialog-extended nfd) add_subdirectory(../../external/parallel-rdp parallel-rdp) add_subdirectory(../../external/unarr unarr) @@ -72,7 +70,7 @@ add_executable(kaizen-qt InputSettings.hpp InputSettings.cpp) -target_link_libraries(kaizen-qt PUBLIC Qt6::Core Qt6::Gui Qt6::Widgets fmt mio nlohmann_json nfd parallel-rdp backend) +target_link_libraries(kaizen-qt PUBLIC Qt6::Core Qt6::Gui Qt6::Widgets fmt mio nlohmann_json parallel-rdp backend) target_compile_definitions(kaizen-qt PUBLIC SDL_MAIN_HANDLED) file(COPY ../../resources/ DESTINATION ${PROJECT_BINARY_DIR}/resources/) diff --git a/src/frontend/EmuThread.cpp b/src/frontend/EmuThread.cpp index 02c8d465..f05af38e 100644 --- a/src/frontend/EmuThread.cpp +++ b/src/frontend/EmuThread.cpp @@ -1,17 +1,13 @@ #include -#include -#include -#include "Audio.hpp" #include -EmuThread::EmuThread(std::unique_ptr&& instance_, std::unique_ptr&& wsiPlatform_, std::unique_ptr&& windowInfo_, SettingsWindow& settings) noexcept - : instance(std::move(instance_)), wsiPlatform(std::move(wsiPlatform_)), - windowInfo(std::move(windowInfo_)), - controller(SDL_GameControllerClose, "GameController"), +EmuThread::EmuThread(const std::shared_ptr& instance_, const std::shared_ptr& wsiPlatform_, const std::shared_ptr& windowInfo_, SettingsWindow& settings) noexcept + : instance(instance_), wsiPlatform(wsiPlatform_), + windowInfo(windowInfo_), core(parallel), settings(settings) {} [[noreturn]] void EmuThread::run() noexcept { - parallel.Init(instance.get(), std::move(wsiPlatform), std::move(windowInfo), core.cpu->GetMem().GetRDRAMPtr()); + parallel.Init(instance, wsiPlatform, windowInfo, core.cpu->GetMem().GetRDRAMPtr()); SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER); bool controllerConnected = false; @@ -25,11 +21,11 @@ EmuThread::EmuThread(std::unique_ptr&& instance_, std::unique switch(e.type) { case SDL_CONTROLLERDEVICEADDED: { const int index = e.cdevice.which; - controller.Construct(SDL_GameControllerOpen, index); + controller = SDL_GameControllerOpen(index); Util::info("Found controller!"); - auto serial = SDL_GameControllerGetSerial(controller.get()); - auto name = SDL_GameControllerName(controller.get()); - auto path = SDL_GameControllerPath(controller.get()); + auto serial = SDL_GameControllerGetSerial(controller); + auto name = SDL_GameControllerName(controller); + auto path = SDL_GameControllerPath(controller); Util::info("\tName: {}", name ? name : "Not available"); Util::info("\tSerial: {}", serial ? serial : "Not available"); Util::info("\tPath: {}", path ? path : "Not available"); @@ -37,7 +33,7 @@ EmuThread::EmuThread(std::unique_ptr&& instance_, std::unique } break; case SDL_CONTROLLERDEVICEREMOVED: { controllerConnected = false; - controller.Destroy(); + SDL_GameControllerClose(controller); } break; } } @@ -59,22 +55,22 @@ EmuThread::EmuThread(std::unique_ptr&& instance_, std::unique if(controllerConnected) { n64::PIF& pif = core.cpu->GetMem().mmio.si.pif; - pif.UpdateButton(0, n64::Controller::Key::A, SDL_GameControllerGetButton(controller.get(), SDL_CONTROLLER_BUTTON_A)); - pif.UpdateButton(0, n64::Controller::Key::B, SDL_GameControllerGetButton(controller.get(), SDL_CONTROLLER_BUTTON_X)); - pif.UpdateButton(0, n64::Controller::Key::Z, SDL_GameControllerGetAxis(controller.get(), SDL_CONTROLLER_AXIS_TRIGGERLEFT) == SDL_JOYSTICK_AXIS_MAX); - pif.UpdateButton(0, n64::Controller::Key::Start, SDL_GameControllerGetButton(controller.get(), SDL_CONTROLLER_BUTTON_START)); - pif.UpdateButton(0, n64::Controller::Key::DUp, SDL_GameControllerGetButton(controller.get(), SDL_CONTROLLER_BUTTON_DPAD_UP)); - pif.UpdateButton(0, n64::Controller::Key::DDown, SDL_GameControllerGetButton(controller.get(), SDL_CONTROLLER_BUTTON_DPAD_DOWN)); - pif.UpdateButton(0, n64::Controller::Key::DLeft, SDL_GameControllerGetButton(controller.get(), SDL_CONTROLLER_BUTTON_DPAD_LEFT)); - pif.UpdateButton(0, n64::Controller::Key::DRight, SDL_GameControllerGetButton(controller.get(), SDL_CONTROLLER_BUTTON_DPAD_RIGHT)); - pif.UpdateButton(0, n64::Controller::Key::LT, SDL_GameControllerGetButton(controller.get(), SDL_CONTROLLER_BUTTON_LEFTSHOULDER)); - pif.UpdateButton(0, n64::Controller::Key::RT, SDL_GameControllerGetButton(controller.get(), SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)); - pif.UpdateButton(0, n64::Controller::Key::CUp, SDL_GameControllerGetAxis(controller.get(), SDL_CONTROLLER_AXIS_RIGHTY) <= -127); - pif.UpdateButton(0, n64::Controller::Key::CDown, SDL_GameControllerGetAxis(controller.get(), SDL_CONTROLLER_AXIS_RIGHTY) >= 127); - pif.UpdateButton(0, n64::Controller::Key::CLeft, SDL_GameControllerGetAxis(controller.get(), SDL_CONTROLLER_AXIS_RIGHTX) <= -127); - pif.UpdateButton(0, n64::Controller::Key::CRight, SDL_GameControllerGetAxis(controller.get(), SDL_CONTROLLER_AXIS_RIGHTX) >= 127); + pif.UpdateButton(0, n64::Controller::Key::A, SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_A)); + pif.UpdateButton(0, n64::Controller::Key::B, SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_X)); + pif.UpdateButton(0, n64::Controller::Key::Z, SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT) == SDL_JOYSTICK_AXIS_MAX); + pif.UpdateButton(0, n64::Controller::Key::Start, SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_START)); + pif.UpdateButton(0, n64::Controller::Key::DUp, SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_UP)); + pif.UpdateButton(0, n64::Controller::Key::DDown, SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_DOWN)); + pif.UpdateButton(0, n64::Controller::Key::DLeft, SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_LEFT)); + pif.UpdateButton(0, n64::Controller::Key::DRight, SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_RIGHT)); + pif.UpdateButton(0, n64::Controller::Key::LT, SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_LEFTSHOULDER)); + pif.UpdateButton(0, n64::Controller::Key::RT, SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)); + pif.UpdateButton(0, n64::Controller::Key::CUp, SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY) <= -127); + pif.UpdateButton(0, n64::Controller::Key::CDown, SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY) >= 127); + pif.UpdateButton(0, n64::Controller::Key::CLeft, SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX) <= -127); + pif.UpdateButton(0, n64::Controller::Key::CRight, SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX) >= 127); - float xclamped = SDL_GameControllerGetAxis(controller.get(), SDL_CONTROLLER_AXIS_LEFTX); + float xclamped = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX); if(xclamped < 0) { xclamped /= float(std::abs(SDL_JOYSTICK_AXIS_MIN)); } else { @@ -83,7 +79,7 @@ EmuThread::EmuThread(std::unique_ptr&& instance_, std::unique xclamped *= 86; - float yclamped = SDL_GameControllerGetAxis(controller.get(), SDL_CONTROLLER_AXIS_LEFTY); + float yclamped = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); if(yclamped < 0) { yclamped /= float(std::abs(SDL_JOYSTICK_AXIS_MIN)); } else { diff --git a/src/frontend/EmuThread.hpp b/src/frontend/EmuThread.hpp index 481e30b8..99c3b700 100644 --- a/src/frontend/EmuThread.hpp +++ b/src/frontend/EmuThread.hpp @@ -9,37 +9,37 @@ class EmuThread : public QThread { Q_OBJECT - std::unique_ptr instance; - std::unique_ptr wsiPlatform; - std::unique_ptr windowInfo; + std::shared_ptr instance; + std::shared_ptr wsiPlatform; + std::shared_ptr windowInfo; public: - explicit EmuThread(std::unique_ptr&& instance, std::unique_ptr&& wsiPlatform, std::unique_ptr&& windowInfo, SettingsWindow&) noexcept; + explicit EmuThread(const std::shared_ptr& instance, const std::shared_ptr& wsiPlatform, const std::shared_ptr& windowInfo, SettingsWindow&) noexcept; [[noreturn]] void run() noexcept override; - Util::AutoRelease controller; + SDL_GameController* controller = nullptr; ParallelRDP parallel; n64::Core core; SettingsWindow& settings; - bool running = false; - void TogglePause() - { - running = !running; + void TogglePause() { + core.pause = !core.pause; } - void Reset() - { - running = false; + void SetRender(bool v) { + core.render = v; + } + + void Reset() { + core.pause = true; core.Stop(); core.LoadROM(core.rom); - running = true; + core.pause = false; } - void Stop() - { + void Stop() { core.rom = {}; - running = false; + core.pause = true; core.Stop(); } }; \ No newline at end of file diff --git a/src/frontend/KaizenQt.cpp b/src/frontend/KaizenQt.cpp index c6803404..332fac16 100644 --- a/src/frontend/KaizenQt.cpp +++ b/src/frontend/KaizenQt.cpp @@ -28,16 +28,18 @@ KaizenQt::KaizenQt() noexcept : QWidget(nullptr) { grabKeyboard(); }); } + void KaizenQt::ConnectMainWindowSignalsToSlots() noexcept { connect(mainWindow.get(), &MainWindowController::OpenSettings, this, [this]() { settingsWindow->show(); }); connect(mainWindow.get(), &MainWindowController::OpenROM, this, &KaizenQt::LoadROM); - connect(mainWindow.get(), &MainWindowController::Exit, this, []() { - QApplication::quit(); - }); + connect(mainWindow.get(), &MainWindowController::Exit, this, &KaizenQt::Quit); connect(mainWindow.get(), &MainWindowController::Reset, emuThread.get(), &EmuThread::Reset); connect(mainWindow.get(), &MainWindowController::Stop, emuThread.get(), &EmuThread::Stop); + connect(mainWindow.get(), &MainWindowController::Stop, this, [this]() { + mainWindow->setWindowTitle("Kaizen"); + }); connect(mainWindow.get(), &MainWindowController::Pause, emuThread.get(), &EmuThread::TogglePause); } @@ -53,64 +55,70 @@ void KaizenQt::dropEvent(QDropEvent* event) { } void KaizenQt::LoadROM(const QString& fileName) noexcept { + mainWindow->view.actionPause->setEnabled(true); + mainWindow->view.actionReset->setEnabled(true); + mainWindow->view.actionStop->setEnabled(true); emuThread->start(); emuThread->core.LoadROM(fileName.toStdString()); + mainWindow->setWindowTitle(emuThread->core.cpu->GetMem().rom.gameNameDB.c_str()); } -void KaizenQt::closeEvent(QCloseEvent*) { - emuThread->Stop(); +void KaizenQt::Quit() noexcept { + if(emuThread) { + emuThread->SetRender(false); + emuThread->Stop(); + } + QApplication::quit(); } -void KaizenQt::LoadTAS(const QString& fileName) noexcept { - emuThread->core.LoadTAS(fileName.toStdString()); +void KaizenQt::LoadTAS(const QString& fileName) const noexcept { + emuThread->core.LoadTAS(fs::path(fileName.toStdString())); } void KaizenQt::keyPressEvent(QKeyEvent *e) { emuThread->core.pause = true; + n64::Mem& mem = emuThread->core.cpu->GetMem(); + n64::PIF& pif = mem.mmio.si.pif; + auto k = static_cast(e->key()); - if(k == settingsWindow->keyMap[0]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::A, true); - if(k == settingsWindow->keyMap[1]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::B, true); - if(k == settingsWindow->keyMap[2]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::Z, true); - if(k == settingsWindow->keyMap[3]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::Start, true); - if(k == settingsWindow->keyMap[4]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::LT, true); - if(k == settingsWindow->keyMap[5]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::RT, true); - if(k == settingsWindow->keyMap[6]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::DUp, true); - if(k == settingsWindow->keyMap[7]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::DDown, true); - if(k == settingsWindow->keyMap[8]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::DLeft, true); - if(k == settingsWindow->keyMap[9]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::DRight, true); - if(k == settingsWindow->keyMap[10]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::CUp, true); - if(k == settingsWindow->keyMap[11]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::CDown, true); - if(k == settingsWindow->keyMap[12]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::CLeft, true); - if(k == settingsWindow->keyMap[13]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::CRight, true); - if(k == settingsWindow->keyMap[14]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateAxis(0, n64::Controller::Axis::Y, 86); - if(k == settingsWindow->keyMap[15]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateAxis(0, n64::Controller::Axis::Y, -86); - if(k == settingsWindow->keyMap[16]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateAxis(0, n64::Controller::Axis::X, -86); - if(k == settingsWindow->keyMap[17]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateAxis(0, n64::Controller::Axis::X, 86); + for(int i = 0; i < 14; i++) { + if(k == settingsWindow->keyMap[i]) + pif.UpdateButton(0, static_cast(i), true); + } + + if (k == settingsWindow->keyMap[14]) + pif.UpdateAxis(0, n64::Controller::Axis::Y, 86); + if (k == settingsWindow->keyMap[15]) + pif.UpdateAxis(0, n64::Controller::Axis::Y, -86); + if (k == settingsWindow->keyMap[16]) + pif.UpdateAxis(0, n64::Controller::Axis::X, -86); + if (k == settingsWindow->keyMap[17]) + pif.UpdateAxis(0, n64::Controller::Axis::X, 86); + emuThread->core.pause = false; QWidget::keyPressEvent(e); } void KaizenQt::keyReleaseEvent(QKeyEvent *e) { emuThread->core.pause = true; + n64::Mem& mem = emuThread->core.cpu->GetMem(); + n64::PIF& pif = mem.mmio.si.pif; + auto k = static_cast(e->key()); - if (k == settingsWindow->keyMap[0]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::A, false); - if (k == settingsWindow->keyMap[1]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::B, false); - if (k == settingsWindow->keyMap[2]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::Z, false); - if (k == settingsWindow->keyMap[3]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::Start, false); - if (k == settingsWindow->keyMap[4]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::LT, false); - if (k == settingsWindow->keyMap[5]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::RT, false); - if (k == settingsWindow->keyMap[6]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::DUp, false); - if (k == settingsWindow->keyMap[7]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::DDown, false); - if (k == settingsWindow->keyMap[8]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::DLeft, false); - if (k == settingsWindow->keyMap[9]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::DRight, false); - if (k == settingsWindow->keyMap[10]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::CUp, false); - if (k == settingsWindow->keyMap[11]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::CDown, false); - if (k == settingsWindow->keyMap[12]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::CLeft, false); - if (k == settingsWindow->keyMap[13]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateButton(0, n64::Controller::Key::CRight, false); - if (k == settingsWindow->keyMap[14]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateAxis(0, n64::Controller::Axis::Y, 0); - if (k == settingsWindow->keyMap[15]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateAxis(0, n64::Controller::Axis::Y, 0); - if (k == settingsWindow->keyMap[16]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateAxis(0, n64::Controller::Axis::X, 0); - if (k == settingsWindow->keyMap[17]) emuThread->core.cpu->GetMem().mmio.si.pif.UpdateAxis(0, n64::Controller::Axis::X, 0); + for(int i = 0; i < 14; i++) { + if(k == settingsWindow->keyMap[i]) + pif.UpdateButton(0, static_cast(i), false); + } + + if (k == settingsWindow->keyMap[14]) + pif.UpdateAxis(0, n64::Controller::Axis::Y, 0); + if (k == settingsWindow->keyMap[15]) + pif.UpdateAxis(0, n64::Controller::Axis::Y, 0); + if (k == settingsWindow->keyMap[16]) + pif.UpdateAxis(0, n64::Controller::Axis::X, 0); + if (k == settingsWindow->keyMap[17]) + pif.UpdateAxis(0, n64::Controller::Axis::X, 0); + emuThread->core.pause = false; - QWidget::keyPressEvent(e); + QWidget::keyReleaseEvent(e); } \ No newline at end of file diff --git a/src/frontend/KaizenQt.hpp b/src/frontend/KaizenQt.hpp index 21f1cdd7..23c129a9 100644 --- a/src/frontend/KaizenQt.hpp +++ b/src/frontend/KaizenQt.hpp @@ -27,14 +27,14 @@ class KaizenQt : public QWidget { Q_OBJECT public: KaizenQt() noexcept; - void LoadTAS(const QString& path) noexcept; + void LoadTAS(const QString& path) const noexcept; void LoadROM(const QString& path) noexcept; void dropEvent(QDropEvent*) override; void dragEnterEvent(QDragEnterEvent*) override; void keyPressEvent(QKeyEvent*) override; void keyReleaseEvent(QKeyEvent*) override; - void closeEvent(QCloseEvent*) override; private: + void Quit() noexcept; void ConnectMainWindowSignalsToSlots() noexcept; std::unique_ptr mainWindow; std::unique_ptr settingsWindow; diff --git a/src/frontend/MainWindow.cpp b/src/frontend/MainWindow.cpp index 85b58fef..b4588467 100644 --- a/src/frontend/MainWindow.cpp +++ b/src/frontend/MainWindow.cpp @@ -19,9 +19,6 @@ void MainWindowController::ConnectSignalsToSlots() noexcept { "All supported types (*.zip *.ZIP *.7z *.7Z *.rar *.RAR *.tar *.TAR *.n64 *.N64 *.v64 *.V64 *.z64 *.Z64)"); if (!file_name.isEmpty()) { - view.actionPause->setEnabled(true); - view.actionReset->setEnabled(true); - view.actionStop->setEnabled(true); emit OpenROM(file_name); view.vulkanWidget->show(); } @@ -31,6 +28,10 @@ void MainWindowController::ConnectSignalsToSlots() noexcept { emit Exit(); }); + connect(this, &MainWindowController::destroyed, this, [this]() { + emit Exit(); + }); + connect(view.actionReset, &QAction::triggered, this, [this]() { emit Reset(); }); diff --git a/src/frontend/main.cpp b/src/frontend/main.cpp index 5e08594d..ebec2993 100644 --- a/src/frontend/main.cpp +++ b/src/frontend/main.cpp @@ -10,15 +10,17 @@ int main(int argc, char** argv) { QCommandLineParser parser; parser.setApplicationDescription(QCoreApplication::applicationName()); parser.addHelpOption(); - parser.addPositionalArgument("rom", "Rom to launch from command-line"); - parser.addPositionalArgument("m64", "Mupen Movie to replay"); + parser.addOptions({ + {"rom", "Rom to launch from command-line", "path"}, + {"movie", "Mupen Movie to replay", "path"} + }); parser.process(app); KaizenQt kaizenQt; - if (parser.positionalArguments().size() > 0) { - kaizenQt.LoadROM(parser.positionalArguments().first()); - if (parser.positionalArguments().size() > 1) { - kaizenQt.LoadTAS(parser.positionalArguments()[1]); + if (parser.isSet("rom")) { + kaizenQt.LoadROM(parser.value("rom")); + if (parser.isSet("movie")) { + kaizenQt.LoadTAS(parser.value("movie")); } } diff --git a/src/frontend/mainwindow.ui b/src/frontend/mainwindow.ui index 9dda72b4..c1ad4475 100644 --- a/src/frontend/mainwindow.ui +++ b/src/frontend/mainwindow.ui @@ -7,7 +7,7 @@ 0 0 800 - 600 + 646 diff --git a/src/utils/FloatingPoint.hpp b/src/utils/FloatingPoint.hpp new file mode 100644 index 00000000..078d2f4c --- /dev/null +++ b/src/utils/FloatingPoint.hpp @@ -0,0 +1,121 @@ +// +// Created by simone on 6/25/24. +// + +#pragma once +#include +#include +#include + +namespace Util { +template +static inline T roundCeil(float f) { +#ifdef SIMD_SUPPORT + __m128 t = _mm_set_ss(f); + t = _mm_round_ss(t, t, _MM_FROUND_TO_POS_INF); + return _mm_cvtss_f32(t); +#else + return ceilf(f); +#endif +} + +template +static inline T roundCeil(double f) { +#ifdef SIMD_SUPPORT + __m128d t = _mm_set_sd(f); + t = _mm_round_sd(t, t, _MM_FROUND_TO_POS_INF); + return _mm_cvtsd_f64(t); +#else + return ceil(f); +#endif +} + +template +static inline T roundNearest(float f) { +#ifdef SIMD_SUPPORT + __m128 t = _mm_set_ss(f); + t = _mm_round_ss(t, t, _MM_FROUND_TO_NEAREST_INT); + return _mm_cvtss_f32(t); +#else + return roundf(f); +#endif +} + +template +static inline T roundNearest(double f) { +#ifdef SIMD_SUPPORT + __m128d t = _mm_set_sd(f); + t = _mm_round_sd(t, t, _MM_FROUND_TO_NEAREST_INT); + return _mm_cvtsd_f64(t); +#else + return round(f); +#endif +} + +template +static inline T roundCurrent(float f) { +#ifdef SIMD_SUPPORT + auto t = _mm_set_ss(f); + t = _mm_round_ss(t, t, _MM_FROUND_CUR_DIRECTION); + return _mm_cvtss_f32(t); +#else + return rint(f); +#endif +} + +template +static inline T roundCurrent(double f) { +#ifdef SIMD_SUPPORT + auto t = _mm_set_sd(f); + t = _mm_round_sd(t, t, _MM_FROUND_CUR_DIRECTION); + return _mm_cvtsd_f64(t); +#else + return rint(f); +#endif +} + + +template +static inline T roundFloor(float f) { +#ifdef SIMD_SUPPORT +__m128 t = _mm_set_ss(f); + t = _mm_round_ss(t, t, _MM_FROUND_TO_NEG_INF); + return _mm_cvtss_f32(t); +#else +return floor(f); +#endif +} + +template +static inline T roundFloor(double f) { +#ifdef SIMD_SUPPORT + __m128d t = _mm_set_sd(f); + t = _mm_round_sd(t, t, _MM_FROUND_TO_NEG_INF); + return _mm_cvtsd_f64(t); +#else + return floor(f); +#endif +} + +template +static inline T roundTrunc(float f) { +#ifdef SIMD_SUPPORT + __m128 t = _mm_set_ss(f); + t = _mm_round_ss(t, t, _MM_FROUND_TO_ZERO); + return _mm_cvtss_f32(t); +#else + return trunc(f); +#endif +} + +template +static inline T roundTrunc(double f) { +#ifdef SIMD_SUPPORT + __m128d t = _mm_set_sd(f); + t = _mm_round_sd(t, t, _MM_FROUND_TO_ZERO); + return _mm_cvtsd_f64(t); +#else + return trunc(f); +#endif +} +} \ No newline at end of file diff --git a/src/utils/MemoryHelpers.hpp b/src/utils/MemoryHelpers.hpp index e9f72ec5..2fae7a09 100644 --- a/src/utils/MemoryHelpers.hpp +++ b/src/utils/MemoryHelpers.hpp @@ -6,46 +6,6 @@ #include namespace Util { -template -struct AutoRelease { - AutoRelease(void (*dtor)(T*), const char* name = "") : dtor(dtor), name(name) { } - - AutoRelease(T* (*ctor)(Args...), void (*dtor)(T*), const char* name = "", Args... args) : dtor(dtor), name(name) { - thing = ctor(args...); - } - - T* get() { - if (!thing) { - Util::panic("AutoRelease::{} is null!", name); - } - return thing; - } - - void Construct(T* (*ctor)(Args...), Args... args) { - if(thing) { - dtor(thing); - } - - thing = ctor(args...); - } - - void Destroy() { - if(thing) { - dtor(thing); - } - } - - ~AutoRelease() { - if(thing) { - dtor(thing); - } - } -private: - const char* name = ""; - T* thing = nullptr; - void (*dtor)(T*) = nullptr; -}; - template static FORCE_INLINE T ReadAccess(const u8* data, u32 index) { if constexpr (sizeof(T) == 8) { diff --git a/src/utils/log.hpp b/src/utils/log.hpp index 8ad7e7d2..f87f3c19 100644 --- a/src/utils/log.hpp +++ b/src/utils/log.hpp @@ -9,13 +9,13 @@ namespace Util { enum LogLevel : u8 { - Trace, Debug, Info, Warn, Error, Always + Trace, Debug, Warn, Info, Error, Always }; #ifndef NDEBUG static constexpr auto globalLogLevel = Debug; #else -static constexpr auto globalLogLevel = Error; +static constexpr auto globalLogLevel = Info; #endif template