public ushort Step() { if (_mmu.InterruptsExist) { // Timer interrupt should end HALT // Note: Should not trigger interrupt if IME is disabled var isHalt = _mmu.ReadByte(PC) == 0x76; if (isHalt) { PC++; // Skip past HALT } if (IME) { if (_mmu.VblankInterrupt) { _mmu.VblankInterrupt = false; Interrupt(0x0040); } else if (_mmu.LcdStatInterrupt) { _mmu.LcdStatInterrupt = false; Interrupt(0x0048); } else if (_mmu.TimerInterrupt) { _mmu.TimerInterrupt = false; Interrupt(0x0050); } else if (_mmu.SerialInterrupt) { _mmu.SerialInterrupt = false; Interrupt(0x0058); } else if (_mmu.JoyPadInterrupt) { _mmu.JoyPadInterrupt = false; Interrupt(0x0060); } return(3); } } var op = _mmu.ReadByte(PC++); // Fetch var isCb = false; if (op == 0xcb) { isCb = true; op = _mmu.ReadByte(PC++); } var ops = isCb ? _cbOps : _ops; ops[op](); // Decode, Execute var cycles = isCb ? _cbCycles[op] : _conditional ? _conditionalCycles[op] : _cycles[op]; _conditional = false; return(cycles); }
public void Return(Registers registers, IMmu mmu) { // Decrement stack pointer registers.SP.Value -= 2; // Set program counter to current stack location registers.PC.Registers[0].Value = mmu.ReadByte(registers.SP.Value); registers.PC.Registers[1].Value = mmu.ReadByte((ushort)(registers.SP.Value + 1)); }
public void Execute(Instruction instruction, ICpu cpu, IMmu mmu) { var originalValue = cpu.Registers.A.Value; cpu.Registers.A.Value += mmu.ReadByte(cpu.Registers.HL.Value); cpu.Registers.F.ZeroFlag = cpu.Registers.A.Value == 0; cpu.Registers.F.SubtractFlag = false; cpu.Registers.F.CarryFlag = cpu.Registers.A.Value <= mmu.ReadByte(cpu.Registers.HL.Value); // TODO: ??? cpu.Registers.F.HalfCarryFlag = (((originalValue & 0xF) + (1 & 0xF)) & 0x10) == 0x10; }
public void Execute(Instruction instruction, ICpu cpu, IMmu mmu) { var n = mmu.ReadByte(cpu.Registers.HL.Value); cpu.Registers.F.ZeroFlag = cpu.Registers.A.Value == n; cpu.Registers.F.CarryFlag = cpu.Registers.A.Value < n; cpu.Registers.F.SubtractFlag = true; // TODO: ??? cpu.Registers.F.HalfCarryFlag = (((cpu.Registers.A.Value & 0xF) - (n & 0xF)) & 0x10) == 0x10; }
public void Execute(Instruction instruction, ICpu cpu, IMmu mmu) { var offset = mmu.ReadByte((ushort)(cpu.Registers.PC.Value + 1)); mmu.WriteByte((ushort)(0xFF00 + offset), cpu.Registers.A.Value); }
private InstructionTimings Interpret(IRegisters registers, IMmu mmu, IAlu alu, IPeripheralManager peripherals, InterpreterHelper helper, Operation operation, DecodedBlock block) { helper.Operation = operation; var timer = new InstructionTimingsBuilder(); switch (operation.OpCode) { case OpCode.NoOperation: break; case OpCode.Stop: case OpCode.Halt: SyncProgramCounter(registers, block); break; case OpCode.Load: if (operation.Operand1 == operation.Operand2) { break; } helper.Operand1 = helper.Operand2; if (operation.Operand2 == Operand.I || operation.Operand2 == Operand.R) { // LD A, R & LD A, I also reset H & N and copy IFF2 to P/V registers.AccumulatorAndFlagsRegisters.Flags.SetResultFlags(registers.AccumulatorAndFlagsRegisters.A); registers.AccumulatorAndFlagsRegisters.Flags.HalfCarry = false; registers.AccumulatorAndFlagsRegisters.Flags.Subtract = false; registers.AccumulatorAndFlagsRegisters.Flags.ParityOverflow = registers.InterruptFlipFlop2; } break; case OpCode.Load16: helper.WordOperand1 = helper.WordOperand2; break; case OpCode.Push: helper.PushStackPointer(); mmu.WriteWord(registers.StackPointer, helper.WordOperand1); break; case OpCode.Pop: helper.WordOperand1 = mmu.ReadWord(registers.StackPointer); helper.PopStackPointer(); break; case OpCode.Add: helper.Alu8BitOperation(alu.Add); break; case OpCode.AddWithCarry: helper.Alu8BitOperation(alu.AddWithCarry); break; case OpCode.Subtract: helper.Alu8BitOperation(alu.Subtract); break; case OpCode.SubtractWithCarry: helper.Alu8BitOperation(alu.SubtractWithCarry); break; case OpCode.And: helper.Alu8BitOperation(alu.And); break; case OpCode.Or: helper.Alu8BitOperation(alu.Or); break; case OpCode.Xor: helper.Alu8BitOperation(alu.Xor); break; case OpCode.Compare: alu.Compare(registers.AccumulatorAndFlagsRegisters.A, helper.Operand1); break; case OpCode.Increment: helper.Operand1 = alu.Increment(helper.Operand1); break; case OpCode.Decrement: helper.Operand1 = alu.Decrement(helper.Operand1); break; case OpCode.Add16: helper.Alu16BitOperation(alu.Add); break; case OpCode.AddWithCarry16: helper.Alu16BitOperation(alu.AddWithCarry); break; case OpCode.SubtractWithCarry16: helper.Alu16BitOperation(alu.SubtractWithCarry); break; case OpCode.Increment16: // INC ss (no flags changes so implemented directly) helper.WordOperand1 = (ushort)(helper.WordOperand1 + 1); break; case OpCode.Decrement16: // DEC ss (no flags changes so implemented directly) helper.WordOperand1 = (ushort)(helper.WordOperand1 - 1); break; case OpCode.Exchange: { var w = helper.WordOperand2; helper.WordOperand2 = helper.WordOperand1; helper.WordOperand1 = w; } break; case OpCode.ExchangeAccumulatorAndFlags: registers.SwitchToAlternativeAccumulatorAndFlagsRegisters(); break; case OpCode.ExchangeGeneralPurpose: registers.SwitchToAlternativeGeneralPurposeRegisters(); break; case OpCode.Jump: if (operation.FlagTest == FlagTest.None || helper.DoFlagTest()) { registers.ProgramCounter = helper.WordOperand1; } else { SyncProgramCounter(registers, block); } break; case OpCode.JumpRelative: if (operation.FlagTest == FlagTest.None || helper.DoFlagTest()) { helper.JumpToDisplacement(); if (operation.FlagTest != FlagTest.None) { timer.Add(1, 5); } } SyncProgramCounter(registers, block); break; case OpCode.DecrementJumpRelativeIfNonZero: registers.GeneralPurposeRegisters.B--; if (registers.GeneralPurposeRegisters.B != 0) { helper.JumpToDisplacement(); timer.Add(1, 5); } SyncProgramCounter(registers, block); break; case OpCode.Call: SyncProgramCounter(registers, block); if (operation.FlagTest == FlagTest.None || helper.DoFlagTest()) { helper.PushStackPointer(); mmu.WriteWord(registers.StackPointer, registers.ProgramCounter); registers.ProgramCounter = helper.WordOperand1; if (operation.FlagTest != FlagTest.None) { timer.Add(2, 7); } } break; case OpCode.Return: if (operation.FlagTest == FlagTest.None || helper.DoFlagTest()) { registers.ProgramCounter = mmu.ReadWord(registers.StackPointer); helper.PopStackPointer(); if (operation.FlagTest != FlagTest.None) { timer.Add(2, 6); } } else { SyncProgramCounter(registers, block); } break; case OpCode.ReturnFromInterrupt: registers.ProgramCounter = mmu.ReadWord(registers.StackPointer); helper.PopStackPointer(); registers.InterruptFlipFlop1 = true; break; case OpCode.ReturnFromNonmaskableInterrupt: registers.ProgramCounter = mmu.ReadWord(registers.StackPointer); helper.PopStackPointer(); registers.InterruptFlipFlop1 = registers.InterruptFlipFlop2; break; case OpCode.Reset: SyncProgramCounter(registers, block); helper.PushStackPointer(); mmu.WriteWord(registers.StackPointer, registers.ProgramCounter); registers.ProgramCounter = helper.WordOperand1; break; case OpCode.Input: helper.Operand1 = peripherals.ReadByteFromPort(helper.Operand2, operation.Operand2 == Operand.n ? registers.AccumulatorAndFlagsRegisters.A : registers.GeneralPurposeRegisters.B); break; case OpCode.Output: peripherals.WriteByteToPort(helper.Operand2, operation.Operand2 == Operand.n ? registers.AccumulatorAndFlagsRegisters.A : registers.GeneralPurposeRegisters.B, helper.Operand1); break; case OpCode.RotateLeftWithCarry: helper.Operand1 = alu.RotateLeftWithCarry(helper.Operand1); if (operation.OpCodeMeta == OpCodeMeta.UseAlternativeFlagAffection) { registers.AccumulatorAndFlagsRegisters.Flags.Zero = false; registers.AccumulatorAndFlagsRegisters.Flags.Sign = false; } break; case OpCode.RotateLeft: helper.Operand1 = alu.RotateLeft(helper.Operand1); if (operation.OpCodeMeta == OpCodeMeta.UseAlternativeFlagAffection) { registers.AccumulatorAndFlagsRegisters.Flags.Zero = false; registers.AccumulatorAndFlagsRegisters.Flags.Sign = false; } break; case OpCode.RotateRightWithCarry: helper.Operand1 = alu.RotateRightWithCarry(helper.Operand1); if (operation.OpCodeMeta == OpCodeMeta.UseAlternativeFlagAffection) { registers.AccumulatorAndFlagsRegisters.Flags.Zero = false; registers.AccumulatorAndFlagsRegisters.Flags.Sign = false; } break; case OpCode.RotateRight: helper.Operand1 = alu.RotateRight(helper.Operand1); if (operation.OpCodeMeta == OpCodeMeta.UseAlternativeFlagAffection) { registers.AccumulatorAndFlagsRegisters.Flags.Zero = false; registers.AccumulatorAndFlagsRegisters.Flags.Sign = false; } break; case OpCode.RotateLeftDigit: { var result = alu.RotateLeftDigit(registers.AccumulatorAndFlagsRegisters.A, mmu.ReadByte(registers.GeneralPurposeRegisters.HL)); registers.AccumulatorAndFlagsRegisters.A = result.Accumulator; mmu.WriteByte(registers.GeneralPurposeRegisters.HL, result.Result); } break; case OpCode.RotateRightDigit: { var result = alu.RotateRightDigit(registers.AccumulatorAndFlagsRegisters.A, mmu.ReadByte(registers.GeneralPurposeRegisters.HL)); registers.AccumulatorAndFlagsRegisters.A = result.Accumulator; mmu.WriteByte(registers.GeneralPurposeRegisters.HL, result.Result); } break; case OpCode.ShiftLeft: helper.Operand1 = alu.ShiftLeft(helper.Operand1); break; case OpCode.ShiftLeftSet: helper.Operand1 = alu.ShiftLeftSet(helper.Operand1); break; case OpCode.ShiftRight: helper.Operand1 = alu.ShiftRight(helper.Operand1); break; case OpCode.ShiftRightLogical: helper.Operand1 = alu.ShiftRightLogical(helper.Operand1); break; case OpCode.BitTest: alu.BitTest(helper.Operand1, operation.ByteLiteral); break; case OpCode.BitSet: helper.Operand1 = alu.BitSet(helper.Operand1, operation.ByteLiteral); break; case OpCode.BitReset: helper.Operand1 = alu.BitReset(helper.Operand1, operation.ByteLiteral); break; case OpCode.TransferIncrement: helper.BlockTransfer(); break; case OpCode.TransferIncrementRepeat: helper.BlockTransferRepeat(timer); break; case OpCode.TransferDecrement: helper.BlockTransfer(true); break; case OpCode.TransferDecrementRepeat: helper.BlockTransferRepeat(timer, true); break; case OpCode.SearchIncrement: helper.BlockSearch(); break; case OpCode.SearchIncrementRepeat: helper.BlockSearchRepeat(timer); break; case OpCode.SearchDecrement: helper.BlockSearch(true); break; case OpCode.SearchDecrementRepeat: helper.BlockSearchRepeat(timer, true); break; case OpCode.InputTransferIncrement: helper.InputTransfer(); break; case OpCode.InputTransferIncrementRepeat: helper.InputTransferRepeat(timer); break; case OpCode.InputTransferDecrement: helper.InputTransfer(true); break; case OpCode.InputTransferDecrementRepeat: helper.InputTransferRepeat(timer, true); break; case OpCode.OutputTransferIncrement: helper.OutputTransfer(); break; case OpCode.OutputTransferIncrementRepeat: helper.OutputTransferRepeat(timer); break; case OpCode.OutputTransferDecrement: helper.OutputTransfer(true); break; case OpCode.OutputTransferDecrementRepeat: helper.OutputTransferRepeat(timer, true); break; case OpCode.DecimalArithmeticAdjust: registers.AccumulatorAndFlagsRegisters.A = alu.DecimalAdjust(registers.AccumulatorAndFlagsRegisters.A, _cpuMode == CpuMode.Z80); break; case OpCode.NegateOnesComplement: registers.AccumulatorAndFlagsRegisters.A = (byte)~registers.AccumulatorAndFlagsRegisters.A; registers.AccumulatorAndFlagsRegisters.Flags.SetUndocumentedFlags(registers.AccumulatorAndFlagsRegisters.A); registers.AccumulatorAndFlagsRegisters.Flags.HalfCarry = true; registers.AccumulatorAndFlagsRegisters.Flags.Subtract = true; break; case OpCode.NegateTwosComplement: registers.AccumulatorAndFlagsRegisters.A = alu.Subtract(0, registers.AccumulatorAndFlagsRegisters.A); break; case OpCode.InvertCarryFlag: registers.AccumulatorAndFlagsRegisters.Flags.SetUndocumentedFlags(registers.AccumulatorAndFlagsRegisters.A); registers.AccumulatorAndFlagsRegisters.Flags.HalfCarry = _cpuMode != CpuMode.GameBoy && registers.AccumulatorAndFlagsRegisters.Flags.Carry; registers.AccumulatorAndFlagsRegisters.Flags.Subtract = false; registers.AccumulatorAndFlagsRegisters.Flags.Carry = !registers.AccumulatorAndFlagsRegisters.Flags.Carry; break; case OpCode.SetCarryFlag: registers.AccumulatorAndFlagsRegisters.Flags.SetUndocumentedFlags(registers.AccumulatorAndFlagsRegisters.A); registers.AccumulatorAndFlagsRegisters.Flags.HalfCarry = false; registers.AccumulatorAndFlagsRegisters.Flags.Subtract = false; registers.AccumulatorAndFlagsRegisters.Flags.Carry = true; break; case OpCode.DisableInterrupts: registers.InterruptFlipFlop1 = false; registers.InterruptFlipFlop2 = false; break; case OpCode.EnableInterrupts: registers.InterruptFlipFlop1 = true; registers.InterruptFlipFlop2 = true; break; case OpCode.InterruptMode0: registers.InterruptMode = InterruptMode.InterruptMode0; break; case OpCode.InterruptMode1: registers.InterruptMode = InterruptMode.InterruptMode1; break; case OpCode.InterruptMode2: registers.InterruptMode = InterruptMode.InterruptMode2; break; case OpCode.Swap: helper.Operand1 = alu.Swap(helper.Operand1); break; case OpCode.LoadIncrement: helper.Operand1 = helper.Operand2; registers.GeneralPurposeRegisters.HL++; break; case OpCode.LoadDecrement: helper.Operand1 = helper.Operand2; registers.GeneralPurposeRegisters.HL--; break; default: throw new ArgumentOutOfRangeException(); } if (operation.OpCodeMeta == OpCodeMeta.AutoCopy) { // Autocopy for DD/FD prefix helper.Operand2 = helper.Operand1; } return(timer.GetInstructionTimings()); }
public void Execute(Instruction instruction, ICpu cpu, IMmu mmu) { var n = cpu.ReadImmediateN(); cpu.Registers.A.Value = mmu.ReadByte((ushort)(0xFF00 + n)); }
public void Execute(Instruction instruction, ICpu cpu, IMmu mmu) { cpu.Registers.H.Value = mmu.ReadByte(cpu.Registers.HL.Value); }
private void RenderBackground() { // Note: Performance hotspot SetPalette(_bgPalette, _mmu.LcdDefaultPalette); var lcdc = _mmu.ReadByte(0xFF40); // Second tile set starts at 0x8800, but since the map // stores the tile number as a signed byte (-128 - 127) // a 0 value corresponds to 0x9000. Using 0x9000 allows // the tile number to always be added to the tileSet // start address to produce the correct tile address. var tileSet = lcdc.AND(0x10) > 0 ? 0x8000 : 0x9000; var tileMap = lcdc.AND(0x08) > 0 ? 0x9C00 : 0x9800; var scrollY = _mmu.ReadByte(0xFF42); var scrollX = _mmu.ReadByte(0xFF43); var currLine = _mmu.LcdCurrentLine; var yWrap = (scrollY + currLine) % 256; var tileY = (byte)(yWrap % 8); var tileRow = yWrap / 8; var tileMapOffset = tileMap + tileRow * 32; var fbOffset = currLine * _screenWidth; for (int x = 0; x < _screenWidth; x++) { var xWrap = (scrollX + x) % 256; var tileX = (byte)(xWrap % 8); var tileColumn = xWrap / 8; var tileNumAddress = (ushort)(tileMapOffset + tileColumn); int tileNum = _mmu.ReadByte(tileNumAddress); if (tileSet == 0x9000) { tileNum = (sbyte)tileNum; } var tileAddress = (ushort)(tileSet + tileNum * 16); var paletteIndex = ReadPaletteIndex(tileAddress, tileX, tileY); FrameBuffer[fbOffset + x] = _bgPalette[paletteIndex]; } }
public byte ReadByte(long index) { return(_mmu.ReadByte((ushort)index)); }