Added Snapshot save feature

This commit is contained in:
2026-04-28 11:24:50 +01:00
parent 851dfba78f
commit 38bef38f96
7 changed files with 103 additions and 31 deletions

View File

@@ -14,9 +14,9 @@
<None Remove="ROMS\Snapshot\GHILL48K.sna" /> <None Remove="ROMS\Snapshot\GHILL48K.sna" />
<None Remove="ROMS\Snapshot\manic.sna" /> <None Remove="ROMS\Snapshot\manic.sna" />
<None Remove="ROMS\TAP\Chuckie Egg.tap" /> <None Remove="ROMS\TAP\Chuckie Egg.tap" />
<None Remove="ROMS\TAP\DizzyB-TreasureIsland.tap" /> <None Remove="ROMS\TAP\Grange Hill.TAP" />
<None Remove="ROMS\TAP\GHILL48K.TAP" /> <None Remove="ROMS\TAP\Manic Miner.TAP" />
<None Remove="ROMS\TAP\MANIC.TAP" /> <None Remove="ROMS\TAP\Treasure Island - Dizzy.tap" />
<None Remove="ROMS\TAP\zexall.tap" /> <None Remove="ROMS\TAP\zexall.tap" />
</ItemGroup> </ItemGroup>
@@ -36,13 +36,13 @@
<EmbeddedResource Include="ROMS\TAP\Chuckie Egg.tap"> <EmbeddedResource Include="ROMS\TAP\Chuckie Egg.tap">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="ROMS\TAP\DizzyB-TreasureIsland.tap"> <EmbeddedResource Include="ROMS\TAP\Grange Hill.TAP">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="ROMS\TAP\GHILL48K.TAP"> <EmbeddedResource Include="ROMS\TAP\Manic Miner.TAP">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="ROMS\TAP\MANIC.TAP"> <EmbeddedResource Include="ROMS\TAP\Treasure Island - Dizzy.tap">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="ROMS\TAP\zexall.tap"> <EmbeddedResource Include="ROMS\TAP\zexall.tap">

View File

