// ADC __attribute__((hot, always_inline)) static inline void adc(struct cpu_state *cpu, uint8_t value) { #ifdef ENABLE_DECIMAL_MODE if(cpu->d) { uint8_t al = (cpu->a & 0x0f) + (value & 0x0f) + cpu->c; uint8_t ah = (cpu->a >> 4) + (value >> 4); if(al > 9) { al += 6; ah++; } cpu->c = (ah > 9); if(cpu->c) { ah += 6; } cpu->a = (ah << 4) | (al & 0x0f); update_zn(cpu, cpu->a); // Note: overflow flag behavior in decimal mode is undefined on 6502 return; } #endif uint16_t sum = cpu->a + value + cpu->c; cpu->c = (sum > 0xff); uint8_t result = sum & 0xff; cpu->v = (~(cpu->a ^ value) & (cpu->a ^ result)) & 0x80 ? 1 : 0; cpu->a = result; update_zn(cpu, result); } // ADC ($nn,X) static void opcode_adc_indx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t zp = memory_read(state, cpu->pc++); // T1 memory_read_dummy(state, zp); // T2 uint8_t ptr = (zp + cpu->x) & 0xff; uint8_t lo = memory_read(state, ptr); // T3 uint8_t hi = memory_read(state, (ptr + 1) & 0xff); // T4 uint16_t addr = lo | (hi << 8); uint8_t value = memory_read(state, addr); // T5 adc(cpu, value); // T6+ } // ADC $nn __attribute__((hot)) static void opcode_adc_zp(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t addr = memory_read(state, cpu->pc++); // T1 uint8_t value = memory_read(state, addr); // T2 adc(cpu, value); // T3 } // ADC #$nn static void opcode_adc_imm(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t value = memory_read(state, cpu->pc++); // T1 adc(cpu, value); // T2 } // ADC $nnnn static void opcode_adc_abs(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 uint16_t addr = lo | (hi << 8); uint8_t value = memory_read(state, addr); // T3 adc(cpu, value); // T4 } // ADC ($nn),Y static void opcode_adc_indy(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t zp = memory_read(state, cpu->pc++); // T1 uint8_t lo = memory_read(state, zp); // T2 uint8_t hi = memory_read(state, (zp + 1) & 0xff); // T3 uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->y; if(PAGE_CROSSED(base, addr)) { memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); // T4 (dummy if crossed) } uint8_t value = memory_read(state, addr); // T4 or T5 adc(cpu, value); // T5 or T6 } // ADC $nn,X static void opcode_adc_zpx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t base = memory_read(state, cpu->pc++); // T1 memory_read_dummy(state, base); // T2 uint8_t addr = (base + cpu->x) & 0xff; uint8_t value = memory_read(state, addr); // T3 adc(cpu, value); // T4 } // ADC $nnnn,Y static void opcode_adc_absy(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->y; if(PAGE_CROSSED(base, addr)) { memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); // T3 dummy } uint8_t value = memory_read(state, addr); // T3 or T4 adc(cpu, value); // T4 or T5 } // ADC $nnnn,X static void opcode_adc_absx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->x; if(PAGE_CROSSED(base, addr)) { memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); // T3 dummy } uint8_t value = memory_read(state, addr); // T3 or T4 adc(cpu, value); // T4 or T5 } // AND ($nn,X) static void opcode_and_indx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t zp = memory_read(state, cpu->pc++); // T1 memory_read_dummy(state, zp); // T2 uint8_t ptr = (zp + cpu->x) & 0xff; uint8_t lo = memory_read(state, ptr); // T3 uint8_t hi = memory_read(state, (ptr + 1) & 0xff); // T4 uint16_t addr = lo | (hi << 8); uint8_t value = memory_read(state, addr); // T5 cpu->a &= value; // T6+ update_zn(cpu, cpu->a); } // AND $nn static void opcode_and_zp(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t addr = memory_read(state, cpu->pc++); // T1 uint8_t value = memory_read(state, addr); // T2 cpu->a &= value; // T3 update_zn(cpu, cpu->a); } // AND #$nn static void opcode_and_imm(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t value = memory_read(state, cpu->pc++); // T1 cpu->a &= value; // T2 update_zn(cpu, cpu->a); } // AND $nnnn static void opcode_and_abs(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 uint16_t addr = lo | (hi << 8); uint8_t value = memory_read(state, addr); // T3 cpu->a &= value; // T4 update_zn(cpu, cpu->a); } // AND ($nn),Y static void opcode_and_indy(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t zp = memory_read(state, cpu->pc++); // T1 uint8_t lo = memory_read(state, zp); // T2 uint8_t hi = memory_read(state, (zp + 1) & 0xff); // T3 uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->y; if(PAGE_CROSSED(base, addr)) { memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); // T4 dummy } uint8_t value = memory_read(state, addr); // T4 or T5 cpu->a &= value; // T5 or T6 update_zn(cpu, cpu->a); } // AND $nn,X static void opcode_and_zpx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t base = memory_read(state, cpu->pc++); // T1 memory_read_dummy(state, base); // T2 uint8_t addr = (base + cpu->x) & 0xff; uint8_t value = memory_read(state, addr); // T3 cpu->a &= value; // T4 update_zn(cpu, cpu->a); } // AND $nnnn,Y static void opcode_and_absy(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->y; if(PAGE_CROSSED(base, addr)) { memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); // T3 dummy } uint8_t value = memory_read(state, addr); // T3 or T4 cpu->a &= value; // T4 or T5 update_zn(cpu, cpu->a); } // AND $nnnn,X static void opcode_and_absx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->x; if(PAGE_CROSSED(base, addr)) { memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); // T3 dummy } uint8_t value = memory_read(state, addr); // T3 or T4 cpu->a &= value; // T4 or T5 update_zn(cpu, cpu->a); } // ASL // ASL $nn static void opcode_asl_zp(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t addr = memory_read(state, cpu->pc++); // T1 uint8_t value = memory_read(state, addr); // T2 memory_write(state, addr, value); // T3 (dummy write) cpu->c = (value >> 7) & 1; value <<= 1; memory_write(state, addr, value); // T4 update_zn(cpu, value); } // ASL A __attribute__((hot)) static void opcode_asl_acc(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); // T1 (dummy read at PC) cpu->c = (cpu->a >> 7) & 1; cpu->a <<= 1; update_zn(cpu, cpu->a); // T2 } // ASL $nnnn static void opcode_asl_abs(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 uint16_t addr = lo | (hi << 8); uint8_t value = memory_read(state, addr); // T3 memory_write(state, addr, value); // T4 (dummy write) cpu->c = (value >> 7) & 1; value <<= 1; memory_write(state, addr, value); // T5 update_zn(cpu, value); } // ASL $nn,X static void opcode_asl_zpx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t base = memory_read(state, cpu->pc++); // T1 memory_read_dummy(state, base); // T2 uint8_t addr = (base + cpu->x) & 0xff; uint8_t value = memory_read(state, addr); // T3 memory_write(state, addr, value); // T4 (dummy write) cpu->c = (value >> 7) & 1; value <<= 1; memory_write(state, addr, value); // T5 update_zn(cpu, value); } // ASL $nnnn,X static void opcode_asl_absx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->x; memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); // T3 uint8_t value = memory_read(state, addr); // T4 memory_write(state, addr, value); // T5 (dummy write) cpu->c = (value >> 7) & 1; value <<= 1; memory_write(state, addr, value); // T6 update_zn(cpu, value); } // BIT // BIT $nn static void opcode_bit_zp(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t addr = memory_read(state, cpu->pc++); // T1 uint8_t value = memory_read(state, addr); // T2 cpu->z = ((cpu->a & value) == 0); cpu->n = (value >> 7) & 1; cpu->v = (value >> 6) & 1; // T3 } // BIT $nnnn static void opcode_bit_abs(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 uint16_t addr = lo | (hi << 8); uint8_t value = memory_read(state, addr); // T3 cpu->z = ((cpu->a & value) == 0); cpu->n = (value >> 7) & 1; cpu->v = (value >> 6) & 1; // T4 } // BRK static void opcode_brk(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; cpu->pc++; // BRK is 1 byte, skip padding byte memory_read_dummy(state, cpu->pc); // T1 padding read uint8_t pcl = cpu->pc & 0xff; uint8_t pch = cpu->pc >> 8; memory_write(state, 0x0100 + cpu->sp--, pch); // T2 memory_write(state, 0x0100 + cpu->sp--, pcl); // T3 memory_write(state, 0x0100 + cpu->sp--, pack_flags(cpu) | 0x10); // T4 uint8_t lo = memory_read(state, 0xfffe); // T5 uint8_t hi = memory_read(state, 0xffff); // T6 cpu->pc = lo | (hi << 8); // T7 cpu->i = 1; } // BRANCHES // static inline int page_crossed(uint16_t a, uint16_t b) { // return (a & 0xff00) != (b & 0xff00); // } __attribute__((hot)) static void opcode_bpl(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t offset = memory_read(state, cpu->pc++); // T1 uint16_t new_pc = cpu->pc + (int8_t)offset; if(!cpu->n) { memory_read_dummy(state, cpu->pc); // T2 if(PAGE_CROSSED(cpu->pc, new_pc)) { memory_read_dummy(state, (cpu->pc & 0xff00) | (new_pc & 0x00ff)); // T3 } cpu->pc = new_pc; // T3 or T4 } } static void opcode_bmi(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t offset = memory_read(state, cpu->pc++); uint16_t new_pc = cpu->pc + (int8_t)offset; if(cpu->n) { memory_read_dummy(state, cpu->pc); if(PAGE_CROSSED(cpu->pc, new_pc)) { memory_read_dummy(state, (cpu->pc & 0xff00) | (new_pc & 0x00ff)); } cpu->pc = new_pc; } } static void opcode_bvc(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t offset = memory_read(state, cpu->pc++); uint16_t new_pc = cpu->pc + (int8_t)offset; if(!cpu->v) { memory_read_dummy(state, cpu->pc); if(PAGE_CROSSED(cpu->pc, new_pc)) { memory_read_dummy(state, (cpu->pc & 0xff00) | (new_pc & 0x00ff)); } cpu->pc = new_pc; } } static void opcode_bvs(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t offset = memory_read(state, cpu->pc++); uint16_t new_pc = cpu->pc + (int8_t)offset; if(cpu->v) { memory_read_dummy(state, cpu->pc); if(PAGE_CROSSED(cpu->pc, new_pc)) { memory_read_dummy(state, (cpu->pc & 0xff00) | (new_pc & 0x00ff)); } cpu->pc = new_pc; } } static void opcode_bcc(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t offset = memory_read(state, cpu->pc++); uint16_t new_pc = cpu->pc + (int8_t)offset; if(!cpu->c) { memory_read_dummy(state, cpu->pc); if(PAGE_CROSSED(cpu->pc, new_pc)) { memory_read_dummy(state, (cpu->pc & 0xff00) | (new_pc & 0x00ff)); } cpu->pc = new_pc; } } static void opcode_bcs(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t offset = memory_read(state, cpu->pc++); uint16_t new_pc = cpu->pc + (int8_t)offset; if(cpu->c) { memory_read_dummy(state, cpu->pc); if(PAGE_CROSSED(cpu->pc, new_pc)) { memory_read_dummy(state, (cpu->pc & 0xff00) | (new_pc & 0x00ff)); } cpu->pc = new_pc; } } __attribute__((hot)) static void opcode_bne(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t offset = memory_read(state, cpu->pc++); uint16_t new_pc = cpu->pc + (int8_t)offset; if(!cpu->z) { memory_read_dummy(state, cpu->pc); if(PAGE_CROSSED(cpu->pc, new_pc)) { memory_read_dummy(state, (cpu->pc & 0xff00) | (new_pc & 0x00ff)); } cpu->pc = new_pc; } } __attribute__((hot)) static void opcode_beq(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t offset = memory_read(state, cpu->pc++); uint16_t new_pc = cpu->pc + (int8_t)offset; if(cpu->z) { memory_read_dummy(state, cpu->pc); if(PAGE_CROSSED(cpu->pc, new_pc)) { memory_read_dummy(state, (cpu->pc & 0xff00) | (new_pc & 0x00ff)); } cpu->pc = new_pc; } } // SET/CLEAR flags __attribute__((hot)) static void opcode_clc(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); cpu->c = 0; } static void opcode_cld(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); cpu->d = 0; } static void opcode_cli(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); cpu->i = 0; } static void opcode_clv(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); cpu->v = 0; } static void opcode_sec(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); cpu->c = 1; } static void opcode_sed(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); cpu->d = 1; } static void opcode_sei(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); cpu->i = 1; } // CMP static inline void cmp(struct cpu_state * restrict cpu, uint8_t value) { uint8_t result = cpu->a - value; cpu->c = (cpu->a >= value); cpu->z = (result == 0); cpu->n = (result & 0x80) != 0; // update_zn(cpu, result); } static void opcode_cmp_indx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t zp = memory_read(state, cpu->pc++); // T1 memory_read_dummy(state, zp); // T2 uint8_t ptr = (zp + cpu->x) & 0xff; uint8_t lo = memory_read(state, ptr); // T3 uint8_t hi = memory_read(state, (ptr + 1) & 0xff); // T4 uint16_t addr = lo | (hi << 8); uint8_t value = memory_read(state, addr); // T5 cmp(cpu, value); // T6 } static void opcode_cmp_zp(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t addr = memory_read(state, cpu->pc++); // T1 uint8_t value = memory_read(state, addr); // T2 cmp(cpu, value); // T3 } static void opcode_cmp_imm(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t value = memory_read(state, cpu->pc++); // T1 cmp(cpu, value); // T2 } static void opcode_cmp_abs(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 uint16_t addr = lo | (hi << 8); uint8_t value = memory_read(state, addr); // T3 cmp(cpu, value); // T4 } static void opcode_cmp_indy(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t zp = memory_read(state, cpu->pc++); // T1 uint8_t lo = memory_read(state, zp); // T2 uint8_t hi = memory_read(state, (zp + 1) & 0xff); // T3 uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->y; if(PAGE_CROSSED(base, addr)) { memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); // T4 dummy } uint8_t value = memory_read(state, addr); // T4 or T5 cmp(cpu, value); // T5 or T6 } static void opcode_cmp_zpx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t base = memory_read(state, cpu->pc++); // T1 memory_read_dummy(state, base); // T2 uint8_t addr = (base + cpu->x) & 0xff; uint8_t value = memory_read(state, addr); // T3 cmp(cpu, value); // T4 } static void opcode_cmp_absy(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->y; if(PAGE_CROSSED(base, addr)) { memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); // T3 dummy } uint8_t value = memory_read(state, addr); // T3 or T4 cmp(cpu, value); // T4 or T5 } static void opcode_cmp_absx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->x; if(PAGE_CROSSED(base, addr)) { memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); // T3 dummy } uint8_t value = memory_read(state, addr); // T3 or T4 cmp(cpu, value); // T4 or T5 } // CPX static inline void cpx(struct cpu_state * restrict cpu, uint8_t value) { uint8_t result = cpu->x - value; cpu->c = (cpu->x >= value); update_zn(cpu, result); } static void opcode_cpx_imm(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t value = memory_read(state, cpu->pc++); // T1 cpx(cpu, value); // T2 } static void opcode_cpx_zp(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t addr = memory_read(state, cpu->pc++); // T1 uint8_t value = memory_read(state, addr); // T2 cpx(cpu, value); // T3 } static void opcode_cpx_abs(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 uint16_t addr = lo | (hi << 8); uint8_t value = memory_read(state, addr); // T3 cpx(cpu, value); // T4 } // CPY static inline void cpy(struct cpu_state * restrict cpu, uint8_t value) { uint8_t result = cpu->y - value; cpu->c = (cpu->y >= value); update_zn(cpu, result); } static void opcode_cpy_imm(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t value = memory_read(state, cpu->pc++); // T1 cpy(cpu, value); // T2 } static void opcode_cpy_zp(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t addr = memory_read(state, cpu->pc++); // T1 uint8_t value = memory_read(state, addr); // T2 cpy(cpu, value); // T3 } static void opcode_cpy_abs(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 uint16_t addr = lo | (hi << 8); uint8_t value = memory_read(state, addr); // T3 cpy(cpu, value); // T4 } // DEC static void opcode_dec_zp(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t addr = memory_read(state, cpu->pc++); // T1 uint8_t value = memory_read(state, addr); // T2 memory_write(state, addr, value); // T3 (dummy write) value--; memory_write(state, addr, value); // T4 update_zn(cpu, value); // T5 } static void opcode_dec_zpx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t base = memory_read(state, cpu->pc++); // T1 memory_read_dummy(state, base); // T2 uint8_t addr = (base + cpu->x) & 0xff; uint8_t value = memory_read(state, addr); // T3 memory_write(state, addr, value); // T4 (dummy write) value--; memory_write(state, addr, value); // T5 update_zn(cpu, value); // T6 } static void opcode_dec_abs(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 uint16_t addr = lo | (hi << 8); uint8_t value = memory_read(state, addr); // T3 memory_write(state, addr, value); // T4 (dummy write) value--; memory_write(state, addr, value); // T5 update_zn(cpu, value); // T6 } static void opcode_dec_absx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->x; memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); // T3 uint8_t value = memory_read(state, addr); // T4 memory_write(state, addr, value); // T5 (dummy write) value--; memory_write(state, addr, value); // T6 update_zn(cpu, value); // T7 } // EOR static void opcode_eor_indx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t zp = memory_read(state, cpu->pc++); // T1 memory_read_dummy(state, zp); // T2 uint8_t ptr = (zp + cpu->x) & 0xff; uint8_t lo = memory_read(state, ptr); // T3 uint8_t hi = memory_read(state, (ptr + 1) & 0xff); // T4 uint16_t addr = lo | (hi << 8); uint8_t value = memory_read(state, addr); // T5 cpu->a ^= value; // T6 update_zn(cpu, cpu->a); } static void opcode_eor_zp(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t addr = memory_read(state, cpu->pc++); // T1 uint8_t value = memory_read(state, addr); // T2 cpu->a ^= value; // T3 update_zn(cpu, cpu->a); } static void opcode_eor_imm(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t value = memory_read(state, cpu->pc++); // T1 cpu->a ^= value; // T2 update_zn(cpu, cpu->a); } static void opcode_eor_abs(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 uint16_t addr = lo | (hi << 8); uint8_t value = memory_read(state, addr); // T3 cpu->a ^= value; // T4 update_zn(cpu, cpu->a); } static void opcode_eor_indy(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t zp = memory_read(state, cpu->pc++); // T1 uint8_t lo = memory_read(state, zp); // T2 uint8_t hi = memory_read(state, (zp + 1) & 0xff); // T3 uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->y; if(PAGE_CROSSED(base, addr)) { memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); // T4 dummy } uint8_t value = memory_read(state, addr); // T4 or T5 cpu->a ^= value; // T5 or T6 update_zn(cpu, cpu->a); } static void opcode_eor_zpx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t base = memory_read(state, cpu->pc++); // T1 memory_read_dummy(state, base); // T2 uint8_t addr = (base + cpu->x) & 0xff; uint8_t value = memory_read(state, addr); // T3 cpu->a ^= value; // T4 update_zn(cpu, cpu->a); } static void opcode_eor_absy(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->y; if(PAGE_CROSSED(base, addr)) { memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); // T3 dummy } uint8_t value = memory_read(state, addr); // T3 or T4 cpu->a ^= value; // T4 or T5 update_zn(cpu, cpu->a); } static void opcode_eor_absx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->x; if(PAGE_CROSSED(base, addr)) { memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); // T3 dummy } uint8_t value = memory_read(state, addr); // T3 or T4 cpu->a ^= value; // T4 or T5 update_zn(cpu, cpu->a); } // INC static void opcode_inc_zp(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t addr = memory_read(state, cpu->pc++); // T1 uint8_t value = memory_read(state, addr); // T2 memory_write(state, addr, value); // T3 (dummy write) value++; memory_write(state, addr, value); // T4 update_zn(cpu, value); // T5 } static void opcode_inc_zpx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t base = memory_read(state, cpu->pc++); // T1 memory_read_dummy(state, base); // T2 uint8_t addr = (base + cpu->x) & 0xff; uint8_t value = memory_read(state, addr); // T3 memory_write(state, addr, value); // T4 (dummy write) value++; memory_write(state, addr, value); // T5 update_zn(cpu, value); // T6 } static void opcode_inc_abs(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 uint16_t addr = lo | (hi << 8); uint8_t value = memory_read(state, addr); // T3 memory_write(state, addr, value); // T4 (dummy write) value++; memory_write(state, addr, value); // T5 update_zn(cpu, value); // T6 } static void opcode_inc_absx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->x; memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); // T3 uint8_t value = memory_read(state, addr); // T4 memory_write(state, addr, value); // T5 (dummy write) value++; memory_write(state, addr, value); // T6 update_zn(cpu, value); // T7 } // JMP/JSR __attribute__((hot)) static void opcode_jmp_abs(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 cpu->pc = lo | (hi << 8); // T3 } static void opcode_jmp_ind(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t ptr_lo = memory_read(state, cpu->pc++); // T1 uint8_t ptr_hi = memory_read(state, cpu->pc++); // T2 uint16_t ptr = ptr_lo | (ptr_hi << 8); uint8_t lo = memory_read(state, ptr); // T3 uint8_t hi; if((ptr & 0x00ff) == 0x00ff) { hi = memory_read(state, ptr & 0xff00); // Emulate 6502 bug } else { hi = memory_read(state, ptr + 1); } // T4 cpu->pc = lo | (hi << 8); // T5 } __attribute__((hot)) static void opcode_jsr(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); uint8_t hi = memory_read(state, cpu->pc++); uint16_t return_addr = cpu->pc - 1; memory_read_dummy(state, 0x0100 | cpu->sp); // T4 memory_write(state, 0x0100 | cpu->sp--, (return_addr >> 8)); // T5 memory_write(state, 0x0100 | cpu->sp--, (return_addr & 0xff)); // T6 cpu->pc = lo | (hi << 8); } // LDA static void opcode_lda_indx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t zp = memory_read(state, cpu->pc++); // T1 memory_read_dummy(state, zp); // T2 uint8_t ptr = (zp + cpu->x) & 0xff; uint8_t lo = memory_read(state, ptr); // T3 uint8_t hi = memory_read(state, (ptr + 1) & 0xff); // T4 uint16_t addr = lo | (hi << 8); uint8_t value = memory_read(state, addr); // T5 cpu->a = value; // T6 update_zn(cpu, value); } __attribute__((hot)) static void opcode_lda_zp(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t addr = memory_read(state, cpu->pc++); // T1 uint8_t value = memory_read(state, addr); // T2 cpu->a = value; // T3 update_zn(cpu, value); } static void opcode_lda_imm(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t value = memory_read(state, cpu->pc++); // T1 cpu->a = value; // T2 update_zn(cpu, value); } __attribute__((hot)) static void opcode_lda_abs(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 uint16_t addr = lo | (hi << 8); uint8_t value = memory_read(state, addr); // T3 cpu->a = value; // T4 update_zn(cpu, value); } static void opcode_lda_indy(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t zp = memory_read(state, cpu->pc++); // T1 uint8_t lo = memory_read(state, zp); // T2 uint8_t hi = memory_read(state, (zp + 1) & 0xff); // T3 uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->y; if(PAGE_CROSSED(base, addr)) { memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); // T4 dummy } uint8_t value = memory_read(state, addr); // T4 or T5 cpu->a = value; // T5 or T6 update_zn(cpu, value); } static void opcode_lda_zpx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t base = memory_read(state, cpu->pc++); // T1 memory_read_dummy(state, base); // T2 uint8_t addr = (base + cpu->x) & 0xff; uint8_t value = memory_read(state, addr); // T3 cpu->a = value; // T4 update_zn(cpu, value); } static void opcode_lda_absy(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->y; if(PAGE_CROSSED(base, addr)) { memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); // T3 } uint8_t value = memory_read(state, addr); // T3 or T4 cpu->a = value; // T4 or T5 update_zn(cpu, value); } __attribute__((hot)) static void opcode_lda_absx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->x; if(PAGE_CROSSED(base, addr)) { memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); // T3 } uint8_t value = memory_read(state, addr); // T3 or T4 cpu->a = value; // T4 or T5 update_zn(cpu, value); } // LDX static void opcode_ldx_imm(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t value = memory_read(state, cpu->pc++); cpu->x = value; update_zn(cpu, value); } static void opcode_ldx_zp(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t addr = memory_read(state, cpu->pc++); uint8_t value = memory_read(state, addr); cpu->x = value; update_zn(cpu, value); } static void opcode_ldx_zpy(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t base = memory_read(state, cpu->pc++); memory_read_dummy(state, base); uint8_t addr = (base + cpu->y) & 0xff; uint8_t value = memory_read(state, addr); cpu->x = value; update_zn(cpu, value); } static void opcode_ldx_abs(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); uint8_t hi = memory_read(state, cpu->pc++); uint16_t addr = lo | (hi << 8); uint8_t value = memory_read(state, addr); cpu->x = value; update_zn(cpu, value); } static void opcode_ldx_absy(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); uint8_t hi = memory_read(state, cpu->pc++); uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->y; if(PAGE_CROSSED(base, addr)) { memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); } uint8_t value = memory_read(state, addr); cpu->x = value; update_zn(cpu, value); } // LDY static void opcode_ldy_imm(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t value = memory_read(state, cpu->pc++); cpu->y = value; update_zn(cpu, value); } static void opcode_ldy_zp(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t addr = memory_read(state, cpu->pc++); uint8_t value = memory_read(state, addr); cpu->y = value; update_zn(cpu, value); } static void opcode_ldy_zpx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t base = memory_read(state, cpu->pc++); memory_read_dummy(state, base); uint8_t addr = (base + cpu->x) & 0xff; uint8_t value = memory_read(state, addr); cpu->y = value; update_zn(cpu, value); } static void opcode_ldy_abs(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); uint8_t hi = memory_read(state, cpu->pc++); uint16_t addr = lo | (hi << 8); uint8_t value = memory_read(state, addr); cpu->y = value; update_zn(cpu, value); } static void opcode_ldy_absx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); uint8_t hi = memory_read(state, cpu->pc++); uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->x; if(PAGE_CROSSED(base, addr)) { memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); } uint8_t value = memory_read(state, addr); cpu->y = value; update_zn(cpu, value); } // LSR static void opcode_lsr_zp(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t addr = memory_read(state, cpu->pc++); // T1 uint8_t value = memory_read(state, addr); // T2 memory_write(state, addr, value); // T3 (dummy write) cpu->c = value & 1; value >>= 1; memory_write(state, addr, value); // T4 update_zn(cpu, value); // T5 } static void opcode_lsr_acc(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); // T1 cpu->c = cpu->a & 1; cpu->a >>= 1; update_zn(cpu, cpu->a); // T2 } static void opcode_lsr_abs(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 uint16_t addr = lo | (hi << 8); uint8_t value = memory_read(state, addr); // T3 memory_write(state, addr, value); // T4 (dummy write) cpu->c = value & 1; value >>= 1; memory_write(state, addr, value); // T5 update_zn(cpu, value); // T6 } static void opcode_lsr_zpx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t base = memory_read(state, cpu->pc++); // T1 memory_read_dummy(state, base); // T2 uint8_t addr = (base + cpu->x) & 0xff; uint8_t value = memory_read(state, addr); // T3 memory_write(state, addr, value); // T4 (dummy write) cpu->c = value & 1; value >>= 1; memory_write(state, addr, value); // T5 update_zn(cpu, value); // T6 } static void opcode_lsr_absx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->x; memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); // T3 uint8_t value = memory_read(state, addr); // T4 memory_write(state, addr, value); // T5 (dummy write) cpu->c = value & 1; value >>= 1; memory_write(state, addr, value); // T6 update_zn(cpu, value); // T7 } // NOP static void opcode_nop(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); // T1: dummy read // T2: nothing changes } // ORA static void opcode_ora_indx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t zp = memory_read(state, cpu->pc++); // T1 memory_read_dummy(state, zp); // T2 uint8_t ptr = (zp + cpu->x) & 0xff; uint8_t lo = memory_read(state, ptr); // T3 uint8_t hi = memory_read(state, (ptr + 1) & 0xff); // T4 uint16_t addr = lo | (hi << 8); uint8_t value = memory_read(state, addr); // T5 cpu->a |= value; // T6 update_zn(cpu, cpu->a); } static void opcode_ora_zp(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t addr = memory_read(state, cpu->pc++); uint8_t value = memory_read(state, addr); cpu->a |= value; update_zn(cpu, cpu->a); } static void opcode_ora_imm(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t value = memory_read(state, cpu->pc++); cpu->a |= value; update_zn(cpu, cpu->a); } static void opcode_ora_abs(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); uint8_t hi = memory_read(state, cpu->pc++); uint16_t addr = lo | (hi << 8); uint8_t value = memory_read(state, addr); cpu->a |= value; update_zn(cpu, cpu->a); } static void opcode_ora_indy(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t zp = memory_read(state, cpu->pc++); uint8_t lo = memory_read(state, zp); uint8_t hi = memory_read(state, (zp + 1) & 0xff); uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->y; if(PAGE_CROSSED(base, addr)) { memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); } uint8_t value = memory_read(state, addr); cpu->a |= value; update_zn(cpu, cpu->a); } static void opcode_ora_zpx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t base = memory_read(state, cpu->pc++); memory_read_dummy(state, base); uint8_t addr = (base + cpu->x) & 0xff; uint8_t value = memory_read(state, addr); cpu->a |= value; update_zn(cpu, cpu->a); } static void opcode_ora_absy(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); uint8_t hi = memory_read(state, cpu->pc++); uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->y; if(PAGE_CROSSED(base, addr)) { memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); } uint8_t value = memory_read(state, addr); cpu->a |= value; update_zn(cpu, cpu->a); } static void opcode_ora_absx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); uint8_t hi = memory_read(state, cpu->pc++); uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->x; if(PAGE_CROSSED(base, addr)) { memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); } uint8_t value = memory_read(state, addr); cpu->a |= value; update_zn(cpu, cpu->a); } // PHA static void opcode_pha(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); // T1 memory_write(state, 0x0100 + cpu->sp--, cpu->a); // T2 } // PHP static void opcode_php(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); // T1 memory_write(state, 0x0100 + cpu->sp--, pack_flags(cpu) | 0x10); } // PLA static void opcode_pla(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); // T1 memory_read_dummy(state, 0x0100 + ((cpu->sp + 1) & 0xff)); // T2 uint8_t value = memory_read(state, 0x0100 + ((cpu->sp + 1) & 0xff)); // T3 cpu->sp++; cpu->a = value; update_zn(cpu, value); // T4 } // PLP static void opcode_plp(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); // T1 memory_read_dummy(state, 0x0100 + ((cpu->sp + 1) & 0xff)); // T2 uint8_t value = memory_read(state, 0x0100 + ((cpu->sp + 1) & 0xff)); // T3 cpu->sp++; unpack_flags(cpu, value); // T4 } // RTI static void opcode_rti(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); // T1 memory_read_dummy(state, 0x0100 + ((cpu->sp + 1) & 0xff)); // T2 uint8_t flags = memory_read(state, 0x0100 + ((cpu->sp + 1) & 0xff)); // T3 cpu->sp++; unpack_flags(cpu, flags); // T4 uint8_t lo = memory_read(state, 0x0100 + ((cpu->sp + 1) & 0xff)); // T5 cpu->sp++; uint8_t hi = memory_read(state, 0x0100 + ((cpu->sp + 1) & 0xff)); // T6 cpu->sp++; cpu->pc = lo | (hi << 8); } // RTS static void opcode_rts(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); // T1 memory_read_dummy(state, 0x0100 | cpu->sp); // T2 (discard) uint8_t lo = memory_read(state, 0x0100 | ++cpu->sp); // T3 uint8_t hi = memory_read(state, 0x0100 | ++cpu->sp); // T4 uint16_t ret = (hi << 8) | lo; memory_read_dummy(state, ret); // T5 cpu->pc = ret + 1; // Final PC set } // ROL static void opcode_rol_zp(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t addr = memory_read(state, cpu->pc++); uint8_t value = memory_read(state, addr); memory_write(state, addr, value); // Dummy write uint8_t in = cpu->c; cpu->c = (value >> 7) & 1; value = (value << 1) | in; memory_write(state, addr, value); update_zn(cpu, value); } static void opcode_rol_acc(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); uint8_t in = cpu->c; cpu->c = (cpu->a >> 7) & 1; cpu->a = (cpu->a << 1) | in; update_zn(cpu, cpu->a); } static void opcode_rol_abs(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); uint8_t hi = memory_read(state, cpu->pc++); uint16_t addr = lo | (hi << 8); uint8_t value = memory_read(state, addr); memory_write(state, addr, value); // Dummy write uint8_t in = cpu->c; cpu->c = (value >> 7) & 1; value = (value << 1) | in; memory_write(state, addr, value); update_zn(cpu, value); } static void opcode_rol_zpx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t base = memory_read(state, cpu->pc++); memory_read_dummy(state, base); uint8_t addr = (base + cpu->x) & 0xff; uint8_t value = memory_read(state, addr); memory_write(state, addr, value); // Dummy write uint8_t in = cpu->c; cpu->c = (value >> 7) & 1; value = (value << 1) | in; memory_write(state, addr, value); update_zn(cpu, value); } static void opcode_rol_absx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); uint8_t hi = memory_read(state, cpu->pc++); uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->x; memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); uint8_t value = memory_read(state, addr); memory_write(state, addr, value); // Dummy write uint8_t in = cpu->c; cpu->c = (value >> 7) & 1; value = (value << 1) | in; memory_write(state, addr, value); update_zn(cpu, value); } // ROR static void opcode_ror_zp(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t addr = memory_read(state, cpu->pc++); uint8_t value = memory_read(state, addr); memory_write(state, addr, value); // Dummy write uint8_t in = cpu->c; cpu->c = value & 1; value = (value >> 1) | (in << 7); memory_write(state, addr, value); update_zn(cpu, value); } static void opcode_ror_acc(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); uint8_t in = cpu->c; cpu->c = cpu->a & 1; cpu->a = (cpu->a >> 1) | (in << 7); update_zn(cpu, cpu->a); } static void opcode_ror_abs(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); uint8_t hi = memory_read(state, cpu->pc++); uint16_t addr = lo | (hi << 8); uint8_t value = memory_read(state, addr); memory_write(state, addr, value); // Dummy write uint8_t in = cpu->c; cpu->c = value & 1; value = (value >> 1) | (in << 7); memory_write(state, addr, value); update_zn(cpu, value); } static void opcode_ror_zpx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t base = memory_read(state, cpu->pc++); memory_read_dummy(state, base); uint8_t addr = (base + cpu->x) & 0xff; uint8_t value = memory_read(state, addr); memory_write(state, addr, value); // Dummy write uint8_t in = cpu->c; cpu->c = value & 1; value = (value >> 1) | (in << 7); memory_write(state, addr, value); update_zn(cpu, value); } static void opcode_ror_absx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); uint8_t hi = memory_read(state, cpu->pc++); uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->x; memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); uint8_t value = memory_read(state, addr); memory_write(state, addr, value); // Dummy write uint8_t in = cpu->c; cpu->c = value & 1; value = (value >> 1) | (in << 7); memory_write(state, addr, value); update_zn(cpu, value); } // SBC static inline void sbc(struct cpu_state * restrict cpu, uint8_t value) { #ifdef ENABLE_DECIMAL_MODE if(cpu->d) { uint8_t al = (cpu->a & 0x0f) - (value & 0x0f) - (cpu->c ? 0 : 1); uint8_t ah = (cpu->a >> 4) - (value >> 4); if((int8_t)al < 0) { al -= 6; ah--; } cpu->c = ((int8_t)ah >= 0); if(!cpu->c) { ah -= 6; } cpu->a = (ah << 4) | (al & 0x0f); update_zn(cpu, cpu->a); // Note: overflow flag behavior in decimal mode is undefined on 6502 return; } #endif uint16_t tmp = cpu->a - value - (cpu->c ? 0 : 1); cpu->v = ((cpu->a ^ value) & (cpu->a ^ tmp)) & 0x80 ? 1 : 0; cpu->c = (tmp < 0x100); cpu->a = tmp & 0xff; update_zn(cpu, cpu->a); } static void opcode_sbc_indx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t zp = memory_read(state, cpu->pc++); memory_read_dummy(state, zp); uint8_t ptr = (zp + cpu->x) & 0xff; uint8_t lo = memory_read(state, ptr); uint8_t hi = memory_read(state, (ptr + 1) & 0xff); uint16_t addr = lo | (hi << 8); uint8_t value = memory_read(state, addr); sbc(cpu, value); } static void opcode_sbc_zp(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t addr = memory_read(state, cpu->pc++); uint8_t value = memory_read(state, addr); sbc(cpu, value); } static void opcode_sbc_imm(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t value = memory_read(state, cpu->pc++); sbc(cpu, value); } static void opcode_sbc_abs(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); uint8_t hi = memory_read(state, cpu->pc++); uint16_t addr = lo | (hi << 8); uint8_t value = memory_read(state, addr); sbc(cpu, value); } static void opcode_sbc_indy(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t zp = memory_read(state, cpu->pc++); uint8_t lo = memory_read(state, zp); uint8_t hi = memory_read(state, (zp + 1) & 0xff); uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->y; if(PAGE_CROSSED(base, addr)) { memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); } uint8_t value = memory_read(state, addr); sbc(cpu, value); } static void opcode_sbc_zpx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t base = memory_read(state, cpu->pc++); memory_read_dummy(state, base); uint8_t addr = (base + cpu->x) & 0xff; uint8_t value = memory_read(state, addr); sbc(cpu, value); } static void opcode_sbc_absy(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); uint8_t hi = memory_read(state, cpu->pc++); uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->y; if(PAGE_CROSSED(base, addr)) { memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); } uint8_t value = memory_read(state, addr); sbc(cpu, value); } static void opcode_sbc_absx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); uint8_t hi = memory_read(state, cpu->pc++); uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->x; if(PAGE_CROSSED(base, addr)) { memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); } uint8_t value = memory_read(state, addr); sbc(cpu, value); } // TAX static void opcode_tax(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); cpu->x = cpu->a; update_zn(cpu, cpu->x); } static void opcode_tay(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); cpu->y = cpu->a; update_zn(cpu, cpu->y); } static void opcode_txa(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); cpu->a = cpu->x; update_zn(cpu, cpu->a); } static void opcode_tya(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); cpu->a = cpu->y; update_zn(cpu, cpu->a); } static void opcode_tsx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); cpu->x = cpu->sp; update_zn(cpu, cpu->x); } static void opcode_txs(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); cpu->sp = cpu->x; } // INX static void opcode_inx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); cpu->x++; update_zn(cpu, cpu->x); } // INY static void opcode_iny(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); cpu->y++; update_zn(cpu, cpu->y); } // DEX static void opcode_dex(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); cpu->x--; update_zn(cpu, cpu->x); } // DEY static void opcode_dey(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; memory_read_dummy(state, cpu->pc); cpu->y--; update_zn(cpu, cpu->y); } // STA static void opcode_sta_indx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t zp = memory_read(state, cpu->pc++); memory_read_dummy(state, zp); uint8_t ptr = (zp + cpu->x) & 0xff; uint8_t lo = memory_read(state, ptr); uint8_t hi = memory_read(state, (ptr + 1) & 0xff); uint16_t addr = lo | (hi << 8); memory_write(state, addr, cpu->a); } __attribute__((hot)) static void opcode_sta_zp(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t addr = memory_read(state, cpu->pc++); memory_write(state, addr, cpu->a); } static void opcode_sta_abs(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); uint8_t hi = memory_read(state, cpu->pc++); uint16_t addr = lo | (hi << 8); memory_write(state, addr, cpu->a); } static void opcode_sta_indy(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t zp = memory_read(state, cpu->pc++); // T1 uint8_t lo = memory_read(state, zp); // T2 uint8_t hi = memory_read(state, (zp + 1) & 0xff); // T3 uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->y; memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); memory_write(state, addr, cpu->a); // T5 } static void opcode_sta_zpx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t base = memory_read(state, cpu->pc++); memory_read_dummy(state, base); uint8_t addr = (base + cpu->x) & 0xff; memory_write(state, addr, cpu->a); } static void opcode_sta_absy(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); uint8_t hi = memory_read(state, cpu->pc++); uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->y; memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); memory_write(state, addr, cpu->a); } __attribute__((hot)) static void opcode_sta_absx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); // T1 uint8_t hi = memory_read(state, cpu->pc++); // T2 uint16_t base = lo | (hi << 8); uint16_t addr = base + cpu->x; memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); // T3 (always) memory_write(state, addr, cpu->a); // T4 } // STX static void opcode_stx_zp(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t addr = memory_read(state, cpu->pc++); memory_write(state, addr, cpu->x); } static void opcode_stx_abs(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); uint8_t hi = memory_read(state, cpu->pc++); uint16_t addr = lo | (hi << 8); memory_write(state, addr, cpu->x); } static void opcode_stx_zpy(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t base = memory_read(state, cpu->pc++); memory_read_dummy(state, base); uint8_t addr = (base + cpu->y) & 0xff; memory_write(state, addr, cpu->x); } // STY static void opcode_sty_zp(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t addr = memory_read(state, cpu->pc++); memory_write(state, addr, cpu->y); } static void opcode_sty_abs(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t lo = memory_read(state, cpu->pc++); uint8_t hi = memory_read(state, cpu->pc++); uint16_t addr = lo | (hi << 8); memory_write(state, addr, cpu->y); } static void opcode_sty_zpx(struct nes_state *state) { struct cpu_state * restrict cpu = &state->cpu; uint8_t base = memory_read(state, cpu->pc++); memory_read_dummy(state, base); uint8_t addr = (base + cpu->x) & 0xff; memory_write(state, addr, cpu->y); } __attribute__((noinline)) static void init_opcode_lut(void) { for(uint32_t i = 0; i < 256; ++i) { opcode_lut[i] = opcode_nop; // make sure erroneous opcodes just do "NOP", this might change in the future! } opcode_lut[0x00] = opcode_brk; opcode_lut[0x01] = opcode_ora_indx; opcode_lut[0x05] = opcode_ora_zp; opcode_lut[0x06] = opcode_asl_zp; opcode_lut[0x08] = opcode_php; opcode_lut[0x09] = opcode_ora_imm; opcode_lut[0x0a] = opcode_asl_acc; opcode_lut[0x0d] = opcode_ora_abs; opcode_lut[0x0e] = opcode_asl_abs; opcode_lut[0x10] = opcode_bpl; opcode_lut[0x11] = opcode_ora_indy; opcode_lut[0x15] = opcode_ora_zpx; opcode_lut[0x16] = opcode_asl_zpx; opcode_lut[0x18] = opcode_clc; opcode_lut[0x19] = opcode_ora_absy; opcode_lut[0x1d] = opcode_ora_absx; opcode_lut[0x1e] = opcode_asl_absx; opcode_lut[0x20] = opcode_jsr; opcode_lut[0x21] = opcode_and_indx; opcode_lut[0x24] = opcode_bit_zp; opcode_lut[0x25] = opcode_and_zp; opcode_lut[0x26] = opcode_rol_zp; opcode_lut[0x28] = opcode_plp; opcode_lut[0x29] = opcode_and_imm; opcode_lut[0x2a] = opcode_rol_acc; opcode_lut[0x2c] = opcode_bit_abs; opcode_lut[0x2d] = opcode_and_abs; opcode_lut[0x2e] = opcode_rol_abs; opcode_lut[0x30] = opcode_bmi; opcode_lut[0x31] = opcode_and_indy; opcode_lut[0x35] = opcode_and_zpx; opcode_lut[0x36] = opcode_rol_zpx; opcode_lut[0x38] = opcode_sec; opcode_lut[0x39] = opcode_and_absy; opcode_lut[0x3d] = opcode_and_absx; opcode_lut[0x3e] = opcode_rol_absx; opcode_lut[0x40] = opcode_rti; opcode_lut[0x41] = opcode_eor_indx; opcode_lut[0x45] = opcode_eor_zp; opcode_lut[0x46] = opcode_lsr_zp; opcode_lut[0x48] = opcode_pha; opcode_lut[0x49] = opcode_eor_imm; opcode_lut[0x4a] = opcode_lsr_acc; opcode_lut[0x4c] = opcode_jmp_abs; opcode_lut[0x4d] = opcode_eor_abs; opcode_lut[0x4e] = opcode_lsr_abs; opcode_lut[0x50] = opcode_bvc; opcode_lut[0x51] = opcode_eor_indy; opcode_lut[0x55] = opcode_eor_zpx; opcode_lut[0x56] = opcode_lsr_zpx; opcode_lut[0x58] = opcode_cli; opcode_lut[0x59] = opcode_eor_absy; opcode_lut[0x5d] = opcode_eor_absx; opcode_lut[0x5e] = opcode_lsr_absx; opcode_lut[0x60] = opcode_rts; opcode_lut[0x61] = opcode_adc_indx; opcode_lut[0x65] = opcode_adc_zp; opcode_lut[0x66] = opcode_ror_zp; opcode_lut[0x68] = opcode_pla; opcode_lut[0x69] = opcode_adc_imm; opcode_lut[0x6a] = opcode_ror_acc; opcode_lut[0x6c] = opcode_jmp_ind; opcode_lut[0x6d] = opcode_adc_abs; opcode_lut[0x6e] = opcode_ror_abs; opcode_lut[0x70] = opcode_bvs; opcode_lut[0x71] = opcode_adc_indy; opcode_lut[0x75] = opcode_adc_zpx; opcode_lut[0x76] = opcode_ror_zpx; opcode_lut[0x78] = opcode_sei; opcode_lut[0x79] = opcode_adc_absy; opcode_lut[0x7d] = opcode_adc_absx; opcode_lut[0x7e] = opcode_ror_absx; opcode_lut[0x81] = opcode_sta_indx; opcode_lut[0x84] = opcode_sty_zp; opcode_lut[0x85] = opcode_sta_zp; opcode_lut[0x86] = opcode_stx_zp; opcode_lut[0x88] = opcode_dey; opcode_lut[0x8a] = opcode_txa; opcode_lut[0x8c] = opcode_sty_abs; opcode_lut[0x8d] = opcode_sta_abs; opcode_lut[0x8e] = opcode_stx_abs; opcode_lut[0x90] = opcode_bcc; opcode_lut[0x91] = opcode_sta_indy; opcode_lut[0x94] = opcode_sty_zpx; opcode_lut[0x95] = opcode_sta_zpx; opcode_lut[0x96] = opcode_stx_zpy; opcode_lut[0x98] = opcode_tya; opcode_lut[0x99] = opcode_sta_absy; opcode_lut[0x9a] = opcode_txs; opcode_lut[0x9d] = opcode_sta_absx; opcode_lut[0xa0] = opcode_ldy_imm; opcode_lut[0xa1] = opcode_lda_indx; opcode_lut[0xa2] = opcode_ldx_imm; opcode_lut[0xa4] = opcode_ldy_zp; opcode_lut[0xa5] = opcode_lda_zp; opcode_lut[0xa6] = opcode_ldx_zp; opcode_lut[0xa8] = opcode_tay; opcode_lut[0xa9] = opcode_lda_imm; opcode_lut[0xaa] = opcode_tax; opcode_lut[0xac] = opcode_ldy_abs; opcode_lut[0xad] = opcode_lda_abs; opcode_lut[0xae] = opcode_ldx_abs; opcode_lut[0xb0] = opcode_bcs; opcode_lut[0xb1] = opcode_lda_indy; opcode_lut[0xb4] = opcode_ldy_zpx; opcode_lut[0xb5] = opcode_lda_zpx; opcode_lut[0xb6] = opcode_ldx_zpy; opcode_lut[0xb8] = opcode_clv; opcode_lut[0xb9] = opcode_lda_absy; opcode_lut[0xba] = opcode_tsx; opcode_lut[0xbc] = opcode_ldy_absx; opcode_lut[0xbd] = opcode_lda_absx; opcode_lut[0xbe] = opcode_ldx_absy; opcode_lut[0xc0] = opcode_cpy_imm; opcode_lut[0xc1] = opcode_cmp_indx; opcode_lut[0xc4] = opcode_cpy_zp; opcode_lut[0xc5] = opcode_cmp_zp; opcode_lut[0xc6] = opcode_dec_zp; opcode_lut[0xc8] = opcode_iny; opcode_lut[0xc9] = opcode_cmp_imm; opcode_lut[0xca] = opcode_dex; opcode_lut[0xcc] = opcode_cpy_abs; opcode_lut[0xcd] = opcode_cmp_abs; opcode_lut[0xce] = opcode_dec_abs; opcode_lut[0xd0] = opcode_bne; opcode_lut[0xd1] = opcode_cmp_indy; opcode_lut[0xd5] = opcode_cmp_zpx; opcode_lut[0xd6] = opcode_dec_zpx; opcode_lut[0xd8] = opcode_cld; opcode_lut[0xd9] = opcode_cmp_absy; opcode_lut[0xdd] = opcode_cmp_absx; opcode_lut[0xde] = opcode_dec_absx; opcode_lut[0xe0] = opcode_cpx_imm; opcode_lut[0xe1] = opcode_sbc_indx; opcode_lut[0xe4] = opcode_cpx_zp; opcode_lut[0xe5] = opcode_sbc_zp; opcode_lut[0xe6] = opcode_inc_zp; opcode_lut[0xe8] = opcode_inx; opcode_lut[0xe9] = opcode_sbc_imm; opcode_lut[0xea] = opcode_nop; opcode_lut[0xeb] = opcode_sbc_imm; opcode_lut[0xec] = opcode_cpx_abs; opcode_lut[0xed] = opcode_sbc_abs; opcode_lut[0xee] = opcode_inc_abs; opcode_lut[0xf0] = opcode_beq; opcode_lut[0xf1] = opcode_sbc_indy; opcode_lut[0xf5] = opcode_sbc_zpx; opcode_lut[0xf6] = opcode_inc_zpx; opcode_lut[0xf8] = opcode_sed; opcode_lut[0xf9] = opcode_sbc_absy; opcode_lut[0xfd] = opcode_sbc_absx; opcode_lut[0xfe] = opcode_inc_absx; }