using System; using System.Collections.Generic; namespace MCEmuCore.GBMonolith { public struct OpCode { public readonly byte Instruction; public byte? Arg1; public byte? Arg2; } class Cpu { const byte ZERO_FLAG = 0b10000000; const byte SUB_FLAG = 0b01000000; const byte HALF_CARRY = 0b00100000; const byte FULL_CARRY = 0b00010000; private readonly CpuRegisters registers; #region OpCodes private readonly Dictionary> OpCodeTable; #endregion public Cpu() { registers = new CpuRegisters(); OpCodeTable = new Dictionary>() { #region OpCodes 0x00-0x0F {0x00, (op) =>{ registers.PC += 1; return true; } }, {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 true; } else { throw new InvalidOperationException(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 true; } }, {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 true; } }, {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 true; } }, {0x06, (op) => { // LD B,d8 if (op.Arg1.HasValue) { registers.B = op.Arg1.Value; registers.PC += 2; return true; } else { throw new InvalidOperationException(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 true; } }, {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 true; } }, {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 true; } }, {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 true; } }, {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 true; } }, {0x0E, (op) => { // LD C,d8 if (op.Arg1.HasValue) { registers.C = op.Arg1.Value; registers.PC += 2; return true; } else { throw new InvalidOperationException(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 true; } }, #endregion #region OpCodes 0x10-0x1F {0x10, (op) => { // STOP 0 // Halts CPU & LCD until button pressed throw new NotImplementedException("STOP"); } } #endregion #region OpCodes 0x20-0x2F #endregion #region OpCodes 0x30-0x3F #endregion #region OpCodes 0x40-0x4F #endregion #region OpCodes 0x50-0x5F #endregion #region OpCodes 0x60-0x6F #endregion #region OpCodes 0x70-0x7F #endregion #region OpCodes 0x80-0x8F #endregion #region OpCodes 0x90-0x9F #endregion #region OpCodes 0xA0-0xAF #endregion #region OpCodes 0xB0-0xBF #endregion #region OpCodes 0xC0-0xCF #endregion #region OpCodes 0xD0-0xDF #endregion #region OpCodes 0xE0-0xEF #endregion #region OpCodes 0xF0-0xFF #endregion }; } } [Serializable] public class InvalidOperationException : Exception { public OpCode OpCode { get; set; } public InvalidOperationException() { } public InvalidOperationException(OpCode opCode, string message) : base(message) { OpCode = opCode; } public InvalidOperationException(string message) : base(message) { } public InvalidOperationException(string message, Exception inner) : base(message, inner) { } protected InvalidOperationException( System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) { } } }