// 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<