emu_nes

An NES emulator for learning how the 6502 and similar computers of the era worked
Log | Files | Refs | README | LICENSE

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(&regs.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 }