示例#1
0
        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);
        }
示例#2
0
        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));
        }
示例#3
0
        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;
        }
示例#4
0
        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;
        }
示例#5
0
        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);
        }
示例#6
0
        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());
        }
示例#7
0
        public void Execute(Instruction instruction, ICpu cpu, IMmu mmu)
        {
            var n = cpu.ReadImmediateN();

            cpu.Registers.A.Value = mmu.ReadByte((ushort)(0xFF00 + n));
        }
示例#8
0
 public void Execute(Instruction instruction, ICpu cpu, IMmu mmu)
 {
     cpu.Registers.H.Value = mmu.ReadByte(cpu.Registers.HL.Value);
 }
示例#9
0
        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];
            }
        }
示例#10
0
 public byte ReadByte(long index)
 {
     return(_mmu.ReadByte((ushort)index));
 }