Files
MCEmuCore/MCEmuCore.GBMonolith/Cpu.cs

1711 lines
54 KiB
C#

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<Func<OpCode, int>> OpCodeArray;
#endregion
public Cpu()
{
registers = new CpuRegisters();
OpCodeArray = new List<Func<OpCode, int>>()
{
#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) { }
}
}