; ARM assembly tutorial for Asmonite, example 18 for the Blue Pill (STM32F103, Arm Cortex-M3).
; Adapted from the ARM-ASM-Tutorial by Niklas Guertler (example BlinkSysTickInterrupt); see ..\LICENSE.txt.
;
; The SysTick interrupt example on the chip of the original tutorial. The SysTick, the vector table, VTOR and
; the IT blocks are the same as on the Pico 2 (the Cortex-M3 and the Cortex-M33 share them); only the LED is
; switched the way of the STM32: GPIOC_BSRR, bit 29 clears PC13 (LED on), bit 13 sets it (LED off).
; The program runs in the RAM; the core runs from the internal 8 MHz oscillator (HSI) after a reset, so an
; event of the SysTick (1500000 cycles) is about 0.19 s.
.target stm32f103
.arch thumb
.imagebase 0x20000000

RCC_APB2ENR         equ 0x40021018
IOPCEN              equ 1 << 4
GPIOC_BASE          equ 0x40011000
GPIO_CRH            equ 0x04
GPIO_BSRR           equ 0x10
LED_ON              equ 1 << 29         ; BR13: PC13 low
LED_OFF             equ 1 << 13         ; BS13: PC13 high
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 0x20005000

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 0, 0, 0, 0               ;  7 .. 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

_start:
    ldr  r0, lit_scs
    adr  r1, vectors
    str  r1, [r0, #SCB_VTOR]    ; the vector table is ours
    adr  r0, blink_step
    movs r1, #0
    strb r1, [r0]               ; blink_step = 0
    adr  r2, blink_table
    ldrb r1, [r2]
    adr  r0, timer_events
    strb r1, [r0]               ; timer_events = the first entry of the table
    bl   setup_led
    ldr  r0, lit_gpioc
    ldr  r1, lit_led_on
    str  r1, [r0, #GPIO_BSRR]   ; the first step: LED on
    ldr  r0, lit_timer_value
    bl   start_systick
    cpsie i                     ; allow interrupts (PRIMASK = 0)
sleep_loop:
    wfi                         ; sleep until an interrupt
    b    sleep_loop

; ---- called by the SysTick at every event ----
systick_handler:
    adr  r0, timer_events
    ldrb r3, [r0]
    subs r3, r3, #1             ; one event less
    itt  ne
    strbne r3, [r0]             ; not yet 0: store it
    bne  handler_done           ; and return
    adr  r0, blink_step
    ldrb r1, [r0]
    adds r1, r1, #1             ; the next entry
    cmp  r1, #(blink_table_end - blink_table)
    it   hs
    movhs r1, #0                ; after the last one: from the start again
    strb r1, [r0]
    adr  r2, blink_table
    ldrb r3, [r2, r1]           ; its number of events
    adr  r0, timer_events
    strb r3, [r0]
    tst  r1, #1                 ; even entry: on, odd entry: off
    ite  eq
    ldreq r2, lit_led_on
    ldrne r2, lit_led_off
    ldr  r0, lit_gpioc
    str  r2, [r0, #GPIO_BSRR]
handler_done:
    bx   lr

; ---- start the SysTick with its interrupt: r0 = cycles between two events ----
start_systick:
    ldr  r1, lit_scs
    str  r0, [r1, #SYST_RVR]
    movs r0, #0
    str  r0, [r1, #SYST_CVR]
    movs r0, #7                 ; ENABLE | TICKINT | CLKSOURCE
    str  r0, [r1, #SYST_CSR]
    bx   lr

; ---- the clock of port C on, PC13 an output (push-pull, 2 MHz); changes r0, r1 ----
setup_led:
    ldr  r1, lit_rcc_apb2enr
    ldr  r0, [r1]
    orr  r0, r0, #IOPCEN
    str  r0, [r1]
    ldr  r1, lit_gpioc
    ldr  r0, [r1, #GPIO_CRH]
    bic  r0, r0, #0x00f00000
    orr  r0, r0, #0x00200000
    str  r0, [r1, #GPIO_CRH]
    bx   lr

hang:
    b    hang

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:
blink_step:   db 0
timer_events: db 0
    align 4
lit_rcc_apb2enr: dd RCC_APB2ENR
lit_gpioc:       dd GPIOC_BASE
lit_led_on:      dd LED_ON
lit_led_off:     dd LED_OFF
lit_scs:         dd SCS_BASE
lit_timer_value: dd TIMER_VALUE
