← Hub
Handbook babel8-v2
Open the VM →

The complete field manual

Babel-8 and Babble,
without the mystery.

Start here if “four framebuffers,” palette slots, wrapping memory, or yielding once per frame sounds like fantasy-computer folklore. This handbook explains the high-level Babble language and every Babel-8 v2 machine operation, including the strange corners that make random bytecode safe and interesting.

01 / first program

Start in five minutes

Babble is the C-like source language. Babel-8 is the bytecode machine that runs the result. Ordinary programs should start in Babble; raw Babel instructions are useful when inspecting generated code, generating random artifacts, or exploiting operations the high-level compiler does not expose yet.

u8 x = 28;
u8 y = 28;

fn main() {
    palette(0, NAVY);
    palette(1, CYAN);

    layer(0);
    fill(0);                  // opaque background using palette slot 0

    loop {
        layer(1);
        clear();              // erase last frame's moving layer

        if (held(LEFT))  { x--; }
        if (held(RIGHT)) { x++; }
        if (held(UP))    { y--; }
        if (held(DOWN))  { y++; }

        fill_rect(x, y, 4, 4, 1, REPLACE);
        yield;                // let the browser present this frame
    }
}

The central trick: draw the permanent background once on layer 0, then clear and redraw only moving objects on layer 1. Clearing layer 1 reveals layer 0 instead of destroying it.

02 / what exists

The machine model

Artifact

Exactly 1,024 immutable bytes: 512 code bytes followed by 512 asset bytes.

Code

256 fixed instruction slots. Every instruction is exactly an opcode byte plus an operand byte.

CPU

Eight byte registers, two 10-bit index registers, an 8-bit instruction PC, and zero/carry flags.

Memory

1,024 writable RAM bytes, 512 immutable asset bytes, a 32-address return stack, and a 64-byte data stack.

Display

Four independently drawn 64×64 layers. Each pixel stores a palette slot and a separate visibility bit.

Devices

Eight buttons, four audio voices, four frame timers, a deterministic RNG, and a 32-bit frame counter.

The machine never faults. Addresses, registers, stacks, and the PC wrap. Division by zero has a defined result. All 256 opcode values do something deterministic. This is why arbitrary 1,024-byte files can be run without validating them first.

03 / compositor

Why there are four framebuffers

They are layers, numbered 0 through 3. Layer 0 is the bottom; layer 3 is the top. Every drawing operation affects only the active layer selected by layer(n). At presentation time, the compositor checks layer 3, then 2, then 1, then 0, and displays the first visible pixel.

LayerConventional useHow often to redraw
0Background, map, terrain, star fieldOnce, or only when the room changes
1Players, enemies, moving world objectsClear and redraw every gameplay frame
2Particles, flashes, projectiles, temporary effectsClear/redraw or scroll independently
3HUD, score, menus, cursorOnly when UI state changes

Transparent is not black

Every layer pixel has a two-bit color slot and a separate visibility bit. A transparent pixel reveals the layer below. A visible pixel whose color is slot 0 covers lower layers with whatever master color slot 0 currently selects—often black.

clear() makes the active layer transparent. fill(0) makes it opaquely color 0. Those operations look similar on layer 0 but behave completely differently on upper layers.

Moving without damaging the background

// Draw once.
layer(0);
fill(NAVY);         // Wrong: fill expects slot 0–3, not master color.

// Correct:
palette(0, NAVY);
layer(0);
fill(0);

// Every frame:
layer(1);
clear();
sprite1(x, y, hero, 2, REPLACE);

Flips, scrolling, rotation, inversion, row shift, and column shift affect only the active layer. They move colors and visibility together. Pixels revealed by scrolling are transparent.

04 / palette

Choosing colors

Screen pixels store only a slot from 0 to 3. The active palette maps those four slots to four of the machine's 16 master colors. Calling palette(slot, masterColor) changes the meaning of every existing pixel using that slot immediately.

palette(0, BLACK);
palette(1, CYAN);
palette(2, ORANGE);
palette(3, WHITE);

