Add ISaveRam implementation for AppleII, fix bug which caused DiskIIController to not be correctly stated

This commit is contained in:
CasualPokePlayer 2023-04-04 23:46:26 -07:00
parent 9ae5f786de
commit 8737203f3a
11 changed files with 142 additions and 20 deletions

View File

@ -4,21 +4,28 @@ namespace Jellyfish.Virtu
{
internal abstract class Disk525
{
protected byte[] Data;
protected readonly byte[] Data;
private readonly byte[] Original;
public bool IsWriteProtected;
protected Disk525(byte[] data, bool isWriteProtected)
{
Data = data;
Original = (byte[])data.Clone();
IsWriteProtected = isWriteProtected;
}
public virtual void Sync(IComponentSerializer ser)
{
ser.Sync(nameof(Data), ref Data, false);
ser.SyncDelta("DataDelta", Original, Data);
ser.Sync(nameof(IsWriteProtected), ref IsWriteProtected);
}
public void DeltaUpdate(Action<byte[], byte[]> callback)
{
callback(Data, Original);
}
public static Disk525 CreateDisk(string name, byte[] data, bool isWriteProtected)
{
if (name == null)

View File

@ -1,4 +1,6 @@
namespace Jellyfish.Virtu
using System;
namespace Jellyfish.Virtu
{
public sealed class DiskIIDrive
{
@ -29,8 +31,6 @@
ser.Sync(nameof(_trackNumber), ref _trackNumber);
ser.Sync(nameof(_trackOffset), ref _trackOffset);
ser.Sync(nameof(_trackData), ref _trackData, false);
// TODO: save the delta, this is saving the rom into save states
_disk?.Sync(ser);
}
@ -120,5 +120,11 @@
private const int TrackNumberMax = 0x44;
private const int PhaseCount = 4;
public void DeltaUpdate(Action<byte[], byte[]> callback)
{
FlushTrack();
_disk.DeltaUpdate(callback);
}
}
}

View File

@ -13,5 +13,7 @@
void Sync(string name, ref byte[] val, bool useNull);
void Sync(string name, ref ushort[] val, bool useNull);
void Sync(string name, ref int[] val, bool useNull);
void SyncDelta<T>(string name, T[] original, T[] current) where T : unmanaged;
}
}

Binary file not shown.

View File

@ -198,7 +198,7 @@ namespace BizHawk.Client.Common
CommonEntriesFor(VSystemID.Raw.AmstradCPC, basePath: Path.Combine(".", "AmstradCPC"), omitSaveRAM: true),
CommonEntriesFor(VSystemID.Raw.AppleII, basePath: Path.Combine(".", "Apple II"), omitSaveRAM: true),
CommonEntriesFor(VSystemID.Raw.AppleII, basePath: Path.Combine(".", "Apple II")),
CommonEntriesFor(VSystemID.Raw.Arcade, basePath: Path.Combine(".", "Arcade")),

View File

@ -26,6 +26,7 @@ using BizHawk.Emulation.Common.Base_Implementations;
using BizHawk.Emulation.Cores;
using BizHawk.Emulation.Cores.Arcades.MAME;
using BizHawk.Emulation.Cores.Calculators.TI83;
using BizHawk.Emulation.Cores.Computers.AppleII;
using BizHawk.Emulation.Cores.Computers.Commodore64;
using BizHawk.Emulation.Cores.Consoles.NEC.PCE;
using BizHawk.Emulation.Cores.Consoles.Nintendo.Ares64;
@ -1922,7 +1923,7 @@ namespace BizHawk.Client.EmuHawk
byte[] sram;
// some cores might not know how big the saveram ought to be, so just send it the whole file
if (Emulator is C64 or MGBAHawk or NeoGeoPort or NES { BoardName: "FDS" })
if (Emulator is AppleII or C64 or MGBAHawk or NeoGeoPort or NES { BoardName: "FDS" })
{
sram = File.ReadAllBytes(saveRamPath);
}

View File

@ -379,6 +379,7 @@ namespace BizHawk.Emulation.Common
case ".PO":
case ".DO":
case ".NIB":
game.System = VSystemID.Raw.AppleII;
break;

View File

@ -0,0 +1,78 @@
using System;
using System.IO;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.AppleII
{
public partial class AppleII : ISaveRam
{
private byte[][] _diskDeltas;
private void InitSaveRam()
{
_diskDeltas = new byte[DiskCount][];
}
public bool SaveRamModified => true;
public byte[] CloneSaveRam()
{
using var ms = new MemoryStream();
using var bw = new BinaryWriter(ms);
SaveDelta();
bw.Write(DiskCount);
for (var i = 0; i < DiskCount; i++)
{
bw.WriteByteBuffer(_diskDeltas[i]);
}
return ms.ToArray();
}
public void StoreSaveRam(byte[] data)
{
using var ms = new MemoryStream(data, false);
using var br = new BinaryReader(ms);
var ndisks = br.ReadInt32();
if (ndisks != DiskCount)
{
throw new InvalidOperationException("Disk count mismatch!");
}
for (var i = 0; i < DiskCount; i++)
{
_diskDeltas[i] = br.ReadByteBuffer(returnNull: true);
}
LoadDelta(true);
}
private void SaveDelta()
{
_machine.DiskIIController.Drive1.DeltaUpdate((original, current) =>
{
_diskDeltas[CurrentDisk] = DeltaSerializer.GetDelta<byte>(original, current).ToArray();
});
}
private void LoadDelta(bool maybeDifferent)
{
_machine.DiskIIController.Drive1.DeltaUpdate((original, current) =>
{
if (_diskDeltas[CurrentDisk] is not null)
{
DeltaSerializer.ApplyDelta<byte>(original, current, _diskDeltas[CurrentDisk]);
}
else if (maybeDifferent)
{
original.AsSpan().CopyTo(current);
}
});
}
}
}

