diff --git a/Processors/6502/6502.hpp b/Processors/6502/6502.hpp index 6e511b9f8..067399fbd 100644 --- a/Processors/6502/6502.hpp +++ b/Processors/6502/6502.hpp @@ -163,14 +163,13 @@ class ProcessorBase: public ProcessorStorage { */ struct ExecutionState: public Reflection::StructImpl { ReflectableEnum(Phase, - Reset, IRQ, NMI, Instruction, Stopped, Waiting, Jammed + Instruction, Stopped, Waiting, Jammed ); /// Current executon phase, e.g. standard instruction flow or responding to an IRQ. Phase phase; - /// A count of the number of cycles since this instance of this phase last began. - /// E.g. if the phase is currently execution an instruction, this might be 0 to 7. - int cycles_into_phase; + int micro_program; + int micro_program_offset; // The following are very internal things. At the minute I // consider these 'reliable' for inter-launch state @@ -292,7 +291,8 @@ inline ProcessorBase::State::ExecutionState::ExecutionState() { if(needs_declare()) { AnnounceEnum(Phase); DeclareField(phase); - DeclareField(cycles_into_phase); + DeclareField(micro_program); + DeclareField(micro_program_offset); DeclareField(operation); DeclareField(operand); DeclareField(address); diff --git a/Processors/6502/Implementation/6502Base.cpp b/Processors/6502/Implementation/6502Base.cpp index 8b4b16afc..7e4e677e3 100644 --- a/Processors/6502/Implementation/6502Base.cpp +++ b/Processors/6502/Implementation/6502Base.cpp @@ -63,7 +63,6 @@ ProcessorBase::State ProcessorBase::get_state() { // Fill in execution state. state.execution_state.operation = operation_; state.execution_state.operand = operand_; - state.execution_state.cycles_into_phase = cycles_in_phase_; state.execution_state.address = address_.full; state.execution_state.next_address = next_address_.full; if(is_jammed_) { @@ -73,27 +72,13 @@ ProcessorBase::State ProcessorBase::get_state() { } else if(stop_is_active_) { state.execution_state.phase = State::ExecutionState::Phase::Stopped; } else { - // Test for the micro-op pointer being inside the reset, IRQ or NMI programs. - // If not then the only thing left is instruction. - auto is_in_program = [this](const MicroOp *const op) -> bool { - if(scheduled_program_counter_ < op) return false; + state.execution_state.phase = State::ExecutionState::Phase::Instruction; - const MicroOp *final_op = op; - while(*final_op != OperationMoveToNextProgram) { - ++final_op; - } - return scheduled_program_counter_ < final_op; - }; + const auto micro_offset = size_t(scheduled_program_counter_ - &operations_[0][0]); + const auto list_length = sizeof(InstructionList) / sizeof(MicroOp); - if(is_in_program(get_reset_program())) { - state.execution_state.phase = State::ExecutionState::Phase::Reset; - } else if(is_in_program(get_irq_program())) { - state.execution_state.phase = State::ExecutionState::Phase::IRQ; - } else if(is_in_program(get_nmi_program())) { - state.execution_state.phase = State::ExecutionState::Phase::NMI; - } else { - state.execution_state.phase = State::ExecutionState::Phase::Instruction; - } + state.execution_state.micro_program = int(micro_offset / list_length); + state.execution_state.micro_program_offset = int(micro_offset % list_length); } return state; diff --git a/Processors/6502/Implementation/6502Implementation.hpp b/Processors/6502/Implementation/6502Implementation.hpp index c23eb6f9a..03706c04f 100644 --- a/Processors/6502/Implementation/6502Implementation.hpp +++ b/Processors/6502/Implementation/6502Implementation.hpp @@ -13,17 +13,7 @@ */ template void Processor::run_for(const Cycles cycles) { - static const MicroOp do_branch[] = { - CycleReadFromPC, - CycleAddSignedOperandToPC, - OperationMoveToNextProgram - }; static uint8_t throwaway_target; - static const MicroOp fetch_decode_execute[] = { - CycleFetchOperation, - CycleFetchOperand, - OperationDecodeOperation - }; // These plus program below act to give the compiler permission to update these values // without touching the class storage (i.e. it explicitly says they need be completely up @@ -38,15 +28,15 @@ template void Proces if(interrupt_requests_) {\ if(interrupt_requests_ & (InterruptRequestFlags::Reset | InterruptRequestFlags::PowerOn)) {\ interrupt_requests_ &= ~InterruptRequestFlags::PowerOn;\ - scheduled_program_counter_ = get_reset_program();\ + scheduled_program_counter_ = operations_[size_t(OperationsSlot::Reset)];\ } else if(interrupt_requests_ & InterruptRequestFlags::NMI) {\ interrupt_requests_ &= ~InterruptRequestFlags::NMI;\ - scheduled_program_counter_ = get_nmi_program();\ + scheduled_program_counter_ = operations_[size_t(OperationsSlot::NMI)];\ } else if(interrupt_requests_ & InterruptRequestFlags::IRQ) {\ - scheduled_program_counter_ = get_irq_program();\ + scheduled_program_counter_ = operations_[size_t(OperationsSlot::IRQ)];\ } \ } else {\ - scheduled_program_counter_ = fetch_decode_execute;\ + scheduled_program_counter_ = operations_[size_t(OperationsSlot::FetchDecodeExecute)];\ }\ cycles_in_phase_ = 0; \ } @@ -570,7 +560,7 @@ template void Proces #define BRA(condition) \ pc_.full++; \ if(condition) { \ - scheduled_program_counter_ = do_branch; \ + scheduled_program_counter_ = operations_[size_t(OperationsSlot::DoBRA)]; \ } case OperationBPL: BRA(!(negative_result_&0x80)); continue; @@ -597,7 +587,7 @@ template void Proces // 65C02 modification to all branches: a branch that is taken but requires only a single cycle // to target its destination skips any pending interrupts. // Cf. http://forum.6502.org/viewtopic.php?f=4&t=1634 - scheduled_program_counter_ = fetch_decode_execute; + scheduled_program_counter_ = operations_[size_t(OperationsSlot::FetchDecodeExecute)]; } continue; @@ -615,22 +605,9 @@ template void Proces // and (iii) read from the corresponding zero page. const uint8_t mask = uint8_t(1 << ((operation_ >> 4)&7)); if((operand_ & mask) == ((operation_ & 0x80) ? mask : 0)) { - static const MicroOp do_branch[] = { - CycleFetchOperand, // Fetch offset. - OperationIncrementPC, - CycleFetchFromHalfUpdatedPC, - OperationAddSignedOperandToPC16, - OperationMoveToNextProgram - }; - scheduled_program_counter_ = do_branch; + scheduled_program_counter_ = operations_[size_t(OperationsSlot::DoBBRBBS)]; } else { - static const MicroOp do_not_branch[] = { - CycleFetchOperand, - OperationIncrementPC, - CycleFetchFromHalfUpdatedPC, - OperationMoveToNextProgram - }; - scheduled_program_counter_ = do_not_branch; + scheduled_program_counter_ = operations_[size_t(OperationsSlot::DoNotBBRBBS)]; } } break; @@ -734,56 +711,6 @@ void ProcessorBase::set_nmi_line(bool active) { nmi_line_is_enabled_ = active; } -inline const ProcessorStorage::MicroOp *ProcessorStorage::get_reset_program() { - static constexpr MicroOp reset[] = { - CycleFetchOperand, - CycleFetchOperand, - CycleNoWritePush, - CycleNoWritePush, - OperationRSTPickVector, - CycleNoWritePush, - OperationSetNMIRSTFlags, - CycleReadVectorLow, - CycleReadVectorHigh, - OperationMoveToNextProgram - }; - return reset; -} - -inline const ProcessorStorage::MicroOp *ProcessorStorage::get_irq_program() { - static constexpr MicroOp irq[] = { - CycleFetchOperand, - CycleFetchOperand, - CyclePushPCH, - CyclePushPCL, - OperationBRKPickVector, - OperationSetOperandFromFlags, - CyclePushOperand, - OperationSetIRQFlags, - CycleReadVectorLow, - CycleReadVectorHigh, - OperationMoveToNextProgram - }; - return irq; -} - -inline const ProcessorStorage::MicroOp *ProcessorStorage::get_nmi_program() { - static constexpr MicroOp nmi[] = { - CycleFetchOperand, - CycleFetchOperand, - CyclePushPCH, - CyclePushPCL, - OperationNMIPickVector, - OperationSetOperandFromFlags, - CyclePushOperand, - OperationSetNMIRSTFlags, - CycleReadVectorLow, - CycleReadVectorHigh, - OperationMoveToNextProgram - }; - return nmi; -} - uint8_t ProcessorStorage::get_flags() { return carry_flag_ | overflow_flag_ | (inverse_interrupt_flag_ ^ Flag::Interrupt) | (negative_result_ & 0x80) | (zero_result_ ? 0 : Flag::Zero) | Flag::Always | decimal_flag_; } diff --git a/Processors/6502/Implementation/6502Storage.cpp b/Processors/6502/Implementation/6502Storage.cpp index e2d365272..e87d6140a 100644 --- a/Processors/6502/Implementation/6502Storage.cpp +++ b/Processors/6502/Implementation/6502Storage.cpp @@ -82,7 +82,7 @@ ProcessorStorage::ProcessorStorage(Personality personality) { decimal_flag_ &= Flag::Decimal; overflow_flag_ &= Flag::Overflow; - const InstructionList operations_6502[256] = { + const InstructionList operations_6502[] = { /* 0x00 BRK */ Program(CycleIncPCPushPCH, CyclePushPCL, OperationBRKPickVector, OperationSetOperandFromFlagsWithBRKSet, CyclePushOperand, OperationSetIRQFlags, CycleReadVectorLow, CycleReadVectorHigh), /* 0x01 ORA x, ind */ IndexedIndirectRead(OperationORA), /* 0x02 JAM */ JAM, /* 0x03 ASO x, ind */ IndexedIndirectReadModifyWrite(OperationASO), @@ -218,8 +218,79 @@ ProcessorStorage::ProcessorStorage(Personality personality) { /* 0xfa NOP # */ ImpliedNop(), /* 0xfb INS abs, y */ AbsoluteYReadModifyWrite(OperationINS), /* 0xfc NOP abs, x */ AbsoluteXNop(), /* 0xfd SBC abs, x */ AbsoluteXRead(OperationSBC), /* 0xfe INC abs, x */ AbsoluteXReadModifyWrite(OperationINC), /* 0xff INS abs, x */ AbsoluteXReadModifyWrite(OperationINS), + + /* 0x100: Fetch, decode, execute. */ + { + CycleFetchOperation, + CycleFetchOperand, + OperationDecodeOperation + }, + + /* 0x101: Reset. */ + Program( + CycleFetchOperand, + CycleFetchOperand, + CycleNoWritePush, + CycleNoWritePush, + OperationRSTPickVector, + CycleNoWritePush, + OperationSetNMIRSTFlags, + CycleReadVectorLow, + CycleReadVectorHigh + ), + + /* 0x102: IRQ. */ + Program( + CycleFetchOperand, + CycleFetchOperand, + CyclePushPCH, + CyclePushPCL, + OperationBRKPickVector, + OperationSetOperandFromFlags, + CyclePushOperand, + OperationSetIRQFlags, + CycleReadVectorLow, + CycleReadVectorHigh + ), + + /* 0x103: NMI. */ + Program( + CycleFetchOperand, + CycleFetchOperand, + CyclePushPCH, + CyclePushPCL, + OperationNMIPickVector, + OperationSetOperandFromFlags, + CyclePushOperand, + OperationSetNMIRSTFlags, + CycleReadVectorLow, + CycleReadVectorHigh + ), + + /* 0x104: Do BRA. */ + Program( + CycleReadFromPC, + CycleAddSignedOperandToPC + ), + + /* 0x105: Do BBR or BBS. */ + Program( + CycleFetchOperand, // Fetch offset. + OperationIncrementPC, + CycleFetchFromHalfUpdatedPC, + OperationAddSignedOperandToPC16 + ), + + /* 0x106: Complete BBR or BBS without branching. */ + Program( + CycleFetchOperand, + OperationIncrementPC, + CycleFetchFromHalfUpdatedPC + ) }; + static_assert(sizeof(operations_6502) == sizeof(operations_)); + // Install the basic 6502 table. memcpy(operations_, operations_6502, sizeof(operations_)); diff --git a/Processors/6502/Implementation/6502Storage.hpp b/Processors/6502/Implementation/6502Storage.hpp index f41965aa7..89801bd4f 100644 --- a/Processors/6502/Implementation/6502Storage.hpp +++ b/Processors/6502/Implementation/6502Storage.hpp @@ -24,7 +24,7 @@ class ProcessorStorage { This micro-instruction set was put together in a fairly ad hoc fashion, I'm afraid, so is unlikely to be optimal. */ - enum MicroOp { + enum MicroOp: uint8_t { CycleFetchOperation, // fetches (PC) to operation_, storing PC to last_operation_pc_ before incrementing it CycleFetchOperand, // 6502: fetches from (PC) to operand_; 65C02: as 6502 unless operation_ indicates a one-cycle NOP, in which case this is a no0op OperationDecodeOperation, // schedules the microprogram associated with operation_ @@ -197,8 +197,32 @@ class ProcessorStorage { OperationScheduleStop, // puts the processor into STP mode (i.e. it'll do nothing until a reset is received) }; - using InstructionList = MicroOp[10]; - InstructionList operations_[256]; + using InstructionList = MicroOp[12]; + /// Defines the locations in operations_ of various named microprograms; the first 256 entries + /// in operations_ are mapped directly from instruction codes and therefore not named. + enum class OperationsSlot { + /// Fetches the next operation, and its operand, then schedules the corresponding set of operations_. + /// [Caveat: the 65C02 adds single-cycle NOPs; this microprogram won't fetch an operand for those]. + FetchDecodeExecute = 256, + + /// Performs the 6502's reset sequence. + Reset, + /// Performs the 6502's IRQ sequence. + IRQ, + /// Performs the 6502's NMI sequence. + NMI, + + /// Performs a branch, e.g. the entry for BCC will evaluate whether carry is clear and, if so, will jump + /// to this instruction list. + DoBRA, + + /// On a 65c02, + DoBBRBBS, + DoNotBBRBBS, + + Max + }; + InstructionList operations_[size_t(OperationsSlot::Max)]; const MicroOp *scheduled_program_counter_ = nullptr; int cycles_in_phase_ = 0; @@ -261,27 +285,6 @@ class ProcessorStorage { uint8_t irq_line_ = 0, irq_request_history_ = 0; bool nmi_line_is_enabled_ = false, set_overflow_line_is_enabled_ = false; - - /*! - Gets the program representing an RST response. - - @returns The program representing an RST response. - */ - inline const MicroOp *get_reset_program(); - - /*! - Gets the program representing an IRQ response. - - @returns The program representing an IRQ response. - */ - inline const MicroOp *get_irq_program(); - - /*! - Gets the program representing an NMI response. - - @returns The program representing an NMI response. - */ - inline const MicroOp *get_nmi_program(); }; #endif /* _502Storage_h */