mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-27 16:31:31 +00:00
Merge branch 'master' into AppleDelay
This commit is contained in:
commit
f8d46f8f3d
@ -61,7 +61,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;
|
||||
@ -299,7 +299,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_) {
|
||||
|
@ -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:
|
||||
|
@ -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),
|
||||
|
@ -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];
|
||||
|
@ -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_;
|
||||
|
@ -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];
|
||||
|
@ -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_;
|
||||
|
@ -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;
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,52 +320,68 @@ 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
|
||||
}
|
||||
|
||||
#undef Program
|
||||
#undef Absolute
|
||||
#undef AbsoluteX
|
||||
#undef AbsoluteY
|
||||
#undef Zero
|
||||
#undef ZeroX
|
||||
#undef ZeroY
|
||||
#undef IndexedIndirect
|
||||
#undef IndirectIndexed
|
||||
#undef Read
|
||||
#undef Write
|
||||
#undef ReadModifyWrite
|
||||
#undef AbsoluteRead
|
||||
#undef AbsoluteXRead
|
||||
#undef AbsoluteYRead
|
||||
#undef ZeroRead
|
||||
#undef ZeroXRead
|
||||
#undef ZeroYRead
|
||||
#undef IndexedIndirectRead
|
||||
#undef IndirectIndexedRead
|
||||
#undef AbsoluteWrite
|
||||
#undef AbsoluteXWrite
|
||||
#undef AbsoluteYWrite
|
||||
#undef ZeroWrite
|
||||
#undef ZeroXWrite
|
||||
#undef ZeroYWrite
|
||||
#undef IndexedIndirectWrite
|
||||
#undef IndirectIndexedWrite
|
||||
#undef AbsoluteReadModifyWrite
|
||||
#undef AbsoluteXReadModifyWrite
|
||||
#undef AbsoluteYReadModifyWrite
|
||||
#undef ZeroReadModifyWrite
|
||||
#undef ZeroXReadModifyWrite
|
||||
#undef ZeroYReadModify
|
||||
#undef IndexedIndirectReadModify
|
||||
#undef IndirectIndexedReadModify
|
||||
#undef Immediate
|
||||
#undef Implied
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user