- name
- annotate
- description
- Enrich a GSL decompilation (a .gsl produced by libre99gsl decompile) with runtime evidence, interactively: compile the decompilation itself, play it headlessly with libre99probe, improve symbol names and comments, and prove after every edit that the file still compiles byte-identically (libre99gsl verify). Use when asked to annotate, enrich, or explore a decompiled TI-99/4A cartridge.
# Annotate a GSL decompilation with runtime evidence
You will enrich a decompiled `.gsl` file with meaningful names and comments
**earned by actually running the program** — without changing a single byte
of what the file compiles to.
The `.gsl` is the single source of truth: you compile *it* and run the
result. The original cartridge image is never needed — the decompiler proved
byte-identity when it produced the file, and you preserve that identity
edit by edit, so the compiled decompilation *is* the program.
## The interactive loop
This is a conversation, not a batch job:
1. The user starts the flow with the `.gsl` path and whatever they already
know — what the program is, where companion media lives (data disks),
what to focus on, standing orders ("don't touch the sound driver").
Those in-prompt hints are the steering channel; there is no hints file.
2. You run a **pass**: as much annotation as the evidence supports. Don't
interrupt the pass with questions unless you are truly blocked (a
missing file, an ambiguous instruction) — prefer finishing and asking
in the report.
3. You end the pass with the **report** (Step 5): coverage and its delta,
renames with evidence, discoveries, what stayed unreached, and exactly
what hint or media would unlock more.
4. The user replies with new hints or directions — run another pass, same
conversation. They stop when returns diminish.
Conversations are ephemeral; the file is not. Record every durable
user-supplied fact (media paths, what the game is, standing orders) in the
notes block's CONTEXT section (Step 4) so later passes — in this
conversation or a fresh one — inherit them.
## The four iron rules
1. **The file must stay byte-identical.** Before the first edit, compile
the pristine file into the pass baseline; after every batch of edits,
verify against it:
```
./target/release/libre99gsl compile <file.gsl> -o <scratch>/baseline.ctg --format ctg
./target/release/libre99gsl verify <file.gsl> <scratch>/baseline.ctg --format ctg
```
If verify fails, fix or revert the batch before doing anything else;
never finish with a failing verify. (The check is name-blind — renames
and comments cannot break it; touching real statements or data can.
Byte-identity is transitive: the decompiler proved the file against the
original cartridge, each pass proves itself against the previous state,
so the chain back to the original never breaks.)
2. **Addresses are ground truth — never remove them.** Every declaration
keeps its `@ 0xNNNN`, every function header keeps its `// >NNNN:` line.
Renamed **functions keep the address suffix** (`sub_C41E` →
`store_menu_C41E`); **variables may take plain names** (`b_8340` →
`party_gold`) because their declaration pins the address — the same
convention the decompiler's own static renames use. Never edit the
`format`/`title` declarations.
3. **Evidence discipline.** Rename only what you *observed*: the trace or
coverage placed execution there, the screen showed it, memory changed in
step with it. Prefix comments `observed:` for session facts and
`likely:` for inference. No evidence → no rename; a plausible guess is
worse than an honest `sub_XXXX`.
4. **Copyright.** Decompilations of commercial cartridges — and everything
derived from them (the enriched `.gsl`, the compiled baseline, evidence
files, session scripts) — stay **outside this repository**. Never commit
them; keep them next to the `.gsl` or in scratch space.
## Step 0 — set up
- `cargo build --release -p libre99-probe -p libre99-gsl`
- Read the `.gsl` header comment (the first ~60 lines) and the **tail** of
the file: an existing `EXPLORATION NOTES` block means this is a later
pass — read it fully (especially CONTEXT) and build on it. Passes are
cumulative; never discard earlier sessions, renames, or notes.
- Pick a scratch directory for the baseline image, session scripts,
evidence dumps, and screenshots.
- Compile the baseline (iron rule 1) **before any edit**, and run a
baseline `verify` — it must pass (if it doesn't, stop and tell the
user). The baseline `.ctg` is also the image every probe session runs.
## Step 1 — index the file (don't read all of it)
The file may be hundreds of KB. Grep, don't read end to end:
- `grep -n '^fn ' file.gsl` → the function index: every name and `@ 0xNNNN`
address. A function's byte span runs from its address to the next one's —
this index is what turns trace/coverage addresses into function names.
- The `// >NNNN:` headers already carry the static analysis: effects
(`formats screen text`, `reads keyboard`, …), `// prints:`, `// calls:` /
`// called from:`. Your job is the layer static analysis cannot reach:
what the code *means* in the running program.
## Step 2 — play (the evidence loop)
`libre99probe` is the control surface — `docs/PROBE.md` is the manual — and
`<scratch>/baseline.ctg` is the image it runs. Two driving modes, both good:
- **Script replay**: keep `session-N.txt`, append commands, rerun
`./target/release/libre99probe <scratch>/baseline.ctg --script
session-N.txt`. The emulator is deterministic, so the script *is* the
session — and the replayable proof of every claim you make from it.
- **Checkpoints**: `save`/`load` state files to branch-explore (checkpoint
before a menu, try option 1, `load`, try option 2) without replaying.
The loop is: `screen` → decide → `press`/`type` → `settle` → repeat. For
custom-font or sprite-heavy screens where the ASCII decode is unreadable,
`shot file.png` and view the image. A standard opening:
```
frames 180 # power-up to the master title
press space # any key → selection menu
settle
press 2 # the cartridge's first program
settle
```
If the user named companion media, mount it (`disk 1 <path.dsk>` — live, no
reset needed) and take the program's load path.
Evidence channels, and when to use each:
- `cover on` at session start and leave it on (it's cheap). At session end,
`cover` for the summary and `cover save <file>` for the ranges. Bucketed
against the Step-1 function index this yields the coverage measure:
*N of M functions observed executing*.
- `trace on` only around a moment — "what runs when I press B here?" — then
`trace summary` / `trace save`, since the log grows fast; `trace on`
restarts it. Fetches at `>6000`+ are the program's own code executing.
- `peek` scratchpad ranges before/after an action to see which cells changed
— the raw material for variable names (`observed: decremented each combat
round`).
- `audio 30` to confirm an action beeped (sound-driver attribution).
- The strongest naming evidence combines channels: *function X executed
while the screen said Y* (trace window + `screen` in the same moment).
Plan sessions around the user's hints. Probe menus and verbs systematically
— save states make it cheap to try every option of every menu. Reaching all
code is intractable; make reasonable, hint-guided efforts, then *document*
what was not reached and why rather than guessing.
## Step 3 — edit
- **Renames**: whole-word search-and-replace across the file — declaration,
call sites, and `calls:`/`called from:` cross-references all use the same
spelling, and a missed site is a compile error that `verify` will catch.
- **Comments**: terse and in place. Explain what the code *means* in the
program ("`observed:` prints the store menu"), not what the instructions
do. Use `likely:` sparingly and honestly.
- Batch small (one subsystem at a time) and run `verify` after each batch,
so a mistake is easy to bisect.
## Step 4 — the EXPLORATION NOTES block
Maintain exactly one block comment at the very end of the file — append it
on the first pass, update it (bump the pass number, merge sessions, renames,
and context) on later passes:
```
// =====================================================================
// EXPLORATION NOTES — pass 2 (3 sessions) — by the /annotate skill
// =====================================================================
// CONTEXT (durable facts from the user — future passes rely on these)
// This is Tunnels of Doom, TI's 1982 dungeon-crawl RPG.
// Data disk: /path/to/tod-data.dsk (mount DSK1; answer 2 at the
// "LOAD DATA FROM" prompt). Don't rename the sound driver.
// COVERAGE
// functions observed executing: 212/344 (62%)
// cart GROM addresses read: 18412/40960
// SESSIONS (deterministic keystroke scripts — replay to reproduce)
// 1 "boot to party creation" (session-1.txt):
// frames 180 / press space / settle / press 2 / settle / press 1 / ...
// 2 "store + combat" (session-2.txt): ...
// RENAMES (one per line, with evidence)
// sub_C41E -> store_menu_C41E observed: executed while screen read
// "GENERAL STORE"; draws its menu
// b_8340 -> party_gold observed: fell 250->175 on a purchase
// UNREACHED (and why)
// >D9A2->DB00 cassette error path — needs a mid-load cassette fault
// NEXT HINTS (what would unlock more)
// - a data disk in DSK1 would open everything past "LOAD DATA FROM"
```
The block doubles as the machine-readable record: scripts verbatim, one
rename per line, the user's durable context. It is how the next pass — and
the user — knows where things stand.
## Step 5 — report, then iterate
- Final `verify` must print `verify OK` — quote that line in your report.
- Report to the user: coverage numbers (and their delta from the previous
pass), the rename count with the two or three best examples, notable
discoveries about how the program works, what stayed unreached, and —
phrased as questions they can answer in their next message — exactly
what hints, media, or focus areas would make another pass worthwhile.
- If the user replies with more, fold their answers into CONTEXT and run
the next pass.
Ver en GitHub