@@ -31,6 +31,9 @@
menuStrip1 = new MenuStrip(); menuStrip1 = new MenuStrip();
fileToolStripMenuItem = new ToolStripMenuItem(); fileToolStripMenuItem = new ToolStripMenuItem();
openToolStripMenuItem = new ToolStripMenuItem(); openToolStripMenuItem = new ToolStripMenuItem();
includedToolStripMenuItem = new ToolStripMenuItem();
tAPToolStripMenuItem1 = new ToolStripMenuItem();
sNAToolStripMenuItem1 = new ToolStripMenuItem();
tAPToolStripMenuItem = new ToolStripMenuItem(); tAPToolStripMenuItem = new ToolStripMenuItem();
sNAToolStripMenuItem = new ToolStripMenuItem(); sNAToolStripMenuItem = new ToolStripMenuItem();
exitToolStripMenuItem = new ToolStripMenuItem(); exitToolStripMenuItem = new ToolStripMenuItem();
@@ -43,16 +46,14 @@
resetToolStripMenuItem1 = new ToolStripMenuItem(); resetToolStripMenuItem1 = new ToolStripMenuItem();
optionsToolStripMenuItem = new ToolStripMenuItem(); optionsToolStripMenuItem = new ToolStripMenuItem();
HighSpeedToolStripMenuItem = new ToolStripMenuItem(); HighSpeedToolStripMenuItem = new ToolStripMenuItem();
includedToolStripMenuItem = new ToolStripMenuItem(); saveSnapshotToolStripMenuItem = new ToolStripMenuItem();
tAPToolStripMenuItem1 = new ToolStripMenuItem();
sNAToolStripMenuItem1 = new ToolStripMenuItem();
menuStrip1.SuspendLayout(); menuStrip1.SuspendLayout();
SuspendLayout(); SuspendLayout();
// //
// menuStrip1 // menuStrip1
// //
menuStrip1.ImageScalingSize = new Size(24, 24); menuStrip1.ImageScalingSize = new Size(24, 24);
menuStrip1.Items.AddRange(new ToolStripItem[] { fileToolStripMenuItem, viewToolStripMenuItem, machineToolStripMenuItem, optionsToolStripMenuItem }); menuStrip1.Items.AddRange(new ToolStripItem[] { fileToolStripMenuItem, machineToolStripMenuItem, viewToolStripMenuItem, optionsToolStripMenuItem });
menuStrip1.Location = new Point(0, 0); menuStrip1.Location = new Point(0, 0);
menuStrip1.Name = "menuStrip1"; menuStrip1.Name = "menuStrip1";
menuStrip1.Padding = new Padding(5, 2, 0, 2); menuStrip1.Padding = new Padding(5, 2, 0, 2);
@@ -62,7 +63,7 @@
// //
// fileToolStripMenuItem // fileToolStripMenuItem
// //
fileToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { openToolStripMenuItem, exitToolStripMenuItem }); fileToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { openToolStripMenuItem, saveSnapshotToolStripMenuItem, exitToolStripMenuItem });
fileToolStripMenuItem.Name = "fileToolStripMenuItem"; fileToolStripMenuItem.Name = "fileToolStripMenuItem";
fileToolStripMenuItem.Size = new Size(46, 24); fileToolStripMenuItem.Size = new Size(46, 24);
fileToolStripMenuItem.Text = "File"; fileToolStripMenuItem.Text = "File";
@@ -71,9 +72,28 @@
// //
openToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { includedToolStripMenuItem, tAPToolStripMenuItem, sNAToolStripMenuItem }); openToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { includedToolStripMenuItem, tAPToolStripMenuItem, sNAToolStripMenuItem });
openToolStripMenuItem.Name = "openToolStripMenuItem"; openToolStripMenuItem.Name = "openToolStripMenuItem";
openToolStripMenuItem.Size = new Size(128, 26); openToolStripMenuItem.Size = new Size(224, 26);
openToolStripMenuItem.Text = "Open"; openToolStripMenuItem.Text = "Open";
// //
// includedToolStripMenuItem
//
includedToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { tAPToolStripMenuItem1, sNAToolStripMenuItem1 });
includedToolStripMenuItem.Name = "includedToolStripMenuItem";
includedToolStripMenuItem.Size = new Size(149, 26);
includedToolStripMenuItem.Text = "Included";
//
// tAPToolStripMenuItem1
//
tAPToolStripMenuItem1.Name = "tAPToolStripMenuItem1";
tAPToolStripMenuItem1.Size = new Size(121, 26);
tAPToolStripMenuItem1.Text = "TAP";
//
// sNAToolStripMenuItem1
//
sNAToolStripMenuItem1.Name = "sNAToolStripMenuItem1";
sNAToolStripMenuItem1.Size = new Size(121, 26);
sNAToolStripMenuItem1.Text = "SNA";
//
// tAPToolStripMenuItem // tAPToolStripMenuItem
// //
tAPToolStripMenuItem.Name = "tAPToolStripMenuItem"; tAPToolStripMenuItem.Name = "tAPToolStripMenuItem";
@@ -91,7 +111,7 @@
// exitToolStripMenuItem // exitToolStripMenuItem
// //
exitToolStripMenuItem.Name = "exitToolStripMenuItem"; exitToolStripMenuItem.Name = "exitToolStripMenuItem";
exitToolStripMenuItem.Size = new Size(128, 26); exitToolStripMenuItem.Size = new Size(224, 26);
exitToolStripMenuItem.Text = "Exit"; exitToolStripMenuItem.Text = "Exit";
exitToolStripMenuItem.Click += btnExit_Click; exitToolStripMenuItem.Click += btnExit_Click;
// //
@@ -154,28 +174,16 @@
// HighSpeedToolStripMenuItem // HighSpeedToolStripMenuItem
// //
HighSpeedToolStripMenuItem.Name = "HighSpeedToolStripMenuItem"; HighSpeedToolStripMenuItem.Name = "HighSpeedToolStripMenuItem";
HighSpeedToolStripMenuItem.Size = new Size(224, 26); HighSpeedToolStripMenuItem.Size = new Size(170, 26);
HighSpeedToolStripMenuItem.Text = "High Speed"; HighSpeedToolStripMenuItem.Text = "High Speed";
HighSpeedToolStripMenuItem.Click += btnHighSpeedToggle_Click; HighSpeedToolStripMenuItem.Click += btnHighSpeedToggle_Click;
// //
// includedToolStripMenuItem // saveSnapshotToolStripMenuItem
// //
includedToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { tAPToolStripMenuItem1, sNAToolStripMenuItem1 }); saveSnapshotToolStripMenuItem.Name = "saveSnapshotToolStripMenuItem";
includedToolStripMenuItem.Name = "includedToolStripMenuItem"; saveSnapshotToolStripMenuItem.Size = new Size(224, 26);
includedToolStripMenuItem.Size = new Size(149, 26); saveSnapshotToolStripMenuItem.Text = "Save Snapshot";
includedToolStripMenuItem.Text = "Included"; saveSnapshotToolStripMenuItem.Click += SaveSNAMenuItem_Click;
//
// tAPToolStripMenuItem1
//
tAPToolStripMenuItem1.Name = "tAPToolStripMenuItem1";
tAPToolStripMenuItem1.Size = new Size(224, 26);
tAPToolStripMenuItem1.Text = "TAP";
//
// sNAToolStripMenuItem1
//
sNAToolStripMenuItem1.Name = "sNAToolStripMenuItem1";
sNAToolStripMenuItem1.Size = new Size(224, 26);
sNAToolStripMenuItem1.Text = "SNA";
// //
// Form1 // Form1
// //
@@ -212,5 +220,6 @@
private ToolStripMenuItem includedToolStripMenuItem; private ToolStripMenuItem includedToolStripMenuItem;
private ToolStripMenuItem tAPToolStripMenuItem1; private ToolStripMenuItem tAPToolStripMenuItem1;
private ToolStripMenuItem sNAToolStripMenuItem1; private ToolStripMenuItem sNAToolStripMenuItem1;
private ToolStripMenuItem saveSnapshotToolStripMenuItem;
} }
} }

