Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Register I/O

Instructions for moving data between the value stack, the 256-slot register file, calldata, and output slots: LOAD, STOW, DROP, INPUT, OUTPUT. Byte values, operand layouts and stack effects are in the Register I/O section of the opcode reference.

Registers Default to Unset, Not Int(0)

Every register starts as RegVal::Unset, not Int(0). Only LOAD and OUTPUT fault UnsetRegister when reading an unset register; every other instruction that reads a register instead reports RegisterType, with unset as the actual variant held, for example register r7 holds unset, expected vec<int> from VECPUSH r7, register r7 holds unset, expected model|sample from GETLINE r7, or register r7 holds unset, expected sample from ENERGY r0 r7. LOAD r3 as the first instruction of a program, before anything has written to r3, fails at runtime with register r3 is unset. The verifier’s uninitialised-register check catches the same defect statically for straight-line code, one reason to run xquad verify before xquad run: the sequence PUSH 1 / STOW r0 / DROP r0 / LOAD r0 is rejected by verify with register r0 read at byte 0x0006 before being written, before the runtime fault is ever reached.

LOAD distinguishes two failure modes for a register that is not usable as an integer. An unset register faults UnsetRegister; a register holding something other than Int, for example a Model from BQMX, faults RegisterType instead, naming the actual variant held. LOAD on a register that holds a freshly allocated Model fails with register r0 holds model, expected int, a different message from the unset case.

Stack-Register Bridge

The value stack holds only i64 integers. LOAD pushes a register’s Int value onto the stack; STOW pops the stack top and writes it into a register as Int. Neither coerces: LOAD on a non-Int register errors rather than reinterpreting the value as an integer.

Richer types, models, vectors and samples, never touch the stack directly. Moving them into or out of the VM goes through INPUT and OUTPUT instead, against the calldata and output-slot arrays rather than the stack. INPUT reg pops a calldata slot index and clones calldata[slot] into reg; OUTPUT reg pops an output slot index and clones reg’s current value into outputs[slot]. Either direction transfers any RegVal variant, not only Int, since calldata and outputs are typed as Vec<RegVal> on the Rust side; the xquad CLI’s --calldata flag only accepts a comma-separated integer list, so injecting a Model or Sample through calldata is something host code building on the xqvm/xqasm crates can do, not something the CLI exposes directly. PUSH 0; INPUT r0; PUSH 0; OUTPUT r0, given --calldata 77, round-trips a value through a register with no arithmetic in between: output slot 0 ends up holding Int(77). Both INPUT and OUTPUT fault with CallDataIndex/OutputIndex respectively when the popped slot index is out of range, and OUTPUT faults UnsetRegister if reg was never written, the same as LOAD.

Memory Management

DROP reg is the only instruction that explicitly frees a register’s allocation, resetting it to Unset and releasing whatever Model, Sample, VecInt or VecXqmx the slot held. It does not leave an integer zero behind: the register is unreadable until the next STOW, INPUT or allocator call.