JMP: Jump
5pub(crate) fn jmp_absolute<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 6 match cpu.state.cycle { 7 1 => { 8 cpu.state.t0 = fetch_operand(cpu, bus); 9 } 10 2 => { 11 final_cycle(cpu, bus); 12 13 let pc_msb = fetch_operand(cpu, bus); 14 cpu.registers.pc = u16::from_le_bytes([cpu.state.t0, pc_msb]); 15 } 16 _ => invalid_cycle!(cpu), 17 } 18}
20pub(crate) fn jmp_absolute_long<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 21 match cpu.state.cycle { 22 1 => { 23 cpu.state.t0 = fetch_operand(cpu, bus); 24 } 25 2 => { 26 cpu.state.t1 = fetch_operand(cpu, bus); 27 } 28 3 => { 29 final_cycle(cpu, bus); 30 31 cpu.registers.pbr = fetch_operand(cpu, bus); 32 33 cpu.registers.pc = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]); 34 } 35 _ => invalid_cycle!(cpu), 36 } 37} 38 39pub(crate) fn jmp_indirect<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 40 match cpu.state.cycle { 41 1 => { 42 cpu.state.t0 = fetch_operand(cpu, bus); 43 } 44 2 => { 45 cpu.state.t1 = fetch_operand(cpu, bus); 46 } 47 3 => { 48 let address = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]); 49 cpu.state.t2 = bus.read(address.into()); 50 } 51 4 => { 52 final_cycle(cpu, bus); 53 54 let address = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]); 55 let pc_msb = bus.read(address.wrapping_add(1).into()); 56 cpu.registers.pc = u16::from_le_bytes([cpu.state.t2, pc_msb]); 57 } 58 _ => invalid_cycle!(cpu), 59 } 60} 61 62pub(crate) fn jmp_indexed_indirect<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 63 match cpu.state.cycle { 64 1 => { 65 cpu.state.t0 = fetch_operand(cpu, bus); 66 } 67 2 => { 68 cpu.state.t1 = fetch_operand(cpu, bus); 69 } 70 3 => { 71 // Idle cycle for indexing 72 bus.idle(); 73 } 74 4 => { 75 let bank_addr = 76 u16::from_le_bytes([cpu.state.t0, cpu.state.t1]).wrapping_add(cpu.registers.x); 77 let address = u24_address(cpu.registers.pbr, bank_addr); 78 cpu.state.t2 = bus.read(address); 79 } 80 5 => { 81 final_cycle(cpu, bus); 82 83 let bank_addr = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]) 84 .wrapping_add(cpu.registers.x) 85 .wrapping_add(1); 86 let address = u24_address(cpu.registers.pbr, bank_addr); 87 let pc_msb = bus.read(address); 88 cpu.registers.pc = u16::from_le_bytes([cpu.state.t2, pc_msb]); 89 } 90 _ => invalid_cycle!(cpu), 91 } 92} 93 94pub(crate) fn jmp_indirect_long<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 95 match cpu.state.cycle { 96 1 => { 97 cpu.state.t0 = fetch_operand(cpu, bus); 98 } 99 2 => { 100 cpu.state.t1 = fetch_operand(cpu, bus); 101 } 102 3 => { 103 let address = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]); 104 cpu.state.t2 = bus.read(address.into()); 105 } 106 4 => { 107 let address = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]); 108 cpu.state.t3 = bus.read(address.wrapping_add(1).into()); 109 } 110 5 => { 111 final_cycle(cpu, bus); 112 113 let address = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]); 114 cpu.registers.pbr = bus.read(address.wrapping_add(2).into()); 115 116 cpu.registers.pc = u16::from_le_bytes([cpu.state.t2, cpu.state.t3]); 117 } 118 _ => invalid_cycle!(cpu), 119 } 120}
JSR: Jump to subroutine
123pub(crate) fn jsr_absolute<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 124 match cpu.state.cycle { 125 1 => { 126 cpu.state.t0 = fetch_operand(cpu, bus); 127 } 128 2 => { 129 cpu.state.t1 = fetch_operand(cpu, bus); 130 } 131 3 => { 132 bus.idle(); 133 } 134 4 => { 135 let push_pc = cpu.registers.pc.wrapping_sub(1); 136 bus.write(cpu.registers.s.into(), push_pc.msb()); 137 cpu.registers.s = cpu.registers.s.wrapping_sub(1); 138 139 if cpu.registers.emulation_mode { 140 ensure_page_1_stack(&mut cpu.registers); 141 } 142 } 143 5 => { 144 final_cycle(cpu, bus); 145 146 let push_pc = cpu.registers.pc.wrapping_sub(1); 147 bus.write(cpu.registers.s.into(), push_pc.lsb()); 148 cpu.registers.s = cpu.registers.s.wrapping_sub(1); 149 150 cpu.registers.pc = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]); 151 152 if cpu.registers.emulation_mode { 153 ensure_page_1_stack(&mut cpu.registers); 154 } 155 } 156 _ => invalid_cycle!(cpu), 157 } 158}
160pub(crate) fn jsr_indirect_indexed<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 161 match cpu.state.cycle { 162 1 => { 163 cpu.state.t0 = fetch_operand(cpu, bus); 164 } 165 2 => { 166 bus.write(cpu.registers.s.into(), cpu.registers.pc.msb()); 167 cpu.registers.s = cpu.registers.s.wrapping_sub(1); 168 169 if cpu.registers.emulation_mode { 170 ensure_page_1_stack(&mut cpu.registers); 171 } 172 } 173 3 => { 174 bus.write(cpu.registers.s.into(), cpu.registers.pc.lsb()); 175 cpu.registers.s = cpu.registers.s.wrapping_sub(1); 176 177 if cpu.registers.emulation_mode { 178 ensure_page_1_stack(&mut cpu.registers); 179 } 180 } 181 4 => { 182 cpu.state.t1 = fetch_operand(cpu, bus); 183 } 184 5 => { 185 bus.idle(); 186 } 187 6 => { 188 let bank_addr = 189 u16::from_le_bytes([cpu.state.t0, cpu.state.t1]).wrapping_add(cpu.registers.x); 190 let address = u24_address(cpu.registers.pbr, bank_addr); 191 cpu.state.t2 = bus.read(address); 192 } 193 7 => { 194 final_cycle(cpu, bus); 195 196 let bank_addr = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]) 197 .wrapping_add(cpu.registers.x) 198 .wrapping_add(1); 199 let address = u24_address(cpu.registers.pbr, bank_addr); 200 let pc_msb = bus.read(address); 201 cpu.registers.pc = u16::from_le_bytes([cpu.state.t2, pc_msb]); 202 } 203 _ => invalid_cycle!(cpu), 204 } 205}
JSL: Jump to subroutine long
208pub(crate) fn jsl<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 209 match cpu.state.cycle { 210 1 => { 211 cpu.state.t0 = fetch_operand(cpu, bus); 212 } 213 2 => { 214 cpu.state.t1 = fetch_operand(cpu, bus); 215 } 216 3 => { 217 bus.write(cpu.registers.s.into(), cpu.registers.pbr); 218 cpu.registers.s = cpu.registers.s.wrapping_sub(1); 219 } 220 4 => { 221 bus.idle(); 222 } 223 5 => { 224 cpu.registers.pbr = fetch_operand(cpu, bus); 225 } 226 6 => { 227 let push_pc = cpu.registers.pc.wrapping_sub(1); 228 bus.write(cpu.registers.s.into(), push_pc.msb()); 229 cpu.registers.s = cpu.registers.s.wrapping_sub(1); 230 } 231 7 => { 232 final_cycle(cpu, bus); 233 234 let push_pc = cpu.registers.pc.wrapping_sub(1); 235 bus.write(cpu.registers.s.into(), push_pc.lsb()); 236 cpu.registers.s = cpu.registers.s.wrapping_sub(1); 237 238 cpu.registers.pc = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]); 239 240 if cpu.registers.emulation_mode { 241 ensure_page_1_stack(&mut cpu.registers); 242 } 243 } 244 _ => invalid_cycle!(cpu), 245 } 246}
248macro_rules! impl_branch { 249 ($name:ident $(, $flag:ident == $value:expr)?) => { 250 pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 251 match cpu.state.cycle { 252 1 => { 253 poll_interrupt_lines(cpu, bus); 254 255 cpu.state.t0 = fetch_operand(cpu, bus); 256 257 // Check whether to take branch 258 $( 259 if cpu.registers.p.$flag != $value { 260 cpu.state.cycle = 0; 261 } 262 )? 263 } 264 2 => { 265 poll_interrupt_lines(cpu, bus); 266 267 bus.idle(); 268 269 let offset = cpu.state.t0 as i8 as u16; 270 let original_page = cpu.registers.pc & 0xFF00; 271 cpu.registers.pc = cpu.registers.pc.wrapping_add(offset); 272 273 // In emulation mode, crossing a page adds an extra cycle 274 if !cpu.registers.emulation_mode || original_page == cpu.registers.pc & 0xFF00 { 275 cpu.state.cycle = 0; 276 } 277 } 278 3 => { 279 final_cycle(cpu, bus); 280 281 // Penalty cycle for page crossing in emulation mode 282 bus.idle(); 283 } 284 _ => invalid_cycle!(cpu) 285 } 286 } 287 }; 288}
BCC: Branch if carry clear
291impl_branch!(bcc, carry == false);
BCS: Branch if carry set
294impl_branch!(bcs, carry == true);
BEQ: Branch if equal
297impl_branch!(beq, zero == true);
BMI: Branch if minus
300impl_branch!(bmi, negative == true);
BNE: Branch if not equal
303impl_branch!(bne, zero == false);
BPL: Branch if plus
306impl_branch!(bpl, negative == false);
BRA: Branch always
309impl_branch!(bra);
BVC: Branch if overflow clear
312impl_branch!(bvc, overflow == false);
BVS: Branch if overflow set
315impl_branch!(bvs, overflow == true);
BRL: Branch long
318pub(crate) fn brl<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 319 match cpu.state.cycle { 320 1 => { 321 cpu.state.t0 = fetch_operand(cpu, bus); 322 } 323 2 => { 324 cpu.state.t1 = fetch_operand(cpu, bus); 325 } 326 3 => { 327 final_cycle(cpu, bus); 328 329 bus.idle(); 330 331 let offset = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]); 332 cpu.registers.pc = cpu.registers.pc.wrapping_add(offset); 333 } 334 _ => invalid_cycle!(cpu), 335 } 336}
BRK + COP + hardware interrupt handler (NMI + IRQ) BRK: Breakpoint software interrupt COP: Coprocessor software interrupt
341pub(crate) fn handle_interrupt<B: BusInterface>( 342 cpu: &mut Wdc65816, 343 bus: &mut B, 344 interrupt: InterruptType, 345) { 346 match cpu.state.cycle { 347 0 => { 348 // For hardware interrupts only; read operand but don't increment PC 349 bus.read(u24_address(cpu.registers.pbr, cpu.registers.pc)); 350 } 351 1 => { 352 if interrupt.is_software() { 353 fetch_operand(cpu, bus); 354 } else { 355 // Read operand but don't increment PC 356 bus.read(u24_address(cpu.registers.pbr, cpu.registers.pc)); 357 } 358 359 if cpu.registers.emulation_mode { 360 // Emulation mode skips pushing PBR 361 cpu.state.cycle += 1; 362 } 363 } 364 2 => { 365 bus.write(cpu.registers.s.into(), cpu.registers.pbr); 366 cpu.registers.s = cpu.registers.s.wrapping_sub(1); 367 } 368 3 => { 369 bus.write(cpu.registers.s.into(), cpu.registers.pc.msb()); 370 cpu.registers.s = cpu.registers.s.wrapping_sub(1); 371 372 if cpu.registers.emulation_mode { 373 ensure_page_1_stack(&mut cpu.registers); 374 } 375 } 376 4 => { 377 bus.write(cpu.registers.s.into(), cpu.registers.pc.lsb()); 378 cpu.registers.s = cpu.registers.s.wrapping_sub(1); 379 380 if cpu.registers.emulation_mode { 381 ensure_page_1_stack(&mut cpu.registers); 382 } 383 } 384 5 => { 385 let p_mask = if cpu.registers.emulation_mode && !interrupt.is_software() { 386 // Mask out the x flag, which emulates the 6502's b flag in emulation mode 387 !0x10 388 } else { 389 !0 390 }; 391 392 bus.write(cpu.registers.s.into(), u8::from(cpu.registers.p) & p_mask); 393 cpu.registers.s = cpu.registers.s.wrapping_sub(1); 394 395 cpu.registers.p.irq_disabled = true; 396 cpu.registers.p.decimal_mode = false; 397 398 if cpu.registers.emulation_mode { 399 ensure_page_1_stack(&mut cpu.registers); 400 } 401 } 402 6 => { 403 let vector = if cpu.registers.emulation_mode { 404 interrupt.emulation_vector() 405 } else { 406 interrupt.native_vector() 407 }; 408 cpu.state.t0 = bus.read(vector.into()); 409 } 410 7 => { 411 final_cycle(cpu, bus); 412 413 let vector = if cpu.registers.emulation_mode { 414 interrupt.emulation_vector() 415 } else { 416 interrupt.native_vector() 417 }; 418 let pc_msb = bus.read(vector.wrapping_add(1).into()); 419 cpu.registers.pc = u16::from_le_bytes([cpu.state.t0, pc_msb]); 420 cpu.registers.pbr = 0; 421 } 422 _ => invalid_cycle!(cpu), 423 } 424}
RTS: Return from subroutine
427pub(crate) fn rts<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 428 match cpu.state.cycle { 429 1 | 2 => { 430 bus.idle(); 431 } 432 3 => { 433 cpu.registers.s = cpu.registers.s.wrapping_add(1); 434 435 if cpu.registers.emulation_mode { 436 ensure_page_1_stack(&mut cpu.registers); 437 } 438 439 cpu.state.t0 = bus.read(cpu.registers.s.into()); 440 } 441 4 => { 442 cpu.registers.s = cpu.registers.s.wrapping_add(1); 443 444 if cpu.registers.emulation_mode { 445 ensure_page_1_stack(&mut cpu.registers); 446 } 447 448 cpu.state.t1 = bus.read(cpu.registers.s.into()); 449 } 450 5 => { 451 final_cycle(cpu, bus); 452 453 bus.idle(); 454 455 cpu.registers.pc = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]).wrapping_add(1); 456 } 457 _ => invalid_cycle!(cpu), 458 } 459}
RTL: Return from subroutine long
462pub(crate) fn rtl<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 463 match cpu.state.cycle { 464 1 | 2 => { 465 bus.idle(); 466 } 467 3 => { 468 cpu.registers.s = cpu.registers.s.wrapping_add(1); 469 cpu.state.t0 = bus.read(cpu.registers.s.into()); 470 } 471 4 => { 472 cpu.registers.s = cpu.registers.s.wrapping_add(1); 473 cpu.state.t1 = bus.read(cpu.registers.s.into()); 474 } 475 5 => { 476 final_cycle(cpu, bus); 477 478 cpu.registers.s = cpu.registers.s.wrapping_add(1); 479 cpu.registers.pbr = bus.read(cpu.registers.s.into()); 480 481 cpu.registers.pc = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]).wrapping_add(1); 482 483 if cpu.registers.emulation_mode { 484 ensure_page_1_stack(&mut cpu.registers); 485 } 486 } 487 _ => invalid_cycle!(cpu), 488 } 489}
RTI: Return from interrupt
492pub(crate) fn rti<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 493 match cpu.state.cycle { 494 1 | 2 => { 495 bus.idle(); 496 } 497 3 => { 498 cpu.registers.s = cpu.registers.s.wrapping_add(1); 499 500 if cpu.registers.emulation_mode { 501 ensure_page_1_stack(&mut cpu.registers); 502 } 503 504 let p = bus.read(cpu.registers.s.into()); 505 if cpu.registers.emulation_mode { 506 // m and x bits are forced to 1 in emulation mode 507 cpu.registers.p = (0x30 | p).into(); 508 } else { 509 cpu.registers.p = p.into(); 510 511 // Immediately truncate X and Y if x bit was set 512 if cpu.registers.p.index_size == SizeBits::Eight { 513 cpu.registers.x &= 0x00FF; 514 cpu.registers.y &= 0x00FF; 515 } 516 } 517 } 518 4 => { 519 cpu.registers.s = cpu.registers.s.wrapping_add(1); 520 521 if cpu.registers.emulation_mode { 522 ensure_page_1_stack(&mut cpu.registers); 523 } 524 525 cpu.state.t0 = bus.read(cpu.registers.s.into()); 526 } 527 5 => { 528 cpu.registers.s = cpu.registers.s.wrapping_add(1); 529 530 if cpu.registers.emulation_mode { 531 // Emulation mode skips the last cycle (pull PBR) 532 final_cycle(cpu, bus); 533 534 ensure_page_1_stack(&mut cpu.registers); 535 536 let pc_msb = bus.read(cpu.registers.s.into()); 537 cpu.registers.pc = u16::from_le_bytes([cpu.state.t0, pc_msb]); 538 } else { 539 cpu.state.t1 = bus.read(cpu.registers.s.into()); 540 } 541 } 542 6 => { 543 final_cycle(cpu, bus); 544 545 cpu.registers.s = cpu.registers.s.wrapping_add(1); 546 cpu.registers.pbr = bus.read(cpu.registers.s.into()); 547 548 cpu.registers.pc = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]); 549 } 550 _ => invalid_cycle!(cpu), 551 } 552}
PEA: Push effective address
555pub(crate) fn pea<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 556 match cpu.state.cycle { 557 1 => { 558 cpu.state.t0 = fetch_operand(cpu, bus); 559 } 560 2 => { 561 cpu.state.t1 = fetch_operand(cpu, bus); 562 } 563 3 => { 564 bus.write(cpu.registers.s.into(), cpu.state.t1); 565 cpu.registers.s = cpu.registers.s.wrapping_sub(1); 566 } 567 4 => { 568 final_cycle(cpu, bus); 569 570 bus.write(cpu.registers.s.into(), cpu.state.t0); 571 cpu.registers.s = cpu.registers.s.wrapping_sub(1); 572 573 if cpu.registers.emulation_mode { 574 ensure_page_1_stack(&mut cpu.registers); 575 } 576 } 577 _ => invalid_cycle!(cpu), 578 } 579}
PEI: Push effective indirect address
582pub(crate) fn pei<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 583 match cpu.state.cycle { 584 1 => { 585 cpu.state.t0 = fetch_operand(cpu, bus); 586 check_direct_page_low_byte(cpu); 587 } 588 2 => { 589 // Idle cycle if D LSB is non-zero 590 bus.idle(); 591 } 592 3 => { 593 let address = cpu.registers.d.wrapping_add(cpu.state.t0.into()); 594 cpu.state.t1 = bus.read(address.into()); 595 } 596 4 => { 597 let address = index_direct_page(cpu, cpu.state.t0, 1); 598 cpu.state.t2 = bus.read(address.into()); 599 } 600 5 => { 601 bus.write(cpu.registers.s.into(), cpu.state.t2); 602 cpu.registers.s = cpu.registers.s.wrapping_sub(1); 603 } 604 6 => { 605 final_cycle(cpu, bus); 606 607 bus.write(cpu.registers.s.into(), cpu.state.t1); 608 cpu.registers.s = cpu.registers.s.wrapping_sub(1); 609 610 if cpu.registers.emulation_mode { 611 ensure_page_1_stack(&mut cpu.registers); 612 } 613 } 614 _ => invalid_cycle!(cpu), 615 } 616}
PER: Push effective relative address
619pub(crate) fn per<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 620 match cpu.state.cycle { 621 1 => { 622 cpu.state.t0 = fetch_operand(cpu, bus); 623 } 624 2 => { 625 cpu.state.t1 = fetch_operand(cpu, bus); 626 } 627 3 => { 628 bus.idle(); 629 } 630 4 => { 631 let offset = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]); 632 let address = cpu.registers.pc.wrapping_add(offset); 633 bus.write(cpu.registers.s.into(), address.msb()); 634 cpu.registers.s = cpu.registers.s.wrapping_sub(1); 635 } 636 5 => { 637 final_cycle(cpu, bus); 638 639 let offset = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]); 640 let address = cpu.registers.pc.wrapping_add(offset); 641 bus.write(cpu.registers.s.into(), address.lsb()); 642 cpu.registers.s = cpu.registers.s.wrapping_sub(1); 643 644 if cpu.registers.emulation_mode { 645 ensure_page_1_stack(&mut cpu.registers); 646 } 647 } 648 _ => invalid_cycle!(cpu), 649 } 650}
652macro_rules! impl_halt { 653 ($name:ident, $field:ident) => { 654 pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 655 match cpu.state.cycle { 656 1 => { 657 bus.idle(); 658 } 659 2 => { 660 final_cycle(cpu, bus); 661 662 bus.idle(); 663 664 cpu.state.$field = true; 665 } 666 _ => invalid_cycle!(cpu), 667 } 668 } 669 }; 670}
WAI: Wait for interrupt
673impl_halt!(wai, waiting);
STP: Stop the clock
676impl_halt!(stp, stopped);