emu_nes/cpu.c

1704 lines
26 KiB
C
Raw Normal View History

2024-05-20 10:09:19 -04:00
#include <stdint.h>
#include <stdio.h>
2024-06-08 08:11:06 -04:00
#include <stdlib.h>
#include <string.h>
2024-06-08 08:11:06 -04:00
#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))
2024-05-20 10:09:19 -04:00
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;
2024-05-20 10:09:19 -04:00
struct cpu_flags status;
uint16_t pc;
};
struct registers regs;
enum addressing_mode {
2024-06-04 06:46:11 -04:00
AM_ACC,
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,
2024-05-20 10:09:19 -04:00
};
2024-06-04 06:24:09 -04:00
/* 64K address space, 16bit words */
uint8_t memory[0x16000];
2024-05-20 10:09:19 -04:00
/* example program taken from https://bugzmanov.github.io/nes_ebook/chapter_3_1.html */
uint8_t program[] = { 0xa9, 0xc0, 0xaa, 0xe8, 0x00 };
uint32_t cycles = 0;
2024-06-09 06:37:09 -04:00
static uint8_t
2024-06-08 08:11:06 -04:00
peek(uint16_t addr)
2024-06-04 06:24:09 -04:00
{
return memory[addr];
}
2024-06-09 06:37:09 -04:00
static uint16_t
2024-06-04 06:24:09 -04:00
peek16(uint16_t addr)
{
/* bytes are stored in little-endian (low then high) */
return (uint16_t)memory[addr] | ((uint16_t)memory[addr + 1] << 8);
}
2024-06-09 06:37:09 -04:00
static void
memwrite(uint16_t addr, uint8_t byte)
{
memory[addr] = b;
}
static void
memwrite16(uint16_t addr, uint16_t word)
{
/* 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)
2024-06-08 08:11:06 -04:00
{
uint8_t arg, val;
if (mode != AM_ABS && mode != AM_ABS_X && mode != AM_ABS_Y)
arg = peek(regs.pc++);
else
arg = peek16(regs.pc), regs.pc += 2;
2024-06-08 08:11:06 -04:00
switch (mode) {
case AM_IMM:
case AM_REL:
2024-06-08 08:11:06 -04:00
val = arg;
break;
case AM_ZP:
2024-06-08 08:11:06 -04:00
val = peek(arg % 256);
break;
case AM_ZP_X:
2024-06-08 08:11:06 -04:00
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:
2024-06-08 08:11:06 -04:00
val = peek16(arg + regs.x);
break;
case AM_ABS_Y:
2024-06-08 08:11:06 -04:00
val = peek16(arg + regs.y);
break;
case AM_IND_X:
2024-06-08 08:11:06 -04:00
val = peek(peek((arg + regs.x) % 256) + peek((arg + regs.x + 1) % 256) * 256);
break;
case AM_IND_Y:
2024-06-08 08:11:06 -04:00
val = peek(peek(arg) + peek((arg + 1) % 256) * 256 + regs.y);
break;
default:
fprintf(stderr, "INVALID ADDRESSING MODE\n");
2024-06-08 08:11:06 -04:00
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;
2024-06-08 08:11:06 -04:00
regs.a = sum & 0xFF;
regs.status.carry = sum > 0xFF;
/* overflow flag formula: https://stackoverflow.com/a/29224684 */
2024-06-09 06:05:31 -04:00
regs.status.overflow = (~(regs.a ^ arg) & (regs.a ^ sum) & 0x80) > 0;
2024-06-08 08:11:06 -04:00
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(uint8_t arg)
{
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
bbr(uint8_t arg)
{
/* TODO: complete this */
}
static void
bbs(uint8_t arg)
{
/* TODO: complete this */
}
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 void
bit(uint8_t arg)
{
regs.status.zero = (regs.a & arg) == 0;
regs.status.overflow = (arg & (1 << 6)) > 0;
regs.status.negative = (arg & (1 << 7)) > 0;
}
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
bra(uint8_t arg)
{
/* TODO: complete this */
}
static void
brk(void)
2024-05-24 02:20:08 -04:00
{
/* TODO: push regs.pc and regs.status to stack and load IRQ vector */
regs.status.brk = 1;
exit(0);
2024-05-24 02:20:08 -04:00
}
static void
bvc(uint8_t arg)
{
if (regs.status.overflow == 0)
regs.pc += arg;
}
static void
bvs(uint8_t arg)
{
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;
regs.status.negative = (tmp & (1 << 7)) > 0;
}
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;
regs.status.negative = (tmp & (1 << 7)) > 0;
}
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;
regs.status.negative = (tmp & (1 << 7)) > 0;
}
static void
dec(uint8_t arg)
{
/* TODO: complete this */
}
static void
dex(uint8_t arg)
{
/* TODO: complete this */
}
static void
dey(uint8_t arg)
{
/* TODO: complete this */
}
static void
eor(uint8_t arg)
{
/* TODO: complete this */
}
static void
inc(uint8_t arg)
{
/* TODO: complete this */
}
static void
inx(void)
{
regs.x++;
STATUS_UPDATE_ZERO(regs.x);
STATUS_UPDATE_NEGATIVE(regs.x);
}
static void
iny(uint8_t arg)
{
/* TODO: complete this */
}
static void
jmp(uint8_t arg)
{
/* TODO: complete this */
}
static void
jsr(uint8_t arg)
{
/* TODO: complete this */
}
static void
lda(uint8_t arg)
2024-05-24 02:20:08 -04:00
{
2024-06-04 06:24:09 -04:00
printf("arg1 $%02X\n", arg);
regs.a = arg;
2024-06-04 06:24:09 -04:00
STATUS_UPDATE_ZERO(regs.a);
STATUS_UPDATE_NEGATIVE(regs.a);
2024-05-24 02:20:08 -04:00
}
static void
ldx(uint8_t arg)
{
/* TODO: complete this */
}
static void
ldy(uint8_t arg)
{
/* TODO: complete this */
}
static void
lsr(uint8_t arg)
{
/* TODO: complete this */
}
static void
nop(uint8_t arg)
{
/* TODO: complete this */
}
static void
ora(uint8_t arg)
{
/* TODO: complete this */
}
static void
pha(uint8_t arg)
{
/* TODO: complete this */
}
static void
php(uint8_t arg)
{
/* TODO: complete this */
}
static void
phx(uint8_t arg)
{
/* TODO: complete this */
}
static void
phy(uint8_t arg)
{
/* TODO: complete this */
}
static void
pla(uint8_t arg)
{
/* TODO: complete this */
}
static void
plp(uint8_t arg)
{
/* TODO: complete this */
}
static void
plx(uint8_t arg)
{
/* TODO: complete this */
}
static void
ply(uint8_t arg)
{
/* TODO: complete this */
}
static void
rmb(uint8_t arg)
{
/* TODO: complete this */
}
static void
rol(uint8_t arg)
{
/* TODO: complete this */
}
static void
ror(uint8_t arg)
{
/* TODO: complete this */
}
static void
rti(uint8_t arg)
{
/* TODO: complete this */
}
static void
rts(uint8_t arg)
{
/* TODO: complete this */
}
static void
sbc(uint8_t arg)
{
/* SBC is described online as ADC with argument as two's complement */
adc(~arg + 1);
}
static void
sec(uint8_t arg)
{
/* TODO: complete this */
}
static void
sed(uint8_t arg)
{
/* TODO: complete this */
}
static void
sei(uint8_t arg)
{
/* TODO: complete this */
}
static void
smb(uint8_t arg)
{
/* TODO: complete this */
}
static void
sta(uint8_t arg)
{
/* TODO: complete this */
}
static void
stp(uint8_t arg)
{
/* TODO: complete this */
}
static void
stx(uint8_t arg)
{
/* TODO: complete this */
}
static void
sty(uint8_t arg)
{
/* TODO: complete this */
}
static void
stz(uint8_t arg)
{
/* TODO: complete this */
}
static void
tax(void)
{
regs.x = regs.a;
STATUS_UPDATE_ZERO(regs.x);
STATUS_UPDATE_NEGATIVE(regs.x);
}
static void
tay(uint8_t arg)
{
/* TODO: complete this */
}
static void
trb(uint8_t arg)
{
/* TODO: complete this */
}
static void
tsb(uint8_t arg)
{
/* TODO: complete this */
}
static void
tsx(uint8_t arg)
{
/* TODO: complete this */
}
static void
txa(uint8_t arg)
{
/* TODO: complete this */
}
static void
txs(uint8_t arg)
{
/* TODO: complete this */
}
static void
tya(uint8_t arg)
{
/* TODO: complete this */
}
static void
wai(uint8_t arg)
{
/* TODO: complete this */
}
static void
unp(uint8_t arg)
{
/* TODO: complete this */
}
2024-06-09 06:37:09 -04:00
static void
interpret(void)
2024-05-20 10:09:19 -04:00
{
uint8_t opcode;
for (;;) {
2024-06-04 06:24:09 -04:00
opcode = peek(regs.pc++);
2024-05-20 10:09:19 -04:00
printf("opcode: $%02X\n", opcode);
2024-05-20 10:09:19 -04:00
switch (opcode) {
case 0x69:
adc(opcode_arg(AM_IMM));
cycles += 2;
2024-06-08 08:11:06 -04:00
break;
case 0x65:
adc(opcode_arg(AM_ZP));
cycles += 3;
2024-06-08 08:11:06 -04:00
break;
case 0x75:
adc(opcode_arg(AM_ZP_X));
cycles += 4;
break;
case 0x6d:
adc(opcode_arg(AM_ABS));
cycles += 4;
2024-06-08 08:11:06 -04:00
break;
case 0x7d:
adc(opcode_arg(AM_ABS_X));
cycles += 4;
break;
case 0x79:
adc(opcode_arg(AM_ABS_Y));
cycles += 4;
break;
case 0x72:
adc(opcode_arg(AM_IND));
cycles += 5;
break;
case 0x61:
adc(opcode_arg(AM_IND_X));
cycles += 6;
2024-06-08 08:11:06 -04:00
break;
case 0x71:
adc(opcode_arg(AM_IND_Y));
cycles += 5;
2024-06-08 08:11:06 -04:00
break;
case 0x29:
and(opcode_arg(AM_IMM));
cycles += 2;
2024-06-08 08:11:06 -04:00
break;
case 0x25:
and(opcode_arg(AM_ZP));
cycles += 3;
2024-06-08 08:11:06 -04:00
break;
case 0x35:
and(opcode_arg(AM_ZP_X));
cycles += 4;
2024-06-08 08:11:06 -04:00
break;
case 0x2d:
and(opcode_arg(AM_ABS));
cycles += 4;
2024-05-24 02:20:08 -04:00
break;
case 0x3d:
and(opcode_arg(AM_ABS_X));
cycles += 4;
break;
case 0x39:
and(opcode_arg(AM_ABS_Y));
cycles += 4;
break;
case 0x32:
and(opcode_arg(AM_IND));
cycles += 5;
break;
case 0x21:
and(opcode_arg(AM_IND_X));
cycles += 6;
break;
case 0x31:
and(opcode_arg(AM_IND_Y));
cycles += 5;
break;
case 0x0a:
asl(opcode_arg(AM_ACC));
cycles += 2;
break;
case 0x06:
asl(opcode_arg(AM_ZP));
cycles += 5;
break;
case 0x16:
asl(opcode_arg(AM_ZP_X));
cycles += 6;
break;
case 0x0e:
asl(opcode_arg(AM_ABS));
cycles += 6;
break;
case 0x1e:
asl(opcode_arg(AM_ABS_X));
cycles += 6;
break;
case 0x0f:
bbr(opcode_arg(AM_REL));
cycles += 4;
break;
case 0x1f:
bbr(opcode_arg(AM_REL));
cycles += 4;
break;
case 0x2f:
bbr(opcode_arg(AM_REL));
cycles += 4;
break;
case 0x3f:
bbr(opcode_arg(AM_REL));
cycles += 4;
break;
case 0x4f:
bbr(opcode_arg(AM_REL));
cycles += 4;
break;
case 0x5f:
bbr(opcode_arg(AM_REL));
cycles += 4;
break;
case 0x6f:
bbr(opcode_arg(AM_REL));
cycles += 4;
break;
case 0x7f:
bbr(opcode_arg(AM_REL));
cycles += 4;
break;
case 0x8f:
bbs(opcode_arg(AM_REL));
cycles += 4;
break;
case 0x9f:
bbs(opcode_arg(AM_REL));
cycles += 4;
break;
case 0xaf:
bbs(opcode_arg(AM_REL));
cycles += 4;
break;
case 0xbf:
bbs(opcode_arg(AM_REL));
cycles += 4;
break;
case 0xcf:
bbs(opcode_arg(AM_REL));
cycles += 4;
break;
case 0xdf:
bbs(opcode_arg(AM_REL));
cycles += 4;
break;
case 0xef:
bbs(opcode_arg(AM_REL));
cycles += 4;
break;
case 0xff:
bbs(opcode_arg(AM_REL));
cycles += 4;
break;
case 0x90:
bcc(opcode_arg(AM_IMM));
cycles += 2;
break;
case 0xb0:
bcs(opcode_arg(AM_IMM));
cycles += 2;
break;
case 0xf0:
beq(opcode_arg(AM_IMM));
cycles += 2;
break;
case 0x89:
bit(opcode_arg(AM_IMM));
cycles += 2;
break;
case 0x24:
bit(opcode_arg(AM_ZP));
cycles += 3;
break;
case 0x34:
bit(opcode_arg(AM_ZP_X));
cycles += 4;
break;
case 0x2c:
bit(opcode_arg(AM_ABS));
cycles += 4;
break;
case 0x3c:
bit(opcode_arg(AM_ABS_X));
cycles += 4;
break;
case 0x30:
bmi(opcode_arg(AM_IMM));
cycles += 2;
break;
case 0xd0:
bne(opcode_arg(AM_IMM));
cycles += 2;
break;
case 0x10:
bpl(opcode_arg(AM_IMM));
cycles += 2;
break;
case 0x80:
bra(opcode_arg(AM_IMM));
cycles += 3;
break;
case 0x00:
brk();
cycles += 7;
break;
case 0x50:
bvc(opcode_arg(AM_IMM));
cycles += 2;
break;
case 0x70:
bvs(opcode_arg(AM_IMM));
cycles += 2;
break;
case 0x18:
clc(opcode_arg(AM_ACC));
cycles += 2;
break;
case 0xd8:
cld(opcode_arg(AM_ACC));
cycles += 2;
break;
case 0x58:
cli(opcode_arg(AM_ACC));
cycles += 2;
break;
case 0xb8:
clv(opcode_arg(AM_ACC));
cycles += 2;
break;
case 0xc9:
cmp(opcode_arg(AM_IMM));
cycles += 2;
break;
case 0xc5:
cmp(opcode_arg(AM_ZP));
cycles += 3;
break;
case 0xd5:
cmp(opcode_arg(AM_ZP_X));
cycles += 4;
break;
case 0xcd:
cmp(opcode_arg(AM_ABS));
cycles += 4;
break;
case 0xdd:
cmp(opcode_arg(AM_ABS_X));
cycles += 4;
break;
case 0xd9:
cmp(opcode_arg(AM_ABS_Y));
cycles += 4;
break;
case 0xd2:
cmp(opcode_arg(AM_IND));
cycles += 5;
break;
case 0xc1:
cmp(opcode_arg(AM_IND_X));
cycles += 6;
break;
case 0xd1:
cmp(opcode_arg(AM_IND_Y));
cycles += 5;
break;
case 0xe0:
cpx(opcode_arg(AM_IMM));
cycles += 2;
break;
case 0xe4:
cpx(opcode_arg(AM_ZP));
cycles += 3;
break;
case 0xec:
cpx(opcode_arg(AM_ABS));
cycles += 4;
break;
case 0xc0:
cpy(opcode_arg(AM_IMM));
cycles += 2;
break;
case 0xc4:
cpy(opcode_arg(AM_ZP));
cycles += 3;
break;
case 0xcc:
cpy(opcode_arg(AM_ABS));
cycles += 4;
break;
case 0x3a:
dec(opcode_arg(AM_ACC));
cycles += 2;
break;
case 0xc6:
dec(opcode_arg(AM_ZP));
cycles += 5;
break;
case 0xd6:
dec(opcode_arg(AM_ZP_X));
cycles += 6;
break;
case 0xce:
dec(opcode_arg(AM_ABS));
cycles += 6;
break;
case 0xde:
dec(opcode_arg(AM_ABS_X));
cycles += 7;
break;
case 0xca:
dex(opcode_arg(AM_ACC));
cycles += 2;
break;
case 0x88:
dey(opcode_arg(AM_ACC));
cycles += 2;
break;
case 0x49:
eor(opcode_arg(AM_IMM));
cycles += 2;
break;
case 0x45:
eor(opcode_arg(AM_ZP));
cycles += 3;
break;
case 0x55:
eor(opcode_arg(AM_ZP_X));
cycles += 4;
break;
case 0x4d:
eor(opcode_arg(AM_ABS));
cycles += 4;
break;
case 0x5d:
eor(opcode_arg(AM_ABS_X));
cycles += 4;
break;
case 0x59:
eor(opcode_arg(AM_ABS_Y));
cycles += 4;
break;
case 0x52:
eor(opcode_arg(AM_IND));
cycles += 5;
break;
case 0x41:
eor(opcode_arg(AM_IND_X));
cycles += 6;
break;
case 0x51:
eor(opcode_arg(AM_IND_Y));
cycles += 5;
break;
case 0x1a:
inc(opcode_arg(AM_ACC));
cycles += 2;
break;
case 0xe6:
inc(opcode_arg(AM_ZP));
cycles += 5;
break;
case 0xf6:
inc(opcode_arg(AM_ZP_X));
cycles += 6;
break;
case 0xee:
inc(opcode_arg(AM_ABS));
cycles += 6;
break;
case 0xfe:
inc(opcode_arg(AM_ABS_X));
cycles += 7;
break;
case 0xe8:
inx();
cycles += 2;
break;
case 0xc8:
iny(opcode_arg(AM_ACC));
cycles += 2;
break;
case 0x4c:
jmp(opcode_arg(AM_ABS));
cycles += 3;
break;
case 0x6c:
jmp(opcode_arg(AM_IND));
cycles += 6;
break;
case 0x7c:
jmp(opcode_arg(AM_ABS_X));
cycles += 6;
break;
case 0x20:
jsr(opcode_arg(AM_ABS));
cycles += 6;
2024-05-24 02:20:08 -04:00
break;
case 0xa9:
lda(opcode_arg(AM_IMM));
cycles += 2;
break;
case 0xa5:
lda(opcode_arg(AM_ZP));
cycles += 3;
break;
case 0xb5:
lda(opcode_arg(AM_ZP_X));
cycles += 4;
2024-05-24 02:20:08 -04:00
break;
case 0xad:
lda(opcode_arg(AM_ABS));
cycles += 4;
break;
case 0xbd:
lda(opcode_arg(AM_ABS_X));
cycles += 4;
break;
case 0xb9:
lda(opcode_arg(AM_ABS_Y));
cycles += 4;
break;
case 0xb2:
lda(opcode_arg(AM_IND));
cycles += 5;
break;
case 0xa1:
lda(opcode_arg(AM_IND_X));
cycles += 6;
2024-05-24 02:20:08 -04:00
break;
case 0xb1:
lda(opcode_arg(AM_IND_Y));
cycles += 5;
break;
case 0xa2:
ldx(opcode_arg(AM_IMM));
cycles += 2;
2024-05-24 02:20:08 -04:00
break;
case 0xa6:
ldx(opcode_arg(AM_ZP));
cycles += 3;
2024-05-24 02:20:08 -04:00
break;
case 0xb6:
ldx(opcode_arg(AM_ZP_Y));
cycles += 4;
2024-05-24 02:20:08 -04:00
break;
case 0xae:
ldx(opcode_arg(AM_ABS));
cycles += 4;
break;
case 0xbe:
ldx(opcode_arg(AM_ABS_Y));
cycles += 4;
break;
case 0xa0:
ldy(opcode_arg(AM_IMM));
cycles += 2;
break;
case 0xa4:
ldy(opcode_arg(AM_ZP));
cycles += 3;
break;
case 0xb4:
ldy(opcode_arg(AM_ZP_X));
cycles += 4;
break;
case 0xac:
ldy(opcode_arg(AM_ABS));
cycles += 4;
break;
case 0xbc:
ldy(opcode_arg(AM_ABS_X));
cycles += 4;
break;
case 0x4a:
lsr(opcode_arg(AM_ACC));
cycles += 2;
break;
case 0x46:
lsr(opcode_arg(AM_ZP));
cycles += 5;
break;
case 0x56:
lsr(opcode_arg(AM_ZP_X));
cycles += 6;
break;
case 0x4e:
lsr(opcode_arg(AM_ABS));
cycles += 6;
break;
case 0x5e:
lsr(opcode_arg(AM_ABS_X));
cycles += 6;
break;
case 0xea:
nop(opcode_arg(AM_ACC));
cycles += 2;
break;
case 0x09:
ora(opcode_arg(AM_IMM));
cycles += 2;
break;
case 0x05:
ora(opcode_arg(AM_ZP));
cycles += 3;
break;
case 0x15:
ora(opcode_arg(AM_ZP_X));
cycles += 4;
break;
case 0x0d:
ora(opcode_arg(AM_ABS));
cycles += 4;
break;
case 0x1d:
ora(opcode_arg(AM_ABS_X));
cycles += 4;
break;
case 0x19:
ora(opcode_arg(AM_ABS_Y));
cycles += 4;
break;
case 0x12:
ora(opcode_arg(AM_IND));
cycles += 5;
break;
case 0x01:
ora(opcode_arg(AM_IND_X));
cycles += 6;
break;
case 0x11:
ora(opcode_arg(AM_IND_Y));
cycles += 5;
break;
case 0x48:
pha(opcode_arg(AM_ACC));
cycles += 3;
break;
case 0x08:
php(opcode_arg(AM_ACC));
cycles += 3;
break;
case 0xda:
phx(opcode_arg(AM_ACC));
cycles += 3;
break;
case 0x5a:
phy(opcode_arg(AM_ACC));
cycles += 3;
break;
case 0x68:
pla(opcode_arg(AM_ACC));
cycles += 4;
break;
case 0x28:
plp(opcode_arg(AM_ACC));
cycles += 4;
break;
case 0xfa:
plx(opcode_arg(AM_ACC));
cycles += 4;
break;
case 0x7a:
ply(opcode_arg(AM_ACC));
cycles += 4;
break;
case 0x07:
rmb(opcode_arg(AM_REL));
cycles += 5;
break;
case 0x17:
rmb(opcode_arg(AM_REL));
cycles += 5;
break;
case 0x27:
rmb(opcode_arg(AM_REL));
cycles += 5;
break;
case 0x37:
rmb(opcode_arg(AM_REL));
cycles += 5;
break;
case 0x47:
rmb(opcode_arg(AM_REL));
cycles += 5;
break;
case 0x57:
rmb(opcode_arg(AM_REL));
cycles += 5;
break;
case 0x67:
rmb(opcode_arg(AM_REL));
cycles += 5;
break;
case 0x77:
rmb(opcode_arg(AM_REL));
cycles += 5;
break;
case 0x2a:
rol(opcode_arg(AM_ACC));
cycles += 2;
break;
case 0x26:
rol(opcode_arg(AM_ZP));
cycles += 5;
break;
case 0x36:
rol(opcode_arg(AM_ZP_X));
cycles += 6;
break;
case 0x2e:
rol(opcode_arg(AM_ABS));
cycles += 6;
break;
case 0x3e:
rol(opcode_arg(AM_ABS_X));
cycles += 6;
break;
case 0x6a:
ror(opcode_arg(AM_ACC));
cycles += 2;
break;
case 0x66:
ror(opcode_arg(AM_ZP));
cycles += 5;
break;
case 0x76:
ror(opcode_arg(AM_ZP_X));
cycles += 6;
break;
case 0x6e:
ror(opcode_arg(AM_ABS));
cycles += 6;
break;
case 0x7e:
ror(opcode_arg(AM_ABS_X));
cycles += 6;
break;
case 0x40:
rti(opcode_arg(AM_ACC));
cycles += 6;
break;
case 0x60:
rts(opcode_arg(AM_ACC));
cycles += 6;
break;
case 0xe9:
sbc(opcode_arg(AM_IMM));
cycles += 2;
break;
case 0xe5:
sbc(opcode_arg(AM_ZP));
cycles += 3;
break;
case 0xf5:
sbc(opcode_arg(AM_ZP_X));
cycles += 4;
break;
case 0xed:
sbc(opcode_arg(AM_ABS));
cycles += 4;
break;
case 0xfd:
sbc(opcode_arg(AM_ABS_X));
cycles += 4;
break;
case 0xf9:
sbc(opcode_arg(AM_ABS_Y));
cycles += 4;
break;
case 0xf2:
sbc(opcode_arg(AM_IND));
cycles += 5;
break;
case 0xe1:
sbc(opcode_arg(AM_IND_X));
cycles += 6;
break;
case 0xf1:
sbc(opcode_arg(AM_IND_Y));
cycles += 5;
break;
case 0x38:
sec(opcode_arg(AM_ACC));
cycles += 2;
break;
case 0xf8:
sed(opcode_arg(AM_ACC));
cycles += 2;
break;
case 0x78:
sei(opcode_arg(AM_ACC));
cycles += 2;
break;
case 0x87:
smb(opcode_arg(AM_REL));
cycles += 5;
break;
case 0x97:
smb(opcode_arg(AM_REL));
cycles += 5;
break;
case 0xa7:
smb(opcode_arg(AM_REL));
cycles += 5;
break;
case 0xb7:
smb(opcode_arg(AM_REL));
cycles += 5;
break;
case 0xc7:
smb(opcode_arg(AM_REL));
cycles += 5;
break;
case 0xd7:
smb(opcode_arg(AM_REL));
cycles += 5;
break;
case 0xe7:
smb(opcode_arg(AM_REL));
cycles += 5;
break;
case 0xf7:
smb(opcode_arg(AM_REL));
cycles += 5;
break;
case 0x85:
sta(opcode_arg(AM_ZP));
cycles += 4;
break;
case 0x95:
sta(opcode_arg(AM_ZP_X));
cycles += 5;
break;
case 0x8d:
sta(opcode_arg(AM_ABS));
cycles += 5;
break;
case 0x9d:
sta(opcode_arg(AM_ABS_X));
cycles += 6;
break;
case 0x99:
sta(opcode_arg(AM_ABS_Y));
cycles += 6;
break;
case 0x92:
sta(opcode_arg(AM_IND));
cycles += 6;
break;
case 0x81:
sta(opcode_arg(AM_IND_X));
cycles += 7;
break;
case 0x91:
sta(opcode_arg(AM_IND_Y));
cycles += 7;
break;
case 0xdb:
stp(opcode_arg(AM_ACC));
cycles += 2;
break;
case 0x86:
stx(opcode_arg(AM_ZP));
cycles += 4;
break;
case 0x96:
stx(opcode_arg(AM_ZP_Y));
cycles += 5;
break;
case 0x8e:
stx(opcode_arg(AM_ABS));
cycles += 5;
break;
case 0x84:
sty(opcode_arg(AM_ZP));
cycles += 4;
break;
case 0x94:
sty(opcode_arg(AM_ZP_X));
cycles += 5;
break;
case 0x8c:
sty(opcode_arg(AM_ABS));
cycles += 5;
break;
case 0x64:
stz(opcode_arg(AM_ZP));
cycles += 4;
break;
case 0x74:
stz(opcode_arg(AM_ZP_X));
cycles += 5;
break;
case 0x9c:
stz(opcode_arg(AM_ABS));
cycles += 5;
break;
case 0x9e:
stz(opcode_arg(AM_ABS_X));
cycles += 6;
break;
case 0xaa:
tax();
cycles += 2;
break;
case 0xa8:
tay(opcode_arg(AM_ACC));
cycles += 2;
break;
case 0x14:
trb(opcode_arg(AM_ZP));
cycles += 5;
break;
case 0x1c:
trb(opcode_arg(AM_ABS));
cycles += 6;
break;
case 0x04:
tsb(opcode_arg(AM_ZP));
cycles += 5;
break;
case 0x0c:
tsb(opcode_arg(AM_ABS));
cycles += 6;
break;
case 0xba:
tsx(opcode_arg(AM_ACC));
cycles += 2;
break;
case 0x8a:
txa(opcode_arg(AM_ACC));
cycles += 2;
break;
case 0x9a:
txs(opcode_arg(AM_ACC));
cycles += 2;
break;
case 0x98:
tya(opcode_arg(AM_ACC));
cycles += 2;
break;
case 0xcb:
wai(opcode_arg(AM_ACC));
cycles += 5;
break;
case 0x03:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0x0b:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0x13:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0x1b:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0x23:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0x2b:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0x33:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0x3b:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0x43:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0x4b:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0x53:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0x5b:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0x63:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0x6b:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0x73:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0x7b:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0x83:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0x8b:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0x93:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0x9b:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0xa3:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0xab:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0xb3:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0xbb:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0xc3:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0xd3:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0xe3:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0xeb:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0xf3:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0xfb:
unp(opcode_arg(AM_ACC));
cycles += 1;
break;
case 0x02:
unp(opcode_arg(AM_IMM));
cycles += 2;
break;
case 0x22:
unp(opcode_arg(AM_IMM));
cycles += 2;
break;
case 0x42:
unp(opcode_arg(AM_IMM));
cycles += 2;
break;
case 0x62:
unp(opcode_arg(AM_IMM));
cycles += 2;
break;
case 0x82:
unp(opcode_arg(AM_IMM));
cycles += 2;
break;
case 0xc2:
unp(opcode_arg(AM_IMM));
cycles += 2;
break;
case 0xe2:
unp(opcode_arg(AM_IMM));
cycles += 2;
break;
case 0x44:
unp(opcode_arg(AM_ZP));
cycles += 3;
break;
case 0x54:
unp(opcode_arg(AM_ZP_X));
cycles += 4;
break;
case 0xd4:
unp(opcode_arg(AM_ZP_X));
cycles += 4;
break;
case 0xf4:
unp(opcode_arg(AM_ZP_X));
cycles += 4;
break;
case 0x5c:
unp(opcode_arg(AM_ABS));
cycles += 8;
break;
case 0xdc:
unp(opcode_arg(AM_ABS_X));
cycles += 4;
break;
case 0xfc:
unp(opcode_arg(AM_ABS_X));
cycles += 4;
2024-05-24 02:20:08 -04:00
break;
2024-05-20 10:09:19 -04:00
default:
printf("opcode $%02X not implemented\n", opcode);
2024-05-20 10:09:19 -04:00
break;
}
printf("status: %i%i%i%i%i%i%i%i\n", regs.status.carry,
regs.status.zero, regs.status.interrupt_disable,
regs.status.decimal_mode, regs.status.brk,
regs.status.unused, regs.status.overflow,
regs.status.negative);
printf("A: $%02X, X: $%02X, Y: $%02X, SP: $%02X, PC: $%02X\n",
regs.a, regs.x, regs.y, regs.sp, regs.pc);
2024-05-20 10:09:19 -04:00
}
}
2024-06-04 06:24:09 -04:00
/* 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.unused = 1;
}
int
main(void)
{
2024-06-04 06:24:09 -04:00
cpu_init();
if (sizeof(program) > (0x10000 - 0x8000)) {
fprintf(stderr, "program is too big for memory\n");
return 1;
}
2024-06-08 08:11:06 -04:00
memcpy(memory + 0x8000, program, sizeof(program));
regs.pc = 0x8000;
printf("Initial State:\n");
printf("status: %i%i%i%i%i%i%i%i\n", regs.status.carry,
regs.status.zero, regs.status.interrupt_disable,
regs.status.decimal_mode, regs.status.brk,
regs.status.unused, regs.status.overflow,
regs.status.negative);
printf("A: $%02X, X: $%02X, Y: $%02X, SP: $%02X, PC: $%02X\n",
regs.a, regs.x, regs.y, regs.sp, regs.pc);
putchar('\n');
interpret();
return 0;
}