The src/core directory contains the main LCC.js toolchain modules:
assembler.jsinterpreter.jslinker.jslcc.js
These modules are the primary implementation of the standard LCC.js workflow.
Responsible for translating source input into machine-code output.
Current responsibilities:
- assemble
.asource files - parse
.binand.hexinputs - generate
.eor.ooutput - support in-memory assembly through
assembleSource(...)
Per-run configuration must be passed as assembleSource(source, options) options, not
pre-set on the instance. The relevant fields are:
inputFileName,outputFileNamelistingLoadPoint— the-l<hex>display offset (#1238)verboseModeOn,explainModeOn,userName(#1277)onProgress— the pass-banner sink (#1397)
Why setting them on the instance first is silently wiped: assembleSource() calls
resetAssemblyState() as its first step, which clears every per-run field. The seam then
re-applies the caller-provided values from options after the reset. So a field assigned
to the instance before the call is cleared before the passes run; an omitted option means
"this run has none" (default off / 0 / null), never a value inherited from a prior
assembly on the same instance. The CLI path (main()) threads these through the options for
exactly this reason.
The instance fields still exist and are read during/after assembly — e.g.
formatAssemblerError reads this.explainModeOn, and main()'s object-module report
consumes this.userName after assembleSource() returns — but it is the option, applied
after the reset, that puts the right value there. Single point of truth for the field list:
resetAssemblyState() (#1423).
Responsible for executing .e executables on the simulated LCC machine.
Current responsibilities:
- load and validate executable images
- execute instructions and traps
- enforce runtime protections such as infinite-loop detection
- support in-memory execution through
executeBuffer(...)
Responsible for combining one or more .o object modules into a single .e executable.
Current responsibilities:
- parse object-module headers and code
- resolve global and external references
- adjust addresses and create a final executable
- expose pure seams:
parseObjectModuleBuffer(...)(one.obuffer → module) andlinkObjectModules(buffers, options) → { outputBytes }(link.obuffers →.eimage), both throwing typedLinkerError; the CLI wrapper (link/main) owns file I/O, progress logs, and exit codes
Responsible for orchestrating the standard end-to-end workflow.
Current responsibilities:
- choose assemble / link / execute behavior from input type
- coordinate the assembler, linker, and interpreter
- own top-level CLI option parsing
- own report-writing orchestration
The core modules are being refactored toward a clearer boundary:
- reusable in-memory APIs throw typed errors
- CLI/wrapper paths own console output, exit behavior, and file I/O
- shared concerns such as report generation and artifact naming live in
src/utils
At the moment:
assembler.jsandinterpreter.jsalready have meaningful pure seamslinker.jshas begun that transition but is still more wrapper-orientedlcc.jsremains intentionally orchestration-focused rather than becoming a pure library surface