mirror of
https://github.com/st3fan/ewm.git
synced 2026-10-25 17:02:19 +00:00
mouse-m5-docs
22
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
73a38e19fa |
Mouse plan M5: docs + as-built notes/MOUSE.md
The closer for the AppleMouse II card (plans/20260721-01, now complete). No code — the as-built record and the config surface's documentation. - notes/MOUSE.md: the as-built — synthetic firmware and the identification / screen-hole map, the private DEVSEL protocol, the interrupt model and the reusable maskable-IRQ line (so Mockingboard inherits it), and the coordinate decision (absolute/mapped, revised from the plan's relative/captured). - README: the "What's emulated" list gains the AppleMouse II; a runnable example (examples/mouse.json, a slot-4 mouse //e) checked by readme_examples_parse. - notes/JSON_CONFIG.md: the slot-card inventory gains "mouse" (one, any slot 1-7, both families); notes/STATE.md lists the Mou Persist fields; notes/IDEAS.md ticks the AppleMouse item and points Mockingboard at the shared IRQ line. Gate: readme_examples_parse + standard gates (382 pass). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
9a27b68b2b |
Add builtin:apple2e config + docs (//e original-plan E4) (#321)
The closer for the original //e: a `builtin:apple2e` config so the machine E3 added is reachable by name, and the docs to match. This completes plans/20260720-02. - configs/apple2e.json: model apple2e, aux ext80col, UniDisk 3.5 (Liron) in slot 5, Disk ][ in slot 6, RGB — the same layout as apple2enhanced, on the 6502. Registered in BUILTINS (sorted apple2 < apple2e < apple2enhanced); the builtin list / title / model / self-containment tests extend. - Boot gate: builtin_apple2e_boots_dos33 builds the machine through the config pipeline and boots DOS 3.3 to the Applesoft `]` prompt (the //e autostarts the slot 6 disk; DOS 3.3 runs on the 6502). - Docs: README "What's emulated" folds the original and Enhanced //e into one entry and gains a builtin:apple2e profile; notes/JSON_CONFIG.md model inventory + the aux-validity note cover both //e models; IDEAS.md marks "NMOS //e" landed. Gates: cargo fmt / clippy -D warnings / cargo test (365 pass, incl. the new boot gate and readme_examples_parse with the new builtin:apple2e example). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
869ba8175e |
Rename builtin:apple2e -> apple2enhanced (//e original-plan E1) (#316)
Our `apple2e` was really the Enhanced //e (65C02 + 342-0303/0304 system ROMs + the 342-0265 MouseText video ROM). Rename the config-level token to the honest `apple2enhanced`, freeing `apple2e` for the true original (unenhanced) //e that E2-E4 add. Mechanical, mirroring the `2plus`->`apple2plus` rename (#308); no ROM or machine changes. What moved (the config surface only): - `config::Model` serde token + `Model::token()`; `git mv configs/apple2e.json -> apple2enhanced.json` with model updated and title made honest ("Apple //e" -> "Enhanced Apple //e", reserving "Apple //e" for E4's original). BUILTINS re-sorts in place (apple2enhanced sorts before apple2plus, so no manual reorder). - The `Persist` INFO model string mirrors the token by design (apple2/apple2plus/...), so it renames too, keeping save-states self-describing as `apple2enhanced`. - Every `"apple2e"` config-token literal in tests/error strings; usage and `--config` help text; README, notes/JSON_CONFIG.md, notes/MAC_APP.md, examples/myiie.json. Both JSON schemas regenerated from source (EWM_UPDATE_SCHEMA=1), not hand-edited. - `builtin:list` column widened 12->16 so the longer name still aligns. Deliberately NOT touched (internal, renamed later by E3): the Rust identifiers `TwoType::Apple2E` / `Model::TwoE`, the `two_e_*` test files, and their "apple2e must construct" panic strings — they name the type, which stays until E3 splits the two machines. Verified: cargo fmt --all --check, cargo clippy --all-targets -D warnings, and cargo test (359 pass, incl. the golden-BMP screenshots and readme_examples_parse). Gate: `builtin:list` shows apple2enhanced; the old `builtin:apple2e` errors "no built-in config ... (available: apple1, apple2, apple2enhanced, apple2plus, replica1)". Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
2150f7e0a8 |
Bare ewm two boots builtin:apple2plus (no in-code default machine) (#313)
Owner's decision: the default machine *is* a config, not an in-code layout. When parse_options collects no sources, it now loads builtin:apple2plus, so `ewm two` and `ewm two --config builtin:apple2plus` build the identical machine — code and configs describe the ][+ once, the way `ewm one` already builds its default from the replica1 builtin. Consequences: the old in-code default's Thunderclock in slot 1 is retired (builtin:apple2plus is language@0 + diskii@6, no clock — a stock ][+); and, combined with the title field, the bare-two window now reads "EWM - Apple ][+" (the owner's first computer). The golden-BMP screenshots are unaffected — the Thunderclock never rendered. Two default-pinning tests updated to the new behavior: bare_two_is_the_apple2plus_builtin asserts bare == the builtin, and the print-config round trip checks the title and language card instead of the retired clock. default_slot_cards stays as Two::new's constructor default (the lib/test path, distinct from the CLI default). Docs: the README profiles section and JSON_CONFIG note now say the default is builtin:apple2plus; the config-sources backlog item is marked done. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
2a36ca634f |
Window title from the config: EWM, or EWM - <title> (#312)
The window bar was picked by matching options.model, so an apple2 machine fell into the ][+ arm and showed "Apple ][+". Replace it with a config-driven title: a new top-level `title` field, window reads "EWM - <title>" when set and plain "EWM" otherwise (config::window_title, shared by both frontends). The five built-in configs set it to the machine name: Apple 1, Replica 1, Apple ][, Apple ][+, Apple //e. The old "EWM v0.1 / …" strings are gone; the bootloader window aligns to "EWM - Bootloader". Both `two` and `one` carry the title in Options (from apply_config, and round-tripped by options_to_config so --print-config shows it). It comes from the document only: a bare `ewm two`/`ewm one` stays "EWM" even though one's normalize fills the board from a builtin — so the title tracks what you asked for, not the machine underneath. Tests: window_title formatting, and every built-in carries its machine-name title. Schemas regenerated for the new field. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
c76edb68c3 |
builtin:apple2 config + PR#6 DOS 3.3 boot gate (A3) (#311)
Phase A3 of plans/20260720-01-original-apple2.md, closing the plan. configs/apple2.json is the machine the request started from: a 48K Apple ][ (no slot-0 language card), Integer BASIC, a Disk ][ in slot 6, green monitor — registered in BUILTINS (sorted: apple1, apple2, apple2e, apple2plus, replica1). The self-containment sweep and the builtin-name/list assertions extend to it. Boot gate: builtin:apple2 with the DOS 3.3 System Master in drive 1 resets to the Monitor — asserting it does NOT autostart the disk (a ][+ would) — then C600G runs the slot 6 boot ROM and DOS 3.3 loads (its cold-start banner is the marker). No `]` check: the original ][ has Integer BASIC, not Applesoft, so DOS drops to `>`. Docs: the README What's-emulated list gains the Apple ][; the two profiles section adds builtin:apple2 with its JSON and the C600G/PR#6 boot note; JSON_CONFIG's model inventory lists apple2. Plan complete. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
64dc24f7a5 |
Rename model tokens: 2plus -> apple2plus, 2e -> apple2e (#308)
Spell out the two-family model tokens so every machine reads the same way — apple1, apple2e, apple2plus, replica1 — ahead of adding the original Apple ][ as `apple2` (plans/20260720-01). Pre-1.0, so the old terse tokens go outright: `--set machine:model=2plus` and `--config builtin:2plus` are now apple2plus / apple2e, and an old name errors with the available list. Mechanical and wide: the config::Model serde tokens and Model::token(), the BUILTINS table (renamed, re-sorted, include_str paths), configs/ files (git mv 2plus.json/2e.json -> apple2plus.json/apple2e.json) and their model values, the ewm/tests/configs fixtures, every "2plus"/"2e" string literal in tests and error messages, the examples, both schemas, and the live docs (README, JSON_CONFIG, IDEAS, MAC_APP). Guarded word-boundary replacement so e2e, new_2e and hex escaped it. The builtin:list column widened to fit the 10-char "apple2plus". Verified: fmt, clippy -D warnings, the full suite (37 binaries incl. golden-BMP and the README example check) pass; builtin:list and the old-token error checked against the real binary. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
c9c1a75fb6 |
Disk images over HTTP, cached with ETag revalidation (#306)
* --tty-telnet: proactive echo negotiation (fix telnet double-echo) The telnet echo suppression was reactive — the server only announced WILL ECHO / WILL SGA after the client sent an IAC first. But a classic telnet client sits in line-mode-with-local-echo and waits for the server to negotiate, so the announcement never went out, the client kept echoing locally, and every line appeared twice (its echo plus the machine's own authentic echo). A telnet server that wants remote echo has to offer it proactively on connect. The new --tty-telnet flag (implies --tty) does exactly that: it writes WILL ECHO + WILL SGA before the banner and marks the filter active so the reactive path doesn't double-announce. Bare --tty stays byte-clean for local terminals and nc (kickoff option B); the reactive fallback still helps a plain --tty client that does negotiate first. The systemd @.service switches its ExecStart to --tty-telnet. The growing session-behavior params (throttle, banner, telnet) fold into a TtyConfig struct. Tests: proactive announce leads the stream (before the banner) and doesn't double-announce when the client then sends its own IAC; the flag implies --tty and parses. Verified live: `ewm one --tty-telnet | xxd` shows FF FB 01 FF FB 03 then the prompt. Docs: scripts/systemd/README.md and notes/APPLE1.md explain proactive --tty-telnet vs byte-clean --tty, with troubleshooting for the double-echo and the nc negotiation bytes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Disk images over HTTP, downloaded once into a cache A floppy, hard-drive or 3.5" image path may now be an http(s):// URL: ewm two --set 'machine:slots:6:drive1=https://example.com/Frogger.dsk' ewm/src/fetch.rs downloads it into $XDG_CACHE_HOME/ewm (else ~/.cache/ewm) / <sha1 of the URL> / <file name from the URL>, beside a meta.json holding the ETag and Last-Modified. The digest keys the directory because URLs are not filesystem-safe; the file keeps its own name so the cache is browsable and errors read normally. The hash is the crate's own SHA-1 (ws::sha1) — no dependency for that. Freshness is one *conditional* GET (If-None-Match / If-Modified-Since) rather than HEAD-then-GET: a 304 costs a single round trip. If the request fails and a copy is cached, the cached copy is used, so a machine assembled from URLs still boots offline; a cold cache with no server is a loud error instead. A URL is a source, not a path: resolve_paths leaves it alone (like builtin:), while referenced_files still counts it, so a built-in config may not carry one — builtins must run offline. Downloaded hard drives mount memory-only. This needed a real change, not just leaving the file handle unopened: in hdd.rs an unopenable file means WRITE PROTECTED, which is a different (and still supported) state for a local image. Hdd now carries an explicit `writeback` flag — false for downloads, so ProDOS writes update the in-memory image and never reach the cached file, and revalidating a download cannot destroy them. Floppies already behaved this way. Dependency: ureq (blocking + rustls), owner's decision — the one place the tiny-dependency budget gives way, because TLS is not something to hand-roll. It brings ~25 transitive crates including ring, so the headless server build now compiles native crypto too. Tests: the cache round trip against a real local HTTP server (cold download, conditional revalidation with a 304, offline fallback, and a cold cache with nothing listening failing loudly); URL naming and digest stability including degenerate URLs; memory-only vs write-back block writes at the register level; URLs surviving path resolution; and the wiring gate — a machine builds with DOS 3.3 served over HTTP into drive 1, which only passes if the mount path really fetched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
237955a54a |
Docs: the one profiles as schematics; as-built notes (R4) (#298)
Phase R4, closing plans/20260719-03-one-machine-components.md. The README's one-profiles section now shows what the plan was for: the profile JSON is the machine's schematic — CPU, every RAM bank with size and address, every ROM image with its address — with the mountable builtin: ROM names listed, user ROM paths mentioned, the whole-board semantics and reset-vector rule explained, and the faithful details told in English (BASIC preloaded in the $E000 RAM bank as the cassette would have; the Replica 1's $E000-$FFFF being the Krusader distribution byte for byte). notes/APPLE1.md gains the as-built section (registry, byte-identity gates, from_components, whole-board semantics, the profile decisions); notes/JSON_CONFIG.md's schema inventory adds machine.cpu and the generalized memory regions with the three-way family rules. Plan marked complete. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d672893b05 |
Docs: one profiles, JSON_CONFIG as-built, REMOTE Phase 7 note (O5) (#292)
Phase O5, the last phase of plans/20260719-02-one-config.md. The README gains "The one machine profiles" alongside two's — both builtins in English and JSON with their boot commands, a note that bare `ewm one` boots the Replica 1, and the one-family key rules in a sentence. The example check extends to `-- one` commands (readme_examples_parse dispatches on the subcommand; one::parse_options is pub(crate) for it) so one's examples are enforced like two's. JSON_CONFIG.md records the as-built decisions: one document type with a derived family, the cross-check table and its one-place relax point, the shared collect_document parameterization, minimal builtins, boo booting via builtin:, and one's flag retirement. The schema inventory names which keys are apple2-family-only, and the status table gains the one-family row. REMOTE.md's Phase 7 notes that the config surface it wanted now exists — relaxing the remote-block family check is a one-place change. Plan marked complete. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
de6abcd1ce |
Flag retirement: the CLI after config sources (F1–F3) (#286)
* Flag retirement F1: the quiet seven Plan 20260719-01: --scanlines, --fps, --strict, --debug, --boot-delay, --trace/--trace= and --state are gone from `ewm two` — they were single-value duplicates of config keys (display.scanlines, display.fps, cpu.strict, debug.enabled, boot.delay, debug.trace, state.path), all reachable via --set or any config source. Retired flags fall into the generic usage error (owner's ruling: v0.0.1, no transition ceremony); a test sweeps the list and pins the --set spellings. Scanlines::parse went with its only caller (the flag's optional-value peek); MonitorStyle::parse follows in F2 with --color. Docs swept: README's feature blurb, STATE.md and REMOTE.md speak state.path / --set state:path now, IDEAS.md's trace item names debug.trace. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Flag retirement F2: the muscle-memory trio Plan 20260719-01: --model, --color and --aux are gone from `ewm two` — machine.model, display.monitor and machine.aux are config keys, and `--config builtin:2e` is as short as `--model 2e` was. The --model alias spellings (2+, ][+, //e, iie) go with it, as does --color's optional-value peek (bare --color meant rgb) and MonitorStyle::parse, its last caller. The aux token survives internally (Options.aux, rebuilt by apply_config); only the flag is gone. Swept the consumers: main.rs's getting-started hint and subcommand line, the README feature blurbs and quick-start (builtin-first now, enforced by readme_two_examples_parse), JSON_CONFIG.md's schema inventory (sources are config + --set for everything), and the --color/--aux comment references in scr.rs, aux.rs, config.rs and two.rs. `one` keeps its --model — it has no config surface (plan non-goal). Historical notes (APPLE_IIE_ENHANCED, REWRITE, landed IDEAS items) keep their period wording. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Flag retirement F3: --memory Plan 20260719-01, last phase: --memory is gone from `ewm two`; memory regions come from config or overlay files (machine.memory), the one setting --set cannot reach (arrays) — apply_set's error text now says so. two's parse_memory_option went with the flag (one keeps its own copy and its --memory flag: no config surface there, plan non-goal). The print-config round trip composes the region through an overlay now, which is exactly the documented replacement. With this, `ewm two`'s surface is the plan's goal shape: the four sources (--config, --config-overlay, --set, --print-config), --serve, and the debug tooling (--wozbug, --break, hidden --screenshot). Plan marked complete with the kickoff decisions recorded as built. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * README: wrap the quick-start commands; document the three two profiles The "start a machine directly" commands wrap with backslash continuations (the readme_two_examples_parse test joins them, so they stay checked). A new section explains that bare `ewm two` boots the default machine — a ][+ with the 16K Language Card (the classic 64K build), Thunderclock, and Disk II — and documents all three machine profiles in English and JSON: the default (with its config-terms equivalent, printable via --print-config), builtin:2plus, and builtin:2e, each with its explicit `--config builtin:<name>` boot command. The later ready-made-configs paragraph folded into it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * README: brief Experimental section for WozBug, VNC, and the Mac app WozBug's walkthrough and the Native Mac app section fold into a new Experimental section at the bottom — three short bullets, each linking to its working document (notes/DEBUGGING_TOOLS.md, notes/REMOTE.md, notes/MAC_APP.md). The remote console gets its first README mention there; its --serve bullet leaves the source-kinds list, which is back to the three document sources. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * README: drop the cc65 tests/ sentence The C-era assembly test programs are being removed in #284; the Testing section no longer mentions them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * README: complete Key commands section The keys table becomes a "Key commands" section and gains the missing entries: Cmd-K (the Command Palette, available on every machine) and Cmd-Shift-R (reboot, power off/on), plus a Machines column instead of per-row parentheticals. A short paragraph documents the palette's own keys (type to filter, arrows, Return, Esc), that everything else — including Ctrl-A..Z control characters — goes to the emulated machine, and drag-and-drop disk swapping. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
a3c856aab3 |
Docs sweep: README around the four config sources (C5) (#282)
Phase C5, the last phase of plans/20260718-02-config-sources.md. The README's config section is rewritten around the compositional model — one document, four source kinds merged in command-line order (--config base, --config-overlay layers, --set overrides, convenience flags on top) — with the Total Replay overlay as the worked example and --config builtin:2e in the quick-start. The example configs are now committed under examples/ (myiie.json and drive-with-total-replay.json, shown verbatim in the README), so every example references real files. --print-config closes the section as the "what machine did I just describe?" / linter answer. The gate's scripted check is the readme_two_examples_parse test in two.rs: it extracts every `cargo run --release -- two ...` example from the README (quote-aware, continuation-joining) and runs it through parse_options, which opens every --config/--config-overlay source — so a renamed flag, a bad example path, or an example config that stops validating fails the suite. Notes brought up to date: JSON_CONFIG.md's status table gains the config-sources row (C1-C5 done) and Phase C is marked as seeded by options_to_config; IDEAS.md moves machine config files from "in progress" to "landed" with the remaining palette work named; MAC_APP.md's Phase 3 bullet records that .ewmachine bundles can be a thin layer over the landed source surface. Also fixes the --fps usage text (the default is 40, not 30 — drift spotted during C4). Verified: the README's composed, complete-config, and overlay-only examples run against the real binary from the repo root (exit 0, expected documents); fmt, clippy -D warnings, and the full suite (including the golden-BMP screenshot tests) pass. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
5ee1546db8 |
--print-config: print the machine the command line describes (C4) (#281)
Phase C4 of plans/20260718-02-config-sources.md. With four layering
sources, "what machine did I just describe?" needs a first-class
answer:
ewm two --config builtin:2e \
--config-overlay drive-with-total-replay.json \
--set display:monitor=amber --print-config
prints the final machine — sources *and* convenience flags applied,
i.e. exactly what a real run would build — as config JSON on stdout
and exits 0. Load and validation errors still exit nonzero first, so
the flag doubles as a config linter for scripts and CI.
options_to_config (two.rs) is the one Options→Config mapping, the
inverse of apply_config, kept as a single function so the palette's
"save current setup" (JSON_CONFIG Phase C) can reuse it. The plan's
full-mapping option worked out; the pre-convenience-flags fallback
wasn't needed. --wozbug, --break and the hidden --screenshot are debug
tooling, not machine configuration, and don't appear in the document.
The printed document is explicit where it matters and quiet where it
doesn't: model, slot table, display and cpu settings print even at
their defaults (stable against future default changes), while
off-by-default extras (strict, debug, boot delay, remote) appear only
when enabled. config::compact_document does the shaping — it drops
nulls, empty arrays, and sections that emptied out, but keeps a
genuinely bare "slots": {} table, which means "no cards", not
"default layout".
Verified: e2e tests pin the gate — a command line composed from every
source kind prints a document that, fed back via --config, yields
identical Options (Options/ServeOptions/MemoryOption grew PartialEq
for exactly this) — plus the default machine and bare-table round
trips and the exit codes; fmt, clippy -D warnings, and the full suite
(including the golden-BMP screenshot tests) pass; the real binary was
exercised by hand for the composed and linter cases.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
37f5e788a5 |
--config-overlay: layer partial configs onto the base (C3) (#280)
Phase C3 of plans/20260718-02-config-sources.md, on top of C2's
partial parsing. The machine configuration surface is now
compositional:
ewm two --config builtin:2plus \
--config-overlay drive-with-total-replay.json \
--set display:monitor=amber
--config-overlay <source> is repeatable, and all document sources —
the --config base, overlays, --set — apply strictly in command-line
order through the existing merge. Overlay files load through the
structural-only typed path: the overlay file is named in errors and
its relative paths resolve against its own directory, the same
portability property config files have. The builtin: scheme is shared
with --config (a complete config is a valid overlay), and overlay-only
command lines start from the default machine, like bare --set.
Slots materialization extends to overlays (merge_overlay_document): an
overlay carrying machine.slots onto a slotless document materializes
the default table first, so `--config-overlay hdd7.json` means "the
default machine plus a hard drive in slot 7" rather than a literal
one-slot machine. A base's explicit table stays literal — the plan's
asymmetry — and all four base x overlay slots combinations are pinned
in config.rs tests before the flag wiring, per the plan's hazard note.
--set's materialization is untouched, so the boo launcher's drag-drop
paths behave identically.
Kickoff decision, recorded as built: at most one --config, per the
plan's recommendation — a second errors with "only one --config
allowed; use --config-overlay for additional layers". Two complete
machines silently deep-merging read as an accident once partial layers
had their own flag.
Verified: fmt, clippy -D warnings, the full test suite (including the
golden-BMP screenshot tests), plus the real binary exercised by hand —
the one-config error, the partial-to---config refusal, the overlay
composition, and the known (accepted, documented) edge that
`--config-overlay builtin:2e` over the ][+ default fails the //e
slot-0 completeness check.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
fc90fff5a8 |
Partial configs: relax the parse, split validation (C2) (#279)
Phase C2 of plans/20260718-02-config-sources.md — the enabling change
for --config-overlay (C3), landed separately so C3 stays small.
Config.machine and Machine.model become Option in the serde types, so
any partial fragment ({}, a slots-only overlay) parses. Validation
splits to match: validate() keeps every check a lone fragment can be
judged on (unknown keys, enum values, slot rules, multiplicity), while
the new validate_complete() runs only on the final layered document —
machine.model present, plus the model cross-checks (aux is //e-only,
the //e has no slot 0) that an overlay can't answer until the merged
document names the model.
The loader contract keeps configs and overlays distinct concepts:
load/load_source_document (the --config path) still require
completeness per file — a partial file fails early with "machine.model
is required (is this an overlay? use --config-overlay)" — while
load_document now runs only the structural pass (file named in errors,
relative paths resolved) and is the path overlays will load through.
from_document runs both passes on the merged document and points a
missing model at the fix: "start from --config, e.g. --config
builtin:2plus".
Schema: option (2) from the plan. The generated schema is now
overlay-shaped, so the golden test post-processes the requiredness
(machine, machine.model) back into schema/ewm-config.schema.json and
commits the relaxed variant as schema/ewm-config-overlay.schema.json
with its own title/description, giving overlay files editor support.
Round-tripping the generated schema through serde_json::Value would
lose schemars' canonical keyword ordering, so the test converts back
to schemars::Schema before serializing.
Verified: fmt, clippy -D warnings, and the full test suite (including
the golden-BMP screenshot tests) pass; new tests pin the fragment
round-trip through load_document, the per-path completeness messages,
and the model cross-checks moving to the final-document pass. Behavior
of every existing command line is unchanged.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
aae19c97b8 |
Built-in machine configs: --config builtin:2plus / builtin:2e (C1) (#278)
Phase C1 of plans/20260718-02-config-sources.md. The configs/ files are embedded at compile time (include_str!, a static table in config.rs), so --config builtin:<name> works from any installed binary; names are the schema's model tokens, matching the renamed files 1:1 (configs/2plus.json, configs/2e.json). builtin:list prints names with descriptions and exits like --help; an unknown name errors listing the available names. Built-ins are self-contained by rule: load_builtin rejects any file reference (there is no directory to resolve relative paths against) and a test pins the property, along with every builtin carrying the new top-level description field (schema regenerated) and the table staying sorted. Verified: builtin:2plus/2e produce the same machine as their committed files (unit + integration tests), built-ins layer with --set like any source, builtin:list and unknown-name exits exercised against the real binary, and the full suite (306 tests, golden BMPs included) is green with fmt and clippy clean. Claude-Session: https://claude.ai/code/session_014tBgXL3aSDMgV2CoxTmBQ6 Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
185f64895f |
Emulate the UniDisk 3.5 Controller (Liron) with .2mg images (liron.rs) (#259)
The SmartPort card that put 800K 3.5" drives on the Apple II, as
virtual hardware in the hdd.rs style: hand-assembled $Cn00 firmware
over magic DEVSEL ports, no IWM emulation. Two drives, .2mg images of
exactly 400K or 800K, ProDOS order; the 2IMG locked flag mounts
read-only and writes land back in the file at data_offset, header
preserved.
The firmware carries a real SmartPort identity - signature $Cn07=$00,
ID type at $CnFB, ProDOS entry via $CnFF with the SmartPort dispatch
at entry+3 - and implements both protocols: the ProDOS block driver
with the drive bit in $43, and a SmartPort dispatch (the full
JSR-with-inline-bytes convention, return address fixed up past the
command and parameter-list bytes) serving STATUS, READ_BLOCK and
WRITE_BLOCK, with block calls translated onto the ProDOS pump.
Squeezing two protocols into one 256-byte page took a tiny two-pass
assembler (Asm) instead of hdd_rom's hand-counted offsets, plus a few
documented economies: the third SmartPort block byte is ignored (800K
is 1600 blocks), $45 is not preserved across transfers, and empty
drives report 0 blocks from ProDOS STATUS.
The boot test settled the plan's open question: the Enhanced //e
Autostart scan accepts the SmartPort signature, so ProDOS 2.4.3 boots
to Bitsy Bye from an 800K .2mg in slot 5 through this firmware.
Config: {"card": "liron", "drive1": ..., "drive2": ...}, any slot 1-7;
configs/enhanced.json now carries one in slot 5. WozBug SLOTS shows
the drives: S5: UniDisk 3.5 (D1 800K, D2 -).
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
9fad92a5ed |
Emulate the Saturn Systems 128K RAM Board in slot 0 (saturn.rs) (#257)
The classic third-party alternative to the Apple Language Card, per
the Saturn Operations Manual ch. 9: 128K as eight 16K banks that all
live at $D000-$FFFF, each bank two 4K banks (A/B) at $D000 plus its
own 8K. Bank 1 speaks the exact Language Card protocol, which is the
card's compatibility story - DOS 3.3, Pascal and VisiCalc treat it as
a 16K card and never notice the other seven banks.
The $C08x decode: A2=0 is the Language Card state machine (A3 now the
4K bank, A0/A1 read/write enables, copied from alc.rs including the
read-twice write-enable - the manual's "PEEK or POKE" prose is looser,
but the LC-compatible semantics are what software relies on); A2=1
selects the 16K bank ($C084-7 are banks 1-4, $C08C-F banks 5-8), with
the board state persisting across switches. Power-up: ROM read, write
protect, bank 1.
The slot-0 plumbing grows from a language_card bool into two::Slot0
(Language / Saturn128 / Empty), mirroring how SlotDevice mirrors the
config cards. The config gains {"card": "saturn128"}, slot 0 only,
][+ only, same rules as the language card; WozBug's SLOTS shows
S0: Saturn 128K (bank n).
Tested: all sixteen $C08x offsets against the manual's table, eight
banks holding independent contents on the bus with the A/B split at
$D000 and the shared per-bank 8K, and the compatibility claim end to
end - DOS 3.3 boots, prints (LOADING INTEGER INTO LANGUAGE CARD),
and INT/FP swap between Integer BASIC in Saturn bank 1 and Applesoft
in ROM.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
7ccd4b369d |
Model slot 0: the ][+ Language Card becomes a configurable card (#256)
The Alc was hardwired into Two::new_2plus, so every ][+ was a 64K
machine and configs/plus.json could not even mention the card it was
supposed to demonstrate. Slot 0 now lives in the machine.slots table
(owner's decision — hardware-faithful, keyed "0"), restricted to
"language" or "empty": the socket has no $Cn00 firmware space, so
nothing else can work there, and the //e rejects "0" outright — its
language card is soldered onto the motherboard.
The literal-table rule now covers slot 0, a deliberate breaking change:
a ][+ config whose slots table omits "0" is a stock 48K machine, with
the motherboard ROM directly at $D000-$FFFF and slot 0's DEVSEL range
as unmapped as any other empty slot's. The default table (and the
--set materialization) gains "0": {"card": "language"}, so bare
command lines keep the classic 64K build and
--set machine:slots:0:card=empty
is the 48K opt-out. configs/plus.json now declares the card.
Slot 0 never becomes a SlotDevice: build_machine consumes it as a
language_card flag on Two::new_with_slots, and new_2plus either wires
the Alc as before or maps the ROM straight onto the bus.
Two::language_card() reports the socket and WozBug's SLOTS shows an
S0 line on the ][+. A guard catches the --set plus --model 2e path
that bypasses config validation.
Tested: the $C083/$C083 write-enable sequence changes $D000 with the
card and bounces off ROM without it, and DOS 3.3 boots to the prompt
on the 48K machine — it just skips loading Integer BASIC.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
6a6fd790eb |
Make machine construction slot-table-driven (JSON_CONFIG Phase B) (#252)
* Make machine construction slot-table-driven (JSON_CONFIG Phase B) The machine builders no longer hard-wire the card set — Two::new_* constructs from a slot table, so a config can put any card in any slot: - Dsk/Clk/Hdd decode only the DEVSEL low nibble, so one implementation serves any slot; the clock and hard-disk firmware are generated per slot (clk_rom/hdd_rom patch the unit byte, $Cn page, and port operands), with golden tests pinning the slot-1/slot-7 images byte-for-byte to the pre-generator statics. The IouE holds owned slot ROM copies so generated firmware can be installed. - Two::new_with_slots builds up to three Disk ][ controllers (the classic maximum), one Thunderclock, any number of hard drives, and empty slots (reading $00, which the Autostart scan skips). new/ new_with_aux delegate with the default table, so the whole existing suite runs unchanged. Boot stays the real Autostart ROM slot scan — highest populated slot wins, as on hardware; no config boot hint. - The Phase A layout gate in config validation is replaced by multiplicity rules; machine.slots is now optional (absent = the default layout, present = literal). --drive1/--drive2/--hdd became slot 6/7 sugar merged into the table, preserving CLI-over-config precedence. Drive lights OR across controllers; drag-drop targets the boot controller. - New ewm/tests/two_slots.rs: controller independence at the bus level, boot-scan order, empty slots falling through to BASIC, a clock in slot 2 surviving a full ProDOS 2.4.3 boot, and hard drives in two slots serving distinct images. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Add ready-made machine configs for the classic setups configs/plus.json is the Apple ][+ (language card built in): green monitor, Disk ][ in slot 6, nothing else. configs/enhanced.json is the Enhanced //e (65C02 inherent to the model): extended 80-column 64K card in the aux slot, Disk ][ in slot 6, color monitor. Neither names a disk — the --drive1/--drive2 flags merge into the config's slot 6 card. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Replace the drive flags with generic --set config overrides --set <key>=<value> overrides any config value by colon-separated key path (--set machine:slots:6:drive1=game.dsk, --set display:monitor= amber); the --drive1/--drive2/--hdd sugar flags are removed. Config sources now layer left-to-right into one JSON document: --config files load through the typed path (per-file validation and relative-path resolution intact) and serialize back to JSON — the config types gained serde::Serialize, fronting Phase C — while --set mutates the document directly; one typed conversion + validation runs at the end. Deep-merge rules: objects recurse, null/empty-array overlays are no-ops, and objects whose "card" discriminators differ replace wholesale. A --set entering machine:slots on a slotless document materializes the default table first, so a bare --set machine:slots:6:drive1=x behaves exactly like the removed --drive1. Values parse as JSON when they are JSON (numbers, booleans, whole objects — the one-line --hdd replacement) and as plain strings otherwise. The boo-menu launcher speaks --set for dropped media; README and the notes record the new syntax and the multi-file deep-merge semantic. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Fix CI: point the full.json fixture at a committed disk image The disks directory is gitignored with only four images force-added; "Total Replay v6.0.1.hdv" exists only in local working trees, so the two_config.rs existence assertion failed on CI (and has kept main red since #251 merged). The fixture's slot 7 hard drive now uses the committed ProDOS_2_4_3.po block image instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
e83139331e |
Add JSON machine configuration via --config (JSON_CONFIG Phase A)
A whole machine can now be described in one JSON file and started with `ewm two --config myemulator.json`: - ewm/src/config.rs: serde types mirroring the schema; load() parses, validates semantically (Phase A accepts only today's buildable layout: slot 1 thunderclock, slot 6 diskii, slot 7 harddrive, any aux card), and resolves relative paths against the config file's directory. - --config in parse_options via a two-pass parse: pass 1 seeds Options from the file, pass 2 is the unchanged flag loop — so explicitly given CLI flags override config values structurally. - Config-only settings cpu.speed and input.controller wire to the existing palette mechanisms at startup (including the audio cycle-to-sample pairing and a preferred-gamepad name scan). - schema/ewm-config.schema.json: draft 2020-12, derived from the config structs by a golden-file test (schemars is a dev-dependency only; regenerate with EWM_UPDATE_SCHEMA=1 cargo test -p ewm). - Tests: schema lockstep, parse/validation/rejection messages, relative-path resolution, CLI-overrides-config precedence, and a boot gate (a config naming the DOS 3.3 disk boots to the ] prompt exactly like --drive1 does). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
6f7a79c9ad |
Add the JSON machine-configuration plan (notes/JSON_CONFIG.md)
Plan for `ewm two --config myemulator.json`, per the owner's decisions: JSON over TOML (better supported); a committed JSON Schema (schema/ewm-config.schema.json, derived from the serde structs via schemars and locked by a round-trip test) covering everything the CLI and Command Palette configure today -- model, drives, hdd, aux card, monitor style, scanlines, boot delay, fps, memory regions, the palette's accelerator speeds and controller pick; plus configurable slots 1-7 (diskii with drive images, harddrive, thunderclock, empty -- multiple Disk ][ controllers legal in the schema). CLI options stay; only --config is added, with explicit flags overriding config values, and relative paths resolving against the config file's directory (the property .ewmachine bundles need). Three phases: A = --config + schema + today's layout expressible (other layouts error clearly); B = table-driven machine construction for real slot flexibility (multiple controllers at $C080+slot*16 with per-slot P5 ROMs, empty slots, boot-scan order); C = palette "save machine configuration" round-tripping into the .ewmachine story. MAC_APP.md Phase 3 rewritten TOML->JSON; the IDEAS.md bullet now points here. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |