diff options
| author | Peter Fors <peter.fors@mindkiller.com> | 2025-03-29 19:57:00 +0100 |
|---|---|---|
| committer | Peter Fors <peter.fors@mindkiller.com> | 2025-03-29 19:57:00 +0100 |
| commit | e8ff6bf2ab9982c5e5ab8d8f4e7adcc5207d079d (patch) | |
| tree | a91ec214f5cd0e95c3d26ee0ba916cb187e3aede | |
first mknes commit
| -rwxr-xr-x | build.sh | 79 | ||||
| -rw-r--r-- | cpu.c | 101 | ||||
| -rw-r--r-- | cpu_opcodes.c | 2287 | ||||
| -rw-r--r-- | cpu_opcodes_ud.c | 1232 | ||||
| -rw-r--r-- | ines2.c | 90 | ||||
| -rw-r--r-- | mapper.c | 28 | ||||
| -rw-r--r-- | mapper_nrom.c | 27 | ||||
| -rw-r--r-- | memory.c | 95 | ||||
| -rw-r--r-- | mknes.c | 102 | ||||
| -rw-r--r-- | mknes.h | 146 | ||||
| -rw-r--r-- | ppu.c | 490 |
11 files changed, 4677 insertions, 0 deletions
diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..efe3158 --- /dev/null +++ b/build.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +# Set the project name here +PROJECT_NAME="mknes" # Change this for each new project + +# Base configuration common to all builds +CFLAGS="-std=gnu11 " +CFLAGS+="-mavx2 -mtune=native -mfunction-return=keep -mindirect-branch=keep " +CFLAGS+="-fwrapv -ffast-math -fno-trapping-math -fwhole-program " +CFLAGS+="-fno-stack-protector -fno-PIE -no-pie -fno-strict-aliasing -ffunction-sections -fdata-sections " +CFLAGS+="-Wall -Wextra " +CFLAGS+="-Wno-unused-parameter -Wno-sign-compare -Wno-trigraphs -Wno-maybe-uninitialized " +CFLAGS+="-Wno-unused-variable -Wno-unused-const-variable -Wno-unused-function " + +LDFLAGS="-Wl,--gc-sections " + +# Base include paths (common for all platforms) +INCLUDE_PATHS="-Ibase -I.." + +# Linux-specific includes and libraries +LINUX_INCLUDE="-I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2" +LINUX_LIBS="-lpipewire-0.3 -lXi -lX11 -lGL -lm -ldl -pthread" + +# Windows-specific includes and libraries +# WINDOWS_INCLUDE="" +# WINDOWS_LIBS="-lwinmm -lksuser -lole32 -lmmdevapi -lavrt -lgdi32 -lopengl32 -luuid" + +# Determine build type-specific flags +BUILD_TYPE=$1 + +if [ -z "$BUILD_TYPE" ]; then + BUILD_TYPE="normal" +fi + +case "$BUILD_TYPE" in + "normal") + CFLAGS+=" -g -O2 -DDEBUG_INTERNAL" + ;; + "release") + CFLAGS+=" -s -O2" + ;; + "debug") + CFLAGS+=" -g -O0" + LDFLAGS+=" -fno-pie -no-pie" + ;; + *) + echo "Unknown build type: $BUILD_TYPE" + exit 1 + ;; +esac + +# Rebuild assets every time we compile +#rm -rf data +#mkdir -p data/p{1,2,3,4,5,6,7,8} +#env -C org_assets ../../bin/mks_time ./process.sh + +# Make sure the <basecode> shaders are up to date if you are experimenting with them. +#env -C ../base ../bin/shader2h 140 vertex_shader vertex_shader.glsl +#env -C ../base ../bin/shader2h 140 fragment_shader shader.h fragment_shader.glsl + +# Make sure the compilation stops if any error happens. +set -e + +# Build Linux version +( + # ../bin/ctime -begin .${PROJECT_NAME}_linux + gcc $CFLAGS ${PROJECT_NAME}.c -o ${PROJECT_NAME} $INCLUDE_PATHS $LINUX_INCLUDE $LDFLAGS $LINUX_LIBS + # ../bin/ctime -end .${PROJECT_NAME}_linux $? +) & + +# Build Windows version +# ( +# ../bin/ctime -begin .${PROJECT_NAME}_windows +# x86_64-w64-mingw32-gcc $CFLAGS ${PROJECT_NAME}.c -o ${PROJECT_NAME}.exe $INCLUDE_PATHS $WINDOWS_INCLUDE $LDFLAGS $WINDOWS_LIBS +# # x86_64-w64-mingw32-gcc $CFLAGS ${PROJECT_NAME}.c -o ${PROJECT_NAME}.exe -mwindows $INCLUDE_PATHS $WINDOWS_INCLUDE $LDFLAGS $WINDOWS_LIBS +# ../bin/ctime -end .${PROJECT_NAME}_windows $? +# ) & + +wait @@ -0,0 +1,101 @@ +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +// REMOVE FOR NES!!!!! +// #define ENABLE_DECIMAL_MODE + +__attribute__((always_inline)) +static inline uint8_t pack_flags(struct cpu_state *cpu) { + return (cpu->n << 7) | (cpu->v << 6) | (1 << 5) | (cpu->d << 3) | (cpu->i << 2) | (cpu->z << 1) | cpu->c; +} + +__attribute__((always_inline)) +static inline void unpack_flags(struct cpu_state *cpu, uint8_t value) { + cpu->n = (value >> 7) & 1; + cpu->v = (value >> 6) & 1; + cpu->d = (value >> 3) & 1; + cpu->i = (value >> 2) & 1; + cpu->z = (value >> 1) & 1; + cpu->c = value & 1; +} + + +static inline void update_zn(struct cpu_state *cpu, uint8_t result) { + cpu->z = (result == 0); + cpu->n = (result & 0x80) != 0; +} + + + +static void (*opcode_lut[256])(struct nes_state *state); + +uint32_t line = 1; + +struct addr_result { + uint32_t addr; + uint8_t value; +}; +#include "cpu_opcodes.c" +#include "cpu_opcodes_ud.c" + +static inline void do_nmi(struct nes_state * restrict state) { + struct cpu_state * restrict cpu = &state->cpu; + + memory_read_dummy(state, cpu->pc); // T1: dummy read (fetch suppressed) + + 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: push P (B flag clear) + + uint8_t lo = memory_read(state, 0xfffa); // T5 + uint8_t hi = memory_read(state, 0xfffb); // T6 + + cpu->pc = lo | (hi << 8); // T7 + cpu->i = 1; +} + +static inline void do_irq(struct nes_state * restrict state) { + struct cpu_state * restrict cpu = &state->cpu; + + memory_read_dummy(state, cpu->pc); // T1: dummy read (fetch suppressed) + + 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: push P (B flag clear) + + 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; +} + +static inline void check_interrupts(struct nes_state * restrict state) { + struct cpu_state * restrict cpu = &state->cpu; + + if(state->nmi_pending) { + state->nmi_pending = 0; + do_nmi(state); + } else if(state->irq_pending && cpu->i == 0) { + do_irq(state); + } +} + +static void cpu_tick(struct nes_state *state) { + struct cpu_state * restrict cpu = &state->cpu; + + check_interrupts(state); + + // printf("%5.5d %4.4x: ", line++, cpu->pc); + uint8_t opcode = memory_read(state, cpu->pc++); + // printf("%2.2x a:%2.2x x:%2.2x y:%2.2x p:%2.2x sp:%2.2x cycle: %d\n", opcode, cpu->a, cpu->x, cpu->y, pack_flags(cpu), cpu->sp, cycle); + opcode_lut[opcode](state); + +} diff --git a/cpu_opcodes.c b/cpu_opcodes.c new file mode 100644 index 0000000..2d308da --- /dev/null +++ b/cpu_opcodes.c @@ -0,0 +1,2287 @@ + + +// ADC + +static inline void adc(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; + 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 * restrict 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 +static void opcode_adc_zp(struct nes_state * restrict 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 * restrict 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 * restrict 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 * restrict 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((base & 0xff00) != (addr & 0xff00)) { + 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 * restrict 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 * restrict 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((base & 0xff00) != (addr & 0xff00)) { + 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 * restrict 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((base & 0xff00) != (addr & 0xff00)) { + 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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((base & 0xff00) != (addr & 0xff00)) { + 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 * restrict 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 * restrict 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((base & 0xff00) != (addr & 0xff00)) { + 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 * restrict 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((base & 0xff00) != (addr & 0xff00)) { + 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 * restrict 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 +static void opcode_asl_acc(struct nes_state * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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); +} + +static void opcode_bpl(struct nes_state * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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_bne(struct nes_state * restrict 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; + } +} + +static void opcode_beq(struct nes_state * restrict 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 + +static void opcode_clc(struct nes_state * restrict state) { + struct cpu_state * restrict cpu = &state->cpu; + memory_read_dummy(state, cpu->pc); + cpu->c = 0; +} + +static void opcode_cld(struct nes_state * restrict state) { + struct cpu_state * restrict cpu = &state->cpu; + memory_read_dummy(state, cpu->pc); + cpu->d = 0; +} + +static void opcode_cli(struct nes_state * restrict state) { + struct cpu_state * restrict cpu = &state->cpu; + memory_read_dummy(state, cpu->pc); + cpu->i = 0; +} + +static void opcode_clv(struct nes_state * restrict state) { + struct cpu_state * restrict cpu = &state->cpu; + memory_read_dummy(state, cpu->pc); + cpu->v = 0; +} + +static void opcode_sec(struct nes_state * restrict state) { + struct cpu_state * restrict cpu = &state->cpu; + memory_read_dummy(state, cpu->pc); + cpu->c = 1; +} + +static void opcode_sed(struct nes_state * restrict state) { + struct cpu_state * restrict cpu = &state->cpu; + memory_read_dummy(state, cpu->pc); + cpu->d = 1; +} + +static void opcode_sei(struct nes_state * restrict 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); + update_zn(cpu, result); +} + +static void opcode_cmp_indx(struct nes_state * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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((base & 0xff00) != (addr & 0xff00)) { + 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 * restrict 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 * restrict 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((base & 0xff00) != (addr & 0xff00)) { + 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 * restrict 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((base & 0xff00) != (addr & 0xff00)) { + 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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((base & 0xff00) != (addr & 0xff00)) { + 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 * restrict 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 * restrict 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((base & 0xff00) != (addr & 0xff00)) { + 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 * restrict 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((base & 0xff00) != (addr & 0xff00)) { + 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 + +static void opcode_jmp_abs(struct nes_state * restrict 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 * restrict 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 +} + +static void opcode_jsr(struct nes_state * restrict 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 * restrict 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); +} + +static void opcode_lda_zp(struct nes_state * restrict 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 * restrict 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); +} + +static void opcode_lda_abs(struct nes_state * restrict 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 * restrict 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((base & 0xff00) != (addr & 0xff00)) { + 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 * restrict 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 * restrict 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((base & 0xff00) != (addr & 0xff00)) { + 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); +} + +static void opcode_lda_absx(struct nes_state * restrict 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((base & 0xff00) != (addr & 0xff00)) { + 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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((base & 0xff00) != (addr & 0xff00)) { + 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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((base & 0xff00) != (addr & 0xff00)) { + 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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((base & 0xff00) != (addr & 0xff00)) { + 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 * restrict 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 * restrict 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((base & 0xff00) != (addr & 0xff00)) { + 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 * restrict 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((base & 0xff00) != (addr & 0xff00)) { + 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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((base & 0xff00) != (addr & 0xff00)) { + 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 * restrict 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 * restrict 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((base & 0xff00) != (addr & 0xff00)) { + 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 * restrict 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((base & 0xff00) != (addr & 0xff00)) { + 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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); +} + +static void opcode_sta_zp(struct nes_state * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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); +} + +static void opcode_sta_absx(struct nes_state * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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 * restrict 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) { + 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; +} + + + + + + + + + + + + diff --git a/cpu_opcodes_ud.c b/cpu_opcodes_ud.c new file mode 100644 index 0000000..a679cae --- /dev/null +++ b/cpu_opcodes_ud.c @@ -0,0 +1,1232 @@ + + + +// NOP + +static void opcode_nop_imm(struct nes_state * restrict state) { + struct cpu_state * restrict cpu = &state->cpu; + + memory_read(state, cpu->pc++); // T1: consume operand +} + +static void opcode_nop_zp(struct nes_state * restrict state) { + struct cpu_state * restrict cpu = &state->cpu; + + uint8_t addr = memory_read(state, cpu->pc++); // T1 + memory_read_dummy(state, addr); // T2 +} + +static void opcode_nop_abs(struct nes_state * restrict 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); + + memory_read_dummy(state, addr); // T3 +} + +static void opcode_nop_zpx(struct nes_state * restrict state) { + struct cpu_state * restrict cpu = &state->cpu; + + uint8_t base = memory_read(state, cpu->pc++); // T1: fetch operand + memory_read_dummy(state, base); // T2: internal timing quirk (not used, but real) + uint8_t addr = (base + cpu->x) & 0xff; + memory_read_dummy(state, addr); // T3: final bus read to correct address +} + +static void opcode_nop_absx(struct nes_state * restrict 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((base & 0xff00) != (addr & 0xff00)) { + memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); // T3 dummy read + } + + memory_read_dummy(state, addr); // T4 — final bus cycle (even if not used) +} + + +static void opcode_nop_implied(struct nes_state * restrict state) { + struct cpu_state * restrict cpu = &state->cpu; + + memory_read_dummy(state, cpu->pc); // T1 +} + + +// LAX + +static void opcode_lax_imm(struct nes_state * restrict state) { + struct cpu_state * restrict cpu = &state->cpu; + + uint8_t value = memory_read(state, cpu->pc++); + cpu->a = value; + cpu->x = value; + update_zn(cpu, value); +} + +static void opcode_lax_indx(struct nes_state * restrict 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); + cpu->a = value; + cpu->x = value; + update_zn(cpu, value); +} + +static void opcode_lax_zp(struct nes_state * restrict 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; + cpu->x = value; + update_zn(cpu, value); +} + +static void opcode_lax_zpy(struct nes_state * restrict 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->a = value; + cpu->x = value; + update_zn(cpu, value); +} + +static void opcode_lax_abs(struct nes_state * restrict 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; + cpu->x = value; + update_zn(cpu, value); +} + +static void opcode_lax_absy(struct nes_state * restrict 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((base & 0xff00) != (addr & 0xff00)) { + memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); + } + + uint8_t value = memory_read(state, addr); + cpu->a = value; + cpu->x = value; + update_zn(cpu, value); +} + +static void opcode_lax_indy(struct nes_state * restrict 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((base & 0xff00) != (addr & 0xff00)) { + memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); + } + + uint8_t value = memory_read(state, addr); + cpu->a = value; + cpu->x = value; + update_zn(cpu, value); +} + + +// SAX + +static void opcode_sax_indx(struct nes_state * restrict 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; + uint8_t lo = memory_read(state, ptr & 0xff); // T3 + uint8_t hi = memory_read(state, (ptr + 1) & 0xff); // T4 + uint16_t addr = lo | (hi << 8); + memory_write(state, addr, cpu->a & cpu->x); // T5 +} + + +static void opcode_sax_zp(struct nes_state * restrict state) { + struct cpu_state * restrict cpu = &state->cpu; + + uint8_t addr = memory_read(state, cpu->pc++); + memory_write(state, addr, cpu->a & cpu->x); +} + +static void opcode_sax_abs(struct nes_state * restrict 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 & cpu->x); +} + +static void opcode_sax_zpy(struct nes_state * restrict 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->a & cpu->x); +} + +static void opcode_sax_absy(struct nes_state * restrict 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 & cpu->x); +} + +static void opcode_sax_indy(struct nes_state * restrict 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)); // T4 (always) + memory_write(state, addr, cpu->a & cpu->x); // T5 +} + + + +// DCP + +static void opcode_dcp_indx(struct nes_state * restrict 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); + memory_write(state, addr, value); // dummy write + value--; + memory_write(state, addr, value); + + uint16_t tmp = cpu->a - value; + cpu->c = (cpu->a >= value); + update_zn(cpu, tmp & 0xff); +} + +static void opcode_dcp_zp(struct nes_state * restrict 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 + value--; + memory_write(state, addr, value); + + uint16_t tmp = cpu->a - value; + cpu->c = (cpu->a >= value); + update_zn(cpu, tmp & 0xff); +} + +static void opcode_dcp_abs(struct nes_state * restrict 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 + value--; + memory_write(state, addr, value); + + uint16_t tmp = cpu->a - value; + cpu->c = (cpu->a >= value); + update_zn(cpu, tmp & 0xff); +} + +static void opcode_dcp_indy(struct nes_state * restrict 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((base & 0xff00) != (addr & 0xff00)) { + memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); + } + + uint8_t value = memory_read(state, addr); + memory_write(state, addr, value); // dummy write + value--; + memory_write(state, addr, value); + + uint16_t tmp = cpu->a - value; + cpu->c = (cpu->a >= value); + update_zn(cpu, tmp & 0xff); +} + +static void opcode_dcp_zpx(struct nes_state * restrict 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 + value--; + memory_write(state, addr, value); + + uint16_t tmp = cpu->a - value; + cpu->c = (cpu->a >= value); + update_zn(cpu, tmp & 0xff); +} + +static void opcode_dcp_absy(struct nes_state * restrict 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)); + + uint8_t value = memory_read(state, addr); + memory_write(state, addr, value); // dummy write + value--; + memory_write(state, addr, value); + + uint16_t tmp = cpu->a - value; + cpu->c = (cpu->a >= value); + update_zn(cpu, tmp & 0xff); +} + +static void opcode_dcp_absx(struct nes_state * restrict 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 + value--; + memory_write(state, addr, value); + + uint16_t tmp = cpu->a - value; + cpu->c = (cpu->a >= value); + update_zn(cpu, tmp & 0xff); +} + + +// ISC + +static void opcode_isc_indx(struct nes_state * restrict 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); + memory_write(state, addr, value); // dummy write + value++; + memory_write(state, addr, value); + + sbc(cpu, value); +} + +static void opcode_isc_zp(struct nes_state * restrict 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 + value++; + memory_write(state, addr, value); + + sbc(cpu, value); +} + +static void opcode_isc_abs(struct nes_state * restrict 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 + value++; + memory_write(state, addr, value); + + sbc(cpu, value); +} + +static void opcode_isc_indy(struct nes_state * restrict 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((base & 0xff00) != (addr & 0xff00)) { + memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); + } + + uint8_t value = memory_read(state, addr); + memory_write(state, addr, value); // dummy write + value++; + memory_write(state, addr, value); + + sbc(cpu, value); +} + +static void opcode_isc_zpx(struct nes_state * restrict 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 + value++; + memory_write(state, addr, value); + + sbc(cpu, value); +} + +static void opcode_isc_absy(struct nes_state * restrict 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)); + + uint8_t value = memory_read(state, addr); + memory_write(state, addr, value); // dummy write + value++; + memory_write(state, addr, value); + + sbc(cpu, value); +} + +static void opcode_isc_absx(struct nes_state * restrict 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 + value++; + memory_write(state, addr, value); + + sbc(cpu, value); +} + + +// SLO + +static void opcode_slo_indx(struct nes_state * restrict 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); + memory_write(state, addr, value); // dummy write + + cpu->c = (value >> 7) & 1; + value <<= 1; + + memory_write(state, addr, value); + cpu->a |= value; + update_zn(cpu, cpu->a); +} + +static void opcode_slo_zp(struct nes_state * restrict 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 + + cpu->c = (value >> 7) & 1; + value <<= 1; + + memory_write(state, addr, value); + cpu->a |= value; + update_zn(cpu, cpu->a); +} + +static void opcode_slo_abs(struct nes_state * restrict 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 + + cpu->c = (value >> 7) & 1; + value <<= 1; + + memory_write(state, addr, value); + cpu->a |= value; + update_zn(cpu, cpu->a); +} + +static void opcode_slo_indy(struct nes_state * restrict 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((base & 0xff00) != (addr & 0xff00)) { + memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); + } + + uint8_t value = memory_read(state, addr); + memory_write(state, addr, value); // dummy write + + cpu->c = (value >> 7) & 1; + value <<= 1; + + memory_write(state, addr, value); + cpu->a |= value; + update_zn(cpu, cpu->a); +} + +static void opcode_slo_zpx(struct nes_state * restrict 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 + + cpu->c = (value >> 7) & 1; + value <<= 1; + + memory_write(state, addr, value); + cpu->a |= value; + update_zn(cpu, cpu->a); +} + +static void opcode_slo_absy(struct nes_state * restrict 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)); + + uint8_t value = memory_read(state, addr); + memory_write(state, addr, value); // dummy write + + cpu->c = (value >> 7) & 1; + value <<= 1; + + memory_write(state, addr, value); + cpu->a |= value; + update_zn(cpu, cpu->a); +} + +static void opcode_slo_absx(struct nes_state * restrict 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 + + cpu->c = (value >> 7) & 1; + value <<= 1; + + memory_write(state, addr, value); + cpu->a |= value; + update_zn(cpu, cpu->a); +} + + +// RLA + +static void opcode_rla_indx(struct nes_state * restrict 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); + 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); + cpu->a &= value; + update_zn(cpu, cpu->a); +} + +static void opcode_rla_zp(struct nes_state * restrict 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); + cpu->a &= value; + update_zn(cpu, cpu->a); +} + +static void opcode_rla_abs(struct nes_state * restrict 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); + cpu->a &= value; + update_zn(cpu, cpu->a); +} + +static void opcode_rla_indy(struct nes_state * restrict 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((base & 0xff00) != (addr & 0xff00)) { + 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); + cpu->a &= value; + update_zn(cpu, cpu->a); +} + +static void opcode_rla_zpx(struct nes_state * restrict 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); + cpu->a &= value; + update_zn(cpu, cpu->a); +} + +static void opcode_rla_absy(struct nes_state * restrict 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)); + + 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); + cpu->a &= value; + update_zn(cpu, cpu->a); +} + +static void opcode_rla_absx(struct nes_state * restrict 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); + cpu->a &= value; + update_zn(cpu, cpu->a); +} + + +// SRE + +static void opcode_sre_indx(struct nes_state * restrict 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); + memory_write(state, addr, value); // dummy write + + cpu->c = value & 1; + value >>= 1; + + memory_write(state, addr, value); + cpu->a ^= value; + update_zn(cpu, cpu->a); +} + +static void opcode_sre_zp(struct nes_state * restrict 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 + + cpu->c = value & 1; + value >>= 1; + + memory_write(state, addr, value); + cpu->a ^= value; + update_zn(cpu, cpu->a); +} + +static void opcode_sre_abs(struct nes_state * restrict 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 + + cpu->c = value & 1; + value >>= 1; + + memory_write(state, addr, value); + cpu->a ^= value; + update_zn(cpu, cpu->a); +} + +static void opcode_sre_indy(struct nes_state * restrict 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((base & 0xff00) != (addr & 0xff00)) { + memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); + } + + uint8_t value = memory_read(state, addr); + memory_write(state, addr, value); // dummy write + + cpu->c = value & 1; + value >>= 1; + + memory_write(state, addr, value); + cpu->a ^= value; + update_zn(cpu, cpu->a); +} + +static void opcode_sre_zpx(struct nes_state * restrict 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 + + cpu->c = value & 1; + value >>= 1; + + memory_write(state, addr, value); + cpu->a ^= value; + update_zn(cpu, cpu->a); +} + +static void opcode_sre_absy(struct nes_state * restrict 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)); + + uint8_t value = memory_read(state, addr); + memory_write(state, addr, value); // dummy write + + cpu->c = value & 1; + value >>= 1; + + memory_write(state, addr, value); + cpu->a ^= value; + update_zn(cpu, cpu->a); +} + +static void opcode_sre_absx(struct nes_state * restrict 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 + + cpu->c = value & 1; + value >>= 1; + + memory_write(state, addr, value); + cpu->a ^= value; + update_zn(cpu, cpu->a); +} + + +// opcode_rra + +static void opcode_rra_indx(struct nes_state * restrict 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); + memory_write(state, addr, value); // dummy write + + uint8_t new_c = value & 1; + value = (value >> 1) | (cpu->c << 7); + cpu->c = new_c; + + memory_write(state, addr, value); + adc(cpu, value); +} + +static void opcode_rra_zp(struct nes_state * restrict 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 new_c = value & 1; + value = (value >> 1) | (cpu->c << 7); + cpu->c = new_c; + + memory_write(state, addr, value); + adc(cpu, value); +} + +static void opcode_rra_abs(struct nes_state * restrict 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 new_c = value & 1; + value = (value >> 1) | (cpu->c << 7); + cpu->c = new_c; + + memory_write(state, addr, value); + adc(cpu, value); +} + +static void opcode_rra_indy(struct nes_state * restrict 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((base & 0xff00) != (addr & 0xff00)) { + memory_read_dummy(state, (base & 0xff00) | (addr & 0x00ff)); + } + + uint8_t value = memory_read(state, addr); + memory_write(state, addr, value); // dummy write + + uint8_t new_c = value & 1; + value = (value >> 1) | (cpu->c << 7); + cpu->c = new_c; + + memory_write(state, addr, value); + adc(cpu, value); +} + +static void opcode_rra_zpx(struct nes_state * restrict 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 new_c = value & 1; + value = (value >> 1) | (cpu->c << 7); + cpu->c = new_c; + + memory_write(state, addr, value); + adc(cpu, value); +} + +static void opcode_rra_absy(struct nes_state * restrict 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)); + + uint8_t value = memory_read(state, addr); + memory_write(state, addr, value); // dummy write + + uint8_t new_c = value & 1; + value = (value >> 1) | (cpu->c << 7); + cpu->c = new_c; + + memory_write(state, addr, value); + adc(cpu, value); +} + +static void opcode_rra_absx(struct nes_state * restrict 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 new_c = value & 1; + value = (value >> 1) | (cpu->c << 7); + cpu->c = new_c; + + memory_write(state, addr, value); + adc(cpu, value); +} + + +// ALR + +static void opcode_alr_imm(struct nes_state * restrict state) { + struct cpu_state * restrict cpu = &state->cpu; + uint8_t value = memory_read(state, cpu->pc++); + cpu->a &= value; + cpu->c = cpu->a & 1; + cpu->a >>= 1; + update_zn(cpu, cpu->a); +} + + +// ANC + +static void opcode_anc_imm(struct nes_state * restrict state) { + struct cpu_state * restrict cpu = &state->cpu; + uint8_t value = memory_read(state, cpu->pc++); + cpu->a &= value; + cpu->c = (cpu->a >> 7) & 1; + update_zn(cpu, cpu->a); +} + + +// ARR + +static void opcode_arr_imm(struct nes_state * restrict state) { + struct cpu_state * restrict cpu = &state->cpu; + + uint8_t value = memory_read(state, cpu->pc++); + cpu->a &= value; + + uint8_t in = cpu->c << 7; + cpu->c = (cpu->a >> 0) & 1; + cpu->a = (cpu->a >> 1) | in; + + update_zn(cpu, cpu->a); + cpu->v = ((cpu->a >> 6) ^ (cpu->a >> 5)) & 1; +} + + +// XAA + +static void opcode_xaa_imm(struct nes_state * restrict state) { + struct cpu_state * restrict cpu = &state->cpu; + + uint8_t value = memory_read(state, cpu->pc++); + cpu->a = cpu->a & cpu->x & value; + update_zn(cpu, cpu->a); +} + + +// AXS + +static void opcode_axs_imm(struct nes_state * restrict state) { + struct cpu_state * restrict cpu = &state->cpu; + + uint8_t value = memory_read(state, cpu->pc++); + cpu->a = cpu->x & value; + update_zn(cpu, cpu->a); +} + + + + +__attribute__((noinline)) +static void init_opcode_ud_lut(void) { + opcode_lut[0x80] = opcode_nop_imm; + opcode_lut[0x82] = opcode_nop_imm; + opcode_lut[0x89] = opcode_nop_imm; + opcode_lut[0xc2] = opcode_nop_imm; + opcode_lut[0xe2] = opcode_nop_imm; + + opcode_lut[0x04] = opcode_nop_zp; + opcode_lut[0x44] = opcode_nop_zp; + opcode_lut[0x64] = opcode_nop_zp; + + opcode_lut[0x0c] = opcode_nop_abs; + + opcode_lut[0x14] = opcode_nop_zpx; + opcode_lut[0x34] = opcode_nop_zpx; + opcode_lut[0x54] = opcode_nop_zpx; + opcode_lut[0x74] = opcode_nop_zpx; + opcode_lut[0xd4] = opcode_nop_zpx; + opcode_lut[0xf4] = opcode_nop_zpx; + + opcode_lut[0x1c] = opcode_nop_absx; + opcode_lut[0x3c] = opcode_nop_absx; + opcode_lut[0x5c] = opcode_nop_absx; + opcode_lut[0x7c] = opcode_nop_absx; + opcode_lut[0xdc] = opcode_nop_absx; + opcode_lut[0xfc] = opcode_nop_absx; + + opcode_lut[0x1a] = opcode_nop_implied; + opcode_lut[0x3a] = opcode_nop_implied; + opcode_lut[0x5a] = opcode_nop_implied; + opcode_lut[0x7a] = opcode_nop_implied; + opcode_lut[0xda] = opcode_nop_implied; + opcode_lut[0xfa] = opcode_nop_implied; + + opcode_lut[0xab] = opcode_lax_imm; + opcode_lut[0xa3] = opcode_lax_indx; + opcode_lut[0xaf] = opcode_lax_abs; + opcode_lut[0xbb] = opcode_lax_absy; + opcode_lut[0xbf] = opcode_lax_absy; + opcode_lut[0xa7] = opcode_lax_zp; + opcode_lut[0xb7] = opcode_lax_zpy; + opcode_lut[0xb3] = opcode_lax_indy; + + opcode_lut[0x83] = opcode_sax_indx; + opcode_lut[0x8f] = opcode_sax_abs; + opcode_lut[0x9f] = opcode_sax_absy; + opcode_lut[0x87] = opcode_sax_zp; + opcode_lut[0x97] = opcode_sax_zpy; + opcode_lut[0x93] = opcode_sax_indy; + + opcode_lut[0xc3] = opcode_dcp_indx; + opcode_lut[0xc7] = opcode_dcp_zp; + opcode_lut[0xcf] = opcode_dcp_abs; + opcode_lut[0xd3] = opcode_dcp_indy; + opcode_lut[0xd7] = opcode_dcp_zpx; + opcode_lut[0xdb] = opcode_dcp_absy; + opcode_lut[0xdf] = opcode_dcp_absx; + + opcode_lut[0xe3] = opcode_isc_indx; + opcode_lut[0xe7] = opcode_isc_zp; + opcode_lut[0xef] = opcode_isc_abs; + opcode_lut[0xf3] = opcode_isc_indy; + opcode_lut[0xf7] = opcode_isc_zpx; + opcode_lut[0xfb] = opcode_isc_absy; + opcode_lut[0xff] = opcode_isc_absx; + + opcode_lut[0x03] = opcode_slo_indx; + opcode_lut[0x07] = opcode_slo_zp; + opcode_lut[0x0f] = opcode_slo_abs; + opcode_lut[0x13] = opcode_slo_indy; + opcode_lut[0x17] = opcode_slo_zpx; + opcode_lut[0x1b] = opcode_slo_absy; + opcode_lut[0x1f] = opcode_slo_absx; + + opcode_lut[0x23] = opcode_rla_indx; + opcode_lut[0x27] = opcode_rla_zp; + opcode_lut[0x2f] = opcode_rla_abs; + opcode_lut[0x33] = opcode_rla_indy; + opcode_lut[0x37] = opcode_rla_zpx; + opcode_lut[0x3b] = opcode_rla_absy; + opcode_lut[0x3f] = opcode_rla_absx; + + opcode_lut[0x43] = opcode_sre_indx; + opcode_lut[0x47] = opcode_sre_zp; + opcode_lut[0x4f] = opcode_sre_abs; + opcode_lut[0x53] = opcode_sre_indy; + opcode_lut[0x57] = opcode_sre_zpx; + opcode_lut[0x5b] = opcode_sre_absy; + opcode_lut[0x5f] = opcode_sre_absx; + + opcode_lut[0x63] = opcode_rra_indx; + opcode_lut[0x67] = opcode_rra_zp; + opcode_lut[0x6f] = opcode_rra_abs; + opcode_lut[0x73] = opcode_rra_indy; + opcode_lut[0x77] = opcode_rra_zpx; + opcode_lut[0x7b] = opcode_rra_absy; + opcode_lut[0x7f] = opcode_rra_absx; + + opcode_lut[0x32] = opcode_anc_imm; + opcode_lut[0x4b] = opcode_alr_imm; + opcode_lut[0x8b] = opcode_xaa_imm; + opcode_lut[0xcb] = opcode_axs_imm; + opcode_lut[0x6b] = opcode_arr_imm; +} @@ -0,0 +1,90 @@ + + + +// iNES header fields +#define INES_HEADER_SIZE 16 +#define INES_PRG_SIZE_LSB 4 +#define INES_CHR_SIZE_LSB 5 +#define INES_FLAGS6 6 +#define INES_FLAGS7 7 +#define INES_PRG_CHR_MSB 9 +#define INES_CHR_EXP 8 +#define INES_PRG_EXP 9 + +// Size units +#define PRG_ROM_UNIT 16384 +#define CHR_ROM_UNIT 8192 +#define TRAINER_SIZE 512 + +// Flag masks +#define FLAG6_TRAINER 0x04 +#define FLAG6_FOUR_SCREEN 0x08 +#define FLAG6_VERTICAL_MIRROR 0x01 + +// Mirroring modes +#define MIRROR_HORIZONTAL 0 +#define MIRROR_VERTICAL 1 +#define MIRROR_FOUR_SCREEN 2 + + +static int load_ines2(struct nes_state *state, const char *path) { + FILE *f = fopen(path, "rb"); + if(!f) { + return -1; + } + + uint8_t header[INES_HEADER_SIZE]; + fread(header, 1, INES_HEADER_SIZE, f); + + uint8_t prg_lsb = header[INES_PRG_SIZE_LSB]; + uint8_t chr_lsb = header[INES_CHR_SIZE_LSB]; + uint8_t prg_msb = header[INES_PRG_CHR_MSB] & 0x0f; + uint8_t chr_msb = (header[INES_PRG_CHR_MSB] & 0xf0) >> 4; + + uint32_t prg_size = (prg_msb << 8 | prg_lsb) * PRG_ROM_UNIT; + uint32_t chr_size = (chr_msb << 8 | chr_lsb) * CHR_ROM_UNIT; + + // NES 2.0 exponential format (only if lower byte is 0x0f) + if(prg_lsb == 0x0f) { + prg_size = 1 << header[INES_PRG_EXP]; + } + if(chr_lsb == 0x0f) { + chr_size = 1 << header[INES_CHR_EXP]; + } + + // Extract mapper + uint8_t mapper_low = (header[INES_FLAGS6] >> 4); + uint8_t mapper_high = (header[INES_FLAGS7] & 0xf0); + uint8_t mapper_ext = (header[INES_PRG_CHR_MSB] & 0x0f) << 8; + state->ines.mapper = mapper_low | mapper_high | mapper_ext; + + // Extract mirroring + if(header[INES_FLAGS6] & FLAG6_FOUR_SCREEN) { + state->ines.mirroring = MIRROR_FOUR_SCREEN; + } else if(header[INES_FLAGS6] & FLAG6_VERTICAL_MIRROR) { + state->ines.mirroring = MIRROR_VERTICAL; + } else { + state->ines.mirroring = MIRROR_HORIZONTAL; + } + + state->ines.prg_size = prg_size; + state->ines.chr_size = chr_size; + + // Skip trainer if present + if(header[INES_FLAGS6] & FLAG6_TRAINER) { + fseek(f, TRAINER_SIZE, SEEK_CUR); + } + + // Read PRG + fread(state->rom, 1, prg_size, f); + + // Read CHR if present + if(chr_size > 0) { + fread(state->chrrom, 1, chr_size, f); + } + + fclose(f); + return 0; +} + + diff --git a/mapper.c b/mapper.c new file mode 100644 index 0000000..620be7d --- /dev/null +++ b/mapper.c @@ -0,0 +1,28 @@ + + +#include "mapper_nrom.c" +// #include "mapper_mmc1.c" +// #include "mapper_uxrom.c" + +static void mapper_setup(struct state *state) { + switch(state->ines.mapper) { + case 0: + state->mapper.read = mapper_nrom_read; + state->mapper.write = mapper_nrom_write; + state->mapper.tick = mapper_nrom_tick; + state->mapper.init = mapper_nrom_init; + state->mapper.init(state); + break; + + default: + // fallback: NROM-compatible + state->mapper.read = mapper_nrom_read; + state->mapper.write = mapper_nrom_write; + state->mapper.tick = mapper_nrom_tick; + state->mapper.init = mapper_nrom_init; + state->mapper.init(state); + break; + } +} + + diff --git a/mapper_nrom.c b/mapper_nrom.c new file mode 100644 index 0000000..425db31 --- /dev/null +++ b/mapper_nrom.c @@ -0,0 +1,27 @@ + + +static void mapper_nrom_init(struct state *state) { + // Nothing to initialize for NROM +} + +static uint8_t mapper_nrom_read(struct state *state, uint16_t addr) { + uint32_t prg_size = state->ines.prg_size; + + if(state->ines.prg_size == 16384) { + return state->rom[addr & 0x3fff]; + } else { + return state->rom[addr - 0x8000]; + } + return 0; +} + +static void mapper_nrom_write(struct state *state, uint16_t addr, uint8_t value) { + (void)state; + (void)addr; + (void)value; +} + +static void mapper_nrom_tick(struct state *state) { + (void)state; +} + diff --git a/memory.c b/memory.c new file mode 100644 index 0000000..cb29344 --- /dev/null +++ b/memory.c @@ -0,0 +1,95 @@ + + + +static uint8_t memory_read(struct nes_state *restrict state, uint32_t offset) { + state->cycle++; + for(uint32_t i = 0; i < 3; ++i) { + ppu_tick(state); + } + + if(offset < 0x2000) { + return state->ram[offset & 0x07ff]; + } else if(offset < 0x4000) { + switch(offset & 7) { + case 2: return ppu_read_2002(state); + case 4: return ppu_read_2004(state); + case 7: return ppu_read_2007(state); + default: return 0; + } + } else if(offset < 0x4020) { + // TODO: APU and I/O reads + return 0; + } else if(offset >= 0x6000) { + return state->mapper.read(state, offset); + } else { + return 0; + } +} + +static void memory_write(struct nes_state *restrict state, uint32_t offset, uint8_t value) { + state->cycle++; + for(uint32_t i = 0; i < 3; ++i) { + ppu_tick(state); + } + + if(offset < 0x2000) { + state->ram[offset & 0x07ff] = value; + } else if(offset < 0x4000) { + switch(offset & 7) { + case 0: state->ppu.ctrl = value; break; + case 1: state->ppu.mask = value; break; + case 3: ppu_write_2003(state, value); break; + case 4: ppu_write_2004(state, value); break; + case 5: ppu_write_2005(state, value); break; + case 6: ppu_write_2006(state, value); break; + case 7: ppu_write_2007(state, value); break; + default: break; + } + } else if(offset == 0x4014) { + ppu_dma_4014(state, value); + } else if(offset < 0x4020) { + // TODO: APU and I/O writes + } else if(offset >= 0x6000) { + state->mapper.write(state, offset, value); + } +} + +static uint8_t memory_read_dma(struct nes_state *restrict state, uint32_t offset) { + // Do not tick CPU/PPU/APU — caller handles timing + if(offset < 0x2000) { + return state->ram[offset & 0x07ff]; + } else if(offset < 0x4000) { + // PPU register reads are ignored during DMA + return 0; + } else if(offset < 0x4020) { + // APU and I/O — usually ignored or blocked during DMA + return 0; + } else if(offset >= 0x6000) { + return state->mapper.read(state, offset); + } else { + return 0; + } +} + +static uint8_t memory_read_dummy(struct nes_state *restrict state, uint32_t offset) { + state->cycle++; + for(uint32_t i = 0; i < 3; ++i) { + ppu_tick(state); + } + + if(offset < 0x2000) { + return 0; + } else if(offset < 0x4000) { + switch(offset & 7) { + case 2: return ppu_read_2002(state); + case 7: return ppu_read_2007(state); + default: return 0; + } + } else if(offset < 0x4020) { + return 0; + } else if(offset >= 0x6000) { + return state->mapper.read(state, offset); + } else { + return 0; + } +} @@ -0,0 +1,102 @@ + + +#include "base.c" + +#include "mknes.h" +// #include "apu.c" +#include "ppu.c" +#include "memory.c" +#include "cpu.c" +#include "ines2.c" + + + + +static void render_callback() { + +} + +static void audio_callback(int16_t *buffer, size_t frames) { + +} + +static void init_callback() { +} + +// int main(void) { +// struct nes_state state = {0}; + +// init_opcode_lut(); +// init_opcode_ud_lut(); +// state.cpu.sp = 0xfd; + +// // FILE *f = fopen("nestest.nes", "rb"); +// // fseek(f, 16, SEEK_SET); +// // fread(&state.memory[0xc000], 1, 0x4000, f); +// // fclose(f); +// // state.cpu.pc = 0xc000; + +// FILE *f = fopen("6502_functional_test.bin", "rb"); +// fread(state.memory, 1, 0x10000, f); +// fclose(f); +// state.cpu.pc = 0x0400; + +// size_t i; +// for(i = 0; i < 70000000; ++i) { +// cpu_tick(&state); +// if(state.cpu.pc == 0x3469) break; +// } +// // printf("%lld", i); +// return 0; +// } + + + + +// bool running = true; +// uint64_t next_update = mkfw_gettime(); +// int64_t frame_duration_ns = 16666667; // NTSC ~60.0988 Hz + +// audio_sync_init(next_update); // Initialize sync base time + +// while(running && !mkfw_should_close()) { +// mkfw_pump_messages(); +// if(key_pressed(MKS_KEY_ESCAPE)) { running = false; } + +// #ifdef PROFILER +// reset_profiling_data(); +// #endif + +// render_callback(); +// apply_phosphor_decay(); +// update_keyboard_state(); +// update_modifier_state(); +// update_mouse_state(); +// state.frame_number++; + +// #ifndef PERF_TEST +// render_frame(); +// #ifdef PROFILER +// debug_render(); +// #endif + +// audio_throttle_emulator(state.frame_number, &frame_duration_ns); + +// uint64_t now = mkfw_gettime(); +// int64_t remaining = next_update - now; + +// if(remaining > 0) { +// if(remaining > ONE_MILLISECOND_NS) { +// mkfw_sleep(remaining - ONE_MILLISECOND_NS); +// } +// while(mkfw_gettime() < next_update) { /**/ } +// } else { +// next_update = now; +// } + +// next_update += frame_duration_ns; + +// mkfw_swap_buffers(); +// #endif +// } + @@ -0,0 +1,146 @@ + + + +// PPUSTATUS ($2002) flags +#define PPU_STATUS_VBLANK 0x80 +#define PPU_STATUS_SPRITE0_HIT 0x40 +#define PPU_STATUS_OVERFLOW 0x20 + +// PPUMASK ($2001) flags +#define PPU_MASK_SHOW_BG 0x08 +#define PPU_MASK_SHOW_SPRITES 0x10 + +// PPUCTRL ($2000) flags +#define PPU_CTRL_NMI_ENABLE 0x80 +#define PPU_CTRL_MASTER_SLAVE 0x40 +#define PPU_CTRL_SPRITE_HEIGHT 0x20 +#define PPU_CTRL_BG_TABLE 0x10 +#define PPU_CTRL_SPRITE_TABLE 0x08 +#define PPU_CTRL_INCREMENT 0x04 +#define PPU_CTRL_NT_SELECT_Y 0x02 +#define PPU_CTRL_NT_SELECT_X 0x01 + + +struct nes_state; + +struct ppu_state { + uint8_t pixels[240*256]; + uint8_t sprite_pixels[256]; // Sprite pixel color indexes (BG priority resolved) + uint8_t sprite_zero_flags[256]; // 1 if pixel came from sprite #0 and is nonzero + // Sprite memory + uint8_t oam[256]; + uint8_t sec_oam[32]; + + uint8_t oam_addr; + uint8_t read_buffer; + + uint32_t scanline; // 0–261 + uint32_t dot; // 0–340 + + // Scroll state + uint32_t coarse_x_offs; + uint32_t coarse_y_offs; + uint32_t fine_x; + uint32_t fine_y; + uint32_t nt_base_x; + uint32_t nt_base_y; + + // Latch state for $2005/$2006 + uint8_t write_toggle; + uint32_t temp_coarse_x_offs; + uint32_t temp_coarse_y_offs; + uint32_t temp_fine_x; + uint32_t temp_fine_y; + uint32_t temp_nt_base_x; + uint32_t temp_nt_base_y; + + // Background shift registers + uint32_t bg_tile_lsb; + uint32_t bg_tile_msb; + uint32_t bg_attr_lsb; + uint32_t bg_attr_msb; + + // Tile fetch latches + uint8_t nt_byte; + uint8_t attr_byte; + uint8_t tile_lsb; + uint8_t tile_msb; + + // Control and status + uint8_t ctrl; // $2000 + uint8_t mask; // $2001 + uint8_t status; // $2002 + + // Flags + uint8_t frame_even; + uint8_t nmi_occurred; + uint8_t nmi_output; + +}; + + +struct cpu_state { + uint32_t pc; // Program Counter + uint8_t sp; // Stack Pointer + uint8_t a; // Accumulator + uint8_t x; // X Register + uint8_t y; // Y Register + uint8_t p; // Processor Status Flags (this can be expanded with separate flags if needed) + uint8_t n; // Negative Flag + uint8_t v; // Overflow Flag + // uint8_t b; // Break Flag (Set by BRK instruction) + uint8_t d; // Decimal Flag + uint8_t i; // Interrupt Disable Flag + uint8_t z; // Zero Flag + uint8_t c; // Carry Flag +// -- + uint8_t die; // KIL instruction found! +}; + + +struct ines_state { + uint16_t mapper; + uint8_t mirroring; // 0 = H, 1 = V, 2 = 4-screen + uint32_t prg_size; + uint32_t chr_size; +}; + +struct mapper { + void (*init)(struct nes_state *state); + uint8_t (*read)(struct nes_state *state, uint16_t addr); + void (*write)(struct nes_state *state, uint16_t addr, uint8_t value); + void (*tick)(struct nes_state *state); +}; + +union mapper_data { + // struct nrom_mapper nrom; + // struct mmc1_mapper mmc1; + // ... others +}; + +struct nes_state { + struct ines_state ines; + struct cpu_state cpu; + struct ppu_state ppu; + struct mapper mapper; + union mapper_data map; + size_t cycle; + uint8_t ram[2048]; + uint8_t rom[4 * 1024 * 1024]; + uint8_t chrrom[4 * 1024 * 1024]; + uint8_t ciram[2048]; + uint8_t palette[0x20]; + uint8_t irq_pending; + uint8_t nmi_pending; +}; + +static const uint32_t nes_palette_argb[64] = { + 0xff757575, 0xff8f1b27, 0xffab0000, 0xff9f0047, 0xff77008f, 0xff1300ab, 0xff0000a7, 0xff000b7f, + 0xff002f43, 0xff004700, 0xff005100, 0xff173f00, 0xff5f3f1b, 0xff000000, 0xff000000, 0xff000000, + 0xffbcbcbc, 0xffef7300, 0xffef3b23, 0xfff30083, 0xffbf00bf, 0xff5b00e7, 0xff002bdb, 0xff0f4fcb, + 0xff00738b, 0xff009700, 0xff00ab00, 0xff3b9300, 0xff8b8300, 0xff000000, 0xff000000, 0xff000000, + 0xffffffff, 0xffffbf3f, 0xffff975f, 0xfffd8ba7, 0xffff7bff, 0xffb777ff, 0xff6377ff, 0xff3b9bff, + 0xff3fbff3, 0xff13d383, 0xff4bdf4f, 0xff98f858, 0xffdbeb00, 0xff000000, 0xff000000, 0xff000000, + 0xffffffff, 0xffffe7ab, 0xffffd7c7, 0xffffcbd7, 0xffffc7ff, 0xffdbc7ff, 0xffb3bfff, 0xffabdbff, + 0xffa3e7ff, 0xff83f7c7, 0xffb3ffbf, 0xffcfffb3, 0xfff3ff9f, 0xff000000, 0xff000000, 0xff000000 +}; @@ -0,0 +1,490 @@ + +static uint8_t memory_read_dma(struct nes_state *state, uint32_t offset); + +static void ppu_reset_scroll_latch(struct nes_state *state) { + state->ppu.write_toggle = 0; +} + +static uint32_t compute_nt_address(struct nes_state *state) { + struct ppu_state *ppu = &state->ppu; + return 0x2000 | ppu->nt_base_y | ppu->nt_base_x | ppu->coarse_y_offs | ppu->coarse_x_offs; +} + + +static uint32_t compute_at_address(struct nes_state *state) { + struct ppu_state *ppu = &state->ppu; + uint32_t nt_offset = ppu->nt_base_y | ppu->nt_base_x; + uint32_t attr_x = (ppu->coarse_x_offs >> 2) & 0x07; + uint32_t attr_y = (ppu->coarse_y_offs >> 2) & 0x38; + return 0x23c0 | nt_offset | attr_y | attr_x; +} + +static void increment_x(struct nes_state *state) { + struct ppu_state *ppu = &state->ppu; + + if(ppu->coarse_x_offs == 31 << 0) { + ppu->coarse_x_offs = 0; + ppu->nt_base_x ^= 0x400; + } else { + ppu->coarse_x_offs += 1 << 0; + } +} + +static void increment_y(struct nes_state *state) { + struct ppu_state *ppu = &state->ppu; + + if(ppu->fine_y < 7) { + ppu->fine_y++; + return; + } + + ppu->fine_y = 0; + + uint32_t coarse_y = ppu->coarse_y_offs >> 5; + + if(coarse_y == 29) { + ppu->coarse_y_offs = 0; + ppu->nt_base_y ^= 0x800; + } else if(coarse_y == 31) { + ppu->coarse_y_offs = 0; + // Do not toggle nt_base_y — stays the same + } else { + ppu->coarse_y_offs += 1 << 5; + } +} + + +static void ppu_vram_write(struct nes_state *state, uint32_t addr, uint8_t value) { + // PPU address bus is 14 bits, wraps every 0x4000 + addr &= 0x3fff; + + if(addr < 0x2000) { + // Pattern table (CHR ROM is read-only, ignore write) + return; + } + + if(addr < 0x3f00) { + // Nametable RAM (CIRAM), mirrored every 0x1000 + uint32_t nt_index = addr & 0x0fff; + state->ciram[nt_index] = value; + return; + } + + if(addr < 0x4000) { + // Palette RAM (0x3F00–0x3FFF), mirrored every 32 bytes + addr = 0x3f00 + (addr & 0x1f); + + // Palette mirroring: $3F10/$3F14/$3F18/$3F1C mirror $3F00/$3F04/$3F08/$3F0C + if((addr & 0x13) == 0x10) { + addr &= ~0x10; + } + + state->palette[addr & 0x1f] = value; + } +} + +static uint8_t ppu_vram_read(struct nes_state *state, uint32_t addr) { + // PPU address bus is 14 bits, wraps every 0x4000 + addr &= 0x3fff; + + if(addr < 0x2000) { + // Pattern table (CHR ROM) + return state->chrrom[addr]; + } + + if(addr < 0x3f00) { + // Nametables (0x2000–0x2FFF mirrored every 0x1000) + uint32_t nt_index = addr & 0x0fff; + return state->ciram[nt_index]; + } + + if(addr < 0x4000) { + // Palette RAM (0x3F00–0x3FFF, mirrored every 32 bytes) + addr = 0x3f00 + (addr & 0x1f); + + // Palette mirroring: $3F10/$3F14/$3F18/$3F1C mirror $3F00/$3F04/$3F08/$3F0C + if((addr & 0x13) == 0x10) { + addr &= ~0x10; + } + + return state->palette[addr & 0x1f]; + } + + return 0; +} + +static uint8_t ppu_read_2002(struct nes_state *state) { + struct ppu_state *ppu = &state->ppu; + uint8_t result = ppu->status; + + ppu->status &= ~PPU_STATUS_VBLANK; + ppu->write_toggle = 0; + + return result; +} + +static void ppu_write_2003(struct nes_state *state, uint8_t value) { + state->ppu.oam_addr = value; +} + +static uint8_t ppu_read_2004(struct nes_state *state) { + return state->ppu.oam[state->ppu.oam_addr]; +} + +static void ppu_write_2004(struct nes_state *state, uint8_t value) { + state->ppu.oam[state->ppu.oam_addr] = value; + state->ppu.oam_addr++; +} + + +static void ppu_write_2005(struct nes_state *state, uint8_t value) +{ + struct ppu_state *ppu = &state->ppu; + + if(ppu->write_toggle == 0) { + ppu->temp_fine_x = value & 7; + ppu->temp_coarse_x_offs = (value >> 3) & 0x1f; + ppu->temp_coarse_x_offs <<= 0; + + ppu->write_toggle = 1; + } else { + ppu->temp_fine_y = value & 7; + uint8_t coarse_y = (value >> 3) & 0x1f; + ppu->temp_coarse_y_offs = coarse_y << 5; + + if(coarse_y >= 30) { + // vertical nametable select + ppu->temp_nt_base_y = 0x800; + } else { + ppu->temp_nt_base_y = 0x000; + } + + ppu->write_toggle = 0; + } +} + +static void ppu_write_2006(struct nes_state *state, uint8_t value) +{ + struct ppu_state *ppu = &state->ppu; + + if(ppu->write_toggle == 0) { + // High byte: bits 14–8 of VRAM address + ppu->temp_nt_base_y = (value & 0x0c) << 8; + ppu->temp_nt_base_x = (value & 0x01) << 10; + ppu->temp_coarse_y_offs = (value & 0x03) << 3; + ppu->temp_coarse_y_offs <<= 5; + + ppu->write_toggle = 1; + } else { + // Low byte: bits 7–0 of VRAM address + ppu->temp_coarse_x_offs = (value & 0x1f) << 0; + ppu->temp_fine_y = (value >> 2) & 7; + + // On second write, transfer temp -> current scroll state + ppu->coarse_x_offs = ppu->temp_coarse_x_offs; + ppu->coarse_y_offs = ppu->temp_coarse_y_offs; + ppu->fine_x = ppu->temp_fine_x; + ppu->fine_y = ppu->temp_fine_y; + ppu->nt_base_x = ppu->temp_nt_base_x; + ppu->nt_base_y = ppu->temp_nt_base_y; + + ppu->write_toggle = 0; + } +} + +static uint8_t ppu_read_2007(struct nes_state *state) { + struct ppu_state *ppu = &state->ppu; + + uint32_t addr = compute_nt_address(state); + if(ppu->fine_y) { + addr += ppu->fine_y; + } + + uint8_t result; + + if(addr < 0x3f00) { + // Return buffered value, load new one + result = ppu->read_buffer; + ppu->read_buffer = ppu_vram_read(state, addr); + } else { + // Palette reads bypass buffer + result = ppu_vram_read(state, addr); + ppu->read_buffer = ppu_vram_read(state, addr & 0x2fff); // mirror to pattern/nametable area + } + + // Auto-increment address + if(ppu->ctrl & PPU_CTRL_INCREMENT) { + increment_y(state); + } else { + increment_x(state); + } + + return result; +} + + +static void ppu_write_2007(struct nes_state *state, uint8_t value) { + struct ppu_state *ppu = &state->ppu; + uint32_t addr = compute_nt_address(state); + + if(ppu->fine_y) + addr += ppu->fine_y; + + ppu_vram_write(state, addr, value); + + if(ppu->ctrl & PPU_CTRL_INCREMENT) { + increment_y(state); + } else { + increment_x(state); + } +} + +static uint8_t ppu_pattern_read(struct nes_state *state, uint8_t tile_index, uint8_t fine_y, uint8_t plane) { + struct ppu_state *ppu = &state->ppu; + uint32_t base = (ppu->ctrl & PPU_CTRL_BG_TABLE) ? 0x1000 : 0x0000; + uint32_t addr = base + (tile_index << 4) + fine_y; + + if(plane) { + addr += 8; + } + + return ppu_vram_read(state, addr); +} + +static void ppu_eval_sprites(struct nes_state *state) { + struct ppu_state *ppu = &state->ppu; + + uint8_t scanline = ppu->scanline; + uint8_t height = (ppu->ctrl & PPU_CTRL_SPRITE_HEIGHT) ? 16 : 8; + + uint8_t count = 0; + + for(uint8_t i = 0; i < 64; i++) { + uint8_t *sprite = &ppu->oam[i * 4]; + uint8_t sprite_y = sprite[0]; + + if(scanline >= sprite_y && scanline < sprite_y + height) { + if(count < 8) { + uint8_t *dest = &ppu->sec_oam[count * 4]; + dest[0] = sprite[0]; + dest[1] = sprite[1]; + dest[2] = sprite[2]; + dest[3] = sprite[3]; + count++; + } else { + ppu->status |= PPU_STATUS_OVERFLOW; + break; + } + } + } +} + +static void ppu_clear_sec_oam(struct nes_state *state) { + struct ppu_state *ppu = &state->ppu; + for(int i=0; i<32; i++) { + ppu->sec_oam[i] = 0xff; + } +} + +static void ppu_render_sprites(struct nes_state *state) +{ + struct ppu_state *ppu = &state->ppu; + + // Clear sprite pixel buffer + for(int x=0;x<256;x++) { + ppu->sprite_pixels[x] = 0; + ppu->sprite_zero_flags[x] = 0; + } + + for(int i=0;i<8;i++) { + uint8_t *sprite = &ppu->sec_oam[i * 4]; + uint8_t y = sprite[0]; + uint8_t tile = sprite[1]; + uint8_t attr = sprite[2]; + uint8_t x = sprite[3]; + + uint8_t scanline = ppu->scanline; + uint8_t height = (ppu->ctrl & PPU_CTRL_SPRITE_HEIGHT) ? 16 : 8; + int sprite_y = scanline - y; + + if(attr & 0x80) { + // Vertical flip + sprite_y = height - 1 - sprite_y; + } + + uint32_t base = (ppu->ctrl & PPU_CTRL_SPRITE_TABLE) ? 0x1000 : 0x0000; + uint32_t addr = base + tile * 16 + sprite_y; + + uint8_t lo = ppu_vram_read(state, addr); + uint8_t hi = ppu_vram_read(state, addr + 8); + + for(int j=0;j<8;j++) { + int sx = x + (attr & 0x40 ? j : (7 - j)); // horizontal flip + if(sx >= 256) + continue; + + uint8_t bit = 1 << j; + uint8_t p0 = (lo & (1 << (7 - j))) ? 1 : 0; + uint8_t p1 = (hi & (1 << (7 - j))) ? 1 : 0; + uint8_t color = (attr & 0x03) << 2 | (p1 << 1) | p0; + + if((p0 | p1) == 0) + continue; + + if(ppu->sprite_pixels[sx] == 0) { + ppu->sprite_pixels[sx] = color | 0x10; + if(i == 0) + ppu->sprite_zero_flags[sx] = 1; + } + } + } +} + + +static void ppu_tick(struct nes_state *state) { + struct ppu_state *ppu = &state->ppu; + + if(ppu->scanline == 241 && ppu->dot == 1) { + ppu->status |= PPU_STATUS_VBLANK; + ppu->nmi_occurred = 1; + } + + if(ppu->scanline == 261 && ppu->dot == 1) { + ppu->status &= ~PPU_STATUS_VBLANK; + ppu->status &= ~PPU_STATUS_SPRITE0_HIT; + ppu->status &= ~PPU_STATUS_OVERFLOW; + ppu->nmi_occurred = 0; + } + + if((ppu->scanline < 240 || ppu->scanline == 261) && (ppu->dot >= 1 && ppu->dot <= 256)) { + uint32_t cycle = (ppu->dot - 1) % 8; + + switch(cycle) { + case 0: { + ppu->bg_tile_lsb = (ppu->bg_tile_lsb & 0xffff0000) | ppu->tile_lsb; + ppu->bg_tile_msb = (ppu->bg_tile_msb & 0xffff0000) | ppu->tile_msb; + ppu->bg_attr_lsb = (ppu->attr_byte & 1) ? 0xff : 0x00; + ppu->bg_attr_msb = (ppu->attr_byte & 2) ? 0xff : 0x00; + } break; + + case 1: { + ppu->nt_byte = ppu_vram_read(state, compute_nt_address(state)); + } break; + + case 3: { + ppu->attr_byte = ppu_vram_read(state, compute_at_address(state)); + } break; + + case 5: { + ppu->tile_lsb = ppu_pattern_read(state, ppu->nt_byte, ppu->fine_y, 0); + } break; + + case 7: { + ppu->tile_msb = ppu_pattern_read(state, ppu->nt_byte, ppu->fine_y, 1); + } break; + } + + ppu->bg_tile_lsb <<= 1; + ppu->bg_tile_msb <<= 1; + ppu->bg_attr_lsb <<= 1; + ppu->bg_attr_msb <<= 1; + + if(ppu->scanline < 240) { + uint8_t p0 = (ppu->bg_tile_lsb & 0x8000) ? 1 : 0; + uint8_t p1 = (ppu->bg_tile_msb & 0x8000) ? 1 : 0; + uint8_t a0 = (ppu->bg_attr_lsb & 0x80) ? 1 : 0; + uint8_t a1 = (ppu->bg_attr_msb & 0x80) ? 1 : 0; + uint8_t color = (a1 << 3) | (a0 << 2) | (p1 << 1) | p0; + + uint32_t index = ppu->scanline * 256 + (ppu->dot - 1); + + uint8_t bg_color = color; + uint8_t sprite_color = ppu->sprite_pixels[ppu->dot - 1]; + + uint8_t final_color = bg_color; + + if(sprite_color && ((bg_color & 3) == 0 || !(ppu->mask & 0x04))) { + // Sprite above BG, or BG is transparent + final_color = sprite_color; + } + + if(ppu->sprite_zero_flags[ppu->dot - 1] && + (bg_color & 3) && (sprite_color & 0x1f) && + (ppu->mask & 0x18)) { + ppu->status |= PPU_STATUS_SPRITE0_HIT; + } + + ppu->pixels[index] = final_color; + + } + } + + if(ppu->scanline < 240 && ppu->dot == 257) { + ppu_render_sprites(state); + } + + if(ppu->scanline < 240 || ppu->scanline == 261) { + if(ppu->dot == 256) { + increment_x(state); + increment_y(state); + } else if(ppu->dot == 328) { + increment_x(state); + } + } + + ppu->dot++; + + if(ppu->scanline == 261 && ppu->dot == 339 && ppu->frame_even) { + if(ppu->mask & (PPU_MASK_SHOW_BG | PPU_MASK_SHOW_SPRITES)) { + ppu->dot++; + } + } + + if(ppu->dot > 340) { + ppu->dot = 0; + ppu->scanline++; + + if(ppu->scanline > 261) { + ppu->scanline = 0; + ppu->frame_even ^= 1; + } + } + + // Clear secondary OAM at start of scanline + if(ppu->scanline < 240 || ppu->scanline == 261) { + if(ppu->dot == 1) { + ppu_clear_sec_oam(state); + } + } + + // Perform sprite evaluation + if(ppu->scanline < 240 && ppu->dot == 65) { + ppu_eval_sprites(state); + } +} + + +static void ppu_dma_4014(struct nes_state *state, uint8_t page) { + uint32_t base = page << 8; + + for(int i = 0; i < 256; i++) { + uint8_t value = memory_read_dma(state, base + i); + state->ppu.oam[i] = value; + + ppu_tick(state); + ppu_tick(state); + ppu_tick(state); + // apu_tick(state); + state->cycle++; + } + + // DMA takes 513 cycles on even CPU cycles, 514 on odd + int extra = (state->cycle & 1) ? 514 : 513; + for(int i = 0; i < extra; i++) { + ppu_tick(state); + ppu_tick(state); + ppu_tick(state); + // apu_tick(state); + state->cycle++; + } +} |
