first commit
This commit is contained in:
commit
7675355d17
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,45 @@
|
|||
NESpire v0.30 - Franchioping's mod (just tangrs' save states on 0.30v)
|
||||
|
||||
[ About ]
|
||||
|
||||
NESpire is an emulator that allows you to play Nintendo Entertainment
|
||||
System (NES) games on the TI-Nspire family of calculators.
|
||||
|
||||
[ Using NESpire ]
|
||||
|
||||
First, you must have Ndless installed to use NESpire. Ndless can be found
|
||||
at http://www.ticalc.org/archives/files/fileinfo/426/42626.html.
|
||||
|
||||
Any NES ROM you wish to play must be named with a .nes.tns extension, and
|
||||
sent to the calculator in the same folder NESpire was sent to. Open NESpire
|
||||
from the documents menu, and you will be presented with the ROM selection
|
||||
menu. Choose a ROM using the arrow keys and press Shift to select one,
|
||||
or press Esc to exit.
|
||||
|
||||
[ Controls ]
|
||||
|
||||
Game controls:
|
||||
Tab = B
|
||||
Esc = A
|
||||
Clear = Select
|
||||
Shift = Start
|
||||
|
||||
Emulator controls:
|
||||
3 - Save State (doesnt write to file)
|
||||
4 - Load State (doesnt write to file)
|
||||
1,2,5,6 = Set frameskip
|
||||
* = Fast-forward
|
||||
B = Reverse border color
|
||||
P = Pause (Many NES games have their own pause feature, but it will
|
||||
still consume just as much power as when playing the game.
|
||||
Using P to pause is preferred, so as not to waste battery life.)
|
||||
Q = Quit
|
||||
R = Reverse colors
|
||||
S = Write save file (Only applicable to games which actually have
|
||||
battery-backed save memory; e.g. Zelda, Final Fantasy; Doesn't
|
||||
write the State)
|
||||
|
||||
[ Known Issues ]
|
||||
|
||||
* Marble Madness: Display of text window at beginning of level is glitchy.
|
||||
* Super Mario Bros. 3: Ground in title screen shakes up a down by 1 pixel.
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
PREFIX=nspire-
|
||||
|
||||
all: nes_emu.tns clean_p
|
||||
@echo DONE
|
||||
|
||||
nes_emu.tns : nespire.elf
|
||||
genzehn --input $< --output $@ --name "nes_emu"
|
||||
make-prg $@ ./nes_emu.prg.tns
|
||||
|
||||
nespire.elf : main.o cpu.o debug.o memory.o ppu.o rom.o menu.o
|
||||
$(PREFIX)ld main.o cpu.o debug.o memory.o ppu.o rom.o menu.o -o $@
|
||||
|
||||
%.o : %.S
|
||||
$(PREFIX)gcc -Wall -W -marm -Os -c $< -o $@
|
||||
|
||||
|
||||
clean_p:
|
||||
@rm -f *.o nespire.elf
|
||||
|
||||
|
||||
clean: clean_p
|
||||
@rm -f nes_emu.tns nes_emu.prg.tns
|
||||
|
|
@ -0,0 +1,958 @@
|
|||
#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
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
#include "nes.inc"
|
||||
#ifdef DEBUG
|
||||
|
||||
.globl chrout
|
||||
chrout:
|
||||
mov r1, #0x90000000
|
||||
add r1, r1, #0x00020000
|
||||
strb r0, [r1]
|
||||
bx lr
|
||||
|
||||
.globl hexout
|
||||
hexout:
|
||||
mov r2, #0x90000000
|
||||
add r2, r2, #0x00020000
|
||||
hexloop:
|
||||
sub r1, r1, #4
|
||||
mov r3, r0, lsr r1
|
||||
and r3, #15
|
||||
cmp r3, #10
|
||||
addcc r3, r3, #'0'
|
||||
addcs r3, r3, #'A'-10
|
||||
strb r3, [r2]
|
||||
cmp r1, #0
|
||||
bne hexloop
|
||||
bx lr
|
||||
|
||||
.globl fps_counter
|
||||
fps_counter:
|
||||
push {r0-r3,lr}
|
||||
ldr r2, [r9, #s_frame_count]
|
||||
add r2, r2, #1
|
||||
str r2, [r9, #s_frame_count]
|
||||
ldr r0, [r9, #s_frame_count_rtc]
|
||||
mov r1, #0x90000000
|
||||
add r1, r1, #0x00090000
|
||||
ldr r1, [r1]
|
||||
str r1, [r9, #s_frame_count_rtc]
|
||||
cmp r0, r1
|
||||
popeq {r0-r3,pc}
|
||||
mov r0, r2
|
||||
mov r1, #32
|
||||
bl hexout
|
||||
mov r0, #10
|
||||
bl chrout
|
||||
mov r2, #0
|
||||
str r2, [r9, #s_frame_count]
|
||||
pop {r0-r3,pc}
|
||||
|
||||
.globl trace_read
|
||||
trace_read:
|
||||
push {r0-r12,lr}
|
||||
mov r0, #'R'
|
||||
bl chrout
|
||||
mov r0, r2
|
||||
mov r1, #16
|
||||
bl hexout
|
||||
mov r0, #13
|
||||
bl chrout
|
||||
mov r0, #10
|
||||
bl chrout
|
||||
pop {r0-r12,lr}
|
||||
b mem_read+4
|
||||
|
||||
.globl trace_write
|
||||
trace_write:
|
||||
push {r0-r12,lr}
|
||||
mov r4, r0
|
||||
mov r0, #'W'
|
||||
bl chrout
|
||||
mov r0, r2
|
||||
mov r1, #16
|
||||
bl hexout
|
||||
mov r0, #'='
|
||||
bl chrout
|
||||
mov r0, r4
|
||||
mov r1, #8
|
||||
bl hexout
|
||||
mov r0, #13
|
||||
bl chrout
|
||||
mov r0, #10
|
||||
bl chrout
|
||||
pop {r0-r12,lr}
|
||||
b mem_write+4
|
||||
|
||||
.globl trace
|
||||
trace:
|
||||
push {r0-r12, lr}
|
||||
mrs r12, cpsr
|
||||
|
||||
mov r0, #'P'; bl chrout
|
||||
mov r0, #'C'; bl chrout
|
||||
mov r0, #'='; bl chrout
|
||||
|
||||
ldr r0, [r9, #s_pc_base]
|
||||
sub r0, cpu_pc, r0
|
||||
sub r0, r0, #1
|
||||
mov r1, #16
|
||||
bl hexout
|
||||
|
||||
mov r0, #'['; bl chrout
|
||||
ldrb r0, [cpu_pc, #-1]
|
||||
mov r1, #8
|
||||
bl hexout
|
||||
mov r0, #']'; bl chrout
|
||||
|
||||
mov r0, #' '; bl chrout
|
||||
|
||||
mov r0, #'A'; bl chrout
|
||||
mov r0, #'='; bl chrout
|
||||
mov r0, cpu_a, lsr #24; mov r1, #8; bl hexout
|
||||
|
||||
mov r0, #' '; bl chrout
|
||||
|
||||
mov r0, #'X'; bl chrout
|
||||
mov r0, #'='; bl chrout
|
||||
mov r0, cpu_x, lsr #24; mov r1, #8; bl hexout
|
||||
|
||||
mov r0, #' '; bl chrout
|
||||
|
||||
mov r0, #'Y'; bl chrout
|
||||
mov r0, #'='; bl chrout
|
||||
mov r0, cpu_y, lsr #24; mov r1, #8; bl hexout
|
||||
|
||||
mov r0, #' '; bl chrout
|
||||
|
||||
mov r0, #'F'; bl chrout
|
||||
mov r0, #'='; bl chrout
|
||||
ldr r4, [r9, #s_flags_di]
|
||||
mov r0, #'N'; tst cpu_flags, #0x80000000; moveq r0, #'n'; bl chrout
|
||||
mov r0, #'V'; tst cpu_flags, #0x10000000; moveq r0, #'v'; bl chrout
|
||||
mov r0, #'-'; bl chrout
|
||||
mov r0, #'-'; bl chrout
|
||||
mov r0, #'D'; tst r4, #0x08; moveq r0, #'d'; bl chrout
|
||||
mov r0, #'I'; tst r4, #0x04; moveq r0, #'i'; bl chrout
|
||||
mov r0, #'Z'; tst cpu_flags, #0x40000000; moveq r0, #'z'; bl chrout
|
||||
mov r0, #'C'; tst cpu_flags, #0x20000000; moveq r0, #'c'; bl chrout
|
||||
|
||||
mov r0, #' '; bl chrout
|
||||
|
||||
mov r0, #'S'; bl chrout
|
||||
mov r0, #'='; bl chrout
|
||||
mov r0, cpu_sp; mov r1, #8; bl hexout
|
||||
|
||||
mov r0, #' '; bl chrout
|
||||
ldr r0, [r9, #s_ppu_scanline]
|
||||
mov r1, #16; bl hexout
|
||||
mov r0, #':'; bl chrout
|
||||
mov r0, #0x0100
|
||||
add r0, #0x0054
|
||||
sub r0, cpu_cycles
|
||||
mov r1, #16; bl hexout
|
||||
|
||||
mov r0, #13; bl chrout
|
||||
mov r0, #10; bl chrout
|
||||
|
||||
msr cpsr_f, r12
|
||||
pop {r0-r12, pc}
|
||||
#endif
|
||||
Binary file not shown.
|
|
@ -0,0 +1,416 @@
|
|||
#include "nes.inc"
|
||||
|
||||
.string "PRG"
|
||||
|
||||
.globl main
|
||||
main:
|
||||
push {r4-r11, lr}
|
||||
@ Allocate the state data structure from the stack and zero it out
|
||||
mov r5, sp
|
||||
sub sp, sp, #s_SIZE
|
||||
bic sp, sp, #s_ALIGN - 1
|
||||
mov r9, sp
|
||||
sub sp, sp, #s_SIZE // Reserve space for state ADDED
|
||||
|
||||
mov r4, #0
|
||||
mov r6, #s_SIZE
|
||||
1: subs r6, r6, #4
|
||||
str r4, [r9, r6]
|
||||
bne 1b
|
||||
str r5, [r9, #s_saved_sp]
|
||||
|
||||
@ Get our folder path
|
||||
add r4, r9, #s_path
|
||||
mov r2, r4
|
||||
movs r0, r0 @ argc
|
||||
ldrne r1, [r1] @ argv[0]
|
||||
movnes r1, r1
|
||||
beq 2f
|
||||
1: ldrb r0, [r1], #1
|
||||
strb r0, [r4], #1
|
||||
teq r0, #'\'
|
||||
teqne r0, #'/'
|
||||
moveq r2, r4
|
||||
movs r0, r0
|
||||
bne 1b
|
||||
2: str r2, [r9, #s_path_filename]
|
||||
|
||||
@ Check hardware type
|
||||
ldr r0, =0x900A0000
|
||||
ldr r0, [r0]
|
||||
bic r0, #0xFF000000
|
||||
cmp r0, #0x10
|
||||
bne 1f
|
||||
@ Non-CX
|
||||
mov r0, #0xDC000000
|
||||
add r0, #0x08
|
||||
adr r1, interrupt_handler_noncx
|
||||
mvn r2, #0
|
||||
mov r3, #0
|
||||
mov r4, #3
|
||||
b 2f
|
||||
1:
|
||||
sub r0, #0x100
|
||||
cmp r0, #0x001
|
||||
bne unknown_hardware
|
||||
@ CX
|
||||
mov r0, #0xDC000000
|
||||
add r0, #0x10
|
||||
adr r1, interrupt_handler_cx
|
||||
mov r2, #0
|
||||
mov r3, #1
|
||||
mov r4, #1
|
||||
2:
|
||||
str r0, [r9, #s_hw_irq_masks]
|
||||
str r1, [r9, #s_hw_irq_handler]
|
||||
str r2, [r9, #s_hw_keypad_invert]
|
||||
str r3, [r9, #s_hw_color]
|
||||
str r4, [r9, #s_frameskip]
|
||||
|
||||
bl init_interrupts
|
||||
bl init_keypad
|
||||
bl toggle_border
|
||||
bl rom_menu
|
||||
bl clear_screen
|
||||
|
||||
@ Set CPU to power-on state
|
||||
mov cpu_a, #0
|
||||
mov cpu_x, #0
|
||||
mov cpu_y, #0
|
||||
mov cpu_sp, #0x100 @ RESET will bring this to 0x1FD
|
||||
mov cpu_flags, #0
|
||||
|
||||
@ Start CPU emulation
|
||||
b reset
|
||||
|
||||
.globl exit_emulator
|
||||
exit_emulator:
|
||||
ldr r0, [r9, #s_prg_ptr]
|
||||
swi e_free
|
||||
bl restore_interrupts
|
||||
unknown_hardware:
|
||||
ldr sp, [r9, #s_saved_sp]
|
||||
pop {r4-r11, pc}
|
||||
|
||||
init_interrupts:
|
||||
str r9, [pc, #state_ptr - (.+8)]
|
||||
|
||||
msr cpsr_c, #0xD3 @ Interrupts off
|
||||
|
||||
@ Disable everything except the timer interrupt (IRQ 19)
|
||||
ldr r0, [r9, #s_hw_irq_masks]
|
||||
ldr r2, [r0]
|
||||
str r2, [r9, #s_saved_irq_mask]
|
||||
str r2, [r0, #4]
|
||||
mov r2, #1 << 19
|
||||
str r2, [r0]
|
||||
|
||||
@ Set the IRQ vector
|
||||
mov r1, #0xA4000000
|
||||
ldr r2, [r1, #0x38]
|
||||
str r2, [r9, #s_saved_irq_handler]
|
||||
ldr r2, [r9, #s_hw_irq_handler]
|
||||
str r2, [r1, #0x38]
|
||||
|
||||
msr cpsr_c, #0x13 @ Interrupts on
|
||||
bx lr
|
||||
|
||||
interrupt_handler_cx:
|
||||
push {r0-r1, lr}
|
||||
ldr r0, =0x900D0000
|
||||
mov r1, #1
|
||||
str r1, [r0, #0x0C]
|
||||
b interrupt_handler_common
|
||||
interrupt_handler_noncx:
|
||||
push {r0-r1, lr}
|
||||
mov r0, #0xDC000000
|
||||
ldr r1, =0x900A0000
|
||||
ldr lr, [r0, #0x24]
|
||||
ldr lr, [r0, #0x28]
|
||||
mov lr, #1
|
||||
str lr, [r1, #0x20]
|
||||
mov lr, #1 << 19
|
||||
str lr, [r0, #0x04]
|
||||
mov lr, #8
|
||||
str lr, [r0, #0x2C]
|
||||
interrupt_handler_common:
|
||||
@ Advance the frame timer by 3/300 of a second
|
||||
ldr r1, [pc, #state_ptr - (.+8)]
|
||||
ldrb lr, [r1, #s_frame_timer]
|
||||
add lr, lr, #3
|
||||
strb lr, [r1, #s_frame_timer]
|
||||
pop {r0-r1, lr}
|
||||
subs pc, lr, #4
|
||||
.pool
|
||||
state_ptr:
|
||||
.word 0
|
||||
|
||||
restore_interrupts:
|
||||
msr cpsr_c, #0xD3 @ Interrupts off
|
||||
|
||||
ldr r0, [r9, #s_hw_irq_masks]
|
||||
mvn r2, #0
|
||||
str r2, [r0, #4]
|
||||
ldr r2, [r9, #s_saved_irq_mask]
|
||||
str r2, [r0]
|
||||
|
||||
mov r1, #0xA4000000
|
||||
ldr r2, [r9, #s_saved_irq_handler]
|
||||
str r2, [r1, #0x38]
|
||||
bx lr
|
||||
|
||||
.globl newframe
|
||||
newframe:
|
||||
str lr, [sp, #-4]!
|
||||
|
||||
ldrb r0, [r9, #s_message_timer]
|
||||
subs r0, #1
|
||||
strplb r0, [r9, #s_message_timer]
|
||||
|
||||
ldr r0, [r9, #s_frameskip_cur]
|
||||
ldr r10, [r9, #s_frameskip]
|
||||
subs r0, r0, #1
|
||||
addmi r0, r0, r10
|
||||
str r0, [r9, #s_frameskip_cur]
|
||||
|
||||
mov r8, #0
|
||||
pause_loop:
|
||||
|
||||
#define num_command_keys 12
|
||||
@ Scan keypad
|
||||
ldr r3, [r9, #s_hw_keypad_invert]
|
||||
mov r4, #0
|
||||
ldr r5, =0x900E0010
|
||||
mov r6, #num_command_keys - 1
|
||||
ldr r7, [r9, #s_keypad_command_map]
|
||||
1: ldrb r0, [r7, r6]
|
||||
and r1, r0, #0x60
|
||||
ldr r1, [r5, r1, lsr #3]
|
||||
eor r1, r3, r1, ror r0
|
||||
and r1, #1
|
||||
orr r4, r1, lsl r6
|
||||
subs r6, #1
|
||||
bpl 1b
|
||||
|
||||
ldr r5, [r9, #s_command_keys_pressed]
|
||||
str r4, [r9, #s_command_keys_pressed]
|
||||
bic r5, r4, r5
|
||||
|
||||
tst r5, #1 << 0; movne r10, #1
|
||||
tst r5, #1 << 1; movne r10, #2
|
||||
//tst r5, #1 << 2; movne r10, #3
|
||||
tst r5, #1 << 2; blne save_state
|
||||
//tst r5, #1 << 3; movne r10, #4
|
||||
tst r5, #1 << 3; blne load_state
|
||||
|
||||
tst r5, #1 << 4; movne r10, #5
|
||||
tst r5, #1 << 5; movne r10, #6
|
||||
tst r5, #1 << 6; blne toggle_border @ B (Border)
|
||||
tst r5, #1 << 7; mvnne r8, r8 @ P (Pause)
|
||||
tst r5, #1 << 8; bne exit_emulator @ Q (Quit)
|
||||
tst r5, #1 << 9; blne invert_colors @ R (Reverse)
|
||||
tst r5, #1 << 10; blne sram_save @ S (Save SRAM)
|
||||
tst r4, #1 << 11; bne fast_forward @ *
|
||||
|
||||
@ Keep looping until the frame timer reaches 5/300 (1/60) of a second
|
||||
ldrb r0, [r9, #s_frame_timer]
|
||||
subs r0, r0, #5
|
||||
movcc r0, #0
|
||||
mcrcc p15, 0, r0, c7, c0, 4
|
||||
bcc pause_loop
|
||||
strb r0, [r9, #s_frame_timer]
|
||||
#ifdef DEBUG
|
||||
bl fps_counter
|
||||
#endif
|
||||
movs r8, r8
|
||||
bne pause_loop
|
||||
fast_forward:
|
||||
str r10, [r9, #s_frameskip]
|
||||
|
||||
mov lr, pc
|
||||
ldr pc, [r9, #s_keypad_read_input]
|
||||
str r0, [r9, #s_input_status]
|
||||
|
||||
ldr pc, [sp], #4
|
||||
|
||||
init_keypad:
|
||||
str lr, [sp, #-4]!
|
||||
|
||||
@ Temporarily enable access to the ADC (if it wasn't enabled already)
|
||||
@ and get the last read value from channel 3 (keypad type)
|
||||
@ Would use the system call, but it wasn't present yet in Ndless 1.7
|
||||
ldr r0, =0x900B0018
|
||||
ldr r1, [r0]
|
||||
bic r2, r1, #0x10
|
||||
str r2, [r0]
|
||||
mov r2, #0xC4000000
|
||||
ldr r2, [r2, #0x170]
|
||||
str r1, [r0]
|
||||
|
||||
sub r2, #0x40
|
||||
cmp r2, #0x59 - 0x40
|
||||
adrcc r2, touchpad_command_map
|
||||
adrcs r2, clickpad_command_map
|
||||
str r2, [r9, #s_keypad_command_map]
|
||||
adrcc r2, touchpad_read_input
|
||||
adrcs r2, clickpad_read_input
|
||||
str r2, [r9, #s_keypad_read_input]
|
||||
|
||||
ldrcs pc, [sp], #4
|
||||
|
||||
mov r0, #0xFF
|
||||
mov r1, #0xFF
|
||||
adr r2, touchpad_info_page
|
||||
swi e_touchpad_write
|
||||
mov r0, #0x04
|
||||
mov r1, #0x07
|
||||
add r2, r9, #s_touchpad_size
|
||||
swi e_touchpad_read
|
||||
mov r0, #0xFF
|
||||
mov r1, #0xFF
|
||||
adr r2, touchpad_main_page
|
||||
swi e_touchpad_write
|
||||
|
||||
ldr pc, [sp], #4
|
||||
|
||||
touchpad_info_page:
|
||||
.byte 0x10
|
||||
touchpad_main_page:
|
||||
.byte 0x04
|
||||
|
||||
@ 1 2 3 4 5 6 B P Q R S *
|
||||
clickpad_command_map:
|
||||
.byte 0x17,0x15,0x13,0x27,0x25,0x23,0x64,0x28,0x26,0x24,0x22,0x31
|
||||
touchpad_command_map:
|
||||
.byte 0x17,0x64,0x13,0x27,0x56,0x23,0x45,0x22,0x21,0x20,0x16,0x48
|
||||
|
||||
.align 4
|
||||
|
||||
clickpad_read_input:
|
||||
mvn r2, #0xFF
|
||||
ldr r0, =0x900E0000
|
||||
ldrd r0, [r0, #0x18]
|
||||
ldr r3, [r9, #s_hw_keypad_invert]
|
||||
eor r0, r3
|
||||
eor r1, r3
|
||||
tst r0, #1 << 25; orrne r2, r2, #0x08 @ Caps (Start)
|
||||
tst r1, #1 << 7; orrne r2, r2, #0x01 @ Esc (A)
|
||||
tst r1, #1 << 9; orrne r2, r2, #0x02 @ Tab (B)
|
||||
tst r1, #1 << 16; orrne r2, r2, #0x10 @ Up
|
||||
tst r1, #1 << 18; orrne r2, r2, #0x80 @ Right
|
||||
tst r1, #1 << 20; orrne r2, r2, #0x20 @ Down
|
||||
tst r1, #1 << 22; orrne r2, r2, #0x40 @ Left
|
||||
tst r1, #1 << 24; orrne r2, r2, #0x04 @ Clear (Select)
|
||||
mov r0, r2
|
||||
bx lr
|
||||
|
||||
touchpad_read_input:
|
||||
push {r4, lr}
|
||||
mvn r4, #0xFF
|
||||
|
||||
ldr r0, =0x900E0000
|
||||
ldrd r0, [r0, #0x18]
|
||||
ldr r2, [r9, #s_hw_keypad_invert]
|
||||
eor r0, r2
|
||||
eor r1, r2
|
||||
tst r1, #1 << 7; orrne r4, r4, #0x01 @ Esc (A)
|
||||
tst r1, #1 << 9; orrne r4, r4, #0x02 @ Tab (B)
|
||||
tst r0, #1 << 25; orrne r4, r4, #0x04 @ Clear (Select)
|
||||
tst r1, #1 << 24; orrne r4, r4, #0x08 @ Caps (Start)
|
||||
|
||||
sub sp, #0x0C
|
||||
mov r0, #0x02
|
||||
mov r1, #0x0A
|
||||
add r2, sp, #0x02
|
||||
swi e_touchpad_read
|
||||
movs r0, r0
|
||||
beq 1f
|
||||
ldrb r0, [sp, #0x0A]
|
||||
tst r0, #0x01
|
||||
beq 1f
|
||||
|
||||
ldrb r0, [sp, #0x02]
|
||||
ldrb r1, [sp, #0x03]
|
||||
ldrb r2, [r9, #s_touchpad_size]
|
||||
ldrb r3, [r9, #s_touchpad_size+1]
|
||||
orr r0, r1, r0, lsl #8
|
||||
orr r2, r3, r2, lsl #8
|
||||
add r0, r0, lsl #1
|
||||
cmp r0, r2; orrcc r4, r4, #0x40 @ Left
|
||||
cmp r0, r2, lsl #1; orrcs r4, r4, #0x80 @ Right
|
||||
|
||||
ldrb r0, [sp, #0x04]
|
||||
ldrb r1, [sp, #0x05]
|
||||
ldrb r2, [r9, #s_touchpad_size+2]
|
||||
ldrb r3, [r9, #s_touchpad_size+3]
|
||||
orr r0, r1, r0, lsl #8
|
||||
orr r2, r3, r2, lsl #8
|
||||
add r0, r0, lsl #1
|
||||
cmp r0, r2; orrcc r4, r4, #0x20 @ Down
|
||||
cmp r0, r2, lsl #1; orrcs r4, r4, #0x10 @ Up
|
||||
1:
|
||||
add sp, #0x0C
|
||||
|
||||
mov r0, r4
|
||||
pop {r4, pc}
|
||||
|
||||
.pool
|
||||
|
||||
save_state:
|
||||
str lr, saved_state_cpu_status+56
|
||||
adr lr, saved_state_cpu_status
|
||||
stm lr, {r0-r13}
|
||||
mrs r0, cpsr
|
||||
str r0, saved_state_cpu_cpsr
|
||||
|
||||
mov r1, #s_SIZE
|
||||
sub r2, r9, #s_SIZE
|
||||
mov r3, r9
|
||||
sstate_loop:
|
||||
ldr r0, [r3]
|
||||
str r0, [r2]
|
||||
add r3, r3, #4
|
||||
add r2, r2, #4
|
||||
sub r1, r1, #4
|
||||
cmp r1, #0
|
||||
bne sstate_loop
|
||||
|
||||
mov r0, #1
|
||||
str r0, save_state_exists
|
||||
|
||||
ldr lr, saved_state_cpu_status+56 // subroutine return
|
||||
|
||||
load_state:
|
||||
ldr r0, save_state_exists
|
||||
cmp r0, #0
|
||||
moveq pc,lr
|
||||
|
||||
mov r1, #s_SIZE
|
||||
sub r3, r9, #s_SIZE
|
||||
mov r2, r9
|
||||
lstate_loop:
|
||||
ldr r0, [r3]
|
||||
str r0, [r2]
|
||||
add r3, r3, #4
|
||||
add r2, r2, #4
|
||||
sub r1, r1, #4
|
||||
cmp r1, #0
|
||||
bne lstate_loop
|
||||
|
||||
ldr r0, saved_state_cpu_cpsr
|
||||
msr cpsr_all, r0
|
||||
adr lr, saved_state_cpu_status
|
||||
ldm lr, {r0-r13,pc} // returns to previous instruction after save_state
|
||||
|
||||
|
||||
save_state_exists: .word 0
|
||||
|
||||
saved_state_cpu_status:
|
||||
.rept 15
|
||||
.word 0
|
||||
.endr
|
||||
saved_state_cpu_cpsr:
|
||||
.word 0
|
||||
|
||||
.pool
|
||||
|
||||
|
|
@ -0,0 +1,372 @@
|
|||
#include "nes.inc"
|
||||
|
||||
.globl mem_read_split
|
||||
.globl mem_read
|
||||
.globl mem_write_split
|
||||
.globl mem_write
|
||||
.globl mem_jump_split
|
||||
.globl mem_jump
|
||||
|
||||
@ Input:
|
||||
@ r2 = address low byte (split), full address (unsplit)
|
||||
@ r3 = address high byte (split)
|
||||
@ Output:
|
||||
@ r0 = byte read
|
||||
@ r2 = full address
|
||||
@ r3 invalidated
|
||||
@ All other registers preserved
|
||||
|
||||
mem_read_split:
|
||||
add r2, r2, r3, lsl #8
|
||||
mem_read:
|
||||
mov r3, r2, lsr #13
|
||||
add pc, pc, r3, lsl #4
|
||||
nop
|
||||
mem_read_ram:
|
||||
@ 0000-1FFF: RAM
|
||||
bic r3, r2, #0x11800
|
||||
ldrb r0, [r9, r3]
|
||||
bx lr
|
||||
nop
|
||||
@ 2000-3FFF: PPU registers
|
||||
and r3, r2, #7
|
||||
add r3, pc, r3, lsl #2
|
||||
add pc, r3, #ppu_read_table - (. + 4)
|
||||
nop
|
||||
@ 4000-5FFF: 2A03 registers
|
||||
sub r3, r2, #0x4000
|
||||
cmp r3, #0x16
|
||||
beq mem_read_4016
|
||||
b mem_read_bad
|
||||
@ 6000-7FFF: SRAM
|
||||
add r3, r9, #s_sram - 0x6000
|
||||
ldrb r0, [r3, r2]
|
||||
bx lr
|
||||
nop
|
||||
@ 8000-9FFF: ROM
|
||||
ldr r3, [r9, #s_mem_map + 0x10]
|
||||
ldrb r0, [r3, r2]
|
||||
bx lr
|
||||
nop
|
||||
@ A000-BFFF: ROM
|
||||
ldr r3, [r9, #s_mem_map + 0x14]
|
||||
ldrb r0, [r3, r2]
|
||||
bx lr
|
||||
nop
|
||||
@ C000-DFFF: ROM
|
||||
ldr r3, [r9, #s_mem_map + 0x18]
|
||||
ldrb r0, [r3, r2]
|
||||
bx lr
|
||||
nop
|
||||
@ E000-FFFF: ROM
|
||||
ldr r3, [r9, #s_mem_map + 0x1C]
|
||||
ldrb r0, [r3, r2]
|
||||
bx lr
|
||||
nop
|
||||
@ 10000-100FE: RAM (overflow)
|
||||
b mem_read_ram
|
||||
mem_read_bad:
|
||||
@ TODO: print debug message
|
||||
mov r0, #0
|
||||
bx lr
|
||||
ppu_read_table:
|
||||
b mem_read_bad
|
||||
b mem_read_bad
|
||||
b mem_read_2002
|
||||
b mem_read_bad
|
||||
b mem_read_2004
|
||||
b mem_read_bad
|
||||
b mem_read_bad
|
||||
mem_read_2007:
|
||||
ldr r2, [r9, #s_ppu_address]
|
||||
bic r2, r2, #0xC000
|
||||
mov r3, r2, lsr #10
|
||||
add r3, r9, r3, lsl #2
|
||||
|
||||
@ Buffered VRAM read
|
||||
ldr r3, [r3, #s_ppu_mem_map]
|
||||
ldrb r0, [r9, #s_ppu_data]
|
||||
ldrb r3, [r3, r2]
|
||||
strb r3, [r9, #s_ppu_data]
|
||||
|
||||
@ Palette is read directly
|
||||
cmp r2, #0x3F00
|
||||
andcs r2, r2, #0x1F
|
||||
addcs r3, r9, #s_ppu_palette
|
||||
ldrcsb r0, [r3, r2]
|
||||
|
||||
@ Advance ppu_address by 1 or 32
|
||||
ldr r3, [r9, #s_ppu_control]
|
||||
ldr r2, [r9, #s_ppu_address]
|
||||
tst r3, #0x04
|
||||
addeq r2, r2, #0x0001
|
||||
addne r2, r2, #0x0020
|
||||
bic r2, r2, #0x8000
|
||||
str r2, [r9, #s_ppu_address]
|
||||
@ Fix our flagrant mangling of r2
|
||||
mov r2, #0x2000
|
||||
add r2, r2, #0x7
|
||||
bx lr
|
||||
mem_read_2004:
|
||||
ldrb r3, [r9, #s_ppu_oam_addr]
|
||||
add r0, r9, #s_ppu_oam_ram
|
||||
ldrb r0, [r0, r3]
|
||||
and r3, r3, #0x03
|
||||
cmp r3, #0x02
|
||||
andeq r0, r0, #0xE3
|
||||
bx lr
|
||||
mem_read_2002:
|
||||
ldrb r0, [r9, #s_ppu_status]
|
||||
mov r3, #0x00
|
||||
strb r3, [r9, #s_ppu_scroll+2]
|
||||
bic r3, r0, #0x80
|
||||
strb r3, [r9, #s_ppu_status]
|
||||
bx lr
|
||||
|
||||
mem_read_4016:
|
||||
ldr r3, [r9, #s_input_queue]
|
||||
and r0, r3, #1
|
||||
mov r3, r3, asr #1
|
||||
str r3, [r9, #s_input_queue]
|
||||
bx lr
|
||||
|
||||
@ Input:
|
||||
@ r0 = byte to write (high bits ignored)
|
||||
@ r2 = address low byte (split), full address (unsplit)
|
||||
@ r3 = address high byte (split)
|
||||
@ Output:
|
||||
@ r0, r2, r3 invalidated
|
||||
@ All other registers preserved
|
||||
|
||||
mem_write_split:
|
||||
add r2, r2, r3, lsl #8
|
||||
mem_write:
|
||||
@push {r0}
|
||||
@mov r3, r2, lsr #13
|
||||
@add r3, r9, r3, lsl #2
|
||||
@ldr r0, [r3, #0xC00]
|
||||
@add r0, r0, #1
|
||||
@str r0, [r3, #0xC00]
|
||||
@pop {r0}
|
||||
mov r3, r2, lsr #13
|
||||
add pc, pc, r3, lsl #4
|
||||
nop
|
||||
@ 0000-1FFF: RAM
|
||||
mem_write_ram:
|
||||
bic r3, r2, #0x11800
|
||||
strb r0, [r9, r3]
|
||||
bx lr
|
||||
nop
|
||||
@ 2000-3FFF: PPU registers
|
||||
and r3, r2, #7
|
||||
add r3, pc, r3, lsl #2
|
||||
add pc, r3, #ppu_write_table - (. + 4)
|
||||
nop
|
||||
@ 4000-5FFF: APU registers
|
||||
sub r3, r2, #0x4000
|
||||
b mem_write_4000_to_4017
|
||||
nop
|
||||
nop
|
||||
@ 6000-7FFF: SRAM
|
||||
add r3, r9, #s_sram - 0x6000
|
||||
strb r0, [r3, r2]
|
||||
bx lr
|
||||
nop
|
||||
@ 8000-FFFF: Mapper registers
|
||||
.rept 4
|
||||
str lr, [sp, #-4]!
|
||||
adr lr, return_from_mapper
|
||||
ldr pc, [r9, #s_mapper]
|
||||
nop
|
||||
.endr
|
||||
@ 10000-100FE: RAM (overflow)
|
||||
b mem_write_ram
|
||||
return_from_mapper:
|
||||
ldr r0, [r9, #s_pc_base]
|
||||
ldr lr, [sp], #4
|
||||
sub r4, r4, #1
|
||||
sub r2, r4, r0
|
||||
b mem_jump
|
||||
|
||||
ppu_write_table:
|
||||
b mem_write_2000
|
||||
b mem_write_2001
|
||||
bx lr @ No-op
|
||||
b mem_write_2003
|
||||
b mem_write_2004
|
||||
b mem_write_2005
|
||||
b mem_write_2006
|
||||
@ Reg 2007:
|
||||
mem_write_2007:
|
||||
ldr r2, [r9, #s_ppu_address]
|
||||
bic r2, r2, #0xC000
|
||||
|
||||
cmp r2, #0x3F00
|
||||
bcs mem_write_2007_palette
|
||||
mov r3, r2, lsr #10
|
||||
add r3, r9, r3, lsl #2
|
||||
ldr r3, [r3, #s_ppu_mem_map]
|
||||
strb r0, [r3, r2] @ TODO: don't allow write to CHR-ROM
|
||||
|
||||
b mem_write_2007_common
|
||||
mem_write_2007_palette:
|
||||
and r0, r0, #0x3F
|
||||
and r2, r2, #0x1F
|
||||
|
||||
@ +00/+10, +04/+14, +08/+18, +0C/+1C are mirrored pairs
|
||||
add r3, r9, #s_ppu_palette
|
||||
tst r2, #0x03
|
||||
strb r0, [r3, r2]
|
||||
eoreq r2, r2, #0x10
|
||||
streqb r0, [r3, r2]
|
||||
|
||||
mov r0, #0
|
||||
strb r0, [r9, #s_palette_cache_valid]
|
||||
mem_write_2007_common:
|
||||
@ Advance ppu_address by 1 or 32
|
||||
ldr r3, [r9, #s_ppu_control]
|
||||
ldr r2, [r9, #s_ppu_address]
|
||||
tst r3, #0x04
|
||||
addeq r2, r2, #0x0001
|
||||
addne r2, r2, #0x0020
|
||||
bic r2, r2, #0x8000
|
||||
str r2, [r9, #s_ppu_address]
|
||||
bx lr
|
||||
|
||||
@ Reg 2006: Set PPU address, first hi byte, then lo
|
||||
mem_write_2006:
|
||||
ldr r3, [r9, #s_ppu_scroll]
|
||||
tst r3, #0x10000
|
||||
eor r3, r3, #0x10000
|
||||
str r3, [r9, #s_ppu_scroll]
|
||||
bne mem_write_2006_second
|
||||
mem_write_2006_first:
|
||||
and r0, r0, #0x3F
|
||||
strb r0, [r9, #s_ppu_scroll + 1]
|
||||
bx lr
|
||||
mem_write_2006_second:
|
||||
strb r0, [r9, #s_ppu_scroll]
|
||||
strb r0, [r9, #s_ppu_address]
|
||||
mov r0, r3, lsr #8
|
||||
strb r0, [r9, #s_ppu_address + 1]
|
||||
bx lr
|
||||
@ Reg 2005: Set scroll position, first x, then y
|
||||
mem_write_2005:
|
||||
ldr r3, [r9, #s_ppu_scroll]
|
||||
and r0, r0, #0xFF
|
||||
tst r3, #0x10000
|
||||
eor r3, r3, #0x10000
|
||||
bne mem_write_2005_second
|
||||
mem_write_2005_first:
|
||||
bic r3, r3, #0xE0000000
|
||||
bic r3, r3, #0x0000001F
|
||||
orr r3, r3, r0, ror #3
|
||||
str r3, [r9, #s_ppu_scroll]
|
||||
bx lr
|
||||
mem_write_2005_second:
|
||||
mov r0, r0, ror #3
|
||||
bic r3, r3, #0x03E0
|
||||
orr r3, r3, r0, lsl #5
|
||||
bic r3, r3, #0x7000
|
||||
orr r3, r3, r0, lsr #17
|
||||
str r3, [r9, #s_ppu_scroll]
|
||||
bx lr
|
||||
@ Reg 2004: Sprite data
|
||||
mem_write_2004:
|
||||
ldrb r3, [r9, #s_ppu_oam_addr]
|
||||
add r2, r9, #s_ppu_oam_ram
|
||||
strb r0, [r2, r3]
|
||||
add r3, r3, #1
|
||||
strb r3, [r9, #s_ppu_oam_addr]
|
||||
mov r0, #0
|
||||
strb r0, [r9, #s_spr_loc_table_valid]
|
||||
bx lr
|
||||
@ Reg 2003: Sprite address
|
||||
mem_write_2003:
|
||||
strb r0, [r9, #s_ppu_oam_addr]
|
||||
bx lr
|
||||
@ Reg 2001: PPU Mask
|
||||
mem_write_2001:
|
||||
strb r0, [r9, #s_ppu_mask]
|
||||
bx lr
|
||||
@ Reg 2000: PPU Control
|
||||
mem_write_2000:
|
||||
@ TODO: if sprites changed 8x8 <-> 8x16, invalidate table
|
||||
ldr r3, [r9, #s_ppu_scroll]
|
||||
strb r0, [r9, #s_ppu_control]
|
||||
bic r3, #0x0C00
|
||||
and r0, r0, #0x03
|
||||
orr r3, r3, r0, lsl #10
|
||||
str r3, [r9, #s_ppu_scroll]
|
||||
@ TODO: Generate NMI if 2002.b7 set and 2000.b7 changed from 0 to 1
|
||||
bx lr
|
||||
|
||||
mem_write_4000_to_4017:
|
||||
cmp r3, #0x14
|
||||
beq mem_write_4014
|
||||
cmp r3, #0x16
|
||||
bxne lr
|
||||
mem_write_4016:
|
||||
ldr r0, [r9, #s_input_status]
|
||||
str r0, [r9, #s_input_queue]
|
||||
bx lr
|
||||
|
||||
mem_write_4014:
|
||||
push {r1}
|
||||
|
||||
@ Store 256 bytes to OAM RAM
|
||||
mov r0, r0, lsl #8
|
||||
mov r2, r0, lsr #13
|
||||
add r2, r9, r2, lsl #2
|
||||
ldr r2, [r2, #s_mem_map]
|
||||
add r0, r2, r0
|
||||
|
||||
ldrb r3, [r9, #s_ppu_oam_addr]
|
||||
add r2, r9, #s_ppu_oam_ram
|
||||
add r2, r2, r3
|
||||
rsb r3, r3, #0x100
|
||||
mem_write_4014_loop:
|
||||
ldrb r1, [r0], #1
|
||||
subs r3, r3, #1
|
||||
strb r1, [r2], #1
|
||||
bne mem_write_4014_loop
|
||||
|
||||
ldrb r3, [r9, #s_ppu_oam_addr]
|
||||
add r2, r9, #s_ppu_oam_ram
|
||||
cmp r3, #0
|
||||
beq mem_write_4014_done
|
||||
mem_write_4014_loop2:
|
||||
ldrb r1, [r0], #1
|
||||
subs r3, r3, #1
|
||||
strb r1, [r2], #1
|
||||
bne mem_write_4014_loop2
|
||||
mem_write_4014_done:
|
||||
mov r0, #0
|
||||
strb r0, [r9, #s_spr_loc_table_valid]
|
||||
pop {r1}
|
||||
@ CPU is paused for 513 cycles while the transfer completes
|
||||
sub cpu_cycles, cpu_cycles, #512 * CPU_CYCLE_LENGTH
|
||||
sub cpu_cycles, cpu_cycles, #1 * CPU_CYCLE_LENGTH
|
||||
bx lr
|
||||
|
||||
mem_jump_split:
|
||||
add r2, r2, r3, lsl #8
|
||||
mem_jump:
|
||||
@ Most jumps will probably be to ROM. Optimize for higher addresses
|
||||
cmp r2, #0x6000
|
||||
bcc mem_jump_low
|
||||
mem_jump_ok:
|
||||
mov r0, r2, lsr #13
|
||||
add r0, r9, r0, lsl #2
|
||||
ldr r0, [r0, #s_mem_map]
|
||||
str r0, [r9, #s_pc_base]
|
||||
|
||||
add cpu_pc, r0, r2
|
||||
ldrb r1, [cpu_pc], #1
|
||||
bx lr
|
||||
mem_jump_low:
|
||||
cmp r2, #0x2000
|
||||
biccc r2, r2, #0x1800 @ RAM mirroring
|
||||
bcc mem_jump_ok
|
||||
@ Jump to 2000-5FFF range - should never happen.
|
||||
ERROR 0x0001
|
||||
|
|
@ -0,0 +1,264 @@
|
|||
#include "nes.inc"
|
||||
|
||||
nes_rom_file_mask:
|
||||
.string "*.nes.tns"
|
||||
title:
|
||||
.string " NESpire v0.30 -- Shift=start, Esc=quit"
|
||||
.align 4
|
||||
|
||||
.globl rom_menu
|
||||
rom_menu:
|
||||
push {r4-r11, lr}
|
||||
|
||||
ldr r0, [r9, #s_path_filename]
|
||||
adr r1, nes_rom_file_mask
|
||||
swi e_strcpy
|
||||
|
||||
#define liststart r4
|
||||
#define listend r5
|
||||
#define listmax r8
|
||||
@ Using save ram as a buffer to hold list of filenames
|
||||
add liststart, r9, #s_sram
|
||||
mov listend, liststart
|
||||
add listmax, liststart, #0x2000
|
||||
|
||||
sub sp, #308
|
||||
mov r0, sp
|
||||
add r1, r9, #s_path
|
||||
swi e_NU_Get_First
|
||||
movs r0, r0
|
||||
bne no_files
|
||||
next_file:
|
||||
@ Append filename to list
|
||||
mov r0, listend
|
||||
add r1, sp, #13
|
||||
1: teq r0, listmax
|
||||
beq filename_buf_full
|
||||
ldrb r2, [r1], #1
|
||||
movs r2, r2
|
||||
strb r2, [r0], #1
|
||||
bne 1b
|
||||
mov listend, r0
|
||||
|
||||
mov r0, sp
|
||||
swi e_NU_Get_Next
|
||||
movs r0, r0
|
||||
beq next_file
|
||||
filename_buf_full:
|
||||
mov r0, sp
|
||||
swi e_NU_Done
|
||||
no_files:
|
||||
add sp, #308
|
||||
|
||||
#define cursor r6
|
||||
#define pagetop r7
|
||||
#define pagebottom r8
|
||||
#define keypressed r10
|
||||
#define keyrepeat r11
|
||||
mov cursor, liststart
|
||||
mov pagetop, liststart
|
||||
mov keypressed, #0
|
||||
mov keyrepeat, #0
|
||||
|
||||
menu_redraw:
|
||||
adr r0, title
|
||||
mov r1, #0
|
||||
mov r2, #40
|
||||
mov r3, #-1
|
||||
bl display_string
|
||||
|
||||
mov pagebottom, pagetop
|
||||
str r10, [sp, #-4]!
|
||||
mov r10, #640
|
||||
menu_next_row:
|
||||
teq pagebottom, cursor
|
||||
moveq r0, #0x10
|
||||
movne r0, #' '
|
||||
mov r1, r10
|
||||
bl display_char
|
||||
cmp pagebottom, listend
|
||||
bcs menu_draw_done
|
||||
mov r0, pagebottom
|
||||
add r1, r10, #1
|
||||
add r2, r10, #40
|
||||
mov r3, #0
|
||||
bl display_string
|
||||
1: ldrb r0, [pagebottom], #1
|
||||
movs r0, r0
|
||||
bne 1b
|
||||
add r10, #640
|
||||
teq r10, #640 * 15
|
||||
bne menu_next_row
|
||||
menu_draw_done:
|
||||
ldr r10, [sp], #4
|
||||
|
||||
menu_waitkey:
|
||||
mov r0, #0
|
||||
mcr p15, 0, r0, c7, c0, 4
|
||||
mov lr, pc
|
||||
ldr pc, [r9, #s_keypad_read_input]
|
||||
|
||||
mov r1, keypressed
|
||||
mov keypressed, r0
|
||||
|
||||
teq r0, r1
|
||||
movne keyrepeat, #25
|
||||
bne 1f
|
||||
movs keyrepeat, keyrepeat
|
||||
subnes keyrepeat, #1
|
||||
bne menu_waitkey
|
||||
mov keyrepeat, #5
|
||||
1:
|
||||
|
||||
tst r0, #0x01
|
||||
bne exit_emulator
|
||||
tst r0, #0x10
|
||||
bne menu_up
|
||||
tst r0, #0x20
|
||||
bne menu_down
|
||||
tst r0, #0x08
|
||||
bne menu_start
|
||||
b menu_waitkey
|
||||
|
||||
menu_up:
|
||||
teq cursor, liststart
|
||||
beq menu_waitkey
|
||||
1: sub cursor, #1
|
||||
teq cursor, liststart
|
||||
ldrneb r0, [cursor, #-1]
|
||||
movnes r0, r0
|
||||
bne 1b
|
||||
cmp cursor, pagetop
|
||||
movcc pagetop, cursor
|
||||
b menu_redraw
|
||||
|
||||
menu_down:
|
||||
mov r0, cursor
|
||||
1: ldrb r1, [r0], #1
|
||||
movs r1, r1
|
||||
bne 1b
|
||||
cmp r0, listend
|
||||
bcs menu_waitkey
|
||||
mov cursor, r0
|
||||
cmp cursor, pagebottom
|
||||
bcc menu_redraw
|
||||
1: ldrb r0, [pagetop], #1
|
||||
movs r0, r0
|
||||
bne 1b
|
||||
b menu_redraw
|
||||
|
||||
menu_start:
|
||||
@ Append filename to directory
|
||||
ldr r0, [r9, #s_path_filename]
|
||||
mov r1, cursor
|
||||
1: ldrb r2, [r1], #1
|
||||
movs r2, r2
|
||||
strb r2, [r0], #1
|
||||
bne 1b
|
||||
sub r0, #8
|
||||
str r0, [r9, #s_path_extension]
|
||||
|
||||
@ Try to load ROM (full path)
|
||||
add r0, r9, #s_path
|
||||
bl load_rom
|
||||
movs r0, r0
|
||||
beq load_success
|
||||
|
||||
@ Display error message
|
||||
mov r1, #7
|
||||
mov r2, #40
|
||||
mov r3, #-1
|
||||
bl display_string
|
||||
adr r0, error_hdr
|
||||
mov r1, #0
|
||||
mov r2, #7
|
||||
mov r3, #-1
|
||||
bl display_string
|
||||
b menu_waitkey
|
||||
error_hdr:
|
||||
.string "ERROR:"
|
||||
.align 4
|
||||
|
||||
load_success:
|
||||
@ Clear save ram (since we used it to hold filenames)
|
||||
add r0, r9, #s_sram
|
||||
mov r1, #0
|
||||
mov r2, #0x2000
|
||||
swi e_memset
|
||||
|
||||
@ If game has battery-backed save ram, try to load from save file
|
||||
bl sram_load
|
||||
|
||||
pop {r4-r11, pc}
|
||||
|
||||
@ r0 = character
|
||||
@ r1 = position (row * 640 + column)
|
||||
display_char:
|
||||
mov r2, #0
|
||||
@ r2 = color (0 = normal, -1 = reverse)
|
||||
display_char_withcolor:
|
||||
push {r4, lr}
|
||||
mov r12, #0xC0000000
|
||||
ldr r12, [r12, #0x10]
|
||||
adr r4, font
|
||||
add r4, r0, lsl #4
|
||||
mov r3, #0x10
|
||||
ldr r0, [r9, #s_hw_color]
|
||||
movs r0, r0
|
||||
bne display_char_16bpp
|
||||
display_char_4bpp:
|
||||
add r12, r1, lsl #2
|
||||
2: ldrb r1, [r4], #1
|
||||
mvn r0, r2
|
||||
1: ror r0, #24
|
||||
lsrs r1, #1
|
||||
eorcs r0, #0x0F
|
||||
lsrs r1, #1
|
||||
eorcs r0, #0xF0
|
||||
adds r3, #0x40000000
|
||||
bcc 1b
|
||||
str r0, [r12], #160
|
||||
subs r3, #1
|
||||
bne 2b
|
||||
pop {r4, pc}
|
||||
display_char_16bpp:
|
||||
add r12, r1, lsl #4
|
||||
2: ldrb r1, [r4], #1
|
||||
lsl r1, #24
|
||||
1: mvn r0, #0
|
||||
lsls r1, #1
|
||||
andcs r0, r0, lsl #16
|
||||
lsls r1, #1
|
||||
andcs r0, r0, lsr #16
|
||||
eor r0, r2
|
||||
str r0, [r12], #4
|
||||
adds r3, #0x40000000
|
||||
bcc 1b
|
||||
add r12, #640 - 16
|
||||
subs r3, #1
|
||||
bne 2b
|
||||
pop {r4, pc}
|
||||
@ r0 = string
|
||||
@ r1 = start position
|
||||
@ r2 = end position
|
||||
@ r3 = color (0 = normal, -1 = reverse)
|
||||
.globl display_string
|
||||
display_string:
|
||||
push {r4-r7, lr}
|
||||
mov r4, r0
|
||||
mov r5, r1
|
||||
mov r6, r2
|
||||
mov r7, r3
|
||||
|
||||
1: ldrb r0, [r4]
|
||||
movs r0, r0
|
||||
addne r4, #1
|
||||
mov r1, r5
|
||||
add r5, #1
|
||||
mov r2, r7
|
||||
bl display_char_withcolor
|
||||
cmp r5, r6
|
||||
bcc 1b
|
||||
pop {r4-r7, pc}
|
||||
font:
|
||||
.incbin "font.bin"
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
//#define DEBUG
|
||||
|
||||
//#define TRACE bl trace
|
||||
#define TRACE
|
||||
|
||||
#ifdef DEBUG
|
||||
.macro ERROR code
|
||||
bkpt #\code
|
||||
.endm
|
||||
.macro CERROR cond, code
|
||||
b\cond .+8
|
||||
b .+8
|
||||
bkpt #\code
|
||||
.endm
|
||||
#else
|
||||
.macro ERROR code
|
||||
b exit_emulator
|
||||
.endm
|
||||
.macro CERROR cond, code
|
||||
b\cond exit_emulator
|
||||
.endm
|
||||
#endif
|
||||
|
||||
// System calls
|
||||
#define e_fopen 0
|
||||
#define e_fread 1
|
||||
#define e_fwrite 2
|
||||
#define e_fclose 3
|
||||
#define e_malloc 5
|
||||
#define e_free 6
|
||||
#define e_memset 7
|
||||
#define e_NU_Get_First 23
|
||||
#define e_NU_Get_Next 24
|
||||
#define e_NU_Done 25
|
||||
#define e_strcpy 27
|
||||
#define e_touchpad_read 75
|
||||
#define e_touchpad_write 76
|
||||
|
||||
// Global data structure: r9 points to this at all times
|
||||
// Note some requirements:
|
||||
// * to do "ldr reg, [r9, #s_xxx]", offset must be below 0x1000
|
||||
// * to do "add reg, r9, #s_xxx", offset must be representable as a shifted byte
|
||||
// When changing an existing offset, don't forget to "del *.o"!
|
||||
|
||||
#define s_wram 0x0000 // 0800 bytes
|
||||
|
||||
// CPU data
|
||||
#define s_mem_map 0x0800 // 0024 bytes (9 entries, 4 bytes each)
|
||||
#define s_flags_di 0x0824 // 0004 bytes
|
||||
#define s_pc_base 0x0828 // 0004 bytes
|
||||
#define s_interrupts 0x082C
|
||||
#define s_irq_from_apu 0x082C
|
||||
#define s_irq_from_mapper 0x082D
|
||||
#define s_nmi_reset 0x082F // set to FF for reset, 80 for nmi
|
||||
|
||||
#define s_input_status 0x0840
|
||||
#define s_input_queue 0x0844
|
||||
|
||||
// PPU ("Picture Processing Unit") data
|
||||
#define s_ppu_mem_map 0x0880 // 0040 bytes
|
||||
#define s_ppu_palette 0x08C0 // 0020 bytes
|
||||
#define s_ppu_flags 0x08E0
|
||||
#define s_ppu_control s_ppu_flags+0 // $2000
|
||||
#define s_ppu_mask s_ppu_flags+1 // $2001
|
||||
#define s_ppu_status s_ppu_flags+2 // $2002
|
||||
#define s_ppu_oam_addr 0x08E4 // $2003
|
||||
#define s_ppu_scroll 0x08E8 // $2005 (x/y toggle in bit 16, x fine in bits 29-31)
|
||||
#define s_ppu_address 0x08EC // $2006
|
||||
#define s_ppu_data 0x08F0 // $2007 buffer
|
||||
#define s_ppu_scanline 0x08F4
|
||||
#define s_ppu_oam_ram 0x0900 // 0100 bytes - must be 256-byte aligned
|
||||
|
||||
// Emulation data (no relation to anything in an actual NES)
|
||||
#define s_frame_count 0x0A00
|
||||
#define s_frame_count_rtc 0x0A04
|
||||
#define s_saved_sp 0x0A08
|
||||
#define s_frame_timer 0x0A0C
|
||||
#define s_touchpad_size 0x0A10
|
||||
#define s_keypad_command_map 0x0A14
|
||||
#define s_keypad_read_input 0x0A18
|
||||
#define s_command_keys_pressed 0x0A1C
|
||||
#define s_saved_irq_mask 0x0A20
|
||||
#define s_saved_irq_handler 0x0A24
|
||||
#define s_frameskip 0x0A28
|
||||
#define s_frameskip_cur 0x0A2C
|
||||
#define s_spr_loc_table_valid 0x0A30
|
||||
#define s_hw_irq_masks 0x0A34
|
||||
#define s_hw_irq_handler 0x0A38
|
||||
#define s_hw_keypad_invert 0x0A3C
|
||||
#define s_palette_cache 0x0A40 // 0080 bytes
|
||||
#define s_palette_cache_valid 0x0AC0
|
||||
#define s_hw_color 0x0AC4
|
||||
#define s_border_color 0x0AC8
|
||||
#define s_message_timer 0x0AD0
|
||||
|
||||
// ROM data
|
||||
#define s_rom_header 0x0AE0
|
||||
#define s_mapper 0x0AEC
|
||||
#define s_prg_size 0x0AF0
|
||||
#define s_prg_ptr 0x0AF4
|
||||
#define s_chr_size 0x0AF8
|
||||
#define s_chr_ptr 0x0AFC
|
||||
|
||||
#define s_mapper_state 0x0B00 // 0010 bytes
|
||||
|
||||
#define s_path_filename 0x0B80
|
||||
#define s_path_extension 0x0B84
|
||||
|
||||
// The big stuff
|
||||
#define s_spr_loc_table 0x0B90 // 0870 bytes (9 * 240)
|
||||
#define s_name_table_ram 0x1400 // 1000 bytes - must be 128-byte aligned
|
||||
#define s_sram 0x2400 // 2000 bytes
|
||||
#define s_path 0x4400 // 0200 bytes
|
||||
|
||||
#define s_SIZE 0x4600
|
||||
#define s_ALIGN 0x0100
|
||||
|
||||
// CPU register usage:
|
||||
// r0 = general purpose temporary
|
||||
// r1 = next instruction byte
|
||||
// r2 = address low byte or full address
|
||||
// r3 = address high byte
|
||||
#define cpu_pc r4
|
||||
#define cpu_cycles r5
|
||||
#define cpu_a r6
|
||||
#define cpu_x r7
|
||||
#define cpu_y r8
|
||||
#define cpu_sp r10
|
||||
#define cpu_flags r11
|
||||
#define cpu_itable r12
|
||||
|
||||
#define CPU_CYCLE_LENGTH 3
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,589 @@
|
|||
#include "nes.inc"
|
||||
|
||||
.globl ppu_next_scanline
|
||||
ppu_next_scanline:
|
||||
push {r4-r8, r10-r11, lr}
|
||||
|
||||
ldr r10, [r9, #s_ppu_flags]
|
||||
|
||||
ldr r0, [r9, #s_ppu_scanline]
|
||||
|
||||
add r0, r0, #1
|
||||
|
||||
cmp r0, #-1 @ Vblank is over
|
||||
biceq r10, r10, #0xC00000
|
||||
|
||||
cmp r0, #241 @ Vblank is starting
|
||||
bne 1f
|
||||
mov r0, #-21
|
||||
orr r10, r10, #0x800000
|
||||
and r2, r10, #0x80 @ NMI enabled?
|
||||
strb r2, [r9, #s_nmi_reset]
|
||||
1:
|
||||
|
||||
str r0, [r9, #s_ppu_scanline]
|
||||
|
||||
cmp r0, #240
|
||||
bcs rendering_off
|
||||
|
||||
tst r10, #0x1800
|
||||
beq 1f
|
||||
cmp r0, #0
|
||||
@ Line 0: Move to (X,Y) position specified in s_ppu_scroll
|
||||
@ Lines 1-239: Move back to X position specified in s_ppu_scroll
|
||||
ldr r1, [r9, #s_ppu_address]
|
||||
ldr r0, [r9, #s_ppu_scroll]
|
||||
moveq r2, #0xFF00
|
||||
addeq r2, r2, #0x00FF
|
||||
movne r2, #0x0400
|
||||
addne r2, r2, #0x001F
|
||||
and r0, r0, r2
|
||||
bic r1, r1, r2
|
||||
orr r1, r1, r0
|
||||
str r1, [r9, #s_ppu_address]
|
||||
1:
|
||||
|
||||
ldr r0, [r9, #s_frameskip_cur]
|
||||
cmp r0, #0
|
||||
bne frameskipped
|
||||
|
||||
@ Allocate scanline pixel buffer
|
||||
sub sp, sp, #272
|
||||
|
||||
@ Step I: Render background
|
||||
tst r10, #0x0800
|
||||
beq background_disabled
|
||||
mov r1, r10, lsl #8
|
||||
and r1, r1, #0x1000
|
||||
|
||||
ldr r2, [r9, #s_ppu_address]
|
||||
and r8, r2, #0x1F
|
||||
add r1, r1, r2, lsr #12
|
||||
bl get_name_table_pointers
|
||||
|
||||
mov r11, #33
|
||||
ldr r6, =0x08040201
|
||||
ldr r7, =0xEEEEEEEE
|
||||
draw_chr:
|
||||
@ Get attribute for this block
|
||||
ldrb r12, [r0, r8, lsr #2]
|
||||
|
||||
@ Get character
|
||||
ldrb r3, [r2, r8]
|
||||
|
||||
tst r2, #0x40 @ Assuming nametable is 128-byte aligned
|
||||
movne r12, r12, lsr #4
|
||||
tst r8, #0x02
|
||||
moveq r12, r12, lsl #2
|
||||
and r12, r12, #0x0C
|
||||
orr r12, r12, #0x03
|
||||
orr r12, r12, lsl #8
|
||||
orr r12, r12, lsl #16
|
||||
|
||||
@ Get pixels for appropriate row of character
|
||||
add r4, r1, r3, lsl #4
|
||||
mov r3, r4, lsr #10
|
||||
add r3, r9, r3, lsl #2
|
||||
ldr r3, [r3, #s_ppu_mem_map]
|
||||
ldrb r3, [r4, r3]! @ Low plane
|
||||
ldrb r4, [r4, #8] @ High plane
|
||||
|
||||
@ Move to next character over
|
||||
add r8, r8, #1
|
||||
cmp r8, #0x20
|
||||
bleq swap_name_table
|
||||
|
||||
@ Unpack each pixel into a nybble
|
||||
mul r3, r6, r3
|
||||
mul r4, r6, r4
|
||||
orr r3, r7, r3, lsr #3
|
||||
orr r4, r7, r4, lsr #3
|
||||
and r4, r3, r4, ror #31
|
||||
|
||||
@ Unpack nybbles into bytes
|
||||
and r3, r12, r4, lsr #4
|
||||
and r4, r12, r4
|
||||
stmia sp!, {r3-r4}
|
||||
|
||||
subs r11, r11, #1
|
||||
bne draw_chr
|
||||
sub sp, sp, #264
|
||||
|
||||
@ Blank out left 8 pixels
|
||||
tst r10, #0x0200
|
||||
bne background_done
|
||||
ldr r0, [r9, #s_ppu_scroll]
|
||||
mov r3, #0
|
||||
add r0, sp, r0, lsr #29
|
||||
strb r3, [r0]
|
||||
strb r3, [r0, #1]
|
||||
strb r3, [r0, #2]
|
||||
strb r3, [r0, #3]
|
||||
strb r3, [r0, #4]
|
||||
strb r3, [r0, #5]
|
||||
strb r3, [r0, #6]
|
||||
strb r3, [r0, #7]
|
||||
background_done:
|
||||
|
||||
ldr r0, [r9, #s_ppu_scroll]
|
||||
add sp, sp, r0, lsr #29
|
||||
|
||||
@ Step II: Sprites
|
||||
tst r10, #0x1000
|
||||
beq no_sprites
|
||||
|
||||
@ Get sprite height (minus 1)
|
||||
tst r10, #0x0020
|
||||
moveq r12, #7
|
||||
movne r12, #15
|
||||
|
||||
ldrb r1, [r9, #s_spr_loc_table_valid]
|
||||
movs r1, r1
|
||||
bleq refresh_spr_loc_table
|
||||
|
||||
ldr r0, [r9, #s_ppu_scanline]
|
||||
|
||||
add r2, r9, #s_spr_loc_table
|
||||
add r2, r2, r0, lsl #3
|
||||
ldrb r1, [r2, r0]!
|
||||
|
||||
cmp r1, #0
|
||||
beq no_sprites
|
||||
|
||||
@ Only up to 8 sprites are actually stored in the table
|
||||
cmp r1, #8
|
||||
movcs r1, #8
|
||||
|
||||
sub r0, r0, #1
|
||||
sprite_loop:
|
||||
ldrb r4, [r2, #1]!
|
||||
add r8, r9, #s_ppu_oam_ram
|
||||
ldr r4, [r8, r4]
|
||||
|
||||
@ Get offset from the top
|
||||
and r6, r4, #0xFF
|
||||
sub r5, r0, r6
|
||||
|
||||
bl fetch_sprite_bits
|
||||
|
||||
@ Get palette
|
||||
mov r8, r4, lsr #14
|
||||
and r8, r8, #0x0C
|
||||
orr r8, r8, #0x30
|
||||
|
||||
tst r4, #0x400000
|
||||
movne r5, r5, lsl #7
|
||||
moveq r3, #31
|
||||
movne r3, #1
|
||||
|
||||
mov r6, #8
|
||||
add r11, sp, r4, lsr #24
|
||||
tst r4, #0x200000
|
||||
bne sprite_draw_low_pri
|
||||
sprite_draw_high_pri:
|
||||
ldrb lr, [r11], #1
|
||||
and r7, r5, #0x80
|
||||
orr r7, r8, r7, lsr #7
|
||||
tst r5, #0x8000
|
||||
addne r7, r7, #2
|
||||
tst r7, #3
|
||||
beq 1f
|
||||
tst lr, #0x20
|
||||
streqb r7, [r11, #-1]
|
||||
1: mov r5, r5, ror r3
|
||||
subs r6, r6, #1
|
||||
bne sprite_draw_high_pri
|
||||
b sprite_next
|
||||
sprite_draw_low_pri:
|
||||
ldrb lr, [r11], #1
|
||||
and r7, r5, #0x80
|
||||
orr r7, r8, r7, lsr #7
|
||||
tst r5, #0x8000
|
||||
addne r7, r7, #2
|
||||
tst r7, #3
|
||||
beq 1f
|
||||
tst lr, #3
|
||||
orrne r7, lr, #0x20
|
||||
strb r7, [r11, #-1]
|
||||
1: mov r5, r5, ror r3
|
||||
subs r6, r6, #1
|
||||
bne sprite_draw_low_pri
|
||||
sprite_next:
|
||||
subs r1, r1, #1
|
||||
bne sprite_loop
|
||||
no_sprites:
|
||||
|
||||
@ Step III: Draw to screen
|
||||
ldrb r0, [r9, #s_message_timer]
|
||||
ldr r8, [r9, #s_ppu_scanline]
|
||||
movs r0, r0
|
||||
movne r0, #16
|
||||
cmp r8, r0
|
||||
addcc sp, #256
|
||||
bcc draw_done
|
||||
|
||||
mov r4, #0xC0000000
|
||||
ldr r4, [r4, #0x10]
|
||||
add r5, r9, #s_palette_cache
|
||||
|
||||
ldr r0, [r9, #s_hw_color]
|
||||
ldrb r1, [r9, #s_palette_cache_valid]
|
||||
movs r0, r0
|
||||
bne draw_color
|
||||
|
||||
movs r1, r1
|
||||
bleq refresh_palette_cache_bw
|
||||
|
||||
@ Draw in black and white
|
||||
add r8, r8, r8, lsl #2
|
||||
add r4, r4, r8, lsl #5
|
||||
add r4, r4, #0x10
|
||||
|
||||
mov r8, #256
|
||||
1: ldrb r0, [sp], #1
|
||||
ldrb r1, [sp], #1
|
||||
ldrb r2, [sp], #1
|
||||
ldrb r3, [sp], #1
|
||||
ldrb r0, [r5, r0]
|
||||
ldrb r1, [r5, r1]
|
||||
ldrb r2, [r5, r2]
|
||||
ldrb r3, [r5, r3]
|
||||
orr r0, r1, r0, lsl #4
|
||||
orr r0, r0, r2, lsl #12
|
||||
orr r0, r0, r3, lsl #8
|
||||
strh r0, [r4], #2
|
||||
subs r8, r8, #4
|
||||
bne 1b
|
||||
|
||||
b draw_done
|
||||
draw_color:
|
||||
movs r1, r1
|
||||
bleq refresh_palette_cache_color
|
||||
|
||||
add r8, r8, r8, lsl #2
|
||||
add r4, r4, r8, lsl #7
|
||||
add r4, r4, #0x40
|
||||
|
||||
mov r8, #256
|
||||
1: ldrb r0, [sp], #1
|
||||
ldrb r1, [sp], #1
|
||||
ldrb r2, [sp], #1
|
||||
ldrb r3, [sp], #1
|
||||
add r0, r0
|
||||
add r1, r1
|
||||
add r2, r2
|
||||
add r3, r3
|
||||
ldrh r0, [r5, r0]
|
||||
ldrh r1, [r5, r1]
|
||||
ldrh r2, [r5, r2]
|
||||
ldrh r3, [r5, r3]
|
||||
orr r0, r1, lsl #16
|
||||
str r0, [r4], #4
|
||||
orr r2, r3, lsl #16
|
||||
str r2, [r4], #4
|
||||
subs r8, r8, #4
|
||||
bne 1b
|
||||
|
||||
|
||||
draw_done:
|
||||
ldr r0, [r9, #s_ppu_scroll]
|
||||
add sp, sp, #16
|
||||
sub sp, sp, r0, lsr #29
|
||||
|
||||
frameskipped:
|
||||
@ Check for sprite 0 hit
|
||||
@ (TODO: should only occur when two opaque pixels collide)
|
||||
tst r10, #0x1000
|
||||
beq sprite_0_done
|
||||
tst r10, #0x0020
|
||||
moveq r12, #7
|
||||
movne r12, #15
|
||||
ldr r0, [r9, #s_ppu_scanline]
|
||||
ldr r4, [r9, #s_ppu_oam_ram]
|
||||
sub r0, r0, #1
|
||||
and r6, r4, #0xFF
|
||||
sub r5, r0, r6
|
||||
cmp r5, r12
|
||||
bls sprite_0_check
|
||||
sprite_0_done:
|
||||
|
||||
tst r10, #0x1800
|
||||
beq rendering_off
|
||||
@ Move down by one pixel
|
||||
ldr r1, [r9, #s_ppu_address]
|
||||
add r1, r1, #0x1000
|
||||
cmp r1, #0x8000
|
||||
bcc 1f
|
||||
bic r1, r1, #0x8000
|
||||
add r1, r1, #0x0020
|
||||
ands r2, r1, #0x03E0
|
||||
subeq r1, r1, #0x0400 @ If Y wraps 31->0, no name table change
|
||||
cmp r2, #0x03C0
|
||||
eoreq r1, r1, #0x0BC0 @ If Y wraps 29->0, name table change
|
||||
1: str r1, [r9, #s_ppu_address]
|
||||
|
||||
rendering_off:
|
||||
str r10, [r9, #s_ppu_flags]
|
||||
ldr r0, [r9, #s_ppu_scanline]
|
||||
cmp r0, #-21
|
||||
bleq newframe
|
||||
pop {r4-r8, r10-r11, pc}
|
||||
|
||||
.pool
|
||||
|
||||
swap_name_table:
|
||||
ldr r2, [r9, #s_ppu_address]
|
||||
mov r8, #0
|
||||
eor r2, r2, #0x0400
|
||||
get_name_table_pointers:
|
||||
and r2, r2, #0x0FE0
|
||||
orr r2, r2, #0x2000
|
||||
mov r5, r2, lsr #10
|
||||
add r5, r9, r5, lsl #2
|
||||
ldr r5, [r5, #s_ppu_mem_map]
|
||||
|
||||
and r0, r2, #0xFC00
|
||||
orr r0, r0, r2, lsr #4
|
||||
orr r0, r0, #0x03C0
|
||||
bic r0, r0, #7
|
||||
|
||||
add r2, r5, r2
|
||||
add r0, r5, r0
|
||||
|
||||
bx lr
|
||||
|
||||
background_disabled:
|
||||
@ Weird NES behavior: if rendering is completely disabled (both BG and sprite),
|
||||
@ and PPUADDR points inside palette, draw that color.
|
||||
@ Otherwise, just draw color 0
|
||||
tst r10, #0x1800
|
||||
bne 1f
|
||||
ldr r0, [r9, #s_ppu_address]
|
||||
ands lr, r0, #0x3F00
|
||||
and r0, r0, #0x1F
|
||||
orr r0, r0, #0x20 @ use alternate palette (doesn't map $04,$08,$0C -> $00)
|
||||
orr r0, r0, r0, lsl #8
|
||||
orr r0, r0, r0, lsl #16
|
||||
cmp lr, #0x3F00
|
||||
1: movne r0, #0
|
||||
|
||||
mov r11, #264
|
||||
1: subs r11, r11, #4
|
||||
str r0, [sp, r11]
|
||||
bne 1b
|
||||
b background_done
|
||||
|
||||
sprite_0_check:
|
||||
bl fetch_sprite_bits
|
||||
movs r5, r5
|
||||
orrne r10, r10, #0x400000
|
||||
b sprite_0_done
|
||||
|
||||
fetch_sprite_bits:
|
||||
@ Vertical flip
|
||||
tst r4, #0x800000
|
||||
rsbne r5, r5, r12
|
||||
|
||||
@ Get CHR address
|
||||
tst r10, #0x0020
|
||||
moveq r8, r10, lsl #9
|
||||
movne r8, r4, lsl #4
|
||||
and r8, r8, #0x1000
|
||||
moveq r6, #0xFF
|
||||
movne r6, #0xFE
|
||||
and r6, r6, r4, lsr #8
|
||||
addne r6, r6, r5, lsr #3
|
||||
and r5, r5, #7
|
||||
|
||||
add r6, r8, r6, lsl #4
|
||||
mov r8, r6, lsr #10
|
||||
add r8, r9, r8, lsl #2
|
||||
ldr r8, [r8, #s_ppu_mem_map]
|
||||
add r6, r8, r6
|
||||
|
||||
ldrb r5, [r6, r5]! @ low plane
|
||||
ldrb r6, [r6, #8] @ high plane
|
||||
|
||||
orr r5, r5, r6, lsl #8
|
||||
|
||||
@ Check if sprite needs to be clipped against left edge of screen
|
||||
cmp r4, #0x08000000
|
||||
bxcs lr
|
||||
clip_sprite:
|
||||
tst r10, #0x0400
|
||||
bxne lr
|
||||
mov r3, r4, lsr #24
|
||||
add r3, pc, r3, lsl #2
|
||||
ldr r3, [r3, #sprite_clip_table - (.+4)]
|
||||
tst r4, #0x400000
|
||||
biceq r5, r5, r3
|
||||
bicne r5, r5, r3, lsr #16
|
||||
bx lr
|
||||
sprite_clip_table:
|
||||
.word 0xFFFFFFFF
|
||||
.word 0x7F7FFEFE
|
||||
.word 0x3F3FFCFC
|
||||
.word 0x1F1FF8F8
|
||||
.word 0x0F0FF0F0
|
||||
.word 0x0707E0E0
|
||||
.word 0x0303C0C0
|
||||
.word 0x01018080
|
||||
|
||||
refresh_spr_loc_table:
|
||||
@ Start by clearing the table (0 sprites for every scanline)
|
||||
add r1, r9, #s_spr_loc_table
|
||||
mov r2, #0
|
||||
mov r3, #240
|
||||
1: strb r2, [r1], #9
|
||||
subs r3, r3, #1
|
||||
bne 1b
|
||||
|
||||
add r2, r9, #s_spr_loc_table
|
||||
add r2, r2, #9
|
||||
|
||||
@ Loop over each sprite
|
||||
add r0, r9, #s_ppu_oam_ram
|
||||
sub r0, r0, #4
|
||||
mov r6, #64
|
||||
spr_loc_loop1:
|
||||
@ Get first scanline (minus one) of sprite
|
||||
ldrb r3, [r0, #4]!
|
||||
|
||||
rsbs r4, r3, #239 @ Number of visible scanlines
|
||||
bls spr_loc_done
|
||||
cmp r4, r12
|
||||
addhi r4, r12, #1
|
||||
|
||||
add r1, r2, r3, lsl #3
|
||||
add r1, r1, r3
|
||||
|
||||
@ Loop over each scanline this sprite is in,
|
||||
@ appending the sprite index to each one's list
|
||||
spr_loc_loop2:
|
||||
ldrb r5, [r1]
|
||||
add r5, r5, #1
|
||||
cmp r5, #8
|
||||
strlsb r0, [r1, r5] @ Assuming OAM is 256-byte aligned
|
||||
strb r5, [r1], #9
|
||||
subs r4, r4, #1
|
||||
bne spr_loc_loop2
|
||||
spr_loc_done:
|
||||
subs r6, r6, #1
|
||||
bne spr_loc_loop1
|
||||
|
||||
mov r0, #1
|
||||
strb r0, [r9, #s_spr_loc_table_valid]
|
||||
bx lr
|
||||
|
||||
refresh_palette_cache_bw:
|
||||
adr r0, nes_color_to_gray_table
|
||||
add r1, r9, #s_ppu_palette
|
||||
add r2, r5, #0x40
|
||||
mov r3, #0x1F
|
||||
1: ldrb r6, [r1, r3]
|
||||
ldrb r6, [r0, r6]
|
||||
strb r6, [r2, #-1]!
|
||||
subs r3, r3, #1
|
||||
bpl 1b
|
||||
mov r3, #0x1F
|
||||
1: tst r3, #0x03
|
||||
ldreqb r6, [r1]
|
||||
ldrneb r6, [r1, r3]
|
||||
ldrb r6, [r0, r6]
|
||||
strb r6, [r2, #-1]!
|
||||
subs r3, r3, #1
|
||||
bpl 1b
|
||||
strb r3, [r9, #s_palette_cache_valid]
|
||||
bx lr
|
||||
|
||||
refresh_palette_cache_color:
|
||||
adr r0, nes_color_to_rgb_table
|
||||
add r1, r9, #s_ppu_palette
|
||||
add r2, r5, #0x80
|
||||
mov r3, #0x1F
|
||||
1: ldrb r6, [r1, r3]
|
||||
add r6, r6
|
||||
ldrh r6, [r0, r6]
|
||||
strh r6, [r2, #-2]!
|
||||
subs r3, r3, #1
|
||||
bpl 1b
|
||||
mov r3, #0x1F
|
||||
1: tst r3, #0x03
|
||||
ldreqb r6, [r1]
|
||||
ldrneb r6, [r1, r3]
|
||||
add r6, r6
|
||||
ldrh r6, [r0, r6]
|
||||
strh r6, [r2, #-2]!
|
||||
subs r3, r3, #1
|
||||
bpl 1b
|
||||
strb r3, [r9, #s_palette_cache_valid]
|
||||
bx lr
|
||||
|
||||
.globl invert_colors
|
||||
invert_colors:
|
||||
adr r0, nes_color_to_gray_table
|
||||
mov r2, #64
|
||||
1: subs r2, r2, #1
|
||||
ldrb r1, [r0, r2]
|
||||
eor r1, r1, #0x0F
|
||||
strb r1, [r0, r2]
|
||||
bne 1b
|
||||
adr r0, nes_color_to_rgb_table
|
||||
mov r2, #128
|
||||
1: subs r2, r2, #4
|
||||
ldr r1, [r0, r2]
|
||||
mvn r1, r1
|
||||
str r1, [r0, r2]
|
||||
bne 1b
|
||||
strb r2, [r9, #s_palette_cache_valid]
|
||||
bx lr
|
||||
|
||||
nes_color_to_gray_table:
|
||||
.byte 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0
|
||||
.byte 10, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0
|
||||
.byte 15,10,10,10,10,10,10,10,10,10,10,10,10, 5, 0, 0
|
||||
.byte 15,13,13,13,13,13,13,13,13,13,13,13,13,11, 0, 0
|
||||
|
||||
@ .byte 7, 3, 4, 4, 5, 6, 6, 4, 3, 3, 4, 3, 3, 0, 0, 0
|
||||
@ .byte 11, 7, 6, 7, 7, 8, 8, 8, 7, 7, 8, 7, 7, 0, 0, 0
|
||||
@ .byte 15,10, 9, 8,11,11,10,11,12,10,11,12,12, 7, 0, 0
|
||||
@ .byte 15,13,13,13,13,13,12,13,14,14,13,14,14,12, 0, 0
|
||||
|
||||
nes_color_to_rgb_table:
|
||||
.hword 0x73ae,0x20d1,0x0015,0x4013,0x880e,0xa802,0xa000,0x7840
|
||||
.hword 0x4160,0x0220,0x0280,0x01e2,0x19eb,0x0000,0x0000,0x0000
|
||||
.hword 0xbdf7,0x039d,0x21dd,0x801e,0xb817,0xe00b,0xd940,0xca61
|
||||
.hword 0x8b80,0x04a0,0x0540,0x0487,0x0411,0x0000,0x0000,0x0000
|
||||
.hword 0xffff,0x3dff,0x5cbf,0x445f,0xf3df,0xfbb6,0xfbac,0xfcc7
|
||||
.hword 0xf5e7,0x8682,0x4ee9,0x5fd3,0x075b,0x7bcf,0x0000,0x0000
|
||||
.hword 0xffff,0xaf3f,0xc6bf,0xd65f,0xfe3f,0xfe3b,0xfdf6,0xfed5
|
||||
.hword 0xff34,0xe7f4,0xaf97,0xb7f9,0x9ffe,0xc638,0x0000,0x0000
|
||||
|
||||
.globl toggle_border
|
||||
toggle_border:
|
||||
ldr r0, [r9, #s_border_color]
|
||||
mvn r0, r0
|
||||
str r0, [r9, #s_border_color]
|
||||
.globl clear_screen
|
||||
clear_screen:
|
||||
ldr r1, [r9, #s_border_color]
|
||||
mov r0, #0xC0000000
|
||||
ldr r0, [r0, #0x10]
|
||||
ldr r2, [r9, #s_hw_color]
|
||||
movs r2, r2
|
||||
mov r2, #0x9600
|
||||
lslne r2, #2
|
||||
1: str r1, [r0], #4
|
||||
subs r2, #4
|
||||
bne 1b
|
||||
bx lr
|
||||
|
||||
.globl display_ingame_message
|
||||
display_ingame_message:
|
||||
mov r1, #60
|
||||
strb r1, [r9, #s_message_timer]
|
||||
mov r1, #4
|
||||
mov r2, #36
|
||||
ldr r3, [r9, #s_border_color]
|
||||
mvn r3, r3
|
||||
b display_string
|
||||
|
|
@ -0,0 +1,609 @@
|
|||
#include "nes.inc"
|
||||
|
||||
map_prg_32kB:
|
||||
ldr r1, [r9, #s_prg_size]
|
||||
ldr r2, [r9, #s_prg_ptr]
|
||||
and r0, r1, r0, lsl #15
|
||||
add r0, r0, r2
|
||||
map_prg_32kB_from_pointer:
|
||||
sub r0, r0, #0x8000
|
||||
str r0, [r9, #s_mem_map + 16] @ 8000
|
||||
str r0, [r9, #s_mem_map + 20] @ A000
|
||||
str r0, [r9, #s_mem_map + 24] @ C000
|
||||
str r0, [r9, #s_mem_map + 28] @ E000
|
||||
bx lr
|
||||
map_prg_16kB_to_8000:
|
||||
ldr r1, [r9, #s_prg_size]
|
||||
ldr r2, [r9, #s_prg_ptr]
|
||||
and r0, r1, r0, lsl #14
|
||||
add r0, r0, r2
|
||||
sub r0, r0, #0x8000
|
||||
str r0, [r9, #s_mem_map + 16] @ 8000
|
||||
str r0, [r9, #s_mem_map + 20] @ A000
|
||||
bx lr
|
||||
map_prg_16kB_to_C000:
|
||||
ldr r1, [r9, #s_prg_size]
|
||||
ldr r2, [r9, #s_prg_ptr]
|
||||
and r0, r1, r0, lsl #14
|
||||
add r0, r0, r2
|
||||
sub r0, r0, #0xC000
|
||||
str r0, [r9, #s_mem_map + 24] @ C000
|
||||
str r0, [r9, #s_mem_map + 28] @ E000
|
||||
bx lr
|
||||
|
||||
map_prg_8kB:
|
||||
ldr r2, [r9, #s_prg_size]
|
||||
ldr r3, [r9, #s_prg_ptr]
|
||||
and r0, r2, r0, lsl #13
|
||||
add r0, r0, r3
|
||||
sub r0, r0, r1
|
||||
add r1, r9, r1, lsr #11
|
||||
str r0, [r1, #s_mem_map]
|
||||
bx lr
|
||||
|
||||
|
||||
map_chr_8kB:
|
||||
ldr r2, [r9, #s_chr_size]
|
||||
ldr r3, [r9, #s_chr_ptr]
|
||||
and r0, r2, r0, lsl #13
|
||||
add r0, r0, r3
|
||||
str r0, [r9, #s_ppu_mem_map + 0x00] @ 0000
|
||||
str r0, [r9, #s_ppu_mem_map + 0x04] @ 0400
|
||||
str r0, [r9, #s_ppu_mem_map + 0x08] @ 0800
|
||||
str r0, [r9, #s_ppu_mem_map + 0x0C] @ 0C00
|
||||
str r0, [r9, #s_ppu_mem_map + 0x10] @ 1000
|
||||
str r0, [r9, #s_ppu_mem_map + 0x14] @ 1400
|
||||
str r0, [r9, #s_ppu_mem_map + 0x18] @ 1800
|
||||
str r0, [r9, #s_ppu_mem_map + 0x1C] @ 1C00
|
||||
bx lr
|
||||
map_chr_4kB_to_0000:
|
||||
mov r1, #0x0000
|
||||
b map_chr_4kB
|
||||
map_chr_4kB_to_1000:
|
||||
mov r1, #0x1000
|
||||
map_chr_4kB:
|
||||
ldr r2, [r9, #s_chr_size]
|
||||
ldr r3, [r9, #s_chr_ptr]
|
||||
and r0, r2, r0, lsl #12
|
||||
add r0, r0, r3
|
||||
sub r0, r0, r1
|
||||
add r1, r9, r1, lsr #8
|
||||
str r0, [r1, #s_ppu_mem_map + 0x00] @ +0000
|
||||
str r0, [r1, #s_ppu_mem_map + 0x04] @ +0400
|
||||
str r0, [r1, #s_ppu_mem_map + 0x08] @ +0800
|
||||
str r0, [r1, #s_ppu_mem_map + 0x0C] @ +0C00
|
||||
bx lr
|
||||
map_chr_2kB:
|
||||
ldr r2, [r9, #s_chr_size]
|
||||
ldr r3, [r9, #s_chr_ptr]
|
||||
and r0, r2, r0, lsl #10
|
||||
add r0, r0, r3
|
||||
sub r0, r0, r1
|
||||
add r1, r9, r1, lsr #8
|
||||
str r0, [r1, #s_ppu_mem_map + 0x00] @ +0000
|
||||
str r0, [r1, #s_ppu_mem_map + 0x04] @ +0400
|
||||
bx lr
|
||||
map_chr_1kB:
|
||||
ldr r2, [r9, #s_chr_size]
|
||||
ldr r3, [r9, #s_chr_ptr]
|
||||
and r0, r2, r0, lsl #10
|
||||
add r0, r0, r3
|
||||
sub r0, r0, r1
|
||||
add r1, r9, r1, lsr #8
|
||||
str r0, [r1, #s_ppu_mem_map]
|
||||
bx lr
|
||||
|
||||
mirror_1screen_lo:
|
||||
add r0, r9, #s_name_table_ram - 0x2000
|
||||
b mirror_1screen
|
||||
mirror_1screen_hi:
|
||||
add r0, r9, #s_name_table_ram + 0x0400 - 0x2000
|
||||
mirror_1screen:
|
||||
str r0, [r9, #s_ppu_mem_map + 0x20] @ 2000
|
||||
sub r0, r0, #0x0400
|
||||
str r0, [r9, #s_ppu_mem_map + 0x24] @ 2400
|
||||
sub r0, r0, #0x0400
|
||||
str r0, [r9, #s_ppu_mem_map + 0x28] @ 2800
|
||||
sub r0, r0, #0x0400
|
||||
str r0, [r9, #s_ppu_mem_map + 0x2C] @ 2C00
|
||||
sub r0, r0, #0x0400
|
||||
str r0, [r9, #s_ppu_mem_map + 0x30] @ 3000
|
||||
sub r0, r0, #0x0400
|
||||
str r0, [r9, #s_ppu_mem_map + 0x34] @ 3400
|
||||
sub r0, r0, #0x0400
|
||||
str r0, [r9, #s_ppu_mem_map + 0x38] @ 3800
|
||||
sub r0, r0, #0x0400
|
||||
str r0, [r9, #s_ppu_mem_map + 0x3C] @ 3C00
|
||||
bx lr
|
||||
|
||||
mirror_vert:
|
||||
add r0, r9, #s_name_table_ram - 0x2000
|
||||
str r0, [r9, #s_ppu_mem_map + 0x20] @ 2000
|
||||
str r0, [r9, #s_ppu_mem_map + 0x24] @ 2400
|
||||
add r0, r9, #s_name_table_ram - 0x2800
|
||||
str r0, [r9, #s_ppu_mem_map + 0x28] @ 2800
|
||||
str r0, [r9, #s_ppu_mem_map + 0x2C] @ 2C00
|
||||
add r0, r9, #s_name_table_ram - 0x3000
|
||||
str r0, [r9, #s_ppu_mem_map + 0x30] @ 3000
|
||||
str r0, [r9, #s_ppu_mem_map + 0x34] @ 3400
|
||||
add r0, r9, #s_name_table_ram - 0x3800
|
||||
str r0, [r9, #s_ppu_mem_map + 0x38] @ 3800
|
||||
str r0, [r9, #s_ppu_mem_map + 0x3C] @ 3C00
|
||||
bx lr
|
||||
|
||||
mirror_horiz:
|
||||
add r0, r9, #s_name_table_ram - 0x2000
|
||||
str r0, [r9, #s_ppu_mem_map + 0x20] @ 2000
|
||||
add r0, r9, #s_name_table_ram - 0x2400
|
||||
str r0, [r9, #s_ppu_mem_map + 0x24] @ 2400
|
||||
str r0, [r9, #s_ppu_mem_map + 0x28] @ 2800
|
||||
add r0, r9, #s_name_table_ram - 0x2800
|
||||
str r0, [r9, #s_ppu_mem_map + 0x2C] @ 2C00
|
||||
add r0, r9, #s_name_table_ram - 0x3000
|
||||
str r0, [r9, #s_ppu_mem_map + 0x30] @ 2000
|
||||
add r0, r9, #s_name_table_ram - 0x3400
|
||||
str r0, [r9, #s_ppu_mem_map + 0x34] @ 2400
|
||||
str r0, [r9, #s_ppu_mem_map + 0x38] @ 2800
|
||||
add r0, r9, #s_name_table_ram - 0x3800
|
||||
str r0, [r9, #s_ppu_mem_map + 0x3C] @ 2C00
|
||||
bx lr
|
||||
|
||||
mirror_4screen:
|
||||
add r0, r9, #s_name_table_ram - 0x2000
|
||||
str r0, [r9, #s_ppu_mem_map + 0x20] @ 2000
|
||||
str r0, [r9, #s_ppu_mem_map + 0x24] @ 2400
|
||||
str r0, [r9, #s_ppu_mem_map + 0x28] @ 2800
|
||||
str r0, [r9, #s_ppu_mem_map + 0x2C] @ 2C00
|
||||
add r0, r9, #s_name_table_ram - 0x3000
|
||||
str r0, [r9, #s_ppu_mem_map + 0x30] @ 3000
|
||||
str r0, [r9, #s_ppu_mem_map + 0x34] @ 3400
|
||||
str r0, [r9, #s_ppu_mem_map + 0x38] @ 3800
|
||||
str r0, [r9, #s_ppu_mem_map + 0x3C] @ 3C00
|
||||
bx lr
|
||||
|
||||
.globl load_rom
|
||||
load_rom:
|
||||
push {r4-r11, lr}
|
||||
|
||||
adr r1, file_mode
|
||||
swi e_fopen
|
||||
movs r4, r0
|
||||
moveq r5, #error_open - error_messages
|
||||
beq error
|
||||
|
||||
@ Read ROM header
|
||||
add r0, r9, #s_rom_header
|
||||
mov r1, #16
|
||||
mov r2, #1
|
||||
mov r3, r4
|
||||
swi e_fread
|
||||
mov r5, #error_bad_header - error_messages
|
||||
movs r0, r0
|
||||
beq error_fclose
|
||||
ldr r1, [r9, #s_rom_header]
|
||||
ldr r2, =0x1A53454E
|
||||
cmp r1, r2
|
||||
bne error_fclose
|
||||
|
||||
ldrb r5, [r9, #s_rom_header + 4] @ Number of PRG-ROM banks
|
||||
movs r5, r5, lsl #14
|
||||
moveq r5, #0x400000
|
||||
sub r0, r5, #1
|
||||
str r0, [r9, #s_prg_size]
|
||||
|
||||
ldrb r7, [r9, #s_rom_header + 5] @ Number of CHR-ROM banks
|
||||
movs r6, r7, lsl #13
|
||||
moveq r6, #0x2000
|
||||
sub r0, r6, #1
|
||||
str r0, [r9, #s_chr_size]
|
||||
|
||||
add r0, r5, r6
|
||||
swi e_malloc
|
||||
movs r0, r0
|
||||
moveq r5, #error_no_memory - error_messages
|
||||
beq error_fclose
|
||||
str r0, [r9, #s_prg_ptr]
|
||||
add r0, r5
|
||||
str r0, [r9, #s_chr_ptr]
|
||||
|
||||
@ Clear CHR-RAM if present
|
||||
movs r1, r7
|
||||
moveq r2, #0x2000
|
||||
moveq r6, #0
|
||||
swieq e_memset
|
||||
|
||||
@ Read PRG-ROM and CHR-ROM from file
|
||||
ldr r0, [r9, #s_prg_ptr]
|
||||
add r1, r5, r6
|
||||
mov r2, #1
|
||||
mov r3, r4
|
||||
swi e_fread
|
||||
movs r0, r0
|
||||
moveq r5, #error_rom_read - error_messages
|
||||
beq error_fclose
|
||||
|
||||
mov r0, r4
|
||||
swi e_fclose
|
||||
|
||||
@ Initialize CPU memory map
|
||||
@ RAM
|
||||
str r9, [r9, #s_mem_map + 0] @ 0000
|
||||
@ SRAM
|
||||
add r1, r9, #s_sram - 0x6000
|
||||
str r1, [r9, #s_mem_map + 12] @ 6000
|
||||
@ ROM low
|
||||
mov r0, #0
|
||||
bl map_prg_16kB_to_8000
|
||||
@ ROM high
|
||||
mov r0, #-1
|
||||
bl map_prg_16kB_to_C000
|
||||
@ RAM wraparound
|
||||
sub r1, r9, #0x10000
|
||||
str r1, [r9, #s_mem_map + 32] @ 10000
|
||||
|
||||
@ Initialize PPU memory map
|
||||
mov r0, #0
|
||||
bl map_chr_8kB
|
||||
|
||||
@ Name table
|
||||
ldrb r6, [r9, #s_rom_header + 6]
|
||||
adr lr, 1f
|
||||
tst r6, #8
|
||||
bne mirror_4screen
|
||||
tst r6, #1
|
||||
bne mirror_vert
|
||||
beq mirror_horiz
|
||||
1:
|
||||
|
||||
@ Get low 4 bits of mapper number
|
||||
mov r0, r6, lsr #4
|
||||
@ Get high 4 bits of mapper number (unless it looks like
|
||||
@ there's junk in the header, in which case ignore them)
|
||||
ldr r1, [r9, #s_rom_header + 12]
|
||||
movs r1, r1
|
||||
ldreqb r1, [r9, #s_rom_header + 7]
|
||||
andeq r1, r1, #0xF0
|
||||
orreq r0, r0, r1
|
||||
|
||||
adr r1, mapper_table
|
||||
adr r2, mapper_table_end
|
||||
1: cmp r2, r1
|
||||
moveq r5, #error_bad_mapper - error_messages
|
||||
beq error
|
||||
ldrh r5, [r2, #-2]!
|
||||
ldrh r4, [r2, #-2]!
|
||||
cmp r0, r4
|
||||
bne 1b
|
||||
add r0, r1, r5
|
||||
str r0, [r9, #s_mapper]
|
||||
|
||||
mov r0, #0
|
||||
pop {r4-r11, pc}
|
||||
.pool
|
||||
|
||||
file_mode:
|
||||
.string "rb"
|
||||
error_open:
|
||||
.string "couldn't open file"
|
||||
error_bad_header:
|
||||
.string "not an NES file"
|
||||
error_no_memory:
|
||||
.string "not enough memory"
|
||||
error_rom_read:
|
||||
.string "couldn't read ROM"
|
||||
error_bad_mapper:
|
||||
.string "unimplemented mapper"
|
||||
.align 4
|
||||
|
||||
error_fclose:
|
||||
mov r0, r4
|
||||
swi e_fclose
|
||||
error:
|
||||
ldr r0, [r9, #s_prg_ptr]
|
||||
swi e_free
|
||||
mov r0, #0
|
||||
str r0, [r9, #s_prg_ptr]
|
||||
error_messages = .+8
|
||||
add r0, pc, r5
|
||||
pop {r4-r11, pc}
|
||||
|
||||
mapper_table:
|
||||
.macro MAPPER n, addr; .hword \n, \addr - mapper_table; .endm
|
||||
MAPPER 0, mapper_NROM
|
||||
MAPPER 1, mapper_MMC1
|
||||
MAPPER 2, mapper_UxROM
|
||||
MAPPER 3, mapper_CNROM
|
||||
MAPPER 4, mapper_MMC3
|
||||
MAPPER 7, mapper_AxROM
|
||||
MAPPER 11, mapper_Color_Dreams
|
||||
MAPPER 34, mapper_BxROM
|
||||
MAPPER 66, mapper_GxROM
|
||||
MAPPER 228, mapper_Action_Enterprises
|
||||
mapper_table_end:
|
||||
|
||||
mapper_NROM:
|
||||
bx lr
|
||||
|
||||
#define s_mmc1_shift_reg (s_mapper_state)
|
||||
#define s_mmc1_control (s_mapper_state+4)
|
||||
#define s_mmc1_chr0 (s_mapper_state+5)
|
||||
#define s_mmc1_chr1 (s_mapper_state+6)
|
||||
#define s_mmc1_prg (s_mapper_state+7)
|
||||
mapper_MMC1:
|
||||
push {lr}
|
||||
ldrb r3, [r9, #s_mmc1_shift_reg]
|
||||
tst r0, #0x80
|
||||
bne mmc1_reset
|
||||
and r0, r0, #1
|
||||
movs r3, r3, lsr #1
|
||||
orr r0, r3, r0, lsl #4
|
||||
strb r0, [r9, #s_mmc1_shift_reg]
|
||||
popcc {pc}
|
||||
and r2, r2, #0x6000
|
||||
add r2, r9, r2, lsr #13
|
||||
strb r0, [r2, #s_mmc1_control]
|
||||
b mmc1_update
|
||||
mmc1_reset:
|
||||
ldrb r0, [r9, #s_mmc1_control]
|
||||
orr r0, r0, #0x0C
|
||||
strb r0, [r9, #s_mmc1_control]
|
||||
mmc1_update:
|
||||
mov r0, #0x10
|
||||
strb r0, [r9, #s_mmc1_shift_reg]
|
||||
|
||||
ldrb r3, [r9, #s_mmc1_control]
|
||||
adr lr, 1f
|
||||
and r3, r3, #3
|
||||
add pc, pc, r3, lsl #2
|
||||
nop
|
||||
b mirror_1screen_lo
|
||||
b mirror_1screen_hi
|
||||
b mirror_vert
|
||||
b mirror_horiz
|
||||
1:
|
||||
|
||||
@ Update CHR
|
||||
ldrb r3, [r9, #s_mmc1_control]
|
||||
ldrb r0, [r9, #s_mmc1_chr0]
|
||||
tst r3, #0x10
|
||||
bne 1f
|
||||
mov r0, r0, lsr #1
|
||||
bl map_chr_8kB
|
||||
b 2f
|
||||
1: bl map_chr_4kB_to_0000
|
||||
ldrb r0, [r9, #s_mmc1_chr1]
|
||||
bl map_chr_4kB_to_1000
|
||||
2:
|
||||
|
||||
@ Update PRG
|
||||
ldrb r3, [r9, #s_mmc1_control]
|
||||
ldrb r0, [r9, #s_mmc1_prg]
|
||||
@ Mode 0-1
|
||||
tst r3, #0x08
|
||||
bne 1f
|
||||
mov r0, r0, lsr #1
|
||||
bl map_prg_32kB
|
||||
pop {pc}
|
||||
1:
|
||||
|
||||
@ Mode 2
|
||||
tst r3, #0x04
|
||||
bne 1f
|
||||
bl map_prg_16kB_to_C000
|
||||
mov r0, #0
|
||||
bl map_prg_16kB_to_8000
|
||||
pop {pc}
|
||||
@ Mode 3
|
||||
1: bl map_prg_16kB_to_8000
|
||||
mov r0, #-1
|
||||
bl map_prg_16kB_to_C000
|
||||
pop {pc}
|
||||
|
||||
mapper_UxROM:
|
||||
b map_prg_16kB_to_8000
|
||||
|
||||
mapper_CNROM:
|
||||
b map_chr_8kB
|
||||
|
||||
#define s_mmc3_bank (s_mapper_state)
|
||||
#define s_mmc3_bank_select (s_mapper_state+8)
|
||||
#define s_mmc3_counter_reload (s_mapper_state+9)
|
||||
#define s_mmc3_counter (s_mapper_state+10)
|
||||
#define s_mmc3_counter_reset (s_mapper_state+11)
|
||||
#define s_mmc3_irq_enabled (s_mapper_state+12)
|
||||
mapper_MMC3:
|
||||
and r1, r2, #0x6000
|
||||
and r2, r2, #0x0001
|
||||
orr r2, r2, r1, lsr #12
|
||||
add pc, pc, r2, lsl #2
|
||||
nop
|
||||
b mmc3_bank_select
|
||||
b mmc3_bank_data
|
||||
b mmc3_mirroring
|
||||
bx lr
|
||||
b mmc3_irq_latch
|
||||
b mmc3_irq_reload
|
||||
b mmc3_irq_disable
|
||||
b mmc3_irq_enable
|
||||
mmc3_bank_select:
|
||||
strb r0, [r9, #s_mmc3_bank_select]
|
||||
b mmc3_update
|
||||
mmc3_bank_data:
|
||||
ldrb r1, [r9, #s_mmc3_bank_select]
|
||||
and r3, r1, #7
|
||||
add r3, r3, r9
|
||||
strb r0, [r3, #s_mmc3_bank]
|
||||
b mmc3_update
|
||||
mmc3_mirroring:
|
||||
@ Don't do anything if game uses 4-screen mirroring
|
||||
ldrb r3, [r9, #s_rom_header+6]
|
||||
tst r3, #8
|
||||
bxne lr
|
||||
tst r0, #1
|
||||
beq mirror_vert
|
||||
b mirror_horiz
|
||||
mmc3_irq_latch:
|
||||
strb r0, [r9, #s_mmc3_counter_reload]
|
||||
bx lr
|
||||
mmc3_irq_reload:
|
||||
mov r0, #0xFF
|
||||
strb r0, [r9, #s_mmc3_counter_reset]
|
||||
bx lr
|
||||
mmc3_irq_disable:
|
||||
mov r0, #0
|
||||
strb r0, [r9, #s_irq_from_mapper]
|
||||
mmc3_irq_enable:
|
||||
and r0, r2, #1
|
||||
strb r0, [r9, #s_mmc3_irq_enabled]
|
||||
bx lr
|
||||
.globl mmc3_scanline
|
||||
mmc3_scanline:
|
||||
ldrb r0, [r9, #s_mmc3_counter]
|
||||
|
||||
ldrb r1, [r9, #s_mmc3_counter_reset]
|
||||
bic r1, r0, r1
|
||||
subs r1, r1, #1
|
||||
ldrmib r1, [r9, #s_mmc3_counter_reload]
|
||||
strb r1, [r9, #s_mmc3_counter]
|
||||
mov r2, #0
|
||||
strb r2, [r9, #s_mmc3_counter_reset]
|
||||
|
||||
cmp r1, #0
|
||||
bxne lr
|
||||
cmp r0, #0
|
||||
bxeq lr
|
||||
|
||||
ldrb r0, [r9, #s_mmc3_irq_enabled]
|
||||
strb r0, [r9, #s_irq_from_mapper]
|
||||
bx lr
|
||||
mmc3_update:
|
||||
push {r4, r5, lr}
|
||||
ldrb r5, [r9, #s_mmc3_bank_select]
|
||||
|
||||
mov r4, #0x1000
|
||||
and r4, r4, r5, lsl #5
|
||||
ldrb r0, [r9, #s_mmc3_bank+0]; eor r1, r4, #0x0000; bl map_chr_2kB
|
||||
ldrb r0, [r9, #s_mmc3_bank+1]; eor r1, r4, #0x0800; bl map_chr_2kB
|
||||
ldrb r0, [r9, #s_mmc3_bank+2]; eor r1, r4, #0x1000; bl map_chr_1kB
|
||||
ldrb r0, [r9, #s_mmc3_bank+3]; eor r1, r4, #0x1400; bl map_chr_1kB
|
||||
ldrb r0, [r9, #s_mmc3_bank+4]; eor r1, r4, #0x1800; bl map_chr_1kB
|
||||
ldrb r0, [r9, #s_mmc3_bank+5]; eor r1, r4, #0x1C00; bl map_chr_1kB
|
||||
|
||||
mov r4, #0x4000
|
||||
and r4, r4, r5, lsl #8
|
||||
ldrb r0, [r9, #s_mmc3_bank+6]; eor r1, r4, #0x8000; bl map_prg_8kB
|
||||
ldrb r0, [r9, #s_mmc3_bank+7]; mov r1, #0xA000; bl map_prg_8kB
|
||||
mov r0, #-2; eor r1, r4, #0xC000; bl map_prg_8kB
|
||||
|
||||
pop {r4, r5, pc}
|
||||
|
||||
mapper_AxROM:
|
||||
push {r4, lr}
|
||||
mov r4, r0
|
||||
tst r4, #0x10
|
||||
bleq mirror_1screen_lo
|
||||
blne mirror_1screen_hi
|
||||
mov r0, r4
|
||||
pop {r4, lr}
|
||||
b map_prg_32kB
|
||||
|
||||
mapper_Color_Dreams:
|
||||
push {r4, lr}
|
||||
mov r4, r0
|
||||
bl map_prg_32kB
|
||||
mov r0, r4, lsr #4
|
||||
pop {r4, lr}
|
||||
b map_chr_8kB
|
||||
|
||||
mapper_BxROM:
|
||||
b map_prg_32kB
|
||||
|
||||
mapper_GxROM:
|
||||
push {r4, lr}
|
||||
mov r4, r0
|
||||
bl map_chr_8kB
|
||||
mov r0, r4, lsr #4
|
||||
pop {r4, lr}
|
||||
b map_prg_32kB
|
||||
|
||||
mapper_Action_Enterprises:
|
||||
push {r4, lr}
|
||||
mov r4, r2
|
||||
and r0, r0, #3
|
||||
orr r0, r0, r2, lsl #2
|
||||
bl map_chr_8kB
|
||||
mov r0, r4, lsr #7
|
||||
and r0, r0, #0x3F
|
||||
mov r0, r0, lsl #15
|
||||
ldr r1, [r9, #s_prg_size]
|
||||
ldr r2, [r9, #s_prg_ptr]
|
||||
cmp r0, r1
|
||||
andhi r0, r0, r1
|
||||
add r0, r0, r2
|
||||
bl map_prg_32kB_from_pointer
|
||||
bl mirror_vert
|
||||
pop {r4, pc}
|
||||
|
||||
.globl sram_load
|
||||
sram_load:
|
||||
ldrb r0, [r9, #s_rom_header+6]
|
||||
tst r0, #0x02
|
||||
bxeq lr
|
||||
push {r4, lr}
|
||||
ldr r0, [r9, #s_path_extension]
|
||||
adr r1, save_ext
|
||||
swi e_strcpy
|
||||
add r0, r9, #s_path
|
||||
adr r1, save_read_mode
|
||||
swi e_fopen
|
||||
movs r4, r0
|
||||
popeq {r4, pc}
|
||||
add r0, r9, #s_sram
|
||||
mov r1, #0x2000
|
||||
mov r2, #1
|
||||
mov r3, r4
|
||||
swi e_fread
|
||||
mov r0, r4
|
||||
swi e_fclose
|
||||
pop {r4, pc}
|
||||
.globl sram_save
|
||||
sram_save:
|
||||
push {r4-r5, lr}
|
||||
ldrb r0, [r9, #s_rom_header+6]
|
||||
tst r0, #0x02
|
||||
adreq r5, no_saves
|
||||
beq 1f
|
||||
ldr r0, [r9, #s_path_extension]
|
||||
adr r1, save_ext
|
||||
swi e_strcpy
|
||||
add r0, r9, #s_path
|
||||
adr r1, save_write_mode
|
||||
swi e_fopen
|
||||
adr r5, save_error
|
||||
movs r4, r0
|
||||
beq 1f
|
||||
add r0, r9, #s_sram
|
||||
mov r1, #0x2000
|
||||
mov r2, #1
|
||||
mov r3, r4
|
||||
swi e_fwrite
|
||||
movs r0, r0
|
||||
adrne r5, save_success
|
||||
mov r0, r4
|
||||
swi e_fclose
|
||||
1: mov r0, r5
|
||||
pop {r4-r5, lr}
|
||||
b display_ingame_message
|
||||
save_ext:
|
||||
.string "sav.tns"
|
||||
save_read_mode:
|
||||
.string "rb"
|
||||
save_write_mode:
|
||||
.string "wb"
|
||||
no_saves:
|
||||
.string "Game has no save memory"
|
||||
save_error:
|
||||
.string "File error while saving"
|
||||
save_success:
|
||||
.string "Saved"
|
||||
.align 4
|
||||
Loading…
Reference in New Issue