GBC即时存档基本实现

This commit is contained in:
sin365 2025-02-26 15:48:05 +08:00
parent ec09050634
commit eb19e99ac2
17 changed files with 260 additions and 44 deletions

View File

@ -19,5 +19,19 @@
samplesPerFrame = cyclesPerFrame = cyclesPerSample = -1;
}
#region AxiState
public void LoadAxiStatus(AxiEssgssStatusData data)
{
base.LoadAxiStatus(data);
}
public AxiEssgssStatusData SaveAxiStatus()
{
AxiEssgssStatusData data = base.SaveAxiStatus();
return data;
}
#endregion
}
}

View File

@ -133,15 +133,60 @@ namespace Essgee.Emulation.Audio
public void LoadAxiStatus(AxiEssgssStatusData data)
{
volumeRightLeft = data.MemberData[nameof(volumeRightLeft)];
vinEnableRightLeft = data.MemberData[nameof(vinEnableRightLeft)].ToBoolArray();
clockRate = BitConverter.ToDouble(data.MemberData[nameof(clockRate)]);
refreshRate = BitConverter.ToDouble(data.MemberData[nameof(refreshRate)]);
channel1Enable = data.MemberData[nameof(channel1Enable)].ToBoolArray();
channel2Enable = data.MemberData[nameof(channel2Enable)].ToBoolArray();
channel3Enable = data.MemberData[nameof(channel3Enable)].ToBoolArray();
channel4Enable = data.MemberData[nameof(channel4Enable)].ToBoolArray();
isSoundHwEnabled = BitConverter.ToBoolean(data.MemberData[nameof(isSoundHwEnabled)]);
samplesPerFrame = BitConverter.ToInt32(data.MemberData[nameof(samplesPerFrame)]);
cyclesPerFrame = BitConverter.ToInt32(data.MemberData[nameof(cyclesPerFrame)]);
cyclesPerSample = BitConverter.ToInt32(data.MemberData[nameof(cyclesPerSample)]);
sampleCycleCount = BitConverter.ToInt32(data.MemberData[nameof(sampleCycleCount)]);
frameCycleCount = BitConverter.ToInt32(data.MemberData[nameof(frameCycleCount)]);
channel1ForceEnable = BitConverter.ToBoolean(data.MemberData[nameof(channel1ForceEnable)]);
channel2ForceEnable = BitConverter.ToBoolean(data.MemberData[nameof(channel2ForceEnable)]);
channel3ForceEnable = BitConverter.ToBoolean(data.MemberData[nameof(channel3ForceEnable)]);
channel4ForceEnable = BitConverter.ToBoolean(data.MemberData[nameof(channel4ForceEnable)]);
}
public AxiEssgssStatusData SaveAxiStatus()
{
AxiEssgssStatusData data = new AxiEssgssStatusData();
data.MemberData[nameof(volumeRightLeft)] = volumeRightLeft;
data.MemberData[nameof(vinEnableRightLeft)] = vinEnableRightLeft.ToByteArray();
data.MemberData[nameof(clockRate)] = BitConverter.GetBytes(clockRate);
data.MemberData[nameof(refreshRate)] = BitConverter.GetBytes(refreshRate);
data.MemberData[nameof(channel1Enable)] = channel1Enable.ToByteArray();
data.MemberData[nameof(channel2Enable)] = channel2Enable.ToByteArray();
data.MemberData[nameof(channel3Enable)] = channel3Enable.ToByteArray();
data.MemberData[nameof(channel4Enable)] = channel4Enable.ToByteArray();
data.MemberData[nameof(isSoundHwEnabled)] = BitConverter.GetBytes(isSoundHwEnabled);
data.MemberData[nameof(samplesPerFrame)] = BitConverter.GetBytes(samplesPerFrame);
data.MemberData[nameof(cyclesPerFrame)] = BitConverter.GetBytes(cyclesPerFrame);
data.MemberData[nameof(cyclesPerSample)] = BitConverter.GetBytes(cyclesPerSample);
data.MemberData[nameof(sampleCycleCount)] = BitConverter.GetBytes(sampleCycleCount);
data.MemberData[nameof(frameCycleCount)] = BitConverter.GetBytes(frameCycleCount);
data.MemberData[nameof(channel1ForceEnable)] = BitConverter.GetBytes(channel1ForceEnable);
data.MemberData[nameof(channel2ForceEnable)] = BitConverter.GetBytes(channel2ForceEnable);
data.MemberData[nameof(channel3ForceEnable)] = BitConverter.GetBytes(channel3ForceEnable);
data.MemberData[nameof(channel4ForceEnable)] = BitConverter.GetBytes(channel4ForceEnable);
return data;
}
#endregion

View File

