summaryrefslogtreecommitdiff
path: root/mknes_cpu_opcodes.c
diff options
context:
space:
mode:
authorPeter Fors <peter.fors@mindkiller.com>2025-10-09 22:07:52 +0200
committerPeter Fors <peter.fors@mindkiller.com>2025-10-09 22:07:52 +0200
commit030724a9aea346e4a9843d5842fb28c6d6c4cf1a (patch)
treef06fb84aaef64b2f4e2d81b3d2d3eef71bad83ec /mknes_cpu_opcodes.c
parent412b2ef851516c1de8ba5006ddd284192cbcaf9b (diff)
Rearrangement and refactoring and optimizations and more accuracy
Diffstat (limited to 'mknes_cpu_opcodes.c')
-rw-r--r--mknes_cpu_opcodes.c2299
1 files changed, 2299 insertions, 0 deletions
diff --git a/mknes_cpu_opcodes.c b/mknes_cpu_opcodes.c
new file mode 100644
index 0000000..0254b5c
--- /dev/null
+++ b/mknes_cpu_opcodes.c
@@ -0,0 +1,2299 @@
+
+
+// ADC
+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;
+}
+
+
+
+
+
+
+
+
+
+
+
+