Title: WAFER - WebAssembly Forth Engine in Rust
Tags: wafer::ir wafer::primitives wafer::boot_fth wafer::runtime wafer::constants wafer::memory wafer::architecture wafer::intermediate wafer::advanced wafer::error wafer::forthvm wafer::optimizer wafer::codegen wafer::design wafer::design_decisions wafer::hostaccess wafer::peephole wafer::basic wafer::control_flow wafer::ir_float_arithmetic wafer::ir_control_flow wafer::testing wafer::boot wafer::ir_memory wafer::dictionary wafer::dictionary_details

Front: What are the 5 stages of the WAFER compilation pipeline?
Back: Forth Source → Outer Interpreter (tokenize + dispatch) → IR (Vec<IrOp>) → Optimizer (6 passes) → WASM Codegen (wasm-encoder) → wasmtime execution
Code: // lib.rs doc comment:
// Forth Source -> Outer Interpreter -> IR -> Optimize -> WASM Codegen
Source: crates/core/src/lib.rs:9

Front: What crate does WAFER use to generate WASM bytecode?
Back: `wasm-encoder` — builds WASM modules programmatically (types, imports, functions, code sections). NOT a text-format assembler.
Source: crates/core/src/codegen.rs:12

Front: What crate does WAFER use to execute WASM modules?
Back: `wasmtime` — Bytecode Alliance WASM runtime. Provides Engine, Store, Module, Instance, Memory, Global, Table, Func.
Source: crates/core/src/outer.rs:14

Front: How many source files are in wafer-core? Name them.
Back: 12 files: outer.rs (9820), codegen.rs (4205), optimizer.rs (1013), dictionary.rs (906), export.rs (409), runner.rs (402), ir.rs (259), consolidate.rs (169), memory.rs (148), error.rs (84), config.rs (61), lib.rs (28), js_loader.rs (163)

Front: What is the relationship between a Forth word and a WASM module in WAFER?
Back: Each compiled Forth word becomes its own WASM module with one function. Modules share memory, globals (dsp/rsp/fsp), and a function table via wasmtime imports. Words call each other via call_indirect through the shared table.

Front: What are the 6 imports every WAFER WASM module expects?
Back: 1. emit (func: i32 → void) — character output callback
2. memory (16 pages = 1 MiB) — shared linear memory
3. dsp (global mut i32) — data stack pointer
4. rsp (global mut i32) — return stack pointer
5. fsp (global mut i32) — float stack pointer
6. table (funcref) — shared function table for call_indirect
Source: crates/core/src/codegen.rs:25-41

Front: What are the 3 types of words in WAFER?
Back: 1. IR primitives — compiled to WASM via Vec<IrOp>, inlineable (DUP, +, @)
2. Host functions — Rust closures in wasmtime, NOT inlineable (., .S, M*, ACCEPT)
3. Forth-defined words — compiled by outer interpreter (: SQUARE DUP * ;)
Source: crates/core/src/outer.rs:2422-2478

Front: What is the role of outer.rs in WAFER?
Back: Contains ForthVM<R: Runtime> — the complete Forth virtual machine, generic over execution backend. Outer interpreter (tokenize → lookup → interpret/compile), all primitive registration, control-flow compilation, DOES> support. 8703 lines.
Source: crates/core/src/outer.rs:1

Front: What is the Runtime trait and why does it exist?
Back: Defined in runtime.rs. Abstracts over WASM execution backends:
- Memory r/w (mem_read_i32, mem_write_slice, etc.)
- Globals (get/set_dsp, rsp, fsp)
- Table (table_size, ensure_table_size)
- Module lifecycle (instantiate_and_install, call_func)
- Host functions (register_host_func)
Two implementations: NativeRuntime (wasmtime), WebRuntime (js-sys).
ForthVM<R: Runtime> is completely decoupled from any specific WASM engine.
Source: crates/core/src/runtime.rs:72

Front: What is HostAccess and how is it used?
Back: Trait for memory/global access from within host function callbacks.
Methods: mem_read_i32, mem_write_i32, mem_read_u8, mem_write_u8, mem_read_slice, mem_write_slice, mem_len, get/set_dsp, get/set_rsp, get/set_fsp, call_func.
NativeRuntime implements it via CallerHostAccess (wrapping wasmtime::Caller).
WebRuntime implements it via WebHostAccess (wrapping js_sys typed arrays).
HostFn = Box<dyn Fn(&mut dyn HostAccess) -> Result<()>> — same closure works on both runtimes.
Source: crates/core/src/runtime.rs:17

Front: What are the 3 crates in the WAFER workspace?
Back: 1. wafer-core — compiler, optimizer, codegen, dictionary, Runtime trait, outer interpreter
Feature flags: default=["native"], "native" enables wasmtime
2. wafer (cli) — CLI REPL (rustyline), wafer build/run commands
3. wafer-web — browser REPL (wasm-bindgen + WebRuntime + HTML/CSS/JS frontend)
Source: Cargo.toml workspace

Front: What is NativeRuntime?
Back: Wasmtime-based implementation of Runtime trait (runtime_native.rs, 328 lines).
Owns: Engine, Store<NativeVmHost>, Memory, Table, Globals (dsp/rsp/fsp), emit_func.
instantiate_and_install: Module::new + Instance::new with 6 imports.
register_host_func: creates wasmtime Func that bridges HostFn → CallerHostAccess.
Behind "native" feature flag.
Source: crates/core/src/runtime_native.rs:107

Front: What is WebRuntime?
Back: Browser-based implementation of Runtime trait (crates/web/runtime_web.rs, 542 lines).
Uses js_sys::WebAssembly for module instantiation.
Memory: JsValue wrapping WebAssembly.Memory, accessed via Int32Array/Uint8Array views.
Globals: JsValue wrapping WebAssembly.Global objects.
Host functions: JS closures created via Closure::wrap, stored in _closures Vec to prevent GC.
Runs entirely in the browser — no wasmtime dependency.
Source: crates/web/src/runtime_web.rs:12

Front: What is WaferRepl?
Back: The wasm-bindgen entry point in crates/web/src/lib.rs.
Wraps ForthVM<WebRuntime>.
Methods: new() (create VM), evaluate(input) (returns output), data_stack(), is_compiling(), reset().
Built with: wasm-pack build --target web --out-dir www/pkg
Source: crates/web/src/lib.rs:13

Front: What feature flags does wafer-core have?
Back: default = ["native"]
native — enables dep:wasmtime and all native-only modules:
runtime_native.rs, runner.rs, export.rs, consolidate.rs, js_loader.rs
Without "native" — pure Rust only: dictionary, IR, optimizer, codegen, outer interpreter, runtime trait.
wafer-web uses wafer-core without "native" feature.
Source: crates/core/Cargo.toml:11-12

Front: What is the role of codegen.rs?
Back: Translates optimized IR (Vec<IrOp>) to WASM bytecode using wasm-encoder. Handles DSP caching, scratch locals, stack-to-local promotion. Builds complete WASM modules including imports, types, function sections.
Source: crates/core/src/codegen.rs:1

Front: What is batch_mode in ForthVM and why does it exist?
Back: During boot (register_primitives), batch_mode=true defers WASM compilation. All ~40 IR primitives are collected, then compiled into a single WASM module via compile_batch(). This amortizes runtime compilation overhead — one rt.instantiate_and_install() instead of 40.
Code: self.batch_mode = true;
// ... register all primitives ...
// deferred_ir.push((word_id, ir_body));
self.compile_batch()?;
Source: crates/core/src/outer.rs

Front: Why does each Forth word compile to its own WASM module?
Back: 1. Incremental compilation: defining a new word doesn't recompile anything
2. Isolation: each word is independently validated by wasmtime
3. wasmtime linking model: modules share imports (memory, globals, table)
4. REPL-friendly: immediate feedback, no whole-program recompile
Trade-off: call_indirect overhead between words (mitigated by CONSOLIDATE)

Front: Why use an IR instead of compiling Forth directly to WASM?
Back: 1. Optimization: IR enables peephole, constant folding, inlining, DCE, tail calls
2. Separation of concerns: outer interpreter doesn't need to know WASM encoding
3. Portability: IR could target other backends
4. Testability: IR is easy to inspect and test
5. Consolidation: IR bodies are stored for later recompilation into single module

Front: Why does WAFER use wasm-encoder + wasmtime instead of Cranelift directly?
Back: 1. Standard WASM: output is valid .wasm, can run in browsers AND natively
2. Runtime abstraction: same WASM bytes work on wasmtime (CLI) and browser (js-sys)
3. wasmtime handles Cranelift internally — best JIT performance for free
4. Portability: WASM is platform-independent
5. Validation: wasmtime validates modules, catching codegen bugs
6. wasm-encoder is simple: just build bytes, no complex IR

Front: Why was the Runtime trait introduced?
Back: To support the browser REPL without duplicating the entire ForthVM.
Before: ForthVM directly owned wasmtime types (Engine, Store, Memory, etc.)
After: ForthVM<R: Runtime> is generic — same compiler code works with:
- NativeRuntime (wasmtime) for CLI/tests/AOT
- WebRuntime (js-sys) for browser
Host functions use HostFn = Box<dyn Fn(&mut dyn HostAccess)> — one closure definition serves both runtimes.
The refactor extracted ~1100 lines of wasmtime-specific code from outer.rs into runtime_native.rs.

Front: Why are stacks in linear memory instead of WASM locals?
Back: Default: stacks live in linear memory (data stack at 0x0600, grows down).
Reason: Forth semantics require stack introspection (DEPTH, PICK, SP@), which WASM locals can't provide.
Optimization: stack-to-local promotion lifts values into WASM locals when the compiler can prove stack depth is statically known (no calls, no SP@). Best of both worlds.

Front: Why is the DSP cached in a WASM local?
Back: The data stack pointer (dsp) is a wasmtime global. Globals are slower than locals. So:
1. At function entry: local.get $dsp_global → local.set $cached_dsp
2. During function: all stack ops use local $cached_dsp
3. Before calls: write back local → global (callee needs correct dsp)
4. After calls: reload local from global (callee may have changed dsp)
5. At function exit: write back
Net effect: most operations avoid global access overhead.
Code: const CACHED_DSP_LOCAL: u32 = 0;
fn dsp_writeback(f) { local.get 0; global.set $dsp }
fn dsp_reload(f) { global.get $dsp; local.set 0 }
Source: crates/core/src/codegen.rs:56-181

Front: Why does boot.fth replace Rust host functions with Forth definitions?
Back: 1. Self-hosting goal: maximize Forth, minimize Rust
2. Performance: compiled Forth with inlining + optimization beats host function dispatch (call_indirect → Rust closure has overhead)
3. Inlinability: Forth definitions have IR bodies that the optimizer can inline; host functions cannot be inlined
4. Consolidation: Forth words participate in single-module recompilation (direct calls); host functions always use call_indirect
Source: crates/core/boot.fth:1-3

Front: Why does WAFER use -1 (all bits set) for TRUE instead of 1?
Back: Forth 2012 standard: TRUE = -1 (0xFFFFFFFF). All bits set.
Reason: allows bitwise AND as a conditional select: flag AND value.
If TRUE were 1, AND would only preserve the lowest bit.
With -1: TRUE AND x = x (identity). FALSE AND x = 0.
Code: // In codegen, bool_to_forth_flag:
// 0 - result: if result=1 => -1, if result=0 => 0
f.instruction(&I32Const(0));
f.instruction(&LocalGet(tmp));
f.instruction(&I32Sub);
Source: crates/core/src/codegen.rs:214-222

Front: Why does the optimizer run peephole 5 times across the pipeline?
Back: Each optimization pass can create new peephole opportunities:
- After inline: inlined body may have adjacent ops that simplify
- After constant fold: folded constants may create identity patterns (PushI32(0), Add)
- After strength reduce: new patterns from reduced ops
- After DCE: dead code removal may leave adjacent simplifiable ops
The peephole pass itself runs to fixpoint (inner loop), but the outer pipeline runs it 5 times at different stages.
Code: // Phase 1: peephole → fold → strength → peephole
// Phase 2: inline → peephole → fold → strength → peephole
// Phase 3: dce → peephole
// Phase 4: tail_call
Source: crates/core/src/optimizer.rs:37-85

Front: Why is tail call detection the LAST optimizer pass?
Back: 1. TailCall emits WASM `return` after the call — if inlining converts TailCall back to Call (detailcall), early tail-call detection is wasted
2. DCE might eliminate the tail position entirely
3. Need return-stack balance check on FINAL IR, not intermediate
4. Inlining must happen first so we know which calls remain
Source: crates/core/src/optimizer.rs:79-84

