1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-02 20:30:00 +00:00

Wraps all registers into a struct, so that I can implement abort.

Makes some preparations for ready too.
This commit is contained in:
Thomas Harte 2020-10-15 18:42:38 -04:00
parent 3c6adc1ff4
commit c0a1c34012
6 changed files with 321 additions and 294 deletions

View File

@ -36,8 +36,8 @@ template <Type processor_type, typename BusHandler, bool uses_ready_line> class
};
template <typename BusHandler, bool uses_ready_line> class Processor<Type::TWDC65816, BusHandler, uses_ready_line>:
public CPU::WDC65816::Processor<BusHandler> {
using CPU::WDC65816::Processor<BusHandler>::Processor;
public CPU::WDC65816::Processor<BusHandler, uses_ready_line> {
using CPU::WDC65816::Processor<BusHandler, uses_ready_line>::Processor;
};
}

View File

@ -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 <typename BusHandler> class Processor: public ProcessorBase {
template <typename BusHandler, bool uses_ready_line> class Processor: public ProcessorBase {
public:
/*!
Constructs an instance of the 6502 that will use @c bus_handler for all bus communications.
*/
Processor(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_;
};

View File

@ -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;
}
}

View File

@ -6,27 +6,22 @@
// Copyright © 2020 Thomas Harte. All rights reserved.
//
template <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler, bool uses_ready_line> void Processor<BusHandler, uses_ready_line>::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 <typename BusHandler> void Processor<BusHandler>::run_for(const Cycles
#ifndef NDEBUG
// As a sanity check.
bus_value = nullptr;
bus_value_ = nullptr;
#endif
switch(operation) {
@ -52,13 +47,13 @@ template <typename BusHandler> void Processor<BusHandler>::run_for(const Cycles
next_op_ = &micro_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_ = &micro_ops_[active_instruction_->program_offsets[size_flag]];
instruction_buffer_.clear();
} continue;
@ -68,21 +63,21 @@ template <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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 <typename BusHandler> void Processor<BusHandler>::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

View File

@ -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);
}
}

View File

@ -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 1623.
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 1623.
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.