Skip to content

lince_app

The standalone CLI executable: lince-emu. A thin shell over lince::runtime::Emulator that parses arguments, builds an EmulatorConfig, wires the default services, loads an ELF, and runs.

Depends on lince_runtime (transitively, on everything).

Source layout

src/app/
├── CMakeLists.txt
└── src/
    └── main.cpp

A single translation unit. Around 200 LOC including CLI parsing, error formatting, and the post-mortem dump.

What main.cpp does

  1. Parse arguments with getopt_long. Each flag is documented in the CLI reference.
  2. Build an EmulatorConfig. Map each flag to the matching field; leave the rest at their defaults.
  3. Construct the Emulator:
    auto emu_res = lince::runtime::Emulator::create(cfg);
    if (!emu_res) { /* exit 3 */ }
    
  4. Inject defaults:
    emu->set_logger(std::make_unique<lince::defaults::StdoutLogger>(level));
    emu->set_character_device(
        std::make_unique<lince::defaults::StdoutCharDevice>());
    
  5. Initialise (emu->initialize()), wiring the four built-in peripherals into the bus.
  6. Load the ELF (emu->load_elf(path)). On failure, exit 4.
  7. Run (emu->run_for(SimTimeNs{cfg_budget})).
  8. Dispatch on RunResult::reason:
    • DurationExpired / DeadlineReached → exit 0.
    • ErrorMode → dump the post-mortem of core 0, exit 5.
    • Breakpoint → if a GDB stub is bound, hand control to the stub's stop loop; otherwise exit 5.

The post-mortem

When ErrorMode fires, the CLI dumps:

  • pc, npc, tbr, tt, psr, wim.
  • All eight global registers.
  • The active window's input, local, and output registers.

The format is one line per register class, 0x%08x per word, mirroring what a SPARC debugger would show. Future versions will add the GR712RC-specific FT registers.

Why such a minimal CLI?

Three reasons:

  1. The library is the product. The CLI exists so newcomers can run lince-emu --image hello.elf in one shot, but the canonical integration is via lince_runtime.
  2. The CLI is the canonical example of injecting defaults. New embedders learn by reading main.cpp.
  3. An SMP2 wrapper has no use for it. Anything CLI-flag-shaped becomes an SMP2 model property.

Extending the CLI

If you need a flag that is not in EmulatorConfig today (e.g. a fault-injection schedule, a richer logger, an alternative ICharacterDevice), you have two reasonable paths:

  • Fork main.cpp. It is intentionally short; copy it, add your flags, link against lince_runtime, ship a different binary.
  • Add the field to EmulatorConfig. If the new knob is general enough to belong on the public API, file a PR. The CLI then exposes it as a flag.

The CLI will not grow into a configuration framework. Anything sufficiently complex is a library consumer's job.