View File

@@ -237,6 +237,69 @@ namespace Desktop
}); });
} }
private void SaveSNAMenuItem_Click(object? sender, EventArgs e)
{
_isPaused = true;
using (SaveFileDialog sfd = new SaveFileDialog())
{
sfd.Filter = "Spectrum SNA Files|*.sna";
if (sfd.ShowDialog() == DialogResult.OK)
{
SaveSNA(sfd.FileName);
}
}
_isPaused = false;
}
public void SaveSNA(string filepath)
{
// 0. Back up the live state BEFORE modifying it
ushort originalSP = _cpu.SP;
byte originalMemLow = _memoryBus.Read((ushort)(_cpu.SP - 2));
byte originalMemHigh = _memoryBus.Read((ushort)(_cpu.SP - 1));
// 1. We must push the PC onto the stack before saving!
_cpu.SP -= 2;
_memoryBus.Write(_cpu.SP, (byte)(_cpu.PC & 0xFF)); // Low byte
_memoryBus.Write((ushort)(_cpu.SP + 1), (byte)(_cpu.PC >> 8)); // High byte
using (FileStream fs = new FileStream(filepath, FileMode.Create))
using (BinaryWriter bw = new BinaryWriter(fs))
{
// 2. Write the 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);
bw.Write(_cpu.BC_Prime.Low); bw.Write(_cpu.BC_Prime.High);
bw.Write(_cpu.AF_Prime.Low); bw.Write(_cpu.AF_Prime.High);
bw.Write(_cpu.HL.Low); bw.Write(_cpu.HL.High);
bw.Write(_cpu.DE.Low); bw.Write(_cpu.DE.High);
bw.Write(_cpu.BC.Low); bw.Write(_cpu.BC.High);
bw.Write(_cpu.IY.Low); bw.Write(_cpu.IY.High);
bw.Write(_cpu.IX.Low); bw.Write(_cpu.IX.High);
// IFF2 determines the interrupt state in SNA
bw.Write((byte)(_cpu.IFF2 ? 0x04 : 0x00));
bw.Write(_cpu.R);
bw.Write(_cpu.AF.Low); bw.Write(_cpu.AF.High);
bw.Write((byte)(_cpu.SP & 0xFF)); bw.Write((byte)(_cpu.SP >> 8));
bw.Write((byte)_cpu.InterruptMode);
bw.Write(_simpleIoBus.BorderColorIndex);
// 3. Dump the 48K RAM
for (int i = 0x4000; i <= 0xFFFF; i++)
{
bw.Write(_memoryBus.Read((ushort)i));
}
}
// 4. RESTORE the emulator's state so the game can resume safely
_cpu.SP = originalSP;
_memoryBus.Write((ushort)(originalSP - 2), originalMemLow);
_memoryBus.Write((ushort)(originalSP - 1), originalMemHigh);
}
private void PopulateIncludedTapsMenu() private void PopulateIncludedTapsMenu()
{ {
// 1. Get the current assembly (your .exe) // 1. Get the current assembly (your .exe)

Binary file not shown.