NESpire/source/cpu.S

959 lines
22 KiB
ArmAsm

#include "nes.inc"
@ NES 2A03 (like 6502, just without decimal mode) CPU emulation core
@ Some caveats:
@ - Code is assumed to be contiguous in memory (apparently "The Magic of
@ Scheherazade" violates this by having code cross from bank 3 to bank 7,
@ but it's not worth it to kill performance just for one obscure game)
@ - Extra memory accesses (in read-modify-write instructions,
@ or indexing carry) are not implemented
@ - Most of the "undocumented" instructions are not implemented
.equ NES_FLAG_C, 0x01
.equ NES_FLAG_Z, 0x02
.equ NES_FLAG_I, 0x04
.equ NES_FLAG_D, 0x08
.equ NES_FLAG_B, 0x10
.equ NES_FLAG_V, 0x40
.equ NES_FLAG_N, 0x80
.equ ARM_FLAG_V, 0x10000000
.equ ARM_FLAG_C, 0x20000000
.equ ARM_FLAG_Z, 0x40000000
.equ ARM_FLAG_N, 0x80000000
@ Advance to next instruction. Its first byte must be pre-loaded into r1.
.macro NEXT cycles
subs cpu_cycles, cpu_cycles, #\cycles * CPU_CYCLE_LENGTH
TRACE
addpl pc, cpu_itable, r1, lsl #2
b cpu_leave
.endm
@ To avoid having to deal with the whole Cartesian product of operations and
@ addressing modes, we make the assembler generate them with macros. Each R_*,
@ W_*, or RMW_* macro is a template for instructions that do read, write, or
@ read-modify-write operations, respectively, in a particular addressing mode.
.macro R_Imm op
ldrb r0, [cpu_pc], #1
ldrb r1, [cpu_pc], #1
\op
NEXT 2
.endm
.macro RMW_Reg reg, op
ldrb r1, [cpu_pc], #1
\op \reg
NEXT 2
.endm
.macro RMW_A op; RMW_Reg cpu_a, \op; .endm
.macro RMW_X op; RMW_Reg cpu_x, \op; .endm
.macro RMW_Y op; RMW_Reg cpu_y, \op; .endm
.macro R_Zp op
ldrb r2, [cpu_pc], #1
ldrb r1, [cpu_pc], #1
ldrb r0, [r9, r2]
\op
NEXT 3
.endm
.macro W_Zp op
ldrb r2, [cpu_pc], #1
ldrb r1, [cpu_pc], #1
\op
strb r0, [r9, r2]
NEXT 3
.endm
.macro RMW_Zp op
ldrb r2, [cpu_pc], #1
ldrb r1, [cpu_pc], #1
ldrb r0, [r9, r2]
\op
strb r0, [r9, r2]
NEXT 5
.endm
.macro R_Zp_XY index, op
ldrb r2, [cpu_pc], #1
ldrb r1, [cpu_pc], #1
add r2, \index, r2, lsl #24
ldrb r0, [r9, r2, lsr #24]
\op
NEXT 4
.endm
.macro W_Zp_XY index, op
ldrb r2, [cpu_pc], #1
ldrb r1, [cpu_pc], #1
\op
add r2, \index, r2, lsl #24
strb r0, [r9, r2, lsr #24]
NEXT 4
.endm
.macro RMW_Zp_XY index, op
ldrb r2, [cpu_pc], #1
ldrb r1, [cpu_pc], #1
add r2, \index, r2, lsl #24
ldrb r0, [r9, r2, lsr #24]
\op
strb r0, [r9, r2, lsr #24]
NEXT 6
.endm
.macro R_Zp_X op; R_Zp_XY cpu_x, \op; .endm
.macro R_Zp_Y op; R_Zp_XY cpu_y, \op; .endm
.macro W_Zp_X op; W_Zp_XY cpu_x, \op; .endm
.macro W_Zp_Y op; W_Zp_XY cpu_y, \op; .endm
.macro RMW_Zp_X op; RMW_Zp_XY cpu_x, \op; .endm
.macro R_Abs op
ldrb r2, [cpu_pc], #1
ldrb r3, [cpu_pc], #1
ldrb r1, [cpu_pc], #1
bl mem_read_split
\op
NEXT 4
.endm
.macro W_Abs op
ldrb r2, [cpu_pc], #1
ldrb r3, [cpu_pc], #1
ldrb r1, [cpu_pc], #1
\op
bl mem_write_split
NEXT 4
.endm
.macro RMW_Abs op
ldrb r2, [cpu_pc], #1
ldrb r3, [cpu_pc], #1
ldrb r1, [cpu_pc], #1
bl mem_read_split
\op
bl mem_write
NEXT 6
.endm
.macro R_Abs_XY index, op
ldrb r2, [cpu_pc], #1
ldrb r3, [cpu_pc], #1
ldrb r1, [cpu_pc], #1
add r2, r2, \index, lsr #24
tst r2, #0x100
subne cpu_cycles, cpu_cycles, #CPU_CYCLE_LENGTH
bl mem_read_split
\op
NEXT 4
.endm
.macro W_Abs_XY index, op
ldrb r2, [cpu_pc], #1
ldrb r3, [cpu_pc], #1
ldrb r1, [cpu_pc], #1
add r2, r2, \index, lsr #24
\op
bl mem_write_split
NEXT 5
.endm
.macro RMW_Abs_XY index, op
ldrb r2, [cpu_pc], #1
ldrb r3, [cpu_pc], #1
ldrb r1, [cpu_pc], #1
add r2, r2, \index, lsr #24
bl mem_read_split
\op
bl mem_write
NEXT 7
.endm
.macro R_Abs_X op; R_Abs_XY cpu_x, \op; .endm
.macro R_Abs_Y op; R_Abs_XY cpu_y, \op; .endm
.macro W_Abs_X op; W_Abs_XY cpu_x, \op; .endm
.macro W_Abs_Y op; W_Abs_XY cpu_y, \op; .endm
.macro RMW_Abs_X op; RMW_Abs_XY cpu_x, \op; .endm
.macro RMW_Abs_Y op; RMW_Abs_XY cpu_y, \op; .endm
.macro R_Ind_X op
ldrb r3, [cpu_pc], #1
ldrb r1, [cpu_pc], #1
add r3, cpu_x, r3, lsl #24
ldrb r2, [r9, r3, lsr #24]
add r3, r3, #0x01000000
ldrb r3, [r9, r3, lsr #24]
bl mem_read_split
\op
NEXT 6
.endm
.macro W_Ind_X op
ldrb r3, [cpu_pc], #1
ldrb r1, [cpu_pc], #1
\op
add r3, cpu_x, r3, lsl #24
ldrb r2, [r9, r3, lsr #24]
add r3, r3, #0x01000000
ldrb r3, [r9, r3, lsr #24]
bl mem_write_split
NEXT 6
.endm
.macro RMW_Ind_X op
ldrb r3, [cpu_pc], #1
ldrb r1, [cpu_pc], #1
add r3, cpu_x, r3, lsl #24
ldrb r2, [r9, r3, lsr #24]
add r3, r3, #0x01000000
ldrb r3, [r9, r3, lsr #24]
bl mem_read_split
\op
bl mem_write
NEXT 8
.endm
.macro R_Ind_Y op
ldrb r3, [cpu_pc], #1
ldrb r1, [cpu_pc], #1
ldrb r2, [r9, r3]
add r3, r3, #1
and r3, r3, #0xFF
ldrb r3, [r9, r3]
add r2, r2, cpu_y, lsr #24
tst r2, #0x100
subne cpu_cycles, cpu_cycles, #CPU_CYCLE_LENGTH
bl mem_read_split
\op
NEXT 5
.endm
.macro W_Ind_Y op
ldrb r3, [cpu_pc], #1
ldrb r1, [cpu_pc], #1
\op
ldrb r2, [r9, r3]
add r3, r3, #1
and r3, r3, #0xFF
ldrb r3, [r9, r3]
add r2, r2, cpu_y, lsr #24
bl mem_write_split
NEXT 6
.endm
.macro RMW_Ind_Y op
ldrb r3, [cpu_pc], #1
ldrb r1, [cpu_pc], #1
ldrb r2, [r9, r3]
add r3, r3, #1
and r3, r3, #0xFF
ldrb r3, [r9, r3]
add r2, r2, cpu_y, lsr #24
bl mem_read_split
\op
bl mem_write
NEXT 8
.endm
@ Read operations
.macro OP_LDA
bic cpu_flags, cpu_flags, #ARM_FLAG_N | ARM_FLAG_Z
movs cpu_a, r0, lsl #24
orrmi cpu_flags, cpu_flags, #ARM_FLAG_N
orreq cpu_flags, cpu_flags, #ARM_FLAG_Z
.endm
.macro OP_LDX
bic cpu_flags, cpu_flags, #ARM_FLAG_N | ARM_FLAG_Z
movs cpu_x, r0, lsl #24
orrmi cpu_flags, cpu_flags, #ARM_FLAG_N
orreq cpu_flags, cpu_flags, #ARM_FLAG_Z
.endm
.macro OP_LDY
bic cpu_flags, cpu_flags, #ARM_FLAG_N | ARM_FLAG_Z
movs cpu_y, r0, lsl #24
orrmi cpu_flags, cpu_flags, #ARM_FLAG_N
orreq cpu_flags, cpu_flags, #ARM_FLAG_Z
.endm
.macro OP_ORA
bic cpu_flags, cpu_flags, #ARM_FLAG_N | ARM_FLAG_Z
orrs cpu_a, cpu_a, r0, lsl #24
orrmi cpu_flags, cpu_flags, #ARM_FLAG_N
orreq cpu_flags, cpu_flags, #ARM_FLAG_Z
.endm
.macro OP_AND
bic cpu_flags, cpu_flags, #ARM_FLAG_N | ARM_FLAG_Z
ands cpu_a, cpu_a, r0, lsl #24
orrmi cpu_flags, cpu_flags, #ARM_FLAG_N
orreq cpu_flags, cpu_flags, #ARM_FLAG_Z
.endm
.macro OP_EOR
bic cpu_flags, cpu_flags, #ARM_FLAG_N | ARM_FLAG_Z
eors cpu_a, cpu_a, r0, lsl #24
orrmi cpu_flags, cpu_flags, #ARM_FLAG_N
orreq cpu_flags, cpu_flags, #ARM_FLAG_Z
.endm
.macro OP_CMP
@ ARM sets V, 6502 does not, so must deal with flags manually
bic cpu_flags, cpu_flags, #ARM_FLAG_N | ARM_FLAG_Z | ARM_FLAG_C
cmp cpu_a, r0, lsl #24
orrmi cpu_flags, cpu_flags, #ARM_FLAG_N
orreq cpu_flags, cpu_flags, #ARM_FLAG_Z
orrcs cpu_flags, cpu_flags, #ARM_FLAG_C
.endm
.macro OP_CPX
bic cpu_flags, cpu_flags, #ARM_FLAG_N | ARM_FLAG_Z | ARM_FLAG_C
cmp cpu_x, r0, lsl #24
orrmi cpu_flags, cpu_flags, #ARM_FLAG_N
orreq cpu_flags, cpu_flags, #ARM_FLAG_Z
orrcs cpu_flags, cpu_flags, #ARM_FLAG_C
.endm
.macro OP_CPY
bic cpu_flags, cpu_flags, #ARM_FLAG_N | ARM_FLAG_Z | ARM_FLAG_C
cmp cpu_y, r0, lsl #24
orrmi cpu_flags, cpu_flags, #ARM_FLAG_N
orreq cpu_flags, cpu_flags, #ARM_FLAG_Z
orrcs cpu_flags, cpu_flags, #ARM_FLAG_C
.endm
.macro OP_ADC
msr cpsr_f, cpu_flags
subcs r0, r0, #0x100
adcs cpu_a, cpu_a, r0, ror #8
mrs cpu_flags, cpsr
.endm
.macro OP_SBC
msr cpsr_f, cpu_flags
subcc r0, r0, #0x100
sbcs cpu_a, cpu_a, r0, ror #8
mrs cpu_flags, cpsr
.endm
.macro OP_BIT
bic cpu_flags, #ARM_FLAG_N | ARM_FLAG_Z | ARM_FLAG_V
tst cpu_a, r0, lsl #24
orreq cpu_flags, cpu_flags, #ARM_FLAG_Z
movs r0, r0, lsl #25
orrcs cpu_flags, cpu_flags, #ARM_FLAG_N
orrmi cpu_flags, cpu_flags, #ARM_FLAG_V
.endm
@ Write operations
.macro OP_STA
mov r0, cpu_a, lsr #24
.endm
.macro OP_STX
mov r0, cpu_x, lsr #24
.endm
.macro OP_STY
mov r0, cpu_y, lsr #24
.endm
@ Read-modify-write operations
.macro OP_ASL
msr cpsr_f, cpu_flags
movs r0, r0, lsl #25
mrs cpu_flags, cpsr
mov r0, r0, lsr #24
.endm
.macro OP_ASLR reg
msr cpsr_f, cpu_flags
movs \reg, \reg, lsl #1
mrs cpu_flags, cpsr
.endm
.macro OP_ROL
mov r0, r0, lsl #24
tst cpu_flags, #ARM_FLAG_C
addne r0, r0, #0x800000
msr cpsr_f, cpu_flags
movs r0, r0, lsl #1
mrs cpu_flags, cpsr
mov r0, r0, lsr #24
.endm
.macro OP_ROLR reg
tst cpu_flags, #ARM_FLAG_C
addne \reg, \reg, #0x800000
msr cpsr_f, cpu_flags
movs \reg, \reg, lsl #1
mrs cpu_flags, cpsr
.endm
.macro OP_LSR
msr cpsr_f, cpu_flags
movs r0, r0, lsr #1
mrs cpu_flags, cpsr
.endm
.macro OP_LSRR reg
msr cpsr_f, cpu_flags
movs \reg, \reg, lsr #25
mov \reg, \reg, lsl #24
mrs cpu_flags, cpsr
.endm
.macro OP_ROR
msr cpsr_f, cpu_flags
movs r0, r0, rrx
mrs cpu_flags, cpsr
orr r0, r0, lsr #24
.endm
.macro OP_RORR reg
mov \reg, \reg, lsr #24
msr cpsr_f, cpu_flags
movs \reg, \reg, rrx
mrs cpu_flags, cpsr
orr \reg, \reg, lsl #24
and \reg, \reg, #0xFF000000
.endm
.macro OP_DEC
bic cpu_flags, cpu_flags, #ARM_FLAG_N | ARM_FLAG_Z
sub r0, r0, #1
movs r3, r0, lsl #24
orrmi cpu_flags, cpu_flags, #ARM_FLAG_N
orreq cpu_flags, cpu_flags, #ARM_FLAG_Z
.endm
.macro OP_DECR reg
bic cpu_flags, cpu_flags, #ARM_FLAG_N | ARM_FLAG_Z
subs \reg, \reg, #0x01000000
orrmi cpu_flags, cpu_flags, #ARM_FLAG_N
orreq cpu_flags, cpu_flags, #ARM_FLAG_Z
.endm
.macro OP_INC
bic cpu_flags, cpu_flags, #ARM_FLAG_N | ARM_FLAG_Z
add r0, r0, #1
movs r3, r0, lsl #24
orrmi cpu_flags, cpu_flags, #ARM_FLAG_N
orreq cpu_flags, cpu_flags, #ARM_FLAG_Z
.endm
.macro OP_INCR reg
bic cpu_flags, cpu_flags, #ARM_FLAG_N | ARM_FLAG_Z
adds \reg, \reg, #0x01000000
orrmi cpu_flags, cpu_flags, #ARM_FLAG_N
orreq cpu_flags, cpu_flags, #ARM_FLAG_Z
.endm
#define R(op, arg) R_##arg OP_##op
#define W(op, arg) W_##arg OP_##op
#define RMW(op, arg) RMW_##arg OP_##op
@ Other stuff...
.macro SPUSH reg
strb \reg, [r9, cpu_sp]
sub cpu_sp, cpu_sp, #1
orr cpu_sp, cpu_sp, #0x100
.endm
.macro SPULL reg
add cpu_sp, cpu_sp, #1
bic cpu_sp, cpu_sp, #0x200
orr cpu_sp, cpu_sp, #0x100
ldrb \reg, [r9, cpu_sp]
.endm
.macro INTERRUPT num, flags
.if num == 0xFFFC
subs cpu_sp, cpu_sp, #3
orr cpu_sp, cpu_sp, #0x100
.else
bl push_pc
mov r2, #\flags
bl push_flags
.endif
ldr r0, [r9, #s_flags_di]
ldr r2, [r9, #s_mem_map + 28]
orr r0, r0, #NES_FLAG_I
str r0, [r9, #s_flags_di]
add r2, r2, #0x10000
ldrh r2, [r2, #\num - 0x10000]
bl mem_jump
NEXT 7
.endm
.macro BRANCH flag, value
tst cpu_flags, #ARM_FLAG_\flag
ldrsb r2, [cpu_pc], #1
.if \value
bne take_branch
.else
beq take_branch
.endif
ldrb r1, [cpu_pc], #1
NEXT 2
.endm
take_branch:
@ Get PC
ldr r0, [r9, #s_pc_base]
sub cpu_pc, cpu_pc, r0
@ Add the branch offset already stored in r2
add r2, cpu_pc, r2
bic r2, r2, #0xFF000000
bic r2, r2, #0x00FF0000
@ Extra cycle if page changed
eor r0, cpu_pc, r2
tst r0, #0xFF00
subne cpu_cycles, cpu_cycles, #CPU_CYCLE_LENGTH
@ Set new PC
bl mem_jump
NEXT 3
.macro CHANGE_FLAG op, flag
.if NES_FLAG_\flag & 0xC3
ldrb r1, [cpu_pc], #1
\op cpu_flags, cpu_flags, #ARM_FLAG_\flag
.else
ldr r0, [r9, #s_flags_di]
ldrb r1, [cpu_pc], #1
\op r0, r0, #NES_FLAG_\flag
str r0, [r9, #s_flags_di]
.endif
NEXT 2
.endm
push_flags:
ldr r0, [r9, #s_flags_di]
msr cpsr_f, cpu_flags
orrmi r0, r0, #NES_FLAG_N
orrvs r0, r0, #NES_FLAG_V
orreq r0, r0, #NES_FLAG_Z
orrcs r0, r0, #NES_FLAG_C
orr r0, r0, r2
SPUSH r0
bx lr
pull_flags:
SPULL r0
and r2, r0, #0x0C
str r2, [r9, #s_flags_di]
bic cpu_flags, cpu_flags, #ARM_FLAG_N|ARM_FLAG_Z|ARM_FLAG_C|ARM_FLAG_V
and r2, r0, #NES_FLAG_N
orr cpu_flags, cpu_flags, r2, lsl #24
and r2, r0, #NES_FLAG_V
orr cpu_flags, cpu_flags, r2, lsl #22
and r2, r0, #NES_FLAG_Z | NES_FLAG_C
orr cpu_flags, cpu_flags, r2, lsl #29
bx lr
push_pc:
ldr r0, [r9, #s_pc_base]
sub r0, cpu_pc, r0
mov r1, r0, lsr #8
SPUSH r1
SPUSH r0
bx lr
.global reset
reset:
mov r0, #0
str r0, [r9, #s_ppu_scanline]
mov cpu_cycles, #-1
strb cpu_cycles, [r9, #s_nmi_reset] @ 0xFF = reset
main_loop:
add cpu_cycles, cpu_cycles, #0x100
add cpu_cycles, cpu_cycles, #0x055
adr cpu_itable, insn_table
ldr r2, [r9, #s_interrupts]
ldr r0, [r9, #s_flags_di]
movs r2, r2
bmi nmi_or_reset
beq cpu_enter
tst r0, #NES_FLAG_I
beq irq
cpu_enter:
ldrb r1, [cpu_pc], #1
TRACE
add pc, cpu_itable, r1, lsl #2
cpu_leave:
sub cpu_pc, cpu_pc, #1
bl ppu_next_scanline
@ TODO: this should be for MMC3 only
ldr r0, [r9, #s_ppu_control]
tst r0, #0x1800
beq 1f
ldr r0, [r9, #s_ppu_scanline]
cmp r0, #241
blcc mmc3_scanline
1:
b main_loop
nmi_or_reset:
tst r2, #0x7F000000
bic r2, r2, #0xFF000000
str r2, [r9, #s_interrupts]
bne intr_reset
INTERRUPT 0xFFFA, 0x20
intr_reset:
INTERRUPT 0xFFFC, 0x00
irq:
INTERRUPT 0xFFFE, 0x20
insn_table:
b insn_00;b insn_01;b insn_02;b insn_03;b insn_04;b insn_05;b insn_06;b insn_07
b insn_08;b insn_09;b insn_0a;b insn_0b;b insn_0c;b insn_0d;b insn_0e;b insn_0f
b insn_10;b insn_11;b insn_12;b insn_13;b insn_14;b insn_15;b insn_16;b insn_17
b insn_18;b insn_19;b insn_1a;b insn_1b;b insn_1c;b insn_1d;b insn_1e;b insn_1f
b insn_20;b insn_21;b insn_22;b insn_23;b insn_24;b insn_25;b insn_26;b insn_27
b insn_28;b insn_29;b insn_2a;b insn_2b;b insn_2c;b insn_2d;b insn_2e;b insn_2f
b insn_30;b insn_31;b insn_32;b insn_33;b insn_34;b insn_35;b insn_36;b insn_37
b insn_38;b insn_39;b insn_3a;b insn_3b;b insn_3c;b insn_3d;b insn_3e;b insn_3f
b insn_40;b insn_41;b insn_42;b insn_43;b insn_44;b insn_45;b insn_46;b insn_47
b insn_48;b insn_49;b insn_4a;b insn_4b;b insn_4c;b insn_4d;b insn_4e;b insn_4f
b insn_50;b insn_51;b insn_52;b insn_53;b insn_54;b insn_55;b insn_56;b insn_57
b insn_58;b insn_59;b insn_5a;b insn_5b;b insn_5c;b insn_5d;b insn_5e;b insn_5f
b insn_60;b insn_61;b insn_62;b insn_63;b insn_64;b insn_65;b insn_66;b insn_67
b insn_68;b insn_69;b insn_6a;b insn_6b;b insn_6c;b insn_6d;b insn_6e;b insn_6f
b insn_70;b insn_71;b insn_72;b insn_73;b insn_74;b insn_75;b insn_76;b insn_77
b insn_78;b insn_79;b insn_7a;b insn_7b;b insn_7c;b insn_7d;b insn_7e;b insn_7f
b insn_80;b insn_81;b insn_82;b insn_83;b insn_84;b insn_85;b insn_86;b insn_87
b insn_88;b insn_89;b insn_8a;b insn_8b;b insn_8c;b insn_8d;b insn_8e;b insn_8f
b insn_90;b insn_91;b insn_92;b insn_93;b insn_94;b insn_95;b insn_96;b insn_97
b insn_98;b insn_99;b insn_9a;b insn_9b;b insn_9c;b insn_9d;b insn_9e;b insn_9f
b insn_a0;b insn_a1;b insn_a2;b insn_a3;b insn_a4;b insn_a5;b insn_a6;b insn_a7
b insn_a8;b insn_a9;b insn_aa;b insn_ab;b insn_ac;b insn_ad;b insn_ae;b insn_af
b insn_b0;b insn_b1;b insn_b2;b insn_b3;b insn_b4;b insn_b5;b insn_b6;b insn_b7
b insn_b8;b insn_b9;b insn_ba;b insn_bb;b insn_bc;b insn_bd;b insn_be;b insn_bf
b insn_c0;b insn_c1;b insn_c2;b insn_c3;b insn_c4;b insn_c5;b insn_c6;b insn_c7
b insn_c8;b insn_c9;b insn_ca;b insn_cb;b insn_cc;b insn_cd;b insn_ce;b insn_cf
b insn_d0;b insn_d1;b insn_d2;b insn_d3;b insn_d4;b insn_d5;b insn_d6;b insn_d7
b insn_d8;b insn_d9;b insn_da;b insn_db;b insn_dc;b insn_dd;b insn_de;b insn_df
b insn_e0;b insn_e1;b insn_e2;b insn_e3;b insn_e4;b insn_e5;b insn_e6;b insn_e7
b insn_e8;b insn_e9;b insn_ea;b insn_eb;b insn_ec;b insn_ed;b insn_ee;b insn_ef
b insn_f0;b insn_f1;b insn_f2;b insn_f3;b insn_f4;b insn_f5;b insn_f6;b insn_f7
b insn_f8;b insn_f9;b insn_fa;b insn_fb;b insn_fc;b insn_fd;b insn_fe;b insn_ff
insn_00: @ BRK
add cpu_pc, cpu_pc, #1
INTERRUPT 0xFFFE, 0x30
insn_01: R(ORA, Ind_X)
insn_05: R(ORA, Zp)
insn_06: RMW(ASL, Zp)
insn_08: @ PHP
ldrb r1, [cpu_pc], #1
mov r2, #0x30
bl push_flags
NEXT 3
insn_09: R(ORA, Imm)
insn_0a: RMW(ASLR, A)
insn_0d: R(ORA, Abs)
insn_0e: RMW(ASL, Abs)
insn_10: BRANCH N, 0 @ BPL
insn_11: R(ORA, Ind_Y)
insn_15: R(ORA, Zp_X)
insn_16: RMW(ASL, Zp_X)
insn_18: CHANGE_FLAG bic, C
insn_19: R(ORA, Abs_Y)
insn_1d: R(ORA, Abs_X)
insn_1e: RMW(ASL, Abs_X)
insn_20: @ JSR Absolute
ldrb r2, [cpu_pc], #1
ldrb r3, [cpu_pc]
bl push_pc
bl mem_jump_split
NEXT 6
insn_21: R(AND, Ind_X)
insn_24: R(BIT, Zp)
insn_25: R(AND, Zp)
insn_26: RMW(ROL, Zp)
insn_28: @ PLP
ldrb r1, [cpu_pc], #1
bl pull_flags
NEXT 4
insn_29: R(AND, Imm)
insn_2a: RMW(ROLR, A)
insn_2c: R(BIT, Abs)
insn_2d: R(AND, Abs)
insn_2e: RMW(ROL, Abs)
insn_30: BRANCH N, 1
insn_31: R(AND, Ind_Y)
insn_35: R(AND, Zp_X)
insn_36: RMW(ROL, Zp_X)
insn_38: CHANGE_FLAG orr, C
insn_39: R(AND, Abs_Y)
insn_3d: R(AND, Abs_X)
insn_3e: RMW(ROL, Abs_X)
insn_40: @ RTI
bl pull_flags
SPULL r2
SPULL r3
bl mem_jump_split
NEXT 6
insn_41: R(EOR, Ind_X)
insn_45: R(EOR, Zp)
insn_46: RMW(LSR, Zp)
insn_48: @ PHA
mov r0, cpu_a, lsr #24
ldrb r1, [cpu_pc], #1
SPUSH r0
NEXT 3
insn_49: R(EOR, Imm)
insn_4a: RMW(LSRR, A)
insn_4c: @ JMP Absolute
ldrb r2, [cpu_pc], #1
ldrb r3, [cpu_pc], #1
bl mem_jump_split
NEXT 3
insn_4d: R(EOR, Abs)
insn_4e: RMW(LSR, Abs)
insn_50: BRANCH V, 0
insn_51: R(EOR, Ind_Y)
insn_55: R(EOR, Zp_X)
insn_56: RMW(LSR, Zp_X)
insn_58: CHANGE_FLAG bic, I
insn_59: R(EOR, Abs_Y)
insn_5d: R(EOR, Abs_X)
insn_5e: RMW(LSR, Abs_X)
insn_60: @ RTS
SPULL r2
SPULL r3
add r2, r2, #1
bl mem_jump_split
NEXT 6
insn_61: R(ADC, Ind_X)
insn_65: R(ADC, Zp)
insn_66: RMW(ROR, Zp)
insn_68: @ PLA
SPULL r0
ldrb r1, [cpu_pc], #1
msr cpsr_f, cpu_flags
mov r0, r0, lsl #24
movs cpu_a, r0
mrs cpu_flags, cpsr
NEXT 4
insn_69: R(ADC, Imm)
insn_6a: RMW(RORR, A)
insn_6c: @ JMP Indirect
ldrb r2, [cpu_pc], #1
ldrb r3, [cpu_pc], #1
bl mem_read_split
mov r1, r0
add r2, r2, #1
tst r2, #0xFF
subeq r2, r2, #0x100 @ Correct for "bug"
bl mem_read
add r2, r1, r0, lsl #8
bl mem_jump
NEXT 5
insn_6d: R(ADC, Abs)
insn_6e: RMW(ROR, Abs)
insn_70: BRANCH V, 1
insn_71: R(ADC, Ind_Y)
insn_75: R(ADC, Zp_X)
insn_76: RMW(ROR, Zp_X)
insn_78: CHANGE_FLAG orr, I
insn_79: R(ADC, Abs_Y)
insn_7d: R(ADC, Abs_X)
insn_7e: RMW(ROR, Abs_X)
insn_81: W(STA, Ind_X)
insn_84: W(STY, Zp)
insn_85: W(STA, Zp)
insn_86: W(STX, Zp)
insn_88: RMW(DECR, Y)
insn_8a: @ TXA
ldrb r1, [cpu_pc], #1
msr cpsr_f, cpu_flags
movs cpu_a, cpu_x
mrs cpu_flags, cpsr
NEXT 2
insn_8c: W(STY, Abs)
insn_8d: W(STA, Abs)
insn_8e: W(STX, Abs)
insn_90: BRANCH C, 0
insn_91: W(STA, Ind_Y)
insn_94: W(STY, Zp_X)
insn_95: W(STA, Zp_X)
insn_96: W(STX, Zp_Y)
insn_98: @ TYA
ldrb r1, [cpu_pc], #1
msr cpsr_f, cpu_flags
movs cpu_a, cpu_y
mrs cpu_flags, cpsr
NEXT 2
insn_99: W(STA, Abs_Y)
insn_9a: @ TXS
ldrb r1, [cpu_pc], #1
mov cpu_sp, cpu_x, lsr #24
orr cpu_sp, cpu_sp, #0x100
NEXT 2
insn_9d: W(STA, Abs_X)
insn_a0: R(LDY, Imm)
insn_a1: R(LDA, Ind_X)
insn_a2: R(LDX, Imm)
insn_a4: R(LDY, Zp)
insn_a5: R(LDA, Zp)
insn_a6: R(LDX, Zp)
insn_a8: @ TAY
ldrb r1, [cpu_pc], #1
msr cpsr_f, cpu_flags
movs cpu_y, cpu_a
mrs cpu_flags, cpsr
NEXT 2
insn_a9: R(LDA, Imm)
insn_aa: @ TAX
ldrb r1, [cpu_pc], #1
msr cpsr_f, cpu_flags
movs cpu_x, cpu_a
mrs cpu_flags, cpsr
NEXT 2
insn_ac: R(LDY, Abs)
insn_ad: R(LDA, Abs)
insn_ae: R(LDX, Abs)
insn_b0: BRANCH C, 1
insn_b1: R(LDA, Ind_Y)
insn_b4: R(LDY, Zp_X)
insn_b5: R(LDA, Zp_X)
insn_b6: R(LDX, Zp_Y)
insn_b8: CHANGE_FLAG bic, V
insn_b9: R(LDA, Abs_Y)
insn_ba: @ TSX
ldrb r1, [cpu_pc], #1
msr cpsr_f, cpu_flags
mov r0, cpu_sp, lsl #24
movs cpu_x, r0
mrs cpu_flags, cpsr
NEXT 2
insn_bc: R(LDY, Abs_X)
insn_bd: R(LDA, Abs_X)
insn_be: R(LDX, Abs_Y)
insn_c0: R(CPY, Imm)
insn_c1: R(CMP, Ind_X)
insn_c4: R(CPY, Zp)
insn_c5: R(CMP, Zp)
insn_c6: RMW(DEC, Zp)
insn_c8: RMW(INCR, Y)
insn_c9: R(CMP, Imm)
insn_ca: RMW(DECR, X)
insn_cc: R(CPY, Abs)
insn_cd: R(CMP, Abs)
insn_ce: RMW(DEC, Abs)
insn_d0: BRANCH Z, 0
insn_d1: R(CMP, Ind_Y)
insn_d5: R(CMP, Zp_X)
insn_d6: RMW(DEC, Zp_X)
insn_d8: CHANGE_FLAG bic, D
insn_d9: R(CMP, Abs_Y)
insn_dd: R(CMP, Abs_X)
insn_de: RMW(DEC, Abs_X)
insn_e0: R(CPX, Imm)
insn_e1: R(SBC, Ind_X)
insn_e4: R(CPX, Zp)
insn_e5: R(SBC, Zp)
insn_e6: RMW(INC, Zp)
insn_e8: RMW(INCR, X)
insn_eb: @ undocumented SBC #Imm
insn_e9: R(SBC, Imm)
insn_ec: R(CPX, Abs)
insn_ed: R(SBC, Abs)
insn_ee: RMW(INC, Abs)
insn_f0: BRANCH Z, 1
insn_f1: R(SBC, Ind_Y)
insn_f5: R(SBC, Zp_X)
insn_f6: RMW(INC, Zp_X)
insn_f8: CHANGE_FLAG orr, D
insn_f9: R(SBC, Abs_Y)
insn_fd: R(SBC, Abs_X)
insn_fe: RMW(INC, Abs_X)
@ No-ops
insn_14: @ NOP Zp,X
insn_34: @ NOP Zp,X
insn_54: @ NOP Zp,X
insn_74: @ NOP Zp,X
insn_d4: @ NOP Zp,X
insn_f4: @ NOP Zp,X
sub cpu_cycles, cpu_cycles, #CPU_CYCLE_LENGTH
insn_04: @ NOP Zp
insn_44: @ NOP Zp
insn_64: @ NOP Zp
sub cpu_cycles, cpu_cycles, #CPU_CYCLE_LENGTH
insn_80: @ NOP Imm
insn_82: @ NOP Imm
insn_89: @ NOP Imm
insn_c2: @ NOP Imm
insn_e2: @ NOP Imm
add cpu_pc, cpu_pc, #1
insn_1a: @ NOP
insn_3a: @ NOP
insn_5a: @ NOP
insn_7a: @ NOP
insn_da: @ NOP
insn_ea: @ NOP (official)
insn_fa: @ NOP
ldrb r1, [cpu_pc], #1
NEXT 2
@ Opcodes with undocumented functionality (not implemented)
insn_03:
insn_07:
insn_0b:
insn_0c:
insn_0f:
insn_13:
insn_17:
insn_1b:
insn_1c:
insn_1f:
insn_23:
insn_27:
insn_2b:
insn_2f:
insn_33:
insn_37:
insn_3b:
insn_3c:
insn_3f:
insn_43:
insn_47:
insn_4b:
insn_4f:
insn_53:
insn_57:
insn_5b:
insn_5c:
insn_5f:
insn_63:
insn_67:
insn_6b:
insn_6f:
insn_73:
insn_77:
insn_7b:
insn_7c:
insn_7f:
insn_83:
insn_87:
insn_8b:
insn_8f:
insn_93:
insn_97:
insn_9b:
insn_9c:
insn_9e:
insn_9f:
insn_a3:
insn_a7:
insn_ab:
insn_af:
insn_b3:
insn_b7:
insn_bb:
insn_bf:
insn_c3:
insn_c7:
insn_cb:
insn_cf:
insn_d3:
insn_d7:
insn_db:
insn_dc:
insn_df:
insn_e3:
insn_e7:
insn_ef:
insn_f3:
insn_f7:
insn_fb:
insn_fc:
insn_ff:
@ Opcodes that hang the CPU
insn_02:
insn_12:
insn_22:
insn_32:
insn_42:
insn_52:
insn_62:
insn_72:
insn_92:
insn_b2:
insn_d2:
insn_f2:
ERROR 0x0000