From be01203cc11f50e1020539dcdfdf8e7021cf0c12 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Mon, 13 Aug 2018 22:17:22 -0400 Subject: [PATCH 01/10] Starts to expand the range of supported 6502s. This fully implements the NES 6502 because, well, it's virtually no extra work, and ensures that RDY takes effect on write cycles on 65C02s. --- Machines/AppleII/AppleII.cpp | 4 +- Machines/Atari2600/Cartridges/Cartridge.hpp | 4 +- .../Commodore/1540/Implementation/C1540.cpp | 2 +- .../1540/Implementation/C1540Base.hpp | 2 +- Machines/Commodore/Vic-20/Vic20.cpp | 4 +- Machines/Electron/Electron.cpp | 4 +- Machines/Oric/Oric.cpp | 4 +- Processors/6502/6502.hpp | 18 +++++--- .../Implementation/6502Implementation.hpp | 18 ++++---- .../6502/Implementation/6502Storage.cpp | 46 +++++++++++-------- 10 files changed, 61 insertions(+), 45 deletions(-) diff --git a/Machines/AppleII/AppleII.cpp b/Machines/AppleII/AppleII.cpp index bce8d2171..778dcd38c 100644 --- a/Machines/AppleII/AppleII.cpp +++ b/Machines/AppleII/AppleII.cpp @@ -63,7 +63,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine: uint8_t *ram_, *aux_ram_; }; - CPU::MOS6502::Processor<ConcreteMachine, false> 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<AppleII::Video::Video<VideoBusHandler, is_iie()>> video_; int cycles_into_current_line_ = 0; @@ -301,7 +301,7 @@ template <Analyser::Static::AppleII::Target::Model model> 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 T> class Cartridge: public: Cartridge(const std::vector<uint8_t> &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 T> class Cartridge: } protected: - CPU::MOS6502::Processor<Cartridge<T>, true> m6502_; + CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, Cartridge<T>, true> m6502_; std::vector<uint8_t> 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<MachineBase, false> m6502_; + CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, MachineBase, false> m6502_; std::shared_ptr<Storage::Disk::Drive> 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<ConcreteMachine, false> m6502_; + CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, ConcreteMachine, false> m6502_; std::vector<uint8_t> character_rom_; std::vector<uint8_t> 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<ConcreteMachine, false> m6502_; + CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, ConcreteMachine, false> 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 <Analyser::Static::Oric::Target::DiskInterface disk_interface> 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 <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co const uint16_t basic_invisible_ram_top_ = 0xffff; const uint16_t basic_visible_ram_top_ = 0xbfff; - CPU::MOS6502::Processor<ConcreteMachine, false> m6502_; + CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, ConcreteMachine, false> m6502_; // RAM and ROM std::vector<uint8_t> rom_, microdisc_rom_, colour_rom_; 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 <typename T, bool uses_ready_line> class Processor: public ProcessorBase { +template <Personality personality, typename T, bool uses_ready_line> 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 <typename T, bool uses_ready_line> class Processor: public ProcessorBas void set_ready_line(bool active); private: - Personality personality_; T &bus_handler_; }; diff --git a/Processors/6502/Implementation/6502Implementation.hpp b/Processors/6502/Implementation/6502Implementation.hpp index 401b26e12..3c6478289 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 <typename T, bool uses_ready_line> void Processor<T, uses_ready_line>::run_for(const Cycles cycles) { +template <Personality personality, typename T, bool uses_ready_line> void Processor<personality, T, uses_ready_line>::run_for(const Cycles cycles) { static const MicroOp do_branch[] = { CycleReadFromPC, CycleAddSignedOperandToPC, @@ -98,7 +98,7 @@ 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 @@ -150,7 +150,7 @@ if(number_of_cycles <= Cycles(0)) break; case CycleReadVectorHigh: read_mem(pc_.bytes.high, nextAddress.full+1); break; case OperationSetI: inverse_interrupt_flag_ = 0; - if(personality_ != P6502) decimal_flag_ = false; + if(is_65c02(personality)) decimal_flag_ = false; continue; case CyclePullPCL: s_++; read_mem(pc_.bytes.low, s_ | 0x100); break; @@ -264,7 +264,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<uint16_t>(a_) - static_cast<uint16_t>(operand_) - notCarry; uint16_t temp16; @@ -283,7 +283,7 @@ if(number_of_cycles <= Cycles(0)) break; carry_flag_ = (temp16 > 0xff) ? 0 : Flag::Carry; a_ = static_cast<uint8_t>(temp16); - if(personality_ != P6502) { + if(is_65c02(personality)) { negative_result_ = zero_result_ = a_; read_mem(operand_, address_.full); break; @@ -295,7 +295,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<uint16_t>(a_) + static_cast<uint16_t>(operand_) + static_cast<uint16_t>(carry_flag_); uint8_t low_nibble = (a_ & 0xf) + (operand_ & 0xf) + carry_flag_; @@ -309,7 +309,7 @@ if(number_of_cycles <= Cycles(0)) break; a_ = static_cast<uint8_t>(result); zero_result_ = static_cast<uint8_t>(decimalResult); - if(personality_ != P6502) { + if(is_65c02(personality)) { negative_result_ = zero_result_ = a_; read_mem(operand_, address_.full); break; @@ -611,7 +611,7 @@ if(number_of_cycles <= Cycles(0)) break; continue; } - if(uses_ready_line && ready_line_is_enabled_ && isReadOperation(nextBusOperation)) { + if(uses_ready_line && ready_line_is_enabled_ && (is_65c02(personality) || isReadOperation(nextBusOperation))) { ready_is_active_ = true; break; } @@ -629,7 +629,7 @@ if(number_of_cycles <= Cycles(0)) break; bus_handler_.flush(); } -template <typename T, bool uses_ready_line> void Processor<T, uses_ready_line>::set_ready_line(bool active) { +template <Personality personality, typename T, bool uses_ready_line> void Processor<personality, T, uses_ready_line>::set_ready_line(bool active) { assert(uses_ready_line); if(active) { ready_line_is_enabled_ = true; diff --git a/Processors/6502/Implementation/6502Storage.cpp b/Processors/6502/Implementation/6502Storage.cpp index d8fae7c4f..da0a9285a 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)) @@ -225,7 +225,7 @@ ProcessorStorage::ProcessorStorage(Personality personality) { 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,17 +235,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. @@ -304,12 +293,33 @@ 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)); + 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 { + // TODO } - for(int c = 0x87; c <= 0xf7; c += 0x10) { - Install(c, ZeroReadModifyWrite(OperationSMB)); + + if(has_stpwai(personality)) { + + } else { + // TODO } } #undef Install From 1eca4463b3f4506ba2eb067fde43685503773e9c Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Tue, 14 Aug 2018 19:33:48 -0400 Subject: [PATCH 02/10] Ensures NMI can no longer usurp BRK on 65C02s. --- Processors/6502/Implementation/6502Implementation.hpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Processors/6502/Implementation/6502Implementation.hpp b/Processors/6502/Implementation/6502Implementation.hpp index 3c6478289..ac1f74af7 100644 --- a/Processors/6502/Implementation/6502Implementation.hpp +++ b/Processors/6502/Implementation/6502Implementation.hpp @@ -140,9 +140,13 @@ 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; From ddf45a00100716ebfb9243bf15d99b00eb551568 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Tue, 14 Aug 2018 19:49:14 -0400 Subject: [PATCH 03/10] Ensures NMI and RST reset D on 65C02s. --- .../Bridges/TestMachine6502.mm | 2 +- Processors/6502/AllRAM/6502AllRAM.cpp | 19 ++++++++++++++----- .../Implementation/6502Implementation.hpp | 9 +++++++-- .../6502/Implementation/6502Storage.cpp | 2 +- .../6502/Implementation/6502Storage.hpp | 2 +- 5 files changed, 24 insertions(+), 10 deletions(-) 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/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 <Personality personality> 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<ConcreteAllRAMProcessor, false> mos6502_; + CPU::MOS6502::Processor<personality, ConcreteAllRAMProcessor, false> mos6502_; }; } AllRAMProcessor *AllRAMProcessor::Processor(Personality personality) { - return new ConcreteAllRAMProcessor(personality); +#define Bind(p) case p: return new ConcreteAllRAMProcessor<p>(); + 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 ac1f74af7..09eee7cd6 100644 --- a/Processors/6502/Implementation/6502Implementation.hpp +++ b/Processors/6502/Implementation/6502Implementation.hpp @@ -152,10 +152,13 @@ if(number_of_cycles <= Cycles(0)) break; 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(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; case CyclePullPCH: s_++; read_mem(pc_.bytes.high, s_ | 0x100); break; @@ -681,6 +684,7 @@ inline const ProcessorStorage::MicroOp *ProcessorStorage::get_reset_program() { CycleNoWritePush, OperationRSTPickVector, CycleNoWritePush, + OperationSetNMIRSTFlags, CycleReadVectorLow, CycleReadVectorHigh, OperationMoveToNextProgram @@ -697,7 +701,7 @@ inline const ProcessorStorage::MicroOp *ProcessorStorage::get_irq_program() { OperationBRKPickVector, OperationSetOperandFromFlags, CyclePushOperand, - OperationSetI, + OperationSetIRQFlags, CycleReadVectorLow, CycleReadVectorHigh, OperationMoveToNextProgram @@ -714,6 +718,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 da0a9285a..35754fb01 100644 --- a/Processors/6502/Implementation/6502Storage.cpp +++ b/Processors/6502/Implementation/6502Storage.cpp @@ -80,7 +80,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), diff --git a/Processors/6502/Implementation/6502Storage.hpp b/Processors/6502/Implementation/6502Storage.hpp index 85d4d1f3d..8f4fbf661 100644 --- a/Processors/6502/Implementation/6502Storage.hpp +++ b/Processors/6502/Implementation/6502Storage.hpp @@ -25,7 +25,7 @@ class ProcessorStorage { enum MicroOp { CycleFetchOperation, CycleFetchOperand, OperationDecodeOperation, CycleIncPCPushPCH, CyclePushPCH, CyclePushPCL, CyclePushA, CyclePushOperand, - CyclePushX, CyclePushY, OperationSetI, + CyclePushX, CyclePushY, OperationSetIRQFlags, OperationSetNMIRSTFlags, OperationBRKPickVector, OperationNMIPickVector, OperationRSTPickVector, CycleReadVectorLow, CycleReadVectorHigh, From 901e0d65b9e0e5b84695e40426e5fff29ee8a48b Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Tue, 14 Aug 2018 22:17:53 -0400 Subject: [PATCH 04/10] Documents all 6502 micro-operations. Also makes sure 1-cycle NOPs really, definitely are one cycle only on a 65C02 and eliminates OperationCopyOperandFromA as a redundant copy of OperationSTA. --- .../Implementation/6502Implementation.hpp | 16 +- .../6502/Implementation/6502Storage.cpp | 2 +- .../6502/Implementation/6502Storage.hpp | 203 ++++++++++++++---- 3 files changed, 167 insertions(+), 54 deletions(-) diff --git a/Processors/6502/Implementation/6502Implementation.hpp b/Processors/6502/Implementation/6502Implementation.hpp index 09eee7cd6..62b860078 100644 --- a/Processors/6502/Implementation/6502Implementation.hpp +++ b/Processors/6502/Implementation/6502Implementation.hpp @@ -104,6 +104,9 @@ if(number_of_cycles <= Cycles(0)) break; operation_ == 0xdb ) { read_mem(operand_, pc_.full); + break; + } else { + continue; } break; @@ -173,11 +176,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; @@ -204,6 +207,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; @@ -516,8 +520,6 @@ 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 diff --git a/Processors/6502/Implementation/6502Storage.cpp b/Processors/6502/Implementation/6502Storage.cpp index 35754fb01..400f97118 100644 --- a/Processors/6502/Implementation/6502Storage.cpp +++ b/Processors/6502/Implementation/6502Storage.cpp @@ -61,7 +61,7 @@ using namespace CPU::MOS6502; #define IndirectIndexedReadModifyWrite(...) Program(IndirectIndexed, 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) diff --git a/Processors/6502/Implementation/6502Storage.hpp b/Processors/6502/Implementation/6502Storage.hpp index 8f4fbf661..248076a47 100644 --- a/Processors/6502/Implementation/6502Storage.hpp +++ b/Processors/6502/Implementation/6502Storage.hpp @@ -23,62 +23,173 @@ class ProcessorStorage { to perform whereas those called OperationX occur for free (so, in effect, their cost is loaded onto the next cycle). */ enum MicroOp { - CycleFetchOperation, CycleFetchOperand, OperationDecodeOperation, CycleIncPCPushPCH, - CyclePushPCH, CyclePushPCL, CyclePushA, CyclePushOperand, - CyclePushX, CyclePushY, OperationSetIRQFlags, OperationSetNMIRSTFlags, + 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; if address_ now does not equal next_address_, schedules a throwaway read from address_ + CycleAddYToAddressLow, // calculates address_ + y and stores it to next_address_; copies next_address_.l back to address_.l; if address_ now does not equal next_address_, schedules a throwaway read from address_ + CycleAddXToAddressLowRead, // calculates address_ + x and stores it to next_address; copies next_address.l back to address_.l; schedules a throwaway read from address_ + CycleAddYToAddressLowRead, // calculates address_ + y and stores it to next_address; copies next_address.l back to address_.l; schedules a throwaway read from address_ + 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 + 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 + 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 + CycleScheduleJam // schedules the program for operation F2 }; using InstructionList = MicroOp[10]; From c35dca783f37d781ae4f5537f345f662e3522b2c Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Wed, 15 Aug 2018 18:47:53 -0400 Subject: [PATCH 05/10] Ensures that page-crossing indexing no longer causes an extra read of an invalid address on the 65C02. It rereads the last byte of the instruction stream instead. --- .../Implementation/6502Implementation.hpp | 20 ++++++++++++++----- .../6502/Implementation/6502Storage.hpp | 14 +++++++------ 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/Processors/6502/Implementation/6502Implementation.hpp b/Processors/6502/Implementation/6502Implementation.hpp index 62b860078..ed67b32d8 100644 --- a/Processors/6502/Implementation/6502Implementation.hpp +++ b/Processors/6502/Implementation/6502Implementation.hpp @@ -436,32 +436,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; diff --git a/Processors/6502/Implementation/6502Storage.hpp b/Processors/6502/Implementation/6502Storage.hpp index 248076a47..9dadc45d2 100644 --- a/Processors/6502/Implementation/6502Storage.hpp +++ b/Processors/6502/Implementation/6502Storage.hpp @@ -17,10 +17,12 @@ 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, // fetches (PC) to operation_, storing PC to last_operation_pc_ before incrementing it @@ -72,10 +74,10 @@ class ProcessorStorage { 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 - CycleAddXToAddressLow, // calculates address_ + x and stores it to next_address_; copies next_address_.l back to address_.l; if address_ now does not equal next_address_, schedules a throwaway read from address_ - CycleAddYToAddressLow, // calculates address_ + y and stores it to next_address_; copies next_address_.l back to address_.l; if address_ now does not equal next_address_, schedules a throwaway read from address_ - CycleAddXToAddressLowRead, // calculates address_ + x and stores it to next_address; copies next_address.l back to address_.l; schedules a throwaway read from address_ - CycleAddYToAddressLowRead, // calculates address_ + y and stores it to next_address; copies next_address.l back to address_.l; schedules a throwaway read from address_ + 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_ OperationIncrementPC, // increments the PC From 6806193dc29e9965595155281a504f70e4529816 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Wed, 15 Aug 2018 19:17:37 -0400 Subject: [PATCH 06/10] Ensures that "Read/Modify/Write instructions absolute indexed in same page" take only six cycles on a 65C02. --- Processors/6502/Implementation/6502Storage.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Processors/6502/Implementation/6502Storage.cpp b/Processors/6502/Implementation/6502Storage.cpp index 400f97118..8b2e92ee3 100644 --- a/Processors/6502/Implementation/6502Storage.cpp +++ b/Processors/6502/Implementation/6502Storage.cpp @@ -60,6 +60,9 @@ 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(OperationSTA, op, OperationCopyOperandToA) @@ -235,8 +238,6 @@ ProcessorStorage::ProcessorStorage(Personality personality) { // Add BRA. Install(0x80, Program(OperationBRA)); - // Add NOPs. - // The 1-byte, 1-cycle (!) NOPs. for(int c = 0x03; c <= 0xf3; c += 0x10) { Install(c, ImpliedNop()); @@ -293,6 +294,16 @@ ProcessorStorage::ProcessorStorage(Personality personality) { Install(0x14, ZeroReadModifyWrite(OperationTRB)); Install(0x1c, AbsoluteReadModifyWrite(OperationTRB)); + // 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)); + if(has_bbrbbsrmbsmb(personality)) { // Add BBS and BBR. These take five cycles. My guessed breakdown is: // 1. read opcode From 60e00ddd0205d5bcce74f3c7d96a506663303885 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Wed, 15 Aug 2018 22:07:17 -0400 Subject: [PATCH 07/10] Correction: the test for not skipping an operand fetch requires a 65C02. --- Processors/6502/Implementation/6502Implementation.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Processors/6502/Implementation/6502Implementation.hpp b/Processors/6502/Implementation/6502Implementation.hpp index ed67b32d8..c9eff152e 100644 --- a/Processors/6502/Implementation/6502Implementation.hpp +++ b/Processors/6502/Implementation/6502Implementation.hpp @@ -98,7 +98,7 @@ if(number_of_cycles <= Cycles(0)) break; // governs everything else on the 6502: that two bytes will always // be fetched. if( - is_65c02(personality) || + !is_65c02(personality) || (operation_&7) != 3 || operation_ == 0xcb || operation_ == 0xdb From 592ec69d36e3f54874a0b6c587ab84cff50559e5 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Wed, 15 Aug 2018 22:42:04 -0400 Subject: [PATCH 08/10] Causes the 65C02 not to accept interrupts immediately after untaken branches. --- Processors/6502/Implementation/6502Implementation.hpp | 8 +++++++- Processors/6502/Implementation/6502Storage.hpp | 5 ++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Processors/6502/Implementation/6502Implementation.hpp b/Processors/6502/Implementation/6502Implementation.hpp index c9eff152e..98879f6c2 100644 --- a/Processors/6502/Implementation/6502Implementation.hpp +++ b/Processors/6502/Implementation/6502Implementation.hpp @@ -533,7 +533,13 @@ if(number_of_cycles <= Cycles(0)) break; // 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; \ + } else if(is_65c02(personality)) { \ + scheduled_program_counter_ = fetch_decode_execute; \ + } case OperationBPL: BRA(!(negative_result_&0x80)); continue; case OperationBMI: BRA(negative_result_&0x80); continue; diff --git a/Processors/6502/Implementation/6502Storage.hpp b/Processors/6502/Implementation/6502Storage.hpp index 9dadc45d2..a8266a55f 100644 --- a/Processors/6502/Implementation/6502Storage.hpp +++ b/Processors/6502/Implementation/6502Storage.hpp @@ -160,8 +160,11 @@ class ProcessorStorage { 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 + 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 + // 65C02 modification to all branches: if the branch isn't taken, the next fetch-decode-execute + // sequence is scheduled immediately, without any possibility of responding to an interrupt. + // Cf. http://forum.6502.org/viewtopic.php?f=4&t=1634 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 From b3bdfa9f46ff299af0938a469aad6d61ae7cff53 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Thu, 16 Aug 2018 20:47:49 -0400 Subject: [PATCH 09/10] Corrected: it's three-cycle 65C02 branches that ignore interrupts, not two. --- Processors/6502/Implementation/6502Implementation.hpp | 11 +++++++---- Processors/6502/Implementation/6502Storage.hpp | 5 +---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Processors/6502/Implementation/6502Implementation.hpp b/Processors/6502/Implementation/6502Implementation.hpp index 98879f6c2..47e0bcfc9 100644 --- a/Processors/6502/Implementation/6502Implementation.hpp +++ b/Processors/6502/Implementation/6502Implementation.hpp @@ -537,8 +537,6 @@ if(number_of_cycles <= Cycles(0)) break; pc_.full++; \ if(condition) { \ scheduled_program_counter_ = do_branch; \ - } else if(is_65c02(personality)) { \ - scheduled_program_counter_ = fetch_decode_execute; \ } case OperationBPL: BRA(!(negative_result_&0x80)); continue; @@ -551,6 +549,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<uint16_t>(pc_.full + (int8_t)operand_); pc_.bytes.low = nextAddress.bytes.low; @@ -559,6 +559,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; @@ -595,8 +600,6 @@ if(number_of_cycles <= Cycles(0)) break; } } break; -#undef BRA - // MARK: - Transfers case OperationTXA: zero_result_ = negative_result_ = a_ = x_; continue; diff --git a/Processors/6502/Implementation/6502Storage.hpp b/Processors/6502/Implementation/6502Storage.hpp index a8266a55f..e877c4c98 100644 --- a/Processors/6502/Implementation/6502Storage.hpp +++ b/Processors/6502/Implementation/6502Storage.hpp @@ -162,9 +162,6 @@ class ProcessorStorage { 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 - // 65C02 modification to all branches: if the branch isn't taken, the next fetch-decode-execute - // sequence is scheduled immediately, without any possibility of responding to an interrupt. - // Cf. http://forum.6502.org/viewtopic.php?f=4&t=1634 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 @@ -184,7 +181,7 @@ class ProcessorStorage { 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 + 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_ From 0e7f54f375c9ae9dd8d5e8181527fe3eddc0eae4 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Fri, 17 Aug 2018 21:49:06 -0400 Subject: [PATCH 10/10] Implements STP and WAI, and ensures all unimplemented 65C02 instructions are NOP for all 65C02s. --- .../Implementation/6502Implementation.hpp | 81 +++++++++++++------ .../6502/Implementation/6502Storage.cpp | 58 ++++++++++++- .../6502/Implementation/6502Storage.hpp | 7 +- 3 files changed, 117 insertions(+), 29 deletions(-) diff --git a/Processors/6502/Implementation/6502Implementation.hpp b/Processors/6502/Implementation/6502Implementation.hpp index 47e0bcfc9..03637ca72 100644 --- a/Processors/6502/Implementation/6502Implementation.hpp +++ b/Processors/6502/Implementation/6502Implementation.hpp @@ -34,40 +34,62 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces 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(); } @@ -188,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; @@ -639,6 +669,9 @@ if(number_of_cycles <= Cycles(0)) break; continue; } + 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; diff --git a/Processors/6502/Implementation/6502Storage.cpp b/Processors/6502/Implementation/6502Storage.cpp index 8b2e92ee3..19c2fbf5b 100644 --- a/Processors/6502/Implementation/6502Storage.cpp +++ b/Processors/6502/Implementation/6502Storage.cpp @@ -73,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 @@ -224,6 +224,32 @@ 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)); \ @@ -304,6 +330,15 @@ ProcessorStorage::ProcessorStorage(Personality personality) { 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 @@ -324,13 +359,28 @@ ProcessorStorage::ProcessorStorage(Personality personality) { Install(c, ZeroReadModifyWrite(OperationSMB)); } } else { - // TODO + 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()); + } } + // Outstanding: + // 0xcb, 0xdb, if(has_stpwai(personality)) { - + Install(0xcb, Program(OperationScheduleWait)); + Install(0xdb, Program(OperationScheduleStop)); } else { - // TODO + Install(0xcb, ImpliedNop()); + Install(0xdb, ZeroXNop()); } } #undef Install diff --git a/Processors/6502/Implementation/6502Storage.hpp b/Processors/6502/Implementation/6502Storage.hpp index e877c4c98..fd7b54b5f 100644 --- a/Processors/6502/Implementation/6502Storage.hpp +++ b/Processors/6502/Implementation/6502Storage.hpp @@ -191,7 +191,10 @@ class ProcessorStorage { 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 - CycleScheduleJam // schedules the program for operation F2 + + 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]; @@ -252,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;