Analytical Combinator

by bruno77

A programmable circuit combinator controlled via a RISC-V inspired assembly language. Supports reading red and green input signals and writing up to four output signals per tick.

Content
11 hours ago
2.0
8
Circuit network

Changelog

Version: 0.8.11
Date: 01. 04. 2026
  Features:
    - Added LI rd, imm — load immediate. Sets rd = imm directly. Syntactic sugar for
      ADDI rd, x0, imm; cleaner when the intent is loading a constant rather than adding.
      Accepts decimal, hex (0x prefix), and negative values. All standard error checks apply.
    - Instructions are now case-insensitive. Mnemonics may be written in upper, lower, or
      mixed case (ADDI, addi, Addi). The mnemonic is uppercased once during parsing via
      instruction:upper() before any comparison. Register names (x0-x31, o0-o3) and signal
      names remain case-sensitive as Factorio requires.
  Documentation:
    - Updated README.md and LICENSE.md to user-supplied versions. LICENSE now retains
      Joakim Lönnegren's original MIT copyright alongside the Analytical Combinator
      contributors' copyright. README includes Credits section acknowledging the original
      mod and Claude's role in development.
    - README: LI added to arithmetic table; new "Instruction case" section documents
      case-insensitive parsing.
    - 12 new tests (132 total): LI basic use, hex/negative immediates, x0 suppression,
      overwrite, error cases; case-insensitive mnemonic tests including a lower-case
      loop with BNEI; signal/register case-sensitivity confirmation.
Version: 0.8.10
Date: 29. 03. 2026
  Bugfixes:
    - Fixed BEQ and BNE: missing register nil-checks meant that a literal (e.g. BEQ x3, 0,
      label) or invalid register (e.g. BEQ x3, x99, label) was silently miscompared rather
      than raising an error. nil==nil is true in Lua, so BEQ x3, x99 would always branch
      regardless of x3; BEQ x3, 0 would never branch. Both now validate both operands first.
    - Fixed WSIG: the count source register (third argument) had no nil check. An invalid
      register name would silently emit a signal with count nil (treated as 0).
    - Fixed JAL: the destination register for the saved return address had no nil check.
      Writing to an invalid key (e.g. x99) would silently create a junk table entry.
    - Fixed SLT: added nil check on the destination register (first argument).
  Features:
    - Added BEQI rs, imm, label — branch if rs == immediate value
    - Added BNEI rs, imm, label — branch if rs != immediate value
    - Added BLTI rs, imm, label — branch if rs < immediate value
    - Added BLEI rs, imm, label — branch if rs <= immediate value
    - Added BGTI rs, imm, label — branch if rs > immediate value
    - Added BGEI rs, imm, label — branch if rs >= immediate value
    - All immediate branches accept hex literals (0x prefix) and validate both the
      register and immediate operands, erroring cleanly on invalid input.
    - 20 new tests including regression tests for the nil-check bugs. 120 tests total.
Version: 0.8.9
Date: 29. 03. 2026
  Features:
    - Added MULI rd, rs, imm — multiply register by immediate (rd = rs * imm)
    - Added DIV  rd, rs, rt  — integer division, floor toward -inf (rd = floor(rs/rt))
    - Added DIVI rd, rs, imm — divide by immediate
    - Added REM  rd, rs, rt  — remainder, sign follows dividend C-style (math.fmod)
    - Added REMI rd, rs, imm — remainder by immediate
    - DIV, DIVI, REM, REMI all produce a descriptive runtime error on divide-by-zero
      rather than crashing. Lua's math.floor and math.fmod are used to give consistent
      signed behaviour across all values.
    - Note: MUL was already present since 0.8.4; MULI is the new immediate variant.
    - 18 new tests covering basic operation, negative values, divide-by-zero errors,
      hex immediates, and a practical modulo wrap-around example. 100 tests total.
Version: 0.8.8
Date: 29. 03. 2026
  Changes:
    - Fixed JAL to save IP+1 (address of the next instruction) rather than the line number
      of the JAL itself. This matches standard RISC-V convention and makes JR work cleanly
      without a compensating +1. Existing programs using JAL x0 (discard return address)
      are unaffected. Programs relying on the specific numeric value saved by JAL will need
      to add 1 to their expected values.
    - Fixed JR to jump directly to the value in rs (no +1 adjustment needed now that JAL
      saves the correct return address).
    - JR x0 is now a defined special case: jumps to line 1 (program restart). Since x0 is
      always 0 and 0 is not a valid line number, this provides a convenient restart without
      requiring a label on line 1.
    - Updated all tests: JAL return-address assertions updated (x1=3→4 etc.),
      JR test comments corrected, new JR x0 restart test added. 82 tests total.
    - README: JAL/JR table entries updated; subroutine example rewritten — the old version
      needed an awkward BGT workaround because JAL saved the wrong value; the new example
      is clean and straightforward.
