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:
PC
to save register (say, r7
)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.
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.