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; private bool halted = false; private bool interruptsEnabled = true; private IMemory memory; #region OpCodes private readonly List> OpCodeArray; #endregion public Cpu() { registers = new CpuRegisters(); OpCodeArray = new List>() { #region OpCodes #region OpCodes 0x00-0x0F // 0x00 (op) => { // NOP return 4; }, // 0x01 (op) => { // LD BC,d16 registers.BC = ReadShortFromInstruction(); return 12; }, // 0x02 (op) => { // LD (BC), A memory.WriteByte(0xFF00 + registers.BC, registers.A); return 8; }, // 0x03 (op) => { registers.BC += 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 registers.B = ReadInstruction(); return 8; }, // 0x07 (op) => { // RLCA registers.Flags.Zero = false; registers.Flags.N_Subtract = false; registers.Flags.HalfCarry = false; registers.Flags.Carry = (registers.A & 0b1000_0000) != 0; registers.A = (byte)((registers.A << 1) + (registers.A >> 7)); return 4; }, // 0x08 (op) => { // LD (a16), SP // Load SP into address int address = ReadShortFromInstruction(); memory.WriteShort(address, registers.SP); return 20; }, // 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 registers.A = memory.ReadByte(0xFF + registers.BC); return 8; }, // 0x0B (op) => { // DEC BC registers.BC -= 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 registers.C = ReadInstruction(); return 8; }, // 0x0F (op) => { // RRCA registers.Flags.Zero = false; registers.Flags.N_Subtract = false; registers.Flags.HalfCarry = false; registers.Flags.Carry = (registers.A & 0b0000_0001) != 0; registers.A = (byte)((registers.A >> 1) + (registers.A << 7)); 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 registers.DE = ReadShortFromInstruction(); return 12; }, // 0x12 (op) => { // LD (DE), A memory.WriteByte(0xFF00 + registers.DE, registers.A); return 8; }, // 0x13 (op) => { // INC DE registers.DE += 1; return 8; }, // 0x14 (op) => { registers.D = IncrementRegister(registers.D); return 4; }, // 0x15 (op) => { registers.D = DecrementRegister(registers.D); return 4; }, // 0x16 (op) => { registers.D = ReadInstruction(); return 8; }, // 0x17 (op) => { // RLA int carryBit = registers.Flags.Carry ? 1 : 0; registers.Flags.Zero = false; registers.Flags.N_Subtract = false; registers.Flags.HalfCarry = false; registers.Flags.Carry = (registers.A & 0b1000_0000) != 0; registers.A = (byte)((registers.A << 1) + carryBit); return 4; }, // 0x18 (op) => { byte jump = ReadInstruction(); registers.PC += jump; return 12; }, // 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 byte val = memory.ReadByte(registers.DE); registers.A = val; return 8; }, // 0x1B (op) => { // DEC DE registers.DE -= 1; return 8; }, // 0x1C (op) => { registers.E = IncrementRegister(registers.E); return 4; }, // 0x1D (op) => { registers.E = IncrementRegister(registers.E); return 4; }, // 0x1E (op) => { registers.E = ReadInstruction(); return 8; }, // 0x1F (op) => { // RRA int carryBit = registers.Flags.Carry ? 1 : 0; registers.Flags.Zero = false; registers.Flags.N_Subtract = false; registers.Flags.HalfCarry = false; registers.Flags.Carry = (registers.A & 0b0000_0001) != 0; registers.A = (byte)((registers.A >> 1) + (carryBit << 7)); return 4; }, #endregion #region OpCodes 0x20-0x2F // 0x20 (op) => { byte relativeJump = ReadInstruction(); if (!registers.Flags.Zero) { registers.PC += relativeJump; return 12; } else { return 8; } }, // 0x21 (op) => { // LD HL, d16 registers.HL = ReadShortFromInstruction(); return 12; }, // 0x22 (op) => { // LD (HL+), A memory.WriteByte(registers.HL++, registers.A); return 8; }, // 0x23 (op) => { registers.HL += 1; return 8; }, // 0x24 (op) => { registers.H = IncrementRegister(registers.H); return 4; }, // 0x25 (op) => { registers.H = DecrementRegister(registers.H); return 4; }, // 0x26 (op) => { registers.H = ReadInstruction(); return 8; }, // 0x27 (op) => { throw new NotImplementedException("0x27"); }, // 0x28 (op) => { byte relativeJump = ReadInstruction(); if (registers.Flags.Zero) { registers.PC += relativeJump; return 12; } else { return 8; } }, // 0x29 (op) => { registers.HL = AddWideRegisters(registers.HL, registers.HL); return 8; }, // 0x2A (op) => { byte val = memory.ReadByte(registers.HL++); registers.A = val; return 8; }, // 0x2B (op) => { registers.HL -= 1; return 8; }, // 0x2C (op) => { registers.L = IncrementRegister(registers.L); return 4; }, // 0x2D (op) => { registers.L = DecrementRegister(registers.L); return 4; }, // 0x2E (op) => { registers.L = ReadInstruction(); return 8; }, // 0x2F (op) => { registers.A = (byte)~registers.A; registers.Flags.HalfCarry = true; registers.Flags.N_Subtract = true; return 4; }, #endregion #region OpCodes 0x30-0x3F // 0x30 (op) => { byte relativeJump = ReadInstruction(); if (!registers.Flags.Carry) { registers.PC += relativeJump; return 12; } else { return 8; } }, // 0x31 (op) => { // LD SP, d16 registers.SP = ReadShortFromInstruction(); return 12; }, // 0x32 (op) => { memory.WriteByte(registers.HL--, registers.A); return 8; }, // 0x33 (op) => { registers.SP += 1; return 8; }, // 0x34 (op) => { byte val = IncrementRegister(memory.ReadByte(registers.HL)); memory.WriteByte(registers.HL, val); return 12; }, // 0x35 (op) => { byte val = DecrementRegister(memory.ReadByte(registers.HL)); memory.WriteByte(registers.HL, val); return 12; }, // 0x36 (op) => { byte val = ReadInstruction(); memory.WriteByte(registers.HL, val); return 12; }, // 0x37 (op) => { registers.Flags.Carry = true; registers.Flags.HalfCarry = false; registers.Flags.N_Subtract = false; return 4; }, // 0x38 (op) => { byte relativeJump = ReadInstruction(); if (registers.Flags.Carry) { registers.PC += relativeJump; return 12; } else { return 8; } }, // 0x39 (op) => { registers.HL = AddWideRegisters(registers.HL, registers.SP); return 8; }, // 0x3A (op) => { registers.A = memory.ReadByte(registers.HL--); return 8; }, // 0x3B (op) => { registers.SP -= 1; return 8; }, // 0x3C (op) => { registers.A = IncrementRegister(registers.A); return 4; }, // 0x3D (op) => { registers.A = DecrementRegister(registers.A); return 4; }, // 0x3E (op) => { registers.A = ReadInstruction(); return 8; }, // 0x3F (op) => { registers.Flags.Carry = !registers.Flags.Carry; registers.Flags.HalfCarry = false; registers.Flags.N_Subtract = false; return 4; }, #endregion #region OpCodes 0x40-0x4F // 0x40 (op) => { registers.B = registers.B; return 4; }, // 0x41 (op) => { registers.B = registers.C; return 4; }, // 0x42 (op) => { registers.B = registers.D; return 4; }, // 0x43 (op) => { registers.B = registers.E; return 4; }, // 0x44 (op) => { registers.B = registers.H; return 4; }, // 0x45 (op) => { registers.B = registers.L; return 4; }, // 0x46 (op) => { registers.B = memory.ReadByte(registers.HL); return 8; }, // 0x47 (op) => { registers.B = registers.A; return 4; }, // 0x48 (op) => { registers.C = registers.B; return 4; }, // 0x49 (op) => { registers.C = registers.C; return 4; }, // 0x4A (op) => { registers.C = registers.D; return 4; }, // 0x4B (op) => { registers.C = registers.E; return 4; }, // 0x4C (op) => { registers.C = registers.H; return 4; }, // 0x4D (op) => { registers.C = registers.L; return 4; }, // 0x4E (op) => { registers.C = memory.ReadByte(registers.HL); return 8; }, // 0x4F (op) => { registers.C = registers.A; return 4; }, #endregion #region OpCodes 0x50-0x5F // 0x50 (op) => { registers.D = registers.B; return 4; }, // 0x51 (op) => { registers.D = registers.C; return 4; }, // 0x52 (op) => { registers.D = registers.D; return 4; }, // 0x53 (op) => { registers.D = registers.E; return 4; }, // 0x54 (op) => { registers.D = registers.H; return 4; }, // 0x55 (op) => { registers.D = registers.L; return 4; }, // 0x56 (op) => { registers.D = memory.ReadByte(registers.HL); return 8; }, // 0x57 (op) => { registers.D = registers.A; return 4; }, // 0x58 (op) => { registers.E = registers.B; return 4; }, // 0x59 (op) => { registers.E = registers.C; return 4; }, // 0x5A (op) => { registers.E = registers.D; return 4; }, // 0x5B (op) => { registers.E = registers.E; return 4; }, // 0x5C (op) => { registers.E = registers.H; return 4; }, // 0x5D (op) => { registers.E = registers.L; return 4; }, // 0x5E (op) => { registers.E = memory.ReadByte(registers.HL); return 8; }, // 0x5F (op) => { registers.E = registers.A; return 4; }, #endregion #region OpCodes 0x60-0x6F // 0x60 (op) => { registers.H = registers.B; return 4; }, // 0x61 (op) => { registers.H = registers.C; return 4; }, // 0x62 (op) => { registers.H = registers.D; return 4; }, // 0x63 (op) => { registers.H = registers.E; return 4; }, // 0x64 (op) => { registers.H = registers.H; return 4; }, // 0x65 (op) => { registers.H = registers.L; return 4; }, // 0x66 (op) => { registers.H = memory.ReadByte(registers.HL); return 8; }, // 0x67 (op) => { registers.H = registers.A; return 4; }, // 0x68 (op) => { registers.L = registers.B; return 4; }, // 0x69 (op) => { registers.L = registers.C; return 4; }, // 0x6A (op) => { registers.L = registers.D; return 4; }, // 0x6B (op) => { registers.L = registers.E; return 4; }, // 0x6C (op) => { registers.L = registers.H; return 4; }, // 0x6D (op) => { registers.L = registers.L; return 4; }, // 0x6E (op) => { registers.L = memory.ReadByte(registers.HL); return 8; }, // 0x6F (op) => { registers.L = registers.A; return 4; }, #endregion #region OpCodes 0x70-0x7F // 0x70 (op) => { memory.WriteByte(registers.HL, registers.B); return 8; }, // 0x71 (op) => { memory.WriteByte(registers.HL, registers.C); return 8; }, // 0x72 (op) => { memory.WriteByte(registers.HL, registers.D); return 8; }, // 0x73 (op) => { memory.WriteByte(registers.HL, registers.E); return 8; }, // 0x74 (op) => { memory.WriteByte(registers.HL, registers.H); return 8; }, // 0x75 (op) => { memory.WriteByte(registers.HL, registers.L); return 8; }, // 0x76 (op) => { halted = true; return 4; }, // 0x77 (op) => { memory.WriteByte(registers.HL, registers.A); return 8; }, // 0x78 (op) => { registers.A = registers.B; return 4; }, // 0x79 (op) => { registers.A = registers.C; return 4; }, // 0x7A (op) => { registers.A = registers.D; return 4; }, // 0x7B (op) => { registers.A = registers.E; return 4; }, // 0x7C (op) => { registers.A = registers.H; return 4; }, // 0x7D (op) => { registers.A = registers.L; return 4; }, // 0x7E (op) => { registers.A = memory.ReadByte(registers.HL); return 8; }, // 0x7F (op) => { registers.A = registers.A; return 4; }, #endregion #region OpCodes 0x80-0x8F // 0x80 (op) => { registers.A = AddRegisters(registers.A, registers.B); return 4; }, // 0x81 (op) => { registers.A = AddRegisters(registers.A, registers.C); return 4; }, // 0x82 (op) => { registers.A = AddRegisters(registers.A, registers.D); return 4; }, // 0x83 (op) => { registers.A = AddRegisters(registers.A, registers.E); return 4; }, // 0x84 (op) => { registers.A = AddRegisters(registers.A, registers.H); return 4; }, // 0x85 (op) => { registers.A = AddRegisters(registers.A, registers.L); return 4; }, // 0x86 (op) => { registers.A = AddRegisters(registers.A, memory.ReadByte(registers.HL)); return 8; }, // 0x87 (op) => { registers.A = AddRegisters(registers.A, registers.A); return 4; }, // 0x88 (op) => { registers.A = AddRegisters(registers.A, registers.B, true); return 4; }, // 0x89 (op) => { registers.A = AddRegisters(registers.A, registers.C, true); return 4; }, // 0x8A (op) => { registers.A = AddRegisters(registers.A, registers.D, true); return 4; }, // 0x8B (op) => { registers.A = AddRegisters(registers.A, registers.E, true); return 4; }, // 0x8C (op) => { registers.A = AddRegisters(registers.A, registers.H, true); return 4; }, // 0x8D (op) => { registers.A = AddRegisters(registers.A, registers.L, true); return 4; }, // 0x8E (op) => { registers.A = AddRegisters(registers.A, memory.ReadByte(registers.HL), true); return 8; }, // 0x8F (op) => { registers.A = AddRegisters(registers.A, registers.A, true); return 4; }, #endregion #region OpCodes 0x90-0x9F // 0x90 (op) => { registers.A = SubtractRegisters(registers.A, registers.B); return 4; }, // 0x91 (op) => { registers.A = SubtractRegisters(registers.A, registers.C); return 4; }, // 0x92 (op) => { registers.A = SubtractRegisters(registers.A, registers.D); return 4; }, // 0x93 (op) => { registers.A = SubtractRegisters(registers.A, registers.E); return 4; }, // 0x94 (op) => { registers.A = SubtractRegisters(registers.A, registers.H); return 4; }, // 0x95 (op) => { registers.A = SubtractRegisters(registers.A, registers.L); return 4; }, // 0x96 (op) => { registers.A = SubtractRegisters(registers.A, memory.ReadByte(registers.HL)); return 8; }, // 0x97 (op) => { registers.A = SubtractRegisters(registers.A, registers.A); return 4; }, // 0x98 (op) => { registers.A = SubtractRegisters(registers.A, registers.B, true); return 4; }, // 0x99 (op) => { registers.A = SubtractRegisters(registers.A, registers.C, true); return 4; }, // 0x9A (op) => { registers.A = SubtractRegisters(registers.A, registers.D, true); return 4; }, // 0x9B (op) => { registers.A = SubtractRegisters(registers.A, registers.E, true); return 4; }, // 0x9C (op) => { registers.A = SubtractRegisters(registers.A, registers.H, true); return 4; }, // 0x9D (op) => { registers.A = SubtractRegisters(registers.A, registers.L, true); return 4; }, // 0x9E (op) => { registers.A = SubtractRegisters(registers.A, memory.ReadByte(registers.HL), true); return 8; }, // 0x9F (op) => { registers.A = SubtractRegisters(registers.A, registers.A, true); return 4; }, #endregion #region OpCodes 0xA0-0xAF // 0xA0 (op) => { registers.A = AndRegisters(registers.A, registers.B); return 4; }, // 0xA1 (op) => { registers.A = AndRegisters(registers.A, registers.C); return 4; }, // 0xA2 (op) => { registers.A = AndRegisters(registers.A, registers.D); return 4; }, // 0xA3 (op) => { registers.A = AndRegisters(registers.A, registers.E); return 4; }, // 0xA4 (op) => { registers.A = AndRegisters(registers.A, registers.H); return 4; }, // 0xA5 (op) => { registers.A = AndRegisters(registers.A, registers.L); return 4; }, // 0xA6 (op) => { registers.A = AndRegisters(registers.A, memory.ReadByte(registers.HL)); return 8; }, // 0xA7 (op) => { registers.A = AndRegisters(registers.A, registers.A); return 4; }, // 0xA8 (op) => { registers.A = XorRegisters(registers.A, registers.B); return 4; }, // 0xA9 (op) => { registers.A = XorRegisters(registers.A, registers.C); return 4; }, // 0xAA (op) => { registers.A = XorRegisters(registers.A, registers.D); return 4; }, // 0xAB (op) => { registers.A = XorRegisters(registers.A, registers.E); return 4; }, // 0xAC (op) => { registers.A = XorRegisters(registers.A, registers.H); return 4; }, // 0xAD (op) => { registers.A = XorRegisters(registers.A, registers.L); return 4; }, // 0xAE (op) => { registers.A = XorRegisters(registers.A, memory.ReadByte(registers.HL)); return 8; }, // 0xAF (op) => { registers.A = XorRegisters(registers.A, registers.A); return 4; }, #endregion #region OpCodes 0xB0-0xBF // 0xB0 (op) => { registers.A = OrRegisters(registers.A, registers.B); return 4; }, // 0xB1 (op) => { registers.A = OrRegisters(registers.A, registers.C); return 4; }, // 0xB2 (op) => { registers.A = OrRegisters(registers.A, registers.D); return 4; }, // 0xB3 (op) => { registers.A = OrRegisters(registers.A, registers.E); return 4; }, // 0xB4 (op) => { registers.A = OrRegisters(registers.A, registers.H); return 4; }, // 0xB5 (op) => { registers.A = OrRegisters(registers.A, registers.L); return 4; }, // 0xB6 (op) => { registers.A = OrRegisters(registers.A, memory.ReadByte(registers.HL)); return 8; }, // 0xB7 (op) => { registers.A = OrRegisters(registers.A, registers.A); return 4; }, // 0xB8 (op) => { SubtractRegisters(registers.A, registers.B); return 4; }, // 0xB9 (op) => { SubtractRegisters(registers.A, registers.C); return 4; }, // 0xBA (op) => { SubtractRegisters(registers.A, registers.D); return 4; }, // 0xBB (op) => { SubtractRegisters(registers.A, registers.E); return 4; }, // 0xBC (op) => { SubtractRegisters(registers.A, registers.H); return 4; }, // 0xBD (op) => { SubtractRegisters(registers.A, registers.L); return 4; }, // 0xBE (op) => { SubtractRegisters(registers.A, memory.ReadByte(registers.HL)); return 8; }, // 0xBF (op) => { SubtractRegisters(registers.A, registers.A); return 4; }, #endregion #region OpCodes 0xC0-0xCF // 0xC0 (op) => { if (!registers.Flags.Zero) { Jump(PopShort()); return 20; } else { return 8; } }, // 0xC1 (op) => { registers.BC = PopShort(); return 12; }, // 0xC2 (op) => { ushort jumpAddress = ReadShortFromInstruction(); if (!registers.Flags.Zero) { Jump(jumpAddress); return 16; } else { return 12; } }, // 0xC3 (op) => { ushort jumpAddress = ReadShortFromInstruction(); Jump(jumpAddress); return 16; }, // 0xC4 (op) => { ushort jumpAddress = ReadShortFromInstruction(); if (!registers.Flags.Zero) { PushShort(registers.PC); Jump(jumpAddress); return 16; } else { return 12; } }, // 0xC5 (op) => { PushShort(registers.BC); return 16; }, // 0xC6 (op) => { registers.A = AddRegisters(registers.A, ReadInstruction()); return 8; }, // 0xC7 (op) => { PushShort(registers.PC); Jump(0x0000); return 16; }, // 0xC8 (op) => { if (registers.Flags.Zero) { Jump(PopShort()); return 20; } else { return 8; } }, // 0xC9 (op) => { Jump(PopShort()); return 16; }, // 0xCA (op) => { ushort jumpAddress = ReadShortFromInstruction(); if (registers.Flags.Zero) { Jump(jumpAddress); return 16; } else { return 12; } }, // 0xCB (op) => { throw new NotImplementedException("0xCB"); }, // 0xCC (op) => { ushort jumpAddress = ReadShortFromInstruction(); if (registers.Flags.Zero) { PushShort(registers.PC); Jump(jumpAddress); return 24; } else { return 12; } }, // 0xCD (op) => { PushShort(registers.PC); Jump(ReadShortFromInstruction()); return 24; }, // 0xCE (op) => { registers.A = AddRegisters(registers.A, ReadInstruction(), true); return 8; }, // 0xCF (op) => { PushShort(registers.PC); Jump(0x0008); return 16; }, #endregion #region OpCodes 0xD0-0xDF // 0xD0 (op) => { if (!registers.Flags.Carry) { Jump(PopShort()); return 20; } else { return 8; } }, // 0xD1 (op) => { registers.DE = PopShort(); return 12; }, // 0xD2 (op) => { ushort jumpAddress = ReadShortFromInstruction(); if (!registers.Flags.Carry) { Jump(jumpAddress); return 16; } else { return 12; } }, // 0xD3 (op) => { throw new IllegalInstructionException(op, "Instruction does not exist: 0xD3"); }, // 0xD4 (op) => { ushort jumpAddress = ReadShortFromInstruction(); if (!registers.Flags.Carry) { PushShort(registers.PC); Jump(jumpAddress); return 24; } else { return 12; } }, // 0xD5 (op) => { PushShort(registers.DE); return 16; }, // 0xD6 (op) => { registers.A = SubtractRegisters(registers.A, ReadInstruction()); return 8; }, // 0xD7 (op) => { PushShort(registers.PC); Jump(0x0010); return 16; }, // 0xD8 (op) => { if (registers.Flags.Carry) { Jump(PopShort()); return 20; } else { return 8; } }, // 0xD9 (op) => { Jump(PopShort()); interruptsEnabled = true; return 16; }, // 0xDA (op) => { ushort jumpAddress = ReadShortFromInstruction(); if (registers.Flags.Carry) { Jump(jumpAddress); return 16; } else { return 12; } }, // 0xDB (op) => { throw new IllegalInstructionException(op, "Instruction does not exist"); }, // 0xDC (op) => { ushort jumpAddress = ReadShortFromInstruction(); if (registers.Flags.Carry) { PushShort(registers.PC); Jump(jumpAddress); return 24; } else { return 12; } }, // 0xDD (op) => { throw new IllegalInstructionException(op, "Instruction does not exist"); }, // 0xDE (op) => { registers.A = SubtractRegisters(registers.A, ReadInstruction(), true); return 8; }, // 0xDF (op) => { PushShort(registers.PC); Jump(0x0018); return 16; }, #endregion #region OpCodes 0xE0-0xEF // 0xE0 (op) => { memory.WriteByte(0xFF00 + ReadInstruction(), registers.A); return 12; }, // 0xE1 (op) => { registers.HL = PopShort(); return 12; }, // 0xE2 (op) => { memory.WriteByte(0xFF00 + registers.C, registers.A); return 8; }, // 0xE3 (op) => { throw new IllegalInstructionException(op, "Instruction does not exist"); }, // 0xE4 (op) => { throw new IllegalInstructionException(op, "Instruction does not exist"); }, // 0xE5 (op) => { PushShort(registers.HL); return 16; }, // 0xE6 (op) => { registers.A = AndRegisters(registers.A, ReadInstruction()); return 8; }, // 0xE7 (op) => { PushShort(registers.PC); Jump(0x0020); return 16; }, // 0xE8 (op) => { registers.SP = AddWideRegisters(registers.SP, ReadInstruction()); registers.Flags.Zero = false; return 16; }, // 0xE9 (op) => { Jump(registers.HL); return 4; }, // 0xEA (op) => { memory.WriteByte(ReadShortFromInstruction(), registers.A); return 16; }, // 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) => { registers.A = XorRegisters(registers.A, ReadInstruction()); return 8; }, // 0xEF (op) => { PushShort(registers.PC); Jump(0x0028); return 16; }, #endregion #region OpCodes 0xF0-0xFF // 0xF0 (op) => { registers.A = memory.ReadByte(0xFF + ReadInstruction()); return 12; }, // 0xF1 (op) => { registers.AF = PopShort(); return 12; }, // 0xF2 (op) => { registers.A = memory.ReadByte(0xFF + registers.C); return 8; throw new NotImplementedException("0xF2"); }, // 0xF3 (op) => { interruptsEnabled = false; return 4; }, // 0xF4 (op) => { throw new IllegalInstructionException(op, "Instruction does not exist: 0xF4"); }, // 0xF5 (op) => { PushShort(registers.AF); return 16; }, // 0xF6 (op) => { registers.A = OrRegisters(registers.A, ReadInstruction()); throw new NotImplementedException("0xF6"); }, // 0xF7 (op) => { PushShort(registers.PC); Jump(0x0030); return 16; }, // 0xF8 (op) => { registers.HL = AddWideRegisters(registers.SP, ReadInstruction()); registers.Flags.Zero = false; return 12; }, // 0xF9 (op) => { registers.SP = registers.HL; return 4; }, // 0xFA (op) => { registers.A = memory.ReadByte(ReadShortFromInstruction()); return 16; }, // 0xFB (op) => { interruptsEnabled = true; return 4; }, // 0xFC (op) => { throw new IllegalInstructionException(op, "Instruction does not exist"); }, // 0xFD (op) => { throw new IllegalInstructionException(op, "Instruction does not exist"); }, // 0xFE (op) => { SubtractRegisters(registers.A, ReadInstruction()); return 8; }, // 0xFF (op) => { PushShort(registers.PC); Jump(0x0038); return 16; } #endregion #endregion }; } private void Jump(ushort address) { registers.PC = address; } private void Jump(byte msb, byte lsb) { Jump(BytesToShort(msb, lsb)); } private void PushShort(ushort value) { memory.WriteByte(--registers.SP, (byte)(value >> 8)); memory.WriteByte(--registers.SP, (byte)(value & 0xFF)); } private ushort PopShort() { byte lsb = memory.ReadByte(registers.SP++); byte msb = memory.ReadByte(registers.SP++); return BytesToShort(msb, lsb); } private byte ReadInstruction() { registers.PC += 1; return 0; } private ushort ReadShortFromInstruction() { byte lsb = ReadInstruction(); byte msb = ReadInstruction(); return BytesToShort(msb, lsb); } private ushort BytesToShort(byte msb, byte lsb) { return (ushort)(lsb + (msb << 8)); } private byte IncrementRegister(byte register) { return AddRegisters(register, 1); } private byte DecrementRegister(byte register) { return SubtractRegisters(register, 1); } private byte AddRegisters(byte register1, byte register2, bool withCarry = false) { if (withCarry && registers.Flags.Carry) { register2 += 1; } byte reg = (byte)(register1 + register2); registers.Flags.N_Subtract = false; registers.Flags.Zero = reg == 0; registers.Flags.Carry = ((register1 + register2) & 0x100) == 0x100; registers.Flags.HalfCarry = (((register1 & 0xf) + (register2 & 0xf)) & 0x10) == 0x10; return reg; } private byte SubtractRegisters(byte register1, byte register2, bool withCarry = false) { if (withCarry && registers.Flags.Carry) { register2 += 1; } byte reg = (byte)(register1 - register2); registers.Flags.N_Subtract = true; registers.Flags.Zero = reg == 0; registers.Flags.Carry = ((0x100 + register1 + register2) & 0x100) == 0x100; registers.Flags.HalfCarry = ((0x10 + (register1 & 0xf) - (register2 & 0xf)) & 0x10) == 0x10; return reg; } private byte AndRegisters(byte register1, byte register2) { byte val = (byte)(register1 & register2); registers.Flags.Carry = false; registers.Flags.HalfCarry = true; registers.Flags.N_Subtract = false; registers.Flags.Zero = val == 0; return val; } private byte OrRegisters(byte register1, byte register2) { byte val = (byte)(register1 | register2); registers.Flags.Carry = false; registers.Flags.HalfCarry = false; registers.Flags.N_Subtract = false; registers.Flags.Zero = val == 0; return val; } private byte XorRegisters(byte register1, byte register2) { byte val = (byte)(register1 ^ register2); registers.Flags.Carry = false; registers.Flags.HalfCarry = false; registers.Flags.N_Subtract = false; registers.Flags.Zero = val == 0; return val; } private ushort AddWideRegisters(ushort wideRegister1, ushort wideRegister2) { // ADD HL, BC registers.Flags.N_Subtract = false; registers.Flags.HalfCarry = (((wideRegister1 & 0x0FFF) + (wideRegister2 & 0x0FFF) & 0x1000) == 0x1000); registers.Flags.Carry = (((wideRegister1 + wideRegister2) & 0x10000) == 0x10000); 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) { } } }