Interrupts added at 50fps. Dummy keyboard. Ready for graphics!
This commit is contained in:
@@ -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}";
|
||||
|
||||
Reference in New Issue
Block a user