1use bincode::{Decode, Encode}; 2 3use jgenesis_common::num::{GetBit, SignBit}; 4 5use crate::bus::BusInterface; 6use crate::{CpuRegisters, IRQ_VECTOR, Mos6502, NMI_VECTOR, StatusFlags, StatusReadContext}; 7 8#[derive(Debug, Clone, Encode, Decode)] 9pub struct InstructionState { 10 pub opcode: u8, 11 pub executing_interrupt: bool, 12 pub cycle: u8, 13 pub operand_first_byte: u8, 14 pub operand_second_byte: u8, 15 pub target_first_byte: u8, 16 pub target_second_byte: u8, 17 pub indirect_byte: u8, 18 pub interrupt_vector: u16, 19 pub pending_interrupt: bool, 20 pub instruction_complete: bool, 21} 22 23impl Default for InstructionState { 24 fn default() -> Self { 25 Self { 26 opcode: 0, 27 executing_interrupt: false, 28 cycle: 0, 29 operand_first_byte: 0, 30 operand_second_byte: 0, 31 target_first_byte: 0, 32 target_second_byte: 0, 33 indirect_byte: 0, 34 interrupt_vector: 0, 35 pending_interrupt: false, 36 instruction_complete: true, 37 } 38 } 39} 40 41#[inline] 42fn poll_interrupt_lines<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) { 43 cpu.state.pending_interrupt |= 44 bus.nmi() || (!cpu.registers.status.interrupt_disable && bus.irq()); 45} 46 47#[inline] 48fn final_cycle<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) { 49 poll_interrupt_lines(cpu, bus); 50 cpu.state.instruction_complete = true; 51} 52 53#[inline] 54fn fetch_operand<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) -> u8 { 55 let operand = bus.read(cpu.registers.pc); 56 cpu.registers.pc = cpu.registers.pc.wrapping_add(1); 57 operand 58} 59 60macro_rules! invalid_cycle { 61 ($cpu:expr) => { 62 panic!("Invalid cycle: {}", $cpu.state.cycle) 63 }; 64} 65 66macro_rules! impl_read_immediate { 67 ($cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => {{ 68 final_cycle($cpu, $bus); 69 70 let $operand = fetch_operand($cpu, $bus); 71 let $registers_param = &mut $cpu.registers; 72 $body 73 }}; 74} 75 76macro_rules! impl_read_zero_page { 77 ($cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => { 78 match $cpu.state.cycle { 79 0 => { 80 $cpu.state.operand_first_byte = fetch_operand($cpu, $bus); 81 } 82 1 => { 83 final_cycle($cpu, $bus); 84 85 let $operand = $bus.read($cpu.state.operand_first_byte.into()); 86 let $registers_param = &mut $cpu.registers; 87 $body 88 } 89 _ => invalid_cycle!($cpu), 90 } 91 }; 92} 93 94macro_rules! impl_read_zero_page_indexed { 95 (index: $index:ident, $cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => { 96 match $cpu.state.cycle { 97 0 => { 98 $cpu.state.operand_first_byte = fetch_operand($cpu, $bus); 99 } 100 1 => { 101 $bus.read($cpu.state.operand_first_byte.into()); 102 } 103 2 => { 104 final_cycle($cpu, $bus); 105 106 let index = $cpu.registers.$index; 107 let address = $cpu.state.operand_first_byte.wrapping_add(index); 108 let $operand = $bus.read(address.into()); 109 let $registers_param = &mut $cpu.registers; 110 $body 111 } 112 _ => invalid_cycle!($cpu), 113 } 114 }; 115} 116 117macro_rules! impl_read_absolute { 118 ($cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => { 119 match $cpu.state.cycle { 120 0 => { 121 $cpu.state.operand_first_byte = fetch_operand($cpu, $bus); 122 } 123 1 => { 124 $cpu.state.operand_second_byte = fetch_operand($cpu, $bus); 125 } 126 2 => { 127 final_cycle($cpu, $bus); 128 129 let address = u16::from_le_bytes([ 130 $cpu.state.operand_first_byte, 131 $cpu.state.operand_second_byte, 132 ]); 133 let $operand = $bus.read(address); 134 let $registers_param = &mut $cpu.registers; 135 $body 136 } 137 _ => invalid_cycle!($cpu), 138 } 139 }; 140} 141 142macro_rules! impl_read_absolute_indexed { 143 (index: $index:ident, $cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => { 144 match $cpu.state.cycle { 145 0 => { 146 $cpu.state.operand_first_byte = fetch_operand($cpu, $bus); 147 } 148 1 => { 149 $cpu.state.operand_second_byte = fetch_operand($cpu, $bus); 150 } 151 2 => { 152 poll_interrupt_lines($cpu, $bus); 153 154 let (address_lsb, overflowed) = 155 $cpu.state.operand_first_byte.overflowing_add($cpu.registers.$index); 156 let address = u16::from_le_bytes([address_lsb, $cpu.state.operand_second_byte]); 157 let $operand = $bus.read(address); 158 159 if !overflowed { 160 let $registers_param = &mut $cpu.registers; 161 $body 162 $cpu.state.instruction_complete = true; 163 } 164 } 165 3 => { 166 final_cycle($cpu, $bus); 167 168 let address = u16::from_le_bytes([$cpu.state.operand_first_byte, $cpu.state.operand_second_byte]) 169 .wrapping_add($cpu.registers.$index.into()); 170 let $operand = $bus.read(address); 171 let $registers_param = &mut $cpu.registers; 172 $body 173 } 174 _ => invalid_cycle!($cpu) 175 } 176 } 177} 178 179macro_rules! impl_read_indirect_x { 180 ($cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => { 181 match $cpu.state.cycle { 182 0 => { 183 $cpu.state.operand_first_byte = fetch_operand($cpu, $bus); 184 } 185 1 => { 186 $bus.read($cpu.state.operand_first_byte.into()); 187 } 188 2 => { 189 let address = $cpu.state.operand_first_byte.wrapping_add($cpu.registers.x); 190 $cpu.state.target_first_byte = $bus.read(address.into()); 191 } 192 3 => { 193 let address = 194 $cpu.state.operand_first_byte.wrapping_add($cpu.registers.x).wrapping_add(1); 195 $cpu.state.target_second_byte = $bus.read(address.into()); 196 } 197 4 => { 198 final_cycle($cpu, $bus); 199 200 let address = u16::from_le_bytes([ 201 $cpu.state.target_first_byte, 202 $cpu.state.target_second_byte, 203 ]); 204 let $operand = $bus.read(address); 205 let $registers_param = &mut $cpu.registers; 206 $body 207 } 208 _ => invalid_cycle!($cpu), 209 } 210 }; 211} 212 213macro_rules! impl_read_indirect_y { 214 ($cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => { 215 match $cpu.state.cycle { 216 0 => { 217 $cpu.state.operand_first_byte = fetch_operand($cpu, $bus); 218 } 219 1 => { 220 $cpu.state.target_first_byte = $bus.read($cpu.state.operand_first_byte.into()); 221 } 222 2 => { 223 $cpu.state.target_second_byte = 224 $bus.read($cpu.state.operand_first_byte.wrapping_add(1).into()); 225 } 226 3 => { 227 poll_interrupt_lines($cpu, $bus); 228 229 let (address_lsb, overflowed) = $cpu.state.target_first_byte.overflowing_add($cpu.registers.y); 230 let address = u16::from_le_bytes([address_lsb, $cpu.state.target_second_byte]); 231 let $operand = $bus.read(address); 232 233 if !overflowed { 234 let $registers_param = &mut $cpu.registers; 235 $body 236 $cpu.state.instruction_complete = true; 237 } 238 } 239 4 => { 240 final_cycle($cpu, $bus); 241 242 let address = u16::from_le_bytes([$cpu.state.target_first_byte, $cpu.state.target_second_byte]) 243 .wrapping_add($cpu.registers.y.into()); 244 let $operand = $bus.read(address); 245 let $registers_param = &mut $cpu.registers; 246 $body 247 } 248 _ => invalid_cycle!($cpu) 249 } 250 } 251} 252 253macro_rules! impl_read_instruction { 254 (immediate, $($rest:tt)*) => { 255 impl_read_immediate!($($rest)*) 256 }; 257 (zero_page, $($rest:tt)*) => { 258 impl_read_zero_page!($($rest)*) 259 }; 260 (zero_page_x, $($rest:tt)*) => { 261 impl_read_zero_page_indexed!(index: x, $($rest)*) 262 }; 263 (zero_page_y, $($rest:tt)*) => { 264 impl_read_zero_page_indexed!(index: y, $($rest)*) 265 }; 266 (absolute, $($rest:tt)*) => { 267 impl_read_absolute!($($rest)*) 268 }; 269 (absolute_x, $($rest:tt)*) => { 270 impl_read_absolute_indexed!(index: x, $($rest)*) 271 }; 272 (absolute_y, $($rest:tt)*) => { 273 impl_read_absolute_indexed!(index: y, $($rest)*) 274 }; 275 (indirect_x, $($rest:tt)*) => { 276 impl_read_indirect_x!($($rest)*) 277 }; 278 (indirect_y, $($rest:tt)*) => { 279 impl_read_indirect_y!($($rest)*) 280 }; 281} 282 283macro_rules! impl_read_fn { 284 ($name:ident, $addressing_mode:tt, |$operand:ident, $registers:ident| $body:block) => { 285 fn $name<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) { 286 impl_read_instruction!($addressing_mode, cpu, bus, |$operand, $registers| $body); 287 } 288 }; 289} 290 291macro_rules! impl_store_zero_page { 292 ($cpu:expr, $bus:expr, $register:expr) => { 293 match $cpu.state.cycle { 294 0 => { 295 $cpu.state.operand_first_byte = fetch_operand($cpu, $bus); 296 } 297 1 => { 298 final_cycle($cpu, $bus); 299 300 let address = $cpu.state.operand_first_byte.into(); 301 $bus.write(address, $register); 302 } 303 _ => invalid_cycle!($cpu), 304 } 305 }; 306} 307 308macro_rules! impl_store_zero_page_indexed { 309 (index: $index:ident, $cpu:expr, $bus:expr, $register:expr) => { 310 match $cpu.state.cycle { 311 0 => { 312 $cpu.state.operand_first_byte = fetch_operand($cpu, $bus); 313 } 314 1 => { 315 $bus.read($cpu.state.operand_first_byte.into()); 316 } 317 2 => { 318 final_cycle($cpu, $bus); 319 320 let address = 321 $cpu.state.operand_first_byte.wrapping_add($cpu.registers.$index).into(); 322 $bus.write(address, $register); 323 } 324 _ => invalid_cycle!($cpu), 325 } 326 }; 327} 328 329macro_rules! impl_store_absolute { 330 ($cpu:expr, $bus:expr, $register:expr) => { 331 match $cpu.state.cycle { 332 0 => { 333 $cpu.state.operand_first_byte = fetch_operand($cpu, $bus); 334 } 335 1 => { 336 $cpu.state.operand_second_byte = fetch_operand($cpu, $bus); 337 } 338 2 => { 339 final_cycle($cpu, $bus); 340 341 let address = u16::from_le_bytes([ 342 $cpu.state.operand_first_byte, 343 $cpu.state.operand_second_byte, 344 ]); 345 $bus.write(address, $register); 346 } 347 _ => invalid_cycle!($cpu), 348 } 349 }; 350} 351 352macro_rules! impl_store_absolute_indexed { 353 (index: $index:ident, $cpu:expr, $bus:expr, $register:expr) => { 354 match $cpu.state.cycle { 355 0 => { 356 $cpu.state.operand_first_byte = fetch_operand($cpu, $bus); 357 } 358 1 => { 359 $cpu.state.operand_second_byte = fetch_operand($cpu, $bus); 360 } 361 2 => { 362 let address_lsb = $cpu.state.operand_first_byte.wrapping_add($cpu.registers.$index); 363 let address = u16::from_le_bytes([address_lsb, $cpu.state.operand_second_byte]); 364 $bus.read(address); 365 } 366 3 => { 367 final_cycle($cpu, $bus); 368 369 let address = u16::from_le_bytes([ 370 $cpu.state.operand_first_byte, 371 $cpu.state.operand_second_byte, 372 ]) 373 .wrapping_add($cpu.registers.$index.into()); 374 $bus.write(address, $register); 375 } 376 _ => invalid_cycle!($cpu), 377 } 378 }; 379} 380 381macro_rules! impl_store_indirect_x { 382 ($cpu:expr, $bus:expr, $register:expr) => { 383 match $cpu.state.cycle { 384 0 => { 385 $cpu.state.operand_first_byte = fetch_operand($cpu, $bus); 386 } 387 1 => { 388 $bus.read($cpu.state.operand_first_byte.into()); 389 } 390 2 => { 391 let address = $cpu.state.operand_first_byte.wrapping_add($cpu.registers.x); 392 $cpu.state.target_first_byte = $bus.read(address.into()); 393 } 394 3 => { 395 let address = 396 $cpu.state.operand_first_byte.wrapping_add($cpu.registers.x).wrapping_add(1); 397 $cpu.state.target_second_byte = $bus.read(address.into()); 398 } 399 4 => { 400 final_cycle($cpu, $bus); 401 402 let address = u16::from_le_bytes([ 403 $cpu.state.target_first_byte, 404 $cpu.state.target_second_byte, 405 ]); 406 $bus.write(address, $register); 407 } 408 _ => invalid_cycle!($cpu), 409 } 410 }; 411} 412 413macro_rules! impl_store_indirect_y { 414 ($cpu:expr, $bus:expr, $register:expr) => { 415 match $cpu.state.cycle { 416 0 => { 417 $cpu.state.operand_first_byte = fetch_operand($cpu, $bus); 418 } 419 1 => { 420 $cpu.state.target_first_byte = $bus.read($cpu.state.operand_first_byte.into()); 421 } 422 2 => { 423 let address = $cpu.state.operand_first_byte.wrapping_add(1); 424 $cpu.state.target_second_byte = $bus.read(address.into()); 425 } 426 3 => { 427 let address_lsb = $cpu.state.target_first_byte.wrapping_add($cpu.registers.y); 428 let address = u16::from_le_bytes([address_lsb, $cpu.state.target_second_byte]); 429 $bus.read(address); 430 } 431 4 => { 432 final_cycle($cpu, $bus); 433 434 let address = u16::from_le_bytes([ 435 $cpu.state.target_first_byte, 436 $cpu.state.target_second_byte, 437 ]) 438 .wrapping_add($cpu.registers.y.into()); 439 $bus.write(address, $register); 440 } 441 _ => invalid_cycle!($cpu), 442 } 443 }; 444} 445 446macro_rules! impl_store { 447 (zero_page, $($rest:tt)*) => { 448 impl_store_zero_page!($($rest)*) 449 }; 450 (zero_page_x, $($rest:tt)*) => { 451 impl_store_zero_page_indexed!(index: x, $($rest)*) 452 }; 453 (zero_page_y, $($rest:tt)*) => { 454 impl_store_zero_page_indexed!(index: y, $($rest)*) 455 }; 456 (absolute, $($rest:tt)*) => { 457 impl_store_absolute!($($rest)*) 458 }; 459 (absolute_x, $($rest:tt)*) => { 460 impl_store_absolute_indexed!(index: x, $($rest)*) 461 }; 462 (absolute_y, $($rest:tt)*) => { 463 impl_store_absolute_indexed!(index: y, $($rest)*) 464 }; 465 (indirect_x, $($rest:tt)*) => { 466 impl_store_indirect_x!($($rest)*) 467 }; 468 (indirect_y, $($rest:tt)*) => { 469 impl_store_indirect_y!($($rest)*) 470 }; 471} 472 473// STA, STX, STY, unofficial SAX 474macro_rules! impl_store_fn { 475 ($name:ident, ax, $addressing_mode:tt) => { 476 fn $name<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) { 477 impl_store!($addressing_mode, cpu, bus, cpu.registers.accumulator & cpu.registers.x); 478 } 479 }; 480 ($name:ident, $register:ident, $addressing_mode:tt) => { 481 fn $name<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) { 482 impl_store!($addressing_mode, cpu, bus, cpu.registers.$register); 483 } 484 }; 485} 486 487impl_store_fn!(sta_zero_page, accumulator, zero_page); 488impl_store_fn!(sta_zero_page_x, accumulator, zero_page_x); 489impl_store_fn!(sta_absolute, accumulator, absolute); 490impl_store_fn!(sta_absolute_x, accumulator, absolute_x); 491impl_store_fn!(sta_absolute_y, accumulator, absolute_y); 492impl_store_fn!(sta_indirect_x, accumulator, indirect_x); 493impl_store_fn!(sta_indirect_y, accumulator, indirect_y); 494 495impl_store_fn!(stx_zero_page, x, zero_page); 496impl_store_fn!(stx_zero_page_y, x, zero_page_y); 497impl_store_fn!(stx_absolute, x, absolute); 498 499impl_store_fn!(sty_zero_page, y, zero_page); 500impl_store_fn!(sty_zero_page_x, y, zero_page_x); 501impl_store_fn!(sty_absolute, y, absolute); 502 503impl_store_fn!(sax_zero_page, ax, zero_page); 504impl_store_fn!(sax_zero_page_y, ax, zero_page_y); 505impl_store_fn!(sax_absolute, ax, absolute); 506impl_store_fn!(sax_indirect_x, ax, indirect_x); 507 508macro_rules! impl_modify_accumulator { 509 ($cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => {{ 510 final_cycle($cpu, $bus); 511 512 $bus.read($cpu.registers.pc); 513 514 let $operand = $cpu.registers.accumulator; 515 let $registers_param = &mut $cpu.registers; 516 $registers_param.accumulator = $body; 517 }}; 518} 519 520macro_rules! impl_modify_zero_page { 521 ($cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => { 522 match $cpu.state.cycle { 523 0 => { 524 $cpu.state.operand_first_byte = fetch_operand($cpu, $bus); 525 } 526 1 => { 527 $cpu.state.target_first_byte = $bus.read($cpu.state.operand_first_byte.into()); 528 } 529 2 => { 530 $bus.write($cpu.state.operand_first_byte.into(), $cpu.state.target_first_byte); 531 } 532 3 => { 533 final_cycle($cpu, $bus); 534 535 let $operand = $cpu.state.target_first_byte; 536 let $registers_param = &mut $cpu.registers; 537 let value = $body; 538 $bus.write($cpu.state.operand_first_byte.into(), value); 539 } 540 _ => invalid_cycle!($cpu), 541 } 542 }; 543} 544 545macro_rules! impl_modify_zero_page_x { 546 ($cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => { 547 match $cpu.state.cycle { 548 0 => { 549 $cpu.state.operand_first_byte = fetch_operand($cpu, $bus); 550 } 551 1 => { 552 $bus.read($cpu.state.operand_first_byte.into()); 553 } 554 2 => { 555 let address = $cpu.state.operand_first_byte.wrapping_add($cpu.registers.x).into(); 556 $cpu.state.target_first_byte = $bus.read(address); 557 } 558 3 => { 559 let address = $cpu.state.operand_first_byte.wrapping_add($cpu.registers.x).into(); 560 $bus.write(address, $cpu.state.target_first_byte); 561 } 562 4 => { 563 final_cycle($cpu, $bus); 564 565 let address = $cpu.state.operand_first_byte.wrapping_add($cpu.registers.x).into(); 566 567 let $operand = $cpu.state.target_first_byte; 568 let $registers_param = &mut $cpu.registers; 569 let value = $body; 570 571 $bus.write(address, value); 572 } 573 _ => invalid_cycle!($cpu), 574 } 575 }; 576} 577 578macro_rules! impl_modify_absolute { 579 ($cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => { 580 match $cpu.state.cycle { 581 0 => { 582 $cpu.state.operand_first_byte = fetch_operand($cpu, $bus); 583 } 584 1 => { 585 $cpu.state.operand_second_byte = fetch_operand($cpu, $bus); 586 } 587 2 => { 588 let address = u16::from_le_bytes([ 589 $cpu.state.operand_first_byte, 590 $cpu.state.operand_second_byte, 591 ]); 592 $cpu.state.target_first_byte = $bus.read(address); 593 } 594 3 => { 595 let address = u16::from_le_bytes([ 596 $cpu.state.operand_first_byte, 597 $cpu.state.operand_second_byte, 598 ]); 599 $bus.write(address, $cpu.state.target_first_byte); 600 } 601 4 => { 602 final_cycle($cpu, $bus); 603 604 let $operand = $cpu.state.target_first_byte; 605 let $registers_param = &mut $cpu.registers; 606 let value = $body; 607 608 let address = u16::from_le_bytes([ 609 $cpu.state.operand_first_byte, 610 $cpu.state.operand_second_byte, 611 ]); 612 $bus.write(address, value); 613 } 614 _ => invalid_cycle!($cpu), 615 } 616 }; 617} 618 619macro_rules! impl_modify_absolute_indexed { 620 (index: $index:ident, $cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => { 621 match $cpu.state.cycle { 622 0 => { 623 $cpu.state.operand_first_byte = fetch_operand($cpu, $bus); 624 } 625 1 => { 626 $cpu.state.operand_second_byte = fetch_operand($cpu, $bus); 627 } 628 2 => { 629 let address_lsb = $cpu.state.operand_first_byte.wrapping_add($cpu.registers.$index); 630 let address = u16::from_le_bytes([address_lsb, $cpu.state.operand_second_byte]); 631 $bus.read(address); 632 } 633 3 => { 634 let address = u16::from_le_bytes([ 635 $cpu.state.operand_first_byte, 636 $cpu.state.operand_second_byte, 637 ]) 638 .wrapping_add($cpu.registers.$index.into()); 639 $cpu.state.target_first_byte = $bus.read(address); 640 } 641 4 => { 642 let address = u16::from_le_bytes([ 643 $cpu.state.operand_first_byte, 644 $cpu.state.operand_second_byte, 645 ]) 646 .wrapping_add($cpu.registers.$index.into()); 647 $bus.write(address, $cpu.state.target_first_byte); 648 } 649 5 => { 650 final_cycle($cpu, $bus); 651 652 let address = u16::from_le_bytes([ 653 $cpu.state.operand_first_byte, 654 $cpu.state.operand_second_byte, 655 ]) 656 .wrapping_add($cpu.registers.$index.into()); 657 658 let $operand = $cpu.state.target_first_byte; 659 let $registers_param = &mut $cpu.registers; 660 let value = $body; 661 662 $bus.write(address, value); 663 } 664 _ => invalid_cycle!($cpu), 665 } 666 }; 667} 668 669macro_rules! impl_modify_indirect_x { 670 ($cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => { 671 match $cpu.state.cycle { 672 0 => { 673 $cpu.state.operand_first_byte = fetch_operand($cpu, $bus); 674 } 675 1 => { 676 $bus.read($cpu.state.operand_first_byte.into()); 677 } 678 2 => { 679 let address = $cpu.state.operand_first_byte.wrapping_add($cpu.registers.x); 680 $cpu.state.target_first_byte = $bus.read(address.into()); 681 } 682 3 => { 683 let address = 684 $cpu.state.operand_first_byte.wrapping_add($cpu.registers.x).wrapping_add(1); 685 $cpu.state.target_second_byte = $bus.read(address.into()); 686 } 687 4 => { 688 let address = u16::from_le_bytes([ 689 $cpu.state.target_first_byte, 690 $cpu.state.target_second_byte, 691 ]); 692 $cpu.state.indirect_byte = $bus.read(address); 693 } 694 5 => { 695 let address = u16::from_le_bytes([ 696 $cpu.state.target_first_byte, 697 $cpu.state.target_second_byte, 698 ]); 699 $bus.write(address, $cpu.state.indirect_byte); 700 } 701 6 => { 702 final_cycle($cpu, $bus); 703 704 let $operand = $cpu.state.indirect_byte; 705 let $registers_param = &mut $cpu.registers; 706 let value = $body; 707 708 let address = u16::from_le_bytes([ 709 $cpu.state.target_first_byte, 710 $cpu.state.target_second_byte, 711 ]); 712 $bus.write(address, value); 713 } 714 _ => invalid_cycle!($cpu), 715 } 716 }; 717} 718 719macro_rules! impl_modify_indirect_y { 720 ($cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => { 721 match $cpu.state.cycle { 722 0 => { 723 $cpu.state.operand_first_byte = fetch_operand($cpu, $bus); 724 } 725 1 => { 726 $cpu.state.target_first_byte = $bus.read($cpu.state.operand_first_byte.into()); 727 } 728 2 => { 729 let address = $cpu.state.operand_first_byte.wrapping_add(1).into(); 730 $cpu.state.target_second_byte = $bus.read(address); 731 } 732 3 => { 733 let address_lsb = $cpu.state.target_first_byte.wrapping_add($cpu.registers.y); 734 let address = u16::from_le_bytes([address_lsb, $cpu.state.target_second_byte]); 735 $bus.read(address); 736 } 737 4 => { 738 let address = u16::from_le_bytes([ 739 $cpu.state.target_first_byte, 740 $cpu.state.target_second_byte, 741 ]) 742 .wrapping_add($cpu.registers.y.into()); 743 $cpu.state.indirect_byte = $bus.read(address); 744 } 745 5 => { 746 let address = u16::from_le_bytes([ 747 $cpu.state.target_first_byte, 748 $cpu.state.target_second_byte, 749 ]) 750 .wrapping_add($cpu.registers.y.into()); 751 $bus.write(address, $cpu.state.indirect_byte); 752 } 753 6 => { 754 final_cycle($cpu, $bus); 755 756 let address = u16::from_le_bytes([ 757 $cpu.state.target_first_byte, 758 $cpu.state.target_second_byte, 759 ]) 760 .wrapping_add($cpu.registers.y.into()); 761 762 let $operand = $cpu.state.indirect_byte; 763 let $registers_param = &mut $cpu.registers; 764 let value = $body; 765 766 $bus.write(address, value); 767 } 768 _ => invalid_cycle!($cpu), 769 } 770 }; 771} 772 773macro_rules! impl_modify_instruction { 774 (accumulator, $($rest:tt)*) => { 775 impl_modify_accumulator!($($rest)*) 776 }; 777 (zero_page, $($rest:tt)*) => { 778 impl_modify_zero_page!($($rest)*) 779 }; 780 (zero_page_x, $($rest:tt)*) => { 781 impl_modify_zero_page_x!($($rest)*) 782 }; 783 (absolute, $($rest:tt)*) => { 784 impl_modify_absolute!($($rest)*) 785 }; 786 (absolute_x, $($rest:tt)*) => { 787 impl_modify_absolute_indexed!(index: x, $($rest)*) 788 }; 789 (absolute_y, $($rest:tt)*) => { 790 impl_modify_absolute_indexed!(index: y, $($rest)*) 791 }; 792 (indirect_x, $($rest:tt)*) => { 793 impl_modify_indirect_x!($($rest)*) 794 }; 795 (indirect_y, $($rest:tt)*) => { 796 impl_modify_indirect_y!($($rest)*) 797 }; 798} 799 800macro_rules! impl_modify_fn { 801 ($name:ident, $addressing_mode:tt, |$operand:ident, $registers:ident| $body:block) => { 802 fn $name<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) { 803 impl_modify_instruction!($addressing_mode, cpu, bus, |$operand, $registers| $body); 804 } 805 }; 806} 807 808macro_rules! impl_registers_only_fn { 809 ($name:ident, |$registers:ident| $body:block) => { 810 fn $name<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) { 811 final_cycle(cpu, bus); 812 813 // Spurious operand read 814 bus.read(cpu.registers.pc); 815 816 let $registers = &mut cpu.registers; 817 $body 818 } 819 }; 820} 821 822// LDA, LDX, LDY 823macro_rules! impl_load { 824 ($name:ident, $register:ident, $addressing_mode:tt) => { 825 impl_read_fn!($name, $addressing_mode, |operand, registers| { 826 registers.$register = operand; 827 registers.status.set_negative(operand.bit(7)).set_zero(operand == 0); 828 }); 829 }; 830} 831 832impl_load!(lda_immediate, accumulator, immediate); 833impl_load!(lda_zero_page, accumulator, zero_page); 834impl_load!(lda_zero_page_x, accumulator, zero_page_x); 835impl_load!(lda_absolute, accumulator, absolute); 836impl_load!(lda_absolute_x, accumulator, absolute_x); 837impl_load!(lda_absolute_y, accumulator, absolute_y); 838impl_load!(lda_indirect_x, accumulator, indirect_x); 839impl_load!(lda_indirect_y, accumulator, indirect_y); 840 841impl_load!(ldx_immediate, x, immediate); 842impl_load!(ldx_zero_page, x, zero_page); 843impl_load!(ldx_zero_page_y, x, zero_page_y); 844impl_load!(ldx_absolute, x, absolute); 845impl_load!(ldx_absolute_y, x, absolute_y); 846 847impl_load!(ldy_immediate, y, immediate); 848impl_load!(ldy_zero_page, y, zero_page); 849impl_load!(ldy_zero_page_x, y, zero_page_x); 850impl_load!(ldy_absolute, y, absolute); 851impl_load!(ldy_absolute_x, y, absolute_x); 852 853fn add(accumulator: u8, value: u8, flags: &mut StatusFlags) -> u8 { 854 let existing_carry = flags.carry; 855 856 let (result, carry1) = accumulator.overflowing_add(value); 857 let (result, carry2) = result.overflowing_add(existing_carry.into()); 858 let new_carry = carry1 || carry2; 859 860 let bit_6_carry = (accumulator & 0x7F) + (value & 0x7F) + u8::from(existing_carry) >= 0x80; 861 let overflow = new_carry ^ bit_6_carry; 862 863 flags 864 .set_negative(result.bit(7)) 865 .set_overflow(overflow) 866 .set_zero(result == 0) 867 .set_carry(new_carry); 868 869 result 870} 871 872fn add_bcd(accumulator: u8, value: u8, flags: &mut StatusFlags) -> u8 { 873 // Formulas from http://www.6502.org/tutorials/decimal_mode.html#A which correctly handle 874 // invalid values and undocumented behaviors 875 876 let existing_carry: u8 = flags.carry.into(); 877 878 let mut al = (accumulator & 0x0F) + (value & 0x0F) + existing_carry; 879 if al >= 0x0A { 880 al = 0x10 | ((al + 0x06) & 0x0F); 881 } 882 883 let mut a = u16::from(accumulator & 0xF0) + u16::from(value & 0xF0) + u16::from(al); 884 if a >= 0xA0 { 885 a += 0x60; 886 } 887 888 let s = i16::from((accumulator & 0xF0) as i8) + i16::from((value & 0xF0) as i8) + i16::from(al); 889 let overflow = !(-128..128).contains(&s); 890 891 let result = a as u8; 892 893 // The 6502 calculates the Z flag based on the binary arithmetic result 894 flags.zero = accumulator.wrapping_add(value).wrapping_add(existing_carry) == 0; 895 flags.carry = a >= 0x0100; 896 flags.negative = s.bit(7); 897 flags.overflow = overflow; 898 899 result 900} 901 902// ADC 903macro_rules! impl_add_with_carry { 904 ($name:ident, $addressing_mode:tt) => { 905 impl_read_fn!($name, $addressing_mode, |operand, registers| { 906 registers.accumulator = if registers.in_decimal_mode() { 907 add_bcd(registers.accumulator, operand, &mut registers.status) 908 } else { 909 add(registers.accumulator, operand, &mut registers.status) 910 }; 911 }); 912 }; 913} 914 915impl_add_with_carry!(adc_immediate, immediate); 916impl_add_with_carry!(adc_zero_page, zero_page); 917impl_add_with_carry!(adc_zero_page_x, zero_page_x); 918impl_add_with_carry!(adc_absolute, absolute); 919impl_add_with_carry!(adc_absolute_x, absolute_x); 920impl_add_with_carry!(adc_absolute_y, absolute_y); 921impl_add_with_carry!(adc_indirect_x, indirect_x); 922impl_add_with_carry!(adc_indirect_y, indirect_y); 923 924fn and(accumulator: u8, value: u8, flags: &mut StatusFlags) -> u8 { 925 let result = accumulator & value; 926 flags.set_negative(result.bit(7)).set_zero(result == 0); 927 result 928} 929 930// AND 931macro_rules! impl_and { 932 ($name:ident, $addressing_mode:tt) => { 933 impl_read_fn!($name, $addressing_mode, |operand, registers| { 934 registers.accumulator = and(registers.accumulator, operand, &mut registers.status); 935 }); 936 }; 937} 938 939impl_and!(and_immediate, immediate); 940impl_and!(and_zero_page, zero_page); 941impl_and!(and_zero_page_x, zero_page_x); 942impl_and!(and_absolute, absolute); 943impl_and!(and_absolute_x, absolute_x); 944impl_and!(and_absolute_y, absolute_y); 945impl_and!(and_indirect_x, indirect_x); 946impl_and!(and_indirect_y, indirect_y); 947 948fn bit_test(accumulator: u8, value: u8, flags: &mut StatusFlags) { 949 flags.set_negative(value.bit(7)).set_overflow(value.bit(6)).set_zero(accumulator & value == 0); 950} 951 952// BIT 953macro_rules! impl_bit_test { 954 ($name:ident, $addressing_mode:tt) => { 955 impl_read_fn!($name, $addressing_mode, |operand, registers| { 956 bit_test(registers.accumulator, operand, &mut registers.status); 957 }); 958 }; 959} 960 961impl_bit_test!(bit_zero_page, zero_page); 962impl_bit_test!(bit_absolute, absolute); 963 964fn compare(register: u8, value: u8, flags: &mut StatusFlags) { 965 flags 966 .set_negative(register.wrapping_sub(value).bit(7)) 967 .set_zero(register == value) 968 .set_carry(register >= value); 969} 970 971// CMP, CPX, CPY 972macro_rules! impl_compare { 973 ($name:ident, $register:ident, $addressing_mode:tt) => { 974 impl_read_fn!($name, $addressing_mode, |operand, registers| { 975 compare(registers.$register, operand, &mut registers.status); 976 }); 977 }; 978} 979 980impl_compare!(cmp_immediate, accumulator, immediate); 981impl_compare!(cmp_zero_page, accumulator, zero_page); 982impl_compare!(cmp_zero_page_x, accumulator, zero_page_x); 983impl_compare!(cmp_absolute, accumulator, absolute); 984impl_compare!(cmp_absolute_x, accumulator, absolute_x); 985impl_compare!(cmp_absolute_y, accumulator, absolute_y); 986impl_compare!(cmp_indirect_x, accumulator, indirect_x); 987impl_compare!(cmp_indirect_y, accumulator, indirect_y); 988 989impl_compare!(cpx_immediate, x, immediate); 990impl_compare!(cpx_zero_page, x, zero_page); 991impl_compare!(cpx_absolute, x, absolute); 992 993impl_compare!(cpy_immediate, y, immediate); 994impl_compare!(cpy_zero_page, y, zero_page); 995impl_compare!(cpy_absolute, y, absolute); 996 997fn xor(accumulator: u8, value: u8, flags: &mut StatusFlags) -> u8 { 998 let result = accumulator ^ value; 999 flags.set_negative(result.bit(7)).set_zero(result == 0); 1000 result 1001} 1002 1003// EOR 1004macro_rules! impl_xor { 1005 ($name:ident, $addressing_mode:tt) => { 1006 impl_read_fn!($name, $addressing_mode, |operand, registers| { 1007 registers.accumulator = xor(registers.accumulator, operand, &mut registers.status); 1008 }); 1009 }; 1010} 1011 1012impl_xor!(eor_immediate, immediate); 1013impl_xor!(eor_zero_page, zero_page); 1014impl_xor!(eor_zero_page_x, zero_page_x); 1015impl_xor!(eor_absolute, absolute); 1016impl_xor!(eor_absolute_x, absolute_x); 1017impl_xor!(eor_absolute_y, absolute_y); 1018impl_xor!(eor_indirect_x, indirect_x); 1019impl_xor!(eor_indirect_y, indirect_y); 1020 1021fn or(accumulator: u8, value: u8, flags: &mut StatusFlags) -> u8 { 1022 let result = accumulator | value; 1023 flags.set_negative(result.bit(7)).set_zero(result == 0); 1024 result 1025} 1026 1027// ORA 1028macro_rules! impl_or { 1029 ($name:ident, $addressing_mode:tt) => { 1030 impl_read_fn!($name, $addressing_mode, |operand, registers| { 1031 registers.accumulator = or(registers.accumulator, operand, &mut registers.status); 1032 }); 1033 }; 1034} 1035 1036impl_or!(ora_immediate, immediate); 1037impl_or!(ora_zero_page, zero_page); 1038impl_or!(ora_zero_page_x, zero_page_x); 1039impl_or!(ora_absolute, absolute); 1040impl_or!(ora_absolute_x, absolute_x); 1041impl_or!(ora_absolute_y, absolute_y); 1042impl_or!(ora_indirect_x, indirect_x); 1043impl_or!(ora_indirect_y, indirect_y); 1044 1045fn subtract(accumulator: u8, value: u8, flags: &mut StatusFlags) -> u8 { 1046 // Carry flag is inverted in subtraction 1047 let existing_borrow = u8::from(!flags.carry); 1048 1049 let (result, borrowed1) = accumulator.overflowing_sub(value); 1050 let (result, borrowed2) = result.overflowing_sub(existing_borrow); 1051 let borrowed = borrowed1 || borrowed2; 1052 1053 let bit_6_borrowed = accumulator & 0x7F < (value & 0x7F) + existing_borrow; 1054 let overflow = borrowed ^ bit_6_borrowed; 1055 1056 flags 1057 .set_negative(result.bit(7)) 1058 .set_overflow(overflow) 1059 .set_zero(result == 0) 1060 .set_carry(!borrowed); 1061 1062 result 1063} 1064 1065fn subtract_bcd(accumulator: u8, value: u8, flags: &mut StatusFlags) -> u8 { 1066 // Formulas from http://www.6502.org/tutorials/decimal_mode.html#A which correctly handle 1067 // invalid values and undocumented behaviors 1068 1069 let existing_borrow: u8 = (!flags.carry).into(); 1070 1071 let mut al = u16::from(accumulator & 0x0F) 1072 .wrapping_sub(u16::from(value & 0x0F)) 1073 .wrapping_sub(u16::from(existing_borrow)); 1074 if al.sign_bit() { 1075 al = (al.wrapping_sub(0x06) & 0x0F).wrapping_sub(0x10); 1076 } 1077 1078 let mut a = 1079 u16::from(accumulator & 0xF0).wrapping_sub(u16::from(value & 0xF0)).wrapping_add(al); 1080 if a.sign_bit() { 1081 a = a.wrapping_sub(0x60); 1082 } 1083 1084 // Carry and overflow flags are set based on binary arithmetic 1085 let borrow = u16::from(accumulator) < u16::from(value) + u16::from(existing_borrow); 1086 let bit_6_borrow = accumulator & 0x7F < (value & 0x7F) + existing_borrow; 1087 let overflow = bit_6_borrow != borrow; 1088 1089 let result = a as u8; 1090 1091 // Z and N flags are set based on binary arithmetic 1092 let binary_result = accumulator.wrapping_sub(value).wrapping_sub(existing_borrow); 1093 1094 flags.zero = binary_result == 0; 1095 flags.negative = binary_result.sign_bit(); 1096 flags.carry = !borrow; 1097 flags.overflow = overflow; 1098 1099 result 1100} 1101 1102// SBC 1103macro_rules! impl_subtract_with_carry { 1104 ($name:ident, $addressing_mode:tt) => { 1105 impl_read_fn!($name, $addressing_mode, |operand, registers| { 1106 registers.accumulator = if registers.in_decimal_mode() { 1107 subtract_bcd(registers.accumulator, operand, &mut registers.status) 1108 } else { 1109 subtract(registers.accumulator, operand, &mut registers.status) 1110 }; 1111 }); 1112 }; 1113} 1114 1115impl_subtract_with_carry!(sbc_immediate, immediate); 1116impl_subtract_with_carry!(sbc_zero_page, zero_page); 1117impl_subtract_with_carry!(sbc_zero_page_x, zero_page_x); 1118impl_subtract_with_carry!(sbc_absolute, absolute); 1119impl_subtract_with_carry!(sbc_absolute_x, absolute_x); 1120impl_subtract_with_carry!(sbc_absolute_y, absolute_y); 1121impl_subtract_with_carry!(sbc_indirect_x, indirect_x); 1122impl_subtract_with_carry!(sbc_indirect_y, indirect_y); 1123 1124fn shift_left(value: u8, flags: &mut StatusFlags) -> u8 { 1125 let shifted = value << 1; 1126 flags.set_carry(value.bit(7)).set_negative(shifted.bit(7)).set_zero(shifted == 0); 1127 shifted 1128} 1129 1130// ASL 1131macro_rules! impl_shift_left { 1132 ($name:ident, $addressing_mode:tt) => { 1133 impl_modify_fn!($name, $addressing_mode, |operand, registers| { 1134 shift_left(operand, &mut registers.status) 1135 }); 1136 }; 1137} 1138 1139impl_shift_left!(asl_accumulator, accumulator); 1140impl_shift_left!(asl_zero_page, zero_page); 1141impl_shift_left!(asl_zero_page_x, zero_page_x); 1142impl_shift_left!(asl_absolute, absolute); 1143impl_shift_left!(asl_absolute_x, absolute_x); 1144 1145fn decrement(value: u8, flags: &mut StatusFlags) -> u8 { 1146 let decremented = value.wrapping_sub(1); 1147 flags.set_negative(decremented.bit(7)).set_zero(decremented == 0); 1148 decremented 1149} 1150 1151// DEC 1152macro_rules! impl_decrement { 1153 ($name:ident, $addressing_mode:tt) => { 1154 impl_modify_fn!($name, $addressing_mode, |operand, registers| { 1155 decrement(operand, &mut registers.status) 1156 }); 1157 }; 1158} 1159 1160impl_decrement!(dec_zero_page, zero_page); 1161impl_decrement!(dec_zero_page_x, zero_page_x); 1162impl_decrement!(dec_absolute, absolute); 1163impl_decrement!(dec_absolute_x, absolute_x); 1164 1165fn increment(value: u8, flags: &mut StatusFlags) -> u8 { 1166 let incremented = value.wrapping_add(1); 1167 flags.set_negative(incremented.bit(7)).set_zero(incremented == 0); 1168 incremented 1169} 1170 1171// INC 1172macro_rules! impl_increment { 1173 ($name:ident, $addressing_mode:tt) => { 1174 impl_modify_fn!($name, $addressing_mode, |operand, registers| { 1175 increment(operand, &mut registers.status) 1176 }); 1177 }; 1178} 1179 1180impl_increment!(inc_zero_page, zero_page); 1181impl_increment!(inc_zero_page_x, zero_page_x); 1182impl_increment!(inc_absolute, absolute); 1183impl_increment!(inc_absolute_x, absolute_x); 1184 1185fn logical_shift_right(value: u8, flags: &mut StatusFlags) -> u8 { 1186 let shifted = value >> 1; 1187 flags.set_carry(value.bit(0)).set_negative(false).set_zero(shifted == 0); 1188 shifted 1189} 1190 1191// LSR 1192macro_rules! impl_logical_shift_right { 1193 ($name:ident, $addressing_mode:tt) => { 1194 impl_modify_fn!($name, $addressing_mode, |operand, registers| { 1195 logical_shift_right(operand, &mut registers.status) 1196 }); 1197 }; 1198} 1199 1200impl_logical_shift_right!(lsr_accumulator, accumulator); 1201impl_logical_shift_right!(lsr_zero_page, zero_page); 1202impl_logical_shift_right!(lsr_zero_page_x, zero_page_x); 1203impl_logical_shift_right!(lsr_absolute, absolute); 1204impl_logical_shift_right!(lsr_absolute_x, absolute_x); 1205 1206fn rotate_left(value: u8, flags: &mut StatusFlags) -> u8 { 1207 let rotated = (value << 1) | u8::from(flags.carry); 1208 flags.set_carry(value.bit(7)).set_negative(rotated.bit(7)).set_zero(rotated == 0); 1209 rotated 1210} 1211 1212// ROL 1213macro_rules! impl_rotate_left { 1214 ($name:ident, $addressing_mode:tt) => { 1215 impl_modify_fn!($name, $addressing_mode, |operand, registers| { 1216 rotate_left(operand, &mut registers.status) 1217 }); 1218 }; 1219} 1220 1221impl_rotate_left!(rol_accumulator, accumulator); 1222impl_rotate_left!(rol_zero_page, zero_page); 1223impl_rotate_left!(rol_zero_page_x, zero_page_x); 1224impl_rotate_left!(rol_absolute, absolute); 1225impl_rotate_left!(rol_absolute_x, absolute_x); 1226 1227fn rotate_right(value: u8, flags: &mut StatusFlags) -> u8 { 1228 let rotated = (value >> 1) | (u8::from(flags.carry) << 7); 1229 flags.set_carry(value.bit(0)).set_negative(rotated.bit(7)).set_zero(rotated == 0); 1230 rotated 1231} 1232 1233// ROR 1234macro_rules! impl_rotate_right { 1235 ($name:ident, $addressing_mode:tt) => { 1236 impl_modify_fn!($name, $addressing_mode, |operand, registers| { 1237 rotate_right(operand, &mut registers.status) 1238 }); 1239 }; 1240} 1241 1242impl_rotate_right!(ror_accumulator, accumulator); 1243impl_rotate_right!(ror_zero_page, zero_page); 1244impl_rotate_right!(ror_zero_page_x, zero_page_x); 1245impl_rotate_right!(ror_absolute, absolute); 1246impl_rotate_right!(ror_absolute_x, absolute_x); 1247 1248// SLO (unofficial; combination of ASL and ORA) 1249macro_rules! impl_shift_left_or { 1250 ($name:ident, $addressing_mode:tt) => { 1251 impl_modify_fn!($name, $addressing_mode, |operand, registers| { 1252 let shifted = shift_left(operand, &mut registers.status); 1253 registers.accumulator = or(registers.accumulator, shifted, &mut registers.status); 1254 shifted 1255 }); 1256 }; 1257} 1258 1259impl_shift_left_or!(slo_zero_page, zero_page); 1260impl_shift_left_or!(slo_zero_page_x, zero_page_x); 1261impl_shift_left_or!(slo_absolute, absolute); 1262impl_shift_left_or!(slo_absolute_x, absolute_x); 1263impl_shift_left_or!(slo_absolute_y, absolute_y); 1264impl_shift_left_or!(slo_indirect_x, indirect_x); 1265impl_shift_left_or!(slo_indirect_y, indirect_y); 1266 1267// RLA (unofficial; combination of ROL and AND) 1268macro_rules! impl_rotate_left_and { 1269 ($name:ident, $addressing_mode:tt) => { 1270 impl_modify_fn!($name, $addressing_mode, |operand, registers| { 1271 let rotated = rotate_left(operand, &mut registers.status); 1272 registers.accumulator = and(registers.accumulator, rotated, &mut registers.status); 1273 rotated 1274 }); 1275 }; 1276} 1277 1278impl_rotate_left_and!(rla_zero_page, zero_page); 1279impl_rotate_left_and!(rla_zero_page_x, zero_page_x); 1280impl_rotate_left_and!(rla_absolute, absolute); 1281impl_rotate_left_and!(rla_absolute_x, absolute_x); 1282impl_rotate_left_and!(rla_absolute_y, absolute_y); 1283impl_rotate_left_and!(rla_indirect_x, indirect_x); 1284impl_rotate_left_and!(rla_indirect_y, indirect_y); 1285 1286// SRE (unofficial; combination of LSR and EOR) 1287macro_rules! impl_shift_right_xor { 1288 ($name:ident, $addressing_mode:tt) => { 1289 impl_modify_fn!($name, $addressing_mode, |operand, registers| { 1290 let shifted = logical_shift_right(operand, &mut registers.status); 1291 registers.accumulator = xor(registers.accumulator, shifted, &mut registers.status); 1292 shifted 1293 }); 1294 }; 1295} 1296 1297impl_shift_right_xor!(sre_zero_page, zero_page); 1298impl_shift_right_xor!(sre_zero_page_x, zero_page_x); 1299impl_shift_right_xor!(sre_absolute, absolute); 1300impl_shift_right_xor!(sre_absolute_x, absolute_x); 1301impl_shift_right_xor!(sre_absolute_y, absolute_y); 1302impl_shift_right_xor!(sre_indirect_x, indirect_x); 1303impl_shift_right_xor!(sre_indirect_y, indirect_y); 1304 1305// RRA (unofficial; combination of ROR and ADC) 1306macro_rules! impl_rotate_right_add { 1307 ($name:ident, $addressing_mode:tt) => { 1308 impl_modify_fn!($name, $addressing_mode, |operand, registers| { 1309 let rotated = rotate_right(operand, &mut registers.status); 1310 registers.accumulator = if registers.in_decimal_mode() { 1311 add_bcd(registers.accumulator, rotated, &mut registers.status) 1312 } else { 1313 add(registers.accumulator, rotated, &mut registers.status) 1314 }; 1315 rotated 1316 }); 1317 }; 1318} 1319 1320impl_rotate_right_add!(rra_zero_page, zero_page); 1321impl_rotate_right_add!(rra_zero_page_x, zero_page_x); 1322impl_rotate_right_add!(rra_absolute, absolute); 1323impl_rotate_right_add!(rra_absolute_x, absolute_x); 1324impl_rotate_right_add!(rra_absolute_y, absolute_y); 1325impl_rotate_right_add!(rra_indirect_x, indirect_x); 1326impl_rotate_right_add!(rra_indirect_y, indirect_y); 1327 1328// DCP (unofficial; combination of DEC and CMP) 1329macro_rules! impl_decrement_compare { 1330 ($name:ident, $addressing_mode:tt) => { 1331 impl_modify_fn!($name, $addressing_mode, |operand, registers| { 1332 let decremented = decrement(operand, &mut registers.status); 1333 compare(registers.accumulator, decremented, &mut registers.status); 1334 decremented 1335 }); 1336 }; 1337} 1338 1339impl_decrement_compare!(dcp_zero_page, zero_page); 1340impl_decrement_compare!(dcp_zero_page_x, zero_page_x); 1341impl_decrement_compare!(dcp_absolute, absolute); 1342impl_decrement_compare!(dcp_absolute_x, absolute_x); 1343impl_decrement_compare!(dcp_absolute_y, absolute_y); 1344impl_decrement_compare!(dcp_indirect_x, indirect_x); 1345impl_decrement_compare!(dcp_indirect_y, indirect_y); 1346 1347// ISC (unofficial; combination of INC and SBC) 1348macro_rules! impl_increment_subtract { 1349 ($name:ident, $addressing_mode:tt) => { 1350 impl_modify_fn!($name, $addressing_mode, |operand, registers| { 1351 let incremented = increment(operand, &mut registers.status); 1352 registers.accumulator = if registers.in_decimal_mode() { 1353 subtract_bcd(registers.accumulator, incremented, &mut registers.status) 1354 } else { 1355 subtract(registers.accumulator, incremented, &mut registers.status) 1356 }; 1357 incremented 1358 }); 1359 }; 1360} 1361 1362impl_increment_subtract!(isc_zero_page, zero_page); 1363impl_increment_subtract!(isc_zero_page_x, zero_page_x); 1364impl_increment_subtract!(isc_absolute, absolute); 1365impl_increment_subtract!(isc_absolute_x, absolute_x); 1366impl_increment_subtract!(isc_absolute_y, absolute_y); 1367impl_increment_subtract!(isc_indirect_x, indirect_x); 1368impl_increment_subtract!(isc_indirect_y, indirect_y); 1369 1370// CLC, CLD, CLI, CLV, SEC, SED, SEI 1371macro_rules! impl_set_status_flag { 1372 ($name:ident, $flag:ident = $value:expr) => { 1373 impl_registers_only_fn!($name, |registers| { 1374 registers.status.$flag = $value; 1375 }); 1376 }; 1377} 1378 1379impl_set_status_flag!(clc, carry = false); 1380impl_set_status_flag!(cld, decimal = false); 1381impl_set_status_flag!(cli, interrupt_disable = false); 1382impl_set_status_flag!(clv, overflow = false); 1383impl_set_status_flag!(sec, carry = true); 1384impl_set_status_flag!(sed, decimal = true); 1385impl_set_status_flag!(sei, interrupt_disable = true); 1386 1387// INX, INY 1388macro_rules! impl_increment_register { 1389 ($name:ident, $register:ident) => { 1390 impl_registers_only_fn!($name, |registers| { 1391 let value = registers.$register.wrapping_add(1); 1392 registers.$register = value; 1393 registers.status.set_negative(value.bit(7)).set_zero(value == 0); 1394 }); 1395 }; 1396} 1397 1398impl_increment_register!(inx, x); 1399impl_increment_register!(iny, y); 1400 1401// DEX, DEY 1402macro_rules! impl_decrement_register { 1403 ($name:ident, $register:ident) => { 1404 impl_registers_only_fn!($name, |registers| { 1405 let value = registers.$register.wrapping_sub(1); 1406 registers.$register = value; 1407 registers.status.set_negative(value.bit(7)).set_zero(value == 0); 1408 }); 1409 }; 1410} 1411 1412impl_decrement_register!(dex, x); 1413impl_decrement_register!(dey, y); 1414 1415macro_rules! set_transfer_flags { 1416 (sp, $registers:expr, $value:expr) => {}; 1417 ($to:ident, $registers:expr, $value:expr) => { 1418 $registers.status.set_negative($value.bit(7)).set_zero($value == 0); 1419 }; 1420} 1421 1422// TAX, TAY, TSX, TXA, TXS, TYA 1423macro_rules! impl_register_transfer { 1424 ($name:ident, $from:ident -> $to:ident) => { 1425 impl_registers_only_fn!($name, |registers| { 1426 let value = registers.$from; 1427 registers.$to = value; 1428 set_transfer_flags!($to, registers, value); 1429 }); 1430 }; 1431} 1432 1433impl_register_transfer!(tax, accumulator -> x); 1434impl_register_transfer!(tay, accumulator -> y); 1435impl_register_transfer!(tsx, sp -> x); 1436impl_register_transfer!(txa, x -> accumulator); 1437impl_register_transfer!(txs, x -> sp); 1438impl_register_transfer!(tya, y -> accumulator); 1439 1440// NOP 1441impl_registers_only_fn!(nop, |_registers| {}); 1442 1443// BCC, BCS, BEQ, BMI, BNE, BPL, BVC, BVS 1444macro_rules! impl_branch { 1445 ($name:ident, $flag:ident == $flag_value:expr) => { 1446 fn $name<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) { 1447 match cpu.state.cycle { 1448 0 => { 1449 poll_interrupt_lines(cpu, bus); 1450 1451 cpu.state.operand_first_byte = fetch_operand(cpu, bus); 1452 1453 if cpu.registers.status.$flag != $flag_value { 1454 cpu.state.instruction_complete = true; 1455 } 1456 } 1457 1 => { 1458 bus.read(cpu.registers.pc); 1459 1460 let offset = cpu.state.operand_first_byte as i8; 1461 let pc = cpu.registers.pc.wrapping_add_signed(offset.into()); 1462 1463 if cpu.registers.pc & 0xFF00 == pc & 0xFF00 { 1464 cpu.registers.pc = pc; 1465 cpu.state.instruction_complete = true; 1466 } 1467 } 1468 2 => { 1469 final_cycle(cpu, bus); 1470 1471 let offset = cpu.state.operand_first_byte as i8; 1472 let pc = cpu.registers.pc.wrapping_add_signed(offset.into()); 1473 1474 bus.read((cpu.registers.pc & 0xFF00) | (pc & 0x00FF)); 1475 1476 cpu.registers.pc = pc; 1477 } 1478 _ => invalid_cycle!(cpu), 1479 } 1480 } 1481 }; 1482} 1483 1484impl_branch!(bcc, carry == false); 1485impl_branch!(bcs, carry == true); 1486impl_branch!(beq, zero == true); 1487impl_branch!(bmi, negative == true); 1488impl_branch!(bne, zero == false); 1489impl_branch!(bpl, negative == false); 1490impl_branch!(bvc, overflow == false); 1491impl_branch!(bvs, overflow == true); 1492 1493// JMP 1494fn jmp_absolute<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) { 1495 match cpu.state.cycle { 1496 0 => { 1497 cpu.state.operand_first_byte = fetch_operand(cpu, bus); 1498 } 1499 1 => { 1500 final_cycle(cpu, bus); 1501 1502 let address_msb = bus.read(cpu.registers.pc); 1503 cpu.registers.pc = u16::from_le_bytes([cpu.state.operand_first_byte, address_msb]); 1504 } 1505 _ => invalid_cycle!(cpu), 1506 } 1507} 1508 1509// JMP 1510fn jmp_indirect<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) { 1511 match cpu.state.cycle { 1512 0 => { 1513 cpu.state.operand_first_byte = fetch_operand(cpu, bus); 1514 } 1515 1 => { 1516 cpu.state.operand_second_byte = fetch_operand(cpu, bus); 1517 } 1518 2 => { 1519 let address = 1520 u16::from_le_bytes([cpu.state.operand_first_byte, cpu.state.operand_second_byte]); 1521 cpu.state.target_first_byte = bus.read(address); 1522 } 1523 3 => { 1524 final_cycle(cpu, bus); 1525 1526 let address_lsb = cpu.state.operand_first_byte.wrapping_add(1); 1527 let address = u16::from_le_bytes([address_lsb, cpu.state.operand_second_byte]); 1528 let pc_msb = bus.read(address); 1529 1530 cpu.registers.pc = u16::from_le_bytes([cpu.state.target_first_byte, pc_msb]); 1531 } 1532 _ => invalid_cycle!(cpu), 1533 } 1534} 1535 1536macro_rules! read_register_for_push { 1537 (accumulator, $registers:expr) => { 1538 $registers.accumulator 1539 }; 1540 (p, $registers:expr) => { 1541 $registers.status.to_byte(StatusReadContext::PushStack) 1542 }; 1543} 1544 1545// PHA, PHP 1546macro_rules! impl_push_stack { 1547 ($name:ident, $register:tt) => { 1548 fn $name<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) { 1549 match cpu.state.cycle { 1550 0 => { 1551 bus.read(cpu.registers.pc); 1552 } 1553 1 => { 1554 final_cycle(cpu, bus); 1555 1556 let address = u16::from_be_bytes([0x01, cpu.registers.sp]); 1557 let value = read_register_for_push!($register, cpu.registers); 1558 bus.write(address, value); 1559 cpu.registers.sp = cpu.registers.sp.wrapping_sub(1); 1560 } 1561 _ => invalid_cycle!(cpu), 1562 } 1563 } 1564 }; 1565} 1566 1567impl_push_stack!(pha, accumulator); 1568impl_push_stack!(php, p); 1569 1570macro_rules! write_register_for_pull { 1571 (accumulator, $registers:expr, $value:expr) => {{ 1572 let value = $value; 1573 $registers.accumulator = value; 1574 $registers.status.set_negative(value.bit(7)).set_zero(value == 0); 1575 }}; 1576 (p, $registers:expr, $value:expr) => { 1577 $registers.status = StatusFlags::from_byte($value); 1578 }; 1579} 1580 1581// PLA, PLP 1582macro_rules! impl_pull_stack { 1583 ($name:ident, $register:tt) => { 1584 fn $name<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) { 1585 match cpu.state.cycle { 1586 0 => { 1587 bus.read(cpu.registers.pc); 1588 } 1589 1 => { 1590 bus.read(u16::from_be_bytes([0x01, cpu.registers.sp])); 1591 } 1592 2 => { 1593 final_cycle(cpu, bus); 1594 1595 cpu.registers.sp = cpu.registers.sp.wrapping_add(1); 1596 let value = bus.read(u16::from_be_bytes([0x01, cpu.registers.sp])); 1597 write_register_for_pull!($register, cpu.registers, value); 1598 } 1599 _ => invalid_cycle!(cpu), 1600 } 1601 } 1602 }; 1603} 1604 1605impl_pull_stack!(pla, accumulator); 1606impl_pull_stack!(plp, p); 1607 1608#[inline] 1609fn push_pc_msb<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) { 1610 let address = u16::from_be_bytes([0x01, cpu.registers.sp]); 1611 bus.write(address, (cpu.registers.pc >> 8) as u8); 1612 cpu.registers.sp = cpu.registers.sp.wrapping_sub(1); 1613} 1614 1615#[inline] 1616fn push_pc_lsb<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) { 1617 let address = u16::from_be_bytes([0x01, cpu.registers.sp]); 1618 bus.write(address, cpu.registers.pc as u8); 1619 cpu.registers.sp = cpu.registers.sp.wrapping_sub(1); 1620} 1621 1622#[inline] 1623fn pull_pc_lsb<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) { 1624 cpu.registers.sp = cpu.registers.sp.wrapping_add(1); 1625 cpu.registers.pc = bus.read(u16::from_be_bytes([0x01, cpu.registers.sp])).into(); 1626} 1627 1628#[inline] 1629fn pull_pc_msb<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) { 1630 cpu.registers.sp = cpu.registers.sp.wrapping_add(1); 1631 let pc_msb = bus.read(u16::from_be_bytes([0x01, cpu.registers.sp])); 1632 cpu.registers.pc |= u16::from(pc_msb) << 8; 1633} 1634 1635// JSR (jump to subroutine) 1636fn jsr<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) { 1637 match cpu.state.cycle { 1638 0 => { 1639 cpu.state.operand_first_byte = fetch_operand(cpu, bus); 1640 } 1641 1 => { 1642 // Spurious stack read 1643 bus.read(u16::from_be_bytes([0x01, cpu.registers.sp])); 1644 } 1645 2 => { 1646 push_pc_msb(cpu, bus); 1647 } 1648 3 => { 1649 push_pc_lsb(cpu, bus); 1650 } 1651 4 => { 1652 final_cycle(cpu, bus); 1653 1654 let address_msb = bus.read(cpu.registers.pc); 1655 cpu.registers.pc = u16::from_le_bytes([cpu.state.operand_first_byte, address_msb]); 1656 } 1657 _ => invalid_cycle!(cpu), 1658 } 1659} 1660 1661// RTS (return from subroutine) 1662fn rts<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) { 1663 match cpu.state.cycle { 1664 0 => { 1665 // Spurious operand read 1666 bus.read(cpu.registers.pc); 1667 } 1668 1 => { 1669 // Spurious stack read 1670 bus.read(u16::from_be_bytes([0x01, cpu.registers.sp])); 1671 } 1672 2 => { 1673 pull_pc_lsb(cpu, bus); 1674 } 1675 3 => { 1676 pull_pc_msb(cpu, bus); 1677 } 1678 4 => { 1679 final_cycle(cpu, bus); 1680 1681 // Fetch operand and increment PC, ignore fetch result 1682 fetch_operand(cpu, bus); 1683 } 1684 _ => invalid_cycle!(cpu), 1685 } 1686} 1687 1688// RTI (return from interrupt) 1689fn rti<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) { 1690 match cpu.state.cycle { 1691 0 => { 1692 // Spurious operand read 1693 bus.read(cpu.registers.pc); 1694 } 1695 1 => { 1696 // Spurious stack read 1697 bus.read(u16::from_be_bytes([0x01, cpu.registers.sp])); 1698 } 1699 2 => { 1700 cpu.registers.sp = cpu.registers.sp.wrapping_add(1); 1701 let value = bus.read(u16::from_be_bytes([0x01, cpu.registers.sp])); 1702 cpu.registers.status = StatusFlags::from_byte(value); 1703 } 1704 3 => { 1705 pull_pc_lsb(cpu, bus); 1706 } 1707 4 => { 1708 final_cycle(cpu, bus); 1709 1710 pull_pc_msb(cpu, bus); 1711 } 1712 _ => invalid_cycle!(cpu), 1713 } 1714} 1715 1716#[inline] 1717fn interrupt_push_status<B: BusInterface>( 1718 cpu: &mut Mos6502, 1719 bus: &mut B, 1720 read_ctx: StatusReadContext, 1721) { 1722 let stack_address = u16::from_be_bytes([0x01, cpu.registers.sp]); 1723 bus.write(stack_address, cpu.registers.status.to_byte(read_ctx)); 1724 cpu.registers.sp = cpu.registers.sp.wrapping_sub(1); 1725 1726 cpu.state.interrupt_vector = if bus.nmi() { 1727 bus.acknowledge_nmi(); 1728 NMI_VECTOR 1729 } else { 1730 IRQ_VECTOR 1731 }; 1732} 1733 1734#[inline] 1735fn interrupt_pull_pc_lsb<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) { 1736 cpu.registers.pc = bus.read(cpu.state.interrupt_vector).into(); 1737 cpu.registers.status.interrupt_disable = true; 1738} 1739 1740#[inline] 1741fn interrupt_pull_pc_msb<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) { 1742 let pc_msb = bus.read(cpu.state.interrupt_vector + 1); 1743 cpu.registers.pc |= u16::from(pc_msb) << 8; 1744} 1745 1746// Hardware interrupt servicing routine + BRK (force interrupt) 1747fn interrupt_service_routine<const BRK: bool>(cpu: &mut Mos6502, bus: &mut impl BusInterface) { 1748 match cpu.state.cycle { 1749 0 => { 1750 if BRK { 1751 fetch_operand(cpu, bus); 1752 } else { 1753 // Spurious operand read 1754 bus.read(cpu.registers.pc); 1755 } 1756 } 1757 1 => { 1758 push_pc_msb(cpu, bus); 1759 } 1760 2 => { 1761 push_pc_lsb(cpu, bus); 1762 } 1763 3 => { 1764 let ctx = if BRK { 1765 StatusReadContext::Brk 1766 } else { 1767 StatusReadContext::HardwareInterruptHandler 1768 }; 1769 interrupt_push_status(cpu, bus, ctx); 1770 } 1771 4 => { 1772 interrupt_pull_pc_lsb(cpu, bus); 1773 } 1774 5 => { 1775 // Interrupt service routine does not poll interrupt lines at end of "instruction" 1776 cpu.state.instruction_complete = true; 1777 cpu.state.executing_interrupt = false; 1778 interrupt_pull_pc_msb(cpu, bus); 1779 } 1780 _ => invalid_cycle!(cpu), 1781 } 1782} 1783 1784fn execute_unofficial_store<B: BusInterface>( 1785 cpu: &mut Mos6502, 1786 bus: &mut B, 1787 register_value: u8, 1788 index: u8, 1789) { 1790 // This is a buggy instruction that is only implemented because CPU test ROMs test 1791 // it. 1792 // This implementation ANDs the X/Y register with the high byte of the address plus 1 1793 // and then stores that value, but only if the indexing did not overflow. 1794 1795 let address = u16::from_le_bytes([cpu.state.operand_first_byte, cpu.state.operand_second_byte]) 1796 .wrapping_add(index.into()); 1797 let (_, overflowed) = cpu.state.operand_first_byte.overflowing_add(index); 1798 1799 let value = register_value & cpu.state.operand_second_byte.wrapping_add(1); 1800 1801 if !overflowed { 1802 bus.write(address, value); 1803 } 1804} 1805 1806// SHX, SHY (unofficial) 1807macro_rules! impl_unofficial_store { 1808 ($name:ident, register: $register:ident, index: $index:ident) => { 1809 fn $name<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) { 1810 match cpu.state.cycle { 1811 0 => { 1812 cpu.state.operand_first_byte = fetch_operand(cpu, bus); 1813 } 1814 1 => { 1815 cpu.state.operand_second_byte = fetch_operand(cpu, bus); 1816 } 1817 2 => { 1818 let address_lsb = 1819 cpu.state.operand_first_byte.wrapping_add(cpu.registers.$index); 1820 let address = u16::from_le_bytes([address_lsb, cpu.state.operand_second_byte]); 1821 bus.read(address); 1822 } 1823 3 => { 1824 final_cycle(cpu, bus); 1825 1826 let value = cpu.registers.$register; 1827 let index = cpu.registers.$index; 1828 execute_unofficial_store(cpu, bus, value, index); 1829 } 1830 _ => invalid_cycle!(cpu), 1831 } 1832 } 1833 }; 1834} 1835 1836impl_unofficial_store!(shy, register: y, index: x); 1837impl_unofficial_store!(shx, register: x, index: y); 1838 1839// AHX, TAS (unofficial) (not actually implemented because these opcodes are very unstable) 1840fn unimplemented_unofficial_store_absolute_y<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) { 1841 match cpu.state.cycle { 1842 0 => { 1843 cpu.state.operand_first_byte = fetch_operand(cpu, bus); 1844 } 1845 1 => { 1846 cpu.state.operand_second_byte = fetch_operand(cpu, bus); 1847 } 1848 2 => { 1849 let address_lsb = cpu.state.operand_first_byte.wrapping_add(cpu.registers.y); 1850 let address = u16::from_le_bytes([address_lsb, cpu.state.operand_second_byte]); 1851 cpu.state.target_first_byte = bus.read(address); 1852 } 1853 3 => { 1854 final_cycle(cpu, bus); 1855 1856 let address_lsb = cpu.state.operand_first_byte.wrapping_add(cpu.registers.y); 1857 let address = u16::from_le_bytes([address_lsb, cpu.state.operand_second_byte]); 1858 bus.write(address, cpu.state.target_first_byte); 1859 } 1860 _ => invalid_cycle!(cpu), 1861 } 1862} 1863 1864// AHX, TAS (unofficial) (not actually implemented because these opcodes are very unstable) 1865fn unimplemented_unofficial_store_indirect_y<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) { 1866 match cpu.state.cycle { 1867 0 => { 1868 cpu.state.operand_first_byte = fetch_operand(cpu, bus); 1869 } 1870 1 => { 1871 cpu.state.target_first_byte = bus.read(cpu.state.operand_first_byte.into()); 1872 } 1873 2 => { 1874 let address = cpu.state.operand_first_byte.wrapping_add(1).into(); 1875 cpu.state.target_second_byte = bus.read(address); 1876 } 1877 3 => { 1878 let address_lsb = cpu.state.target_first_byte.wrapping_add(cpu.registers.y); 1879 let address = u16::from_le_bytes([address_lsb, cpu.state.target_second_byte]); 1880 cpu.state.indirect_byte = bus.read(address); 1881 } 1882 4 => { 1883 final_cycle(cpu, bus); 1884 1885 let address_lsb = cpu.state.target_first_byte.wrapping_add(cpu.registers.y); 1886 let address = u16::from_le_bytes([address_lsb, cpu.state.target_second_byte]); 1887 bus.write(address, cpu.state.indirect_byte); 1888 } 1889 _ => invalid_cycle!(cpu), 1890 } 1891} 1892 1893// ANC (unofficial; combination of AND and ASL) 1894impl_read_fn!(anc, immediate, |operand, registers| { 1895 // ANC performs an AND and then sets the C flag the way that ASL does 1896 registers.accumulator = and(registers.accumulator, operand, &mut registers.status); 1897 registers.status.carry = registers.accumulator.bit(7); 1898}); 1899 1900// ALR (unofficial; combination of AND and LSR) 1901impl_read_fn!(alr, immediate, |operand, registers| { 1902 // ALR simply performs an AND followed by an LSR 1903 let and_value = and(registers.accumulator, operand, &mut registers.status); 1904 registers.accumulator = logical_shift_right(and_value, &mut registers.status); 1905}); 1906 1907fn and_with_rotate_right(registers: &mut CpuRegisters, operand: u8) { 1908 // ARR is like a mix of AND, ROR, and ADC; the accumulator is set to (A & #imm) rotated, 1909 // but the flags are set differently from ROR 1910 1911 let and_value = and(registers.accumulator, operand, &mut StatusFlags::new()); 1912 registers.accumulator = (and_value >> 1) | (u8::from(registers.status.carry) << 7); 1913 1914 // The overflow flag is set as if an ADC was performed between the AND and ROR, and 1915 // the carry flag is set based on what was bit 7 prior to the rotation 1916 let overflow = registers.accumulator.bit(6) ^ registers.accumulator.bit(5); 1917 registers 1918 .status 1919 .set_negative(registers.accumulator.bit(7)) 1920 .set_overflow(overflow) 1921 .set_carry(registers.accumulator.bit(6)) 1922 .set_zero(registers.accumulator == 0); 1923} 1924 1925// ARR (unofficial; combination of AND, ROR, and ADC) 1926impl_read_fn!(arr, immediate, |operand, registers| { 1927 and_with_rotate_right(registers, operand); 1928}); 1929 1930// LAX (unofficial) 1931macro_rules! impl_load_transfer_ax { 1932 ($name:ident, $addressing_mode:tt) => { 1933 impl_read_fn!($name, $addressing_mode, |operand, registers| { 1934 // LAX simply performs LDA and LDX simultaneously 1935 1936 registers.accumulator = operand; 1937 registers.x = operand; 1938 1939 registers.status.set_negative(operand.bit(7)).set_zero(operand == 0); 1940 }); 1941 }; 1942} 1943 1944impl_load_transfer_ax!(lax_immediate, immediate); 1945impl_load_transfer_ax!(lax_zero_page, zero_page); 1946impl_load_transfer_ax!(lax_zero_page_y, zero_page_y); 1947impl_load_transfer_ax!(lax_absolute, absolute); 1948impl_load_transfer_ax!(lax_absolute_y, absolute_y); 1949impl_load_transfer_ax!(lax_indirect_x, indirect_x); 1950impl_load_transfer_ax!(lax_indirect_y, indirect_y); 1951 1952// XAA (unofficial; loads X & <imm> into A) 1953impl_read_fn!(xaa, immediate, |operand, registers| { 1954 registers.accumulator = registers.x & operand; 1955 registers 1956 .status 1957 .set_negative(registers.accumulator.bit(7)) 1958 .set_zero(registers.accumulator == 0); 1959}); 1960 1961// AXS (unofficial) 1962impl_read_fn!(axs, immediate, |operand, registers| { 1963 // AXS sets X to (A&X) - #imm, while ignoring the current carry flag. The flags 1964 // are set not from the subtraction operation but from a CMP between (A&X) and #imm 1965 1966 let ax = registers.accumulator & registers.x; 1967 let mut flags = StatusFlags { 1968 // Set carry to true because SBC inverts the carry flag for borrowing 1969 carry: true, 1970 ..StatusFlags::new() 1971 }; 1972 registers.x = subtract(ax, operand, &mut flags); 1973 1974 compare(ax, operand, &mut registers.status); 1975}); 1976 1977// LAS (unofficial; sets A, X, and S to S & value) 1978impl_read_fn!(las, absolute_y, |operand, registers| { 1979 let new_value = operand & registers.sp; 1980 1981 registers.accumulator = new_value; 1982 registers.x = new_value; 1983 registers.sp = new_value; 1984 1985 registers.status.set_negative(new_value.bit(7)).set_zero(new_value == 0); 1986}); 1987 1988// unofficial NOPs 1989macro_rules! impl_multi_byte_noop { 1990 ($name:ident, $addressing_mode:tt) => { 1991 impl_read_fn!($name, $addressing_mode, |_operand, _registers| {}); 1992 }; 1993} 1994 1995impl_multi_byte_noop!(nop_immediate, immediate); 1996impl_multi_byte_noop!(nop_zero_page, zero_page); 1997impl_multi_byte_noop!(nop_zero_page_x, zero_page_x); 1998impl_multi_byte_noop!(nop_absolute, absolute); 1999impl_multi_byte_noop!(nop_absolute_x, absolute_x); 2000 2001pub fn execute_cycle<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) { 2002 if cpu.state.executing_interrupt { 2003 interrupt_service_routine::<false>(cpu, bus); 2004 cpu.state.cycle += 1; 2005 return; 2006 } 2007 2008 match cpu.state.opcode { 2009 0x00 => interrupt_service_routine::<true>(cpu, bus), // BRK 2010 0x01 => ora_indirect_x(cpu, bus), 2011 0x03 => slo_indirect_x(cpu, bus), 2012 0x04 | 0x44 | 0x64 => nop_zero_page(cpu, bus), 2013 0x05 => ora_zero_page(cpu, bus), 2014 0x06 => asl_zero_page(cpu, bus), 2015 0x07 => slo_zero_page(cpu, bus), 2016 0x08 => php(cpu, bus), 2017 0x09 => ora_immediate(cpu, bus), 2018 0x0A => asl_accumulator(cpu, bus), 2019 0x0B | 0x2B => anc(cpu, bus), 2020 0x0C => nop_absolute(cpu, bus), 2021 0x0D => ora_absolute(cpu, bus), 2022 0x0E => asl_absolute(cpu, bus), 2023 0x0F => slo_absolute(cpu, bus), 2024 0x10 => bpl(cpu, bus), 2025 0x11 => ora_indirect_y(cpu, bus), 2026 0x13 => slo_indirect_y(cpu, bus), 2027 0x14 | 0x34 | 0x54 | 0x74 | 0xD4 | 0xF4 => nop_zero_page_x(cpu, bus), 2028 0x15 => ora_zero_page_x(cpu, bus), 2029 0x16 => asl_zero_page_x(cpu, bus), 2030 0x17 => slo_zero_page_x(cpu, bus), 2031 0x18 => clc(cpu, bus), 2032 0x19 => ora_absolute_y(cpu, bus), 2033 0x1A | 0x3A | 0x5A | 0x7A | 0xDA | 0xEA | 0xFA => nop(cpu, bus), 2034 0x1B => slo_absolute_y(cpu, bus), 2035 0x1C | 0x3C | 0x5C | 0x7C | 0xDC | 0xFC => nop_absolute_x(cpu, bus), 2036 0x1D => ora_absolute_x(cpu, bus), 2037 0x1E => asl_absolute_x(cpu, bus), 2038 0x1F => slo_absolute_x(cpu, bus), 2039 0x20 => jsr(cpu, bus), 2040 0x21 => and_indirect_x(cpu, bus), 2041 0x23 => rla_indirect_x(cpu, bus), 2042 0x24 => bit_zero_page(cpu, bus), 2043 0x25 => and_zero_page(cpu, bus), 2044 0x26 => rol_zero_page(cpu, bus), 2045 0x27 => rla_zero_page(cpu, bus), 2046 0x28 => plp(cpu, bus), 2047 0x29 => and_immediate(cpu, bus), 2048 0x2A => rol_accumulator(cpu, bus), 2049 0x2C => bit_absolute(cpu, bus), 2050 0x2D => and_absolute(cpu, bus), 2051 0x2E => rol_absolute(cpu, bus), 2052 0x2F => rla_absolute(cpu, bus), 2053 0x30 => bmi(cpu, bus), 2054 0x31 => and_indirect_y(cpu, bus), 2055 0x33 => rla_indirect_y(cpu, bus), 2056 0x35 => and_zero_page_x(cpu, bus), 2057 0x36 => rol_zero_page_x(cpu, bus), 2058 0x37 => rla_zero_page_x(cpu, bus), 2059 0x38 => sec(cpu, bus), 2060 0x39 => and_absolute_y(cpu, bus), 2061 0x3B => rla_absolute_y(cpu, bus), 2062 0x3D => and_absolute_x(cpu, bus), 2063 0x3E => rol_absolute_x(cpu, bus), 2064 0x3F => rla_absolute_x(cpu, bus), 2065 0x40 => rti(cpu, bus), 2066 0x41 => eor_indirect_x(cpu, bus), 2067 0x43 => sre_indirect_x(cpu, bus), 2068 0x45 => eor_zero_page(cpu, bus), 2069 0x46 => lsr_zero_page(cpu, bus), 2070 0x47 => sre_zero_page(cpu, bus), 2071 0x48 => pha(cpu, bus), 2072 0x49 => eor_immediate(cpu, bus), 2073 0x4A => lsr_accumulator(cpu, bus), 2074 0x4B => alr(cpu, bus), 2075 0x4C => jmp_absolute(cpu, bus), 2076 0x4D => eor_absolute(cpu, bus), 2077 0x4E => lsr_absolute(cpu, bus), 2078 0x4F => sre_absolute(cpu, bus), 2079 0x50 => bvc(cpu, bus), 2080 0x51 => eor_indirect_y(cpu, bus), 2081 0x53 => sre_indirect_y(cpu, bus), 2082 0x55 => eor_zero_page_x(cpu, bus), 2083 0x56 => lsr_zero_page_x(cpu, bus), 2084 0x57 => sre_zero_page_x(cpu, bus), 2085 0x58 => cli(cpu, bus), 2086 0x59 => eor_absolute_y(cpu, bus), 2087 0x5B => sre_absolute_y(cpu, bus), 2088 0x5D => eor_absolute_x(cpu, bus), 2089 0x5E => lsr_absolute_x(cpu, bus), 2090 0x5F => sre_absolute_x(cpu, bus), 2091 0x60 => rts(cpu, bus), 2092 0x61 => adc_indirect_x(cpu, bus), 2093 0x63 => rra_indirect_x(cpu, bus), 2094 0x65 => adc_zero_page(cpu, bus), 2095 0x66 => ror_zero_page(cpu, bus), 2096 0x67 => rra_zero_page(cpu, bus), 2097 0x68 => pla(cpu, bus), 2098 0x69 => adc_immediate(cpu, bus), 2099 0x6A => ror_accumulator(cpu, bus), 2100 0x6B => arr(cpu, bus), 2101 0x6C => jmp_indirect(cpu, bus), 2102 0x6D => adc_absolute(cpu, bus), 2103 0x6E => ror_absolute(cpu, bus), 2104 0x6F => rra_absolute(cpu, bus), 2105 0x70 => bvs(cpu, bus), 2106 0x71 => adc_indirect_y(cpu, bus), 2107 0x73 => rra_indirect_y(cpu, bus), 2108 0x75 => adc_zero_page_x(cpu, bus), 2109 0x76 => ror_zero_page_x(cpu, bus), 2110 0x77 => rra_zero_page_x(cpu, bus), 2111 0x78 => sei(cpu, bus), 2112 0x79 => adc_absolute_y(cpu, bus), 2113 0x7B => rra_absolute_y(cpu, bus), 2114 0x7D => adc_absolute_x(cpu, bus), 2115 0x7E => ror_absolute_x(cpu, bus), 2116 0x7F => rra_absolute_x(cpu, bus), 2117 0x80 | 0x82 | 0x89 | 0xC2 | 0xE2 => nop_immediate(cpu, bus), 2118 0x81 => sta_indirect_x(cpu, bus), 2119 0x83 => sax_indirect_x(cpu, bus), 2120 0x84 => sty_zero_page(cpu, bus), 2121 0x85 => sta_zero_page(cpu, bus), 2122 0x86 => stx_zero_page(cpu, bus), 2123 0x87 => sax_zero_page(cpu, bus), 2124 0x88 => dey(cpu, bus), 2125 0x8A => txa(cpu, bus), 2126 0x8B => xaa(cpu, bus), 2127 0x8C => sty_absolute(cpu, bus), 2128 0x8D => sta_absolute(cpu, bus), 2129 0x8E => stx_absolute(cpu, bus), 2130 0x8F => sax_absolute(cpu, bus), 2131 0x90 => bcc(cpu, bus), 2132 0x91 => sta_indirect_y(cpu, bus), 2133 0x93 => unimplemented_unofficial_store_indirect_y(cpu, bus), 2134 0x94 => sty_zero_page_x(cpu, bus), 2135 0x95 => sta_zero_page_x(cpu, bus), 2136 0x96 => stx_zero_page_y(cpu, bus), 2137 0x97 => sax_zero_page_y(cpu, bus), 2138 0x98 => tya(cpu, bus), 2139 0x99 => sta_absolute_y(cpu, bus), 2140 0x9A => txs(cpu, bus), 2141 0x9B | 0x9F => unimplemented_unofficial_store_absolute_y(cpu, bus), 2142 0x9C => shy(cpu, bus), 2143 0x9D => sta_absolute_x(cpu, bus), 2144 0x9E => shx(cpu, bus), 2145 0xA0 => ldy_immediate(cpu, bus), 2146 0xA1 => lda_indirect_x(cpu, bus), 2147 0xA2 => ldx_immediate(cpu, bus), 2148 0xA3 => lax_indirect_x(cpu, bus), 2149 0xA4 => ldy_zero_page(cpu, bus), 2150 0xA5 => lda_zero_page(cpu, bus), 2151 0xA6 => ldx_zero_page(cpu, bus), 2152 0xA7 => lax_zero_page(cpu, bus), 2153 0xA8 => tay(cpu, bus), 2154 0xA9 => lda_immediate(cpu, bus), 2155 0xAA => tax(cpu, bus), 2156 0xAB => lax_immediate(cpu, bus), 2157 0xAC => ldy_absolute(cpu, bus), 2158 0xAD => lda_absolute(cpu, bus), 2159 0xAE => ldx_absolute(cpu, bus), 2160 0xAF => lax_absolute(cpu, bus), 2161 0xB0 => bcs(cpu, bus), 2162 0xB1 => lda_indirect_y(cpu, bus), 2163 0xB3 => lax_indirect_y(cpu, bus), 2164 0xB4 => ldy_zero_page_x(cpu, bus), 2165 0xB5 => lda_zero_page_x(cpu, bus), 2166 0xB6 => ldx_zero_page_y(cpu, bus), 2167 0xB7 => lax_zero_page_y(cpu, bus), 2168 0xB8 => clv(cpu, bus), 2169 0xB9 => lda_absolute_y(cpu, bus), 2170 0xBA => tsx(cpu, bus), 2171 0xBB => las(cpu, bus), 2172 0xBC => ldy_absolute_x(cpu, bus), 2173 0xBD => lda_absolute_x(cpu, bus), 2174 0xBE => ldx_absolute_y(cpu, bus), 2175 0xBF => lax_absolute_y(cpu, bus), 2176 0xC0 => cpy_immediate(cpu, bus), 2177 0xC1 => cmp_indirect_x(cpu, bus), 2178 0xC3 => dcp_indirect_x(cpu, bus), 2179 0xC4 => cpy_zero_page(cpu, bus), 2180 0xC5 => cmp_zero_page(cpu, bus), 2181 0xC6 => dec_zero_page(cpu, bus), 2182 0xC7 => dcp_zero_page(cpu, bus), 2183 0xC8 => iny(cpu, bus), 2184 0xC9 => cmp_immediate(cpu, bus), 2185 0xCA => dex(cpu, bus), 2186 0xCB => axs(cpu, bus), 2187 0xCC => cpy_absolute(cpu, bus), 2188 0xCD => cmp_absolute(cpu, bus), 2189 0xCE => dec_absolute(cpu, bus), 2190 0xCF => dcp_absolute(cpu, bus), 2191 0xD0 => bne(cpu, bus), 2192 0xD1 => cmp_indirect_y(cpu, bus), 2193 0xD3 => dcp_indirect_y(cpu, bus), 2194 0xD5 => cmp_zero_page_x(cpu, bus), 2195 0xD6 => dec_zero_page_x(cpu, bus), 2196 0xD7 => dcp_zero_page_x(cpu, bus), 2197 0xD8 => cld(cpu, bus), 2198 0xD9 => cmp_absolute_y(cpu, bus), 2199 0xDB => dcp_absolute_y(cpu, bus), 2200 0xDD => cmp_absolute_x(cpu, bus), 2201 0xDE => dec_absolute_x(cpu, bus), 2202 0xDF => dcp_absolute_x(cpu, bus), 2203 0xE0 => cpx_immediate(cpu, bus), 2204 0xE1 => sbc_indirect_x(cpu, bus), 2205 0xE3 => isc_indirect_x(cpu, bus), 2206 0xE4 => cpx_zero_page(cpu, bus), 2207 0xE5 => sbc_zero_page(cpu, bus), 2208 0xE6 => inc_zero_page(cpu, bus), 2209 0xE7 => isc_zero_page(cpu, bus), 2210 0xE8 => inx(cpu, bus), 2211 0xE9 | 0xEB => sbc_immediate(cpu, bus), 2212 0xEC => cpx_absolute(cpu, bus), 2213 0xED => sbc_absolute(cpu, bus), 2214 0xEE => inc_absolute(cpu, bus), 2215 0xEF => isc_absolute(cpu, bus), 2216 0xF0 => beq(cpu, bus), 2217 0xF1 => sbc_indirect_y(cpu, bus), 2218 0xF3 => isc_indirect_y(cpu, bus), 2219 0xF5 => sbc_zero_page_x(cpu, bus), 2220 0xF6 => inc_zero_page_x(cpu, bus), 2221 0xF7 => isc_zero_page_x(cpu, bus), 2222 0xF8 => sed(cpu, bus), 2223 0xF9 => sbc_absolute_y(cpu, bus), 2224 0xFB => isc_absolute_y(cpu, bus), 2225 0xFD => sbc_absolute_x(cpu, bus), 2226 0xFE => inc_absolute_x(cpu, bus), 2227 0xFF => isc_absolute_x(cpu, bus), 2228 0x02 | 0x12 | 0x22 | 0x32 | 0x42 | 0x52 | 0x62 | 0x72 | 0x92 | 0xB2 | 0xD2 | 0xF2 => { 2229 // KIL unofficial opcodes; executing any of these halts the CPU until a reset or power cycle 2230 cpu.frozen = true; 2231 } 2232 } 2233 2234 cpu.state.cycle += 1; 2235}