Fixed per scanline interrupts. No artifacts in MMCOI

This commit is contained in:
2026-05-15 23:38:40 +01:00
parent 4e745b4fbc
commit ec40e04ff3
8 changed files with 263 additions and 104 deletions

View File

@@ -37,13 +37,8 @@ namespace Desktop
public ParsonsForm1()
{
InitializeComponent();
this.Text = $"Parsons Master System 2026 - {_currentRomName}";
_machine = new SmsMachine();
_audioPlayer = new NAudioPlayer();
_machine.AudioProcessor.AudioDevice = _audioPlayer;
PopulateIncludedRomsMenu();
// These are perfectly safe for the Visual Studio Designer!
this.KeyPreview = true;
this.KeyDown += Form1_KeyDown;
this.KeyUp += Form1_KeyUp;
@@ -51,6 +46,21 @@ namespace Desktop
this.ResizeRedraw = true;
}
// The Designer ignores this completely, but the compiled game runs it instantly!
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.Text = $"Parsons Master System - {_currentRomName}";
// Safe to initialize hardware and files here!
_machine = new SmsMachine();
_audioPlayer = new NAudioPlayer();
_machine.AudioProcessor.AudioDevice = _audioPlayer;
PopulateIncludedRomsMenu();
}
private void DrawScreen()
{
// Rapidly copy our VDP FrameBuffer into the Windows Bitmap
@@ -65,7 +75,8 @@ namespace Desktop
// Always call the base method so Windows can draw your MenuStrip!
base.OnPaint(e);
if (_screenBitmap != null)
// THE FIX: We MUST ensure the designer has actually built the menu strip before asking for its height!
if (_screenBitmap != null && menuStrip1 != null)
{
// 1. Set the rendering mode for perfect, chunky retro pixels!
e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
@@ -73,7 +84,7 @@ namespace Desktop
// 2. Calculate the drawing area.
// We start drawing BELOW the MenuStrip so it doesn't get covered up.
int topOffset = menuStrip1.Height; // Change 'menuStrip1' if your menu has a different (Name)
int topOffset = menuStrip1.Height;
Rectangle renderArea = new Rectangle(
0,
topOffset,
@@ -205,42 +216,37 @@ namespace Desktop
_currentRomName = Path.GetFileNameWithoutExtension(filePath);
this.Text = $"Parsons Master System - {_currentRomName}";
// 5. LOAD THE NEW SAVE DATA!
string savPath = Path.ChangeExtension(_currentRomPath, ".sav");
_machine.MemoryBus.LoadSaveData(savPath);
// 5. LOAD THE NEW SAVE DATA FROM THE EXE FOLDER!
string savPath = GetSaveFilePath();
if (savPath != null)
{
_machine.MemoryBus.LoadSaveData(savPath);
}
// 6. Turn the power on!
StartEmulator();
}
private string GetSaveFilePath()
{
// Don't try to save if a game hasn't been loaded yet!
if (string.IsNullOrEmpty(_currentRomName) || _currentRomName == "No ROM")
return null;
//private async void LoadRomAndStart(string filePath)
//{
// StopEmulator();
// if (_emulatorTask != null)
// {
// await _emulatorTask;
// }
// Application.StartupPath is the exact folder containing your .exe
string exeFolder = Application.StartupPath;
// // 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();
//}
// Combines the exe folder with "GameName.sav"
return Path.Combine(exeFolder, _currentRomName + ".sav");
}
private void SaveCurrentSram()
{
if (!string.IsNullOrEmpty(_currentRomPath) && _machine != null)
if (_machine != null)
{
// Swaps ".sms" for ".sav"
string savPath = Path.ChangeExtension(_currentRomPath, ".sav");
_machine.MemoryBus.SaveSaveData(savPath);
string savPath = GetSaveFilePath();
if (savPath != null)
{
_machine.MemoryBus.SaveSaveData(savPath);
}
}
}
@@ -361,5 +367,38 @@ namespace Desktop
SaveCurrentSram();
_audioPlayer?.Stop();
}
private async void saveStateToolStripMenuItem_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(_currentRomName) || _currentRomName == "No ROM") return;
// 1. Politely ask the emulator loop to pause, and wait for it to finish its current frame!
StopEmulator();
if (_emulatorTask != null) await _emulatorTask;
// 2. Change the extension to .state and save it right next to the .exe
string statePath = Path.ChangeExtension(GetSaveFilePath(), ".state");
_machine.SaveState(statePath);
// 3. Resume the emulator seamlessly
StartEmulator();
}
private async void loadStateToolStripMenuItem_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(_currentRomName) || _currentRomName == "No ROM") return;
string statePath = Path.ChangeExtension(GetSaveFilePath(), ".state");
if (!File.Exists(statePath)) return;
// 1. Pause the emulator
StopEmulator();
if (_emulatorTask != null) await _emulatorTask;
// 2. Inject the frozen state into the silicon!
_machine.LoadState(statePath);
// 3. Resume
StartEmulator();
}
}
}