Repository navigation
v0.6.0
Breaking
- The board format names its parameters and drops its link count:
- A board is
{ components };linksis gone, and the links are numbered from 0 to the highest
id a pin names. A component may leave out an emptyinputsoroutputs. opsis replaced by named parameters: a CLK'speriodandmode, a RAM'sread, a ROM's
data, a recorder'scapacityandrecordTicks, and an addressable register'sdataBus. A
parameter on any other type, or a missingperiod,capacityordataBus, is refused as
BadParam { idx, ty, param, reason }.- A ROM's
datais bytes (aUint8Arrayor an array of 0–255 in JS, an array in JSON). Short
data is still zero-filled; data longer than the ROM's table is refused instead of cut off. - The CLI's JSON boards refuse unknown fields, such as a leftover
linksorops. The JS
bindings ignore them, so a board that still passesopsis refused for a CLK, recorder or
addressable register but silently gets an all-zero ROM and a RAM in its default mode. - A link id must be below 2³⁰ (
LinkIdOverflow);LinkOutOfRangeandLinkCountOverfloware
gone, andBadArityno longer carries anopscount. .lgbis version 3: no link count in the header, and the parameters follow each component's
pins, flagged in the reservedu16after its type id.- In Rust, the parameters are optional fields of
ComponentDescriptor(with theClkModeand
RamReadenums;record_ticksreplacesRECORD_TICKS), set with struct-update syntax over
ComponentDescriptor::new(ty, inputs, outputs).BoardBuilder::new()takes no link count,
component()noops, andadd()appends aComponentDescriptor.
- A board is
- A CLK is a square wave by default,
periodticks low thenperiodhigh; the old one-tick pulse
afterperiodticks low ismode: "pulse". A period of 1 is unchanged. A halted CLK restarts
its cycle once its halt input goes low, instead of resuming its count where the halt caught it. - A RAM's outputs follow its address: they show the addressed word one tick after the address or
the memory (including throughwriteState) changes, with no clock. Writes still happen on a
rising clock. The old outputs, which change only on a rising clock, areread: "clocked". - A MUX takes no parameter: its select width is the one
swiths + 2ˢinputs. A DEMUX takes
its select bits first, as a MUX does:[s0, …, s(n-1), data]instead of[data, s0, …]. - The LED matrix (type 204) is a generic addressable register whose outputs are its stored bits
(CompType::AddressableRegisterin Rust, formerlyLedMatrix). ItsdataBusis its data bus
width, from 1 to its output count, instead of a size where anything above 4 meant 8. Its outputs
form words of that width, the last possibly partial; its address bus is
⌈log₂(⌈outputs / dataBus⌉)⌉ bits, so every output can be written, and its input count must be
exactly address + data + clock. The editor's 4×4, 8×8 and 16×16 matrices keep their pins with
dataBus4, 8 and 8. - Node.js 22 or newer is required; Node.js 20 is end-of-life.
- Building from source needs Rust 1.88 or newer.
Added
- Invalid links: a link is invalid while its drivers disagree, or while an SR flip-flop clocked
with S and R both high drives it (Q set, Q̄ low, until a clock with only S or only R). It keeps
its wired-OR value and nothing downstream is flagged. Snapshots list them:snap.invalidon
Node,invalidPtr/invalidLen(bytes ofu32ids) on WASM, andsnapshot_invalid(),
link_invalid(id)andinvalid_links()in Rust;sim traceframes carry a non-emptyinvalid
list. - An encoder can have a valid output after its number bits, high whenever any input is, so input 0
alone differs from no input:2ⁿinputs takenoutputs, orn + 1with the valid pin.
Performance
- Ticks are 46–66% faster on every benchmark board (57% on the real-world CPU board), and a board
with only clocks running ticks more than twice as fast. A tick visits only the component types
it has work for, finds a link's consumers with one lookup, and skips bounds checks on link ids,
which board compile already keeps in range. NOT, DELAY, two-input AND/OR/XOR, the adders and the
D flip-flop read their wiring from one record each, and a link with a single driver flips without
counting its drivers. Out of range, WASMlink(id)now readsfalseinstead of trapping.
Fixed
- An addressable register whose word count is not a power of two no longer writes past its own
outputs when addressed beyond its last word, which drove the next component's output pins or
crashed the simulation (a trap in WASM, a thrown error in Node). Such a write now lands only on
the outputs that exist. - A CLK whose period is above 2³¹ no longer fires every tick.
- A link driven by more than 65 535 outputs is refused as
TooManyDrivers { link, count, max }.
Its count of high drivers used to wrap, so with every driver high it could read low. - On Node, a
stop()made right afterrunAsync(), before the worker picked the run up, is no
longer lost. The run used to go on forever, and its promise kept Node from exiting.
Full Changelog: v0.5.0...v0.6.0