From c0a1c3401236378edfd77c2cfb47451b798c6980 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 15 Oct 2020 18:42:38 -0400 Subject: [PATCH] Wraps all registers into a struct, so that I can implement abort. Makes some preparations for ready too. --- Processors/6502Esque/6502Selector.hpp | 4 +- Processors/65816/65816.hpp | 19 +- Processors/65816/Implementation/65816Base.cpp | 38 +- .../Implementation/65816Implementation.hpp | 441 +++++++++--------- .../65816/Implementation/65816Storage.cpp | 56 +-- .../65816/Implementation/65816Storage.hpp | 57 ++- 6 files changed, 321 insertions(+), 294 deletions(-) diff --git a/Processors/6502Esque/6502Selector.hpp b/Processors/6502Esque/6502Selector.hpp index 411bba40f..d91932c17 100644 --- a/Processors/6502Esque/6502Selector.hpp +++ b/Processors/6502Esque/6502Selector.hpp @@ -36,8 +36,8 @@ template class }; template class Processor: - public CPU::WDC65816::Processor { - using CPU::WDC65816::Processor::Processor; + public CPU::WDC65816::Processor { + using CPU::WDC65816::Processor::Processor; }; } diff --git a/Processors/65816/65816.hpp b/Processors/65816/65816.hpp index b9e5cfbc1..f98d9deb5 100644 --- a/Processors/65816/65816.hpp +++ b/Processors/65816/65816.hpp @@ -34,17 +34,34 @@ class ProcessorBase: protected ProcessorStorage { inline void set_irq_line(bool); inline void set_nmi_line(bool); inline void set_reset_line(bool); + inline void set_abort_line(bool); void set_value_of_register(Register r, uint16_t value); inline bool is_jammed() const; uint16_t get_value_of_register(Register r) const; }; -template class Processor: public ProcessorBase { +template class Processor: public ProcessorBase { public: + /*! + Constructs an instance of the 6502 that will use @c bus_handler for all bus communications. + */ Processor(BusHandler &bus_handler) : bus_handler_(bus_handler) {} + + /*! + Runs the 6502 for a supplied number of cycles. + + @param cycles The number of cycles to run the 6502 for. + */ 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. + */ + void set_ready_line(bool active); + private: BusHandler &bus_handler_; }; diff --git a/Processors/65816/Implementation/65816Base.cpp b/Processors/65816/Implementation/65816Base.cpp index d68f21007..fed760fa7 100644 --- a/Processors/65816/Implementation/65816Base.cpp +++ b/Processors/65816/Implementation/65816Base.cpp @@ -12,33 +12,33 @@ using namespace CPU::WDC65816; uint16_t ProcessorBase::get_value_of_register(Register r) const { switch (r) { - case Register::ProgramCounter: return pc_; + case Register::ProgramCounter: return registers_.pc; case Register::LastOperationAddress: return last_operation_pc_; - case Register::StackPointer: return s_.full; + case Register::StackPointer: return registers_.s.full; case Register::Flags: return get_flags(); - case Register::A: return a_.full; - case Register::X: return x_.full; - case Register::Y: return y_.full; - case Register::EmulationFlag: return emulation_flag_; - case Register::DataBank: return data_bank_ >> 16; - case Register::ProgramBank: return program_bank_ >> 16; - case Register::Direct: return direct_; + case Register::A: return registers_.a.full; + case Register::X: return registers_.x.full; + case Register::Y: return registers_.y.full; + case Register::EmulationFlag: return registers_.emulation_flag; + case Register::DataBank: return registers_.data_bank >> 16; + case Register::ProgramBank: return registers_.program_bank >> 16; + case Register::Direct: return registers_.direct; default: return 0; } } void ProcessorBase::set_value_of_register(Register r, uint16_t value) { switch (r) { - case Register::ProgramCounter: pc_ = value; break; - case Register::StackPointer: s_.full = value; break; - case Register::Flags: set_flags(uint8_t(value)); break; - case Register::A: a_.full = value; break; - case Register::X: x_.full = value; break; - case Register::Y: y_.full = value; break; - case Register::EmulationFlag: set_emulation_mode(value); break; - case Register::DataBank: data_bank_ = uint32_t(value) << 16; break; - case Register::ProgramBank: program_bank_ = uint32_t(value) << 16; break; - case Register::Direct: direct_ = value; break; + case Register::ProgramCounter: registers_.pc = value; break; + case Register::StackPointer: registers_.s.full = value; break; + case Register::Flags: set_flags(uint8_t(value)); break; + case Register::A: registers_.a.full = value; break; + case Register::X: registers_.x.full = value; break; + case Register::Y: registers_.y.full = value; break; + case Register::EmulationFlag: set_emulation_mode(value); break; + case Register::DataBank: registers_.data_bank = uint32_t(value) << 16; break; + case Register::ProgramBank: registers_.program_bank = uint32_t(value) << 16; break; + case Register::Direct: registers_.direct = value; break; default: break; } } diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp index 5c99ffe34..1080bbe2b 100644 --- a/Processors/65816/Implementation/65816Implementation.hpp +++ b/Processors/65816/Implementation/65816Implementation.hpp @@ -6,27 +6,22 @@ // Copyright © 2020 Thomas Harte. All rights reserved. // -template void Processor::run_for(const Cycles cycles) { - // Temporary storage for the next bus cycle. - uint32_t bus_address = 0; - uint8_t *bus_value = nullptr; - uint8_t throwaway = 0; - BusOperation bus_operation = BusOperation::None; +template void Processor::run_for(const Cycles cycles) { #define perform_bus(address, value, operation) \ - bus_address = address; \ - bus_value = value; \ - bus_operation = operation + bus_address_ = address; \ + bus_value_ = value; \ + bus_operation_ = operation #define read(address, value) perform_bus(address, value, MOS6502Esque::Read) #define write(address, value) perform_bus(address, value, MOS6502Esque::Write) -#define m_flag() mx_flags_[0] -#define x_flag() mx_flags_[1] +#define m_flag() registers_.mx_flags[0] +#define x_flag() registers_.mx_flags[1] -#define x() (x_.full & x_masks_[1]) -#define y() (y_.full & x_masks_[1]) -#define stack_address() ((s_.full & e_masks_[1]) | (0x0100 & e_masks_[0])) +#define x() (registers_.x.full & registers_.x_masks[1]) +#define y() (registers_.y.full & registers_.x_masks[1]) +#define stack_address() ((registers_.s.full & registers_.e_masks[1]) | (0x0100 & registers_.e_masks[0])) Cycles number_of_cycles = cycles + cycles_left_to_run_; while(number_of_cycles > Cycles(0)) { @@ -35,7 +30,7 @@ template void Processor::run_for(const Cycles #ifndef NDEBUG // As a sanity check. - bus_value = nullptr; + bus_value_ = nullptr; #endif switch(operation) { @@ -52,13 +47,13 @@ template void Processor::run_for(const Cycles next_op_ = µ_ops_[offset]; instruction_buffer_.clear(); data_buffer_.clear(); - last_operation_pc_ = pc_; + last_operation_pc_ = registers_.pc; } continue; case OperationDecode: { active_instruction_ = &instructions[instruction_buffer_.value]; - const auto size_flag = mx_flags_[active_instruction_->size_field]; + const auto size_flag = registers_.mx_flags[active_instruction_->size_field]; next_op_ = µ_ops_[active_instruction_->program_offsets[size_flag]]; instruction_buffer_.clear(); } continue; @@ -68,21 +63,21 @@ template void Processor::run_for(const Cycles // case CycleFetchIncrementPC: - read(pc_ | program_bank_, instruction_buffer_.next_input()); - ++pc_; + read(registers_.pc | registers_.program_bank, instruction_buffer_.next_input()); + ++registers_.pc; break; case CycleFetchOpcode: - perform_bus(pc_ | program_bank_, instruction_buffer_.next_input(), MOS6502Esque::ReadOpcode); - ++pc_; + perform_bus(registers_.pc | registers_.program_bank, instruction_buffer_.next_input(), MOS6502Esque::ReadOpcode); + ++registers_.pc; break; case CycleFetchPC: - read(pc_ | program_bank_, instruction_buffer_.next_input()); + read(registers_.pc | registers_.program_bank, instruction_buffer_.next_input()); break; case CycleFetchPCThrowaway: - read(pc_ | program_bank_, &throwaway); + read(registers_.pc | registers_.program_bank, &bus_throwaway_); break; // @@ -98,11 +93,11 @@ template void Processor::run_for(const Cycles break; case CycleFetchDataThrowaway: - read(data_address_, &throwaway); + read(data_address_, &bus_throwaway_); break; case CycleFetchIncorrectDataAddress: - read(incorrect_data_address_, &throwaway); + read(incorrect_data_address_, &bus_throwaway_); break; case CycleFetchIncrementData: @@ -133,7 +128,7 @@ template void Processor::run_for(const Cycles break; case CycleFetchBlockY: - read(((instruction_buffer_.value & 0xff00) << 8) | y(), &throwaway); + read(((instruction_buffer_.value & 0xff00) << 8) | y(), &bus_throwaway_); break; case CycleStoreBlockY: @@ -148,28 +143,28 @@ template void Processor::run_for(const Cycles // #define stack_access(value, operation) \ - bus_address = stack_address(); \ - bus_value = value; \ - bus_operation = operation; + bus_address_ = stack_address(); \ + bus_value_ = value; \ + bus_operation_ = operation; case CyclePush: stack_access(data_buffer_.next_output_descending(), MOS6502Esque::Write); - --s_.full; + --registers_.s.full; break; case CyclePullIfNotEmulation: - if(emulation_flag_) { + if(registers_.emulation_flag) { continue; } [[fallthrough]]; case CyclePull: - ++s_.full; + ++registers_.s.full; stack_access(data_buffer_.next_input(), MOS6502Esque::Read); break; case CycleAccessStack: - stack_access(&throwaway, MOS6502Esque::Read); + stack_access(&bus_throwaway_, MOS6502Esque::Read); break; #undef stack_access @@ -193,7 +188,7 @@ template void Processor::run_for(const Cycles case OperationCopyPCToData: data_buffer_.size = 2; - data_buffer_.value = pc_; + data_buffer_.value = registers_.pc; continue; case OperationCopyInstructionToData: @@ -206,21 +201,21 @@ template void Processor::run_for(const Cycles continue; case OperationCopyAToData: - data_buffer_.value = a_.full & m_masks_[1]; + data_buffer_.value = registers_.a.full & registers_.m_masks[1]; data_buffer_.size = 2 - m_flag(); continue; case OperationCopyDataToA: - a_.full = (a_.full & m_masks_[0]) + (data_buffer_.value & m_masks_[1]); + registers_.a.full = (registers_.a.full & registers_.m_masks[0]) + (data_buffer_.value & registers_.m_masks[1]); continue; case OperationCopyPBRToData: data_buffer_.size = 1; - data_buffer_.value = program_bank_ >> 16; + data_buffer_.value = registers_.program_bank >> 16; continue; case OperationCopyDataToPC: - pc_ = uint16_t(data_buffer_.value); + registers_.pc = uint16_t(data_buffer_.value); continue; // @@ -228,7 +223,7 @@ template void Processor::run_for(const Cycles // case OperationConstructAbsolute: - data_address_ = instruction_buffer_.value + data_bank_; + data_address_ = instruction_buffer_.value + registers_.data_bank; data_address_increment_mask_ = 0xff'ff'ff; continue; @@ -244,7 +239,7 @@ template void Processor::run_for(const Cycles // Used for JMP and JSR (absolute, x). case OperationConstructAbsoluteIndexedIndirect: - data_address_ = program_bank_ + ((instruction_buffer_.value + x()) & 0xffff); + data_address_ = registers_.program_bank + ((instruction_buffer_.value + x()) & 0xffff); data_address_increment_mask_ = 0x00'ff'ff; continue; @@ -255,8 +250,8 @@ template void Processor::run_for(const Cycles case OperationConstructAbsoluteXRead: case OperationConstructAbsoluteX: - data_address_ = instruction_buffer_.value + x() + data_bank_; - incorrect_data_address_ = (data_address_ & 0xff) | (instruction_buffer_.value & 0xff00) + data_bank_; + data_address_ = instruction_buffer_.value + x() + registers_.data_bank; + incorrect_data_address_ = (data_address_ & 0xff) | (instruction_buffer_.value & 0xff00) + registers_.data_bank; // If the incorrect address isn't actually incorrect, skip its usage. if(operation == OperationConstructAbsoluteXRead && data_address_ == incorrect_data_address_) { @@ -267,8 +262,8 @@ template void Processor::run_for(const Cycles case OperationConstructAbsoluteYRead: case OperationConstructAbsoluteY: - data_address_ = instruction_buffer_.value + y() + data_bank_; - incorrect_data_address_ = (data_address_ & 0xff) + (instruction_buffer_.value & 0xff00) + data_bank_; + data_address_ = instruction_buffer_.value + y() + registers_.data_bank; + incorrect_data_address_ = (data_address_ & 0xff) + (instruction_buffer_.value & 0xff00) + registers_.data_bank; // If the incorrect address isn't actually incorrect, skip its usage. if(operation == OperationConstructAbsoluteYRead && data_address_ == incorrect_data_address_) { @@ -278,38 +273,38 @@ template void Processor::run_for(const Cycles continue; case OperationConstructDirect: - data_address_ = (direct_ + instruction_buffer_.value) & 0xffff; + data_address_ = (registers_.direct + instruction_buffer_.value) & 0xffff; data_address_increment_mask_ = 0x00'ff'ff; - if(!(direct_&0xff)) { + if(!(registers_.direct&0xff)) { // If the low byte is 0 and this is emulation mode, incrementing // is restricted to the low byte. - data_address_increment_mask_ = e_masks_[1]; + data_address_increment_mask_ = registers_.e_masks[1]; ++next_op_; } continue; case OperationConstructDirectLong: - data_address_ = (direct_ + instruction_buffer_.value) & 0xffff; + data_address_ = (registers_.direct + instruction_buffer_.value) & 0xffff; data_address_increment_mask_ = 0x00'ff'ff; - if(!(direct_&0xff)) { + if(!(registers_.direct&0xff)) { ++next_op_; } continue; case OperationConstructDirectIndirect: - data_address_ = data_bank_ + data_buffer_.value; + data_address_ = registers_.data_bank + data_buffer_.value; data_address_increment_mask_ = 0xff'ff'ff; data_buffer_.clear(); continue; case OperationConstructDirectIndexedIndirect: - data_address_ = data_bank_ + ( - ((direct_ + x() + instruction_buffer_.value) & e_masks_[1]) + - (direct_ & e_masks_[0]) + data_address_ = registers_.data_bank + ( + ((registers_.direct + x() + instruction_buffer_.value) & registers_.e_masks[1]) + + (registers_.direct & registers_.e_masks[0]) ) & 0xffff; data_address_increment_mask_ = 0x00'ff'ff; - if(!(direct_&0xff)) { + if(!(registers_.direct&0xff)) { ++next_op_; } continue; @@ -330,43 +325,43 @@ template void Processor::run_for(const Cycles case OperationConstructDirectX: data_address_ = ( - (direct_ & e_masks_[0]) + - ((instruction_buffer_.value + direct_ + x()) & e_masks_[1]) + (registers_.direct & registers_.e_masks[0]) + + ((instruction_buffer_.value + registers_.direct + x()) & registers_.e_masks[1]) ) & 0xffff; data_address_increment_mask_ = 0x00'ff'ff; - incorrect_data_address_ = (direct_ & 0xff00) + (data_address_ & 0x00ff); - if(!(direct_&0xff)) { + incorrect_data_address_ = (registers_.direct & 0xff00) + (data_address_ & 0x00ff); + if(!(registers_.direct&0xff)) { ++next_op_; } continue; case OperationConstructDirectY: data_address_ = ( - (direct_ & e_masks_[0]) + - ((instruction_buffer_.value + direct_ + y()) & e_masks_[1]) + (registers_.direct & registers_.e_masks[0]) + + ((instruction_buffer_.value + registers_.direct + y()) & registers_.e_masks[1]) ) & 0xffff; data_address_increment_mask_ = 0x00'ff'ff; - incorrect_data_address_ = (direct_ & 0xff00) + (data_address_ & 0x00ff); - if(!(direct_&0xff)) { + incorrect_data_address_ = (registers_.direct & 0xff00) + (data_address_ & 0x00ff); + if(!(registers_.direct&0xff)) { ++next_op_; } continue; case OperationConstructStackRelative: - data_address_ = (s_.full + instruction_buffer_.value) & 0xffff; + data_address_ = (registers_.s.full + instruction_buffer_.value) & 0xffff; data_address_increment_mask_ = 0x00'ff'ff; continue; case OperationConstructStackRelativeIndexedIndirect: - data_address_ = data_bank_ + data_buffer_.value + y(); + data_address_ = registers_.data_bank + data_buffer_.value + y(); data_address_increment_mask_ = 0xff'ff'ff; data_buffer_.clear(); continue; case OperationConstructPER: - data_buffer_.value = instruction_buffer_.value + pc_; + data_buffer_.value = instruction_buffer_.value + registers_.pc; data_buffer_.size = 2; continue; @@ -389,31 +384,31 @@ template void Processor::run_for(const Cycles } else if(pending_exceptions_ & NMI) { pending_exceptions_ &= ~NMI; data_address_ = 0xfffa; - } else if(pending_exceptions_ & IRQ & flags_.inverse_interrupt) { + } else if(pending_exceptions_ & IRQ & registers_.flags.inverse_interrupt) { pending_exceptions_ &= ~IRQ; data_address_ = 0xfffe; } else { is_brk = active_instruction_ == instructions; // Given that BRK has opcode 00. if(is_brk) { - data_address_ = emulation_flag_ ? 0xfffe : 0xfff6; + data_address_ = registers_.emulation_flag ? 0xfffe : 0xfff6; } else { // Implicitly: COP. data_address_ = 0xfff4; } } - data_buffer_.value = (pc_ << 8) | get_flags(); - if(emulation_flag_) { + data_buffer_.value = (registers_.pc << 8) | get_flags(); + if(registers_.emulation_flag) { if(is_brk) data_buffer_.value |= Flag::Break; data_buffer_.size = 3; ++next_op_; } else { - data_buffer_.value |= program_bank_ << 24; + data_buffer_.value |= registers_.program_bank << 24; data_buffer_.size = 4; - program_bank_ = 0; + registers_.program_bank = 0; } - flags_.inverse_interrupt = 0; + registers_.flags.inverse_interrupt = 0; } continue; // @@ -421,10 +416,10 @@ template void Processor::run_for(const Cycles // #define LD(dest, src, masks) dest.full = (dest.full & masks[0]) | (src & masks[1]) -#define m_top() (instruction_buffer_.value >> m_shift_) & 0xff -#define x_top() (x_.full >> x_shift_) & 0xff -#define y_top() (y_.full >> x_shift_) & 0xff -#define a_top() (a_.full >> m_shift_) & 0xff +#define m_top() (instruction_buffer_.value >> registers_.m_shift) & 0xff +#define x_top() (registers_.x.full >> registers_.x_shift) & 0xff +#define y_top() (registers_.y.full >> registers_.x_shift) & 0xff +#define a_top() (registers_.a.full >> registers_.m_shift) & 0xff case OperationPerform: switch(active_instruction_->operation) { @@ -434,28 +429,28 @@ template void Processor::run_for(const Cycles // case LDA: - LD(a_, data_buffer_.value, m_masks_); - flags_.set_nz(a_.full, m_shift_); + LD(registers_.a, data_buffer_.value, registers_.m_masks); + registers_.flags.set_nz(registers_.a.full, registers_.m_shift); break; case LDX: - LD(x_, data_buffer_.value, x_masks_); - flags_.set_nz(x_.full, x_shift_); + LD(registers_.x, data_buffer_.value, registers_.x_masks); + registers_.flags.set_nz(registers_.x.full, registers_.x_shift); break; case LDY: - LD(y_, data_buffer_.value, x_masks_); - flags_.set_nz(y_.full, x_shift_); + LD(registers_.y, data_buffer_.value, registers_.x_masks); + registers_.flags.set_nz(registers_.y.full, registers_.x_shift); break; case PLB: - data_bank_ = (data_buffer_.value & 0xff) << 16; - flags_.set_nz(instruction_buffer_.value); + registers_.data_bank = (data_buffer_.value & 0xff) << 16; + registers_.flags.set_nz(instruction_buffer_.value); break; case PLD: - direct_ = data_buffer_.value; - flags_.set_nz(instruction_buffer_.value); + registers_.direct = data_buffer_.value; + registers_.flags.set_nz(instruction_buffer_.value); break; case PLP: @@ -463,7 +458,7 @@ template void Processor::run_for(const Cycles break; case STA: - data_buffer_.value = a_.full & m_masks_[1]; + data_buffer_.value = registers_.a.full & registers_.m_masks[1]; data_buffer_.size = 2 - m_flag(); break; @@ -473,27 +468,27 @@ template void Processor::run_for(const Cycles break; case STX: - data_buffer_.value = x_.full & x_masks_[1]; + data_buffer_.value = registers_.x.full & registers_.x_masks[1]; data_buffer_.size = 2 - x_flag(); break; case STY: - data_buffer_.value = y_.full & x_masks_[1]; + data_buffer_.value = registers_.y.full & registers_.x_masks[1]; data_buffer_.size = 2 - m_flag(); break; case PHB: - data_buffer_.value = data_bank_ >> 16; + data_buffer_.value = registers_.data_bank >> 16; data_buffer_.size = 1; break; case PHK: - data_buffer_.value = program_bank_ >> 16; + data_buffer_.value = registers_.program_bank >> 16; data_buffer_.size = 1; break; case PHD: - data_buffer_.value = direct_; + data_buffer_.value = registers_.direct; data_buffer_.size = 2; break; @@ -501,7 +496,7 @@ template void Processor::run_for(const Cycles data_buffer_.value = get_flags(); data_buffer_.size = 1; - if(emulation_flag_) { + if(registers_.emulation_flag) { // On the 6502, the break flag is set during a PHP. data_buffer_.value |= Flag::Break; } @@ -514,69 +509,69 @@ template void Processor::run_for(const Cycles // (and make reasonable guesses as to the N flag). case TXS: - s_ = x_.full & x_masks_[1]; + registers_.s = registers_.x.full & registers_.x_masks[1]; break; case TSX: - LD(x_, s_.full, x_masks_); - flags_.set_nz(x_.full, x_shift_); + LD(registers_.x, registers_.s.full, registers_.x_masks); + registers_.flags.set_nz(registers_.x.full, registers_.x_shift); break; case TXY: - LD(y_, x_.full, x_masks_); - flags_.set_nz(y_.full, x_shift_); + LD(registers_.y, registers_.x.full, registers_.x_masks); + registers_.flags.set_nz(registers_.y.full, registers_.x_shift); break; case TYX: - LD(x_, y_.full, x_masks_); - flags_.set_nz(x_.full, x_shift_); + LD(registers_.x, registers_.y.full, registers_.x_masks); + registers_.flags.set_nz(registers_.x.full, registers_.x_shift); break; case TAX: - LD(x_, a_.full, x_masks_); - flags_.set_nz(x_.full, x_shift_); + LD(registers_.x, registers_.a.full, registers_.x_masks); + registers_.flags.set_nz(registers_.x.full, registers_.x_shift); break; case TAY: - LD(y_, a_.full, x_masks_); - flags_.set_nz(y_.full, x_shift_); + LD(registers_.y, registers_.a.full, registers_.x_masks); + registers_.flags.set_nz(registers_.y.full, registers_.x_shift); break; case TXA: - LD(a_, x_.full, m_masks_); - flags_.set_nz(a_.full, m_shift_); + LD(registers_.a, registers_.x.full, registers_.m_masks); + registers_.flags.set_nz(registers_.a.full, registers_.m_shift); break; case TYA: - LD(a_, y_.full, m_masks_); - flags_.set_nz(a_.full, m_shift_); + LD(registers_.a, registers_.y.full, registers_.m_masks); + registers_.flags.set_nz(registers_.a.full, registers_.m_shift); break; case TCD: - direct_ = a_.full; - flags_.set_nz(a_.full, 8); + registers_.direct = registers_.a.full; + registers_.flags.set_nz(registers_.a.full, 8); break; case TDC: - a_.full = direct_; - flags_.set_nz(a_.full, 8); + registers_.a.full = registers_.direct; + registers_.flags.set_nz(registers_.a.full, 8); break; case TCS: - s_.full = a_.full; + registers_.s.full = registers_.a.full; // No need to worry about byte masking here; for the stack it's handled as the emulation runs. break; case TSC: - a_.full = stack_address(); - flags_.set_nz(a_.full, 8); + registers_.a.full = stack_address(); + registers_.flags.set_nz(registers_.a.full, 8); break; case XBA: { - const uint8_t a_low = a_.halves.low; - a_.halves.low = a_.halves.high; - a_.halves.high = a_low; - flags_.set_nz(a_.halves.low); + const uint8_t a_low = registers_.a.halves.low; + registers_.a.halves.low = registers_.a.halves.high; + registers_.a.halves.high = a_low; + registers_.flags.set_nz(registers_.a.halves.low); } break; // @@ -584,38 +579,38 @@ template void Processor::run_for(const Cycles // case JML: - program_bank_ = instruction_buffer_.value & 0xff0000; + registers_.program_bank = instruction_buffer_.value & 0xff0000; [[fallthrough]]; case JMP: - pc_ = uint16_t(instruction_buffer_.value); + registers_.pc = uint16_t(instruction_buffer_.value); break; case JMPind: - pc_ = data_buffer_.value; + registers_.pc = data_buffer_.value; break; case RTS: - pc_ = data_buffer_.value + 1; + registers_.pc = data_buffer_.value + 1; break; case JSL: - program_bank_ = instruction_buffer_.value & 0xff0000; + registers_.program_bank = instruction_buffer_.value & 0xff0000; [[fallthrough]]; case JSR: - data_buffer_.value = pc_; + data_buffer_.value = registers_.pc; data_buffer_.size = 2; - pc_ = instruction_buffer_.value; + registers_.pc = instruction_buffer_.value; break; case RTI: - pc_ = uint16_t(data_buffer_.value >> 8); + registers_.pc = uint16_t(data_buffer_.value >> 8); set_flags(uint8_t(data_buffer_.value)); - if(!emulation_flag_) { - program_bank_ = (data_buffer_.value & 0xff000000) >> 8; + if(!registers_.emulation_flag) { + registers_.program_bank = (data_buffer_.value & 0xff000000) >> 8; } break; @@ -624,33 +619,33 @@ template void Processor::run_for(const Cycles // case MVP: - data_bank_ = (instruction_buffer_.value & 0xff) << 16; - --x_.full; - --y_.full; - --a_.full; - if(a_.full) pc_ -= 3; + registers_.data_bank = (instruction_buffer_.value & 0xff) << 16; + --registers_.x.full; + --registers_.y.full; + --registers_.a.full; + if(registers_.a.full) registers_.pc -= 3; break; case MVN: - data_bank_ = (instruction_buffer_.value & 0xff) << 16; - ++x_.full; - ++y_.full; - --a_.full; - if(a_.full) pc_ -= 3; + registers_.data_bank = (instruction_buffer_.value & 0xff) << 16; + ++registers_.x.full; + ++registers_.y.full; + --registers_.a.full; + if(registers_.a.full) registers_.pc -= 3; break; // // Flag manipulation. // - case CLC: flags_.carry = 0; break; - case CLI: flags_.inverse_interrupt = Flag::Interrupt; break; - case CLV: flags_.overflow = 0; break; - case CLD: flags_.decimal = 0; break; + case CLC: registers_.flags.carry = 0; break; + case CLI: registers_.flags.inverse_interrupt = Flag::Interrupt; break; + case CLV: registers_.flags.overflow = 0; break; + case CLD: registers_.flags.decimal = 0; break; - case SEC: flags_.carry = Flag::Carry; break; - case SEI: flags_.inverse_interrupt = 0; break; - case SED: flags_.decimal = Flag::Decimal; break; + case SEC: registers_.flags.carry = Flag::Carry; break; + case SEI: registers_.flags.inverse_interrupt = 0; break; + case SED: registers_.flags.decimal = Flag::Decimal; break; case REP: set_flags(get_flags() &~ instruction_buffer_.value); @@ -661,9 +656,9 @@ template void Processor::run_for(const Cycles break; case XCE: { - const bool old_emulation_flag = emulation_flag_; - set_emulation_mode(flags_.carry); - flags_.carry = old_emulation_flag; + const bool old_emulation_flag = registers_.emulation_flag; + set_emulation_mode(registers_.flags.carry); + registers_.flags.carry = old_emulation_flag; } break; // @@ -672,36 +667,36 @@ template void Processor::run_for(const Cycles case INC: ++data_buffer_.value; - flags_.set_nz(data_buffer_.value, m_shift_); + registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); break;; case DEC: --data_buffer_.value; - flags_.set_nz(data_buffer_.value, m_shift_); + registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); break; case INX: { - const uint16_t x_inc = x_.full + 1; - LD(x_, x_inc, x_masks_); - flags_.set_nz(x_.full, x_shift_); + const uint16_t x_inc = registers_.x.full + 1; + LD(registers_.x, x_inc, registers_.x_masks); + registers_.flags.set_nz(registers_.x.full, registers_.x_shift); } break; case DEX: { - const uint16_t x_dec = x_.full - 1; - LD(x_, x_dec, x_masks_); - flags_.set_nz(x_.full, x_shift_); + const uint16_t x_dec = registers_.x.full - 1; + LD(registers_.x, x_dec, registers_.x_masks); + registers_.flags.set_nz(registers_.x.full, registers_.x_shift); } break; case INY: { - const uint16_t y_inc = y_.full + 1; - LD(y_, y_inc, x_masks_); - flags_.set_nz(y_.full, x_shift_); + const uint16_t y_inc = registers_.y.full + 1; + LD(registers_.y, y_inc, registers_.x_masks); + registers_.flags.set_nz(registers_.y.full, registers_.x_shift); } break; case DEY: { - const uint16_t y_dec = y_.full - 1; - LD(y_, y_dec, x_masks_); - flags_.set_nz(y_.full, x_shift_); + const uint16_t y_dec = registers_.y.full - 1; + LD(registers_.y, y_dec, registers_.x_masks); + registers_.flags.set_nz(registers_.y.full, registers_.x_shift); } break; // @@ -709,38 +704,38 @@ template void Processor::run_for(const Cycles // case AND: - a_.full &= data_buffer_.value | m_masks_[0]; - flags_.set_nz(a_.full, m_shift_); + registers_.a.full &= data_buffer_.value | registers_.m_masks[0]; + registers_.flags.set_nz(registers_.a.full, registers_.m_shift); break; case EOR: - a_.full ^= data_buffer_.value; - flags_.set_nz(a_.full, m_shift_); + registers_.a.full ^= data_buffer_.value; + registers_.flags.set_nz(registers_.a.full, registers_.m_shift); break; case ORA: - a_.full |= data_buffer_.value; - flags_.set_nz(a_.full, m_shift_); + registers_.a.full |= data_buffer_.value; + registers_.flags.set_nz(registers_.a.full, registers_.m_shift); break; case BIT: - flags_.set_n(data_buffer_.value, m_shift_); - flags_.set_z(data_buffer_.value & a_.full, m_shift_); - flags_.overflow = data_buffer_.value & Flag::Overflow; + registers_.flags.set_n(data_buffer_.value, registers_.m_shift); + registers_.flags.set_z(data_buffer_.value & registers_.a.full, registers_.m_shift); + registers_.flags.overflow = data_buffer_.value & Flag::Overflow; break; case BITimm: - flags_.set_z(data_buffer_.value & a_.full, m_shift_); + registers_.flags.set_z(data_buffer_.value & registers_.a.full, registers_.m_shift); break; case TRB: - flags_.set_z(data_buffer_.value & a_.full, m_shift_); - data_buffer_.value &= ~a_.full; + registers_.flags.set_z(data_buffer_.value & registers_.a.full, registers_.m_shift); + data_buffer_.value &= ~registers_.a.full; break; case TSB: - flags_.set_z(data_buffer_.value & a_.full, m_shift_); - data_buffer_.value |= a_.full; + registers_.flags.set_z(data_buffer_.value & registers_.a.full, registers_.m_shift); + data_buffer_.value |= registers_.a.full; break; // @@ -752,27 +747,27 @@ template void Processor::run_for(const Cycles next_op_ += 3; \ } else { \ data_buffer_.size = 2; \ - data_buffer_.value = pc_ + int8_t(instruction_buffer_.value); \ + data_buffer_.value = registers_.pc + int8_t(instruction_buffer_.value); \ \ - if((pc_ & 0xff00) == (instruction_buffer_.value & 0xff00)) { \ + if((registers_.pc & 0xff00) == (instruction_buffer_.value & 0xff00)) { \ ++next_op_; \ } \ } - case BPL: BRA(!(flags_.negative_result&0x80)); break; - case BMI: BRA(flags_.negative_result&0x80); break; - case BVC: BRA(!flags_.overflow); break; - case BVS: BRA(flags_.overflow); break; - case BCC: BRA(!flags_.carry); break; - case BCS: BRA(flags_.carry); break; - case BNE: BRA(flags_.zero_result); break; - case BEQ: BRA(!flags_.zero_result); break; + case BPL: BRA(!(registers_.flags.negative_result&0x80)); break; + case BMI: BRA(registers_.flags.negative_result&0x80); break; + case BVC: BRA(!registers_.flags.overflow); break; + case BVS: BRA(registers_.flags.overflow); break; + case BCC: BRA(!registers_.flags.carry); break; + case BCS: BRA(registers_.flags.carry); break; + case BNE: BRA(registers_.flags.zero_result); break; + case BEQ: BRA(!registers_.flags.zero_result); break; case BRA: BRA(true); break; #undef BRA case BRL: - pc_ += int16_t(instruction_buffer_.value); + registers_.pc += int16_t(instruction_buffer_.value); break; // @@ -780,28 +775,28 @@ template void Processor::run_for(const Cycles // case ASL: - flags_.carry = data_buffer_.value >> (7 + m_shift_); + registers_.flags.carry = data_buffer_.value >> (7 + registers_.m_shift); data_buffer_.value <<= 1; - flags_.set_nz(data_buffer_.value, m_shift_); + registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); break; case LSR: - flags_.carry = data_buffer_.value & 1; + registers_.flags.carry = data_buffer_.value & 1; data_buffer_.value >>= 1; - flags_.set_nz(data_buffer_.value, m_shift_); + registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); break; case ROL: - data_buffer_.value = (data_buffer_.value << 1) | flags_.carry; - flags_.carry = data_buffer_.value >> (8 + m_shift_); - flags_.set_nz(data_buffer_.value, m_shift_); + data_buffer_.value = (data_buffer_.value << 1) | registers_.flags.carry; + registers_.flags.carry = data_buffer_.value >> (8 + registers_.m_shift); + registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); break; case ROR: { const uint8_t next_carry = data_buffer_.value & 1; - data_buffer_.value = (data_buffer_.value >> 1) | (flags_.carry << (7 + m_shift_)); - flags_.carry = next_carry; - flags_.set_nz(data_buffer_.value, m_shift_); + data_buffer_.value = (data_buffer_.value >> 1) | (registers_.flags.carry << (7 + registers_.m_shift)); + registers_.flags.carry = next_carry; + registers_.flags.set_nz(data_buffer_.value, registers_.m_shift); } break; // @@ -810,23 +805,23 @@ template void Processor::run_for(const Cycles #define cp(v, shift, masks) {\ const uint32_t temp32 = (v.full & masks[1]) - (data_buffer_.value & masks[1]); \ - flags_.set_nz(uint16_t(temp32), shift); \ - flags_.carry = ((~temp32) >> (8 + shift))&1; \ + registers_.flags.set_nz(uint16_t(temp32), shift); \ + registers_.flags.carry = ((~temp32) >> (8 + shift))&1; \ } - case CMP: cp(a_, m_shift_, m_masks_); break; - case CPX: cp(x_, x_shift_, x_masks_); break; - case CPY: cp(y_, x_shift_, x_masks_); break; + case CMP: cp(registers_.a, registers_.m_shift, registers_.m_masks); break; + case CPX: cp(registers_.x, registers_.x_shift, registers_.x_masks); break; + case CPY: cp(registers_.y, registers_.x_shift, registers_.x_masks); break; #undef cp case SBC: - if(flags_.decimal) { + if(registers_.flags.decimal) { // I've yet to manage to find a rational way to map this to an ADC, // hence the yucky repetition of code here. - const uint16_t a = a_.full & m_masks_[1]; + const uint16_t a = registers_.a.full & registers_.m_masks[1]; unsigned int result = 0; - unsigned int borrow = flags_.carry ^ 1; + unsigned int borrow = registers_.flags.carry ^ 1; #define nibble(mask, adjustment, carry) \ result += (a & mask) - (data_buffer_.value & mask) - borrow; \ @@ -841,23 +836,23 @@ template void Processor::run_for(const Cycles #undef nibble - flags_.overflow = ~(( (result ^ a_.full) & (result ^ data_buffer_.value) ) >> (1 + m_shift_))&0x40; - flags_.set_nz(result, m_shift_); - flags_.carry = ((borrow >> 16)&1)^1; - LD(a_, result, m_masks_); + registers_.flags.overflow = ~(( (result ^ registers_.a.full) & (result ^ data_buffer_.value) ) >> (1 + registers_.m_shift))&0x40; + registers_.flags.set_nz(result, registers_.m_shift); + registers_.flags.carry = ((borrow >> 16)&1)^1; + LD(registers_.a, result, registers_.m_masks); break; } - data_buffer_.value = ~data_buffer_.value & m_masks_[1]; + data_buffer_.value = ~data_buffer_.value & registers_.m_masks[1]; [[fallthrough]]; case ADC: { int result; - const uint16_t a = a_.full & m_masks_[1]; + const uint16_t a = registers_.a.full & registers_.m_masks[1]; - if(flags_.decimal) { - result = flags_.carry; + if(registers_.flags.decimal) { + result = registers_.flags.carry; #define nibble(mask, limit, adjustment, carry) \ result += (a & mask) + (data_buffer_.value & mask); \ @@ -871,13 +866,13 @@ template void Processor::run_for(const Cycles #undef nibble } else { - result = a + data_buffer_.value + flags_.carry; + result = a + data_buffer_.value + registers_.flags.carry; } - flags_.overflow = (( (result ^ a_.full) & (result ^ data_buffer_.value) ) >> (1 + m_shift_))&0x40; - flags_.set_nz(result, m_shift_); - flags_.carry = (result >> (8 + m_shift_))&1; - LD(a_, result, m_masks_); + registers_.flags.overflow = (( (result ^ registers_.a.full) & (result ^ data_buffer_.value) ) >> (1 + registers_.m_shift))&0x40; + registers_.flags.set_nz(result, registers_.m_shift); + registers_.flags.carry = (result >> (8 + registers_.m_shift))&1; + LD(registers_.a, result, registers_.m_masks); } break; // @@ -901,12 +896,12 @@ template void Processor::run_for(const Cycles #undef y_top #undef a_top - // TODO: the ready line. - // Store a selection as to the exceptions, if any, that would be honoured after this cycle if the // next thing is a MoveToNextProgram. - selected_exceptions_ = pending_exceptions_ & (flags_.inverse_interrupt | PowerOn | Reset | NMI); - number_of_cycles -= bus_handler_.perform_bus_operation(bus_operation, bus_address, bus_value); + selected_exceptions_ = pending_exceptions_ & (registers_.flags.inverse_interrupt | PowerOn | Reset | NMI); + number_of_cycles -= bus_handler_.perform_bus_operation(bus_operation_, bus_address_, bus_value_); + + // TODO: RDY line. } #undef read diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp index c6a2d80ac..593801914 100644 --- a/Processors/65816/Implementation/65816Storage.cpp +++ b/Processors/65816/Implementation/65816Storage.cpp @@ -1028,63 +1028,63 @@ ProcessorStorage::ProcessorStorage() { } void ProcessorStorage::set_reset_state() { - data_bank_ = 0; - program_bank_ = 0; - direct_ = 0; - flags_.decimal = 0; - flags_.inverse_interrupt = 0; + registers_.data_bank = 0; + registers_.program_bank = 0; + registers_.direct = 0; + registers_.flags.decimal = 0; + registers_.flags.inverse_interrupt = 0; set_emulation_mode(true); } void ProcessorStorage::set_emulation_mode(bool enabled) { - if(emulation_flag_ == enabled) { + if(registers_.emulation_flag == enabled) { return; } - emulation_flag_ = enabled; + registers_.emulation_flag = enabled; if(enabled) { set_m_x_flags(true, true); - x_.halves.high = y_.halves.high = 0; - e_masks_[0] = 0xff00; - e_masks_[1] = 0x00ff; + registers_.x.halves.high = registers_.y.halves.high = 0; + registers_.e_masks[0] = 0xff00; + registers_.e_masks[1] = 0x00ff; } else { - e_masks_[0] = 0x0000; - e_masks_[1] = 0xffff; - s_.halves.high = 1; // To pretend it was 1 all along; this implementation actually ignores - // the top byte while in emulation mode. + registers_.e_masks[0] = 0x0000; + registers_.e_masks[1] = 0xffff; + registers_.s.halves.high = 1; // To pretend it was 1 all along; this implementation actually ignores + // the top byte while in emulation mode. } } void ProcessorStorage::set_m_x_flags(bool m, bool x) { // true/1 => 8bit for both flags. - mx_flags_[0] = m; - mx_flags_[1] = x; + registers_.mx_flags[0] = m; + registers_.mx_flags[1] = x; - m_masks_[0] = m ? 0xff00 : 0x0000; - m_masks_[1] = m ? 0x00ff : 0xffff; - m_shift_ = m ? 0 : 8; + registers_.m_masks[0] = m ? 0xff00 : 0x0000; + registers_.m_masks[1] = m ? 0x00ff : 0xffff; + registers_.m_shift = m ? 0 : 8; - x_masks_[0] = x ? 0xff00 : 0x0000; - x_masks_[1] = x ? 0x00ff : 0xffff; - x_shift_ = x ? 0 : 8; + registers_.x_masks[0] = x ? 0xff00 : 0x0000; + registers_.x_masks[1] = x ? 0x00ff : 0xffff; + registers_.x_shift = x ? 0 : 8; } uint8_t ProcessorStorage::get_flags() const { - uint8_t result = flags_.get(); + uint8_t result = registers_.flags.get(); - if(!emulation_flag_) { + if(!registers_.emulation_flag) { result &= ~(Flag::MemorySize | Flag::IndexSize); - result |= mx_flags_[0] * Flag::MemorySize; - result |= mx_flags_[1] * Flag::IndexSize; + result |= registers_.mx_flags[0] * Flag::MemorySize; + result |= registers_.mx_flags[1] * Flag::IndexSize; } return result; } void ProcessorStorage::set_flags(uint8_t value) { - flags_.set(value); + registers_.flags.set(value); - if(!emulation_flag_) { + if(!registers_.emulation_flag) { set_m_x_flags(value & Flag::MemorySize, value & Flag::IndexSize); } } diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp index 7698345e0..41f6709f3 100644 --- a/Processors/65816/Implementation/65816Storage.hpp +++ b/Processors/65816/Implementation/65816Storage.hpp @@ -241,49 +241,64 @@ struct ProcessorStorage { FetchDecodeExecute }; - // Registers. - RegisterPair16 a_; - RegisterPair16 x_, y_; - RegisterPair16 s_; - uint16_t pc_; - // A helper for testing. uint16_t last_operation_pc_; Instruction *active_instruction_; Cycles cycles_left_to_run_; - // Flags aplenty. - MOS6502Esque::LazyFlags flags_; - uint8_t mx_flags_[2] = {1, 1}; // [0] = m; [1] = x. In both cases either `0` or `1`; `1` => 8-bit. - uint16_t m_masks_[2] = {0xff00, 0x00ff}; // [0] = src mask; [1] = dst mask. - uint16_t x_masks_[2] = {0xff00, 0x00ff}; // [0] = src mask; [1] = dst mask. - uint16_t e_masks_[2] = {0xff00, 0x00ff}; - int m_shift_ = 0; - int x_shift_ = 0; - bool emulation_flag_ = true; + // All registers are boxed up into a struct so that they can be stored and restored in support of abort. + struct Registers { + // Registers. + RegisterPair16 a; + RegisterPair16 x, y; + RegisterPair16 s; + uint16_t pc; - // I.e. the offset for direct addressing (outside of emulation mode). - uint16_t direct_ = 0; + // Flags aplenty. + MOS6502Esque::LazyFlags flags; + uint8_t mx_flags[2] = {1, 1}; // [0] = m; [1] = x. In both cases either `0` or `1`; `1` => 8-bit. + uint16_t m_masks[2] = {0xff00, 0x00ff}; // [0] = src mask; [1] = dst mask. + uint16_t x_masks[2] = {0xff00, 0x00ff}; // [0] = src mask; [1] = dst mask. + uint16_t e_masks[2] = {0xff00, 0x00ff}; + int m_shift = 0; + int x_shift = 0; + bool emulation_flag = true; - // Banking registers are all stored with the relevant byte - // shifted up bits 16–23. - uint32_t data_bank_ = 0; // i.e. DBR. - uint32_t program_bank_ = 0; // i.e. PBR. + // I.e. the offset for direct addressing (outside of emulation mode). + uint16_t direct = 0; + // Banking registers are all stored with the relevant byte + // shifted up bits 16–23. + uint32_t data_bank = 0; // i.e. DBR. + uint32_t program_bank = 0; // i.e. PBR. + } registers_, abort_registers_copy_; + + // The next bus transaction. + uint32_t bus_address_ = 0; + uint8_t *bus_value_ = nullptr; + static inline uint8_t bus_throwaway_ = 0; + BusOperation bus_operation_ = BusOperation::None; + + // A bitfield for various exceptions. static constexpr int PowerOn = 1 << 0; static constexpr int Reset = 1 << 1; static constexpr int IRQ = Flag::Interrupt; // This makes masking a lot easier later on; this is 1 << 2. static constexpr int NMI = 1 << 3; + static constexpr int Abort = 1 << 4; int pending_exceptions_ = PowerOn; // By default. int selected_exceptions_ = 0; + bool ready_line_ = false; + // Just to be safe. static_assert(PowerOn != IRQ); static_assert(Reset != IRQ); static_assert(NMI != IRQ); + static_assert(Abort != IRQ); /// Sets the required exception flags necessary to exit a STP or WAI. int required_exceptions_ = 0; + BusOperation stp_wai_bus_operation_ = BusOperation::None; /// Defines a four-byte buffer which can be cleared or filled in single-byte increments from least significant byte /// to most significant.