Oberon RTK

gen-asm

Translate inline assembly blocks in Oberon source files

Name

gen-asm – translate inline assembly blocks in Oberon source files

Synopsis

gen-asm path [path …] [--recursive] [--dry-run] [-v]

Description

gen-asm finds (* asm ... end asm *) blocks (case-insensitive) in Oberon .mod files and generates the corresponding SYSTEM.EMIT, SYSTEM.EMITH, SYSTEM.LDREG, and SYSTEM.REG calls. Generated code is placed between (* +asm *) and (* -asm *) markers. The tool replaces the generated block on each re-run; the ASM block is the source of truth.

Both (* ASM ... END ASM *) and (* asm ... end asm *) are recognised. Generated markers are always lowercase. If SYSTEM is not in the module's IMPORT list and generated code requires it, the tool adds it automatically.

Handcrafted SYSTEM.EMIT and similar calls outside the markers are never touched.

Mnemonics, register names and system register names are case-insensitive.

Arguments

path
One or more .mod files or directories. When a directory is given, all .mod files in that directory are processed.

Options

--recursive, -r
Scan directories recursively for .mod files.
--dry-run, -n
Show which files would be updated without writing.
-v, --verbose
Print per-file status.

ASM Block Syntax

Assembly is placed in a structured comment block:

(* asm
  mrs r11, PSP -> stackframeBase
  dsb
  isb
end asm *)

Each line contains one assembly instruction. Empty lines and Oberon comments (* ... *) within the block are ignored.

Variable Binding

The -> operator connects assembly registers to Oberon variables:

Output (register to variable):
mrs r11, PSP -> stackframeBase
Generates SYSTEM.EMIT(...) followed by stackframeBase := SYSTEM.REG(11).
Input (variable to register):
ExcPrioBlock -> msr BASEPRI, r3
Generates SYSTEM.LDREG(3, ExcPrioBlock) followed by SYSTEM.EMIT(...).

Without ->, the instruction generates only the SYSTEM.EMIT or SYSTEM.EMITH call.

Generated Code Markers

(* asm
  dsb
  isb
end asm *)
(* +asm *)
SYSTEM.EMIT(0F3BF8F4FH);  (* dsb *)
SYSTEM.EMIT(0F3BF8F6FH);  (* isb *)
(* -asm *)

Everything between (* +asm *) and (* -asm *) is owned by the tool and replaced on each run. On the first run, the markers and generated code are inserted after the ASM block. Both uppercase and lowercase markers are recognised for re-run; generated markers are always lowercase.

Supported Instructions

Barriers

Instruction Encoding Size Description
dsb SYSTEM.EMIT 32-bit Data Synchronisation Barrier
isb SYSTEM.EMIT 32-bit Instruction Synchronisation Barrier
dmb SYSTEM.EMIT 32-bit Data Memory Barrier

No operands.

Hint Instructions

Instruction Encoding Size Description
nop SYSTEM.EMITH 16-bit No Operation
wfi SYSTEM.EMITH 16-bit Wait For Interrupt
wfe SYSTEM.EMITH 16-bit Wait For Event
sev SYSTEM.EMITH 16-bit Send Event

No operands.

Interrupt Control

Instruction Encoding Size Description
cpsie i SYSTEM.EMITH 16-bit Enable interrupts (clear PRIMASK)
cpsid i SYSTEM.EMITH 16-bit Disable interrupts (set PRIMASK)
cpsie f SYSTEM.EMITH 16-bit Enable faults (clear FAULTMASK)
cpsid f SYSTEM.EMITH 16-bit Disable faults (set FAULTMASK)

Special Register Access

Instruction Encoding Size Description
mrs Rd, sysreg SYSTEM.EMIT 32-bit Read system register to Rd
msr sysreg, Rn SYSTEM.EMIT 32-bit Write Rn to system register

Rd, Rn: r0r15.

Supported system registers:

Name SYSm Description
APSR_nzcvq 00H Condition flags
XPSR 03H Combined status
IPSR 05H Interrupt status
MSP 08H Main Stack Pointer
PSP 09H Process Stack Pointer
MSPLIM 0AH MSP limit
PSPLIM 0BH PSP limit
PRIMASK 10H Interrupt mask
BASEPRI 11H Base priority
FAULTMASK 13H Fault mask
CONTROL 14H Thread mode control
MSP_NS 88H Non-secure MSP
PSP_NS 89H Non-secure PSP
MSPLIM_NS 8AH Non-secure MSP limit
PSPLIM_NS 8BH Non-secure PSP limit
CONTROL_NS 94H Non-secure CONTROL
SP_NS 98H Current NS stack pointer

System register names are case-insensitive.

Branch Instructions

Instruction Encoding Size Description
bx Rn SYSTEM.EMITH 16-bit Branch and exchange
bxns lr SYSTEM.EMITH 16-bit Branch to Non-secure (LR only)
blxns Rn SYSTEM.EMITH 16-bit Branch with link to Non-secure

Stack Instructions

Instruction Encoding Size Description
pop.w {LR} SYSTEM.EMIT 32-bit Pop LR (Thumb2 wide)
add sp, #N SYSTEM.EMITH 16-bit Adjust stack pointer (N: multiple of 4, 0–508)