Front: Why can't words with Exit be inlined?
Back: WASM `return` exits the CURRENT function. If an inlined word contains Exit (→ return), it would exit the CALLER's function, not just the inlined code. There's no 'return from inline' in WASM. The contains_exit() guard prevents this.
Code: fn contains_exit(ops: &[IrOp]) -> bool {
// Also blocks ForthLocalGet/Set — would collide with caller's locals
matches!(op, IrOp::Exit | IrOp::ForthLocalGet(_) | IrOp::ForthLocalSet(_))
}
Source: crates/core/src/optimizer.rs:633-664

Front: Why does CONSOLIDATE exist?
Back: Normal JIT: each word = separate module, calls via call_indirect (table lookup).
CONSOLIDATE: merges all JIT-compiled words into ONE module.
- call_indirect → direct `call` (for words in the module)
- wasmtime can optimize across call boundaries
- ~2-3x speedup for call-heavy code
External calls (host functions) remain call_indirect.
Source: crates/core/src/consolidate.rs:1-9

Front: Why does WAFER use a linked list for the dictionary instead of a hash map?
Back: 1. Forth standard specifies linked-list traversal semantics (TRAVERSE-WORDLIST)
2. Dictionary lives in linear memory (simulates WASM memory layout)
3. Standard requires specific entry format (link + flags + name + code field)
BUT: WAFER also has a HashMap index for O(1) fast-path lookup, falling back to linked-list walk for words not yet indexed. Best of both worlds.
Source: crates/core/src/dictionary.rs:10-48

Front: Why does WAFER store Forth flags as -1/0 instead of 1/0 in comparisons?
Back: Forth 2012 standard requires: TRUE = -1 (all bits set), FALSE = 0.
WASM comparisons produce 0/1, so codegen must convert:
bool_to_forth_flag: 0 - result → -1 if true, 0 if false
This is a single i32.sub instruction (cheap).

