Graeme Winter

Assembly / data

Concepts

Well aware of the data() instruction but had not figured out how to make it useful, now have. It can be used to inject instructions into the program but can also be used to inject data in if used carefully, because it is just writing bytes to a stream of instruction output in memory.

Key concept: PC register keeps track of the current instruction position. Emitting data into the stream is fine if we skip it in execution, so we have:

The data words are then available by ldr(r0, [r7, offset]) keeping in mind that offset is in 4-byte words so the words are at 0, 4, 8… the ldr instruction takes two cycles to execute. For reasons of how µPython is written bits 30, 31 must be zero -> cannot usefully store addresses 😞 without doing some additional poking.

Program

from machine import Pin
import time

led = Pin(25, Pin.OUT)
pin = Pin(0, Pin.OUT)

XOR = 0xD000001C


@micropython.asm_thumb
def blink(r0):
    push({r4, r5, r6, r7})
    align(4)
    mov(r7, pc)
    b(entry)
    align(4)
    data(4, (1 << 25) | 1)  # LED pin
    data(4, 10)  # ticks
    data(4, 100_000_000)  # total cycles
    align(2)

    label(entry)  # start of program
    mov(r6, r0)
    ldr(r1, [r7, 8])
    label(tick)
    sub(r1, r1, 1)
    ldr(r0, [r7, 0])
    str(r0, [r6, 0])  # toggle GPIO
    ldr(r0, [r7, 4])
    label(clock)
    sub(r0, r0, 1)
    cmp(r0, 0)
    bne(clock)
    nop()
    nop()
    cmp(r1, 0)
    bne(tick)
    pop({r4, r5, r6, r7})


t0 = time.ticks_us()
blink(XOR)
t1 = time.ticks_us()
print(t1 - t0)

Probing the state of GPIO0 with the oscilloscope shows a 800ns square wave with 50% duty as expected, and the overall time reports as a hair over 40s.