@ -3,7 +3,7 @@ using System;
namespace Essgee.Emulation.Audio
{
interface IAudio : IAxiStatus
interface IAudio : IAxiEssgssStatus
{
event EventHandler<EnqueueSamplesEventArgs> EnqueueSamples;
void OnEnqueueSamples(EnqueueSamplesEventArgs e);

View File

@ -1,6 +1,6 @@
namespace Essgee.Emulation.CPU
{
interface ICPU : IAxiStatus
interface ICPU : IAxiEssgssStatus
{
void Startup();
void Shutdown();

View File

@ -1,4 +1,9 @@
namespace Essgee.Emulation.CPU
using Essgee.Emulation.Cartridges;
using Essgee.Emulation.Video;
using System;
using UnityEngine.UIElements;
namespace Essgee.Emulation.CPU
{
public class SM83CGB : SM83
{
@ -7,6 +12,21 @@
public SM83CGB(MemoryReadDelegate memoryRead, MemoryWriteDelegate memoryWrite) : base(memoryRead, memoryWrite) { }
#region AxiState
public void LoadAxiStatus(AxiEssgssStatusData data)
{
base.LoadAxiStatus(data);
IsDoubleSpeed = BitConverter.ToBoolean(data.MemberData[nameof(IsDoubleSpeed)]);
}
public AxiEssgssStatusData SaveAxiStatus()
{
AxiEssgssStatusData data = base.SaveAxiStatus();
data.MemberData[nameof(IsDoubleSpeed)] = BitConverter.GetBytes(IsDoubleSpeed);
return data;
}
#endregion
protected override void EnterHaltState()
{
if (ime)

View File

@ -1,6 +1,6 @@
namespace Essgee.Emulation.Cartridges
{
internal interface ICartridge : IAxiStatus
internal interface ICartridge : IAxiEssgssStatus
{
void LoadRom(byte[] data);
void LoadRam(byte[] data);

View File

@ -70,12 +70,25 @@ namespace Essgee.Emulation.Cartridges.Nintendo
public void LoadAxiStatus(AxiEssgssStatusData data)
{
//TODO GB相机暂时不实现
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)]);
cameraCycles = BitConverter.ToInt32(data.MemberData[nameof(cameraCycles)]);
camClocksLeft = BitConverter.ToInt32(data.MemberData[nameof(camClocksLeft)]);
}
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(cameraCycles)] = BitConverter.GetBytes(cameraCycles);
data.MemberData[nameof(camClocksLeft)] = BitConverter.GetBytes(camClocksLeft);
return data;
}
#endregion

View File

@ -402,15 +402,6 @@ namespace Essgee.Emulation.Machines
audio?.Shutdown();
}
public void SetState(Dictionary<string, object> state)
{
throw new NotImplementedException();
}
public Dictionary<string, object> GetState()
{
throw new NotImplementedException();
}
public Dictionary<string, object> GetDebugInformation()
{

View File

@ -162,18 +162,129 @@ namespace Essgee.Emulation.Machines
public GameBoyColor() { }
#region AxiState
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.Array2DMemberData[nameof(wram)].Get2DArrayBytesData();
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)]);
serialFastClockSpeed = BitConverter.ToBoolean(data.MemberData[nameof(serialFastClockSpeed)]);
serialTransferInProgress = BitConverter.ToBoolean(data.MemberData[nameof(serialTransferInProgress)]);
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)]);
speedIsDouble = BitConverter.ToBoolean(data.MemberData[nameof(speedIsDouble)]);
speedSwitchPending = BitConverter.ToBoolean(data.MemberData[nameof(speedSwitchPending)]);
bootstrapDisabled = BitConverter.ToBoolean(data.MemberData[nameof(bootstrapDisabled)]);
irSendingSignal = BitConverter.ToBoolean(data.MemberData[nameof(irSendingSignal)]);
irNotReceivingSignal = BitConverter.ToBoolean(data.MemberData[nameof(irNotReceivingSignal)]);
irReadEnableA = BitConverter.ToBoolean(data.MemberData[nameof(irReadEnableA)]);
irReadEnableB = BitConverter.ToBoolean(data.MemberData[nameof(irReadEnableB)]);
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.Array2DMemberData[nameof(wram)] = new AxiEssgssStatusData_2DArray(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(serialFastClockSpeed)] = BitConverter.GetBytes(serialFastClockSpeed);
data.MemberData[nameof(serialTransferInProgress)] = BitConverter.GetBytes(serialTransferInProgress);
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(speedIsDouble)] = BitConverter.GetBytes(speedIsDouble);
data.MemberData[nameof(speedSwitchPending)] = BitConverter.GetBytes(speedSwitchPending);
data.MemberData[nameof(bootstrapDisabled)] = BitConverter.GetBytes(bootstrapDisabled);
data.MemberData[nameof(irSendingSignal)] = BitConverter.GetBytes(irSendingSignal);
data.MemberData[nameof(irNotReceivingSignal)] = BitConverter.GetBytes(irNotReceivingSignal);
data.MemberData[nameof(irReadEnableA)] = BitConverter.GetBytes(irReadEnableA);
data.MemberData[nameof(irReadEnableB)] = BitConverter.GetBytes(irReadEnableB);
//看是否需要记录这部分
//ushort[] irDatabase;
//int irDatabaseBaseIndex, irDatabaseStep;
//int irDatabaseCurrentIndex, irCycles;
//bool irExternalTransferActive;
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
public void Initialize()
{
bootstrap = null;

View File

@ -5,7 +5,7 @@ using System.Collections.Generic;
namespace Essgee.Emulation.Machines
{
public interface IMachine : IAxiStatus
public interface IMachine : IAxiEssgssStatus
{
event EventHandler<SendLogMessageEventArgs> SendLogMessage;
event EventHandler<EventArgs> EmulationReset;

View File

@ -1,6 +1,6 @@
namespace Essgee.Emulation.Peripherals
{
interface IPeripheral : IAxiStatus
interface IPeripheral : IAxiEssgssStatus
{
void Startup();
void Shutdown();

View File

@ -3,7 +3,7 @@ using System;
namespace Essgee.Emulation.Video
{
interface IVideo : IAxiStatus
interface IVideo : IAxiEssgssStatus
{
(int X, int Y, int Width, int Height) Viewport { get; }

View File

@ -43,7 +43,7 @@ internal static class AxiEssgssStatusDataExtention
return AxiStatus.saveCover.ToAxiEssgssStatusData(byteArray);
}
}
public interface IAxiStatus
public interface IAxiEssgssStatus
{
public void LoadAxiStatus(AxiEssgssStatusData data);
public AxiEssgssStatusData SaveAxiStatus();
@ -222,4 +222,50 @@ internal static class AxiStatus
return array2D;
}
public static byte[] FlattenByteArray3D(this byte[,,] array3D)
{
int layer = array3D.GetLength(0);
int rows = array3D.GetLength(1);
int cols = array3D.GetLength(2);
byte[] array1D = new byte[layer * rows * cols];
int index = 0;
for (int i = 0; i < layer; i++)
{
for (int j = 0; j < rows; j++)
{
for (int k = 0; k < cols; k++)
{
array1D[index++] = array3D[i, j, k];
}
}
}
return array1D;
}
public static byte[,,] CreateByteArray3D(this byte[] array1D, int layer, int rows, int cols)
{
if (array1D.Length != layer * rows * cols)
{
throw new ArgumentException("The length of the 1D array does not match the specified dimensions for the 3D array.");
}
byte[,,] array3D = new byte[layer, rows, cols];
int index = 0;
for (int i = 0; i < layer; i++)
{
for (int j = 0; j < rows; j++)
{
for (int k = 0; k < cols; k++)
{
array3D[i, j, k] = array1D[index++];
}
}
}
return array3D;
}
}

View File

@ -47,9 +47,10 @@ public class Essgeeinit : MonoBehaviour
ugeSaveConver = new UEGSaveByteConvert();
InitAll(uegResources, Application.persistentDataPath);
//LoadAndRunCartridge("G:/psjapa.sms");
LoadAndRunCartridge("G:/Phantasy Star (USA, Europe) (Rev A).zip");
//LoadAndRunCartridge("G:/Phantasy Star (USA, Europe) (Rev A).zip");
//LoadAndRunCartridge("G:/Ninja_Gaiden_(UE)_type_A_[!].sms");
//LoadAndRunCartridge("G:/SML2.gb");
LoadAndRunCartridge("G:/BaiduNetdiskDownload/밍艱濫쫠-촬묵깡考濫[숌]錦攣경1.11.gbc");
}
void OnDisable()

View File

@ -1,23 +0,0 @@
//using MAME.Core;
//using UnityEngine;
//public class UniMouse : MonoBehaviour, IMouse
//{
// static int mX, mY;
// public byte[] buttons = new byte[2];
// void Update()
// {
// mX = (int)Input.mousePosition.x;
// mY = (int)Input.mousePosition.y;
// buttons[0] = Input.GetMouseButton(0) ? (byte)1 : (byte)0;
// buttons[1] = Input.GetMouseButton(1) ? (byte)1 : (byte)0;
// }
// public void MouseXY(out int X, out int Y, out byte[] MouseButtons)
// {
// X = mX;
// Y = mY * -1;
// MouseButtons = buttons;
// }
//}

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: d5855fd9c3285e144b7db2b76cf55d77