Supervisor Call

Instruction Encoding Size Description
svc #N SYSTEM.EMITH 16-bit Supervisor call (N: 0–255)

Security

Instruction Encoding Size Description
tt Rd, Rn SYSTEM.EMIT 32-bit Test Target (SAU query)

GPR Move (for register sanitisation)

Instruction Encoding Size Description
mov Rd, #imm8 SYSTEM.EMITH 16-bit Move immediate to R0–R7 (imm: 0–255)
mov.w Rd, #imm SYSTEM.EMIT 32-bit Move immediate to R0–R15

mov uses the 16-bit Thumb encoding and is limited to R0–R7. mov.w uses the 32-bit Thumb2 encoding and supports all registers. For mov.w, immediate 0 and 16-bit values (0–65535) are supported.

FPU Instructions (for register sanitisation)

Instruction Encoding Size Description
vmov Sn, Rn SYSTEM.EMIT 32-bit Core register to single FP register
vmov Dd, Rm, Rn SYSTEM.EMIT 32-bit Two core registers to double FP register
vmsr FPSCR, Rn SYSTEM.EMIT 32-bit Write core register to FPSCR

Sn: s0s31. Dd: d0d15. Rm, Rn: r0r15.

Use vmov Dd, Rm, Rn with both core registers zeroed to clear two single-precision registers per instruction (D0 = S0:S1, D1 = S2:S3, etc.).

Examples

Barrier sequence

(* asm
  dsb
  isb
end asm *)

Read system register with variable binding

(* asm
  mrs r11, PSP -> stackframeBase
end asm *)

Generates:

(* +asm *)
SYSTEM.EMIT(0F3EF8B09H);  (* mrs r11, PSP *)
stackframeBase := SYSTEM.REG(11);  (* r11 -> stackframeBase *)
(* -asm *)

Write system register with variable binding

(* asm
  ExcPrioBlock -> msr BASEPRI, r3
end asm *)

Generates:

(* +asm *)
SYSTEM.LDREG(3, ExcPrioBlock);  (* ExcPrioBlock -> r3 *)
SYSTEM.EMIT(0F3838811H);  (* msr BASEPRI, r3 *)
(* -asm *)

BASEPRI critical section

(* asm
  mrs r7, BASEPRI -> saved
  ExcPrioBlock -> msr BASEPRI, r3
end asm *)
(* critical section body *)
(* asm
  saved -> msr BASEPRI, r7
end asm *)

Secure procedure epilogue

(* asm
  add sp, #20
  pop.w {LR}
  bxns lr
end asm *)

GPR and FPU sanitisation (before BLXNS)

(* asm
  mov r0, #0
  mov r1, #0
  mov r2, #0
  mov r3, #0
  mov r4, #0
  mov r5, #0
  mov r6, #0
  mov r7, #0
  mov.w r8, #0
  mov.w r9, #0
  mov.w r10, #0
  mov.w r12, #0
  msr APSR_nzcvq, r0
  vmov d0, r0, r0
  vmov d1, r0, r0
  vmov d2, r0, r0
  vmov d3, r0, r0
  vmov d4, r0, r0
  vmov d5, r0, r0
  vmov d6, r0, r0
  vmov d7, r0, r0
  vmov d8, r0, r0
  vmov d9, r0, r0
  vmov d10, r0, r0
  vmov d11, r0, r0
  vmov d12, r0, r0
  vmov d13, r0, r0
  vmov d14, r0, r0
  vmov d15, r0, r0
  vmsr FPSCR, r0
end asm *)

Process a directory

python gen-asm.py config/

Recursive processing with dry run

python gen-asm.py --recursive lib/v3.1/ --dry-run

Target Compatibility

The supported instructions use Thumb and Thumb-2 encodings common to all ARM Cortex-M processors. The tool does not check the target – it encodes whatever the programmer writes. Most instructions (barriers, hints, interrupt control, mrs/msr with common registers, bx, stack ops, mov) work across the Cortex-M family, including Cortex-M0/M0+ (RP2040) and Cortex-M33 (RP2350, STM32).

Some instructions and registers are specific to Cortex-M33 (Armv8-M):

  • Security Extension instructions: bxns, blxns, tt
  • Security Extension registers: MSP_NS, PSP_NS, MSPLIM, PSPLIM, CONTROL_NS, SP_NS
  • BASEPRI and FAULTMASK (Armv7-M and Armv8-M, not Armv6-M)
  • MSPLIM, PSPLIM (Armv8-M only)

Using a target-specific instruction on the wrong processor will produce a valid encoding but fault at runtime.

Limitations

  • This is not a general ARM assembler. Only the instruction subset listed above is supported.
  • The programmer is responsible for register selection and target compatibility. The tool does not interact with the compiler's register allocator and does not check the target architecture.
  • Instructions are emitted in source order. No reordering or optimisation.

See Also

  • sec-epilogue – insert Secure return epilogues
  • gen-secure – generate NSC veneers and NS interface modules

Last updated: 16 March 2026