View File

@ -30,6 +30,7 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
private void SyncState(AppleSerializer ser)
{
int version = 2;
var oldCurrentDisk = CurrentDisk;
ser.BeginSection(nameof(AppleII));
ser.Sync(nameof(version), ref version);
ser.Sync("Frame", ref _frame);
@ -66,10 +67,27 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
_machine.NoSlotClock.Sync(ser);
ser.EndSection();
// disk change, we need to swap disks so SyncDelta works later
if (CurrentDisk != oldCurrentDisk)
{
_machine.DiskIIController.Drive1.InsertDisk("junk" + _romSet[CurrentDisk].Extension, (byte[])_romSet[CurrentDisk].Data.Clone(), false);
}
ser.BeginSection("DiskIIController");
_machine.DiskIIController.Sync(ser);
ser.EndSection();
ser.BeginSection("InactiveDisks");
for (var i = 0; i < DiskCount; i++)
{
// the current disk is handled in DiskIIController
if (i != CurrentDisk)
{
ser.Sync($"DiskDelta{i}", ref _diskDeltas[i], useNull: true);
}
}
ser.EndSection();
ser.EndSection();
}

View File

@ -23,22 +23,30 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
[CoreConstructor(VSystemID.Raw.AppleII)]
public AppleII(CoreLoadParameters<Settings, SyncSettings> lp)
{
_romSet = lp.Roms.Select(r => r.RomData).ToList();
static (byte[], string) GetRomAndExt(IRomAsset romAssert)
{
var ext = romAssert.Extension.ToUpperInvariant();
return ext switch
{
".DSK" or ".PO" or ".DO" or ".NIB" => (romAssert.FileData, ext),
".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?)
_ => (romAssert.FileData, ".DSK") // no idea, let's assume it's just a .DSK?
};
}
_romSet = lp.Roms.Select(GetRomAndExt).ToList();
var ser = new BasicServiceProvider(this);
ServiceProvider = ser;
const string TRACE_HEADER = "6502: PC, opcode, register (A, X, Y, P, SP, Cy), flags (NVTBDIZC)";
_tracer = new TraceBuffer(TRACE_HEADER);
_disk1 = _romSet[0];
_appleIIRom = lp.Comm.CoreFileProvider.GetFirmwareOrThrow(new(SystemId, "AppleIIe"), "The Apple IIe BIOS firmware is required");
_diskIIRom = lp.Comm.CoreFileProvider.GetFirmwareOrThrow(new(SystemId, "DiskII"), "The DiskII firmware is required");
_machine = new Components(_appleIIRom, _diskIIRom);
// make a writable memory stream cloned from the rom.
// for junk.dsk the .dsk is important because it determines the format from that
InitSaveRam();
InitDisk();
ser.Register<ITraceable>(_tracer);
@ -55,11 +63,10 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
private static readonly ControllerDefinition AppleIIController;
private readonly List<byte[]> _romSet = new List<byte[]>();
private readonly List<(byte[] Data, string Extension)> _romSet;
private readonly ITraceable _tracer;
private readonly Components _machine;
private byte[] _disk1;
private readonly byte[] _appleIIRom;
private readonly byte[] _diskIIRom;
@ -75,12 +82,14 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
public void SetDisk(int discNum)
{
SaveDelta();
CurrentDisk = discNum;
InitDisk();
}
private void IncrementDisk()
{
SaveDelta();
CurrentDisk++;
if (CurrentDisk >= _romSet.Count)
{
@ -92,6 +101,7 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
private void DecrementDisk()
{
SaveDelta();
CurrentDisk--;
if (CurrentDisk < 0)
{
@ -103,11 +113,10 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
private void InitDisk()
{
_disk1 = _romSet[CurrentDisk];
// make a writable memory stream cloned from the rom.
// for junk.dsk the .dsk is important because it determines the format from that
_machine.Memory.DiskIIController.Drive1.InsertDisk("junk.dsk", (byte[])_disk1.Clone(), false);
// the extension is important here because it determines the format from that
_machine.DiskIIController.Drive1.InsertDisk("junk" + _romSet[CurrentDisk].Extension, (byte[])_romSet[CurrentDisk].Data.Clone(), false);
LoadDelta(false);
}
private static readonly List<string> RealButtons = new List<string>(Keyboard.GetKeyNames()
@ -120,7 +129,7 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
};
public bool DriveLightEnabled => true;
public bool DriveLightOn => _machine.Memory.DiskIIController.DriveLight;
public bool DriveLightOn => _machine.DiskIIController.DriveLight;
private bool _nextPressed;
private bool _prevPressed;

View File

@ -32,7 +32,7 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
emptySlot,
emptySlot,
emptySlot,
new DiskIIController(Video, diskIIRom),
DiskIIController,
emptySlot);
Cpu.Reset();