; ARM assembly tutorial for Asmonite, example 18 for the Raspberry Pi Pico (RP2040, Arm Cortex-M0+).
; Adapted from the ARM-ASM-Tutorial by Niklas Guertler (example BlinkSysTickInterrupt); see ..\LICENSE.txt.
;
; The same program as ..\18_systick_interrupt.asm. What is different on the M0+:
;   - no IT blocks: a condition is a branch around the instruction ("bne handler_done")
;   - no "ldrb r1, [r0], #1" (post-index) and no "tst r1, #1" with a number: a register holds the 1
;   - the vector table of the M0+ has fewer entries (no MemManage, BusFault, UsageFault, SecureFault,
;     DebugMonitor); the SysTick is entry 15 as well
.target pico
.arch thumb
.imagebase 0x20000000

RESETS_BASE         equ 0x4000c000
RESETS_RESET_DONE   equ 0x08
RESET_BITS          equ (1 << 5) | (1 << 8)
IO_BANK0_BASE       equ 0x40014000
GPIO25_CTRL         equ 0xcc
FUNCSEL_SIO         equ 5
SIO_BASE            equ 0xd0000000
SIO_GPIO_OUT_SET    equ 0x14
SIO_GPIO_OUT_CLR    equ 0x18
SIO_GPIO_OE_SET     equ 0x24
ALIAS_CLR           equ 0x3000
SCS_BASE            equ 0xe000e000
SYST_CSR            equ 0x10
SYST_RVR            equ 0x14
SYST_CVR            equ 0x18
TIMER_VALUE         equ 1500000
SRAM_END            equ 0x20042000

vectors:
    dd SRAM_END                 ;  0: the stack pointer at the start
    dd _start + 1               ;  1: reset
    dd hang + 1                 ;  2: NMI
    dd hang + 1                 ;  3: HardFault
    dd 0, 0, 0, 0, 0, 0, 0      ;  4 .. 10: reserved on the M0+
    dd hang + 1                 ; 11: SVCall
    dd 0, 0                     ; 12, 13: reserved
    dd hang + 1                 ; 14: PendSV
    dd systick_handler + 1      ; 15: SysTick

_start:
    ldr  r0, lit_vtor
    ldr  r1, lit_vectors        ; the M0+: adr only forwards, so the address from a word
    str  r1, [r0]               ; VTOR (0xe000ed08): our vector table
    adr  r0, blink_step
    movs r1, #0
    strb r1, [r0]
    adr  r2, blink_table
    ldrb r1, [r2]
    adr  r0, timer_events
    strb r1, [r0]
    bl   setup_led
    ldr  r0, lit_sio
    ldr  r1, lit_led_bit
    str  r1, [r0, #SIO_GPIO_OUT_SET]
    ldr  r0, lit_timer_value
    bl   start_systick
    cpsie i                     ; allow interrupts (PRIMASK = 0): the boot ROM may leave them off
sleep_loop:
    wfi
    b    sleep_loop

systick_handler:
    adr  r0, timer_events
    ldrb r3, [r0]
    subs r3, r3, #1
    beq  next_step              ; 0: the next entry of the table
    strb r3, [r0]               ; else store the rest and return
    bx   lr
next_step:
    adr  r0, blink_step
    ldrb r1, [r0]
    adds r1, r1, #1
    cmp  r1, #(blink_table_end - blink_table)
    blo  step_ok
    movs r1, #0
step_ok:
    strb r1, [r0]
    adr  r2, blink_table
    ldrb r3, [r2, r1]
    adr  r0, timer_events
    strb r3, [r0]
    ldr  r0, lit_sio
    ldr  r2, lit_led_bit
    movs r3, #1
    tst  r1, r3                 ; even entry: on, odd entry: off
    bne  led_off
    str  r2, [r0, #SIO_GPIO_OUT_SET]
    bx   lr
led_off:
    str  r2, [r0, #SIO_GPIO_OUT_CLR]
    bx   lr

start_systick:
    ldr  r1, lit_scs
    str  r0, [r1, #SYST_RVR]
    movs r0, #0
    str  r0, [r1, #SYST_CVR]
    movs r0, #7
    str  r0, [r1, #SYST_CSR]
    bx   lr

hang:
    b    hang

setup_led:
    ldr  r1, lit_resets_clr
    ldr  r0, lit_reset_bits
    str  r0, [r1]
    ldr  r1, lit_resets
setup_wait:
    ldr  r2, [r1, #RESETS_RESET_DONE]
    ands r2, r0
    cmp  r2, r0
    bne  setup_wait
    ldr  r1, lit_gpio25_ctrl      ; the M0+: an offset of at most 124 in str, so the whole address
    movs r0, #FUNCSEL_SIO
    str  r0, [r1]
    ldr  r1, lit_sio
    ldr  r0, lit_led_bit
    str  r0, [r1, #SIO_GPIO_OE_SET]
    bx   lr

    align 4                     ; the M0+: adr wants an address that is a multiple of 4
blink_table:
    db 2, 2, 2, 2, 2, 2
    db 5, 2, 5, 2, 5, 2
    db 2, 2, 2, 2, 2, 8
blink_table_end:
    align 4
blink_step:   db 0
    align 4
timer_events: db 0
    align 4
lit_resets:      dd RESETS_BASE
lit_resets_clr:  dd RESETS_BASE + ALIAS_CLR
lit_reset_bits:  dd RESET_BITS
lit_gpio25_ctrl: dd IO_BANK0_BASE + GPIO25_CTRL
lit_sio:         dd SIO_BASE
lit_led_bit:     dd 1 << 25
lit_scs:         dd SCS_BASE
lit_vtor:        dd 0xe000ed08
lit_vectors:     dd vectors
lit_timer_value: dd TIMER_VALUE
