初步实现GB即时存档

This commit is contained in:
sin365 2025-02-25 16:51:52 +08:00
parent 48f465c35e
commit 008a9bf6a5
11 changed files with 179 additions and 21 deletions

View File

@ -68,9 +68,9 @@ namespace Essgee.Emulation.CPU
if (AppEnvironment.EnableSuperSlowCPULogger)
{
logFile = @"D:\Temp\Essgee\log-lr35902.txt";
numLogEntries = 0;
logEntries = new string[2000];
//logFile = @"D:\Temp\Essgee\log-lr35902.txt";
//numLogEntries = 0;
//logEntries = new string[2000];
}
}

View File

@ -70,6 +70,7 @@ namespace Essgee.Emulation.Cartridges.Nintendo
public void LoadAxiStatus(AxiEssgssStatusData data)
{
//TODO GB相机暂时不实现
}
public AxiEssgssStatusData SaveAxiStatus()

View File

@ -1,4 +1,5 @@
using System;
using System.Linq;
namespace Essgee.Emulation.Cartridges.Nintendo
{
@ -31,11 +32,26 @@ namespace Essgee.Emulation.Cartridges.Nintendo
public void LoadAxiStatus(AxiEssgssStatusData data)
{
ramData = data.MemberData[nameof(ramData)];
hasBattery = BitConverter.ToBoolean(data.MemberData[nameof(hasBattery)]);
romBank = data.MemberData[nameof(romBank)].First();
ramBank = data.MemberData[nameof(ramBank)].First();
ramEnable = BitConverter.ToBoolean(data.MemberData[nameof(ramEnable)]);
bankingMode = data.MemberData[nameof(bankingMode)].First();
//看是否还需要补存储字段
}
public AxiEssgssStatusData SaveAxiStatus()
{
AxiEssgssStatusData data = new AxiEssgssStatusData();
data.MemberData[nameof(ramData)] = ramData;
data.MemberData[nameof(hasBattery)] = BitConverter.GetBytes(hasBattery);
//看是否还需要补存储字段
data.MemberData[nameof(romBank)] = BitConverter.GetBytes(romBank);
data.MemberData[nameof(ramBank)] = BitConverter.GetBytes(ramBank);
data.MemberData[nameof(ramEnable)] = BitConverter.GetBytes(ramEnable);
data.MemberData[nameof(bankingMode)] = BitConverter.GetBytes(bankingMode);
return data;
}
#endregion

View File

@ -1,4 +1,5 @@
using System;
using System.Linq;
namespace Essgee.Emulation.Cartridges.Nintendo
{
@ -23,13 +24,25 @@ namespace Essgee.Emulation.Cartridges.Nintendo
}
#region AxiState
public void LoadAxiStatus(AxiEssgssStatusData data)
{
ramData = data.MemberData[nameof(ramData)];
hasBattery = BitConverter.ToBoolean(data.MemberData[nameof(hasBattery)]);
romBank = data.MemberData[nameof(romBank)].First();
ramEnable = BitConverter.ToBoolean(data.MemberData[nameof(ramEnable)]);
//看是否还需要补存储字段
}
public AxiEssgssStatusData SaveAxiStatus()
{
AxiEssgssStatusData data = new AxiEssgssStatusData();
data.MemberData[nameof(ramData)] = ramData;
data.MemberData[nameof(hasBattery)] = BitConverter.GetBytes(hasBattery);
//看是否还需要补存储字段
data.MemberData[nameof(romBank)] = BitConverter.GetBytes(romBank);
data.MemberData[nameof(ramEnable)] = BitConverter.GetBytes(ramEnable);
return data;
}
#endregion

View File

@ -135,11 +135,26 @@ namespace Essgee.Emulation.Cartridges.Nintendo
public void LoadAxiStatus(AxiEssgssStatusData data)
{
ramData = data.MemberData[nameof(ramData)];
hasBattery = BitConverter.ToBoolean(data.MemberData[nameof(hasBattery)]);
hasRTC = BitConverter.ToBoolean(data.MemberData[nameof(hasRTC)]);
romBank = data.MemberData[nameof(romBank)].First();
ramBank = data.MemberData[nameof(ramBank)].First();
ramEnable = BitConverter.ToBoolean(data.MemberData[nameof(ramEnable)]);
//看是否还需要补存储字段
}
public AxiEssgssStatusData SaveAxiStatus()
{
AxiEssgssStatusData data = new AxiEssgssStatusData();
data.MemberData[nameof(ramData)] = ramData;
data.MemberData[nameof(hasBattery)] = BitConverter.GetBytes(hasBattery);
data.MemberData[nameof(hasRTC)] = BitConverter.GetBytes(hasRTC);
//看是否还需要补存储字段
data.MemberData[nameof(romBank)] = BitConverter.GetBytes(romBank);
data.MemberData[nameof(ramBank)] = BitConverter.GetBytes(ramBank);
data.MemberData[nameof(ramEnable)] = BitConverter.GetBytes(ramEnable);
return data;
}
#endregion

View File

@ -1,4 +1,5 @@
using System;
using System.Linq;
namespace Essgee.Emulation.Cartridges.Nintendo
{
@ -34,11 +35,26 @@ namespace Essgee.Emulation.Cartridges.Nintendo
public void LoadAxiStatus(AxiEssgssStatusData data)
{
ramData = data.MemberData[nameof(ramData)];
hasBattery = BitConverter.ToBoolean(data.MemberData[nameof(hasBattery)]);
hasRumble = BitConverter.ToBoolean(data.MemberData[nameof(hasRumble)]);
romBank = BitConverter.ToUInt16(data.MemberData[nameof(romBank)]);
ramBank = data.MemberData[nameof(ramBank)].First();
ramEnable = BitConverter.ToBoolean(data.MemberData[nameof(ramEnable)]);
//看是否还需要补存储字段
}
public AxiEssgssStatusData SaveAxiStatus()
{
AxiEssgssStatusData data = new AxiEssgssStatusData();
AxiEssgssStatusData data = new AxiEssgssStatusData();
data.MemberData[nameof(ramData)] = ramData;
data.MemberData[nameof(hasBattery)] = BitConverter.GetBytes(hasBattery);
data.MemberData[nameof(hasRumble)] = BitConverter.GetBytes(hasRumble);
//看是否还需要补存储字段
data.MemberData[nameof(romBank)] = BitConverter.GetBytes(romBank);
data.MemberData[nameof(ramBank)] = BitConverter.GetBytes(ramBank);
data.MemberData[nameof(ramEnable)] = BitConverter.GetBytes(ramEnable);
return data;
}
#endregion

View File

@ -1,4 +1,5 @@
using System;
using System.Xml;
namespace Essgee.Emulation.Cartridges.Nintendo
{
@ -17,11 +18,17 @@ namespace Essgee.Emulation.Cartridges.Nintendo
public void LoadAxiStatus(AxiEssgssStatusData data)
{
ramData = data.MemberData[nameof(ramData)];
hasBattery = BitConverter.ToBoolean(data.MemberData[nameof(hasBattery)]);
//看是否还需要补存储字段
}
public AxiEssgssStatusData SaveAxiStatus()
{
AxiEssgssStatusData data = new AxiEssgssStatusData();
data.MemberData[nameof(ramData)] = ramData;
data.MemberData[nameof(hasBattery)] = BitConverter.GetBytes(hasBattery);
//看是否还需要补存储字段
return data;
}
#endregion

View File

@ -99,11 +99,12 @@ namespace Essgee.Emulation
public (string Manufacturer, string Model, string DatFileName, double RefreshRate, double PixelAspectRatio, (string Name, string Description)[] RuntimeOptions) Information =>
(emulator.ManufacturerName, emulator.ModelName, emulator.DatFilename, emulator.RefreshRate, emulator.PixelAspectRatio, emulator.RuntimeOptions);
public EmulatorHandler(Type type, Action<Exception> exceptionHandler = null)
public EmulatorHandler(Type type, Action<Exception> exceptionHandler = null, IAxiEssgssStatusBytesCover statusBytesCover = null)
{
this.exceptionHandler = exceptionHandler;
emulator = (IMachine)Activator.CreateInstance(type);
AxiStatus.Init(statusBytesCover);
}
public void SetConfiguration(IConfiguration config)

View File

@ -136,11 +136,93 @@ namespace Essgee.Emulation.Machines
public void LoadAxiStatus(AxiEssgssStatusData data)
{
//config 暂时不需要存什么?
//configuration. = data.MemberData[nameof(configuration.)].ToEnum<TVStandard>();
if (data.MemberData.ContainsKey(nameof(bootstrap)))
bootstrap = data.MemberData[nameof(bootstrap)];
cartridge.LoadAxiStatus(data.ClassData[nameof(cartridge)]);
wram = data.MemberData[nameof(wram)];
hram = data.MemberData[nameof(hram)];
ie = data.MemberData[nameof(ie)].First();
cpu.LoadAxiStatus(data.ClassData[nameof(cpu)]);
video.LoadAxiStatus(data.ClassData[nameof(video)]);
audio.LoadAxiStatus(data.ClassData[nameof(audio)]);
//看是否还需要补存储字段
joypadRegister = data.MemberData[nameof(joypadRegister)].First();
serialData = data.MemberData[nameof(serialData)].First();
serialUseInternalClock = BitConverter.ToBoolean(data.MemberData[nameof(serialUseInternalClock)]);
divider = data.MemberData[nameof(divider)].First();
timerCounter = data.MemberData[nameof(timerCounter)].First();
clockCycleCount = BitConverter.ToUInt16(data.MemberData[nameof(clockCycleCount)]);
timerModulo = data.MemberData[nameof(timerModulo)].First();
timerRunning = BitConverter.ToBoolean(data.MemberData[nameof(timerRunning)]);
timerInputClock = data.MemberData[nameof(timerInputClock)].First();
timerOverflow = BitConverter.ToBoolean(data.MemberData[nameof(timerOverflow)]);
timerLoading = BitConverter.ToBoolean(data.MemberData[nameof(timerLoading)]);
irqVBlank = BitConverter.ToBoolean(data.MemberData[nameof(irqVBlank)]);
irqLCDCStatus = BitConverter.ToBoolean(data.MemberData[nameof(irqLCDCStatus)]);
irqTimerOverflow = BitConverter.ToBoolean(data.MemberData[nameof(irqTimerOverflow)]);
irqSerialIO = BitConverter.ToBoolean(data.MemberData[nameof(irqSerialIO)]);
irqKeypad = BitConverter.ToBoolean(data.MemberData[nameof(irqKeypad)]);
bootstrapDisabled = BitConverter.ToBoolean(data.MemberData[nameof(bootstrapDisabled)]);
serialBitsCounter = BitConverter.ToInt32(data.MemberData[nameof(serialBitsCounter)]);
serialCycles = BitConverter.ToInt32(data.MemberData[nameof(serialCycles)]);
currentMasterClockCyclesInFrame = BitConverter.ToInt32(data.MemberData[nameof(currentMasterClockCyclesInFrame)]);
totalMasterClockCyclesInFrame = BitConverter.ToInt32(data.MemberData[nameof(totalMasterClockCyclesInFrame)]);
ReconfigureSystem();
}
public AxiEssgssStatusData SaveAxiStatus()
{
AxiEssgssStatusData data = new AxiEssgssStatusData();
//config 暂时不需要存什么?
//data.MemberData[nameof(configuration.TVStandard)] = configuration.TVStandard.ToByteArray();
if (bootstrap != null)
data.MemberData[nameof(bootstrap)] = bootstrap;
data.ClassData[nameof(cartridge)] = cartridge.SaveAxiStatus();
data.MemberData[nameof(wram)] = wram;
data.MemberData[nameof(hram)] = hram;
data.MemberData[nameof(ie)] = BitConverter.GetBytes(ie);
data.ClassData[nameof(cpu)] = cpu.SaveAxiStatus();
data.ClassData[nameof(video)] = video.SaveAxiStatus();
data.ClassData[nameof(audio)] = audio.SaveAxiStatus();
//看是否还需要补存储字段
data.MemberData[nameof(joypadRegister)] = BitConverter.GetBytes(joypadRegister);
data.MemberData[nameof(serialData)] = BitConverter.GetBytes(serialData);
data.MemberData[nameof(serialUseInternalClock)] = BitConverter.GetBytes(serialUseInternalClock);
data.MemberData[nameof(divider)] = BitConverter.GetBytes(divider);
data.MemberData[nameof(timerCounter)] = BitConverter.GetBytes(timerCounter);
data.MemberData[nameof(clockCycleCount)] = BitConverter.GetBytes(clockCycleCount);
data.MemberData[nameof(timerModulo)] = BitConverter.GetBytes(timerModulo);
data.MemberData[nameof(timerRunning)] = BitConverter.GetBytes(timerRunning);
data.MemberData[nameof(timerInputClock)] = BitConverter.GetBytes(timerInputClock);
data.MemberData[nameof(timerOverflow)] = BitConverter.GetBytes(timerOverflow);
data.MemberData[nameof(timerLoading)] = BitConverter.GetBytes(timerLoading);
data.MemberData[nameof(irqVBlank)] = BitConverter.GetBytes(irqVBlank);
data.MemberData[nameof(irqLCDCStatus)] = BitConverter.GetBytes(irqLCDCStatus);
data.MemberData[nameof(irqTimerOverflow)] = BitConverter.GetBytes(irqTimerOverflow);
data.MemberData[nameof(irqSerialIO)] = BitConverter.GetBytes(irqSerialIO);
data.MemberData[nameof(irqKeypad)] = BitConverter.GetBytes(irqKeypad);
data.MemberData[nameof(bootstrapDisabled)] = BitConverter.GetBytes(bootstrapDisabled);
data.MemberData[nameof(serialBitsCounter)] = BitConverter.GetBytes(serialBitsCounter);
data.MemberData[nameof(serialCycles)] = BitConverter.GetBytes(serialCycles);
data.MemberData[nameof(currentMasterClockCyclesInFrame)] = BitConverter.GetBytes(currentMasterClockCyclesInFrame);
data.MemberData[nameof(totalMasterClockCyclesInFrame)] = BitConverter.GetBytes(totalMasterClockCyclesInFrame);
return data;
}
#endregion

View File

@ -301,7 +301,9 @@ namespace Essgee.Emulation.Machines
{
configuration.Region = data.MemberData[nameof(configuration.Region)].ToEnum<Region>();
bootstrap.LoadAxiStatus(data.ClassData[nameof(bootstrap)]);
if (data.ClassData.ContainsKey(nameof(bootstrap)))
bootstrap.LoadAxiStatus(data.ClassData[nameof(bootstrap)]);
cartridge.LoadAxiStatus(data.ClassData[nameof(cartridge)]);
wram = data.MemberData[nameof(wram)];
cpu.LoadAxiStatus(data.ClassData[nameof(cpu)]);
@ -327,7 +329,9 @@ namespace Essgee.Emulation.Machines
AxiEssgssStatusData data = new AxiEssgssStatusData();
data.MemberData[nameof(configuration.Region)] = configuration.Region.ToByteArray();
data.ClassData[nameof(bootstrap)] = bootstrap.SaveAxiStatus();
if (bootstrap != null)
data.ClassData[nameof(bootstrap)] = bootstrap.SaveAxiStatus();
data.ClassData[nameof(cartridge)] = cartridge.SaveAxiStatus();
data.MemberData[nameof(wram)] = wram;
data.ClassData[nameof(cpu)] = cpu.SaveAxiStatus();

View File

@ -12,6 +12,7 @@ public class AxiEssgssStatusData
public Dictionary<string, AxiEssgssStatusData> ClassData = new Dictionary<string, AxiEssgssStatusData>();
}
[Serializable]
public class AxiEssgssStatusData_2DArray
{
@ -30,25 +31,16 @@ public class AxiEssgssStatusData_2DArray
return array1D.CreateByteArray2D(rows, cols);
}
}
public static class AxiEssgssStatusDataExtention
internal static class AxiEssgssStatusDataExtention
{
public static byte[] ToByteArray(this AxiEssgssStatusData data)
internal static byte[] ToByteArray(this AxiEssgssStatusData data)
{
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(ms, data);
return ms.ToArray();
}
return AxiStatus.saveCover.ToByteArray(data);
}
public static AxiEssgssStatusData ToAxiEssgssStatusData(this byte[] byteArray)
internal static AxiEssgssStatusData ToAxiEssgssStatusData(this byte[] byteArray)
{
using (MemoryStream ms = new MemoryStream(byteArray))
{
BinaryFormatter formatter = new BinaryFormatter();
return (AxiEssgssStatusData)formatter.Deserialize(ms);
}
return AxiStatus.saveCover.ToAxiEssgssStatusData(byteArray);
}
}
public interface IAxiStatus
@ -57,8 +49,19 @@ public interface IAxiStatus
public AxiEssgssStatusData SaveAxiStatus();
}
public interface IAxiEssgssStatusBytesCover
{
public byte[] ToByteArray(AxiEssgssStatusData data);
public AxiEssgssStatusData ToAxiEssgssStatusData(byte[] byteArray);
}
internal static class AxiStatus
{
public static IAxiEssgssStatusBytesCover saveCover { get; private set; }
public static void Init(IAxiEssgssStatusBytesCover coverter)
{
saveCover = coverter;
}
// 将任意枚举数组转换为byte[]
public static byte[] ToByteArray<TEnum>(this TEnum[] enumArray) where TEnum : struct, Enum
{