Metal ARM x03 - Toggle LED (Lab Notes)

Project 3 consists of:

Work Completed

The full assembly program is:

; vector table -------------------------------
x2000.8000                      ; sp starting address
thumb-addr!(&reset)             ; reset handler address | 1 (thumb mode)
pad-with-to!(x00, x0000.00B8)   ; skip to 0xB8, GPIO F interrupt handler address
thumb-addr!(&button-press)      ; GPIO F button press event handler | 1 (thumb mode)
pad-with-to!(x00, x0000.026C)   ; pad out the rest of the vtable
; --------------------------------------------

@reset
    ; enable clock at RCGCGPIO - x400F.E608 
    ; set bit 5 to 1
    MOVW R0 xE608
    MOVT R0 x400F
    LDR R1 [R0]
    MOVS R2 x20             ; bits[5] = 1
    ORRS R1 R2
    STR R1 [R0]

    ; We need to use the AHB (vs. APB), AHB is the only option supported
    ; by the board but it's not set on reset
    MOVW R0 xE06C 
    MOVT R0 x400F
    LDR R1 [R0]
    MOVS R2 x20             ; bits[5] = 1
    ORRS R1 R2
    STR R1 [R0]

    ; wait for GPIO F to be ready for use via PRGPIO
    MOVW R0 xEA08 
    MOVT R0 x400F
    MOVS R2 x20

    @waitready
        LDR R1 [R0]
        ANDS R1 R2
        BEQ &waitready


    ; Set direction of GPIO port F pins: LED -> output, button -> input 
    ; GPIODIR register at offset x400, set pins[1..=3] to 1
    ; bits[0] and bits[4] are implicitly 0 (inputs)
    MOVW R0 xD400
    MOVT R0 x4005
    LDR R1 [R0]
    MOVS R2 x0E             ; bits[1..=3] = 1, bits[0,4] = 0
    ORRS R1 R2
    STR R1 [R0]

    ; Enable pullup on GPIO F[4]
    ; GPIOPUR register at offset x510, set pins[4] to 1
    MOVW R0 xD510
    MOVT R0 x4005
    LDR R1 [R0]
    MOVS R2 x10             ; bits[4] = 1
    ORRS R1 R2
    STR R1 [R0]

    ; Enable as digital I/O
    ; offset at x51C, pins 1, 2, 3, 4 set to 1
    MOVW R0 xD51C
    MOVT R0 x4005
    LDR R1 [R0]
    MOVS R2 x1E             ; bits[1..=4] = 1
    ORRS R1 R2
    STR R1 [R0]

    ; Set GPIOIS (cleared is edge-detect, set is level-detect)
    ; Since it's cleared on reset, do nothing

    ; Set GPIOIEV (cleared is falling-edge, set is rising-edge)
    ; Since it's cleared on reset, do nothing

    ; Enable NVIC interrupt handling
    ; GPIO F is interrupt 30, corresponding to bits[30] at xE000.E000,
    ; offset x100
    MOVW R0 xE100
    MOVT R0 xE000
    LDR R1 [R0]
    MOVS R2 x00
    MOVT R2 x4000           ; bits[30] = 1
    ORRS R1 R2
    STR R1 [R0]

    ; Enable interrupt on GPIOF[4]
    ; GPIOIM register at offset x410, set pins[4] to 1
    MOVW R0 xD410
    MOVT R0 x4005
    LDR R1 [R0]
    MOVS R2 x10             ; bits[4] = 1
    ORRS R1 R2
    STR R1 [R0]

    @wait-for-event
        B &wait-for-event


; Toggle red LED, GPIO F pin 1
@button-press
    ; clear interrupt
    ; offset x41C, port F at x4005.D000, pins[4]
    MOVW R0 xD41C
    MOVT R0 x4005
    MOVS R5 x10
    LDR R1 [R0]     ; Lack of atomics is now glaring, need to figure that out
    ORRS R1 R5
    STR R1 [R0]     

    ; we'll read GPIO F data at offset x03FC, which is the masked addr
    ; for mask xFF. Also, realized this is a bit unsafe if we get interrupted
    ; since we're writing all bits. Baby steps
    MOVW R0 xD3FC
    MOVT R0 x4005
    LDR R1 [R0]
    MOVS R2 x02    ; bits[1] (pin 1)
    EORS R1 R2
    STR R1 [R0]

    ; return
    ; load EXC_RETURN (pre-loaded into LR) into PC
    BX LR    

Next Steps

I mentioned in Project 2 that repeating the same setup code is kind of onerous, and I felt the same in this project. In the next project I'd like to add chasm support for function calls and some kind of include statement. This implies multiple source files, and would be the first foray into a primitive linking step.

Depending on how that goes I'll either wrap it up or try to cover an embedded feature. Not having any debug printing has been kind of painful, so maybe investigating and implementing that is next.