Skip to content

Commit 8737203

Browse files
Add ISaveRam implementation for AppleII, fix bug which caused DiskIIController to not be correctly stated
1 parent 9ae5f78 commit 8737203

File tree

11 files changed

+142
-20
lines changed

11 files changed

+142
-20
lines changed

ExternalCoreProjects/Virtu/Disk525.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,28 @@ namespace Jellyfish.Virtu
44
{
55
internal abstract class Disk525
66
{
7-
protected byte[] Data;
7+
protected readonly byte[] Data;
8+
private readonly byte[] Original;
89
public bool IsWriteProtected;
910

1011
protected Disk525(byte[] data, bool isWriteProtected)
1112
{
1213
Data = data;
14+
Original = (byte[])data.Clone();
1315
IsWriteProtected = isWriteProtected;
1416
}
1517

1618
public virtual void Sync(IComponentSerializer ser)
1719
{
18-
ser.Sync(nameof(Data), ref Data, false);
20+
ser.SyncDelta("DataDelta", Original, Data);
1921
ser.Sync(nameof(IsWriteProtected), ref IsWriteProtected);
2022
}
2123

24+
public void DeltaUpdate(Action<byte[], byte[]> callback)
25+
{
26+
callback(Data, Original);
27+
}
28+
2229
public static Disk525 CreateDisk(string name, byte[] data, bool isWriteProtected)
2330
{
2431
if (name == null)

ExternalCoreProjects/Virtu/DiskIIDrive.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
namespace Jellyfish.Virtu
1+
using System;
2+
3+
namespace Jellyfish.Virtu
24
{
35
public sealed class DiskIIDrive
46
{
@@ -29,8 +31,6 @@ public void Sync(IComponentSerializer ser)
2931
ser.Sync(nameof(_trackNumber), ref _trackNumber);
3032
ser.Sync(nameof(_trackOffset), ref _trackOffset);
3133
ser.Sync(nameof(_trackData), ref _trackData, false);
32-
33-
// TODO: save the delta, this is saving the rom into save states
3434
_disk?.Sync(ser);
3535
}
3636

@@ -120,5 +120,11 @@ internal void FlushTrack()
120120

121121
private const int TrackNumberMax = 0x44;
122122
private const int PhaseCount = 4;
123+
124+
public void DeltaUpdate(Action<byte[], byte[]> callback)
125+
{
126+
FlushTrack();
127+
_disk.DeltaUpdate(callback);
128+
}
123129
}
124130
}

ExternalCoreProjects/Virtu/IComponentSerializer.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,7 @@ public interface IComponentSerializer
1313
void Sync(string name, ref byte[] val, bool useNull);
1414
void Sync(string name, ref ushort[] val, bool useNull);
1515
void Sync(string name, ref int[] val, bool useNull);
16+
17+
void SyncDelta<T>(string name, T[] original, T[] current) where T : unmanaged;
1618
}
1719
}

References/Virtu.dll

512 Bytes
Binary file not shown.

src/BizHawk.Client.Common/config/PathEntryCollection.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ public void ResolveWithDefaults()
198198

199199
CommonEntriesFor(VSystemID.Raw.AmstradCPC, basePath: Path.Combine(".", "AmstradCPC"), omitSaveRAM: true),
200200

201-
CommonEntriesFor(VSystemID.Raw.AppleII, basePath: Path.Combine(".", "Apple II"), omitSaveRAM: true),
201+
CommonEntriesFor(VSystemID.Raw.AppleII, basePath: Path.Combine(".", "Apple II")),
202202

203203
CommonEntriesFor(VSystemID.Raw.Arcade, basePath: Path.Combine(".", "Arcade")),
204204

src/BizHawk.Client.EmuHawk/MainForm.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
using BizHawk.Emulation.Cores;
2727
using BizHawk.Emulation.Cores.Arcades.MAME;
2828
using BizHawk.Emulation.Cores.Calculators.TI83;
29+
using BizHawk.Emulation.Cores.Computers.AppleII;
2930
using BizHawk.Emulation.Cores.Computers.Commodore64;
3031
using BizHawk.Emulation.Cores.Consoles.NEC.PCE;
3132
using BizHawk.Emulation.Cores.Consoles.Nintendo.Ares64;
@@ -1922,7 +1923,7 @@ private void LoadSaveRam()
19221923
byte[] sram;
19231924

