1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-05 10:28:58 +00:00

Implements STP and WAI, and ensures all unimplemented 65C02 instructions are NOP for all 65C02s.

This commit is contained in:
Thomas Harte 2018-08-17 21:49:06 -04:00
parent b3bdfa9f46
commit 0e7f54f375
3 changed files with 117 additions and 29 deletions

View File

@ -34,40 +34,62 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
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();
}
@ -188,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;
@ -639,6 +669,9 @@ if(number_of_cycles <= Cycles(0)) break;
continue;
}
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;

View File

@ -73,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
@ -224,6 +224,32 @@ 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)); \
@ -304,6 +330,15 @@ ProcessorStorage::ProcessorStorage(Personality personality) {
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
@ -324,13 +359,28 @@ ProcessorStorage::ProcessorStorage(Personality personality) {
Install(c, ZeroReadModifyWrite(OperationSMB));
}
} else {
// TODO
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 {
// TODO
Install(0xcb, ImpliedNop());
Install(0xdb, ZeroXNop());
}
}
#undef Install

View File

@ -191,7 +191,10 @@ class ProcessorStorage {
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
CycleScheduleJam // schedules the program for operation F2
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];
@ -252,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;