Fixed Plus 3 Snapshot saving and loading
This commit is contained in:
@@ -18,6 +18,8 @@ namespace Core.Memory
|
|||||||
private byte _port7FFD = 0;
|
private byte _port7FFD = 0;
|
||||||
private byte _port1FFD = 0;
|
private byte _port1FFD = 0;
|
||||||
private bool _pagingDisabled = false;
|
private bool _pagingDisabled = false;
|
||||||
|
public byte Port7FFD => _port7FFD;
|
||||||
|
public int CurrentRamBankSlot3 => _currentRamBankSlot3;
|
||||||
|
|
||||||
public MemoryBus()
|
public MemoryBus()
|
||||||
{
|
{
|
||||||
@@ -94,6 +96,11 @@ namespace Core.Memory
|
|||||||
default: return -1; // -1 means it is pointing to a ROM chip!
|
default: return -1; // -1 means it is pointing to a ROM chip!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public byte[] GetRamBank(int index)
|
||||||
|
{
|
||||||
|
return _ramBanks[index];
|
||||||
|
}
|
||||||
|
|
||||||
public byte Read(ushort address)
|
public byte Read(ushort address)
|
||||||
{
|
{
|
||||||
int slot = address >> 14; // Find the 16KB Slot (0, 1, 2, or 3)
|
int slot = address >> 14; // Find the 16KB Slot (0, 1, 2, or 3)
|
||||||
|
|||||||
@@ -289,61 +289,17 @@ namespace Core
|
|||||||
// SNAPSHOT MANAGEMENT (.SNA)
|
// SNAPSHOT MANAGEMENT (.SNA)
|
||||||
// ====================================================================
|
// ====================================================================
|
||||||
|
|
||||||
public void LoadSnapshot(byte[] snaData)
|
// ====================================================================
|
||||||
{
|
// SNAPSHOT MANAGEMENT (.SNA)
|
||||||
if (snaData.Length != 49179)
|
// ====================================================================
|
||||||
throw new Exception("Invalid 48K SNA File Size!");
|
|
||||||
|
|
||||||
// 1. Load CPU Registers
|
|
||||||
Cpu.I = snaData[0];
|
|
||||||
Cpu.HL_Prime.Word = (ushort)(snaData[1] | (snaData[2] << 8));
|
|
||||||
Cpu.DE_Prime.Word = (ushort)(snaData[3] | (snaData[4] << 8));
|
|
||||||
Cpu.BC_Prime.Word = (ushort)(snaData[5] | (snaData[6] << 8));
|
|
||||||
Cpu.AF_Prime.Word = (ushort)(snaData[7] | (snaData[8] << 8));
|
|
||||||
|
|
||||||
Cpu.HL.Word = (ushort)(snaData[9] | (snaData[10] << 8));
|
|
||||||
Cpu.DE.Word = (ushort)(snaData[11] | (snaData[12] << 8));
|
|
||||||
Cpu.BC.Word = (ushort)(snaData[13] | (snaData[14] << 8));
|
|
||||||
Cpu.IY.Word = (ushort)(snaData[15] | (snaData[16] << 8));
|
|
||||||
Cpu.IX.Word = (ushort)(snaData[17] | (snaData[18] << 8));
|
|
||||||
|
|
||||||
bool iff2 = (snaData[19] & 0x04) != 0;
|
|
||||||
// Note: If IFF1/IFF2 have private setters in Z80.cs, you may need to
|
|
||||||
// trigger an EI/DI instruction or temporarily unlock them.
|
|
||||||
// Assuming they are public/internal for now based on previous code:
|
|
||||||
|
|
||||||
Cpu.R = snaData[20];
|
|
||||||
Cpu.AF.Word = (ushort)(snaData[21] | (snaData[22] << 8));
|
|
||||||
Cpu.SP = (ushort)(snaData[23] | (snaData[24] << 8));
|
|
||||||
|
|
||||||
// Set Interrupt Mode (Assuming you add a setter to Z80.InterruptMode if it's private)
|
|
||||||
// Cpu.InterruptMode = snaData[25];
|
|
||||||
|
|
||||||
// THE BUG FIX: Restore the ULA Border Color!
|
|
||||||
IoBus.BorderColourIndex = snaData[26];
|
|
||||||
|
|
||||||
// 2. Load the 48K RAM Dump
|
|
||||||
for (int i = 0; i < 49152; i++)
|
|
||||||
{
|
|
||||||
Memory.Write((ushort)(0x4000 + i), snaData[27 + i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Pop the Program Counter off the stack to resume
|
|
||||||
byte pcLow = Memory.Read(Cpu.SP);
|
|
||||||
Cpu.SP++;
|
|
||||||
byte pcHigh = Memory.Read(Cpu.SP);
|
|
||||||
Cpu.SP++;
|
|
||||||
Cpu.PC = (ushort)((pcHigh << 8) | pcLow);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SaveSnapshot(string filepath)
|
public void SaveSnapshot(string filepath)
|
||||||
{
|
{
|
||||||
// Back up the live state BEFORE modifying the stack
|
|
||||||
ushort originalSP = Cpu.SP;
|
ushort originalSP = Cpu.SP;
|
||||||
byte originalMemLow = Memory.Read((ushort)(Cpu.SP - 2));
|
byte originalMemLow = Memory.Read((ushort)(Cpu.SP - 2));
|
||||||
byte originalMemHigh = Memory.Read((ushort)(Cpu.SP - 1));
|
byte originalMemHigh = Memory.Read((ushort)(Cpu.SP - 1));
|
||||||
|
|
||||||
// Push the PC onto the stack
|
// Push PC for 48K compatibility
|
||||||
Cpu.SP -= 2;
|
Cpu.SP -= 2;
|
||||||
Memory.Write(Cpu.SP, (byte)(Cpu.PC & 0xFF));
|
Memory.Write(Cpu.SP, (byte)(Cpu.PC & 0xFF));
|
||||||
Memory.Write((ushort)(Cpu.SP + 1), (byte)(Cpu.PC >> 8));
|
Memory.Write((ushort)(Cpu.SP + 1), (byte)(Cpu.PC >> 8));
|
||||||
@@ -351,6 +307,7 @@ namespace Core
|
|||||||
using (System.IO.FileStream fs = new System.IO.FileStream(filepath, System.IO.FileMode.Create))
|
using (System.IO.FileStream fs = new System.IO.FileStream(filepath, System.IO.FileMode.Create))
|
||||||
using (System.IO.BinaryWriter bw = new System.IO.BinaryWriter(fs))
|
using (System.IO.BinaryWriter bw = new System.IO.BinaryWriter(fs))
|
||||||
{
|
{
|
||||||
|
// --- 27 BYTE HEADER ---
|
||||||
bw.Write(Cpu.I);
|
bw.Write(Cpu.I);
|
||||||
bw.Write(Cpu.HL_Prime.Low); bw.Write(Cpu.HL_Prime.High);
|
bw.Write(Cpu.HL_Prime.Low); bw.Write(Cpu.HL_Prime.High);
|
||||||
bw.Write(Cpu.DE_Prime.Low); bw.Write(Cpu.DE_Prime.High);
|
bw.Write(Cpu.DE_Prime.Low); bw.Write(Cpu.DE_Prime.High);
|
||||||
@@ -370,9 +327,34 @@ namespace Core
|
|||||||
bw.Write((byte)Cpu.InterruptMode);
|
bw.Write((byte)Cpu.InterruptMode);
|
||||||
bw.Write(IoBus.BorderColourIndex);
|
bw.Write(IoBus.BorderColourIndex);
|
||||||
|
|
||||||
for (int i = 0x4000; i <= 0xFFFF; i++)
|
if (CurrentModel == MachineModel.Spectrum48K)
|
||||||
{
|
{
|
||||||
bw.Write(Memory.Read((ushort)i));
|
// --- 48K FORMAT (49,179 Bytes) ---
|
||||||
|
for (int i = 0x4000; i <= 0xFFFF; i++) bw.Write(Memory.Read((ushort)i));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// --- 128K FORMAT (131,103 Bytes) ---
|
||||||
|
int activeBank = Memory.CurrentRamBankSlot3;
|
||||||
|
|
||||||
|
bw.Write(Memory.GetRamBank(5)); // 0x4000 - 0x7FFF
|
||||||
|
bw.Write(Memory.GetRamBank(2)); // 0x8000 - 0xBFFF
|
||||||
|
bw.Write(Memory.GetRamBank(activeBank)); // 0xC000 - 0xFFFF
|
||||||
|
|
||||||
|
// 128K Extensions
|
||||||
|
bw.Write((byte)(Cpu.PC & 0xFF));
|
||||||
|
bw.Write((byte)(Cpu.PC >> 8));
|
||||||
|
bw.Write(Memory.Port7FFD);
|
||||||
|
bw.Write((byte)0); // TR-DOS ROM (0 = unused)
|
||||||
|
|
||||||
|
// Write the remaining 5 unpaged banks
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
if (i != 5 && i != 2 && i != activeBank)
|
||||||
|
{
|
||||||
|
bw.Write(Memory.GetRamBank(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,5 +363,79 @@ namespace Core
|
|||||||
Memory.Write((ushort)(originalSP - 2), originalMemLow);
|
Memory.Write((ushort)(originalSP - 2), originalMemLow);
|
||||||
Memory.Write((ushort)(originalSP - 1), originalMemHigh);
|
Memory.Write((ushort)(originalSP - 1), originalMemHigh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void LoadSnapshot(byte[] snaData)
|
||||||
|
{
|
||||||
|
bool is128K = snaData.Length == 131103;
|
||||||
|
|
||||||
|
if (snaData.Length != 49179 && !is128K)
|
||||||
|
throw new Exception($"Invalid SNA File Size! Got {snaData.Length} bytes.");
|
||||||
|
|
||||||
|
// Force the machine into the correct mode
|
||||||
|
SetMachineModel(is128K ? MachineModel.SpectrumPlus2A : MachineModel.Spectrum48K);
|
||||||
|
|
||||||
|
// 1. Load Header
|
||||||
|
Cpu.I = snaData[0];
|
||||||
|
Cpu.HL_Prime.Word = (ushort)(snaData[1] | (snaData[2] << 8));
|
||||||
|
Cpu.DE_Prime.Word = (ushort)(snaData[3] | (snaData[4] << 8));
|
||||||
|
Cpu.BC_Prime.Word = (ushort)(snaData[5] | (snaData[6] << 8));
|
||||||
|
Cpu.AF_Prime.Word = (ushort)(snaData[7] | (snaData[8] << 8));
|
||||||
|
|
||||||
|
Cpu.HL.Word = (ushort)(snaData[9] | (snaData[10] << 8));
|
||||||
|
Cpu.DE.Word = (ushort)(snaData[11] | (snaData[12] << 8));
|
||||||
|
Cpu.BC.Word = (ushort)(snaData[13] | (snaData[14] << 8));
|
||||||
|
Cpu.IY.Word = (ushort)(snaData[15] | (snaData[16] << 8));
|
||||||
|
Cpu.IX.Word = (ushort)(snaData[17] | (snaData[18] << 8));
|
||||||
|
|
||||||
|
if ((snaData[19] & 0x04) != 0) { Cpu.ExecuteOpcode(0xFB); /* Fake EI */ }
|
||||||
|
else { Cpu.ExecuteOpcode(0xF3); /* Fake DI */ }
|
||||||
|
|
||||||
|
Cpu.R = snaData[20];
|
||||||
|
Cpu.AF.Word = (ushort)(snaData[21] | (snaData[22] << 8));
|
||||||
|
Cpu.SP = (ushort)(snaData[23] | (snaData[24] << 8));
|
||||||
|
|
||||||
|
if (snaData[25] == 0) Cpu.ExecuteOpcode(0x46); // IM 0
|
||||||
|
else if (snaData[25] == 1) Cpu.ExecuteOpcode(0x56); // IM 1
|
||||||
|
else if (snaData[25] == 2) Cpu.ExecuteOpcode(0x5E); // IM 2
|
||||||
|
|
||||||
|
IoBus.BorderColourIndex = snaData[26];
|
||||||
|
|
||||||
|
// 2. Load RAM
|
||||||
|
if (!is128K)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 49152; i++) Memory.Write((ushort)(0x4000 + i), snaData[27 + i]);
|
||||||
|
|
||||||
|
byte pcLow = Memory.Read(Cpu.SP); Cpu.SP++;
|
||||||
|
byte pcHigh = Memory.Read(Cpu.SP); Cpu.SP++;
|
||||||
|
Cpu.PC = (ushort)((pcHigh << 8) | pcLow);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Cpu.PC = (ushort)(snaData[49179] | (snaData[49180] << 8));
|
||||||
|
byte port7FFD = snaData[49181];
|
||||||
|
|
||||||
|
// THE TOASTRACK FIX: Force Amstrad ROMs into Toastrack compatibility mode!
|
||||||
|
IoBus.WritePort(0x1FFD, 0x04);
|
||||||
|
IoBus.WritePort(0x7FFD, port7FFD);
|
||||||
|
|
||||||
|
int activeBank = port7FFD & 0x07;
|
||||||
|
|
||||||
|
// Load the 3 visible memory chunks
|
||||||
|
Array.Copy(snaData, 27, Memory.GetRamBank(5), 0, 16384);
|
||||||
|
Array.Copy(snaData, 16411, Memory.GetRamBank(2), 0, 16384);
|
||||||
|
Array.Copy(snaData, 32795, Memory.GetRamBank(activeBank), 0, 16384);
|
||||||
|
|
||||||
|
// Load the remaining 5 hidden memory banks
|
||||||
|
int offset = 49183;
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
if (i != 5 && i != 2 && i != activeBank)
|
||||||
|
{
|
||||||
|
Array.Copy(snaData, offset, Memory.GetRamBank(i), 0, 16384);
|
||||||
|
offset += 16384;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user