; ARM assembly tutorial for Asmonite, example 19: start-up code for the flash (Raspberry Pi Pico 2, Arm Cortex-M33).
; Adapted from the ARM-ASM-Tutorial by Niklas Guertler (examples BlinkInitRAM and BlinkStartup, section
; "Initializing RAM"); see LICENSE.txt.
;
; 18_systick_interrupt.asm for the flash. A program in the flash cannot keep its variables there (the
; flash cannot be written like the RAM), and after a power cycle the RAM holds random values. So the
; program sets up its variables itself at the start, as the start-up code of a C program does:
;   - "data": variables with a start value. The start values stand in the flash (data_load ..
;     data_load_end); a loop copies them into the RAM at RAM_DATA.
;   - "bss": variables that start at 0. A loop fills their place in the RAM with zeros.
; The addresses of the variables in the RAM are constants (equ), worked out from the offsets of the start
; values in the flash: TIMER_EVENTS = RAM_DATA + (d_timer_events - data_load).
; The vector table is at 0x10000000, the start of the flash; the program writes this address into VTOR.
.target pico2
.arch thumb
.imagebase 0x10000000

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
SYST_CSR            equ 0x10
SYST_RVR            equ 0x14
SYST_CVR            equ 0x18
SCB_VTOR            equ 0xd08
TIMER_VALUE         equ 1500000
SRAM_END            equ 0x20082000

RAM_DATA            equ 0x20000000                          ; the "data" variables in the RAM
DATA_SIZE           equ data_load_end - data_load           ; a multiple of 4 (align 4 at the end)
TIMER_EVENTS        equ RAM_DATA + (d_timer_events - data_load)
BSS_START           equ RAM_DATA + DATA_SIZE                ; the "bss" variables behind them
BLINK_STEP          equ BSS_START + 0                       ; 1 byte
BSS_SIZE            equ 4

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 hang + 1                 ;  4: MemManage
    dd hang + 1                 ;  5: BusFault
    dd hang + 1                 ;  6: UsageFault
    dd hang + 1                 ;  7: SecureFault
    dd 0, 0, 0                  ;  8 .. 10: reserved
    dd hang + 1                 ; 11: SVCall
    dd hang + 1                 ; 12: DebugMonitor
    dd 0                        ; 13: reserved
    dd hang + 1                 ; 14: PendSV
    dd systick_handler + 1      ; 15: SysTick

image_def:                      ; for the boot ROM: a program for the Arm cores
    dd 0xffffded3
    db 0x42, 1
    dw 0x1021
    db 0xff
    dw 1
    db 0
    dd 0
    dd 0xab123579

_start:
    ldr  r0, lit_scs
    adr  r1, vectors
    str  r1, [r0, #SCB_VTOR]

    ; data: copy the start values from the flash into the RAM, a word at a time
    ldr  r0, lit_ram_data       ; r0 = where to (RAM)
    ldr  r1, lit_data_size
    add  r1, r1, r0             ; r1 = the end
    adr  r2, data_load          ; r2 = where from (flash)
    b    copy_check
copy_loop:
    ldr  r3, [r2], #4
    str  r3, [r0], #4
copy_check:
    cmp  r0, r1
    blo  copy_loop

    ; bss: zeros
    ldr  r0, lit_bss_start
    ldr  r1, lit_bss_size
    add  r1, r1, r0
    movs r2, #0
    b    zero_check
zero_loop:
    str  r2, [r0], #4
zero_check:
    cmp  r0, r1
    blo  zero_loop

    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:
    ldr  r0, lit_timer_events
    ldrb r3, [r0]
    subs r3, r3, #1
    itt  ne
    strbne r3, [r0]
    bne  handler_done
    ldr  r0, lit_blink_step
    ldrb r1, [r0]
    adds r1, r1, #1
    cmp  r1, #(blink_table_end - blink_table)
    it   hs
    movhs r1, #0
    strb r1, [r0]
    adr  r2, blink_table
    ldrb r3, [r2, r1]
    ldr  r0, lit_timer_events
    strb r3, [r0]
    ldr  r0, lit_sio
    ldr  r2, lit_led_bit
    tst  r1, #1
    ite  eq
    streq r2, [r0, #SIO_GPIO_OUT_SET]
    strne r2, [r0, #SIO_GPIO_OUT_CLR]
handler_done:
    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

; ---- 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

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
data_load:                      ; the start values of the "data" variables
d_timer_events: db 2
    align 4
data_load_end:

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
lit_ram_data:     dd RAM_DATA
lit_data_size:    dd DATA_SIZE
lit_bss_start:    dd BSS_START
lit_bss_size:     dd BSS_SIZE
lit_timer_events: dd TIMER_EVENTS
lit_blink_step:   dd BLINK_STEP
