diff --git a/Machines/AppleII/AppleII.cpp b/Machines/AppleII/AppleII.cpp index c3bca32cd..844a636d4 100644 --- a/Machines/AppleII/AppleII.cpp +++ b/Machines/AppleII/AppleII.cpp @@ -61,7 +61,7 @@ template class ConcreteMachine: uint8_t *ram_, *aux_ram_; }; - CPU::MOS6502::Processor m6502_; + CPU::MOS6502::Processor<(model == Analyser::Static::AppleII::Target::Model::EnhancedIIe) ? CPU::MOS6502::Personality::PSynertek65C02 : CPU::MOS6502::Personality::P6502, ConcreteMachine, false> m6502_; VideoBusHandler video_bus_handler_; std::unique_ptr> video_; int cycles_into_current_line_ = 0; @@ -299,7 +299,7 @@ template class ConcreteMachine: public: ConcreteMachine(const Analyser::Static::AppleII::Target &target, const ROMMachine::ROMFetcher &rom_fetcher): - m6502_((model == Analyser::Static::AppleII::Target::Model::EnhancedIIe) ? CPU::MOS6502::Personality::P65C02 : CPU::MOS6502::Personality::P6502, *this), + m6502_(*this), video_bus_handler_(ram_, aux_ram_), audio_toggle_(audio_queue_), speaker_(audio_toggle_) { diff --git a/Machines/Atari2600/Cartridges/Cartridge.hpp b/Machines/Atari2600/Cartridges/Cartridge.hpp index f5e7987d9..4276bdc7b 100644 --- a/Machines/Atari2600/Cartridges/Cartridge.hpp +++ b/Machines/Atari2600/Cartridges/Cartridge.hpp @@ -32,7 +32,7 @@ template class Cartridge: public: Cartridge(const std::vector &rom) : - m6502_(CPU::MOS6502::Personality::P6502, *this), + m6502_(*this), rom_(rom), bus_extender_(rom_.data(), rom.size()) { // The above works because bus_extender_ is declared after rom_ in the instance storage list; @@ -204,7 +204,7 @@ template class Cartridge: } protected: - CPU::MOS6502::Processor, true> m6502_; + CPU::MOS6502::Processor, true> m6502_; std::vector rom_; private: diff --git a/Machines/Commodore/1540/Implementation/C1540.cpp b/Machines/Commodore/1540/Implementation/C1540.cpp index b6cf3416e..be61e8cb8 100644 --- a/Machines/Commodore/1540/Implementation/C1540.cpp +++ b/Machines/Commodore/1540/Implementation/C1540.cpp @@ -18,7 +18,7 @@ using namespace Commodore::C1540; MachineBase::MachineBase(Personality personality, const ROMMachine::ROMFetcher &rom_fetcher) : Storage::Disk::Controller(1000000), - m6502_(CPU::MOS6502::Personality::P6502, *this), + m6502_(*this), drive_(new Storage::Disk::Drive(1000000, 300, 2)), serial_port_VIA_port_handler_(new SerialPortVIA(serial_port_VIA_)), serial_port_(new SerialPort), diff --git a/Machines/Commodore/1540/Implementation/C1540Base.hpp b/Machines/Commodore/1540/Implementation/C1540Base.hpp index 25e938371..2c4458999 100644 --- a/Machines/Commodore/1540/Implementation/C1540Base.hpp +++ b/Machines/Commodore/1540/Implementation/C1540Base.hpp @@ -143,7 +143,7 @@ class MachineBase: void set_activity_observer(Activity::Observer *observer); protected: - CPU::MOS6502::Processor m6502_; + CPU::MOS6502::Processor m6502_; std::shared_ptr drive_; uint8_t ram_[0x800]; diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 027fec716..8268d063a 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -293,7 +293,7 @@ class ConcreteMachine: public Activity::Source { public: ConcreteMachine(const Analyser::Static::Commodore::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : - m6502_(CPU::MOS6502::Personality::P6502, *this), + m6502_(*this), user_port_via_port_handler_(new UserPortVIA), keyboard_via_port_handler_(new KeyboardVIA), serial_port_(new SerialPort), @@ -703,7 +703,7 @@ class ConcreteMachine: void update_video() { mos6560_->run_for(cycles_since_mos6560_update_.flush()); } - CPU::MOS6502::Processor m6502_; + CPU::MOS6502::Processor m6502_; std::vector character_rom_; std::vector basic_rom_; diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index 4cb17c68e..295d85248 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -50,7 +50,7 @@ class ConcreteMachine: public Activity::Source { public: ConcreteMachine(const Analyser::Static::Acorn::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : - m6502_(CPU::MOS6502::Personality::P6502, *this), + m6502_(*this), sound_generator_(audio_queue_), speaker_(sound_generator_) { memset(key_states_, 0, sizeof(key_states_)); @@ -541,7 +541,7 @@ class ConcreteMachine: m6502_.set_irq_line(interrupt_status_ & 1); } - CPU::MOS6502::Processor m6502_; + CPU::MOS6502::Processor m6502_; // Things that directly constitute the memory map. uint8_t roms_[16][16384]; diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index 62ee0afbe..2d2d7e040 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -207,7 +207,7 @@ template class Co public: ConcreteMachine(const Analyser::Static::Oric::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : - m6502_(CPU::MOS6502::Personality::P6502, *this), + m6502_(*this), ay8910_(audio_queue_), speaker_(ay8910_), via_port_handler_(audio_queue_, ay8910_, speaker_, tape_player_, keyboard_), @@ -575,7 +575,7 @@ template class Co const uint16_t basic_invisible_ram_top_ = 0xffff; const uint16_t basic_visible_ram_top_ = 0xbfff; - CPU::MOS6502::Processor m6502_; + CPU::MOS6502::Processor m6502_; // RAM and ROM std::vector rom_, microdisc_rom_, colour_rom_; diff --git a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.mm b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.mm index d5a7787e9..5b6823991 100644 --- a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.mm +++ b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachine6502.mm @@ -40,7 +40,7 @@ static CPU::MOS6502::Register registerForRegister(CSTestMachine6502Register reg) if(self) { _processor = CPU::MOS6502::AllRAMProcessor::Processor( - is65C02 ? CPU::MOS6502::Personality::P65C02 : CPU::MOS6502::Personality::P6502); + is65C02 ? CPU::MOS6502::Personality::PWDC65C02 : CPU::MOS6502::Personality::P6502); } return self; diff --git a/Processors/6502/6502.hpp b/Processors/6502/6502.hpp index 00fb75429..ab27ceff8 100644 --- a/Processors/6502/6502.hpp +++ b/Processors/6502/6502.hpp @@ -37,11 +37,18 @@ enum Register { The list of 6502 variants supported by this implementation. */ enum Personality { - P6502, // the original 6502, replete with various undocumented instructions - P65C02, // the 65C02; an extended 6502 with a few extra instructions and addressing modes for existing instructions - P65SC02, // like the 65C02, but lacking bit instructions + P6502, // the original [NMOS] 6502, replete with various undocumented instructions + PNES6502, // the NES's 6502, which is like a 6502 but lacks decimal mode (though it retains the decimal flag) + PSynertek65C02, // a 6502 extended with BRA, P[H/L][X/Y], STZ, TRB, TSB and the (zp) addressing mode and a few other additions + PWDC65C02, // like the Synertek, but with BBR, BBS, RMB and SMB + PRockwell65C02, // like the WDC, but with STP and WAI }; +#define is_65c02(p) ((p) >= Personality::PSynertek65C02) +#define has_bbrbbsrmbsmb(p) ((p) >= Personality::PWDC65C02) +#define has_stpwai(p) ((p) >= Personality::PRockwell65C02) +#define has_decimal_mode(p) ((p) != Personality::PNES6502) + /* Flags as defined on the 6502; can be used to decode the result of @c get_value_of_register(Flags) or to form a value for the corresponding set. @@ -199,12 +206,12 @@ class ProcessorBase: public ProcessorStorage { can also nominate whether the processor includes support for the ready line. Declining to support the ready line can produce a minor runtime performance improvement. */ -template class Processor: public ProcessorBase { +template class Processor: public ProcessorBase { public: /*! Constructs an instance of the 6502 that will use @c bus_handler for all bus communications. */ - Processor(Personality personality, T &bus_handler) : ProcessorBase(personality), personality_(personality), bus_handler_(bus_handler) {} + Processor(T &bus_handler) : ProcessorBase(personality), bus_handler_(bus_handler) {} /*! Runs the 6502 for a supplied number of cycles. @@ -221,7 +228,6 @@ template class Processor: public ProcessorBas void set_ready_line(bool active); private: - Personality personality_; T &bus_handler_; }; diff --git a/Processors/6502/AllRAM/6502AllRAM.cpp b/Processors/6502/AllRAM/6502AllRAM.cpp index a8c0f8a6b..43e202efe 100644 --- a/Processors/6502/AllRAM/6502AllRAM.cpp +++ b/Processors/6502/AllRAM/6502AllRAM.cpp @@ -15,10 +15,10 @@ using namespace CPU::MOS6502; namespace { -class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler { +template class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler { public: - ConcreteAllRAMProcessor(Personality personality) : - mos6502_(personality, *this) { + ConcreteAllRAMProcessor() : + mos6502_(*this) { mos6502_.set_power_on(false); } @@ -63,11 +63,20 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler { } private: - CPU::MOS6502::Processor mos6502_; + CPU::MOS6502::Processor mos6502_; }; } AllRAMProcessor *AllRAMProcessor::Processor(Personality personality) { - return new ConcreteAllRAMProcessor(personality); +#define Bind(p) case p: return new ConcreteAllRAMProcessor

(); + switch(personality) { + default: + Bind(Personality::P6502) + Bind(Personality::PNES6502) + Bind(Personality::PSynertek65C02) + Bind(Personality::PWDC65C02) + Bind(Personality::PRockwell65C02) + } +#undef Bind } diff --git a/Processors/6502/Implementation/6502Implementation.hpp b/Processors/6502/Implementation/6502Implementation.hpp index 401b26e12..03637ca72 100644 --- a/Processors/6502/Implementation/6502Implementation.hpp +++ b/Processors/6502/Implementation/6502Implementation.hpp @@ -12,7 +12,7 @@ 6502.hpp, but it's implementation stuff. */ -template void Processor::run_for(const Cycles cycles) { +template void Processor::run_for(const Cycles cycles) { static const MicroOp do_branch[] = { CycleReadFromPC, CycleAddSignedOperandToPC, @@ -34,40 +34,62 @@ template void Processor:: uint8_t *busValue = bus_value_; #define checkSchedule(op) \ -if(!scheduled_program_counter_) {\ -if(interrupt_requests_) {\ - if(interrupt_requests_ & (InterruptRequestFlags::Reset | InterruptRequestFlags::PowerOn)) {\ - interrupt_requests_ &= ~InterruptRequestFlags::PowerOn;\ - scheduled_program_counter_ = get_reset_program();\ - } else if(interrupt_requests_ & InterruptRequestFlags::NMI) {\ - interrupt_requests_ &= ~InterruptRequestFlags::NMI;\ - scheduled_program_counter_ = get_nmi_program();\ - } else if(interrupt_requests_ & InterruptRequestFlags::IRQ) {\ - scheduled_program_counter_ = get_irq_program();\ - } \ -} else {\ - scheduled_program_counter_ = fetch_decode_execute;\ -}\ -op;\ -} + if(!scheduled_program_counter_) {\ + if(interrupt_requests_) {\ + if(interrupt_requests_ & (InterruptRequestFlags::Reset | InterruptRequestFlags::PowerOn)) {\ + interrupt_requests_ &= ~InterruptRequestFlags::PowerOn;\ + scheduled_program_counter_ = get_reset_program();\ + } else if(interrupt_requests_ & InterruptRequestFlags::NMI) {\ + interrupt_requests_ &= ~InterruptRequestFlags::NMI;\ + scheduled_program_counter_ = get_nmi_program();\ + } else if(interrupt_requests_ & InterruptRequestFlags::IRQ) {\ + scheduled_program_counter_ = get_irq_program();\ + } \ + } else {\ + scheduled_program_counter_ = fetch_decode_execute;\ + }\ + op;\ + } #define bus_access() \ -interrupt_requests_ = (interrupt_requests_ & ~InterruptRequestFlags::IRQ) | irq_request_history_; \ -irq_request_history_ = irq_line_ & inverse_interrupt_flag_; \ -number_of_cycles -= bus_handler_.perform_bus_operation(nextBusOperation, busAddress, busValue); \ -nextBusOperation = BusOperation::None; \ -if(number_of_cycles <= Cycles(0)) break; + interrupt_requests_ = (interrupt_requests_ & ~InterruptRequestFlags::IRQ) | irq_request_history_; \ + irq_request_history_ = irq_line_ & inverse_interrupt_flag_; \ + number_of_cycles -= bus_handler_.perform_bus_operation(nextBusOperation, busAddress, busValue); \ + nextBusOperation = BusOperation::None; \ + if(number_of_cycles <= Cycles(0)) break; checkSchedule(); Cycles number_of_cycles = cycles + cycles_left_to_run_; while(number_of_cycles > Cycles(0)) { + // Deal with a potential RDY state, if this 6502 has anything connected to ready. while(uses_ready_line && ready_is_active_ && number_of_cycles > Cycles(0)) { number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue); } - if(!uses_ready_line || !ready_is_active_) { + // Deal with a potential STP state, if this 6502 implements STP. + while(has_stpwai(personality) && stop_is_active_ && number_of_cycles > Cycles(0)) { + number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue); + if(interrupt_requests_ & InterruptRequestFlags::Reset) { + stop_is_active_ = false; + checkSchedule(); + break; + } + } + + // Deal with a potential WAI state, if this 6502 implements WAI. + while(has_stpwai(personality) && wait_is_active_ && number_of_cycles > Cycles(0)) { + number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue); + interrupt_requests_ |= (irq_line_ & inverse_interrupt_flag_); + if(interrupt_requests_ & InterruptRequestFlags::NMI || irq_line_) { + wait_is_active_ = false; + checkSchedule(); + break; + } + } + + if((!uses_ready_line || !ready_is_active_) && (!has_stpwai(personality) || (!wait_is_active_ && !stop_is_active_))) { if(nextBusOperation != BusOperation::None) { bus_access(); } @@ -98,12 +120,15 @@ if(number_of_cycles <= Cycles(0)) break; // governs everything else on the 6502: that two bytes will always // be fetched. if( - personality_ == P6502 || + !is_65c02(personality) || (operation_&7) != 3 || operation_ == 0xcb || operation_ == 0xdb ) { read_mem(operand_, pc_.full); + break; + } else { + continue; } break; @@ -140,17 +165,24 @@ if(number_of_cycles <= Cycles(0)) break; case CycleReadFromPC: throwaway_read(pc_.full); break; case OperationBRKPickVector: - // NMI can usurp BRK-vector operations - nextAddress.full = (interrupt_requests_ & InterruptRequestFlags::NMI) ? 0xfffa : 0xfffe; - interrupt_requests_ &= ~InterruptRequestFlags::NMI; // TODO: this probably doesn't happen now? + if(is_65c02(personality)) { + nextAddress.full = 0xfffe; + } else { + // NMI can usurp BRK-vector operations on the pre-C 6502s. + nextAddress.full = (interrupt_requests_ & InterruptRequestFlags::NMI) ? 0xfffa : 0xfffe; + interrupt_requests_ &= ~InterruptRequestFlags::NMI; + } continue; case OperationNMIPickVector: nextAddress.full = 0xfffa; continue; case OperationRSTPickVector: nextAddress.full = 0xfffc; continue; case CycleReadVectorLow: read_mem(pc_.bytes.low, nextAddress.full); break; case CycleReadVectorHigh: read_mem(pc_.bytes.high, nextAddress.full+1); break; - case OperationSetI: + case OperationSetIRQFlags: inverse_interrupt_flag_ = 0; - if(personality_ != P6502) decimal_flag_ = false; + if(is_65c02(personality)) decimal_flag_ = false; + continue; + case OperationSetNMIRSTFlags: + if(is_65c02(personality)) decimal_flag_ = false; continue; case CyclePullPCL: s_++; read_mem(pc_.bytes.low, s_ | 0x100); break; @@ -166,11 +198,11 @@ if(number_of_cycles <= Cycles(0)) break; case OperationSetFlagsFromX: zero_result_ = negative_result_ = x_; continue; case OperationSetFlagsFromY: zero_result_ = negative_result_ = y_; continue; - case CycleIncrementPCAndReadStack: pc_.full++; throwaway_read(s_ | 0x100); break; - case CycleReadPCLFromAddress: read_mem(pc_.bytes.low, address_.full); break; - case CycleReadPCHFromAddressLowInc: address_.bytes.low++; read_mem(pc_.bytes.high, address_.full); break; - case CycleReadPCHFromAddressFixed: if(!address_.bytes.low) address_.bytes.high++; read_mem(pc_.bytes.high, address_.full); break; - case CycleReadPCHFromAddressInc: address_.full++; read_mem(pc_.bytes.high, address_.full); break; + case CycleIncrementPCAndReadStack: pc_.full++; throwaway_read(s_ | 0x100); break; + case CycleReadPCLFromAddress: read_mem(pc_.bytes.low, address_.full); break; + case CycleReadPCHFromAddressLowInc: address_.bytes.low++; read_mem(pc_.bytes.high, address_.full); break; + case CycleReadPCHFromAddressFixed: if(!address_.bytes.low) address_.bytes.high++; read_mem(pc_.bytes.high, address_.full); break; + case CycleReadPCHFromAddressInc: address_.full++; read_mem(pc_.bytes.high, address_.full); break; case CycleReadAndIncrementPC: { uint16_t oldPC = pc_.full; @@ -178,13 +210,21 @@ if(number_of_cycles <= Cycles(0)) break; throwaway_read(oldPC); } break; -// MARK: - JAM +// MARK: - JAM, WAI, STP - case CycleScheduleJam: { + case OperationScheduleJam: { is_jammed_ = true; scheduled_program_counter_ = operations_[CPU::MOS6502::JamOpcode]; } continue; + case OperationScheduleStop: + stop_is_active_ = true; + break; + + case OperationScheduleWait: + wait_is_active_ = true; + break; + // MARK: - Bitwise case OperationORA: a_ |= operand_; negative_result_ = zero_result_ = a_; continue; @@ -197,6 +237,7 @@ if(number_of_cycles <= Cycles(0)) break; case OperationLDX: x_ = negative_result_ = zero_result_ = operand_; continue; case OperationLDY: y_ = negative_result_ = zero_result_ = operand_; continue; case OperationLAX: a_ = x_ = negative_result_ = zero_result_ = operand_; continue; + case OperationCopyOperandToA: a_ = operand_; continue; case OperationSTA: operand_ = a_; continue; case OperationSTX: operand_ = x_; continue; @@ -264,7 +305,7 @@ if(number_of_cycles <= Cycles(0)) break; case OperationINS: operand_++; // deliberate fallthrough case OperationSBC: - if(decimal_flag_) { + if(decimal_flag_ && has_decimal_mode(personality)) { const uint16_t notCarry = carry_flag_ ^ 0x1; const uint16_t decimalResult = static_cast(a_) - static_cast(operand_) - notCarry; uint16_t temp16; @@ -283,7 +324,7 @@ if(number_of_cycles <= Cycles(0)) break; carry_flag_ = (temp16 > 0xff) ? 0 : Flag::Carry; a_ = static_cast(temp16); - if(personality_ != P6502) { + if(is_65c02(personality)) { negative_result_ = zero_result_ = a_; read_mem(operand_, address_.full); break; @@ -295,7 +336,7 @@ if(number_of_cycles <= Cycles(0)) break; // deliberate fallthrough case OperationADC: - if(decimal_flag_) { + if(decimal_flag_ && has_decimal_mode(personality)) { const uint16_t decimalResult = static_cast(a_) + static_cast(operand_) + static_cast(carry_flag_); uint8_t low_nibble = (a_ & 0xf) + (operand_ & 0xf) + carry_flag_; @@ -309,7 +350,7 @@ if(number_of_cycles <= Cycles(0)) break; a_ = static_cast(result); zero_result_ = static_cast(decimalResult); - if(personality_ != P6502) { + if(is_65c02(personality)) { negative_result_ = zero_result_ = a_; read_mem(operand_, address_.full); break; @@ -425,32 +466,42 @@ if(number_of_cycles <= Cycles(0)) break; // MARK: - Addressing Mode Work +#define page_crossing_stall_read() \ + if(is_65c02(personality)) { \ + throwaway_read(pc_.full - 1); \ + } else { \ + throwaway_read(address_.full); \ + } + case CycleAddXToAddressLow: nextAddress.full = address_.full + x_; address_.bytes.low = nextAddress.bytes.low; - if(address_.bytes.high != nextAddress.bytes.high) { - throwaway_read(address_.full); + if(address_.bytes.high != nextAddress.bytes.high) { + page_crossing_stall_read(); break; } continue; case CycleAddXToAddressLowRead: nextAddress.full = address_.full + x_; address_.bytes.low = nextAddress.bytes.low; - throwaway_read(address_.full); + page_crossing_stall_read(); break; case CycleAddYToAddressLow: nextAddress.full = address_.full + y_; address_.bytes.low = nextAddress.bytes.low; if(address_.bytes.high != nextAddress.bytes.high) { - throwaway_read(address_.full); + page_crossing_stall_read(); break; } continue; case CycleAddYToAddressLowRead: nextAddress.full = address_.full + y_; address_.bytes.low = nextAddress.bytes.low; - throwaway_read(address_.full); + page_crossing_stall_read(); break; + +#undef page_crossing_stall_read + case OperationCorrectAddressHigh: address_.full = nextAddress.full; continue; @@ -509,12 +560,14 @@ if(number_of_cycles <= Cycles(0)) break; case OperationIncrementPC: pc_.full++; continue; case CycleFetchOperandFromAddress: read_mem(operand_, address_.full); break; case CycleWriteOperandToAddress: write_mem(operand_, address_.full); break; - case OperationCopyOperandFromA: operand_ = a_; continue; - case OperationCopyOperandToA: a_ = operand_; continue; // MARK: - Branching -#define BRA(condition) pc_.full++; if(condition) scheduled_program_counter_ = do_branch +#define BRA(condition) \ + pc_.full++; \ + if(condition) { \ + scheduled_program_counter_ = do_branch; \ + } case OperationBPL: BRA(!(negative_result_&0x80)); continue; case OperationBMI: BRA(negative_result_&0x80); continue; @@ -526,6 +579,8 @@ if(number_of_cycles <= Cycles(0)) break; case OperationBEQ: BRA(!zero_result_); continue; case OperationBRA: BRA(true); continue; +#undef BRA + case CycleAddSignedOperandToPC: nextAddress.full = static_cast(pc_.full + (int8_t)operand_); pc_.bytes.low = nextAddress.bytes.low; @@ -534,6 +589,11 @@ if(number_of_cycles <= Cycles(0)) break; pc_.full = nextAddress.full; throwaway_read(halfUpdatedPc); break; + } else if(is_65c02(personality)) { + // 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; } continue; @@ -570,8 +630,6 @@ if(number_of_cycles <= Cycles(0)) break; } } break; -#undef BRA - // MARK: - Transfers case OperationTXA: zero_result_ = negative_result_ = a_ = x_; continue; @@ -611,7 +669,10 @@ if(number_of_cycles <= Cycles(0)) break; continue; } - if(uses_ready_line && ready_line_is_enabled_ && isReadOperation(nextBusOperation)) { + if(has_stpwai(personality) && (stop_is_active_ || wait_is_active_)) { + break; + } + if(uses_ready_line && ready_line_is_enabled_ && (is_65c02(personality) || isReadOperation(nextBusOperation))) { ready_is_active_ = true; break; } @@ -629,7 +690,7 @@ if(number_of_cycles <= Cycles(0)) break; bus_handler_.flush(); } -template void Processor::set_ready_line(bool active) { +template void Processor::set_ready_line(bool active) { assert(uses_ready_line); if(active) { ready_line_is_enabled_ = true; @@ -677,6 +738,7 @@ inline const ProcessorStorage::MicroOp *ProcessorStorage::get_reset_program() { CycleNoWritePush, OperationRSTPickVector, CycleNoWritePush, + OperationSetNMIRSTFlags, CycleReadVectorLow, CycleReadVectorHigh, OperationMoveToNextProgram @@ -693,7 +755,7 @@ inline const ProcessorStorage::MicroOp *ProcessorStorage::get_irq_program() { OperationBRKPickVector, OperationSetOperandFromFlags, CyclePushOperand, - OperationSetI, + OperationSetIRQFlags, CycleReadVectorLow, CycleReadVectorHigh, OperationMoveToNextProgram @@ -710,6 +772,7 @@ inline const ProcessorStorage::MicroOp *ProcessorStorage::get_nmi_program() { OperationNMIPickVector, OperationSetOperandFromFlags, CyclePushOperand, + OperationSetNMIRSTFlags, CycleReadVectorLow, CycleReadVectorHigh, OperationMoveToNextProgram diff --git a/Processors/6502/Implementation/6502Storage.cpp b/Processors/6502/Implementation/6502Storage.cpp index d8fae7c4f..e2d365272 100644 --- a/Processors/6502/Implementation/6502Storage.cpp +++ b/Processors/6502/Implementation/6502Storage.cpp @@ -29,7 +29,7 @@ using namespace CPU::MOS6502; #define Read(...) CycleFetchOperandFromAddress, __VA_ARGS__ #define Write(...) __VA_ARGS__, CycleWriteOperandToAddress -#define ReadModifyWrite(...) CycleFetchOperandFromAddress, (personality == P6502) ? CycleWriteOperandToAddress : CycleFetchOperandFromAddress, __VA_ARGS__, CycleWriteOperandToAddress +#define ReadModifyWrite(...) CycleFetchOperandFromAddress, is_65c02(personality) ? CycleFetchOperandFromAddress : CycleWriteOperandToAddress, __VA_ARGS__, CycleWriteOperandToAddress #define AbsoluteRead(op) Program(Absolute, Read(op)) #define AbsoluteXRead(op) Program(AbsoluteXr, Read(op)) @@ -60,8 +60,11 @@ using namespace CPU::MOS6502; #define IndexedIndirectReadModifyWrite(...) Program(IndexedIndirect, ReadModifyWrite(__VA_ARGS__)) #define IndirectIndexedReadModifyWrite(...) Program(IndirectIndexed, ReadModifyWrite(__VA_ARGS__)) +#define FastAbsoluteXReadModifyWrite(...) Program(AbsoluteXr, ReadModifyWrite(__VA_ARGS__)) +#define FastAbsoluteYReadModifyWrite(...) Program(AbsoluteYr, ReadModifyWrite(__VA_ARGS__)) + #define Immediate(op) Program(OperationIncrementPC, op) -#define Implied(op) Program(OperationCopyOperandFromA, op, OperationCopyOperandToA) +#define Implied(op) Program(OperationSTA, op, OperationCopyOperandToA) #define ZeroNop() Program(Zero, CycleFetchOperandFromAddress) #define ZeroXNop() Program(ZeroX, CycleFetchOperandFromAddress) @@ -70,7 +73,7 @@ using namespace CPU::MOS6502; #define ImpliedNop() {OperationMoveToNextProgram} #define ImmediateNop() Program(OperationIncrementPC) -#define JAM {CycleFetchOperand, CycleScheduleJam} +#define JAM {CycleFetchOperand, OperationScheduleJam} ProcessorStorage::ProcessorStorage(Personality personality) { // only the interrupt flag is defined upon reset but get_flags isn't going to @@ -80,7 +83,7 @@ ProcessorStorage::ProcessorStorage(Personality personality) { overflow_flag_ &= Flag::Overflow; const InstructionList operations_6502[256] = { - /* 0x00 BRK */ Program(CycleIncPCPushPCH, CyclePushPCL, OperationBRKPickVector, OperationSetOperandFromFlagsWithBRKSet, CyclePushOperand, OperationSetI, CycleReadVectorLow, CycleReadVectorHigh), + /* 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), /* 0x04 NOP zpg */ ZeroNop(), /* 0x05 ORA zpg */ ZeroRead(OperationORA), @@ -221,11 +224,37 @@ ProcessorStorage::ProcessorStorage(Personality personality) { memcpy(operations_, operations_6502, sizeof(operations_)); // Patch the table according to the chip's personality. + // + // The 6502 and NES 6502 both have the same mapping of operation codes to actions + // (respect for the decimal mode flag aside); included in that are 'unofficial' + // operations — spots that are not formally defined to do anything but which the + // processor makes no particular effort to react to in a well-defined way. + // + // The 65C02s add some official instructions but also ensure that all of the + // undefined ones act as no-ops of various addressing modes. + // + // So the branch below has to add a bunch of new actions but also removes various + // others by dint of replacing them with NOPs. + // + // Those 6502 opcodes that need redefining, one way or the other, are: + // + // 0x02, 0x03, 0x04, 0x07, 0x0b, 0x0c, 0x0f, 0x12, 0x13, 0x14, 0x17, 0x1a, 0x1b, 0x1c, 0x1f, + // 0x22, 0x23, 0x27, 0x2b, 0x2f, 0x32, 0x33, 0x34, 0x37, 0x3a, 0x3b, 0x3c, 0x3f, + // 0x42, 0x43, 0x47, 0x4b, 0x4f, 0x52, 0x53, 0x57, 0x5a, 0x5b, 0x5f, + // 0x62, 0x63, 0x64, 0x67, 0x6b, 0x6f, 0x72, 0x73, 0x74, 0x77, 0x7b, 0x7a, 0x7c, 0x7f, + // 0x80, 0x82, 0x83, 0x87, 0x89, 0x8b, 0x8f, 0x92, 0x93, 0x97, 0x9b, 0x9e, 0x9c, 0x9f, + // 0xa3, 0xa7, 0xab, 0xaf, 0xb2, 0xb3, 0xb7, 0xbb, 0xbf, + // 0xc3, 0xc7, 0xcb, 0xcf, 0xd2, 0xd3, 0xd7, 0xda, 0xdb, 0xdf, + // 0xe3, 0xe7, 0xeb, 0xef, 0xf2, 0xf3, 0xf7, 0xfa, 0xfb, 0xff + // + // ... not including those that aren't defined on the 6502 but perform NOPs exactly like they + // would on a 65C02. + #define Install(location, instructions) {\ const InstructionList code = instructions; \ memcpy(&operations_[location], code, sizeof(InstructionList)); \ } - if(personality != P6502) { + if(is_65c02(personality)) { // Add P[L/H][X/Y]. Install(0x5a, Program(CyclePushY)); Install(0xda, Program(CyclePushX)); @@ -235,19 +264,6 @@ ProcessorStorage::ProcessorStorage(Personality personality) { // Add BRA. Install(0x80, Program(OperationBRA)); - // Add BBS and BBR. These take five cycles. My guessed breakdown is: - // 1. read opcode - // 2. read operand - // 3. read zero page - // 4. read second operand - // 5. read from PC without top byte fixed yet - // ... with the caveat that (3) and (4) could be the other way around. - for(int location = 0x0f; location <= 0xff; location += 0x10) { - Install(location, Program(OperationLoadAddressZeroPage, CycleFetchOperandFromAddress, OperationBBRBBS)); - } - - // Add NOPs. - // The 1-byte, 1-cycle (!) NOPs. for(int c = 0x03; c <= 0xf3; c += 0x10) { Install(c, ImpliedNop()); @@ -304,52 +320,68 @@ ProcessorStorage::ProcessorStorage(Personality personality) { Install(0x14, ZeroReadModifyWrite(OperationTRB)); Install(0x1c, AbsoluteReadModifyWrite(OperationTRB)); - // Add RMB and SMB. - for(int c = 0x07; c <= 0x77; c += 0x10) { - Install(c, ZeroReadModifyWrite(OperationRMB)); + // Install faster ASL, LSR, ROL, ROR abs,[x/y]. Note: INC, DEC deliberately not improved. + Install(0x1e, FastAbsoluteXReadModifyWrite(OperationASL)); + Install(0x1f, FastAbsoluteXReadModifyWrite(OperationASO)); + Install(0x3e, FastAbsoluteXReadModifyWrite(OperationROL)); + Install(0x3f, FastAbsoluteXReadModifyWrite(OperationRLA)); + Install(0x5e, FastAbsoluteXReadModifyWrite(OperationLSR)); + Install(0x5f, FastAbsoluteXReadModifyWrite(OperationLSE)); + Install(0x7e, FastAbsoluteXReadModifyWrite(OperationROR)); + Install(0x7f, FastAbsoluteXReadModifyWrite(OperationRRA, OperationADC)); + + // Outstanding: + // 0x07, 0x0f, 0x17, 0x1f, + // 0x27, 0x2f, 0x37, 0x3f, + // 0x47, 0x4f, 0x57, 0x5f, + // 0x67, 0x6f, 0x77, 0x7f, + // 0x87, 0x8f, 0x97, 0x9f, + // 0xa7, 0xaf, 0xb7, 0xbf, + // 0xc7, 0xcb, 0xcf, 0xd7, 0xdb, 0xdf, + // 0xe7, 0xef, 0xf7, 0xff + if(has_bbrbbsrmbsmb(personality)) { + // Add BBS and BBR. These take five cycles. My guessed breakdown is: + // 1. read opcode + // 2. read operand + // 3. read zero page + // 4. read second operand + // 5. read from PC without top byte fixed yet + // ... with the caveat that (3) and (4) could be the other way around. + for(int location = 0x0f; location <= 0xff; location += 0x10) { + Install(location, Program(OperationLoadAddressZeroPage, CycleFetchOperandFromAddress, OperationBBRBBS)); + } + + // Add RMB and SMB. + for(int c = 0x07; c <= 0x77; c += 0x10) { + Install(c, ZeroReadModifyWrite(OperationRMB)); + } + for(int c = 0x87; c <= 0xf7; c += 0x10) { + Install(c, ZeroReadModifyWrite(OperationSMB)); + } + } else { + for(int location = 0x0f; location <= 0xef; location += 0x20) { + Install(location, AbsoluteNop()); + } + for(int location = 0x1f; location <= 0xff; location += 0x20) { + Install(location, AbsoluteXNop()); + } + for(int c = 0x07; c <= 0xe7; c += 0x20) { + Install(c, ZeroNop()); + } + for(int c = 0x17; c <= 0xf7; c += 0x20) { + Install(c, ZeroXNop()); + } } - for(int c = 0x87; c <= 0xf7; c += 0x10) { - Install(c, ZeroReadModifyWrite(OperationSMB)); + + // Outstanding: + // 0xcb, 0xdb, + if(has_stpwai(personality)) { + Install(0xcb, Program(OperationScheduleWait)); + Install(0xdb, Program(OperationScheduleStop)); + } else { + Install(0xcb, ImpliedNop()); + Install(0xdb, ZeroXNop()); } } #undef Install } - -#undef Program -#undef Absolute -#undef AbsoluteX -#undef AbsoluteY -#undef Zero -#undef ZeroX -#undef ZeroY -#undef IndexedIndirect -#undef IndirectIndexed -#undef Read -#undef Write -#undef ReadModifyWrite -#undef AbsoluteRead -#undef AbsoluteXRead -#undef AbsoluteYRead -#undef ZeroRead -#undef ZeroXRead -#undef ZeroYRead -#undef IndexedIndirectRead -#undef IndirectIndexedRead -#undef AbsoluteWrite -#undef AbsoluteXWrite -#undef AbsoluteYWrite -#undef ZeroWrite -#undef ZeroXWrite -#undef ZeroYWrite -#undef IndexedIndirectWrite -#undef IndirectIndexedWrite -#undef AbsoluteReadModifyWrite -#undef AbsoluteXReadModifyWrite -#undef AbsoluteYReadModifyWrite -#undef ZeroReadModifyWrite -#undef ZeroXReadModifyWrite -#undef ZeroYReadModify -#undef IndexedIndirectReadModify -#undef IndirectIndexedReadModify -#undef Immediate -#undef Implied diff --git a/Processors/6502/Implementation/6502Storage.hpp b/Processors/6502/Implementation/6502Storage.hpp index 85d4d1f3d..fd7b54b5f 100644 --- a/Processors/6502/Implementation/6502Storage.hpp +++ b/Processors/6502/Implementation/6502Storage.hpp @@ -17,68 +17,184 @@ class ProcessorStorage { protected: ProcessorStorage(Personality); - /* + /*! This emulation functions by decomposing instructions into micro programs, consisting of the micro operations - as per the enum below. Each micro op takes at most one cycle. By convention, those called CycleX take a cycle + defined by MicroOp. Each micro op takes at most one cycle. By convention, those called CycleX take a cycle to perform whereas those called OperationX occur for free (so, in effect, their cost is loaded onto the next cycle). + + This micro-instruction set was put together in a fairly ad hoc fashion, I'm afraid, so is unlikely to be optimal. */ enum MicroOp { - CycleFetchOperation, CycleFetchOperand, OperationDecodeOperation, CycleIncPCPushPCH, - CyclePushPCH, CyclePushPCL, CyclePushA, CyclePushOperand, - CyclePushX, CyclePushY, OperationSetI, + 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_ + OperationMoveToNextProgram, // either schedules the next fetch-decode-execute or an interrupt response if a request has been pending for at least one cycle - OperationBRKPickVector, OperationNMIPickVector, OperationRSTPickVector, - CycleReadVectorLow, CycleReadVectorHigh, + CycleIncPCPushPCH, // increments the PC and pushes PC.h to the stack + CyclePushPCL, // pushes PC.l to the stack + CyclePushPCH, // pushes PC.h to the stack + CyclePushA, // pushes A to the stack + CyclePushX, // pushes X to the stack + CyclePushY, // pushes Y to the stack + CyclePushOperand, // pushes operand_ to the stack - CycleReadFromS, CycleReadFromPC, - CyclePullOperand, CyclePullPCL, CyclePullPCH, CyclePullA, - CyclePullX, CyclePullY, - CycleNoWritePush, - CycleReadAndIncrementPC, CycleIncrementPCAndReadStack, CycleIncrementPCReadPCHLoadPCL, CycleReadPCHLoadPCL, - CycleReadAddressHLoadAddressL, + OperationSetIRQFlags, // 6502: sets I; 65C02: sets I and resets D + OperationSetNMIRSTFlags, // 6502: no-op. 65C02: resets D - CycleReadPCLFromAddress, CycleReadPCHFromAddressLowInc, CycleReadPCHFromAddressFixed, CycleReadPCHFromAddressInc, + OperationBRKPickVector, // 65C02: sets next_address_ to the BRK vector location; 6502: as 65C02 if no NMI is pending; otherwise sets next_address_ to the NMI address and resets the internal NMI-pending flag + OperationNMIPickVector, // sets next_address_ to the NMI vector + OperationRSTPickVector, // sets next_address_ to the RST vector + CycleReadVectorLow, // reads PC.l from next_address_ + CycleReadVectorHigh, // reads PC.h from (next_address_+1) - CycleLoadAddressAbsolute, - OperationLoadAddressZeroPage, CycleLoadAddessZeroX, CycleLoadAddessZeroY, CycleAddXToAddressLow, - CycleAddYToAddressLow, CycleAddXToAddressLowRead, OperationCorrectAddressHigh, CycleAddYToAddressLowRead, - OperationMoveToNextProgram, OperationIncrementPC, - CycleFetchOperandFromAddress, CycleWriteOperandToAddress, OperationCopyOperandFromA, OperationCopyOperandToA, - CycleIncrementPCFetchAddressLowFromOperand, CycleAddXToOperandFetchAddressLow, CycleIncrementOperandFetchAddressHigh, OperationDecrementOperand, - CycleFetchAddressLowFromOperand, - OperationIncrementOperand, OperationORA, OperationAND, OperationEOR, - OperationINS, OperationADC, OperationSBC, OperationLDA, - OperationLDX, OperationLDY, OperationLAX, OperationSTA, - OperationSTX, OperationSTY, OperationSTZ, - OperationSAX, OperationSHA, - OperationSHX, OperationSHY, OperationSHS, OperationCMP, - OperationCPX, OperationCPY, OperationBIT, OperationBITNoNV, - OperationASL, OperationRMB, OperationSMB, - OperationASO, OperationROL, OperationRLA, OperationLSR, - OperationLSE, OperationASR, OperationROR, OperationRRA, - OperationCLC, OperationCLI, OperationCLV, OperationCLD, - OperationSEC, OperationSEI, OperationSED, - OperationTRB, OperationTSB, + CycleReadFromS, // performs a read from the stack pointer, throwing the result away + CycleReadFromPC, // performs a read from the program counter, throwing the result away - OperationINC, OperationDEC, OperationINX, OperationDEX, - OperationINY, OperationDEY, OperationINA, OperationDEA, + CyclePullPCL, // pulls PC.l from the stack + CyclePullPCH, // pulls PC.h from the stack + CyclePullA, // pulls A from the stack + CyclePullX, // pulls X from the stack + CyclePullY, // pulls Y from the stack + CyclePullOperand, // pulls operand_ from the stack - OperationBPL, OperationBMI, OperationBVC, OperationBVS, - OperationBCC, OperationBCS, OperationBNE, OperationBEQ, - OperationBRA, OperationBBRBBS, + CycleNoWritePush, // decrements S as though it were a push, but reads from the new stack address instead of writing + CycleReadAndIncrementPC, // reads from the PC, throwing away the result, and increments the PC + CycleIncrementPCAndReadStack, // increments the PC and reads from the stack pointer, throwing away the result + CycleIncrementPCReadPCHLoadPCL, // increments the PC, schedules a read of PC.h from the post-incremented PC, then copies operand_ to PC.l + CycleReadPCHLoadPCL, // schedules a read of PC.h from the post-incremented PC, then copies operand_ to PC.l + CycleReadAddressHLoadAddressL, // increments the PC; copies operand_ to address_.l; reads address_.h from the new PC - OperationTXA, OperationTYA, OperationTXS, OperationTAY, - OperationTAX, OperationTSX, + CycleReadPCLFromAddress, // reads PC.l from address_ + CycleReadPCHFromAddressLowInc, // increments address_.l and reads PC.h from address_ + CycleReadPCHFromAddressFixed, // if address_.l is 0, increments address_.h; and reads PC.h from address_ + CycleReadPCHFromAddressInc, // increments address_ and reads PC.h from it - OperationARR, OperationSBX, OperationLXA, OperationANE, - OperationANC, OperationLAS, + CycleLoadAddressAbsolute, // copies operand_ to address_.l, increments the PC, reads address_.h from PC, increments the PC again + OperationLoadAddressZeroPage, // copies operand_ to address_ and increments the PC + CycleLoadAddessZeroX, // copies (operand_+x)&0xff to address_, increments the PC, and reads from operand_, throwing away the result + CycleLoadAddessZeroY, // copies (operand_+y)&0xff to address_, increments the PC, and reads from operand_, throwing away the result - CycleFetchFromHalfUpdatedPC, CycleAddSignedOperandToPC, OperationAddSignedOperandToPC16, + CycleAddXToAddressLow, // calculates address_ + x and stores it to next_address_; copies next_address_.l back to address_.l; 6502: if address_ now does not equal next_address_, schedules a throwaway read from address_; 65C02: schedules a throaway read from PC-1 + CycleAddYToAddressLow, // calculates address_ + y and stores it to next_address_; copies next_address_.l back to address_.l; 6502: if address_ now does not equal next_address_, schedules a throwaway read from address_; 65C02: schedules a throaway read from PC-1 + CycleAddXToAddressLowRead, // calculates address_ + x and stores it to next_address; copies next_address.l back to address_.l; 6502: schedules a throwaway read from address_; 65C02: schedules a throaway read from PC-1 + CycleAddYToAddressLowRead, // calculates address_ + y and stores it to next_address; copies next_address.l back to address_.l; 6502: schedules a throwaway read from address_; 65C02: schedules a throaway read from PC-1 + OperationCorrectAddressHigh, // copies next_address_ to address_ - OperationSetFlagsFromOperand, OperationSetOperandFromFlagsWithBRKSet, - OperationSetOperandFromFlags, - OperationSetFlagsFromA, OperationSetFlagsFromX, OperationSetFlagsFromY, - CycleScheduleJam + OperationIncrementPC, // increments the PC + CycleFetchOperandFromAddress, // fetches operand_ from address_ + CycleWriteOperandToAddress, // writes operand_ to address_ + + CycleIncrementPCFetchAddressLowFromOperand, // increments the PC and loads address_.l from (operand_) + CycleAddXToOperandFetchAddressLow, // adds x [in]to operand_, producing an 8-bit result, and reads address_.l from (operand_) + CycleIncrementOperandFetchAddressHigh, // increments operand_, producing an 8-bit result, and reads address_.h from (operand_) + OperationDecrementOperand, // decrements operand_ + OperationIncrementOperand, // increments operand_ + CycleFetchAddressLowFromOperand, // reads address_.l from (operand_) + + OperationORA, // ORs operand_ into a, setting the negative and zero flags + OperationAND, // ANDs operand_ into a, setting the negative and zero flags + OperationEOR, // EORs operand_ into a, setting the negative and zero flags + + OperationINS, // increments operand_, then performs an SBC of operand_ from a + OperationADC, // performs an ADC of operand_ into a_; if this is a 65C02 and decimal mode is set, performs an extra read to operand_ from address_ + OperationSBC, // performs an SBC of operand_ from a_; if this is a 65C02 and decimal mode is set, performs an extra read to operand_ from address_ + + OperationCMP, // CMPs a and operand_, setting negative, zero and carry flags + OperationCPX, // CMPs x and operand_, setting negative, zero and carry flags + OperationCPY, // CMPs y and operand_, setting negative, zero and carry flags + OperationBIT, // sets the zero, negative and overflow flags as per a BIT of operand_ against a + OperationBITNoNV, // sets the zero flag as per a BIT of operand_ against a + + OperationLDA, // loads a with operand_, setting the negative and zero flags + OperationLDX, // loads x with operand_, setting the negative and zero flags + OperationLDY, // loads y with operand_, setting the negative and zero flags + OperationLAX, // loads a and x with operand_, setting the negative and zero flags + OperationCopyOperandToA, // sets a_ = operand_, not setting any flags + + OperationSTA, // loads operand_ with a + OperationSTX, // loads operand_ with x + OperationSTY, // loads operand_ with y + OperationSTZ, // loads operand_ with 0 + OperationSAX, // loads operand_ with a & x + OperationSHA, // loads operand_ with a & x & (address.h+1) + OperationSHX, // loads operand_ with x & (address.h+1) + OperationSHY, // loads operand_ with y & (address.h+1) + OperationSHS, // loads s with a & x, then loads operand_ with s & (address.h+1) + + OperationASL, // shifts operand_ left, moving the top bit into carry and setting the negative and zero flags + OperationASO, // performs an ASL of operand and ORs it into a + OperationROL, // performs a ROL of operand_ + OperationRLA, // performs a ROL of operand_ and ANDs it into a + OperationLSR, // shifts operand_ right, setting carry, negative and zero flags + OperationLSE, // performs an LSR and EORs the result into a + OperationASR, // ANDs operand_ into a, then performs an LSR + OperationROR, // performs a ROR of operand_, setting carry, negative and zero flags + OperationRRA, // performs a ROR of operand_ but sets only the carry flag + + OperationCLC, // resets the carry flag + OperationCLI, // resets I + OperationCLV, // resets the overflow flag + OperationCLD, // resets the decimal flag + OperationSEC, // sets the carry flag + OperationSEI, // sets I + OperationSED, // sets the decimal flag + + OperationRMB, // resets the bit in operand_ implied by operatiopn_ + OperationSMB, // sets the bit in operand_ implied by operatiopn_ + OperationTRB, // sets zero according to operand_ & a, then resets any bits in operand_ that are set in a + OperationTSB, // sets zero according to operand_ & a, then sets any bits in operand_ that are set in a + + OperationINC, // increments operand_, setting the negative and zero flags + OperationDEC, // decrements operand_, setting the negative and zero flags + OperationINX, // increments x, setting the negative and zero flags + OperationDEX, // decrements x, setting the negative and zero flags + OperationINY, // increments y, setting the negative and zero flags + OperationDEY, // decrements y, setting the negative and zero flags + OperationINA, // increments a, setting the negative and zero flags + OperationDEA, // decrements a, setting the negative and zero flags + + OperationBPL, // schedules the branch program if the negative flag is clear + OperationBMI, // schedules the branch program if the negative flag is set + OperationBVC, // schedules the branch program if the overflow flag is clear + OperationBVS, // schedules the branch program if the overflow flag is set + OperationBCC, // schedules the branch program if the carry flag is clear + OperationBCS, // schedules the branch program if the carry flag is set + OperationBNE, // schedules the branch program if the zero flag is clear + OperationBEQ, // schedules the branch program if the zero flag is set; 65C02: otherwise jumps straight into a fetch-decode-execute without considering whether to take an interrupt + OperationBRA, // schedules the branch program + + OperationBBRBBS, // inspecting the operation_, if the appropriate bit of operand_ is set or clear schedules a program to read and act upon the second operand; otherwise schedule a program to read and discard it + + OperationTXA, // copies x to a, setting the zero and negative flags + OperationTYA, // copies y to a, setting the zero and negative flags + OperationTXS, // copies x to s + OperationTAY, // copies a to y, setting the zero and negative flags + OperationTAX, // copies a to x, setting the zero and negative flags + OperationTSX, // copies s to x, setting the zero and negative flags + + /* The following are amongst the 6502's undocumented (/unintended) operations */ + OperationARR, // performs a mixture of ANDing operand_ into a, and shifting the result right + OperationSBX, // performs a mixture of an SBC of x&a and operand_, mutating x + OperationLXA, // loads a and x with (a | 0xee) & operand, setting the negative and zero flags + OperationANE, // loads a_ with (a | 0xee) & operand & x, setting the negative and zero flags + OperationANC, // ANDs operand_ into a, setting the negative and zero flags, and loading carry as if the result were shifted right + OperationLAS, // loads a, x and s with s & operand, setting the negative and zero flags + + CycleFetchFromHalfUpdatedPC, // performs a throwaway read from (PC + (signed)operand).l combined with PC.h + CycleAddSignedOperandToPC, // sets next_address to PC + (signed)operand. If the high byte of next_address differs from the PC, schedules a throwaway read from the half-updated PC. 65C02 specific: if the top two bytes are the same, proceeds directly to fetch-decode-execute, ignoring any pending interrupts. + OperationAddSignedOperandToPC16, // adds (signed)operand into the PC + + OperationSetFlagsFromOperand, // sets all flags based on operand_ + OperationSetOperandFromFlagsWithBRKSet, // sets operand_ to the value of all flags, with the break flag set + OperationSetOperandFromFlags, // sets operand_ to the value of all flags + + OperationSetFlagsFromA, // sets the zero and negative flags based on the value of a + OperationSetFlagsFromX, // sets the zero and negative flags based on the value of x + OperationSetFlagsFromY, // sets the zero and negative flags based on the value of y + + OperationScheduleJam, // schedules the program for operation F2 + OperationScheduleWait, // puts the processor into WAI mode (i.e. it'll do nothing until an interrupt is received) + OperationScheduleStop, // puts the processor into STP mode (i.e. it'll do nothing until a reset is received) }; using InstructionList = MicroOp[10]; @@ -139,6 +255,8 @@ class ProcessorStorage { bool ready_is_active_ = false; bool ready_line_is_enabled_ = false; + bool stop_is_active_ = false; + bool wait_is_active_ = false; uint8_t irq_line_ = 0, irq_request_history_ = 0; bool nmi_line_is_enabled_ = false, set_overflow_line_is_enabled_ = false;