Fixed Plus 3 Snapshot saving and loading

This commit is contained in:
2026-04-30 16:28:58 +01:00
parent bc2748250d
commit 7a4f34af65
2 changed files with 113 additions and 50 deletions

View File

@@ -18,6 +18,8 @@ namespace Core.Memory
private byte _port7FFD = 0;
private byte _port1FFD = 0;
private bool _pagingDisabled = false;
public byte Port7FFD => _port7FFD;
public int CurrentRamBankSlot3 => _currentRamBankSlot3;
public MemoryBus()
{
@@ -94,6 +96,11 @@ namespace Core.Memory
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)
{
int slot = address >> 14; // Find the 16KB Slot (0, 1, 2, or 3)

View File

@@ -289,61 +289,17 @@ namespace Core
// SNAPSHOT MANAGEMENT (.SNA)
// ====================================================================
public void LoadSnapshot(byte[] snaData)
{
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);
}
// ====================================================================
// SNAPSHOT MANAGEMENT (.SNA)
// ====================================================================
public void SaveSnapshot(string filepath)
{
// Back up the live state BEFORE modifying the stack
ushort originalSP = Cpu.SP;
byte originalMemLow = Memory.Read((ushort)(Cpu.SP - 2));
byte originalMemHigh = Memory.Read((ushort)(Cpu.SP - 1));
// Push the PC onto the stack
// Push PC for 48K compatibility
Cpu.SP -= 2;
Memory.Write(Cpu.SP, (byte)(Cpu.PC & 0xFF));
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.BinaryWriter bw = new System.IO.BinaryWriter(fs))
{
// --- 27 BYTE HEADER ---
bw.Write(Cpu.I);
bw.Write(Cpu.HL_Prime.Low); bw.Write(Cpu.HL_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(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 - 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;
}
}
}
}
}
}