Skip to content

v0.6.0

Choose a tag to compare

@github-actions github-actions released this 18 Oct 11:41
· 9 commits to main since this release

Breaking

  • The board format names its parameters and drops its link count:
    • A board is { components }; links is gone, and the links are numbered from 0 to the highest
      id a pin names. A component may leave out an empty inputs or outputs.
    • ops is replaced by named parameters: a CLK's period and mode, a RAM's read, a ROM's
      data, a recorder's capacity and recordTicks, and an addressable register's dataBus. A
      parameter on any other type, or a missing period, capacity or dataBus, is refused as
      BadParam { idx, ty, param, reason }.
    • A ROM's data is bytes (a Uint8Array or 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 links or ops. The JS
      bindings ignore them, so a board that still passes ops is 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); LinkOutOfRange and LinkCountOverflow are
      gone, and BadArity no longer carries an ops count.
    • .lgb is version 3: no link count in the header, and the parameters follow each component's
      pins, flagged in the reserved u16 after its type id.
    • In Rust, the parameters are optional fields of ComponentDescriptor (with the ClkMode and
      RamRead enums; record_ticks replaces RECORD_TICKS), set with struct-update syntax over
      ComponentDescriptor::new(ty, inputs, outputs). BoardBuilder::new() takes no link count,
      component() no ops, and add() appends a ComponentDescriptor.
  • A CLK is a square wave by default, period ticks low then period high; the old one-tick pulse
    after period ticks low is mode: "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 through writeState) changes, with no clock. Writes still happen on a
    rising clock. The old outputs, which change only on a rising clock, are read: "clocked".
  • A MUX takes no parameter: its select width is the one s with s + 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::AddressableRegister in Rust, formerly LedMatrix). Its dataBus is 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
    dataBus 4, 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.invalid on
    Node, invalidPtr / invalidLen (bytes of u32 ids) on WASM, and snapshot_invalid(),
    link_invalid(id) and invalid_links() in Rust; sim trace frames carry a non-empty invalid
    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 take n outputs, or n + 1 with 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, WASM link(id) now reads false instead 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 after runAsync(), 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