Added double buffering ish.
This commit is contained in:
@@ -1827,7 +1827,8 @@ namespace Core.Cpu
|
|||||||
return 16;
|
return 16;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
throw new NotImplementedException($"Extended ED Opcode 0x{extendedOpcode:X2} at PC 0x{(PC - 1):X4} is not implemented.");
|
return 8;
|
||||||
|
//throw new NotImplementedException($"Extended ED Opcode 0x{extendedOpcode:X2} at PC 0x{(PC - 1):X4} is not implemented.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,41 +42,80 @@ namespace Core.Memory
|
|||||||
|
|
||||||
public byte Read(ushort address)
|
public byte Read(ushort address)
|
||||||
{
|
{
|
||||||
if (address < 0x4000) // ROM Slot 0 (0x0000 - 0x3FFF)
|
if (address < 0x4000)
|
||||||
{
|
{
|
||||||
// SMS Hardware Quirk: The first 1KB (0x0000 - 0x03FF) is NEVER paged.
|
if (address < 0x0400)
|
||||||
if (address < 0x0400) return ReadFromCartridge(0, address);
|
|
||||||
|
|
||||||
return ReadFromCartridge(_romBank0, address);
|
|
||||||
}
|
|
||||||
if (address < 0x8000) // ROM Slot 1 (0x4000 - 0x7FFF)
|
|
||||||
{
|
|
||||||
return ReadFromCartridge(_romBank1, address & 0x3FFF);
|
|
||||||
}
|
|
||||||
if (address < 0xC000) // ROM Slot 2 (0x8000 - 0xBFFF)
|
|
||||||
{
|
|
||||||
// --- THE MISSING LINK: Read from Save RAM if enabled! ---
|
|
||||||
if ((_mapperControl & 0x08) != 0)
|
|
||||||
{
|
{
|
||||||
int ramBank = (_mapperControl & 0x04) != 0 ? 1 : 0;
|
return _cartridgeRom[address];
|
||||||
int ramOffset = (ramBank * 0x4000) + (address & 0x1FFF);
|
|
||||||
return _cartridgeRam[ramOffset];
|
|
||||||
}
|
}
|
||||||
|
// Slot 0
|
||||||
// Otherwise, read from the ROM as usual
|
return _cartridgeRom[(_romBank0 * 0x4000) + address];
|
||||||
return ReadFromCartridge(_romBank2, address & 0x3FFF);
|
}
|
||||||
|
if (address < 0x8000)
|
||||||
|
{
|
||||||
|
// Slot 1
|
||||||
|
return _cartridgeRom[(_romBank1 * 0x4000) + (address - 0x4000)];
|
||||||
|
}
|
||||||
|
if (address < 0xC000)
|
||||||
|
{
|
||||||
|
// Slot 2 (Or SRAM)
|
||||||
|
if (SramUsed && (_mapperControl & 0x08) != 0)
|
||||||
|
return _cartridgeRam[address - 0x8000];
|
||||||
|
else
|
||||||
|
return _cartridgeRom[(_romBank2 * 0x4000) + (address - 0x8000)];
|
||||||
}
|
}
|
||||||
|
|
||||||
// System RAM (0xC000 - 0xFFFF)
|
// THE FIX 1: Bitwise AND perfectly forces 0xE000-0xFFFF to mirror down to 0xC000!
|
||||||
return _workRam[address & 0x1FFF];
|
return _workRam[address & 0x1FFF];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Write(ushort address, byte value)
|
||||||
|
{
|
||||||
|
if (address < 0x8000) return; // Cannot write to physical ROM
|
||||||
|
|
||||||
|
if (address < 0xC000)
|
||||||
|
{
|
||||||
|
if (SramUsed && (_mapperControl & 0x08) != 0)
|
||||||
|
_cartridgeRam[address - 0x8000] = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// THE FIX 1: Save to RAM using the mirror mask
|
||||||
|
_workRam[address & 0x1FFF] = value;
|
||||||
|
|
||||||
|
// THE FIX 2: Mapper Control Registers live at the very end of the mirrored RAM!
|
||||||
|
if (address >= 0xFFFC)
|
||||||
|
{
|
||||||
|
// Calculate the absolute maximum number of banks inside the currently loaded ROM
|
||||||
|
int totalBanks = _cartridgeRom.Length / 0x4000;
|
||||||
|
|
||||||
|
// Safety catch for empty slots
|
||||||
|
if (totalBanks == 0) totalBanks = 1;
|
||||||
|
|
||||||
|
if (address == 0xFFFC)
|
||||||
|
{
|
||||||
|
_mapperControl = value;
|
||||||
|
}
|
||||||
|
else if (address == 0xFFFD)
|
||||||
|
{
|
||||||
|
_romBank0 = value % totalBanks; // Force the bank to stay inside the array bounds!
|
||||||
|
}
|
||||||
|
else if (address == 0xFFFE)
|
||||||
|
{
|
||||||
|
_romBank1 = value % totalBanks;
|
||||||
|
}
|
||||||
|
else if (address == 0xFFFF)
|
||||||
|
{
|
||||||
|
_romBank2 = value % totalBanks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//public byte Read(ushort address)
|
//public byte Read(ushort address)
|
||||||
//{
|
//{
|
||||||
// if (address < 0x4000) // ROM Slot 0 (0x0000 - 0x3FFF)
|
// if (address < 0x4000) // ROM Slot 0 (0x0000 - 0x3FFF)
|
||||||
// {
|
// {
|
||||||
// // SMS Hardware Quirk: The first 1KB (0x0000 - 0x03FF) is NEVER paged.
|
// // SMS Hardware Quirk: The first 1KB (0x0000 - 0x03FF) is NEVER paged.
|
||||||
// // It is hardwired to Bank 0 so the interrupt handlers don't crash.
|
|
||||||
// if (address < 0x0400) return ReadFromCartridge(0, address);
|
// if (address < 0x0400) return ReadFromCartridge(0, address);
|
||||||
|
|
||||||
// return ReadFromCartridge(_romBank0, address);
|
// return ReadFromCartridge(_romBank0, address);
|
||||||
@@ -87,103 +126,73 @@ namespace Core.Memory
|
|||||||
// }
|
// }
|
||||||
// if (address < 0xC000) // ROM Slot 2 (0x8000 - 0xBFFF)
|
// if (address < 0xC000) // ROM Slot 2 (0x8000 - 0xBFFF)
|
||||||
// {
|
// {
|
||||||
// // Check if Cartridge RAM is enabled (Bit 3 of Mapper Control)
|
// // --- THE MISSING LINK: Read from Save RAM if enabled! ---
|
||||||
// if ((_mapperControl & 0x08) != 0)
|
// if ((_mapperControl & 0x08) != 0)
|
||||||
// {
|
// {
|
||||||
// // Bit 2 decides if we read the first 16KB half or the second 16KB half of the RAM chip
|
|
||||||
// int ramBank = (_mapperControl & 0x04) != 0 ? 1 : 0;
|
// int ramBank = (_mapperControl & 0x04) != 0 ? 1 : 0;
|
||||||
// int ramOffset = (ramBank * 0x4000) + (address & 0x3FFF);
|
// int ramOffset = (ramBank * 0x4000) + (address & 0x1FFF);
|
||||||
// return _cartridgeRam[ramOffset];
|
// return _cartridgeRam[ramOffset];
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
// // Otherwise, read from the ROM as usual
|
||||||
// return ReadFromCartridge(_romBank2, address & 0x3FFF);
|
// return ReadFromCartridge(_romBank2, address & 0x3FFF);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// // If we are here, we are in System RAM (0xC000 - 0xFFFF)
|
// // System RAM (0xC000 - 0xFFFF)
|
||||||
// // The & 0x1FFF handles the 8KB mirroring automatically!
|
|
||||||
// return _workRam[address & 0x1FFF];
|
// return _workRam[address & 0x1FFF];
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
|
||||||
//public void Write(ushort address, byte value)
|
//public void Write(ushort address, byte value)
|
||||||
//{
|
//{
|
||||||
// // --- 1. CARTRIDGE RAM (Save Data) ---
|
|
||||||
// if (address < 0xC000)
|
// if (address < 0xC000)
|
||||||
// {
|
// {
|
||||||
|
// // Bypass the lock if they are trying to save their game!
|
||||||
// if (address >= 0x8000 && (_mapperControl & 0x08) != 0)
|
// if (address >= 0x8000 && (_mapperControl & 0x08) != 0)
|
||||||
// {
|
// {
|
||||||
|
// SramUsed = true;
|
||||||
// int ramBank = (_mapperControl & 0x04) != 0 ? 1 : 0;
|
// int ramBank = (_mapperControl & 0x04) != 0 ? 1 : 0;
|
||||||
// int ramOffset = (ramBank * 0x4000) + (address & 0x3FFF);
|
// int ramOffset = (ramBank * 0x4000) + (address & 0x1FFF);
|
||||||
// _cartridgeRam[ramOffset] = value;
|
// _cartridgeRam[ramOffset] = value;
|
||||||
|
// return;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// // You cannot write to a ROM cartridge!
|
// // You cannot write to a ROM cartridge!
|
||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// // --- 2. SYSTEM RAM ---
|
// // Write to System RAM
|
||||||
// _workRam[address & 0x1FFF] = value;
|
// _workRam[address & 0x1FFF] = value;
|
||||||
|
|
||||||
// // --- 3. SEGA MAPPER (With Hardware Mirroring!) ---
|
// // --- THE SEGA MAPPER ---
|
||||||
// // By checking the masked address (address & 0x1FFF), we perfectly
|
// if (address == 0xFFFC) // <-- ADD THIS
|
||||||
// // catch writes to 0xFFFC-0xFFFF *and* their 0xDFFC-0xDFFF mirrors!
|
// {
|
||||||
// int mapperAddress = address & 0x1FFF;
|
// _mapperControl = value;
|
||||||
|
// }
|
||||||
|
// else if (address == 0xFFFD)
|
||||||
|
// {
|
||||||
|
// _romBank0 = value;
|
||||||
|
// }
|
||||||
|
|
||||||
// if (mapperAddress == 0x1FFC) _mapperControl = value;
|
// // Write to System RAM
|
||||||
// else if (mapperAddress == 0x1FFD) _romBank0 = value;
|
// _workRam[address & 0x1FFF] = value;
|
||||||
// else if (mapperAddress == 0x1FFE) _romBank1 = value;
|
|
||||||
// else if (mapperAddress == 0x1FFF) _romBank2 = value;
|
// // --- THE SEGA MAPPER ---
|
||||||
|
// // If the CPU wrote to the very top of memory, it is commanding a bank swap!
|
||||||
|
// if (address == 0xFFFD)
|
||||||
|
// {
|
||||||
|
// _romBank0 = value;
|
||||||
|
// }
|
||||||
|
// else if (address == 0xFFFE)
|
||||||
|
// {
|
||||||
|
// _romBank1 = value;
|
||||||
|
// }
|
||||||
|
// else if (address == 0xFFFF)
|
||||||
|
// {
|
||||||
|
// _romBank2 = value;
|
||||||
|
// }
|
||||||
//}
|
//}
|
||||||
|
|
||||||
public void Write(ushort address, byte value)
|
|
||||||
{
|
|
||||||
if (address < 0xC000)
|
|
||||||
{
|
|
||||||
// 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 & 0x1FFF);
|
|
||||||
_cartridgeRam[ramOffset] = value;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// You cannot write to a ROM cartridge!
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write to System RAM
|
|
||||||
_workRam[address & 0x1FFF] = value;
|
|
||||||
|
|
||||||
// --- THE SEGA MAPPER ---
|
|
||||||
if (address == 0xFFFC) // <-- ADD THIS
|
|
||||||
{
|
|
||||||
_mapperControl = value;
|
|
||||||
}
|
|
||||||
else if (address == 0xFFFD)
|
|
||||||
{
|
|
||||||
_romBank0 = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write to System RAM
|
|
||||||
_workRam[address & 0x1FFF] = value;
|
|
||||||
|
|
||||||
// --- THE SEGA MAPPER ---
|
|
||||||
// If the CPU wrote to the very top of memory, it is commanding a bank swap!
|
|
||||||
if (address == 0xFFFD)
|
|
||||||
{
|
|
||||||
_romBank0 = value;
|
|
||||||
}
|
|
||||||
else if (address == 0xFFFE)
|
|
||||||
{
|
|
||||||
_romBank1 = value;
|
|
||||||
}
|
|
||||||
else if (address == 0xFFFF)
|
|
||||||
{
|
|
||||||
_romBank2 = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte ReadFromCartridge(int bankIndex, int offset)
|
private byte ReadFromCartridge(int bankIndex, int offset)
|
||||||
{
|
{
|
||||||
if (!_isCartridgeLoaded) return 0xFF; // Floating bus if no game
|
if (!_isCartridgeLoaded) return 0xFF; // Floating bus if no game
|
||||||
|
|||||||
180
Desktop/Form1.cs
180
Desktop/Form1.cs
@@ -63,11 +63,17 @@ namespace Desktop
|
|||||||
PopulateIncludedRomsMenu();
|
PopulateIncludedRomsMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawScreen()
|
private void DrawScreen(int[] safeFrame)
|
||||||
{
|
{
|
||||||
// Rapidly copy our VDP FrameBuffer into the Windows Bitmap
|
// Rapidly the Frame into the Windows Bitmap
|
||||||
var data = _screenBitmap.LockBits(new Rectangle(0, 0, 256, 192), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
|
var data = _screenBitmap.LockBits(new Rectangle(0, 0, 256, 192), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
|
||||||
Marshal.Copy(_machine.VideoProcessor.FrameBuffer, 0, data.Scan0, _machine.VideoProcessor.FrameBuffer.Length);
|
|
||||||
|
//Frame buffer draw
|
||||||
|
Marshal.Copy(safeFrame, 0, data.Scan0, safeFrame.Length);
|
||||||
|
|
||||||
|
//Direct VDP draw
|
||||||
|
//Marshal.Copy(_machine.VideoProcessor.FrameBuffer, 0, data.Scan0, _machine.VideoProcessor.FrameBuffer.Length);
|
||||||
|
|
||||||
_screenBitmap.UnlockBits(data);
|
_screenBitmap.UnlockBits(data);
|
||||||
this.Invalidate();
|
this.Invalidate();
|
||||||
TotalFrameCount++;
|
TotalFrameCount++;
|
||||||
@@ -137,88 +143,108 @@ namespace Desktop
|
|||||||
double nextFrameTargetTime = _stopwatch.Elapsed.TotalMilliseconds + TargetFrameTime;
|
double nextFrameTargetTime = _stopwatch.Elapsed.TotalMilliseconds + TargetFrameTime;
|
||||||
double lastFpsUpdate = _stopwatch.Elapsed.TotalMilliseconds;
|
double lastFpsUpdate = _stopwatch.Elapsed.TotalMilliseconds;
|
||||||
int framesThisSecond = 0;
|
int framesThisSecond = 0;
|
||||||
|
try
|
||||||
while (IsRunning)
|
|
||||||
{
|
{
|
||||||
//targetFps = isTurboMode ? 1000 : 59.94;
|
while (IsRunning)
|
||||||
//TargetFrameTime = 1000.0 / targetFps;
|
|
||||||
BeginInvoke((System.Windows.Forms.MethodInvoker)delegate
|
|
||||||
{
|
{
|
||||||
targetFps = isTurboMode ? 1000 : 59.94;
|
//targetFps = isTurboMode ? 1000 : 59.94;
|
||||||
TargetFrameTime = 1000.0 / targetFps;
|
//TargetFrameTime = 1000.0 / targetFps;
|
||||||
});
|
|
||||||
double frameStartTime = _stopwatch.Elapsed.TotalMilliseconds;
|
|
||||||
|
|
||||||
// --- POLL PHYSICAL CONTROLLER ---
|
|
||||||
if (XInput.XInputGetState(0, out XInput.XINPUT_STATE state) == 0)
|
|
||||||
{
|
|
||||||
ushort btns = state.Gamepad.wButtons;
|
|
||||||
byte padState = 0xFF;
|
|
||||||
|
|
||||||
if ((btns & 0x0001) != 0) padState &= 0xFE; // Up
|
|
||||||
if ((btns & 0x0002) != 0) padState &= 0xFD; // Down
|
|
||||||
if ((btns & 0x0004) != 0) padState &= 0xFB; // Left
|
|
||||||
if ((btns & 0x0008) != 0) padState &= 0xF7; // Right
|
|
||||||
if ((btns & 0x1000) != 0) padState &= 0xEF; // Button 1
|
|
||||||
if ((btns & 0x2000) != 0) padState &= 0xDF; // Button 2
|
|
||||||
|
|
||||||
// THE FIX: Constantly update the gamepad state, even when it's 0xFF!
|
|
||||||
_machine.IoBus.Joypad1Gamepad = padState;
|
|
||||||
}
|
|
||||||
// --------------------------------
|
|
||||||
_machine.RunFrame();
|
|
||||||
|
|
||||||
// 2. FIRE AND FORGET! Tell Windows to draw, but DO NOT WAIT for it to finish!
|
|
||||||
BeginInvoke((System.Windows.Forms.MethodInvoker)delegate { DrawScreen(); });
|
|
||||||
|
|
||||||
// 3. Safety catch
|
|
||||||
if (_machine.Breakpoint.HasValue && _machine.Cpu.PC == _machine.Breakpoint.Value)
|
|
||||||
{
|
|
||||||
IsRunning = false;
|
|
||||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
|
||||||
BeginInvoke((System.Windows.Forms.MethodInvoker)delegate { _debugger?.uiUpdateTimer_Tick(null, EventArgs.Empty); });
|
|
||||||
#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Calculate TRUE Frame Time (Compute Time) BEFORE we go to sleep!
|
|
||||||
FrameTime = _stopwatch.Elapsed.TotalMilliseconds - frameStartTime;
|
|
||||||
|
|
||||||
// --- HIGH PRECISION THROTTLE ---
|
|
||||||
while (_stopwatch.Elapsed.TotalMilliseconds < nextFrameTargetTime)
|
|
||||||
{
|
|
||||||
if (nextFrameTargetTime - _stopwatch.Elapsed.TotalMilliseconds > 2.0)
|
|
||||||
{
|
|
||||||
Thread.Sleep(1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Thread.SpinWait(10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double currentTime = _stopwatch.Elapsed.TotalMilliseconds;
|
|
||||||
TotalFrameCount++;
|
|
||||||
framesThisSecond++;
|
|
||||||
|
|
||||||
if (currentTime - lastFpsUpdate >= 1000.0)
|
|
||||||
{
|
|
||||||
FramesPerSecond = framesThisSecond / ((currentTime - lastFpsUpdate) / 1000.0);
|
|
||||||
framesThisSecond = 0;
|
|
||||||
lastFpsUpdate = currentTime;
|
|
||||||
|
|
||||||
BeginInvoke((System.Windows.Forms.MethodInvoker)delegate
|
BeginInvoke((System.Windows.Forms.MethodInvoker)delegate
|
||||||
{
|
{
|
||||||
this.Text = $"{_romType} Parsons Master System 2026 - {_currentRomName} [FPS: {FramesPerSecond:F0}]";
|
targetFps = isTurboMode ? 1000 : 59.94;
|
||||||
|
TargetFrameTime = 1000.0 / targetFps;
|
||||||
});
|
});
|
||||||
|
double frameStartTime = _stopwatch.Elapsed.TotalMilliseconds;
|
||||||
|
|
||||||
|
// --- POLL PHYSICAL CONTROLLER ---
|
||||||
|
if (XInput.XInputGetState(0, out XInput.XINPUT_STATE state) == 0)
|
||||||
|
{
|
||||||
|
ushort btns = state.Gamepad.wButtons;
|
||||||
|
byte padState = 0xFF;
|
||||||
|
|
||||||
|
if ((btns & 0x0001) != 0) padState &= 0xFE; // Up
|
||||||
|
if ((btns & 0x0002) != 0) padState &= 0xFD; // Down
|
||||||
|
if ((btns & 0x0004) != 0) padState &= 0xFB; // Left
|
||||||
|
if ((btns & 0x0008) != 0) padState &= 0xF7; // Right
|
||||||
|
if ((btns & 0x1000) != 0) padState &= 0xEF; // Button 1
|
||||||
|
if ((btns & 0x2000) != 0) padState &= 0xDF; // Button 2
|
||||||
|
|
||||||
|
// THE FIX: Constantly update the gamepad state, even when it's 0xFF!
|
||||||
|
_machine.IoBus.Joypad1Gamepad = padState;
|
||||||
|
}
|
||||||
|
// --------------------------------
|
||||||
|
_machine.RunFrame();
|
||||||
|
|
||||||
|
int[] frozenFrame = (int[])_machine.VideoProcessor.FrameBuffer.Clone();
|
||||||
|
|
||||||
|
// 2. FIRE AND FORGET! Tell Windows to draw, but DO NOT WAIT for it to finish!
|
||||||
|
BeginInvoke((System.Windows.Forms.MethodInvoker)delegate { DrawScreen(frozenFrame); });
|
||||||
|
|
||||||
|
// 3. Safety catch
|
||||||
|
if (_machine.Breakpoint.HasValue && _machine.Cpu.PC == _machine.Breakpoint.Value)
|
||||||
|
{
|
||||||
|
IsRunning = false;
|
||||||
|
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||||
|
BeginInvoke((System.Windows.Forms.MethodInvoker)delegate { _debugger?.uiUpdateTimer_Tick(null, EventArgs.Empty); });
|
||||||
|
#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Calculate TRUE Frame Time (Compute Time) BEFORE we go to sleep!
|
||||||
|
FrameTime = _stopwatch.Elapsed.TotalMilliseconds - frameStartTime;
|
||||||
|
|
||||||
|
// --- HIGH PRECISION THROTTLE ---
|
||||||
|
while (_stopwatch.Elapsed.TotalMilliseconds < nextFrameTargetTime)
|
||||||
|
{
|
||||||
|
if (nextFrameTargetTime - _stopwatch.Elapsed.TotalMilliseconds > 2.0)
|
||||||
|
{
|
||||||
|
Thread.Sleep(1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Thread.SpinWait(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double currentTime = _stopwatch.Elapsed.TotalMilliseconds;
|
||||||
|
TotalFrameCount++;
|
||||||
|
framesThisSecond++;
|
||||||
|
|
||||||
|
if (currentTime - lastFpsUpdate >= 1000.0)
|
||||||
|
{
|
||||||
|
FramesPerSecond = framesThisSecond / ((currentTime - lastFpsUpdate) / 1000.0);
|
||||||
|
framesThisSecond = 0;
|
||||||
|
lastFpsUpdate = currentTime;
|
||||||
|
|
||||||
|
BeginInvoke((System.Windows.Forms.MethodInvoker)delegate
|
||||||
|
{
|
||||||
|
this.Text = $"{_romType} Parsons Master System 2026 - {_currentRomName} [FPS: {FramesPerSecond:F0}]";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
nextFrameTargetTime += TargetFrameTime;
|
||||||
|
|
||||||
|
if (currentTime > nextFrameTargetTime + TargetFrameTime)
|
||||||
|
{
|
||||||
|
nextFrameTargetTime = currentTime + TargetFrameTime;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
IsRunning = false;
|
||||||
|
|
||||||
nextFrameTargetTime += TargetFrameTime;
|
this.Invoke((System.Windows.Forms.MethodInvoker)delegate
|
||||||
|
|
||||||
if (currentTime > nextFrameTargetTime + TargetFrameTime)
|
|
||||||
{
|
{
|
||||||
nextFrameTargetTime = currentTime + TargetFrameTime;
|
MessageBox.Show(
|
||||||
}
|
$"The Z80 CPU Panicked!\n\n" +
|
||||||
|
$"PC Address: 0x{_machine.Cpu.PC:X4}\n" +
|
||||||
|
$"Error: {ex.Message}\n\n" +
|
||||||
|
$"Stack Trace:\n{ex.StackTrace}",
|
||||||
|
"Fatal Emulator Crash",
|
||||||
|
MessageBoxButtons.OK,
|
||||||
|
MessageBoxIcon.Error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user