Skip to content
ALB Wiki

Diff — ALB FASM backend

Comparing rev 5 → rev 6

{{infobox
| title = ALB FASM backend
| target_flag = `-t fasm`
| produces = x86-64 Flat Assembler source (.asm)
| output = Windows PE64 — console, GUI, or DLL
| assembler = [Flat Assembler](https://flatassembler.net) (fasm.exe)
| emitter_unit = `Emit_Native_FASM` (emit_native_fasm.adb)
| status = Primary native backend; `--build` supported
}}
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](https://flatassembler.net), 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.
```alb One-liner
albt hello.alb -t fasm --build
→ fasm_hello/hello.asm → fasm.exe → hello.exe (a few kilobytes, instantly)
```
It is one of only three backends — with [[ALB C backend|C]] and [[ALB FASM16 backend|FASM16]] — where `--build` is supported end to end: the compiler emits the file **and** drives the assembler.
-
## What it produces
+
# What it produces
Every generated file opens the same way — a comment banner, then the PE64 header:
```text
; ==========================================================
; 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
+
# The file anatomy
A FASM output is organized into the classic PE sections:
- **`.text`** — code: the runtime library, then your program under `start:`
- **`.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
+
# 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**:
```text
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
+
# The hardened runtime
ALB does not trust the machine blindly — the emitted program carries a safety runtime:
```text
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_Flush` whole-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_Symbol` loads DLL functions at runtime; ALB's `IMPORT_DLL` lowers to direct calls
```fact The odd superpowers
`CLAIM/DROP/BIND/SWEEP` memory ownership, and **native ASM blocks** — raw assembly embedded inside ALB source for when only a specific instruction will do. (Temporal variables and reversible blocks get their own sections below — they earned it.)
```
-
## The logic engine — Prolog in machine code
+
# 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
```asm
invoke ALB_Assert, addr father_str, homer_bart_arg, 0
fastcall ALB_Query, <'father', ...>
```
— the **Logic** pillar of the name (Prolog and Datalog), executing in a PE with no logic library, no VM, and no garbage collector required to answer a query.
-
## The temporal engine
+
# 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`:
```asm
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 value
- **`x PAST`** — `history[head - 1]` with wraparound: literally *the previous write*, a variable you can read backwards
- **`x TIMELINE`** — `lea rax, [ALB_TEMP_x_history]` — a pointer to the *whole ring*: the variable's history as an array
- **`x FUTURE`** — the language's speculative read (the C backend synthesizes `ALB_TEMP_FUTURE_*` functions for it)
```fact Why this is stranger than it sounds
A temporal variable is a tiny tape — and `PAST` isn't a debugger trick or a log replay. The generated code just keeps the last N values resident and indexes one back. Time travel, reduced to a ring buffer and two instructions.
```
-
## REVERSIBLE — the compiler says no
+
# 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
+
# 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 state
- **`ALB_NN_Apply_Activation:`** — activation by code: linear, ReLU, step, and a clamped ±128→×8 scaled tanh — all integers
- **`ALB_NN_Infer:`** — a pure-integer forward pass over a meta-descriptor of layer sizes, offsets, weights, and biases
- **`ALB_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.
```warn FASM-only: EMOTION
One learning feature is exclusive to this backend: `EMOTION` — declared emotional-state axes (max 8) baked as `ALB_EMOTION_<name>` byte tables, steered with `SET_AXIS` / `ADD_AXIS` / `GET_AXIS` statements — plus `ALB_Emotion_Bias_Inputs`, `ALB_Emotion_Dominant`, and `PREDICT MARKOV … WITH EMOTION` (`ALB_Markov_Predict_Emotion`), a predictor whose next-state choice is bent by the emotion vector. The compiler itself enforces the exclusivity: *"EMOTION requires -t fasm (not available on FASM16/other backends)."*
```
-
## Sockets, sniffers, and sandboxed children
+
# 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` — with `ALB_NET_Load_Proc` resolving `ws2_32` exports **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) get `Sniff_Config` + `Resolve_Sniff_IP` + a per-sniffer `…_ERROR` cell
- **`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_Scalar` and `Read/Write_Buffer` into the child's memory, `Dump`, and `Terminate_Safe` — "safe process" as a security model, not a hope
-
## Formats and escapes
+
# Formats and escapes
- **`MOUNT ARCHIVE` blocks** — `ALB_ARCHIVE_Path` + a mounted-archive reader (`Archive_Runtime_Used`)
- **`STREAM BYPASS` blocks** — `ALB_STREAM_Src`/`ALB_STREAM_Copy`, a fast path that skips the normal file plumbing
- **`ALB_FILE_XOR_Transform`** — XOR a whole file through a key, in asm
- **`ALB_MEM_Zero`** — explicit memory zeroing, emitted when secure wipes are wanted
- **`EXPORT PPM` blocks** — `ALB_PPM_Export`/`ALB_PPM_Exit` dump the framebuffer to a `.ppm` image — the world's simplest image format, hand-assembled
-
## COMPTIME — the compile-time interpreter
+
# 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
+
# 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`), `VIEWPORT` declarations with `RATIO SPACE` blocks and `RATIO PIN` chains, and `MORTON TILE` — Z-order-curve iteration over surfaces, the cache-friendly traversal pattern as a language statement
- **Retro graphics** — `COLOR_LUT` declarations and `APPLY LUT` (palette-table graphics, the way the classics did it), `BITMAP FONT` and `SYSTEM FONT` declarations, `BLIT SAFE`, and `POLY` (fasm only — FASM16 steers you to rect/line/circle/triangle)
- **FASM-only declarations** — `STATIC SPRITE` (sprite data baked at compile time) and `VISUAL 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`), `SIMD` intrinsics, `PEEK`/`POKE`, bitfield declarations, and `INLINEC`'s sibling `ENABLE 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 TO` clauses on routines, `STRICT`, `TRY`/`CATCH`/`THROW`, `SELECT`/`MATCH`, and `FALLBACK` blocks (a 32-deep mode stack of named fallback labels, no heap)
- **State and time** — `SAVE STATE` / `LOAD STATE` (whole-program checkpoint via `call ALB_Save_State` — shared with FASM16), `ADVANCE` to step temporal rings forward (also as a postfix op), and the chrono-global registry for time-aware state
- **Files with teeth** — `ENCRYPT FILE`/`DECRYPT FILE` ride the `ALB_FILE_XOR_Transform`; `READLINE`, `FLUSH`, and the full seek suite round out I/O
```note What the compiler itself admits
`albt.adb` is ~42,000 lines and the AST has ~250 node kinds. The honest summary isn't "a BASIC that emits asm" — it's a multi-paradigm language where Prolog, temporal logic, neural inference, reversible computing, compile-time evaluation, and capability security all happen to lower to `mov`.
```
-
## Concurrency, honestly stated
+
# 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
+
# 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
+
# 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`) and `Buffer_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_LET` match-macro possible
```note Where it lives
In the source tree: `EMITTER/emit_native_fasm.adb`, driven by `albt.adb`. Emitted sources land in `fasm_&lt;name&gt;/` by default. The whole toolchain is Apache 2.0.
```
-
## See also
+
# 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
[[Category:Backends]]
[[Category:AdaLogic BASIC]]