fill_rect(4, 4, 20, 10, 2, REPLACE); // uses slot 2 → ORANGE
0 · BLACK
#000000
1 · NAVY
#1B1F3B
2 · PURPLE
#4C2A63
3 · PLUM
#8B3A62
4 · RED
#C94C4C
5 · ORANGE
#E47D3E
6 · YELLOW
#F2C14E
7 · CREAM
#F6E8B1
8 · FOREST
#2D6A4F
9 · GREEN
#40916C
10 · MINT
#52B788
11 · CYAN
#48CAE4
12 · BLUE
#277DA1
13 · INDIGO
#5A67D8
14 · VIOLET
#C77DFF
15 · WHITE
#FFFFFF

Blend modes

ConstantValueEffectGood for
REPLACE0Store source color and make visibleNormal drawing
XOR1destination XOR sourceBlinking cursors, reversible effects
ADD2(destination + source) & 3Two-bit glow and additive effects
ERASE3Clear visibility; stored color remainsReveal lower layers

XOR and Add treat a transparent destination as color 0, then make it visible. Palette changes are effectively free color cycling because pixel data does not change.

05 / time

Frames, yield, sleep, and work

A host frame samples buttons, increments the frame counter, ticks timers and timed voices, then gives the CPU 16,384 work units. The CPU continues until it yields, sleeps, halts, reaches a breakpoint, or cannot fit the next atomic instruction into the remaining budget.

When to yield

Put yield; once at the end of the main game loop. It declares that the frame is complete and execution should resume at the following instruction next frame.

fn main() {
    setup();
    loop {
        read_input();
        update_game();
        draw_frame();
        yield;
    }
}

Without yield, a loop can run many times in one host frame until the work budget is exhausted. Button edges, timers, and the displayed image will not advance between those iterations.

Yield versus sleep

OperationMeaning
yieldStop this frame; resume next frame.
sleep(n)Stop now and skip n complete future frames before resuming.
sleep(0)Behaves like yield.
timer_setStarts a timer that ticks independently while your code continues or sleeps.
halt/return from mainStop indefinitely until reset.

Expensive instructions are atomic. A 16,384-cost full-layer hash either runs completely or waits for a fresh frame budget; it never produces a half-hashed state.

06 / controls

Held, pressed, and released

FunctionTrue whenTypical use
held(key)The key is currently downMovement or sustained action
pressed(key)The key changed from up to down this frameMenus, firing once, toggles
released(key)The key changed from down to up this frameCharge attacks, drag end

Keys are LEFT, RIGHT, UP, DOWN, A, B, START, and SELECT.

Edges are sampled once per host frame. If you test pressed(A) repeatedly before yielding, it remains true for that entire frame.

07 / high-level language

Babble language reference

Values and declarations

u8 score = 0;                    // global byte
u8 map[64] = [1, 2, 3, 4];      // remaining elements start at zero
const SPEED = 2;                 // compile-time value
asset hero = [0x18, 0x3C, 0x7E, 0xDB, 0xFF, 0x24, 0x42, 0x81];

fn add(u8 left, u8 right) {
    u8 result = left + right;    // local byte
    return result;
}

All scalar values are unsigned bytes. Arithmetic wraps modulo 256: 255 + 1 is 0 and 0 - 1 is 255. Arrays occupy consecutive RAM bytes. Assets occupy immutable asset ROM and do not consume RAM.

Operators

GroupOperatorsNotes
Arithmetic+ - * / %Unsigned byte results; division by zero gives zero, modulo zero preserves the dividend at machine level.
Bits& | ^ ~ << >>Shifts use a compile-time amount and only its low three bits.
Comparison== != < <= > >=Unsigned comparisons returning 0 or 1.
Boolean! && ||Zero is false; nonzero is true.
Assignment= += -= *= /= %= ++ --All results wrap to a byte.

Control flow

if (score >= 10) { score = 0; } else { score++; }

while (timer_get(0) != 0) { yield; }

for (u8 i = 0; i < 16; i++) {
    map[i] = i * 3;
}

switch (mode) {
    case 0: fill(0); break;
    case 1: fill(1); break;
    default: clear(); break;
}

loop {
    if (pressed(B)) { break; }
    if (!held(A)) { yield; continue; }
    score++;
}

Functions and recursion

