// 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 Noddybox.Emulation. 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
private enum EventType
{
HALT,
EDHook
}
#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 byte I;
private byte R;
private bool IFF1;
private bool IFF2;
private bool raise;
private bool nmi;
private byte devbyte;
private byte 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)(((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<
/// Set/get the current state of the accumulator.
///
public byte Acummulator
{
get {return A;}
set {A = value;}
}
///
/// Set/get the current state of the flag register.
///
public Z80Flags StatusFlags
{
get {return F;}
set {F = value;}
}
///
/// The current state of the BC register pair.
///
public Register16 BC_Register
{
get { return BC; }
set { BC = value; }
}
///
/// The current state of the DE register pair.
///
public Register16 DE_Register
{
get { return DE; }
set { DE = value; }
}
///
/// The current state of the HL register pair.
///
public Register16 HL_Register
{
get { return HL; }
set { HL = value; }
}
///
/// The current state of the stack pointer.
///
public ushort StackPointer
{
get { return SP; }
set { SP = value; }
}
///
/// The current state of the program counter.
///
public ushort ProgramCounter
{
get { return PC; }
set { PC = value; }
}
///
/// The alternate AF' register.
///
public Register16 AF_Alternate
{
get { return AF_; }
set { AF_ = value; }
}
///
/// The alternate BC' register.
///
public Register16 BC_Alternate
{
get { return BC_; }
set { BC_ = value; }
}
///
/// The alternate DE' register.
///
public Register16 DE_Alternate
{
get { return DE_; }
set { DE_ = value; }
}
///
/// The alternate HL' register.
///
public Register16 HL_Alternate
{
get { return HL_; }
set { HL_ = value; }
}
///
/// The state of the HALT line.
///
public bool HaltLine
{
get { return HALT; }
set { HALT = value; }
}
///
/// The state of the IFF1 register.
///
public bool IFF1_Regsiter
{
get { return IFF1; }
set { IFF1 = value; }
}
///
/// The state of the IFF2 register.
///
public bool IFF2_Regsiter
{
get { return IFF2; }
set { IFF2 = value; }
}
///
/// The state of the IM register.
///
public byte IM_Register
{
get { return IM; }
set { IM = value; }
}
///
/// The state of the RAM refresh register.
///
public byte Refresh
{
get { return R; }
set { R = value; }
}
///
/// The state of the interrupt vector register.
///
public byte InterruptVector
{
get { return I; }
set { I = value; }
}
#endregion
#region Events
///
/// The CPU has been halted.
///
public event EventHandler HaltEvent;
///
/// A no-operation ED opcode has been executed. Useful for placing
/// hooks into the Z80 code.
///
public event EventHandler EDNopEvent;
///
/// Called to raise
///
/// The event arguments.
protected void OnHaltEvent(Z80CpuEventArgs e)
{
EventHandler handler = HaltEvent;
if (handler != null)
{
handler(this, e);
}
}
///
/// Called to raise
///
/// The event arguments.
protected void OnEDNopEvent(Z80CpuEventArgs e)
{
EventHandler handler = EDNopEvent;
if (handler != null)
{
handler(this, e);
}
}
private void TriggerEvent(EventType type, byte opcode)
{
Z80CpuEventArgs e = new Z80CpuEventArgs
{
Opcode = opcode,
A = this.A,
F = this.F,
BC = this.BC,
DE = this.DE,
HL = this.HL,
SP = this.SP,
PC = this.PC,
AF_ = this.AF_,
BC_ = this.BC_,
DE_ = this.DE_,
HL_ = this.HL_
};
switch(type)
{
case EventType.HALT:
OnHaltEvent(e);
break;
case EventType.EDHook:
OnEDNopEvent(e);
break;
}
A = e.A;
F = e.F;
BC = e.BC;
DE = e.DE;
HL = e.HL;
SP = e.SP;
PC = e.PC;
AF_ = e.AF_;
BC_ = e.BC_;
DE_ = e.DE_;
HL_ = e.HL_;
}
#endregion
}
}