commit 1e9070ecd3c9607f448a2b5ffa4db914941da7d7 Author: Nathaniel Walizer Date: Tue Nov 26 23:22:19 2024 -0800 First commit of functioning 6502 emulator - Passes Klaus Dormann's functional tests - Internal memory must be polled - Registers not named by convention diff --git a/e6502.c b/e6502.c new file mode 100644 index 0000000..e9ebed0 --- /dev/null +++ b/e6502.c @@ -0,0 +1,842 @@ +#include "e6502.h" + + +// Instruction Addressing + +typedef uint8_t* e6502_Address; + +static inline e6502_Address e6502_mem_adr(e6502_Core* core, + e6502_Mem_Addr adr) { + return &core->memory[adr]; +} + +static inline e6502_Mem_Addr e6502_adr_mem(e6502_Core* core, + e6502_Address adr) { + return (adr - core->memory); +} + + + +static inline uint8_t e6502_adr_r8(e6502_Core*, + e6502_Address adr) { + return *adr; +} + +static inline uint8_t e6502_adr_r16(e6502_Core*, + e6502_Address adr) { + return *(uint16_t*)adr; +} + +static inline void e6502_adr_w8(e6502_Core*, + e6502_Address adr, + uint8_t val) { + *adr = val; +} + +static inline void e6502_adr_w16(e6502_Core*, + e6502_Address adr, + uint16_t val) { + *(uint16_t*)adr = val; +} + + +// Read values after opcodes + +static inline uint8_t arg8(e6502_Core* core) { + return e6502_r8(core, core->registers.PC++); +} + +static inline uint16_t arg16(e6502_Core* core) { + return (arg8(core) | ((uint16_t)arg8(core) << 8)); +} + + +// Interpret opcode values + +static inline e6502_Address adr_none(e6502_Core*) { + return 0; +} + +static inline e6502_Address adr_ind(e6502_Core* core) { + return e6502_mem_adr(core, e6502_r16(core, arg16(core))); +} + +static inline e6502_Address adr_ind_x(e6502_Core* core) { + return e6502_mem_adr(core, e6502_r16( + core, + (uint8_t)(core->registers.X + arg8(core)) + )); +} + +static inline e6502_Address adr_ind_y(e6502_Core* core) { + uint16_t zp_adr = e6502_r16(core, arg8(core)); + uint8_t old_page = zp_adr >> 8; + uint16_t new_adr = core->registers.Y + zp_adr; + if (new_adr >> 8 != old_page) core->cycle++; + return e6502_mem_adr(core, new_adr); +} + +static inline e6502_Address adr_abs_i(e6502_Core* core, + uint8_t i) { + uint16_t abs_adr = arg16(core); + uint8_t old_page = abs_adr >> 8; + uint16_t new_adr = i + abs_adr; + if (new_adr >> 8 != old_page) core->cycle++; + return e6502_mem_adr(core, new_adr); +} + +static inline e6502_Address adr_abs_x(e6502_Core* core) { + return adr_abs_i(core, core->registers.X); +} + +static inline e6502_Address adr_abs_y(e6502_Core* core) { + return adr_abs_i(core, core->registers.Y); +} + +static inline e6502_Address adr_zp(e6502_Core* core) { + return e6502_mem_adr(core, arg8(core)); +} + +static inline e6502_Address adr_zp_x(e6502_Core* core) { + return e6502_mem_adr( + core, (uint8_t)(arg8(core) + core->registers.X) + ); +} + +static inline e6502_Address adr_zp_y(e6502_Core* core) { + return e6502_mem_adr( + core, (uint8_t)(arg8(core) + core->registers.Y) + ); +} + +static inline e6502_Address adr_abs(e6502_Core* core) { + return e6502_mem_adr(core, arg16(core)); +} + +static inline e6502_Address adr_imm(e6502_Core* core) { + return e6502_mem_adr(core, core->registers.PC++); +} + +static inline e6502_Address adr_rel(e6502_Core* core) { + return e6502_mem_adr( + core, (int8_t)arg8(core) + core->registers.PC + ); +} + +static inline e6502_Address adr_reg_a (e6502_Core* core) { + return &core->registers.A; +} + +static inline e6502_Address adr_reg_x (e6502_Core* core) { + return &core->registers.X; +} + +static inline e6502_Address adr_reg_y (e6502_Core* core) { + return &core->registers.Y; +} + +static inline e6502_Address adr_reg_s (e6502_Core* core) { + return &core->registers.S; +} + +static inline e6502_Address adr_reg_sp (e6502_Core* core) { + return &core->registers.SP; +} + +// Instructions + +static inline void nop(e6502_Core*, e6502_Address) { + // NOP +} + +static inline void brk(e6502_Core *core, e6502_Address) { + e6502_push16(core, core->registers.PC + 1); + e6502_push8(core, core->registers.S | + e6502_Status_1 | + e6502_Status_B); + core->registers.S |= e6502_Status_I; + core->registers.PC = e6502_r16(core, e6502_IRQ_Vec); +} + +static inline void rti(e6502_Core *core, e6502_Address) { + core->registers.S = e6502_pop8(core) & ~(e6502_Status_B); + core->registers.PC = e6502_pop16(core); +} + +static inline void ora(e6502_Core *core, e6502_Address adr) { + core->registers.A |= e6502_adr_r8(core, adr); + core->registers.S &= ~(e6502_Status_Z | e6502_Status_N); + core->registers.S |= (core->registers.A & e6502_Status_N); + if (!core->registers.A) core->registers.S |= e6502_Status_Z; +} + +static inline void eor(e6502_Core *core, e6502_Address adr) { + core->registers.A ^= e6502_adr_r8(core, adr); + core->registers.S &= ~(e6502_Status_Z | e6502_Status_N); + core->registers.S |= (core->registers.A & e6502_Status_N); + if (!core->registers.A) core->registers.S |= e6502_Status_Z; +} + +static inline void asl(e6502_Core *core, e6502_Address adr) { + uint8_t val = e6502_adr_r8(core, adr); + core->registers.S &= ~(e6502_Status_C | + e6502_Status_Z | + e6502_Status_N); + if (val & 0x80) core->registers.S |= e6502_Status_C; + val <<= 1; + core->registers.S |= (val & e6502_Status_N); + if (!val) core->registers.S |= e6502_Status_Z; + e6502_adr_w8(core, adr, val); +} + +static inline void lsr(e6502_Core *core, e6502_Address adr) { + uint8_t val = e6502_adr_r8(core, adr); + core->registers.S &= ~(e6502_Status_C | + e6502_Status_Z | + e6502_Status_N); + core->registers.S |= (val & e6502_Status_C); + val >>= 1; + core->registers.S |= (val & e6502_Status_N); + if (!val) core->registers.S |= e6502_Status_Z; + e6502_adr_w8(core, adr, val); +} + +static inline void rol(e6502_Core *core, e6502_Address adr) { + uint8_t val = e6502_adr_r8(core, adr); + uint8_t new_c = (val & 0x80) ? e6502_Status_C : 0; + val <<= 1; + val |= (core->registers.S & e6502_Status_C); + core->registers.S &= ~(e6502_Status_C | + e6502_Status_Z | + e6502_Status_N); + core->registers.S |= new_c; + if (!val) core->registers.S |= e6502_Status_Z; + core->registers.S |= (val & e6502_Status_N); + e6502_adr_w8(core, adr, val); +} + +static inline void ror(e6502_Core *core, e6502_Address adr) { + uint8_t val = e6502_adr_r8(core, adr); + uint8_t new_c = (val & e6502_Status_C); + val >>= 1; + if (core->registers.S & e6502_Status_C) val |= e6502_Status_N; + core->registers.S &= ~(e6502_Status_C | + e6502_Status_Z | + e6502_Status_N); + core->registers.S |= new_c; + if (!val) core->registers.S |= e6502_Status_Z; + core->registers.S |= (val & e6502_Status_N); + e6502_adr_w8(core, adr, val); +} + +static inline void pha(e6502_Core *core, e6502_Address adr) { + e6502_push8(core, core->registers.A); +} + +static inline void php(e6502_Core *core, e6502_Address adr) { + e6502_push8(core, core->registers.S | + e6502_Status_1 | + e6502_Status_B); +} + +static inline void pla(e6502_Core *core, e6502_Address adr) { + core->registers.A = e6502_pop8(core); + core->registers.S &= ~(e6502_Status_Z | e6502_Status_N); + if (!core->registers.A) core->registers.S |= e6502_Status_Z; + core->registers.S |= (core->registers.A & e6502_Status_N); +} + +static inline void plp(e6502_Core *core, e6502_Address adr) { + core->registers.S = (e6502_pop8(core) | + e6502_Status_1 | + e6502_Status_B); +} + +static inline void b(e6502_Core *core, e6502_Address adr) { + uint8_t old_page = core->registers.PC >> 8; + uint16_t new_addr = e6502_adr_mem(core, adr); + if (old_page != (new_addr >> 8)) core->cycle++; + core->registers.PC = new_addr; + core->cycle++; +} + +static inline void bpl(e6502_Core *core, e6502_Address adr) { + if (!(core->registers.S & e6502_Status_N)) { + b(core, adr); + } +} + +static inline void bmi(e6502_Core *core, e6502_Address adr) { + if (core->registers.S & e6502_Status_N) { + b(core, adr); + } +} + +static inline void bcc(e6502_Core *core, e6502_Address adr) { + if (!(core->registers.S & e6502_Status_C)) { + b(core, adr); + } +} + +static inline void bcs(e6502_Core *core, e6502_Address adr) { + if (core->registers.S & e6502_Status_C) { + b(core, adr); + } +} + +static inline void bvc(e6502_Core *core, e6502_Address adr) { + if (!(core->registers.S & e6502_Status_V)) { + b(core, adr); + } +} + +static inline void bvs(e6502_Core *core, e6502_Address adr) { + if (core->registers.S & e6502_Status_V) { + b(core, adr); + } +} + +static inline void bne(e6502_Core *core, e6502_Address adr) { + if (!(core->registers.S & e6502_Status_Z)) { + b(core, adr); + } +} + +static inline void beq(e6502_Core *core, e6502_Address adr) { + if (core->registers.S & e6502_Status_Z) { + b(core, adr); + } +} + +static inline void clc(e6502_Core *core, e6502_Address) { + core->registers.S &= ~e6502_Status_C; +} + +static inline void cli(e6502_Core *core, e6502_Address) { + core->registers.S &= ~e6502_Status_I; +} + +static inline void cld(e6502_Core *core, e6502_Address) { + core->registers.S &= ~e6502_Status_D; +} + +static inline void clv(e6502_Core *core, e6502_Address) { + core->registers.S &= ~e6502_Status_V; +} + +static inline void sec(e6502_Core *core, e6502_Address) { + core->registers.S |= e6502_Status_C; +} + +static inline void sei(e6502_Core *core, e6502_Address) { + core->registers.S |= e6502_Status_I; +} + +static inline void sed(e6502_Core *core, e6502_Address) { + core->registers.S |= e6502_Status_D; +} + +static inline void jsr(e6502_Core *core, e6502_Address adr) { + e6502_push16(core, core->registers.PC - 1); + core->registers.PC = e6502_adr_mem(core, adr); +} + +static inline void rts(e6502_Core *core, e6502_Address) { + core->registers.PC = e6502_pop16(core) + 1; +} + +static inline void jmp(e6502_Core *core, e6502_Address adr) { + core->registers.PC = e6502_adr_mem(core, adr); +} + +static inline void bit(e6502_Core *core, e6502_Address adr) { + uint8_t val = e6502_adr_r8(core, adr); + core->registers.S &= ~(e6502_Status_Z | + e6502_Status_V | + e6502_Status_N); + core->registers.S |= (val & (e6502_Status_V | + e6502_Status_N)); + if (!(val & core->registers.A)) { + core->registers.S |= e6502_Status_Z; + } +} + +static inline void cmp(e6502_Core *core, + e6502_Address adr, + uint8_t minuend) { + uint8_t val = e6502_adr_r8(core, adr); + uint8_t diff = minuend - val; + core->registers.S &= ~(e6502_Status_C | + e6502_Status_Z | + e6502_Status_N); + if (minuend >= val) core->registers.S |= e6502_Status_C; + if (diff == 0) core->registers.S |= e6502_Status_Z; + core->registers.S |= (diff & e6502_Status_N); +} + +static inline void cpa(e6502_Core *core, e6502_Address adr) { + cmp(core, adr, core->registers.A); +} + +static inline void cpx(e6502_Core *core, e6502_Address adr) { + cmp(core, adr, core->registers.X); +} + +static inline void cpy(e6502_Core *core, e6502_Address adr) { + cmp(core, adr, core->registers.Y); +} + +static inline void and(e6502_Core *core, e6502_Address adr) { + core->registers.A &= e6502_adr_r8(core, adr); + core->registers.S &= ~(e6502_Status_Z | e6502_Status_N); + core->registers.S |= (core->registers.A & e6502_Status_N); + if (!core->registers.A) core->registers.S |= e6502_Status_Z; +} + +static inline void adc(e6502_Core *core, e6502_Address adr) { + uint8_t addend = e6502_adr_r8(core, adr); + uint32_t sum = core->registers.A + + addend + + (core->registers.S & e6502_Status_C); + core->registers.S &= ~(e6502_Status_C | e6502_Status_Z | + e6502_Status_V | e6502_Status_N); + if (sum > 0xFF) core->registers.S |= e6502_Status_C; + if (!(uint8_t)sum) core->registers.S |= e6502_Status_Z; + if ( (addend & 0x80) == (core->registers.A & 0x80) && + (sum & 0x80) != (addend & 0x80)) { + core->registers.S |= e6502_Status_V; + } + core->registers.S |= (sum & e6502_Status_N); + core->registers.A = sum; +} + +static inline void sbc(e6502_Core *core, e6502_Address adr) { + uint8_t addend = e6502_adr_r8(core, adr); + uint32_t sum = core->registers.A - + addend - + (1 - (core->registers.S & e6502_Status_C)); + core->registers.S &= ~(e6502_Status_C | e6502_Status_Z | + e6502_Status_V | e6502_Status_N); + if (sum < 0x100) core->registers.S |= e6502_Status_C; + if (!(uint8_t)sum) core->registers.S |= e6502_Status_Z; + if ( (sum & 0x80) != (core->registers.A & 0x80) && + (addend & 0x80) != (core->registers.A & 0x80)) { + core->registers.S |= e6502_Status_V; + } + core->registers.S |= (sum & e6502_Status_N); + core->registers.A = sum; +} + +static inline void inc(e6502_Core *core, e6502_Address adr) { + uint8_t val = e6502_adr_r8(core, adr) + 1; + core->registers.S &= ~(e6502_Status_Z | e6502_Status_N); + core->registers.S |= (val & e6502_Status_N); + if (!val) core->registers.S |= e6502_Status_Z; + e6502_adr_w8(core, adr, val); +} + +static inline void dec(e6502_Core *core, e6502_Address adr) { + uint8_t val = e6502_adr_r8(core, adr) - 1; + core->registers.S &= ~(e6502_Status_Z | e6502_Status_N); + core->registers.S |= (val & e6502_Status_N); + if (!val) core->registers.S |= e6502_Status_Z; + e6502_adr_w8(core, adr, val); +} + +static inline void sta(e6502_Core *core, e6502_Address adr) { + e6502_adr_w8(core, adr, core->registers.A); +} + +static inline void stx(e6502_Core *core, e6502_Address adr) { + e6502_adr_w8(core, adr, core->registers.X); +} + +static inline void sty(e6502_Core *core, e6502_Address adr) { + e6502_adr_w8(core, adr, core->registers.Y); +} + +static inline void t(e6502_Core *core, + e6502_Address adr, + uint8_t reg) { + core->registers.S &= ~(e6502_Status_Z | e6502_Status_N); + core->registers.S |= (reg & e6502_Status_N); + if (!reg) core->registers.S |= e6502_Status_Z; + e6502_adr_w8(core, adr, reg); +} + +static inline void ta(e6502_Core *core, e6502_Address adr) { + t(core, adr, core->registers.A); +} + +static inline void txs(e6502_Core *core, e6502_Address adr) { + core->registers.SP = core->registers.X; +} + +static inline void txa(e6502_Core *core, e6502_Address adr) { + t(core, &core->registers.A, core->registers.X); +} + +static inline void ty(e6502_Core *core, e6502_Address adr) { + t(core, adr, core->registers.Y); +} + +static inline void ts(e6502_Core *core, e6502_Address adr) { + t(core, adr, core->registers.SP); +} + +static inline void lda(e6502_Core *core, e6502_Address adr) { + t(core, &core->registers.A, e6502_adr_r8(core, adr)); +} + +static inline void ldx(e6502_Core *core, e6502_Address adr) { + t(core, &core->registers.X, e6502_adr_r8(core, adr)); +} + +static inline void ldy(e6502_Core *core, e6502_Address adr) { + t(core, &core->registers.Y, e6502_adr_r8(core, adr)); +} + + +typedef struct { + void(*operator)(e6502_Core*, e6502_Address); + e6502_Address(*address)(e6502_Core*); + uint16_t cycles; +} e6502_Instruction; + +static e6502_Instruction e6502_instructions[256] = { + // 0x00 + [0b00000000] = {brk, adr_none, 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, adr_reg_a, 2}, + [0b00001110] = {asl, adr_abs, 6}, + + [0b00001000] = {php, adr_none, 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_none, 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, adr_reg_a, 2}, + [0b00101110] = {rol, adr_abs, 6}, + + [0b00101000] = {plp, adr_none, 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_none, 2}, + + // 0x40 + [0b01000000] = {rti, adr_none, 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, adr_reg_a, 2}, + [0b01001110] = {lsr, adr_abs, 6}, + + [0b01001000] = {pha, adr_none, 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_none, 2}, + + // 0x60 + [0b01100000] = {rts, adr_none, 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, adr_reg_a, 2}, + [0b01101110] = {ror, adr_abs, 6}, + + [0b01101000] = {pla, adr_none, 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_none, 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] = {dec, adr_reg_y, 2}, + + [0b10001010] = {txa, adr_none, 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] = {ty, adr_reg_a, 2}, + [0b10011010] = {txs, adr_none, 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] = {ta, adr_reg_x, 2}, + [0b10101000] = {ta, adr_reg_y, 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_none, 2}, + + [0b10111010] = {ts, adr_reg_x, 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] = {inc, adr_reg_y, 2}, + + [0b11000110] = {dec, adr_zp, 5}, + [0b11001110] = {dec, adr_abs, 6}, + [0b11010110] = {dec, adr_zp_x, 6}, + [0b11011110] = {dec, adr_abs_x, 7}, + + [0b11001010] = {dec, adr_reg_x, 2}, + + // 0xD0 + [0b11010000] = {bne, adr_rel, 2}, + + [0b11011000] = {cld, adr_none, 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] = {inc, adr_reg_x, 2}, + + [0b11101010] = {nop, adr_none, 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_none, 2}, + +}; + +int e6502_instr_size(uint8_t opcode) { + e6502_Instruction* instr = &e6502_instructions[opcode]; + if ( instr->address == adr_none || + instr->address == adr_reg_a || + instr->address == adr_reg_x || + instr->address == adr_reg_y || + instr->address == adr_reg_s || + instr->address == adr_reg_sp) { + return 1; + } else if ( instr->address == adr_abs || + instr->address == adr_abs_x || + instr->address == adr_abs_y) { + return 3; + } else { + return 2; + } +} + +/* +int e6502_dump_instr(e6502_Core* core, e6502_Mem_Addr addr, + FILE* file) { + uint8_t opcode = e6502_r8(core, addr); + int size = e6502_instr_size(opcode); + fprintf(file, "$%02x", opcode); + if (size == 2) { + fprintf(file, " $%02x", e6502_r8(core, addr + 1)); + } else if (size == 3) { + fprintf(file, " $%04x", e6502_r16(core, addr + 1)); + } + fputc('\n', file); +} + +int e6502_dump_regs(e6502_Core* core, FILE* file) { + fprintf(file, "S:%02x A:%02x X:%02x Y:%02x P:%02x\n", + core->registers.SP, core->registers.A, + core->registers.X, core->registers.Y, + core->registers.S); +} +*/ + +int e6502_run(e6502_Core* core, + int remaining, + int* run, + int instructions) { + int status = 0; + int start = (instructions ? core->instr : core->cycle); + int end = start + remaining; + int last_pc = -1; + while ((instructions ? core->instr : core->cycle) < end) { + if (core->registers.PC == last_pc) { + // Trapped. + status = -2; + break; + } + last_pc = core->registers.PC; + uint8_t opcode = e6502_r8(core, core->registers.PC); + e6502_Instruction* instr = &e6502_instructions[opcode]; + if (!instr->operator) { +/* + fprintf(stdout, "$%04x: %02x\n", last_pc, opcode); +*/ + status = -1; + break; + } else { + core->registers.PC++; + e6502_Address adr = instr->address(core); +/* + uint16_t size = core->registers.PC - last_pc; +*/ + instr->operator(core, adr); + core->cycle += instr->cycles; + core->instr++; +/* + fprintf(stdout, "$%04x: $%02x", last_pc, opcode); + if (size == 2) { + fprintf(stdout, " $%02x", e6502_r8(core, last_pc + 1)); + } else if (size == 3) { + fprintf(stdout, " $%02x%02x", e6502_r8(core, last_pc + 2), e6502_r8(core, last_pc + 1)); + } + fputc('\n', stdout); + e6502_dump_regs(core, stdout); +*/ + } + } + if (run) { + *run = (instructions ? core->instr : core->cycle) - start; + } + return status; +} + +int e6502_reset(e6502_Core* core) { + core->registers.PC = e6502_r16(core, e6502_Reset_Vec); + core->registers.SP -= 3; + core->registers.S |= (e6502_Status_B | e6502_Status_1); +} diff --git a/e6502.h b/e6502.h new file mode 100644 index 0000000..568d116 --- /dev/null +++ b/e6502.h @@ -0,0 +1,94 @@ +#include + + +typedef enum { + e6502_Status_C = 0b00000001, + e6502_Status_Z = 0b00000010, + e6502_Status_I = 0b00000100, + e6502_Status_D = 0b00001000, + e6502_Status_B = 0b00010000, + e6502_Status_1 = 0b00100000, + e6502_Status_V = 0b01000000, + e6502_Status_N = 0b10000000, +} e6502_Status_Flag_Bit; + +typedef struct { + uint16_t PC; + uint8_t SP; + uint8_t A; + uint8_t X; + uint8_t Y; + uint8_t S; +} e6502_Registers; + +typedef struct { + e6502_Registers registers; + int cycle; + int instr; + uint8_t memory[65536]; +} e6502_Core; + +#define e6502_Memory_Stack (0x0100U) + +#define e6502_IRQ_Vec (0xFFFEU) +#define e6502_Reset_Vec (0xFFFCU) +#define e6502_NMI_Vec (0xFFFAU) + + +typedef uint16_t e6502_Mem_Addr; + +/* +uint8_t e6502_r8(e6502_Core* core, e6502_Mem_Addr adr); + +void e6502_w8(e6502_Core* core, e6502_Mem_Addr adr, uint8_t val); +*/ + +static inline uint8_t e6502_r8(e6502_Core* core, + e6502_Mem_Addr adr) { + return core->memory[adr]; +} + +static inline void e6502_w8(e6502_Core* core, + e6502_Mem_Addr adr, + uint8_t val) { + core->memory[adr] = val; +} + +static inline uint16_t e6502_r16(e6502_Core* core, + e6502_Mem_Addr adr) { + return e6502_r8(core, adr) | + ((uint16_t)e6502_r8(core, adr + 1) << 8); +} + +static inline void e6502_w16(e6502_Core* core, + e6502_Mem_Addr adr, + uint16_t val) { + e6502_w8(core, adr, val); + e6502_w8(core, adr + 1, val >> 8); +} + +static inline void e6502_push8(e6502_Core* core, uint8_t val) { + e6502_w8(core, e6502_Memory_Stack + core->registers.SP--, val); +} + +static inline void e6502_push16(e6502_Core* core, uint16_t val) { + e6502_push8(core, val >> 8); + e6502_push8(core, val & 0xFF); +} + +static inline uint8_t e6502_pop8(e6502_Core* core) { + return e6502_r8(core, e6502_Memory_Stack + + ++core->registers.SP); +} + +static inline uint16_t e6502_pop16(e6502_Core* core) { + return (e6502_pop8(core) | ((uint16_t)e6502_pop8(core) << 8)); +} + + +int e6502_run(e6502_Core* core, + int remaining, + int* run, + int instructions); + +int e6502_reset(e6502_Core* core);