Functions accept up to four u8 parameters. Arguments travel through R0–R3 and the return value uses R0. Recursive calls are supported by saving the caller's static parameter/local frame and live expression registers on the 64-byte data stack.

Recursion must be bounded. The return stack has 32 entries and the data stack has 64 bytes. Both wrap instead of faulting, so excessive recursion corrupts older caller state rather than producing a friendly error.

Compiler behavior

Globals and locals receive fixed RAM addresses. Initial values are emitted as startup instructions. The optional ROM optimizer folds constants, emits direct comparison branches, threads jumps, and removes redundant or unreachable instructions. The artifact is always 1,024 bytes even when only a few code slots are used.

08 / standard library

Every Babble builtin

FunctionSignatureWhat it does and what to watch
Layerlayer(n)Select layer 0–3. Every later drawing or layer transform uses this layer until changed.
Clearclear()Make every active-layer pixel transparent. This reveals lower layers; it does not paint black.
Fillfill(color)Opaque-fill with palette slot 0–3. Pass a slot, not a master color constant.
Pixelpixel(x, y, color, blend = REPLACE)Draw one wrapped pixel on the active layer using a palette slot and blend mode.
Eraseerase(x, y)Clear one visibility bit, exposing lower layers.
Read layerread_layer(x, y)Return slot 0–3 for a visible active-layer pixel or 255 when transparent.
Read screenread_screen(x, y)Return the final composited slot 0–3. It never returns transparency.
Horizontal linehline(x, y, length, color)Draw 1–64 pixels using Replace. Coordinates wrap.
Vertical linevline(x, y, length, color)Vertical equivalent of hline.
Rectanglerect(x, y, width, height, color, blend)Draw a wrapped outline, maximum 16×16 per instruction. Corners are attempted by touching edges.
Filled rectanglefill_rect(x, y, width, height, color, blend)Draw a wrapped filled rectangle, maximum 16×16 per instruction.
1-bit spritesprite1(x, y, asset, color, blend)Draw an 8-byte 8×8 sprite. One bits draw; zero bits are transparent.
2-bit spritesprite2(x, y, asset, transparent, blend)Draw a 16-byte packed 8×8 sprite; choose one source color as transparent.
Heldheld(key)Return 1 while a button is down.
Key aliaskey(key)Legacy alias for held(key). Prefer held in new source.
Pressedpressed(key)Return 1 on the up-to-down edge sampled for the current frame.
Releasedreleased(key)Return 1 on the down-to-up edge sampled for the current frame.
Randomrandom(mask = 0)Advance deterministic RNG and XOR the low byte with the mask. The artifact seeds the sequence.
Frameframe(byte = 0)Read byte 0–3 of the 32-bit frame counter; byte 0 changes every frame.
Get timertimer_get(timer)Read timer 0–3. Nonzero timers decrement at the start of each host frame.
Set timertimer_set(timer, frames)Write an 8-bit countdown. Useful for cooldowns without blocking.
Voicevoice(voice, tone, waveform, volume)Start/update an indefinite voice. Tone 0 or volume 0 is silent.
Beepbeep(voice, tone, waveform, durationCode)Play at volume 15 for 1, 2, 4, 8, 16, 32, 64, or 128 frames.
Silencesilence(voice)Stop one of four voices.
Silence allsilence_all()Stop all four voices.
Palettepalette(slot, masterColor)Map active slot 0–3 to master color 0–15. Existing pixels recolor immediately.
Sleepsleep(frames)End this frame and skip that many complete future frames. Devices still tick.
Yieldyield or yield()End CPU execution for this frame and resume at the next instruction next frame.
1-bit collisioncollide1(x, y, asset, fullScreen = 0)Test visible sprite bits against lower layers, or the full composite when requested. Does not draw.
2-bit collisioncollide2(x, y, asset, transparent, fullScreen = 0)Collision test using a selected transparent source color.
Invertinvert()XOR visible active-layer colors with 3. Transparent pixels remain transparent.
Scroll leftscroll_left(amount)Move active layer left 1–64 pixels; vacated pixels become transparent.
Scroll rightscroll_right(amount)Move active layer right.
Scroll upscroll_up(amount)Move active layer up.
Scroll downscroll_down(amount)Move active layer down.

