2024-05-18 18:43:54 +01:00
# include " n e s . i n c "
.string " PRG"
.globl main
main :
push { r4 - r11 , l r }
@ Allocate the state data structure from the stack and zero it out
mov r5 , s p
sub s p , s p , #s _ S I Z E
bic s p , s p , #s _ A L I G N - 1
mov r9 , s p
2024-05-19 12:41:06 +01:00
sub s p , s p , #s _ S I Z E / / R e s e r v e s p a c e f o r s t a t e A D D E D
2024-05-18 18:43:54 +01:00
2024-05-20 21:32:41 +01:00
2024-05-18 18:43:54 +01:00
mov r4 , #0
mov r6 , #s _ S I Z E
1 : subs r6 , r6 , #4
str r4 , [ r9 , r6 ]
bne 1 b
str r5 , [ r9 , #s _ s a v e d _ s p ]
2024-05-20 21:32:41 +01:00
2024-05-18 18:43:54 +01:00
@ Get our folder path
add r4 , r9 , #s _ p a t h
mov r2 , r4
movs r0 , r0 @ argc
ldrne r1 , [ r1 ] @ argv[0]
movnes r1 , r1
beq 2 f
1 : ldrb r0 , [ r1 ] , #1
strb r0 , [ r4 ] , #1
teq r0 , #' \ '
teqne r0 , #' / '
moveq r2 , r4
movs r0 , r0
bne 1 b
2 : str r2 , [ r9 , #s _ p a t h _ f i l e n a m e ]
@ Check hardware type
ldr r0 , =0x900A0000
ldr r0 , [ r0 ]
bic r0 , #0xFF000000
cmp r0 , #0x10
bne 1 f
@ Non-CX
mov r0 , #0xDC000000
add r0 , #0x08
adr r1 , i n t e r r u p t _ h a n d l e r _ n o n c x
mvn r2 , #0
mov r3 , #0
mov r4 , #3
b 2 f
1 :
sub r0 , #0x100
cmp r0 , #0x001
bne u n k n o w n _ h a r d w a r e
@ CX
mov r0 , #0xDC000000
add r0 , #0x10
adr r1 , i n t e r r u p t _ h a n d l e r _ c x
mov r2 , #0
mov r3 , #1
mov r4 , #1
2 :
str r0 , [ r9 , #s _ h w _ i r q _ m a s k s ]
str r1 , [ r9 , #s _ h w _ i r q _ h a n d l e r ]
str r2 , [ r9 , #s _ h w _ k e y p a d _ i n v e r t ]
str r3 , [ r9 , #s _ h w _ c o l o r ]
str r4 , [ r9 , #s _ f r a m e s k i p ]
bl i n i t _ i n t e r r u p t s
bl i n i t _ k e y p a d
bl t o g g l e _ b o r d e r
bl r o m _ m e n u
bl c l e a r _ s c r e e n
@ Set CPU to power-on state
mov c p u _ a , #0
mov c p u _ x , #0
mov c p u _ y , #0
mov c p u _ s p , #0x100 @ RESET will bring this to 0x1FD
mov c p u _ f l a g s , #0
@ Start CPU emulation
b r e s e t
.globl exit_emulator
exit_emulator :
ldr r0 , [ r9 , #s _ p r g _ p t r ]
swi e _ f r e e
bl r e s t o r e _ i n t e r r u p t s
unknown_hardware :
ldr s p , [ r9 , #s _ s a v e d _ s p ]
pop { r4 - r11 , p c }
init_interrupts :
str r9 , [ p c , #s t a t e _ p t r - ( . + 8 ) ]
msr c p s r _ c , #0xD3 @ Interrupts off
@ Disable everything except the timer interrupt (IRQ 19)
ldr r0 , [ r9 , #s _ h w _ i r q _ m a s k s ]
ldr r2 , [ r0 ]
str r2 , [ r9 , #s _ s a v e d _ i r q _ m a s k ]
str r2 , [ r0 , #4 ]
mov r2 , #1 < < 1 9
str r2 , [ r0 ]
@ Set the IRQ vector
mov r1 , #0xA4000000
ldr r2 , [ r1 , #0x38 ]
str r2 , [ r9 , #s _ s a v e d _ i r q _ h a n d l e r ]
ldr r2 , [ r9 , #s _ h w _ i r q _ h a n d l e r ]
str r2 , [ r1 , #0x38 ]
msr c p s r _ c , #0x13 @ Interrupts on
bx l r
interrupt_handler_cx :
push { r0 - r1 , l r }
ldr r0 , =0x900D0000
mov r1 , #1
str r1 , [ r0 , #0x0C ]
b i n t e r r u p t _ h a n d l e r _ c o m m o n
interrupt_handler_noncx :
push { r0 - r1 , l r }
mov r0 , #0xDC000000
ldr r1 , =0x900A0000
ldr l r , [ r0 , #0x24 ]
ldr l r , [ r0 , #0x28 ]
mov l r , #1
str l r , [ r1 , #0x20 ]
mov l r , #1 < < 1 9
str l r , [ r0 , #0x04 ]
mov l r , #8
str l r , [ r0 , #0x2C ]
interrupt_handler_common :
@ Advance the frame timer by 3/300 of a second
ldr r1 , [ p c , #s t a t e _ p t r - ( . + 8 ) ]
ldrb l r , [ r1 , #s _ f r a m e _ t i m e r ]
add l r , l r , #3
strb l r , [ r1 , #s _ f r a m e _ t i m e r ]
pop { r0 - r1 , l r }
subs p c , l r , #4
.pool
state_ptr :
.word 0
restore_interrupts :
msr c p s r _ c , #0xD3 @ Interrupts off
ldr r0 , [ r9 , #s _ h w _ i r q _ m a s k s ]
mvn r2 , #0
str r2 , [ r0 , #4 ]
ldr r2 , [ r9 , #s _ s a v e d _ i r q _ m a s k ]
str r2 , [ r0 ]
mov r1 , #0xA4000000
ldr r2 , [ r9 , #s _ s a v e d _ i r q _ h a n d l e r ]
str r2 , [ r1 , #0x38 ]
bx l r
.globl newframe
newframe :
str l r , [ s p , #- 4 ] !
ldrb r0 , [ r9 , #s _ m e s s a g e _ t i m e r ]
subs r0 , #1
strplb r0 , [ r9 , #s _ m e s s a g e _ t i m e r ]
ldr r0 , [ r9 , #s _ f r a m e s k i p _ c u r ]
ldr r10 , [ r9 , #s _ f r a m e s k i p ]
subs r0 , r0 , #1
addmi r0 , r0 , r10
str r0 , [ r9 , #s _ f r a m e s k i p _ c u r ]
mov r8 , #0
pause_loop :
2024-05-19 12:41:06 +01:00
# define n u m _ c o m m a n d _ k e y s 1 4
2024-05-18 18:43:54 +01:00
@ Scan keypad
ldr r3 , [ r9 , #s _ h w _ k e y p a d _ i n v e r t ]
mov r4 , #0
ldr r5 , =0x900E0010
mov r6 , #n u m _ c o m m a n d _ k e y s - 1
ldr r7 , [ r9 , #s _ k e y p a d _ c o m m a n d _ m a p ]
1 : ldrb r0 , [ r7 , r6 ]
and r1 , r0 , #0x60
ldr r1 , [ r5 , r1 , l s r #3 ]
eor r1 , r3 , r1 , r o r r0
and r1 , #1
orr r4 , r1 , l s l r6
subs r6 , #1
bpl 1 b
ldr r5 , [ r9 , #s _ c o m m a n d _ k e y s _ p r e s s e d ]
str r4 , [ r9 , #s _ c o m m a n d _ k e y s _ p r e s s e d ]
bic r5 , r4 , r5
tst r5 , #1 < < 0 ; movne r10, #1
tst r5 , #1 < < 1 ; movne r10, #2
2024-05-19 12:41:06 +01:00
tst r5 , #1 < < 2 ; movne r10, #3
tst r5 , #1 < < 3 ; movne r10, #4
2024-05-18 18:43:54 +01:00
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 < < 1 0 ; blne sram_save @ S (Save SRAM)
tst r4 , #1 < < 1 1 ; bne fast_forward @ *
2024-05-19 12:41:06 +01:00
tst r5 , #1 < < 1 2 ; blne save_state
tst r5 , #1 < < 1 3 ; blne load_state
2024-05-18 18:43:54 +01:00
@ Keep looping until the frame timer reaches 5/300 (1/60) of a second
ldrb r0 , [ r9 , #s _ f r a m e _ t i m e r ]
subs r0 , r0 , #5
movcc r0 , #0
mcrcc p15 , 0 , r0 , c7 , c0 , 4
bcc p a u s e _ l o o p
strb r0 , [ r9 , #s _ f r a m e _ t i m e r ]
# ifdef D E B U G
bl f p s _ c o u n t e r
# endif
movs r8 , r8
bne p a u s e _ l o o p
fast_forward :
str r10 , [ r9 , #s _ f r a m e s k i p ]
mov l r , p c
ldr p c , [ r9 , #s _ k e y p a d _ r e a d _ i n p u t ]
str r0 , [ r9 , #s _ i n p u t _ s t a t u s ]
ldr p c , [ s p ] , #4
init_keypad :
str l r , [ s p , #- 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 - 0 x40
adrcc r2 , t o u c h p a d _ c o m m a n d _ m a p
adrcs r2 , c l i c k p a d _ c o m m a n d _ m a p
str r2 , [ r9 , #s _ k e y p a d _ c o m m a n d _ m a p ]
adrcc r2 , t o u c h p a d _ r e a d _ i n p u t
adrcs r2 , c l i c k p a d _ r e a d _ i n p u t
str r2 , [ r9 , #s _ k e y p a d _ r e a d _ i n p u t ]
ldrcs p c , [ s p ] , #4
mov r0 , #0xFF
mov r1 , #0xFF
adr r2 , t o u c h p a d _ i n f o _ p a g e
swi e _ t o u c h p a d _ w r i t e
mov r0 , #0x04
mov r1 , #0x07
add r2 , r9 , #s _ t o u c h p a d _ s i z e
swi e _ t o u c h p a d _ r e a d
mov r0 , #0xFF
mov r1 , #0xFF
adr r2 , t o u c h p a d _ m a i n _ p a g e
swi e _ t o u c h p a d _ w r i t e
ldr p c , [ s p ] , #4
touchpad_info_page :
.byte 0x10
touchpad_main_page :
.byte 0x04
2024-05-19 12:41:06 +01:00
@ 1 2 3 4 5 6 B P Q R S * ^ x^2
2024-05-18 18:43:54 +01:00
clickpad_command_map :
2024-05-19 12:41:06 +01:00
.byte 0 x1 7 ,0 x15 ,0 x13 ,0 x27 ,0 x25 ,0 x23 ,0 x64 ,0 x28 ,0 x26 ,0 x24 ,0 x22 ,0 x31 ,0 x49 ,0 x39
2024-05-18 18:43:54 +01:00
touchpad_command_map :
2024-05-19 12:41:06 +01:00
.byte 0 x1 7 ,0 x64 ,0 x13 ,0 x27 ,0 x56 ,0 x23 ,0 x45 ,0 x22 ,0 x21 ,0 x20 ,0 x16 ,0 x48 ,0 x49 ,0 x29
2024-05-18 18:43:54 +01:00
.align 4
clickpad_read_input :
2024-05-19 12:41:06 +01:00
@ r2 = 0xFFFFFF00
2024-05-18 18:43:54 +01:00
mvn r2 , #0xFF
2024-05-19 12:41:06 +01:00
@Load keypad state
@ loads address into r0
@ Each bit in the 900E0010-900E001F registers represents a key. Only bits 0 to 10 are used in each halfword. The mapping depends on the currently used keypad.
2024-05-18 18:43:54 +01:00
ldr r0 , =0x900E0000
2024-05-19 12:41:06 +01:00
@ loads double word in (adress + 0x18) into r0
ldrd r0 , [ r0 , #0x18 ]
@invert
2024-05-18 18:43:54 +01:00
ldr r3 , [ r9 , #s _ h w _ k e y p a d _ i n v e r t ]
eor r0 , r3
eor r1 , r3
2024-05-19 12:41:06 +01:00
@key mapping
2024-05-18 18:43:54 +01:00
tst r0 , #1 < < 2 5 ; 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 < < 1 6 ; orrne r2, r2, #0x10 @ Up
tst r1 , #1 < < 1 8 ; orrne r2, r2, #0x80 @ Right
tst r1 , #1 < < 2 0 ; orrne r2, r2, #0x20 @ Down
tst r1 , #1 < < 2 2 ; orrne r2, r2, #0x40 @ Left
tst r1 , #1 < < 2 4 ; orrne r2, r2, #0x04 @ Clear (Select)
mov r0 , r2
bx l r
touchpad_read_input :
push { r4 , l r }
mvn r4 , #0xFF
ldr r0 , =0x900E0000
ldrd r0 , [ r0 , #0x18 ]
ldr r2 , [ r9 , #s _ h w _ k e y p a d _ i n v e r t ]
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 < < 2 5 ; orrne r4, r4, #0x04 @ Clear (Select)
tst r1 , #1 < < 2 4 ; orrne r4, r4, #0x08 @ Caps (Start)
sub s p , #0x0C
mov r0 , #0x02
mov r1 , #0x0A
add r2 , s p , #0x02
swi e _ t o u c h p a d _ r e a d
movs r0 , r0
beq 1 f
ldrb r0 , [ s p , #0x0A ]
tst r0 , #0x01
beq 1 f
ldrb r0 , [ s p , #0x02 ]
ldrb r1 , [ s p , #0x03 ]
ldrb r2 , [ r9 , #s _ t o u c h p a d _ s i z e ]
ldrb r3 , [ r9 , #s _ t o u c h p a d _ s i z e + 1 ]
orr r0 , r1 , r0 , l s l #8
orr r2 , r3 , r2 , l s l #8
add r0 , r0 , l s l #1
cmp r0 , r2 ; orrcc r4, r4, #0x40 @ Left
cmp r0 , r2 , l s l #1 ; orrcs r4, r4, #0x80 @ Right
ldrb r0 , [ s p , #0x04 ]
ldrb r1 , [ s p , #0x05 ]
ldrb r2 , [ r9 , #s _ t o u c h p a d _ s i z e + 2 ]
ldrb r3 , [ r9 , #s _ t o u c h p a d _ s i z e + 3 ]
orr r0 , r1 , r0 , l s l #8
orr r2 , r3 , r2 , l s l #8
add r0 , r0 , l s l #1
cmp r0 , r2 ; orrcc r4, r4, #0x20 @ Down
cmp r0 , r2 , l s l #1 ; orrcs r4, r4, #0x10 @ Up
1 :
add s p , #0x0C
mov r0 , r4
pop { r4 , p c }
.pool
save_state :
str l r , s a v e d _ s t a t e _ c p u _ s t a t u s + 5 6
adr l r , s a v e d _ s t a t e _ c p u _ s t a t u s
stm l r , { r0 - r13 }
mrs r0 , c p s r
str r0 , s a v e d _ s t a t e _ c p u _ c p s r
mov r1 , #s _ S I Z E
sub r2 , r9 , #s _ S I Z E
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 s s t a t e _ l o o p
mov r0 , #1
str r0 , s a v e _ s t a t e _ e x i s t s
ldr l r , s a v e d _ s t a t e _ c p u _ s t a t u s + 5 6 / / s u b r o u t i n e r e t u r n
load_state :
ldr r0 , s a v e _ s t a t e _ e x i s t s
cmp r0 , #0
moveq p c ,l r
mov r1 , #s _ S I Z E
sub r3 , r9 , #s _ S I Z E
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 l s t a t e _ l o o p
ldr r0 , s a v e d _ s t a t e _ c p u _ c p s r
msr c p s r _ a l l , r0
adr l r , s a v e d _ s t a t e _ c p u _ s t a t u s
ldm l r , { r0 - r13 ,p c } / / r e t u r n s t o p r e v i o u s i n s t r u c t i o n a f t e r s a v e _ s t a t e
save_state_exists : .word 0
saved_state_cpu_status :
.rept 15
.word 0
.endr
saved_state_cpu_cpsr :
.word 0
.pool