public static void Return(IWriteonlyRegister <ushort> pc, IStack stack) { var low = stack.Pop(); var high = stack.Pop(); Load(pc, BitUtils.Combine(low, high)); }
public static void Pop(IWriteonlyRegister <ushort> target, IStack stack) { var low = stack.Pop(); var high = stack.Pop(); target.Value = BitUtils.Combine(low, high); }
public void Combine(byte high, byte low, ushort expected) { Assert.AreEqual(expected, BitUtils.Combine(low, high)); }
/// <summary> /// Executes the next instruction and modifies the passed machine state accordingly. /// </summary> /// <param name="m">Machine state before execution.</param> /// <returns>Elapsed clock cycles.</returns> public static int ExecuteNextInstruction(IMachineState m) { var opcode = NextInstructionByte(m); switch (opcode) { #region NOP case 0x00: return(4); #endregion #region LD reg, reg' case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x47: case 0x48: case 0x49: case 0x4A: case 0x4B: case 0x4C: case 0x4D: case 0x4F: case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x57: case 0x58: case 0x59: case 0x5A: case 0x5B: case 0x5C: case 0x5D: case 0x5F: case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x67: case 0x68: case 0x69: case 0x6A: case 0x6B: case 0x6C: case 0x6D: case 0x6F: case 0x7F: case 0x78: case 0x79: case 0x7A: case 0x7B: case 0x7C: case 0x7D: { var regTarget = m.Registers.GetRegisterById((opcode & 0x38) >> 3); var regSource = m.Registers.GetRegisterById(opcode & 0x07); Instructions.Load(regTarget, regSource.Value); return(4); } #endregion #region LD reg, imm case 0x06: case 0x0E: case 0x16: case 0x1E: case 0x26: case 0x2E: case 0x3E: { var reg = m.Registers.GetRegisterById((opcode & 0x38) >> 3); Instructions.Load(reg, NextInstructionByte(m)); return(8); } #endregion #region LD reg, (HL) case 0x7E: case 0x46: case 0x4E: case 0x56: case 0x5E: case 0x66: case 0x6E: { var regTarget = m.Registers.GetRegisterById((opcode & 0x38) >> 3); var value = m.Memory[m.Registers.HL.Value]; Instructions.Load(regTarget, value); return(8); } #endregion #region LD (HL), reg case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x77: { var target = new MemoryLocation(m.Memory, m.Registers.HL.Value); var regSource = m.Registers.GetRegisterById(opcode & 0x07); Instructions.Load(target, regSource.Value); return(8); } #endregion #region LD (HL), imm case 0x36: { var target = new MemoryLocation(m.Memory, m.Registers.HL.Value); Instructions.Load(target, NextInstructionByte(m)); return(12); } #endregion #region LD A, (BC) case 0x0A: { var source = new MemoryLocation(m.Memory, m.Registers.BC.Value); Instructions.Load(m.Registers.A, source.Value); return(8); } #endregion #region LD A, (DE) case 0x1A: { var source = new MemoryLocation(m.Memory, m.Registers.DE.Value); Instructions.Load(m.Registers.A, source.Value); return(8); } #endregion #region LD A, (C) case 0xF2: { var source = new MemoryLocation(m.Memory, 0xFF00 + m.Registers.C.Value); Instructions.Load(m.Registers.A, source.Value); return(8); } #endregion #region LD (C), A case 0xE2: { var target = new MemoryLocation(m.Memory, 0xFF00 + m.Registers.C.Value); Instructions.Load(target, m.Registers.A.Value); return(8); } #endregion #region LD A, (n) case 0xF0: { var source = new MemoryLocation(m.Memory, 0xFF00 + NextInstructionByte(m)); Instructions.Load(m.Registers.A, source.Value); return(12); } #endregion #region LD (n), A case 0xE0: { var target = new MemoryLocation(m.Memory, 0xFF00 + NextInstructionByte(m)); Instructions.Load(target, m.Registers.A.Value); return(12); } #endregion #region LD A, (nn) case 0xFA: { var low = NextInstructionByte(m); var high = NextInstructionByte(m); var source = new MemoryLocation(m.Memory, BitUtils.Combine(low, high)); Instructions.Load(m.Registers.A, source.Value); return(16); } #endregion #region LD (nn), A case 0xEA: { var low = NextInstructionByte(m); var high = NextInstructionByte(m); var target = new MemoryLocation(m.Memory, BitUtils.Combine(low, high)); Instructions.Load(target, m.Registers.A.Value); return(16); } #endregion #region LD A, (HL+) case 0x2A: { var source = new MemoryLocation(m.Memory, m.Registers.HL.Value); Instructions.Load(m.Registers.A, source.Value); m.Registers.HL.Value++; return(8); } #endregion #region LD A, (HL-) case 0x3A: { var source = new MemoryLocation(m.Memory, m.Registers.HL.Value); Instructions.Load(m.Registers.A, source.Value); m.Registers.HL.Value--; return(8); } #endregion #region LD (BC), A case 0x02: { var target = new MemoryLocation(m.Memory, m.Registers.BC.Value); Instructions.Load(target, m.Registers.A.Value); return(8); } #endregion #region LD (DE), A case 0x12: { var target = new MemoryLocation(m.Memory, m.Registers.DE.Value); Instructions.Load(target, m.Registers.A.Value); return(8); } #endregion #region LD (HL+), A case 0x22: { var target = new MemoryLocation(m.Memory, m.Registers.HL.Value); Instructions.Load(target, m.Registers.A.Value); m.Registers.HL.Value++; return(8); } #endregion #region LD (HL-), A case 0x32: { var target = new MemoryLocation(m.Memory, m.Registers.HL.Value); Instructions.Load(target, m.Registers.A.Value); m.Registers.HL.Value--; return(8); } #endregion #region LD reg16, imm16 case 0x01: case 0x11: case 0x21: case 0x31: { var low = NextInstructionByte(m); var high = NextInstructionByte(m); var target = m.Registers.GetSpecialRegisterPairById((opcode & 0x30) >> 4); Instructions.Load(target, BitUtils.Combine(low, high)); return(12); } #endregion #region LD SP, HL case 0xF9: { Instructions.Load(m.Registers.SP, m.Registers.HL.Value); return(8); } #endregion #region PUSH qq case 0xF5: case 0xC5: case 0xD5: case 0xE5: { var reg = m.Registers.GetGeneralRegisterPairById((opcode & 0x30) >> 4); Instructions.Push(m.Stack, reg.Value); return(16); } #endregion #region POP qq case 0xF1: case 0xC1: case 0xD1: case 0xE1: { var reg = m.Registers.GetGeneralRegisterPairById((opcode & 0x30) >> 4); Instructions.Pop(reg, m.Stack); return(12); } #endregion #region LDHL SP, e case 0xF8: { var e = (sbyte)NextInstructionByte(m); Instructions.LoadWithSignedOffset(m.Registers.HL, m.Registers.SP, e, m.Registers.Flags); return(12); } #endregion #region LD (nn), SP case 0x08: { var low = NextInstructionByte(m); var high = NextInstructionByte(m); var targetLow = new MemoryLocation(m.Memory, BitUtils.Combine(low, high)); var targetHigh = new MemoryLocation(m.Memory, BitUtils.Combine(low, high) + 1); Instructions.Load(targetLow, m.Registers.SP.Value.GetLow()); Instructions.Load(targetHigh, m.Registers.SP.Value.GetHigh()); return(20); } #endregion #region ADD A, reg case 0x87: case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: { var regSource = m.Registers.GetRegisterById(opcode & 0x7); Instructions.Add(m.Registers.A, regSource.Value, m.Registers.Flags); return(4); } #endregion #region ADD A, (HL) case 0x86: { var source = new MemoryLocation(m.Memory, m.Registers.HL.Value); Instructions.Add(m.Registers.A, source.Value, m.Registers.Flags); return(8); } #endregion #region ADD A, imm case 0xC6: { var imm = NextInstructionByte(m); Instructions.Add(m.Registers.A, imm, m.Registers.Flags); return(8); } #endregion #region ADC A, reg case 0x8F: case 0x88: case 0x89: case 0x8A: case 0x8B: case 0x8C: case 0x8D: { var regSource = m.Registers.GetRegisterById(opcode & 0x7); Instructions.AddPlusCarry(m.Registers.A, regSource.Value, m.Registers.Flags); return(4); } #endregion #region ADC A, (HL) case 0x8E: { var source = new MemoryLocation(m.Memory, m.Registers.HL.Value); Instructions.AddPlusCarry(m.Registers.A, source.Value, m.Registers.Flags); return(8); } #endregion #region ADC A, imm case 0xCE: { var imm = NextInstructionByte(m); Instructions.AddPlusCarry(m.Registers.A, imm, m.Registers.Flags); return(8); } #endregion #region SUB A, reg case 0x97: case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: { var regSource = m.Registers.GetRegisterById(opcode & 0x7); Instructions.Subtract(m.Registers.A, regSource.Value, m.Registers.Flags); return(4); } #endregion #region SUB A, (HL) case 0x96: { var source = new MemoryLocation(m.Memory, m.Registers.HL.Value); Instructions.Subtract(m.Registers.A, source.Value, m.Registers.Flags); return(8); } #endregion #region SUB A, imm case 0xD6: { var imm = NextInstructionByte(m); Instructions.Subtract(m.Registers.A, imm, m.Registers.Flags); return(8); } #endregion #region SBC A, reg case 0x9F: case 0x98: case 0x99: case 0x9A: case 0x9B: case 0x9C: case 0x9D: { var regSource = m.Registers.GetRegisterById(opcode & 0x7); Instructions.SubtractMinusCarry(m.Registers.A, regSource.Value, m.Registers.Flags); return(4); } #endregion #region SBC A, (HL) case 0x9E: { var source = new MemoryLocation(m.Memory, m.Registers.HL.Value); Instructions.SubtractMinusCarry(m.Registers.A, source.Value, m.Registers.Flags); return(8); } #endregion #region SBC A, imm case 0xDE: { var imm = NextInstructionByte(m); Instructions.SubtractMinusCarry(m.Registers.A, imm, m.Registers.Flags); return(8); } #endregion #region AND A, reg case 0xA7: case 0xA0: case 0xA1: case 0xA2: case 0xA3: case 0xA4: case 0xA5: { var regSource = m.Registers.GetRegisterById(opcode & 0x7); Instructions.And(m.Registers.A, regSource.Value, m.Registers.Flags); return(4); } #endregion #region AND A, (HL) case 0xA6: { var source = new MemoryLocation(m.Memory, m.Registers.HL.Value); Instructions.And(m.Registers.A, source.Value, m.Registers.Flags); return(8); } #endregion #region AND A, imm case 0xE6: { var imm = NextInstructionByte(m); Instructions.And(m.Registers.A, imm, m.Registers.Flags); return(8); } #endregion #region OR A, reg case 0xB7: case 0xB0: case 0xB1: case 0xB2: case 0xB3: case 0xB4: case 0xB5: { var regSource = m.Registers.GetRegisterById(opcode & 0x7); Instructions.Or(m.Registers.A, regSource.Value, m.Registers.Flags); return(4); } #endregion #region OR A, (HL) case 0xB6: { var source = new MemoryLocation(m.Memory, m.Registers.HL.Value); Instructions.Or(m.Registers.A, source.Value, m.Registers.Flags); return(8); } #endregion #region OR A, imm case 0xF6: { var imm = NextInstructionByte(m); Instructions.Or(m.Registers.A, imm, m.Registers.Flags); return(8); } #endregion #region XOR A, reg case 0xAF: case 0xA8: case 0xA9: case 0xAA: case 0xAB: case 0xAC: case 0xAD: { var regSource = m.Registers.GetRegisterById(opcode & 0x7); Instructions.Xor(m.Registers.A, regSource.Value, m.Registers.Flags); return(4); } #endregion #region XOR A, (HL) case 0xAE: { var source = new MemoryLocation(m.Memory, m.Registers.HL.Value); Instructions.Xor(m.Registers.A, source.Value, m.Registers.Flags); return(8); } #endregion #region XOR A, imm case 0xEE: { var imm = NextInstructionByte(m); Instructions.Xor(m.Registers.A, imm, m.Registers.Flags); return(8); } #endregion #region CP A, reg case 0xBF: case 0xB8: case 0xB9: case 0xBA: case 0xBB: case 0xBC: case 0xBD: { var regSource = m.Registers.GetRegisterById(opcode & 0x7); Instructions.Compare(m.Registers.A, regSource.Value, m.Registers.Flags); return(4); } #endregion #region CP A, (HL) case 0xBE: { var source = new MemoryLocation(m.Memory, m.Registers.HL.Value); Instructions.Compare(m.Registers.A, source.Value, m.Registers.Flags); return(8); } #endregion #region CP A, imm case 0xFE: { var imm = NextInstructionByte(m); Instructions.Compare(m.Registers.A, imm, m.Registers.Flags); return(8); } #endregion #region INC reg case 0x3C: case 0x04: case 0x0C: case 0x14: case 0x1C: case 0x24: case 0x2C: { var reg = m.Registers.GetRegisterById((opcode & 0x38) >> 3); Instructions.Increment(reg, m.Registers.Flags); return(4); } #endregion #region INC (HL) case 0x34: { var source = new MemoryLocation(m.Memory, m.Registers.HL.Value); Instructions.Increment(source, m.Registers.Flags); return(8); } #endregion #region DEC reg case 0x3D: case 0x05: case 0x0D: case 0x15: case 0x1D: case 0x25: case 0x2D: { var reg = m.Registers.GetRegisterById((opcode & 0x38) >> 3); Instructions.Decrement(reg, m.Registers.Flags); return(4); } #endregion #region DEC (HL) case 0x35: { var source = new MemoryLocation(m.Memory, m.Registers.HL.Value); Instructions.Decrement(source, m.Registers.Flags); return(8); } #endregion #region ADD HL, reg16 case 0x09: case 0x19: case 0x29: case 0x39: { var regSource = m.Registers.GetSpecialRegisterPairById((opcode & 0x30) >> 4); Instructions.Add(m.Registers.HL, regSource.Value, m.Registers.Flags); return(8); } #endregion #region ADD SP, e case 0xE8: { var reg = m.Registers.SP; var e = (sbyte)NextInstructionByte(m); Instructions.LoadWithSignedOffset(reg, reg, e, m.Registers.Flags); return(16); } #endregion #region INC reg16 case 0x03: case 0x13: case 0x23: case 0x33: { var regSource = m.Registers.GetSpecialRegisterPairById((opcode & 0x30) >> 4); Instructions.Increment(regSource); return(8); } #endregion #region DEC reg16 case 0x0B: case 0x1B: case 0x2B: case 0x3B: { var regSource = m.Registers.GetSpecialRegisterPairById((opcode & 0x30) >> 4); Instructions.Decrement(regSource); return(8); } #endregion // TODO re-check if these pairs are flipped, looks weird #region RLCA case 0x07: { Instructions.RotateLeftA(m.Registers.A, m.Registers.Flags); return(4); } #endregion #region RLA case 0x17: { Instructions.RotateLeftThroughCarryA(m.Registers.A, m.Registers.Flags); return(4); } #endregion #region RRCA case 0x0F: { Instructions.RotateRightA(m.Registers.A, m.Registers.Flags); return(4); } #endregion #region RRA case 0x1F: { Instructions.RotateRightThroughCarryA(m.Registers.A, m.Registers.Flags); return(4); } #endregion case 0xCB: { return(ExecuteCB(m)); } // TODO timing correct? #region JP imm16 case 0xC3: { var low = NextInstructionByte(m); var high = NextInstructionByte(m); Instructions.Load(m.Registers.PC, BitUtils.Combine(low, high)); return(12); } #endregion // TODO: timing correct? #region JP cc, imm16 case 0xC2: case 0xCA: case 0xD2: case 0xDA: { var low = NextInstructionByte(m); var high = NextInstructionByte(m); var condition = (JumpCondition)((opcode & 0x18) >> 3); Instructions.ConditionalJump(m.Registers.PC, BitUtils.Combine(low, high), condition, m.Registers.Flags); return(12); } #endregion // TODO re-check jump offset (should be correct though) // TODO timing #region JR e case 0x18: { var offset = (sbyte)NextInstructionByte(m); Instructions.JumpByOffset(m.Registers.PC, offset); return(8); } #endregion // TODO: timing? #region JR cc, e case 0x20: case 0x28: case 0x30: case 0x38: { var condition = (JumpCondition)((opcode & 0x18) >> 3); var offset = (sbyte)NextInstructionByte(m); Instructions.ConditionalJumpByOffset(m.Registers.PC, offset, condition, m.Registers.Flags); return(8); } #endregion #region JP (HL) case 0xE9: { // This doesn't use the memory location pointed to by HL, but the value of HL itself! Instructions.Load(m.Registers.PC, m.Registers.HL.Value); return(4); } #endregion #region CALL imm16 case 0xCD: { var low = NextInstructionByte(m); var high = NextInstructionByte(m); Instructions.Call(m.Registers.PC, BitUtils.Combine(low, high), m.Stack); return(24); } #endregion // TODO: timing! #region CALL cc, imm16 case 0xC4: case 0xCC: case 0xD4: case 0xDC: { var low = NextInstructionByte(m); var high = NextInstructionByte(m); var condition = (JumpCondition)((opcode & 0x18) >> 3); Instructions.ConditionalCall(m.Registers.PC, BitUtils.Combine(low, high), m.Stack, condition, m.Registers.Flags); return(24); } #endregion // TODO timing, manual says 16 cycles? #region RET case 0xC9: { Instructions.Return(m.Registers.PC, m.Stack); return(8); } #endregion // TODO timing, manual says 16 cycles? #region RETI case 0xD9: { Instructions.Return(m.Registers.PC, m.Stack); m.InterruptMasterEnable = true; // "returned to its pre-interrupt status"? return(8); } #endregion // TODO: timing #region RET cc case 0xC0: case 0xC8: case 0xD0: case 0xD8: { var condition = (JumpCondition)((opcode & 0x18) >> 3); Instructions.ConditionalReturn(m.Registers.PC, m.Stack, condition, m.Registers.Flags); return(8); } #endregion // TODO timing, manual says 16 cycles? #region RST t case 0xC7: case 0xCF: case 0xD7: case 0xDF: case 0xE7: case 0xEF: case 0xF7: case 0xFF: { var restartOffset = (byte)((opcode & 0b0011_1000) >> 3); Instructions.Restart(m.Registers.PC, restartOffset, m.Stack); return(16); } #endregion #region DAA case 0x27: { Instructions.DecimalAdjust(m.Registers.A, m.Registers.Flags); return(4); } #endregion #region CPL case 0x2F: { Instructions.Complement(m.Registers.A, m.Registers.Flags); return(4); } #endregion #region CCF case 0x3F: { Instructions.ComplementCarryFlag(m.Registers.Flags); return(4); } #endregion #region SCF case 0x37: { Instructions.SetCarryFlag(m.Registers.Flags); return(4); } #endregion #region DI case 0xF3: { m.InterruptMasterEnable = false; return(4); } #endregion #region EI case 0xFB: { m.InterruptMasterEnable = true; return(4); } #endregion #region HALT case 0x76: { m.Halted = true; return(4); } #endregion #region STOP case 0x10: { m.Stopped = true; return(4); } #endregion default: throw new InvalidOperationException("Invalid opcode."); } }
// TODO: Case statement is the same as in Cpu. Unify them somehow to make it // less ugly. public static Instruction DisassembleInstruction(IEnumerator <byte> lookahead) { var count = 0; Func <byte> next = () => { lookahead.MoveNext(); count++; return(lookahead.Current); }; var opcode = next(); switch (opcode) { #region NOP case 0x00: return(new Instruction("NOP", count)); #endregion #region LD reg, reg' case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x47: case 0x48: case 0x49: case 0x4A: case 0x4B: case 0x4C: case 0x4D: case 0x4F: case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x57: case 0x58: case 0x59: case 0x5A: case 0x5B: case 0x5C: case 0x5D: case 0x5F: case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x67: case 0x68: case 0x69: case 0x6A: case 0x6B: case 0x6C: case 0x6D: case 0x6F: case 0x7F: case 0x78: case 0x79: case 0x7A: case 0x7B: case 0x7C: case 0x7D: { var regTarget = Enum.GetName(typeof(RegisterName), (opcode & 0x38) >> 3); var regSource = Enum.GetName(typeof(RegisterName), opcode & 0x07); return(new Instruction($"LD {regTarget}, {regSource}", count)); } #endregion #region LD reg, imm case 0x06: case 0x0E: case 0x16: case 0x1E: case 0x26: case 0x2E: case 0x3E: { var regTarget = Enum.GetName(typeof(RegisterName), (opcode & 0x38) >> 3); return(new Instruction($"LD {regTarget}, 0x{next():X2}", count)); } #endregion #region LD reg, (HL) case 0x7E: case 0x46: case 0x4E: case 0x56: case 0x5E: case 0x66: case 0x6E: { var regTarget = Enum.GetName(typeof(RegisterName), (opcode & 0x38) >> 3); return(new Instruction($"LD {regTarget}, (HL)", count)); } #endregion #region LD (HL), reg case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x77: { var regSource = Enum.GetName(typeof(RegisterName), opcode & 0x07); return(new Instruction($"LD (HL), {regSource}", count)); } #endregion #region LD (HL), imm case 0x36: { return(new Instruction($"LD (HL), 0x{next():X2}", count)); } #endregion #region LD A, (BC) case 0x0A: { return(new Instruction("LD A, (BC)", count)); } #endregion #region LD A, (DE) case 0x1A: { return(new Instruction("LD A, (DE)", count)); } #endregion #region LD A, (C) case 0xF2: { return(new Instruction("LD A, (0xFF00 + C)", count)); } #endregion #region LD (C), A case 0xE2: { return(new Instruction("LD (0xFF00 + C), A", count)); } #endregion #region LD A, (n) case 0xF0: { return(new Instruction($"LD A, (0xFF{next():X2})", count)); } #endregion #region LD (n), A case 0xE0: { return(new Instruction($"LD (0xFF{next():X2}), A", count)); } #endregion #region LD A, (nn) case 0xFA: { return(new Instruction($"LD A, (0x{BitUtils.Combine(next(), next()):X4})", count)); } #endregion #region LD (nn), A case 0xEA: { return(new Instruction($"LD (0x{BitUtils.Combine(next(), next()):X4}), A", count)); } #endregion #region LD A, (HL+) case 0x2A: { return(new Instruction("LD A, (HL+)", count)); } #endregion #region LD A, (HL-) case 0x3A: { return(new Instruction("LD A, (HL-)", count)); } #endregion #region LD (BC), A case 0x02: { return(new Instruction("LD (BC), A", count)); } #endregion #region LD (DE), A case 0x12: { return(new Instruction("LD (DE), A", count)); } #endregion #region LD (HL+), A case 0x22: { return(new Instruction("LD (HL+), A", count)); } #endregion #region LD (HL-), A case 0x32: { return(new Instruction("LD (HL-), A", count)); } #endregion #region LD reg16, imm16 case 0x01: case 0x11: case 0x21: case 0x31: { var regTarget = Enum.GetName(typeof(SpecialRegisterName), (opcode & 0x30) >> 4); return(new Instruction($"LD {regTarget}, 0x{BitUtils.Combine(next(), next()):X4}", count)); } #endregion #region LD SP, HL case 0xF9: { return(new Instruction("LD SP, HL", count)); } #endregion #region PUSH qq case 0xF5: case 0xC5: case 0xD5: case 0xE5: { var regTarget = Enum.GetName(typeof(SpecialRegisterName), (opcode & 0x30) >> 4); return(new Instruction($"PUSH {regTarget}", count)); } #endregion #region POP qq case 0xF1: case 0xC1: case 0xD1: case 0xE1: { var regTarget = Enum.GetName(typeof(SpecialRegisterName), (opcode & 0x30) >> 4); return(new Instruction($"POP {regTarget}", count)); } #endregion #region LDHL SP, e case 0xF8: { var e = next(); //Instructions.Load(m.Registers.HL, (ushort)(m.Registers.SP.Value + e - 128)); return(new Instruction("LDHL SP, e", count)); } #endregion #region LD (nn), SP case 0x08: { return(new Instruction($"LD (0x{BitUtils.Combine(next(), next()):X4}), SP", count)); } #endregion #region ADD A, reg case 0x87: case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: { var regSource = Enum.GetName(typeof(RegisterName), opcode & 0x7); return(new Instruction($"ADD A, {regSource}", count)); } #endregion #region ADD A, (HL) case 0x86: { return(new Instruction("ADD A, (HL)", count)); } #endregion #region ADD A, imm case 0xC6: { return(new Instruction($"ADD A, 0x{next():X2}", count)); } #endregion #region ADC A, reg case 0x8F: case 0x88: case 0x89: case 0x8A: case 0x8B: case 0x8C: case 0x8D: { var regSource = Enum.GetName(typeof(RegisterName), opcode & 0x7); return(new Instruction($"ADC A, {regSource}", count)); } #endregion #region ADC A, (HL) case 0x8E: { return(new Instruction("ADC A, (HL)", count)); } #endregion #region ADC A, imm case 0xCE: { return(new Instruction($"ADC A, 0x{next():X2}", count)); } #endregion #region SUB A, reg case 0x97: case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: { var regSource = Enum.GetName(typeof(RegisterName), opcode & 0x7); return(new Instruction($"SUB A, {regSource}", count)); } #endregion #region SUB A, (HL) case 0x96: { return(new Instruction("SUB A, (HL)", count)); } #endregion #region SUB A, imm case 0xD6: { var immediate = next(); return(new Instruction($"SUB A, 0x{immediate:X2}", count)); } #endregion #region SBC A, reg case 0x9F: case 0x98: case 0x99: case 0x9A: case 0x9B: case 0x9C: case 0x9D: { var regSource = Enum.GetName(typeof(RegisterName), opcode & 0x7); return(new Instruction($"SBC A, {regSource}", count)); } #endregion #region SBC A, (HL) case 0x9E: { return(new Instruction("SBC A, (HL)", count)); } #endregion #region SBC A, imm case 0xDE: { var immediate = next(); return(new Instruction($"SBC A, 0x{immediate:X2}", count)); } #endregion #region AND A, reg case 0xA7: case 0xA0: case 0xA1: case 0xA2: case 0xA3: case 0xA4: case 0xA5: { var regSource = Enum.GetName(typeof(RegisterName), opcode & 0x7); return(new Instruction($"AND A, {regSource}", count)); } #endregion #region AND A, (HL) case 0xA6: { return(new Instruction("AND A, (HL)", count)); } #endregion #region AND A, imm case 0xE6: { var immediate = next(); return(new Instruction($"AND A, 0x{immediate:X2}", count)); } #endregion #region OR A, reg case 0xB7: case 0xB0: case 0xB1: case 0xB2: case 0xB3: case 0xB4: case 0xB5: { var regSource = Enum.GetName(typeof(RegisterName), opcode & 0x7); return(new Instruction($"OR A, {regSource}", count)); } #endregion #region OR A, (HL) case 0xB6: { return(new Instruction("OR A, (HL)", count)); } #endregion #region OR A, imm case 0xF6: { var immediate = next(); return(new Instruction($"OR A, 0x{immediate:X2}", count)); } #endregion #region XOR A, reg case 0xAF: case 0xA8: case 0xA9: case 0xAA: case 0xAB: case 0xAC: case 0xAD: { var regSource = Enum.GetName(typeof(RegisterName), opcode & 0x7); return(new Instruction($"XOR A, {regSource}", count)); } #endregion #region XOR A, (HL) case 0xAE: { return(new Instruction("XOR A, (HL)", count)); } #endregion #region XOR A, imm case 0xEE: { var immediate = next(); return(new Instruction($"XOR A, 0x{immediate:X2}", count)); } #endregion #region CP A, reg case 0xBF: case 0xB8: case 0xB9: case 0xBA: case 0xBB: case 0xBC: case 0xBD: { var regSource = Enum.GetName(typeof(RegisterName), opcode & 0x7); return(new Instruction($"CP A, {regSource}", count)); } #endregion #region CP A, (HL) case 0xBE: { return(new Instruction("CP A, (HL)", count)); } #endregion #region CP A, imm case 0xFE: { var immediate = next(); return(new Instruction($"CP A, 0x{immediate:X2}", count)); } #endregion #region INC reg case 0x3C: case 0x04: case 0x0C: case 0x14: case 0x1C: case 0x24: case 0x2C: { var regSource = Enum.GetName(typeof(RegisterName), (opcode & 0x38) >> 3); return(new Instruction($"INC {regSource}", count)); } #endregion #region INC (HL) case 0x34: { return(new Instruction("INC (HL)", count)); } #endregion #region DEC reg case 0x3D: case 0x05: case 0x0D: case 0x15: case 0x1D: case 0x25: case 0x2D: { var regSource = Enum.GetName(typeof(RegisterName), (opcode & 0x38) >> 3); return(new Instruction($"DEC {regSource}", count)); } #endregion #region DEC (HL) case 0x35: { return(new Instruction("DEC (HL)", count)); } #endregion #region ADD HL, reg16 case 0x09: case 0x19: case 0x29: case 0x39: { var regSource = Enum.GetName(typeof(PairedRegisterName), (opcode & 0x30) >> 4); return(new Instruction($"ADD HL, {regSource}", count)); } #endregion // TODO #region ADD SP, e case 0xE8: { // ... var e = next(); return(new Instruction("ADD SP, e", count)); } #endregion #region INC reg16 case 0x03: case 0x13: case 0x23: case 0x33: { var regSource = Enum.GetName(typeof(PairedRegisterName), (opcode & 0x30) >> 4); return(new Instruction($"INC {regSource}", count)); } #endregion #region DEC reg16 case 0x0B: case 0x1B: case 0x2B: case 0x3B: { var regSource = Enum.GetName(typeof(PairedRegisterName), (opcode & 0x30) >> 4); return(new Instruction($"DEC {regSource}", count)); } #endregion #region RLCA case 0x07: { return(new Instruction("RLCA", count)); } #endregion #region RLA case 0x17: { return(new Instruction("RLA", count)); } #endregion #region RRCA case 0x0F: { return(new Instruction("RRCA", count)); } #endregion #region RRA case 0x1F: { return(new Instruction("RRA", count)); } #endregion case 0xCB: { return(DisassembleCBInstruction(lookahead)); } #region JP imm16 case 0xC3: { var immediate = BitUtils.Combine(next(), next()); return(new Instruction($"JP 0x{immediate:X4}", count)); } #endregion #region JP cc, imm16 case 0xC2: case 0xCA: case 0xD2: case 0xDA: { var immediate = BitUtils.Combine(next(), next()); var condition = Enum.GetName(typeof(JumpCondition), (opcode & 0x18) >> 3); return(new Instruction($"JP {condition}, {immediate}", count)); } #endregion // TODO: what is e-2?? #region JR e case 0x18: { var e = (sbyte)next(); return(new Instruction($"JR {e}", count)); } #endregion // TODO: e #region JR cc, e case 0x20: case 0x28: case 0x30: case 0x38: { var condition = Enum.GetName(typeof(JumpCondition), (opcode & 0x18) >> 3); var offset = (sbyte)next(); return(new Instruction($"JR {condition}, {offset}", count)); } #endregion #region JP (HL) case 0xE9: { return(new Instruction("JP (HL)", count)); } #endregion #region CALL imm16 case 0xCD: { var immediate = BitUtils.Combine(next(), next()); return(new Instruction($"CALL 0x{immediate:X4}", count)); } #endregion #region CALL cc, imm16 case 0xC4: case 0xCC: case 0xD4: case 0xDC: { var immediate = BitUtils.Combine(next(), next()); var condition = Enum.GetName(typeof(JumpCondition), (opcode & 0x18) >> 3); return(new Instruction($"CALL {condition}, {immediate}", count)); } #endregion #region RET case 0xC9: { return(new Instruction("RET", count)); } #endregion #region RETI case 0xD9: { return(new Instruction("RETI", count)); } #endregion #region RET cc case 0xC0: case 0xC8: case 0xD0: case 0xD8: { var condition = Enum.GetName(typeof(JumpCondition), (opcode & 0x18) >> 3); return(new Instruction($"RET {condition}", count)); } #endregion #region RST t case 0xC7: case 0xCF: case 0xD7: case 0xDF: case 0xE7: case 0xEF: case 0xF7: case 0xFF: { var resetAddr = (byte)(((opcode & 0b0011_1000) >> 3) * 0x08); return(new Instruction($"RST 0x{resetAddr:X2}", count)); } #endregion #region DAA case 0x27: { return(new Instruction("DAA", count)); } #endregion #region CPL case 0x2F: { return(new Instruction("CPL", count)); } #endregion #region CCF case 0x3F: { return(new Instruction("CCF", count)); } #endregion #region SCF case 0x37: { return(new Instruction("SCF", count)); } #endregion #region DI case 0xF3: { return(new Instruction("DI", count)); } #endregion #region EI case 0xFB: { return(new Instruction("EI", count)); } #endregion #region HALT case 0x76: { return(new Instruction("HALT", count)); } #endregion #region STOP case 0x10: { return(new Instruction("STOP", count)); } #endregion default: return(new Instruction("???", 1)); //throw new InvalidOperationException("Invalid opcode."); } }