Constants accepted by builtins

Keys: LEFT RIGHT UP DOWN A B START SELECT

Blend modes: REPLACE XOR ADD ERASE

Waveforms: SQUARE TRIANGLE SAW SINE

Master colors: BLACK NAVY PURPLE PLUM RED ORANGE YELLOW CREAM FOREST GREEN MINT CYAN BLUE INDIGO VIOLET WHITE

09 / storage

RAM, assets, and stacks

RAM

RAM is 1,024 mutable bytes. Babble assigns globals, parameters, locals, and arrays fixed addresses. Array indexes are not bounds-checked; an out-of-range index continues into neighboring RAM and ultimately wraps at 1,024.

Asset ROM

Asset ROM is 512 immutable bytes stored in the second half of the artifact. Use it for sprites, maps, lookup tables, or arbitrary constants. Asset addresses wrap modulo 512. A one-bit sprite consumes 8 bytes; a two-bit sprite consumes 16.

X and Y index registers

Raw Babel code uses 10-bit X and Y for the full RAM address range, asset access, block copying, sprite sources, and bulk operations. Direct LOAD and STORE reach only addresses 0–255; LOADX/STOREX reach all 1,024 bytes through X or Y plus an encoded offset.

Two separate stacks

StackCapacityUsed by
Return32 instruction addressesCALL, RET, Babble function calls
Data64 bytesPUSH, POP, compiler-preserved recursive frames

Both stacks are circular. Pushing when full overwrites the oldest logical entry. Popping when empty returns zero.

10 / patterns

Practical recipes

Stable background

Draw it once on layer 0. Never clear that layer during ordinary movement.

Moving actor

Every frame: select layer 1, clear, update position, draw actor, yield.

Screen flash

Fill layer 2, yield or sleep briefly, invert it, then clear it to reveal gameplay.

Palette animation

Keep pixel slots unchanged and rotate their master-color mappings with palette.

Cooldown

On action, set a timer. Permit the next action only when timer_get returns zero.

Collision without drawing

Select the actor layer, test its sprite against lower layers, then decide whether to move.

Palette cycling

switch (frame(0) & 15) {
    case 0: palette(1, CYAN); break;
    case 8: palette(1, VIOLET); break;
    default: break;
}

Non-blocking fire cooldown

if (pressed(A) && timer_get(0) == 0) {
    spawn_projectile();
    beep(0, 110, SQUARE, 2);
    timer_set(0, 12);
}

Recursive work followed by halt

u8 input = 10;
u8 result;

fn fib(u8 n) {
    if (n < 2) { return n; }
    return fib(n - 1) + fib(n - 2);
}

fn main() {
    result = fib(input);
    return; // return from main compiles to HALT
}

11 / sharp edges

Important quirks

Everything wraps

Bytes, coordinates, PC, memory addresses, index registers, and stacks wrap instead of faulting.

Color 0 is not transparent

Visibility is separate. Use erase/clear to reveal lower layers.

Coordinates wrap at 64

A sprite or rectangle crossing an edge appears on the opposite side.

Subtraction carry means no borrow

After compare/subtract, C=1 means the unsigned left side was greater than or equal to the right.

Input edges last a frame

pressed remains true until execution yields and the host samples another frame.

Random depends on the artifact

Changing or optimizing code changes the artifact seed and therefore the deterministic random sequence.

Layer 0 starts visible

At reset it is opaque color slot 0; layers 1–3 begin transparent.

Padding is executable

Unused code bytes are zero, which means LDI R0, 0, not invalid memory.

Branch addresses are instructions

Target 0x20 means instruction slot 32, stored at artifact byte offset 0x40.

Expensive operations are atomic

If an operation does not fit this frame's remaining budget, it waits untouched for the next frame.

Rectangle corners repeat

Raw rectangle edges attempt touching corners more than once; XOR/Add can expose that drawing order.

Decompiler output is pseudo-source

Names and structure are lost. Raw registers, goto, and asm helpers may not recompile.

12 / raw machine

Bytecode anatomy

Instruction n lives at code bytes 2n and 2n+1. The first byte is the opcode; the second is an immediate, register selector, descriptor, or branch destination depending on the opcode.

