; ARM assembly tutorial for Asmonite, example 10: functions (Raspberry Pi Pico 2, Arm Cortex-M33).
; Adapted from the ARM-ASM-Tutorial by Niklas Guertler (examples OffsetAddressing and BlinkFunctions);
; see LICENSE.txt.
;
; "bl name" (branch with link) jumps to a function and puts the address behind it into lr; "bx lr" jumps
; back there. A function that calls another one must save lr first ("push {lr}") and take it back at the
; end ("pop {pc}" puts it straight into pc: that is the return). The LED blinks 5 times, then the program
; waits in "b $". F8 (Step over) runs a whole call; F7 goes into it.
.target pico2
.arch thumb
.imagebase 0x20000000

RESETS_BASE         equ 0x40020000
RESETS_RESET_DONE   equ 0x08
RESET_BITS          equ (1 << 6) | (1 << 9)     ; IO_BANK0, PADS_BANK0
IO_BANK0_BASE       equ 0x40028000
GPIO25_CTRL         equ 0xcc
FUNCSEL_SIO         equ 5
PADS_BANK0_BASE     equ 0x40038000
PADS_GPIO25         equ 0x68
PAD_ISO             equ 1 << 8
SIO_BASE            equ 0xd0000000
SIO_GPIO_OUT_SET    equ 0x18
SIO_GPIO_OUT_CLR    equ 0x20
SIO_GPIO_OE_SET     equ 0x38
ALIAS_CLR           equ 0x3000
DELAY_COUNT         equ 1000000

_start:
    bl   enable_gpio            ; IO_BANK0 and PADS_BANK0 out of reset
    bl   configure_led          ; GPIO 25: SIO, output
    movs r5, #5                 ; r5 = number of flashes
    bl   blink
    b    $

; ---- blink r5 times; changes r0 .. r5 ----
blink:
    push {lr}                   ; blink calls delay: keep its own return address
    ldr  r0, lit_sio
    ldr  r1, lit_led_bit
    ldr  r3, lit_delay_value
blink_loop:
    str  r1, [r0, #SIO_GPIO_OUT_SET]
    bl   delay
    str  r1, [r0, #SIO_GPIO_OUT_CLR]
    bl   delay
    subs r5, r5, #1
    bne  blink_loop
    pop  {pc}                   ; return

; ---- take IO_BANK0 and PADS_BANK0 out of reset; changes r0 .. r2 ----
enable_gpio:
    ldr  r1, lit_resets_clr
    ldr  r0, lit_reset_bits
    str  r0, [r1]               ; RESETS.RESET through its clear alias
    ldr  r1, lit_resets
enable_wait:
    ldr  r2, [r1, #RESETS_RESET_DONE]
    and  r2, r2, r0
    cmp  r2, r0
    bne  enable_wait
    bx   lr

; ---- GPIO 25: no pad isolation, function SIO, output; changes r0, r1 ----
configure_led:
    ldr  r1, lit_pads_clr
    mov  r0, #PAD_ISO
    str  r0, [r1, #PADS_GPIO25]
    ldr  r1, lit_io_bank0
    movs r0, #FUNCSEL_SIO
    str  r0, [r1, #GPIO25_CTRL]
    ldr  r1, lit_sio
    ldr  r0, lit_led_bit
    str  r0, [r1, #SIO_GPIO_OE_SET]
    bx   lr

; ---- wait: count r3 down in r4; changes r4 ----
delay:
    mov  r4, r3
delay_loop:
    subs r4, r4, #1
    bne  delay_loop
    bx   lr

    align 4
lit_resets:      dd RESETS_BASE
lit_resets_clr:  dd RESETS_BASE + ALIAS_CLR
lit_reset_bits:  dd RESET_BITS
lit_pads_clr:    dd PADS_BANK0_BASE + ALIAS_CLR
lit_io_bank0:    dd IO_BANK0_BASE
lit_sio:         dd SIO_BASE
lit_led_bit:     dd 1 << 25
lit_delay_value: dd DELAY_COUNT
