| 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) & 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) — 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 |