初步实现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) if (AppEnvironment.EnableSuperSlowCPULogger)
{ {
logFile = @"D:\Temp\Essgee\log-lr35902.txt"; //logFile = @"D:\Temp\Essgee\log-lr35902.txt";
numLogEntries = 0; //numLogEntries = 0;
logEntries = new string[2000]; //logEntries = new string[2000];
} }
} }

View File

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

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Linq;
namespace Essgee.Emulation.Cartridges.Nintendo namespace Essgee.Emulation.Cartridges.Nintendo
{ {
@ -31,11 +32,26 @@ namespace Essgee.Emulation.Cartridges.Nintendo
public void LoadAxiStatus(AxiEssgssStatusData data) 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() 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(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; return data;
} }
#endregion #endregion

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Linq;
namespace Essgee.Emulation.Cartridges.Nintendo namespace Essgee.Emulation.Cartridges.Nintendo
{ {
@ -23,13 +24,25 @@ namespace Essgee.Emulation.Cartridges.Nintendo
} }
#region AxiState #region AxiState
public void LoadAxiStatus(AxiEssgssStatusData data) 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() 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(romBank)] = BitConverter.GetBytes(romBank);
data.MemberData[nameof(ramEnable)] = BitConverter.GetBytes(ramEnable);
return data; return data;
} }
#endregion #endregion

View File

@ -135,11 +135,26 @@ namespace Essgee.Emulation.Cartridges.Nintendo
public void LoadAxiStatus(AxiEssgssStatusData data) 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() 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(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; return data;
} }
#endregion #endregion

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Linq;
namespace Essgee.Emulation.Cartridges.Nintendo namespace Essgee.Emulation.Cartridges.Nintendo
{ {
@ -34,11 +35,26 @@ namespace Essgee.Emulation.Cartridges.Nintendo
public void LoadAxiStatus(AxiEssgssStatusData data) 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() 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; return data;
} }
#endregion #endregion

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Xml;
namespace Essgee.Emulation.Cartridges.Nintendo namespace Essgee.Emulation.Cartridges.Nintendo
{ {
@ -17,11 +18,17 @@ namespace Essgee.Emulation.Cartridges.Nintendo
public void LoadAxiStatus(AxiEssgssStatusData data) public void LoadAxiStatus(AxiEssgssStatusData data)
{ {
ramData = data.MemberData[nameof(ramData)];
hasBattery = BitConverter.ToBoolean(data.MemberData[nameof(hasBattery)]);
//看是否还需要补存储字段
} }
public AxiEssgssStatusData SaveAxiStatus() public AxiEssgssStatusData SaveAxiStatus()
{ {
AxiEssgssStatusData data = new AxiEssgssStatusData(); AxiEssgssStatusData data = new AxiEssgssStatusData();
data.MemberData[nameof(ramData)] = ramData;
data.MemberData[nameof(hasBattery)] = BitConverter.GetBytes(hasBattery);
//看是否还需要补存储字段
return data; return data;
} }
#endregion #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 => 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); (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; this.exceptionHandler = exceptionHandler;
emulator = (IMachine)Activator.CreateInstance(type); emulator = (IMachine)Activator.CreateInstance(type);
AxiStatus.Init(statusBytesCover);
} }
public void SetConfiguration(IConfiguration config) public void SetConfiguration(IConfiguration config)

View File

@ -136,11 +136,93 @@ namespace Essgee.Emulation.Machines
public void LoadAxiStatus(AxiEssgssStatusData data) 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() public AxiEssgssStatusData SaveAxiStatus()
{ {
AxiEssgssStatusData data = new AxiEssgssStatusData(); 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; return data;
} }
#endregion #endregion

View File

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

View File

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