diff --git a/Processors/6502/Implementation/6502Implementation.hpp b/Processors/6502/Implementation/6502Implementation.hpp index 47e0bcfc9..03637ca72 100644 --- a/Processors/6502/Implementation/6502Implementation.hpp +++ b/Processors/6502/Implementation/6502Implementation.hpp @@ -34,40 +34,62 @@ template 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; diff --git a/Processors/6502/Implementation/6502Storage.cpp b/Processors/6502/Implementation/6502Storage.cpp index 8b2e92ee3..19c2fbf5b 100644 --- a/Processors/6502/Implementation/6502Storage.cpp +++ b/Processors/6502/Implementation/6502Storage.cpp @@ -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 diff --git a/Processors/6502/Implementation/6502Storage.hpp b/Processors/6502/Implementation/6502Storage.hpp index e877c4c98..fd7b54b5f 100644 --- a/Processors/6502/Implementation/6502Storage.hpp +++ b/Processors/6502/Implementation/6502Storage.hpp @@ -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;