959 lines
22 KiB
ArmAsm
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
|