diff --git a/Assets/dll/ares64_interpreter.wbx.zst b/Assets/dll/ares64_interpreter.wbx.zst index 8a13eada308..1fff9061dd5 100644 Binary files a/Assets/dll/ares64_interpreter.wbx.zst and b/Assets/dll/ares64_interpreter.wbx.zst differ diff --git a/Assets/dll/ares64_recompiler.wbx.zst b/Assets/dll/ares64_recompiler.wbx.zst index 53d50d2c0dd..632f668dde2 100644 Binary files a/Assets/dll/ares64_recompiler.wbx.zst and b/Assets/dll/ares64_recompiler.wbx.zst differ diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Ares64/Ares64.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Ares64/Ares64.cs index 7ab15239592..ea7b2ff49e0 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Ares64/Ares64.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Ares64/Ares64.cs @@ -1,5 +1,6 @@ +using System.Buffers.Binary; +using System.Collections.Generic; using System.IO; -using System.Linq; using BizHawk.Common; using BizHawk.Emulation.Common; @@ -64,18 +65,40 @@ static bool IsGBRom(byte[] rom) return ninLogoSha1 == SHA1Checksum.ComputeDigestHex(new ReadOnlySpan(rom).Slice(0x104, 48)); } + List gbRoms = [ ]; + byte[] rom = null; + byte[] disk = null; + byte[] error = null; + byte[] sdCard = null; + // TODO: this is normally handled frontend side // except XML files don't go through RomGame // (probably should, but needs refactoring) - foreach (var r in lp.Roms) _ = N64RomByteswapper.ToZ64Native(r.RomData); // no-op if N64 magic bytes not present + foreach (var r in lp.Roms) + { + _ = N64RomByteswapper.ToZ64Native(r.RomData); // no-op if N64 magic bytes not present - var gbRoms = lp.Roms.FindAll(r => IsGBRom(r.FileData)).Select(r => r.FileData).ToArray(); - var rom = lp.Roms.Find(r => !gbRoms.Contains(r.FileData) && (char)r.RomData[0x3B] is 'N' or 'C')?.RomData; - var (disk, error) = TransformDisk(lp.Roms.Find(r => !gbRoms.Contains(r.FileData) && r.RomData != rom)?.FileData); + if (r.FileData.Length is 0x435B0C0 or 0x3DEC800) + { + (disk, error) = TransformDisk(r.FileData); + } + else if (IsGBRom(r.FileData)) + { + gbRoms.Add(r.FileData); + } + else if ((char) r.RomData[0x3B] is 'N' or 'C') + { + rom = r.RomData; + } + else if(BinaryPrimitives.ReadUInt16LittleEndian(r.FileData.AsSpan(0x1FE, 2)) == 0xAA55) + { + sdCard = r.FileData; + } + } if (rom is null && disk is null) { - if (gbRoms.Length == 0 && lp.Roms.Count == 1) // let's just assume it's an N64 ROM then + if (gbRoms.Count == 0 && lp.Roms.Count == 1) // let's just assume it's an N64 ROM then { rom = lp.Roms[0].RomData; } @@ -116,7 +139,7 @@ static bool IsGBRom(byte[] rom) } byte[] GetGBRomOrNull(int n) - => n < gbRoms.Length ? gbRoms[n] : null; + => n < gbRoms.Count ? gbRoms[n] : null; unsafe { @@ -129,7 +152,8 @@ byte[] GetGBRomOrNull(int n) gb1RomPtr = GetGBRomOrNull(0), gb2RomPtr = GetGBRomOrNull(1), gb3RomPtr = GetGBRomOrNull(2), - gb4RomPtr = GetGBRomOrNull(3)) + gb4RomPtr = GetGBRomOrNull(3), + sdPtr = sdCard) { var loadData = new LibAres64.LoadData { @@ -151,6 +175,8 @@ byte[] GetGBRomOrNull(int n) Gb3RomLen = GetGBRomOrNull(2)?.Length ?? 0, Gb4RomData = (IntPtr)gb4RomPtr, Gb4RomLen = GetGBRomOrNull(3)?.Length ?? 0, + SdData = (IntPtr)sdPtr, + SdLen = sdCard?.Length ?? 0, }; if (!_core.Init(ref loadData, ControllerSettings, pal, GetRtcTime(!DeterministicEmulation))) { diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Ares64/LibAres64.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Ares64/LibAres64.cs index 3de167b1656..1bd897faef0 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Ares64/LibAres64.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Ares64/LibAres64.cs @@ -106,6 +106,8 @@ public struct LoadData public long Gb3RomLen; public IntPtr Gb4RomData; public long Gb4RomLen; + public IntPtr SdData; + public long SdLen; } [BizImport(CC)] diff --git a/waterbox/ares64/BizInterface.cpp b/waterbox/ares64/BizInterface.cpp index a29315dfad9..60d26dac0db 100644 --- a/waterbox/ares64/BizInterface.cpp +++ b/waterbox/ares64/BizInterface.cpp @@ -147,6 +147,7 @@ static array_view* diskErrorData = nullptr; static array_view* saveData = nullptr; static array_view* rtcData = nullptr; static array_view* gbRomData[4] = { nullptr, nullptr, nullptr, nullptr, }; +static array_view* sdData = nullptr; typedef enum { @@ -417,6 +418,8 @@ typedef struct u8* DiskErrorData; u64 DiskErrorLen; GbRom GbRoms[4]; + u8* SdData; + u64 SdLen; } LoadData; static bool LoadRom(LoadData* loadData, bool isPal) @@ -560,6 +563,16 @@ ECL_EXPORT bool Init(LoadData* loadData, ControllerType* controllers, bool isPal platform->bizpak->append(name, *iplData); } + if (loadData->SdData) + { + name = "sd.raw"; + len = loadData->SdLen; + data = new u8[len]; + memcpy(data, loadData->SdData, len); + sdData = new array_view(data, len); + platform->bizpak->append(name, *sdData); + } + string region = isPal ? "PAL" : "NTSC"; platform->bizpak->setAttribute("region", region); diff --git a/waterbox/ares64/ares/ares/n64/cartridge/cartridge.cpp b/waterbox/ares64/ares/ares/n64/cartridge/cartridge.cpp index 14a1f788046..9a627db1700 100644 --- a/waterbox/ares64/ares/ares/n64/cartridge/cartridge.cpp +++ b/waterbox/ares64/ares/ares/n64/cartridge/cartridge.cpp @@ -7,6 +7,7 @@ Cartridge& cartridge = cartridgeSlot.cartridge; #include "flash.cpp" #include "rtc.cpp" #include "joybus.cpp" +#include "sc64.cpp" #include "isviewer.cpp" #include "debugger.cpp" #include "serialization.cpp" @@ -54,6 +55,14 @@ auto Cartridge::connect() -> void { isviewer.tracer->setTerminal(true); } + if(auto fp = pak->read("sd.raw")) { + sc64.sd.allocate(fp->size()); + sc64.sd.load(fp); + sc64.buffer.allocate(0x2000); + sc64.buffer.fill(); + sc64.enabled = true; + } + debugger.load(node); power(false); @@ -69,6 +78,10 @@ auto Cartridge::disconnect() -> void { flash.reset(); isviewer.ram.reset(); pak.reset(); + if(sc64.enabled) { + sc64.sd.reset(); + sc64.buffer.reset(); + } node.reset(); } @@ -88,6 +101,12 @@ auto Cartridge::save() -> void { } rtc.save(); + + if(sc64.enabled) { + if(auto fp = system.pak->write("sd.raw")) { + sc64.sd.save(fp); + } + } } auto Cartridge::power(bool reset) -> void { diff --git a/waterbox/ares64/ares/ares/n64/cartridge/cartridge.hpp b/waterbox/ares64/ares/ares/n64/cartridge/cartridge.hpp index 9efbf30a4a4..3acdccaac4e 100644 --- a/waterbox/ares64/ares/ares/n64/cartridge/cartridge.hpp +++ b/waterbox/ares64/ares/ares/n64/cartridge/cartridge.hpp @@ -4,6 +4,41 @@ struct Cartridge { Memory::Readable16 rom; Memory::Writable16 ram; Memory::Writable16 eeprom; + struct SC64 { + static constexpr u32 SDSectorWords = 512/4; + + n1 enabled = false; + Memory::Writable sd; + Memory::Writable buffer; + + n2 lockState = 0; + n1 busy = 0; + n1 error = 0; + u32 data0 = 0; + u32 data1 = 0; + + n1 sdIsInit = 0; + u32 sdAddr = 0; + + auto regReadWord(u32 address) -> u64; + auto regWriteWord(u32 address, u64 data_) -> void; + + auto serialize(serializer& s) -> void; + + template + auto regRead(u32 address) -> u64 { + if constexpr(Size == Word) return regReadWord(address); + debug(unimplemented, "[SC64] Register read of size other than Word"); + return 0; //Other sizes unimplemented + } + + template + auto regWrite(u32 address, u64 data) -> void { + if constexpr(Size == Word) return regWriteWord(address, data); + debug(unimplemented, "[SC64] Register write of size other than Word"); + return; //Other sizes unimplemented + } + } sc64; struct Flash : Memory::Writable { template auto read(u32 address) -> u64 { diff --git a/waterbox/ares64/ares/ares/n64/cartridge/sc64.cpp b/waterbox/ares64/ares/ares/n64/cartridge/sc64.cpp new file mode 100644 index 00000000000..42edba29d26 --- /dev/null +++ b/waterbox/ares64/ares/ares/n64/cartridge/sc64.cpp @@ -0,0 +1,143 @@ +auto Cartridge::SC64::regReadWord(u32 address) -> u64 { + n32 data; + data.bit(0,31) = (address << 16) | (address & 0xFFFF); + address = (address & 0x1F) >> 2; + + if (lockState != 2) { + return u64(data); + } + + if(address == 0) { + //SCR, command execution and status + //Mostly unimplemented + data.bit(31) = busy; + data.bit(30) = error; + } + + if(address == 1) { + //DATA0, command arg/result 0 + data.bit(0,31) = data0; + } + + if(address == 2) { + //DATA1, command arg/result 1 + data.bit(0,31) = data1; + } + + if(address == 3) { + //IDENTIFIER, flashcart identifier + //Read-only + data.bit(0,31) = 0x53'43'76'32; /* SCv2 */ + } + + if(address == 4) { + //KEY + //Write-only + } + + if(address == 5) { + //IRQ + //Unimplemented + } + + if(address == 6) { + //AUX + //Unimplemented + } + + return u64(data); +} + +auto Cartridge::SC64::regWriteWord(u32 address, u64 data_) -> void { + address = (address & 0x1F) >> 2; + n32 data = data_; + + if(address == 0 && lockState == 2) { + //SCR, command execution and status + //Writes initiate a command + u8 cmd = data.bit(0,7); + + switch (cmd) { + case 'i': //SD Operation + if(data1 == 1) //SD Init + sdIsInit = true; + break; + + case 'I': //SD Sector Set + sdAddr = data0 * SC64::SDSectorWords * 4; + break; + + case 's': //SD Read Sectors + if(sdIsInit) { + u32 piAddress = data0; + u32 numSectors = data1; + u32 numWords = numSectors * SC64::SDSectorWords; + //Note: Only data buffer is supported here but real SC64 is more flexible + for(auto offset : range(numWords)) + buffer.write(piAddress + offset * 4, sd.read(sdAddr + offset * 4)); + } + break; + + case 'S': //SD Write Sectors + if(sdIsInit) { + u32 piAddress = data0; + u32 numSectors = data1; + u32 numWords = numSectors * SC64::SDSectorWords; + //Note: Only data buffer is supported here but real SC64 is more flexible + for(auto offset : range(numWords)) + sd.write(sdAddr + offset * 4, buffer.read(piAddress + offset * 4)); + } + break; + + default: break; //Unrecongized/Unimplemented + } + } + + if(address == 1 && lockState == 2) { + //DATA0, command arg/result 0 + data0 = data.bit(0,31); + } + + if(address == 2 && lockState == 2) { + //DATA1, command arg/result 1 + data1 = data.bit(0,31); + } + + if(address == 3 && lockState == 2) { + //IDENTIFIER, flashcart identifier + //Read-only + } + + if(address == 4) { + //KEY + if(data.bit(0,31) == 0x00000000 || data.bit(0,31) == 0xFFFFFFFF) + lockState = 0; + else if(data.bit(0,31) == 0x5F554E4C && lockState == 0) + lockState = 1; + else if(data.bit(0,31) == 0x4F434B5F && lockState == 1) + lockState = 2; + } + + if(address == 5 && lockState == 2) { + //IRQ + //Unimplemented + } + + if(address == 6 && lockState == 2) { + //AUX + //Unimplemented + } +} + +auto Cartridge::SC64::serialize(serializer& s) -> void { + s(enabled); + s(sd); + s(buffer); + s(lockState); + s(busy); + s(error); + s(data0); + s(data1); + s(sdIsInit); + s(sdAddr); +} diff --git a/waterbox/ares64/ares/ares/n64/cartridge/serialization.cpp b/waterbox/ares64/ares/ares/n64/cartridge/serialization.cpp index 72c96f2d97c..9667e4e6fbe 100644 --- a/waterbox/ares64/ares/ares/n64/cartridge/serialization.cpp +++ b/waterbox/ares64/ares/ares/n64/cartridge/serialization.cpp @@ -3,4 +3,5 @@ auto Cartridge::serialize(serializer& s) -> void { s(eeprom); s(flash); s(rtc); + s(sc64); } diff --git a/waterbox/ares64/ares/ares/n64/pi/bus.hpp b/waterbox/ares64/ares/ares/n64/pi/bus.hpp index 5473b833fb4..44447991ff7 100644 --- a/waterbox/ares64/ares/ares/n64/pi/bus.hpp +++ b/waterbox/ares64/ares/ares/n64/pi/bus.hpp @@ -50,6 +50,20 @@ inline auto PI::busRead(u32 address) -> u32 { if(address <= 0x1000'0000 + cartridge.rom.size - 1) { return cartridge.rom.read(address); } + if(cartridge.sc64.enabled) { + if(address <= 0x1ffd'ffff) { + return unmapped; + } + if(address <= 0x1ffe'0000 + cartridge.sc64.buffer.size - 1) { + return cartridge.sc64.buffer.read(address); + } + if(address <= 0x1ffe'ffff) { + return unmapped; + } + if(address <= 0x1fff'ffff) { + return cartridge.sc64.regRead(address); + } + } return unmapped; } @@ -105,6 +119,20 @@ inline auto PI::busWrite(u32 address, u32 data) -> void { if(address <= 0x1000'0000 + cartridge.rom.size - 1) { return cartridge.rom.write(address, data); } + if(cartridge.sc64.enabled) { + if(address <= 0x1ffd'ffff) { + return; + } + if(address <= 0x1ffe'0000 + cartridge.sc64.buffer.size - 1) { + return cartridge.sc64.buffer.write(address, data); + } + if(address <= 0x1ffe'ffff) { + return; + } + if(address <= 0x1fff'ffff) { + return cartridge.sc64.regWrite(address, data); + } + } if(address <= 0x7fff'ffff) return; }