Front: What is `user_here` in ForthVM?
Back: Pointer to next free address in WASM linear memory for user data (variables, CREATE'd words). Separate from dictionary.here() which tracks dictionary-internal allocation. Synced to SYSVAR_HERE (memory offset 12) before each evaluate call.
Source: crates/core/src/outer.rs:212

Front: What is `ir_bodies` in ForthVM?
Back: HashMap<WordId, Vec<IrOp>> — stores the optimized IR body of every compiled word. Used by: (1) optimizer's inline pass to look up callee bodies, (2) CONSOLIDATE to recompile everything, (3) wafer build to export.
Source: crates/core/src/outer.rs:243

Front: What is the `control_stack` in ForthVM?
Back: Vec<ControlEntry> — compile-time stack for nested control flow. IF pushes ControlEntry::If, DO pushes ControlEntry::Do, etc. THEN/LOOP/REPEAT pop and emit the corresponding IrOp. Not the runtime return stack — this is purely compile-time.
Source: crates/core/src/outer.rs:197

Front: What is `pending_actions` in ForthVM?
Back: Arc<Mutex<Vec<PendingAction>>> — queue of actions from host functions that need compiler-side processing. Used by COMPILE, (CompileCall), CS-PICK, CS-ROLL, and POSTPONE of control-flow words. Processed after immediate word returns.
Source: crates/core/src/outer.rs:229

Front: What is `pending_define` in ForthVM?
Back: Arc<Mutex<Vec<i32>>> — signals from host functions to the outer interpreter: 1=CONSTANT, 2=VARIABLE, 3=CREATE, 4=EVALUATE. Host function sets the code, outer interpreter reads it after execution and performs the action.
Source: crates/core/src/outer.rs:227

Front: What does `does_definitions` store?
Back: HashMap<WordId, DoesDefinition> — for each DOES>-based defining word, stores: create_ir (code before DOES>), does_action_id (WordId of code after DOES>), has_create flag. Used when the defining word executes to set up new instances.
Source: crates/core/src/outer.rs:216

Front: What happened to the `emit_func` field in ForthVM?
Back: It moved into the Runtime implementation. NativeRuntime owns emit_func as a wasmtime::Func. WebRuntime creates it as a JS closure. ForthVM no longer directly holds wasmtime types — it only interacts via the Runtime trait.
Source: crates/core/src/runtime_native.rs:116

Front: What are `two_value_words` and `fvalue_words`?
Back: HashSet<u32> tracking which word IDs are 2VALUEs or FVALUEs. TO needs to know: regular VALUE stores 1 cell, 2VALUE stores 2 cells, FVALUE stores 1 float (8 bytes). Without these sets, TO wouldn't know the storage semantics.
Source: crates/core/src/outer.rs:237-239

Front: How many fields does ForthVM have? Name the major groups.
Back: ~35 fields in 7 groups:
1. Runtime: rt: R (generic — replaces old engine/store/memory/table/dsp/rsp/fsp/emit_func)
2. Compilation: state, compiling_name, compiling_ir, control_stack, compiling_word_id, compiling_locals
3. Output: output (Arc<Mutex<String>>)
4. Dictionary bridge: dictionary, user_here, here_cell, base_cell
5. Word metadata: ir_bodies, host_word_names, word_pfa_map, does_definitions
6. Host function shared state: pending_define, pending_actions, pending_does_patch, throw_code, word_lookup
7. Config + advanced: config, batch_mode, deferred_ir, marker_states, conditional_skip_depth, substitutions, search_order, next_wid, toplevel_ir
Source: crates/core/src/outer.rs:173-260

Front: What WASM local index is the cached DSP?
Back: Local 0 (CACHED_DSP_LOCAL). At function entry: global.get $dsp → local.set 0. All stack ops use local 0. Scratch locals start at SCRATCH_BASE = 1.
Source: crates/core/src/codegen.rs:58-61

Front: What does `dsp_writeback` do and when is it called?
Back: Writes the cached DSP local back to the $dsp global: `local.get 0; global.set $dsp`. Called before: (1) call_indirect/call (callee needs correct dsp), (2) function exit (return).
Code: fn dsp_writeback(f: &mut Function) {
f.instruction(&LocalGet(CACHED_DSP_LOCAL))
.instruction(&GlobalSet(DSP));
}
Source: crates/core/src/codegen.rs:167-173

Front: How does codegen emit IrOp::Dup?
Back: Dup = peek top of stack, push copy:
1. peek(f): local.get $dsp; i32.load (value now on WASM operand stack)
2. push_via_local(f, SCRATCH_BASE): local.set $tmp; dsp_dec; local.get $dsp; local.get $tmp; i32.store
Source: crates/core/src/codegen.rs:359-362

Front: How does codegen emit IrOp::Call(id)?
Back: 1. dsp_writeback (callee needs correct dsp)
2. If id == self_word_id (self-recursion): emit direct `call WORD_FUNC`
3. Else: i32.const fn_index; call_indirect (type_void, table 0)
4. dsp_reload (callee may have changed dsp)
Source: crates/core/src/codegen.rs (emit_op Call branch)

Front: What is EmitCtx and what fields does it have?
Back: Carries context for WASM code emission:
- f64_local_0, f64_local_1: scratch locals for float ops
- forth_local_base: base WASM local for Forth locals ({: ... :})
- loop_local_base: base local for DO/LOOP index/limit pairs
- loop_locals: Vec<(index_local, limit_local)> stack for nested loops
- fast_loop_depth: nesting depth of loops using local fast path
- self_word_id: Option<WordId> for self-recursion detection
- open_blocks: Vec<u32> for flat forward branches (CS-ROLL)
Source: crates/core/src/codegen.rs:229-250

Front: What are TYPE_VOID and TYPE_I32 in codegen?
Back: Type section indices: TYPE_VOID=0 is () → () (used by most word functions and call_indirect), TYPE_I32=1 is (i32) → () (used by the emit import function).
Source: crates/core/src/codegen.rs:44-45

Front: How does the codegen handle DO/LOOP?
Back: Fast path: index and limit stored in WASM locals (no return stack).
- DO: pop limit and index from data stack into locals
- Loop body: I (RFetch) reads from index local
- LOOP: increment index local, compare with limit, br_if to loop start
- LEAVE: set SYSVAR_LEAVE_FLAG, break out of loop
Fallback: if loop is too complex, use return stack (rpush/rpop).
Source: crates/core/src/codegen.rs (DoLoop handling)

Front: What are the 7 phases of boot.fth?
Back: 1. Stack/memory: DEPTH, PICK, 2OVER, 2ROT, WITHIN, 2@, 2!, FILL, CMOVE, MOVE, ERASE, /STRING, -TRAILING
2. Double-cell arithmetic: D+, DNEGATE, D-, DABS, D0=, D0<, D=, D<, D2*, D2/, DMAX, DMIN, M+, DU<
3. Mixed arithmetic: SM/REM, FM/MOD, */, */MOD
4. HERE and ALIGNED: HERE, ALLOT, comma, C-comma, ALIGN
5. I/O + pictured numeric output: TYPE, SPACES, <# HOLD HOLDS SIGN # #S #> . U. .R U.R D. D.R
6. DEFER support: DEFER!, DEFER@
7. String ops + misc: COMPARE, -TRAILING, SOURCE, FALIGNED, SFALIGNED, DFALIGNED
Source: crates/core/boot.fth

Front: How is DEPTH defined in boot.fth and why?
Back: : DEPTH SP@ 5632 SWAP - 2 RSHIFT ;
5632 = DATA_STACK_TOP (0x1600). Stack grows down, so depth = (top - sp) / 4.
SP@ must come first — it reads dsp BEFORE DEPTH's own literal pushes affect it.
2 RSHIFT = divide by 4 (arithmetic right shift, CELL_SIZE=4).
Code: : DEPTH SP@ 5632 SWAP - 2 RSHIFT ;
Source: crates/core/boot.fth:12

Front: What magic numbers appear in boot.fth and what do they mean?
Back: 5632 (0x1600) = DATA_STACK_TOP
1472 (0x05C0) = PICT_BUF_TOP (also WORD_BUF_BASE)
12 = SYSVAR_HERE offset
28 = SYSVAR_HLD offset
64 = INPUT_BUFFER_BASE
24 = SYSVAR_NUM_TIB offset
Source: crates/core/boot.fth

Front: How does pictured numeric output work in boot.fth?
Back: <# initializes HLD to PICT_BUF_TOP (1472)
HOLD decrements HLD and stores a character (grows downward)
# extracts one digit: divides ud by BASE via two UM/MODs, converts digit to ASCII, HOLDs it
#S calls # repeatedly until ud is zero
#> returns (c-addr u) pointing to the formatted string in the pictured buffer
SIGN adds '-' if the original number was negative
Code: : <# 1472 28 ! ;
: HOLD 28 @ 1- DUP 28 ! C! ;
: # BASE @ >R 0 R@ UM/MOD R> SWAP >R UM/MOD
SWAP DUP 9 > IF 7 + THEN 48 + HOLD R> ;
Source: crates/core/boot.fth:193-224

Front: Why is . (dot) defined in Forth instead of as a Rust host function?
Back: : . DUP ABS 0 <# #S ROT SIGN #> TYPE SPACE ;
1. Self-hosting goal: Forth definitions > Rust
2. Compiled Forth with inlining beats host function dispatch
3. . becomes inlineable (IR body available to optimizer)
4. Participates in CONSOLIDATE (direct calls in single module)
5. Respects BASE correctly via pictured numeric output
Source: crates/core/boot.fth:228

Front: What are eval_output and eval_stack test helpers?
Back: eval_output("forth code") → creates ForthVM, evaluates code, returns output String
eval_stack("forth code") → creates ForthVM, evaluates code, returns data stack as Vec<i32>
Both create a fresh VM for each test (isolated).
Source: crates/core/src/outer.rs (test module)

Front: How does the compliance test infrastructure work?
Back: 1. boot_with_prerequisites(): create VM, load tester.fr, core.fr, utilities.fth, coreexttest.fth
2. run_suite(vm, file): reset #ERRORS to 0, load test file, read #ERRORS from data stack
3. Assert #ERRORS == 0 for pass
4. 11 word sets tested: Core, Core+, CoreExt, Double, Exception, Facility, File, Float, Locals, Memory, String
Source: crates/core/tests/compliance.rs

Front: What are the 4 ways to run WAFER?
Back: 1. `wafer` — interactive CLI REPL (rustyline, NativeRuntime)
2. `wafer file.fth` — evaluate file and exit (NativeRuntime)
3. `wafer build file.fth` — compile to .wasm or --native executable
4. Browser REPL — wasm-pack build crates/web, serve www/, WebRuntime
Also: `wafer run file.wasm` — execute pre-compiled module
Source: crates/cli/src/main.rs:58-83

Front: How does the native executable trick work (wafer build --native)?
Back: 1. AOT-compile WASM via wasmtime Engine::precompile_module()
2. Read current wafer binary
3. Append: [wafer binary] + [precompiled payload] + [metadata JSON] + [24-byte trailer]
4. Trailer: payload_len(8) + metadata_len(8) + "WAFEREXE"(8)
5. On startup, check_embedded_payload() reads trailer, extracts payload, runs it
Code: const NATIVE_MAGIC: &[u8; 8] = b"WAFEREXE";
const TRAILER_SIZE: u64 = 24;
Source: crates/cli/src/main.rs:12-14

Front: Name all 13 ControlEntry variants.
Back: If, IfElse, Do, Begin, BeginWhile, BeginWhileWhile,
PostDoubleWhileRepeat, PostDoubleWhileRepeatElse,
Case, Of, QDo, Ahead, BeginRef, ForwardBlock
Source: crates/core/src/outer.rs:36-105

Front: How does IF...ELSE...THEN compile?
Back: 1. IF: push ControlEntry::If { then_body: [] }; subsequent IR goes to then_body
2. ELSE: pop If, push ControlEntry::IfElse { then_body, else_body: [] }; subsequent IR goes to else_body
3. THEN: pop IfElse (or If), emit IrOp::If { then_body, else_body }
The IR is a tree — nested bodies, not flat branches.

Front: How does DO...LOOP compile?
Back: 1. DO: push ControlEntry::Do { body: [] }; subsequent IR goes to body
2. LOOP: pop Do, emit IrOp::DoLoop { body, is_plus_loop: false }
3. +LOOP: same but is_plus_loop: true
The limit and index are expected on the data stack before the DoLoop executes.

Front: How does BEGIN...WHILE...REPEAT compile?
Back: 1. BEGIN: push ControlEntry::Begin { body: [] }
2. WHILE: pop Begin, push ControlEntry::BeginWhile { test: body, body: [] }
(everything before WHILE becomes the test)
3. REPEAT: pop BeginWhile, emit IrOp::BeginWhileRepeat { test, body }

Front: What is CASE...OF...ENDOF...ENDCASE compilation?
Back: 1. CASE: push ControlEntry::Case { prefix, endof_branches: [] }
2. OF: duplicate test value, compare, pop Case, push ControlEntry::Of
3. ENDOF: pop Of, save (test, body) pair, push back Case with new branch
4. ENDCASE: pop Case, emit nested If chain from endof_branches
Desugared into nested IrOp::If at compile time.

Front: What are CS-PICK and CS-ROLL and why are they complex?
Back: Programming-Tools words that manipulate the compile-time control stack.
CS-PICK duplicates a control-flow entry N deep (e.g., reference a BEGIN from inside nested structures).
CS-ROLL rotates control-flow entries (e.g., move an IF dest across other structures).
Complex because they break the structured control-flow assumption — WAFER linearizes these into Block/BranchIfFalse/EndBlock IR ops for flat forward branches.
Source: crates/core/src/outer.rs:99-105

Front: What does compile_consolidated_module() produce?
Back: A single WASM module containing ALL compiled Forth words as separate functions.
- Each word gets a function index within the module
- Call(id) where id is in the module → direct `call N` (not call_indirect)
- Call(id) where id is NOT in the module → call_indirect (host functions)
- TailCall(id) in module → direct call + return
Source: crates/core/src/codegen.rs (compile_consolidated_module)

Front: What metadata does wafer build embed in the .wasm file?
Back: ExportMetadata in a "wafer" custom section (JSON):
- version: 1
- entry_table_index: Option<u32>
- host_functions: Vec<(table_index, name)>
- memory_size: u32
- dsp_init, rsp_init, fsp_init: initial stack pointers
Source: crates/core/src/export.rs:21-36

Front: What is the align4 function?
Back: (addr + 3) & !3 — rounds up to next 4-byte boundary. Used to align the code field after variable-length name in dictionary entries.
Code: fn align4(addr: u32) -> u32 {
(addr + 3) & !3
}
Source: crates/core/src/dictionary.rs:51-53

Front: What is the hash index in Dictionary?
Back: HashMap<String, Vec<(wid, word_addr, fn_index, is_immediate)>>
Maps uppercase name → list of entries across wordlists.
find() checks search_order against this index (O(1) average).
Fallback: linked-list walk for words not yet in index.
Updated by reveal() and set_immediate().
Source: crates/core/src/dictionary.rs:43

Front: What is DictionaryState and when is it used?
Back: Snapshot of dictionary state: latest, here, next_fn_index, index (HashMap clone).
Used by MARKER: save_state() captures current state, restore_state() reverts.
Does NOT save the actual memory bytes — just pointers and metadata.
Source: crates/core/src/dictionary.rs:502-509

Front: How does Dictionary::create() lay out an entry?
Back: Starting at self.here:
1. Write link field (4 bytes): points to previous LATEST
2. Write flags byte (1 byte): HIDDEN | length (optionally | IMMEDIATE)
3. Write name bytes (N bytes, uppercase)
4. Zero-pad to 4-byte alignment
5. Write code field (4 bytes): next_fn_index (auto-incremented)
6. Update latest = entry_start, here = after code field
Code: // entry_start = self.here
// [link:4][flags:1][name:N][pad:0-3][code:4]
Source: crates/core/src/dictionary.rs:74-124

Front: What are the inline criteria?
Back: A Call(id) is inlined if ALL of:
1. Body exists in `bodies` HashMap
2. body.len() <= max_size (8)
3. No self-recursion (contains_call_to check)
4. No Exit (would return from caller)
5. No ForthLocalGet/Set (would collide with caller's locals)
When inlined, TailCall ops are converted back to Call via detailcall().
Source: crates/core/src/optimizer.rs:499-526

Front: How does tail_call_detect decide if a tail call is safe?
Back: 1. IR must be non-empty
2. Return stack must be balanced: count ToR and FromR, depth must be 0
(Unbalanced means >R without matching R>, which would corrupt return stack on tail call)
3. convert_tail_call on last op: Call → TailCall
4. Recurses into If branches: if last op is If, check both then/else branches
Code: fn is_return_stack_balanced(ops: &[IrOp]) -> bool {
let mut depth: i32 = 0;
for op in ops {
match op {
IrOp::ToR => depth += 1,
IrOp::FromR => depth -= 1,
_ => {}
}
}
depth == 0
}
Source: crates/core/src/optimizer.rs:671-693

Front: What is the optimizer pass ordering and why does it matter?
Back: Phase 1 (simplify): peephole → fold → strength_reduce → peephole
Phase 2 (inline + re-simplify): inline → peephole → fold → strength_reduce → peephole
Phase 3 (eliminate): dce → peephole
Phase 4 (finalize): tail_call_detect
Order matters because:
- Inline before fold: inlined body may have constant expressions
- Fold before strength: folding may produce power-of-2 constants
- DCE after fold: folded constants enable dead-branch elimination
- Tail call last: must operate on final IR
- Peephole between each: cleanup after every transformation
Source: crates/core/src/optimizer.rs:37-85

Front: IrOp::PushI32(i32) — what does it do?
Back: Push a 32-bit integer constant.
Code: IrOp::PushI32(i32)
Source: crates/core/src/ir.rs

Front: IrOp::PushI64(i64) — what does it do?
Back: Stack: (double-cell)
Push a 64-bit integer constant (double-cell).
Code: IrOp::PushI64(i64)
Source: crates/core/src/ir.rs

Front: IrOp::PushF64(f64) — what does it do?
Back: Push a 64-bit float constant.
Code: IrOp::PushF64(f64)
Source: crates/core/src/ir.rs

Front: IrOp::TwoDup — what does it do?
Back: Stack: ( a b -- a b a b )
Two-item duplication: ( a b -- a b a b )
Code: IrOp::TwoDup
Source: crates/core/src/ir.rs

Front: IrOp::TwoDrop — what does it do?
Back: Stack: ( a b -- )
Two-item drop: ( a b -- )
Code: IrOp::TwoDrop
Source: crates/core/src/ir.rs

Front: IrOp::DivMod — what does it do?
Back: Stack: ( n1 n2 -- rem quot )
Combined division and modulus: ( n1 n2 -- rem quot )
Code: IrOp::DivMod
Source: crates/core/src/ir.rs

Front: IrOp::ArithRshift — what does it do?
Back: Stack: (signed)
Arithmetic (signed) right shift -- used by 2/.
Code: IrOp::ArithRshift
Source: crates/core/src/ir.rs

Front: IrOp::Fetch — what does it do?
Back: Stack: ( addr -- x )
Fetch cell from address: ( addr -- x )
Code: IrOp::Fetch
Source: crates/core/src/ir.rs

Front: IrOp::Store — what does it do?
Back: Stack: ( x addr -- )
Store cell to address: ( x addr -- )
Code: IrOp::Store
Source: crates/core/src/ir.rs

Front: IrOp::CFetch — what does it do?
Back: Stack: ( addr -- char )
Fetch byte: ( addr -- char )
Code: IrOp::CFetch
Source: crates/core/src/ir.rs

Front: IrOp::CStore — what does it do?
Back: Stack: ( char addr -- )
Store byte: ( char addr -- )
Code: IrOp::CStore
Source: crates/core/src/ir.rs

Front: IrOp::PlusStore — what does it do?
Back: Stack: ( n addr -- )
Add to cell at address: ( n addr -- )
Code: IrOp::PlusStore
Source: crates/core/src/ir.rs

Front: IrOp::Call(WordId) — what does it do?
Back: Call another word.
Code: IrOp::Call(WordId)
Source: crates/core/src/ir.rs

Front: IrOp::TailCall(WordId) — what does it do?
Back: Tail-call optimization.
Code: IrOp::TailCall(WordId)
Source: crates/core/src/ir.rs

Front: IrOp::If — what does it do?
Back: IF ... ELSE ... THEN
Code: IrOp::If
Source: crates/core/src/ir.rs

Front: IrOp::DoLoop — what does it do?
Back: DO ... LOOP
Code: IrOp::DoLoop
Source: crates/core/src/ir.rs

Front: IrOp::BeginUntil — what does it do?
Back: BEGIN ... UNTIL
Code: IrOp::BeginUntil
Source: crates/core/src/ir.rs

Front: IrOp::BeginAgain — what does it do?
Back: Stack: (infinite loop)
BEGIN ... AGAIN (infinite loop)
Code: IrOp::BeginAgain
Source: crates/core/src/ir.rs

Front: IrOp::BeginWhileRepeat — what does it do?
Back: BEGIN ... WHILE ... REPEAT
Code: IrOp::BeginWhileRepeat
Source: crates/core/src/ir.rs

Front: IrOp::BeginDoubleWhileRepeat — what does it do?
Back: BEGIN test1 WHILE test2 WHILE body REPEAT `after_repeat` ELSE `else_body` THEN Two nested WHILEs in a single BEGIN loop. When the first WHILE fails, control goes to `else_body`. When the second WHILE fails, control goes to `after_repeat`. REPEAT jumps back to BEGIN.
Code: IrOp::BeginDoubleWhileRepeat
Source: crates/core/src/ir.rs

Front: IrOp::Exit — what does it do?
Back: Return from current word.
Code: IrOp::Exit
Source: crates/core/src/ir.rs

Front: IrOp::LoopRestartIfFalse — what does it do?
Back: Stack: (used by CS-PICK'd BEGIN + UNTIL)
Conditional restart of enclosing loop (used by CS-PICK'd BEGIN + UNTIL). Pops flag; if false, restart the loop. Desugared into nested `If` before codegen.
Code: IrOp::LoopRestartIfFalse
Source: crates/core/src/ir.rs

Front: IrOp::Block(u32) — what does it do?
Back: Open a WASM `block`. `BranchIfFalse` can target this to skip to `EndBlock`.
Code: IrOp::Block(u32)
Source: crates/core/src/ir.rs

Front: IrOp::BranchIfFalse(u32) — what does it do?
Back: Pop flag; if false, branch to matching `EndBlock` with this label.
Code: IrOp::BranchIfFalse(u32)
Source: crates/core/src/ir.rs

Front: IrOp::EndBlock(u32) — what does it do?
Back: Close the `block` with this label.
Code: IrOp::EndBlock(u32)
Source: crates/core/src/ir.rs

Front: IrOp::ToR — what does it do?
Back: Stack: ( x -- )
Move to return stack: ( x -- ) ( R: -- x )
Code: IrOp::ToR
Source: crates/core/src/ir.rs

Front: IrOp::FromR — what does it do?
Back: Stack: ( -- x )
Move from return stack: ( -- x ) ( R: x -- )
Code: IrOp::FromR
Source: crates/core/src/ir.rs

Front: IrOp::RFetch — what does it do?
Back: Stack: ( -- x )
Copy from return stack: ( -- x ) ( R: x -- x )
Code: IrOp::RFetch
Source: crates/core/src/ir.rs

Front: IrOp::LoopJ — what does it do?
Back: Stack: (J)
Read outer DO/LOOP index (J): ( -- n ) Compiled to local.get when loop locals are available.
Code: IrOp::LoopJ
Source: crates/core/src/ir.rs

Front: IrOp::ForthLocalGet(u32) — what does it do?
Back: Stack: ( -- x )
Get Forth local variable N: ( -- x )
Code: IrOp::ForthLocalGet(u32)
Source: crates/core/src/ir.rs

Front: IrOp::ForthLocalSet(u32) — what does it do?
Back: Stack: ( x -- )
Set Forth local variable N: ( x -- )
Code: IrOp::ForthLocalSet(u32)
Source: crates/core/src/ir.rs

Front: IrOp::Emit — what does it do?
Back: Stack: ( char -- )
Output character: ( char -- )
Code: IrOp::Emit
Source: crates/core/src/ir.rs

Front: IrOp::Dot — what does it do?
Back: Stack: ( n -- )
Print number: ( n -- )
Code: IrOp::Dot
Source: crates/core/src/ir.rs

Front: IrOp::Cr — what does it do?
Back: Output newline.
Code: IrOp::Cr
Source: crates/core/src/ir.rs

Front: IrOp::Type — what does it do?
Back: Stack: ( c-addr u -- )
Output string: ( c-addr u -- )
Code: IrOp::Type
Source: crates/core/src/ir.rs

Front: IrOp::Execute — what does it do?
Back: Stack: ( xt -- )
Execute word by function table index: ( xt -- )
Code: IrOp::Execute
Source: crates/core/src/ir.rs

Front: IrOp::SpFetch — what does it do?
Back: Stack: ( -- addr )
Push the current data-stack pointer: ( -- addr )
Code: IrOp::SpFetch
Source: crates/core/src/ir.rs

Front: IrOp::FDup — what does it do?
Back: Stack: ( F: r -- r r )
Float duplicate: ( F: r -- r r )
Code: IrOp::FDup
Source: crates/core/src/ir.rs

Front: IrOp::FDrop — what does it do?
Back: Stack: ( F: r -- )
Float drop: ( F: r -- )
Code: IrOp::FDrop
Source: crates/core/src/ir.rs

Front: IrOp::FSwap — what does it do?
Back: Stack: ( F: r1 r2 -- r2 r1 )
Float swap: ( F: r1 r2 -- r2 r1 )
Code: IrOp::FSwap
Source: crates/core/src/ir.rs

Front: IrOp::FOver — what does it do?
Back: Stack: ( F: r1 r2 -- r1 r2 r1 )
Float over: ( F: r1 r2 -- r1 r2 r1 )
Code: IrOp::FOver
Source: crates/core/src/ir.rs

Front: IrOp::FAdd — what does it do?
Back: Stack: ( F: r1 r2 -- r1+r2 )
Float add: ( F: r1 r2 -- r1+r2 )
Code: IrOp::FAdd
Source: crates/core/src/ir.rs

Front: IrOp::FSub — what does it do?
Back: Stack: ( F: r1 r2 -- r1-r2 )
Float subtract: ( F: r1 r2 -- r1-r2 )
Code: IrOp::FSub
Source: crates/core/src/ir.rs

Front: IrOp::FMul — what does it do?
Back: Stack: ( F: r1 r2 -- r1*r2 )
Float multiply: ( F: r1 r2 -- r1*r2 )
Code: IrOp::FMul
Source: crates/core/src/ir.rs

Front: IrOp::FDiv — what does it do?
Back: Stack: ( F: r1 r2 -- r1/r2 )
Float divide: ( F: r1 r2 -- r1/r2 )
Code: IrOp::FDiv
Source: crates/core/src/ir.rs

Front: IrOp::FNegate — what does it do?
Back: Stack: ( F: r -- -r )
Float negate: ( F: r -- -r )
Code: IrOp::FNegate
Source: crates/core/src/ir.rs

Front: IrOp::FAbs — what does it do?
Back: Stack: ( F: r -- |r| )
Float absolute value: ( F: r -- |r| )
Code: IrOp::FAbs
Source: crates/core/src/ir.rs

Front: IrOp::FSqrt — what does it do?
Back: Stack: ( F: r -- sqrt(r)
Float square root: ( F: r -- sqrt(r) )
Code: IrOp::FSqrt
Source: crates/core/src/ir.rs

Front: IrOp::FMin — what does it do?
Back: Stack: ( F: r1 r2 -- min(r1,r2)
Float minimum: ( F: r1 r2 -- min(r1,r2) )
Code: IrOp::FMin
Source: crates/core/src/ir.rs

Front: IrOp::FMax — what does it do?
Back: Stack: ( F: r1 r2 -- max(r1,r2)
Float maximum: ( F: r1 r2 -- max(r1,r2) )
Code: IrOp::FMax
Source: crates/core/src/ir.rs

Front: IrOp::FFloor — what does it do?
Back: Stack: ( F: r -- floor(r)
Float floor: ( F: r -- floor(r) )
Code: IrOp::FFloor
Source: crates/core/src/ir.rs

Front: IrOp::FRound — what does it do?
Back: Stack: ( F: r -- round(r)
Float round to nearest even: ( F: r -- round(r) )
Code: IrOp::FRound
Source: crates/core/src/ir.rs

Front: IrOp::FZeroEq — what does it do?
Back: Stack: ( F: r -- )
Float zero equal: ( F: r -- ) ( -- flag )
Code: IrOp::FZeroEq
Source: crates/core/src/ir.rs

Front: IrOp::FZeroLt — what does it do?
Back: Stack: ( F: r -- )
Float zero less-than: ( F: r -- ) ( -- flag )
Code: IrOp::FZeroLt
Source: crates/core/src/ir.rs

Front: IrOp::FEq — what does it do?
Back: Stack: ( F: r1 r2 -- )
Float equal: ( F: r1 r2 -- ) ( -- flag )
Code: IrOp::FEq
Source: crates/core/src/ir.rs

Front: IrOp::FLt — what does it do?
Back: Stack: ( F: r1 r2 -- )
Float less-than: ( F: r1 r2 -- ) ( -- flag )
Code: IrOp::FLt
Source: crates/core/src/ir.rs

Front: IrOp::FetchFloat — what does it do?
Back: Stack: ( addr -- )
Float fetch: ( addr -- ) ( F: -- r )
Code: IrOp::FetchFloat
Source: crates/core/src/ir.rs

Front: IrOp::StoreFloat — what does it do?
Back: Stack: ( addr -- )
Float store: ( addr -- ) ( F: r -- )
Code: IrOp::StoreFloat
Source: crates/core/src/ir.rs

Front: IrOp::StoF — what does it do?
Back: Stack: ( n -- )
Single to float: ( n -- ) ( F: -- r )
Code: IrOp::StoF
Source: crates/core/src/ir.rs

Front: IrOp::FtoS — what does it do?
Back: Stack: ( F: r -- )
Float to single: ( F: r -- ) ( -- n )
Code: IrOp::FtoS
Source: crates/core/src/ir.rs

Front: memory.rs: What is PAGE_SIZE?
Back: Size of one memory page in WASM (64 KiB).
Value: 65536
Code: pub const PAGE_SIZE: u32 = 65536;
Source: crates/core/src/memory.rs

Front: memory.rs: What is INITIAL_PAGES?
Back: Initial number of memory pages.
Value: 16
Code: pub const INITIAL_PAGES: u32 = 16;
Source: crates/core/src/memory.rs

Front: memory.rs: What is MAX_PAGES?
Back: Maximum number of memory pages.
Value: 256
Code: pub const MAX_PAGES: u32 = 256;
Source: crates/core/src/memory.rs

Front: memory.rs: What is SYSVAR_BASE?
Back: System variables region (STATE, BASE, >IN, HLD, etc.)
Value: 0x0000
Code: pub const SYSVAR_BASE: u32 = 0x0000;
Source: crates/core/src/memory.rs

Front: memory.rs: What region starts at 0x0000?
Back: SYSVAR_BASE: System variables region (STATE, BASE, >IN, HLD, etc.)
Source: crates/core/src/memory.rs

Front: memory.rs: What is SYSVAR_SIZE?
Back: Size of system variables region.
Value: 64
Code: pub const SYSVAR_SIZE: u32 = 64;
Source: crates/core/src/memory.rs

Front: memory.rs: What is INPUT_BUFFER_BASE?
Back: Input buffer for source parsing.
Value: SYSVAR_BASE + SYSVAR_SIZE
Code: pub const INPUT_BUFFER_BASE: u32 = SYSVAR_BASE + SYSVAR_SIZE;
Source: crates/core/src/memory.rs

Front: memory.rs: What is INPUT_BUFFER_SIZE?
Back: Size of input buffer.
Value: 1024
Code: pub const INPUT_BUFFER_SIZE: u32 = 1024;
Source: crates/core/src/memory.rs

Front: memory.rs: What is PAD_BASE?
Back: PAD - scratch area for string formatting.
Value: INPUT_BUFFER_BASE + INPUT_BUFFER_SIZE
Code: pub const PAD_BASE: u32 = INPUT_BUFFER_BASE + INPUT_BUFFER_SIZE;
Source: crates/core/src/memory.rs

Front: memory.rs: What is PAD_SIZE?
Back: Size of PAD.
Value: 256
Code: pub const PAD_SIZE: u32 = 256;
Source: crates/core/src/memory.rs

Front: memory.rs: What is PICT_BUF_BASE?
Back: Pictured numeric output buffer (<# ... #>). Grows downward from top.
Value: PAD_BASE + PAD_SIZE
Code: pub const PICT_BUF_BASE: u32 = PAD_BASE + PAD_SIZE;
Source: crates/core/src/memory.rs

Front: memory.rs: What is PICT_BUF_SIZE?
Back: Size of pictured output buffer (2*`BITS_PER_CELL` + 2 = 66 min, 128 for margin).
Value: 128
Code: pub const PICT_BUF_SIZE: u32 = 128;
Source: crates/core/src/memory.rs

Front: memory.rs: What is PICT_BUF_TOP?
Back: Top of pictured output buffer (HLD starts here, grows down).
Value: PICT_BUF_BASE + PICT_BUF_SIZE
Code: pub const PICT_BUF_TOP: u32 = PICT_BUF_BASE + PICT_BUF_SIZE;
Source: crates/core/src/memory.rs

Front: memory.rs: What is WORD_BUF_BASE?
Back: WORD buffer — transient counted-string area for WORD output.
Value: PICT_BUF_TOP
Code: pub const WORD_BUF_BASE: u32 = PICT_BUF_TOP;
Source: crates/core/src/memory.rs

Front: memory.rs: What is WORD_BUF_SIZE?
Back: Size of WORD buffer (max name 31 + 1 length + padding).
Value: 64
Code: pub const WORD_BUF_SIZE: u32 = 64;
Source: crates/core/src/memory.rs

Front: memory.rs: What is DATA_STACK_BASE?
Back: Data stack region (fallback when types are unknown). Grows downward from the top of this region.
Value: WORD_BUF_BASE + WORD_BUF_SIZE
Code: pub const DATA_STACK_BASE: u32 = WORD_BUF_BASE + WORD_BUF_SIZE;
Source: crates/core/src/memory.rs

Front: memory.rs: What is DATA_STACK_SIZE?
Back: Size of data stack region.
Value: 4096
Code: pub const DATA_STACK_SIZE: u32 = 4096;
Source: crates/core/src/memory.rs

Front: memory.rs: What is RETURN_STACK_BASE?
Back: Return stack region. Grows downward.
Value: DATA_STACK_BASE + DATA_STACK_SIZE
Code: pub const RETURN_STACK_BASE: u32 = DATA_STACK_BASE + DATA_STACK_SIZE;
Source: crates/core/src/memory.rs

Front: memory.rs: What is RETURN_STACK_SIZE?
Back: Size of return stack region.
Value: 4096
Code: pub const RETURN_STACK_SIZE: u32 = 4096;
Source: crates/core/src/memory.rs

Front: memory.rs: What is FLOAT_STACK_BASE?
Back: Floating-point stack region (fallback). Grows downward.
Value: RETURN_STACK_BASE + RETURN_STACK_SIZE
Code: pub const FLOAT_STACK_BASE: u32 = RETURN_STACK_BASE + RETURN_STACK_SIZE;
Source: crates/core/src/memory.rs

Front: memory.rs: What is FLOAT_STACK_SIZE?
Back: Size of float stack region.
Value: 2048
Code: pub const FLOAT_STACK_SIZE: u32 = 2048;
Source: crates/core/src/memory.rs

Front: memory.rs: What is DICTIONARY_BASE?
Back: Dictionary region start. Grows upward.
Value: FLOAT_STACK_BASE + FLOAT_STACK_SIZE
Code: pub const DICTIONARY_BASE: u32 = FLOAT_STACK_BASE + FLOAT_STACK_SIZE;
Source: crates/core/src/memory.rs

Front: memory.rs: What is DATA_STACK_TOP?
Back: Initial top of data stack (grows down from here).
Value: DATA_STACK_BASE + DATA_STACK_SIZE
Code: pub const DATA_STACK_TOP: u32 = DATA_STACK_BASE + DATA_STACK_SIZE;
Source: crates/core/src/memory.rs

Front: memory.rs: What is RETURN_STACK_TOP?
Back: Initial top of return stack (grows down from here).
Value: RETURN_STACK_BASE + RETURN_STACK_SIZE
Code: pub const RETURN_STACK_TOP: u32 = RETURN_STACK_BASE + RETURN_STACK_SIZE;
Source: crates/core/src/memory.rs

Front: memory.rs: What is FLOAT_STACK_TOP?
Back: Initial top of float stack (grows down from here).
Value: FLOAT_STACK_BASE + FLOAT_STACK_SIZE
Code: pub const FLOAT_STACK_TOP: u32 = FLOAT_STACK_BASE + FLOAT_STACK_SIZE;
Source: crates/core/src/memory.rs

Front: memory.rs: What is CELL_SIZE?
Back: Size of one cell (4 bytes for i32).
Value: 4
Code: pub const CELL_SIZE: u32 = 4;
Source: crates/core/src/memory.rs

Front: memory.rs: What is DOUBLE_CELL_SIZE?
Back: Size of one double-cell (8 bytes).
Value: 8
Code: pub const DOUBLE_CELL_SIZE: u32 = 8;
Source: crates/core/src/memory.rs

Front: memory.rs: What is FLOAT_SIZE?
Back: Size of one float (8 bytes for f64).
Value: 8
Code: pub const FLOAT_SIZE: u32 = 8;
Source: crates/core/src/memory.rs

Front: memory.rs: What is SYSVAR_STATE?
Back: STATE: 0 = interpreting, -1 (0xFFFFFFFF) = compiling.
Value: SYSVAR_BASE
Code: pub const SYSVAR_STATE: u32 = SYSVAR_BASE;
Source: crates/core/src/memory.rs

Front: memory.rs: What is SYSVAR_BASE_VAR?
Back: BASE: current number base (default 10).
Value: SYSVAR_BASE + 4
Code: pub const SYSVAR_BASE_VAR: u32 = SYSVAR_BASE + 4;
Source: crates/core/src/memory.rs

Front: memory.rs: What is SYSVAR_TO_IN?
Back: >IN: offset into the input buffer.
Value: SYSVAR_BASE + 8
Code: pub const SYSVAR_TO_IN: u32 = SYSVAR_BASE + 8;
Source: crates/core/src/memory.rs

Front: memory.rs: What is SYSVAR_HERE?
Back: HERE: next free dictionary address.
Value: SYSVAR_BASE + 12
Code: pub const SYSVAR_HERE: u32 = SYSVAR_BASE + 12;
Source: crates/core/src/memory.rs

Front: memory.rs: What is SYSVAR_LATEST?
Back: LATEST: pointer to the most recent dictionary entry.
Value: SYSVAR_BASE + 16
Code: pub const SYSVAR_LATEST: u32 = SYSVAR_BASE + 16;
Source: crates/core/src/memory.rs

Front: memory.rs: What is SYSVAR_SOURCE_ID?
Back: SOURCE-ID: current input source (0 = user input, -1 = string).
Value: SYSVAR_BASE + 20
Code: pub const SYSVAR_SOURCE_ID: u32 = SYSVAR_BASE + 20;
Source: crates/core/src/memory.rs

Front: memory.rs: What is SYSVAR_NUM_TIB?
Back: #TIB: length of current input.
Value: SYSVAR_BASE + 24
Code: pub const SYSVAR_NUM_TIB: u32 = SYSVAR_BASE + 24;
Source: crates/core/src/memory.rs

Front: memory.rs: What is SYSVAR_HLD?
Back: HLD: pointer for pictured numeric output.
Value: SYSVAR_BASE + 28
Code: pub const SYSVAR_HLD: u32 = SYSVAR_BASE + 28;
Source: crates/core/src/memory.rs

Front: memory.rs: What is SYSVAR_LEAVE_FLAG?
Back: LEAVE flag: nonzero when LEAVE has been called inside a DO loop.
Value: SYSVAR_BASE + 32
Code: pub const SYSVAR_LEAVE_FLAG: u32 = SYSVAR_BASE + 32;
Source: crates/core/src/memory.rs

Front: WaferError::StackUnderflow — when is this error raised?
Back: Error message: "stack underflow"
Code: WaferError::StackUnderflow
Source: crates/core/src/error.rs

Front: WaferError::StackOverflow — when is this error raised?
Back: Error message: "stack overflow"
Code: WaferError::StackOverflow
Source: crates/core/src/error.rs

Front: WaferError::ReturnStackUnderflow — when is this error raised?
Back: Error message: "return stack underflow"
Code: WaferError::ReturnStackUnderflow
Source: crates/core/src/error.rs

Front: WaferError::ReturnStackOverflow — when is this error raised?
Back: Error message: "return stack overflow"
Code: WaferError::ReturnStackOverflow
Source: crates/core/src/error.rs

Front: WaferError::FloatStackUnderflow — when is this error raised?
Back: Error message: "float stack underflow"
Code: WaferError::FloatStackUnderflow
Source: crates/core/src/error.rs

Front: WaferError::FloatStackOverflow — when is this error raised?
Back: Error message: "float stack overflow"
Code: WaferError::FloatStackOverflow
Source: crates/core/src/error.rs

Front: WaferError::UnknownWord — when is this error raised?
Back: Error message: "unknown word: {0}"
Contains: String
Code: WaferError::UnknownWord
Source: crates/core/src/error.rs

Front: WaferError::DivisionByZero — when is this error raised?
Back: Error message: "division by zero"
Code: WaferError::DivisionByZero
Source: crates/core/src/error.rs

Front: WaferError::InvalidAddress — when is this error raised?
Back: Error message: "invalid memory address: {0:#x}"
Contains: u32
Code: WaferError::InvalidAddress
Source: crates/core/src/error.rs

Front: WaferError::DictionaryOverflow — when is this error raised?
Back: Error message: "dictionary overflow"
Code: WaferError::DictionaryOverflow
Source: crates/core/src/error.rs

Front: WaferError::CompileError — when is this error raised?
Back: Error message: "compilation error: {0}"
Contains: String
Code: WaferError::CompileError
Source: crates/core/src/error.rs

Front: WaferError::InvalidNumber — when is this error raised?
Back: Error message: "invalid number: {0}"
Contains: String
Code: WaferError::InvalidNumber
Source: crates/core/src/error.rs

Front: WaferError::NameTooLong — when is this error raised?
Back: Error message: "word name too long: {0}"
Contains: String
Code: WaferError::NameTooLong
Source: crates/core/src/error.rs

Front: WaferError::ControlMismatch — when is this error raised?
Back: Error message: "control structure mismatch: {0}"
Contains: String
Code: WaferError::ControlMismatch
Source: crates/core/src/error.rs

Front: WaferError::CodegenError — when is this error raised?
Back: Error message: "WASM codegen error: {0}"
Contains: String
Code: WaferError::CodegenError
Source: crates/core/src/error.rs

Front: WaferError::ValidationError — when is this error raised?
Back: Error message: "WASM validation error: {0}"
Contains: String
Code: WaferError::ValidationError
Source: crates/core/src/error.rs

Front: WaferError::IoError — when is this error raised?
Back: Error message: "I/O error: {0}"
Contains: String
Code: WaferError::IoError
Source: crates/core/src/error.rs

Front: WaferError::Throw — when is this error raised?
Back: Error message: "THROW code {0}"
Contains: i32
Code: WaferError::Throw
Source: crates/core/src/error.rs

Front: WaferError::Abort — when is this error raised?
Back: Error message: "{0}"
Contains: String
Code: WaferError::Abort
Source: crates/core/src/error.rs

Front: Peephole: PushI32(n), Drop → ?
Back: remove both
Source: crates/core/src/optimizer.rs

Front: Peephole: Dup, Drop → ?
Back: remove both
Source: crates/core/src/optimizer.rs

Front: Peephole: Swap, Swap → ?
Back: remove both
Source: crates/core/src/optimizer.rs

Front: Peephole: Swap, Drop → ?
Back: Nip
Source: crates/core/src/optimizer.rs

Front: Peephole: PushI32(0), Add → ?
Back: identity, remove both
Source: crates/core/src/optimizer.rs

Front: Peephole: PushI32(0), Or → ?
Back: identity, remove both
Source: crates/core/src/optimizer.rs

Front: Peephole: PushI32(-1), And → ?
Back: identity, remove both
Source: crates/core/src/optimizer.rs

Front: Peephole: PushI32(1), Mul → ?
Back: identity, remove both
Source: crates/core/src/optimizer.rs

Front: Peephole: PushF64, FDrop → ?
Back: remove both
Source: crates/core/src/optimizer.rs

Front: Peephole: FDup, FDrop → ?
Back: remove both
Source: crates/core/src/optimizer.rs

Front: Peephole: FSwap, FSwap → ?
Back: remove both
Source: crates/core/src/optimizer.rs

Front: Peephole: FNegate, FNegate → ?
Back: remove both
Source: crates/core/src/optimizer.rs

Front: Peephole: Over, Over → ?
Back: TwoDup
Source: crates/core/src/optimizer.rs

Front: Peephole: Drop, Drop → ?
Back: TwoDrop
Source: crates/core/src/optimizer.rs

Front: Forth word DUP — what is its IR body?
Back: IR: [IrOp::Dup]
Code: register_primitive("DUP", False, vec![IrOp::Dup])
Source: crates/core/src/outer.rs

Front: Forth word DROP — what is its IR body?
Back: IR: [IrOp::Drop]
Code: register_primitive("DROP", False, vec![IrOp::Drop])
Source: crates/core/src/outer.rs

Front: Forth word SWAP — what is its IR body?
Back: IR: [IrOp::Swap]
Code: register_primitive("SWAP", False, vec![IrOp::Swap])
Source: crates/core/src/outer.rs

Front: Forth word OVER — what is its IR body?
Back: IR: [IrOp::Over]
Code: register_primitive("OVER", False, vec![IrOp::Over])
Source: crates/core/src/outer.rs

Front: Forth word ROT — what is its IR body?
Back: IR: [IrOp::Rot]
Code: register_primitive("ROT", False, vec![IrOp::Rot])
Source: crates/core/src/outer.rs

Front: Forth word NIP — what is its IR body?
Back: IR: [IrOp::Nip]
Code: register_primitive("NIP", False, vec![IrOp::Nip])
Source: crates/core/src/outer.rs

Front: Forth word TUCK — what is its IR body?
Back: IR: [IrOp::Tuck]
Code: register_primitive("TUCK", False, vec![IrOp::Tuck])
Source: crates/core/src/outer.rs

Front: Forth word + — what is its IR body?
Back: IR: [IrOp::Add]
Code: register_primitive("+", False, vec![IrOp::Add])
Source: crates/core/src/outer.rs

Front: Forth word - — what is its IR body?
Back: IR: [IrOp::Sub]
Code: register_primitive("-", False, vec![IrOp::Sub])
Source: crates/core/src/outer.rs

Front: Forth word * — what is its IR body?
Back: IR: [IrOp::Mul]
Code: register_primitive("*", False, vec![IrOp::Mul])
Source: crates/core/src/outer.rs

Front: Forth word /MOD — what is its IR body?
Back: IR: [IrOp::DivMod]
Code: register_primitive("/MOD", False, vec![IrOp::DivMod])
Source: crates/core/src/outer.rs

Front: Forth word NEGATE — what is its IR body?
Back: IR: [IrOp::Negate]
Code: register_primitive("NEGATE", False, vec![IrOp::Negate])
Source: crates/core/src/outer.rs

Front: Forth word ABS — what is its IR body?
Back: IR: [IrOp::Abs]
Code: register_primitive("ABS", False, vec![IrOp::Abs])
Source: crates/core/src/outer.rs

Front: Forth word / — what is its IR body?
Back: IR: [IrOp::DivMod, IrOp::Swap, IrOp::Drop]
Code: register_primitive("/", False, vec![IrOp::DivMod, IrOp::Swap, IrOp::Drop])
Source: crates/core/src/outer.rs

Front: Forth word MOD — what is its IR body?
Back: IR: [IrOp::DivMod, IrOp::Drop]
Code: register_primitive("MOD", False, vec![IrOp::DivMod, IrOp::Drop])
Source: crates/core/src/outer.rs

Front: Forth word = — what is its IR body?
Back: IR: [IrOp::Eq]
Code: register_primitive("=", False, vec![IrOp::Eq])
Source: crates/core/src/outer.rs

Front: Forth word <> — what is its IR body?
Back: IR: [IrOp::NotEq]
Code: register_primitive("<>", False, vec![IrOp::NotEq])
Source: crates/core/src/outer.rs

Front: Forth word < — what is its IR body?
Back: IR: [IrOp::Lt]
Code: register_primitive("<", False, vec![IrOp::Lt])
Source: crates/core/src/outer.rs

Front: Forth word > — what is its IR body?
Back: IR: [IrOp::Gt]
Code: register_primitive(">", False, vec![IrOp::Gt])
Source: crates/core/src/outer.rs

Front: Forth word U< — what is its IR body?
Back: IR: [IrOp::LtUnsigned]
Code: register_primitive("U<", False, vec![IrOp::LtUnsigned])
Source: crates/core/src/outer.rs

Front: Forth word 0= — what is its IR body?
Back: IR: [IrOp::ZeroEq]
Code: register_primitive("0=", False, vec![IrOp::ZeroEq])
Source: crates/core/src/outer.rs

Front: Forth word 0< — what is its IR body?
Back: IR: [IrOp::ZeroLt]
Code: register_primitive("0<", False, vec![IrOp::ZeroLt])
Source: crates/core/src/outer.rs

Front: Forth word AND — what is its IR body?
Back: IR: [IrOp::And]
Code: register_primitive("AND", False, vec![IrOp::And])
Source: crates/core/src/outer.rs

Front: Forth word OR — what is its IR body?
Back: IR: [IrOp::Or]
Code: register_primitive("OR", False, vec![IrOp::Or])
Source: crates/core/src/outer.rs

Front: Forth word XOR — what is its IR body?
Back: IR: [IrOp::Xor]
Code: register_primitive("XOR", False, vec![IrOp::Xor])
Source: crates/core/src/outer.rs

Front: Forth word INVERT — what is its IR body?
Back: IR: [IrOp::Invert]
Code: register_primitive("INVERT", False, vec![IrOp::Invert])
Source: crates/core/src/outer.rs

Front: Forth word LSHIFT — what is its IR body?
Back: IR: [IrOp::Lshift]
Code: register_primitive("LSHIFT", False, vec![IrOp::Lshift])
Source: crates/core/src/outer.rs

Front: Forth word RSHIFT — what is its IR body?
Back: IR: [IrOp::Rshift]
Code: register_primitive("RSHIFT", False, vec![IrOp::Rshift])
Source: crates/core/src/outer.rs

Front: Forth word @ — what is its IR body?
Back: IR: [IrOp::Fetch]
Code: register_primitive("@", False, vec![IrOp::Fetch])
Source: crates/core/src/outer.rs

Front: Forth word ! — what is its IR body?
Back: IR: [IrOp::Store]
Code: register_primitive("!", False, vec![IrOp::Store])
Source: crates/core/src/outer.rs

Front: Forth word C@ — what is its IR body?
Back: IR: [IrOp::CFetch]
Code: register_primitive("C@", False, vec![IrOp::CFetch])
Source: crates/core/src/outer.rs

Front: Forth word C! — what is its IR body?
Back: IR: [IrOp::CStore]
Code: register_primitive("C!", False, vec![IrOp::CStore])
Source: crates/core/src/outer.rs

Front: Forth word +! — what is its IR body?
Back: IR: [IrOp::PlusStore]
Code: register_primitive("+!", False, vec![IrOp::PlusStore])
Source: crates/core/src/outer.rs

Front: Forth word >R — what is its IR body?
Back: IR: [IrOp::ToR]
Code: register_primitive(">R", False, vec![IrOp::ToR])
Source: crates/core/src/outer.rs

Front: Forth word R> — what is its IR body?
Back: IR: [IrOp::FromR]
Code: register_primitive("R>", False, vec![IrOp::FromR])
Source: crates/core/src/outer.rs

Front: Forth word R@ — what is its IR body?
Back: IR: [IrOp::RFetch]
Code: register_primitive("R@", False, vec![IrOp::RFetch])
Source: crates/core/src/outer.rs

Front: Forth word EMIT — what is its IR body?
Back: IR: [IrOp::Emit]
Code: register_primitive("EMIT", False, vec![IrOp::Emit])
Source: crates/core/src/outer.rs

Front: Forth word CR — what is its IR body?
Back: IR: [IrOp::Cr]
Code: register_primitive("CR", False, vec![IrOp::Cr])
Source: crates/core/src/outer.rs

Front: Forth word TRUE — what is its IR body?
Back: IR: [IrOp::PushI32(-1)]
Code: register_primitive("TRUE", False, vec![IrOp::PushI32(-1)])
Source: crates/core/src/outer.rs

Front: Forth word FALSE — what is its IR body?
Back: IR: [IrOp::PushI32(0)]
Code: register_primitive("FALSE", False, vec![IrOp::PushI32(0)])
Source: crates/core/src/outer.rs

Front: Forth word BL — what is its IR body?
Back: IR: [IrOp::PushI32(32)]
Code: register_primitive("BL", False, vec![IrOp::PushI32(32)])
Source: crates/core/src/outer.rs

Front: Forth word SPACE — what is its IR body?
Back: IR: [IrOp::PushI32(32), IrOp::Emit]
Code: register_primitive("SPACE", False, vec![IrOp::PushI32(32), IrOp::Emit])
Source: crates/core/src/outer.rs

Front: Forth word 1+ — what is its IR body?
Back: IR: [IrOp::PushI32(1), IrOp::Add]
Code: register_primitive("1+", False, vec![IrOp::PushI32(1), IrOp::Add])
Source: crates/core/src/outer.rs

Front: Forth word 1- — what is its IR body?
Back: IR: [IrOp::PushI32(1), IrOp::Sub]
Code: register_primitive("1-", False, vec![IrOp::PushI32(1), IrOp::Sub])
Source: crates/core/src/outer.rs

Front: Forth word 2* — what is its IR body?
Back: IR: [IrOp::PushI32(1), IrOp::Lshift]
Code: register_primitive("2*", False, vec![IrOp::PushI32(1), IrOp::Lshift])
Source: crates/core/src/outer.rs

Front: Forth word 2/ — what is its IR body?
Back: IR: [IrOp::PushI32(1), IrOp::ArithRshift]
Code: register_primitive("2/", False, vec![IrOp::PushI32(1), IrOp::ArithRshift])
Source: crates/core/src/outer.rs

Front: Forth word I — what is its IR body?
Back: IR: [IrOp::RFetch]
Code: register_primitive("I", False, vec![IrOp::RFetch])
Source: crates/core/src/outer.rs

Front: Forth word J — what is its IR body?
Back: IR: [IrOp::LoopJ]
Code: register_primitive("J", False, vec![IrOp::LoopJ])
Source: crates/core/src/outer.rs

Front: Forth word CELLS — what is its IR body?
Back: IR: [IrOp::PushI32(4), IrOp::Mul]
Code: register_primitive("CELLS", False, vec![IrOp::PushI32(4), IrOp::Mul])
Source: crates/core/src/outer.rs

Front: Forth word CELL+ — what is its IR body?
Back: IR: [IrOp::PushI32(4), IrOp::Add]
Code: register_primitive("CELL+", False, vec![IrOp::PushI32(4), IrOp::Add])
Source: crates/core/src/outer.rs

Front: Forth word CHARS — what is its IR body?
Back: IR: [])?; self.register_primitive("CHAR+", false, vec![IrOp::PushI32(1), IrOp::Add]
Code: register_primitive("CHARS", False, vec![])?; self.register_primitive("CHAR+", false, vec![IrOp::PushI32(1), IrOp::Add])
Source: crates/core/src/outer.rs

Front: Forth word 2DUP — what is its IR body?
Back: IR: [IrOp::Over, IrOp::Over]
Code: register_primitive("2DUP", False, vec![IrOp::Over, IrOp::Over])
Source: crates/core/src/outer.rs

Front: Forth word 2DROP — what is its IR body?
Back: IR: [IrOp::Drop, IrOp::Drop]
Code: register_primitive("2DROP", False, vec![IrOp::Drop, IrOp::Drop])
Source: crates/core/src/outer.rs

Front: Forth word 0<> — what is its IR body?
Back: IR: [IrOp::ZeroEq, IrOp::ZeroEq]
Code: register_primitive("0<>", False, vec![IrOp::ZeroEq, IrOp::ZeroEq])
Source: crates/core/src/outer.rs

Front: Forth word 0> — what is its IR body?
Back: IR: [IrOp::PushI32(0), IrOp::Gt]
Code: register_primitive("0>", False, vec![IrOp::PushI32(0), IrOp::Gt])
Source: crates/core/src/outer.rs

Front: Forth word EXECUTE — what is its IR body?
Back: IR: [IrOp::Execute]
Code: register_primitive("EXECUTE", False, vec![IrOp::Execute])
Source: crates/core/src/outer.rs

Front: Forth word SP@ — what is its IR body?
Back: IR: [IrOp::SpFetch]
Code: register_primitive("SP@", False, vec![IrOp::SpFetch])
Source: crates/core/src/outer.rs

Front: Forth word 2>R — what is its IR body?
Back: IR: [IrOp::Swap, IrOp::ToR, IrOp::ToR]
Code: register_primitive("2>R", False, vec![IrOp::Swap, IrOp::ToR, IrOp::ToR])
Source: crates/core/src/outer.rs

Front: Forth word 2R> — what is its IR body?
Back: IR: [IrOp::FromR, IrOp::FromR, IrOp::Swap]
Code: register_primitive("2R>", False, vec![IrOp::FromR, IrOp::FromR, IrOp::Swap])
Source: crates/core/src/outer.rs

Front: Forth word U> — what is its IR body?
Back: IR: [IrOp::Swap, IrOp::LtUnsigned]
Code: register_primitive("U>", False, vec![IrOp::Swap, IrOp::LtUnsigned])
Source: crates/core/src/outer.rs

Front: Forth word S>D — what is its IR body?
Back: IR: [IrOp::Dup, IrOp::ZeroLt]
Code: register_primitive("S>D", False, vec![IrOp::Dup, IrOp::ZeroLt])
Source: crates/core/src/outer.rs

Front: Forth word >IN — what is its IR body?
Back: IR: [IrOp::PushI32(SYSVAR_TO_IN as i32)]
Code: register_primitive(">IN", False, vec![IrOp::PushI32(SYSVAR_TO_IN as i32)])
Source: crates/core/src/outer.rs

Front: Forth word STATE — what is its IR body?
Back: IR: [IrOp::PushI32(SYSVAR_STATE as i32)]
Code: register_primitive("STATE", False, vec![IrOp::PushI32(SYSVAR_STATE as i32)])
Source: crates/core/src/outer.rs

Front: Forth word BASE — what is its IR body?
Back: IR: [IrOp::PushI32(SYSVAR_BASE_VAR as i32)]
Code: register_primitive("BASE", False, vec![IrOp::PushI32(SYSVAR_BASE_VAR as i32)])
Source: crates/core/src/outer.rs

Front: Forth word D>S — what is its IR body?
Back: IR: [IrOp::Drop]
Code: register_primitive("D>S", False, vec![IrOp::Drop])
Source: crates/core/src/outer.rs

Front: Forth word FORTH-WORDLIST — what is its IR body?
Back: IR: [IrOp::PushI32(1)]
Code: register_primitive("FORTH-WORDLIST", False, vec![IrOp::PushI32(1)])
Source: crates/core/src/outer.rs

Front: Forth word FDROP — what is its IR body?
Back: IR: [IrOp::FDrop]
Code: register_primitive("FDROP", False, vec![IrOp::FDrop])
Source: crates/core/src/outer.rs

Front: Forth word FDUP — what is its IR body?
Back: IR: [IrOp::FDup]
Code: register_primitive("FDUP", False, vec![IrOp::FDup])
Source: crates/core/src/outer.rs

Front: Forth word FSWAP — what is its IR body?
Back: IR: [IrOp::FSwap]
Code: register_primitive("FSWAP", False, vec![IrOp::FSwap])
Source: crates/core/src/outer.rs

Front: Forth word FOVER — what is its IR body?
Back: IR: [IrOp::FOver]
Code: register_primitive("FOVER", False, vec![IrOp::FOver])
Source: crates/core/src/outer.rs

Front: Forth word FNIP — what is its IR body?
Back: IR: [IrOp::FSwap, IrOp::FDrop]
Code: register_primitive("FNIP", False, vec![IrOp::FSwap, IrOp::FDrop])
Source: crates/core/src/outer.rs

Front: Forth word FTUCK — what is its IR body?
Back: IR: [IrOp::FSwap, IrOp::FOver]
Code: register_primitive("FTUCK", False, vec![IrOp::FSwap, IrOp::FOver])
Source: crates/core/src/outer.rs

Front: Forth word F+ — what is its IR body?
Back: IR: [IrOp::FAdd]
Code: register_primitive("F+", False, vec![IrOp::FAdd])
Source: crates/core/src/outer.rs

Front: Forth word F- — what is its IR body?
Back: IR: [IrOp::FSub]
Code: register_primitive("F-", False, vec![IrOp::FSub])
Source: crates/core/src/outer.rs

Front: Forth word F* — what is its IR body?
Back: IR: [IrOp::FMul]
Code: register_primitive("F*", False, vec![IrOp::FMul])
Source: crates/core/src/outer.rs

Front: Forth word F/ — what is its IR body?
Back: IR: [IrOp::FDiv]
Code: register_primitive("F/", False, vec![IrOp::FDiv])
Source: crates/core/src/outer.rs

Front: Forth word FNEGATE — what is its IR body?
Back: IR: [IrOp::FNegate]
Code: register_primitive("FNEGATE", False, vec![IrOp::FNegate])
Source: crates/core/src/outer.rs

Front: Forth word FABS — what is its IR body?
Back: IR: [IrOp::FAbs]
Code: register_primitive("FABS", False, vec![IrOp::FAbs])
Source: crates/core/src/outer.rs

Front: Forth word FMAX — what is its IR body?
Back: IR: [IrOp::FMax]
Code: register_primitive("FMAX", False, vec![IrOp::FMax])
Source: crates/core/src/outer.rs

Front: Forth word FMIN — what is its IR body?
Back: IR: [IrOp::FMin]
Code: register_primitive("FMIN", False, vec![IrOp::FMin])
Source: crates/core/src/outer.rs

Front: Forth word FSQRT — what is its IR body?
Back: IR: [IrOp::FSqrt]
Code: register_primitive("FSQRT", False, vec![IrOp::FSqrt])
Source: crates/core/src/outer.rs

Front: Forth word FLOOR — what is its IR body?
Back: IR: [IrOp::FFloor]
Code: register_primitive("FLOOR", False, vec![IrOp::FFloor])
Source: crates/core/src/outer.rs

Front: Forth word FROUND — what is its IR body?
Back: IR: [IrOp::FRound]
Code: register_primitive("FROUND", False, vec![IrOp::FRound])
Source: crates/core/src/outer.rs

Front: Forth word F0= — what is its IR body?
Back: IR: [IrOp::FZeroEq]
Code: register_primitive("F0=", False, vec![IrOp::FZeroEq])
Source: crates/core/src/outer.rs

Front: Forth word F0< — what is its IR body?
Back: IR: [IrOp::FZeroLt]
Code: register_primitive("F0<", False, vec![IrOp::FZeroLt])
Source: crates/core/src/outer.rs

Front: Forth word F= — what is its IR body?
Back: IR: [IrOp::FEq]
Code: register_primitive("F=", False, vec![IrOp::FEq])
Source: crates/core/src/outer.rs

Front: Forth word F< — what is its IR body?
Back: IR: [IrOp::FLt]
Code: register_primitive("F<", False, vec![IrOp::FLt])
Source: crates/core/src/outer.rs

Front: Forth word F@ — what is its IR body?
Back: IR: [IrOp::FetchFloat]
Code: register_primitive("F@", False, vec![IrOp::FetchFloat])
Source: crates/core/src/outer.rs

Front: Forth word F! — what is its IR body?
Back: IR: [IrOp::StoreFloat]
Code: register_primitive("F!", False, vec![IrOp::StoreFloat])
Source: crates/core/src/outer.rs

Front: Forth word S>F — what is its IR body?
Back: IR: [IrOp::StoF]
Code: register_primitive("S>F", False, vec![IrOp::StoF])
Source: crates/core/src/outer.rs

Front: Forth word F>S — what is its IR body?
Back: IR: [IrOp::FtoS]
Code: register_primitive("F>S", False, vec![IrOp::FtoS])
Source: crates/core/src/outer.rs

Front: boot.fth: How is DEPTH defined?
Back: DATA_STACK_TOP = 5632 (0x1600), uses arithmetic right shift for / 4
Code: : DEPTH SP@ 5632 SWAP - 2 RSHIFT ;
Source: crates/core/boot.fth

Front: boot.fth: How is PICK defined?
Back: PICK ( xn..x0 n -- xn..x0 xn ) copy nth stack item
Code: : PICK 1+ CELLS SP@ + @ ;
Source: crates/core/boot.fth

Front: boot.fth: How is 2OVER defined?
Back: 2OVER ( x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2 )
Code: : 2OVER 3 PICK 3 PICK ;
Source: crates/core/boot.fth

Front: boot.fth: How is 2ROT defined?
Back: 2ROT ( x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2 )
Code: : 2ROT 2>R 2SWAP 2R> 2SWAP ;
Source: crates/core/boot.fth

Front: boot.fth: How is WITHIN defined?
Back: WITHIN ( n lo hi -- flag ) true if lo <= n < hi (unsigned)
Code: : WITHIN OVER - >R - R> U< ;
Source: crates/core/boot.fth

Front: boot.fth: How is 2@ defined?
Back: 2@ ( addr -- x1 x2 ) fetch double-cell, low addr = deeper stack
Code: : 2@ DUP CELL+ @ SWAP @ ;
Source: crates/core/boot.fth

Front: boot.fth: How is 2! defined?
Back: 2! ( x1 x2 addr -- ) store double-cell
Code: : 2! SWAP OVER ! CELL+ ! ;
Source: crates/core/boot.fth

Front: boot.fth: How is FILL defined?
Back: FILL ( addr u char -- ) fill u bytes with char
Code: : FILL ROT ROT 0 ?DO 2DUP I + C! LOOP 2DROP ;
Source: crates/core/boot.fth

Front: boot.fth: How is CMOVE defined?
Back: CMOVE ( src dst u -- ) forward byte copy
Code: : CMOVE 0 ?DO OVER I + C@ OVER I + C! LOOP 2DROP ;
Source: crates/core/boot.fth

Front: boot.fth: How is CMOVE> defined?
Back: CMOVE> ( src dst u -- ) backward byte copy (for overlap)
Code: : CMOVE> DUP 0= IF DROP 2DROP EXIT THEN 1- >R BEGIN R@ 0< INVERT WHILE OVER R@ + C@ OVER R@ + C! R> 1- >R REPEAT R> DROP 2DROP ;
Source: crates/core/boot.fth

Front: boot.fth: How is MOVE defined?
Back: MOVE ( src dst u -- ) smart copy (handles overlap)
Code: : MOVE DUP 0= IF DROP 2DROP EXIT THEN >R 2DUP U< IF R> CMOVE> ELSE R> CMOVE THEN ;
Source: crates/core/boot.fth

Front: boot.fth: How is ERASE defined?
Back: ERASE ( addr u -- ) zero fill
Code: : ERASE 0 FILL ;
Source: crates/core/boot.fth

Front: boot.fth: How is BLANK defined?
Back: BLANK ( addr u -- ) space fill
Code: : BLANK BL FILL ;
Source: crates/core/boot.fth

Front: boot.fth: How is /STRING defined?
Back: /STRING ( addr u n -- addr+n u-n )
Code: : /STRING ROT OVER + ROT ROT - ;
Source: crates/core/boot.fth

Front: boot.fth: How is -TRAILING defined?
Back: -TRAILING ( addr u -- addr u' ) remove trailing spaces
Code: : -TRAILING BEGIN DUP 0> WHILE 2DUP + 1- C@ BL <> IF EXIT THEN 1- REPEAT ;
Source: crates/core/boot.fth

Front: boot.fth: How is D+ defined?
Back: D+ ( d1 d2 -- d3 ) double-cell addition with carry
Code: : D+ >R SWAP >R DUP >R + DUP R> U< IF R> R> + 1+ ELSE R> R> + THEN ;
Source: crates/core/boot.fth

Front: boot.fth: How is DNEGATE defined?
Back: DNEGATE ( d -- -d ) double-cell negate (two's complement)
Code: : DNEGATE INVERT SWAP INVERT SWAP 1 0 D+ ;
Source: crates/core/boot.fth

Front: boot.fth: How is D- defined?
Back: D- ( d1 d2 -- d3 ) double-cell subtraction
Code: : D- DNEGATE D+ ;
Source: crates/core/boot.fth

Front: boot.fth: How is DABS defined?
Back: DABS ( d -- |d| ) double-cell absolute value
Code: : DABS DUP 0< IF DNEGATE THEN ;
Source: crates/core/boot.fth

Front: boot.fth: How is D0= defined?
Back: D0= ( d -- flag ) true if d is zero
Code: : D0= OR 0= ;
Source: crates/core/boot.fth

Front: boot.fth: How is D0< defined?
Back: D0< ( d -- flag ) true if d is negative
Code: : D0< NIP 0< ;
Source: crates/core/boot.fth

Front: boot.fth: How is D= defined?
Back: D= ( d1 d2 -- flag ) true if d1 = d2
Code: : D= D- D0= ;
Source: crates/core/boot.fth

Front: boot.fth: How is D< defined?
Back: Compare high cells first (signed); if equal, compare low cells unsigned.
Code: : D< ROT 2DUP = IF 2DROP U< ELSE 2SWAP 2DROP > THEN ;
Source: crates/core/boot.fth

Front: boot.fth: How is D2* defined?
Back: D2* ( d -- d*2 ) double-cell shift left
Code: : D2* 2DUP D+ ;
Source: crates/core/boot.fth

Front: boot.fth: How is D2/ defined?
Back: D2/ ( d -- d/2 ) double-cell arithmetic shift right
Code: : D2/ DUP 1 AND 31 LSHIFT >R 2/ SWAP 1 RSHIFT R> OR SWAP ;
Source: crates/core/boot.fth

Front: boot.fth: How is DMAX defined?
Back: DMAX ( d1 d2 -- d-max ) double-cell maximum
Code: : DMAX 2OVER 2OVER D< IF 2SWAP THEN 2DROP ;
Source: crates/core/boot.fth

Front: boot.fth: How is DMIN defined?
Back: DMIN ( d1 d2 -- d-min ) double-cell minimum
Code: : DMIN 2OVER 2OVER D< INVERT IF 2SWAP THEN 2DROP ;
Source: crates/core/boot.fth

Front: boot.fth: How is M+ defined?
Back: M+ ( d n -- d+n ) add single to double
Code: : M+ S>D D+ ;
Source: crates/core/boot.fth

Front: boot.fth: How is DU< defined?
Back: Compare high cells (unsigned); if equal, compare low cells unsigned.
Code: : DU< ROT 2DUP = IF 2DROP U< ELSE SWAP U< NIP NIP THEN ;
Source: crates/core/boot.fth

Front: boot.fth: How is SM/REM defined?
Back: Remainder sign: same as dividend.
Code: : SM/REM OVER >R 2DUP XOR >R ABS >R DABS R> UM/MOD R> 0< IF NEGATE THEN SWAP R> 0< IF NEGATE THEN SWAP ;
Source: crates/core/boot.fth

Front: boot.fth: How is FM/MOD defined?
Back: FM/MOD ( d n -- rem quot ) floored division
Code: : FM/MOD DUP >R SM/REM OVER 0<> OVER 0< AND IF 1- SWAP R> + SWAP ELSE R> DROP THEN ;
Source: crates/core/boot.fth

Front: boot.fth: How is */ defined?
Back: Must use SM/REM (symmetric) to match WAFER's WASM i32.div_s semantics.
Code: : */ >R M* R> SM/REM SWAP DROP ;
Source: crates/core/boot.fth

Front: boot.fth: How is */MOD defined?
Back: */MOD ( n1 n2 n3 -- rem quot )
Code: : */MOD >R M* R> SM/REM ;
Source: crates/core/boot.fth

Front: boot.fth: How is HERE defined?
Back: and whenever host ALLOT/comma modifies it.
Code: : HERE 12 @ ;
Source: crates/core/boot.fth

Front: boot.fth: How is ALLOT defined?
Back: ALLOT ( n -- ) advance HERE by n bytes
Code: : ALLOT HERE + 12 ! ;
Source: crates/core/boot.fth

Front: boot.fth: How is , defined?
Back: , ( x -- ) store cell at HERE, advance by cell
Code: : , HERE ! 1 CELLS ALLOT ;
Source: crates/core/boot.fth

Front: boot.fth: How is C, defined?
Back: C, ( char -- ) store byte at HERE, advance by 1
Code: : C, HERE C! 1 ALLOT ;
Source: crates/core/boot.fth

Front: boot.fth: How is ALIGN defined?
Back: ALIGN ( -- ) align HERE to cell boundary
Code: : ALIGN HERE ALIGNED 12 ! ;
Source: crates/core/boot.fth

Front: boot.fth: How is TYPE defined?
Back: TYPE ( c-addr u -- ) output u characters
Code: : TYPE 0 ?DO DUP C@ EMIT 1+ LOOP DROP ;
Source: crates/core/boot.fth

Front: boot.fth: How is SPACES defined?
Back: SPACES ( n -- ) output n spaces
Code: : SPACES 0 ?DO SPACE LOOP ;
Source: crates/core/boot.fth

Front: boot.fth: How is <# defined?
Back: <# ( -- ) begin pictured numeric output
Code: : <# 1472 28 ! ;
Source: crates/core/boot.fth

Front: boot.fth: How is HOLD defined?
Back: HOLD ( char -- ) add character to pictured output
Code: : HOLD 28 @ 1- DUP 28 ! C! ;
Source: crates/core/boot.fth

Front: boot.fth: How is HOLDS defined?
Back: HOLDS ( addr u -- ) add string to pictured output
Code: : HOLDS BEGIN DUP 0> WHILE 1- 2DUP + C@ HOLD REPEAT 2DROP ;
Source: crates/core/boot.fth

Front: boot.fth: How is SIGN defined?
Back: SIGN ( n -- ) if negative, add '-'
Code: : SIGN 0< IF 45 HOLD THEN ;
Source: crates/core/boot.fth

Front: boot.fth: How is # defined?
Back: Second UM/MOD divides (rem1 ud-lo) by BASE -> digit, quot-lo
Code: : # BASE @ >R 0 R@ UM/MOD R> SWAP >R UM/MOD SWAP DUP 9 > IF 7 + THEN 48 + HOLD R> ;
Source: crates/core/boot.fth

Front: boot.fth: How is #S defined?
Back: #S ( ud -- 0 0 ) convert all digits
Code: : #S BEGIN # 2DUP OR 0= UNTIL ;
Source: crates/core/boot.fth

Front: boot.fth: How is #> defined?
Back: #> ( ud -- c-addr u ) end pictured output, return string
Code: : #> 2DROP 28 @ 1472 OVER - ;
Source: crates/core/boot.fth

Front: boot.fth: How is . defined?
Back: . ( n -- ) print signed number and space
Code: : . DUP ABS 0 <# #S ROT SIGN #> TYPE SPACE ;
Source: crates/core/boot.fth

Front: boot.fth: How is U. defined?
Back: U. ( u -- ) print unsigned number and space
Code: : U. 0 <# #S #> TYPE SPACE ;
Source: crates/core/boot.fth

Front: boot.fth: How is .R defined?
Back: .R ( n width -- ) print right-justified signed number
Code: : .R >R DUP ABS 0 <# #S ROT SIGN #> R> OVER - SPACES TYPE ;
Source: crates/core/boot.fth

Front: boot.fth: How is U.R defined?
Back: U.R ( u width -- ) print right-justified unsigned number
Code: : U.R >R 0 <# #S #> R> OVER - SPACES TYPE ;
Source: crates/core/boot.fth

Front: boot.fth: How is D. defined?
Back: D. ( d -- ) print signed double number and space
Code: : D. SWAP OVER DABS <# #S ROT SIGN #> TYPE SPACE ;
Source: crates/core/boot.fth

Front: boot.fth: How is D.R defined?
Back: D.R ( d width -- ) print right-justified signed double
Code: : D.R >R SWAP OVER DABS <# #S ROT SIGN #> R> OVER - SPACES TYPE ;
Source: crates/core/boot.fth

Front: boot.fth: How is DEFER! defined?
Back: DEFER! ( xt xt-deferred -- ) store xt into a deferred word
Code: : DEFER! >BODY ! ;
Source: crates/core/boot.fth

Front: boot.fth: How is DEFER@ defined?
Back: DEFER@ ( xt-deferred -- xt ) retrieve xt from a deferred word
Code: : DEFER@ >BODY @ ;
Source: crates/core/boot.fth

Front: boot.fth: How is COMPARE defined?
Back: COMPARE ( addr1 u1 addr2 u2 -- n ) compare two strings lexicographically
Code: : COMPARE ROT 2DUP SWAP - >R MIN 0 ?DO OVER I + C@ OVER I + C@ 2DUP <> IF < IF 2DROP R> DROP -1 UNLOOP EXIT ELSE 2DROP R> DROP 1 UNLOOP EXIT THEN THEN 2DROP LOOP 2DROP R> DUP 0< IF DROP -1 EXIT THEN 0> IF 1 EXIT THEN 0 ;
Source: crates/core/boot.fth

Front: boot.fth: How is -TRAILING defined?
Back: -TRAILING ( c-addr u1 -- c-addr u2 ) remove trailing spaces
Code: : -TRAILING BEGIN DUP WHILE 2DUP + 1- C@ BL = WHILE 1- REPEAT THEN ;
Source: crates/core/boot.fth

Front: boot.fth: How is SOURCE defined?
Back: INPUT_BUFFER_BASE = 64, SYSVAR_NUM_TIB = 24
Code: : SOURCE 64 24 @ ;
Source: crates/core/boot.fth

Front: boot.fth: How is FALIGNED defined?
Back: FALIGNED ( addr -- addr ) align to 8-byte float boundary
Code: : FALIGNED 7 + -8 AND ;
Source: crates/core/boot.fth

Front: boot.fth: How is SFALIGNED defined?
Back: SFALIGNED ( addr -- addr ) align to 4-byte single-float boundary
Code: : SFALIGNED 3 + -4 AND ;
Source: crates/core/boot.fth

Front: boot.fth: How is DFALIGNED defined?
Back: DFALIGNED ( addr -- addr ) align to 8-byte double-float boundary
Code: : DFALIGNED 7 + -8 AND ;
Source: crates/core/boot.fth

Front: boot.fth: How is FALIGN defined?
Back: FALIGN ( -- ) align HERE to 8-byte float boundary
Code: : FALIGN HERE FALIGNED 12 ! ;
Source: crates/core/boot.fth

Front: boot.fth: How is SFALIGN defined?
Back: SFALIGN ( -- ) align HERE to 4-byte single-float boundary
Code: : SFALIGN ALIGN ;
Source: crates/core/boot.fth

Front: boot.fth: How is DFALIGN defined?
Back: DFALIGN ( -- ) align HERE to 8-byte double-float boundary
Code: : DFALIGN FALIGN ;
Source: crates/core/boot.fth

Front: HostAccess::mem_read_i32() — what does it do?
Back: Read a 32-bit integer from linear memory at `addr` (little-endian).
Code: fn mem_read_i32(&mut self, addr: u32) -> i32
Source: crates/core/src/runtime.rs

Front: HostAccess::mem_write_i32() — what does it do?
Back: Write a 32-bit integer to linear memory at `addr` (little-endian).
Code: fn mem_write_i32(&mut self, addr: u32, val: i32)
Source: crates/core/src/runtime.rs

Front: HostAccess::mem_read_u8() — what does it do?
Back: Read a single byte from linear memory.
Code: fn mem_read_u8(&mut self, addr: u32) -> u8
Source: crates/core/src/runtime.rs

Front: HostAccess::mem_write_u8() — what does it do?
Back: Write a single byte to linear memory.
Code: fn mem_write_u8(&mut self, addr: u32, val: u8)
Source: crates/core/src/runtime.rs

Front: HostAccess::mem_read_slice() — what does it do?
Back: Read a slice of bytes from linear memory.
Code: fn mem_read_slice(&mut self, addr: u32, len: usize) -> Vec<u8>
Source: crates/core/src/runtime.rs

Front: HostAccess::mem_write_slice() — what does it do?
Back: Write a slice of bytes to linear memory.
Code: fn mem_write_slice(&mut self, addr: u32, data: &[u8])
Source: crates/core/src/runtime.rs

Front: HostAccess::mem_len() — what does it do?
Back: Total size of linear memory in bytes.
Code: fn mem_len(&mut self) -> usize
Source: crates/core/src/runtime.rs

Front: HostAccess::get_dsp() — what does it do?
Back: Read the data stack pointer global.
Code: fn get_dsp(&mut self) -> u32
Source: crates/core/src/runtime.rs

Front: HostAccess::set_dsp() — what does it do?
Back: Write the data stack pointer global.
Code: fn set_dsp(&mut self, val: u32)
Source: crates/core/src/runtime.rs

Front: HostAccess::get_rsp() — what does it do?
Back: Read the return stack pointer global.
Code: fn get_rsp(&mut self) -> u32
Source: crates/core/src/runtime.rs

Front: HostAccess::set_rsp() — what does it do?
Back: Write the return stack pointer global.
Code: fn set_rsp(&mut self, val: u32)
Source: crates/core/src/runtime.rs

Front: HostAccess::get_fsp() — what does it do?
Back: Read the float stack pointer global.
Code: fn get_fsp(&mut self) -> u32
Source: crates/core/src/runtime.rs

Front: HostAccess::set_fsp() — what does it do?
Back: Write the float stack pointer global.
Code: fn set_fsp(&mut self, val: u32)
Source: crates/core/src/runtime.rs

Front: HostAccess::call_func() — what does it do?
Back: Call a function in the shared table by index. Needed by CATCH to invoke the xt it receives.
Code: fn call_func(&mut self, fn_index: u32) -> anyhow::Result<()>
Source: crates/core/src/runtime.rs

Front: Runtime::new() — what does it do?
Back: Create a new runtime with shared linear memory, function table, stack pointer globals, and an `emit` host function wired to `output`.
Code: fn new(
Source: crates/core/src/runtime.rs

Front: Runtime::mem_read_i32() — what does it do?
Back: Read a 32-bit integer from linear memory at `addr` (little-endian).
Code: fn mem_read_i32(&mut self, addr: u32) -> i32
Source: crates/core/src/runtime.rs

Front: Runtime::mem_write_i32() — what does it do?
Back: Write a 32-bit integer to linear memory at `addr` (little-endian).
Code: fn mem_write_i32(&mut self, addr: u32, val: i32)
Source: crates/core/src/runtime.rs

Front: Runtime::mem_read_u8() — what does it do?
Back: Read a single byte from linear memory.
Code: fn mem_read_u8(&mut self, addr: u32) -> u8
Source: crates/core/src/runtime.rs

Front: Runtime::mem_write_u8() — what does it do?
Back: Write a single byte to linear memory.
Code: fn mem_write_u8(&mut self, addr: u32, val: u8)
Source: crates/core/src/runtime.rs

Front: Runtime::mem_read_slice() — what does it do?
Back: Read a slice of bytes from linear memory.
Code: fn mem_read_slice(&mut self, addr: u32, len: usize) -> Vec<u8>
Source: crates/core/src/runtime.rs

Front: Runtime::mem_write_slice() — what does it do?
Back: Write a slice of bytes to linear memory.
Code: fn mem_write_slice(&mut self, addr: u32, data: &[u8])
Source: crates/core/src/runtime.rs

Front: Runtime::mem_len() — what does it do?
Back: Total size of linear memory in bytes.
Code: fn mem_len(&mut self) -> usize
Source: crates/core/src/runtime.rs

Front: Runtime::get_dsp() — what does it do?
Back: Read the data stack pointer global.
Code: fn get_dsp(&mut self) -> u32
Source: crates/core/src/runtime.rs

Front: Runtime::set_dsp() — what does it do?
Back: Write the data stack pointer global.
Code: fn set_dsp(&mut self, val: u32)
Source: crates/core/src/runtime.rs

Front: Runtime::get_rsp() — what does it do?
Back: Read the return stack pointer global.
Code: fn get_rsp(&mut self) -> u32
Source: crates/core/src/runtime.rs

Front: Runtime::set_rsp() — what does it do?
Back: Write the return stack pointer global.
Code: fn set_rsp(&mut self, val: u32)
Source: crates/core/src/runtime.rs

Front: Runtime::get_fsp() — what does it do?
Back: Read the float stack pointer global.
Code: fn get_fsp(&mut self) -> u32
Source: crates/core/src/runtime.rs

Front: Runtime::set_fsp() — what does it do?
Back: Write the float stack pointer global.
Code: fn set_fsp(&mut self, val: u32)
Source: crates/core/src/runtime.rs

Front: Runtime::table_size() — what does it do?
Back: Current number of entries in the function table.
Code: fn table_size(&mut self) -> u32
Source: crates/core/src/runtime.rs

Front: Runtime::ensure_table_size() — what does it do?
Back: Grow the table if needed so that index `needed` is valid.
Code: fn ensure_table_size(&mut self, needed: u32) -> anyhow::Result<()>
Source: crates/core/src/runtime.rs

Front: Runtime::instantiate_and_install() — what does it do?
Back: Compile WASM bytes into a module, instantiate it with shared imports (memory, table, globals, emit), and install the exported function at `fn_index` in the shared table.
Code: fn instantiate_and_install(&mut self, wasm_bytes: &[u8], fn_index: u32) -> anyhow::Result<()>
Source: crates/core/src/runtime.rs

Front: Runtime::call_func() — what does it do?
Back: Call the function at `fn_index` in the shared table.
Code: fn call_func(&mut self, fn_index: u32) -> anyhow::Result<()>
Source: crates/core/src/runtime.rs

Front: Runtime::register_host_func() — what does it do?
Back: Register a void→void host function at `fn_index` in the shared table. The callback receives a [`HostAccess`] for memory and global operations. It may also capture shared state via `Arc<Mutex<...>>`.
Code: fn register_host_func(&mut self, fn_index: u32, f: HostFn) -> anyhow::Result<()>
Source: crates/core/src/runtime.rs