Updated Z80 CPU to fix interrupts etc...
This commit is contained in:
@@ -30,7 +30,8 @@ namespace Core.Cpu
|
|||||||
public bool IFF1 { get; private set; } = false;
|
public bool IFF1 { get; private set; } = false;
|
||||||
public bool IFF2 { get; private set; } = false;
|
public bool IFF2 { get; private set; } = false;
|
||||||
public bool InterruptRequested { 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
|
// Main Register Set
|
||||||
public RegisterPair AF;
|
public RegisterPair AF;
|
||||||
@@ -50,7 +51,7 @@ namespace Core.Cpu
|
|||||||
|
|
||||||
// Special Purpose Registers
|
// Special Purpose Registers
|
||||||
public ushort PC; // Program Counter
|
public ushort PC; // Program Counter
|
||||||
public ushort SP; // Stack Pointer
|
public ushort SP;
|
||||||
public byte I; // Interrupt Vector
|
public byte I; // Interrupt Vector
|
||||||
public byte R; // Memory Refresh
|
public byte R; // Memory Refresh
|
||||||
|
|
||||||
@@ -98,6 +99,10 @@ namespace Core.Cpu
|
|||||||
IFF2 = false;
|
IFF2 = false;
|
||||||
InterruptMode = 0;
|
InterruptMode = 0;
|
||||||
TotalTStates = 0;
|
TotalTStates = 0;
|
||||||
|
|
||||||
|
_eiDelay = 0;
|
||||||
|
IsHalted = false;
|
||||||
|
InterruptRequested = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveState(BinaryWriter bw)
|
public void SaveState(BinaryWriter bw)
|
||||||
@@ -137,6 +142,7 @@ namespace Core.Cpu
|
|||||||
|
|
||||||
public int RequestInterrupt()
|
public int RequestInterrupt()
|
||||||
{
|
{
|
||||||
|
IsHalted = false;
|
||||||
InterruptRequested = true;
|
InterruptRequested = true;
|
||||||
// 1. If the ROM has disabled interrupts (DI), ignore the request
|
// 1. If the ROM has disabled interrupts (DI), ignore the request
|
||||||
if (!IFF1) return 0;
|
if (!IFF1) return 0;
|
||||||
@@ -145,6 +151,8 @@ namespace Core.Cpu
|
|||||||
IFF1 = false;
|
IFF1 = false;
|
||||||
IFF2 = false;
|
IFF2 = false;
|
||||||
|
|
||||||
|
_eiDelay = 0;
|
||||||
|
|
||||||
// 3. Push the current Program Counter to the stack so we can return later
|
// 3. Push the current Program Counter to the stack so we can return later
|
||||||
Push(PC);
|
Push(PC);
|
||||||
|
|
||||||
@@ -216,22 +224,36 @@ namespace Core.Cpu
|
|||||||
|
|
||||||
public int Step()
|
public int Step()
|
||||||
{
|
{
|
||||||
bool triggerEi = _eiPending;
|
int tStates;
|
||||||
|
|
||||||
// Fetch the next opcode and increment the Program Counter
|
R = (byte)((R & 0x80) | ((R + 1) & 0x7F));
|
||||||
byte opcode = ReadMemory(PC++);
|
|
||||||
R = (byte)((R + 1) & 0x7F);
|
if (IsHalted)
|
||||||
int tStates = ExecuteOpcode(opcode);
|
{
|
||||||
|
// The CPU is asleep! Do not fetch instructions, just pass the time.
|
||||||
|
tStates = 4;
|
||||||
TotalTStates += tStates;
|
TotalTStates += tStates;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Normal execution
|
||||||
|
byte opcode = ReadMemory(PC++);
|
||||||
|
tStates = ExecuteOpcode(opcode);
|
||||||
|
TotalTStates += tStates;
|
||||||
|
}
|
||||||
|
|
||||||
if (triggerEi)
|
// 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;
|
IFF1 = true;
|
||||||
IFF2 = true;
|
IFF2 = true;
|
||||||
_eiPending = false;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode and execute
|
|
||||||
return tStates;
|
return tStates;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -931,17 +953,21 @@ namespace Core.Cpu
|
|||||||
case 0x73: WriteMemory(HL.Word, DE.Low); return 7;
|
case 0x73: WriteMemory(HL.Word, DE.Low); return 7;
|
||||||
case 0x74: WriteMemory(HL.Word, HL.High); return 7;
|
case 0x74: WriteMemory(HL.Word, HL.High); return 7;
|
||||||
case 0x75: WriteMemory(HL.Word, HL.Low); return 7;
|
case 0x75: WriteMemory(HL.Word, HL.Low); return 7;
|
||||||
case 0x76: //HALT
|
case 0x76: // HALT
|
||||||
if (!InterruptRequested)
|
IsHalted = true;
|
||||||
{
|
|
||||||
PC--;
|
|
||||||
return 4;
|
return 4;
|
||||||
}
|
|
||||||
else
|
//case 0x76: //HALT
|
||||||
{
|
// if (!InterruptRequested)
|
||||||
InterruptRequested = false;
|
// {
|
||||||
return 4;
|
// PC--;
|
||||||
}
|
// return 4;
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// InterruptRequested = false;
|
||||||
|
// return 4;
|
||||||
|
// }
|
||||||
case 0x77: WriteMemory(HL.Word, AF.High); return 7;
|
case 0x77: WriteMemory(HL.Word, AF.High); return 7;
|
||||||
|
|
||||||
// --- LD A, r ---
|
// --- LD A, r ---
|
||||||
@@ -1279,7 +1305,11 @@ namespace Core.Cpu
|
|||||||
case 0xF3: // DI
|
case 0xF3: // DI
|
||||||
IFF1 = false;
|
IFF1 = false;
|
||||||
IFF2 = 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;
|
return 4;
|
||||||
case 0xf5: //push af
|
case 0xf5: //push af
|
||||||
Push(AF.Word);
|
Push(AF.Word);
|
||||||
@@ -1290,9 +1320,7 @@ namespace Core.Cpu
|
|||||||
case 0xF9: // LD SP, HL
|
case 0xF9: // LD SP, HL
|
||||||
SP = HL.Word;
|
SP = HL.Word;
|
||||||
return 6;
|
return 6;
|
||||||
case 0xFB: // EI
|
|
||||||
_eiPending = true;
|
|
||||||
return 4;
|
|
||||||
case 0xFD:
|
case 0xFD:
|
||||||
return ExecuteFDPrefix();
|
return ExecuteFDPrefix();
|
||||||
case 0xFE: // CP n
|
case 0xFE: // CP n
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ namespace Core.Memory
|
|||||||
if (address < 0xC000)
|
if (address < 0xC000)
|
||||||
{
|
{
|
||||||
// Slot 2 (Or SRAM)
|
// Slot 2 (Or SRAM)
|
||||||
if (SramUsed && (_mapperControl & 0x08) != 0)
|
if ((_mapperControl & 0x08) != 0)
|
||||||
return _cartridgeRam[address - 0x8000];
|
return _cartridgeRam[address - 0x8000];
|
||||||
else
|
else
|
||||||
return _cartridgeRom[(_romBank2 * 0x4000) + (address - 0x8000)];
|
return _cartridgeRom[(_romBank2 * 0x4000) + (address - 0x8000)];
|
||||||
@@ -80,7 +80,7 @@ namespace Core.Memory
|
|||||||
|
|
||||||
if (address < 0xC000)
|
if (address < 0xC000)
|
||||||
{
|
{
|
||||||
if (SramUsed && (_mapperControl & 0x08) != 0)
|
if ((_mapperControl & 0x08) != 0)
|
||||||
_cartridgeRam[address - 0x8000] = value;
|
_cartridgeRam[address - 0x8000] = value;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ namespace Core
|
|||||||
public SmsVdp VideoProcessor { get; private set; }
|
public SmsVdp VideoProcessor { get; private set; }
|
||||||
public SmsApu AudioProcessor { get; private set; }
|
public SmsApu AudioProcessor { get; private set; }
|
||||||
public ushort? Breakpoint { get; set; } = null;
|
public ushort? Breakpoint { get; set; } = null;
|
||||||
|
private int _tStateCarryover = 0;
|
||||||
|
|
||||||
// NTSC SMS T-States per frame
|
// NTSC SMS T-States per frame
|
||||||
public const int TStatesPerFrame = 59736; //NTSC
|
public const int TStatesPerFrame = 59736; //NTSC
|
||||||
@@ -40,13 +41,15 @@ namespace Core
|
|||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
MemoryBus.CleanRAMData();
|
MemoryBus.CleanRAMData();
|
||||||
|
VideoProcessor.Reset();
|
||||||
Cpu.Reset();
|
Cpu.Reset();
|
||||||
|
_tStateCarryover = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void RunFrame()
|
public void RunFrame()
|
||||||
{
|
{
|
||||||
int tStatesThisFrame = 0;
|
int tStatesThisFrame = _tStateCarryover;
|
||||||
while (tStatesThisFrame < TStatesPerFrame) // Standard NTSC frame time
|
while (tStatesThisFrame < TStatesPerFrame) // Standard NTSC frame time
|
||||||
{
|
{
|
||||||
// 1. Run one CPU instruction
|
// 1. Run one CPU instruction
|
||||||
@@ -58,13 +61,17 @@ namespace Core
|
|||||||
AudioProcessor.Update(cycles);
|
AudioProcessor.Update(cycles);
|
||||||
|
|
||||||
// 3. Check if the VDP is begging for attention!
|
// 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();
|
int intCycles = Cpu.RequestInterrupt();
|
||||||
|
if (intCycles > 0)
|
||||||
|
{
|
||||||
tStatesThisFrame += intCycles;
|
tStatesThisFrame += intCycles;
|
||||||
VideoProcessor.Update(intCycles);
|
VideoProcessor.Update(intCycles);
|
||||||
AudioProcessor.Update(intCycles);
|
AudioProcessor.Update(intCycles);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 4. THE RESTORED BREAKPOINT TRAP
|
// 4. THE RESTORED BREAKPOINT TRAP
|
||||||
if (Breakpoint.HasValue && Cpu.PC == Breakpoint.Value)
|
if (Breakpoint.HasValue && Cpu.PC == Breakpoint.Value)
|
||||||
|
|||||||
@@ -376,6 +376,17 @@ namespace Core.Video
|
|||||||
|
|
||||||
return (255 << 24) | (r << 16) | (g << 8) | b;
|
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)
|
public void SaveState(BinaryWriter bw)
|
||||||
{
|
{
|
||||||
bw.Write(VRAM);
|
bw.Write(VRAM);
|
||||||
|
|||||||
40
Desktop/DebuggerForm.Designer.cs
generated
40
Desktop/DebuggerForm.Designer.cs
generated
@@ -63,6 +63,8 @@
|
|||||||
lblTone2 = new Label();
|
lblTone2 = new Label();
|
||||||
lblNoise = new Label();
|
lblNoise = new Label();
|
||||||
lblTone0 = new Label();
|
lblTone0 = new Label();
|
||||||
|
lblHalt = new Label();
|
||||||
|
lblR = new Label();
|
||||||
groupBox1.SuspendLayout();
|
groupBox1.SuspendLayout();
|
||||||
SuspendLayout();
|
SuspendLayout();
|
||||||
//
|
//
|
||||||
@@ -243,7 +245,7 @@
|
|||||||
// lblIff1
|
// lblIff1
|
||||||
//
|
//
|
||||||
lblIff1.AutoSize = true;
|
lblIff1.AutoSize = true;
|
||||||
lblIff1.Location = new Point(9, 426);
|
lblIff1.Location = new Point(10, 472);
|
||||||
lblIff1.Name = "lblIff1";
|
lblIff1.Name = "lblIff1";
|
||||||
lblIff1.Size = new Size(35, 20);
|
lblIff1.Size = new Size(35, 20);
|
||||||
lblIff1.TabIndex = 24;
|
lblIff1.TabIndex = 24;
|
||||||
@@ -252,7 +254,7 @@
|
|||||||
// lblIff2
|
// lblIff2
|
||||||
//
|
//
|
||||||
lblIff2.AutoSize = true;
|
lblIff2.AutoSize = true;
|
||||||
lblIff2.Location = new Point(9, 469);
|
lblIff2.Location = new Point(10, 515);
|
||||||
lblIff2.Name = "lblIff2";
|
lblIff2.Name = "lblIff2";
|
||||||
lblIff2.Size = new Size(35, 20);
|
lblIff2.Size = new Size(35, 20);
|
||||||
lblIff2.TabIndex = 25;
|
lblIff2.TabIndex = 25;
|
||||||
@@ -261,7 +263,7 @@
|
|||||||
// lblIE
|
// lblIE
|
||||||
//
|
//
|
||||||
lblIE.AutoSize = true;
|
lblIE.AutoSize = true;
|
||||||
lblIE.Location = new Point(9, 389);
|
lblIE.Location = new Point(10, 435);
|
||||||
lblIE.Name = "lblIE";
|
lblIE.Name = "lblIE";
|
||||||
lblIE.Size = new Size(26, 20);
|
lblIE.Size = new Size(26, 20);
|
||||||
lblIE.TabIndex = 26;
|
lblIE.TabIndex = 26;
|
||||||
@@ -287,7 +289,7 @@
|
|||||||
// lblFrames
|
// lblFrames
|
||||||
//
|
//
|
||||||
lblFrames.AutoSize = true;
|
lblFrames.AutoSize = true;
|
||||||
lblFrames.Location = new Point(11, 528);
|
lblFrames.Location = new Point(10, 605);
|
||||||
lblFrames.Margin = new Padding(2, 0, 2, 0);
|
lblFrames.Margin = new Padding(2, 0, 2, 0);
|
||||||
lblFrames.Name = "lblFrames";
|
lblFrames.Name = "lblFrames";
|
||||||
lblFrames.Size = new Size(124, 20);
|
lblFrames.Size = new Size(124, 20);
|
||||||
@@ -297,7 +299,7 @@
|
|||||||
// lblFPS
|
// lblFPS
|
||||||
//
|
//
|
||||||
lblFPS.AutoSize = true;
|
lblFPS.AutoSize = true;
|
||||||
lblFPS.Location = new Point(103, 606);
|
lblFPS.Location = new Point(102, 683);
|
||||||
lblFPS.Margin = new Padding(2, 0, 2, 0);
|
lblFPS.Margin = new Padding(2, 0, 2, 0);
|
||||||
lblFPS.Name = "lblFPS";
|
lblFPS.Name = "lblFPS";
|
||||||
lblFPS.Size = new Size(32, 20);
|
lblFPS.Size = new Size(32, 20);
|
||||||
@@ -307,7 +309,7 @@
|
|||||||
// lblFrameTime
|
// lblFrameTime
|
||||||
//
|
//
|
||||||
lblFrameTime.AutoSize = true;
|
lblFrameTime.AutoSize = true;
|
||||||
lblFrameTime.Location = new Point(48, 565);
|
lblFrameTime.Location = new Point(47, 642);
|
||||||
lblFrameTime.Margin = new Padding(2, 0, 2, 0);
|
lblFrameTime.Margin = new Padding(2, 0, 2, 0);
|
||||||
lblFrameTime.Name = "lblFrameTime";
|
lblFrameTime.Name = "lblFrameTime";
|
||||||
lblFrameTime.Size = new Size(87, 20);
|
lblFrameTime.Size = new Size(87, 20);
|
||||||
@@ -351,9 +353,9 @@
|
|||||||
groupBox1.Controls.Add(lblNoise);
|
groupBox1.Controls.Add(lblNoise);
|
||||||
groupBox1.Controls.Add(lblTone0);
|
groupBox1.Controls.Add(lblTone0);
|
||||||
groupBox1.Location = new Point(213, 526);
|
groupBox1.Location = new Point(213, 526);
|
||||||
groupBox1.Margin = new Padding(2, 2, 2, 2);
|
groupBox1.Margin = new Padding(2);
|
||||||
groupBox1.Name = "groupBox1";
|
groupBox1.Name = "groupBox1";
|
||||||
groupBox1.Padding = new Padding(2, 2, 2, 2);
|
groupBox1.Padding = new Padding(2);
|
||||||
groupBox1.Size = new Size(318, 178);
|
groupBox1.Size = new Size(318, 178);
|
||||||
groupBox1.TabIndex = 35;
|
groupBox1.TabIndex = 35;
|
||||||
groupBox1.TabStop = false;
|
groupBox1.TabStop = false;
|
||||||
@@ -399,11 +401,31 @@
|
|||||||
lblTone0.TabIndex = 0;
|
lblTone0.TabIndex = 0;
|
||||||
lblTone0.Text = "Tone0";
|
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
|
// DebuggerForm
|
||||||
//
|
//
|
||||||
AutoScaleDimensions = new SizeF(8F, 20F);
|
AutoScaleDimensions = new SizeF(8F, 20F);
|
||||||
AutoScaleMode = AutoScaleMode.Font;
|
AutoScaleMode = AutoScaleMode.Font;
|
||||||
ClientSize = new Size(965, 714);
|
ClientSize = new Size(965, 714);
|
||||||
|
Controls.Add(lblR);
|
||||||
|
Controls.Add(lblHalt);
|
||||||
Controls.Add(groupBox1);
|
Controls.Add(groupBox1);
|
||||||
Controls.Add(CpuRun);
|
Controls.Add(CpuRun);
|
||||||
Controls.Add(btnCpuStep);
|
Controls.Add(btnCpuStep);
|
||||||
@@ -478,6 +500,8 @@
|
|||||||
private Label lblTone2;
|
private Label lblTone2;
|
||||||
private Label lblNoise;
|
private Label lblNoise;
|
||||||
private Label lblTone0;
|
private Label lblTone0;
|
||||||
|
private Label lblHalt;
|
||||||
|
private Label lblR;
|
||||||
//private TextBox textBox4;
|
//private TextBox textBox4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,11 +110,12 @@ namespace Desktop
|
|||||||
lblFrames.Text = $"Frames Rendered: {_mainForm.TotalFrameCount}";
|
lblFrames.Text = $"Frames Rendered: {_mainForm.TotalFrameCount}";
|
||||||
lblFrameTime.Text = $"Frame Time: {((float)_mainForm.FrameTime):F1}ms";
|
lblFrameTime.Text = $"Frame Time: {((float)_mainForm.FrameTime):F1}ms";
|
||||||
lblFPS.Text = $"FPS: {_mainForm.FramesPerSecond:F2}";
|
lblFPS.Text = $"FPS: {_mainForm.FramesPerSecond:F2}";
|
||||||
|
lblHalt.Text = $"CPU Halted: {_cpu.IsHalted}";
|
||||||
|
lblR.Text = $"R: {_cpu.R:X2}";
|
||||||
UpdateMemoryView();
|
UpdateMemoryView();
|
||||||
UpdateStackView();
|
UpdateStackView();
|
||||||
UpdateDisassemblyView();
|
UpdateDisassemblyView();
|
||||||
// --- AUDIO REGISTERS ---
|
// --- AUDIO REGISTERS ---//
|
||||||
// Use _mainForm to access the Machine, and then grab the AudioProcessor!
|
|
||||||
if (_mainForm._machine != null && _mainForm._machine.AudioProcessor != null)
|
if (_mainForm._machine != null && _mainForm._machine.AudioProcessor != null)
|
||||||
{
|
{
|
||||||
ushort[] apuRegs = _mainForm._machine.AudioProcessor.Registers;
|
ushort[] apuRegs = _mainForm._machine.AudioProcessor.Registers;
|
||||||
@@ -668,6 +669,9 @@ namespace Desktop
|
|||||||
case 0xC9:
|
case 0xC9:
|
||||||
mnemonic = "RET";
|
mnemonic = "RET";
|
||||||
break;
|
break;
|
||||||
|
case 0x76:
|
||||||
|
mnemonic = "HALT!";
|
||||||
|
break;
|
||||||
case 0xCB:
|
case 0xCB:
|
||||||
cbOp = _memoryBus.Read((ushort)(currentPc + 1));
|
cbOp = _memoryBus.Read((ushort)(currentPc + 1));
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user