Version: 0.8.7
Date: 29. 03. 2026
  Features:
    - Added BLT rd, rt, label — branch if rs < rt
    - Added BLE rd, rt, label — branch if rs <= rt
    - Added BGT rd, rt, label — branch if rs > rt
    - Added BGE rd, rt, label — branch if rs >= rt
    - Added JR rs — jump to instruction at rs+1, enabling subroutine returns.
      JAL saves the line number of the JAL instruction itself, so JR adds 1 to
      land on the instruction after the call site.
    - 16 new tests (81 total): branch taken/not-taken cases for all four new
      instructions, JR call/return, nested calls with distinct return registers,
      and error cases.
    - README: control flow table updated; new subroutine call/return example.
Version: 0.8.6
Date: 29. 03. 2026
  Features:
    - Added SRL rd, rs, rt   — shift right logical by register (zero-fill)
    - Added SRLI rd, rs, imm — shift right logical by immediate (zero-fill)
    - Hex immediates: all immediate arguments now accept 0x prefix (e.g. ADDI x10, x0, 0xFF).
      tonumber() auto-detects base, so decimal and hex both work everywhere.
  Changes:
    - Renamed shift instructions to match RISC-V convention:
        SHL  → SLL  (shift left logical, register)
        SHLI → SLLI (shift left logical, immediate)
        SHR  → SRA  (shift right arithmetic, register)
        SHRI → SRAI (shift right arithmetic, immediate)
    - Moved bitlib setup (bit32/bit) to module level rather than inside step() to
      avoid re-evaluating it on every tick.
    - Fixed indentation bug: the else clause of the instruction dispatcher was
      accidentally indented inside the CNTSG block.
  Documentation:
    - README Shifts table expanded to all six variants with logical/arithmetic distinction noted.
    - README new section "Immediate value formats" documents decimal, hex, and negative literals.
    - Test suite: shift tests renamed; 4 new tests contrasting SRL/SRLI (zero-fill) against
      SRA/SRAI (sign-extend) on negative values. 65 tests total.
Version: 0.8.5
Date: 29. 03. 2026
  Bugfixes:
    - Fixed bitwise instruction implementation to use bit32 or bit library functions rather
      than typical operators.
Version: 0.8.4
Date: 29. 03. 2026
  Features:
    - Added MUL rd, rs, rt  — multiply two registers (rd = rs * rt)
    - Added AND rd, rs, rt  — bitwise AND
    - Added OR  rd, rs, rt  — bitwise OR
    - Added XOR rd, rs, rt  — bitwise XOR
    - Added NOT rd, rs      — bitwise NOT, unary (rd = ~rs)
    - Added SHL  rd, rs, rt  — shift left by register
    - Added SHLI rd, rs, imm — shift left by immediate
    - Added SHR  rd, rs, rt  — logical shift right by register
    - Added SHRI rd, rs, imm — logical shift right by immediate
    - Added CNTSR rd — count of distinct signals on the red input wire
    - Added CNTSG rd — count of distinct signals on the green input wire
    - All new instructions respect the x0 write-suppression rule and produce
      descriptive errors for wrong argument counts or invalid register names
    - 25 new tests (61 total)
    - README: Bitwise and Shifts instruction tables added; CNTSR/CNTSG added to
      circuit input table; two new examples (bit masking, signal presence detection)
Version: 0.8.3
Date: 29. 03. 2026
  Bugfixes:
    - Fixed README: the "Red/green mixer" example was using SUB (red minus green) despite
      claiming to output a sum. Replaced with two correct ADD-based examples:
      "Red/green signal sum" (continuous summing loop) and "Red/green sum with threshold gate"
      (fires signal-A once combined total exceeds 500).
Version: 0.8.2
Date: 29. 03. 2026
  Features:
    - Added ADD instruction: ADD rd, rs, rt sets rd = rs + rt (register + register addition).
      This is the missing complement to SUB and ADDI. Useful for combining values from two
      sources, e.g. summing red and green wire readings of the same signal.
      Example: RSIGR x10, iron-plate / RSIGG x11, iron-plate / ADD x12, x10, x11
    - Added 6 tests for ADD covering basic addition, accumulation in place, x0 write
      suppression, negative values, invalid register, and wrong argument count.
    - README: ADD added to instruction table; new example showing red+green signal sum
      with threshold gate.
Version: 0.8.1
Date: 29. 03. 2026
  Features:
    - Assembly code editor now uses a monospace font (NotoMono via the built-in "default-mono"
      descriptor). A FontPrototype "ac-mono-14" is declared in prototypes/fonts.lua and applied
      to the text-box at GUI creation time via textbox.style.font. No external TTF file is
      required as NotoMono-Regular.ttf ships with Factorio core.
