; ARM assembly tutorial for Asmonite, example 17: wait with the SysTick timer (Raspberry Pi Pico 2, Arm Cortex-M33).
; Adapted from the ARM-ASM-Tutorial by Niklas Guertler (example BlinkSysTick); see LICENSE.txt.
;
; Every Cortex-M has the SysTick: a 24-bit counter that counts down with the clock of the core and starts
; again at the reload value. Each time it passes 0 it sets the bit COUNTFLAG (bit 16 of SYST_CSR); reading
; SYST_CSR clears it. The waiting does not depend on how fast the loop is any more: wait_systick waits for
; r0 of these events. The pattern comes from a table of bytes, as in 13_blink_bytes.asm.
;   SYST_CSR (0xe000e010): bit 0 ENABLE, bit 1 TICKINT (interrupt, see 18), bit 2 CLKSOURCE (1: the core clock)
;   SYST_RVR (0xe000e014): the reload value; SYST_CVR (0xe000e018): the counter (writing clears 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
SCS_BASE            equ 0xe000e000      ; the System Control Space of the core
SYST_CSR            equ 0x10
SYST_RVR            equ 0x14
SYST_CVR            equ 0x18
COUNTFLAG           equ 1 << 16
TIMER_VALUE         equ 1500000         ; clock cycles between two events

_start:
    bl   setup_led
    ldr  r0, lit_timer_value
    bl   start_systick
    bl   blink_pattern
    b    $

blink_pattern:
    push {r4, r5, r6, r7, r8, lr}
    ldr  r4, lit_sio
    ldr  r5, lit_led_bit
    adr  r7, blink_table
    adr  r8, blink_table_end
pattern_loop:
    str  r5, [r4, #SIO_GPIO_OUT_SET]
    ldrb r0, [r7], #1
    bl   wait_systick
    str  r5, [r4, #SIO_GPIO_OUT_CLR]
    ldrb r0, [r7], #1
    bl   wait_systick
    cmp  r7, r8
    blo  pattern_loop
    pop  {r4, r5, r6, r7, r8, pc}

; ---- start the SysTick: r0 = cycles between two events ----
start_systick:
    ldr  r1, lit_scs
    str  r0, [r1, #SYST_RVR]
    movs r0, #0
    str  r0, [r1, #SYST_CVR]    ; start from the reload value
    movs r0, #5                 ; ENABLE | CLKSOURCE, no interrupt
    str  r0, [r1, #SYST_CSR]
    bx   lr

; ---- wait for r0 events of the SysTick ----
wait_systick:
    ldr  r1, lit_scs
wait_loop:
    ldr  r2, [r1, #SYST_CSR]    ; reading clears COUNTFLAG
    tst  r2, #COUNTFLAG
    beq  wait_loop
    subs r0, r0, #1
    bne  wait_loop
    bx   lr

; ---- IO_BANK0 and PADS_BANK0 out of reset, GPIO 25: SIO, output; changes r0 .. r2 ----
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]
    and  r2, r2, r0
    cmp  r2, r0
    bne  setup_wait
    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

    align 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
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_scs:         dd SCS_BASE
lit_timer_value: dd TIMER_VALUE
