#include "e6502.h" #ifdef E6502_DEBUG #include #include "opcodes.h" #endif // Instruction Addressing #ifdef E6502_POLL_MEM 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* core, e6502_Address adr) { return adr[0]; } static inline uint8_t e6502_adr_r16(e6502_Core* core, e6502_Address adr) { return (adr[0] | ((uint16_t)adr[1] << 8)); } static inline void e6502_adr_w8(e6502_Core* core, e6502_Address adr, uint8_t val) { adr[0] = val; } static inline void e6502_adr_w16(e6502_Core* core, e6502_Address adr, uint16_t val) { adr[0] = val; adr[1] = val >> 8; } #else // !E6502_POLL_MEM typedef uint16_t e6502_Address; static inline e6502_Address e6502_mem_adr(e6502_Core* core, e6502_Mem_Addr adr) { return adr; } static inline e6502_Mem_Addr e6502_adr_mem(e6502_Core* core, e6502_Address adr) { return adr; } static inline uint8_t e6502_adr_r8(e6502_Core* core, e6502_Address adr) { return e6502_r8(core, adr); } static inline void e6502_adr_w8(e6502_Core* core, e6502_Address adr, uint8_t val) { e6502_w8(core, adr, val); } #endif // !E6502_POLL_MEM // 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_imp(e6502_Core* core) { return 0; } static inline e6502_Address adr_acc(e6502_Core* 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 ); } // Instructions static inline void nop(e6502_Core* core, e6502_Address adr) { // NOP } static inline void brk(e6502_Core *core, e6502_Address adr) { e6502_push16(core, core->registers.PC + 1); e6502_push8(core, core->registers.P | e6502_Status_1 | e6502_Status_B); core->registers.P |= e6502_Status_I; core->registers.PC = e6502_r16(core, e6502_IRQ_Vec); } static inline void rti(e6502_Core *core, e6502_Address adr) { core->registers.P = e6502_pop8(core) | e6502_Status_1 | 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.P &= ~(e6502_Status_Z | e6502_Status_N); core->registers.P |= (core->registers.A & e6502_Status_N); if (!core->registers.A) core->registers.P |= e6502_Status_Z; } static inline void eor(e6502_Core *core, e6502_Address adr) { core->registers.A ^= e6502_adr_r8(core, adr); core->registers.P &= ~(e6502_Status_Z | e6502_Status_N); core->registers.P |= (core->registers.A & e6502_Status_N); if (!core->registers.A) core->registers.P |= e6502_Status_Z; } static inline void asl(e6502_Core *core, e6502_Address adr) { // TODO: Emulate double-read or double-write uint8_t val = e6502_adr_r8(core, adr); core->registers.P &= ~(e6502_Status_C | e6502_Status_Z | e6502_Status_N); if (val & 0x80) core->registers.P |= e6502_Status_C; val <<= 1; core->registers.P |= (val & e6502_Status_N); if (!val) core->registers.P |= e6502_Status_Z; e6502_adr_w8(core, adr, val); } static inline void asl_a(e6502_Core *core, e6502_Address adr) { uint8_t val = core->registers.A; core->registers.P &= ~(e6502_Status_C | e6502_Status_Z | e6502_Status_N); if (val & 0x80) core->registers.P |= e6502_Status_C; val <<= 1; core->registers.P |= (val & e6502_Status_N); if (!val) core->registers.P |= e6502_Status_Z; core->registers.A = val; } static inline void lsr(e6502_Core *core, e6502_Address adr) { // TODO: Emulate double-read or double-write uint8_t val = e6502_adr_r8(core, adr); core->registers.P &= ~(e6502_Status_C | e6502_Status_Z | e6502_Status_N); core->registers.P |= (val & e6502_Status_C); val >>= 1; core->registers.P |= (val & e6502_Status_N); if (!val) core->registers.P |= e6502_Status_Z; e6502_adr_w8(core, adr, val); } static inline void lsr_a(e6502_Core *core, e6502_Address adr) { uint8_t val = core->registers.A; core->registers.P &= ~(e6502_Status_C | e6502_Status_Z | e6502_Status_N); core->registers.P |= (val & e6502_Status_C); val >>= 1; core->registers.P |= (val & e6502_Status_N); if (!val) core->registers.P |= e6502_Status_Z; core->registers.A = val; } static inline void rol(e6502_Core *core, e6502_Address adr) { // TODO: Emulate double-read or double-write uint8_t val = e6502_adr_r8(core, adr); uint8_t new_c = (val & 0x80) ? e6502_Status_C : 0; val <<= 1; val |= (core->registers.P & e6502_Status_C); core->registers.P &= ~(e6502_Status_C | e6502_Status_Z | e6502_Status_N); core->registers.P |= new_c; if (!val) core->registers.P |= e6502_Status_Z; core->registers.P |= (val & e6502_Status_N); e6502_adr_w8(core, adr, val); } static inline void rol_a(e6502_Core *core, e6502_Address adr) { uint8_t val = core->registers.A; uint8_t new_c = (val & 0x80) ? e6502_Status_C : 0; val <<= 1; val |= (core->registers.P & e6502_Status_C); core->registers.P &= ~(e6502_Status_C | e6502_Status_Z | e6502_Status_N); core->registers.P |= new_c; if (!val) core->registers.P |= e6502_Status_Z; core->registers.P |= (val & e6502_Status_N); core->registers.A = val; } static inline void ror(e6502_Core *core, e6502_Address adr) { // TODO: Emulate double-read or double-write uint8_t val = e6502_adr_r8(core, adr); uint8_t new_c = (val & e6502_Status_C); val >>= 1; if (core->registers.P & e6502_Status_C) val |= e6502_Status_N; core->registers.P &= ~(e6502_Status_C | e6502_Status_Z | e6502_Status_N); core->registers.P |= new_c; if (!val) core->registers.P |= e6502_Status_Z; core->registers.P |= (val & e6502_Status_N); e6502_adr_w8(core, adr, val); } static inline void ror_a(e6502_Core *core, e6502_Address adr) { uint8_t val = core->registers.A; uint8_t new_c = (val & e6502_Status_C); val >>= 1; if (core->registers.P & e6502_Status_C) val |= e6502_Status_N; core->registers.P &= ~(e6502_Status_C | e6502_Status_Z | e6502_Status_N); core->registers.P |= new_c; if (!val) core->registers.P |= e6502_Status_Z; core->registers.P |= (val & e6502_Status_N); core->registers.A = 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.P | e6502_Status_1 | e6502_Status_B); } static inline void pla(e6502_Core *core, e6502_Address adr) { core->registers.A = e6502_pop8(core); core->registers.P &= ~(e6502_Status_Z | e6502_Status_N); if (!core->registers.A) core->registers.P |= e6502_Status_Z; core->registers.P |= (core->registers.A & e6502_Status_N); } static inline void plp(e6502_Core *core, e6502_Address adr) { core->registers.P = (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.P & e6502_Status_N)) { b(core, adr); } } static inline void bmi(e6502_Core *core, e6502_Address adr) { if (core->registers.P & e6502_Status_N) { b(core, adr); } } static inline void bcc(e6502_Core *core, e6502_Address adr) { if (!(core->registers.P & e6502_Status_C)) { b(core, adr); } } static inline void bcs(e6502_Core *core, e6502_Address adr) { if (core->registers.P & e6502_Status_C) { b(core, adr); } } static inline void bvc(e6502_Core *core, e6502_Address adr) { if (!(core->registers.P & e6502_Status_V)) { b(core, adr); } } static inline void bvs(e6502_Core *core, e6502_Address adr) { if (core->registers.P & e6502_Status_V) { b(core, adr); } } static inline void bne(e6502_Core *core, e6502_Address adr) { if (!(core->registers.P & e6502_Status_Z)) { b(core, adr); } } static inline void beq(e6502_Core *core, e6502_Address adr) { if (core->registers.P & e6502_Status_Z) { b(core, adr); } } static inline void clc(e6502_Core *core, e6502_Address adr) { core->registers.P &= ~e6502_Status_C; } static inline void cli(e6502_Core *core, e6502_Address adr) { core->registers.P &= ~e6502_Status_I; } static inline void cld(e6502_Core *core, e6502_Address adr) { core->registers.P &= ~e6502_Status_D; } static inline void clv(e6502_Core *core, e6502_Address adr) { core->registers.P &= ~e6502_Status_V; } static inline void sec(e6502_Core *core, e6502_Address adr) { core->registers.P |= e6502_Status_C; } static inline void sei(e6502_Core *core, e6502_Address adr) { core->registers.P |= e6502_Status_I; } static inline void sed(e6502_Core *core, e6502_Address adr) { core->registers.P |= 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 jmp(e6502_Core *core, e6502_Address adr) { core->registers.PC = e6502_adr_mem(core, adr); } static inline void rts(e6502_Core *core, e6502_Address adr) { core->registers.PC = e6502_pop16(core) + 1; } static inline void bit(e6502_Core *core, e6502_Address adr) { uint8_t val = e6502_adr_r8(core, adr); core->registers.P &= ~(e6502_Status_Z | e6502_Status_V | e6502_Status_N); core->registers.P |= (val & (e6502_Status_V | e6502_Status_N)); if (!(val & core->registers.A)) { core->registers.P |= 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.P &= ~(e6502_Status_C | e6502_Status_Z | e6502_Status_N); if (minuend >= val) core->registers.P |= e6502_Status_C; if (diff == 0) core->registers.P |= e6502_Status_Z; core->registers.P |= (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.P &= ~(e6502_Status_Z | e6502_Status_N); core->registers.P |= (core->registers.A & e6502_Status_N); if (!core->registers.A) core->registers.P |= 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.P & e6502_Status_C); core->registers.P &= ~(e6502_Status_C | e6502_Status_Z | e6502_Status_V | e6502_Status_N); if (sum > 0xFF) core->registers.P |= e6502_Status_C; if (!(uint8_t)sum) core->registers.P |= e6502_Status_Z; if ( (addend & 0x80) == (core->registers.A & 0x80) && (sum & 0x80) != (addend & 0x80)) { core->registers.P |= e6502_Status_V; } core->registers.P |= (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.P & e6502_Status_C)); core->registers.P &= ~(e6502_Status_C | e6502_Status_Z | e6502_Status_V | e6502_Status_N); if (sum < 0x100) core->registers.P |= e6502_Status_C; if (!(uint8_t)sum) core->registers.P |= e6502_Status_Z; if ( (sum & 0x80) != (core->registers.A & 0x80) && (addend & 0x80) != (core->registers.A & 0x80)) { core->registers.P |= e6502_Status_V; } core->registers.P |= (sum & e6502_Status_N); core->registers.A = sum; } static inline void inc(e6502_Core *core, e6502_Address adr) { // TODO: Emulate double-read or double-write uint8_t val = e6502_adr_r8(core, adr); e6502_adr_w8(core, adr, val); val += 1; core->registers.P &= ~(e6502_Status_Z | e6502_Status_N); core->registers.P |= (val & e6502_Status_N); if (!val) core->registers.P |= e6502_Status_Z; e6502_adr_w8(core, adr, val); } static inline void ini(e6502_Core *core, uint8_t* reg) { uint8_t val = *reg + 1; core->registers.P &= ~(e6502_Status_Z | e6502_Status_N); core->registers.P |= (val & e6502_Status_N); if (!val) core->registers.P |= e6502_Status_Z; *reg = val; } static inline void inx(e6502_Core *core, e6502_Address adr) { ini(core, &core->registers.X); } static inline void iny(e6502_Core *core, e6502_Address adr) { ini(core, &core->registers.Y); } static inline void dec(e6502_Core *core, e6502_Address adr) { // TODO: Emulate double-read or double-write uint8_t val = e6502_adr_r8(core, adr) - 1; core->registers.P &= ~(e6502_Status_Z | e6502_Status_N); core->registers.P |= (val & e6502_Status_N); if (!val) core->registers.P |= e6502_Status_Z; e6502_adr_w8(core, adr, val); } static inline void dei(e6502_Core *core, uint8_t* reg) { uint8_t val = *reg - 1; core->registers.P &= ~(e6502_Status_Z | e6502_Status_N); core->registers.P |= (val & e6502_Status_N); if (!val) core->registers.P |= e6502_Status_Z; *reg = val; } static inline void dex(e6502_Core *core, e6502_Address adr) { return dei(core, &core->registers.X); } static inline void dey(e6502_Core *core, e6502_Address adr) { return dei(core, &core->registers.Y); } 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, uint8_t* reg, uint8_t val) { core->registers.P &= ~(e6502_Status_Z | e6502_Status_N); core->registers.P |= (val & e6502_Status_N); if (!val) core->registers.P |= e6502_Status_Z; *reg = val; } static inline void tax(e6502_Core *core, e6502_Address adr) { t(core, &core->registers.X, core->registers.A); } static inline void tay(e6502_Core *core, e6502_Address adr) { t(core, &core->registers.Y, core->registers.A); } static inline void txs(e6502_Core *core, e6502_Address adr) { core->registers.S = core->registers.X; } static inline void txa(e6502_Core *core, e6502_Address adr) { t(core, &core->registers.A, core->registers.X); } static inline void tya(e6502_Core *core, e6502_Address adr) { t(core, &core->registers.A, core->registers.Y); } static inline void tsx(e6502_Core *core, e6502_Address adr) { t(core, &core->registers.X, core->registers.S); } 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 const e6502_Instruction e6502_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}, }; #ifdef E6502_DEBUG int e6502_instr_size(uint8_t opcode) { const e6502_Instruction* instr = &e6502_instructions[opcode]; if ( instr->address == adr_imp || instr->address == adr_acc) { return 1; } else if ( instr->address == adr_abs || instr->address == adr_abs_x || instr->address == adr_abs_y) { return 3; } else { return 2; } } void 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); } void e6502_dump_regs(e6502_Core* core, FILE* file) { fprintf(file, "S:%02x A:%02x X:%02x Y:%02x P:%02x\n", core->registers.S, core->registers.A, core->registers.X, core->registers.Y, core->registers.P); } #endif static void e6502_irq(e6502_Core* core) { if (!(core->registers.P & e6502_Status_I)) { e6502_push16(core, core->registers.PC); e6502_push8(core, (core->registers.P & ~e6502_Status_B) | e6502_Status_1); core->registers.P |= e6502_Status_I; core->registers.PC = e6502_r16(core, e6502_IRQ_Vec); } } static void e6502_nmi(e6502_Core* core) { e6502_push16(core, core->registers.PC); e6502_push8(core, (core->registers.P & ~e6502_Status_B) | e6502_Status_1); core->registers.P |= e6502_Status_I; core->registers.PC = e6502_r16(core, e6502_NMI_Vec); } int e6502_step(e6502_Core* core) { core->cycle = 0; #ifdef E6502_DEBUG int last_pc = core->registers.PC; #endif uint8_t opcode = e6502_r8(core, core->registers.PC); const e6502_Instruction* instr = &e6502_instructions[opcode]; if (!instr->operator) { #ifdef E6502_DEBUG fprintf(stdout, "$%04x: $%02x -> ILLEGAL\n", last_pc, opcode); #endif #ifndef E6502_ILLEGAL core->cycle = -1; #else core->registers.PC++; core->cycle += 1; #endif } else { core->registers.PC++; e6502_Address adr = instr->address(core); #ifdef E6502_DEBUG uint16_t size = core->registers.PC - last_pc; uint16_t val = 0; if (size > 1) { if (size == 2) val = e6502_r8(core, last_pc + 1); else val = e6502_r16(core, last_pc + 1); } e6502_dump_regs(core, stdout); fprintf(stdout, "$%04x: ", last_pc); e6502_dump_instr(core, last_pc, stdout); fputs(" -> ", stdout); e6502_fprintf_instr(stdout, opcode, (uint8_t*)&val); fputc('\n', stdout); #endif instr->operator(core, adr); core->cycle += instr->cycles; if ( (core->pins & e6502_Pin_NMI) && !(core->pins & e6502_NMI_Serviced)) { core->pins |= e6502_NMI_Serviced; e6502_nmi(core); } else if ( (core->pins & e6502_Pin_IRQ) && !(core->registers.P & e6502_Status_I)) { e6502_irq(core); } } return core->cycle; } int e6502_run(e6502_Core* core, int remaining, int* run, int instructions) { int status = 0; int count = 0; int last_pc = -1; (void)last_pc; while (count < remaining) { #ifdef E6502_HCF if (core->registers.PC == last_pc) { // Trapped. status = -2; break; } #endif last_pc = core->registers.PC; int cpu_cycles = e6502_step(core); if (cpu_cycles <= 0) { status = cpu_cycles; break; } count += (instructions ? 1 : cpu_cycles); } if (run) { *run = count; } return status; } #ifdef E6502_POLL_MEM void e6502_init(e6502_Core* core) { /* nuthin' */ } #else void e6502_init(e6502_Core* core, e6502_Read read, e6502_Write write, void* context) { core->bus_read = read; core->bus_write = write; core->bus_context = context; } #endif void e6502_reset(e6502_Core* core) { core->registers.PC = e6502_r16(core, e6502_Reset_Vec); core->registers.S -= 3; core->registers.P |= (e6502_Status_B | e6502_Status_1); } void e6502_set_nmi(e6502_Core* core, int active) { if (active) core->pins |= e6502_Pin_NMI; else core->pins &= ~(e6502_Pin_NMI | e6502_NMI_Serviced); } void e6502_set_irq(e6502_Core* core, int active) { if (active) core->pins |= e6502_Pin_IRQ; else core->pins &= ~e6502_Pin_IRQ; }