Version: 0.8.0
Date: 29. 03. 2026
  Changes:
    - New technology tree icon (256x256): dark PCB circle with circuit traces, central IC chip,
      amber LED, and assembly code line art — no longer uses the vanilla circuit-network graphic
    - New mod portal thumbnail (144x144): shows the combinator body with screen chip, green
      input wire, red output wire, and a floating assembly code snippet
    - Rewrote all locale descriptions in locale/en/base.cfg to specifically describe the
      assembly-language programming model rather than generic combinator marketing copy
Version: 0.7.9
Date: 29. 03. 2026
  Changes:
    - Mod renamed from "Assembly Combinator" to "Analytical Combinator" to distinguish it
      from the original constant-combinator-based mod by joalon. All internal entity names,
      storage keys, GUI names, prototype IDs, and locale strings updated accordingly.
    - License changed to Zero-Clause BSD (0BSD) — no attribution required.
    - Updated mod description to better reflect RISC-V inspired assembly language and
      dual-wire input capability.
Version: 0.7.8
Date: 28. 03. 2026
  Bugfixes:
    - Fixed crash when WSIG is used with virtual or fluid signals (e.g. signal-red, crude-oil).
      Output signals were always emitted with type="item", causing Factorio to reject any name
      that isn't a valid item. write_outputs now resolves each signal name against
      prototypes.virtual_signal, prototypes.item, and prototypes.fluid in that order to
      determine the correct type. Invalid signal names are silently skipped rather than crashing.
Version: 0.7.7
Date: 28. 03. 2026
  Documentation:
    - Fixed incorrect README description for the Threshold Gate example which claimed the
      output signal appeared "on the red wire". WSIG writes to an output register with no
      wire-colour specificity; the signal appears on whichever wire(s) are physically connected
      to the output side of the combinator.
Version: 0.7.6
Date: 28. 03. 2026
  Bugfixes:
    - Fixed "everything" signal appearing on output wire in alt-mode and leaking onto the circuit
      network. The always-true condition used signal-everything as its first_signal, which is a
      special aggregate virtual signal that Factorio propagates to the output. Replaced with a
      pure constant condition (first_constant=0, comparator="=", second_constant=0) which
      references no signals at all and has no side effects on the network.
Version: 0.7.5
Date: 28. 03. 2026
  Bugfixes:
    - Fixed crash on load: defines.circuit_connector_id was removed in Factorio 2.0 and replaced
      by defines.wire_connector_id with per-colour IDs. get_circuit_network() now takes a single
      wire_connector_id (e.g. combinator_input_red / combinator_input_green) rather than a
      wire_type + circuit_connector_id pair. Fixed in both events.lua and gui.lua.
Version: 0.7.4
Date: 28. 03. 2026
  Bugfixes:
    - Fixed RSIGR and RSIGG always reading zero: get_circuit_network() requires an explicit
      circuit_connector_id for entities with separate input and output connectors. Without it
      the call returns nil on a decider combinator. Both events.lua and gui.lua now pass
      defines.circuit_connector_id.combinator_input when reading the input-side network.
Version: 0.7.3
Date: 28. 03. 2026
  Bugfixes:
    - Fixed crash when loading a saved game containing analytical combinators: Factorio does not
      persist Lua metatables across saves, so stored cpu objects lost all their methods on load.
      control.lua now re-attaches the cpu module metatable in both on_load (normal resume) and
      on_configuration_changed (mod update). Also backfills the input_signals field for saves
      predating 0.7.0 that were created without it.
Date: 28. 03. 2026
  Bugfixes:
    - Fixed crash on placement: LuaDeciderCombinatorControlBehavior does not expose
      sections_count / add_section / get_section (those belong to the constant combinator).
      Output writing now uses behavior.parameters with a conditions+outputs table, which is
      the correct 2.0 API for decider combinators. Clearing outputs uses behavior.parameters=nil.
Date: 28. 03. 2026
  Graphics:
    - Added custom microchip display sprite (30x22) replacing the decider combinator's
      comparison operator symbols (=, >, <, ≠, ≥, ≤) so the entity is visually distinct
      in-world regardless of which operator mode Factorio internally selects
    - Added custom 64x64 inventory/crafting icon showing the combinator body with the chip
      symbol on screen, a green status LED, and an 'AC' label
Version: 0.7.0
Date: 28. 03. 2026
  Major Features:
    - Fork of Assembly combinator version 0.6.2 from joalon
    - Analytical combinator is now based on a Decider Combinator, providing both input and output
      circuit network connections (red and green wires)
    - Added RSIGR instruction: read a named signal from the red input wire into a register
    - Added RSIGG instruction: read a named signal from the green input wire into a register
    - Red and green channels are read independently each tick, enabling programs to respond
      to live circuit network state
  Changes:
    - Recipe now requires a Decider Combinator instead of a Constant Combinator