| 1 | package com.rox.cpu.mos6502; | |
| 2 | ||
| 3 | import com.rox.clock.ClockWatcher; | |
| 4 | import com.rox.mem.*; | |
| 5 | ||
| 6 | import java.util.*; | |
| 7 | ||
| 8 | import static com.rox.cpu.mos6502.MOS6502MicroOp.*; | |
| 9 | ||
| 10 | public class MOS6502 implements ClockWatcher { | |
| 11 | /** 7-cycle hardware interrupt sequence: 2 dummy PC reads, push PCH/PCL/status (B=0), set I, jump to vector */ | |
| 12 | private static final MOS6502Operation[][] IRQ_SEQUENCE = { | |
| 13 | { ADDRESS_CURRENT_PC, DUMMY_READ }, | |
| 14 | { ADDRESS_CURRENT_PC, DUMMY_READ }, | |
| 15 | { PUSH_PCH }, | |
| 16 | { PUSH_PCL }, | |
| 17 | { PUSH_PROCESSOR_STATUS_WITHOUT_BREAK, INTERRUPT }, | |
| 18 | { ADDRESS_IV_LOW, PCL_FROM_MEM }, | |
| 19 | { ADDRESS_IV_HIGH, PCH_FROM_MEM } | |
| 20 | }; | |
| 21 | ||
| 22 | private static final MOS6502Operation[][] NMI_SEQUENCE = { | |
| 23 | { ADDRESS_CURRENT_PC, DUMMY_READ }, | |
| 24 | { ADDRESS_CURRENT_PC, DUMMY_READ }, | |
| 25 | { PUSH_PCH }, | |
| 26 | { PUSH_PCL }, | |
| 27 | { PUSH_PROCESSOR_STATUS_WITHOUT_BREAK, INTERRUPT }, | |
| 28 | { ADDRESS_NMI_LOW, PCL_FROM_MEM }, | |
| 29 | { ADDRESS_NMI_HIGH, PCH_FROM_MEM } | |
| 30 | }; | |
| 31 | ||
| 32 | private static final int RESET_VECTOR_LOW_ADDRESS = 0xFFFC; | |
| 33 | private static final int RESET_VECTOR_HIGH_ADDRESS = 0xFFFD; | |
| 34 | ||
| 35 | private final LatchedMemoryBus latchedMemory; | |
| 36 | ||
| 37 | private final Deque<MOS6502Operation[]> opsInTicksStack = new ArrayDeque<>(); | |
| 38 | private MOS6502Environment environment; | |
| 39 | private MOS6502ALU alu; | |
| 40 | private int stallCycles; | |
| 41 | ||
| 42 | public MOS6502Environment getEnvironmentSnapshot(){ | |
| 43 |
1
1. getEnvironmentSnapshot : replaced return value with null for com/rox/cpu/mos6502/MOS6502::getEnvironmentSnapshot → KILLED |
return environment.clone(); |
| 44 | } | |
| 45 | ||
| 46 | public MOS6502(final LatchedMemoryBus latchedMemory){ | |
| 47 | this(latchedMemory, new MOS6502Environment()); | |
| 48 | } | |
| 49 | ||
| 50 | public MOS6502(final LatchedMemoryBus latchedMemory, final MOS6502Environment environment) { | |
| 51 | this.latchedMemory = latchedMemory; | |
| 52 | this.environment = environment; | |
| 53 | this.alu = new MOS6502ALU(this.environment); | |
| 54 | } | |
| 55 | ||
| 56 | /** Set the level-sensitive hardware IRQ line, as a device such as the APU would */ | |
| 57 | public void setIRQLine(boolean asserted){ | |
| 58 |
1
1. setIRQLine : removed call to com/rox/cpu/mos6502/MOS6502Environment::setIRQLine → KILLED |
environment.setIRQLine(asserted); |
| 59 | } | |
| 60 | ||
| 61 | /** Signal a non-maskable interrupt, latched until serviced */ | |
| 62 | public void signalNMI(){ | |
| 63 |
1
1. signalNMI : removed call to com/rox/cpu/mos6502/MOS6502Environment::signalNMI → KILLED |
environment.signalNMI(); |
| 64 | } | |
| 65 | ||
| 66 | /** The current program counter - cheap enough to read every instruction, unlike {@link #getEnvironmentSnapshot()}'s copy. */ | |
| 67 | public int programCounter(){ | |
| 68 |
1
1. programCounter : replaced int return with 0 for com/rox/cpu/mos6502/MOS6502::programCounter → KILLED |
return environment.getPC(); |
| 69 | } | |
| 70 | ||
| 71 | /** A, X, Y and SP packed per {@link MOS6502Snapshot#packRegisters} - cheap enough to read every instruction. */ | |
| 72 | public int packedRegisters(){ | |
| 73 |
1
1. packedRegisters : replaced int return with 0 for com/rox/cpu/mos6502/MOS6502::packedRegisters → KILLED |
return MOS6502Snapshot.packRegisters(environment.getA(), environment.getX(), environment.getY(), |
| 74 | environment.getStackPointer()); | |
| 75 | } | |
| 76 | ||
| 77 | /** Flags, IRQ line and NMI latch packed per {@link MOS6502Snapshot#packFlags} - cheap enough to read every instruction. */ | |
| 78 | public int packedFlags(){ | |
| 79 |
1
1. packedFlags : replaced int return with 0 for com/rox/cpu/mos6502/MOS6502::packedFlags → KILLED |
return MOS6502Snapshot.packFlags(environment.negative, environment.signedOverflow, environment.breakFlag, |
| 80 | environment.d, environment.i, environment.zero, environment.carry, | |
| 81 | environment.isIRQLineAsserted(), environment.isNMIPending()); | |
| 82 | } | |
| 83 | ||
| 84 | /** Set the program counter, e.g. to point at the start of an assembled program */ | |
| 85 | public void setPC(final int newPC){ | |
| 86 |
1
1. setPC : removed call to com/rox/cpu/mos6502/MOS6502Environment::setPC → KILLED |
environment.setPC(newPC); |
| 87 | } | |
| 88 | ||
| 89 | /** | |
| 90 | * Reset: read the reset vector ($FFFC/$FFFD) and jump the program counter there, as real | |
| 91 | * hardware does on power-on/reset - mirrors the IRQ ($FFFE/$FFFF) / NMI ($FFFA/$FFFB) | |
| 92 | * vector-read pattern already used by the interrupt sequences above. | |
| 93 | */ | |
| 94 | public void reset(){ | |
| 95 |
1
1. reset : removed call to com/rox/mem/LatchedMemoryBus::loadMemoryAddress → KILLED |
latchedMemory.loadMemoryAddress(RESET_VECTOR_LOW_ADDRESS); |
| 96 | final int low = latchedMemory.fetch(); | |
| 97 |
1
1. reset : removed call to com/rox/mem/LatchedMemoryBus::loadMemoryAddress → KILLED |
latchedMemory.loadMemoryAddress(RESET_VECTOR_HIGH_ADDRESS); |
| 98 | final int high = latchedMemory.fetch(); | |
| 99 |
3
1. reset : removed call to com/rox/cpu/mos6502/MOS6502::setPC → KILLED 2. reset : Replaced bitwise OR with AND → KILLED 3. reset : Replaced Shift Left with Shift Right → KILLED |
setPC((high << 8) | low); |
| 100 | } | |
| 101 | ||
| 102 | /** | |
| 103 | * Stall the CPU for the given number of cycles (e.g. OAM DMA's 513/514-cycle pause) - real | |
| 104 | * hardware freezes the CPU mid-instruction rather than letting it run ahead, so this must take | |
| 105 | * effect on the very next {@link #tick()}, not just after the current instruction finishes. | |
| 106 | */ | |
| 107 | public void stall(final int cycles){ | |
| 108 |
1
1. stall : Replaced integer addition with subtraction → KILLED |
stallCycles += cycles; |
| 109 | } | |
| 110 | ||
| 111 | /** | |
| 112 | * True when the last {@link #tick()} completed an instruction (or interrupt sequence) and no DMA | |
| 113 | * stall is outstanding, so the next tick begins a fresh fetch or interrupt entry - the only point | |
| 114 | * at which the CPU's state is fully described by {@link #getEnvironmentSnapshot()}. Variable-cycle | |
| 115 | * extra ticks (page crosses, taken branches) are pushed onto {@code opsInTicksStack} in the same | |
| 116 | * tick that requests them, so an empty stack already accounts for them. | |
| 117 | */ | |
| 118 | public boolean isAtInstructionBoundary(){ | |
| 119 |
3
1. isAtInstructionBoundary : negated conditional → KILLED 2. isAtInstructionBoundary : negated conditional → KILLED 3. isAtInstructionBoundary : replaced boolean return with true for com/rox/cpu/mos6502/MOS6502::isAtInstructionBoundary → KILLED |
return opsInTicksStack.isEmpty() && stallCycles == 0; |
| 120 | } | |
| 121 | ||
| 122 | /** | |
| 123 | * Captures the CPU's complete state - only meaningful at an instruction boundary, where nothing is | |
| 124 | * left part-way through {@code opsInTicksStack} or a DMA stall to be lost. | |
| 125 | * | |
| 126 | * @throws IllegalStateException if not {@link #isAtInstructionBoundary()} | |
| 127 | */ | |
| 128 | public MOS6502Snapshot snapshot(){ | |
| 129 |
1
1. snapshot : negated conditional → KILLED |
if (!isAtInstructionBoundary()){ |
| 130 | throw new IllegalStateException("CPU state can only be captured at an instruction boundary"); | |
| 131 | } | |
| 132 |
1
1. snapshot : replaced return value with null for com/rox/cpu/mos6502/MOS6502::snapshot → KILLED |
return liveState(); |
| 133 | } | |
| 134 | ||
| 135 | /** | |
| 136 | * The CPU's registers and flags right now, at any point - including part-way through an | |
| 137 | * instruction, when they may be half-updated and nothing in flight is captured. For display only; | |
| 138 | * use {@link #snapshot()} for anything that will be {@link #restore}d. | |
| 139 | */ | |
| 140 | public MOS6502Snapshot liveState(){ | |
| 141 |
1
1. liveState : replaced return value with null for com/rox/cpu/mos6502/MOS6502::liveState → KILLED |
return new MOS6502Snapshot(environment.getPC(), environment.getA(), environment.getX(), environment.getY(), |
| 142 | environment.getStackPointer(), environment.getIR(), environment.getADL(), environment.getADH(), | |
| 143 | environment.negative, environment.signedOverflow, environment.breakFlag, environment.d, | |
| 144 | environment.i, environment.zero, environment.carry, | |
| 145 | environment.isIRQLineAsserted(), environment.isNMIPending()); | |
| 146 | } | |
| 147 | ||
| 148 | /** Puts the CPU back exactly as {@code snapshot} captured it, abandoning anything part-way through. */ | |
| 149 | public void restore(final MOS6502Snapshot snapshot){ | |
| 150 | final MOS6502Environment restored = new MOS6502Environment(snapshot.carry(), snapshot.zero(), snapshot.negative(), | |
| 151 | snapshot.signedOverflow(), snapshot.breakFlag(), snapshot.pc(), snapshot.ir(), snapshot.adl(), snapshot.adh(), | |
| 152 | snapshot.a(), snapshot.x(), snapshot.y()); | |
| 153 | restored.d = snapshot.decimal(); | |
| 154 | restored.i = snapshot.interruptDisable(); | |
| 155 |
1
1. restore : removed call to com/rox/cpu/mos6502/MOS6502Environment::setStackPointer → KILLED |
restored.setStackPointer(snapshot.stackPointer()); |
| 156 |
1
1. restore : removed call to com/rox/cpu/mos6502/MOS6502Environment::setIRQLine → KILLED |
restored.setIRQLine(snapshot.irqLineAsserted()); |
| 157 |
1
1. restore : negated conditional → KILLED |
if (snapshot.nmiPending()){ |
| 158 |
1
1. restore : removed call to com/rox/cpu/mos6502/MOS6502Environment::signalNMI → KILLED |
restored.signalNMI(); |
| 159 | } | |
| 160 | environment = restored; | |
| 161 | alu = new MOS6502ALU(environment); //the ALU reads/writes flags through its own environment reference | |
| 162 |
1
1. restore : removed call to java/util/Deque::clear → KILLED |
opsInTicksStack.clear(); |
| 163 | stallCycles = 0; | |
| 164 | } | |
| 165 | ||
| 166 | @Override | |
| 167 | public void tick() { | |
| 168 |
2
1. tick : negated conditional → KILLED 2. tick : changed conditional boundary → KILLED |
if (stallCycles > 0){ |
| 169 |
1
1. tick : Replaced integer subtraction with addition → KILLED |
stallCycles--; |
| 170 | return; | |
| 171 | } | |
| 172 |
1
1. tick : negated conditional → KILLED |
if (opsInTicksStack.isEmpty()){ |
| 173 |
1
1. tick : negated conditional → KILLED |
if (environment.hasPendingInterrupt()){ |
| 174 | // Mirrors fetchNextOp(): the cycle that schedules the sequence also performs its first tick's work | |
| 175 |
2
1. tick : negated conditional → KILLED 2. tick : removed call to com/rox/cpu/mos6502/MOS6502::stackMicroOperations → KILLED |
stackMicroOperations(environment.consumeNMI() ? NMI_SEQUENCE : IRQ_SEQUENCE); |
| 176 |
1
1. tick : removed call to com/rox/cpu/mos6502/MOS6502::executeNextInstructionInStack → KILLED |
executeNextInstructionInStack(); |
| 177 |
1
1. tick : removed call to com/rox/cpu/mos6502/MOS6502::performAdditionalRequestedMicroOp → SURVIVED |
performAdditionalRequestedMicroOp(); |
| 178 | } else { | |
| 179 |
1
1. tick : removed call to com/rox/cpu/mos6502/MOS6502::stackMicroOperations → KILLED |
stackMicroOperations(fetchNextOp()); |
| 180 | } | |
| 181 | } else { | |
| 182 |
1
1. tick : removed call to com/rox/cpu/mos6502/MOS6502::executeNextInstructionInStack → KILLED |
executeNextInstructionInStack(); |
| 183 |
1
1. tick : removed call to com/rox/cpu/mos6502/MOS6502::performAdditionalRequestedMicroOp → KILLED |
performAdditionalRequestedMicroOp(); |
| 184 | } | |
| 185 | } | |
| 186 | ||
| 187 | private MOS6502OpCode fetchNextOp() { | |
| 188 |
1
1. fetchNextOp : removed call to com/rox/cpu/mos6502/MOS6502MicroOp::execute → KILLED |
FETCH.execute(environment, latchedMemory, alu); |
| 189 | /*DEBUG*///System.out.println("TICK>(!) Fetched next opcode: " + opcode + "\t - " + environment); | |
| 190 |
1
1. fetchNextOp : replaced return value with null for com/rox/cpu/mos6502/MOS6502::fetchNextOp → KILLED |
return MOS6502OpCode.from(environment.getIR()); //decode |
| 191 | } | |
| 192 | ||
| 193 | /** Schedule micro operatiosn of opcdoe in reverse defined order in stack */ | |
| 194 | private void stackMicroOperations(MOS6502OpCode opcode) { | |
| 195 |
1
1. stackMicroOperations : removed call to com/rox/cpu/mos6502/MOS6502::stackMicroOperations → KILLED |
stackMicroOperations(opcode.getOperations()); |
| 196 | } | |
| 197 | ||
| 198 | /** Schedule the given per-tick operations in reverse order onto the stack */ | |
| 199 | private void stackMicroOperations(MOS6502Operation[][] operations) { | |
| 200 |
3
1. stackMicroOperations : changed conditional boundary → KILLED 2. stackMicroOperations : Replaced integer subtraction with addition → KILLED 3. stackMicroOperations : negated conditional → KILLED |
for (int tick=operations.length-1; tick>=0; tick--){ |
| 201 |
1
1. stackMicroOperations : removed call to java/util/Deque::push → KILLED |
opsInTicksStack.push(operations[tick]); |
| 202 | } | |
| 203 | } | |
| 204 | ||
| 205 | private void executeNextInstructionInStack() { | |
| 206 | final MOS6502Operation[] opsThisTick = opsInTicksStack.pop(); | |
| 207 | ||
| 208 | for (MOS6502Operation op : opsThisTick){ | |
| 209 |
1
1. executeNextInstructionInStack : removed call to com/rox/cpu/mos6502/MOS6502Operation::execute → KILLED |
op.execute(environment, latchedMemory, alu); |
| 210 | /*DEBUG*///System.out.println("TICK> " + op + "\t - " + environment); | |
| 211 | } | |
| 212 | } | |
| 213 | ||
| 214 | /** Execute additional requested micro operations, for use in page crosses for example */ | |
| 215 | private void performAdditionalRequestedMicroOp() { | |
| 216 |
1
1. performAdditionalRequestedMicroOp : negated conditional → KILLED |
if (environment.additionalTickPending()){ |
| 217 |
1
1. performAdditionalRequestedMicroOp : removed call to java/util/Deque::push → KILLED |
opsInTicksStack.push(new MOS6502Operation[] { environment.getPendingOperation() }); |
| 218 | } | |
| 219 | } | |
| 220 | } | |
Mutations | ||
| 43 |
1.1 |
|
| 58 |
1.1 |
|
| 63 |
1.1 |
|
| 68 |
1.1 |
|
| 73 |
1.1 |
|
| 79 |
1.1 |
|
| 86 |
1.1 |
|
| 95 |
1.1 |
|
| 97 |
1.1 |
|
| 99 |
1.1 2.2 3.3 |
|
| 108 |
1.1 |
|
| 119 |
1.1 2.2 3.3 |
|
| 129 |
1.1 |
|
| 132 |
1.1 |
|
| 141 |
1.1 |
|
| 155 |
1.1 |
|
| 156 |
1.1 |
|
| 157 |
1.1 |
|
| 158 |
1.1 |
|
| 162 |
1.1 |
|
| 168 |
1.1 2.2 |
|
| 169 |
1.1 |
|
| 172 |
1.1 |
|
| 173 |
1.1 |
|
| 175 |
1.1 2.2 |
|
| 176 |
1.1 |
|
| 177 |
1.1 |
|
| 179 |
1.1 |
|
| 182 |
1.1 |
|
| 183 |
1.1 |
|
| 188 |
1.1 |
|
| 190 |
1.1 |
|
| 195 |
1.1 |
|
| 200 |
1.1 2.2 3.3 |
|
| 201 |
1.1 |
|
| 209 |
1.1 |
|
| 216 |
1.1 |
|
| 217 |
1.1 |