00: 00 2A    LDI R0, 0x2A
01: 10 01    ADDI R0, 0x01
02: 48 05    STORE [0x05], R0
03: F1 00    HALT

Flags

Z means the most recent flag-setting result was zero. Addition sets C on carry. Subtraction and comparison set C when no borrow was required. Many movement, memory, drawing, and device instructions preserve flags.

Conditional move conditions

CodeNameCondition
0ZZero
1NZNot zero
2CCarry / no borrow
3NCNo carry / borrow
4GTC and not Z, unsigned greater-than after CMP
5LENot C or Z, unsigned less-than-or-equal
6ALAlways
7NVNever

13 / complete ISA

Every Babel-8 instruction

Ranges with eight opcodes use the opcode's low three bits to choose R0–R7. Ignored operand bits may contain anything. All operations below are valid for every operand byte.

00–0F · Data movement

OpcodesInstructionOperation and flags
00–07LDI Rv, imm8Load operand into Rv. Flags unchanged.
08–0FMOV Rv, RsCopy register selected by operand bits 2–0. Flags unchanged.

10–1F · Immediate arithmetic

OpcodesInstructionOperation
10–17ADDI Rv, imm8Add operand, wrap to byte, update Z and carry C.
18–1FSUBI Rv, imm8Subtract operand, update Z and no-borrow C.

20–2F · ALU and conditional moves

20–27 ALU Rv, Rs uses operand bits 7–4 for the operation and bits 2–0 for Rs.

ModeNameEffect
0ADDRv += Rs; Z and carry.
1ADCRv += Rs + C; Z and carry.
2SUBRv -= Rs; Z and no-borrow C.
3SBCRv -= Rs + (C ? 0 : 1).
4MULMultiply; C indicates overflow.
5DIVUnsigned division; divisor zero gives 0 and C=1.
6MODUnsigned remainder; divisor zero preserves Rv and sets C.
7ANDBitwise AND; update Z, clear C.
8ORBitwise OR; update Z, clear C.
9XORBitwise XOR; update Z, clear C.
ACMPFlags from Rv - Rs without writing a register.
BMINUnsigned minimum; update Z, preserve C.
CMAXUnsigned maximum; update Z, preserve C.
DSWAPExchange Rv and Rs; Z from new Rv.
ETESTFlags from Rv & Rs without writing; clear C.
FMOVFCopy Rs to Rv and update Z.

28–2F CMOV Rv, Rs copies Rs only when operand bits 5–3 select a true condition: Z, NZ, C, NC, GT, LE, always, or never. CMOV never changes flags.

30–3F · Shifts and unary transforms

Opcodes/modeInstructionEffect
30–37 / 0SHLLogical left shift by operand low 3 bits.
30–37 / 1SHRLogical right shift.
30–37 / 2ROLRotate left.
30–37 / 3RORRotate right.
38–3F / 0INCIncrement; C when old value was FF.
38–3F / 1DECDecrement; C when old value was 00.
38–3F / 2NEGTwo's-complement negate; C for 80.
38–3F / 3NOTBitwise invert.
38–3F / 4CLZCount leading zero bits; zero input returns 8.
38–3F / 5BITREVReverse all eight bits.
38–3F / 6SWAPNIBExchange high and low nibbles.
38–3F / 7ABSAbsolute value treating input as signed byte.

40–5F · Memory access

OpcodesInstructionAddressing
40–47LOAD Rv, [imm8]Read RAM address 0–255. Flags unchanged.
48–4FSTORE [imm8], RvWrite RAM address 0–255.
50–57LOADX RvBit 7 selects X/Y, bit 6 RAM/asset, low 6 bits offset.
58–5FSTOREX RvBit 7 selects X/Y; low 7 bits offset; destination is RAM.

Effective RAM addresses wrap at 1,024; asset addresses wrap at 512.

60–6F · Index registers

