cpu.c (16029B)
1 #include <libgen.h> 2 #include <stdbool.h> 3 #include <stdint.h> 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <string.h> 7 8 #include "apu.h" 9 #include "cpu.h" 10 #include "opcodes.h" 11 #include "ppu.h" 12 #include "rom.h" 13 14 #define MAX(a, b) ((a > b) ? a : b) 15 16 #define STATUS_UPDATE_ZERO(r) \ 17 (regs.status.zero = r == 0) 18 #define STATUS_UPDATE_NEGATIVE(r) \ 19 (regs.status.negative = ((r & (1 << 7)) != 0)) 20 #define STATUS_UPDATE_NZ(r) \ 21 {STATUS_UPDATE_ZERO(r); \ 22 STATUS_UPDATE_NEGATIVE(r);} 23 #define STATUS_TO_INT() \ 24 (regs.status.carry | (regs.status.zero << 1) \ 25 | (regs.status.interrupt_disable << 2) | (regs.status.decimal_mode << 3) \ 26 | (regs.status.brk << 4) | (regs.status.unused << 5) \ 27 | (regs.status.overflow << 6) | (regs.status.negative << 7)) 28 29 #define MEMORY_MIRROR(addr) \ 30 if (addr < 0x2000) \ 31 addr &= 0x07FF; 32 33 #define PUSH(b) \ 34 (memwrite(0x0100 + regs.sp--, b)) 35 #define PULL() \ 36 (peek(0x0100 + ++regs.sp)) 37 38 struct rom rom = {0}; 39 uint32_t cycles = 0; 40 41 bool page_crossed = false; 42 43 static uint8_t 44 peek(uint16_t addr) 45 { 46 MEMORY_MIRROR(addr); 47 48 if (addr > 0x7FFF) { 49 if (rom.prg_rom_size == 0x4000) 50 return rom.prg_rom[(addr - 0x8000) % 0x4000]; 51 else if (rom.prg_rom_size == 0x8000) 52 return rom.prg_rom[addr - 0x8000]; 53 else 54 fprintf(stderr, "PRG ROG size is not 0x4000 nor 0x8000\n"), exit(1); 55 } else if ((addr >= 0x4000 && addr <= 0x4013) || addr == 0x4015 || addr == 0x4017) 56 return apu_read(addr); 57 else if (addr >= 0x2000 && addr <= 0x3FFF) 58 return ppu_read(addr); 59 else 60 return memory[addr]; 61 } 62 63 static uint16_t 64 peek16(uint16_t addr) 65 { 66 /* bytes are stored in little-endian (low then high) */ 67 return peek(addr) | (peek(addr + 1) << 8); 68 } 69 70 static void 71 memwrite(uint16_t addr, uint8_t byte) 72 { 73 if (addr >= 0x4000 && addr <= 0x4017) { 74 apu_write(addr, byte); 75 return; 76 } else if (addr >= 0x2000 && addr <= 0x3FFF) { 77 ppu_write(addr, byte); 78 return; 79 } 80 81 MEMORY_MIRROR(addr); 82 83 memory[addr] = byte; 84 } 85 86 static void 87 memwrite16(uint16_t addr, uint16_t word) 88 { 89 MEMORY_MIRROR(addr); 90 91 /* bytes are stored in little-endian (low then high) */ 92 memory[addr] = word & 0xFF; 93 memory[addr + 1] = (word & 0xFF00) >> 8; 94 } 95 96 static uint16_t 97 opcode_mem(enum addressing_mode mode) 98 { 99 uint16_t arg, val; 100 101 if (mode == AM_ACC || mode == AM_NONE) { 102 return 0; 103 } else if (mode != AM_ABS && mode != AM_ABS_X && mode != AM_ABS_Y && mode != AM_IND && mode) { 104 arg = peek(regs.pc++); 105 } else { 106 arg = peek16(regs.pc); 107 regs.pc += 2; 108 } 109 110 switch (mode) { 111 case AM_ZP: 112 val = arg % 256; 113 break; 114 case AM_ZP_X: 115 val = (arg + regs.x) % 256; 116 break; 117 case AM_ZP_Y: 118 val = (arg + regs.y) % 256; 119 break; 120 case AM_REL: 121 val = (int8_t)arg + regs.pc; 122 break; 123 case AM_IMM: 124 case AM_ABS: 125 val = arg; 126 break; 127 case AM_ABS_X: 128 val = arg + regs.x; 129 130 if ((arg & 0xFF00) != (val & 0xFF00)) 131 page_crossed = true; 132 break; 133 case AM_ABS_Y: 134 val = arg + regs.y; 135 136 if ((arg & 0xFF00) != (val & 0xFF00)) 137 page_crossed = true; 138 break; 139 case AM_IND: 140 val = peek(arg) | (peek(((arg + 1) & 0xFF) | (arg & 0xFF00)) << 8); 141 break; 142 case AM_IND_X: 143 val = peek((arg + regs.x) % 256) + peek((arg + regs.x + 1) % 256) * 256; 144 break; 145 case AM_IND_Y: 146 val = peek(arg) + peek((arg + 1) % 256) * 256 + regs.y; 147 148 if (((uint16_t)(val - regs.y) & 0xFF00) != (val & 0xFF00)) 149 page_crossed = true; 150 break; 151 default: 152 fprintf(stderr, "INVALID ADDRESSING MODE %i\n", mode); 153 abort(); 154 } 155 156 return val; 157 } 158 159 static void 160 tick(void) 161 { 162 cycles++; 163 ppu_tick(); 164 ppu_tick(); 165 ppu_tick(); 166 } 167 168 static void 169 branch(uint16_t addr, bool cond) 170 { 171 if (!cond) 172 return; 173 tick(); 174 175 if (((regs.pc + 1) & 0xFF00) != (addr & 0xFF00)) 176 tick(); 177 178 regs.pc = addr; 179 } 180 181 /* OFFICIAL OPCODES */ 182 183 void 184 ADC(uint16_t arg) 185 { 186 uint16_t sum; // 16-bit sum makes it easier to determine carry flag 187 188 sum = regs.a + arg + regs.status.carry; 189 190 regs.status.carry = sum > 0xFF; 191 /* overflow flag formula: https://stackoverflow.com/a/29224684 */ 192 regs.status.overflow = (~(regs.a ^ arg) & (regs.a ^ sum) & 0x80) != 0; 193 regs.a = sum & 0xFF; 194 195 STATUS_UPDATE_NZ(regs.a); 196 } 197 198 void 199 AND(uint16_t arg) 200 { 201 regs.a &= arg; 202 203 STATUS_UPDATE_NZ(regs.a); 204 } 205 206 void 207 ASL_acc(uint16_t arg) 208 { 209 uint16_t tmp; 210 211 tmp = regs.a << 1; 212 regs.a = tmp & 0xFF; 213 214 regs.status.carry = tmp > 0xFF; 215 STATUS_UPDATE_NZ(regs.a); 216 } 217 218 void 219 ASL(uint16_t mem) 220 { 221 uint16_t tmp; 222 223 tmp = peek(mem) << 1; 224 memwrite(mem, tmp & 0xFF); 225 226 regs.status.carry = tmp > 0xFF; 227 regs.status.negative = (tmp & (1 << 7)) != 0; 228 regs.status.zero = (tmp << 1 & 0xFF) == 0; 229 } 230 231 void 232 BCC(uint16_t arg) 233 { 234 branch(arg, regs.status.carry == 0); 235 } 236 237 void 238 BCS(uint16_t arg) 239 { 240 branch(arg, regs.status.carry == 1); 241 } 242 243 void 244 BEQ(uint16_t arg) 245 { 246 branch(arg, regs.status.zero == 1); 247 } 248 249 void 250 BIT(uint16_t arg) 251 { 252 uint8_t tmp = arg; 253 254 regs.status.zero = (regs.a & tmp) == 0; 255 regs.status.overflow = (tmp & (1 << 6)) != 0; 256 STATUS_UPDATE_NEGATIVE(tmp); 257 } 258 259 void 260 BMI(uint16_t arg) 261 { 262 branch(arg, regs.status.negative == 1); 263 } 264 265 void 266 BNE(uint16_t arg) 267 { 268 branch(arg, regs.status.zero == 0); 269 } 270 271 void 272 BPL(uint16_t arg) 273 { 274 branch(arg, regs.status.negative == 0); 275 } 276 277 void 278 BRK(uint16_t arg) 279 { 280 /* TODO: push regs.pc and regs.status to stack and load IRQ vector */ 281 regs.status.brk = 1; 282 exit(0); 283 } 284 285 void 286 BVC(uint16_t arg) 287 { 288 //regs.status.overflow = (STATUS_TO_INT() & (1 << 6)) == 0; 289 //if (regs.status.overflow == 0) 290 branch(arg, (STATUS_TO_INT() & (1 << 6)) == 0); 291 } 292 293 void 294 BVS(uint16_t arg) 295 { 296 regs.status.overflow = (STATUS_TO_INT() & (1 << 6)) != 0; 297 branch(arg, regs.status.overflow == 1); 298 } 299 300 void 301 CLC(uint16_t arg) 302 { 303 regs.status.carry = 0; 304 } 305 306 void 307 CLD(uint16_t arg) 308 { 309 regs.status.decimal_mode = 0; 310 } 311 312 void 313 CLI(uint16_t arg) 314 { 315 regs.status.interrupt_disable = 0; 316 } 317 318 void 319 CLV(uint16_t arg) 320 { 321 regs.status.overflow = 0; 322 } 323 324 void 325 CMP(uint16_t arg) 326 { 327 uint8_t tmp; 328 329 tmp = regs.a - arg; 330 331 regs.status.carry = regs.a >= arg; 332 regs.status.zero = regs.a == arg; 333 STATUS_UPDATE_NEGATIVE(tmp); 334 } 335 336 void 337 CPX(uint16_t arg) 338 { 339 uint8_t tmp; 340 341 tmp = regs.x - arg; 342 343 regs.status.carry = regs.x >= arg; 344 regs.status.zero = regs.x == arg; 345 STATUS_UPDATE_NEGATIVE(tmp); 346 } 347 348 void 349 CPY(uint16_t arg) 350 { 351 uint8_t tmp; 352 353 tmp = regs.y - arg; 354 355 regs.status.carry = regs.y >= arg; 356 regs.status.zero = regs.y == arg; 357 STATUS_UPDATE_NEGATIVE(tmp); 358 } 359 360 void 361 DEC(uint16_t mem) 362 { 363 memwrite(mem, peek(mem) - 1); 364 365 STATUS_UPDATE_NZ(peek(mem)); 366 } 367 368 void 369 DEX(uint16_t arg) 370 { 371 regs.x--; 372 373 STATUS_UPDATE_NZ(regs.x); 374 } 375 376 void 377 DEY(uint16_t arg) 378 { 379 regs.y--; 380 381 STATUS_UPDATE_NZ(regs.y); 382 } 383 384 void 385 EOR(uint16_t arg) 386 { 387 regs.a ^= arg; 388 389 STATUS_UPDATE_NZ(regs.a); 390 } 391 392 void 393 INC(uint16_t mem) 394 { 395 memwrite(mem, peek(mem) + 1); 396 397 STATUS_UPDATE_NZ(peek(mem)); 398 } 399 400 void 401 INX(uint16_t arg) 402 { 403 regs.x++; 404 405 STATUS_UPDATE_NZ(regs.x); 406 } 407 408 void 409 INY(uint16_t arg) 410 { 411 regs.y++; 412 413 STATUS_UPDATE_NZ(regs.y); 414 } 415 416 void 417 JMP(uint16_t arg) 418 { 419 regs.pc = arg; 420 } 421 422 void 423 JSR(uint16_t arg) 424 { 425 uint16_t tmp = regs.pc - 1; 426 427 /* 428 * first push high-byte of return address then low-byte 429 * https://www.masswerk.at/6502/6502_instruction_set.html 430 */ 431 PUSH((tmp & 0xFF00) >> 8); 432 PUSH(tmp & 0xFF); 433 regs.pc = arg; 434 } 435 436 void 437 LDA(uint16_t arg) 438 { 439 regs.a = arg; 440 441 STATUS_UPDATE_NZ(regs.a); 442 } 443 444 void 445 LDX(uint16_t arg) 446 { 447 regs.x = arg; 448 449 STATUS_UPDATE_NZ(regs.x); 450 } 451 452 void 453 LDY(uint16_t arg) 454 { 455 regs.y = arg; 456 457 STATUS_UPDATE_NZ(regs.y); 458 } 459 460 void 461 LSR_acc(uint16_t arg) 462 { 463 regs.status.carry = regs.a & 1; // bit 0 in carry 464 regs.a >>= 1; 465 regs.a &= ~(1 << 7); // bit 7 cleared 466 467 STATUS_UPDATE_NZ(regs.a); 468 } 469 470 void 471 LSR(uint16_t mem) 472 { 473 uint8_t tmp; 474 475 tmp = peek(mem); 476 477 regs.status.carry = tmp & 1; // bit 0 in carry 478 tmp >>= 1; 479 tmp &= ~(1 << 7); // bit 7 cleared 480 481 memwrite(mem, tmp); 482 483 STATUS_UPDATE_NZ(tmp); 484 } 485 486 void 487 NOP(uint16_t arg) 488 { 489 return; 490 } 491 492 void 493 ORA(uint16_t arg) 494 { 495 regs.a |= arg; 496 497 STATUS_UPDATE_NZ(regs.a); 498 } 499 500 void 501 PHA(uint16_t arg) 502 { 503 PUSH(regs.a); 504 } 505 506 void 507 PHP(uint16_t arg) 508 { 509 PUSH(STATUS_TO_INT() | (1 << 4)); 510 } 511 512 void 513 PLA(uint16_t arg) 514 { 515 regs.a = PULL(); 516 517 STATUS_UPDATE_NZ(regs.a); 518 } 519 520 void 521 PLP(uint16_t arg) 522 { 523 uint8_t status; 524 525 status = PULL(); 526 527 regs.status.carry = (status & 1) != 0; 528 regs.status.zero = (status & (1 << 1)) != 0; 529 regs.status.interrupt_disable = (status & (1 << 2)) != 0; 530 regs.status.decimal_mode = (status & (1 << 3)) != 0; 531 regs.status.brk = 0; 532 regs.status.unused = 1; 533 regs.status.overflow = (status & (1 << 6)) != 0; 534 regs.status.negative = (status & (1 << 7)) != 0; 535 } 536 537 void 538 ROL_acc(uint16_t arg) 539 { 540 uint8_t carry; 541 carry = (regs.a & (1 << 7)) != 0; 542 543 regs.a <<= 1; 544 regs.a |= regs.status.carry; 545 546 regs.status.carry = carry; 547 STATUS_UPDATE_NZ(regs.a); 548 } 549 550 void 551 ROL(uint16_t mem) 552 { 553 uint8_t carry, tmp; 554 carry = (peek(mem) & (1 << 7)) != 0; 555 556 tmp = (peek(mem) << 1) | regs.status.carry; 557 memwrite(mem, tmp); 558 559 regs.status.carry = carry; 560 STATUS_UPDATE_NZ(tmp); 561 } 562 563 void 564 ROR_acc(uint16_t arg) 565 { 566 uint8_t carry; 567 carry = regs.a & 1; 568 569 regs.a >>= 1; 570 regs.a |= regs.status.carry << 7; 571 572 regs.status.carry = carry; 573 STATUS_UPDATE_NZ(regs.a); 574 } 575 576 void 577 ROR(uint16_t mem) 578 { 579 uint8_t carry, tmp; 580 carry = peek(mem) & 1; 581 582 tmp = (peek(mem) >> 1) | (regs.status.carry << 7); 583 memwrite(mem, tmp); 584 585 regs.status.carry = carry; 586 STATUS_UPDATE_NZ(tmp); 587 } 588 589 void 590 RTI(uint16_t arg) 591 { 592 PLP(0); 593 regs.pc = PULL() | (PULL() << 8); 594 } 595 596 void 597 RTS(uint16_t arg) 598 { 599 regs.pc = (PULL() | (PULL() << 8)) + 1; 600 } 601 602 void 603 SBC(uint16_t arg) 604 { 605 uint8_t tmp = arg & 0xFF; 606 uint16_t diff; 607 608 diff = regs.a - tmp - !regs.status.carry; 609 610 regs.status.carry = diff > 0xFF; 611 regs.status.carry = !regs.status.carry; 612 /* overflow flag formula: https://stackoverflow.com/a/29224684 */ 613 regs.status.overflow = ((regs.a ^ arg) & (regs.a ^ diff) & 0x80) != 0; 614 regs.a = diff & 0xFF; 615 616 STATUS_UPDATE_NZ(regs.a); 617 } 618 619 void 620 SEC(uint16_t arg) 621 { 622 regs.status.carry = 1; 623 } 624 625 void 626 SED(uint16_t arg) 627 { 628 regs.status.decimal_mode = 1; 629 } 630 631 void 632 SEI(uint16_t arg) 633 { 634 regs.status.interrupt_disable = 1; 635 } 636 637 void 638 STA(uint16_t mem) 639 { 640 memwrite(mem, regs.a); 641 } 642 643 void 644 STX(uint16_t mem) 645 { 646 memwrite(mem, regs.x); 647 } 648 649 void 650 STY(uint16_t mem) 651 { 652 memwrite(mem, regs.y); 653 } 654 655 void 656 TAX(uint16_t arg) 657 { 658 regs.x = regs.a; 659 660 STATUS_UPDATE_NZ(regs.x); 661 } 662 663 void 664 TAY(uint16_t arg) 665 { 666 regs.y = regs.a; 667 668 STATUS_UPDATE_NZ(regs.y); 669 } 670 671 void 672 TSX(uint16_t arg) 673 { 674 // regs.x = PULL(); 675 regs.x = regs.sp; 676 677 STATUS_UPDATE_NZ(regs.x); 678 } 679 680 void 681 TXA(uint16_t arg) 682 { 683 regs.a = regs.x; 684 685 STATUS_UPDATE_NZ(regs.a); 686 } 687 688 void 689 TXS(uint16_t arg) 690 { 691 // PUSH(regs.x); 692 regs.sp = regs.x; 693 } 694 695 void 696 TYA(uint16_t arg) 697 { 698 regs.a = regs.y; 699 700 STATUS_UPDATE_NZ(regs.a); 701 } 702 703 /* UNOFFICIAL OPCODES */ 704 705 void 706 AAC(uint16_t arg) 707 { 708 AND(arg); 709 710 regs.status.carry = regs.status.negative; 711 } 712 713 void 714 SAX(uint16_t arg) 715 { 716 uint8_t tmp = regs.x & regs.a; 717 memwrite(arg, tmp); 718 } 719 720 void 721 ARR(uint16_t arg) 722 { 723 uint8_t tmp = arg & regs.a; 724 725 AND(arg); 726 ROR_acc(0); 727 728 if ((tmp & (1 << 5)) != 0 && (tmp & (1 << 6)) != 0) 729 SEC(0), CLV(0); 730 else if ((tmp & (1 << 5)) == 0 && (tmp & (1 << 6)) == 0) 731 CLC(0), CLV(0); 732 else if ((tmp & (1 << 5)) != 0) 733 CLC(0), regs.status.overflow = 1; 734 else if ((tmp & (1 << 6)) != 0) 735 SEC(0), regs.status.overflow = 1; 736 } 737 738 void 739 ASR(uint16_t arg) 740 { 741 AND(arg); 742 LSR_acc(0); 743 } 744 745 void 746 ATX(uint16_t arg) 747 { 748 AND(arg); 749 TAX(0); 750 } 751 752 void 753 AXA(uint16_t arg) 754 { 755 uint8_t tmp = regs.x & regs.a & 7; 756 memwrite(arg, tmp); 757 } 758 759 void 760 AXS(uint16_t arg) 761 { 762 regs.x &= regs.a; 763 regs.x -= arg; 764 765 regs.status.carry = arg <= regs.x; 766 STATUS_UPDATE_NZ(regs.x); 767 } 768 769 void 770 DCP(uint16_t arg) 771 { 772 uint8_t tmp = peek(arg) - 1; 773 memwrite(arg, tmp); 774 775 regs.status.carry = tmp <= regs.a; 776 STATUS_UPDATE_NZ(regs.a - tmp); 777 } 778 779 void 780 ISB(uint16_t arg) 781 { 782 INC(arg); 783 SBC(peek(arg)); 784 } 785 786 void 787 KIL(uint16_t arg) 788 { 789 /* TODO: figure out how to stop interpret(), I guess with global bool */ 790 NOP(0); 791 } 792 793 void 794 LAR(uint16_t arg) 795 { 796 regs.a = regs.x = regs.sp = regs.a & regs.sp; 797 798 STATUS_UPDATE_NZ(regs.a); 799 } 800 801 void 802 LAX(uint16_t arg) 803 { 804 LDA(arg); 805 LDX(arg); 806 } 807 808 void 809 RLA(uint16_t arg) 810 { 811 ROL(arg); 812 AND(peek(arg)); 813 } 814 815 void 816 RRA(uint16_t arg) 817 { 818 ROR(arg); 819 ADC(peek(arg)); 820 } 821 822 void 823 SLO(uint16_t arg) 824 { 825 ASL(arg); 826 ORA(peek(arg)); 827 } 828 829 void 830 SRE(uint16_t arg) 831 { 832 LSR(arg); 833 EOR(peek(arg)); 834 } 835 836 void 837 SXA(uint16_t arg) 838 { 839 memwrite(arg, regs.x & ((arg & 0xFF00) + 1)); 840 } 841 842 void 843 SYA(uint16_t arg) 844 { 845 memwrite(arg, regs.x & ((arg & 0xFF00) + 1)); 846 } 847 848 void 849 XAA(uint16_t arg) 850 { 851 /* TODO: apparently the exact operation is unknown */ 852 NOP(0); 853 } 854 855 void 856 XAS(uint16_t arg) 857 { 858 regs.sp = regs.a & regs.x; 859 memwrite(arg, regs.sp & ((arg & 0xFF00) + 1)); 860 } 861 862 static void 863 interpret(void) 864 { 865 uint8_t op, spaces; 866 uint16_t arg; 867 enum addressing_mode mode; 868 869 for (;;) { 870 printf("%04X ", regs.pc); 871 872 op = peek(regs.pc++); 873 874 printf("%02X", op); 875 for (uint8_t i = 0; i < opcodes[op].bytes - 1; i++) 876 printf(" %02X", peek(regs.pc + i)); 877 878 if (opcodes[op].bytes == 1) 879 printf(" "); 880 else if (opcodes[op].bytes == 2) 881 printf(" "); 882 else if (opcodes[op].bytes == 3) 883 putchar(' '); 884 putchar((opcodes[op].unofficial) ? '*' : ' '); 885 printf("%s ", opcodes[op].name); 886 887 mode = opcodes[op].mode; 888 arg = opcode_mem(mode); 889 890 switch (mode) { 891 case AM_IMM: 892 printf("#$%02X", arg); 893 break; 894 case AM_ZP: 895 printf("$%02X", arg); 896 break; 897 case AM_ZP_X: 898 printf("$%02X,X @ %02X", peek(regs.pc - 1), arg); 899 break; 900 case AM_ZP_Y: 901 printf("$%02X,Y @ %02X", peek(regs.pc - 1), arg); 902 break; 903 case AM_REL: 904 case AM_ABS: 905 printf("$%04X", arg); 906 break; 907 case AM_ABS_X: 908 printf("$%04X,X @ %04X", (uint16_t)(arg - regs.x), arg); 909 break; 910 case AM_ABS_Y: 911 printf("$%04X,Y @ %04X", (uint16_t)(arg - regs.y), arg); 912 break; 913 case AM_IND: 914 printf("($%04X) = %04X", peek16(regs.pc - 2), arg); 915 break; 916 case AM_IND_X: 917 printf("($%02X,X) @ %02X = %04X", peek(regs.pc - 1), (peek(regs.pc - 1) + regs.x) & 0xFF, arg); 918 break; 919 case AM_IND_Y: 920 printf("($%02X),Y = %04X @ %04X", peek(regs.pc - 1), (uint16_t)(arg - regs.y), arg); 921 break; 922 case AM_ACC: 923 printf("A "); 924 break; 925 case AM_NONE: 926 printf(" "); 927 break; 928 } 929 930 spaces = 9; 931 932 if (opcodes[op].memread || opcodes[op].memwrite) 933 printf(" = %02X", peek(arg)); 934 else 935 spaces += 5; 936 937 if (mode != AM_IND && mode != AM_ABS_X && mode != AM_ABS_Y) 938 spaces += 7; 939 940 switch (opcodes[op].mode) { 941 case AM_IND_X: 942 spaces -= 12; 943 break; 944 case AM_IND_Y: 945 spaces -= 14; 946 break; 947 case AM_ZP: 948 spaces += 4; 949 break; 950 case AM_ZP_X: 951 case AM_ZP_Y: 952 spaces -= 3; 953 break; 954 case AM_REL: 955 case AM_ABS: 956 spaces += 2; 957 break; 958 case AM_IMM: 959 case AM_ACC: 960 case AM_NONE: 961 spaces += 3; 962 break; 963 default: 964 break; 965 } 966 967 while (spaces--) putchar(' '); 968 969 printf("A:%02X X:%02X Y:%02X P:%02X SP:%02X PPU:%3d,%3d CYC:%d\n", 970 regs.a, regs.x, regs.y, STATUS_TO_INT(), regs.sp, 971 ppu.scanlines, ppu.cycles, cycles); 972 973 if (opcodes[op].memread) 974 opcodes[op].instr(peek(arg)); 975 else 976 opcodes[op].instr(arg); 977 978 for (uint8_t i = 0; i < opcodes[op].cycles; i++) 979 tick(); 980 981 if (page_crossed) { 982 if (opcodes[op].page_cross) 983 tick(); 984 page_crossed = false; 985 } 986 } 987 } 988 989 /* https://www.nesdev.org/wiki/CPU_power_up_state */ 990 void 991 cpu_init(void) 992 { 993 regs.a = regs.x = regs.y = 0; 994 regs.pc = 0xFFFC; 995 regs.sp = 0xFD; 996 997 //memset(®s.status, 0, sizeof(regs.status)); 998 regs.status.interrupt_disable = 1; 999 regs.status.unused = 1; 1000 1001 cycles += 7; 1002 for (uint8_t i = 0; i < 7 * 3; i++) 1003 ppu_tick(); 1004 } 1005 1006 int 1007 main(int argc, char *argv[]) 1008 { 1009 FILE *fp; 1010 uint8_t *buf; 1011 size_t buflen; 1012 1013 if (argc != 2) { 1014 fprintf(stderr, "Usage: %s rom.nes\n", basename(argv[0])); 1015 return 1; 1016 } 1017 1018 fp = fopen(argv[1], "r"); 1019 fseek(fp, 0, SEEK_END); 1020 buflen = ftell(fp); 1021 buf = calloc(1, buflen); 1022 fseek(fp, 0, SEEK_SET); 1023 if (fread(buf, 1, buflen, fp) != buflen && ferror(fp)) { 1024 fprintf(stderr, "file %s was not read properly\n", argv[1]); 1025 clearerr(fp); 1026 fclose(fp); 1027 return 1; 1028 } 1029 fclose(fp); 1030 1031 parse_rom(buf, buflen, &rom); 1032 free(buf); 1033 ppu.rom = &rom; 1034 1035 cpu_init(); 1036 1037 /* TODO: move to separate file? */ 1038 if (rom.mapper != 0) { 1039 fprintf(stderr, "Only iNES ROMs using Mapper 0 are supported for now.\n"); 1040 return 1; 1041 } 1042 1043 memwrite16(0xFFFC, 0xC000); 1044 regs.pc = 0xC000; 1045 1046 interpret(); 1047 1048 printf("\n$02 = %02X\n", memory[0x02]); 1049 printf("$03 = %02X\n", memory[0x03]); 1050 1051 free_rom(&rom); 1052 1053 return 0; 1054 }