LDA: Load accumulator (8-bit)
5impl_read_op_u8!( 6 [ 7 immediate -> lda_immediate_u8, 8 direct_page -> lda_direct_page_u8, 9 direct_page_x -> lda_direct_page_x_u8, 10 direct_page_indirect -> lda_direct_page_indirect_u8, 11 direct_page_indexed_indirect -> lda_direct_page_indexed_indirect_u8, 12 direct_page_indirect_indexed -> lda_direct_page_indirect_indexed_u8, 13 direct_page_indirect_long -> lda_direct_page_indirect_long_u8, 14 direct_page_indirect_long_indexed -> lda_direct_page_indirect_long_indexed_u8, 15 absolute -> lda_absolute_u8, 16 absolute_x -> lda_absolute_x_u8, 17 absolute_y -> lda_absolute_y_u8, 18 absolute_long -> lda_absolute_long_u8, 19 absolute_long_x -> lda_absolute_long_x_u8, 20 stack_relative -> lda_stack_relative_u8, 21 stack_relative_indirect_indexed -> lda_stack_relative_indirect_indexed_u8, 22 ] 23 (|registers, value| { 24 registers.a.set_lsb(value); 25 26 registers.p.zero = value == 0; 27 registers.p.negative = value.sign_bit(); 28 }) 29);
LDA: Load accumulator (16-bit)
32impl_read_op_u16!( 33 [ 34 immediate -> lda_immediate_u16, 35 direct_page -> lda_direct_page_u16, 36 direct_page_x -> lda_direct_page_x_u16, 37 direct_page_indirect -> lda_direct_page_indirect_u16, 38 direct_page_indexed_indirect -> lda_direct_page_indexed_indirect_u16, 39 direct_page_indirect_indexed -> lda_direct_page_indirect_indexed_u16, 40 direct_page_indirect_long -> lda_direct_page_indirect_long_u16, 41 direct_page_indirect_long_indexed -> lda_direct_page_indirect_long_indexed_u16, 42 absolute -> lda_absolute_u16, 43 absolute_x -> lda_absolute_x_u16, 44 absolute_y -> lda_absolute_y_u16, 45 absolute_long -> lda_absolute_long_u16, 46 absolute_long_x -> lda_absolute_long_x_u16, 47 stack_relative -> lda_stack_relative_u16, 48 stack_relative_indirect_indexed -> lda_stack_relative_indirect_indexed_u16, 49 ] 50 (|registers, value| { 51 registers.a = value; 52 53 registers.p.zero = value == 0; 54 registers.p.negative = value.sign_bit(); 55 }) 56);
LDX: Load X register (8-bit)
59impl_read_op_u8!( 60 [ 61 immediate -> ldx_immediate_u8, 62 direct_page -> ldx_direct_page_u8, 63 direct_page_y -> ldx_direct_page_y_u8, 64 absolute -> ldx_absolute_u8, 65 absolute_y -> ldx_absolute_y_u8, 66 ] 67 (|registers, value| { 68 registers.x = value.into(); 69 70 registers.p.zero = value == 0; 71 registers.p.negative = value.sign_bit(); 72 }) 73);
LDX: Load X register (16-bit)
76impl_read_op_u16!( 77 [ 78 immediate -> ldx_immediate_u16, 79 direct_page -> ldx_direct_page_u16, 80 direct_page_y -> ldx_direct_page_y_u16, 81 absolute -> ldx_absolute_u16, 82 absolute_y -> ldx_absolute_y_u16, 83 ] 84 (|registers, value| { 85 registers.x = value; 86 87 registers.p.zero = value == 0; 88 registers.p.negative = value.sign_bit(); 89 }) 90);
LDY: Load Y register (8-bit)
93impl_read_op_u8!( 94 [ 95 immediate -> ldy_immediate_u8, 96 direct_page -> ldy_direct_page_u8, 97 direct_page_x -> ldy_direct_page_x_u8, 98 absolute -> ldy_absolute_u8, 99 absolute_x -> ldy_absolute_x_u8, 100 ] 101 (|registers, value| { 102 registers.y = value.into(); 103 104 registers.p.zero = value == 0; 105 registers.p.negative = value.sign_bit(); 106 }) 107);
LDY: Load Y register (16-bit)
110impl_read_op_u16!( 111 [ 112 immediate -> ldy_immediate_u16, 113 direct_page -> ldy_direct_page_u16, 114 direct_page_x -> ldy_direct_page_x_u16, 115 absolute -> ldy_absolute_u16, 116 absolute_x -> ldy_absolute_x_u16, 117 ] 118 (|registers, value| { 119 registers.y = value; 120 121 registers.p.zero = value == 0; 122 registers.p.negative = value.sign_bit(); 123 }) 124);
126macro_rules! impl_transfer_op { 127 (u8: $u8_name:ident, u16: $u16_name:ident, $from:ident -> $to:ident) => { 128 impl_registers_op!($u8_name, |registers| { 129 let value = registers.$from as u8; 130 registers.$to.set_lsb(value); 131 132 registers.p.zero = value == 0; 133 registers.p.negative = value.sign_bit(); 134 }); 135 136 impl_registers_op!($u16_name, |registers| { 137 let value = registers.$from; 138 registers.$to = value; 139 140 registers.p.zero = value == 0; 141 registers.p.negative = value.sign_bit(); 142 }); 143 }; 144}
TAX: Transfer A to X
147impl_transfer_op!(u8: tax_u8, u16: tax_u16, a -> x);
TAY: Transfer A to Y
150impl_transfer_op!(u8: tay_u8, u16: tay_u16, a -> y);
TSX: Transfer S to X
153impl_transfer_op!(u8: tsx_u8, u16: tsx_u16, s -> x);
TXA: Transfer X to A
156impl_transfer_op!(u8: txa_u8, u16: txa_u16, x -> a);
TXY: Transfer X to Y
159impl_transfer_op!(u8: txy_u8, u16: txy_u16, x -> y);
TYA: Transfer Y to A
162impl_transfer_op!(u8: tya_u8, u16: tya_u16, y -> a);
TYX: Transfer Y to X
165impl_transfer_op!(u8: tyx_u8, u16: tyx_u16, y -> x);
TXS: Transfer X to S Does not use impl_transfer_op! because it is always a 16-bit operation unless in emulation mode, and because it does not set flags
TCD: Transfer C (16-bit accumulator) to D
TCS: Transfer C (16-bit accumulator) to S
TDC: Transfer D to C (16-bit accumulator)
TSC: Transfer S to C (16-bit accumulator)
XBA: Exchange B and A accumulator bytes Not using impl_registers_op! because this instruction takes 3 cycles, not 2
218pub(crate) fn xba<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 219 match cpu.state.cycle { 220 1 => { 221 bus.idle(); 222 } 223 2 => { 224 final_cycle(cpu, bus); 225 226 bus.idle(); 227 228 cpu.registers.a = cpu.registers.a.swap_bytes(); 229 230 // Flags are always based on low byte 231 cpu.registers.p.zero = cpu.registers.a & 0x00FF == 0; 232 cpu.registers.p.negative = cpu.registers.a.bit(7); 233 } 234 _ => invalid_cycle!(cpu), 235 } 236}
238macro_rules! store_register { 239 ($registers:expr, zero) => { 240 0_u16 241 }; 242 ($registers:expr, $register:ident) => { 243 $registers.$register 244 }; 245} 246 247macro_rules! impl_store_direct_page_u8 { 248 ($name:ident, $register:ident) => { 249 pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 250 match cpu.state.cycle { 251 1 => { 252 cpu.state.t0 = fetch_operand(cpu, bus); 253 check_direct_page_low_byte(cpu); 254 } 255 2 => { 256 // Idle cycle if D LSB is non-zero 257 bus.idle(); 258 } 259 3 => { 260 final_cycle(cpu, bus); 261 262 let address = cpu.registers.d.wrapping_add(cpu.state.t0.into()); 263 let value = store_register!(cpu.registers, $register); 264 bus.write(address.into(), value as u8); 265 } 266 _ => invalid_cycle!(cpu), 267 } 268 } 269 }; 270} 271 272macro_rules! impl_store_direct_page_u16 { 273 ($name:ident, $register:ident) => { 274 pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 275 match cpu.state.cycle { 276 1 => { 277 cpu.state.t0 = fetch_operand(cpu, bus); 278 check_direct_page_low_byte(cpu); 279 } 280 2 => { 281 // Idle cycle if D LSB is non-zero 282 bus.idle(); 283 } 284 3 => { 285 let address = cpu.registers.d.wrapping_add(cpu.state.t0.into()); 286 let value = store_register!(cpu.registers, $register); 287 bus.write(address.into(), value.lsb()); 288 } 289 4 => { 290 final_cycle(cpu, bus); 291 292 let address = cpu.registers.d.wrapping_add(cpu.state.t0.into()).wrapping_add(1); 293 let value = store_register!(cpu.registers, $register); 294 bus.write(address.into(), value.msb()); 295 } 296 _ => invalid_cycle!(cpu), 297 } 298 } 299 }; 300} 301 302macro_rules! impl_store_direct_page_indexed_u8 { 303 ($name:ident, $register:ident, index: $index:ident) => { 304 pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 305 match cpu.state.cycle { 306 1 => { 307 cpu.state.t0 = fetch_operand(cpu, bus); 308 check_direct_page_low_byte(cpu); 309 } 310 2 => { 311 // Idle cycle if D LSB is non-zero 312 bus.idle(); 313 } 314 3 => { 315 // Idle cycle for indexing 316 bus.idle(); 317 } 318 4 => { 319 final_cycle(cpu, bus); 320 321 let address = index_direct_page(cpu, cpu.state.t0, cpu.registers.$index); 322 let value = store_register!(cpu.registers, $register); 323 bus.write(address.into(), value as u8); 324 } 325 _ => invalid_cycle!(cpu), 326 } 327 } 328 }; 329} 330 331macro_rules! impl_store_direct_page_indexed_u16 { 332 ($name:ident, $register:ident, index: $index:ident) => { 333 pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 334 match cpu.state.cycle { 335 1 => { 336 cpu.state.t0 = fetch_operand(cpu, bus); 337 check_direct_page_low_byte(cpu); 338 } 339 2 => { 340 // Idle cycle if D LSB is non-zero 341 bus.idle(); 342 } 343 3 => { 344 // Idle cycle for indexing 345 bus.idle(); 346 } 347 4 => { 348 let address = index_direct_page(cpu, cpu.state.t0, cpu.registers.$index); 349 let value = store_register!(cpu.registers, $register); 350 bus.write(address.into(), value.lsb()); 351 } 352 5 => { 353 final_cycle(cpu, bus); 354 355 let address = index_direct_page(cpu, cpu.state.t0, cpu.registers.$index); 356 let value = store_register!(cpu.registers, $register); 357 bus.write(address.wrapping_add(1).into(), value.msb()); 358 } 359 _ => invalid_cycle!(cpu), 360 } 361 } 362 }; 363} 364 365macro_rules! impl_store_absolute_u8 { 366 ($name:ident, $register:ident) => { 367 pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 368 match cpu.state.cycle { 369 1 => { 370 cpu.state.t0 = fetch_operand(cpu, bus); 371 } 372 2 => { 373 cpu.state.t1 = fetch_operand(cpu, bus); 374 } 375 3 => { 376 final_cycle(cpu, bus); 377 378 let address = 379 u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.registers.dbr, 0]); 380 let value = store_register!(cpu.registers, $register); 381 bus.write(address, value as u8); 382 } 383 _ => invalid_cycle!(cpu), 384 } 385 } 386 }; 387} 388 389macro_rules! impl_store_absolute_u16 { 390 ($name:ident, $register:ident) => { 391 pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 392 match cpu.state.cycle { 393 1 => { 394 cpu.state.t0 = fetch_operand(cpu, bus); 395 } 396 2 => { 397 cpu.state.t1 = fetch_operand(cpu, bus); 398 } 399 3 => { 400 let address = 401 u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.registers.dbr, 0]); 402 let value = store_register!(cpu.registers, $register); 403 bus.write(address, value.lsb()); 404 } 405 4 => { 406 final_cycle(cpu, bus); 407 408 let base_addr = 409 u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.registers.dbr, 0]); 410 let address = (base_addr + 1) & B::ADDRESS_MASK; 411 let value = store_register!(cpu.registers, $register); 412 bus.write(address, value.msb()); 413 } 414 _ => invalid_cycle!(cpu), 415 } 416 } 417 }; 418} 419 420macro_rules! impl_store_absolute_indexed_u8 { 421 ($name:ident, $register:ident, index: $index:ident) => { 422 pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 423 match cpu.state.cycle { 424 1 => { 425 cpu.state.t0 = fetch_operand(cpu, bus); 426 } 427 2 => { 428 cpu.state.t1 = fetch_operand(cpu, bus); 429 } 430 3 => { 431 // Idle cycle for indexing 432 bus.idle(); 433 } 434 4 => { 435 final_cycle(cpu, bus); 436 437 let base_addr = 438 u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.registers.dbr, 0]); 439 let index: u32 = cpu.registers.$index.into(); 440 let address = (base_addr + index) & B::ADDRESS_MASK; 441 let value = store_register!(cpu.registers, $register); 442 bus.write(address, value as u8); 443 } 444 _ => invalid_cycle!(cpu), 445 } 446 } 447 }; 448} 449 450macro_rules! impl_store_absolute_indexed_u16 { 451 ($name:ident, $register:ident, index: $index:ident) => { 452 pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 453 match cpu.state.cycle { 454 1 => { 455 cpu.state.t0 = fetch_operand(cpu, bus); 456 } 457 2 => { 458 cpu.state.t1 = fetch_operand(cpu, bus); 459 } 460 3 => { 461 // Idle cycle for indexing 462 bus.idle(); 463 } 464 4 => { 465 let base_addr = 466 u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.registers.dbr, 0]); 467 let index: u32 = cpu.registers.$index.into(); 468 let address = (base_addr + index) & B::ADDRESS_MASK; 469 let value = store_register!(cpu.registers, $register); 470 bus.write(address, value.lsb()); 471 } 472 5 => { 473 final_cycle(cpu, bus); 474 475 let base_addr = 476 u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.registers.dbr, 0]); 477 let index: u32 = cpu.registers.$index.into(); 478 let address = (base_addr + index + 1) & B::ADDRESS_MASK; 479 let value = store_register!(cpu.registers, $register); 480 bus.write(address, value.msb()); 481 } 482 _ => invalid_cycle!(cpu), 483 } 484 } 485 }; 486}
STA: Store accumulator
489impl_store_direct_page_u8!(sta_direct_page_u8, a); 490impl_store_direct_page_u16!(sta_direct_page_u16, a); 491impl_store_direct_page_indexed_u8!(sta_direct_page_x_u8, a, index: x); 492impl_store_direct_page_indexed_u16!(sta_direct_page_x_u16, a, index: x); 493impl_store_absolute_u8!(sta_absolute_u8, a); 494impl_store_absolute_u16!(sta_absolute_u16, a); 495impl_store_absolute_indexed_u8!(sta_absolute_x_u8, a, index: x); 496impl_store_absolute_indexed_u16!(sta_absolute_x_u16, a, index: x); 497impl_store_absolute_indexed_u8!(sta_absolute_y_u8, a, index: y); 498impl_store_absolute_indexed_u16!(sta_absolute_y_u16, a, index: y);
Remaining STA addressing modes do not use macros because they're unique to STA
502pub(crate) fn sta_direct_page_indirect_u8<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 503 match cpu.state.cycle { 504 1 => { 505 cpu.state.t0 = fetch_operand(cpu, bus); 506 check_direct_page_low_byte(cpu); 507 } 508 2 => { 509 // Idle cycle if D LSB is non-zero 510 bus.idle(); 511 } 512 3 => { 513 let address = cpu.registers.d.wrapping_add(cpu.state.t0.into()); 514 cpu.state.t1 = bus.read(address.into()); 515 } 516 4 => { 517 let address = cpu.registers.d.wrapping_add(cpu.state.t0.into()); 518 cpu.state.t2 = bus.read(address.wrapping_add(1).into()); 519 } 520 5 => { 521 final_cycle(cpu, bus); 522 523 let address = u32::from_le_bytes([cpu.state.t1, cpu.state.t2, cpu.registers.dbr, 0]); 524 bus.write(address, cpu.registers.a as u8); 525 } 526 _ => invalid_cycle!(cpu), 527 } 528}
530pub(crate) fn sta_direct_page_indirect_u16<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 531 match cpu.state.cycle { 532 1 => { 533 cpu.state.t0 = fetch_operand(cpu, bus); 534 check_direct_page_low_byte(cpu); 535 } 536 2 => { 537 // Idle cycle if D LSB is non-zero 538 bus.idle(); 539 } 540 3 => { 541 let address = cpu.registers.d.wrapping_add(cpu.state.t0.into()); 542 cpu.state.t1 = bus.read(address.into()); 543 } 544 4 => { 545 let address = cpu.registers.d.wrapping_add(cpu.state.t0.into()); 546 cpu.state.t2 = bus.read(address.wrapping_add(1).into()); 547 } 548 5 => { 549 let address = u32::from_le_bytes([cpu.state.t1, cpu.state.t2, cpu.registers.dbr, 0]); 550 bus.write(address, cpu.registers.a.lsb()); 551 } 552 6 => { 553 final_cycle(cpu, bus); 554 555 let address = u32::from_le_bytes([cpu.state.t1, cpu.state.t2, cpu.registers.dbr, 0]); 556 bus.write((address + 1) & B::ADDRESS_MASK, cpu.registers.a.msb()); 557 } 558 _ => invalid_cycle!(cpu), 559 } 560} 561 562pub(crate) fn sta_direct_page_indexed_indirect_u8<B: BusInterface>( 563 cpu: &mut Wdc65816, 564 bus: &mut B, 565) { 566 match cpu.state.cycle { 567 1 => { 568 cpu.state.t0 = fetch_operand(cpu, bus); 569 check_direct_page_low_byte(cpu); 570 } 571 2 | 3 => { 572 // Idle cycles if D LSB is non-zero (2) and for indexing (3) 573 bus.idle(); 574 } 575 4 => { 576 let address = index_direct_page(cpu, cpu.state.t0, cpu.registers.x); 577 cpu.state.t1 = bus.read(address.into()); 578 } 579 5 => { 580 let address = index_direct_page(cpu, cpu.state.t0, cpu.registers.x); 581 cpu.state.t2 = bus.read(address.wrapping_add(1).into()); 582 } 583 6 => { 584 final_cycle(cpu, bus); 585 586 let address = u32::from_le_bytes([cpu.state.t1, cpu.state.t2, cpu.registers.dbr, 0]); 587 bus.write(address, cpu.registers.a as u8); 588 } 589 _ => invalid_cycle!(cpu), 590 } 591} 592 593pub(crate) fn sta_direct_page_indexed_indirect_u16<B: BusInterface>( 594 cpu: &mut Wdc65816, 595 bus: &mut B, 596) { 597 match cpu.state.cycle { 598 1 => { 599 cpu.state.t0 = fetch_operand(cpu, bus); 600 check_direct_page_low_byte(cpu); 601 } 602 2 | 3 => { 603 // Idle cycles if D LSB is non-zero (2) and for indexing (3) 604 bus.idle(); 605 } 606 4 => { 607 let address = index_direct_page(cpu, cpu.state.t0, cpu.registers.x); 608 cpu.state.t1 = bus.read(address.into()); 609 } 610 5 => { 611 let address = index_direct_page(cpu, cpu.state.t0, cpu.registers.x); 612 cpu.state.t2 = bus.read(address.wrapping_add(1).into()); 613 } 614 6 => { 615 let address = u32::from_le_bytes([cpu.state.t1, cpu.state.t2, cpu.registers.dbr, 0]); 616 bus.write(address, cpu.registers.a.lsb()); 617 } 618 7 => { 619 final_cycle(cpu, bus); 620 621 let address = u32::from_le_bytes([cpu.state.t1, cpu.state.t2, cpu.registers.dbr, 0]); 622 bus.write((address + 1) & B::ADDRESS_MASK, cpu.registers.a.msb()); 623 } 624 _ => invalid_cycle!(cpu), 625 } 626} 627 628pub(crate) fn sta_direct_page_indirect_indexed_u8<B: BusInterface>( 629 cpu: &mut Wdc65816, 630 bus: &mut B, 631) { 632 match cpu.state.cycle { 633 1 => { 634 cpu.state.t0 = fetch_operand(cpu, bus); 635 check_direct_page_low_byte(cpu); 636 } 637 2 | 5 => { 638 // Idle cycles for if D LSB is non-zero (2) and for indexing (5) 639 bus.idle(); 640 } 641 3 => { 642 let address = cpu.registers.d.wrapping_add(cpu.state.t0.into()); 643 cpu.state.t1 = bus.read(address.into()); 644 } 645 4 => { 646 let address = cpu.registers.d.wrapping_add(cpu.state.t0.into()); 647 cpu.state.t2 = bus.read(address.wrapping_add(1).into()); 648 } 649 6 => { 650 final_cycle(cpu, bus); 651 652 let base_addr = u32::from_le_bytes([cpu.state.t1, cpu.state.t2, cpu.registers.dbr, 0]); 653 let address = (base_addr + u32::from(cpu.registers.y)) & B::ADDRESS_MASK; 654 bus.write(address, cpu.registers.a as u8); 655 } 656 _ => invalid_cycle!(cpu), 657 } 658} 659 660pub(crate) fn sta_direct_page_indirect_indexed_u16<B: BusInterface>( 661 cpu: &mut Wdc65816, 662 bus: &mut B, 663) { 664 match cpu.state.cycle { 665 1 => { 666 cpu.state.t0 = fetch_operand(cpu, bus); 667 check_direct_page_low_byte(cpu); 668 } 669 2 | 5 => { 670 // Idle cycles for if D LSB is non-zero (2) and for indexing (5) 671 bus.idle(); 672 } 673 3 => { 674 let address = cpu.registers.d.wrapping_add(cpu.state.t0.into()); 675 cpu.state.t1 = bus.read(address.into()); 676 } 677 4 => { 678 let address = cpu.registers.d.wrapping_add(cpu.state.t0.into()); 679 cpu.state.t2 = bus.read(address.wrapping_add(1).into()); 680 } 681 6 => { 682 let base_addr = u32::from_le_bytes([cpu.state.t1, cpu.state.t2, cpu.registers.dbr, 0]); 683 let address = (base_addr + u32::from(cpu.registers.y)) & B::ADDRESS_MASK; 684 bus.write(address, cpu.registers.a.lsb()); 685 } 686 7 => { 687 final_cycle(cpu, bus); 688 689 let base_addr = u32::from_le_bytes([cpu.state.t1, cpu.state.t2, cpu.registers.dbr, 0]); 690 let address = (base_addr + u32::from(cpu.registers.y) + 1) & B::ADDRESS_MASK; 691 bus.write(address, cpu.registers.a.msb()); 692 } 693 _ => invalid_cycle!(cpu), 694 } 695} 696 697pub(crate) fn sta_direct_page_indirect_long_u8<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 698 match cpu.state.cycle { 699 1 => { 700 cpu.state.t0 = fetch_operand(cpu, bus); 701 check_direct_page_low_byte(cpu); 702 } 703 2 => { 704 // Idle cycle if D LSB is non-zero 705 bus.idle(); 706 } 707 3 => { 708 let address = cpu.registers.d.wrapping_add(cpu.state.t0.into()); 709 cpu.state.t1 = bus.read(address.into()); 710 } 711 4 => { 712 let address = cpu.registers.d.wrapping_add(cpu.state.t0.into()); 713 cpu.state.t2 = bus.read(address.wrapping_add(1).into()); 714 } 715 5 => { 716 let address = cpu.registers.d.wrapping_add(cpu.state.t0.into()); 717 cpu.state.t3 = bus.read(address.wrapping_add(2).into()); 718 } 719 6 => { 720 final_cycle(cpu, bus); 721 722 let address = u32::from_le_bytes([cpu.state.t1, cpu.state.t2, cpu.state.t3, 0]); 723 bus.write(address, cpu.registers.a as u8); 724 } 725 _ => invalid_cycle!(cpu), 726 } 727} 728 729pub(crate) fn sta_direct_page_indirect_long_u16<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 730 match cpu.state.cycle { 731 1 => { 732 cpu.state.t0 = fetch_operand(cpu, bus); 733 check_direct_page_low_byte(cpu); 734 } 735 2 => { 736 // Idle cycle if D LSB is non-zero 737 bus.idle(); 738 } 739 3 => { 740 let address = cpu.registers.d.wrapping_add(cpu.state.t0.into()); 741 cpu.state.t1 = bus.read(address.into()); 742 } 743 4 => { 744 let address = cpu.registers.d.wrapping_add(cpu.state.t0.into()); 745 cpu.state.t2 = bus.read(address.wrapping_add(1).into()); 746 } 747 5 => { 748 let address = cpu.registers.d.wrapping_add(cpu.state.t0.into()); 749 cpu.state.t3 = bus.read(address.wrapping_add(2).into()); 750 } 751 6 => { 752 let address = u32::from_le_bytes([cpu.state.t1, cpu.state.t2, cpu.state.t3, 0]); 753 bus.write(address, cpu.registers.a.lsb()); 754 } 755 7 => { 756 final_cycle(cpu, bus); 757 758 let address = u32::from_le_bytes([cpu.state.t1, cpu.state.t2, cpu.state.t3, 0]); 759 bus.write((address + 1) & B::ADDRESS_MASK, cpu.registers.a.msb()); 760 } 761 _ => invalid_cycle!(cpu), 762 } 763} 764 765pub(crate) fn sta_direct_page_indirect_long_indexed_u8<B: BusInterface>( 766 cpu: &mut Wdc65816, 767 bus: &mut B, 768) { 769 match cpu.state.cycle { 770 1 => { 771 cpu.state.t0 = fetch_operand(cpu, bus); 772 check_direct_page_low_byte(cpu); 773 } 774 2 => { 775 // Idle cycle if D LSB is non-zero 776 bus.idle(); 777 } 778 3 => { 779 let address = index_direct_page(cpu, cpu.state.t0, 0); 780 cpu.state.t1 = bus.read(address.into()); 781 } 782 4 => { 783 let address = index_direct_page(cpu, cpu.state.t0, 1); 784 cpu.state.t2 = bus.read(address.into()); 785 } 786 5 => { 787 let address = index_direct_page(cpu, cpu.state.t0, 2); 788 cpu.state.t3 = bus.read(address.into()); 789 } 790 6 => { 791 final_cycle(cpu, bus); 792 793 let base_addr = u32::from_le_bytes([cpu.state.t1, cpu.state.t2, cpu.state.t3, 0]); 794 let address = (base_addr + u32::from(cpu.registers.y)) & B::ADDRESS_MASK; 795 bus.write(address, cpu.registers.a as u8); 796 } 797 _ => invalid_cycle!(cpu), 798 } 799} 800 801pub(crate) fn sta_direct_page_indirect_long_indexed_u16<B: BusInterface>( 802 cpu: &mut Wdc65816, 803 bus: &mut B, 804) { 805 match cpu.state.cycle { 806 1 => { 807 cpu.state.t0 = fetch_operand(cpu, bus); 808 check_direct_page_low_byte(cpu); 809 } 810 2 => { 811 // Idle cycle if D LSB is non-zero 812 bus.idle(); 813 } 814 3 => { 815 let address = cpu.registers.d.wrapping_add(cpu.state.t0.into()); 816 cpu.state.t1 = bus.read(address.into()); 817 } 818 4 => { 819 let address = cpu.registers.d.wrapping_add(cpu.state.t0.into()); 820 cpu.state.t2 = bus.read(address.wrapping_add(1).into()); 821 } 822 5 => { 823 let address = cpu.registers.d.wrapping_add(cpu.state.t0.into()); 824 cpu.state.t3 = bus.read(address.wrapping_add(2).into()); 825 } 826 6 => { 827 let base_addr = u32::from_le_bytes([cpu.state.t1, cpu.state.t2, cpu.state.t3, 0]); 828 let address = (base_addr + u32::from(cpu.registers.y)) & B::ADDRESS_MASK; 829 bus.write(address, cpu.registers.a.lsb()); 830 } 831 7 => { 832 final_cycle(cpu, bus); 833 834 let base_addr = u32::from_le_bytes([cpu.state.t1, cpu.state.t2, cpu.state.t3, 0]); 835 let address = (base_addr + u32::from(cpu.registers.y) + 1) & B::ADDRESS_MASK; 836 bus.write(address, cpu.registers.a.msb()); 837 } 838 _ => invalid_cycle!(cpu), 839 } 840} 841 842pub(crate) fn sta_absolute_long_u8<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 843 match cpu.state.cycle { 844 1 => { 845 cpu.state.t0 = fetch_operand(cpu, bus); 846 } 847 2 => { 848 cpu.state.t1 = fetch_operand(cpu, bus); 849 } 850 3 => { 851 cpu.state.t2 = fetch_operand(cpu, bus); 852 } 853 4 => { 854 final_cycle(cpu, bus); 855 856 let address = u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.state.t2, 0]); 857 bus.write(address, cpu.registers.a as u8); 858 } 859 _ => invalid_cycle!(cpu), 860 } 861} 862 863pub(crate) fn sta_absolute_long_u16<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 864 match cpu.state.cycle { 865 1 => { 866 cpu.state.t0 = fetch_operand(cpu, bus); 867 } 868 2 => { 869 cpu.state.t1 = fetch_operand(cpu, bus); 870 } 871 3 => { 872 cpu.state.t2 = fetch_operand(cpu, bus); 873 } 874 4 => { 875 let address = u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.state.t2, 0]); 876 bus.write(address, cpu.registers.a.lsb()); 877 } 878 5 => { 879 final_cycle(cpu, bus); 880 881 let base_addr = u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.state.t2, 0]); 882 let address = (base_addr + 1) & B::ADDRESS_MASK; 883 bus.write(address, cpu.registers.a.msb()); 884 } 885 _ => invalid_cycle!(cpu), 886 } 887} 888 889pub(crate) fn sta_absolute_long_x_u8<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 890 match cpu.state.cycle { 891 1 => { 892 cpu.state.t0 = fetch_operand(cpu, bus); 893 } 894 2 => { 895 cpu.state.t1 = fetch_operand(cpu, bus); 896 } 897 3 => { 898 cpu.state.t2 = fetch_operand(cpu, bus); 899 } 900 4 => { 901 final_cycle(cpu, bus); 902 903 let base_addr = u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.state.t2, 0]); 904 let index: u32 = cpu.registers.x.into(); 905 let address = (base_addr + index) & B::ADDRESS_MASK; 906 bus.write(address, cpu.registers.a as u8); 907 } 908 _ => invalid_cycle!(cpu), 909 } 910} 911 912pub(crate) fn sta_absolute_long_x_u16<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 913 match cpu.state.cycle { 914 1 => { 915 cpu.state.t0 = fetch_operand(cpu, bus); 916 } 917 2 => { 918 cpu.state.t1 = fetch_operand(cpu, bus); 919 } 920 3 => { 921 cpu.state.t2 = fetch_operand(cpu, bus); 922 } 923 4 => { 924 let base_addr = u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.state.t2, 0]); 925 let index: u32 = cpu.registers.x.into(); 926 let address = (base_addr + index) & B::ADDRESS_MASK; 927 bus.write(address, cpu.registers.a.lsb()); 928 } 929 5 => { 930 final_cycle(cpu, bus); 931 932 let base_addr = u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.state.t2, 0]); 933 let index: u32 = cpu.registers.x.into(); 934 let address = (base_addr + index + 1) & B::ADDRESS_MASK; 935 bus.write(address, cpu.registers.a.msb()); 936 } 937 _ => invalid_cycle!(cpu), 938 } 939} 940 941pub(crate) fn sta_stack_relative_u8<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 942 match cpu.state.cycle { 943 1 => { 944 cpu.state.t0 = fetch_operand(cpu, bus); 945 } 946 2 => { 947 // Idle cycle for indexing 948 bus.idle(); 949 } 950 3 => { 951 final_cycle(cpu, bus); 952 953 let address = cpu.registers.s.wrapping_add(cpu.state.t0.into()); 954 bus.write(address.into(), cpu.registers.a as u8); 955 } 956 _ => invalid_cycle!(cpu), 957 } 958} 959 960pub(crate) fn sta_stack_relative_u16<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 961 match cpu.state.cycle { 962 1 => { 963 cpu.state.t0 = fetch_operand(cpu, bus); 964 } 965 2 => { 966 // Idle cycle for indexing 967 bus.idle(); 968 } 969 3 => { 970 let address = cpu.registers.s.wrapping_add(cpu.state.t0.into()); 971 bus.write(address.into(), cpu.registers.a.lsb()); 972 } 973 4 => { 974 final_cycle(cpu, bus); 975 976 let address = cpu.registers.s.wrapping_add(cpu.state.t0.into()); 977 bus.write(address.wrapping_add(1).into(), cpu.registers.a.msb()); 978 } 979 _ => invalid_cycle!(cpu), 980 } 981} 982 983pub(crate) fn sta_stack_relative_indirect_indexed_u8<B: BusInterface>( 984 cpu: &mut Wdc65816, 985 bus: &mut B, 986) { 987 match cpu.state.cycle { 988 1 => { 989 cpu.state.t0 = fetch_operand(cpu, bus); 990 } 991 2 | 5 => { 992 // Idle cycles for stack indexing (2) and for address indexing (5) 993 bus.idle(); 994 } 995 3 => { 996 let address = cpu.registers.s.wrapping_add(cpu.state.t0.into()); 997 cpu.state.t1 = bus.read(address.into()); 998 } 999 4 => { 1000 let address = cpu.registers.s.wrapping_add(cpu.state.t0.into()); 1001 cpu.state.t2 = bus.read(address.wrapping_add(1).into()); 1002 } 1003 6 => { 1004 final_cycle(cpu, bus); 1005 1006 let base_addr = u32::from_le_bytes([cpu.state.t1, cpu.state.t2, cpu.registers.dbr, 0]); 1007 let address = (base_addr + u32::from(cpu.registers.y)) & B::ADDRESS_MASK; 1008 bus.write(address, cpu.registers.a as u8); 1009 } 1010 _ => invalid_cycle!(cpu), 1011 } 1012} 1013 1014pub(crate) fn sta_stack_relative_indirect_indexed_u16<B: BusInterface>( 1015 cpu: &mut Wdc65816, 1016 bus: &mut B, 1017) { 1018 match cpu.state.cycle { 1019 1 => { 1020 cpu.state.t0 = fetch_operand(cpu, bus); 1021 } 1022 2 | 5 => { 1023 // Idle cycles for stack indexing (2) and for address indexing (5) 1024 bus.idle(); 1025 } 1026 3 => { 1027 let address = cpu.registers.s.wrapping_add(cpu.state.t0.into()); 1028 cpu.state.t1 = bus.read(address.into()); 1029 } 1030 4 => { 1031 let address = cpu.registers.s.wrapping_add(cpu.state.t0.into()); 1032 cpu.state.t2 = bus.read(address.wrapping_add(1).into()); 1033 } 1034 6 => { 1035 let base_addr = u32::from_le_bytes([cpu.state.t1, cpu.state.t2, cpu.registers.dbr, 0]); 1036 let address = (base_addr + u32::from(cpu.registers.y)) & B::ADDRESS_MASK; 1037 bus.write(address, cpu.registers.a.lsb()); 1038 } 1039 7 => { 1040 final_cycle(cpu, bus); 1041 1042 let base_addr = u32::from_le_bytes([cpu.state.t1, cpu.state.t2, cpu.registers.dbr, 0]); 1043 let address = (base_addr + u32::from(cpu.registers.y) + 1) & B::ADDRESS_MASK; 1044 bus.write(address, cpu.registers.a.msb()); 1045 } 1046 _ => invalid_cycle!(cpu), 1047 } 1048}
STX: Store X register
1051impl_store_direct_page_u8!(stx_direct_page_u8, x); 1052impl_store_direct_page_u16!(stx_direct_page_u16, x); 1053impl_store_direct_page_indexed_u8!(stx_direct_page_y_u8, x, index: y); 1054impl_store_direct_page_indexed_u16!(stx_direct_page_y_u16, x, index: y); 1055impl_store_absolute_u8!(stx_absolute_u8, x); 1056impl_store_absolute_u16!(stx_absolute_u16, x);
STY: Store Y register
1059impl_store_direct_page_u8!(sty_direct_page_u8, y); 1060impl_store_direct_page_u16!(sty_direct_page_u16, y); 1061impl_store_direct_page_indexed_u8!(sty_direct_page_x_u8, y, index: x); 1062impl_store_direct_page_indexed_u16!(sty_direct_page_x_u16, y, index: x); 1063impl_store_absolute_u8!(sty_absolute_u8, y); 1064impl_store_absolute_u16!(sty_absolute_u16, y);
STZ: Store zero
1067impl_store_direct_page_u8!(stz_direct_page_u8, zero); 1068impl_store_direct_page_u16!(stz_direct_page_u16, zero); 1069impl_store_direct_page_indexed_u8!(stz_direct_page_x_u8, zero, index: x); 1070impl_store_direct_page_indexed_u16!(stz_direct_page_x_u16, zero, index: x); 1071impl_store_absolute_u8!(stz_absolute_u8, zero); 1072impl_store_absolute_u16!(stz_absolute_u16, zero); 1073impl_store_absolute_indexed_u8!(stz_absolute_x_u8, zero, index: x); 1074impl_store_absolute_indexed_u16!(stz_absolute_x_u16, zero, index: x);
1076macro_rules! impl_push_u8 { 1077 ($name:ident, $register:ident) => { 1078 pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 1079 match cpu.state.cycle { 1080 1 => { 1081 bus.idle(); 1082 } 1083 2 => { 1084 final_cycle(cpu, bus); 1085 1086 bus.write(cpu.registers.s.into(), cpu.registers.$register as u8); 1087 cpu.registers.s = cpu.registers.s.wrapping_sub(1); 1088 1089 if cpu.registers.emulation_mode { 1090 ensure_page_1_stack(&mut cpu.registers); 1091 } 1092 } 1093 _ => invalid_cycle!(cpu), 1094 } 1095 } 1096 }; 1097} 1098 1099macro_rules! impl_push_u16 { 1100 ($name:ident, $register:ident) => { 1101 pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 1102 match cpu.state.cycle { 1103 1 => { 1104 bus.idle(); 1105 } 1106 2 => { 1107 bus.write(cpu.registers.s.into(), cpu.registers.$register.msb()); 1108 cpu.registers.s = cpu.registers.s.wrapping_sub(1); 1109 } 1110 3 => { 1111 final_cycle(cpu, bus); 1112 1113 bus.write(cpu.registers.s.into(), cpu.registers.$register as u8); 1114 cpu.registers.s = cpu.registers.s.wrapping_sub(1); 1115 1116 // Emulation mode check necessary for PHD 1117 // The second write is allowed to go outside of page 1 in emulation mode, but 1118 // the stack gets forced back into page 1 afterwards 1119 if cpu.registers.emulation_mode { 1120 ensure_page_1_stack(&mut cpu.registers); 1121 } 1122 } 1123 _ => invalid_cycle!(cpu), 1124 } 1125 } 1126 }; 1127}
PHB: Push data bank register
1142impl_push_u8!(phb, dbr);
PHD: Push direct page register
1145impl_push_u16!(phd, d);
PHK: Push K register (program bank register)
1148impl_push_u8!(phk, pbr);
PHP: Push processor status register
1151pub(crate) fn php<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 1152 match cpu.state.cycle { 1153 1 => { 1154 bus.idle(); 1155 } 1156 2 => { 1157 final_cycle(cpu, bus); 1158 1159 bus.write(cpu.registers.s.into(), cpu.registers.p.into()); 1160 cpu.registers.s = cpu.registers.s.wrapping_sub(1); 1161 1162 if cpu.registers.emulation_mode { 1163 ensure_page_1_stack(&mut cpu.registers); 1164 } 1165 } 1166 _ => invalid_cycle!(cpu), 1167 } 1168}
1170macro_rules! impl_pull_u8 { 1171 (@write dbr, $registers:expr, $value:expr) => { 1172 $registers.dbr = $value; 1173 }; 1174 (@write $register:ident, $registers:expr, $value:expr) => { 1175 $registers.$register.set_lsb($value); 1176 }; 1177 ($name:ident, $register:ident) => { 1178 pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 1179 match cpu.state.cycle { 1180 1 | 2 => { 1181 bus.idle(); 1182 } 1183 3 => { 1184 final_cycle(cpu, bus); 1185 1186 cpu.registers.s = cpu.registers.s.wrapping_add(1); 1187 1188 if cpu.registers.emulation_mode { 1189 ensure_page_1_stack(&mut cpu.registers); 1190 } 1191 1192 let value = bus.read(cpu.registers.s.into()); 1193 impl_pull_u8!(@write $register, cpu.registers, value); 1194 1195 cpu.registers.p.zero = value == 0; 1196 cpu.registers.p.negative = value.sign_bit(); 1197 } 1198 _ => invalid_cycle!(cpu) 1199 } 1200 } 1201 } 1202} 1203 1204macro_rules! impl_pull_u16 { 1205 ($name:ident, $register:ident) => { 1206 pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 1207 match cpu.state.cycle { 1208 1 | 2 => { 1209 bus.idle(); 1210 } 1211 3 => { 1212 cpu.registers.s = cpu.registers.s.wrapping_add(1); 1213 cpu.state.t0 = bus.read(cpu.registers.s.into()); 1214 } 1215 4 => { 1216 final_cycle(cpu, bus); 1217 1218 cpu.registers.s = cpu.registers.s.wrapping_add(1); 1219 let value_msb = bus.read(cpu.registers.s.into()); 1220 let value = u16::from_le_bytes([cpu.state.t0, value_msb]); 1221 cpu.registers.$register = value; 1222 1223 cpu.registers.p.zero = value == 0; 1224 cpu.registers.p.negative = value.sign_bit(); 1225 1226 // Emulation mode check necessary for PLD, similar to PHD 1227 if cpu.registers.emulation_mode { 1228 ensure_page_1_stack(&mut cpu.registers); 1229 } 1230 } 1231 _ => invalid_cycle!(cpu), 1232 } 1233 } 1234 }; 1235}
PLB: Pull data bank register
1250impl_pull_u8!(plb, dbr);
PLD: Pull direct page register
1253impl_pull_u16!(pld, d);
PLP: Pull processor status register
1256pub(crate) fn plp<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 1257 match cpu.state.cycle { 1258 1 | 2 => { 1259 bus.idle(); 1260 } 1261 3 => { 1262 final_cycle(cpu, bus); 1263 1264 cpu.registers.s = cpu.registers.s.wrapping_add(1); 1265 1266 if cpu.registers.emulation_mode { 1267 ensure_page_1_stack(&mut cpu.registers); 1268 1269 // m and x flags are forced to 1 in emulation mode 1270 let value = 0x30 | bus.read(cpu.registers.s.into()); 1271 cpu.registers.p = value.into(); 1272 } else { 1273 cpu.registers.p = bus.read(cpu.registers.s.into()).into(); 1274 1275 if cpu.registers.p.index_size == SizeBits::Eight { 1276 // Immediately truncate index registers to 8 bits 1277 cpu.registers.x &= 0x00FF; 1278 cpu.registers.y &= 0x00FF; 1279 } 1280 } 1281 } 1282 _ => invalid_cycle!(cpu), 1283 } 1284}
1286macro_rules! impl_move { 1287 ($name:ident, $index_inc_method:ident) => { 1288 pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) { 1289 match cpu.state.cycle { 1290 1 => { 1291 cpu.registers.dbr = fetch_operand(cpu, bus); 1292 } 1293 2 => { 1294 cpu.state.t0 = fetch_operand(cpu, bus); 1295 } 1296 3 => { 1297 let address = u24_address(cpu.state.t0, cpu.registers.x); 1298 cpu.state.t1 = bus.read(address); 1299 } 1300 4 => { 1301 let address = u24_address(cpu.registers.dbr, cpu.registers.y); 1302 bus.write(address, cpu.state.t1); 1303 } 1304 5 => { 1305 bus.idle(); 1306 } 1307 6 => { 1308 final_cycle(cpu, bus); 1309 1310 bus.idle(); 1311 1312 cpu.registers.a = cpu.registers.a.wrapping_sub(1); 1313 if cpu.registers.a != 0xFFFF { 1314 cpu.registers.pc = cpu.registers.pc.wrapping_sub(3); 1315 } 1316 1317 cpu.registers.x = cpu.registers.x.$index_inc_method(1); 1318 cpu.registers.y = cpu.registers.y.$index_inc_method(1); 1319 1320 if cpu.registers.p.index_size == SizeBits::Eight { 1321 cpu.registers.x &= 0x00FF; 1322 cpu.registers.y &= 0x00FF; 1323 } 1324 } 1325 _ => invalid_cycle!(cpu), 1326 } 1327 } 1328 }; 1329}
MVN: Move memory negative
1332impl_move!(mvn, wrapping_add);
MVP: Move memory positive
1335impl_move!(mvp, wrapping_sub);