emu_nes/cpu.c
vin 718f89c9c5 fix incorrect argument for INC
Perhaps a refactor is in order for the arguments to the opcodes.
2024-06-30 09:54:05 -04:00

1992 lines
32 KiB
C

#include <libgen.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "rom.h"
#define MAX(a, b) ((a > b) ? a : b)
#define STATUS_UPDATE_ZERO(r) \
(regs.status.zero = r == 0)
#define STATUS_UPDATE_NEGATIVE(r) \
(regs.status.negative = ((r & (1 << 7)) != 0))
#define STATUS_TO_INT() \
(regs.status.carry | (regs.status.zero << 1) \
| (regs.status.interrupt_disable << 2) | (regs.status.decimal_mode << 3) \
| (regs.status.brk << 4) | (regs.status.unused << 5) \
| (regs.status.overflow << 6) | (regs.status.negative << 7))
#define MEMORY_MIRROR(addr) \
if (addr < 0x2000) \
addr &= 0x07FF; \
else if (addr < 0x4000) \
addr &= 0x2007;
#define PUSH(b) \
(memwrite(0x0100 + regs.sp--, b))
#define PULL() \
(peek(0x0100 + ++regs.sp))
struct cpu_flags {
uint8_t carry : 1;
uint8_t zero : 1;
uint8_t interrupt_disable : 1;
uint8_t decimal_mode : 1;
uint8_t brk : 1;
uint8_t unused : 1;
uint8_t overflow : 1;
uint8_t negative : 1;
};
struct registers {
uint8_t a, x, y, sp;
struct cpu_flags status;
uint16_t pc;
};
struct registers regs = {0};
struct Rom rom = {0};
enum addressing_mode {
AM_ACC,
AM_IMPLICIT,
AM_IMM,
AM_ZP,
AM_ZP_X,
AM_ZP_Y,
AM_REL,
AM_ABS,
AM_ABS_X,
AM_ABS_Y,
AM_IND,
AM_IND_X,
AM_IND_Y,
};
/* 64K address space, 16bit words */
uint8_t memory[0x16000];
uint32_t cycles = 0;
static uint8_t
peek(uint16_t addr)
{
MEMORY_MIRROR(addr);
if (addr > 0x7FFF) {
if (rom.prg_rom_size == 0x4000)
return rom.prg_rom[(addr - 0x8000) % 0x4000];
else if (rom.prg_rom_size == 0x8000)
return rom.prg_rom[addr - 0x8000];
} else {
return memory[addr];
}
}
static uint16_t
peek16(uint16_t addr)
{
/* bytes are stored in little-endian (low then high) */
return peek(addr) | (peek(addr + 1) << 8);
}
static void
memwrite(uint16_t addr, uint8_t byte)
{
MEMORY_MIRROR(addr);
memory[addr] = byte;
}
static void
memwrite16(uint16_t addr, uint16_t word)
{
MEMORY_MIRROR(addr);
/* bytes are stored in little-endian (low then high) */
memory[addr] = word & 0xFF;
memory[addr + 1] = (word & 0xFF00) >> 8;
}
static uint8_t
opcode_arg(enum addressing_mode mode)
{
uint16_t arg, val;
if (mode != AM_ABS && mode != AM_ABS_X && mode != AM_ABS_Y) {
arg = peek(regs.pc++);
printf(" %02X\t", arg);
} else {
arg = peek16(regs.pc);
printf(" %02X %02X\t", peek(regs.pc), peek(regs.pc + 1));
regs.pc += 2;
}
switch (mode) {
case AM_IMM:
case AM_REL:
val = arg;
break;
case AM_ZP:
val = peek(arg % 256);
break;
case AM_ZP_X:
val = peek((arg + regs.x) % 256);
break;
case AM_ZP_Y:
val = peek((arg + regs.y) % 256);
break;
case AM_ABS:
val = peek16(arg);
break;
case AM_ABS_X:
val = peek16(arg + regs.x);
break;
case AM_ABS_Y:
val = peek16(arg + regs.y);
break;
case AM_IND_X:
val = peek(peek((arg + regs.x) % 256) + peek((arg + regs.x + 1) % 256) * 256);
break;
case AM_IND_Y:
val = peek(peek(arg) + peek((arg + 1) % 256) * 256 + regs.y);
break;
default:
fprintf(stderr, "INVALID ADDRESSING MODE %i\n", mode);
abort();
}
return val;
}
static uint16_t
opcode_mem(enum addressing_mode mode)
{
uint16_t arg, val;
if (mode != AM_ABS && mode != AM_ABS_X && mode != AM_ABS_Y && mode != AM_IND && mode) {
arg = peek(regs.pc++);
printf(" %02X\t", arg);
} else {
arg = peek16(regs.pc);
printf(" %02X %02X\t", peek(regs.pc), peek(regs.pc + 1));
regs.pc += 2;
}
switch (mode) {
case AM_ZP:
val = arg % 256;
break;
case AM_ZP_X:
val = (arg + regs.x) % 256;
break;
case AM_ZP_Y:
val = (arg + regs.y) % 256;
break;
case AM_IMM:
case AM_ABS:
val = arg;
break;
case AM_ABS_X:
val = arg + regs.x;
break;
case AM_ABS_Y:
val = arg + regs.y;
break;
case AM_IND:
val = peek16(arg);
break;
case AM_IND_X:
val = peek((arg + regs.x) % 256) + peek((arg + regs.x + 1) % 256) * 256;
break;
case AM_IND_Y:
val = peek(arg) + peek((arg + 1) % 256) * 256 + regs.y;
break;
default:
fprintf(stderr, "INVALID ADDRESSING MODE\n");
abort();
}
return val;
}
static void
adc(uint8_t arg)
{
uint16_t sum; // 16-bit sum makes it easier to determine carry flag
sum = regs.a + arg + regs.status.carry;
regs.status.carry = sum > 0xFF;
/* overflow flag formula: https://stackoverflow.com/a/29224684 */
regs.status.overflow = (~(regs.a ^ arg) & (regs.a ^ sum) & 0x80) != 0;
regs.a = sum & 0xFF;
STATUS_UPDATE_ZERO(regs.a);
STATUS_UPDATE_NEGATIVE(regs.a);
}
static void
and(uint8_t arg)
{
regs.a &= arg;
STATUS_UPDATE_ZERO(regs.a);
STATUS_UPDATE_NEGATIVE(regs.a);
}
static void
asl_acc(void)
{
uint16_t tmp;
tmp = regs.a << 1;
regs.a = tmp & 0xFF;
regs.status.carry = tmp > 0xFF;
STATUS_UPDATE_ZERO(regs.a);
STATUS_UPDATE_NEGATIVE(regs.a);
}
static void
asl(uint16_t mem)
{
uint16_t tmp;
tmp = peek(mem) << 1;
memwrite(mem, tmp & 0xFF);
regs.status.carry = tmp > 0xFF;
STATUS_UPDATE_ZERO(tmp);
STATUS_UPDATE_NEGATIVE(tmp);
}
static void
bcc(uint8_t arg)
{
if (regs.status.carry == 0)
regs.pc += arg;
}
static void
bcs(uint8_t arg)
{
if (regs.status.carry == 1)
regs.pc += arg;
}
static void
beq(uint8_t arg)
{
if (regs.status.zero == 1)
regs.pc += arg;
}
static uint8_t
bit(uint8_t arg)
{
regs.status.zero = (regs.a & arg) == 0;
regs.status.overflow = (arg & (1 << 6)) != 0;
STATUS_UPDATE_NEGATIVE(arg);
return regs.a & arg;
}
static void
bmi(uint8_t arg)
{
if (regs.status.negative == 1)
regs.pc += arg;
}
static void
bne(uint8_t arg)
{
if (regs.status.zero == 0)
regs.pc += arg;
}
static void
bpl(uint8_t arg)
{
if (regs.status.negative == 0)
regs.pc += arg;
}
static void
brk(void)
{
/* TODO: push regs.pc and regs.status to stack and load IRQ vector */
regs.status.brk = 1;
// exit(0);
}
static void
bvc(uint8_t arg)
{
//regs.status.overflow = (STATUS_TO_INT() & (1 << 6)) == 0;
//if (regs.status.overflow == 0)
if ((STATUS_TO_INT() & (1 << 6)) == 0)
regs.pc += arg;
}
static void
bvs(uint8_t arg)
{
regs.status.overflow = (STATUS_TO_INT() & (1 << 6)) != 0;
if (regs.status.overflow == 1)
regs.pc += arg;
}
static void
clc(void)
{
regs.status.carry = 0;
}
static void
cld(void)
{
regs.status.decimal_mode = 0;
}
static void
cli(void)
{
regs.status.interrupt_disable = 0;
}
static void
clv(void)
{
regs.status.overflow = 0;
}
static void
cmp(uint8_t arg)
{
uint8_t tmp;
tmp = regs.a - arg;
regs.status.carry = regs.a >= arg;
regs.status.zero = regs.a == arg;
STATUS_UPDATE_NEGATIVE(tmp);
}
static void
cpx(uint8_t arg)
{
uint8_t tmp;
tmp = regs.x - arg;
regs.status.carry = regs.x >= arg;
regs.status.zero = regs.x == arg;
STATUS_UPDATE_NEGATIVE(tmp);
}
static void
cpy(uint8_t arg)
{
uint8_t tmp;
tmp = regs.y - arg;
regs.status.carry = regs.y >= arg;
regs.status.zero = regs.y == arg;
STATUS_UPDATE_NEGATIVE(tmp);
}
static void
dec(uint16_t mem)
{
memwrite(mem, peek(mem) - 1);
STATUS_UPDATE_ZERO(peek(mem));
STATUS_UPDATE_NEGATIVE(peek(mem));
}
static void
dex(void)
{
regs.x--;
STATUS_UPDATE_ZERO(regs.x);
STATUS_UPDATE_NEGATIVE(regs.x);
}
static void
dey(void)
{
regs.y--;
STATUS_UPDATE_ZERO(regs.y);
STATUS_UPDATE_NEGATIVE(regs.y);
}
static void
eor(uint8_t arg)
{
regs.a ^= arg;
STATUS_UPDATE_ZERO(regs.a);
STATUS_UPDATE_NEGATIVE(regs.a);
}
static void
inc(uint16_t mem)
{
memwrite(mem, peek(mem) + 1);
STATUS_UPDATE_ZERO(peek(mem));
STATUS_UPDATE_NEGATIVE(peek(mem));
}
static void
inx(void)
{
regs.x++;
STATUS_UPDATE_ZERO(regs.x);
STATUS_UPDATE_NEGATIVE(regs.x);
}
static void
iny(void)
{
regs.y++;
STATUS_UPDATE_ZERO(regs.y);
STATUS_UPDATE_NEGATIVE(regs.y);
}
static void
jmp(uint16_t arg)
{
regs.pc = arg;
}
static void
jsr(uint16_t arg)
{
uint16_t tmp = regs.pc - 1;
/*
* first push high-byte of return address then low-byte
* https://www.masswerk.at/6502/6502_instruction_set.html
*/
PUSH((tmp & 0xFF00) >> 8);
PUSH(tmp & 0xFF);
regs.pc = arg;
}
static void
lda(uint8_t arg)
{
regs.a = arg;
STATUS_UPDATE_ZERO(regs.a);
STATUS_UPDATE_NEGATIVE(regs.a);
}
static void
ldx(uint8_t arg)
{
regs.x = arg;
STATUS_UPDATE_ZERO(regs.x);
STATUS_UPDATE_NEGATIVE(regs.x);
}
static void
ldy(uint8_t arg)
{
regs.y = arg;
STATUS_UPDATE_ZERO(regs.y);
STATUS_UPDATE_NEGATIVE(regs.y);
}
static void
lsr_acc(void)
{
regs.status.carry = regs.a & 1; // bit 0 in carry
regs.a >>= 1;
regs.a &= ~(1 << 7); // bit 7 cleared
STATUS_UPDATE_ZERO(regs.a);
STATUS_UPDATE_NEGATIVE(regs.a);
}
static void
lsr(uint16_t mem)
{
uint8_t tmp;
tmp = peek(mem);
regs.status.carry = tmp & 1; // bit 0 in carry
tmp >>= 1;
tmp &= ~(1 << 7); // bit 7 cleared
memwrite(mem, tmp);
STATUS_UPDATE_ZERO(tmp);
STATUS_UPDATE_NEGATIVE(tmp);
}
static void
nop(void)
{
return;
}
static void
ora(uint8_t arg)
{
regs.a |= arg;
STATUS_UPDATE_ZERO(regs.a);
STATUS_UPDATE_NEGATIVE(regs.a);
}
static void
pha(void)
{
PUSH(regs.a);
}
static void
php(void)
{
PUSH(STATUS_TO_INT() | (1 << 4));
}
static void
pla(void)
{
regs.a = PULL();
STATUS_UPDATE_ZERO(regs.a);
STATUS_UPDATE_NEGATIVE(regs.a);
}
static void
plp(void)
{
uint8_t status;
status = PULL();
regs.status.carry = (status & 1) != 0;
regs.status.zero = (status & (1 << 1)) != 0;
regs.status.interrupt_disable = (status & (1 << 2)) != 0;
regs.status.decimal_mode = (status & (1 << 3)) != 0;
regs.status.brk = 0;
regs.status.unused = 1;
regs.status.overflow = (status & (1 << 6)) != 0;
regs.status.negative = (status & (1 << 7)) != 0;
}
static void
rol_acc(void)
{
uint8_t carry;
carry = (regs.a & (1 << 7)) != 0;
regs.a <<= 1;
regs.a |= regs.status.carry;
regs.status.carry = carry;
STATUS_UPDATE_ZERO(regs.a);
STATUS_UPDATE_NEGATIVE(regs.a);
}
static void
rol(uint16_t mem)
{
uint8_t carry, tmp;
carry = (peek(mem) & (1 << 7)) != 0;
tmp = (peek(mem) << 1) | regs.status.carry;
memwrite(mem, tmp);
regs.status.carry = carry;
STATUS_UPDATE_ZERO(tmp);
STATUS_UPDATE_NEGATIVE(tmp);
}
static void
ror_acc(void)
{
uint8_t carry;
carry = regs.a & 1;
regs.a >>= 1;
regs.a |= regs.status.carry << 7;
regs.status.carry = carry;
STATUS_UPDATE_ZERO(regs.a);
STATUS_UPDATE_NEGATIVE(regs.a);
}
static void
ror(uint16_t mem)
{
uint8_t carry, tmp;
carry = peek(mem) & 1;
tmp = (peek(mem) >> 1) | (regs.status.carry << 7);
memwrite(mem, tmp);
regs.status.carry = carry;
STATUS_UPDATE_ZERO(tmp);
STATUS_UPDATE_NEGATIVE(tmp);
}
static void
rti(void)
{
plp();
regs.pc = PULL() | (PULL() << 8);
}
static void
rts(void)
{
regs.pc = (PULL() | (PULL() << 8)) + 1;
}
static void
sbc(uint8_t arg)
{
/* SBC is described online as ADC with argument as two's complement */
adc(~arg);
}
static void
sec(void)
{
regs.status.carry = 1;
}
static void
sed(void)
{
regs.status.decimal_mode = 1;
}
static void
sei(void)
{
regs.status.interrupt_disable = 1;
}
static uint8_t
sta(uint16_t mem)
{
uint8_t tmp = peek(mem);
memwrite(mem, regs.a);
return tmp;
}
static uint8_t
stx(uint16_t mem)
{
uint8_t tmp = peek(mem);
memwrite(mem, regs.x);
return tmp;
}
static uint8_t
sty(uint16_t mem)
{
uint8_t tmp = peek(mem);
memwrite(mem, regs.y);
return tmp;
}
static void
tax(void)
{
regs.x = regs.a;
STATUS_UPDATE_ZERO(regs.x);
STATUS_UPDATE_NEGATIVE(regs.x);
}
static void
tay(void)
{
regs.y = regs.a;
STATUS_UPDATE_ZERO(regs.y);
STATUS_UPDATE_NEGATIVE(regs.y);
}
static void
tsx(void)
{
// regs.x = PULL();
regs.x = regs.sp;
STATUS_UPDATE_ZERO(regs.x);
STATUS_UPDATE_NEGATIVE(regs.x);
}
static void
txa(void)
{
regs.a = regs.x;
STATUS_UPDATE_ZERO(regs.a);
STATUS_UPDATE_NEGATIVE(regs.a);
}
static void
txs(void)
{
// PUSH(regs.x);
regs.sp = regs.x;
}
static void
tya(void)
{
regs.a = regs.y;
STATUS_UPDATE_ZERO(regs.a);
STATUS_UPDATE_NEGATIVE(regs.a);
}
static void
interpret(void)
{
uint8_t opcode, did_memwrite, ret, status;
uint16_t arg, mem, deref;
uint32_t cycles_;
enum addressing_mode mode;
struct registers regs_;
for (;;) {
opcode = peek(regs.pc++);
cycles_ = cycles;
did_memwrite = 0;
status = STATUS_TO_INT();
regs_ = regs;
printf("%04X %02X", regs.pc - 1, opcode);
switch (opcode) {
case 0x69:
mode = AM_IMM;
arg = opcode_arg(mode);
adc(arg);
cycles += 2;
printf("ADC");
break;
case 0x65:
mode = AM_ZP;
arg = opcode_arg(mode);
adc(arg);
cycles += 3;
printf("ADC");
break;
case 0x75:
mode = AM_ZP_X;
arg = opcode_arg(mode);
adc(arg);
cycles += 4;
printf("ADC");
break;
case 0x6d:
mode = AM_ABS;
arg = opcode_arg(mode);
adc(arg);
cycles += 4;
printf("ADC");
break;
case 0x7d:
mode = AM_ABS_X;
arg = opcode_arg(mode);
adc(arg);
cycles += 4;
printf("ADC");
break;
case 0x79:
mode = AM_ABS_Y;
arg = opcode_arg(mode);
adc(arg);
cycles += 4;
printf("ADC");
break;
case 0x61:
mode = AM_IND_X;
arg = opcode_arg(mode);
adc(arg);
cycles += 6;
printf("ADC");
break;
case 0x71:
mode = AM_IND_Y;
arg = opcode_arg(mode);
adc(arg);
cycles += 5;
printf("ADC");
break;
case 0x29:
mode = AM_IMM;
arg = opcode_arg(mode);
and(arg);
cycles += 2;
printf("AND");
break;
case 0x25:
mode = AM_ZP;
arg = opcode_arg(mode);
and(arg);
cycles += 3;
printf("AND");
break;
case 0x35:
mode = AM_ZP_X;
arg = opcode_arg(mode);
and(arg);
cycles += 4;
printf("AND");
break;
case 0x2d:
mode = AM_ABS;
arg = opcode_arg(mode);
and(arg);
cycles += 4;
printf("AND");
break;
case 0x3d:
mode = AM_ABS_X;
arg = opcode_arg(mode);
and(arg);
cycles += 4;
printf("AND");
break;
case 0x39:
mode = AM_ABS_Y;
arg = opcode_arg(mode);
and(arg);
cycles += 4;
printf("AND");
break;
case 0x21:
mode = AM_IND_X;
arg = opcode_arg(mode);
and(arg);
cycles += 6;
printf("AND");
break;
case 0x31:
mode = AM_IND_Y;
arg = opcode_arg(mode);
and(arg);
cycles += 5;
printf("AND");
break;
case 0x0a:
mode = AM_ACC;
asl_acc();
cycles += 2;
printf("\tASL");
break;
case 0x06:
mode = AM_ZP;
arg = opcode_mem(mode);
asl(arg);
cycles += 5;
printf("ASL");
break;
case 0x16:
mode = AM_ZP_X;
arg = opcode_mem(mode);
asl(arg);
cycles += 6;
printf("ASL");
break;
case 0x0e:
mode = AM_ABS;
arg = opcode_mem(mode);
asl(arg);
cycles += 6;
printf("ASL");
break;
case 0x1e:
mode = AM_ABS_X;
arg = opcode_mem(mode);
asl(arg);
cycles += 6;
printf("ASL");
break;
case 0x90:
mode = AM_REL;
arg = opcode_arg(mode);
bcc(arg);
arg += regs_.pc + 1;
cycles += 2;
printf("BCC");
break;
case 0xb0:
mode = AM_REL;
arg = opcode_arg(mode);
bcs(arg);
arg += regs_.pc + 1;
cycles += 2;
printf("BCS");
break;
case 0xf0:
mode = AM_REL;
arg = opcode_arg(mode);
beq(arg);
arg += regs_.pc + 1;
cycles += 2;
printf("BEQ");
break;
case 0x89:
mode = AM_IMM;
arg = opcode_arg(mode);
did_memwrite = 1;
ret = bit(arg);
cycles += 2;
printf("BIT");
break;
case 0x24:
mode = AM_ZP;
arg = opcode_arg(mode);
did_memwrite = 1;
ret = bit(arg);
cycles += 3;
printf("BIT");
break;
case 0x34:
mode = AM_ZP_X;
arg = opcode_arg(mode);
did_memwrite = 1;
ret = bit(arg);
cycles += 4;
printf("BIT");
break;
case 0x2c:
mode = AM_ABS;
arg = opcode_arg(mode);
did_memwrite = 1;
ret = bit(arg);
cycles += 4;
printf("BIT");
break;
case 0x3c:
mode = AM_ABS_X;
arg = opcode_arg(mode);
did_memwrite = 1;
ret = bit(arg);
cycles += 4;
printf("BIT");
break;
case 0x30:
mode = AM_REL;
arg = opcode_arg(mode);
bmi(arg);
arg += regs_.pc + 1;
cycles += 2;
printf("BMI");
break;
case 0xd0:
mode = AM_REL;
arg = opcode_arg(mode);
bne(arg);
arg += regs_.pc + 1;
cycles += 2;
printf("BNE");
break;
case 0x10:
mode = AM_REL;
arg = opcode_arg(mode);
bpl(arg);
arg += regs_.pc + 1;
cycles += 2;
printf("BPL");
break;
case 0x00:
mode = AM_IMPLICIT;
brk();
cycles += 7;
printf("\tBRK");
goto loop_exit;
case 0x50:
mode = AM_REL;
arg = opcode_arg(mode);
bvc(arg);
arg += regs_.pc + 1;
cycles += 2;
printf("BVC");
break;
case 0x70:
mode = AM_REL;
arg = opcode_arg(mode);
bvs(arg);
arg += regs_.pc + 1;
cycles += 2;
printf("BVS");
break;
case 0x18:
mode = AM_IMPLICIT;
clc();
cycles += 2;
printf("\tCLC");
break;
case 0xd8:
mode = AM_IMPLICIT;
cld();
cycles += 2;
printf("\tCLD");
break;
case 0x58:
mode = AM_IMPLICIT;
cli();
cycles += 2;
printf("\tCLI");
break;
case 0xb8:
mode = AM_IMPLICIT;
clv();
cycles += 2;
printf("\tCLV");
break;
case 0xc9:
mode = AM_IMM;
arg = opcode_arg(mode);
cmp(arg);
cycles += 2;
printf("CMP");
break;
case 0xc5:
mode = AM_ZP;
arg = opcode_arg(mode);
cmp(arg);
cycles += 3;
printf("CMP");
break;
case 0xd5:
mode = AM_ZP_X;
arg = opcode_arg(mode);
cmp(arg);
cycles += 4;
printf("CMP");
break;
case 0xcd:
mode = AM_ABS;
arg = opcode_arg(mode);
cmp(arg);
cycles += 4;
printf("CMP");
break;
case 0xdd:
mode = AM_ABS_X;
arg = opcode_arg(mode);
cmp(arg);
cycles += 4;
printf("CMP");
break;
case 0xd9:
mode = AM_ABS_Y;
arg = opcode_arg(mode);
cmp(arg);
cycles += 4;
printf("CMP");
break;
case 0xc1:
mode = AM_IND_X;
arg = opcode_arg(mode);
cmp(arg);
cycles += 6;
printf("CMP");
break;
case 0xd1:
mode = AM_IND_Y;
arg = opcode_arg(mode);
cmp(arg);
cycles += 5;
printf("CMP");
break;
case 0xe0:
mode = AM_IMM;
arg = opcode_arg(mode);
cpx(arg);
cycles += 2;
printf("CPX");
break;
case 0xe4:
mode = AM_ZP;
arg = opcode_arg(mode);
cpx(arg);
cycles += 3;
printf("CPX");
break;
case 0xec:
mode = AM_ABS;
arg = opcode_arg(mode);
cpx(arg);
cycles += 4;
printf("CPX");
break;
case 0xc0:
mode = AM_IMM;
arg = opcode_arg(mode);
cpy(arg);
cycles += 2;
printf("CPY");
break;
case 0xc4:
mode = AM_ZP;
arg = opcode_arg(mode);
cpy(arg);
cycles += 3;
printf("CPY");
break;
case 0xcc:
mode = AM_ABS;
arg = opcode_arg(mode);
cpy(arg);
cycles += 4;
printf("CPY");
break;
case 0xc6:
mode = AM_ZP;
arg = opcode_mem(mode);
dec(arg);
cycles += 5;
printf("DEC");
break;
case 0xd6:
mode = AM_ZP_X;
arg = opcode_mem(mode);
dec(arg);
cycles += 6;
printf("DEC");
break;
case 0xce:
mode = AM_ABS;
arg = opcode_mem(mode);
dec(arg);
cycles += 6;
printf("DEC");
break;
case 0xde:
mode = AM_ABS_X;
arg = opcode_mem(mode);
dec(arg);
cycles += 7;
printf("DEC");
break;
case 0xca:
mode = AM_IMPLICIT;
dex();
cycles += 2;
printf("\tDEX");
break;
case 0x88:
mode = AM_IMPLICIT;
dey();
cycles += 2;
printf("\tDEY");
break;
case 0x49:
mode = AM_IMM;
arg = opcode_arg(mode);
eor(arg);
cycles += 2;
printf("EOR");
break;
case 0x45:
mode = AM_ZP;
arg = opcode_arg(mode);
eor(arg);
cycles += 3;
printf("EOR");
break;
case 0x55:
mode = AM_ZP_X;
arg = opcode_arg(mode);
eor(arg);
cycles += 4;
printf("EOR");
break;
case 0x4d:
mode = AM_ABS;
arg = opcode_arg(mode);
eor(arg);
cycles += 4;
printf("EOR");
break;
case 0x5d:
mode = AM_ABS_X;
arg = opcode_arg(mode);
eor(arg);
cycles += 4;
printf("EOR");
break;
case 0x59:
mode = AM_ABS_Y;
arg = opcode_arg(mode);
eor(arg);
cycles += 4;
printf("EOR");
break;
case 0x41:
mode = AM_IND_X;
arg = opcode_arg(mode);
eor(arg);
cycles += 6;
printf("EOR");
break;
case 0x51:
mode = AM_IND_Y;
arg = opcode_arg(mode);
eor(arg);
cycles += 5;
printf("EOR");
break;
case 0xe6:
mode = AM_ZP;
arg = opcode_arg(mode);
inc(arg);
cycles += 5;
printf("INC");
break;
case 0xf6:
mode = AM_ZP_X;
arg = opcode_arg(mode);
inc(arg);
cycles += 6;
printf("INC");
break;
case 0xee:
mode = AM_ABS;
arg = opcode_mem(mode);
inc(arg);
cycles += 6;
printf("INC");
break;
case 0xfe:
mode = AM_ABS_X;
arg = opcode_arg(mode);
inc(arg);
cycles += 7;
printf("INC");
break;
case 0xe8:
mode = AM_IMPLICIT;
inx();
cycles += 2;
printf("\tINX");
break;
case 0xc8:
mode = AM_IMPLICIT;
iny();
cycles += 2;
printf("\tINY");
break;
case 0x4c:
mode = AM_ABS;
arg = opcode_mem(mode);
jmp(arg);
cycles += 3;
printf("JMP");
break;
case 0x6c:
mode = AM_IND;
mem = peek16(regs.pc);
deref = peek(mem);
arg = opcode_mem(mode);
jmp(deref);
cycles += 6;
printf("JMP");
break;
case 0x7c:
mode = AM_ABS_X;
arg = opcode_arg(mode);
jmp(arg);
cycles += 6;
printf("JMP");
break;
case 0x20:
mode = AM_ABS;
arg = opcode_mem(mode);
jsr(arg);
cycles += 6;
printf("JSR");
break;
case 0xa9:
mode = AM_IMM;
arg = opcode_arg(mode);
lda(arg);
cycles += 2;
printf("LDA");
break;
case 0xa5:
mode = AM_ZP;
arg = opcode_arg(mode);
lda(arg);
cycles += 3;
printf("LDA");
break;
case 0xb5:
mode = AM_ZP_X;
arg = opcode_arg(mode);
lda(arg);
cycles += 4;
printf("LDA");
break;
case 0xad:
mode = AM_ABS;
arg = opcode_arg(mode);
lda(arg);
cycles += 4;
printf("LDA");
break;
case 0xbd:
mode = AM_ABS_X;
arg = opcode_arg(mode);
lda(arg);
cycles += 4;
printf("LDA");
break;
case 0xb9:
mode = AM_ABS_Y;
arg = opcode_arg(mode);
lda(arg);
cycles += 4;
printf("LDA");
break;
case 0xa1:
mode = AM_IND_X;
arg = opcode_arg(mode);
lda(arg);
cycles += 6;
printf("LDA");
break;
case 0xb1:
mode = AM_IND_Y;
arg = opcode_arg(mode);
lda(arg);
cycles += 5;
printf("LDA");
break;
case 0xa2:
mode = AM_IMM;
arg = opcode_mem(mode);
ldx(arg);
cycles += 2;
printf("LDX");
break;
case 0xa6:
mode = AM_ZP;
arg = opcode_arg(mode);
ldx(arg);
cycles += 3;
printf("LDX");
break;
case 0xb6:
mode = AM_ZP_Y;
arg = opcode_arg(mode);
ldx(arg);
cycles += 4;
printf("LDX");
break;
case 0xae:
mode = AM_ABS;
arg = opcode_arg(mode);
ldx(arg);
cycles += 4;
printf("LDX");
break;
case 0xbe:
mode = AM_ABS_Y;
arg = opcode_arg(mode);
ldx(arg);
cycles += 4;
printf("LDX");
break;
case 0xa0:
mode = AM_IMM;
arg = opcode_arg(mode);
ldy(arg);
cycles += 2;
printf("LDY");
break;
case 0xa4:
mode = AM_ZP;
arg = opcode_arg(mode);
ldy(arg);
cycles += 3;
printf("LDY");
break;
case 0xb4:
mode = AM_ZP_X;
arg = opcode_arg(mode);
ldy(arg);
cycles += 4;
printf("LDY");
break;
case 0xac:
mode = AM_ABS;
arg = opcode_arg(mode);
ldy(arg);
cycles += 4;
printf("LDY");
break;
case 0xbc:
mode = AM_ABS_X;
arg = opcode_arg(mode);
ldy(arg);
cycles += 4;
printf("LDY");
break;
case 0x4a:
mode = AM_ACC;
lsr_acc();
cycles += 2;
printf("\tLSR");
break;
case 0x46:
mode = AM_ZP;
arg = opcode_arg(mode);
lsr(arg);
cycles += 5;
printf("LSR");
break;
case 0x56:
mode = AM_ZP_X;
arg = opcode_arg(mode);
lsr(arg);
cycles += 6;
printf("LSR");
break;
case 0x4e:
mode = AM_ABS;
arg = opcode_mem(mode);
lsr(arg);
cycles += 6;
printf("LSR");
break;
case 0x5e:
mode = AM_ABS_X;
arg = opcode_mem(mode);
lsr(arg);
cycles += 6;
printf("LSR");
break;
case 0xea:
mode = AM_IMPLICIT;
nop();
cycles += 2;
printf("\tNOP");
break;
case 0x09:
mode = AM_IMM;
arg = opcode_arg(mode);
ora(arg);
cycles += 2;
printf("ORA");
break;
case 0x05:
mode = AM_ZP;
arg = opcode_arg(mode);
ora(arg);
cycles += 3;
printf("ORA");
break;
case 0x15:
mode = AM_ZP_X;
arg = opcode_arg(mode);
ora(arg);
cycles += 4;
printf("ORA");
break;
case 0x0d:
mode = AM_ABS;
arg = opcode_arg(mode);
ora(arg);
cycles += 4;
printf("ORA");
break;
case 0x1d:
mode = AM_ABS_X;
arg = opcode_arg(mode);
ora(arg);
cycles += 4;
printf("ORA");
break;
case 0x19:
mode = AM_ABS_Y;
arg = opcode_arg(mode);
ora(arg);
cycles += 4;
printf("ORA");
break;
case 0x01:
mode = AM_IND_X;
arg = opcode_arg(mode);
ora(arg);
cycles += 6;
printf("ORA");
break;
case 0x11:
mode = AM_IND_Y;
arg = opcode_arg(mode);
ora(arg);
cycles += 5;
printf("ORA");
break;
case 0x48:
mode = AM_IMPLICIT;
pha();
cycles += 3;
printf("\tPHA");
break;
case 0x08:
mode = AM_IMPLICIT;
php();
cycles += 3;
printf("\tPHP");
break;
case 0x68:
mode = AM_IMPLICIT;
pla();
cycles += 4;
printf("\tPLA");
break;
case 0x28:
mode = AM_IMPLICIT;
plp();
cycles += 4;
printf("\tPLP");
break;
case 0x2a:
mode = AM_ACC;
rol_acc();
cycles += 2;
printf("\tROL");
break;
case 0x26:
mode = AM_ZP;
arg = opcode_arg(mode);
rol(arg);
cycles += 5;
printf("ROL");
break;
case 0x36:
mode = AM_ZP_X;
arg = opcode_arg(mode);
rol(arg);
cycles += 6;
printf("ROL");
break;
case 0x2e:
mode = AM_ABS;
arg = opcode_arg(mode);
rol(arg);
cycles += 6;
printf("ROL");
break;
case 0x3e:
mode = AM_ABS_X;
arg = opcode_arg(mode);
rol(arg);
cycles += 6;
printf("ROL");
break;
case 0x6a:
mode = AM_ACC;
ror_acc();
cycles += 2;
printf("\tROR");
break;
case 0x66:
mode = AM_ZP;
arg = opcode_arg(mode);
ror(arg);
cycles += 5;
printf("ROR");
break;
case 0x76:
mode = AM_ZP_X;
arg = opcode_arg(mode);
ror(arg);
cycles += 6;
printf("ROR");
break;
case 0x6e:
mode = AM_ABS;
arg = opcode_arg(mode);
ror(arg);
cycles += 6;
printf("ROR");
break;
case 0x7e:
mode = AM_ABS_X;
arg = opcode_arg(mode);
ror(arg);
cycles += 6;
printf("ROR");
break;
case 0x40:
mode = AM_IMPLICIT;
rti();
cycles += 6;
printf("\tRTI");
break;
case 0x60:
mode = AM_IMPLICIT;
rts();
cycles += 6;
printf("\tRTS");
break;
case 0xe9:
mode = AM_IMM;
arg = opcode_arg(mode);
sbc(arg);
cycles += 2;
printf("SBC");
break;
case 0xe5:
mode = AM_ZP;
arg = opcode_arg(mode);
sbc(arg);
cycles += 3;
printf("SBC");
break;
case 0xf5:
mode = AM_ZP_X;
arg = opcode_arg(mode);
sbc(arg);
cycles += 4;
printf("SBC");
break;
case 0xed:
mode = AM_ABS;
arg = opcode_arg(mode);
sbc(arg);
cycles += 4;
printf("SBC");
break;
case 0xfd:
mode = AM_ABS_X;
arg = opcode_arg(mode);
sbc(arg);
cycles += 4;
printf("SBC");
break;
case 0xf9:
mode = AM_ABS_Y;
arg = opcode_arg(mode);
sbc(arg);
cycles += 4;
printf("SBC");
break;
case 0xe1:
mode = AM_IND_X;
arg = opcode_arg(mode);
sbc(arg);
cycles += 6;
printf("SBC");
break;
case 0xf1:
mode = AM_IND_Y;
arg = opcode_arg(mode);
sbc(arg);
cycles += 5;
printf("SBC");
break;
case 0x38:
mode = AM_IMPLICIT;
sec();
cycles += 2;
printf("\tSEC");
break;
case 0xf8:
mode = AM_IMPLICIT;
sed();
cycles += 2;
printf("\tSED");
break;
case 0x78:
mode = AM_IMPLICIT;
sei();
cycles += 2;
printf("\tSEI");
break;
case 0x85:
mode = AM_ZP;
arg = opcode_mem(mode);
did_memwrite = 1;
ret = sta(arg);
cycles += 4;
printf("STA");
break;
case 0x95:
mode = AM_ZP_X;
arg = opcode_mem(mode);
did_memwrite = 1;
ret = sta(arg);
cycles += 5;
printf("STA");
break;
case 0x8d:
mode = AM_ABS;
arg = opcode_mem(mode);
did_memwrite = 1;
ret = sta(arg);
cycles += 5;
printf("STA");
break;
case 0x9d:
mode = AM_ABS_X;
arg = opcode_mem(mode);
did_memwrite = 1;
ret = sta(arg);
cycles += 6;
printf("STA");
break;
case 0x99:
mode = AM_ABS_Y;
arg = opcode_mem(mode);
did_memwrite = 1;
ret = sta(arg);
cycles += 6;
printf("STA");
break;
case 0x81:
mode = AM_IND_X;
arg = opcode_mem(mode);
did_memwrite = 1;
ret = sta(arg);
cycles += 7;
printf("STA");
break;
case 0x91:
mode = AM_IND_Y;
arg = opcode_mem(mode);
did_memwrite = 1;
ret = sta(arg);
cycles += 7;
printf("STA");
break;
case 0x86:
mode = AM_ZP;
arg = opcode_mem(mode);
did_memwrite = 1;
ret = stx(arg);
cycles += 4;
printf("STX");
break;
case 0x96:
mode = AM_ZP_Y;
arg = opcode_mem(mode);
did_memwrite = 1;
ret = stx(arg);
cycles += 5;
printf("STX");
break;
case 0x8e:
mode = AM_ABS;
arg = opcode_mem(mode);
did_memwrite = 1;
ret = stx(arg);
cycles += 5;
printf("STX");
break;
case 0x84:
mode = AM_ZP;
arg = opcode_mem(mode);
did_memwrite = 1;
ret = sty(arg);
cycles += 4;
printf("STY");
break;
case 0x94:
mode = AM_ZP_X;
arg = opcode_mem(mode);
did_memwrite = 1;
ret = sty(arg);
cycles += 5;
printf("STY");
break;
case 0x8c:
mode = AM_ABS;
arg = opcode_mem(mode);
did_memwrite = 1;
ret = sty(arg);
cycles += 5;
printf("STY");
break;
case 0xaa:
mode = AM_IMPLICIT;
tax();
cycles += 2;
printf("\tTAX");
break;
case 0xa8:
mode = AM_IMPLICIT;
tay();
cycles += 2;
printf("\tTAY");
break;
case 0xba:
mode = AM_IMPLICIT;
tsx();
cycles += 2;
printf("\tTSX");
break;
case 0x8a:
mode = AM_IMPLICIT;
txa();
cycles += 2;
printf("\tTXA");
break;
case 0x9a:
mode = AM_IMPLICIT;
txs();
cycles += 2;
printf("\tTXS");
break;
case 0x98:
mode = AM_IMPLICIT;
tya();
cycles += 2;
printf("\tTYA");
break;
default:
printf("opcode $%02X not implemented\n", opcode);
break;
}
switch (mode) {
case AM_IMM:
printf(" #$%02X", arg);
if (did_memwrite) printf(" = %02X\t\t\t", ret);
else printf("\t\t\t");
break;
case AM_ZP:
printf(" $%02X", arg);
if (did_memwrite) printf(" = %02X\t\t\t", ret);
else printf("\t\t\t\t");
break;
case AM_ZP_X:
printf(" $%02X,X", arg);
if (did_memwrite) printf(" = %02X\t\t\t", ret);
else printf("\t\t\t\t");
break;
case AM_ZP_Y:
printf(" $%02X,Y", arg);
if (did_memwrite) printf(" = %02X\t\t\t", ret);
else printf("\t\t\t\t");
break;
case AM_REL:
case AM_ABS:
printf(" $%04X", arg);
if (did_memwrite) printf(" = %02X\t\t\t", ret);
else printf("\t\t\t");
break;
case AM_ABS_X:
printf(" $%04X,X", arg);
if (did_memwrite) printf(" = %02X\t\t\t", ret);
else printf("\t\t\t");
break;
case AM_ABS_Y:
printf(" $%04X,Y", arg);
if (did_memwrite) printf(" = %02X\t\t\t", ret);
else printf("\t\t\t");
break;
case AM_IND:
printf(" ($%04X) = %04X\t\t", mem, deref);
break;
case AM_ACC:
case AM_IMPLICIT:
default:
printf("\t\t\t\t");
break;
}
printf("A:%02X X:%02X Y:%02X P:%02X SP:%02X CYC:%d\n",
regs_.a, regs_.x, regs_.y, status, regs_.sp, cycles_);
}
loop_exit:
}
/* https://www.nesdev.org/wiki/CPU_power_up_state */
void
cpu_init(void)
{
regs.a = regs.x = regs.y = 0;
regs.pc = 0xFFFC;
regs.sp = 0xFD;
//memset(&regs.status, 0, sizeof(regs.status));
regs.status.interrupt_disable = 1;
regs.status.unused = 1;
cycles += 7;
}
int
main(int argc, char *argv[])
{
FILE *fp;
uint8_t *buf;
size_t buflen;
if (argc != 2) {
fprintf(stderr, "Usage: %s rom.nes\n", basename(argv[0]));
return 1;
}
fp = fopen(argv[1], "r");
fseek(fp, 0, SEEK_END);
buflen = ftell(fp);
buf = calloc(1, buflen);
fseek(fp, 0, SEEK_SET);
if (fread(buf, 1, buflen, fp) != buflen && ferror(fp)) {
fprintf(stderr, "file %s was not read properly\n", argv[1]);
clearerr(fp);
fclose(fp);
return 1;
}
fclose(fp);
parse_rom(buf, buflen, &rom);
free(buf);
cpu_init();
/* TODO: move to separate file? */
if (rom.mapper != 0) {
fprintf(stderr, "Only iNES ROMs using Mapper 0 are supported for now.\n");
return 1;
}
memwrite16(0xFFFC, 0xC000);
regs.pc = 0xC000;
interpret();
printf("\n$02 = %02X\n", memory[0x02]);
printf("$03 = %02X\n", memory[0x03]);
free_rom(&rom);
return 0;
}