mirror of
https://github.com/TomHarte/CLK.git
synced 2024-10-15 20:24:07 +00:00
Merge pull request #523 from TomHarte/Further65C02
Further corrects 65C02 behaviour
This commit is contained in:
commit
7f0f17f435
@ -63,7 +63,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
|||||||
uint8_t *ram_, *aux_ram_;
|
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_;
|
VideoBusHandler video_bus_handler_;
|
||||||
std::unique_ptr<AppleII::Video::Video<VideoBusHandler, is_iie()>> video_;
|
std::unique_ptr<AppleII::Video::Video<VideoBusHandler, is_iie()>> video_;
|
||||||
int cycles_into_current_line_ = 0;
|
int cycles_into_current_line_ = 0;
|
||||||
@ -301,7 +301,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
ConcreteMachine(const Analyser::Static::AppleII::Target &target, const ROMMachine::ROMFetcher &rom_fetcher):
|
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_),
|
video_bus_handler_(ram_, aux_ram_),
|
||||||
audio_toggle_(audio_queue_),
|
audio_toggle_(audio_queue_),
|
||||||
speaker_(audio_toggle_) {
|
speaker_(audio_toggle_) {
|
||||||
|
@ -32,7 +32,7 @@ template<class T> class Cartridge:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
Cartridge(const std::vector<uint8_t> &rom) :
|
Cartridge(const std::vector<uint8_t> &rom) :
|
||||||
m6502_(CPU::MOS6502::Personality::P6502, *this),
|
m6502_(*this),
|
||||||
rom_(rom),
|
rom_(rom),
|
||||||
bus_extender_(rom_.data(), rom.size()) {
|
bus_extender_(rom_.data(), rom.size()) {
|
||||||
// The above works because bus_extender_ is declared after rom_ in the instance storage list;
|
// 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:
|
protected:
|
||||||
CPU::MOS6502::Processor<Cartridge<T>, true> m6502_;
|
CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, Cartridge<T>, true> m6502_;
|
||||||
std::vector<uint8_t> rom_;
|
std::vector<uint8_t> rom_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -18,7 +18,7 @@ using namespace Commodore::C1540;
|
|||||||
|
|
||||||
MachineBase::MachineBase(Personality personality, const ROMMachine::ROMFetcher &rom_fetcher) :
|
MachineBase::MachineBase(Personality personality, const ROMMachine::ROMFetcher &rom_fetcher) :
|
||||||
Storage::Disk::Controller(1000000),
|
Storage::Disk::Controller(1000000),
|
||||||
m6502_(CPU::MOS6502::Personality::P6502, *this),
|
m6502_(*this),
|
||||||
drive_(new Storage::Disk::Drive(1000000, 300, 2)),
|
drive_(new Storage::Disk::Drive(1000000, 300, 2)),
|
||||||
serial_port_VIA_port_handler_(new SerialPortVIA(serial_port_VIA_)),
|
serial_port_VIA_port_handler_(new SerialPortVIA(serial_port_VIA_)),
|
||||||
serial_port_(new SerialPort),
|
serial_port_(new SerialPort),
|
||||||
|
@ -143,7 +143,7 @@ class MachineBase:
|
|||||||
void set_activity_observer(Activity::Observer *observer);
|
void set_activity_observer(Activity::Observer *observer);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
CPU::MOS6502::Processor<MachineBase, false> m6502_;
|
CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, MachineBase, false> m6502_;
|
||||||
std::shared_ptr<Storage::Disk::Drive> drive_;
|
std::shared_ptr<Storage::Disk::Drive> drive_;
|
||||||
|
|
||||||
uint8_t ram_[0x800];
|
uint8_t ram_[0x800];
|
||||||
|
@ -293,7 +293,7 @@ class ConcreteMachine:
|
|||||||
public Activity::Source {
|
public Activity::Source {
|
||||||
public:
|
public:
|
||||||
ConcreteMachine(const Analyser::Static::Commodore::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
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),
|
user_port_via_port_handler_(new UserPortVIA),
|
||||||
keyboard_via_port_handler_(new KeyboardVIA),
|
keyboard_via_port_handler_(new KeyboardVIA),
|
||||||
serial_port_(new SerialPort),
|
serial_port_(new SerialPort),
|
||||||
@ -703,7 +703,7 @@ class ConcreteMachine:
|
|||||||
void update_video() {
|
void update_video() {
|
||||||
mos6560_->run_for(cycles_since_mos6560_update_.flush());
|
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> character_rom_;
|
||||||
std::vector<uint8_t> basic_rom_;
|
std::vector<uint8_t> basic_rom_;
|
||||||
|
@ -50,7 +50,7 @@ class ConcreteMachine:
|
|||||||
public Activity::Source {
|
public Activity::Source {
|
||||||
public:
|
public:
|
||||||
ConcreteMachine(const Analyser::Static::Acorn::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
ConcreteMachine(const Analyser::Static::Acorn::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
||||||
m6502_(CPU::MOS6502::Personality::P6502, *this),
|
m6502_(*this),
|
||||||
sound_generator_(audio_queue_),
|
sound_generator_(audio_queue_),
|
||||||
speaker_(sound_generator_) {
|
speaker_(sound_generator_) {
|
||||||
memset(key_states_, 0, sizeof(key_states_));
|
memset(key_states_, 0, sizeof(key_states_));
|
||||||
@ -541,7 +541,7 @@ class ConcreteMachine:
|
|||||||
m6502_.set_irq_line(interrupt_status_ & 1);
|
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.
|
// Things that directly constitute the memory map.
|
||||||
uint8_t roms_[16][16384];
|
uint8_t roms_[16][16384];
|
||||||
|
@ -207,7 +207,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
ConcreteMachine(const Analyser::Static::Oric::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
ConcreteMachine(const Analyser::Static::Oric::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
||||||
m6502_(CPU::MOS6502::Personality::P6502, *this),
|
m6502_(*this),
|
||||||
ay8910_(audio_queue_),
|
ay8910_(audio_queue_),
|
||||||
speaker_(ay8910_),
|
speaker_(ay8910_),
|
||||||
via_port_handler_(audio_queue_, ay8910_, speaker_, tape_player_, keyboard_),
|
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_invisible_ram_top_ = 0xffff;
|
||||||
const uint16_t basic_visible_ram_top_ = 0xbfff;
|
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
|
// RAM and ROM
|
||||||
std::vector<uint8_t> rom_, microdisc_rom_, colour_rom_;
|
std::vector<uint8_t> rom_, microdisc_rom_, colour_rom_;
|
||||||
|
@ -40,7 +40,7 @@ static CPU::MOS6502::Register registerForRegister(CSTestMachine6502Register reg)
|
|||||||
|
|
||||||
if(self) {
|
if(self) {
|
||||||
_processor = CPU::MOS6502::AllRAMProcessor::Processor(
|
_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;
|
return self;
|
||||||
|
@ -37,11 +37,18 @@ enum Register {
|
|||||||
The list of 6502 variants supported by this implementation.
|
The list of 6502 variants supported by this implementation.
|
||||||
*/
|
*/
|
||||||
enum Personality {
|
enum Personality {
|
||||||
P6502, // the original 6502, replete with various undocumented instructions
|
P6502, // the original [NMOS] 6502, replete with various undocumented instructions
|
||||||
P65C02, // the 65C02; an extended 6502 with a few extra instructions and addressing modes for existing instructions
|
PNES6502, // the NES's 6502, which is like a 6502 but lacks decimal mode (though it retains the decimal flag)
|
||||||
P65SC02, // like the 65C02, but lacking bit instructions
|
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
|
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.
|
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 also nominate whether the processor includes support for the ready line. Declining to support the ready line
|
||||||
can produce a minor runtime performance improvement.
|
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:
|
public:
|
||||||
/*!
|
/*!
|
||||||
Constructs an instance of the 6502 that will use @c bus_handler for all bus communications.
|
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.
|
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);
|
void set_ready_line(bool active);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Personality personality_;
|
|
||||||
T &bus_handler_;
|
T &bus_handler_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -15,10 +15,10 @@ using namespace CPU::MOS6502;
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler {
|
template <Personality personality> class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler {
|
||||||
public:
|
public:
|
||||||
ConcreteAllRAMProcessor(Personality personality) :
|
ConcreteAllRAMProcessor() :
|
||||||
mos6502_(personality, *this) {
|
mos6502_(*this) {
|
||||||
mos6502_.set_power_on(false);
|
mos6502_.set_power_on(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,11 +63,20 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CPU::MOS6502::Processor<ConcreteAllRAMProcessor, false> mos6502_;
|
CPU::MOS6502::Processor<personality, ConcreteAllRAMProcessor, false> mos6502_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AllRAMProcessor *AllRAMProcessor::Processor(Personality personality) {
|
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.
|
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[] = {
|
static const MicroOp do_branch[] = {
|
||||||
CycleReadFromPC,
|
CycleReadFromPC,
|
||||||
CycleAddSignedOperandToPC,
|
CycleAddSignedOperandToPC,
|
||||||
@ -63,11 +63,33 @@ if(number_of_cycles <= Cycles(0)) break;
|
|||||||
|
|
||||||
while(number_of_cycles > Cycles(0)) {
|
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)) {
|
while(uses_ready_line && ready_is_active_ && number_of_cycles > Cycles(0)) {
|
||||||
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
|
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) {
|
if(nextBusOperation != BusOperation::None) {
|
||||||
bus_access();
|
bus_access();
|
||||||
}
|
}
|
||||||
@ -98,12 +120,15 @@ if(number_of_cycles <= Cycles(0)) break;
|
|||||||
// governs everything else on the 6502: that two bytes will always
|
// governs everything else on the 6502: that two bytes will always
|
||||||
// be fetched.
|
// be fetched.
|
||||||
if(
|
if(
|
||||||
personality_ == P6502 ||
|
!is_65c02(personality) ||
|
||||||
(operation_&7) != 3 ||
|
(operation_&7) != 3 ||
|
||||||
operation_ == 0xcb ||
|
operation_ == 0xcb ||
|
||||||
operation_ == 0xdb
|
operation_ == 0xdb
|
||||||
) {
|
) {
|
||||||
read_mem(operand_, pc_.full);
|
read_mem(operand_, pc_.full);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -140,17 +165,24 @@ if(number_of_cycles <= Cycles(0)) break;
|
|||||||
case CycleReadFromPC: throwaway_read(pc_.full); break;
|
case CycleReadFromPC: throwaway_read(pc_.full); break;
|
||||||
|
|
||||||
case OperationBRKPickVector:
|
case OperationBRKPickVector:
|
||||||
// NMI can usurp BRK-vector operations
|
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;
|
nextAddress.full = (interrupt_requests_ & InterruptRequestFlags::NMI) ? 0xfffa : 0xfffe;
|
||||||
interrupt_requests_ &= ~InterruptRequestFlags::NMI; // TODO: this probably doesn't happen now?
|
interrupt_requests_ &= ~InterruptRequestFlags::NMI;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
case OperationNMIPickVector: nextAddress.full = 0xfffa; continue;
|
case OperationNMIPickVector: nextAddress.full = 0xfffa; continue;
|
||||||
case OperationRSTPickVector: nextAddress.full = 0xfffc; continue;
|
case OperationRSTPickVector: nextAddress.full = 0xfffc; continue;
|
||||||
case CycleReadVectorLow: read_mem(pc_.bytes.low, nextAddress.full); break;
|
case CycleReadVectorLow: read_mem(pc_.bytes.low, nextAddress.full); break;
|
||||||
case CycleReadVectorHigh: read_mem(pc_.bytes.high, nextAddress.full+1); break;
|
case CycleReadVectorHigh: read_mem(pc_.bytes.high, nextAddress.full+1); break;
|
||||||
case OperationSetI:
|
case OperationSetIRQFlags:
|
||||||
inverse_interrupt_flag_ = 0;
|
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;
|
continue;
|
||||||
|
|
||||||
case CyclePullPCL: s_++; read_mem(pc_.bytes.low, s_ | 0x100); break;
|
case CyclePullPCL: s_++; read_mem(pc_.bytes.low, s_ | 0x100); break;
|
||||||
@ -178,13 +210,21 @@ if(number_of_cycles <= Cycles(0)) break;
|
|||||||
throwaway_read(oldPC);
|
throwaway_read(oldPC);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
// MARK: - JAM
|
// MARK: - JAM, WAI, STP
|
||||||
|
|
||||||
case CycleScheduleJam: {
|
case OperationScheduleJam: {
|
||||||
is_jammed_ = true;
|
is_jammed_ = true;
|
||||||
scheduled_program_counter_ = operations_[CPU::MOS6502::JamOpcode];
|
scheduled_program_counter_ = operations_[CPU::MOS6502::JamOpcode];
|
||||||
} continue;
|
} continue;
|
||||||
|
|
||||||
|
case OperationScheduleStop:
|
||||||
|
stop_is_active_ = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OperationScheduleWait:
|
||||||
|
wait_is_active_ = true;
|
||||||
|
break;
|
||||||
|
|
||||||
// MARK: - Bitwise
|
// MARK: - Bitwise
|
||||||
|
|
||||||
case OperationORA: a_ |= operand_; negative_result_ = zero_result_ = a_; continue;
|
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 OperationLDX: x_ = negative_result_ = zero_result_ = operand_; continue;
|
||||||
case OperationLDY: y_ = 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 OperationLAX: a_ = x_ = negative_result_ = zero_result_ = operand_; continue;
|
||||||
|
case OperationCopyOperandToA: a_ = operand_; continue;
|
||||||
|
|
||||||
case OperationSTA: operand_ = a_; continue;
|
case OperationSTA: operand_ = a_; continue;
|
||||||
case OperationSTX: operand_ = x_; continue;
|
case OperationSTX: operand_ = x_; continue;
|
||||||
@ -264,7 +305,7 @@ if(number_of_cycles <= Cycles(0)) break;
|
|||||||
case OperationINS:
|
case OperationINS:
|
||||||
operand_++; // deliberate fallthrough
|
operand_++; // deliberate fallthrough
|
||||||
case OperationSBC:
|
case OperationSBC:
|
||||||
if(decimal_flag_) {
|
if(decimal_flag_ && has_decimal_mode(personality)) {
|
||||||
const uint16_t notCarry = carry_flag_ ^ 0x1;
|
const uint16_t notCarry = carry_flag_ ^ 0x1;
|
||||||
const uint16_t decimalResult = static_cast<uint16_t>(a_) - static_cast<uint16_t>(operand_) - notCarry;
|
const uint16_t decimalResult = static_cast<uint16_t>(a_) - static_cast<uint16_t>(operand_) - notCarry;
|
||||||
uint16_t temp16;
|
uint16_t temp16;
|
||||||
@ -283,7 +324,7 @@ if(number_of_cycles <= Cycles(0)) break;
|
|||||||
carry_flag_ = (temp16 > 0xff) ? 0 : Flag::Carry;
|
carry_flag_ = (temp16 > 0xff) ? 0 : Flag::Carry;
|
||||||
a_ = static_cast<uint8_t>(temp16);
|
a_ = static_cast<uint8_t>(temp16);
|
||||||
|
|
||||||
if(personality_ != P6502) {
|
if(is_65c02(personality)) {
|
||||||
negative_result_ = zero_result_ = a_;
|
negative_result_ = zero_result_ = a_;
|
||||||
read_mem(operand_, address_.full);
|
read_mem(operand_, address_.full);
|
||||||
break;
|
break;
|
||||||
@ -295,7 +336,7 @@ if(number_of_cycles <= Cycles(0)) break;
|
|||||||
|
|
||||||
// deliberate fallthrough
|
// deliberate fallthrough
|
||||||
case OperationADC:
|
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_);
|
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_;
|
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);
|
a_ = static_cast<uint8_t>(result);
|
||||||
zero_result_ = static_cast<uint8_t>(decimalResult);
|
zero_result_ = static_cast<uint8_t>(decimalResult);
|
||||||
|
|
||||||
if(personality_ != P6502) {
|
if(is_65c02(personality)) {
|
||||||
negative_result_ = zero_result_ = a_;
|
negative_result_ = zero_result_ = a_;
|
||||||
read_mem(operand_, address_.full);
|
read_mem(operand_, address_.full);
|
||||||
break;
|
break;
|
||||||
@ -425,32 +466,42 @@ if(number_of_cycles <= Cycles(0)) break;
|
|||||||
|
|
||||||
// MARK: - Addressing Mode Work
|
// 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:
|
case CycleAddXToAddressLow:
|
||||||
nextAddress.full = address_.full + x_;
|
nextAddress.full = address_.full + x_;
|
||||||
address_.bytes.low = nextAddress.bytes.low;
|
address_.bytes.low = nextAddress.bytes.low;
|
||||||
if(address_.bytes.high != nextAddress.bytes.high) {
|
if(address_.bytes.high != nextAddress.bytes.high) {
|
||||||
throwaway_read(address_.full);
|
page_crossing_stall_read();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
case CycleAddXToAddressLowRead:
|
case CycleAddXToAddressLowRead:
|
||||||
nextAddress.full = address_.full + x_;
|
nextAddress.full = address_.full + x_;
|
||||||
address_.bytes.low = nextAddress.bytes.low;
|
address_.bytes.low = nextAddress.bytes.low;
|
||||||
throwaway_read(address_.full);
|
page_crossing_stall_read();
|
||||||
break;
|
break;
|
||||||
case CycleAddYToAddressLow:
|
case CycleAddYToAddressLow:
|
||||||
nextAddress.full = address_.full + y_;
|
nextAddress.full = address_.full + y_;
|
||||||
address_.bytes.low = nextAddress.bytes.low;
|
address_.bytes.low = nextAddress.bytes.low;
|
||||||
if(address_.bytes.high != nextAddress.bytes.high) {
|
if(address_.bytes.high != nextAddress.bytes.high) {
|
||||||
throwaway_read(address_.full);
|
page_crossing_stall_read();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
case CycleAddYToAddressLowRead:
|
case CycleAddYToAddressLowRead:
|
||||||
nextAddress.full = address_.full + y_;
|
nextAddress.full = address_.full + y_;
|
||||||
address_.bytes.low = nextAddress.bytes.low;
|
address_.bytes.low = nextAddress.bytes.low;
|
||||||
throwaway_read(address_.full);
|
page_crossing_stall_read();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
#undef page_crossing_stall_read
|
||||||
|
|
||||||
case OperationCorrectAddressHigh:
|
case OperationCorrectAddressHigh:
|
||||||
address_.full = nextAddress.full;
|
address_.full = nextAddress.full;
|
||||||
continue;
|
continue;
|
||||||
@ -509,12 +560,14 @@ if(number_of_cycles <= Cycles(0)) break;
|
|||||||
case OperationIncrementPC: pc_.full++; continue;
|
case OperationIncrementPC: pc_.full++; continue;
|
||||||
case CycleFetchOperandFromAddress: read_mem(operand_, address_.full); break;
|
case CycleFetchOperandFromAddress: read_mem(operand_, address_.full); break;
|
||||||
case CycleWriteOperandToAddress: write_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
|
// 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 OperationBPL: BRA(!(negative_result_&0x80)); continue;
|
||||||
case OperationBMI: 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 OperationBEQ: BRA(!zero_result_); continue;
|
||||||
case OperationBRA: BRA(true); continue;
|
case OperationBRA: BRA(true); continue;
|
||||||
|
|
||||||
|
#undef BRA
|
||||||
|
|
||||||
case CycleAddSignedOperandToPC:
|
case CycleAddSignedOperandToPC:
|
||||||
nextAddress.full = static_cast<uint16_t>(pc_.full + (int8_t)operand_);
|
nextAddress.full = static_cast<uint16_t>(pc_.full + (int8_t)operand_);
|
||||||
pc_.bytes.low = nextAddress.bytes.low;
|
pc_.bytes.low = nextAddress.bytes.low;
|
||||||
@ -534,6 +589,11 @@ if(number_of_cycles <= Cycles(0)) break;
|
|||||||
pc_.full = nextAddress.full;
|
pc_.full = nextAddress.full;
|
||||||
throwaway_read(halfUpdatedPc);
|
throwaway_read(halfUpdatedPc);
|
||||||
break;
|
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;
|
continue;
|
||||||
|
|
||||||
@ -570,8 +630,6 @@ if(number_of_cycles <= Cycles(0)) break;
|
|||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
#undef BRA
|
|
||||||
|
|
||||||
// MARK: - Transfers
|
// MARK: - Transfers
|
||||||
|
|
||||||
case OperationTXA: zero_result_ = negative_result_ = a_ = x_; continue;
|
case OperationTXA: zero_result_ = negative_result_ = a_ = x_; continue;
|
||||||
@ -611,7 +669,10 @@ if(number_of_cycles <= Cycles(0)) break;
|
|||||||
continue;
|
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;
|
ready_is_active_ = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -629,7 +690,7 @@ if(number_of_cycles <= Cycles(0)) break;
|
|||||||
bus_handler_.flush();
|
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);
|
assert(uses_ready_line);
|
||||||
if(active) {
|
if(active) {
|
||||||
ready_line_is_enabled_ = true;
|
ready_line_is_enabled_ = true;
|
||||||
@ -677,6 +738,7 @@ inline const ProcessorStorage::MicroOp *ProcessorStorage::get_reset_program() {
|
|||||||
CycleNoWritePush,
|
CycleNoWritePush,
|
||||||
OperationRSTPickVector,
|
OperationRSTPickVector,
|
||||||
CycleNoWritePush,
|
CycleNoWritePush,
|
||||||
|
OperationSetNMIRSTFlags,
|
||||||
CycleReadVectorLow,
|
CycleReadVectorLow,
|
||||||
CycleReadVectorHigh,
|
CycleReadVectorHigh,
|
||||||
OperationMoveToNextProgram
|
OperationMoveToNextProgram
|
||||||
@ -693,7 +755,7 @@ inline const ProcessorStorage::MicroOp *ProcessorStorage::get_irq_program() {
|
|||||||
OperationBRKPickVector,
|
OperationBRKPickVector,
|
||||||
OperationSetOperandFromFlags,
|
OperationSetOperandFromFlags,
|
||||||
CyclePushOperand,
|
CyclePushOperand,
|
||||||
OperationSetI,
|
OperationSetIRQFlags,
|
||||||
CycleReadVectorLow,
|
CycleReadVectorLow,
|
||||||
CycleReadVectorHigh,
|
CycleReadVectorHigh,
|
||||||
OperationMoveToNextProgram
|
OperationMoveToNextProgram
|
||||||
@ -710,6 +772,7 @@ inline const ProcessorStorage::MicroOp *ProcessorStorage::get_nmi_program() {
|
|||||||
OperationNMIPickVector,
|
OperationNMIPickVector,
|
||||||
OperationSetOperandFromFlags,
|
OperationSetOperandFromFlags,
|
||||||
CyclePushOperand,
|
CyclePushOperand,
|
||||||
|
OperationSetNMIRSTFlags,
|
||||||
CycleReadVectorLow,
|
CycleReadVectorLow,
|
||||||
CycleReadVectorHigh,
|
CycleReadVectorHigh,
|
||||||
OperationMoveToNextProgram
|
OperationMoveToNextProgram
|
||||||
|
@ -29,7 +29,7 @@ using namespace CPU::MOS6502;
|
|||||||
|
|
||||||
#define Read(...) CycleFetchOperandFromAddress, __VA_ARGS__
|
#define Read(...) CycleFetchOperandFromAddress, __VA_ARGS__
|
||||||
#define Write(...) __VA_ARGS__, CycleWriteOperandToAddress
|
#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 AbsoluteRead(op) Program(Absolute, Read(op))
|
||||||
#define AbsoluteXRead(op) Program(AbsoluteXr, 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 IndexedIndirectReadModifyWrite(...) Program(IndexedIndirect, ReadModifyWrite(__VA_ARGS__))
|
||||||
#define IndirectIndexedReadModifyWrite(...) Program(IndirectIndexed, 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 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 ZeroNop() Program(Zero, CycleFetchOperandFromAddress)
|
||||||
#define ZeroXNop() Program(ZeroX, CycleFetchOperandFromAddress)
|
#define ZeroXNop() Program(ZeroX, CycleFetchOperandFromAddress)
|
||||||
@ -70,7 +73,7 @@ using namespace CPU::MOS6502;
|
|||||||
#define ImpliedNop() {OperationMoveToNextProgram}
|
#define ImpliedNop() {OperationMoveToNextProgram}
|
||||||
#define ImmediateNop() Program(OperationIncrementPC)
|
#define ImmediateNop() Program(OperationIncrementPC)
|
||||||
|
|
||||||
#define JAM {CycleFetchOperand, CycleScheduleJam}
|
#define JAM {CycleFetchOperand, OperationScheduleJam}
|
||||||
|
|
||||||
ProcessorStorage::ProcessorStorage(Personality personality) {
|
ProcessorStorage::ProcessorStorage(Personality personality) {
|
||||||
// only the interrupt flag is defined upon reset but get_flags isn't going to
|
// 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;
|
overflow_flag_ &= Flag::Overflow;
|
||||||
|
|
||||||
const InstructionList operations_6502[256] = {
|
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),
|
/* 0x01 ORA x, ind */ IndexedIndirectRead(OperationORA),
|
||||||
/* 0x02 JAM */ JAM, /* 0x03 ASO x, ind */ IndexedIndirectReadModifyWrite(OperationASO),
|
/* 0x02 JAM */ JAM, /* 0x03 ASO x, ind */ IndexedIndirectReadModifyWrite(OperationASO),
|
||||||
/* 0x04 NOP zpg */ ZeroNop(), /* 0x05 ORA zpg */ ZeroRead(OperationORA),
|
/* 0x04 NOP zpg */ ZeroNop(), /* 0x05 ORA zpg */ ZeroRead(OperationORA),
|
||||||
@ -221,11 +224,37 @@ ProcessorStorage::ProcessorStorage(Personality personality) {
|
|||||||
memcpy(operations_, operations_6502, sizeof(operations_));
|
memcpy(operations_, operations_6502, sizeof(operations_));
|
||||||
|
|
||||||
// Patch the table according to the chip's personality.
|
// 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) {\
|
#define Install(location, instructions) {\
|
||||||
const InstructionList code = instructions; \
|
const InstructionList code = instructions; \
|
||||||
memcpy(&operations_[location], code, sizeof(InstructionList)); \
|
memcpy(&operations_[location], code, sizeof(InstructionList)); \
|
||||||
}
|
}
|
||||||
if(personality != P6502) {
|
if(is_65c02(personality)) {
|
||||||
// Add P[L/H][X/Y].
|
// Add P[L/H][X/Y].
|
||||||
Install(0x5a, Program(CyclePushY));
|
Install(0x5a, Program(CyclePushY));
|
||||||
Install(0xda, Program(CyclePushX));
|
Install(0xda, Program(CyclePushX));
|
||||||
@ -235,19 +264,6 @@ ProcessorStorage::ProcessorStorage(Personality personality) {
|
|||||||
// Add BRA.
|
// Add BRA.
|
||||||
Install(0x80, Program(OperationBRA));
|
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.
|
// The 1-byte, 1-cycle (!) NOPs.
|
||||||
for(int c = 0x03; c <= 0xf3; c += 0x10) {
|
for(int c = 0x03; c <= 0xf3; c += 0x10) {
|
||||||
Install(c, ImpliedNop());
|
Install(c, ImpliedNop());
|
||||||
@ -304,6 +320,37 @@ ProcessorStorage::ProcessorStorage(Personality personality) {
|
|||||||
Install(0x14, ZeroReadModifyWrite(OperationTRB));
|
Install(0x14, ZeroReadModifyWrite(OperationTRB));
|
||||||
Install(0x1c, AbsoluteReadModifyWrite(OperationTRB));
|
Install(0x1c, AbsoluteReadModifyWrite(OperationTRB));
|
||||||
|
|
||||||
|
// 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.
|
// Add RMB and SMB.
|
||||||
for(int c = 0x07; c <= 0x77; c += 0x10) {
|
for(int c = 0x07; c <= 0x77; c += 0x10) {
|
||||||
Install(c, ZeroReadModifyWrite(OperationRMB));
|
Install(c, ZeroReadModifyWrite(OperationRMB));
|
||||||
@ -311,6 +358,30 @@ ProcessorStorage::ProcessorStorage(Personality personality) {
|
|||||||
for(int c = 0x87; c <= 0xf7; c += 0x10) {
|
for(int c = 0x87; c <= 0xf7; c += 0x10) {
|
||||||
Install(c, ZeroReadModifyWrite(OperationSMB));
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 Install
|
||||||
}
|
}
|
||||||
|
@ -17,68 +17,184 @@ class ProcessorStorage {
|
|||||||
protected:
|
protected:
|
||||||
ProcessorStorage(Personality);
|
ProcessorStorage(Personality);
|
||||||
|
|
||||||
/*
|
/*!
|
||||||
This emulation functions by decomposing instructions into micro programs, consisting of the micro operations
|
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).
|
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 {
|
enum MicroOp {
|
||||||
CycleFetchOperation, CycleFetchOperand, OperationDecodeOperation, CycleIncPCPushPCH,
|
CycleFetchOperation, // fetches (PC) to operation_, storing PC to last_operation_pc_ before incrementing it
|
||||||
CyclePushPCH, CyclePushPCL, CyclePushA, CyclePushOperand,
|
CycleFetchOperand, // 6502: fetches from (PC) to operand_; 65C02: as 6502 unless operation_ indicates a one-cycle NOP, in which case this is a no0op
|
||||||
CyclePushX, CyclePushY, OperationSetI,
|
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,
|
CycleIncPCPushPCH, // increments the PC and pushes PC.h to the stack
|
||||||
CycleReadVectorLow, CycleReadVectorHigh,
|
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,
|
OperationSetIRQFlags, // 6502: sets I; 65C02: sets I and resets D
|
||||||
CyclePullOperand, CyclePullPCL, CyclePullPCH, CyclePullA,
|
OperationSetNMIRSTFlags, // 6502: no-op. 65C02: resets D
|
||||||
CyclePullX, CyclePullY,
|
|
||||||
CycleNoWritePush,
|
|
||||||
CycleReadAndIncrementPC, CycleIncrementPCAndReadStack, CycleIncrementPCReadPCHLoadPCL, CycleReadPCHLoadPCL,
|
|
||||||
CycleReadAddressHLoadAddressL,
|
|
||||||
|
|
||||||
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,
|
CycleReadFromS, // performs a read from the stack pointer, throwing the result away
|
||||||
OperationLoadAddressZeroPage, CycleLoadAddessZeroX, CycleLoadAddessZeroY, CycleAddXToAddressLow,
|
CycleReadFromPC, // performs a read from the program counter, throwing the result away
|
||||||
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,
|
|
||||||
|
|
||||||
OperationINC, OperationDEC, OperationINX, OperationDEX,
|
CyclePullPCL, // pulls PC.l from the stack
|
||||||
OperationINY, OperationDEY, OperationINA, OperationDEA,
|
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,
|
CycleNoWritePush, // decrements S as though it were a push, but reads from the new stack address instead of writing
|
||||||
OperationBCC, OperationBCS, OperationBNE, OperationBEQ,
|
CycleReadAndIncrementPC, // reads from the PC, throwing away the result, and increments the PC
|
||||||
OperationBRA, OperationBBRBBS,
|
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,
|
CycleReadPCLFromAddress, // reads PC.l from address_
|
||||||
OperationTAX, OperationTSX,
|
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,
|
CycleLoadAddressAbsolute, // copies operand_ to address_.l, increments the PC, reads address_.h from PC, increments the PC again
|
||||||
OperationANC, OperationLAS,
|
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,
|
OperationIncrementPC, // increments the PC
|
||||||
OperationSetOperandFromFlags,
|
CycleFetchOperandFromAddress, // fetches operand_ from address_
|
||||||
OperationSetFlagsFromA, OperationSetFlagsFromX, OperationSetFlagsFromY,
|
CycleWriteOperandToAddress, // writes operand_ to address_
|
||||||
CycleScheduleJam
|
|
||||||
|
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];
|
using InstructionList = MicroOp[10];
|
||||||
@ -139,6 +255,8 @@ class ProcessorStorage {
|
|||||||
|
|
||||||
bool ready_is_active_ = false;
|
bool ready_is_active_ = false;
|
||||||
bool ready_line_is_enabled_ = 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;
|
uint8_t irq_line_ = 0, irq_request_history_ = 0;
|
||||||
bool nmi_line_is_enabled_ = false, set_overflow_line_is_enabled_ = false;
|
bool nmi_line_is_enabled_ = false, set_overflow_line_is_enabled_ = false;
|
||||||
|
Loading…
Reference in New Issue
Block a user