ALB FASM backend
The ALB FASM backend is AdaLogic BASIC's flagship native emitter: it turns an ALB program into x86-64 assembly source for the Flat Assembler, which then assembles — in a single pass, in milliseconds — into a real Windows PE64 executable with no linker and no C runtime requirement beyond what ALB brings itself.
It is one of only three backends — with C and FASM16 — where --build is supported end to end: the compiler emits the file and drives the assembler.
What it produces
Every generated file opens the same way — a comment banner, then the PE64 header:
; ==========================================================
; GENERATED BY ADALOGIC BASIC (ALB) TRANSPILER
; TARGET: x64 FASM (Bare-Metal PE Executable)
; ==========================================================
format PE64 Console
stack 1000000h
heap 1000000h
entry start
include 'win64a.inc'
The emitter supports three output formats, switched by flags such as --dll:
| Format | Emitted header | Result |
|---|---|---|
| Console | format PE64 Console | Command-line .exe — the default |
| GUI | format PE64 GUI | Windowed .exe — no console |
| DLL | format PE64 DLL at 10000000h | Shared library with an ALB export entry |
Console and GUI builds reserve a 16 MiB stack and 16 MiB heap (stack 1000000h / heap 1000000h) — generous headroom for recursion and the temporal-state system, at zero runtime cost since Windows commits the pages lazily.
The file anatomy
A FASM output is organized into the classic PE sections:
.text— code: the runtime library, then your program understart:.data— initialized writable data: string literals, buffers, the GC grid.bss— zero-initialized storage.idata— imports: only the Win32 functions actually used (printf, ExitProcess, GDI, winsock…).reloc— relocations, emitted for DLL builds
The macro layer — FASM as co-optimizer
Here is the part that surprises people. The emitter writes expressions in a deliberately simple infix-looking style — mov rax, a + mov rax, b — and then defines an ALB_LET macro that uses FASM's match directive to rewrite those patterns into optimized code at assembly time:
match =0 =mov =rax, dest, =mov =rax, a + =mov =rax, b, matched line {
mov rax, a
add rax, b
mov dest, rax
define matched 1
}
The assembler itself pattern-matches each emitted statement and fuses the verbose three-instruction form into a tight mov/add/mov. The compiler stays simple; the assembler does the peephole optimization. Two tools, each doing what it's best at.
Alongside it, ALB_MASK_OP provides branchless selection — a conditional computed as a bitmask (neg / and / not / and / or) instead of a jump, for the constant-time code paths where a branch would be unpredictable or unsafe.
The hardened runtime
ALB does not trust the machine blindly — the emitted program carries a safety runtime:
ALB_Runtime_Trap_DivZero:
sub rsp, 40
invoke printf, ALB_Runtime_DivZero
invoke ExitProcess, 1
Divide by zero, index out of bounds, or leave a declared range and the program reports and halts rather than corrupting onward — the Ada philosophy showing through the BASIC surface. Every runtime routine is wrapped in FASM's if used … end if, so the assembler strips everything your program never calls: a hello world links in almost none of the library below.
And the library is large:
- Strings —
ALB_Str_Eq,Mid/Left/Right,ALB_Hash, length/copy/concat with fixed-capacity vaults - Console — prompts, buffered input with a hand-rolled S64 parser (
ALB_Input_Try_Parse_S64, whitespace and sign handled in raw asm) - Files — open/read/write/close/len/seek, plus
ALB_OS_Load/ALB_OS_Flushwhole-file vaults - Logic — a real Prolog-style knowledge base:
ALB_Assert,ALB_Query,ALB_Find,ALB_FindAll,ALB_Retract— the Prolog half of ALB's ancestry, implemented in machine code - Math — sqrt/sine/cosine/abs, random, and the PURE engine (
ALB_Pure_Add/Sub/Mul/Div/Pow) — exact rational arithmetic with numerator and denominator kept separate, so ⅓ + ⅓ stays exactly ⅓ - Graphics & sound — Win32 window creation, message loop, put-pixel, BGR colors, key and mouse state, music buffer streaming
- Data formats — a complete FITS astronomy file reader (card loop, BITPIX parsing, NAXIS) and an INI parser — emitted only if used
- FFI —
ALB_Load_Foreign_Symbolloads DLL functions at runtime; ALB'sIMPORT_DLLlowers to direct calls
The logic engine — Prolog in machine code
FACT, ASSERT, RETRACT, FIND, FINDALL, KNOWS, and the real ?- query form are all first-class AST node kinds — and the FASM backend answers them with a hand-written knowledge base in assembly: ALB_Assert, ALB_Query, ALB_Find, ALB_FindAll, ALB_Retract. A source line like ASSERT father(homer, bart) lowers to
invoke ALB_Assert, addr father_str, homer_bart_arg, 0
fastcall ALB_Query, <'father', ...>
— the Prolog half of ALB's ancestry, executing in a PE with no logic library, no VM, and no garbage collector required to answer a query.
The temporal engine
TEMPORAL variables get real machinery, not a comment. The compiler tracks up to 512 temporal variables against a 65,536-entry history budget, each declared with its own History_Depth. The FASM emitter gives each one a dedicated ring buffer in .bss:
ALB_TEMP_score_history rq 64 ; 64 slots, one per remembered write
ALB_TEMP_score_head dq 0 ; ring head
Every write inlines the record step — store rax at history[head], inc head, wrap to zero at depth. Then the four temporal operators do what they say:
x NOW— the plain current valuex PAST—history[head - 1]with wraparound: literally the previous write, a variable you can read backwardsx TIMELINE—lea rax, [ALB_TEMP_x_history]— a pointer to the whole ring: the variable's history as an arrayx FUTURE— the language's speculative read (the C backend synthesizesALB_TEMP_FUTURE_*functions for it)
REVERSIBLE — the compiler says no
A REVERSIBLE … END block restricts you to statements that are mathematically invertible: Rev_Add, Rev_Sub, Rev_Xor, Rev_Rol, Rev_Ror, Rev_Swap, Rev_Not, Rev_Neg. Every one has an exact inverse — and every statement that wouldn't be invertible (multiply, divide, I/O, a normal assign that forgets the old value) is rejected at compile time: "non-reversible statement inside REVERSIBLE." The emitter honors the restriction literally — emits width-masked rol/ror against byte/word/dword/qword targets. Landauer-compliant BASIC: a block where nothing is ever erased, so in principle the whole section could run backwards. Supported on fasm, fasm16, and c — the compiler enforces that too.
The learning runtime — forged by the compiler
MARKOV MODEL, PREDICT MARKOV, NEURAL TOPOLOGY, INFER NETWORK, and TRAIN NETWORK aren't library calls — they're language-level statements with their own AST node kinds. When albt sees one, it registers the model in compile-time tables (up to 128 Markov models and 32,768 transition weights; up to 64 neural topologies sharing 512 layer slots), bakes the declared matrices straight into .data, and flips a Runtime_Used flag. Then the compiler's own Phase 4.5 — "Forging FASM Extended Runtime" — appends only the runtimes that were actually requested:
ALB_Markov_Predict:— a ~30-instruction argmax: index the transition-matrix row for the current state, walk it once, return the likeliest next stateALB_NN_Apply_Activation:— activation by code: linear, ReLU, step, and a clamped ±128→×8 scaled tanh — all integersALB_NN_Infer:— a pure-integer forward pass over a meta-descriptor of layer sizes, offsets, weights, and biasesALB_NN_Train:— a perceptron-style delta rule (delta ∈ {-1, 0, +1}: nudge the bias, nudge each weight where the input fired) refining the output layer per epoch
And the weights themselves are seeded by the compiler: Seed_Neural_Weight in albt assigns deterministic structured guesses — identity patterns for same-size layers (weight 256 = 1.0 at ¹⁄₂₅₆ scale), +256/−256 split-pairs when a layer halves, wrap-around when it grows. No RNG; the same program always starts from the same brain.
Sockets, sniffers, and sandboxed children
The extended runtime also carries real machinery most languages farm out to libraries:
- **
ALB_NET_*— a socket runtime in raw asm —Init,Listen_Config,Accept_Config,Send_Config,Receive_Config,Close_Handle— withALB_NET_Load_Procresolvingws2_32exports at runtime**, so the EXE carries no link-time Winsock dependency - **
ALB_NET_Sniff_*— a packet sniffer** — declared sniffers (interface, protocol, port, buffer size — up to 64) getSniff_Config+Resolve_Sniff_IP+ a per-sniffer…_ERRORcell - **
ALB_PROC_*— a child-process sandbox** —Create_Child,Register/Unregister_Child,Resolve_Handle,Is_Owned_Handle(a handle you didn't spawn is not yours to kill),Is_Current_Image,Read/Write_ScalarandRead/Write_Bufferinto the child's memory,Dump, andTerminate_Safe— "safe process" as a security model, not a hope
Formats and escapes
MOUNT ARCHIVEblocks —ALB_ARCHIVE_Path+ a mounted-archive reader (Archive_Runtime_Used)STREAM BYPASSblocks —ALB_STREAM_Src/ALB_STREAM_Copy, a fast path that skips the normal file plumbingALB_FILE_XOR_Transform— XOR a whole file through a key, in asmALB_MEM_Zero— explicit memory zeroing, emitted when secure wipes are wantedEXPORT PPMblocks —ALB_PPM_Export/ALB_PPM_Exitdump the framebuffer to a.ppmimage — the world's simplest image format, hand-assembled
COMPTIME — the compile-time interpreter
A COMPTIME … END block doesn't emit code — it runs inside the compiler. albt carries a real interpreter for it: COMPTIME_WHILE loops (capped at 1,048,576 steps), scalars and 8-dimensional arrays (262,144 values), COMPTIME_PRINT, and arithmetic — then bakes the results into .data via the Emit_Comptime_Bakes pass. Compile-time function execution, in a BASIC, producing literal bytes the program just reads. SYNTH BAKE does the same trick for audio: waveform data computed at compile time, no runtime synthesis cost.
The statement zoo — the rest of the surface
Grouped, since a flat list wouldn't do it justice:
- Canvas and window —
SET FULLSCREEN/RESIZABLE/STRETCHY,SET SHOEBOX(fasm/c: a letterboxed virtual canvas that scales to the window —VIRTUAL_WIDTH/VIRTUAL_HEIGHT/VMOUSE_X/VMOUSE_Y),VIEWPORTdeclarations withRATIO SPACEblocks andRATIO PINchains, andMORTON TILE— Z-order-curve iteration over surfaces, the cache-friendly traversal pattern as a language statement - Retro graphics —
COLOR_LUTdeclarations andAPPLY LUT(palette-table graphics, the way the classics did it),BITMAP FONTandSYSTEM FONTdeclarations,BLIT SAFE, andPOLY(fasm only — FASM16 steers you to rect/line/circle/triangle) - FASM-only declarations —
STATIC SPRITE(sprite data baked at compile time) andVISUAL RULE/VISUAL WHEN— declarative visual rules the backend emits as code - Bare metal, continued —
VIRTUAL_ALLOC/VIRTUAL_FREE/VIRTUAL_PROTECT(raw Win32 virtual memory — the compiler gates these to-t fasm),SIMDintrinsics,PEEK/POKE, bitfield declarations, andINLINEC's siblingENABLE ASM/DISABLE ASM— the polyglot block family (ENABLE C/ADA/LUA/PYTHON/…) exists in the grammar, but ASM is the native dialect here - Contracts and control —
REQUIRE/ENSURE/CONSTRAIN TOclauses on routines,STRICT,TRY/CATCH/THROW,SELECT/MATCH, andFALLBACKblocks (a 32-deep mode stack of named fallback labels, no heap) - State and time —
SAVE STATE/LOAD STATE(whole-program checkpoint viacall ALB_Save_State— shared with FASM16),ADVANCEto step temporal rings forward (also as a postfix op), and the chrono-global registry for time-aware state - Files with teeth —
ENCRYPT FILE/DECRYPT FILEride theALB_FILE_XOR_Transform;READLINE,FLUSH, and the full seek suite round out I/O
Concurrency, honestly stated
SPAWN on this backend is documented in the emitter's own words as "synchronous on FASM (same-core call)" — and SYNC/ATOMIC lower to mfence. The honesty matters: the multi-core dispatch is real on the ALB C backend, deliberately sequential here.
The blackbox
--embed-blackbox installs a VEH crash reporter — a vectored exception handler and a heap-free append-only dump path (ALB_BB_Append_Z, ALB_Blackbox_Handler) that writes a forensic record when a native fault would otherwise vanish silently. A flight recorder for the generated executable, compiled in only when asked for.
Under the hood — the emitter itself
The backend is the Ada package Emit_Native_FASM (~7,300 lines, SPARK-annotated). Its design is a small lesson in compiler construction:
- Two text buffers —
Buffer_Global(lands in.data/.bss) andBuffer_Main(lands in.text) — so declarations and code accumulate independently and merge at flush time - **A stable Emit_* API** —
Emit_If_Start,Emit_For_Start,Emit_BinOp… The same call surface every backend implements, which is how one front end drives ~23 targets - Expression scrubbing — before emitting an operator the emitter backspaces trailing newlines so infix text lands on one line, which is what makes the
ALB_LETmatch-macro possible
See also
- AdaLogic BASIC — the language this emits
- ALB FASM16 backend — its 16-bit sibling
- ALB C backend — the portable sibling
- History of Programming — where assemblers fit in the long arc
- Konrad Zuse · Z3 · Plankalkül — the other end of that arc