Updated Z80 CPU to fix interrupts etc...

This commit is contained in:
2026-05-26 22:02:47 +01:00
parent b5695b5c2f
commit ce46e7ed52
6 changed files with 121 additions and 47 deletions

View File

@@ -30,7 +30,8 @@ namespace Core.Cpu
public bool IFF1 { get; private set; } = false;
public bool IFF2 { get; private set; } = false;
public bool InterruptRequested { get; private set; } = false;
private bool _eiPending = false;
private int _eiDelay = 0;
public bool IsHalted { get; private set; } = false;
// Main Register Set
public RegisterPair AF;
@@ -50,7 +51,7 @@ namespace Core.Cpu
// Special Purpose Registers
public ushort PC; // Program Counter
public ushort SP; // Stack Pointer
public ushort SP;
public byte I; // Interrupt Vector
public byte R; // Memory Refresh
@@ -98,6 +99,10 @@ namespace Core.Cpu
IFF2 = false;
InterruptMode = 0;
TotalTStates = 0;
_eiDelay = 0;
IsHalted = false;
InterruptRequested = false;
}
public void SaveState(BinaryWriter bw)
@@ -137,6 +142,7 @@ namespace Core.Cpu
public int RequestInterrupt()
{
IsHalted = false;
InterruptRequested = true;
// 1. If the ROM has disabled interrupts (DI), ignore the request
if (!IFF1) return 0;
@@ -145,6 +151,8 @@ namespace Core.Cpu
IFF1 = false;
IFF2 = false;
_eiDelay = 0;
// 3. Push the current Program Counter to the stack so we can return later
Push(PC);
@@ -216,22 +224,36 @@ namespace Core.Cpu
public int Step()
{
bool triggerEi = _eiPending;
int tStates;
// Fetch the next opcode and increment the Program Counter
byte opcode = ReadMemory(PC++);
R = (byte)((R + 1) & 0x7F);
int tStates = ExecuteOpcode(opcode);
TotalTStates += tStates;
R = (byte)((R & 0x80) | ((R + 1) & 0x7F));
if (triggerEi)
if (IsHalted)
{
IFF1 = true;
IFF2 = true;
_eiPending = false;
// The CPU is asleep! Do not fetch instructions, just pass the time.
tStates = 4;
TotalTStates += tStates;
}
else
{
// Normal execution
byte opcode = ReadMemory(PC++);
tStates = ExecuteOpcode(opcode);
TotalTStates += tStates;
}
// The interrupt enablement perfectly mimics the physical hardware delay.
// This MUST tick down even if the CPU is halted!
if (_eiDelay > 0)
{
_eiDelay--;
if (_eiDelay == 0)
{
IFF1 = true;
IFF2 = true;
}
}
// Decode and execute
return tStates;
}
@@ -931,17 +953,21 @@ namespace Core.Cpu
case 0x73: WriteMemory(HL.Word, DE.Low); return 7;
case 0x74: WriteMemory(HL.Word, HL.High); return 7;
case 0x75: WriteMemory(HL.Word, HL.Low); return 7;
case 0x76: //HALT
if (!InterruptRequested)
{
PC--;
return 4;
}
else
{
InterruptRequested = false;
return 4;
}
case 0x76: // HALT
IsHalted = true;
return 4;
//case 0x76: //HALT
// if (!InterruptRequested)
// {
// PC--;
// return 4;
// }
// else
// {
// InterruptRequested = false;
// return 4;
// }
case 0x77: WriteMemory(HL.Word, AF.High); return 7;
// --- LD A, r ---
@@ -1279,7 +1305,11 @@ namespace Core.Cpu
case 0xF3: // DI
IFF1 = false;
IFF2 = false;
_eiPending = false;
_eiDelay = 0; // Hard cancel any pending EI delays
return 4;
case 0xFB: // EI
_eiDelay = 2; // Ticks down across the current and subsequent instruction
return 4;
case 0xf5: //push af
Push(AF.Word);
@@ -1290,9 +1320,7 @@ namespace Core.Cpu
case 0xF9: // LD SP, HL
SP = HL.Word;
return 6;
case 0xFB: // EI
_eiPending = true;
return 4;
case 0xFD:
return ExecuteFDPrefix();
case 0xFE: // CP n

View File

@@ -64,7 +64,7 @@ namespace Core.Memory
if (address < 0xC000)
{
// Slot 2 (Or SRAM)
if (SramUsed && (_mapperControl & 0x08) != 0)
if ((_mapperControl & 0x08) != 0)
return _cartridgeRam[address - 0x8000];
else
return _cartridgeRom[(_romBank2 * 0x4000) + (address - 0x8000)];
@@ -80,7 +80,7 @@ namespace Core.Memory
if (address < 0xC000)
{
if (SramUsed && (_mapperControl & 0x08) != 0)
if ((_mapperControl & 0x08) != 0)
_cartridgeRam[address - 0x8000] = value;
return;
}

View File

@@ -17,6 +17,7 @@ namespace Core
public SmsVdp VideoProcessor { get; private set; }
public SmsApu AudioProcessor { get; private set; }
public ushort? Breakpoint { get; set; } = null;
private int _tStateCarryover = 0;
// NTSC SMS T-States per frame
public const int TStatesPerFrame = 59736; //NTSC
@@ -40,13 +41,15 @@ namespace Core
public void Reset()
{
MemoryBus.CleanRAMData();
VideoProcessor.Reset();
Cpu.Reset();
_tStateCarryover = 0;
}
public void RunFrame()
{
int tStatesThisFrame = 0;
int tStatesThisFrame = _tStateCarryover;
while (tStatesThisFrame < TStatesPerFrame) // Standard NTSC frame time
{
// 1. Run one CPU instruction
@@ -58,12 +61,16 @@ namespace Core
AudioProcessor.Update(cycles);
// 3. Check if the VDP is begging for attention!
if (VideoProcessor.InterruptPending && Cpu.IFF1)
//if (VideoProcessor.InterruptPending && Cpu.IFF1)
if (VideoProcessor.InterruptPending)
{
int intCycles = Cpu.RequestInterrupt();
tStatesThisFrame += intCycles;
VideoProcessor.Update(intCycles);
AudioProcessor.Update(intCycles);
if (intCycles > 0)
{
tStatesThisFrame += intCycles;
VideoProcessor.Update(intCycles);
AudioProcessor.Update(intCycles);
}
}
// 4. THE RESTORED BREAKPOINT TRAP

View File

@@ -376,6 +376,17 @@ namespace Core.Video
return (255 << 24) | (r << 16) | (g << 8) | b;
}
public void Reset()
{
_tStateCounter = 0;
_currentScanline = 0;
_lineCounter = 0;
_statusRegister = 0x00;
_controlWord = 0;
_isSecondControlByte = false;
_readBuffer = 0;
}
public void SaveState(BinaryWriter bw)
{
bw.Write(VRAM);

View File

@@ -63,6 +63,8 @@
lblTone2 = new Label();
lblNoise = new Label();
lblTone0 = new Label();
lblHalt = new Label();
lblR = new Label();
groupBox1.SuspendLayout();
SuspendLayout();
//
@@ -243,7 +245,7 @@
// lblIff1
//
lblIff1.AutoSize = true;
lblIff1.Location = new Point(9, 426);
lblIff1.Location = new Point(10, 472);
lblIff1.Name = "lblIff1";
lblIff1.Size = new Size(35, 20);
lblIff1.TabIndex = 24;
@@ -252,7 +254,7 @@
// lblIff2
//
lblIff2.AutoSize = true;
lblIff2.Location = new Point(9, 469);
lblIff2.Location = new Point(10, 515);
lblIff2.Name = "lblIff2";
lblIff2.Size = new Size(35, 20);
lblIff2.TabIndex = 25;
@@ -261,7 +263,7 @@
// lblIE
//
lblIE.AutoSize = true;
lblIE.Location = new Point(9, 389);
lblIE.Location = new Point(10, 435);
lblIE.Name = "lblIE";
lblIE.Size = new Size(26, 20);
lblIE.TabIndex = 26;
@@ -287,7 +289,7 @@
// lblFrames
//
lblFrames.AutoSize = true;
lblFrames.Location = new Point(11, 528);
lblFrames.Location = new Point(10, 605);
lblFrames.Margin = new Padding(2, 0, 2, 0);
lblFrames.Name = "lblFrames";
lblFrames.Size = new Size(124, 20);
@@ -297,7 +299,7 @@
// lblFPS
//
lblFPS.AutoSize = true;
lblFPS.Location = new Point(103, 606);
lblFPS.Location = new Point(102, 683);
lblFPS.Margin = new Padding(2, 0, 2, 0);
lblFPS.Name = "lblFPS";
lblFPS.Size = new Size(32, 20);
@@ -307,7 +309,7 @@
// lblFrameTime
//
lblFrameTime.AutoSize = true;
lblFrameTime.Location = new Point(48, 565);
lblFrameTime.Location = new Point(47, 642);
lblFrameTime.Margin = new Padding(2, 0, 2, 0);
lblFrameTime.Name = "lblFrameTime";
lblFrameTime.Size = new Size(87, 20);
@@ -351,9 +353,9 @@
groupBox1.Controls.Add(lblNoise);
groupBox1.Controls.Add(lblTone0);
groupBox1.Location = new Point(213, 526);
groupBox1.Margin = new Padding(2, 2, 2, 2);
groupBox1.Margin = new Padding(2);
groupBox1.Name = "groupBox1";
groupBox1.Padding = new Padding(2, 2, 2, 2);
groupBox1.Padding = new Padding(2);
groupBox1.Size = new Size(318, 178);
groupBox1.TabIndex = 35;
groupBox1.TabStop = false;
@@ -399,11 +401,31 @@
lblTone0.TabIndex = 0;
lblTone0.Text = "Tone0";
//
// lblHalt
//
lblHalt.AutoSize = true;
lblHalt.Location = new Point(9, 564);
lblHalt.Name = "lblHalt";
lblHalt.Size = new Size(37, 20);
lblHalt.TabIndex = 36;
lblHalt.Text = "Halt";
//
// lblR
//
lblR.AutoSize = true;
lblR.Location = new Point(9, 392);
lblR.Name = "lblR";
lblR.Size = new Size(18, 20);
lblR.TabIndex = 37;
lblR.Text = "R";
//
// DebuggerForm
//
AutoScaleDimensions = new SizeF(8F, 20F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(965, 714);
Controls.Add(lblR);
Controls.Add(lblHalt);
Controls.Add(groupBox1);
Controls.Add(CpuRun);
Controls.Add(btnCpuStep);
@@ -478,6 +500,8 @@
private Label lblTone2;
private Label lblNoise;
private Label lblTone0;
private Label lblHalt;
private Label lblR;
//private TextBox textBox4;
}
}

View File

@@ -110,11 +110,12 @@ namespace Desktop
lblFrames.Text = $"Frames Rendered: {_mainForm.TotalFrameCount}";
lblFrameTime.Text = $"Frame Time: {((float)_mainForm.FrameTime):F1}ms";
lblFPS.Text = $"FPS: {_mainForm.FramesPerSecond:F2}";
lblHalt.Text = $"CPU Halted: {_cpu.IsHalted}";
lblR.Text = $"R: {_cpu.R:X2}";
UpdateMemoryView();
UpdateStackView();
UpdateDisassemblyView();
// --- AUDIO REGISTERS ---
// Use _mainForm to access the Machine, and then grab the AudioProcessor!
// --- AUDIO REGISTERS ---//
if (_mainForm._machine != null && _mainForm._machine.AudioProcessor != null)
{
ushort[] apuRegs = _mainForm._machine.AudioProcessor.Registers;
@@ -668,6 +669,9 @@ namespace Desktop
case 0xC9:
mnemonic = "RET";
break;
case 0x76:
mnemonic = "HALT!";
break;
case 0xCB:
cbOp = _memoryBus.Read((ushort)(currentPc + 1));