#include #include #include #define STATUS_UPDATE_ZERO(r) \ (regs.status.zero = r == 0) #define STATUS_UPDATE_NEGATIVE(r) \ (regs.status.negative = ((r & (1 << 7)) > 0)) 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; enum addressing_mode { AM_ACCUMULATOR, AM_IMMEDIATE, AM_ZERO_PAGE, AM_ZERO_PAGE_X, AM_ZERO_PAGE_Y, AM_RELATIVE, AM_ABSOLUTE, AM_ABSOLUTE_X, AM_ABSOLUTE_Y, AM_INDIRECT, AM_INDIRECT_X, AM_INDIRECT_Y, AM_INDEXED_INDIRECT, AM_INDIRECT_INDEXED, }; uint8_t memory[0x10000]; /* example program taken from https://bugzmanov.github.io/nes_ebook/chapter_3_1.html */ uint8_t program[] = { 0xa9, 0xc0, 0xaa, 0xe8, 0x00 }; void brk(void) { /* $00 */ /* TODO: push regs.pc and regs.status to stack and load IRQ vector */ regs.status.brk = 1; return; } void tax(void) { /* $AA */ regs.x = regs.a; STATUS_UPDATE_ZERO(regs.x); STATUS_UPDATE_NEGATIVE(regs.x); } void inx(void) { /* $E8 */ regs.x++; STATUS_UPDATE_ZERO(regs.x); STATUS_UPDATE_NEGATIVE(regs.x); } void lda(enum addressing_mode mode) { uint8_t val; switch (mode) { case AM_IMMEDIATE: /* $A9 */ val = memory[regs.pc++]; printf("arg1 $%02X\n", val); regs.a = val; break; case AM_ZERO_PAGE: /* $A5 */ /* TODO */ break; case AM_ZERO_PAGE_X: /* $B5 */ /* TODO */ break; case AM_ABSOLUTE: /* $AD */ /* TODO */ break; case AM_ABSOLUTE_X: /* $BD */ /* TODO */ break; case AM_ABSOLUTE_Y: /* $B9 */ /* TODO */ break; case AM_INDIRECT_X: /* $A1 */ /* TODO */ break; case AM_INDIRECT_Y: /* $B1 */ /* TODO */ break; default: return; } STATUS_UPDATE_ZERO(regs.a); STATUS_UPDATE_NEGATIVE(regs.a); } void interpret(void) { uint8_t opcode; for (;;) { opcode = memory[regs.pc++]; printf("opcode: $%02X\n", opcode); switch (opcode) { case 0x00: brk(); return; case 0xa1: lda(AM_INDIRECT_X); break; case 0xa5: lda(AM_ZERO_PAGE); break; case 0xa9: lda(AM_IMMEDIATE); break; case 0xaa: tax(); break; case 0xad: lda(AM_ABSOLUTE); break; case 0xb1: lda(AM_INDIRECT_Y); break; case 0xb5: lda(AM_ZERO_PAGE_X); break; case 0xb9: lda(AM_ABSOLUTE_Y); break; case 0xbd: lda(AM_ABSOLUTE_X); break; case 0xe8: inx(); break; default: printf("opcode $%02X not implemented\n", opcode); 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); } } int main(void) { if (sizeof(program) > (0x10000 - 0x8000)) { fprintf(stderr, "program is too big for memory\n"); return 1; } memcpy(memory + 0x8000, program, sizeof(program)); regs.pc = 0x8000; regs.status.unused = 1; 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; }