19241925
// some cores might not know how big the saveram ought to be, so just send it the whole file
1925-
if (Emulator is C64 or MGBAHawk or NeoGeoPort or NES { BoardName: "FDS" })
1926+
if (Emulator is AppleII or C64 or MGBAHawk or NeoGeoPort or NES { BoardName: "FDS" })
19261927
{
19271928
sram = File.ReadAllBytes(saveRamPath);
19281929
}

src/BizHawk.Emulation.Common/Database/Database.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,7 @@ public static GameInfo GetGameInfo(byte[] romData, string fileName)
379379

380380
case ".PO":
381381
case ".DO":
382+
case ".NIB":
382383
game.System = VSystemID.Raw.AppleII;
383384
break;
384385

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
using System;
2+
using System.IO;
3+
4+
using BizHawk.Common;
5+
using BizHawk.Emulation.Common;
6+
7+
namespace BizHawk.Emulation.Cores.Computers.AppleII
8+
{
9+
public partial class AppleII : ISaveRam
10+
{
11+
private byte[][] _diskDeltas;
12+
13+
private void InitSaveRam()
14+
{
15+
_diskDeltas = new byte[DiskCount][];
16+
}
17+
18+
public bool SaveRamModified => true;
19+
20+
public byte[] CloneSaveRam()
21+
{
22+
using var ms = new MemoryStream();
23+
using var bw = new BinaryWriter(ms);
24+
25+
SaveDelta();
26+
bw.Write(DiskCount);
27+
for (var i = 0; i < DiskCount; i++)
28+
{
29+
bw.WriteByteBuffer(_diskDeltas[i]);
30+
}
31+
32+
return ms.ToArray();
33+
}
34+
35+
public void StoreSaveRam(byte[] data)
36+
{
37+
using var ms = new MemoryStream(data, false);
38+
using var br = new BinaryReader(ms);
39+
40+
var ndisks = br.ReadInt32();
41+
42+
if (ndisks != DiskCount)
43+
{
44+
throw new InvalidOperationException("Disk count mismatch!");
45+
}
46+
47+
for (var i = 0; i < DiskCount; i++)
48+
{
49+
_diskDeltas[i] = br.ReadByteBuffer(returnNull: true);
50+
}
51+
52+
LoadDelta(true);
53+
}
54+
55+
private void SaveDelta()
56+
{
57+
_machine.DiskIIController.Drive1.DeltaUpdate((original, current) =>
58+
{
59+
_diskDeltas[CurrentDisk] = DeltaSerializer.GetDelta<byte>(original, current).ToArray();
60+
});
61+
}
62+
63+
private void LoadDelta(bool maybeDifferent)
64+
{
65+
_machine.DiskIIController.Drive1.DeltaUpdate((original, current) =>
66+
{
67+
if (_diskDeltas[CurrentDisk] is not null)
68+
{
69+
DeltaSerializer.ApplyDelta<byte>(original, current, _diskDeltas[CurrentDisk]);
70+
}
71+
else if (maybeDifferent)
72+
{
73+
original.AsSpan().CopyTo(current);
74+
}
75+
});
76+
}
77+
}
78+
}

src/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IStatable.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public void LoadStateBinary(BinaryReader reader)
3030
private void SyncState(AppleSerializer ser)
3131
{
3232
int version = 2;
33+
var oldCurrentDisk = CurrentDisk;
3334
ser.BeginSection(nameof(AppleII));
3435
ser.Sync(nameof(version), ref version);
3536
ser.Sync("Frame", ref _frame);
@@ -66,10 +67,27 @@ private void SyncState(AppleSerializer ser)
6667
_machine.NoSlotClock.Sync(ser);
6768
ser.EndSection();
6869

70+
// disk change, we need to swap disks so SyncDelta works later
71+
if (CurrentDisk != oldCurrentDisk)
72+
{
73+
_machine.DiskIIController.Drive1.InsertDisk("junk" + _romSet[CurrentDisk].Extension, (byte[])_romSet[CurrentDisk].Data.Clone(), false);
74+
}
75+
6976
ser.BeginSection("DiskIIController");
7077
_machine.DiskIIController.Sync(ser);
7178
ser.EndSection();
7279

80+
ser.BeginSection("InactiveDisks");
81+
for (var i = 0; i < DiskCount; i++)
82+
{
83+
// the current disk is handled in DiskIIController
84+
if (i != CurrentDisk)
85+
{
86+
ser.Sync($"DiskDelta{i}", ref _diskDeltas[i], useNull: true);
87+
}
88+
}
89+
ser.EndSection();
90+
7391
ser.EndSection();
7492
}
7593

