/// <summary> /// Constructs a new CPU instance "wired" with the specified Bus instance. /// </summary> /// <param name="bus">The Bus instance for accessing other devices.</param> public CPU(Bus bus) { this.bus = bus; State = new CPUState(); interruptHandlers = new Dictionary <InterruptType, ushort>() { { InterruptType.Reset, 0xFFFC }, { InterruptType.IRQ, 0xFFFC }, { InterruptType.NMI, 0xFFFA } }; }
/// <summary> /// Invokes the reset sequence of the processor. /// </summary> public void InvokeReset() { // Reset register values State = new CPUState(); // Read the RESET vector and set its contents to the program counter register byte[] newAddress = bus.Read(interruptHandlers[InterruptType.Reset], 2); State.PC = BitConverter.ToUInt16(newAddress, 0); lastOpcode = 0; // Update the UI UpdateEvent(null); }
/// <summary> /// Gets the final operand value for this operation. /// </summary> /// <param name="state">The CPUState instance containing register values and other state properties of the CPU.</param> /// <param name="bus">The Bus instance for accessing devices.</param> /// <returns>A single byte of data.</returns> protected byte GetOperandValue(CPUState state, Bus bus) { if (Instruction.AddressMode == AddressMode.Implied) { return(0); } if (Instruction.AddressMode == AddressMode.Accumulator) { return(state.Accumulator); } if (Instruction.AddressMode != AddressMode.Immediate) { int effectiveAddress = CalculateEffectiveAddress(state, bus); return(bus.Read(effectiveAddress)); } return(Operand[0]); }
/// <summary> /// Calculates an effective address from the operand based on the address mode of the instruction. /// </summary> /// <param name="state">The CPUState instance containing register values and other state properties of the CPU.</param> /// <param name="bus">The Bus instance for accessing devices.</param> /// <returns>The effective address in the memory.</returns> protected int CalculateEffectiveAddress(CPUState state, Bus bus) { byte[] addressBytes; switch (Instruction.AddressMode) { case AddressMode.Absolute: return(GetUInt16FromBytes(Operand)); case AddressMode.AbsoluteX: return(GetUInt16FromBytes(Operand) + state.RegisterX); case AddressMode.AbsoluteY: return(GetUInt16FromBytes(Operand) + state.RegisterY); case AddressMode.ZeroPage: return(Operand[0]); case AddressMode.ZeroPageX: return(Operand[0] + state.RegisterX); case AddressMode.ZeroPageY: return(Operand[0] + state.RegisterY); case AddressMode.Relative: return(state.PC + (sbyte)Operand[0]); // Cast the operand to signed byte (offset can be negative - used in branching instructions) case AddressMode.Indirect: addressBytes = bus.Read(GetUInt16FromBytes(Operand), 2); return(GetUInt16FromBytes(addressBytes)); case AddressMode.IndirectX: addressBytes = bus.Read(Operand[0] + state.RegisterX, 2); return(GetUInt16FromBytes(addressBytes)); case AddressMode.IndirectY: addressBytes = bus.Read(Operand[0], 2); return(GetUInt16FromBytes(addressBytes) + state.RegisterY); } return(0); }
/// <summary> /// Constructs a new UpdateUIEventArgs instance having properties initialized with values from the CPU state and the currently executed Operation. /// </summary> /// <param name="cpuState">The CPU State instance containing register values and other state properties of the CPU.</param> /// <param name="operation">The Operation instance representing the last executed instruction with it's address in the memory and operand value.</param> public UpdateUIEventArgs(CPUState cpuState, Operation operation = null) { RegisterX = cpuState.RegisterX; RegisterY = cpuState.RegisterY; Accumulator = cpuState.Accumulator; StackPointer = cpuState.SP; ProgramCounter = cpuState.PC; Status = cpuState.Status; HasCarryFlag = cpuState.HasStatusFlag(StatusFlag.Carry); HasNegativeFlag = cpuState.HasStatusFlag(StatusFlag.Negative); HasInterruptDisableFlag = cpuState.HasStatusFlag(StatusFlag.InterruptDisable); HasBreakFlag = cpuState.HasStatusFlag(StatusFlag.Break); HasDecimalFlag = cpuState.HasStatusFlag(StatusFlag.Decimal); HasOverflowFlag = cpuState.HasStatusFlag(StatusFlag.Overflow); HasZeroFlag = cpuState.HasStatusFlag(StatusFlag.Zero); if (operation != null) { OperationAddress = operation.Address; OperationOpName = operation.Instruction.OpcodeName; OperationOperand = operation.GetOperandPretty(); } }
/// <summary> /// Increments the Stack Pointer by one and pops a single byte from the Stack. /// </summary> /// <param name="state">The CPUState instance containing register values.</param> public byte StackPop(CPUState state) { state.SP++; return(Read(Simulator.STACK_ADDRESS + state.SP)); }
/// <summary> /// Pushes a single byte on the Stack and decrements the Stack Pointer by one. /// </summary> /// <param name="state">The CPUState instance containing register values.</param> /// <param name="data">The single byte to be pushed on the Stack.</param> public void StackPush(CPUState state, byte data) { Write(Simulator.STACK_ADDRESS + state.SP, data); state.SP--; }
/// <summary> /// Executes the operation, all instructions have their own logic implemented in this method. /// </summary> /// <param name="state">The CPUState instance containing register values and other state properties of the CPU.</param> /// <param name="bus">The Bus instance for accessing devices.</param> public abstract void Execute(CPUState state, Bus bus);
/// <summary> /// Performs some bitwise operations and tests the result. If the result is not equal to zero then the Overflow status flag is set, otherwise the flag is cleared. /// More about this here: http://www.righto.com/2012/12/the-6502-overflow-flag-explained.html /// </summary> /// <param name="state">The CPUState instance containing register values and other state properties of the CPU.</param> /// <param name="value">A value to be used in the bitwise operations.</param> /// <param name="operandValue">An operand value to be used in the bitwise operations.</param> /// <param name="result">A result from the CPU operation.</param> protected void CheckOverflowFlag(CPUState state, int value, byte operandValue, int result) { state.ChangeStatusFlag(StatusFlag.Overflow, ((value ^ result) & (operandValue ^ result) & 0x80) != 0); }
/// <summary> /// Compares the specified value with the operand value and sets the Carry status flag if the value is greather or equals than the operand value, otherwise the flag is cleared. /// </summary> /// <param name="state">The CPUState instance containing register values and other state properties of the CPU.</param> /// <param name="value">The value to be compared.</param> /// <param name="operandValue">The operand value to be compared.</param> protected void CheckCarryFlag(CPUState state, int value, byte operandValue) { state.ChangeStatusFlag(StatusFlag.Carry, value >= operandValue); }
/// <summary> /// Checks the highest order bit of the specified 16-bit value and sets the Negative status flag if it is not equal to zero, otherwise the flag is cleared. /// </summary> /// <param name="state">The CPUState instance containing register values and other state properties of the CPU.</param> /// <param name="value">The value to be checked.</param> protected void CheckNegativeFlag(CPUState state, int value) { state.ChangeStatusFlag(StatusFlag.Negative, (value & 0x80) != 0); }
/// <summary> /// Checks the specified value and sets the Zero status flag if it is equal to zero, otherwise the flag is cleared. /// </summary> /// <param name="state">The CPUState instance containing register values and other state properties of the CPU.</param> /// <param name="value">The value to be checked.</param> protected void CheckZeroFlag(CPUState state, int value) { state.ChangeStatusFlag(StatusFlag.Zero, value == 0); }