/// <summary> /// Adds src to a. /// </summary> /// <param name="op">The op.</param> /// <param name="memory">The memory.</param> /// <returns>The number of ticks the operation took to complete.</returns> /// <remarks>Affected flags: Z 0 H C</remarks> public static int Add(Opcode op, GbMemory memory) => ArithOp8Func(op, memory, (mem, val) => { byte s = (byte)(mem.R.A + val); mem.R.F = (byte)((s == 0 ? RFlags.ZB : 0) | ((((mem.R.A & 0x0F) + (val & 0x0F)) & 0x10) == 0x10 ? RFlags.HB : 0) | (s < mem.R.A ? RFlags.CB : 0)); mem.R.A = s; });
#pragma warning restore S1067 // Expressions should not be too complex /// <summary> /// Subtracts src from a. /// </summary> /// <param name="op">The op.</param> /// <param name="memory">The memory.</param> /// <returns>The number of ticks the operation took to complete.</returns> /// <remarks>Affected flags: Z 1 H C</remarks> public static int Sub(Opcode op, GbMemory memory) => ArithOp8Func(op, memory, (mem, val) => { byte s = (byte)(mem.R.A - val); mem.R.F = (byte)((s == 0 ? RFlags.ZNB : RFlags.NB) | ((mem.R.A & 0xF) < (val & 0xF) ? RFlags.HB : 0) | (s > mem.R.A ? RFlags.CB : 0)); mem.R.A = s; });
public static int LdR16(Opcode op, GbMemory mem) { if (op.Src == 8) { mem.WriteCycle((op.Dest == 2 || op.Dest == 3) ? mem.R.Hl : mem.R.GetR16Sp(op.Dest), mem.R.A); } else { mem.R.A = mem.ReadCycle((op.Src == 2 || op.Src == 3) ? mem.R.Hl : mem.R.GetR16Sp(op.Src)); } if (op.Src == 2 || op.Dest == 2) { mem.R.Hl++; } else if (op.Src == 3 || op.Dest == 3) { mem.R.Hl--; } else { // Do nothing. } return(2); }
/// <summary> /// Disassembles an instruction. /// </summary> /// <param name="memory">The memory.</param> /// <returns>A string representing the instruction at <paramref name="memory"/>.R.PC</returns> private static string DisassembleInstructionInternal(GbMemory memory) { byte b = memory.GetMappedMemory(memory.R.Pc); if (b >= 0x40 && b < 0xC0) { if (b < 0x80) { if (b == 0x76) { return("HALT"); } int dest = ((b >> 3) & 7); int src = (b & 7); return("LD " + GetR8(dest) + "," + GetR8(src)); } return(DisassembleInstructionArith(b)); } if (b == 0xCB) { return(DisassembleInstructionCb(memory.GetMappedMemory((ushort)(memory.R.Pc + 1)))); } return(nmOpStrings[b]); }
public static int LdD16(Opcode op, GbMemory mem) { byte low = mem.ReadCycleI8(); mem.R.SetR16(op.Dest, new GbUInt16(mem.ReadCycleI8(), low)); return(3); }
public static int LdA16Sp(Opcode op, GbMemory mem) { byte low = mem.ReadCycleI8(); byte high = mem.ReadCycleI8(); mem.WriteCycle(new GbUInt16(high, low), mem.R.Sp.LowByte); mem.WriteCycle((GbUInt16)(new GbUInt16(high, low) + 1), mem.R.Sp.HighByte); return(5); }
public static int Push(Opcode op, GbMemory mem) { GbUInt16 r = mem.R.GetR16Af(op.Dest); mem.Update(); mem.WriteCyclePush(r.HighByte); mem.WriteCyclePush(r.LowByte); return(4); }
#pragma warning disable S1067 // Expressions should not be too complex /// <summary> /// Subtracts src and the carry flag from A /// </summary> /// <param name="op">The op.</param> /// <param name="memory">The memory.</param> /// <returns>The number of ticks the operation took to complete.</returns> /// <remarks>Affected flags: Z 1 H C</remarks> public static int Sbc(Opcode op, GbMemory memory) => ArithOp8Func(op, memory, (mem, val) => { // https://github.com/eightlittlebits/elbgb/blob/dffc28001a7a01f93ef9e8abecd7161bcf03cc95/elbgb_core/CPU/LR35902.cs#L1084 // Reimplemented on 8/26/2017 thanks to the previous link. int cIn = mem.R.F.GetBit(RFlags.CF) ? 1 : 0; int res = mem.R.A - val - cIn; mem.R.F = (byte)(((res & 0xFF) == 0 ? RFlags.ZNB : RFlags.NB) | ((mem.R.A & 0xF) - (val & 0xF) - cIn < 0 ? RFlags.HB : 0) | (res < 0 ? RFlags.CB : 0)); mem.R.A = (byte)res; });
public static int LdHlSpR8(Opcode op, GbMemory mem) { sbyte s = (sbyte)mem.ReadCycleI8(); mem.Update(); mem.R.F = (((mem.R.Sp & 0xFF) + (s & 0xFF)) > 0xFF ? RFlags.CB : (byte)0).AssignBit(RFlags.HF, ((mem.R.Sp & 0x0F) + (s & 0x0F)) > 0x0F); mem.R.Hl = mem.R.Sp + s; return(3); }
/// <summary> /// Adds src and the carry flag to a. /// </summary> /// <param name="op">The op.</param> /// <param name="memory">The memory.</param> /// <returns>The number of ticks the operation took to complete.</returns> /// <remarks>Affected flags: Z 0 H C</remarks> public static int Adc(Opcode op, GbMemory memory) => ArithOp8Func(op, memory, (mem, val) => { // https://github.com/eightlittlebits/elbgb/blob/dffc28001a7a01f93ef9e8abecd7161bcf03cc95/elbgb_core/CPU/LR35902.cs#L1038 // Reimplemented on 8/26/2017 thanks to the previous link. int cIn = mem.R.F.GetBit(RFlags.CF) ? 1 : 0; int res = val + cIn + mem.R.A; mem.R.F = ((res & 0xFF) == 0 ? RFlags.ZB : byte.MinValue).AssignBit(RFlags.HF, (mem.R.A & 0x0F) + (val & 0x0F) + cIn > 0x0F).AssignBit(RFlags.CF, res > 0xFF); mem.R.A = (byte)res; });
public static int AddHl(Opcode op, GbMemory mem) { mem.Update(); GbUInt16 val = mem.R.GetR16Sp(op.Src); mem.R.F = mem.R.F.Res(RFlags.NF).AssignBit(RFlags.HF, (((mem.R.Hl & 0xFFF) + (val & 0xFFF)) & 0x1000) == 0x1000) .AssignBit(RFlags.CF, val + mem.R.Hl < mem.R.Hl); mem.R.Hl += val; return(2); }
public static int Ret(Opcode op, GbMemory mem) { byte low = mem.ReadCyclePop(); mem.R.Pc = new GbUInt16(mem.ReadCyclePop(), low); mem.Update(); mem.IME |= op.Dest != 0; // Unlike EI IME gets enabled right away. mem.NextIMEValue = mem.IME; return(4); }
/// <summary> /// Adds a 8 bit signed value to the Stack Pointer. /// </summary> /// <param name="op">The op.</param> /// <param name="memory">The memory.</param> /// <returns>The number of ticks the operation took to complete.</returns> /// <remarks>Affected Flags: - - H C</remarks> public static int AddSpR8(Opcode op, GbMemory memory) { sbyte v = (sbyte)memory.ReadCycleI8(); memory.Update(); memory.Update(); memory.R.F = (((memory.R.Sp & 0x0F) + (v & 0x0F)) > 0x0F ? RFlags.HB : byte.MinValue) .AssignBit(RFlags.CF, ((memory.R.Sp & 0xFF) + (v & 0xFF)) > 0xFF); memory.R.Sp += v; return(4); }
/// <summary> /// Sets the dest bit of the src byte to true /// </summary> /// <param name="opcode">The opcode.</param> /// <param name="memory">The memory.</param> /// <returns>The number of ticks the operation took to complete.</returns> /// <remarks>Affected Flags: None.</remarks> public static int Set(Opcode opcode, GbMemory memory) { if (opcode.Src != 6) { memory.R.SetR8(opcode.Src, (byte)(memory.R.GetR8(opcode.Src) | (1 << opcode.Dest))); return(1); } memory.WriteCycleHl((byte)(memory.ReadCycleHl() | (1 << opcode.Dest))); return(3); }
/// <summary> /// Resources the specified code. /// </summary> /// <param name="opcode">The opcode.</param> /// <param name="memory">The memory.</param> /// <returns>The number of ticks the operation took to complete.</returns> public static int Res(Opcode opcode, GbMemory memory) { if (opcode.Src != 6) { memory.R.SetR8(opcode.Src, memory.R.GetR8(opcode.Src).Res(opcode.Dest)); return(1); } memory.WriteCycleHl(memory.ReadCycleHl().Res(opcode.Dest)); return(3); }
public static int LdD8(Opcode op, GbMemory mem) { byte val = mem.ReadCycleI8(); if (op.Dest == 6) { mem.WriteCycle(mem.R.Hl, val); return(3); } mem.R.SetR8(op.Dest, val); return(2); }
public static int LdH(Opcode op, GbMemory mem) { if (op.Dest == 7) { mem.R.A = mem.ReadCycle(new GbUInt16(0xFF, mem.ReadCycleI8())); } else { mem.WriteCycle(new GbUInt16(0xFF, mem.ReadCycleI8()), mem.R.A); } return(3); }
/// <summary> /// Checks if the dest bit of the src register is true. /// </summary> /// <param name="code">The code.</param> /// <param name="memory">The memory.</param> /// <returns>The number of ticks the operation took to complete.</returns> public static int Bit(Opcode code, GbMemory memory) { if (code.Src != 6) { memory.R.F = (byte)(memory.R.F.AssignBit(RFlags.ZF, !memory.R.GetR8(code.Src).GetBit(code.Dest)) .Res(RFlags.NF) | RFlags.HB); return(1); } memory.R.F = (byte)(memory.R.F.AssignBit(RFlags.ZF, !memory.ReadCycleHl().GetBit(code.Dest)) .Res(RFlags.NF) | RFlags.HB); return(2); }
public static int Jr8(Opcode op, GbMemory mem) { byte val = mem.ReadCycleI8(); if (op.Src != 0 && GetConditionalJumpState(op.Dest, op.Src, mem.R.F)) { return(2); } mem.Update(); mem.R.Pc += (sbyte)val; return(3); }
/// <summary> /// Conditional Return. /// </summary> /// <param name="op">The opcode.</param> /// <param name="mem">The memory.</param> public static int RetC(Opcode op, GbMemory mem) { mem.Update(); if (GetConditionalJumpState(op.Dest, op.Src, mem.R.F)) { return(2); } byte low = mem.ReadCyclePop(); mem.R.Pc = new GbUInt16(mem.ReadCyclePop(), low); mem.Update(); return(5); }
public static int Call(Opcode op, GbMemory mem) { byte low = mem.ReadCycleI8(); byte high = mem.ReadCycleI8(); if (op.Src != 0 && GetConditionalJumpState(op.Dest, op.Src, mem.R.F)) { return(3); } mem.Update(); mem.Call(new GbUInt16(high, low)); return(6); }
/// <summary> /// Runs an instruction and returns the number of ticks it took to complete. /// </summary> /// <param name="memory">The memory.</param> /// <returns>The number of ticks that the instruction took to run.</returns> public static int Run(GbMemory memory) { memory.Update(1); byte opcode = memory.LdI8(); memory.Update(1); if (memory.HaltBugged) { memory.HaltBugged = false; memory.R.Pc--; } return(NmOps[opcode].Invoke(memory)); }
public static int Pop(Opcode op, GbMemory mem) { if (op.Dest == 3) { mem.R.F = (byte)(mem.ReadCyclePop() & 0xF0); mem.R.A = mem.ReadCyclePop(); } else { mem.R.SetR8((op.Dest * 2) + 1, mem.ReadCyclePop()); mem.R.SetR8(op.Dest * 2, mem.ReadCyclePop()); } return(3); }
public static int LdA16(Opcode op, GbMemory mem) { byte low = mem.ReadCycleI8(); byte high = mem.ReadCycleI8(); if (op.Dest == 7) { mem.R.A = mem.ReadCycle(new GbUInt16(high, low)); } else { mem.WriteCycle(new GbUInt16(high, low), mem.R.A); } return(4); }
/// <summary> /// A framework for calling arithmetic operation instructions. /// </summary> /// <param name="op">The op.</param> /// <param name="memory">The memory.</param> /// <param name="operation">The operation.</param> /// <exception cref="ArgumentNullException"><paramref name="operation"/> is null</exception> internal static int ArithOp8Func(Opcode op, GbMemory memory, ArithOp8 operation) { if (operation == null) { throw new ArgumentNullException(nameof(operation)); } if (op.Src != 6 && op.Src != 8) { operation(memory, memory.R.GetR8(op.Src)); return(1); } operation(memory, op.Src == 6 ? memory.ReadCycleHl() : memory.ReadCycleI8()); return(2); }
/// <summary> /// A framework for calling bitwise operation instructions. /// </summary> /// <param name="op">The op.</param> /// <param name="memory">The memory.</param> /// <param name="operation">The operation.</param> /// <exception cref="ArgumentNullException"><paramref name="operation"/> is null</exception> internal static int BitOpFunc(Opcode op, GbMemory memory, BitOp operation) { if (operation == null) { throw new ArgumentNullException(nameof(operation)); } if (op.Src != 6) { memory.R.SetR8(op.Src, operation(memory, memory.R.GetR8(op.Src))); return(1); } memory.WriteCycleHl(operation(memory, memory.ReadCycleHl())); return(3); }
/// <summary> /// Preforms BCD conversion. /// </summary> /// <param name="op">The op.</param> /// <param name="mem">The memory.</param> /// <returns>The number of ticks the operation took to complete.</returns> /// <remarks>Affected Flags: Z - 0 C</remarks> public static int Daa(Opcode op, GbMemory mem) { int res = mem.R.A; if (mem.R.F.GetBit(RFlags.NF)) { if (mem.R.F.GetBit(RFlags.HF)) { res = (res - 6) & 0xFF; } if (mem.R.F.GetBit(RFlags.CF)) { res -= 0x60; } } else { if (mem.R.F.GetBit(RFlags.HF) || (res & 0xF) > 9) { res += 0x06; } if (mem.R.F.GetBit(RFlags.CF) || res > 0x9F) { res += 0x60; } } mem.R.F = (byte)(mem.R.F & RFlags.NCB); if ((res & 0x100) == 0x100) { mem.R.F |= RFlags.CB; } res &= 0xFF; if (res == 0) { mem.R.F |= RFlags.ZB; } mem.R.A = (byte)res; return(1); }
public static int Ld8(Opcode op, GbMemory memory) { if (op.Src == 6 || op.Dest == 6) { if (op.Src == op.Dest) // By math logic, these are both 6 if this is true. { throw new InvalidOperationException("Opcode with dest = 6 and src = 6 is invalid for this instruction."); } if (op.Src == 6) { memory.R.SetR8(op.Dest, memory.ReadCycleHl()); } else { memory.WriteCycle(memory.R.Hl, memory.R.GetR8(op.Src)); } return(2); } memory.R.SetR8(op.Dest, memory.R.GetR8(op.Src)); return(1); }
public static int Rst(Opcode op, GbMemory mem) { mem.Update(); mem.Call(new GbUInt16(0, (byte)(op.Dest * 8))); return(4); }
/// <summary> /// Initializes a new instance of the <see cref="Timer"/> class. /// </summary> /// <param name="memory">The memory.</param> internal Timer(GbMemory memory) => this.memory = memory;