using System; using System.Collections.Generic; using System.Linq; namespace MCEmuCore.GBMonolith { public struct OpCode { public readonly byte Instruction; public byte? Arg1; public byte? Arg2; } class Cpu { private readonly CpuRegisters registers; #region OpCodes private readonly Dictionary> OpCodeTable; private readonly Func[] OpCodeArray; #endregion public Cpu() { registers = new CpuRegisters(); OpCodeTable = new Dictionary>() { #region OpCodes #region OpCodes 0x00-0x0F {0x00, (op) =>{ // NOP registers.PC += 1; return 4; } }, {0x01, (op) => { // LD BC,d16 if (op.Arg1.HasValue && op.Arg2.HasValue) { ushort operand = (ushort)(op.Arg2 + (op.Arg1 << 8)); registers.BC = operand; registers.PC += 3; return 12; } else { throw new IllegalInstructionException(op, "Missing arguments for LD BC"); } } }, {0x02, (op) => { // LD (BC), A throw new NotImplementedException("LD (BC), A"); } }, {0x03, (op) => { registers.BC += 1; registers.PC += 1; return 8; } }, {0x04, (op) => { // INC B byte reg = (byte)(registers.B + 1); registers.B = reg; registers.Flags.Subtract = false; registers.Flags.Zero = reg == 0; // This logic only works for the increment operation registers.Flags.HalfCarry = reg == 16; registers.PC += 1; return 4; } }, {0x05, (op) => { // DEC B byte reg = (byte)((uint)registers.B - 1); registers.B = reg; registers.Flags.Subtract = true; registers.Flags.Zero = reg == 0; // This logic only works for the decrement operation registers.Flags.HalfCarry = reg == 15; registers.PC += 1; return 4; } }, {0x06, (op) => { // LD B,d8 if (op.Arg1.HasValue) { registers.B = op.Arg1.Value; registers.PC += 2; return 8; } else { throw new IllegalInstructionException(op, "Missing arguments for LD B"); } } }, {0x07, (op) => { // RLCA registers.Flags.Zero = false; registers.Flags.Subtract = false; registers.Flags.HalfCarry = false; registers.Flags.Carry = (registers.A & 0b1000_0000) != 0; registers.A = (byte)((registers.A << 1) + (registers.A >> 7)); registers.PC += 1; return 4; } }, {0x08, (op) => { // LD (a16), SP // Load SP into address throw new NotImplementedException("LD (a16), SP"); } }, {0x09, (op) => { // ADD HL, BC registers.Flags.Subtract = false; registers.Flags.HalfCarry = (((registers.HL & 0x0FFF) + (registers.BC & 0x0FFF) & 0x1000) == 0x1000); registers.Flags.Carry = (((registers.HL + registers.BC) & 0x10000) == 0x10000); registers.HL += registers.BC; registers.PC += 1; return 8; } }, {0x0A, (op) => { // LD A, (BC) // Load byte at address stored in BC into A throw new NotImplementedException("LD A, (BC)"); } }, {0x0B, (op) => { // DEC BC registers.BC -= 1; registers.PC += 1; return 8; } }, {0x0C, (op) => { // INC C byte reg = (byte)(registers.C + 1); registers.C = reg; registers.Flags.Subtract = false; registers.Flags.Zero = reg == 0; // This logic only works for the increment operation registers.Flags.HalfCarry = reg == 16; registers.PC += 1; return 4; } }, {0x0D, (op) => { // DEC C byte reg = (byte)((uint)registers.C - 1); registers.C = reg; registers.Flags.Subtract = true; registers.Flags.Zero = reg == 0; // This logic only works for the decrement operation registers.Flags.HalfCarry = reg == 15; registers.PC += 1; return 4; } }, {0x0E, (op) => { // LD C,d8 if (op.Arg1.HasValue) { registers.C = op.Arg1.Value; registers.PC += 2; return 8; } else { throw new IllegalInstructionException(op, "Missing argument for LD B"); } } }, {0x0F, (op) => { // RRCA registers.Flags.Zero = false; registers.Flags.Subtract = false; registers.Flags.HalfCarry = false; registers.Flags.Carry = (registers.A & 0b0000_0001) != 0; registers.A = (byte)((registers.A >> 1) + (registers.A << 7)); registers.PC += 1; return 4; } }, #endregion #region OpCodes 0x10-0x1F {0x10, (op) => { // STOP 0 // Halts CPU & LCD until button pressed throw new NotImplementedException("STOP"); } }, {0x11, (op) => { // LD DE, d16 throw new NotImplementedException("0x11"); } }, {0x12, (op) => { // LD (DE), A throw new NotImplementedException("0x12"); } }, {0x13, (op) => { // INC DE throw new NotImplementedException("0x13"); } }, {0x14, (op) => { throw new NotImplementedException("0x14"); } }, {0x15, (op) => { throw new NotImplementedException("0x15"); } }, {0x16, (op) => { throw new NotImplementedException("0x16"); } }, {0x17, (op) => { throw new NotImplementedException("0x17"); } }, {0x18, (op) => { throw new NotImplementedException("0x18"); } }, {0x19, (op) => { throw new NotImplementedException("0x19"); } }, {0x1A, (op) => { throw new NotImplementedException("0x1A"); } }, {0x1B, (op) => { throw new NotImplementedException("0x1B"); } }, {0x1C, (op) => { throw new NotImplementedException("0c1C"); } }, {0x1D, (op) => { throw new NotImplementedException("0x1D"); } }, {0x1E, (op) => { throw new NotImplementedException("0x1E"); } }, {0x1F, (op) => { throw new NotImplementedException("0x1F"); } }, #endregion #region OpCodes 0x20-0x2F {0x20, (op) => { throw new NotImplementedException("0x20"); } }, {0x21, (op) => { throw new NotImplementedException("0x21"); } }, {0x22, (op) => { throw new NotImplementedException("0x22"); } }, {0x23, (op) => { throw new NotImplementedException("0x23"); } }, {0x24, (op) => { throw new NotImplementedException("0x24"); } }, {0x25, (op) => { throw new NotImplementedException("0x25"); } }, {0x26, (op) => { throw new NotImplementedException("0x26"); } }, {0x27, (op) => { throw new NotImplementedException("0x27"); } }, {0x28, (op) => { throw new NotImplementedException("0x28"); } }, {0x29, (op) => { throw new NotImplementedException("0x29"); } }, {0x2A, (op) => { throw new NotImplementedException("0x2A"); } }, {0x2B, (op) => { throw new NotImplementedException("0x2B"); } }, {0x2C, (op) => { throw new NotImplementedException("0x2C"); } }, {0x2D, (op) => { throw new NotImplementedException("0x2D"); } }, {0x2E, (op) => { throw new NotImplementedException("0x2E"); } }, {0x2F, (op) => { throw new NotImplementedException("0x2F"); } }, #endregion #region OpCodes 0x30-0x3F {0x30, (op) => { throw new NotImplementedException("0x30"); } }, {0x31, (op) => { throw new NotImplementedException("0x31"); } }, {0x32, (op) => { throw new NotImplementedException("0x32"); } }, {0x33, (op) => { throw new NotImplementedException("0x33"); } }, {0x34, (op) => { throw new NotImplementedException("0x34"); } }, {0x35, (op) => { throw new NotImplementedException("0x35"); } }, {0x36, (op) => { throw new NotImplementedException("0x36"); } }, {0x37, (op) => { throw new NotImplementedException("0x37"); } }, {0x38, (op) => { throw new NotImplementedException("0x38"); } }, {0x39, (op) => { throw new NotImplementedException("0x39"); } }, {0x3A, (op) => { throw new NotImplementedException("0x3A"); } }, {0x3B, (op) => { throw new NotImplementedException("0x3B"); } }, {0x3C, (op) => { throw new NotImplementedException("0x3C"); } }, {0x3D, (op) => { throw new NotImplementedException("0x3D"); } }, {0x3E, (op) => { throw new NotImplementedException("0x3E"); } }, {0x3F, (op) => { throw new NotImplementedException("0x3F"); } }, #endregion #region OpCodes 0x40-0x4F {0x40, (op) => { throw new NotImplementedException("0x40"); } }, {0x41, (op) => { throw new NotImplementedException("0x41"); } }, {0x42, (op) => { throw new NotImplementedException("0x42"); } }, {0x43, (op) => { throw new NotImplementedException("0x43"); } }, {0x44, (op) => { throw new NotImplementedException("0x44"); } }, {0x45, (op) => { throw new NotImplementedException("0x45"); } }, {0x46, (op) => { throw new NotImplementedException("0x46"); } }, {0x47, (op) => { throw new NotImplementedException("0x47"); } }, {0x48, (op) => { throw new NotImplementedException("0x48"); } }, {0x49, (op) => { throw new NotImplementedException("0x49"); } }, {0x4A, (op) => { throw new NotImplementedException("0x4A"); } }, {0x4B, (op) => { throw new NotImplementedException("0x4B"); } }, {0x4C, (op) => { throw new NotImplementedException("0x4C"); } }, {0x4D, (op) => { throw new NotImplementedException("0x4D"); } }, {0x4E, (op) => { throw new NotImplementedException("0x4E"); } }, {0x4F, (op) => { throw new NotImplementedException("0x4F"); } }, #endregion #region OpCodes 0x50-0x5F {0x50, (op) => { throw new NotImplementedException("0x50"); } }, {0x51, (op) => { throw new NotImplementedException("0x51"); } }, {0x52, (op) => { throw new NotImplementedException("0x52"); } }, {0x53, (op) => { throw new NotImplementedException("0x53"); } }, {0x54, (op) => { throw new NotImplementedException("0x54"); } }, {0x55, (op) => { throw new NotImplementedException("0x55"); } }, {0x56, (op) => { throw new NotImplementedException("0x56"); } }, {0x57, (op) => { throw new NotImplementedException("0x57"); } }, {0x58, (op) => { throw new NotImplementedException("0x58"); } }, {0x59, (op) => { throw new NotImplementedException("0x59"); } }, {0x5A, (op) => { throw new NotImplementedException("0x5A"); } }, {0x5B, (op) => { throw new NotImplementedException("0x5B"); } }, {0x5C, (op) => { throw new NotImplementedException("0x5C"); } }, {0x5D, (op) => { throw new NotImplementedException("0x5D"); } }, {0x5E, (op) => { throw new NotImplementedException("0x5E"); } }, {0x5F, (op) => { throw new NotImplementedException("0x5F"); } }, #endregion #region OpCodes 0x60-0x6F {0x60, (op) => { throw new NotImplementedException("0x60"); } }, {0x61, (op) => { throw new NotImplementedException("0x61"); } }, {0x62, (op) => { throw new NotImplementedException("0x62"); } }, {0x63, (op) => { throw new NotImplementedException("0x63"); } }, {0x64, (op) => { throw new NotImplementedException("0x64"); } }, {0x65, (op) => { throw new NotImplementedException("0x65"); } }, {0x66, (op) => { throw new NotImplementedException("0x66"); } }, {0x67, (op) => { throw new NotImplementedException("0x67"); } }, {0x68, (op) => { throw new NotImplementedException("0x68"); } }, {0x69, (op) => { throw new NotImplementedException("0x69"); } }, {0x6A, (op) => { throw new NotImplementedException("0x6A"); } }, {0x6B, (op) => { throw new NotImplementedException("0x6B"); } }, {0x6C, (op) => { throw new NotImplementedException("0x6C"); } }, {0x6D, (op) => { throw new NotImplementedException("0x6D"); } }, {0x6E, (op) => { throw new NotImplementedException("0x6E"); } }, {0x6F, (op) => { throw new NotImplementedException("0x6F"); } }, #endregion #region OpCodes 0x70-0x7F {0x70, (op) => { throw new NotImplementedException("0x70"); } }, {0x71, (op) => { throw new NotImplementedException("0x71"); } }, {0x72, (op) => { throw new NotImplementedException("0x72"); } }, {0x73, (op) => { throw new NotImplementedException("0x73"); } }, {0x74, (op) => { throw new NotImplementedException("0x74"); } }, {0x75, (op) => { throw new NotImplementedException("0x75"); } }, {0x76, (op) => { throw new NotImplementedException("0x76"); } }, {0x77, (op) => { throw new NotImplementedException("0x77"); } }, {0x78, (op) => { throw new NotImplementedException("0x78"); } }, {0x79, (op) => { throw new NotImplementedException("0x79"); } }, {0x7A, (op) => { throw new NotImplementedException("0x7A"); } }, {0x7B, (op) => { throw new NotImplementedException("0x7B"); } }, {0x7C, (op) => { throw new NotImplementedException("0x7C"); } }, {0x7D, (op) => { throw new NotImplementedException("0x7D"); } }, {0x7E, (op) => { throw new NotImplementedException("0x7E"); } }, {0x7F, (op) => { throw new NotImplementedException("0x7F"); } }, #endregion #region OpCodes 0x80-0x8F {0x80, (op) => { throw new NotImplementedException("0x80"); } }, {0x81, (op) => { throw new NotImplementedException("0x81"); } }, {0x82, (op) => { throw new NotImplementedException("0x82"); } }, {0x83, (op) => { throw new NotImplementedException("0x83"); } }, {0x84, (op) => { throw new NotImplementedException("0x84"); } }, {0x85, (op) => { throw new NotImplementedException("0x85"); } }, {0x86, (op) => { throw new NotImplementedException("0x86"); } }, {0x87, (op) => { throw new NotImplementedException("0x87"); } }, {0x88, (op) => { throw new NotImplementedException("0x88"); } }, {0x89, (op) => { throw new NotImplementedException("0x89"); } }, {0x8A, (op) => { throw new NotImplementedException("0x8A"); } }, {0x8B, (op) => { throw new NotImplementedException("0x8B"); } }, {0x8C, (op) => { throw new NotImplementedException("0x8C"); } }, {0x8D, (op) => { throw new NotImplementedException("0x8D"); } }, {0x8E, (op) => { throw new NotImplementedException("0x8E"); } }, {0x8F, (op) => { throw new NotImplementedException("0x8F"); } }, #endregion #region OpCodes 0x90-0x9F {0x90, (op) => { throw new NotImplementedException("0x90"); } }, {0x91, (op) => { throw new NotImplementedException("0x91"); } }, {0x92, (op) => { throw new NotImplementedException("0x92"); } }, {0x93, (op) => { throw new NotImplementedException("0x93"); } }, {0x94, (op) => { throw new NotImplementedException("0x94"); } }, {0x95, (op) => { throw new NotImplementedException("0x95"); } }, {0x96, (op) => { throw new NotImplementedException("0x96"); } }, {0x97, (op) => { throw new NotImplementedException("0x97"); } }, {0x98, (op) => { throw new NotImplementedException("0x98"); } }, {0x99, (op) => { throw new NotImplementedException("0x99"); } }, {0x9A, (op) => { throw new NotImplementedException("0x9A"); } }, {0x9B, (op) => { throw new NotImplementedException("0x9B"); } }, {0x9C, (op) => { throw new NotImplementedException("0x9C"); } }, {0x9D, (op) => { throw new NotImplementedException("0x9D"); } }, {0x9E, (op) => { throw new NotImplementedException("0x9E"); } }, {0x9F, (op) => { throw new NotImplementedException("0x9F"); } }, #endregion #region OpCodes 0xA0-0xAF {0xA0, (op) => { throw new NotImplementedException("0xA0"); } }, {0xA1, (op) => { throw new NotImplementedException("0xA1"); } }, {0xA2, (op) => { throw new NotImplementedException("0xA2"); } }, {0xA3, (op) => { throw new NotImplementedException("0xA3"); } }, {0xA4, (op) => { throw new NotImplementedException("0xA4"); } }, {0xA5, (op) => { throw new NotImplementedException("0xA5"); } }, {0xA6, (op) => { throw new NotImplementedException("0xA6"); } }, {0xA7, (op) => { throw new NotImplementedException("0xA7"); } }, {0xA8, (op) => { throw new NotImplementedException("0xA8"); } }, {0xA9, (op) => { throw new NotImplementedException("0xA9"); } }, {0xAA, (op) => { throw new NotImplementedException("0xAA"); } }, {0xAB, (op) => { throw new NotImplementedException("0xAB"); } }, {0xAC, (op) => { throw new NotImplementedException("0xAC"); } }, {0xAD, (op) => { throw new NotImplementedException("0xAD"); } }, {0xAE, (op) => { throw new NotImplementedException("0xAE"); } }, {0xAF, (op) => { throw new NotImplementedException("0xAF"); } }, #endregion #region OpCodes 0xB0-0xBF {0xB0, (op) => { throw new NotImplementedException("0xB0"); } }, {0xB1, (op) => { throw new NotImplementedException("0xB1"); } }, {0xB2, (op) => { throw new NotImplementedException("0xB2"); } }, {0xB3, (op) => { throw new NotImplementedException("0xB3"); } }, {0xB4, (op) => { throw new NotImplementedException("0xB4"); } }, {0xB5, (op) => { throw new NotImplementedException("0xB5"); } }, {0xB6, (op) => { throw new NotImplementedException("0xB6"); } }, {0xB7, (op) => { throw new NotImplementedException("0xB7"); } }, {0xB8, (op) => { throw new NotImplementedException("0xB8"); } }, {0xB9, (op) => { throw new NotImplementedException("0xB9"); } }, {0xBA, (op) => { throw new NotImplementedException("0xBA"); } }, {0xBB, (op) => { throw new NotImplementedException("0xBB"); } }, {0xBC, (op) => { throw new NotImplementedException("0xBC"); } }, {0xBD, (op) => { throw new NotImplementedException("0xBD"); } }, {0xBE, (op) => { throw new NotImplementedException("0xBE"); } }, {0xBF, (op) => { throw new NotImplementedException("0xBF"); } }, #endregion #region OpCodes 0xC0-0xCF {0xC0, (op) => { throw new NotImplementedException("0xC0"); } }, {0xC1, (op) => { throw new NotImplementedException("0xC1"); } }, {0xC2, (op) => { throw new NotImplementedException("0xC2"); } }, {0xC3, (op) => { throw new NotImplementedException("0xC3"); } }, {0xC4, (op) => { throw new NotImplementedException("0xC4"); } }, {0xC5, (op) => { throw new NotImplementedException("0xC5"); } }, {0xC6, (op) => { throw new NotImplementedException("0xC6"); } }, {0xC7, (op) => { throw new NotImplementedException("0xC7"); } }, {0xC8, (op) => { throw new NotImplementedException("0xC8"); } }, {0xC9, (op) => { throw new NotImplementedException("0xC9"); } }, {0xCA, (op) => { throw new NotImplementedException("0xCA"); } }, {0xCB, (op) => { throw new NotImplementedException("0xCB"); } }, {0xCC, (op) => { throw new NotImplementedException("0xCC"); } }, {0xCD, (op) => { throw new NotImplementedException("0xCD"); } }, {0xCE, (op) => { throw new NotImplementedException("0xCE"); } }, {0xCF, (op) => { throw new NotImplementedException("0xCF"); } }, #endregion #region OpCodes 0xD0-0xDF {0xD0, (op) => { throw new NotImplementedException("0xD0"); } }, {0xD1, (op) => { throw new NotImplementedException("0xD1"); } }, {0xD2, (op) => { throw new NotImplementedException("0xD2"); } }, {0xD3, (op) => { throw new IllegalInstructionException(op, "Instruction does not exist"); } }, {0xD4, (op) => { throw new NotImplementedException("0xD4"); } }, {0xD5, (op) => { throw new NotImplementedException("0xD5"); } }, {0xD6, (op) => { throw new NotImplementedException("0xD6"); } }, {0xD7, (op) => { throw new NotImplementedException("0xD7"); } }, {0xD8, (op) => { throw new NotImplementedException("0xD8"); } }, {0xD9, (op) => { throw new NotImplementedException("0xD9"); } }, {0xDA, (op) => { throw new NotImplementedException("0xDA"); } }, {0xDB, (op) => { throw new IllegalInstructionException(op, "Instruction does not exist"); } }, {0xDC, (op) => { throw new NotImplementedException("0xDC"); } }, {0xDD, (op) => { throw new IllegalInstructionException(op, "Instruction does not exist"); } }, {0xDE, (op) => { throw new NotImplementedException("0xDE"); } }, {0xDF, (op) => { throw new NotImplementedException("0xDF"); } }, #endregion #region OpCodes 0xE0-0xEF {0xE0, (op) => { throw new NotImplementedException("0xE0"); } }, {0xE1, (op) => { throw new NotImplementedException("0xE1"); } }, {0xE2, (op) => { throw new NotImplementedException("0xE2"); } }, {0xE3, (op) => { throw new IllegalInstructionException(op, "Instruction does not exist"); } }, {0xE4, (op) => { throw new IllegalInstructionException(op, "Instruction does not exist"); } }, {0xE5, (op) => { throw new NotImplementedException("0xE5"); } }, {0xE6, (op) => { throw new NotImplementedException("0xE6"); } }, {0xE7, (op) => { throw new NotImplementedException("0xE7"); } }, {0xE8, (op) => { throw new NotImplementedException("0xE8"); } }, {0xE9, (op) => { throw new NotImplementedException("0xE9"); } }, {0xEA, (op) => { throw new NotImplementedException("0xEA"); } }, {0xEB, (op) => { throw new IllegalInstructionException(op, "Instruction does not exist"); } }, {0xEC, (op) => { throw new IllegalInstructionException(op, "Instruction does not exist"); } }, {0xED, (op) => { throw new IllegalInstructionException(op, "Instruction does not exist"); } }, {0xEE, (op) => { throw new NotImplementedException("0xEE"); } }, {0xEF, (op) => { throw new NotImplementedException("0xEF"); } }, #endregion #region OpCodes 0xF0-0xFF {0xF0, (op) => { throw new NotImplementedException("0xF0"); } }, {0xF1, (op) => { throw new NotImplementedException("0xF1"); } }, {0xF2, (op) => { throw new NotImplementedException("0xF2"); } }, {0xF3, (op) => { throw new NotImplementedException("0xF3"); } }, {0xF4, (op) => { throw new IllegalInstructionException(op, "Instruction does not exist"); } }, {0xF5, (op) => { throw new NotImplementedException("0xF5"); } }, {0xF6, (op) => { throw new NotImplementedException("0xF6"); } }, {0xF7, (op) => { throw new NotImplementedException("0xF7"); } }, {0xF8, (op) => { throw new NotImplementedException("0xF8"); } }, {0xF9, (op) => { throw new NotImplementedException("0xF9"); } }, {0xFA, (op) => { throw new NotImplementedException("0xFA"); } }, {0xFB, (op) => { throw new NotImplementedException("0xFB"); } }, {0xFC, (op) => { throw new IllegalInstructionException(op, "Instruction does not exist"); } }, {0xFD, (op) => { throw new IllegalInstructionException(op, "Instruction does not exist"); } }, {0xFE, (op) => { throw new NotImplementedException("0xFE"); } }, {0xFF, (op) => { throw new NotImplementedException("0xFF"); } }, #endregion #endregion }; // Arrays are faster than dictionaries? // TODO: Test speed of dictionary vs array OpCodeArray = OpCodeTable.OrderBy(o => o.Key).Select(o => o.Value).ToArray(); } } [Serializable] public class IllegalInstructionException : Exception { public OpCode OpCode { get; set; } public IllegalInstructionException() { } public IllegalInstructionException(OpCode opCode, string message) : base(message) { OpCode = opCode; } public IllegalInstructionException(string message) : base(message) { } public IllegalInstructionException(string message, Exception inner) : base(message, inner) { } protected IllegalInstructionException( System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) { } } }