Skip to main content

Runtime Library — Overview

ARMbasic is a compiler: the PC-side ARMbasic.exe translates BASIC source into native ARM machine code that runs directly on the target. Whenever a compiled program needs a non-trivial primitive — print, wait, flash_write, integer divide, string concatenation, float math — the emitted code does a BL straight into a fixed flash address in the firmware. The collection of small helper functions those calls land on is the runtime library.

This page describes how the runtime is organised; the rest of the Runtime Library section documents each individual call.

Calling convention — global register variables

Every helper takes no formal arguments and returns no value. Instead, six global registers hold the BASIC machine state, and they are live across the whole program:

RegisterVariableRole
r4reg_lindexl-value index — array subscript, IO pin number, baud-rate slot, destination of *x = …
r5ptr_var_segbase offset into the variable segment for the current scope
r6reg_operand / f_operandright-hand operand of a binary operation (typed int or float depending on the helper)
r7reg_accum / f_accumaccumulator — input on entry, result on return
r9reg_nexta "second value" slot — flash source pointer, memcopy source, etc.
r11reg_strscratchscratch — string source pointer in LEFT$/RIGHT$

So the integer divide helper is, in C, simply reg_accum = reg_accum / reg_operand; — no parameters, no return; the compiled code set up r6/r7 before the BL and reads r7 after. This is what makes the helpers so small and cheap to call.

The dispatch table — f_table[]

The firmware exposes a fixed-format f_table[] array at a known flash location. Slots 0..7 are metadata (version, count, CPU code, code start, variable base, board type, …); the remaining slots are 1:1 with runtime functions. The PC compiler:

  1. Pings the live board over UART and reads f_table[].
  2. For each BASIC primitive, looks up its slot, computes the absolute flash address, and emits BL <addr>.
  3. Downloads the compiled hex into the user-code area and runs it.

Nothing in the firmware ever does a runtime dispatch — the calls are direct, baked in at compile time. The table order is version-locked since version 6.08; new primitives can only be appended.

Function groups

Integer arithmetic and comparison

/, MOD, AND, OR, XOR, ABS, the six relational operators, logical NOT, and the n-bit reverse used by REV.

Floating-point arithmetic and comparison

+, -, *, /, unary minus, the six relational operators, plus the four int↔float conversion bridges. f_accum and f_operand are the same r7 / r6 registers, reinterpreted as float.

String building

A 256-byte STR_ACCUM scratch buffer at the top of the variable segment holds the in-progress string. Helpers append to it (f_addstr, f_addsbyte, f_hex2str, f_dec2str, f_left, f_right), copy it out to a destination (f_savestr), or parse it back to a number (f_str2dec). f_strcmp, f_strlen, and the SPRINTF family round out the string surface.

Number print, character print, CR / TAB, string print, float print (with optional rounding), and the BASIC-level SPRINTF wrappers. Internal flags select the BASIC %G format and round-half-up rules inside the shared printf implementation.

Wait / timer

WAIT, WAITmicro, TIMER, and SETTIMER. The microsecond tick is maintained by cpu_micros() driven from SysTick (or TIMER0 on legacy parts).

GPIO

IN(pin), OUT pin = …, DIR(pin), and the direction setter. The runtime calls thin per-CPU bridges (cpu_inbit, cpu_outbit, cpu_rddirbit, cpu_wrdirbit) so the same BASIC code works unchanged across LPC, nRF, and RP2040 targets.

UART

BAUD, RXD, TXD, and TXFREE map to UART_init / cpu_getc / cpu_putc / uart_txfree — again, per-CPU implementations behind a common interface.

ADC

A single weak entry point that each per-CPU file overrides. On parts without an ADC the call returns 0.

Flash write

FWRITE is gated against writing into the code region, tracks the last sector erased so consecutive writes within one sector skip the erase step, and then calls the platform-specific WriteFlash.

Memory copy

FREAD is a plain bytewise memcpy driven by the (r9, r4, r7) register triple — destination, source, and count.

Interrupts

INTSW(0/1) flips the I bit of CPSR via enableIRQ / disableIRQ.

Sort

SORT calls a recursive quicksort over reg_lindex (array base) for reg_accum elements.

Line input and program lifecycle

BASICgetln reads a line from the console and echoes it back — unless the first character is a download / control marker (:, >0, !, @, ^, "), in which case the line is consumed silently. Program exit unwinds the saved SP / LR that the goForIt trampoline stashed on entry and returns control from the running BASIC program back to the firmware shell.

Variable storage

g_var_seg[] is the single backing array for every BASIC variable and array. The compiler emits offsets into this array; the firmware just provides the address. The last 256 bytes are reserved as the string accumulator (STR_ACCUM).

See also

The pages that follow under Runtime Library document each user-visible call in detail: time functions, math functions, floating point, byte/file/string helpers.