MOS6502MicroOp.java

1
package com.rox.cpu.mos6502;
2
3
import com.rox.mem.LatchedMemoryBus;
4
5
//XXX There's probably a fair bit of redundancy here
6
enum MOS6502MicroOp implements MOS6502Operation {
7
    //Temporary
8
    NOP((_, _, _) -> {}),
9
10
    /** Fetch next instruction. <b>Should be implicit</b>  <code>instruction_register := mem[pc++]</code> */
11
    FETCH((env, mem, _) -> {
12
        mem.loadMemoryAddress(env.getAndIncrementPC());
13
        env.setIR(mem.fetch());
14
    }),
15
16
    /** Load address into address bus : <code>address_bus := mem[00:adl]</code> */
17
    ADDRESS_ADL((env, mem, _) -> {
18
        mem.loadMemoryAddress(env.getADL());
19
    }),
20
21
    /** Load address from program counter into address bus : <code>address_bus := mem[pc++]</code> */
22
    ADDRESS_PC((env, mem, _) -> {
23
        mem.loadMemoryAddress(env.getAndIncrementPC());
24
    }),
25
26
    /** Load address from program counter into address bus without advancing it : <code>address_bus := mem[pc]</code> */
27
    ADDRESS_CURRENT_PC((env, mem, _) -> {
28
        mem.loadMemoryAddress(env.getPC());
29
    }),
30
31
    /** Pull addressed value and load into address bus : <code>address_bus := mem[address_bus]</code> */
32
    ADDRESS_MEM_POINTER((_, mem, _) -> {
33
        mem.loadMemoryAddress(mem.fetch());
34
    }),
35
36
    /**
37
     * Load the next addressed value into ADH : <code>adh := mem[(address_bus + 1) &amp; 0xFF]</code>
38
     * <p>
39
     * The second byte of a zero-page pointer for {@code (zp,X)} / {@code (zp),Y}. Wraps within zero page, as on
40
     * the 6502: a pointer at $00FF takes its high byte from $0000, not $0100. Not {@code ADDRESS_PC, ADH_FROM_MEM}:
41
     * that reads the instruction stream, not the pointer.
42
     */
43
    NEXT_MEM_TO_ADH((env, mem, _)->{
44
        int incrementedMemoryLocation = (mem.getAddressedMemory() + 1) & 0xFF;
45
        mem.loadMemoryAddress(incrementedMemoryLocation);
46
        env.setADH(mem.fetch());
47
    }),
48
49
    /** Increment the program counter : <code>pc := pc++</code>*/
50
    INC_PC((env, _, _)->{
51
        env.getAndIncrementPC();
52
    }),
53
54
    /** Increment the address register directly with low byte wrapping : <code>adl := adl++</code>*/
55
    INC_ADL((env, _, _)->{
56
        env.setADL((env.getADL() + 1) & 0xFF);
57
    }),
58
59
    /** Set the program counter to the state of the address bus : <code>pc := ad</code>*/
60
    AD_TO_PC((env, _, _)->{
61
        env.setPC(env.getAD());
62
    }),
63
64
    /** Take the currently addressed memory location and add X:  <code>address_bus := mem[address_bus + X]</code> */
65
    X_OFFSET_ADDRESS((env, mem, _)->{
66
        int basePointer = mem.fetch();
67
        mem.loadMemoryAddress((basePointer + env.getX()) & 0xFF);
68
    }),
69
70
    /** Load address register into address bus : <code>address_bus := mem[adh:adl]</code> */
71
    ADDRESS_AD((env, mem, _) -> {
72
        mem.loadMemoryAddress(env.getAD());
73
    }),
74
75
    /** Add X to ADL, wrapping rather than carrying to ADH on overflow: <code>ADL := ADL + X</code> */
76
    ADL_PLUS_X((env, _, _)->{
77
        int low = env.getADL() + env.getX();
78
        env.setADL(low & 0xFF);
79
        env.setADH(0x00);
80
    }),
81
82
    /** Add Y to ADL, wrapping rather than carrying to ADH on overflow: <code>ADL := ADL + Y</code> */
83
    ADL_PLUS_Y((env, _, _)->{
84
        int low = env.getADL() + env.getY();
85
        env.setADL(low & 0xFF);
86
        env.setADH(0x00);
87
    }),
88
89
    DUMMY_READ((_, mem, _) -> {
90
        mem.fetch();
91
    }),
92
93
    /**
94
     * Load address bus with AD + X: <code>address_bus := AD := AD + X</code> <br/><br/>
95
     * XXX: Needs a better approach 1/2.<br/>
96
     * LDA_ABS_X is a read instruction, so the 6502 can optimistically try to read from the partially indexed address first; if adding X does not cross a page, that read is valid and the instruction finishes in 4 cycles, but if the low byte overflows, the CPU needs one extra cycle to fix the high byte and reread from the correct address. STA_ABS_X is a write instruction, so the CPU cannot safely do that optimistic access because an early write to the wrong address would corrupt memory; it must always spend the indexing/dummy-read cycle before performing the real write, making it 5 cycles whether or not a page is crossed.
97
     */
98
    ADDRESS_AD_PLUS_X((env, mem, _)->{
99
        int originalHigh = env.getADH();
100
101
        int low = env.getADL() + env.getX();
102
        env.setADL(low & 0xFF);
103
104
        if (low > 0xFF) {
105
            env.setADH((originalHigh + 1) & 0xFF);
106
        }
107
108
        mem.loadMemoryAddress(env.getAD());
109
    }),
110
111
    /**
112
     * Load address bus with AD + X, adding an extra tick instruction if there's a page cross: <code>address_bus := AD := AD + X</code> <br/><br/>
113
     * XXX: Needs a better approach 1/2.<br/>
114
     * LDA_ABS_X is a read instruction, so the 6502 can optimistically try to read from the partially indexed address first; if adding X does not cross a page, that read is valid and the instruction finishes in 4 cycles, but if the low byte overflows, the CPU needs one extra cycle to fix the high byte and reread from the correct address. STA_ABS_X is a write instruction, so the CPU cannot safely do that optimistic access because an early write to the wrong address would corrupt memory; it must always spend the indexing/dummy-read cycle before performing the real write, making it 5 cycles whether or not a page is crossed.
115
     */
116
    ADDRESS_AD_PLUS_X_AND_PAGE_CROSS((env, mem, _)->{
117
        int originalHigh = env.getADH();
118
119
        int low = env.getADL() + env.getX();
120
        env.setADL(low & 0xFF);
121
122
        if (low > 0xFF) {
123
            env.requestAdditionalOp((_, m, _) -> {
124
                m.fetch(); //dummy read
125
                env.additionalTickCompleted();
126
            });
127
            env.setADH((originalHigh + 1) & 0xFF);
128
        }
129
130
        mem.loadMemoryAddress(env.getAD());
131
    }),
132
133
    /** Load address bus with AD + Y: <code>address_bus := AD := AD + Y</code> */
134
    ADDRESS_AD_PLUS_Y((env, mem, _)->{
135
        int originalHigh = env.getADH();
136
137
        int low = env.getADL() + env.getY();
138
        env.setADL(low & 0xFF);
139
140
        if (low > 0xFF) {
141
            env.setADH((originalHigh + 1) & 0xFF);
142
        }
143
144
        mem.loadMemoryAddress(env.getAD());
145
    }),
146
147
    /** Load address bus with AD + Y: <code>address_bus := AD := AD + Y</code> */
148
    ADDRESS_AD_PLUS_Y_AND_PAGE_CROSS((env, mem, _)->{
149
        int originalHigh = env.getADH();
150
151
        int low = env.getADL() + env.getY();
152
        env.setADL(low & 0xFF);
153
154
        if (low > 0xFF) {
155
            env.requestAdditionalOp((_, m, _) -> {
156
                m.fetch(); //Dummy read
157
                env.additionalTickCompleted();
158
            });
159
            env.setADH((originalHigh + 1) & 0xFF);
160
        }
161
162
        mem.loadMemoryAddress(env.getAD());
163
    }),
164
165
    /** Set static flags (N and Z) based on the value of the accumulator */
166
    SET_FLAGS_ON_A((env, _, alu)->{
167
        alu.setStaticFlags(env.getA());
168
    }),
169
170
    /** Set static flags (N and Z) based on the value of X */
171
    SET_FLAGS_ON_X((env, _, alu)->{
172
        alu.setStaticFlags(env.getX());
173
    }),
174
175
    /** Set static flags (N and Z) based on the value of Y */
176
    SET_FLAGS_ON_Y((env, _, alu)->{
177
        alu.setStaticFlags(env.getY());
178
    }),
179
180
    /** Set Accumulator to the value on the data bus: <code>A := mem[address_bus]</code> */
181
    A_FROM_AD((env, mem, _) -> {
182
        env.setA(mem.fetch());
183
    }),
184
185
    /** Set A to the value of X <code>A := X</code> */
186
    A_FROM_X((env, _, _)->{
187
        env.setA(env.getX());
188
    }),
189
190
191
    /** Set A to the value of Y <code>A := Y</code> */
192
    A_FROM_Y((env, _, _)->{
193
        env.setA(env.getY());
194
    }),
195
196
    /** Set X to the value on the data bus: <code>X := mem[address_bus]</code> */
197
    X_FROM_AD((env, mem, _) -> {
198
        env.setX(mem.fetch());
199
    }),
200
201
    /** Set X to the value of the Stack Pointer <code>X := Stack Pointer</code> */
202
    X_FROM_SP((env, _, _)->{
203
        env.setX(env.getStackPointer());
204
    }),
205
206
    /** Set the Stack Pointer to the value of X <code>Stack Pointer := X</code> */
207
    SP_FROM_X((env, _, _)->{
208
        env.setStackPointer(env.getX());
209
    }),
210
211
    /** Set X to the value on the data bus: <code>X := mem[address_bus]</code> */
212
    Y_FROM_AD((env, mem, _) -> {
213
        env.setY(mem.fetch());
214
    }),
215
216
    /** Set A to the value at the location specified by the address bus: <code>A := mem[AD]</code> */
217
    A_FROM_MEM((env, mem, _)->{
218
        env.setA(mem.fetch());
219
    }),
220
221
    /** Set X to the value at the location specified by the address bus: <code>X := mem[AD]</code> */
222
    X_FROM_MEM((env, mem, _)->{
223
        env.setX(mem.fetch());
224
    }),
225
226
    /** Set Y to the value at the location specified by the address bus: <code>Y := mem[AD]</code> */
227
    Y_FROM_MEM((env, mem, _)->{
228
        env.setY(mem.fetch());
229
    }),
230
231
    /** Load value from memory into ADL : <code>adl := mem[address_bus]</code> */
232
    ADL_FROM_MEM((env, mem, _)->{
233
        env.setADL(mem.fetch());
234
    }),
235
236
    /** Load value from memory into ADH : <code>adh := mem[address_bus]</code> */
237
    ADH_FROM_MEM((env, mem, _)->{
238
        env.setADH(mem.fetch());
239
    }),
240
241
    /** Set the program counter low byte to the state of the address bus low byte : <code>pcl := adl</code>*/
242
    PCL_FROM_MEM((env, mem, _)->{
243
        env.setPCL(mem.fetch());
244
    }),
245
246
    /** Set the program counter high byte to the state of the address bus high byte : <code>pch := adh</code>*/
247
    PCH_FROM_MEM((env, mem, _)->{
248
        env.setPCH(mem.fetch());
249
    }),
250
251
    /** Set the memory locations indicated by the address bus to the value of A: <code>mem[AD] := A</code> */
252
    MEM_FROM_A((env, mem, _)->{
253
        mem.store(env.getA());
254
    }),
255
256
    /** Set X to the value of A <code>X := A</code> */
257
    X_FROM_A((env, _, _)->{
258
        env.setX(env.getA());
259
    }),
260
261
    /** Set Y to the value of A <code>Y := A</code> */
262
    Y_FROM_A((env, _, _)->{
263
        env.setY(env.getA());
264
    }),
265
266
    /** Set the memory locations indicated by the address bus to the value of X: <code>mem[AD] := X</code> */
267
    MEM_FROM_X((env, mem, _)->{
268
        mem.store(env.getX());
269
    }),
270
271
    /** Set the memory locations indicated by the address bus to the value of Y: <code>mem[AD] := Y</code> */
272
    MEM_FROM_Y((env, mem, _)->{
273
        mem.store(env.getY());
274
    }),
275
276
    /** Increment the value in the X register</code> */
277
    INX((env, _, _) -> {
278
        env.setX((env.getX() + 1) & 0xFF);
279
    }),
280
281
    /** Decrement the value in the X register</code> */
282
    DEX((env, _, _) -> {
283
        env.setX((env.getX() - 1) & 0xFF);
284
    }),
285
286
    /** Increment the value in the Y register</code> */
287
    INY((env, _, _) -> {
288
        env.setY((env.getY() + 1) & 0xFF);
289
    }),
290
291
    /** Decrement the value in the Y register</code> */
292
    DEY((env, _, _) -> {
293
        env.setY((env.getY() - 1) & 0xFF);
294
    }),
295
296
    /** Set the interrupt flag to true <code>I := true</code> **/
297
    INTERRUPT((env, _, _) -> {
298
        env.setI(true);
299
    }),
300
301
    /** Clear the carry flag <code>C := false</code> **/
302
    CLEAR_CARRY((env, _, _) -> {
303
        env.setCarry(false);
304
    }),
305
306
    /** Set the carry flag <code>C := true</code> **/
307
    SET_CARRY((env, _, _) -> {
308
        env.setCarry(true);
309
    }),
310
311
    /** Clear the interrupt disable flag <code>I := false</code> **/
312
    CLEAR_INTERRUPT_DISABLE((env, _, _) -> {
313
        env.setI(false);
314
    }),
315
316
    /** Clear the overflow flag <code>V := false</code> **/
317
    CLEAR_OVERFLOW((env, _, _) -> {
318
        env.setV(false);
319
    }),
320
321
    /** Set the decimal flag <code>D := true</code> **/
322
    SET_DECIMAL((env, _, _) -> {
323
        env.setD(true);
324
    }),
325
326
    /** Clear the decimal flag <code>D := false</code> **/
327
    CLEAR_DECIMAL((env, _, _) -> {
328
        env.setD(false);
329
    }),
330
331
    /** Address the low interrupt vector ready to be read <code>mem[$FF:FE)</code> */
332
    ADDRESS_IV_LOW((_, mem, _) -> {
333
        mem.loadMemoryAddress(0xFFFE);
334
    }),
335
336
    /** Address the high interrupt vector ready to be read <code>mem[$FF:FF)</code> */
337
    ADDRESS_IV_HIGH((_, mem, _) -> {
338
        mem.loadMemoryAddress(0xFFFF);
339
    }),
340
341
    /** Address the low byte of the NMI vector ready to be read <code>mem[$FF:FA)</code> */
342
    ADDRESS_NMI_LOW((_, mem, _) -> {
343
        mem.loadMemoryAddress(0xFFFA);
344
    }),
345
346
    /** Address the high byte of the NMI vector ready to be read <code>mem[$FF:FB)</code> */
347
    ADDRESS_NMI_HIGH((_, mem, _) -> {
348
        mem.loadMemoryAddress(0xFFFB);
349
    }),
350
351
    /** Push the current value of PCH onto the stack and decrement the stack pointer <code>mem[$01:SP--] := PCH</code> **/
352
    PUSH_PCH((env, mem, _) -> {
353
        mem.loadMemoryAddress(0x0100 | env.getStackPointer());
354
        mem.store(env.getPCH());
355
        env.setStackPointer((env.getStackPointer()-1) & 0xFF);
356
    }),
357
358
    /** Push the current value of PCL onto the stack and decrement the stack pointer <code>mem[$01:SP--] := PCL</code> **/
359
    PUSH_PCL((env, mem, _) -> {
360
        mem.loadMemoryAddress(0x0100 | env.getStackPointer());
361
        mem.store(env.getPCL());
362
        env.setStackPointer((env.getStackPointer()-1) & 0xFF);
363
    }),
364
365
    /** Push the current value of A onto the stack and decrement the stack pointer <code>mem[$01:SP--] := A</code> **/
366
    PUSH_A((env, mem, _) -> {
367
        mem.loadMemoryAddress(0x0100 | env.getStackPointer());
368
        mem.store(env.getA());
369
        env.setStackPointer((env.getStackPointer()-1) & 0xFF);
370
    }),
371
372
    /** Increment the stack pointer and pull the value of A from that location <code>A := mem[$01:++SP]</code> **/
373
    PULL_A((env, mem, _) -> {
374
        mem.loadMemoryAddress(0x0100 | env.getStackPointer());
375
        env.setA(mem.fetch());
376
    }),
377
378
    /** Increment the stack pointer and pull the value of PCL from that location <code>PCL := mem[$01:++SP]</code> **/
379
    PULL_PCL((env, mem, _) -> {
380
        mem.loadMemoryAddress(0x0100 | env.getStackPointer());
381
        env.setPCL(mem.fetch());
382
    }),
383
384
    /** Increment the stack pointer and pull the value of PCH from that location <code>PCH := mem[$01:++SP]</code> **/
385
    PULL_PCH((env, mem, _) -> {
386
        mem.loadMemoryAddress(0x0100 | env.getStackPointer());
387
        env.setPCH(mem.fetch());
388
    }),
389
390
    /** Increment the stack pointer <code>SP++</code>*/
391
    INC_SP((env, _, _) -> {
392
        env.setStackPointer((env.getStackPointer()+1) & 0xFF);
393
    }),
394
395
    /** Push the current state of the processor onto the stack with break flag set to 1 and decrement the stack pointer <code>mem[$01:SP--] := Processor Status + break flag</code> **/
396
    PUSH_PROCESSOR_STATUS_WITH_BREAK((env, mem, _) -> {
397
        mem.loadMemoryAddress(0x0100 | env.getStackPointer());
398
        mem.store(env.getStatus(true));
399
        env.setStackPointer((env.getStackPointer()-1) & 0xFF);
400
    }),
401
402
    /** Push the current state of the processor onto the stack with break flag set to 0 and decrement the stack pointer, as a hardware IRQ/NMI does (distinguishing it from BRK/PHP) <code>mem[$01:SP--] := Processor Status</code> **/
403
    PUSH_PROCESSOR_STATUS_WITHOUT_BREAK((env, mem, _) -> {
404
        mem.loadMemoryAddress(0x0100 | env.getStackPointer());
405
        mem.store(env.getStatus(false));
406
        env.setStackPointer((env.getStackPointer()-1) & 0xFF);
407
    }),
408
409
    /** Set the current state of the processor from the stack and increment the stack pointer <code>Processor Status := mem[$01:SP++]</code> **/
410
    PULL_PROCESSOR_STATUS((env, mem, _) -> {
411
        mem.loadMemoryAddress(0x0100 | env.getStackPointer());
412
        env.setStatus(mem.fetch());
413
    }),
414
415
    /** Perform {@link MOS6502ALU#adc(int)} with the value at <code>mem[address_bus]</code> */
416
    ADC((_, mem, alu) -> {
417
        alu.adc(mem.fetch());
418
    }),
419
420
    /** Perform {@link MOS6502ALU#and(int)} with the value at <code>mem[address_bus]</code> */
421
    AND((_, mem, alu) -> {
422
        alu.and(mem.fetch());
423
    }),
424
425
    /** Perform {@link MOS6502ALU#ora(int)} with the value at <code>mem[address_bus]</code> */
426
    ORA((_, mem, alu) -> {
427
        alu.ora(mem.fetch());
428
    }),
429
430
    /** Perform {@link MOS6502ALU#eor(int)} with the value at <code>mem[address_bus]</code> */
431
    EOR((_, mem, alu) -> {
432
        alu.eor(mem.fetch());
433
    }),
434
435
    /** Perform {@link MOS6502ALU#sbc(int)} with the value at <code>mem[address_bus]</code> */
436
    SBC((_, mem, alu) -> {
437
        alu.sbc(mem.fetch());
438
    }),
439
440
    /** Perform {@link MOS6502ALU#cmp(int)} with the value at <code>mem[address_bus]</code> */
441
    CMP((_, mem, alu) -> {
442
        alu.cmp(mem.fetch());
443
    }),
444
445
    /** Perform {@link MOS6502ALU#cpx(int)} with the value at <code>mem[address_bus]</code> */
446
    CPX((_, mem, alu) -> {
447
        alu.cpx(mem.fetch());
448
    }),
449
450
    /** Perform {@link MOS6502ALU#cpy(int)} with the value at <code>mem[address_bus]</code> */
451
    CPY((_, mem, alu) -> {
452
        alu.cpy(mem.fetch());
453
    }),
454
455
    /** Write the addressed value back unchanged, modeling the 6502's read-modify-write dummy write cycle: <code>mem[address_bus] := mem[address_bus]</code> */
456
    DUMMY_WRITE_MEM((_, mem, _) -> {
457
        mem.store(mem.fetch());
458
    }),
459
460
    /** Increment the addressed memory value, wrapping on overflow: <code>mem[address_bus] := mem[address_bus] + 1</code> */
461
    INC_MEM((_, mem, _) -> {
462
        mem.store((mem.fetch() + 1) & 0xFF);
463
    }),
464
465
    /** Decrement the addressed memory value, wrapping on underflow: <code>mem[address_bus] := mem[address_bus] - 1</code> */
466
    DEC_MEM((_, mem, _) -> {
467
        mem.store((mem.fetch() - 1) & 0xFF);
468
    }),
469
470
    /** Set static flags (N and Z) based on the addressed memory value */
471
    SET_FLAGS_ON_MEM((_, mem, alu) -> {
472
        alu.setStaticFlags(mem.fetch());
473
    }),
474
475
    /** Perform {@link MOS6502ALU#bit(int)} with the value at <code>mem[address_bus]</code> */
476
    BIT_TEST((_, mem, alu) -> {
477
        alu.bit(mem.fetch());
478
    }),
479
480
    /** Perform {@link MOS6502ALU#asl(int)} on the accumulator: <code>A := ASL(A)</code> */
481
    ASL_ACC((env, _, alu) -> {
482
        env.setA(alu.asl(env.getA()));
483
    }),
484
485
    /** Perform {@link MOS6502ALU#lsr(int)} on the accumulator: <code>A := LSR(A)</code> */
486
    LSR_ACC((env, _, alu) -> {
487
        env.setA(alu.lsr(env.getA()));
488
    }),
489
490
    /** Perform {@link MOS6502ALU#rol(int)} on the accumulator: <code>A := ROL(A)</code> */
491
    ROL_ACC((env, _, alu) -> {
492
        env.setA(alu.rol(env.getA()));
493
    }),
494
495
    /** Perform {@link MOS6502ALU#ror(int)} on the accumulator: <code>A := ROR(A)</code> */
496
    ROR_ACC((env, _, alu) -> {
497
        env.setA(alu.ror(env.getA()));
498
    }),
499
500
    /** Perform {@link MOS6502ALU#asl(int)} on the addressed memory value: <code>mem[address_bus] := ASL(mem[address_bus])</code> */
501
    ASL_MEM((_, mem, alu) -> {
502
        mem.store(alu.asl(mem.fetch()));
503
    }),
504
505
    /** Perform {@link MOS6502ALU#lsr(int)} on the addressed memory value: <code>mem[address_bus] := LSR(mem[address_bus])</code> */
506
    LSR_MEM((_, mem, alu) -> {
507
        mem.store(alu.lsr(mem.fetch()));
508
    }),
509
510
    /** Perform {@link MOS6502ALU#rol(int)} on the addressed memory value: <code>mem[address_bus] := ROL(mem[address_bus])</code> */
511
    ROL_MEM((_, mem, alu) -> {
512
        mem.store(alu.rol(mem.fetch()));
513
    }),
514
515
    /** Perform {@link MOS6502ALU#ror(int)} on the addressed memory value: <code>mem[address_bus] := ROR(mem[address_bus])</code> */
516
    ROR_MEM((_, mem, alu) -> {
517
        mem.store(alu.ror(mem.fetch()));
518
    }),
519
520
    /** Branch if N is clear: <code>if !N then pc := pc + offset</code> */
521
    BRANCH_IF_POSITIVE((env, mem, _) -> {
522
        branch(env, mem, !env.getN());
523
    }),
524
525
    /** Branch if N is set: <code>if N then pc := pc + offset</code> */
526
    BRANCH_IF_NEGATIVE((env, mem, _) -> {
527
        branch(env, mem, env.getN());
528
    }),
529
530
    /** Branch if V is clear: <code>if !V then pc := pc + offset</code> */
531
    BRANCH_IF_OVERFLOW_CLEAR((env, mem, _) -> {
532
        branch(env, mem, !env.getV());
533
    }),
534
535
    /** Branch if V is set: <code>if V then pc := pc + offset</code> */
536
    BRANCH_IF_OVERFLOW_SET((env, mem, _) -> {
537
        branch(env, mem, env.getV());
538
    }),
539
540
    /** Branch if carry is clear: <code>if !C then pc := pc + offset</code> */
541
    BRANCH_IF_CARRY_CLEAR((env, mem, _) -> {
542
        branch(env, mem, !env.getCarry());
543
    }),
544
545
    /** Branch if carry is set: <code>if C then pc := pc + offset</code> */
546
    BRANCH_IF_CARRY_SET((env, mem, _) -> {
547
        branch(env, mem, env.getCarry());
548
    }),
549
550
    /** Branch if Z is clear: <code>if !Z then pc := pc + offset</code> */
551
    BRANCH_IF_NOT_EQUAL((env, mem, _) -> {
552
        branch(env, mem, !env.getZ());
553
    }),
554
555
    /** Branch if Z is set: <code>if Z then pc := pc + offset</code> */
556
    BRANCH_IF_EQUAL((env, mem, _) -> {
557
        branch(env, mem, env.getZ());
558
    });
559
560
    /**
561
     * Shared PC-relative branch logic. The offset operand is fetched from the already-addressed
562
     * location (via {@code ADDRESS_PC} in the same tick). If not taken, this is the full cost (2 cycles
563
     * total including FETCH). If taken, an additional tick is chained to apply the low-byte offset
564
     * (3 cycles total); if that crosses a page boundary, a further tick is chained to correct the high
565
     * byte (4 cycles total) &mdash; mirroring the real 6502's extra page-cross penalty.
566
     */
567
    private static void branch(final MOS6502Environment env, final LatchedMemoryBus mem, final boolean take) {
568
        final byte offset = (byte) mem.fetch();
569
570
        if (!take) {
571
            return;
572
        }
573
574
        env.requestAdditionalOp((e, m, _) -> {
575
            final int oldPCH = e.getPCH();
576
            final int newLow = e.getPCL() + offset;
577
            e.setPCL(newLow & 0xFF);
578
579
            m.loadMemoryAddress(e.getPC()); //dummy read at (possibly not yet page-corrected) PC
580
            m.fetch();
581
582
            if (newLow < 0 || newLow > 0xFF) {
583
                final int correctedPCH = (oldPCH + (newLow > 0xFF ? 1 : -1)) & 0xFF;
584
                e.requestAdditionalOp((e2, m2, _) -> {
585
                    e2.setPCH(correctedPCH);
586
                    m2.loadMemoryAddress(e2.getPC()); //dummy read at the corrected PC
587
                    m2.fetch();
588
                    e2.additionalTickCompleted();
589
                });
590
            } else {
591
                e.additionalTickCompleted();
592
            }
593
        });
594
    }
595
596
    private final MOS6502Operation op;
597
598
    @Override
599
    public void execute(MOS6502Environment environment, LatchedMemoryBus memory, MOS6502ALU alu) {
600 1 1. execute : removed call to com/rox/cpu/mos6502/MOS6502Operation::execute → KILLED
        op.execute(environment, memory, alu);
601
    }
602
603
    MOS6502MicroOp(final MOS6502Operation op) {
604
        this.op = op;
605
    }
606
}

Mutations

600

1.1
Location : execute
Killed by : com.rox.cpu.mos6502.MOS6502MicroOpTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.MOS6502MicroOpTest]/[method:incPcEndToEnd()]
removed call to com/rox/cpu/mos6502/MOS6502Operation::execute → KILLED

Active mutators

Tests examined


Report generated by PIT 1.22.1