diff --git a/Processors/6502/Implementation/6502Implementation.hpp b/Processors/6502/Implementation/6502Implementation.hpp index 6b6eb267a..223701bb8 100644 --- a/Processors/6502/Implementation/6502Implementation.hpp +++ b/Processors/6502/Implementation/6502Implementation.hpp @@ -708,12 +708,23 @@ void Processor::run_for(const Cycles cycles) { continue; case CycleFetchFromHalfUpdatedPC: { - uint16_t halfUpdatedPc = uint16_t(((pc_.halves.low + int8_t(operand_)) & 0xff) | (pc_.halves.high << 8)); - throwaway_read(halfUpdatedPc); + uint16_t half_updated_pc = uint16_t(((pc_.halves.low + int8_t(operand_)) & 0xff) | (pc_.halves.high << 8)); + throwaway_read(half_updated_pc); } break; + case CycleFetchFromNextAddress: + throwaway_read(next_address_.full); + break; + case OperationAddSignedOperandToPC16: + next_address_ = pc_.full; pc_.full = uint16_t(pc_.full + int8_t(operand_)); + + // Skip a step if 8-bit arithmetic would have been sufficient; + // in practise this operation is used only by BBS/BBR. + if(pc_.halves.high == next_address_.halves.high) { + ++scheduled_program_counter_; + } continue; case OperationBBRBBS: { @@ -725,7 +736,7 @@ void Processor::run_for(const Cycles cycles) { } else { scheduled_program_counter_ = operations_[size_t(OperationsSlot::DoNotBBRBBS)]; } - } break; + } continue; // MARK: - Transfers diff --git a/Processors/6502/Implementation/6502Storage.cpp b/Processors/6502/Implementation/6502Storage.cpp index 34d49afc7..a02178842 100644 --- a/Processors/6502/Implementation/6502Storage.cpp +++ b/Processors/6502/Implementation/6502Storage.cpp @@ -269,18 +269,35 @@ ProcessorStorage::ProcessorStorage(Personality personality) { /* 0x105: Do BBR or BBS. */ Program( - CycleFetchOperand, // Fetch offset. + CycleFetchOperand, // Fetch offset, and increment PC. OperationIncrementPC, - CycleFetchFromHalfUpdatedPC, - OperationAddSignedOperandToPC16 + + OperationAddSignedOperandToPC16, // Calculate target PC, leaving old PC in next_address_ + // and possibly skipping the next instruction. + + CycleFetchFromNextAddress, + CycleFetchFromNextAddress ), + // Six or seven cycles total are: + // (1) operation; + // (2) zero page address as operand; + // (3) zero page address; + // (4) duplicate of (3); + // (5) further operand; + // (6) read from next PC; + // (7) repeat read from next PC if 16-bit arithmetic was required. /* 0x106: Complete BBR or BBS without branching. */ Program( - CycleFetchOperand, - OperationIncrementPC, - CycleFetchFromHalfUpdatedPC + CycleFetchOperand, // Fetch offset. + OperationIncrementPC ) + // Five cycles total are: + // (1) operation; + // (2) zero page address as operand; + // (3) zero page address; + // (4) duplicate of (3); + // (5) further operand, which goes unused. }; static_assert(sizeof(operations_6502) == sizeof(operations_)); @@ -408,15 +425,25 @@ ProcessorStorage::ProcessorStorage(Personality personality) { // 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: + // Add BBS and BBR. These take five, six or seven cycles. First five: // 1. read opcode - // 2. read operand + // 2. read first operand (i.e. zero-page address) // 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. + // 4. reread zero page (presumably as a stall, to make a decision on the above) + // 5. read second operand (i.e. branch offset) + // + // ... and then, if the branch is taken: + // + // 6. read from where next instruction would have been + // 7. reread, if a further stall is necessary to cover up for a 16-bit address change. for(int location = 0x0f; location <= 0xff; location += 0x10) { - Install(location, Program(OperationLoadAddressZeroPage, CycleFetchOperandFromAddress, OperationBBRBBS)); + Install(location, Program( + OperationLoadAddressZeroPage, + CycleFetchOperandFromAddress, // (cycle 3) + CycleFetchOperandFromAddress, // (cycle 4) + OperationBBRBBS // Branches to either OperationsSlot::DoBBRBBS, or to + // OperationSlot::DoNotBBRBBS, depending on data read. + )); } // Add RMB and SMB. diff --git a/Processors/6502/Implementation/6502Storage.hpp b/Processors/6502/Implementation/6502Storage.hpp index 47f5c206c..fd3c9a78d 100644 --- a/Processors/6502/Implementation/6502Storage.hpp +++ b/Processors/6502/Implementation/6502Storage.hpp @@ -186,7 +186,9 @@ class ProcessorStorage { 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 + OperationAddSignedOperandToPC16, // adds (signed)operand into the PC, leaving old PC in next_address_ and skipping a program step if there was no carry from low to high byte + + CycleFetchFromNextAddress, // performs a throwaway fetch from next_address_ OperationSetFlagsFromOperand, // sets all flags based on operand_ OperationSetOperandFromFlagsWithBRKSet, // sets operand_ to the value of all flags, with the break flag set