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 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,26 +224,40 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public string GetFlagsString()
|
||||
{
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user