diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 13a55c0e9..19ec1c533 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -15,9 +15,6 @@ 4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0CCC421C62D0B3001CAC5F /* CRT.cpp */; }; 4B121F951E05E66800BFDA12 /* PCMPatchedTrackTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B121F941E05E66800BFDA12 /* PCMPatchedTrackTests.mm */; }; 4B121F9B1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */; }; - 4B14145B1B58879D00E04248 /* 6502.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414571B58879D00E04248 /* 6502.cpp */; }; - 4B14145D1B5887A600E04248 /* 6502.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414571B58879D00E04248 /* 6502.cpp */; }; - 4B14145E1B5887AA00E04248 /* 6502AllRAM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414591B58879D00E04248 /* 6502AllRAM.cpp */; }; 4B1414601B58885000E04248 /* WolfgangLorenzTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */; }; 4B1414621B58888700E04248 /* KlausDormannTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414611B58888700E04248 /* KlausDormannTests.swift */; }; 4B1497881EE4A1DA00CE2596 /* ZX80O81P.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1497861EE4A1DA00CE2596 /* ZX80O81P.cpp */; }; @@ -83,12 +80,15 @@ 4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */; }; 4B69FB441C4D941400B5F0AA /* TapeUEF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB421C4D941400B5F0AA /* TapeUEF.cpp */; }; 4B69FB461C4D950F00B5F0AA /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B69FB451C4D950F00B5F0AA /* libz.tbd */; }; + 4B6A4C991F58F09E00E3F787 /* 6502Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B6A4C951F58F09E00E3F787 /* 6502Base.cpp */; }; 4B6C73BD1D387AE500AFCFCA /* DiskController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B6C73BB1D387AE500AFCFCA /* DiskController.cpp */; }; 4B77069D1EC904570053B588 /* Z80.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B77069B1EC904570053B588 /* Z80.cpp */; }; 4B7913CC1DFCD80E00175A82 /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7913CA1DFCD80E00175A82 /* Video.cpp */; }; 4B79E4441E3AF38600141F11 /* cassette.png in Resources */ = {isa = PBXBuildFile; fileRef = 4B79E4411E3AF38600141F11 /* cassette.png */; }; 4B79E4451E3AF38600141F11 /* floppy35.png in Resources */ = {isa = PBXBuildFile; fileRef = 4B79E4421E3AF38600141F11 /* floppy35.png */; }; 4B79E4461E3AF38600141F11 /* floppy525.png in Resources */ = {isa = PBXBuildFile; fileRef = 4B79E4431E3AF38600141F11 /* floppy525.png */; }; + 4B7BC7F51F58F27800D1B1B4 /* 6502AllRAM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B6A4C911F58F09E00E3F787 /* 6502AllRAM.cpp */; }; + 4B7BC7F61F58F7D200D1B1B4 /* 6502Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B6A4C951F58F09E00E3F787 /* 6502Base.cpp */; }; 4B8378DC1F336631005CA9E4 /* CharacterMapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8378DA1F336631005CA9E4 /* CharacterMapper.cpp */; }; 4B8378DF1F33675F005CA9E4 /* CharacterMapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8378DD1F33675F005CA9E4 /* CharacterMapper.cpp */; }; 4B8378E21F336920005CA9E4 /* CharacterMapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8378E01F336920005CA9E4 /* CharacterMapper.cpp */; }; @@ -485,10 +485,6 @@ 4B121F971E060CF000BFDA12 /* PCMSegment.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PCMSegment.hpp; sourceTree = ""; }; 4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PCMSegmentEventSourceTests.mm; sourceTree = ""; }; 4B1414501B58848C00E04248 /* ClockSignal-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ClockSignal-Bridging-Header.h"; sourceTree = ""; }; - 4B1414571B58879D00E04248 /* 6502.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = 6502.cpp; sourceTree = ""; }; - 4B1414581B58879D00E04248 /* 6502.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6502.hpp; sourceTree = ""; }; - 4B1414591B58879D00E04248 /* 6502AllRAM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = 6502AllRAM.cpp; sourceTree = ""; }; - 4B14145A1B58879D00E04248 /* 6502AllRAM.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6502AllRAM.hpp; sourceTree = ""; }; 4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WolfgangLorenzTests.swift; sourceTree = ""; }; 4B1414611B58888700E04248 /* KlausDormannTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KlausDormannTests.swift; sourceTree = ""; }; 4B1497861EE4A1DA00CE2596 /* ZX80O81P.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ZX80O81P.cpp; sourceTree = ""; }; @@ -610,6 +606,10 @@ 4B69FB421C4D941400B5F0AA /* TapeUEF.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TapeUEF.cpp; sourceTree = ""; }; 4B69FB431C4D941400B5F0AA /* TapeUEF.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TapeUEF.hpp; sourceTree = ""; }; 4B69FB451C4D950F00B5F0AA /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; + 4B6A4C8E1F58F09E00E3F787 /* 6502.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6502.hpp; sourceTree = ""; }; + 4B6A4C911F58F09E00E3F787 /* 6502AllRAM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = 6502AllRAM.cpp; sourceTree = ""; }; + 4B6A4C921F58F09E00E3F787 /* 6502AllRAM.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6502AllRAM.hpp; sourceTree = ""; }; + 4B6A4C951F58F09E00E3F787 /* 6502Base.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = 6502Base.cpp; sourceTree = ""; }; 4B6C73BB1D387AE500AFCFCA /* DiskController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DiskController.cpp; sourceTree = ""; }; 4B6C73BC1D387AE500AFCFCA /* DiskController.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DiskController.hpp; sourceTree = ""; }; 4B77069B1EC904570053B588 /* Z80.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Z80.cpp; path = Z80/Z80.cpp; sourceTree = ""; }; @@ -1125,10 +1125,9 @@ 4B1414561B58879D00E04248 /* 6502 */ = { isa = PBXGroup; children = ( - 4B1414571B58879D00E04248 /* 6502.cpp */, - 4B1414581B58879D00E04248 /* 6502.hpp */, - 4B1414591B58879D00E04248 /* 6502AllRAM.cpp */, - 4B14145A1B58879D00E04248 /* 6502AllRAM.hpp */, + 4B6A4C8E1F58F09E00E3F787 /* 6502.hpp */, + 4B6A4C901F58F09E00E3F787 /* AllRAM */, + 4B6A4C931F58F09E00E3F787 /* Implementation */, ); path = 6502; sourceTree = ""; @@ -1506,6 +1505,23 @@ path = Formats; sourceTree = ""; }; + 4B6A4C901F58F09E00E3F787 /* AllRAM */ = { + isa = PBXGroup; + children = ( + 4B6A4C911F58F09E00E3F787 /* 6502AllRAM.cpp */, + 4B6A4C921F58F09E00E3F787 /* 6502AllRAM.hpp */, + ); + path = AllRAM; + sourceTree = ""; + }; + 4B6A4C931F58F09E00E3F787 /* Implementation */ = { + isa = PBXGroup; + children = ( + 4B6A4C951F58F09E00E3F787 /* 6502Base.cpp */, + ); + path = Implementation; + sourceTree = ""; + }; 4B77069E1EC9045B0053B588 /* Z80 */ = { isa = PBXGroup; children = ( @@ -2797,6 +2813,7 @@ 4BBC951E1F368D83008F4C34 /* i8272.cpp in Sources */, 4BF1354C1D6D2C300054B2EA /* StaticAnalyser.cpp in Sources */, 4B4A76301DB1A3FA007AAE2E /* AY38910.cpp in Sources */, + 4B6A4C991F58F09E00E3F787 /* 6502Base.cpp in Sources */, 4B2A53A31D117D36003C6002 /* CSVic20.mm in Sources */, 4B2A53A21D117D36003C6002 /* CSElectron.mm in Sources */, 4B8FE2201DA19D7C0090D3CE /* Atari2600OptionsPanel.swift in Sources */, @@ -2817,7 +2834,6 @@ 4BC3B7521CD1956900F86E85 /* OutputShader.cpp in Sources */, 4B4C83701D4F623200CD541F /* D64.cpp in Sources */, 4B5073071DDD3B9400C48FBD /* ArrayBuilder.cpp in Sources */, - 4B14145B1B58879D00E04248 /* 6502.cpp in Sources */, 4BEE0A6F1D72496600532C7B /* Cartridge.cpp in Sources */, 4B8805FB1DCFF807003085B1 /* Oric.cpp in Sources */, 4B5FADC01DE3BF2B00AEC565 /* Microdisc.cpp in Sources */, @@ -2838,13 +2854,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 4B14145E1B5887AA00E04248 /* 6502AllRAM.cpp in Sources */, - 4B14145D1B5887A600E04248 /* 6502.cpp in Sources */, 4B1E85811D176468001EF87D /* 6532Tests.swift in Sources */, 4BDDBA991EF3451200347E61 /* Z80MachineCycleTests.swift in Sources */, 4BC9E1EE1D23449A003FCEE4 /* 6502InterruptTests.swift in Sources */, 4BEF6AAA1D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm in Sources */, 4B924E991E74D22700B76AF1 /* AtariStaticAnalyserTests.mm in Sources */, + 4B7BC7F61F58F7D200D1B1B4 /* 6502Base.cpp in Sources */, + 4B7BC7F51F58F27800D1B1B4 /* 6502AllRAM.cpp in Sources */, 4B08A2751EE35D56008B7065 /* Z80InterruptTests.swift in Sources */, 4BFCA1241ECBDCB400AC40C1 /* AllRAMProcessor.cpp in Sources */, 4B50730A1DDFCFDF00C48FBD /* ArrayBuilderTests.mm in Sources */, diff --git a/Processors/6502/6502.hpp b/Processors/6502/6502.hpp index 6bdf0951c..24e53758e 100644 --- a/Processors/6502/6502.hpp +++ b/Processors/6502/6502.hpp @@ -68,8 +68,117 @@ enum BusOperation { */ extern const uint8_t JamOpcode; +/*! + A class providing empty implementations of the methods a 6502 uses to access the bus. To wire the 6502 to a bus, + machines should subclass BusHandler and then declare a realisation of the 6502 template, suplying their bus + handler. +*/ +class BusHandler { + public: + /*! + Announces that the 6502 has performed the cycle defined by operation, address and value. On the 6502, + all bus cycles take one clock cycle so the amoutn of time advanced is implicit. + + @param operation The type of bus cycle — read, read opcode (i.e. read, with sync active), + write or ready. + @param address The value of the address bus during this bus cycle. + @param value If this is a cycle that puts a value onto the data bus, *value is that value. If this is + a cycle that reads the bus, the bus handler should write a value to *value. Writing to *value during + a read cycle will produce undefined behaviour. + + @returns The number of cycles that passed in objective time while this 6502 bus cycle was ongoing. + On an archetypal machine this will be Cycles(1) but some architectures may choose not to clock the 6502 + during some periods; one way to simulate that is to have the bus handler return a number other than + Cycles(1) to describe lengthened bus cycles. + */ + Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) { + return Cycles(0); + } + + /*! + Announces completion of all the cycles supplied to a .run_for request on the 6502. Intended to allow + bus handlers to perform any deferred output work. + */ + void flush() {} +}; + +/*! + A base class from which the 6502 descends; separated for implementation reasons only. +*/ class ProcessorBase { + public: + /*! + Gets the value of a register. + + @see set_value_of_register + + @param r The register to set. + @returns The value of the register. 8-bit registers will be returned as unsigned. + */ + uint16_t get_value_of_register(Register r); + + /*! + Sets the value of a register. + + @see get_value_of_register + + @param r The register to set. + @param value The value to set. If the register is only 8 bit, the value will be truncated. + */ + void set_value_of_register(Register r, uint16_t value); + + /*! + Sets the current level of the RST line. + + @param active @c true if the line is logically active; @c false otherwise. + */ + inline void set_reset_line(bool active); + + /*! + Gets whether the 6502 would reset at the next opportunity. + + @returns @c true if the line is logically active; @c false otherwise. + */ + inline bool get_is_resetting(); + + /*! + This emulation automatically sets itself up in power-on state at creation, which has the effect of triggering a + reset at the first opportunity. Use @c set_power_on to disable that behaviour. + */ + inline void set_power_on(bool active); + + /*! + Sets the current level of the IRQ line. + + @param active @c true if the line is logically active; @c false otherwise. + */ + inline void set_irq_line(bool active); + + /*! + Sets the current level of the set overflow line. + + @param active @c true if the line is logically active; @c false otherwise. + */ + inline void set_overflow_line(bool active); + + /*! + Sets the current level of the NMI line. + + @param active `true` if the line is logically active; `false` otherwise. + */ + inline void set_nmi_line(bool active); + + /*! + Queries whether the 6502 is now 'jammed'; i.e. has entered an invalid state + such that it will not of itself perform any more meaningful processing. + + @returns @c true if the 6502 is jammed; @c false otherwise. + */ + bool is_jammed(); + protected: + ProcessorBase(); + /* 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 @@ -116,26 +225,7 @@ class ProcessorBase { }; static const MicroOp operations[256][10]; -}; -class BusHandler { - public: - void flush() {} - Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) { - return Cycles(0); - } -}; - -/*! - @abstact An abstract base class for emulation of a 6502 processor via the curiously recurring template pattern/f-bounded polymorphism. - - @discussion Subclasses should implement @c perform_bus_operation(BusOperation operation, uint16_t address, uint8_t *value) in - order to provide the bus on which the 6502 operates and @c flush(), which is called upon completion of a continuous run - of cycles to allow a subclass to bring any on-demand activities up to date. -*/ -template class Processor: public ProcessorBase { - private: - T &bus_handler_; const MicroOp *scheduled_program_counter_; /* @@ -166,9 +256,7 @@ template class Processor: public ProcessorBase { @returns The current value of the flags register. */ - uint8_t get_flags() { - return carry_flag_ | overflow_flag_ | (inverse_interrupt_flag_ ^ Flag::Interrupt) | (negative_result_ & 0x80) | (zero_result_ ? 0 : Flag::Zero) | Flag::Always | decimal_flag_; - } + inline uint8_t get_flags(); /*! Sets the flags register. @@ -177,14 +265,7 @@ template class Processor: public ProcessorBase { @param flags The new value of the flags register. */ - void set_flags(uint8_t flags) { - carry_flag_ = flags & Flag::Carry; - negative_result_ = flags & Flag::Sign; - zero_result_ = (~flags) & Flag::Zero; - overflow_flag_ = flags & Flag::Overflow; - inverse_interrupt_flag_ = (~flags) & Flag::Interrupt; - decimal_flag_ = flags & Flag::Decimal; - } + inline void set_flags(uint8_t flags); bool is_jammed_; Cycles cycles_left_to_run_; @@ -209,761 +290,683 @@ template class Processor: public ProcessorBase { @returns The program representing an RST response. */ - inline const MicroOp *get_reset_program() { - static const MicroOp reset[] = { - CycleFetchOperand, - CycleFetchOperand, - CycleNoWritePush, - CycleNoWritePush, - OperationRSTPickVector, - CycleNoWritePush, - CycleReadVectorLow, - CycleReadVectorHigh, - OperationMoveToNextProgram - }; - return reset; - } + inline const MicroOp *get_reset_program(); /*! Gets the program representing an IRQ response. @returns The program representing an IRQ response. */ - inline const MicroOp *get_irq_program() { - static const MicroOp reset[] = { - CycleFetchOperand, - CycleFetchOperand, - CyclePushPCH, - CyclePushPCL, - OperationBRKPickVector, - OperationSetOperandFromFlags, - CyclePushOperand, - OperationSetI, - CycleReadVectorLow, - CycleReadVectorHigh, - OperationMoveToNextProgram - }; - return reset; - } + inline const MicroOp *get_irq_program(); /*! Gets the program representing an NMI response. @returns The program representing an NMI response. */ - inline const MicroOp *get_nmi_program() { - static const MicroOp reset[] = { - CycleFetchOperand, - CycleFetchOperand, - CyclePushPCH, - CyclePushPCL, - OperationNMIPickVector, - OperationSetOperandFromFlags, - CyclePushOperand, - CycleReadVectorLow, - CycleReadVectorHigh, - OperationMoveToNextProgram - }; - return reset; - } + inline const MicroOp *get_nmi_program(); +}; +/*! + @abstact Template providing emulation of a 6502 processor. + + @discussion Users should provide as the first template parameter a subclass of CPU::MOS6502::BusHandler; the 6502 + will announce its cycle-by-cycle activity via the bus handler, which is responsible for marrying it to a bus. They + 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 { public: /*! Constructs an instance of the 6502 that will use @c bus_handler for all bus communications. */ - Processor(T &bus_handler) : - is_jammed_(false), - ready_line_is_enabled_(false), - ready_is_active_(false), - inverse_interrupt_flag_(0), - irq_request_history_(0), - s_(0), - next_bus_operation_(BusOperation::None), - interrupt_requests_(InterruptRequestFlags::PowerOn), - irq_line_(0), - nmi_line_is_enabled_(false), - set_overflow_line_is_enabled_(false), - scheduled_program_counter_(nullptr), - bus_handler_(bus_handler) { - // only the interrupt flag is defined upon reset but get_flags isn't going to - // mask the other flags so we need to do that, at least - carry_flag_ &= Flag::Carry; - decimal_flag_ &= Flag::Decimal; - overflow_flag_ &= Flag::Overflow; - } + Processor(T &bus_handler) : bus_handler_(bus_handler) {} /*! Runs the 6502 for a supplied number of cycles. - @discussion Subclasses must implement @c perform_bus_operation(BusOperation operation, uint16_t address, uint8_t *value) . - The 6502 will call that method for all bus accesses. The 6502 is guaranteed to perform one bus operation call per cycle. - If it is a read operation then @c value will be seeded with the value 0xff. - @param cycles The number of cycles to run the 6502 for. */ - void run_for(const Cycles cycles) { - static const MicroOp doBranch[] = { - CycleReadFromPC, - CycleAddSignedOperandToPC, - OperationMoveToNextProgram - }; - static uint8_t throwaway_target; - static const MicroOp fetch_decode_execute[] = { - CycleFetchOperation, - CycleFetchOperand, - OperationDecodeOperation - }; - - // These plus program below act to give the compiler permission to update these values - // without touching the class storage (i.e. it explicitly says they need be completely up - // to date in this stack frame only); which saves some complicated addressing - RegisterPair nextAddress = next_address_; - BusOperation nextBusOperation = next_bus_operation_; - uint16_t busAddress = bus_address_; - 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;\ - } - -#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; - - checkSchedule(); - Cycles number_of_cycles = cycles + cycles_left_to_run_; - - while(number_of_cycles > Cycles(0)) { - - 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_) { - if(nextBusOperation != BusOperation::None) { - bus_access(); - } - - while(1) { - - const MicroOp cycle = *scheduled_program_counter_; - scheduled_program_counter_++; - -#define read_op(val, addr) nextBusOperation = BusOperation::ReadOpcode; busAddress = addr; busValue = &val; val = 0xff -#define read_mem(val, addr) nextBusOperation = BusOperation::Read; busAddress = addr; busValue = &val; val = 0xff -#define throwaway_read(addr) nextBusOperation = BusOperation::Read; busAddress = addr; busValue = &throwaway_target; throwaway_target = 0xff -#define write_mem(val, addr) nextBusOperation = BusOperation::Write; busAddress = addr; busValue = &val - - switch(cycle) { - -#pragma mark - Fetch/Decode - - case CycleFetchOperation: { - last_operation_pc_ = pc_; -// printf("%04x\n", pc_.full); - pc_.full++; - read_op(operation_, last_operation_pc_.full); - -// static int last_cycles_left_to_run = 0; -// static bool printed_map[256] = {false}; -// if(!printed_map[operation_]) { -// printed_map[operation_] = true; -// if(last_cycles_left_to_run > cycles_left_to_run_) -// printf("%02x %d\n", operation_, last_cycles_left_to_run - cycles_left_to_run_); -// else -// printf("%02x\n", operation_); -// } -// last_cycles_left_to_run = cycles_left_to_run_; - } break; - - case CycleFetchOperand: - read_mem(operand_, pc_.full); - break; - - case OperationDecodeOperation: -// printf("d %02x\n", operation_); - scheduled_program_counter_ = operations[operation_]; - continue; - - case OperationMoveToNextProgram: - scheduled_program_counter_ = nullptr; - checkSchedule(); - continue; - -#define push(v) {\ - uint16_t targetAddress = s_ | 0x100; s_--;\ - write_mem(v, targetAddress);\ - } - - case CycleIncPCPushPCH: pc_.full++; // deliberate fallthrough - case CyclePushPCH: push(pc_.bytes.high); break; - case CyclePushPCL: push(pc_.bytes.low); break; - case CyclePushOperand: push(operand_); break; - case CyclePushA: push(a_); break; - case CycleNoWritePush: { - uint16_t targetAddress = s_ | 0x100; s_--; - read_mem(operand_, targetAddress); - } - break; - -#undef push - - case CycleReadFromS: throwaway_read(s_ | 0x100); 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? - 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: inverse_interrupt_flag_ = 0; continue; - - case CyclePullPCL: s_++; read_mem(pc_.bytes.low, s_ | 0x100); break; - case CyclePullPCH: s_++; read_mem(pc_.bytes.high, s_ | 0x100); break; - case CyclePullA: s_++; read_mem(a_, s_ | 0x100); break; - case CyclePullOperand: s_++; read_mem(operand_, s_ | 0x100); break; - case OperationSetFlagsFromOperand: set_flags(operand_); continue; - case OperationSetOperandFromFlagsWithBRKSet: operand_ = get_flags() | Flag::Break; continue; - case OperationSetOperandFromFlags: operand_ = get_flags(); continue; - case OperationSetFlagsFromA: zero_result_ = negative_result_ = a_; continue; - - case CycleIncrementPCAndReadStack: pc_.full++; throwaway_read(s_ | 0x100); break; - case CycleReadPCLFromAddress: read_mem(pc_.bytes.low, address_.full); break; - case CycleReadPCHFromAddress: address_.bytes.low++; read_mem(pc_.bytes.high, address_.full); break; - - case CycleReadAndIncrementPC: { - uint16_t oldPC = pc_.full; - pc_.full++; - throwaway_read(oldPC); - } break; - -#pragma mark - JAM - - case CycleScheduleJam: { - is_jammed_ = true; - scheduled_program_counter_ = operations[CPU::MOS6502::JamOpcode]; - } continue; - -#pragma mark - Bitwise - - case OperationORA: a_ |= operand_; negative_result_ = zero_result_ = a_; continue; - case OperationAND: a_ &= operand_; negative_result_ = zero_result_ = a_; continue; - case OperationEOR: a_ ^= operand_; negative_result_ = zero_result_ = a_; continue; - -#pragma mark - Load and Store - - case OperationLDA: a_ = negative_result_ = zero_result_ = operand_; continue; - 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 OperationSTA: operand_ = a_; continue; - case OperationSTX: operand_ = x_; continue; - case OperationSTY: operand_ = y_; continue; - case OperationSAX: operand_ = a_ & x_; continue; - case OperationSHA: operand_ = a_ & x_ & (address_.bytes.high+1); continue; - case OperationSHX: operand_ = x_ & (address_.bytes.high+1); continue; - case OperationSHY: operand_ = y_ & (address_.bytes.high+1); continue; - case OperationSHS: s_ = a_ & x_; operand_ = s_ & (address_.bytes.high+1); continue; - - case OperationLXA: - a_ = x_ = (a_ | 0xee) & operand_; - negative_result_ = zero_result_ = a_; - continue; - -#pragma mark - Compare - - case OperationCMP: { - const uint16_t temp16 = a_ - operand_; - negative_result_ = zero_result_ = (uint8_t)temp16; - carry_flag_ = ((~temp16) >> 8)&1; - } continue; - case OperationCPX: { - const uint16_t temp16 = x_ - operand_; - negative_result_ = zero_result_ = (uint8_t)temp16; - carry_flag_ = ((~temp16) >> 8)&1; - } continue; - case OperationCPY: { - const uint16_t temp16 = y_ - operand_; - negative_result_ = zero_result_ = (uint8_t)temp16; - carry_flag_ = ((~temp16) >> 8)&1; - } continue; - -#pragma mark - BIT - - case OperationBIT: - zero_result_ = operand_ & a_; - negative_result_ = operand_; - overflow_flag_ = operand_&Flag::Overflow; - continue; - -#pragma mark ADC/SBC (and INS) - - case OperationINS: - operand_++; // deliberate fallthrough - case OperationSBC: - if(decimal_flag_) { - const uint16_t notCarry = carry_flag_ ^ 0x1; - const uint16_t decimalResult = (uint16_t)a_ - (uint16_t)operand_ - notCarry; - uint16_t temp16; - - temp16 = (a_&0xf) - (operand_&0xf) - notCarry; - if(temp16 > 0xf) temp16 -= 0x6; - temp16 = (temp16&0x0f) | ((temp16 > 0x0f) ? 0xfff0 : 0x00); - temp16 += (a_&0xf0) - (operand_&0xf0); - - overflow_flag_ = ( ( (decimalResult^a_)&(~decimalResult^operand_) )&0x80) >> 1; - negative_result_ = (uint8_t)temp16; - zero_result_ = (uint8_t)decimalResult; - - if(temp16 > 0xff) temp16 -= 0x60; - - carry_flag_ = (temp16 > 0xff) ? 0 : Flag::Carry; - a_ = (uint8_t)temp16; - continue; - } else { - operand_ = ~operand_; - } - - // deliberate fallthrough - case OperationADC: - if(decimal_flag_) { - const uint16_t decimalResult = (uint16_t)a_ + (uint16_t)operand_ + (uint16_t)carry_flag_; - - uint8_t low_nibble = (a_ & 0xf) + (operand_ & 0xf) + carry_flag_; - if(low_nibble >= 0xa) low_nibble = ((low_nibble + 0x6) & 0xf) + 0x10; - uint16_t result = (uint16_t)(a_ & 0xf0) + (uint16_t)(operand_ & 0xf0) + (uint16_t)low_nibble; - negative_result_ = (uint8_t)result; - overflow_flag_ = (( (result^a_)&(result^operand_) )&0x80) >> 1; - if(result >= 0xa0) result += 0x60; - - carry_flag_ = (result >> 8) ? 1 : 0; - a_ = (uint8_t)result; - zero_result_ = (uint8_t)decimalResult; - } else { - const uint16_t result = (uint16_t)a_ + (uint16_t)operand_ + (uint16_t)carry_flag_; - overflow_flag_ = (( (result^a_)&(result^operand_) )&0x80) >> 1; - negative_result_ = zero_result_ = a_ = (uint8_t)result; - carry_flag_ = (result >> 8)&1; - } - - // fix up in case this was INS - if(cycle == OperationINS) operand_ = ~operand_; - continue; - -#pragma mark - Shifts and Rolls - - case OperationASL: - carry_flag_ = operand_ >> 7; - operand_ <<= 1; - negative_result_ = zero_result_ = operand_; - continue; - - case OperationASO: - carry_flag_ = operand_ >> 7; - operand_ <<= 1; - a_ |= operand_; - negative_result_ = zero_result_ = a_; - continue; - - case OperationROL: { - const uint8_t temp8 = (uint8_t)((operand_ << 1) | carry_flag_); - carry_flag_ = operand_ >> 7; - operand_ = negative_result_ = zero_result_ = temp8; - } continue; - - case OperationRLA: { - const uint8_t temp8 = (uint8_t)((operand_ << 1) | carry_flag_); - carry_flag_ = operand_ >> 7; - operand_ = temp8; - a_ &= operand_; - negative_result_ = zero_result_ = a_; - } continue; - - case OperationLSR: - carry_flag_ = operand_ & 1; - operand_ >>= 1; - negative_result_ = zero_result_ = operand_; - continue; - - case OperationLSE: - carry_flag_ = operand_ & 1; - operand_ >>= 1; - a_ ^= operand_; - negative_result_ = zero_result_ = a_; - continue; - - case OperationASR: - a_ &= operand_; - carry_flag_ = a_ & 1; - a_ >>= 1; - negative_result_ = zero_result_ = a_; - continue; - - case OperationROR: { - const uint8_t temp8 = (uint8_t)((operand_ >> 1) | (carry_flag_ << 7)); - carry_flag_ = operand_ & 1; - operand_ = negative_result_ = zero_result_ = temp8; - } continue; - - case OperationRRA: { - const uint8_t temp8 = (uint8_t)((operand_ >> 1) | (carry_flag_ << 7)); - carry_flag_ = operand_ & 1; - operand_ = temp8; - } continue; - - case OperationDecrementOperand: operand_--; continue; - case OperationIncrementOperand: operand_++; continue; - - case OperationCLC: carry_flag_ = 0; continue; - case OperationCLI: inverse_interrupt_flag_ = Flag::Interrupt; continue; - case OperationCLV: overflow_flag_ = 0; continue; - case OperationCLD: decimal_flag_ = 0; continue; - - case OperationSEC: carry_flag_ = Flag::Carry; continue; - case OperationSEI: inverse_interrupt_flag_ = 0; continue; - case OperationSED: decimal_flag_ = Flag::Decimal; continue; - - case OperationINC: operand_++; negative_result_ = zero_result_ = operand_; continue; - case OperationDEC: operand_--; negative_result_ = zero_result_ = operand_; continue; - case OperationINX: x_++; negative_result_ = zero_result_ = x_; continue; - case OperationDEX: x_--; negative_result_ = zero_result_ = x_; continue; - case OperationINY: y_++; negative_result_ = zero_result_ = y_; continue; - case OperationDEY: y_--; negative_result_ = zero_result_ = y_; continue; - - case OperationANE: - a_ = (a_ | 0xee) & operand_ & x_; - negative_result_ = zero_result_ = a_; - continue; - - case OperationANC: - a_ &= operand_; - negative_result_ = zero_result_ = a_; - carry_flag_ = a_ >> 7; - continue; - - case OperationLAS: - a_ = x_ = s_ = s_ & operand_; - negative_result_ = zero_result_ = a_; - continue; - -#pragma mark - Addressing Mode Work - - case CycleAddXToAddressLow: - nextAddress.full = address_.full + x_; - address_.bytes.low = nextAddress.bytes.low; - if(address_.bytes.high != nextAddress.bytes.high) { - throwaway_read(address_.full); - break; - } - continue; - case CycleAddXToAddressLowRead: - nextAddress.full = address_.full + x_; - address_.bytes.low = nextAddress.bytes.low; - throwaway_read(address_.full); - 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); - break; - } - continue; - case CycleAddYToAddressLowRead: - nextAddress.full = address_.full + y_; - address_.bytes.low = nextAddress.bytes.low; - throwaway_read(address_.full); - break; - case OperationCorrectAddressHigh: - address_.full = nextAddress.full; - continue; - case CycleIncrementPCFetchAddressLowFromOperand: - pc_.full++; - read_mem(address_.bytes.low, operand_); - break; - case CycleAddXToOperandFetchAddressLow: - operand_ += x_; - read_mem(address_.bytes.low, operand_); - break; - case CycleIncrementOperandFetchAddressHigh: - operand_++; - read_mem(address_.bytes.high, operand_); - break; - case CycleIncrementPCReadPCHLoadPCL: // deliberate fallthrough - pc_.full++; - case CycleReadPCHLoadPCL: { - uint16_t oldPC = pc_.full; - pc_.bytes.low = operand_; - read_mem(pc_.bytes.high, oldPC); - } break; - - case CycleReadAddressHLoadAddressL: - address_.bytes.low = operand_; pc_.full++; - read_mem(address_.bytes.high, pc_.full); - break; - - case CycleLoadAddressAbsolute: { - uint16_t nextPC = pc_.full+1; - pc_.full += 2; - address_.bytes.low = operand_; - read_mem(address_.bytes.high, nextPC); - } break; - - case OperationLoadAddressZeroPage: - pc_.full++; - address_.full = operand_; - continue; - - case CycleLoadAddessZeroX: - pc_.full++; - address_.full = (operand_ + x_)&0xff; - throwaway_read(operand_); - break; - - case CycleLoadAddessZeroY: - pc_.full++; - address_.full = (operand_ + y_)&0xff; - throwaway_read(operand_); - 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; - -#pragma mark - Branching - -#define BRA(condition) pc_.full++; if(condition) scheduled_program_counter_ = doBranch - - case OperationBPL: BRA(!(negative_result_&0x80)); continue; - case OperationBMI: BRA(negative_result_&0x80); continue; - case OperationBVC: BRA(!overflow_flag_); continue; - case OperationBVS: BRA(overflow_flag_); continue; - case OperationBCC: BRA(!carry_flag_); continue; - case OperationBCS: BRA(carry_flag_); continue; - case OperationBNE: BRA(zero_result_); continue; - case OperationBEQ: BRA(!zero_result_); continue; - - case CycleAddSignedOperandToPC: - nextAddress.full = (uint16_t)(pc_.full + (int8_t)operand_); - pc_.bytes.low = nextAddress.bytes.low; - if(nextAddress.bytes.high != pc_.bytes.high) { - uint16_t halfUpdatedPc = pc_.full; - pc_.full = nextAddress.full; - throwaway_read(halfUpdatedPc); - break; - } - continue; - -#undef BRA - -#pragma mark - Transfers - - case OperationTXA: zero_result_ = negative_result_ = a_ = x_; continue; - case OperationTYA: zero_result_ = negative_result_ = a_ = y_; continue; - case OperationTXS: s_ = x_; continue; - case OperationTAY: zero_result_ = negative_result_ = y_ = a_; continue; - case OperationTAX: zero_result_ = negative_result_ = x_ = a_; continue; - case OperationTSX: zero_result_ = negative_result_ = x_ = s_; continue; - - case OperationARR: - if(decimal_flag_) { - a_ &= operand_; - uint8_t unshiftedA = a_; - a_ = (uint8_t)((a_ >> 1) | (carry_flag_ << 7)); - zero_result_ = negative_result_ = a_; - overflow_flag_ = (a_^(a_ << 1))&Flag::Overflow; - - if((unshiftedA&0xf) + (unshiftedA&0x1) > 5) a_ = ((a_ + 6)&0xf) | (a_ & 0xf0); - - carry_flag_ = ((unshiftedA&0xf0) + (unshiftedA&0x10) > 0x50) ? 1 : 0; - if(carry_flag_) a_ += 0x60; - } else { - a_ &= operand_; - a_ = (uint8_t)((a_ >> 1) | (carry_flag_ << 7)); - negative_result_ = zero_result_ = a_; - carry_flag_ = (a_ >> 6)&1; - overflow_flag_ = (a_^(a_ << 1))&Flag::Overflow; - } - continue; - - case OperationSBX: - x_ &= a_; - uint16_t difference = x_ - operand_; - x_ = (uint8_t)difference; - negative_result_ = zero_result_ = x_; - carry_flag_ = ((difference >> 8)&1)^1; - continue; - } - - if(uses_ready_line && ready_line_is_enabled_ && isReadOperation(nextBusOperation)) { - ready_is_active_ = true; - break; - } - bus_access(); - } - } - } - - cycles_left_to_run_ = number_of_cycles; - next_address_ = nextAddress; - next_bus_operation_ = nextBusOperation; - bus_address_ = busAddress; - bus_value_ = busValue; - - bus_handler_.flush(); - } - - /*! - Gets the value of a register. - - @see set_value_of_register - - @param r The register to set. - @returns The value of the register. 8-bit registers will be returned as unsigned. - */ - uint16_t get_value_of_register(Register r) { - switch (r) { - case Register::ProgramCounter: return pc_.full; - case Register::LastOperationAddress: return last_operation_pc_.full; - case Register::StackPointer: return s_; - case Register::Flags: return get_flags(); - case Register::A: return a_; - case Register::X: return x_; - case Register::Y: return y_; - case Register::S: return s_; - default: return 0; - } - } - - /*! - Sets the value of a register. - - @see get_value_of_register - - @param r The register to set. - @param value The value to set. If the register is only 8 bit, the value will be truncated. - */ - void set_value_of_register(Register r, uint16_t value) { - switch (r) { - case Register::ProgramCounter: pc_.full = value; break; - case Register::StackPointer: s_ = (uint8_t)value; break; - case Register::Flags: set_flags((uint8_t)value); break; - case Register::A: a_ = (uint8_t)value; break; - case Register::X: x_ = (uint8_t)value; break; - case Register::Y: y_ = (uint8_t)value; break; - case Register::S: s_ = (uint8_t)value; break; - default: break; - } - } + void run_for(const Cycles cycles); /*! Sets the current level of the RDY line. @param active @c true if the line is logically active; @c false otherwise. */ - inline void set_ready_line(bool active) { - assert(uses_ready_line); - if(active) { - ready_line_is_enabled_ = true; - } else { - ready_line_is_enabled_ = false; - ready_is_active_ = false; + void set_ready_line(bool active); + + private: + T &bus_handler_; +}; + + +/* + Implementation. Users: no need to read beyond here. +*/ +template void Processor::run_for(const Cycles cycles) { + static const MicroOp doBranch[] = { + CycleReadFromPC, + CycleAddSignedOperandToPC, + OperationMoveToNextProgram + }; + static uint8_t throwaway_target; + static const MicroOp fetch_decode_execute[] = { + CycleFetchOperation, + CycleFetchOperand, + OperationDecodeOperation + }; + + // These plus program below act to give the compiler permission to update these values + // without touching the class storage (i.e. it explicitly says they need be completely up + // to date in this stack frame only); which saves some complicated addressing + RegisterPair nextAddress = next_address_; + BusOperation nextBusOperation = next_bus_operation_; + uint16_t busAddress = bus_address_; + 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;\ +} + +#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; + + checkSchedule(); + Cycles number_of_cycles = cycles + cycles_left_to_run_; + + while(number_of_cycles > Cycles(0)) { + + 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_) { + if(nextBusOperation != BusOperation::None) { + bus_access(); + } + + while(1) { + + const MicroOp cycle = *scheduled_program_counter_; + scheduled_program_counter_++; + +#define read_op(val, addr) nextBusOperation = BusOperation::ReadOpcode; busAddress = addr; busValue = &val; val = 0xff +#define read_mem(val, addr) nextBusOperation = BusOperation::Read; busAddress = addr; busValue = &val; val = 0xff +#define throwaway_read(addr) nextBusOperation = BusOperation::Read; busAddress = addr; busValue = &throwaway_target; throwaway_target = 0xff +#define write_mem(val, addr) nextBusOperation = BusOperation::Write; busAddress = addr; busValue = &val + + switch(cycle) { + +#pragma mark - Fetch/Decode + + case CycleFetchOperation: { + last_operation_pc_ = pc_; + pc_.full++; + read_op(operation_, last_operation_pc_.full); + } break; + + case CycleFetchOperand: + read_mem(operand_, pc_.full); + break; + + case OperationDecodeOperation: + scheduled_program_counter_ = operations[operation_]; + continue; + + case OperationMoveToNextProgram: + scheduled_program_counter_ = nullptr; + checkSchedule(); + continue; + +#define push(v) {\ + uint16_t targetAddress = s_ | 0x100; s_--;\ + write_mem(v, targetAddress);\ +} + + case CycleIncPCPushPCH: pc_.full++; // deliberate fallthrough + case CyclePushPCH: push(pc_.bytes.high); break; + case CyclePushPCL: push(pc_.bytes.low); break; + case CyclePushOperand: push(operand_); break; + case CyclePushA: push(a_); break; + case CycleNoWritePush: { + uint16_t targetAddress = s_ | 0x100; s_--; + read_mem(operand_, targetAddress); + } + break; + +#undef push + + case CycleReadFromS: throwaway_read(s_ | 0x100); 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? + 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: inverse_interrupt_flag_ = 0; continue; + + case CyclePullPCL: s_++; read_mem(pc_.bytes.low, s_ | 0x100); break; + case CyclePullPCH: s_++; read_mem(pc_.bytes.high, s_ | 0x100); break; + case CyclePullA: s_++; read_mem(a_, s_ | 0x100); break; + case CyclePullOperand: s_++; read_mem(operand_, s_ | 0x100); break; + case OperationSetFlagsFromOperand: set_flags(operand_); continue; + case OperationSetOperandFromFlagsWithBRKSet: operand_ = get_flags() | Flag::Break; continue; + case OperationSetOperandFromFlags: operand_ = get_flags(); continue; + case OperationSetFlagsFromA: zero_result_ = negative_result_ = a_; continue; + + case CycleIncrementPCAndReadStack: pc_.full++; throwaway_read(s_ | 0x100); break; + case CycleReadPCLFromAddress: read_mem(pc_.bytes.low, address_.full); break; + case CycleReadPCHFromAddress: address_.bytes.low++; read_mem(pc_.bytes.high, address_.full); break; + + case CycleReadAndIncrementPC: { + uint16_t oldPC = pc_.full; + pc_.full++; + throwaway_read(oldPC); + } break; + +#pragma mark - JAM + + case CycleScheduleJam: { + is_jammed_ = true; + scheduled_program_counter_ = operations[CPU::MOS6502::JamOpcode]; + } continue; + +#pragma mark - Bitwise + + case OperationORA: a_ |= operand_; negative_result_ = zero_result_ = a_; continue; + case OperationAND: a_ &= operand_; negative_result_ = zero_result_ = a_; continue; + case OperationEOR: a_ ^= operand_; negative_result_ = zero_result_ = a_; continue; + +#pragma mark - Load and Store + + case OperationLDA: a_ = negative_result_ = zero_result_ = operand_; continue; + 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 OperationSTA: operand_ = a_; continue; + case OperationSTX: operand_ = x_; continue; + case OperationSTY: operand_ = y_; continue; + case OperationSAX: operand_ = a_ & x_; continue; + case OperationSHA: operand_ = a_ & x_ & (address_.bytes.high+1); continue; + case OperationSHX: operand_ = x_ & (address_.bytes.high+1); continue; + case OperationSHY: operand_ = y_ & (address_.bytes.high+1); continue; + case OperationSHS: s_ = a_ & x_; operand_ = s_ & (address_.bytes.high+1); continue; + + case OperationLXA: + a_ = x_ = (a_ | 0xee) & operand_; + negative_result_ = zero_result_ = a_; + continue; + +#pragma mark - Compare + + case OperationCMP: { + const uint16_t temp16 = a_ - operand_; + negative_result_ = zero_result_ = (uint8_t)temp16; + carry_flag_ = ((~temp16) >> 8)&1; + } continue; + case OperationCPX: { + const uint16_t temp16 = x_ - operand_; + negative_result_ = zero_result_ = (uint8_t)temp16; + carry_flag_ = ((~temp16) >> 8)&1; + } continue; + case OperationCPY: { + const uint16_t temp16 = y_ - operand_; + negative_result_ = zero_result_ = (uint8_t)temp16; + carry_flag_ = ((~temp16) >> 8)&1; + } continue; + +#pragma mark - BIT + + case OperationBIT: + zero_result_ = operand_ & a_; + negative_result_ = operand_; + overflow_flag_ = operand_&Flag::Overflow; + continue; + +#pragma mark ADC/SBC (and INS) + + case OperationINS: + operand_++; // deliberate fallthrough + case OperationSBC: + if(decimal_flag_) { + const uint16_t notCarry = carry_flag_ ^ 0x1; + const uint16_t decimalResult = (uint16_t)a_ - (uint16_t)operand_ - notCarry; + uint16_t temp16; + + temp16 = (a_&0xf) - (operand_&0xf) - notCarry; + if(temp16 > 0xf) temp16 -= 0x6; + temp16 = (temp16&0x0f) | ((temp16 > 0x0f) ? 0xfff0 : 0x00); + temp16 += (a_&0xf0) - (operand_&0xf0); + + overflow_flag_ = ( ( (decimalResult^a_)&(~decimalResult^operand_) )&0x80) >> 1; + negative_result_ = (uint8_t)temp16; + zero_result_ = (uint8_t)decimalResult; + + if(temp16 > 0xff) temp16 -= 0x60; + + carry_flag_ = (temp16 > 0xff) ? 0 : Flag::Carry; + a_ = (uint8_t)temp16; + continue; + } else { + operand_ = ~operand_; + } + + // deliberate fallthrough + case OperationADC: + if(decimal_flag_) { + const uint16_t decimalResult = (uint16_t)a_ + (uint16_t)operand_ + (uint16_t)carry_flag_; + + uint8_t low_nibble = (a_ & 0xf) + (operand_ & 0xf) + carry_flag_; + if(low_nibble >= 0xa) low_nibble = ((low_nibble + 0x6) & 0xf) + 0x10; + uint16_t result = (uint16_t)(a_ & 0xf0) + (uint16_t)(operand_ & 0xf0) + (uint16_t)low_nibble; + negative_result_ = (uint8_t)result; + overflow_flag_ = (( (result^a_)&(result^operand_) )&0x80) >> 1; + if(result >= 0xa0) result += 0x60; + + carry_flag_ = (result >> 8) ? 1 : 0; + a_ = (uint8_t)result; + zero_result_ = (uint8_t)decimalResult; + } else { + const uint16_t result = (uint16_t)a_ + (uint16_t)operand_ + (uint16_t)carry_flag_; + overflow_flag_ = (( (result^a_)&(result^operand_) )&0x80) >> 1; + negative_result_ = zero_result_ = a_ = (uint8_t)result; + carry_flag_ = (result >> 8)&1; + } + + // fix up in case this was INS + if(cycle == OperationINS) operand_ = ~operand_; + continue; + +#pragma mark - Shifts and Rolls + + case OperationASL: + carry_flag_ = operand_ >> 7; + operand_ <<= 1; + negative_result_ = zero_result_ = operand_; + continue; + + case OperationASO: + carry_flag_ = operand_ >> 7; + operand_ <<= 1; + a_ |= operand_; + negative_result_ = zero_result_ = a_; + continue; + + case OperationROL: { + const uint8_t temp8 = (uint8_t)((operand_ << 1) | carry_flag_); + carry_flag_ = operand_ >> 7; + operand_ = negative_result_ = zero_result_ = temp8; + } continue; + + case OperationRLA: { + const uint8_t temp8 = (uint8_t)((operand_ << 1) | carry_flag_); + carry_flag_ = operand_ >> 7; + operand_ = temp8; + a_ &= operand_; + negative_result_ = zero_result_ = a_; + } continue; + + case OperationLSR: + carry_flag_ = operand_ & 1; + operand_ >>= 1; + negative_result_ = zero_result_ = operand_; + continue; + + case OperationLSE: + carry_flag_ = operand_ & 1; + operand_ >>= 1; + a_ ^= operand_; + negative_result_ = zero_result_ = a_; + continue; + + case OperationASR: + a_ &= operand_; + carry_flag_ = a_ & 1; + a_ >>= 1; + negative_result_ = zero_result_ = a_; + continue; + + case OperationROR: { + const uint8_t temp8 = (uint8_t)((operand_ >> 1) | (carry_flag_ << 7)); + carry_flag_ = operand_ & 1; + operand_ = negative_result_ = zero_result_ = temp8; + } continue; + + case OperationRRA: { + const uint8_t temp8 = (uint8_t)((operand_ >> 1) | (carry_flag_ << 7)); + carry_flag_ = operand_ & 1; + operand_ = temp8; + } continue; + + case OperationDecrementOperand: operand_--; continue; + case OperationIncrementOperand: operand_++; continue; + + case OperationCLC: carry_flag_ = 0; continue; + case OperationCLI: inverse_interrupt_flag_ = Flag::Interrupt; continue; + case OperationCLV: overflow_flag_ = 0; continue; + case OperationCLD: decimal_flag_ = 0; continue; + + case OperationSEC: carry_flag_ = Flag::Carry; continue; + case OperationSEI: inverse_interrupt_flag_ = 0; continue; + case OperationSED: decimal_flag_ = Flag::Decimal; continue; + + case OperationINC: operand_++; negative_result_ = zero_result_ = operand_; continue; + case OperationDEC: operand_--; negative_result_ = zero_result_ = operand_; continue; + case OperationINX: x_++; negative_result_ = zero_result_ = x_; continue; + case OperationDEX: x_--; negative_result_ = zero_result_ = x_; continue; + case OperationINY: y_++; negative_result_ = zero_result_ = y_; continue; + case OperationDEY: y_--; negative_result_ = zero_result_ = y_; continue; + + case OperationANE: + a_ = (a_ | 0xee) & operand_ & x_; + negative_result_ = zero_result_ = a_; + continue; + + case OperationANC: + a_ &= operand_; + negative_result_ = zero_result_ = a_; + carry_flag_ = a_ >> 7; + continue; + + case OperationLAS: + a_ = x_ = s_ = s_ & operand_; + negative_result_ = zero_result_ = a_; + continue; + +#pragma mark - Addressing Mode Work + + case CycleAddXToAddressLow: + nextAddress.full = address_.full + x_; + address_.bytes.low = nextAddress.bytes.low; + if(address_.bytes.high != nextAddress.bytes.high) { + throwaway_read(address_.full); + break; + } + continue; + case CycleAddXToAddressLowRead: + nextAddress.full = address_.full + x_; + address_.bytes.low = nextAddress.bytes.low; + throwaway_read(address_.full); + 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); + break; + } + continue; + case CycleAddYToAddressLowRead: + nextAddress.full = address_.full + y_; + address_.bytes.low = nextAddress.bytes.low; + throwaway_read(address_.full); + break; + case OperationCorrectAddressHigh: + address_.full = nextAddress.full; + continue; + case CycleIncrementPCFetchAddressLowFromOperand: + pc_.full++; + read_mem(address_.bytes.low, operand_); + break; + case CycleAddXToOperandFetchAddressLow: + operand_ += x_; + read_mem(address_.bytes.low, operand_); + break; + case CycleIncrementOperandFetchAddressHigh: + operand_++; + read_mem(address_.bytes.high, operand_); + break; + case CycleIncrementPCReadPCHLoadPCL: // deliberate fallthrough + pc_.full++; + case CycleReadPCHLoadPCL: { + uint16_t oldPC = pc_.full; + pc_.bytes.low = operand_; + read_mem(pc_.bytes.high, oldPC); + } break; + + case CycleReadAddressHLoadAddressL: + address_.bytes.low = operand_; pc_.full++; + read_mem(address_.bytes.high, pc_.full); + break; + + case CycleLoadAddressAbsolute: { + uint16_t nextPC = pc_.full+1; + pc_.full += 2; + address_.bytes.low = operand_; + read_mem(address_.bytes.high, nextPC); + } break; + + case OperationLoadAddressZeroPage: + pc_.full++; + address_.full = operand_; + continue; + + case CycleLoadAddessZeroX: + pc_.full++; + address_.full = (operand_ + x_)&0xff; + throwaway_read(operand_); + break; + + case CycleLoadAddessZeroY: + pc_.full++; + address_.full = (operand_ + y_)&0xff; + throwaway_read(operand_); + 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; + +#pragma mark - Branching + +#define BRA(condition) pc_.full++; if(condition) scheduled_program_counter_ = doBranch + + case OperationBPL: BRA(!(negative_result_&0x80)); continue; + case OperationBMI: BRA(negative_result_&0x80); continue; + case OperationBVC: BRA(!overflow_flag_); continue; + case OperationBVS: BRA(overflow_flag_); continue; + case OperationBCC: BRA(!carry_flag_); continue; + case OperationBCS: BRA(carry_flag_); continue; + case OperationBNE: BRA(zero_result_); continue; + case OperationBEQ: BRA(!zero_result_); continue; + + case CycleAddSignedOperandToPC: + nextAddress.full = (uint16_t)(pc_.full + (int8_t)operand_); + pc_.bytes.low = nextAddress.bytes.low; + if(nextAddress.bytes.high != pc_.bytes.high) { + uint16_t halfUpdatedPc = pc_.full; + pc_.full = nextAddress.full; + throwaway_read(halfUpdatedPc); + break; + } + continue; + +#undef BRA + +#pragma mark - Transfers + + case OperationTXA: zero_result_ = negative_result_ = a_ = x_; continue; + case OperationTYA: zero_result_ = negative_result_ = a_ = y_; continue; + case OperationTXS: s_ = x_; continue; + case OperationTAY: zero_result_ = negative_result_ = y_ = a_; continue; + case OperationTAX: zero_result_ = negative_result_ = x_ = a_; continue; + case OperationTSX: zero_result_ = negative_result_ = x_ = s_; continue; + + case OperationARR: + if(decimal_flag_) { + a_ &= operand_; + uint8_t unshiftedA = a_; + a_ = (uint8_t)((a_ >> 1) | (carry_flag_ << 7)); + zero_result_ = negative_result_ = a_; + overflow_flag_ = (a_^(a_ << 1))&Flag::Overflow; + + if((unshiftedA&0xf) + (unshiftedA&0x1) > 5) a_ = ((a_ + 6)&0xf) | (a_ & 0xf0); + + carry_flag_ = ((unshiftedA&0xf0) + (unshiftedA&0x10) > 0x50) ? 1 : 0; + if(carry_flag_) a_ += 0x60; + } else { + a_ &= operand_; + a_ = (uint8_t)((a_ >> 1) | (carry_flag_ << 7)); + negative_result_ = zero_result_ = a_; + carry_flag_ = (a_ >> 6)&1; + overflow_flag_ = (a_^(a_ << 1))&Flag::Overflow; + } + continue; + + case OperationSBX: + x_ &= a_; + uint16_t difference = x_ - operand_; + x_ = (uint8_t)difference; + negative_result_ = zero_result_ = x_; + carry_flag_ = ((difference >> 8)&1)^1; + continue; + } + + if(uses_ready_line && ready_line_is_enabled_ && isReadOperation(nextBusOperation)) { + ready_is_active_ = true; + break; + } + bus_access(); } } + } - /*! - Sets the current level of the RST line. + cycles_left_to_run_ = number_of_cycles; + next_address_ = nextAddress; + next_bus_operation_ = nextBusOperation; + bus_address_ = busAddress; + bus_value_ = busValue; - @param active @c true if the line is logically active; @c false otherwise. - */ - inline void set_reset_line(bool active) { - interrupt_requests_ = (interrupt_requests_ & ~InterruptRequestFlags::Reset) | (active ? InterruptRequestFlags::Reset : 0); - } + bus_handler_.flush(); +} - /*! - Gets whether the 6502 would reset at the next opportunity. +template void Processor::set_ready_line(bool active) { + assert(uses_ready_line); + if(active) { + ready_line_is_enabled_ = true; + } else { + ready_line_is_enabled_ = false; + ready_is_active_ = false; + } +} - @returns @c true if the line is logically active; @c false otherwise. - */ - inline bool get_is_resetting() { - return !!(interrupt_requests_ & (InterruptRequestFlags::Reset | InterruptRequestFlags::PowerOn)); - } +void ProcessorBase::set_reset_line(bool active) { + interrupt_requests_ = (interrupt_requests_ & ~InterruptRequestFlags::Reset) | (active ? InterruptRequestFlags::Reset : 0); +} - /*! - This emulation automatically sets itself up in power-on state at creation, which has the effect of triggering a - reset at the first opportunity. Use @c set_power_on to disable that behaviour. - */ - inline void set_power_on(bool active) { - interrupt_requests_ = (interrupt_requests_ & ~InterruptRequestFlags::PowerOn) | (active ? InterruptRequestFlags::PowerOn : 0); - } +bool ProcessorBase::get_is_resetting() { + return !!(interrupt_requests_ & (InterruptRequestFlags::Reset | InterruptRequestFlags::PowerOn)); +} - /*! - Sets the current level of the IRQ line. +void ProcessorBase::set_power_on(bool active) { + interrupt_requests_ = (interrupt_requests_ & ~InterruptRequestFlags::PowerOn) | (active ? InterruptRequestFlags::PowerOn : 0); +} - @param active @c true if the line is logically active; @c false otherwise. - */ - inline void set_irq_line(bool active) { - irq_line_ = active ? Flag::Interrupt : 0; - } +void ProcessorBase::set_irq_line(bool active) { + irq_line_ = active ? Flag::Interrupt : 0; +} - /*! - Sets the current level of the set overflow line. +void ProcessorBase::set_overflow_line(bool active) { + // a leading edge will set the overflow flag + if(active && !set_overflow_line_is_enabled_) + overflow_flag_ = Flag::Overflow; + set_overflow_line_is_enabled_ = active; +} - @param active @c true if the line is logically active; @c false otherwise. - */ - inline void set_overflow_line(bool active) { - // a leading edge will set the overflow flag - if(active && !set_overflow_line_is_enabled_) - overflow_flag_ = Flag::Overflow; - set_overflow_line_is_enabled_ = active; - } +void ProcessorBase::set_nmi_line(bool active) { + // NMI is edge triggered, not level + if(active && !nmi_line_is_enabled_) + interrupt_requests_ |= InterruptRequestFlags::NMI; + nmi_line_is_enabled_ = active; +} - /*! - Sets the current level of the NMI line. +inline const ProcessorBase::MicroOp *ProcessorBase::get_reset_program() { + static const MicroOp reset[] = { + CycleFetchOperand, + CycleFetchOperand, + CycleNoWritePush, + CycleNoWritePush, + OperationRSTPickVector, + CycleNoWritePush, + CycleReadVectorLow, + CycleReadVectorHigh, + OperationMoveToNextProgram + }; + return reset; +} - @param active `true` if the line is logically active; `false` otherwise. - */ - inline void set_nmi_line(bool active) { - // NMI is edge triggered, not level - if(active && !nmi_line_is_enabled_) - interrupt_requests_ |= InterruptRequestFlags::NMI; - nmi_line_is_enabled_ = active; - } +inline const ProcessorBase::MicroOp *ProcessorBase::get_irq_program() { + static const MicroOp reset[] = { + CycleFetchOperand, + CycleFetchOperand, + CyclePushPCH, + CyclePushPCL, + OperationBRKPickVector, + OperationSetOperandFromFlags, + CyclePushOperand, + OperationSetI, + CycleReadVectorLow, + CycleReadVectorHigh, + OperationMoveToNextProgram + }; + return reset; +} - /*! - Queries whether the 6502 is now 'jammed'; i.e. has entered an invalid state - such that it will not of itself perform any more meaningful processing. +inline const ProcessorBase::MicroOp *ProcessorBase::get_nmi_program() { + static const MicroOp reset[] = { + CycleFetchOperand, + CycleFetchOperand, + CyclePushPCH, + CyclePushPCL, + OperationNMIPickVector, + OperationSetOperandFromFlags, + CyclePushOperand, + CycleReadVectorLow, + CycleReadVectorHigh, + OperationMoveToNextProgram + }; + return reset; +} - @returns @c true if the 6502 is jammed; @c false otherwise. - */ - inline bool is_jammed() { - return is_jammed_; - } -}; +uint8_t ProcessorBase::get_flags() { + return carry_flag_ | overflow_flag_ | (inverse_interrupt_flag_ ^ Flag::Interrupt) | (negative_result_ & 0x80) | (zero_result_ ? 0 : Flag::Zero) | Flag::Always | decimal_flag_; +} + +void ProcessorBase::set_flags(uint8_t flags) { + carry_flag_ = flags & Flag::Carry; + negative_result_ = flags & Flag::Sign; + zero_result_ = (~flags) & Flag::Zero; + overflow_flag_ = flags & Flag::Overflow; + inverse_interrupt_flag_ = (~flags) & Flag::Interrupt; + decimal_flag_ = flags & Flag::Decimal; +} } } diff --git a/Processors/6502/6502AllRAM.cpp b/Processors/6502/AllRAM/6502AllRAM.cpp similarity index 100% rename from Processors/6502/6502AllRAM.cpp rename to Processors/6502/AllRAM/6502AllRAM.cpp diff --git a/Processors/6502/6502AllRAM.hpp b/Processors/6502/AllRAM/6502AllRAM.hpp similarity index 92% rename from Processors/6502/6502AllRAM.hpp rename to Processors/6502/AllRAM/6502AllRAM.hpp index bfee139f6..084dd4d97 100644 --- a/Processors/6502/6502AllRAM.hpp +++ b/Processors/6502/AllRAM/6502AllRAM.hpp @@ -9,8 +9,8 @@ #ifndef MOS6502AllRAM_cpp #define MOS6502AllRAM_cpp -#include "6502.hpp" -#include "../AllRAMProcessor.hpp" +#include "../6502.hpp" +#include "../../AllRAMProcessor.hpp" namespace CPU { namespace MOS6502 { diff --git a/Processors/6502/6502.cpp b/Processors/6502/Implementation/6502Base.cpp similarity index 90% rename from Processors/6502/6502.cpp rename to Processors/6502/Implementation/6502Base.cpp index 3b277e07e..9f14b6189 100644 --- a/Processors/6502/6502.cpp +++ b/Processors/6502/Implementation/6502Base.cpp @@ -1,12 +1,14 @@ // -// 6502.cpp -// CLK +// 6502Base.cpp +// Clock Signal // -// Created by Thomas Harte on 09/07/2015. -// Copyright © 2015 Thomas Harte. All rights reserved. +// Created by Thomas Harte on 31/08/2017. +// Copyright © 2017 Thomas Harte. All rights reserved. // -#include "6502.hpp" +#include "../6502.hpp" + +using namespace CPU::MOS6502; const uint8_t CPU::MOS6502::JamOpcode = 0xf2; @@ -67,7 +69,7 @@ const uint8_t CPU::MOS6502::JamOpcode = 0xf2; #define JAM {CycleFetchOperand, CycleScheduleJam} -const CPU::MOS6502::ProcessorBase::MicroOp CPU::MOS6502::ProcessorBase::operations[256][10] = { +const ProcessorBase::MicroOp ProcessorBase::operations[256][10] = { /* 0x00 BRK */ Program(CycleIncPCPushPCH, CyclePushPCL, OperationBRKPickVector, OperationSetOperandFromFlagsWithBRKSet, CyclePushOperand, OperationSetI, CycleReadVectorLow, CycleReadVectorHigh), /* 0x01 ORA x, ind */ IndexedIndirectRead(OperationORA), /* 0x02 JAM */ JAM, /* 0x03 ASO x, ind */ IndexedIndirectReadModifyWrite(OperationASO), @@ -243,3 +245,54 @@ const CPU::MOS6502::ProcessorBase::MicroOp CPU::MOS6502::ProcessorBase::operatio #undef IndirectIndexedReadModify #undef Immediate #undef Implied + +uint16_t ProcessorBase::get_value_of_register(Register r) { + switch (r) { + case Register::ProgramCounter: return pc_.full; + case Register::LastOperationAddress: return last_operation_pc_.full; + case Register::StackPointer: return s_; + case Register::Flags: return get_flags(); + case Register::A: return a_; + case Register::X: return x_; + case Register::Y: return y_; + case Register::S: return s_; + default: return 0; + } +} + +void ProcessorBase::set_value_of_register(Register r, uint16_t value) { + switch (r) { + case Register::ProgramCounter: pc_.full = value; break; + case Register::StackPointer: s_ = (uint8_t)value; break; + case Register::Flags: set_flags((uint8_t)value); break; + case Register::A: a_ = (uint8_t)value; break; + case Register::X: x_ = (uint8_t)value; break; + case Register::Y: y_ = (uint8_t)value; break; + case Register::S: s_ = (uint8_t)value; break; + default: break; + } +} + +bool ProcessorBase::is_jammed() { + return is_jammed_; +} + +ProcessorBase::ProcessorBase() : + is_jammed_(false), + ready_line_is_enabled_(false), + ready_is_active_(false), + inverse_interrupt_flag_(0), + irq_request_history_(0), + s_(0), + next_bus_operation_(BusOperation::None), + interrupt_requests_(InterruptRequestFlags::PowerOn), + irq_line_(0), + nmi_line_is_enabled_(false), + set_overflow_line_is_enabled_(false), + scheduled_program_counter_(nullptr) { + // only the interrupt flag is defined upon reset but get_flags isn't going to + // mask the other flags so we need to do that, at least + carry_flag_ &= Flag::Carry; + decimal_flag_ &= Flag::Decimal; + overflow_flag_ &= Flag::Overflow; +}