using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; 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 List> OpCodeArray; #endregion public Cpu() { registers = new CpuRegisters(); OpCodeArray = new List>() { #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) { registers.BC = (ushort)(op.Arg2 + (op.Arg1 << 8)); 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 registers.B = IncrementRegister(registers.B); return 4; }, // 0x05 (op) => { // DEC B registers.B = DecrementRegister(registers.B); 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.HL = AddWideRegisters(registers.HL, registers.BC); 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 registers.C = IncrementRegister(registers.C); return 4; }, // 0x0D (op) => { // DEC C registers.C = DecrementRegister(registers.C); 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 if (op.Arg1.HasValue && op.Arg2.HasValue) { registers.DE = (ushort)(op.Arg2 + (op.Arg1 << 8)); registers.PC += 3; return 12; } else { throw new IllegalInstructionException(op, "Missing arguments for LD DE"); } }, // 0x12 (op) => { // LD (DE), A throw new NotImplementedException("0x12"); }, // 0x13 (op) => { // INC DE registers.DE += 1; registers.PC += 1; return 8; }, // 0x14 (op) => { registers.D = IncrementRegister(registers.D); return 4; }, // 0x15 (op) => { registers.D = DecrementRegister(registers.D); return 4; }, // 0x16 (op) => { if (op.Arg1.HasValue) { registers.D = op.Arg1.Value; registers.PC += 2; return 8; } else { throw new IllegalInstructionException(op, "Missing arguments for LD D"); } }, // 0x17 (op) => { // RLA int carryBit = registers.Flags.Carry ? 1 : 0; 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) + carryBit); registers.PC += 1; return 4; }, // 0x18 (op) => { if (op.Arg1.HasValue) { byte relativeJump = op.Arg1.Value; registers.PC += (ushort)(1 + relativeJump); return 12; } else { throw new IllegalInstructionException(op, "Missing argument for JR r"); } }, // 0x19 (op) => { // ADD HL, DE registers.HL = AddWideRegisters(registers.HL, registers.DE); return 8; }, // 0x1A (op) => { // LD A, (DE) // Load byte at address stored in DE into A throw new NotImplementedException("LD A, (DE)"); }, // 0x1B (op) => { // DEC DE registers.DE -= 1; registers.PC += 1; return 8; }, // 0x1C (op) => { registers.E = IncrementRegister(registers.E); return 4; }, // 0x1D (op) => { registers.E = IncrementRegister(registers.E); return 4; }, // 0x1E (op) => { if (op.Arg1.HasValue) { registers.E = op.Arg1.Value; registers.PC += 2; return 8; } else { throw new IllegalInstructionException(op, "Missing arguments for LD E"); } }, // 0x1F (op) => { // RRA int carryBit = registers.Flags.Carry ? 1 : 0; 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) + (carryBit << 7)); registers.PC += 1; return 4; }, #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 }; } private byte IncrementRegister(byte register) { byte reg = (byte)(register + 1); 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 reg; } private byte DecrementRegister(byte register) { byte reg = (byte)((uint)register - 1); 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 reg; } private ushort AddWideRegisters(ushort wideRegister1, ushort wideRegister2) { // ADD HL, BC registers.Flags.Subtract = false; registers.Flags.HalfCarry = (((wideRegister1 & 0x0FFF) + (wideRegister2 & 0x0FFF) & 0x1000) == 0x1000); registers.Flags.Carry = (((wideRegister1 + wideRegister2) & 0x10000) == 0x10000); registers.PC += 1; return (ushort)(wideRegister1 + wideRegister2); } } [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) { } } }