Fixed per scanline interrupts. No artifacts in MMCOI
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
using Core.Interfaces;
|
using Core.Interfaces;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace Core.Audio
|
namespace Core.Audio
|
||||||
{
|
{
|
||||||
@@ -196,50 +197,29 @@ namespace Core.Audio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SaveState(BinaryWriter bw)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 8; i++) bw.Write(Registers[i]);
|
||||||
|
bw.Write(_latchedRegister);
|
||||||
|
bw.Write(_sampleCycleTracker);
|
||||||
|
bw.Write(_psgCycleTracker);
|
||||||
|
for (int i = 0; i < 4; i++) { bw.Write(_counters[i]); bw.Write(_polarities[i]); }
|
||||||
|
bw.Write(_lfsr);
|
||||||
|
bw.Write(_previousSample);
|
||||||
|
bw.Write(_previousFiltered);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadState(BinaryReader br)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 8; i++) Registers[i] = br.ReadUInt16();
|
||||||
|
_latchedRegister = br.ReadInt32();
|
||||||
|
_sampleCycleTracker = br.ReadDouble();
|
||||||
|
_psgCycleTracker = br.ReadInt32();
|
||||||
|
for (int i = 0; i < 4; i++) { _counters[i] = br.ReadInt32(); _polarities[i] = br.ReadInt32(); }
|
||||||
|
_lfsr = br.ReadUInt16();
|
||||||
|
_previousSample = br.ReadSingle();
|
||||||
|
_previousFiltered = br.ReadSingle();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//using System;
|
|
||||||
|
|
||||||
//namespace Core.Audio
|
|
||||||
//{
|
|
||||||
// public class SmsApu
|
|
||||||
// {
|
|
||||||
// // The 8 internal registers of the PSG
|
|
||||||
// // 0: Tone 0 Frequency (10 bits)
|
|
||||||
// // 1: Tone 0 Volume (4 bits)
|
|
||||||
// // 2: Tone 1 Frequency (10 bits)
|
|
||||||
// // 3: Tone 1 Volume (4 bits)
|
|
||||||
// // 4: Tone 2 Frequency (10 bits)
|
|
||||||
// // 5: Tone 2 Volume (4 bits)
|
|
||||||
// // 6: Noise Control (3 bits)
|
|
||||||
// // 7: Noise Volume (4 bits)
|
|
||||||
// public ushort[] Registers { get; private set; } = new ushort[8];
|
|
||||||
|
|
||||||
// // Remembers which register the CPU is currently talking to
|
|
||||||
// private int _latchedRegister = 0;
|
|
||||||
|
|
||||||
// public SmsApu()
|
|
||||||
// {
|
|
||||||
// // Volumes default to 0x0F (Silence! 0 = max volume, 15 = off)
|
|
||||||
// Registers[1] = 0x0F;
|
|
||||||
// Registers[3] = 0x0F;
|
|
||||||
// Registers[5] = 0x0F;
|
|
||||||
// Registers[7] = 0x0F;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using Core.Interfaces;
|
using Core.Interfaces;
|
||||||
using Core.Io;
|
using Core.Io;
|
||||||
|
|
||||||
@@ -97,6 +98,32 @@ namespace Core.Cpu
|
|||||||
TotalTStates = 0;
|
TotalTStates = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SaveState(BinaryWriter bw)
|
||||||
|
{
|
||||||
|
bw.Write(TotalTStates);
|
||||||
|
bw.Write(InterruptMode);
|
||||||
|
bw.Write(IFF1);
|
||||||
|
bw.Write(IFF2);
|
||||||
|
bw.Write(InterruptRequested);
|
||||||
|
bw.Write(AF.Word); bw.Write(BC.Word); bw.Write(DE.Word); bw.Write(HL.Word);
|
||||||
|
bw.Write(AF_Prime.Word); bw.Write(BC_Prime.Word); bw.Write(DE_Prime.Word); bw.Write(HL_Prime.Word);
|
||||||
|
bw.Write(IX.Word); bw.Write(IY.Word);
|
||||||
|
bw.Write(PC); bw.Write(SP); bw.Write(I); bw.Write(R);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadState(BinaryReader br)
|
||||||
|
{
|
||||||
|
TotalTStates = br.ReadInt64();
|
||||||
|
InterruptMode = br.ReadInt32();
|
||||||
|
IFF1 = br.ReadBoolean();
|
||||||
|
IFF2 = br.ReadBoolean();
|
||||||
|
InterruptRequested = br.ReadBoolean();
|
||||||
|
AF.Word = br.ReadUInt16(); BC.Word = br.ReadUInt16(); DE.Word = br.ReadUInt16(); HL.Word = br.ReadUInt16();
|
||||||
|
AF_Prime.Word = br.ReadUInt16(); BC_Prime.Word = br.ReadUInt16(); DE_Prime.Word = br.ReadUInt16(); HL_Prime.Word = br.ReadUInt16();
|
||||||
|
IX.Word = br.ReadUInt16(); IY.Word = br.ReadUInt16();
|
||||||
|
PC = br.ReadUInt16(); SP = br.ReadUInt16(); I = br.ReadByte(); R = br.ReadByte();
|
||||||
|
}
|
||||||
|
|
||||||
private void ApplyWaitStates(ushort address)
|
private void ApplyWaitStates(ushort address)
|
||||||
{
|
{
|
||||||
// If a system (like a ULA) is attached and listening, ask it for the delay
|
// If a system (like a ULA) is attached and listening, ask it for the delay
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Core.Interfaces;
|
using Core.Interfaces;
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace Core.Memory
|
namespace Core.Memory
|
||||||
{
|
{
|
||||||
@@ -225,6 +226,28 @@ namespace Core.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SaveState(BinaryWriter bw)
|
||||||
|
{
|
||||||
|
bw.Write(_workRam);
|
||||||
|
bw.Write(_cartridgeRam);
|
||||||
|
bw.Write(SramUsed);
|
||||||
|
bw.Write(_mapperControl);
|
||||||
|
bw.Write(_romBank0);
|
||||||
|
bw.Write(_romBank1);
|
||||||
|
bw.Write(_romBank2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadState(BinaryReader br)
|
||||||
|
{
|
||||||
|
Array.Copy(br.ReadBytes(_workRam.Length), _workRam, _workRam.Length);
|
||||||
|
Array.Copy(br.ReadBytes(_cartridgeRam.Length), _cartridgeRam, _cartridgeRam.Length);
|
||||||
|
SramUsed = br.ReadBoolean();
|
||||||
|
_mapperControl = br.ReadByte();
|
||||||
|
_romBank0 = br.ReadInt32();
|
||||||
|
_romBank1 = br.ReadInt32();
|
||||||
|
_romBank2 = br.ReadInt32();
|
||||||
|
}
|
||||||
|
|
||||||
public void CleanRAMData()
|
public void CleanRAMData()
|
||||||
{
|
{
|
||||||
Array.Clear(_workRam, 0, _workRam.Length);
|
Array.Clear(_workRam, 0, _workRam.Length);
|
||||||
|
|||||||
@@ -73,5 +73,30 @@ namespace Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public void SaveState(string filePath)
|
||||||
|
{
|
||||||
|
using (var fs = new System.IO.FileStream(filePath, System.IO.FileMode.Create))
|
||||||
|
using (var bw = new System.IO.BinaryWriter(fs))
|
||||||
|
{
|
||||||
|
Cpu.SaveState(bw);
|
||||||
|
MemoryBus.SaveState(bw);
|
||||||
|
VideoProcessor.SaveState(bw);
|
||||||
|
AudioProcessor.SaveState(bw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadState(string filePath)
|
||||||
|
{
|
||||||
|
if (!System.IO.File.Exists(filePath)) return;
|
||||||
|
|
||||||
|
using (var fs = new System.IO.FileStream(filePath, System.IO.FileMode.Open))
|
||||||
|
using (var br = new System.IO.BinaryReader(fs))
|
||||||
|
{
|
||||||
|
Cpu.LoadState(br);
|
||||||
|
MemoryBus.LoadState(br);
|
||||||
|
VideoProcessor.LoadState(br);
|
||||||
|
AudioProcessor.LoadState(br);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace Core.Video
|
namespace Core.Video
|
||||||
{
|
{
|
||||||
@@ -11,6 +12,10 @@ namespace Core.Video
|
|||||||
public int[] FrameBuffer { get; private set; } = new int[256 * 192];
|
public int[] FrameBuffer { get; private set; } = new int[256 * 192];
|
||||||
private bool[] _priorityBuffer = new bool[256 * 192]; // Tracks priority pixels!
|
private bool[] _priorityBuffer = new bool[256 * 192]; // Tracks priority pixels!
|
||||||
|
|
||||||
|
// Hardware Latches
|
||||||
|
private int _latchedHScroll = 0;
|
||||||
|
private int _latchedVScroll = 0;
|
||||||
|
|
||||||
// The Control Port State Machine (Port 0xBF)
|
// The Control Port State Machine (Port 0xBF)
|
||||||
private bool _isSecondControlByte = false;
|
private bool _isSecondControlByte = false;
|
||||||
private ushort _controlWord = 0;
|
private ushort _controlWord = 0;
|
||||||
@@ -109,12 +114,20 @@ namespace Core.Video
|
|||||||
{
|
{
|
||||||
_tStateCounter += tStates;
|
_tStateCounter += tStates;
|
||||||
|
|
||||||
// 228 T-States per scanline
|
|
||||||
if (_tStateCounter >= 228)
|
if (_tStateCounter >= 228)
|
||||||
{
|
{
|
||||||
_tStateCounter -= 228;
|
_tStateCounter -= 228;
|
||||||
|
|
||||||
// --- MISSING LINE INTERRUPT COUNTDOWN ---
|
// 1. RENDER THE CURRENT LINE FIRST!
|
||||||
|
// The CPU just finished spending 228 cycles on this exact line.
|
||||||
|
// We draw it now using whatever scroll values the CPU set during that time.
|
||||||
|
if (_currentScanline < 192)
|
||||||
|
{
|
||||||
|
RenderScanline(_currentScanline);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. CHECK LINE INTERRUPTS
|
||||||
|
// Now that the line is drawn, we check if we need to alert the CPU for the NEXT line.
|
||||||
if (_currentScanline <= 192)
|
if (_currentScanline <= 192)
|
||||||
{
|
{
|
||||||
_lineCounter--;
|
_lineCounter--;
|
||||||
@@ -128,24 +141,21 @@ namespace Core.Video
|
|||||||
{
|
{
|
||||||
_lineCounter = Registers[10]; // Reload outside active display
|
_lineCounter = Registers[10]; // Reload outside active display
|
||||||
}
|
}
|
||||||
// ----------------------------------------
|
|
||||||
|
|
||||||
|
// 3. MOVE TO THE NEXT LINE
|
||||||
_currentScanline++;
|
_currentScanline++;
|
||||||
|
|
||||||
if (_currentScanline < 192)
|
|
||||||
{
|
|
||||||
RenderScanline(_currentScanline);
|
|
||||||
}
|
|
||||||
else if (_currentScanline == 192)
|
|
||||||
{
|
|
||||||
_statusRegister |= 0x80; // Set VBlank Flag
|
|
||||||
}
|
|
||||||
|
|
||||||
// End of the NTSC frame (262 lines)
|
|
||||||
if (_currentScanline > 261)
|
if (_currentScanline > 261)
|
||||||
{
|
{
|
||||||
_currentScanline = 0;
|
_currentScanline = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4. TRIGGER VBLANK
|
||||||
|
// The Master System sets the VBlank flag at the exact start of scanline 192.
|
||||||
|
if (_currentScanline == 192)
|
||||||
|
{
|
||||||
|
_statusRegister |= 0x80;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,8 +170,8 @@ namespace Core.Video
|
|||||||
|
|
||||||
// --- 1. RENDER BACKGROUND LINE ---
|
// --- 1. RENDER BACKGROUND LINE ---
|
||||||
ushort nameTableBase = (ushort)((Registers[2] & 0x0E) << 10);
|
ushort nameTableBase = (ushort)((Registers[2] & 0x0E) << 10);
|
||||||
byte scrollX = Registers[8];
|
int scrollX = Registers[8];
|
||||||
byte scrollY = Registers[9];
|
int scrollY = Registers[9];
|
||||||
|
|
||||||
// THE FIX: The bits are now in the correct order!
|
// THE FIX: The bits are now in the correct order!
|
||||||
bool lockColScroll = (Registers[0] & 0x80) != 0; // Bit 7: Locks right 8 columns (Fixes R-Type!)
|
bool lockColScroll = (Registers[0] & 0x80) != 0; // Bit 7: Locks right 8 columns (Fixes R-Type!)
|
||||||
@@ -303,5 +313,40 @@ namespace Core.Video
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public void SaveState(BinaryWriter bw)
|
||||||
|
{
|
||||||
|
bw.Write(VRAM);
|
||||||
|
bw.Write(CRAM);
|
||||||
|
bw.Write(Registers);
|
||||||
|
bw.Write(_isSecondControlByte);
|
||||||
|
bw.Write(_controlWord);
|
||||||
|
bw.Write(_readBuffer);
|
||||||
|
bw.Write(_tStateCounter);
|
||||||
|
bw.Write(_currentScanline);
|
||||||
|
bw.Write(_lineCounter);
|
||||||
|
bw.Write(_statusRegister);
|
||||||
|
|
||||||
|
// ADD THESE:
|
||||||
|
bw.Write(_latchedHScroll);
|
||||||
|
bw.Write(_latchedVScroll);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadState(BinaryReader br)
|
||||||
|
{
|
||||||
|
Array.Copy(br.ReadBytes(VRAM.Length), VRAM, VRAM.Length);
|
||||||
|
Array.Copy(br.ReadBytes(CRAM.Length), CRAM, CRAM.Length);
|
||||||
|
Array.Copy(br.ReadBytes(Registers.Length), Registers, Registers.Length);
|
||||||
|
_isSecondControlByte = br.ReadBoolean();
|
||||||
|
_controlWord = br.ReadUInt16();
|
||||||
|
_readBuffer = br.ReadByte();
|
||||||
|
_tStateCounter = br.ReadInt32();
|
||||||
|
_currentScanline = br.ReadInt32();
|
||||||
|
_lineCounter = br.ReadInt32();
|
||||||
|
_statusRegister = br.ReadByte();
|
||||||
|
|
||||||
|
// ADD THESE:
|
||||||
|
_latchedHScroll = br.ReadInt32();
|
||||||
|
_latchedVScroll = br.ReadInt32();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,6 +13,8 @@
|
|||||||
<PublishSingleFile>true</PublishSingleFile>
|
<PublishSingleFile>true</PublishSingleFile>
|
||||||
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
|
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
|
||||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
|
<StartupObject>Desktop.Program</StartupObject>
|
||||||
|
<AssemblyName>Parsons Master System</AssemblyName>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
36
Desktop/Form1.Designer.cs
generated
36
Desktop/Form1.Designer.cs
generated
@@ -36,11 +36,13 @@
|
|||||||
exitToolStripMenuItem = new ToolStripMenuItem();
|
exitToolStripMenuItem = new ToolStripMenuItem();
|
||||||
viewToolStripMenuItem = new ToolStripMenuItem();
|
viewToolStripMenuItem = new ToolStripMenuItem();
|
||||||
debuggerToolStripMenuItem = new ToolStripMenuItem();
|
debuggerToolStripMenuItem = new ToolStripMenuItem();
|
||||||
|
vRAMViewerToolStripMenuItem = new ToolStripMenuItem();
|
||||||
machineToolStripMenuItem = new ToolStripMenuItem();
|
machineToolStripMenuItem = new ToolStripMenuItem();
|
||||||
resetToolStripMenuItem = new ToolStripMenuItem();
|
resetToolStripMenuItem = new ToolStripMenuItem();
|
||||||
helpToolStripMenuItem = new ToolStripMenuItem();
|
helpToolStripMenuItem = new ToolStripMenuItem();
|
||||||
aboutToolStripMenuItem = new ToolStripMenuItem();
|
aboutToolStripMenuItem = new ToolStripMenuItem();
|
||||||
vRAMViewerToolStripMenuItem = new ToolStripMenuItem();
|
saveStateToolStripMenuItem = new ToolStripMenuItem();
|
||||||
|
loadStateToolStripMenuItem = new ToolStripMenuItem();
|
||||||
menuStrip1.SuspendLayout();
|
menuStrip1.SuspendLayout();
|
||||||
SuspendLayout();
|
SuspendLayout();
|
||||||
//
|
//
|
||||||
@@ -99,13 +101,20 @@
|
|||||||
// debuggerToolStripMenuItem
|
// debuggerToolStripMenuItem
|
||||||
//
|
//
|
||||||
debuggerToolStripMenuItem.Name = "debuggerToolStripMenuItem";
|
debuggerToolStripMenuItem.Name = "debuggerToolStripMenuItem";
|
||||||
debuggerToolStripMenuItem.Size = new Size(270, 34);
|
debuggerToolStripMenuItem.Size = new Size(221, 34);
|
||||||
debuggerToolStripMenuItem.Text = "Debugger";
|
debuggerToolStripMenuItem.Text = "Debugger";
|
||||||
debuggerToolStripMenuItem.Click += debuggerToolStripMenuItem_Click;
|
debuggerToolStripMenuItem.Click += debuggerToolStripMenuItem_Click;
|
||||||
//
|
//
|
||||||
|
// vRAMViewerToolStripMenuItem
|
||||||
|
//
|
||||||
|
vRAMViewerToolStripMenuItem.Name = "vRAMViewerToolStripMenuItem";
|
||||||
|
vRAMViewerToolStripMenuItem.Size = new Size(221, 34);
|
||||||
|
vRAMViewerToolStripMenuItem.Text = "VRAM Viewer";
|
||||||
|
vRAMViewerToolStripMenuItem.Click += vramViewerToolStripMenuItem_Click;
|
||||||
|
//
|
||||||
// machineToolStripMenuItem
|
// machineToolStripMenuItem
|
||||||
//
|
//
|
||||||
machineToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { resetToolStripMenuItem });
|
machineToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { resetToolStripMenuItem, saveStateToolStripMenuItem, loadStateToolStripMenuItem });
|
||||||
machineToolStripMenuItem.Name = "machineToolStripMenuItem";
|
machineToolStripMenuItem.Name = "machineToolStripMenuItem";
|
||||||
machineToolStripMenuItem.Size = new Size(94, 29);
|
machineToolStripMenuItem.Size = new Size(94, 29);
|
||||||
machineToolStripMenuItem.Text = "Machine";
|
machineToolStripMenuItem.Text = "Machine";
|
||||||
@@ -113,7 +122,7 @@
|
|||||||
// resetToolStripMenuItem
|
// resetToolStripMenuItem
|
||||||
//
|
//
|
||||||
resetToolStripMenuItem.Name = "resetToolStripMenuItem";
|
resetToolStripMenuItem.Name = "resetToolStripMenuItem";
|
||||||
resetToolStripMenuItem.Size = new Size(156, 34);
|
resetToolStripMenuItem.Size = new Size(270, 34);
|
||||||
resetToolStripMenuItem.Text = "Reset";
|
resetToolStripMenuItem.Text = "Reset";
|
||||||
resetToolStripMenuItem.Click += resetToolStripMenuItem_Click;
|
resetToolStripMenuItem.Click += resetToolStripMenuItem_Click;
|
||||||
//
|
//
|
||||||
@@ -130,12 +139,19 @@
|
|||||||
aboutToolStripMenuItem.Size = new Size(164, 34);
|
aboutToolStripMenuItem.Size = new Size(164, 34);
|
||||||
aboutToolStripMenuItem.Text = "About";
|
aboutToolStripMenuItem.Text = "About";
|
||||||
//
|
//
|
||||||
// vRAMViewerToolStripMenuItem
|
// saveStateToolStripMenuItem
|
||||||
//
|
//
|
||||||
vRAMViewerToolStripMenuItem.Name = "vRAMViewerToolStripMenuItem";
|
saveStateToolStripMenuItem.Name = "saveStateToolStripMenuItem";
|
||||||
vRAMViewerToolStripMenuItem.Size = new Size(270, 34);
|
saveStateToolStripMenuItem.Size = new Size(270, 34);
|
||||||
vRAMViewerToolStripMenuItem.Text = "VRAM Viewer";
|
saveStateToolStripMenuItem.Text = "Save State";
|
||||||
vRAMViewerToolStripMenuItem.Click += vramViewerToolStripMenuItem_Click;
|
saveStateToolStripMenuItem.Click += saveStateToolStripMenuItem_Click;
|
||||||
|
//
|
||||||
|
// loadStateToolStripMenuItem
|
||||||
|
//
|
||||||
|
loadStateToolStripMenuItem.Name = "loadStateToolStripMenuItem";
|
||||||
|
loadStateToolStripMenuItem.Size = new Size(270, 34);
|
||||||
|
loadStateToolStripMenuItem.Text = "Load State";
|
||||||
|
loadStateToolStripMenuItem.Click += loadStateToolStripMenuItem_Click;
|
||||||
//
|
//
|
||||||
// ParsonsForm1
|
// ParsonsForm1
|
||||||
//
|
//
|
||||||
@@ -168,5 +184,7 @@
|
|||||||
private ToolStripMenuItem includedToolStripMenuItem;
|
private ToolStripMenuItem includedToolStripMenuItem;
|
||||||
private ToolStripMenuItem selectROMToolStripMenuItem1;
|
private ToolStripMenuItem selectROMToolStripMenuItem1;
|
||||||
private ToolStripMenuItem vRAMViewerToolStripMenuItem;
|
private ToolStripMenuItem vRAMViewerToolStripMenuItem;
|
||||||
|
private ToolStripMenuItem saveStateToolStripMenuItem;
|
||||||
|
private ToolStripMenuItem loadStateToolStripMenuItem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
105
Desktop/Form1.cs
105
Desktop/Form1.cs
@@ -37,13 +37,8 @@ namespace Desktop
|
|||||||
public ParsonsForm1()
|
public ParsonsForm1()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
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.KeyPreview = true;
|
||||||
this.KeyDown += Form1_KeyDown;
|
this.KeyDown += Form1_KeyDown;
|
||||||
this.KeyUp += Form1_KeyUp;
|
this.KeyUp += Form1_KeyUp;
|
||||||
@@ -51,6 +46,21 @@ namespace Desktop
|
|||||||
this.ResizeRedraw = true;
|
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()
|
private void DrawScreen()
|
||||||
{
|
{
|
||||||
// Rapidly copy our VDP FrameBuffer into the Windows Bitmap
|
// 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!
|
// Always call the base method so Windows can draw your MenuStrip!
|
||||||
base.OnPaint(e);
|
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!
|
// 1. Set the rendering mode for perfect, chunky retro pixels!
|
||||||
e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
|
e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
|
||||||
@@ -73,7 +84,7 @@ namespace Desktop
|
|||||||
|
|
||||||
// 2. Calculate the drawing area.
|
// 2. Calculate the drawing area.
|
||||||
// We start drawing BELOW the MenuStrip so it doesn't get covered up.
|
// 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(
|
Rectangle renderArea = new Rectangle(
|
||||||
0,
|
0,
|
||||||
topOffset,
|
topOffset,
|
||||||
@@ -205,44 +216,39 @@ namespace Desktop
|
|||||||
_currentRomName = Path.GetFileNameWithoutExtension(filePath);
|
_currentRomName = Path.GetFileNameWithoutExtension(filePath);
|
||||||
this.Text = $"Parsons Master System - {_currentRomName}";
|
this.Text = $"Parsons Master System - {_currentRomName}";
|
||||||
|
|
||||||
// 5. LOAD THE NEW SAVE DATA!
|
// 5. LOAD THE NEW SAVE DATA FROM THE EXE FOLDER!
|
||||||
string savPath = Path.ChangeExtension(_currentRomPath, ".sav");
|
string savPath = GetSaveFilePath();
|
||||||
|
if (savPath != null)
|
||||||
|
{
|
||||||
_machine.MemoryBus.LoadSaveData(savPath);
|
_machine.MemoryBus.LoadSaveData(savPath);
|
||||||
|
}
|
||||||
|
|
||||||
// 6. Turn the power on!
|
// 6. Turn the power on!
|
||||||
StartEmulator();
|
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)
|
// Application.StartupPath is the exact folder containing your .exe
|
||||||
//{
|
string exeFolder = Application.StartupPath;
|
||||||
// StopEmulator();
|
|
||||||
// if (_emulatorTask != null)
|
|
||||||
// {
|
|
||||||
// await _emulatorTask;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // 2. Load the file
|
// Combines the exe folder with "GameName.sav"
|
||||||
// byte[] rom = File.ReadAllBytes(filePath);
|
return Path.Combine(exeFolder, _currentRomName + ".sav");
|
||||||
|
}
|
||||||
// // 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()
|
private void SaveCurrentSram()
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(_currentRomPath) && _machine != null)
|
if (_machine != null)
|
||||||
|
{
|
||||||
|
string savPath = GetSaveFilePath();
|
||||||
|
if (savPath != null)
|
||||||
{
|
{
|
||||||
// Swaps ".sms" for ".sav"
|
|
||||||
string savPath = Path.ChangeExtension(_currentRomPath, ".sav");
|
|
||||||
_machine.MemoryBus.SaveSaveData(savPath);
|
_machine.MemoryBus.SaveSaveData(savPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void PopulateIncludedRomsMenu()
|
private void PopulateIncludedRomsMenu()
|
||||||
{
|
{
|
||||||
@@ -361,5 +367,38 @@ namespace Desktop
|
|||||||
SaveCurrentSram();
|
SaveCurrentSram();
|
||||||
_audioPlayer?.Stop();
|
_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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user