Exemplo n.º 1
0
        /// <summary>
        /// Initializes a new instance of the <see cref="CpuCoreBase" /> class.
        /// </summary>
        /// <param name="registers">The registers.</param>
        /// <param name="interruptManager">The interrupt manager.</param>
        /// <param name="peripheralManager">The peripheral manager.</param>
        /// <param name="mmu">The mmu.</param>
        /// <param name="instructionTimer">The instruction timer.</param>
        /// <param name="alu">The alu.</param>
        /// <param name="opCodeDecoder">The opcode decoder.</param>
        /// <param name="instructionBlockFactory">The instruction block decoder.</param>
        /// <param name="dmaController">The dma controller.</param>
        /// <param name="messageBus">The message bus.</param>
        /// <param name="requireInstructionBlockCaching">if set to <c>true</c> [require instruction block caching].</param>
        /// <exception cref="ArgumentException">Instruction block decoder must support caching</exception>
        protected CpuCoreBase(IRegisters registers,
                              IInterruptManager interruptManager,
                              IPeripheralManager peripheralManager,
                              IMmu mmu,
                              IInstructionTimer instructionTimer,
                              IAlu alu,
                              IOpCodeDecoder opCodeDecoder,
                              IInstructionBlockFactory instructionBlockFactory,
                              IDmaController dmaController,
                              IMessageBus messageBus,
                              bool requireInstructionBlockCaching)
        {
            CoreId             = Guid.NewGuid();
            _registers         = registers;
            _interruptManager  = interruptManager;
            _peripheralManager = peripheralManager;
            _mmu = mmu;
            _instructionTimer = instructionTimer;
            _alu                     = alu;
            _opCodeDecoder           = opCodeDecoder;
            _instructionBlockFactory = instructionBlockFactory;
            _dmaController           = dmaController;
            _messageBus              = messageBus;
            messageBus.RegisterHandler(Message.PauseCpu, Pause);
            messageBus.RegisterHandler(Message.ResumeCpu, Resume);

            if (requireInstructionBlockCaching && !_instructionBlockFactory.SupportsInstructionBlockCaching)
            {
                throw new ArgumentException("Instruction block decoder must support caching");
            }
        }
Exemplo n.º 2
0
 /// <summary>
 /// Initializes a new instance of the <see cref="InterpreterHelper"/> class.
 /// </summary>
 /// <param name="operation">The operation.</param>
 /// <param name="registers">The registers.</param>
 /// <param name="mmu">The mmu.</param>
 /// <param name="alu">The alu.</param>
 /// <param name="peripheralManager">The peripheral manager.</param>
 public InterpreterHelper(IRegisters registers, IMmu mmu, IAlu alu, IPeripheralManager peripheralManager)
 {
     _registers         = registers;
     _mmu               = mmu;
     _alu               = alu;
     _peripheralManager = peripheralManager;
 }
Exemplo n.º 3
0
 public ExecutionUnit(IMemory memory, IRegisterFile registerFile, ICpuStack cpuStack, IAlu alu,
     IInputOutputDevice outputDevice, ILookupTables lookupTables)
 {
     _memory = memory;
     _registerFile = registerFile;
     _cpuStack = cpuStack;
     _alu = alu;
     _inputOutputDevice = outputDevice;
     _lookupTables = lookupTables;
 }
Exemplo n.º 4
0
        private InstructionTimings Interpret(IRegisters registers, IMmu mmu, IAlu alu, IPeripheralManager peripherals, DecodedBlock block)
        {
            var helper = new InterpreterHelper(registers, mmu, alu, peripherals);
            var result = block.Operations.Select(o => Interpret(registers, mmu, alu, peripherals, helper, o, block)).Aggregate((t0, t1) => t0 + t1);

            if (_cpuMode == CpuMode.Z80)
            {
                // Add the block length to the 7 lsb of memory refresh register.
                registers.R = (byte)((registers.R + block.Length) & 0x7f);
            }

            return(result);
        }
Exemplo n.º 5
0
        public FlagsRegister4Bit(SystemRegister id, IClock clock, IControlUnit controlUnit, IAlu alu)
        {
            this.id = id;
            Value   = 0;

            this.controlUnit = controlUnit;

            this.alu = alu;

            clock.AddConnectedComponent(this);

            updateFlagsLine = controlUnit.GetControlLine(ControlLineId.UPDATE_FLAGS);
        }
Exemplo n.º 6
0
 /// <summary>
 /// Initializes a new instance of the <see cref="CpuCore" /> class.
 /// </summary>
 /// <param name="registers">The registers.</param>
 /// <param name="interruptManager">The interrupt manager.</param>
 /// <param name="peripheralManager">The peripheral manager.</param>
 /// <param name="mmu">The mmu.</param>
 /// <param name="instructionTimer">The instruction timer.</param>
 /// <param name="alu">The alu.</param>
 /// <param name="opCodeDecoder">The opcode decoder.</param>
 /// <param name="instructionBlockFactory">The instruction block decoder.</param>
 /// <param name="dmaController">The dma controller.</param>
 /// <param name="messageBus">The message bus.</param>
 public CpuCore(IRegisters registers,
                IInterruptManager interruptManager,
                IPeripheralManager peripheralManager,
                IMmu mmu,
                IInstructionTimer instructionTimer,
                IAlu alu,
                IOpCodeDecoder opCodeDecoder,
                IInstructionBlockFactory instructionBlockFactory,
                IDmaController dmaController,
                IMessageBus messageBus)
     : base(registers, interruptManager, peripheralManager, mmu, instructionTimer, alu, opCodeDecoder, instructionBlockFactory, dmaController, messageBus, false)
 {
 }
Exemplo n.º 7
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());
        }
Exemplo n.º 8
0
 /// <summary>
 /// Executes the instruction block.
 /// </summary>
 /// <param name="registers">The registers.</param>
 /// <param name="mmu">The mmu.</param>
 /// <param name="alu">The alu.</param>
 /// <param name="peripheralManager">The peripheral manager.</param>
 /// <returns></returns>
 public InstructionTimings ExecuteInstructionBlock(IRegisters registers,
                                                   IMmu mmu,
                                                   IAlu alu,
                                                   IPeripheralManager peripheralManager) => _action(registers, mmu, alu, peripheralManager) + _staticTimings;