OpcodeInstructionEffect
60SETXLSet low byte of X.
61SETXHSet high two bits of X from operand low two bits.
62SETYLSet low byte of Y.
63SETYHSet high two bits of Y.
64ADDXAdd signed operand to X, wrap at 1,024.
65ADDYAdd signed operand to Y.
66XFROMLoad X from an adjacent wrapping register pair.
67YFROMLoad Y from a register pair.
68XTOWrite X low byte/high two bits to a register pair.
69YTOWrite Y to a register pair.
6ASWAPXYExchange X and Y.
6BINCXIncrement X.
6CDECXDecrement X.
6DINCYIncrement Y.
6EDECYDecrement Y.
6FCMPXYZ when equal; C when X ≥ Y.

70–7F · Control flow

OpcodeInstructionEffect
70JMP addrAlways branch to absolute instruction address.
71JZ addrBranch when Z=1.
72JNZ addrBranch when Z=0.
73JC addrBranch when C=1.
74JNC addrBranch when C=0.
75CALL addrPush already-advanced PC on return stack, then branch.
76RETPop return stack into PC.
77YIELDEnd current host frame.
78–7FDJNZ Rv, addrDecrement selected register and branch if nonzero.

80–8F · Pixels and reads

80–87 PIXEL Rx: operand low 3 bits select y register; bits 4–3 pen; bits 6–5 blend; bit 7 increments x register after drawing.

88–8F PGET Rd: operand selects x and y registers; bit 6 reads the composite instead of active layer. Transparent active-layer reads return FF; composite reads always return 0–3.

90–9F · Shapes

OpcodesInstructionDescriptor
90–93HLINE RbLength=(low 6 bits)+1; pen=high 2 bits.
94–97VLINE RbSame descriptor, vertical.
98–9BRECT RbWidth=low nibble+1; height=high nibble+1.
9C–9FFILLRECT RbSame dimensions, filled.

Base register supplies x; the next supplies y. Rectangles take pen and blend from the following two registers. All coordinates wrap.

A0–AF · Compositor and whole-layer operations

OpcodeInstructionEffect
A0LAYERSelect operand low two bits.
A1CLEARBit 7: transparent clear; otherwise opaque fill with low two bits.
A2INVERTVisible colors XOR 3.
A3FLIPXMirror active layer horizontally.
A4FLIPYMirror vertically.
A5SCROLLLScroll left by low 6 bits + 1.
A6SCROLLRScroll right.
A7SCROLLUScroll up.
A8SCROLLDScroll down.
A9ROTCWRotate 90° clockwise.
AAROTCCWRotate 90° counter-clockwise.
ABNOISERNG-fill layer; bit 7 makes generated zero transparent.
ACTILE1Tile one-bit 8×8 source from asset X or RAM Y.
ADTILE2Tile packed two-bit source.
AEROWSHIFTCircularly shift row R0 by signed R1.
AFCOLSHIFTCircularly shift column R0 by signed R1.

B0–BF · Sprite blitting

B0–B7 BLIT1 Rx draws an 8-byte one-bit sprite. Operand chooses y register, pen, blend, and asset-X versus RAM-Y source.

B8–BF BLIT2 Rx draws a 16-byte packed two-bit sprite. Operand chooses y register, transparent source color, blend, and source memory.

C0–CF · Random and machine reads

C0–C7 RAND Rv advances RNG and writes low RNG byte XOR operand.

READ selectorSourceArgument
0Current input maskIgnored
1Held key booleanLow 3 bits choose key
2Pressed edge maskIgnored by raw machine
3Released edge maskIgnored
4Frame-counter byteLow 2 bits
5System stateLow 5 bits
6Voice stateLow 2 bits
7Palette master colorLow 2 bits

System selectors 0–8 read PC, packed flags, X low/high, Y low/high, return depth, data depth, and active layer. Selector 10 reads sleep; 11–14 timers; 15–18 voice tones; 19–22 remaining timed-voice frames; other reserved selectors return zero.

D0–DF · Audio, palette, timers, and sleep

OpcodesInstructionEffect
D0–D7VOICE RtoneOperand: voice, waveform, volume. Indefinite until changed or silenced.
D8–DBBEEP voiceOperand: tone register, waveform, power-of-two duration code.
DCPALETTEBits 5–4 active slot; bits 3–0 master color.
DDTIMERBit 7 read/write, bits 6–5 timer, low 3 bits register.
DESLEEPImmediate complete future frames; zero yields.
DFSILENCEBit 7 all voices, otherwise selected voice.

