diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b3ab609 --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +#CROSS_COMPILE = arm-none-eabi- +CC = $(CROSS_COMPILE)gcc +LD = $(CC) +#CFLAGS = -mcpu=cortex-m33 -mthumb +CFLAGS += -Wall -Werror -Wshadow +CFLAGS += -g -Ofast +CFLAGS += -DF6502_FLAT +CFLAGS += -DF6502_TEST +CFLAGS += -DF6502_HCF +#CFLAGS += -DF6502_TRACE + +OBJS = src/f6502.o src/f6502_opcodes.o test.o + +all: test + +test: $(OBJS) + $(LD) $(LDFLAGS) -o $@ $^ + +clean: + rm -rf $(OBJS) diff --git a/src/f6502.c b/src/f6502.c new file mode 100644 index 0000000..4265143 --- /dev/null +++ b/src/f6502.c @@ -0,0 +1,1405 @@ +#include "f6502.h" +#include "f6502_consts.h" + +#ifdef F6502_TRACE +#include "f6502_opcodes.h" +#include +#endif + +#ifdef F6502_TEST +#include +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)((char *)__mptr - offsetof(type,member));}) +#endif + + +// TOTO: Temp Hack +volatile uint8_t memval; + +static inline uint8_t f6502_read_zp(f6502_Memory* mem, + uint8_t addr) { + return mem->ram[addr]; +} + +static inline uint16_t f6502_read16_zp(f6502_Memory* mem, + uint8_t addr) { + return ( f6502_read_zp(mem, addr) | + ((uint16_t)f6502_read_zp(mem, addr + 1) << 8)); +} + +static inline uint8_t f6502_read(f6502_Memory* mem, + uint16_t addr) { +#ifdef F6502_FLAT +#ifdef F6502_TRACE +// printf("R $%04X -> %02X\n", addr, mem->ram[addr]); +#endif + return mem->ram[addr]; +#else + if (addr >= 0x8000U) { + return mem->rom_bank[(addr - 0x8000U) >> 13][addr & 0x1FFFFU]; + } + + switch (addr & 0x6000) { + case 0x0000: + return mem->ram[addr & 0x7FFU]; + + case 0x2000: + // TODO: PPU Reg + return 0; + + case 0x4000: + // TODO: APU Reg + return 0; + + case 0x6000: + return mem->sram_bank[addr & 0x1FFFU]; + } + + // Upper byte stays on the bus + return (addr >> 8); +#endif +} + +static inline uint16_t f6502_read16(f6502_Memory* mem, + uint16_t addr) { + return ( f6502_read(mem, addr) | + ((uint16_t)f6502_read(mem, addr + 1) << 8)); +} + +static inline void f6502_write(f6502_Memory* mem, + uint16_t addr, uint8_t val) { +#ifdef F6502_TRACE + printf("W $%04X <- %02X\n", addr, val); +#endif +#ifdef F6502_FLAT +#ifdef F6502_TEST + if (addr == 0xbffc) { + f6502_Core* core = container_of(mem, f6502_Core, memory); + + int irq = val & 0b01; + int nmi = val & 0b10; + + f6502_wire_interrupts(core, + (irq ? f6502_Int_IRQ : 0) | + (nmi ? f6502_Int_NMI : 0) + ); + } +#endif + mem->ram[addr] = val; +#else + // TODO + memval = val; +#endif +} + + +void f6502_init(f6502_Core* core) { + core->int_state = f6502_Int_NMI | f6502_Int_IRQ; + core->int_wiring = f6502_Int_NMI | f6502_Int_IRQ; +} + +void f6502_reset(f6502_Core* core) { + core->registers.PC = f6502_read16(&core->memory, + f6502_Vector_Reset), + core->registers.S -= 3; + core->registers.P |= (f6502_Status_B | f6502_Status_1); +} + +void f6502_wire_interrupts(f6502_Core* core, + f6502_Interrupt ints) { + core->int_wiring = ints; +} + +#define PUSH(core, S, val) \ + f6502_write(&core->memory, f6502_Base_Stack + S--, (val)) +#define PUSH16(core, S, val) \ + PUSH(core, S, ((val) >> 8)); \ + PUSH(core, S, ((val) & 0xFFU)) + +#define POP(core, S, dst) \ + dst = f6502_read(&core->memory, f6502_Base_Stack + ++S) +#define POP16(core, S, dst) \ + POP(core, S, dst); \ + dst |= (f6502_read(&core->memory, \ + f6502_Base_Stack + ++S) << 8) + +#define CLR(R, flags) R &= ~(flags) +#define SET(R, flags) R |= (flags) + +static inline int f6502_interrupt(f6502_Core* core, + f6502_Interrupt interrupt, + uint16_t addr) { + core->int_state &= ~interrupt; + core->int_state |= (core->int_wiring & interrupt); + PUSH16(core, core->registers.S, core->registers.PC); + PUSH(core, core->registers.S, + core->registers.P & ~f6502_Status_B); + CLR(core->registers.P, f6502_Status_D); + SET(core->registers.P, f6502_Status_I); + core->registers.PC = f6502_read16(&core->memory, addr); + return 7; +} + +static inline int f6502_int_ready(const f6502_Core* core, + f6502_Interrupt interrupt) { + return ((core->int_state ^ core->int_wiring) & interrupt); +} + +static inline int f6502_check_interrupts(f6502_Core* core) { + if (f6502_int_ready(core, f6502_Int_NMI)) { + return f6502_interrupt(core, f6502_Int_NMI, + f6502_Vector_NMI); + } else if ( f6502_int_ready(core, f6502_Int_IRQ) && + !(core->registers.P & f6502_Status_I)) { + return f6502_interrupt(core, f6502_Int_IRQ, + f6502_Vector_IRQ); + } + return 0; +} + +#define CLK(n) clocks_elapsed += n + +#define TEST(val) \ + CLR(P, f6502_Status_Z | f6502_Status_N); \ + SET(P, table_test[val]) + +#define A_ABS ({ \ + uint16_t addr_lo = f6502_read(&core->memory, PC++); \ + uint16_t addr_hi = f6502_read(&core->memory, PC++); \ + (addr_lo | (addr_hi << 8)); \ +}) +#define A_ABSX (A_ABS + X) +#define A_ABSY (A_ABS + Y) +#define A_ZP f6502_read(&core->memory, PC++) +#define A_ZPX (uint8_t)(f6502_read(&core->memory, PC++) + X) +#define A_ZPY (uint8_t)(f6502_read(&core->memory, PC++) + Y) +#define A_IX f6502_read16_zp( \ + &core->memory, \ + f6502_read(&core->memory, PC++) + X \ + ) +#define A_IY (f6502_read16_zp( \ + &core->memory, \ + f6502_read(&core->memory, PC++) \ + ) + Y) +#define D_IMM f6502_read(&core->memory, PC++) +#define D_ABS f6502_read(&core->memory, A_ABS) +#define D_ABSX ({ \ + register uint16_t addr_base = A_ABS; \ + register uint16_t addr = addr_base + X; \ + CLK((addr_base & 0x100U) != (addr & 0x100U)); \ + f6502_read(&core->memory, addr); \ +}) +#define D_ABSY ({ \ + register uint16_t addr_base = A_ABS; \ + register uint16_t addr = addr_base + Y; \ + CLK((addr_base & 0x100U) != (addr & 0x100U)); \ + f6502_read(&core->memory, addr); \ +}) +#define D_ZP f6502_read_zp(&core->memory, A_ZP) +#define D_ZPX f6502_read_zp(&core->memory, A_ZPX) +#define D_ZPY f6502_read_zp(&core->memory, A_ZPY) +#define D_IX f6502_read(&core->memory, A_IX) +#define D_IY ({ \ + register uint16_t addr_base = f6502_read16_zp( \ + &core->memory, \ + f6502_read(&core->memory, PC++) \ + ); \ + register uint16_t addr = addr_base + Y; \ + CLK((addr_base & 0x100U) != (addr & 0x100U)); \ + f6502_read(&core->memory, addr); \ +}) + +#define ADC(v) { \ + register uint8_t val = v; \ + register uint16_t res = A + val + (P & f6502_Status_C); \ + CLR(P, f6502_Status_N | f6502_Status_V | \ + f6502_Status_Z | f6502_Status_C); \ + SET(P, table_test[(uint8_t)res]); \ + SET(P, (res > 0xFFU)); \ + if ((~(A ^ val) & (A ^ (uint8_t)res)) & 0x80) { \ + SET(P, f6502_Status_V); \ + } \ + A = res; \ +} +#define AND(v) A &= (v); TEST(A) +#define ASL(a) { \ + CLR(P, f6502_Status_N | f6502_Status_Z | f6502_Status_C); \ + register uint16_t addr = a; \ + register uint8_t val = f6502_read(&core->memory, addr); \ + SET(P, table_asl[val]); \ + val <<= 1; \ + f6502_write(&core->memory, addr, val); \ +} +#define ASL_A() \ + CLR(P, f6502_Status_N | f6502_Status_Z | f6502_Status_C); \ + SET(P, table_asl[A]); \ + A <<= 1 + +#ifdef F6502_HCF +#define BIF_HCF() \ + if (pc_prev == PC + 2) { \ + ++PC; \ + clocks_elapsed = -clocks_elapsed; \ + goto step_done; \ + } +#else +#define BIF_HCF() +#endif +#define BIF(cond) \ + if (cond) { \ + register uint16_t pc_prev = PC; \ + PC += (int8_t)f6502_read(&core->memory, PC); \ + BIF_HCF(); \ + CLK(3 + ((pc_prev & 0x100U) != (PC & 0x100U))); \ + } else { \ + CLK(2); \ + } \ + ++PC +#define BIT(v) { \ + register uint8_t val = (v); \ + CLR(P, f6502_Status_N | f6502_Status_V | f6502_Status_Z); \ + SET(P, (val & (f6502_Status_N | f6502_Status_V))); \ + if (!(val & A)) SET(P, f6502_Status_Z); \ +} +#define CP(reg, v) { \ + uint16_t diff = (uint16_t)reg - (v); \ + CLR(P, f6502_Status_N | f6502_Status_Z | f6502_Status_C); \ + SET(P, table_test[diff & 0xFFU]); \ + if (diff < 0x100) SET(P, f6502_Status_C); \ +} +#define DEC(a) { \ + register uint16_t addr = a; \ + register uint8_t val = f6502_read(&core->memory, addr); \ + f6502_write(&core->memory, addr, --val); \ + TEST(val); \ +} +#define EOR(v) A ^= (v); TEST(A) +#define INC(a) { \ + register uint16_t addr = a; \ + register uint8_t val = f6502_read(&core->memory, addr); \ + f6502_write(&core->memory, addr, ++val); \ + TEST(val); \ +} +#define JMP(a) PC = (a) +#define LD(reg, v) reg = (v); TEST(reg) +#define LSR(a) { \ + register uint16_t addr = a; \ + register uint8_t val = f6502_read(&core->memory, addr); \ + CLR(P, f6502_Status_N | f6502_Status_Z | f6502_Status_C); \ + SET(P, val & f6502_Status_C); \ + val >>= 1; \ + SET(P, val & f6502_Status_N); \ + if (!val) SET(P, f6502_Status_Z); \ + f6502_write(&core->memory, addr, val); \ +} +#define LSR_A() \ + CLR(P, f6502_Status_N | f6502_Status_Z | f6502_Status_C); \ + SET(P, A & f6502_Status_C); \ + A >>= 1; \ + SET(P, A & f6502_Status_N); \ + if (!A) SET(P, f6502_Status_Z) +#define ORA(v) A |= (v); TEST(A) +#define ROL(a) { \ + register uint16_t addr = a; \ + register uint8_t val = f6502_read(&core->memory, addr); \ + register uint8_t new_c = (A & 0x80) ? f6502_Status_C : 0; \ + val = (val << 1) | (P & f6502_Status_C); \ + CLR(P, f6502_Status_C | f6502_Status_Z | f6502_Status_N); \ + P |= new_c; \ + if (!val) SET(P, f6502_Status_Z); \ + SET(P, val & f6502_Status_N); \ + f6502_write(&core->memory, addr, val); \ +} +#define ROL_A() { \ + register uint8_t new_c = (A & 0x80) ? f6502_Status_C : 0; \ + A = (A << 1) | (P & f6502_Status_C); \ + CLR(P, f6502_Status_C | f6502_Status_Z | f6502_Status_N); \ + P |= new_c; \ + if (!A) SET(P, f6502_Status_Z); \ + SET(P, A & f6502_Status_N); \ +} +#define ROR(a) { \ + register uint16_t addr = a; \ + register uint8_t val = f6502_read(&core->memory, addr); \ + register uint8_t new_c = (val & f6502_Status_C); \ + val >>= 1; \ + if (P & f6502_Status_C) val |= f6502_Status_N; \ + CLR(P, f6502_Status_C | f6502_Status_Z | f6502_Status_N); \ + P |= new_c; \ + if (!val) SET(P, f6502_Status_Z); \ + SET(P, (val & f6502_Status_N)); \ + f6502_write(&core->memory, addr, val); \ +} +#define ROR_A() { \ + register uint8_t new_c = (A & f6502_Status_C); \ + A >>= 1; \ + if (P & f6502_Status_C) A |= f6502_Status_N; \ + CLR(P, f6502_Status_C | f6502_Status_Z | f6502_Status_N); \ + P |= new_c; \ + if (!A) SET(P, f6502_Status_Z); \ + SET(P, (A & f6502_Status_N)); \ +} +#define SBC(v) { \ + register uint8_t val = v; \ + register uint16_t res = A - val - (~P & f6502_Status_C);\ + CLR(P, f6502_Status_N | f6502_Status_V | \ + f6502_Status_Z | f6502_Status_C); \ + SET(P, table_test[(uint8_t)res]); \ + SET(P, (res < 0x100U)); \ + if (((A ^ val) & (A ^ (uint8_t)res)) & 0x80) { \ + SET(P, f6502_Status_V); \ + } \ + A = res; \ +} +#define ST(reg, a) f6502_write(&core->memory, (a), reg) + + +static int f6502_do_step(f6502_Core* core, int clocks) { + register int clocks_elapsed = 0; + + register uint16_t PC = core->registers.PC; + register uint8_t S = core->registers.S; + register uint8_t A = core->registers.A; + register uint8_t X = core->registers.X; + register uint8_t Y = core->registers.Y; + register uint8_t P = core->registers.P; + + while (clocks_elapsed < clocks) { + uint8_t opcode = f6502_read(&core->memory, PC++); +#ifdef F6502_TRACE + printf("S:%02x A:%02x X:%02x Y:%02x P:%02x\n", + S, A, X, Y, P); + printf("$%04x: ", PC - 1); + f6502_dump_instr(core, PC - 1); + printf(" -> "); + f6502_fprintf_instr(stdout, opcode, core->memory.ram + PC); + putc('\n', stdout); +#endif + switch (opcode) { + case 0x00: // BRK + ++PC; + PUSH16(core, S, PC); + PUSH(core, S, P | f6502_Status_B | f6502_Status_1); + SET(P, f6502_Status_I); + PC = f6502_read16(&core->memory, f6502_Vector_IRQ); + CLK(7); + break; + + case 0x01: // ORA (zp, X) + ORA(D_IX); + CLK(6); + break; + + // Undefined: 0x02 - 0x04 + + case 0x05: // ORA zp + ORA(D_ZP); + CLK(3); + break; + + case 0x06: // ASL zp + ASL(A_ZP); + CLK(5); + break; + + // Undefined: 0x07 + + case 0x08: // PHP + SET(P, f6502_Status_B); + PUSH(core, S, P); + CLK(3); + break; + + case 0x09: // ORA imm + ORA(D_IMM); + CLK(2); + break; + + case 0x0A: // ASL A + ASL_A(); + CLK(2); + break; + + // Undefined: 0x0B + // Special: 0x0C + + case 0x0D: // ORA abs + ORA(D_ABS); + CLK(4); + break; + + case 0x0E: // ASL abs + ASL(A_ABS); + CLK(6); + break; + + // Undefined: 0x0F + + case 0x10: // BPL + BIF(!(P & f6502_Status_N)); + break; + + case 0x11: // ORA (zp), Y + ORA(D_IY); + CLK(5); + break; + + // Undefined: 0x12 - 0x13 + // Special: 0x14 + + case 0x15: // ORA zp, X + ORA(D_ZPX); + CLK(4); + break; + + case 0x16: // ASL zp, X + ASL(A_ZPX); + CLK(4); + break; + + // Undefined: 0x17 + + case 0x18: // CLC + CLR(P, f6502_Status_C); + CLK(2); + break; + + case 0x19: // ORA abs, Y + ORA(D_ABSY); + CLK(4); + break; + + // Special: 0x1A + // Undefined: 0x1B + // Special: 0x1C + + case 0x1D: // ORA abs, X + ORA(D_ABSX); + CLK(4); + break; + + case 0x1E: // ASL abs, X + ASL(A_ABSX); + CLK(7); + break; + + // Undefined: 0x1F + + case 0x20: // JSR + { uint16_t addr_lo = f6502_read(&core->memory, PC++); + uint16_t addr_hi = f6502_read(&core->memory, PC); + PUSH16(core, S, PC); + PC = addr_lo | (addr_hi << 8); + CLK(6); + } break; + + case 0x21: // AND (zp, X) + AND(D_IX); + CLK(6); + break; + + // Undefined: 0x22 - 0x23 + + case 0x24: // BIT zp + BIT(D_ZP); + CLK(3); + break; + + case 0x25: // AND zp + AND(D_ZP); + CLK(3); + break; + + case 0x26: // ROL zp + ROL(A_ZP); + CLK(5); + break; + + // Undefined: 0x27 + + case 0x28: // PLP + POP(core, S, P); + SET(P, f6502_Status_1 | f6502_Status_B); + CLK(4); + break; + + case 0x29: // AND imm + AND(D_IMM); + CLK(2); + break; + + case 0x2A: // ROL A + ROL_A(); + CLK(2); + break; + + // Undefined: 0x2B + + case 0x2C: // BIT abs + BIT(D_ABS); + CLK(4); + break; + + case 0x2D: // AND abs + AND(D_ABS); + CLK(4); + break; + + case 0x2E: // ROL abs + ROL(A_ABS); + CLK(6); + break; + + // Undefined: 0x2F + + case 0x30: // BMI + BIF(P & f6502_Status_N); + break; + + case 0x31: // AND (zp), Y + AND(D_IY); + CLK(5); + break; + + // Undefined: 0x32 - 0x33 + // Special: 0x34 + + case 0x35: // AND zp, X + AND(D_ZPX); + CLK(4); + break; + + case 0x36: // ROL zp, X + ROL(A_ZPX); + CLK(6); + break; + + // Undefined: 0x37 + + case 0x38: // SEC + SET(P, f6502_Status_C); + CLK(2); + break; + + case 0x39: // AND abs, Y + AND(D_ABSY); + CLK(4); + break; + + // Special: 0x3A + // Undefined: 0x3B + // Special: 0x3C + + case 0x3D: // AND abs, X + AND(D_ABSX); + CLK(4); + break; + + case 0x3E: // ROL abs, X + ROL(A_ABSX); + CLK(7); + break; + + // Undefined: 0x3F + + case 0x40: // RTI + POP(core, S, P); + SET(P, f6502_Status_1); + POP16(core, S, PC); + CLK(6); + break; + + case 0x41: // EOR (zp, X) + EOR(D_IX); + CLK(6); + break; + + // Undefined: 0x42 - 0x43 + // Special: 0x44 + + case 0x45: // EOR zp + EOR(D_ZP); + CLK(3); + break; + + case 0x46: // LSR zp + LSR(A_ZP); + CLK(5); + break; + + // Undefined: 0x47 + + case 0x48: // PHA + PUSH(core, S, A); + CLK(3); + break; + + case 0x49: // EOR imm + EOR(D_IMM); + CLK(2); + break; + + case 0x4A: // LSR A + LSR_A(); + CLK(2); + break; + + // Undefined: 0x4B + + case 0x4C: // JMP abs +#ifdef F6502_HCF + { uint16_t addr = A_ABS; + if (PC - 3 == addr) { + JMP(addr); + clocks_elapsed = -clocks_elapsed; + goto step_done; + } + JMP(addr); + } +#else + // TODO: Can check for HCF loop here + JMP(A_ABS); +#endif + CLK(3); + break; + + case 0x4D: // EOR abs + EOR(D_ABS); + CLK(4); + break; + + case 0x4E: // LSR abs + LSR(A_ABS); + CLK(6); + break; + + // Undefined: 0x4F + + case 0x50: // BVC + BIF(!(P & f6502_Status_V)); + break; + + case 0x51: // EOR (zp), Y + EOR(D_IY); + CLK(5); + break; + + // Undefined: 0x52 - 0x53 + // Special: 0x54 + + case 0x55: // EOR zp, X + EOR(D_ZPX); + CLK(4); + break; + + case 0x56: // LSR zp, X + LSR(A_ZPX); + CLK(6); + break; + + // Undefined: 0x57 + + case 0x58: // CLI + { register uint8_t p_old = P; + CLR(P, f6502_Status_I); + CLK(2); + if ( (p_old & f6502_Status_I) && + f6502_int_ready(core, f6502_Int_IRQ)) { + core->int_state &= ~f6502_Int_IRQ; + core->int_state |= ( core->int_wiring & + f6502_Int_IRQ); + CLK(7); + PUSH16(core, S, PC); + PUSH(core, S, P & ~f6502_Status_B); + CLR(P, f6502_Status_D); + SET(P, f6502_Status_I); + PC = f6502_read16(&core->memory, + f6502_Vector_IRQ); + } + + } break; + + case 0x59: // EOR abs, Y + EOR(D_ABSY); + CLK(4); + break; + + // Special: 0x5A + // Undefined: 0x5B + // Special: 0x5C + + case 0x5D: // EOR abs, X + EOR(D_ABSX); + CLK(4); + break; + + case 0x5E: // LSR abs, X + LSR(A_ABSX); + CLK(7); + break; + + // Undefined: 0x5F + + case 0x60: // RTS + POP16(core, S, PC); + ++PC; + CLK(6); + break; + + case 0x61: // ADC (zp, X) + ADC(D_IX); + CLK(6); + break; + + // Undefined: 0x62 - 0x63 + // Special: 0x64 + + case 0x65: // ADC zp + ADC(D_ZP); + CLK(3); + break; + + case 0x66: // ROR zp + ROR(A_ZP); + CLK(5); + break; + + // Undefined: 0x67 + + case 0x68: // PLA + POP(core, S, A); + TEST(A); + CLK(4); + break; + + case 0x69: // ADC imm + ADC(D_IMM); + CLK(2); + break; + + case 0x6A: // ROR A + ROR_A(); + CLK(2); + break; + + // Undefined: 0x6B + + case 0x6C: // JMP (abs) + { // Reproduce the indirect addressing bug + register uint16_t addr = A_ABS; + register uint8_t addr_lo = f6502_read(&core->memory, + addr); + if ((addr & 0xFFU) == 0xFFU) { + addr = f6502_read(&core->memory, addr - 0xffU); + } else { + addr = f6502_read(&core->memory, addr + 1); + } + JMP(addr_lo | (addr << 8)); + CLK(5); + } break; + + case 0x6D: // ADC abs + ADC(D_ABS); + CLK(4); + break; + + case 0x6E: // ROR abs + ROR(A_ABS); + CLK(6); + break; + + // Undefined: 0x6F + + case 0x70: // BVS + BIF(P & f6502_Status_V); + break; + + case 0x71: // ADC (zp), Y + ADC(D_IY); + CLK(5); + break; + + // Undefined: 0x72 - 0x73 + // Special: 0x74 + + case 0x75: // ADC zp, X + ADC(D_ZPX); + CLK(4); + break; + + case 0x76: // ROR zp, X + ROR(A_ZPX); + CLK(6); + break; + + // Undefined: 0x77 + + case 0x78: // SEI + SET(P, f6502_Status_I); + CLK(2); + break; + + case 0x79: // ADC abs, Y + ADC(D_ABSY); + CLK(4); + break; + + // Special: 0x7A + // Undefined: 0x7B + // Special: 0x7C + + case 0x7D: // ADC abs, X + ADC(D_ABSX); + CLK(4); + break; + + case 0x7E: // ROR abs, X + ROR(A_ABSX); + CLK(7); + break; + + // Undefined: 0x7F + // Special: 0x80 + + case 0x81: // STA (zp, X) + ST(A, A_IX); + CLK(6); + break; + + // Special: 0x82 + // Undefined: 0x83 + + case 0x84: // STY zp + ST(Y, A_ZP); + CLK(3); + break; + + case 0x85: // STA zp + ST(A, A_ZP); + CLK(3); + break; + + case 0x86: // STX zp + ST(X, A_ZP); + CLK(3); + break; + + // Undefined: 0x87 + + case 0x88: // DEY + --Y; + TEST(Y); + CLK(2); + break; + + // Undefined: 0x89 + + case 0x8A: // TXA + A = X; + TEST(A); + CLK(2); + break; + + // Undefined: 0x8B + + case 0x8C: // STY abs + ST(Y, A_ABS); + CLK(4); + break; + + case 0x8D: // STA abs + ST(A, A_ABS); + CLK(4); + break; + + case 0x8E: // STX abs + ST(X, A_ABS); + CLK(4); + break; + + case 0x90: // BCC + BIF(!(P & f6502_Status_C)); + break; + + case 0x91: // STA (zp), Y + ST(A, A_IY); + CLK(6); + break; + + // Undefined: 0x92 - 0x93 + + case 0x94: // STY zp, X + ST(Y, A_ZPX); + CLK(4); + break; + + case 0x95: // STA zp, X + ST(A, A_ZPX); + CLK(4); + break; + + case 0x96: // STX zp, Y + ST(X, A_ZPY); + CLK(4); + break; + + // Undefined: 0x97 + + case 0x98: // TYA + A = Y; + TEST(A); + CLK(2); + break; + + case 0x99: // STA abs, Y + ST(A, A_ABSY); + CLK(5); + break; + + case 0x9A: // TXS + S = X; + CLK(2); + break; + + // Undefined: 0x9B - 0x9C + + case 0x9D: // STA abs, X + ST(A, A_ABSX); + CLK(5); + break; + + // Undefined: 0x9E - 0x9F + + case 0xA0: // LDY imm + LD(Y, D_IMM); + CLK(2); + break; + + case 0xA1: // LDA (zp, X) + LD(A, D_IX); + CLK(6); + break; + + case 0xA2: // LDX imm + LD(X, D_IMM); + CLK(2); + break; + + // Undefined: 0xA3 + + case 0xA4: // LDY zp + LD(Y, D_ZP); + CLK(3); + break; + + case 0xA5: // LDA zp + LD(A, D_ZP); + CLK(3); + break; + + case 0xA6: // LDX zp + LD(X, D_ZP); + CLK(3); + break; + + // Undefined: 0xA7 + + case 0xA8: // TAY + Y = A; + TEST(A); + CLK(2); + break; + + case 0xA9: // LDA imm + LD(A, D_IMM); + CLK(2); + break; + + case 0xAA: // TAX + X = A; + TEST(A); + CLK(2); + break; + + // Undefined: 0xAB + + case 0xAC: // LDY abs + LD(Y, D_ABS); + CLK(4); + break; + + case 0xAD: // LDA abs + LD(A, D_ABS); + CLK(4); + break; + + case 0xAE: // LDX abs + LD(X, D_ABS); + CLK(4); + break; + + // Undefined: 0xAF + + case 0xB0: // BCS + BIF(P & f6502_Status_C); + break; + + case 0xB1: // LDA (zp), Y + LD(A, D_IY); + CLK(5); + break; + + // Undefined: 0xB2 - 0xB3 + + case 0xB4: // LDY zp, X + LD(Y, D_ZPX); + CLK(4); + break; + + case 0xB5: // LDA zp, X + LD(A, D_ZPX); + CLK(4); + break; + + case 0xB6: // LDX zp, Y + LD(X, D_ZPY); + CLK(4); + break; + + // Undefined: 0xB7 + + case 0xB8: // CLV + CLR(P, f6502_Status_V); + CLK(2); + break; + + case 0xB9: // LDA abs, Y + LD(A, D_ABSY); + CLK(4); + break; + + case 0xBA: // TSX + X = S; + TEST(X); + CLK(2); + break; + + // Undefined: 0xBB + + case 0xBC: // LDY abs, X + LD(Y, D_ABSX); + CLK(4); + break; + + case 0xBD: // LDA abs, X + LD(A, D_ABSX); + CLK(4); + break; + + case 0xBE: // LDX abs, Y + LD(X, D_ABSY); + CLK(4); + break; + + // Undefined: 0xBF + + case 0xC0: // CPY imm + CP(Y, D_IMM); + CLK(2); + break; + + case 0xC1: // CMP (zp, X) + CP(A, D_IX); + CLK(6); + break; + + // Special: 0xC2 + // Undefined: 0xC3 + + case 0xC4: // CPY zp + CP(Y, D_ZP); + CLK(3); + break; + + case 0xC5: // CMP zp + CP(A, D_ZP); + CLK(3); + break; + + case 0xC6: // DEC zp + DEC(A_ZP); + CLK(5); + break; + + // Undefined: 0xC7 + + case 0xC8: // INY + ++Y; + TEST(Y); + CLK(2); + break; + + case 0xC9: // CMP imm + CP(A, D_IMM); + CLK(2); + break; + + case 0xCA: // DEX + --X; + TEST(X); + CLK(2); + break; + + // Undefined: 0xCB + + case 0xCC: // CPY abs + CP(Y, D_ABS); + CLK(4); + break; + + case 0xCD: // CMP abs + CP(A, D_ABS); + CLK(4); + break; + + case 0xCE: // DEC abs + DEC(A_ABS); + CLK(6); + break; + + // Undefined: 0xCF + + case 0xD0: // BNE + BIF(!(P & f6502_Status_Z)); + break; + + case 0xD1: // CMP (zp), Y + CP(A, D_IY); + CLK(5); + break; + + // Undefined: 0xD2 - 0xD3 + // Special: 0xD4 + + case 0xD5: // CMP zp, X + CP(A, D_ZPX); + CLK(4); + break; + + case 0xD6: // DEC zp, X + DEC(A_ZPX); + CLK(6); + break; + + // Undefined: 0xD7 + + case 0xD8: // CLD + CLR(P, f6502_Status_D); + CLK(2); + break; + + case 0xD9: // CMP abs, Y + CP(A, D_ABSY); + CLK(4); + break; + + // Special: 0xDA + // Undefined: 0xDB + // Special: 0xDC + + case 0xDD: // CMP abs, X + CP(A, D_ABSX); + CLK(4); + break; + + case 0xDE: // DEC abs, X + DEC(A_ABSX); + CLK(7); + break; + + // Undefined: 0xDF + + case 0xE0: // CPX imm + CP(X, D_IMM); + CLK(2); + break; + + case 0xE1: // SBC (xp, X) + SBC(D_IX); + CLK(6); + break; + + // Special: 0xE2 + // Undefined: 0xE3 + + case 0xE4: // CPX zp + CP(X, D_ZP); + CLK(3); + break; + + case 0xE5: // SBC zp + SBC(D_ZP); + CLK(3); + break; + + case 0xE6: // INC zp + INC(A_ZP); + CLK(5); + break; + + case 0xE8: // INX + ++X; + TEST(X); + CLK(2); + break; + + case 0xE9: // SBC imm + SBC(D_IMM); + CLK(2); + break; + + case 0xEA: // NOP + CLK(2); + break; + + // Undefined: 0xEB + + case 0xEC: // CPX abs + CP(X, D_ABS); + CLK(4); + break; + + case 0xED: // SBC abs + SBC(D_ABS); + CLK(4); + break; + + case 0xEE: // INC abs + INC(A_ABS); + CLK(6); + break; + + // Undefined: 0xEF + + case 0xF0: // BEQ + BIF(P & f6502_Status_Z); + break; + + case 0xF1: // SBC (zp), Y + SBC(D_IY); + CLK(5); + break; + + // Undefined: 0xF2 - 0xF3 + // Special: 0xF4 + + case 0xF5: // SBC zp, X + SBC(D_ZPX); + CLK(4); + break; + + case 0xF6: // INC zp, X + INC(A_ZPX); + CLK(6); + break; + + // Undefined: 0xF7 + + case 0xF8: // SED + SET(P, f6502_Status_D); + CLK(2); + break; + + case 0xF9: // SBC abs, Y + SBC(D_ABSY); + CLK(4); + break; + + // Special: 0xFA + // Undefined: 0xFB + // Special: 0xFC + + case 0xFD: // SBC abs, X + SBC(D_ABSX); + CLK(4); + break; + + case 0xFE: // INC abs, X + INC(A_ABSX); + CLK(7); + break; + + /* "Special" i.e. "unofficial" instructions */ + /* Timing & progression only, no side effects */ + + // DOP, 2 Cycles + case 0x80: + case 0x82: + case 0x89: + case 0xC2: + case 0xE2: + ++PC; + CLK(2); + break; + + // DOP, 3 Cycles + case 0x04: + case 0x44: + case 0x64: + ++PC; + CLK(3); + break; + + // DOP, 4 Cycles + case 0x14: + case 0x34: + case 0x54: + case 0x74: + case 0xD4: + case 0xF4: + ++PC; + CLK(4); + break; + + // TOP, 4 Cycles + case 0x1C: + case 0x3C: + case 0x5C: + case 0x7C: + case 0xDC: + case 0xFC: + PC += 2; + CLK(4); + break; + + // NOP, 2 Cycles + default: + CLK(2); + break; + } + } + +step_done: + core->registers = (f6502_Registers){ + .PC = PC, + .S = S, + .A = A, + .X = X, + .Y = Y, + .P = P, + }; + + return clocks_elapsed; +} + +int f6502_step(f6502_Core* core, int clocks) { + int clocks_elapsed = 0; + // TODO: Why are we processing seven clocks prior to NMI? + if (f6502_int_ready(core, f6502_Int_NMI)) { + clocks_elapsed += f6502_do_step(core, 7); + clocks -= clocks_elapsed; + } + clocks_elapsed += f6502_check_interrupts(core); + return (clocks_elapsed + f6502_do_step(core, clocks)); +} diff --git a/src/f6502.h b/src/f6502.h new file mode 100644 index 0000000..b6cbfdf --- /dev/null +++ b/src/f6502.h @@ -0,0 +1,65 @@ +#ifndef F6502_H_ +#define F6502_H_ + +#include + +#define F6502_RAM_SIZE (0x2000U) + + +typedef enum { + f6502_Status_C = 0b00000001, + f6502_Status_Z = 0b00000010, + f6502_Status_I = 0b00000100, + f6502_Status_D = 0b00001000, + f6502_Status_B = 0b00010000, + f6502_Status_1 = 0b00100000, + f6502_Status_V = 0b01000000, + f6502_Status_N = 0b10000000, +} f6502_Status_Flag; + +#define f6502_Base_Stack (0x0100U) + +#define f6502_Vector_IRQ (0xFFFEU) +#define f6502_Vector_Reset (0xFFFCU) +#define f6502_Vector_NMI (0xFFFAU) + + +typedef struct __attribute__ ((__packed__)) { + uint16_t PC; + uint8_t S; + uint8_t A; + uint8_t X; + uint8_t Y; + uint8_t P; +} f6502_Registers; + +typedef enum { + f6502_Int_NMI = 0b01, + f6502_Int_IRQ = 0b10, +} f6502_Interrupt; + +typedef struct __attribute__ ((__packed__)) { +#ifdef F6502_FLAT + uint8_t ram[65536]; +#else + uint8_t ram[F6502_RAM_SIZE / 4]; // Mirrored 3x + uint8_t *sram_bank; + uint8_t *rom_bank[4]; + // TODO +#endif +} f6502_Memory; + +typedef struct __attribute__ ((__packed__)) { + f6502_Registers registers; + f6502_Interrupt int_state:2; + f6502_Interrupt int_wiring:2; + f6502_Memory memory; +} f6502_Core; + +void f6502_init(f6502_Core*); +void f6502_reset(f6502_Core*); +int f6502_step(f6502_Core*, int clocks); +void f6502_wire_interrupts(f6502_Core*, f6502_Interrupt ints); + + +#endif // F6502_H_ diff --git a/src/f6502_consts.h b/src/f6502_consts.h new file mode 100644 index 0000000..5cd1363 --- /dev/null +++ b/src/f6502_consts.h @@ -0,0 +1,390 @@ +static const uint8_t table_test[256] = { + [0] = f6502_Status_Z, + [128] = f6502_Status_N, + [129] = f6502_Status_N, + [130] = f6502_Status_N, + [131] = f6502_Status_N, + [132] = f6502_Status_N, + [133] = f6502_Status_N, + [134] = f6502_Status_N, + [135] = f6502_Status_N, + [136] = f6502_Status_N, + [137] = f6502_Status_N, + [138] = f6502_Status_N, + [139] = f6502_Status_N, + [140] = f6502_Status_N, + [141] = f6502_Status_N, + [142] = f6502_Status_N, + [143] = f6502_Status_N, + [144] = f6502_Status_N, + [145] = f6502_Status_N, + [146] = f6502_Status_N, + [147] = f6502_Status_N, + [148] = f6502_Status_N, + [149] = f6502_Status_N, + [150] = f6502_Status_N, + [151] = f6502_Status_N, + [152] = f6502_Status_N, + [153] = f6502_Status_N, + [154] = f6502_Status_N, + [155] = f6502_Status_N, + [156] = f6502_Status_N, + [157] = f6502_Status_N, + [158] = f6502_Status_N, + [159] = f6502_Status_N, + [160] = f6502_Status_N, + [161] = f6502_Status_N, + [162] = f6502_Status_N, + [163] = f6502_Status_N, + [164] = f6502_Status_N, + [165] = f6502_Status_N, + [166] = f6502_Status_N, + [167] = f6502_Status_N, + [168] = f6502_Status_N, + [169] = f6502_Status_N, + [170] = f6502_Status_N, + [171] = f6502_Status_N, + [172] = f6502_Status_N, + [173] = f6502_Status_N, + [174] = f6502_Status_N, + [175] = f6502_Status_N, + [176] = f6502_Status_N, + [177] = f6502_Status_N, + [178] = f6502_Status_N, + [179] = f6502_Status_N, + [180] = f6502_Status_N, + [181] = f6502_Status_N, + [182] = f6502_Status_N, + [183] = f6502_Status_N, + [184] = f6502_Status_N, + [185] = f6502_Status_N, + [186] = f6502_Status_N, + [187] = f6502_Status_N, + [188] = f6502_Status_N, + [189] = f6502_Status_N, + [190] = f6502_Status_N, + [191] = f6502_Status_N, + [192] = f6502_Status_N, + [193] = f6502_Status_N, + [194] = f6502_Status_N, + [195] = f6502_Status_N, + [196] = f6502_Status_N, + [197] = f6502_Status_N, + [198] = f6502_Status_N, + [199] = f6502_Status_N, + [200] = f6502_Status_N, + [201] = f6502_Status_N, + [202] = f6502_Status_N, + [203] = f6502_Status_N, + [204] = f6502_Status_N, + [205] = f6502_Status_N, + [206] = f6502_Status_N, + [207] = f6502_Status_N, + [208] = f6502_Status_N, + [209] = f6502_Status_N, + [210] = f6502_Status_N, + [211] = f6502_Status_N, + [212] = f6502_Status_N, + [213] = f6502_Status_N, + [214] = f6502_Status_N, + [215] = f6502_Status_N, + [216] = f6502_Status_N, + [217] = f6502_Status_N, + [218] = f6502_Status_N, + [219] = f6502_Status_N, + [220] = f6502_Status_N, + [221] = f6502_Status_N, + [222] = f6502_Status_N, + [223] = f6502_Status_N, + [224] = f6502_Status_N, + [225] = f6502_Status_N, + [226] = f6502_Status_N, + [227] = f6502_Status_N, + [228] = f6502_Status_N, + [229] = f6502_Status_N, + [230] = f6502_Status_N, + [231] = f6502_Status_N, + [232] = f6502_Status_N, + [233] = f6502_Status_N, + [234] = f6502_Status_N, + [235] = f6502_Status_N, + [236] = f6502_Status_N, + [237] = f6502_Status_N, + [238] = f6502_Status_N, + [239] = f6502_Status_N, + [240] = f6502_Status_N, + [241] = f6502_Status_N, + [242] = f6502_Status_N, + [243] = f6502_Status_N, + [244] = f6502_Status_N, + [245] = f6502_Status_N, + [246] = f6502_Status_N, + [247] = f6502_Status_N, + [248] = f6502_Status_N, + [249] = f6502_Status_N, + [250] = f6502_Status_N, + [251] = f6502_Status_N, + [252] = f6502_Status_N, + [253] = f6502_Status_N, + [254] = f6502_Status_N, + [255] = f6502_Status_N, +}; + +static const uint8_t table_asl[256] = { + f6502_Status_Z, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_N, + f6502_Status_C | f6502_Status_Z, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, + f6502_Status_C | f6502_Status_N, +}; diff --git a/src/f6502_opcodes.c b/src/f6502_opcodes.c new file mode 100644 index 0000000..b360910 --- /dev/null +++ b/src/f6502_opcodes.c @@ -0,0 +1,422 @@ +#include +#include + +#include "f6502.h" +#include "f6502_opcodes.h" + + +#define adr_imp (NULL) + +static int adr_imm(const uint8_t* operand, char* str, int max) { + return snprintf(str, max, "#$%02X", *operand); +} + +static int adr_acc(const uint8_t* operand, char* str, int max) { + return snprintf(str, max, "A"); +} + +static int adr_ind(const uint8_t* operand, char* str, int max) { + return snprintf(str, max, "($%04X)", *(uint16_t*)operand); +} + +static int adr_abs(const uint8_t* operand, char* str, int max) { + return snprintf(str, max, "$%04X", *(uint16_t*)operand); +} + +static inline int adr_abs_reg(char reg, const uint8_t* op, + char* str, int max) { + return snprintf(str, max, "$%04X,%c", *(uint16_t*)op, reg); +} + +static int adr_abs_x(const uint8_t* operand, char* str, int max) { + return adr_abs_reg('X', operand, str, max); +} + +static int adr_abs_y(const uint8_t* operand, char* str, int max) { + return adr_abs_reg('Y', operand, str, max); +} + +#define adr_rel adr_zp +static int adr_zp(const uint8_t* operand, char* str, int max) { + return snprintf(str, max, "$%02X", *operand); +} + +static inline int adr_zp_reg(char reg, const uint8_t* operand, + char* str, int max) { + return snprintf(str, max, "$%02X,%c", *operand, reg); +} + +static int adr_zp_x(const uint8_t* operand, char* str, int max) { + return adr_zp_reg('X', operand, str, max); +} + +static int adr_zp_y(const uint8_t* operand, char* str, int max) { + return adr_zp_reg('Y', operand, str, max); +} + +static inline int adr_ind_reg(char reg, const uint8_t* op, + char* str, int max) { + return snprintf(str, max, "($%04X,%c)", *(uint16_t*)op, reg); +} + +static int adr_ind_x(const uint8_t* operand, char* str, int max) { + return adr_ind_reg('X', operand, str, max); +} + +static int adr_ind_y(const uint8_t* operand, char* str, int max) { + return adr_ind_reg('Y', operand, str, max); +} + +typedef int(*adr_Mode)(const uint8_t*, char*, int); + +typedef struct { + const char *operator; + int(*mode)(const uint8_t*, char*, int); + int cycles; // Unused +} f6502_Instruction; + +#define brk "BRK" +#define ora "ORA" +#define asl "ASL" +#define asl_a asl +#define php "PHP" +#define bpl "BPL" +#define clc "CLC" +#define jsr "JSR" +#define bit "BIT" +#define and "AND" +#define rol "ROL" +#define rol_a rol +#define plp "PLP" +#define bmi "BMI" +#define sec "SEC" +#define rti "RTI" +#define eor "EOR" +#define lsr "LSR" +#define lsr_a lsr +#define pha "PHA" +#define jmp "JMP" +#define bvc "BVC" +#define cli "CLI" +#define rts "RTS" +#define adc "ADC" +#define ror "ROR" +#define ror_a ror +#define pla "PLA" +#define bvs "BVS" +#define sei "SEI" +#define sta "STA" +#define stx "STX" +#define sty "STY" +#define txa "TXA" +#define dey "DEY" +#define bcc "BCC" +#define tya "TYA" +#define txs "TXS" +#define lda "LDA" +#define ldx "LDX" +#define ldy "LDY" +#define tax "TAX" +#define tay "TAY" +#define bcs "BCS" +#define clv "CLV" +#define tsx "TSX" +#define cpy "CPY" +#define cpa "CPA" +#define iny "INY" +#define dec "DEC" +#define dex "DEX" +#define bne "BNE" +#define cld "CLD" +#define cpx "CPX" +#define sbc "SBC" +#define inc "INC" +#define inx "INX" +#define nop "NOP" +#define beq "BEQ" +#define sed "SED" + +static const f6502_Instruction f6502_instructions[256] = { + // 0x00 + [0b00000000] = {brk, adr_imp, 7}, + + [0b00000001] = {ora, adr_ind_x, 6}, + [0b00000101] = {ora, adr_zp, 3}, + [0b00001001] = {ora, adr_imm, 2}, + [0b00001101] = {ora, adr_abs, 4}, + + [0b00000110] = {asl, adr_zp, 5}, + [0b00001010] = {asl_a, adr_acc, 2}, + [0b00001110] = {asl, adr_abs, 6}, + + [0b00001000] = {php, adr_imp, 3}, + + // 0x10 + [0b00010000] = {bpl, adr_rel, 2}, + + [0b00010001] = {ora, adr_ind_y, 5}, + [0b00010101] = {ora, adr_zp_x, 4}, + [0b00011001] = {ora, adr_abs_y, 4}, + [0b00011101] = {ora, adr_abs_x, 4}, + + [0b00011000] = {clc, adr_imp, 2}, + + [0b00010110] = {asl, adr_zp_x, 6}, + [0b00011110] = {asl, adr_abs_x, 7}, + + // 0x20 + [0b00100000] = {jsr, adr_abs, 6}, + + [0b00100100] = {bit, adr_zp, 3}, + [0b00101100] = {bit, adr_abs, 4}, + + [0b00100001] = {and, adr_ind_x, 6}, + [0b00100101] = {and, adr_zp, 3}, + [0b00101001] = {and, adr_imm, 2}, + [0b00101101] = {and, adr_abs, 4}, + + [0b00100110] = {rol, adr_zp, 5}, + [0b00101010] = {rol_a, adr_acc, 2}, + [0b00101110] = {rol, adr_abs, 6}, + + [0b00101000] = {plp, adr_imp, 4}, + + // 0x30 + [0b00110000] = {bmi, adr_rel, 2}, + + [0b00110001] = {and, adr_ind_y, 5}, + [0b00110101] = {and, adr_zp_x, 4}, + [0b00111001] = {and, adr_abs_y, 3}, + [0b00111101] = {and, adr_abs_x, 3}, + + [0b00110110] = {rol, adr_zp_x, 6}, + [0b00111110] = {rol, adr_abs_x, 7}, + + [0b00111000] = {sec, adr_imp, 2}, + + // 0x40 + [0b01000000] = {rti, adr_imp, 6}, + + [0b01000001] = {eor, adr_ind_x, 6}, + [0b01000101] = {eor, adr_zp, 3}, + [0b01001001] = {eor, adr_imm, 2}, + [0b01001101] = {eor, adr_abs, 4}, + + [0b01000110] = {lsr, adr_zp, 5}, + [0b01001010] = {lsr_a, adr_acc, 2}, + [0b01001110] = {lsr, adr_abs, 6}, + + [0b01001000] = {pha, adr_imp, 3}, + + [0b01001100] = {jmp, adr_abs, 3}, + + // 0x50 + [0b01010000] = {bvc, adr_rel, 2}, + + [0b01010001] = {eor, adr_ind_y, 5}, + [0b01010101] = {eor, adr_zp_x, 4}, + [0b01011001] = {eor, adr_abs_y, 4}, + [0b01011101] = {eor, adr_abs_x, 4}, + + [0b01010110] = {lsr, adr_zp_x, 6}, + [0b01011110] = {lsr, adr_abs_x, 7}, + + [0b01011000] = {cli, adr_imp, 2}, + + // 0x60 + [0b01100000] = {rts, adr_imp, 6}, + + [0b01100001] = {adc, adr_ind_x, 6}, + [0b01100101] = {adc, adr_zp, 3}, + [0b01101001] = {adc, adr_imm, 2}, + [0b01101101] = {adc, adr_abs, 4}, + + [0b01100110] = {ror, adr_zp, 5}, + [0b01101010] = {ror_a, adr_acc, 2}, + [0b01101110] = {ror, adr_abs, 6}, + + [0b01101000] = {pla, adr_imp, 4}, + + [0b01101100] = {jmp, adr_ind, 5}, + + // 0x70 + [0b01110000] = {bvs, adr_rel, 2}, + + [0b01110001] = {adc, adr_ind_y, 5}, + [0b01110101] = {adc, adr_zp_x, 4}, + [0b01111001] = {adc, adr_abs_y, 4}, + [0b01111101] = {adc, adr_abs_x, 4}, + + [0b01110110] = {ror, adr_zp_x, 6}, + [0b01111110] = {ror, adr_abs_x, 7}, + + [0b01111000] = {sei, adr_imp, 2}, + + // 0x80 + [0b10000001] = {sta, adr_ind_x, 6}, + [0b10000101] = {sta, adr_zp, 3}, + [0b10001101] = {sta, adr_abs, 4}, + + [0b10000110] = {stx, adr_zp, 3}, + [0b10001110] = {stx, adr_abs, 4}, + + [0b10000100] = {sty, adr_zp, 3}, + [0b10001100] = {sty, adr_abs, 4}, + + [0b10001000] = {dey, adr_imp, 2}, + + [0b10001010] = {txa, adr_imp, 2}, + + // 0x90 + [0b10010000] = {bcc, adr_rel, 2}, + + [0b10010001] = {sta, adr_ind_y, 6}, + [0b10010101] = {sta, adr_zp_x, 4}, + [0b10011001] = {sta, adr_abs_y, 5}, + [0b10011101] = {sta, adr_abs_x, 5}, + + [0b10010110] = {stx, adr_zp_y, 4}, + + [0b10010100] = {sty, adr_zp_x, 4}, + + [0b10011000] = {tya, adr_imp, 2}, + [0b10011010] = {txs, adr_imp, 2}, + + // 0xA0 + [0b10100001] = {lda, adr_ind_x, 6}, + [0b10100101] = {lda, adr_zp, 3}, + [0b10101001] = {lda, adr_imm, 2}, + [0b10101101] = {lda, adr_abs, 4}, + + [0b10100010] = {ldx, adr_imm, 2}, + [0b10100110] = {ldx, adr_zp, 3}, + [0b10101110] = {ldx, adr_abs, 4}, + + [0b10100000] = {ldy, adr_imm, 2}, + [0b10100100] = {ldy, adr_zp, 3}, + [0b10101100] = {ldy, adr_abs, 4}, + + [0b10101010] = {tax, adr_imp, 2}, + [0b10101000] = {tay, adr_imp, 2}, + + // 0xB0 + [0b10110000] = {bcs, adr_rel, 2}, + + [0b10110001] = {lda, adr_ind_y, 5}, + [0b10110101] = {lda, adr_zp_x, 4}, + [0b10111001] = {lda, adr_abs_y, 4}, + [0b10111101] = {lda, adr_abs_x, 4}, + + [0b10110110] = {ldx, adr_zp_y, 4}, + [0b10111110] = {ldx, adr_abs_y, 4}, + + [0b10110100] = {ldy, adr_zp_x, 4}, + [0b10111100] = {ldy, adr_abs_x, 4}, + + [0b10111000] = {clv, adr_imp, 2}, + + [0b10111010] = {tsx, adr_imp, 2}, + + // 0xC0 + [0b11000000] = {cpy, adr_imm, 2}, + [0b11000100] = {cpy, adr_zp, 3}, + [0b11001100] = {cpy, adr_abs, 4}, + + [0b11000001] = {cpa, adr_ind_x, 6}, + [0b11000101] = {cpa, adr_zp, 3}, + [0b11001001] = {cpa, adr_imm, 2}, + [0b11001101] = {cpa, adr_abs, 4}, + + [0b11001000] = {iny, adr_imp, 2}, + + [0b11000110] = {dec, adr_zp, 5}, + [0b11001110] = {dec, adr_abs, 6}, + [0b11010110] = {dec, adr_zp_x, 6}, + [0b11011110] = {dec, adr_abs_x, 7}, + + [0b11001010] = {dex, adr_imp, 2}, + + // 0xD0 + [0b11010000] = {bne, adr_rel, 2}, + + [0b11011000] = {cld, adr_imp, 2}, + + [0b11010001] = {cpa, adr_ind_y, 5}, + [0b11010101] = {cpa, adr_zp_x, 4}, + [0b11011001] = {cpa, adr_abs_y, 4}, + [0b11011101] = {cpa, adr_abs_x, 4}, + + // 0xE0 + [0b11100000] = {cpx, adr_imm, 2}, + [0b11100100] = {cpx, adr_zp, 3}, + [0b11101100] = {cpx, adr_abs, 4}, + + [0b11100001] = {sbc, adr_ind_x, 6}, + [0b11100101] = {sbc, adr_zp, 3}, + [0b11101001] = {sbc, adr_imm, 2}, + [0b11101101] = {sbc, adr_abs, 4}, + + [0b11100110] = {inc, adr_zp, 5}, + [0b11101110] = {inc, adr_abs, 6}, + + [0b11101000] = {inx, adr_imp, 2}, + + [0b11101010] = {nop, adr_imp, 2}, + + // 0xF0 + [0b11110000] = {beq, adr_rel, 2}, + + [0b11110001] = {sbc, adr_ind_y, 5}, + [0b11110101] = {sbc, adr_zp_x, 4}, + [0b11111001] = {sbc, adr_abs_y, 4}, + [0b11111101] = {sbc, adr_abs_x, 4}, + + [0b11110110] = {inc, adr_zp_x, 6}, + [0b11111110] = {inc, adr_abs_x, 7}, + + [0b11111000] = {sed, adr_imp, 2}, + +}; + +int f6502_fprintf_instr(FILE* file, + uint8_t opcode, uint8_t* operand) { + int ret = 0; + const f6502_Instruction* instr = &f6502_instructions[opcode]; + if (NULL == instr->operator) { + ret += fprintf(file, "ILLEGAL: $%02x", opcode); + } else { + ret += fputs(instr->operator, file); + if (NULL != instr->mode) { + char str[16] = {0}; + int len = instr->mode(operand, str, sizeof(str) - 1); + ret += fprintf(file, " %.*s", len, str); + } + } + return ret; +} + +static int f6502_instr_size(uint8_t opcode) { + const f6502_Instruction* instr = &f6502_instructions[opcode]; + if ( instr->mode == adr_imp || + instr->mode == adr_acc) { + return 1; + } else if ( instr->mode == adr_abs || + instr->mode == adr_abs_x || + instr->mode == adr_abs_y) { + return 3; + } else { + return 2; + } +} + +void f6502_dump_instr(f6502_Core* core, uint16_t addr) { + uint8_t opcode = core->memory.ram[addr]; + int size = f6502_instr_size(opcode); + printf("$%02x", opcode); + if (size == 2) { + printf(" $%02x", core->memory.ram[addr + 1]); + } else if (size == 3) { + printf(" $%04x", core->memory.ram[addr + 1] | + ((uint16_t)core->memory.ram[addr + 2] << 8)); + } +} diff --git a/src/f6502_opcodes.h b/src/f6502_opcodes.h new file mode 100644 index 0000000..a11dec6 --- /dev/null +++ b/src/f6502_opcodes.h @@ -0,0 +1,13 @@ +#ifndef F6502_OPCODES_H_ +#define F6502_OPCODES_H_ + +#include + + +int f6502_fprintf_instr(FILE* file, + uint8_t opcode, uint8_t* operand); + +void f6502_dump_instr(f6502_Core* core, uint16_t addr); + + +#endif // F6502_OPCODES_H_ diff --git a/test.c b/test.c new file mode 100644 index 0000000..c0ca545 --- /dev/null +++ b/test.c @@ -0,0 +1,47 @@ +#include + +#include "src/f6502.h" + + +int load(uint8_t* mem, FILE* file) { + return fread(mem, 65536, 1, file); +} + +void print_registers(const f6502_Registers* regs, + FILE* file) { + fprintf(file, "PC: $%04x\n", regs->PC); + fprintf(file, " S: $%02x\n", regs->S); + fprintf(file, " A: $%02x\n", regs->A); + fprintf(file, " X: $%02x\n", regs->X); + fprintf(file, " Y: $%02x\n", regs->Y); + fprintf(file, " P: $%02x\n", regs->P); +} + +static f6502_Core core = {0}; + +int main(int argc, char* argv[]) { + f6502_init(&core); + + if (load(core.memory.ram, stdin) != 1) { + fprintf(stderr, "Failed to load e6502 memory\n"); + return 1; + } + + core.memory.ram[0xbffc] = 0; + f6502_reset(&core); + core.registers.PC = 0x400; + + int total_clocks = 0; + int clocks_elapsed = 1; + + while (clocks_elapsed > 0) { + clocks_elapsed = f6502_step(&core, 1000); + if (clocks_elapsed < 0) total_clocks -= clocks_elapsed; + else total_clocks += clocks_elapsed; + } + + fprintf(stdout, "Ran %d\n", total_clocks); + print_registers(&core.registers, stdout); + + return 0; +} diff --git a/version b/version new file mode 100644 index 0000000..0ea3a94 --- /dev/null +++ b/version @@ -0,0 +1 @@ +0.2.0