From 787e4032326108408da24f81a5b194bc874d6944 Mon Sep 17 00:00:00 2001 From: parsons Date: Fri, 15 May 2026 00:19:45 +0100 Subject: [PATCH] Added SRAM save game functionality --- Core/Memory/SmsMemoryBus.cs | 34 ++++++++++++++++++++++++--- Desktop/Form1.cs | 46 ++++++++++++++++++++++++++++++++++--- Desktop/ROMS/zexdoc.sav | 10 ++++++++ 3 files changed, 84 insertions(+), 6 deletions(-) create mode 100644 Desktop/ROMS/zexdoc.sav diff --git a/Core/Memory/SmsMemoryBus.cs b/Core/Memory/SmsMemoryBus.cs index 3a53045..10bbe81 100644 --- a/Core/Memory/SmsMemoryBus.cs +++ b/Core/Memory/SmsMemoryBus.cs @@ -23,6 +23,7 @@ namespace Core.Memory // A flag to handle cartridges that don't use paging (like early 32KB games) private bool _isCartridgeLoaded = false; + public bool SramUsed { get; private set; } = false; public void LoadCartridge(byte[] romData) { @@ -57,7 +58,7 @@ namespace Core.Memory if ((_mapperControl & 0x08) != 0) { int ramBank = (_mapperControl & 0x04) != 0 ? 1 : 0; - int ramOffset = (ramBank * 0x4000) + (address & 0x3FFF); + int ramOffset = (ramBank * 0x4000) + (address & 0x1FFF); return _cartridgeRam[ramOffset]; } @@ -139,8 +140,9 @@ namespace Core.Memory // Bypass the lock if they are trying to save their game! if (address >= 0x8000 && (_mapperControl & 0x08) != 0) { + SramUsed = true; int ramBank = (_mapperControl & 0x04) != 0 ? 1 : 0; - int ramOffset = (ramBank * 0x4000) + (address & 0x3FFF); + int ramOffset = (ramBank * 0x4000) + (address & 0x1FFF); _cartridgeRam[ramOffset] = value; return; } @@ -196,11 +198,37 @@ namespace Core.Memory return _cartridgeRom[absoluteAddress]; } + public void LoadSaveData(string filePath) + { + if (System.IO.File.Exists(filePath)) + { + byte[] saveData = System.IO.File.ReadAllBytes(filePath); + Array.Copy(saveData, _cartridgeRam, Math.Min(saveData.Length, _cartridgeRam.Length)); + SramUsed = true; // We loaded a save, so it's active! + } + else + { + for (int i = 0; i < _cartridgeRam.Length; i++) + { + _cartridgeRam[i] = 0xFF; + } + SramUsed = false; + } + } + + public void SaveSaveData(string filePath) + { + // Only write a file to the hard drive if the game actually used the Save RAM! + if (SramUsed) + { + System.IO.File.WriteAllBytes(filePath, _cartridgeRam); + } + } public void CleanRAMData() { Array.Clear(_workRam, 0, _workRam.Length); - Array.Clear(_cartridgeRam, 0, _cartridgeRam.Length); + _mapperControl = 0; } } } \ No newline at end of file diff --git a/Desktop/Form1.cs b/Desktop/Form1.cs index ea24f5c..be26a00 100644 --- a/Desktop/Form1.cs +++ b/Desktop/Form1.cs @@ -22,6 +22,7 @@ namespace Desktop public double FrameTime { get; private set; } = 0; public double FramesPerSecond { get; private set; } = 0; private string _currentRomName = "No ROM"; + private string _currentRomPath = ""; private Stopwatch _stopwatch = new System.Diagnostics.Stopwatch(); public bool IsRunning { get; private set; } = false; @@ -181,7 +182,6 @@ namespace Desktop { IsRunning = false; } - private async void LoadRomAndStart(string filePath) { StopEmulator(); @@ -190,20 +190,59 @@ namespace Desktop await _emulatorTask; } + // 1. SAVE THE PREVIOUS GAME! + SaveCurrentSram(); + // 2. Load the file byte[] rom = File.ReadAllBytes(filePath); // 3. Jam it into the Sega Mapper _machine.LoadCartridge(rom); + // 4. Update the path tracking + _currentRomPath = filePath; _currentRomName = Path.GetFileNameWithoutExtension(filePath); this.Text = $"Parsons Master System - {_currentRomName}"; - // 5. Turn the power on! + // 5. LOAD THE NEW SAVE DATA! + string savPath = Path.ChangeExtension(_currentRomPath, ".sav"); + _machine.MemoryBus.LoadSaveData(savPath); + // 6. Turn the power on! StartEmulator(); } + //private async void LoadRomAndStart(string filePath) + //{ + // StopEmulator(); + // if (_emulatorTask != null) + // { + // await _emulatorTask; + // } + + // // 2. Load the file + // byte[] rom = File.ReadAllBytes(filePath); + + // // 3. Jam it into the Sega Mapper + // _machine.LoadCartridge(rom); + + // _currentRomName = Path.GetFileNameWithoutExtension(filePath); + // this.Text = $"Parsons Master System - {_currentRomName}"; + + // // 5. Turn the power on! + + // StartEmulator(); + //} + private void SaveCurrentSram() + { + if (!string.IsNullOrEmpty(_currentRomPath) && _machine != null) + { + // Swaps ".sms" for ".sav" + string savPath = Path.ChangeExtension(_currentRomPath, ".sav"); + _machine.MemoryBus.SaveSaveData(savPath); + } + } + private void PopulateIncludedRomsMenu() { // The folder you used for Golden Axe Warrior @@ -264,7 +303,7 @@ namespace Desktop private void exitToolStripMenuItem_Click(object sender, EventArgs e) { - Environment.Exit(0); + this.Close(); } private void Form1_KeyDown(object sender, KeyEventArgs e) @@ -308,6 +347,7 @@ namespace Desktop private void ParsonsForm1_FormClosing(object sender, FormClosingEventArgs e) { IsRunning = false; + SaveCurrentSram(); _audioPlayer?.Stop(); } } diff --git a/Desktop/ROMS/zexdoc.sav b/Desktop/ROMS/zexdoc.sav new file mode 100644 index 0000000..8499111 --- /dev/null +++ b/Desktop/ROMS/zexdoc.sav @@ -0,0 +1,10 @@ +Z80 Instruction Exerciser 0.21 +Documented flags version +Outputs: +* SDSC Debug Console +* Emulicious Debug Console +* TMS9918 Text Mode +* SRAM + +SCF/CCF tests: + \ No newline at end of file