// This file is part of the Noddybox.Emulation C# suite.
//
// Noddybox.Emulation is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Noddybox.Emulation is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Foobar. If not, see .
//
// Copyright (c) 2012 Ian Cowburn
//
using System;
namespace Noddybox.Emulation.EightBit.Z80
{
///
/// Provides an implementation of a Zilog Z80 processor.
///
public partial class Z80Cpu : ICpu
{
#region Private types
[Flags]
private enum Z80Flags
{
None = 0x00,
Carry = 0x01,
Neg = 0x02,
PV = 0x04,
Hidden3 = 0x08,
HalfCarry = 0x10,
Hidden5 = 0x20,
Zero = 0x40,
Sign = 0x80,
Hidden = Hidden3 | Hidden5
};
#endregion
#region Private data
// Lookup tables
//
private Z80Flags[] PSZtable = new Z80Flags[512];
private Z80Flags[] SZtable = new Z80Flags[512];
private Z80Flags[] Ptable = new Z80Flags[512];
private Z80Flags[] Stable = new Z80Flags[512];
private Z80Flags[] Ztable = new Z80Flags[512];
private Z80Flags[] H35table = new Z80Flags[512];
// Machine accessors
//
private IMemory memory;
private IDevice device;
private Clock clock;
// Main registers
//
private byte A;
private Z80Flags F;
private Register16 BC = new Register16(0);
private Register16 DE = new Register16(0);
private Register16 HL = new Register16(0);
private Register16 IX = new Register16(0);
private Register16 IY = new Register16(0);
private ushort SP;
private ushort PC;
// Alternate registers
//
private Register16 AF_ = new Register16(0);
private Register16 BC_ = new Register16(0);
private Register16 DE_ = new Register16(0);
private Register16 HL_ = new Register16(0);
// Auxilliary registers and flags
//
private ushort I;
private byte R;
private bool IFF1;
private bool IFF2;
private bool raise;
private bool nmi;
private byte devbyte;
private int IM;
private bool HALT;
private int shift;
#endregion
#region Private members
private void CheckInterrupts()
{
if (raise)
{
if (nmi)
{
if (HALT)
{
HALT = false;
PC++;
}
clock.Add(2);
IFF1 = false;
nmi = false;
PUSH(PC);
PC = 0x66;
}
else if (IFF1)
{
if (HALT)
{
HALT = false;
PC++;
}
clock.Add(2);
IFF1 = false;
IFF2 = false;
switch(IM)
{
case 1:
PUSH(PC);
PC = 0x38;
break;
case 2:
PUSH(PC);
PC = (ushort)((I << 8) + devbyte);
break;
default:
DecodeByte(devbyte);
break;
}
}
raise = false;
}
}
#endregion
#region ICpu Members
public void Initialise(IMemory memory, IDevice device, Clock clock)
{
this.memory = memory;
this.device = device;
this.clock = clock;
Reset();
}
public void Reset()
{
PC = 0;
A = 0xff;
F = (Z80Flags)0xff;
BC.reg = 0xffff;
DE.reg = 0xffff;
HL.reg = 0xffff;
AF_.reg = 0xffff;
BC_.reg = 0xffff;
DE_.reg = 0xffff;
HL_.reg = 0xffff;
IX.reg = 0xffff;
IY.reg = 0xffff;
SP = 0xffff;
IFF1 = false;
IFF2 = false;
IM = 0;
I = 0;
R = 0;
HALT = false;
shift = 0;
raise = false;
nmi = false;
devbyte = 0;
}
public void Step()
{
CheckInterrupts();
AddR(1);
shift = 0;
DecodeByte(memory.Read(PC++));
}
public void Run()
{
while(!clock.FrameDone)
{
Step();
}
}
public void MaskableInterrupt(byte value)
{
raise = true;
devbyte = value;
nmi = false;
}
public void NonMaskableInterrupt(byte value)
{
raise = true;
nmi = true;
}
#endregion
#region Constructors
public Z80Cpu()
{
// Verify runtime
//
Register16.Verify();
// Setup lookup tables
//
for(int f = 0; f < 256; f++)
{
Z80Flags p = Z80Flags.None;
Z80Flags z = Z80Flags.None;
Z80Flags s = Z80Flags.None;
Z80Flags h3 = Z80Flags.None;
Z80Flags h5 = Z80Flags.None;
int parity = 0;
for(int b=0; b < 8; b++)
{
if ((f & (1<