wire — dividing one word into fields
When several values sit side by side inside one integer, this handles each field with a single mask. The mask says both where the field starts (position) and how many bits it has (width), so those two numbers, which always drift apart when written by hand, cannot drift. Carrying a date-time as one word (clock.local_packed), putting shard, slot and generation in a handle (budget), reading and writing device register fields — every place one word is used as several fields.
mask = 0x0f00 → position 8 · width 4
wire.pick mask w → (w & mask) >> 8 extract the field's value
wire.put mask v → (v << 8) & mask move a value into the field's position
wire.merge mask w v → (w & ~mask) | put replace only that field of the wordfn m_month output u64 . do return 64424509440 . end
let mo u64 be wire.pick (m_month) w .
guard wire.fits (m_month) 9 . else return 1 .
let w2 u64 be wire.merge (m_month) w 9 .What this module guards
requires ne mask 0. The cost is zero — trailing_zeros of a constant mask folds at compile time, and the machine instruction count equals hand-written code (x86-64 gcc -O2: extract 18 = 18, replace 27 = 27, measured by tests every run).| op | What it does |
|---|---|
shift_of · max_of · solid | Mask position · largest value fitting the field · is the mask contiguous |
pick · put · merge | Extract · move into position · replace only that field |
fits | Does the value fit the field |
polarity v m | Invert only the mask’s bits (active-low) |
reverse_bits v w | Reverse bit order within width w |
onwire order inv v w | Apply a wire descriptor at once (compile-time values) |
permute v tab · is_identity tab | Bit permutation · is it the identity |
to_set now want · to_clear now want | Set mask · clear mask |
Table 50.1 — Ops of wire
Byte order is the builtin byte_swap. The axes for going onto a wire (polarity, bit order, byte order, permutation) are independent of each other.
Why set/clear pairs, not “a value”. Writing a whole value to a port means read → modify → write. If an interrupt turns on another bit in between, my write erases it. So hardware provides set-mask and clear-mask registers (STM32′s BSRR) — nothing is read, so nothing is lost. This is correctness, not convenience (chapter 30). Identity is free — onwire 0 0 v w has the same instruction count as code returning the value unchanged. A permutation table is a runtime value and does not fold, so is_identity lets you ask before calling.
Counter-example. Writing the position again separately from the mask
bit_and (shr w 32) 15 has position (32) and width (15) as two numbers, so fixing one silently breaks. wire.pick takes one number.Counter-example. Inserting without asking whether it fits
wire.merge (m_month) w 20 silently inserts 4 (= 20 & 15). wire.fits answers that question — the two are not one op because answering failure as a value would force unwrapping an option everywhere.Cautions. Write each mask in one place (one name, like fn m_month output u64 .). Masks with holes (non-contiguous bits) are not handled — solid answers that. There is no width/frame axis (an 8-bit value in a 12-bit frame). Why the ops are not get or read — get is a word the processor already uses, so it would never be called, and read collided with C’s read and broke native builds. Library op names must avoid the processor’s words, the specification’s tables and libc alike.