E0–EF · Bulk, cellular, collision, and hash operations

OpcodeInstructionEffect
E0FILLRAMFill all 1,024 RAM bytes with operand.
E1XORRAMXOR every RAM byte with operand.
E2ADDRAMAdd operand to every RAM byte.
E3INVERTRAMBitwise-invert every RAM byte.
E4COPYBLOCKCopy operand+1 bytes from RAM X to RAM Y using temporary-buffer semantics.
E5ROTATERAMCircularly rotate all RAM by signed operand.
E6COLLIDE1Test one-bit sprite at R0,R1 against lower layers or full screen.
E7COLLIDE2Two-bit collision with transparent color descriptor.
E8LIFEOne Conway's Life generation over RAM as a circular 128×64 bitfield.
E9TOTALISTICTotalistic cellular generation; operand bits are next-state rule.
EAXORNEIGHBOURSEach RAM bit becomes left XOR right XOR above XOR below XOR operand bit 0.
EBFLOODFlood active layer from R0,R1; operand selects replacement pen/blend.
ECERODEErode active-layer visibility using toroidal eight-neighbor morphology.
EDDILATEDilate visibility; operand low two bits color new pixels.
EEHASHRAMFNV-1a-32 of RAM into R0–R3 little-endian.
EFHASHSCREENHash composite, active layer, or every layer according to operand.

F0–FF · Lifecycle and data stack

OpcodeInstructionEffect
F0NOPNo effect.
F1HALTStop until reset.
F2YIELDEnd current frame.
F3WAITRSleep for frames held in operand-selected register.
F4RESETCPUReset CPU, index registers, flags, and both stacks; resume at PC 0.
F5RESETRAMZero all RAM.
F6RESETDISPLAYRestore initial layers, palette, and active layer.
F7RESETALLReset mutable state except artifact, frame counter, and current/previous input.
F8–FF / 0PUSH RvPush byte on circular data stack.
F8–FF / 1POP RvPop; empty returns zero.
F8–FF / 2PEEK RvRead top without popping.
F8–FF / 3DEPTH RvRead logical data-stack depth 0–64.

14 / strange and powerful

Crazy memory operations

The E0–EF family is where Babel-8 stops behaving like a tiny ordinary CPU. These are raw machine instructions; most do not yet have direct Babble builtins. You can encounter them in random artifacts, hand-authored bytecode, disassembly, and decompiler output.

Instant transformations

  • FILLRAM, XORRAM, ADDRAM, and INVERTRAM transform all 1 KiB in one atomic instruction.
  • COPYBLOCK moves up to 256 bytes and handles overlap as if a temporary copy was made.
  • ROTATERAM rotates the entire memory image, useful for ring buffers, procedural state, or deliberate chaos.

RAM as a 128×64 cellular world

LIFE, TOTALISTIC, and XORNEIGHBOURS interpret RAM's 8,192 bits as a toroidal 128×64 grid. The source and destination are logically separate, so a generation never reads partially updated neighbors.

Hashes as tiny signatures

HASHRAM and HASHSCREEN place a 32-bit FNV-1a hash in R0–R3. They are useful for conformance tests, procedural decisions, state fingerprints, or checking that two executions remain deterministic.

These operations consume substantial work. LIFE, TOTALISTIC, ERODE, and DILATE cost 8,192 units; hashing every layer costs the entire 16,384-unit frame budget.

15 / understanding programs

Debugger, optimizer, and decompiler

Debugger

Breakpoints can be attached to source lines or raw instruction addresses. A breakpoint pauses before execution. Step instruction runs one atomic operation; step frame samples input and runs until a frame boundary. The layer inspector shows any raw layer without mutating machine state.

ROM optimizer

Enable Optimize ROM to reduce instruction count. Optimization preserves source-level behavior but changes instruction addresses, timing cost, debugger traces, artifact bytes, and therefore the artifact-seeded RNG sequence.

Decompiler

Attempt decompile follows reachable branches, identifies function candidates, names direct RAM locations, recognizes common compiler patterns, and emits pseudo-Babble. Unknown operations become asm(). It cannot recover original names, comments, scopes, or the author's exact control structures.