1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-09 06:29:33 +00:00

Merge pull request #523 from TomHarte/Further65C02

Further corrects 65C02 behaviour
This commit is contained in:
Thomas Harte 2018-08-17 21:58:38 -04:00 committed by GitHub
commit 7f0f17f435
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 415 additions and 148 deletions

View File

@ -63,7 +63,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
uint8_t *ram_, *aux_ram_;
};
CPU::MOS6502::Processor<ConcreteMachine, false> m6502_;
CPU::MOS6502::Processor<(model == Analyser::Static::AppleII::Target::Model::EnhancedIIe) ? CPU::MOS6502::Personality::PSynertek65C02 : CPU::MOS6502::Personality::P6502, ConcreteMachine, false> m6502_;
VideoBusHandler video_bus_handler_;
std::unique_ptr<AppleII::Video::Video<VideoBusHandler, is_iie()>> video_;
int cycles_into_current_line_ = 0;
@ -301,7 +301,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
public:
ConcreteMachine(const Analyser::Static::AppleII::Target &target, const ROMMachine::ROMFetcher &rom_fetcher):
m6502_((model == Analyser::Static::AppleII::Target::Model::EnhancedIIe) ? CPU::MOS6502::Personality::P65C02 : CPU::MOS6502::Personality::P6502, *this),
m6502_(*this),
video_bus_handler_(ram_, aux_ram_),
audio_toggle_(audio_queue_),
speaker_(audio_toggle_) {

View File

@ -32,7 +32,7 @@ template<class T> class Cartridge:
public:
Cartridge(const std::vector<uint8_t> &rom) :
m6502_(CPU::MOS6502::Personality::P6502, *this),
m6502_(*this),
rom_(rom),
bus_extender_(rom_.data(), rom.size()) {
// The above works because bus_extender_ is declared after rom_ in the instance storage list;
@ -204,7 +204,7 @@ template<class T> class Cartridge:
}
protected:
CPU::MOS6502::Processor<Cartridge<T>, true> m6502_;
CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, Cartridge<T>, true> m6502_;
std::vector<uint8_t> rom_;
private:

View File

@ -18,7 +18,7 @@ using namespace Commodore::C1540;
MachineBase::MachineBase(Personality personality, const ROMMachine::ROMFetcher &rom_fetcher) :
Storage::Disk::Controller(1000000),
m6502_(CPU::MOS6502::Personality::P6502, *this),
m6502_(*this),
drive_(new Storage::Disk::Drive(1000000, 300, 2)),
serial_port_VIA_port_handler_(new SerialPortVIA(serial_port_VIA_)),
serial_port_(new SerialPort),

View File

@ -143,7 +143,7 @@ class MachineBase:
void set_activity_observer(Activity::Observer *observer);
protected:
CPU::MOS6502::Processor<MachineBase, false> m6502_;
CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, MachineBase, false> m6502_;
std::shared_ptr<Storage::Disk::Drive> drive_;
uint8_t ram_[0x800];

View File

@ -293,7 +293,7 @@ class ConcreteMachine:
public Activity::Source {
public:
ConcreteMachine(const Analyser::Static::Commodore::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
m6502_(CPU::MOS6502::Personality::P6502, *this),
m6502_(*this),
user_port_via_port_handler_(new UserPortVIA),
keyboard_via_port_handler_(new KeyboardVIA),
serial_port_(new SerialPort),
@ -703,7 +703,7 @@ class ConcreteMachine:
void update_video() {
mos6560_->run_for(cycles_since_mos6560_update_.flush());
}
CPU::MOS6502::Processor<ConcreteMachine, false> m6502_;
CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, ConcreteMachine, false> m6502_;
std::vector<uint8_t> character_rom_;
std::vector<uint8_t> basic_rom_;

View File

@ -50,7 +50,7 @@ class ConcreteMachine:
public Activity::Source {
public:
ConcreteMachine(const Analyser::Static::Acorn::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
m6502_(CPU::MOS6502::Personality::P6502, *this),
m6502_(*this),
sound_generator_(audio_queue_),
speaker_(sound_generator_) {
memset(key_states_, 0, sizeof(key_states_));
@ -541,7 +541,7 @@ class ConcreteMachine:
m6502_.set_irq_line(interrupt_status_ & 1);
}
CPU::MOS6502::Processor<ConcreteMachine, false> m6502_;
CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, ConcreteMachine, false> m6502_;
// Things that directly constitute the memory map.
uint8_t roms_[16][16384];

View File

@ -207,7 +207,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
public:
ConcreteMachine(const Analyser::Static::Oric::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
m6502_(CPU::MOS6502::Personality::P6502, *this),
m6502_(*this),
ay8910_(audio_queue_),
speaker_(ay8910_),
via_port_handler_(audio_queue_, ay8910_, speaker_, tape_player_, keyboard_),
@ -575,7 +575,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
const uint16_t basic_invisible_ram_top_ = 0xffff;
const uint16_t basic_visible_ram_top_ = 0xbfff;
CPU::MOS6502::Processor<ConcreteMachine, false> m6502_;
CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, ConcreteMachine, false> m6502_;
// RAM and ROM
std::vector<uint8_t> rom_, microdisc_rom_, colour_rom_;

View File

@ -40,7 +40,7 @@ static CPU::MOS6502::Register registerForRegister(CSTestMachine6502Register reg)
if(self) {
_processor = CPU::MOS6502::AllRAMProcessor::Processor(
is65C02 ? CPU::MOS6502::Personality::P65C02 : CPU::MOS6502::Personality::P6502);
is65C02 ? CPU::MOS6502::Personality::PWDC65C02 : CPU::MOS6502::Personality::P6502);
}
return self;

View File

@ -37,11 +37,18 @@ enum Register {
The list of 6502 variants supported by this implementation.
*/
enum Personality {
P6502, // the original 6502, replete with various undocumented instructions
P65C02, // the 65C02; an extended 6502 with a few extra instructions and addressing modes for existing instructions
P65SC02, // like the 65C02, but lacking bit instructions
P6502, // the original [NMOS] 6502, replete with various undocumented instructions
PNES6502, // the NES's 6502, which is like a 6502 but lacks decimal mode (though it retains the decimal flag)
PSynertek65C02, // a 6502 extended with BRA, P[H/L][X/Y], STZ, TRB, TSB and the (zp) addressing mode and a few other additions
PWDC65C02, // like the Synertek, but with BBR, BBS, RMB and SMB
PRockwell65C02, // like the WDC, but with STP and WAI
};
#define is_65c02(p) ((p) >= Personality::PSynertek65C02)
#define has_bbrbbsrmbsmb(p) ((p) >= Personality::PWDC65C02)
#define has_stpwai(p) ((p) >= Personality::PRockwell65C02)
#define has_decimal_mode(p) ((p) != Personality::PNES6502)
/*
Flags as defined on the 6502; can be used to decode the result of @c get_value_of_register(Flags) or to form a value for
the corresponding set.
@ -199,12 +206,12 @@ class ProcessorBase: public ProcessorStorage {
can also nominate whether the processor includes support for the ready line. Declining to support the ready line
can produce a minor runtime performance improvement.
*/
template <typename T, bool uses_ready_line> class Processor: public ProcessorBase {
template <Personality personality, typename T, bool uses_ready_line> class Processor: public ProcessorBase {
public:
/*!
Constructs an instance of the 6502 that will use @c bus_handler for all bus communications.
*/
Processor(Personality personality, T &bus_handler) : ProcessorBase(personality), personality_(personality), bus_handler_(bus_handler) {}
Processor(T &bus_handler) : ProcessorBase(personality), bus_handler_(bus_handler) {}
/*!
Runs the 6502 for a supplied number of cycles.
@ -221,7 +228,6 @@ template <typename T, bool uses_ready_line> class Processor: public ProcessorBas
void set_ready_line(bool active);
private:
Personality personality_;
T &bus_handler_;
};

View File

@ -15,10 +15,10 @@ using namespace CPU::MOS6502;
namespace {
class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler {
template <Personality personality> class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler {
public:
ConcreteAllRAMProcessor(Personality personality) :
mos6502_(personality, *this) {
ConcreteAllRAMProcessor() :
mos6502_(*this) {
mos6502_.set_power_on(false);
}
@ -63,11 +63,20 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler {
}
private:
CPU::MOS6502::Processor<ConcreteAllRAMProcessor, false> mos6502_;
CPU::MOS6502::Processor<personality, ConcreteAllRAMProcessor, false> mos6502_;
};
}
AllRAMProcessor *AllRAMProcessor::Processor(Personality personality) {
return new ConcreteAllRAMProcessor(personality);
#define Bind(p) case p: return new ConcreteAllRAMProcessor<p>();
switch(personality) {
default:
Bind(Personality::P6502)
Bind(Personality::PNES6502)
Bind(Personality::PSynertek65C02)
Bind(Personality::PWDC65C02)
Bind(Personality::PRockwell65C02)
}
#undef Bind
}

View File

@ -12,7 +12,7 @@
6502.hpp, but it's implementation stuff.
*/
template <typename T, bool uses_ready_line> void Processor<T, uses_ready_line>::run_for(const Cycles cycles) {
template <Personality personality, typename T, bool uses_ready_line> void Processor<personality, T, uses_ready_line>::run_for(const Cycles cycles) {
static const MicroOp do_branch[] = {
CycleReadFromPC,
CycleAddSignedOperandToPC,
@ -34,40 +34,62 @@ template <typename T, bool uses_ready_line> void Processor<T, uses_ready_line>::
uint8_t *busValue = bus_value_;
#define checkSchedule(op) \
if(!scheduled_program_counter_) {\
if(interrupt_requests_) {\
if(interrupt_requests_ & (InterruptRequestFlags::Reset | InterruptRequestFlags::PowerOn)) {\
interrupt_requests_ &= ~InterruptRequestFlags::PowerOn;\
scheduled_program_counter_ = get_reset_program();\
} else if(interrupt_requests_ & InterruptRequestFlags::NMI) {\
interrupt_requests_ &= ~InterruptRequestFlags::NMI;\
scheduled_program_counter_ = get_nmi_program();\
} else if(interrupt_requests_ & InterruptRequestFlags::IRQ) {\
scheduled_program_counter_ = get_irq_program();\
} \
} else {\
scheduled_program_counter_ = fetch_decode_execute;\
}\
op;\
}
if(!scheduled_program_counter_) {\
if(interrupt_requests_) {\
if(interrupt_requests_ & (InterruptRequestFlags::Reset | InterruptRequestFlags::PowerOn)) {\
interrupt_requests_ &= ~InterruptRequestFlags::PowerOn;\
scheduled_program_counter_ = get_reset_program();\
} else if(interrupt_requests_ & InterruptRequestFlags::NMI) {\
interrupt_requests_ &= ~InterruptRequestFlags::NMI;\
scheduled_program_counter_ = get_nmi_program();\
} else if(interrupt_requests_ & InterruptRequestFlags::IRQ) {\
scheduled_program_counter_ = get_irq_program();\
} \
} else {\
scheduled_program_counter_ = fetch_decode_execute;\
}\
op;\
}
#define bus_access() \
interrupt_requests_ = (interrupt_requests_ & ~InterruptRequestFlags::IRQ) | irq_request_history_; \
irq_request_history_ = irq_line_ & inverse_interrupt_flag_; \
number_of_cycles -= bus_handler_.perform_bus_operation(nextBusOperation, busAddress, busValue); \
nextBusOperation = BusOperation::None; \
if(number_of_cycles <= Cycles(0)) break;
interrupt_requests_ = (interrupt_requests_ & ~InterruptRequestFlags::IRQ) | irq_request_history_; \
irq_request_history_ = irq_line_ & inverse_interrupt_flag_; \
number_of_cycles -= bus_handler_.perform_bus_operation(nextBusOperation, busAddress, busValue); \
nextBusOperation = BusOperation::None; \
if(number_of_cycles <= Cycles(0)) break;
checkSchedule();
Cycles number_of_cycles = cycles + cycles_left_to_run_;
while(number_of_cycles > Cycles(0)) {
// Deal with a potential RDY state, if this 6502 has anything connected to ready.
while(uses_ready_line && ready_is_active_ && number_of_cycles > Cycles(0)) {
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
}
if(!uses_ready_line || !ready_is_active_) {
// Deal with a potential STP state, if this 6502 implements STP.
while(has_stpwai(personality) && stop_is_active_ && number_of_cycles > Cycles(0)) {
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
if(interrupt_requests_ & InterruptRequestFlags::Reset) {
stop_is_active_ = false;
checkSchedule();
break;
}
}
// Deal with a potential WAI state, if this 6502 implements WAI.
while(has_stpwai(personality) && wait_is_active_ && number_of_cycles > Cycles(0)) {
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
interrupt_requests_ |= (irq_line_ & inverse_interrupt_flag_);
if(interrupt_requests_ & InterruptRequestFlags::NMI || irq_line_) {
wait_is_active_ = false;
checkSchedule();
break;
}
}
if((!uses_ready_line || !ready_is_active_) && (!has_stpwai(personality) || (!wait_is_active_ && !stop_is_active_))) {
if(nextBusOperation != BusOperation::None) {
bus_access();
}
@ -98,12 +120,15 @@ if(number_of_cycles <= Cycles(0)) break;
// governs everything else on the 6502: that two bytes will always
// be fetched.
if(
personality_ == P6502 ||
!is_65c02(personality) ||
(operation_&7) != 3 ||
operation_ == 0xcb ||
operation_ == 0xdb
) {
read_mem(operand_, pc_.full);
break;
} else {
continue;
}
break;
@ -140,17 +165,24 @@ if(number_of_cycles <= Cycles(0)) break;
case CycleReadFromPC: throwaway_read(pc_.full); break;
case OperationBRKPickVector:
// NMI can usurp BRK-vector operations
nextAddress.full = (interrupt_requests_ & InterruptRequestFlags::NMI) ? 0xfffa : 0xfffe;
interrupt_requests_ &= ~InterruptRequestFlags::NMI; // TODO: this probably doesn't happen now?
if(is_65c02(personality)) {
nextAddress.full = 0xfffe;
} else {
// NMI can usurp BRK-vector operations on the pre-C 6502s.
nextAddress.full = (interrupt_requests_ & InterruptRequestFlags::NMI) ? 0xfffa : 0xfffe;
interrupt_requests_ &= ~InterruptRequestFlags::NMI;
}
continue;
case OperationNMIPickVector: nextAddress.full = 0xfffa; continue;
case OperationRSTPickVector: nextAddress.full = 0xfffc; continue;
case CycleReadVectorLow: read_mem(pc_.bytes.low, nextAddress.full); break;
case CycleReadVectorHigh: read_mem(pc_.bytes.high, nextAddress.full+1); break;
case OperationSetI:
case OperationSetIRQFlags:
inverse_interrupt_flag_ = 0;
if(personality_ != P6502) decimal_flag_ = false;
if(is_65c02(personality)) decimal_flag_ = false;
continue;
case OperationSetNMIRSTFlags:
if(is_65c02(personality)) decimal_flag_ = false;
continue;
case CyclePullPCL: s_++; read_mem(pc_.bytes.low, s_ | 0x100); break;
@ -166,11 +198,11 @@ if(number_of_cycles <= Cycles(0)) break;
case OperationSetFlagsFromX: zero_result_ = negative_result_ = x_; continue;
case OperationSetFlagsFromY: zero_result_ = negative_result_ = y_; continue;
case CycleIncrementPCAndReadStack: pc_.full++; throwaway_read(s_ | 0x100); break;
case CycleReadPCLFromAddress: read_mem(pc_.bytes.low, address_.full); break;
case CycleReadPCHFromAddressLowInc: address_.bytes.low++; read_mem(pc_.bytes.high, address_.full); break;
case CycleReadPCHFromAddressFixed: if(!address_.bytes.low) address_.bytes.high++; read_mem(pc_.bytes.high, address_.full); break;
case CycleReadPCHFromAddressInc: address_.full++; read_mem(pc_.bytes.high, address_.full); break;
case CycleIncrementPCAndReadStack: pc_.full++; throwaway_read(s_ | 0x100); break;
case CycleReadPCLFromAddress: read_mem(pc_.bytes.low, address_.full); break;
case CycleReadPCHFromAddressLowInc: address_.bytes.low++; read_mem(pc_.bytes.high, address_.full); break;
case CycleReadPCHFromAddressFixed: if(!address_.bytes.low) address_.bytes.high++; read_mem(pc_.bytes.high, address_.full); break;
case CycleReadPCHFromAddressInc: address_.full++; read_mem(pc_.bytes.high, address_.full); break;
case CycleReadAndIncrementPC: {
uint16_t oldPC = pc_.full;
@ -178,13 +210,21 @@ if(number_of_cycles <= Cycles(0)) break;
throwaway_read(oldPC);
} break;
// MARK: - JAM
// MARK: - JAM, WAI, STP
case CycleScheduleJam: {
case OperationScheduleJam: {
is_jammed_ = true;
scheduled_program_counter_ = operations_[CPU::MOS6502::JamOpcode];
} continue;
case OperationScheduleStop:
stop_is_active_ = true;
break;
case OperationScheduleWait:
wait_is_active_ = true;
break;
// MARK: - Bitwise
case OperationORA: a_ |= operand_; negative_result_ = zero_result_ = a_; continue;
@ -197,6 +237,7 @@ if(number_of_cycles <= Cycles(0)) break;
case OperationLDX: x_ = negative_result_ = zero_result_ = operand_; continue;
case OperationLDY: y_ = negative_result_ = zero_result_ = operand_; continue;
case OperationLAX: a_ = x_ = negative_result_ = zero_result_ = operand_; continue;
case OperationCopyOperandToA: a_ = operand_; continue;
case OperationSTA: operand_ = a_; continue;
case OperationSTX: operand_ = x_; continue;
@ -264,7 +305,7 @@ if(number_of_cycles <= Cycles(0)) break;
case OperationINS:
operand_++; // deliberate fallthrough
case OperationSBC:
if(decimal_flag_) {
if(decimal_flag_ && has_decimal_mode(personality)) {
const uint16_t notCarry = carry_flag_ ^ 0x1;
const uint16_t decimalResult = static_cast<uint16_t>(a_) - static_cast<uint16_t>(operand_) - notCarry;
uint16_t temp16;
@ -283,7 +324,7 @@ if(number_of_cycles <= Cycles(0)) break;
carry_flag_ = (temp16 > 0xff) ? 0 : Flag::Carry;
a_ = static_cast<uint8_t>(temp16);
if(personality_ != P6502) {
if(is_65c02(personality)) {
negative_result_ = zero_result_ = a_;
read_mem(operand_, address_.full);
break;
@ -295,7 +336,7 @@ if(number_of_cycles <= Cycles(0)) break;
// deliberate fallthrough
case OperationADC:
if(decimal_flag_) {
if(decimal_flag_ && has_decimal_mode(personality)) {
const uint16_t decimalResult = static_cast<uint16_t>(a_) + static_cast<uint16_t>(operand_) + static_cast<uint16_t>(carry_flag_);
uint8_t low_nibble = (a_ & 0xf) + (operand_ & 0xf) + carry_flag_;
@ -309,7 +350,7 @@ if(number_of_cycles <= Cycles(0)) break;
a_ = static_cast<uint8_t>(result);
zero_result_ = static_cast<uint8_t>(decimalResult);
if(personality_ != P6502) {
if(is_65c02(personality)) {
negative_result_ = zero_result_ = a_;
read_mem(operand_, address_.full);
break;
@ -425,32 +466,42 @@ if(number_of_cycles <= Cycles(0)) break;
// MARK: - Addressing Mode Work
#define page_crossing_stall_read() \
if(is_65c02(personality)) { \
throwaway_read(pc_.full - 1); \
} else { \
throwaway_read(address_.full); \
}
case CycleAddXToAddressLow:
nextAddress.full = address_.full + x_;
address_.bytes.low = nextAddress.bytes.low;
if(address_.bytes.high != nextAddress.bytes.high) {
throwaway_read(address_.full);
if(address_.bytes.high != nextAddress.bytes.high) {
page_crossing_stall_read();
break;
}
continue;
case CycleAddXToAddressLowRead:
nextAddress.full = address_.full + x_;
address_.bytes.low = nextAddress.bytes.low;
throwaway_read(address_.full);
page_crossing_stall_read();
break;
case CycleAddYToAddressLow:
nextAddress.full = address_.full + y_;
address_.bytes.low = nextAddress.bytes.low;
if(address_.bytes.high != nextAddress.bytes.high) {
throwaway_read(address_.full);
page_crossing_stall_read();
break;
}
continue;
case CycleAddYToAddressLowRead:
nextAddress.full = address_.full + y_;
address_.bytes.low = nextAddress.bytes.low;
throwaway_read(address_.full);
page_crossing_stall_read();
break;
#undef page_crossing_stall_read
case OperationCorrectAddressHigh:
address_.full = nextAddress.full;
continue;
@ -509,12 +560,14 @@ if(number_of_cycles <= Cycles(0)) break;
case OperationIncrementPC: pc_.full++; continue;
case CycleFetchOperandFromAddress: read_mem(operand_, address_.full); break;
case CycleWriteOperandToAddress: write_mem(operand_, address_.full); break;
case OperationCopyOperandFromA: operand_ = a_; continue;
case OperationCopyOperandToA: a_ = operand_; continue;
// MARK: - Branching
#define BRA(condition) pc_.full++; if(condition) scheduled_program_counter_ = do_branch
#define BRA(condition) \
pc_.full++; \
if(condition) { \
scheduled_program_counter_ = do_branch; \
}
case OperationBPL: BRA(!(negative_result_&0x80)); continue;
case OperationBMI: BRA(negative_result_&0x80); continue;
@ -526,6 +579,8 @@ if(number_of_cycles <= Cycles(0)) break;
case OperationBEQ: BRA(!zero_result_); continue;
case OperationBRA: BRA(true); continue;
#undef BRA
case CycleAddSignedOperandToPC:
nextAddress.full = static_cast<uint16_t>(pc_.full + (int8_t)operand_);
pc_.bytes.low = nextAddress.bytes.low;
@ -534,6 +589,11 @@ if(number_of_cycles <= Cycles(0)) break;
pc_.full = nextAddress.full;
throwaway_read(halfUpdatedPc);
break;
} else if(is_65c02(personality)) {
// 65C02 modification to all branches: a branch that is taken but requires only a single cycle
// to target its destination skips any pending interrupts.
// Cf. http://forum.6502.org/viewtopic.php?f=4&t=1634
scheduled_program_counter_ = fetch_decode_execute;
}
continue;
@ -570,8 +630,6 @@ if(number_of_cycles <= Cycles(0)) break;
}
} break;
#undef BRA
// MARK: - Transfers
case OperationTXA: zero_result_ = negative_result_ = a_ = x_; continue;
@ -611,7 +669,10 @@ if(number_of_cycles <= Cycles(0)) break;
continue;
}
if(uses_ready_line && ready_line_is_enabled_ && isReadOperation(nextBusOperation)) {
if(has_stpwai(personality) && (stop_is_active_ || wait_is_active_)) {
break;
}
if(uses_ready_line && ready_line_is_enabled_ && (is_65c02(personality) || isReadOperation(nextBusOperation))) {
ready_is_active_ = true;
break;
}
@ -629,7 +690,7 @@ if(number_of_cycles <= Cycles(0)) break;
bus_handler_.flush();
}
template <typename T, bool uses_ready_line> void Processor<T, uses_ready_line>::set_ready_line(bool active) {
template <Personality personality, typename T, bool uses_ready_line> void Processor<personality, T, uses_ready_line>::set_ready_line(bool active) {
assert(uses_ready_line);
if(active) {
ready_line_is_enabled_ = true;
@ -677,6 +738,7 @@ inline const ProcessorStorage::MicroOp *ProcessorStorage::get_reset_program() {
CycleNoWritePush,
OperationRSTPickVector,
CycleNoWritePush,
OperationSetNMIRSTFlags,
CycleReadVectorLow,
CycleReadVectorHigh,
OperationMoveToNextProgram
@ -693,7 +755,7 @@ inline const ProcessorStorage::MicroOp *ProcessorStorage::get_irq_program() {
OperationBRKPickVector,
OperationSetOperandFromFlags,
CyclePushOperand,
OperationSetI,
OperationSetIRQFlags,
CycleReadVectorLow,
CycleReadVectorHigh,
OperationMoveToNextProgram
@ -710,6 +772,7 @@ inline const ProcessorStorage::MicroOp *ProcessorStorage::get_nmi_program() {
OperationNMIPickVector,
OperationSetOperandFromFlags,
CyclePushOperand,
OperationSetNMIRSTFlags,
CycleReadVectorLow,
CycleReadVectorHigh,
OperationMoveToNextProgram

View File

@ -29,7 +29,7 @@ using namespace CPU::MOS6502;
#define Read(...) CycleFetchOperandFromAddress, __VA_ARGS__
#define Write(...) __VA_ARGS__, CycleWriteOperandToAddress
#define ReadModifyWrite(...) CycleFetchOperandFromAddress, (personality == P6502) ? CycleWriteOperandToAddress : CycleFetchOperandFromAddress, __VA_ARGS__, CycleWriteOperandToAddress
#define ReadModifyWrite(...) CycleFetchOperandFromAddress, is_65c02(personality) ? CycleFetchOperandFromAddress : CycleWriteOperandToAddress, __VA_ARGS__, CycleWriteOperandToAddress
#define AbsoluteRead(op) Program(Absolute, Read(op))
#define AbsoluteXRead(op) Program(AbsoluteXr, Read(op))
@ -60,8 +60,11 @@ using namespace CPU::MOS6502;
#define IndexedIndirectReadModifyWrite(...) Program(IndexedIndirect, ReadModifyWrite(__VA_ARGS__))
#define IndirectIndexedReadModifyWrite(...) Program(IndirectIndexed, ReadModifyWrite(__VA_ARGS__))
#define FastAbsoluteXReadModifyWrite(...) Program(AbsoluteXr, ReadModifyWrite(__VA_ARGS__))
#define FastAbsoluteYReadModifyWrite(...) Program(AbsoluteYr, ReadModifyWrite(__VA_ARGS__))
#define Immediate(op) Program(OperationIncrementPC, op)
#define Implied(op) Program(OperationCopyOperandFromA, op, OperationCopyOperandToA)
#define Implied(op) Program(OperationSTA, op, OperationCopyOperandToA)
#define ZeroNop() Program(Zero, CycleFetchOperandFromAddress)
#define ZeroXNop() Program(ZeroX, CycleFetchOperandFromAddress)
@ -70,7 +73,7 @@ using namespace CPU::MOS6502;
#define ImpliedNop() {OperationMoveToNextProgram}
#define ImmediateNop() Program(OperationIncrementPC)
#define JAM {CycleFetchOperand, CycleScheduleJam}
#define JAM {CycleFetchOperand, OperationScheduleJam}
ProcessorStorage::ProcessorStorage(Personality personality) {
// only the interrupt flag is defined upon reset but get_flags isn't going to
@ -80,7 +83,7 @@ ProcessorStorage::ProcessorStorage(Personality personality) {
overflow_flag_ &= Flag::Overflow;
const InstructionList operations_6502[256] = {
/* 0x00 BRK */ Program(CycleIncPCPushPCH, CyclePushPCL, OperationBRKPickVector, OperationSetOperandFromFlagsWithBRKSet, CyclePushOperand, OperationSetI, CycleReadVectorLow, CycleReadVectorHigh),
/* 0x00 BRK */ Program(CycleIncPCPushPCH, CyclePushPCL, OperationBRKPickVector, OperationSetOperandFromFlagsWithBRKSet, CyclePushOperand, OperationSetIRQFlags, CycleReadVectorLow, CycleReadVectorHigh),
/* 0x01 ORA x, ind */ IndexedIndirectRead(OperationORA),
/* 0x02 JAM */ JAM, /* 0x03 ASO x, ind */ IndexedIndirectReadModifyWrite(OperationASO),
/* 0x04 NOP zpg */ ZeroNop(), /* 0x05 ORA zpg */ ZeroRead(OperationORA),
@ -221,11 +224,37 @@ ProcessorStorage::ProcessorStorage(Personality personality) {
memcpy(operations_, operations_6502, sizeof(operations_));
// Patch the table according to the chip's personality.
//
// The 6502 and NES 6502 both have the same mapping of operation codes to actions
// (respect for the decimal mode flag aside); included in that are 'unofficial'
// operations — spots that are not formally defined to do anything but which the
// processor makes no particular effort to react to in a well-defined way.
//
// The 65C02s add some official instructions but also ensure that all of the
// undefined ones act as no-ops of various addressing modes.
//
// So the branch below has to add a bunch of new actions but also removes various
// others by dint of replacing them with NOPs.
//
// Those 6502 opcodes that need redefining, one way or the other, are:
//
// 0x02, 0x03, 0x04, 0x07, 0x0b, 0x0c, 0x0f, 0x12, 0x13, 0x14, 0x17, 0x1a, 0x1b, 0x1c, 0x1f,
// 0x22, 0x23, 0x27, 0x2b, 0x2f, 0x32, 0x33, 0x34, 0x37, 0x3a, 0x3b, 0x3c, 0x3f,
// 0x42, 0x43, 0x47, 0x4b, 0x4f, 0x52, 0x53, 0x57, 0x5a, 0x5b, 0x5f,
// 0x62, 0x63, 0x64, 0x67, 0x6b, 0x6f, 0x72, 0x73, 0x74, 0x77, 0x7b, 0x7a, 0x7c, 0x7f,
// 0x80, 0x82, 0x83, 0x87, 0x89, 0x8b, 0x8f, 0x92, 0x93, 0x97, 0x9b, 0x9e, 0x9c, 0x9f,
// 0xa3, 0xa7, 0xab, 0xaf, 0xb2, 0xb3, 0xb7, 0xbb, 0xbf,
// 0xc3, 0xc7, 0xcb, 0xcf, 0xd2, 0xd3, 0xd7, 0xda, 0xdb, 0xdf,
// 0xe3, 0xe7, 0xeb, 0xef, 0xf2, 0xf3, 0xf7, 0xfa, 0xfb, 0xff
//
// ... not including those that aren't defined on the 6502 but perform NOPs exactly like they
// would on a 65C02.
#define Install(location, instructions) {\
const InstructionList code = instructions; \
memcpy(&operations_[location], code, sizeof(InstructionList)); \
}
if(personality != P6502) {
if(is_65c02(personality)) {
// Add P[L/H][X/Y].
Install(0x5a, Program(CyclePushY));
Install(0xda, Program(CyclePushX));
@ -235,19 +264,6 @@ ProcessorStorage::ProcessorStorage(Personality personality) {
// Add BRA.
Install(0x80, Program(OperationBRA));
// Add BBS and BBR. These take five cycles. My guessed breakdown is:
// 1. read opcode
// 2. read operand
// 3. read zero page
// 4. read second operand
// 5. read from PC without top byte fixed yet
// ... with the caveat that (3) and (4) could be the other way around.
for(int location = 0x0f; location <= 0xff; location += 0x10) {
Install(location, Program(OperationLoadAddressZeroPage, CycleFetchOperandFromAddress, OperationBBRBBS));
}
// Add NOPs.
// The 1-byte, 1-cycle (!) NOPs.
for(int c = 0x03; c <= 0xf3; c += 0x10) {
Install(c, ImpliedNop());
@ -304,12 +320,67 @@ ProcessorStorage::ProcessorStorage(Personality personality) {
Install(0x14, ZeroReadModifyWrite(OperationTRB));
Install(0x1c, AbsoluteReadModifyWrite(OperationTRB));
// Add RMB and SMB.
for(int c = 0x07; c <= 0x77; c += 0x10) {
Install(c, ZeroReadModifyWrite(OperationRMB));
// Install faster ASL, LSR, ROL, ROR abs,[x/y]. Note: INC, DEC deliberately not improved.
Install(0x1e, FastAbsoluteXReadModifyWrite(OperationASL));
Install(0x1f, FastAbsoluteXReadModifyWrite(OperationASO));
Install(0x3e, FastAbsoluteXReadModifyWrite(OperationROL));
Install(0x3f, FastAbsoluteXReadModifyWrite(OperationRLA));
Install(0x5e, FastAbsoluteXReadModifyWrite(OperationLSR));
Install(0x5f, FastAbsoluteXReadModifyWrite(OperationLSE));
Install(0x7e, FastAbsoluteXReadModifyWrite(OperationROR));
Install(0x7f, FastAbsoluteXReadModifyWrite(OperationRRA, OperationADC));
// Outstanding:
// 0x07, 0x0f, 0x17, 0x1f,
// 0x27, 0x2f, 0x37, 0x3f,
// 0x47, 0x4f, 0x57, 0x5f,
// 0x67, 0x6f, 0x77, 0x7f,
// 0x87, 0x8f, 0x97, 0x9f,
// 0xa7, 0xaf, 0xb7, 0xbf,
// 0xc7, 0xcb, 0xcf, 0xd7, 0xdb, 0xdf,
// 0xe7, 0xef, 0xf7, 0xff
if(has_bbrbbsrmbsmb(personality)) {
// Add BBS and BBR. These take five cycles. My guessed breakdown is:
// 1. read opcode
// 2. read operand
// 3. read zero page
// 4. read second operand
// 5. read from PC without top byte fixed yet
// ... with the caveat that (3) and (4) could be the other way around.
for(int location = 0x0f; location <= 0xff; location += 0x10) {
Install(location, Program(OperationLoadAddressZeroPage, CycleFetchOperandFromAddress, OperationBBRBBS));
}
// Add RMB and SMB.
for(int c = 0x07; c <= 0x77; c += 0x10) {
Install(c, ZeroReadModifyWrite(OperationRMB));
}
for(int c = 0x87; c <= 0xf7; c += 0x10) {
Install(c, ZeroReadModifyWrite(OperationSMB));
}
} else {
for(int location = 0x0f; location <= 0xef; location += 0x20) {
Install(location, AbsoluteNop());
}
for(int location = 0x1f; location <= 0xff; location += 0x20) {
Install(location, AbsoluteXNop());
}
for(int c = 0x07; c <= 0xe7; c += 0x20) {
Install(c, ZeroNop());
}
for(int c = 0x17; c <= 0xf7; c += 0x20) {
Install(c, ZeroXNop());
}
}
for(int c = 0x87; c <= 0xf7; c += 0x10) {
Install(c, ZeroReadModifyWrite(OperationSMB));
// Outstanding:
// 0xcb, 0xdb,
if(has_stpwai(personality)) {
Install(0xcb, Program(OperationScheduleWait));
Install(0xdb, Program(OperationScheduleStop));
} else {
Install(0xcb, ImpliedNop());
Install(0xdb, ZeroXNop());
}
}
#undef Install

View File

@ -17,68 +17,184 @@ class ProcessorStorage {
protected:
ProcessorStorage(Personality);
/*
/*!
This emulation functions by decomposing instructions into micro programs, consisting of the micro operations
as per the enum below. Each micro op takes at most one cycle. By convention, those called CycleX take a cycle
defined by MicroOp. Each micro op takes at most one cycle. By convention, those called CycleX take a cycle
to perform whereas those called OperationX occur for free (so, in effect, their cost is loaded onto the next cycle).
This micro-instruction set was put together in a fairly ad hoc fashion, I'm afraid, so is unlikely to be optimal.
*/
enum MicroOp {
CycleFetchOperation, CycleFetchOperand, OperationDecodeOperation, CycleIncPCPushPCH,
CyclePushPCH, CyclePushPCL, CyclePushA, CyclePushOperand,
CyclePushX, CyclePushY, OperationSetI,
CycleFetchOperation, // fetches (PC) to operation_, storing PC to last_operation_pc_ before incrementing it
CycleFetchOperand, // 6502: fetches from (PC) to operand_; 65C02: as 6502 unless operation_ indicates a one-cycle NOP, in which case this is a no0op
OperationDecodeOperation, // schedules the microprogram associated with operation_
OperationMoveToNextProgram, // either schedules the next fetch-decode-execute or an interrupt response if a request has been pending for at least one cycle
OperationBRKPickVector, OperationNMIPickVector, OperationRSTPickVector,
CycleReadVectorLow, CycleReadVectorHigh,
CycleIncPCPushPCH, // increments the PC and pushes PC.h to the stack
CyclePushPCL, // pushes PC.l to the stack
CyclePushPCH, // pushes PC.h to the stack
CyclePushA, // pushes A to the stack
CyclePushX, // pushes X to the stack
CyclePushY, // pushes Y to the stack
CyclePushOperand, // pushes operand_ to the stack
CycleReadFromS, CycleReadFromPC,
CyclePullOperand, CyclePullPCL, CyclePullPCH, CyclePullA,
CyclePullX, CyclePullY,
CycleNoWritePush,
CycleReadAndIncrementPC, CycleIncrementPCAndReadStack, CycleIncrementPCReadPCHLoadPCL, CycleReadPCHLoadPCL,
CycleReadAddressHLoadAddressL,
OperationSetIRQFlags, // 6502: sets I; 65C02: sets I and resets D
OperationSetNMIRSTFlags, // 6502: no-op. 65C02: resets D
CycleReadPCLFromAddress, CycleReadPCHFromAddressLowInc, CycleReadPCHFromAddressFixed, CycleReadPCHFromAddressInc,
OperationBRKPickVector, // 65C02: sets next_address_ to the BRK vector location; 6502: as 65C02 if no NMI is pending; otherwise sets next_address_ to the NMI address and resets the internal NMI-pending flag
OperationNMIPickVector, // sets next_address_ to the NMI vector
OperationRSTPickVector, // sets next_address_ to the RST vector
CycleReadVectorLow, // reads PC.l from next_address_
CycleReadVectorHigh, // reads PC.h from (next_address_+1)
CycleLoadAddressAbsolute,
OperationLoadAddressZeroPage, CycleLoadAddessZeroX, CycleLoadAddessZeroY, CycleAddXToAddressLow,
CycleAddYToAddressLow, CycleAddXToAddressLowRead, OperationCorrectAddressHigh, CycleAddYToAddressLowRead,
OperationMoveToNextProgram, OperationIncrementPC,
CycleFetchOperandFromAddress, CycleWriteOperandToAddress, OperationCopyOperandFromA, OperationCopyOperandToA,
CycleIncrementPCFetchAddressLowFromOperand, CycleAddXToOperandFetchAddressLow, CycleIncrementOperandFetchAddressHigh, OperationDecrementOperand,
CycleFetchAddressLowFromOperand,
OperationIncrementOperand, OperationORA, OperationAND, OperationEOR,
OperationINS, OperationADC, OperationSBC, OperationLDA,
OperationLDX, OperationLDY, OperationLAX, OperationSTA,
OperationSTX, OperationSTY, OperationSTZ,
OperationSAX, OperationSHA,
OperationSHX, OperationSHY, OperationSHS, OperationCMP,
OperationCPX, OperationCPY, OperationBIT, OperationBITNoNV,
OperationASL, OperationRMB, OperationSMB,
OperationASO, OperationROL, OperationRLA, OperationLSR,
OperationLSE, OperationASR, OperationROR, OperationRRA,
OperationCLC, OperationCLI, OperationCLV, OperationCLD,
OperationSEC, OperationSEI, OperationSED,
OperationTRB, OperationTSB,
CycleReadFromS, // performs a read from the stack pointer, throwing the result away
CycleReadFromPC, // performs a read from the program counter, throwing the result away
OperationINC, OperationDEC, OperationINX, OperationDEX,
OperationINY, OperationDEY, OperationINA, OperationDEA,
CyclePullPCL, // pulls PC.l from the stack
CyclePullPCH, // pulls PC.h from the stack
CyclePullA, // pulls A from the stack
CyclePullX, // pulls X from the stack
CyclePullY, // pulls Y from the stack
CyclePullOperand, // pulls operand_ from the stack
OperationBPL, OperationBMI, OperationBVC, OperationBVS,
OperationBCC, OperationBCS, OperationBNE, OperationBEQ,
OperationBRA, OperationBBRBBS,
CycleNoWritePush, // decrements S as though it were a push, but reads from the new stack address instead of writing
CycleReadAndIncrementPC, // reads from the PC, throwing away the result, and increments the PC
CycleIncrementPCAndReadStack, // increments the PC and reads from the stack pointer, throwing away the result
CycleIncrementPCReadPCHLoadPCL, // increments the PC, schedules a read of PC.h from the post-incremented PC, then copies operand_ to PC.l
CycleReadPCHLoadPCL, // schedules a read of PC.h from the post-incremented PC, then copies operand_ to PC.l
CycleReadAddressHLoadAddressL, // increments the PC; copies operand_ to address_.l; reads address_.h from the new PC
OperationTXA, OperationTYA, OperationTXS, OperationTAY,
OperationTAX, OperationTSX,
CycleReadPCLFromAddress, // reads PC.l from address_
CycleReadPCHFromAddressLowInc, // increments address_.l and reads PC.h from address_
CycleReadPCHFromAddressFixed, // if address_.l is 0, increments address_.h; and reads PC.h from address_
CycleReadPCHFromAddressInc, // increments address_ and reads PC.h from it
OperationARR, OperationSBX, OperationLXA, OperationANE,
OperationANC, OperationLAS,
CycleLoadAddressAbsolute, // copies operand_ to address_.l, increments the PC, reads address_.h from PC, increments the PC again
OperationLoadAddressZeroPage, // copies operand_ to address_ and increments the PC
CycleLoadAddessZeroX, // copies (operand_+x)&0xff to address_, increments the PC, and reads from operand_, throwing away the result
CycleLoadAddessZeroY, // copies (operand_+y)&0xff to address_, increments the PC, and reads from operand_, throwing away the result
CycleFetchFromHalfUpdatedPC, CycleAddSignedOperandToPC, OperationAddSignedOperandToPC16,
CycleAddXToAddressLow, // calculates address_ + x and stores it to next_address_; copies next_address_.l back to address_.l; 6502: if address_ now does not equal next_address_, schedules a throwaway read from address_; 65C02: schedules a throaway read from PC-1
CycleAddYToAddressLow, // calculates address_ + y and stores it to next_address_; copies next_address_.l back to address_.l; 6502: if address_ now does not equal next_address_, schedules a throwaway read from address_; 65C02: schedules a throaway read from PC-1
CycleAddXToAddressLowRead, // calculates address_ + x and stores it to next_address; copies next_address.l back to address_.l; 6502: schedules a throwaway read from address_; 65C02: schedules a throaway read from PC-1
CycleAddYToAddressLowRead, // calculates address_ + y and stores it to next_address; copies next_address.l back to address_.l; 6502: schedules a throwaway read from address_; 65C02: schedules a throaway read from PC-1
OperationCorrectAddressHigh, // copies next_address_ to address_
OperationSetFlagsFromOperand, OperationSetOperandFromFlagsWithBRKSet,
OperationSetOperandFromFlags,
OperationSetFlagsFromA, OperationSetFlagsFromX, OperationSetFlagsFromY,
CycleScheduleJam
OperationIncrementPC, // increments the PC
CycleFetchOperandFromAddress, // fetches operand_ from address_
CycleWriteOperandToAddress, // writes operand_ to address_
CycleIncrementPCFetchAddressLowFromOperand, // increments the PC and loads address_.l from (operand_)
CycleAddXToOperandFetchAddressLow, // adds x [in]to operand_, producing an 8-bit result, and reads address_.l from (operand_)
CycleIncrementOperandFetchAddressHigh, // increments operand_, producing an 8-bit result, and reads address_.h from (operand_)
OperationDecrementOperand, // decrements operand_
OperationIncrementOperand, // increments operand_
CycleFetchAddressLowFromOperand, // reads address_.l from (operand_)
OperationORA, // ORs operand_ into a, setting the negative and zero flags
OperationAND, // ANDs operand_ into a, setting the negative and zero flags
OperationEOR, // EORs operand_ into a, setting the negative and zero flags
OperationINS, // increments operand_, then performs an SBC of operand_ from a
OperationADC, // performs an ADC of operand_ into a_; if this is a 65C02 and decimal mode is set, performs an extra read to operand_ from address_
OperationSBC, // performs an SBC of operand_ from a_; if this is a 65C02 and decimal mode is set, performs an extra read to operand_ from address_
OperationCMP, // CMPs a and operand_, setting negative, zero and carry flags
OperationCPX, // CMPs x and operand_, setting negative, zero and carry flags
OperationCPY, // CMPs y and operand_, setting negative, zero and carry flags
OperationBIT, // sets the zero, negative and overflow flags as per a BIT of operand_ against a
OperationBITNoNV, // sets the zero flag as per a BIT of operand_ against a
OperationLDA, // loads a with operand_, setting the negative and zero flags
OperationLDX, // loads x with operand_, setting the negative and zero flags
OperationLDY, // loads y with operand_, setting the negative and zero flags
OperationLAX, // loads a and x with operand_, setting the negative and zero flags
OperationCopyOperandToA, // sets a_ = operand_, not setting any flags
OperationSTA, // loads operand_ with a
OperationSTX, // loads operand_ with x
OperationSTY, // loads operand_ with y
OperationSTZ, // loads operand_ with 0
OperationSAX, // loads operand_ with a & x
OperationSHA, // loads operand_ with a & x & (address.h+1)
OperationSHX, // loads operand_ with x & (address.h+1)
OperationSHY, // loads operand_ with y & (address.h+1)
OperationSHS, // loads s with a & x, then loads operand_ with s & (address.h+1)
OperationASL, // shifts operand_ left, moving the top bit into carry and setting the negative and zero flags
OperationASO, // performs an ASL of operand and ORs it into a
OperationROL, // performs a ROL of operand_
OperationRLA, // performs a ROL of operand_ and ANDs it into a
OperationLSR, // shifts operand_ right, setting carry, negative and zero flags
OperationLSE, // performs an LSR and EORs the result into a
OperationASR, // ANDs operand_ into a, then performs an LSR
OperationROR, // performs a ROR of operand_, setting carry, negative and zero flags
OperationRRA, // performs a ROR of operand_ but sets only the carry flag
OperationCLC, // resets the carry flag
OperationCLI, // resets I
OperationCLV, // resets the overflow flag
OperationCLD, // resets the decimal flag
OperationSEC, // sets the carry flag
OperationSEI, // sets I
OperationSED, // sets the decimal flag
OperationRMB, // resets the bit in operand_ implied by operatiopn_
OperationSMB, // sets the bit in operand_ implied by operatiopn_
OperationTRB, // sets zero according to operand_ & a, then resets any bits in operand_ that are set in a
OperationTSB, // sets zero according to operand_ & a, then sets any bits in operand_ that are set in a
OperationINC, // increments operand_, setting the negative and zero flags
OperationDEC, // decrements operand_, setting the negative and zero flags
OperationINX, // increments x, setting the negative and zero flags
OperationDEX, // decrements x, setting the negative and zero flags
OperationINY, // increments y, setting the negative and zero flags
OperationDEY, // decrements y, setting the negative and zero flags
OperationINA, // increments a, setting the negative and zero flags
OperationDEA, // decrements a, setting the negative and zero flags
OperationBPL, // schedules the branch program if the negative flag is clear
OperationBMI, // schedules the branch program if the negative flag is set
OperationBVC, // schedules the branch program if the overflow flag is clear
OperationBVS, // schedules the branch program if the overflow flag is set
OperationBCC, // schedules the branch program if the carry flag is clear
OperationBCS, // schedules the branch program if the carry flag is set
OperationBNE, // schedules the branch program if the zero flag is clear
OperationBEQ, // schedules the branch program if the zero flag is set; 65C02: otherwise jumps straight into a fetch-decode-execute without considering whether to take an interrupt
OperationBRA, // schedules the branch program
OperationBBRBBS, // inspecting the operation_, if the appropriate bit of operand_ is set or clear schedules a program to read and act upon the second operand; otherwise schedule a program to read and discard it
OperationTXA, // copies x to a, setting the zero and negative flags
OperationTYA, // copies y to a, setting the zero and negative flags
OperationTXS, // copies x to s
OperationTAY, // copies a to y, setting the zero and negative flags
OperationTAX, // copies a to x, setting the zero and negative flags
OperationTSX, // copies s to x, setting the zero and negative flags
/* The following are amongst the 6502's undocumented (/unintended) operations */
OperationARR, // performs a mixture of ANDing operand_ into a, and shifting the result right
OperationSBX, // performs a mixture of an SBC of x&a and operand_, mutating x
OperationLXA, // loads a and x with (a | 0xee) & operand, setting the negative and zero flags
OperationANE, // loads a_ with (a | 0xee) & operand & x, setting the negative and zero flags
OperationANC, // ANDs operand_ into a, setting the negative and zero flags, and loading carry as if the result were shifted right
OperationLAS, // loads a, x and s with s & operand, setting the negative and zero flags
CycleFetchFromHalfUpdatedPC, // performs a throwaway read from (PC + (signed)operand).l combined with PC.h
CycleAddSignedOperandToPC, // sets next_address to PC + (signed)operand. If the high byte of next_address differs from the PC, schedules a throwaway read from the half-updated PC. 65C02 specific: if the top two bytes are the same, proceeds directly to fetch-decode-execute, ignoring any pending interrupts.
OperationAddSignedOperandToPC16, // adds (signed)operand into the PC
OperationSetFlagsFromOperand, // sets all flags based on operand_
OperationSetOperandFromFlagsWithBRKSet, // sets operand_ to the value of all flags, with the break flag set
OperationSetOperandFromFlags, // sets operand_ to the value of all flags
OperationSetFlagsFromA, // sets the zero and negative flags based on the value of a
OperationSetFlagsFromX, // sets the zero and negative flags based on the value of x
OperationSetFlagsFromY, // sets the zero and negative flags based on the value of y
OperationScheduleJam, // schedules the program for operation F2
OperationScheduleWait, // puts the processor into WAI mode (i.e. it'll do nothing until an interrupt is received)
OperationScheduleStop, // puts the processor into STP mode (i.e. it'll do nothing until a reset is received)
};
using InstructionList = MicroOp[10];
@ -139,6 +255,8 @@ class ProcessorStorage {
bool ready_is_active_ = false;
bool ready_line_is_enabled_ = false;
bool stop_is_active_ = false;
bool wait_is_active_ = false;
uint8_t irq_line_ = 0, irq_request_history_ = 0;
bool nmi_line_is_enabled_ = false, set_overflow_line_is_enabled_ = false;