Skip to content

Commit e88d8d9

Browse files
Thar0YoshiRulz
andauthored
ares64 core: Add basic support for the SC64 SD card interface (#4438)
* ares64 core: Add basic support for the SC64 SD card interface Co-authored-by: YoshiRulz <[email protected]>
1 parent 1b07297 commit e88d8d9

File tree

10 files changed

+280
-12
lines changed

10 files changed

+280
-12
lines changed
-10.8 KB
Binary file not shown.
-7.15 KB
Binary file not shown.

src/BizHawk.Emulation.Cores/Consoles/Nintendo/Ares64/Ares64.cs

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
using System.Collections.Generic;
12
using System.IO;
2-
using System.Linq;
33

44
using BizHawk.Common;
55
using BizHawk.Emulation.Common;
@@ -13,6 +13,8 @@ public partial class Ares64 : WaterboxCore, IRegionable
1313
{
1414
private readonly LibAres64 _core;
1515
private readonly Ares64Disassembler _disassembler;
16+
private const int MameFormatSize = 0x435B0C0;
17+
private const int NddFormatSize = 0x3DEC800;
1618

1719
[CoreConstructor(VSystemID.Raw.N64)]
1820
public Ares64(CoreLoadParameters<Ares64Settings, Ares64SyncSettings> lp)
@@ -64,18 +66,40 @@ static bool IsGBRom(byte[] rom)
6466
return ninLogoSha1 == SHA1Checksum.ComputeDigestHex(new ReadOnlySpan<byte>(rom).Slice(0x104, 48));
6567
}
6668

69+
List<byte[]> gbRoms = [ ];
70+
byte[] rom = null;
71+
byte[] disk = null;
72+
byte[] error = null;
73+
byte[] sdCard = null;
74+
6775
// TODO: this is normally handled frontend side
6876
// except XML files don't go through RomGame
6977
// (probably should, but needs refactoring)
70-
foreach (var r in lp.Roms) _ = N64RomByteswapper.ToZ64Native(r.RomData); // no-op if N64 magic bytes not present
78+
foreach (var r in lp.Roms)
79+
{
80+
_ = N64RomByteswapper.ToZ64Native(r.RomData); // no-op if N64 magic bytes not present
7181

72-
var gbRoms = lp.Roms.FindAll(r => IsGBRom(r.FileData)).Select(r => r.FileData).ToArray();
73-
var rom = lp.Roms.Find(r => !gbRoms.Contains(r.FileData) && (char)r.RomData[0x3B] is 'N' or 'C')?.RomData;
74-
var (disk, error) = TransformDisk(lp.Roms.Find(r => !gbRoms.Contains(r.FileData) && r.RomData != rom)?.FileData);
82+
if (r.FileData.Length is MameFormatSize or NddFormatSize)
83+
{
84+
(disk, error) = TransformDisk(r.FileData);
85+
}
86+
else if (IsGBRom(r.FileData))
87+
{
88+
gbRoms.Add(r.FileData);
89+
}
90+
else if ((char) r.RomData[0x3B] is 'N' or 'C')
91+
{
92+
rom = r.RomData;
93+
}
94+
else if (r.FileData.AsSpan(start: 0x1FE) is [ 0x55, 0xAA, .. ])
95+
{
96+
sdCard = r.FileData;
97+
}
98+
}
7599

76100
if (rom is null && disk is null)
77101
{
78-
if (gbRoms.Length == 0 && lp.Roms.Count == 1) // let's just assume it's an N64 ROM then
102+
if (gbRoms.Count == 0 && lp.Roms.Count == 1) // let's just assume it's an N64 ROM then
79103
{
80104
rom = lp.Roms[0].RomData;
81105
}
@@ -116,7 +140,7 @@ static bool IsGBRom(byte[] rom)
116140
}
117141

118142
byte[] GetGBRomOrNull(int n)
119-
=> n < gbRoms.Length ? gbRoms[n] : null;
143+
=> n < gbRoms.Count ? gbRoms[n] : null;
120144

121145
unsafe
122146
{
@@ -129,7 +153,8 @@ byte[] GetGBRomOrNull(int n)
129153
gb1RomPtr = GetGBRomOrNull(0),
130154
gb2RomPtr = GetGBRomOrNull(1),
131155
gb3RomPtr = GetGBRomOrNull(2),
132-
gb4RomPtr = GetGBRomOrNull(3))
156+
gb4RomPtr = GetGBRomOrNull(3),
157+
sdPtr = sdCard)
133158
{
134159
var loadData = new LibAres64.LoadData
135160
{
@@ -151,6 +176,8 @@ byte[] GetGBRomOrNull(int n)
151176
Gb3RomLen = GetGBRomOrNull(2)?.Length ?? 0,
152177
Gb4RomData = (IntPtr)gb4RomPtr,
153178
Gb4RomLen = GetGBRomOrNull(3)?.Length ?? 0,
179+
SdData = (IntPtr)sdPtr,
180+
SdLen = sdCard?.Length ?? 0,
154181
};
155182
if (!_core.Init(ref loadData, ControllerSettings, pal, GetRtcTime(!DeterministicEmulation)))
156183
{
@@ -352,7 +379,7 @@ static bool RepeatCheck(ReadOnlySpan<byte> slice, int size, int repeat)
352379
{
353380
for (int i = 0; i < 4; i++)
354381
{
355-
var systemBlock = disk.Length == 0x3DEC800 ? (systemBlocks[i] + 2) ^ 1 : (systemBlocks[i] + 2);
382+
var systemBlock = disk.Length == NddFormatSize ? (systemBlocks[i] + 2) ^ 1 : (systemBlocks[i] + 2);
356383
var systemOffset = systemBlock * 0x4D08;
357384
ret[systemBlocks[i] + 2] = 1;
358385
if (disk[systemOffset + 0x00] != 0x00) continue;
@@ -414,10 +441,10 @@ private static (byte[] Disk, byte[] Error) TransformDisk(byte[] disk)
414441
if (disk is null) return default;
415442

416443
// already in mame format
417-
if (disk.Length == 0x435B0C0) return (disk, CreateErrorTable(disk));
444+
if (disk.Length == MameFormatSize) return (disk, CreateErrorTable(disk));
418445

419446
// ndd is always 0x3DEC800 bytes apparently?
420-
if (disk.Length != 0x3DEC800) return default;
447+
if (disk.Length != NddFormatSize) return default;
421448

422449
// need the error table for this
423450
var errorTable = CreateErrorTable(disk);
@@ -453,7 +480,7 @@ private static (byte[] Disk, byte[] Error) TransformDisk(byte[] disk)
453480
var dataFormat = new ReadOnlySpan<byte>(disk, systemOffset, 0xE8);
454481

455482
var diskIndex = 0;
456-
var ret = new byte[0x435B0C0];
483+
var ret = new byte[MameFormatSize];
457484

458485
var type = dataFormat[5] & 0xF;
459486
var vzone = 0;

src/BizHawk.Emulation.Cores/Consoles/Nintendo/Ares64/LibAres64.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ public struct LoadData
106106
public long Gb3RomLen;
107107
public IntPtr Gb4RomData;
108108
public long Gb4RomLen;
109+
public IntPtr SdData;
110+
public long SdLen;
109111
}
110112

111113
[BizImport(CC)]

waterbox/ares64/BizInterface.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ static array_view<u8>* diskErrorData = nullptr;
147147
static array_view<u8>* saveData = nullptr;
148148
static array_view<u8>* rtcData = nullptr;
149149
static array_view<u8>* gbRomData[4] = { nullptr, nullptr, nullptr, nullptr, };
150+
static array_view<u8>* sdData = nullptr;
150151

151152
typedef enum
152153
{
@@ -417,6 +418,8 @@ typedef struct
417418
u8* DiskErrorData;
418419
u64 DiskErrorLen;
419420
GbRom GbRoms[4];
421+
u8* SdData;
422+
u64 SdLen;
420423
} LoadData;
421424

422425
static bool LoadRom(LoadData* loadData, bool isPal)
@@ -560,6 +563,16 @@ ECL_EXPORT bool Init(LoadData* loadData, ControllerType* controllers, bool isPal
560563
platform->bizpak->append(name, *iplData);
561564
}
562565

566+
if (loadData->SdData)
567+
{
568+
name = "sd.raw";
569+
len = loadData->SdLen;
570+
data = new u8[len];
571+
memcpy(data, loadData->SdData, len);
572+
sdData = new array_view<u8>(data, len);
573+
platform->bizpak->append(name, *sdData);
574+
}
575+
563576
string region = isPal ? "PAL" : "NTSC";
564577
platform->bizpak->setAttribute("region", region);
565578

waterbox/ares64/ares/ares/n64/cartridge/cartridge.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Cartridge& cartridge = cartridgeSlot.cartridge;
77
#include "flash.cpp"
88
#include "rtc.cpp"
99
#include "joybus.cpp"
10+
#include "sc64.cpp"
1011
#include "isviewer.cpp"
1112
#include "debugger.cpp"
1213
#include "serialization.cpp"
@@ -54,6 +55,14 @@ auto Cartridge::connect() -> void {
5455
isviewer.tracer->setTerminal(true);
5556
}
5657

58+
if(auto fp = pak->read("sd.raw")) {
59+
sc64.sd.allocate(fp->size());
60+
sc64.sd.load(fp);
61+
sc64.buffer.allocate(0x2000);
62+
sc64.buffer.fill();
63+
sc64.enabled = true;
64+
}
65+
5766
debugger.load(node);
5867

5968
power(false);
@@ -69,6 +78,10 @@ auto Cartridge::disconnect() -> void {
6978
flash.reset();
7079
isviewer.ram.reset();
7180
pak.reset();
81+
if(sc64.enabled) {
82+
sc64.sd.reset();
83+
sc64.buffer.reset();
84+
}
7285
node.reset();
7386
}
7487

@@ -88,6 +101,12 @@ auto Cartridge::save() -> void {
88101
}
89102

90103
rtc.save();
104+
105+
if(sc64.enabled) {
106+
if(auto fp = system.pak->write("sd.raw")) {
107+
sc64.sd.save(fp);
108+
}
109+
}
91110
}
92111

93112
auto Cartridge::power(bool reset) -> void {

waterbox/ares64/ares/ares/n64/cartridge/cartridge.hpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,41 @@ struct Cartridge {
44
Memory::Readable16 rom;
55
Memory::Writable16 ram;
66
Memory::Writable16 eeprom;
7+
struct SC64 {
8+
static constexpr u32 SDSectorWords = 512/4;
9+
10+
n1 enabled = false;
11+
Memory::Writable sd;
12+
Memory::Writable buffer;
13+
14+
n2 lockState = 0;
15+
n1 busy = 0;
16+
n1 error = 0;
17+
u32 data0 = 0;
18+
u32 data1 = 0;
19+
20+
n1 sdIsInit = 0;
21+
u32 sdAddr = 0;
22+
23+
auto regReadWord(u32 address) -> u64;
24+
auto regWriteWord(u32 address, u64 data_) -> void;
25+
26+
auto serialize(serializer& s) -> void;
27+
28+
template<u32 Size>
29+
auto regRead(u32 address) -> u64 {
30+
if constexpr(Size == Word) return regReadWord(address);
31+
debug(unimplemented, "[SC64] Register read of size other than Word");
32+
return 0; //Other sizes unimplemented
33+
}
34+
35+
template<u32 Size>
36+
auto regWrite(u32 address, u64 data) -> void {
37+
if constexpr(Size == Word) return regWriteWord(address, data);
38+
debug(unimplemented, "[SC64] Register write of size other than Word");
39+
return; //Other sizes unimplemented
40+
}
41+
} sc64;
742
struct Flash : Memory::Writable {
843
template<u32 Size>
944
auto read(u32 address) -> u64 {
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
auto Cartridge::SC64::regReadWord(u32 address) -> u64 {
2+
n32 data;
3+
data.bit(0,31) = (address << 16) | (address & 0xFFFF);
4+
address = (address & 0x1F) >> 2;
5+
6+
if (lockState != 2) {
7+
return u64(data);
8+
}
9+
10+
if(address == 0) {
11+
//SCR, command execution and status
12+
//Mostly unimplemented
13+
data.bit(31) = busy;
14+
data.bit(30) = error;
15+
}
16+
17+
if(address == 1) {
18+
//DATA0, command arg/result 0
19+
data.bit(0,31) = data0;
20+
}
21+
22+
if(address == 2) {
23+
//DATA1, command arg/result 1
24+
data.bit(0,31) = data1;
25+
}
26+
27+
if(address == 3) {
28+
//IDENTIFIER, flashcart identifier
29+
//Read-only
30+
data.bit(0,31) = 0x53'43'76'32; /* SCv2 */
31+
}
32+
33+
if(address == 4) {
34+
//KEY
35+
//Write-only
36+
}
37+
38+
if(address == 5) {
39+
//IRQ
40+
//Unimplemented
41+
}
42+
43+
if(address == 6) {
44+
//AUX
45+
//Unimplemented
46+
}
47+
48+
return u64(data);
49+
}
50+
51+
auto Cartridge::SC64::regWriteWord(u32 address, u64 data_) -> void {
52+
address = (address & 0x1F) >> 2;
53+
n32 data = data_;
54+
55+
if(address == 0 && lockState == 2) {
56+
//SCR, command execution and status
57+
//Writes initiate a command
58+
u8 cmd = data.bit(0,7);
59+
60+
switch (cmd) {
61+
case 'i': //SD Operation
62+
if(data1 == 1) //SD Init
63+
sdIsInit = true;
64+
break;
65+
66+
case 'I': //SD Sector Set
67+
sdAddr = data0 * SC64::SDSectorWords * 4;
68+
break;
69+
70+
case 's': //SD Read Sectors
71+
if(sdIsInit) {
72+
u32 piAddress = data0;
73+
u32 numSectors = data1;
74+
u32 numWords = numSectors * SC64::SDSectorWords;
75+
//Note: Only data buffer is supported here but real SC64 is more flexible
76+
for(auto offset : range(numWords))
77+
buffer.write<Word>(piAddress + offset * 4, sd.read<Word>(sdAddr + offset * 4));
78+
}
79+
break;
80+
81+
case 'S': //SD Write Sectors
82+
if(sdIsInit) {
83+
u32 piAddress = data0;
84+
u32 numSectors = data1;
85+
u32 numWords = numSectors * SC64::SDSectorWords;
86+
//Note: Only data buffer is supported here but real SC64 is more flexible
87+
for(auto offset : range(numWords))
88+
sd.write<Word>(sdAddr + offset * 4, buffer.read<Word>(piAddress + offset * 4));
89+
}
90+
break;
91+
92+
default: break; //Unrecongized/Unimplemented
93+
}
94+
}
95+
96+
if(address == 1 && lockState == 2) {
97+
//DATA0, command arg/result 0
98+
data0 = data.bit(0,31);
99+
}
100+
101+
if(address == 2 && lockState == 2) {
102+
//DATA1, command arg/result 1
103+
data1 = data.bit(0,31);
104+
}
105+
106+
if(address == 3 && lockState == 2) {
107+
//IDENTIFIER, flashcart identifier
108+
//Read-only
109+
}
110+
111+
if(address == 4) {
112+
//KEY
113+
if(data.bit(0,31) == 0x00000000 || data.bit(0,31) == 0xFFFFFFFF)
114+
lockState = 0;
115+
else if(data.bit(0,31) == 0x5F554E4C && lockState == 0)
116+
lockState = 1;
117+
else if(data.bit(0,31) == 0x4F434B5F && lockState == 1)
118+
lockState = 2;
119+
}
120+
121+
if(address == 5 && lockState == 2) {
122+
//IRQ
123+
//Unimplemented
124+
}
125+
126+
if(address == 6 && lockState == 2) {
127+
//AUX
128+
//Unimplemented
129+
}
130+
}
131+
132+
auto Cartridge::SC64::serialize(serializer& s) -> void {
133+
s(enabled);
134+
s(sd);
135+
s(buffer);
136+
s(lockState);
137+
s(busy);
138+
s(error);
139+
s(data0);
140+
s(data1);
141+
s(sdIsInit);
142+
s(sdAddr);
143+
}

0 commit comments

Comments
 (0)