Epic/06 final touches#6
Merged
diegoparrilla merged 21 commits intomainfrom May 4, 2026
Merged
Conversation
The bundled `httpc/` HTTPS-capable HTTP client was scaffolding
for a never-shipped firmware-download feature. Its only consumer
was `rp/src/download.c` — and download.c's public API
(`download_start`, `download_poll`, `download_finish`,
`download_confirm`, `download_getStatus`, `download_setStatus`,
`download_getFilepath`, `download_setFilepath`,
`download_getUrlComponents`) had zero callers anywhere in the
active source. Both retired together.
- rp/src/CMakeLists.txt: drop add_subdirectory(httpc), the
httpc link line, the download.c source entry, and the
obsolete -DAPP_DOWNLOAD_HTTPS=0 define (also unreferenced).
- Delete rp/src/httpc/, rp/src/download.c, rp/src/include/download.h.
Knock-on benefit: the httpc dependency pulled in mbedtls headers
and the lwip-http stack, both of which now don't compile, so the
RP firmware build is meaningfully faster.
Note: the "download" concept in http_server.c
(stream_download_drive, HC_STREAM_DOWNLOAD) is the SD-card
GET /api/v1/files/<path> response path — completely separate
from the deleted firmware-download module despite the name
collision.
Net diff: -882 lines, +1 line.
Power-on countdown auto-fire now lands the user in the same state
as pressing [U] instead of [F]. Runner is a strict superset of the
GEMDRIVE-only path the [F]/[E] keys produce — it installs GEMDRIVE
the same way and additionally exposes the Runner control surface
to the workstation (`runner status`, `runner run`, `runner adv …`,
the upcoming `runner load` / `runner exec`). For an unattended
boot, Runner mode is the more useful default.
- emul.c:1527 cmdFirmware(NULL) → cmdRunner(NULL).
- emul.c:1525-1526 comment refreshed: "[F]" → "[U]" + the why.
- emul.c:403 section header: "auto-launches GEMDRIVE" →
"auto-launches Runner mode" with the Epic 06 / S2 marker.
`showCounter`'s "Boot will continue in N seconds..." UI text is
neutral about which mode it lands in; no UI string change needed.
Verified on hardware: power on, leave alone, 20 seconds later the
device commits firmware mode AND `runner status` reports
`active: true`.
The advanced-runner hook vector setting was visually nested under
GEMDRIVE's sub-options ("F[o]lder / [D]rive / [R]eloc / Mem[t]op
/ Adv [V]ector"), which made the menu read as one wall of GEMDRIVE
config instead of three distinct config groups. Move it onto its
own top-level row, modelled on the existing "API Endpoint" block
(header line + indented `Label : value`).
Resulting layout:
GEMDRIVE
F[o]lder : ...
[D]rive : C:
[R]eloc addr: ...
Mem[t]op : ...
Adv [V]ector
Hook : etv_timer ($400)
API Endpoint
URL : http://sidecart.local/
IP address : 192.168.1.42
The [V] keypress and ACONFIG_PARAM_ADV_HOOK_VECTOR setting are
untouched — pure render change. The two display strings ("vbl
($70)" / "etv_timer ($400)") are now factored into a
const char *advHookDisplay so the body is three linear
term_printString calls instead of the old snprintf dance.
Adds a dedicated "USB CDC (Debug serial)" block on the setup
menu, modelled on the same header / indented-value layout as the
"API Endpoint" and "Adv [V]ector" blocks. The status field
("connected" / "disconnected") is live-refreshed: a small helper
polled from the main loop watches `usbcdc_getStats(..., &attached)`
and on a transition overdraws just the status line at fixed
row MENU_USBCDC_ROW + 1, leaving the rest of the menu untouched.
Implementation notes:
- Both display strings ("connected " / "disconnected") are
12 chars, so the overdraw fully covers the previous value
with no stale tail bytes — no clear-to-eol needed.
- Live-refresh runs every main-loop iteration (~100 Hz) but
early-exits on no state change, so the steady-state cost is
one bool comparison.
- After the overdraw the helper restores the visible cursor
to "Select an option: " on the bottom row (col 18), so a
plug/unplug event doesn't strand the cursor over the USB
line and break the "type a single key here" UX.
- Gated on menuScreenActive — once the user has left the
setup menu the helper short-circuits and never touches the
framebuffer.
Layout after this:
...
Adv [V]ector
Hook : etv_timer ($400)
API Endpoint
URL : http://sidecart.local/
IP address : 192.168.1.42
USB CDC (Debug serial)
Status : connected
[E]xit (launch) r[U]nner [X] Booster
Select an option: ▌
Verified on hardware: plug a USB cable mid-menu → status flips
to "connected" within one main-loop tick; unplug → flips back to
"disconnected"; cursor stays parked after "Select an option:"
through every transition.
Splits the existing `runner run` (Pexec(0) load-and-go) into a
three-verb lifecycle so a workstation can drive ST programs the
way an interactive debugger would: load once, exec many times,
unload when done.
Wire-protocol additions (m68k ↔ RP):
RUNNER_CMD_LOAD ($0506) Pexec(3) load-only
RUNNER_CMD_EXEC ($0507) Pexec(4) just-go
RUNNER_CMD_UNLOAD ($0508) GEMDOS Mfree(basepage)
RUNNER_CMD_DONE_LOAD ($0587) i32: >0 basepage, <0 -GEMDOS errno
RUNNER_CMD_DONE_EXEC ($0588) i32: program exit code
RUNNER_CMD_DONE_UNLOAD ($0589) i32: 0 OK, <0 -GEMDOS errno
RUNNER_BASEPAGE APP_FREE + 0x1994 4 B input slot for EXEC
HTTP API additions:
POST /api/v1/runner/load sync, body {remote, cmdline}, 200/422/409
POST /api/v1/runner/exec fire-and-forget, empty body, 202/409/503
POST /api/v1/runner/unload sync, empty body, 200/422/409
GET /api/v1/runner adds loaded_basepage, last_load_errno fields
CLI:
python3 cli/sidecart.py runner load /HELLODBG.TOS [cmdline...]
python3 cli/sidecart.py runner exec
python3 cli/sidecart.py runner unload
python3 cli/sidecart.py runner status # now prints "loaded : ..."
Notable details that bit during bring-up:
- RUNNER_BASEPAGE lives in the cartridge ROM region — m68k can
only READ it. Both runner_load and runner_exec must NOT try
to write it ('move.l ..., RUNNER_BASEPAGE' would be a bus
error — 2 bombs); the RP owns every cartridge-region write
via the existing byte-pair-swapped runner_write_u32 helper.
- GEMDOS Pexec(4) takes the basepage in the CMDLINE slot
(14(a0)), NOT the fname slot (10(a0)). The latter (which I
had at first) makes TOS see a NULL basepage and bus-error.
GEMDRIVE's mode 0 → mode 6 rewriter at gemdrive.s:1023 uses
the same convention; it's the canonical evidence.
- Pexec(4) does NOT free the basepage (PE_GO semantics, vs.
Pexec(6) PE_GO_AND_FREE). Re-exec on the same loaded program
is therefore valid; emul_recordRunnerExecDone preserves
pendingBasepage. Explicit `runner unload` calls Mfree.
- runner_load / runner_exec / runner_unload all spin on a
`wait_clear_*` loop after send_sync until the RP has cleared
the cartridge sentinel; without that the m68k re-dispatches
the same command (Pexec is fast enough that the m68k laps
the chandler-callback's clear), which corrupts state.
- Cartridge code budget: 10132 / 10240 bytes. Tight — future
cartridge-side additions in this epic will need to trim the
Cconws traces.
Plus a doc note in README.md "Debug traces" flagging that
powering the Pico via USB before the ST is on causes the
20 s setup-menu countdown to elapse against an absent ST,
auto-firing Runner mode (Epic 06 / S2) before the operator can
press [U]/[E]/[F]. Workaround documented.
Verified end-to-end on hardware:
load /HELLODBG.TOS → basepage cached
status → loaded: basepage 0x...
exec → program runs, exit code reported
exec → program runs again (re-exec works)
unload → Mfree succeeds, basepage cleared
exec → 409 no_program_loaded
…ntdown bar
Layered on top of the existing term-based menu without rewriting
the rendering pipeline. All u8g2 work happens AFTER the term
prints, so the term renderer never has a chance to clobber the
graphical overlays.
Changes in rp/src/emul.c:
drawMenuDividers()
Three 1-px horizontal rules at y=60 / 84 / 116 — the gap
pixels between term rows so they don't slice any character
cell. Visually separates GEMDRIVE / Adv [V]ector / API
Endpoint / USB CDC config groups.
drawIconCell(x, y_top, glyph, visible)
8×9 erase + conditional u8g2_DrawGlyph from
u8g2_font_open_iconic_embedded_1x_t. Erase is one pixel
taller than the nominal 8×8 cell because some glyphs in
this font (notably 0x4D lightbulb) carry an ascender margin
and their topmost pixel lands at y_top - 1; an 8-tall erase
leaves that row painted on hide→show transitions.
drawMenuStatusIcons()
Right-aligned 8×8 icons at the four section headers:
0x42 cog — Adv [V]ector
0x4C hard-drive — GEMDRIVE
0x4D lightbulb — USB CDC (only when tud_cdc_connected())
0x50 wifi — API Endpoint (only when DHCP has IP)
Three earlier candidates for USB CDC (flash 0x43, pulse
0x46, bluetooth 0x4A) all proved unreadable at 8×8 in
hardware testing — lightbulb's silhouette won.
drawCountdownBar(sec_left, sec_total)
Replaces "Boot will continue in N seconds..." plain text
with a filled white box at the bottom strip that shrinks
once per second. The status message ("Booting in N s — any
key halts") is drawn TWICE with different u8g2 clip windows
— once in black against the filled left half, once in white
against the empty right half — so it reads cleanly across
the bar boundary. (XOR mode would be the obvious one-pass
approach but u8g2's default font mode XORs the full glyph
bounding box on the white background, leaving solid black
blocks where letters should be.)
refreshUsbCdcLine()
Now also flips the lightbulb icon in lock-step with the
"connected"/"disconnected" status text so the USB icon is
live, not just a snapshot.
Halt-state message updated:
"Press [E] or [X] to continue." → "Press [E], [U] or [X] to continue."
(replaces both occurrences — refreshSetupInfoLine and the
main-loop transition handler.)
Visual delta: section dividers, four status icons in the right
column, animated white→black countdown bar with always-readable
overlay text. Existing menu functionality (every keypress, the
USB CDC live-refresh, the auto-fire at countdown-zero, the
"Countdown stopped." text) all unchanged.
Tier 2 (logo XBM, two-column layout, change-highlight, nav
pills) and Tier 3 (term-pipeline rewrite, modal folder picker,
sparklines) are parked on Epic 06's backlog with their
prerequisites; pull back only if the menu still feels flat
after this Tier 1 lands.
Documents the three new runner verbs landed in S5+S6+S7 + the
menu polish in S8, so a reader who has only the README and
docs/api.md can drive the full lifecycle on hardware.
docs/api.md
New "Pexec lifecycle: load / exec / unload" subsection
under Runner mode, between /runner/run and /runner/cd.
Covers:
- The verb → GEMDOS Pexec mode mapping table
- RP-side single-slot state (loaded_basepage, last_load_errno
fields surfaced in /api/v1/runner)
- Strict-refuse semantics (409 program_already_loaded)
- Per-endpoint sections for /load, /exec, /unload with curl
+ sidecart examples + response envelopes + error code
listings
- End-to-end lifecycle example (load → status → exec → exec
→ unload → status → exec → 409)
README.md
New "Pexec lifecycle (load / exec / unload)" subsection
under Runner mode with the four-line CLI snippet and a
pointer at the api.md anchor for full details.
The S8 menu polish (icons, dividers, animated countdown bar)
ships entirely on the device — nothing for an end-user to learn
or do, so no README/api.md change there beyond the existing
power-up note from earlier in the epic.
Three small fixes the first S9 pass missed:
- docs/api.md GET /api/v1/runner — response shape now lists
the new loaded_basepage and last_load_errno fields, and the
last_command enum gains PEXEC_LOAD / PEXEC_EXEC /
PEXEC_UNLOAD with a one-paragraph note that links to the
Pexec lifecycle subsection.
- docs/api.md POST /api/v1/runner/exec — adds the 202
response-body example ({ ok, accepted }) so the section
matches the shape of /load and /unload.
- README.md Power-up note — drops the "(per Epic 06 / S2)"
internal-history citation; users following the doc don't
need an epic-and-story tag to understand the auto-Runner
behaviour.
No code change. CLI tests still 105/105.
Replaces the prior README — which opened with the upstream
template's title and was effectively a developer-internals
sketch — with an end-user / app-consumer-focused README in the
shape of the sister md-drives-emulator project.
New top-level structure:
Title + tagline
Highlights (bullet list of features)
Read-before-installing call-out (no auth, template doc link)
🚀 Installation 5-step Booster-app deploy flow
🕹️ Usage boot countdown, [U]/[E]/[F]/[X] table
⚙️ Setup menu screen ASCII screenshot + per-section table
Runner mode (existing CLI snippet kept)
Pexec lifecycle (existing kept)
Advanced Runner (existing kept)
Remote HTTP API enriched with an end-to-end
edit-build-test workflow example
Debug traces (existing kept verbatim)
Project internals absorbs the prior "Shared 64 KB
region layout" + "Cartridge code
layout" sections at the bottom +
adds a small "Building from source"
subsection pointing at programming.md
/ the SidecarTridge programming docs
License (existing kept)
No content from the prior README was lost — the technical
internals just moved to the bottom under a "for contributors
and ST programmers writing apps that talk to md-devops
directly" framing, so a fresh-clone reader gets onboarding
context first and developer-internals after they've decided
they care.
Pure doc change. CLI tests untouched (still 105/105).
Both surfaces — CLI and HTTP API — used to expose the
file/folder management verbs at the top level alongside
unrelated families (ping / runner / debug). The result was
incoherent nesting: `runner run` and `ls` sat at the same depth
even though one is a Runner-mode operation and the other is a
GEMDRIVE drive-management operation.
Hard cutover, no backward-compatible aliases. Now everything
GEMDRIVE-related lives under a `gemdrive` prefix on both
surfaces:
CLI HTTP
------------ ----
sidecart gemdrive volume GET /api/v1/gemdrive/volume
sidecart gemdrive ls [PATH] GET /api/v1/gemdrive/files?path=...
sidecart gemdrive mkdir REMOTE POST /api/v1/gemdrive/folders/<rel>
sidecart gemdrive rmdir REMOTE DELETE /api/v1/gemdrive/folders/<rel>
sidecart gemdrive mvdir FROM TO POST /api/v1/gemdrive/folders/<from>/rename
sidecart gemdrive rm REMOTE DELETE /api/v1/gemdrive/files/<rel>
sidecart gemdrive mv FROM TO POST /api/v1/gemdrive/files/<from>/rename
sidecart gemdrive get REMOTE [LOCAL] GET /api/v1/gemdrive/files/<rel>
sidecart gemdrive put LOCAL [REMOTE] PUT /api/v1/gemdrive/files/<rel>
ping / runner / debug families unchanged (still top-level
`ping` / `runner …` / `debug …` on the CLI, still
/api/v1/{ping,runner,debug}* on the HTTP side).
Mechanical changes:
cli/sidecart.py
- argparse: new gemdrive subparser with the nine verbs as
its subcommands; main() routes args.cmd == "gemdrive"
to a gd_handlers dict by args.gemdrive_cmd.
- URL builders (cmd_volume's literal, folders_url, files_url)
retargeted to the new /api/v1/gemdrive/* paths.
- Module docstring (Subcommands + Examples sections)
refreshed.
cli/test_sidecart.py
- 31 _run_cli argv lists rewritten to inject "gemdrive"
before the verb (Python regex pass).
- 11 last_path assertions retargeted to /api/v1/gemdrive/*.
rp/src/http_server.c
- Static route table entries (/volume, /files exact-match
listing) → /gemdrive/volume, /gemdrive/files.
- Two prefix-dispatcher arms (folders_prefix, files_prefix
including the upload-streaming special case) → the new
/api/v1/gemdrive/* prefixes.
- Location: response headers + cross-reference error
messages refreshed.
docs/api.md, README.md
- Every curl URL and every sidecart CLI snippet refreshed
to the new shapes.
Verified: full RP firmware build green; all 105 CLI tests green.
Two new chapters added before the existing Runner mode section,
in the order a fresh user actually needs them:
## 🛜 First-contact: ping
Why ping is the first thing to test, what success looks like
in human + JSON + curl, and a troubleshooting list for the
common failure modes (mDNS not resolving, wrong host, wrong
network segment) with concrete --host / SIDECART_HOST escape
hatches.
## 💾 GEMDRIVE commands — manage files and folders remotely
Per-verb section for every gemdrive subcommand
(volume / ls / get / put / rm / mv / mkdir / rmdir / mvdir)
with output examples and the constraints that bite (8.3
name limits, 4 MB upload cap, no-implicit-overwrite on mv,
refuses non-empty rmdir, no descendant-cycle on mvdir).
Plus a cross-cutting edit-build-test recipe.
The prior "Remote HTTP Management API" section was duplicating
the CLI examples that now live in the GEMDRIVE chapter; rewrote
it as a brief "under the hood" pointer that maps each CLI
prefix to its HTTP family + links to docs/api.md for the full
reference. Net diff: more total content, less duplication.
Local-only sweeps over the (gitignored) epic docs to keep the
historical-design notes coherent with the shipped state are
done in the working tree but not part of this commit:
- docs/epics/06-final-touches.md: S9 marked shipped, S10 row
added.
- docs/epics/02-http-api.md: 48 URL + 22 CLI rewrites for
the gemdrive cutover.
- docs/epics/03-runner.md: 1 CLI rewrite.
The HTTP API is the device's single control surface — every
remote operation flows through it — so the README now explains
that fact upfront, before launching into the chapters that use
it.
Reordered:
⚙️ Setup menu screen
🌐 Remote HTTP Management API + sidecart.py CLI ← new spot
🛜 First-contact: ping
💾 GEMDRIVE commands
Runner mode
Debug traces
Project internals
License
The promoted chapter now covers in detail what was previously a
brief "under the hood" footnote at the bottom:
- The HTTP API itself: hostname / IP, the family table
(ping / gemdrive / runner / debug → endpoints), and the
framing that the CLI is one of many possible clients.
- cli/sidecart.py specifics:
* Requirements (Python 3.10+, stdlib-only — no pip).
* "Installation" (clone the repo and call it; or symlink
onto $PATH for shorter invocations).
* Host configuration: --host flag + $SIDECART_HOST env
var with precedence rules.
* Global flags table (--host / --json / -q / -h).
* Exit code map (0/1/2/3/4/5/6/7/8) so shell scripts can
branch on category.
* Direct curl equivalents for two example endpoints, to
make the "thin wrapper" framing concrete.
- No-auth warning kept as a callout block.
The previously-existing "Remote HTTP Management API — under the
hood" section near the bottom of the file was the same content
in summary form; deleted as redundant. Net diff: more content
total, less duplication, better narrative flow.
CLI tests: still 105/105.
The Runner mode and Debug traces chapters were the only places
in the README still rolling all subcommands into a single
batch-call code block. Now they match the GEMDRIVE chapter's
shape: one `### subcommand …` heading per verb, a one-or-two-
line description, an example invocation with realistic output,
and any caveats (HTTP status, sync vs fire-and-forget, blast
radius).
Runner mode now has dedicated subsections for:
status, reset, cd, res, meminfo, run, load/exec/unload, and
the four Advanced Runner verbs (adv status / meminfo / jump /
load) under their own `### Advanced Runner` umbrella.
Each carries:
- The exact `$ sidecart …` invocation, copy-pasteable.
- A representative output block (so the reader knows what
success looks like, and what fields they'll see).
- Whether the call is synchronous or fire-and-forget, and the
relevant timeout / HTTP code (10 s + 200 for `runner load`,
202 for fire-and-forget verbs, 5 s for `runner unload`,
etc.).
- For `runner adv jump` / `runner adv load`: the shell-quoting
gotcha for `$hex` arguments and the address constraints
(even, 24-bit, ≥ 0x800, below phystop).
- Cross-references between related verbs (e.g. `runner
meminfo` pointing at `runner adv meminfo` as the wedged-ST
fallback; `runner adv jump` paired with `runner adv load`
for the load-and-jump idiom).
Also adds a short "Foreground vs Advanced" framing paragraph at
the top of Runner mode so readers understand why the same kind
of operation (meminfo) appears twice.
Debug traces now has dedicated subsections for:
- `debug status` — what each diagnostic field means and when
a non-zero `bytes_dropped` / `usbcdc_dropped` matters.
- `debug tail` — long-poll behaviour, when to prefer it over
USB, plus the `tee` capture idiom.
- `USB CDC` — same byte stream, no network, with terminal
examples; called out as a peer surface to `debug tail`
(both can run at once, ring fans out internally).
- End-to-end smoke — the existing HELLODBG.TOS recipe, kept.
The m68k ABI explanation, power-up ordering callout, C / asm
emit examples, and dump-string snippets are all preserved
verbatim — only the trailing transports + smoke block was
restructured.
Previous wording claimed `runner reset` returned the ST to the setup menu, with the user having to press [U] or wait for the countdown again. That's wrong: the firmware-mode commit is held on the Pico (not the ST), so a soft ST reset just lands back in whatever mode was active. The setup menu only reappears on a full power-cycle of the ST combined with a hard reset of the Pico.
…code
Three-way audit of rp/src/http_server.c (source of truth) against
docs/api.md and README.md surfaced a set of stale claims that no
longer match the firmware. Fixing them all in one pass.
docs/api.md:
Runner mode framing (was: "every Runner endpoint returns 409
runner_inactive ... only run/cd/res/meminfo gate on busy")
rewritten as three behavioural buckets matching the actual
handler logic:
- Status reads (`/api/v1/runner`, `/api/v1/runner/adv`):
always 200, never error on inactive Runner — they just
report `active: false` (handlers at http_server.c:1224 and
:2512 have no `runner_inactive` gate).
- Foreground commands (run/cd/res/meminfo + load/exec/unload):
every one of these gates on busy. The previous "four
foreground gating endpoints" claim missed load/exec/unload
(handlers at :1580, :1747, :1800 all return 503 busy).
- VBL-driven commands (`reset` + `adv/*`): ride the VBL ISR,
no busy gate, only `runner_inactive` + (for jump/load)
`wrong_hook`. The `busy` code-vocabulary entry was
rewritten to match.
`POST /api/v1/runner/reset` description rewritten:
- Was: "Fires `RUNNER_CMD_RESET`". Truth (http_server.c:2753):
it fires `RUNNER_ADV_CMD_RESET` — Epic 04/S5 rewired this
to the VBL ISR specifically so it could escape wedged
programs. The handler comment at :2731-2740 spells this out.
- Added the firmware-mode-lives-on-Pico fact: a soft ST reset
re-enters Runner mode automatically; only a power-cycle
plus hard Pico reset returns to the setup menu.
504 gateway_timeout description (was: "Runner endpoint waited
> 1 s") corrected: the actual deadlines are 10 s for `runner
load`, 5 s for `runner unload`, 1 s for meminfo / adv-meminfo /
per-chunk adv-load (constants at http_server.c:1573, 1793,
2080, 2204).
`runner run` / `runner load` example removed the misleading
`--` separator and `cmdline = "-v --file foo"` annotation.
argparse REMAINDER preserves `--` verbatim, so the previous
example would actually have produced `cmdline = "-- -v --file
foo"`. The CLI's own help string says no `--` is needed
(sidecart.py:1287). Added a one-line note that `--`, if
passed, lands in `cmdline` literally.
README.md:
Same three-bucket reframing of the Runner-mode surface (the
previous two-bucket "Foreground / Advanced" split miscategorised
`reset`, which rides the VBL ISR even though its URL doesn't
carry `/adv/`).
The `runner reset` subsection now states up-front that it
rides the VBL ISR via `RUNNER_ADV_CMD_RESET` — that's the
reason it can escape wedged programs and the reason it doesn't
gate on busy. Auto-re-enter-Runner-mode behaviour kept from
the prior fix.
CLI: no changes — sidecart.py was already consistent with the
RP source of truth (105/105 tests still passing).
Continues the docs/api.md vs RP-code vs CLI consistency sweep.
Four more drift fixes; all cite the canonical handler in
rp/src/http_server.c.
docs/api.md:
fs_type list expanded to include `UNKNOWN`. Truth: the
fat_type_name() table at http_server.c:1156-1162 falls through
to "UNKNOWN" when the FatFs FS code isn't one of FAT12 / 16 /
32 / EXFAT — possible on rare card formats. Docs previously
promised one of four values.
Error code vocabulary list (Conventions section) now includes
the four codes that response handlers actually emit but were
missing from the doc:
- `pexec_failed` — runner load Pexec(3) error envelope
(http_server.c:1712).
- `mfree_failed` — runner unload Mfree error envelope
(:1839).
- `program_already_loaded` — strict-refuse on second load
(:1589).
- `no_program_loaded` — exec/unload with no prior load
(:1756, :1809).
These are documented in the per-endpoint sections already;
the vocabulary list is the canonical "what codes can clients
switch on" reference and was incomplete.
Upload errors list now mentions `409 conflict "Cannot PUT
root"` — handle_file_upload_init at :3742 returns this, but
the doc only listed file-exists and is_directory.
README.md:
`gemdrive mv` description said "Both endpoints must stay in
the same drive (the API can't move across drives)". Misleading
— the API jails everything under one GEMDRIVE root and has no
drive-letter concept at all. Rewrote to "Both source and
destination paths are jailed under GEMDRIVE_FOLDER like every
other API path — the API has no notion of multiple drives,
only the one emulated GEMDRIVE root."
CLI: still consistent (105/105 tests pass).
The README is for end users / developers integrating with the
firmware, not for contributors who track our internal Epic-and-
stories planning. Two stray references slipped through:
- "HTTP-management story stitched on top" in the Highlights
intro — generic English usage of "story" but easy to read
as an internal-jargon leak. Rewrote as "HTTP-management
surface stitched on top".
- "Epic 04 rewired it from the foreground poll loop" in the
runner reset framing. Replaced with a behaviour-focused
"it was deliberately wired to the VBL ISR rather than the
foreground poll loop so it can escape wedged programs" —
same fact, no leak of our development process.
A grep for `[Ee]pic|[Ss]tory|\bS[0-9]+\b` in README.md is now
clean.
Until now the README mentioned the two Advanced Runner hook
vectors only in passing — the menu table called out
\`vbl ($70)\` as "faster" and \`etv_timer ($400)\` as "more
compatible" without saying *why*, and the Advanced Runner
chapter just said "adv jump and adv load require \$70". A
reader had no way to make an informed choice between them.
Setup menu chapter:
Added a "Picking a hook vector" subsection right under the
menu-elements table. It walks through:
- **\`vbl (\$70)\`**: hardware VBL exception vector. Standard
m68k trap frame on the supervisor stack, so saved PC and
SR are at known offsets — \`runner adv jump\` and
\`runner adv load\` can rewrite them. Sits ahead of TOS'
VBL chain (works against programs that bypass TOS).
Vulnerable to programs that overwrite \`\$70.l\` directly.
- **\`etv_timer (\$400)\`**: TOS' documented extension hook,
called from inside TOS' VBL chain after TOS' own
bookkeeping. More polite about cooperating with TOS.
Survives programs that rewrite \`\$70.l\`. Trap-frame
layout past the MFP scratch is not stable across TOS
versions — so jump/load refuse with 409 wrong_hook here.
Vulnerable to programs that hijack \`etv_timer\` itself.
- Which verbs work on which vector (status/meminfo/reset
work on either; jump/load need \$70).
- Rule of thumb for picking: keep \`etv_timer\` as the
polite default, switch to \`vbl\` when you need
jump/load, when debugging code that bypasses TOS, or
when something has hijacked \`etv_timer\`.
The menu table's "Adv [V]ector" row now points at the new
subsection instead of the half-truth "(faster) / (more
compatible)" parenthetical.
Advanced Runner chapter intro:
Replaced the brief "adv jump and adv load require the VBL
hook" line with a verb × vector compatibility table, and a
one-paragraph technical reason why jump/load can't safely
run from \`etv_timer\` (trap-frame layout past MFP scratch,
TOS-version-fragile). Cross-references the new setup-menu
subsection for the full trade-off.
CLI tests still passing (no CLI changes — docs only).
The Advanced Runner's hook-vector setting now defaults to
\`vbl\` (\$70) rather than \`etv_timer\` (\$400). Rationale:
- The full Advanced Runner feature set (\`runner adv jump\`
and \`runner adv load\`) only works on \$70, because we
need the standard m68k exception trap-frame layout to
rewrite the saved PC. \`etv_timer\` runs from inside TOS'
VBL chain past the MFP scratch and the layout there is
not stable across TOS versions.
- Defaulting to \`vbl\` means a fresh-flashed device gives
the user every Advanced Runner verb out of the box, no
setup-menu detour. \`etv_timer\` remains available as an
opt-in for callers who deliberately want to ride
downstream of TOS' VBL chain (e.g. to cooperate with a
program that has installed its own raw \$70 handler).
Code changes:
- rp/src/aconfig.c — defaultEntries[] for
ACONFIG_PARAM_ADV_HOOK_VECTOR flipped to "vbl". Comment
rewritten to explain the reasoning.
- rp/src/gemdrive.c — HELLO publish path: aconfigString()
fallback flipped to "vbl"; the strcmp branching reordered
so anything-other-than-"etv_timer" lands on \$70 (was: only
"vbl" landed on \$70, anything else on \$400). Net effect:
a corrupted/garbage setting now falls back to vbl rather
than etv_timer.
- rp/src/emul.c — menu display fallback flipped to "vbl";
display logic reordered so anything-other-than-"etv_timer"
renders as "vbl (\$70)". Same flip in cmdAdvHookVector
(the \`[V]\` toggle) so an empty/missing value cycles to
"etv_timer" first, then back to "vbl".
README:
- Removed the "Rule of thumb" paragraph that recommended
keeping etv_timer as the default — it no longer applies
and the user explicitly didn't want it.
- Setup-menu screenshot updated: the \`Hook\` line now shows
\`vbl (\$70)\`, matching what a fresh boot will display.
- "Picking a hook vector" subsection rewritten to state
\`vbl (\$70)\` as the default up-front and frame
\`etv_timer (\$400)\` as the opt-in alternative; the
pros/cons of each are unchanged.
docs/api.md needed no changes — every existing reference is
neutral about which is default (e.g. "if you flipped
ADV_HOOK_VECTOR to etv_timer") and reads correctly under the
new default.
Updates the three tracked version.txt files (root, rp/version.txt, target/version.txt) and the regenerated m68k target_firmware.h (one cell drift from the version string baked into BOOT.BIN at build time).
Three small touch-ups before tagging:
- README title: rewritten as "SidecarTridge Multi-device
DevOps microfirmware" and the lede paragraph reflowed into
one sentence — drops the dev-jargon "md-devops" name from
the title and matches the brand framing in the rest of the
repo.
- desc/app.json: filled with the actual app's name
("DevOps toolkit"), feature-summary description with the
docs.sidecartridge.com "Learn more" link in the same shape
md-drives-emulator uses, and the four catalog tags
(Development / GEMDRIVE / Debug / CLI). The three
placeholder fields the build pipeline owns
(<APP_UUID> / <BINARY_MD5_HASH> / <APP_VERSION>) are kept
verbatim.
- rp/src/include/target_firmware.h: regenerated from the
rebuilt m68k BOOT.BIN — single byte-pair changed where the
embedded version string lives.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.