src/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.cs

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,30 @@ static AppleII()
2323
[CoreConstructor(VSystemID.Raw.AppleII)]
2424
public AppleII(CoreLoadParameters<Settings, SyncSettings> lp)
2525
{
26-
_romSet = lp.Roms.Select(r => r.RomData).ToList();
26+
static (byte[], string) GetRomAndExt(IRomAsset romAssert)
27+
{
28+
var ext = romAssert.Extension.ToUpperInvariant();
29+
return ext switch
30+
{
31+
".DSK" or ".PO" or ".DO" or ".NIB" => (romAssert.FileData, ext),
32+
".2mg" => throw new NotSupportedException("Unsupported extension .2mg!"), // TODO: add a way to support this (we have hashes of this format in our db it seems?)
33+
_ => (romAssert.FileData, ".DSK") // no idea, let's assume it's just a .DSK?
34+
};
35+
}
36+
37+
_romSet = lp.Roms.Select(GetRomAndExt).ToList();
2738
var ser = new BasicServiceProvider(this);
2839
ServiceProvider = ser;
2940

3041
const string TRACE_HEADER = "6502: PC, opcode, register (A, X, Y, P, SP, Cy), flags (NVTBDIZC)";
3142
_tracer = new TraceBuffer(TRACE_HEADER);
3243

33-
_disk1 = _romSet[0];
34-
3544
_appleIIRom = lp.Comm.CoreFileProvider.GetFirmwareOrThrow(new(SystemId, "AppleIIe"), "The Apple IIe BIOS firmware is required");
3645
_diskIIRom = lp.Comm.CoreFileProvider.GetFirmwareOrThrow(new(SystemId, "DiskII"), "The DiskII firmware is required");
3746

3847
_machine = new Components(_appleIIRom, _diskIIRom);
3948

40-
// make a writable memory stream cloned from the rom.
41-
// for junk.dsk the .dsk is important because it determines the format from that
49+
InitSaveRam();
4250
InitDisk();
4351

4452
ser.Register<ITraceable>(_tracer);
@@ -55,11 +63,10 @@ public AppleII(CoreLoadParameters<Settings, SyncSettings> lp)
5563

5664
private static readonly ControllerDefinition AppleIIController;
5765

58-
private readonly List<byte[]> _romSet = new List<byte[]>();
66+
private readonly List<(byte[] Data, string Extension)> _romSet;
5967
private readonly ITraceable _tracer;
6068

6169
private readonly Components _machine;
62-
private byte[] _disk1;
6370
private readonly byte[] _appleIIRom;
6471
private readonly byte[] _diskIIRom;
6572

@@ -75,12 +82,14 @@ public int CurrentDisk
7582

7683
public void SetDisk(int discNum)
7784
{
85+
SaveDelta();
7886
CurrentDisk = discNum;
7987
InitDisk();
8088
}
8189

8290
private void IncrementDisk()
8391
{
92+
SaveDelta();
8493
CurrentDisk++;
8594
if (CurrentDisk >= _romSet.Count)
8695
{
@@ -92,6 +101,7 @@ private void IncrementDisk()
92101

93102
private void DecrementDisk()
94103
{
104+
SaveDelta();
95105
CurrentDisk--;
96106
if (CurrentDisk < 0)
97107
{
@@ -103,11 +113,10 @@ private void DecrementDisk()
103113

104114
private void InitDisk()
105115
{
106-
_disk1 = _romSet[CurrentDisk];
107-
108116
// make a writable memory stream cloned from the rom.
109-
// for junk.dsk the .dsk is important because it determines the format from that
110-
_machine.Memory.DiskIIController.Drive1.InsertDisk("junk.dsk", (byte[])_disk1.Clone(), false);
117+
// the extension is important here because it determines the format from that
118+
_machine.DiskIIController.Drive1.InsertDisk("junk" + _romSet[CurrentDisk].Extension, (byte[])_romSet[CurrentDisk].Data.Clone(), false);
119+
LoadDelta(false);
111120
}
112121

113122
private static readonly List<string> RealButtons = new List<string>(Keyboard.GetKeyNames()
@@ -120,7 +129,7 @@ private void InitDisk()
120129
};
121130

122131
public bool DriveLightEnabled => true;
123-
public bool DriveLightOn => _machine.Memory.DiskIIController.DriveLight;
132+
public bool DriveLightOn => _machine.DiskIIController.DriveLight;
124133

125134
private bool _nextPressed;
126135
private bool _prevPressed;

0 commit comments

Comments
 (0)