Interrupts added at 50fps. Dummy keyboard. Ready for graphics!

This commit is contained in:
2026-04-15 15:44:24 +01:00
parent d9966099f8
commit 960f2b85cc
2 changed files with 205 additions and 23 deletions

View File

@@ -81,26 +81,68 @@ namespace Desktop
_isRunning = true;
btnRun.Text = "Stop";
// Fire up a background thread
// Fire up a background thread
await Task.Run(() =>
{
try
{
// 1. Setup Frame Timing Variables
const int TStatesPerFrame = 69888;
long nextFrameTargetTStates = _cpu.TotalTStates + TStatesPerFrame;
// 2. Setup Real-World Throttling
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
long frameCount = 0;
while (_isRunning)
{
// --- NEW: Breakpoint Check ---
// We check BEFORE stepping so it stops exactly on the instruction
// --- Breakpoint Check ---
if (_breakpoint.HasValue && _cpu.PC == _breakpoint.Value)
{
_isRunning = false;
break; // Cleanly exit the while loop
break;
}
// -----------------------------
// --- Execute Instruction ---
_cpu.Step();
}
// --- Check for End of Frame ---
if (_cpu.TotalTStates >= nextFrameTargetTStates)
{
// 1. Fire the 50Hz Interrupt!
_cpu.RequestInterrupt();
// 2. Advance the target to the next frame
nextFrameTargetTStates += TStatesPerFrame;
frameCount++;
// 3. Throttle to real-time (50 frames per second = 20ms per frame)
long targetTimeMs = frameCount * 20;
long elapsedMs = stopwatch.ElapsedMilliseconds;
if (elapsedMs < targetTimeMs)
{
// The CPU ran too fast! Put the thread to sleep to let reality catch up.
System.Threading.Thread.Sleep((int)(targetTimeMs - elapsedMs));
}
// Optional: Update the UI every 10 frames so you can watch it run safely
// without overwhelming the WinForms rendering engine.
if (frameCount % 10 == 0)
{
this.Invoke((MethodInvoker)delegate
{
UpdateDisplay();
});
}
}
//this.Invoke((MethodInvoker)delegate {
// UpdateDisplay();
// });
}
}
catch (Exception ex)
{
@@ -250,6 +292,11 @@ namespace Desktop
string mnemonic;
int instructionLength = 1; // Default to 1
byte cbOp = 0;
int opGroup = 0;
int targetBit = 0;
int regIdx = 0;
string[] regNames = { "B", "C", "D", "E", "H", "L", "(HL)", "A" };
string targetReg = "";
switch (opcode)
{
@@ -388,6 +435,14 @@ namespace Desktop
case 0x2B:
mnemonic = "DEC HL";
break;
case 0x2E:
byte lImm = _memoryBus.Read((ushort)(currentPc + 1));
mnemonic = $"LD L, 0x{lImm:X2}";
instructionLength = 2;
break;
case 0x2F:
mnemonic = "CPL";
break;
case 0x30:
sbyte jrNcOffset = (sbyte)_memoryBus.Read((ushort)(currentPc + 1));
ushort dest = (ushort)(currentPc + 2 + jrNcOffset);
@@ -675,27 +730,37 @@ namespace Desktop
break;
case 0xCB:
cbOp = _memoryBus.Read((ushort)(currentPc + 1));
if (cbOp == 0x7E)
// --- THE MISSING MATH EXTRACTION ---
opGroup = cbOp >> 6; // 00 = Shift, 01 = BIT, 10 = RES, 11 = SET
targetBit = (cbOp >> 3) & 0x07; // Extracts a number 0-7
regIdx = cbOp & 0x07; // Extracts register index 0-7
// Map the 0-7 index directly to the Z80 register names
targetReg = regNames[regIdx];
if (opGroup == 0) // Shift/Rotate Group (0x00 to 0x3F)
{
mnemonic = "BIT 7, (HL)";
string[] shiftNames = { "RLC", "RRC", "RL", "RR", "SLA", "SRA", "SLL", "SRL" };
string shiftOp = shiftNames[(cbOp >> 3) & 0x07];
mnemonic = $"{shiftOp} {targetReg}";
}
else if (cbOp == 0x86)
else if (opGroup == 1) // BIT Group (0x40 to 0x7F)
{
mnemonic = "RES 0, (HL)";
mnemonic = $"BIT {targetBit}, {targetReg}";
}
else if (cbOp == 0xAE)
else if (opGroup == 2) // RES Group (0x80 to 0xBF)
{
mnemonic = "RES 5, (HL)";
mnemonic = $"RES {targetBit}, {targetReg}";
}
else if (cbOp == 0xC6)
else if (opGroup == 3) // SET Group (0xC0 to 0xFF)
{
mnemonic = "SET 0, (HL)";
mnemonic = $"SET {targetBit}, {targetReg}";
}
else
{
mnemonic = $"CB UNKNOWN (0x{cbOp:X2})";
mnemonic = $"EXT UNKNOWN (ED {cbOp:X2})";
}
instructionLength = 2;
break;
case 0xCD:
@@ -791,6 +856,10 @@ namespace Desktop
mnemonic = $"LD (0x{addr73:X4}), SP";
instructionLength = 4;
break;
case 0x78:
mnemonic = "IN A, (C)";
instructionLength = 2;
break;
case 0xB0:
mnemonic = "LDIR";
instructionLength = 2;
@@ -877,13 +946,11 @@ namespace Desktop
{
cbOp = _memoryBus.Read((ushort)(currentPc + 1));
int opGroup = cbOp >> 6;
int targetBit = (cbOp >> 3) & 0x07;
int regIdx = cbOp & 0x07;
opGroup = cbOp >> 6;
targetBit = (cbOp >> 3) & 0x07;
regIdx = cbOp & 0x07;
// Map the 0-7 index directly to the Z80 register names
string[] regNames = { "B", "C", "D", "E", "H", "L", "(HL)", "A" };
string targetReg = regNames[regIdx];
targetReg = regNames[regIdx];
if (opGroup == 1) mnemonic = $"BIT {targetBit}, {targetReg}";
else if (opGroup == 2) mnemonic = $"RES {targetBit}, {targetReg}";