Build Log

> What shipped and when.

Reverse-chronological record of the NetISA build. Subscribe via RSS or email to get updates when they ship.

HEARO v1.5.1

Tagged at v1.5.1. Same-day patch release that closes out the three deferred follow-ups from v1.5.0’s release notes: full LHA depacker for YM5 / YM6, FX:N coverage extended from STM/FAR/ULT to MOD/S3M/XM/IT/MTM/669, and tone-portamento-without-instrument fallback fixtures (FXSTM5 / FXFAR5 / FXULT6) added to the wav-matrix.

The LHA piece was the meaningful chunk. Almost every YM file in the wild ships -lh5- compressed (8 KB sliding-window LZSS with static Huffman codes for both alphabets), so v1.5.0’s YM3-only loader effectively meant “no real YM files play.” v1.5.1 implements the LHA level-0/1 header parser plus the LH5 algorithm from scratch (~370 LOC; read from the Yoshizaki LHA spec, MIT-clean). On top of that, the AY loader picks up YM5 / YM6 stream support — richer header, 16 registers per frame versus YM3’s 14, with column-major OR row-major layout depending on the A_STREAMINTERLEAVED attr bit.

Validation: two new wav-matrix cells (LH0 stored wrap of an existing YM3 file, LH5 literal-only encoded wrap of a synthetic YM5 stream) both pass deterministic on 86Box. The LZ back-reference path of the LH5 decoder is verified end-to-end with an additional 5-byte ABABA round-trip fixture decoded by a Python port of the C decoder.

FX:N counter: the v1.5.0 badge only lit up for the post-v1.4 trackers (STM / FAR / ULT) where per-channel modulator state was already memorized for the per-tick effects pump. v1.5.1 adds *_active_fx accessors to the older trackers, surfacing the badge for tone porta + vibrato + tremolo + arpeggio + note-delay on MOD, the equivalent on S3M / IT, and the slim slice of effects MTM / XM / 669 actually pump. XM and 669 surface only what their MVP engines model so the badge is honest rather than misleading.

33 wav-matrix cells now PASS deterministic (was 30 in v1.5.0); HEARO.EXE grew from 231 KB to 235 KB. Real-iron validation on the 486 DX-2 + Vibra 16S remains the v1.6 gate. Release notes at dl.barelybooting.com.

HEARO v1.5.0

Tagged at v1.5.0. Six new decoders (CMF, HSC, STM, RAD v1, FAR, ULT), an AY-3-8910 / YM3 chip-music player with streaming synth, a full per-tick effects pump on the post-v1.4 trackers (STM / FAR / ULT) covering sequencer control, volume slides, pitch slides, tone portamento, vibrato, tremolo, sample offset, and arpeggio, an animated splash intro with OPL2 fanfare, three new themes, and a UI catch-up that makes every shipped feature surface in the runtime UI rather than only in the README.

Trigger-clear semantics on the new tracker effects match MOD / S3M convention so modulators don’t bleed across notes; tone portamento with an empty instrument column falls back to the last-triggered sample, matching how real S3M music writes the effect.

UI surface caught up across four phases: live status-bar indicators ([EQ] [REV:Cathedral] [Karaoke] [V:Spec]), format badge with FX:N counter on the now-playing pane, stereo-separation picker promoted from env-var-only into Settings, browser empty-state guidance, now-playing stopped-state hints, boot-screen “Try: ...” tip line, and three new built-in themes (Borland Blue, Norton Commander, Tokyo Night) bringing the total to 9 + user-defined.

320CDT YMF715 research dossier resolves the 2026-04-29 “chip is invisible” finding (it’s EEPROMless ISA-PnP Sleep state, not a power gate); user workaround is SETYMF /FORCEPNP /INITONLY in AUTOEXEC.BAT, native wake staged behind HEARO_FORCE_OPL3SA_WAKE=1 for iron-test calibration.

Pre-release quality gate: three rounds across five chunks (tracker effects, audio infrastructure, UI, intro splash, whisper + boot orchestration) caught and fixed 22 issues — 3 Critical + 19 Important. The Criticals would have shipped silently: whisper_check clobbered the wake hint before the first frame rendered, intro reveal flashed the full logo on its first 4 frames due to a u8 underflow, and --no-intro cmdline opt-out parsed under the wrong key. 30/30 wav-matrix cells PASS deterministic post-fix.

Real-iron validation on the 486 DX-2 + Vibra 16S is the next gate. The 320CDT path uses the SETYMF user workaround until the native EEPROMless wake gets iron-test calibration. Release notes at dl.barelybooting.com.

HEARO: UI audit closes the loop on Phase 1-4 wiring

HEARO main UI playing FXSTM2.STM with all status indicators active. Top-right of the now-playing pane shows '[STM FX:1]' format badge in bright green confirming 1 channel is running effects. Bottom status bar shows '[EQ] [REV] [Karaoke] [V:Spec]' indicators stacked on the right.
All four status-bar indicators + format badge + FX:N counter live in one frame. Status: [EQ] [REV] [Karaoke] [V:Spec]; format badge: [STM FX:1] (1 channel running effects, brightened to ROLE_STATE_OK).
HEARO main UI rendered in the new Tokyo Night theme. Background shifts to muted dark purple, accents in soft pink and indigo, spectrum visualizer recoloured.
Tokyo Night theme verifies Phase 4 (3 new built-in palettes joined the existing 6).

Pass over everything we shipped today to make sure it has a UI surface and the surface is wired right. Two real gaps found and fixed:

  1. 320CDT wake hint was broken UX. It used printf which would scroll text into HEARO’s text-mode boot screen and then get clobbered by the first UI redraw. New flow: wake_opl3sa3_probe writes a const-pointer hint to wake.c’s slot, hearo.c forwards it into the whisper system after audio init, the existing UI status-bar renderer shows it right-aligned. Layering stays clean — audio/ doesn’t depend on unlock/, the wake registry is the bridge.
  2. No surface for active effects. The format badge brightened for “tracker with effects supported” but didn’t show whether effects were firing right now. Added per-format *_active_fx() helpers walking each channel’s vibrato / porta / slide state, plus a decode_active_effect_count(handle) dispatcher. Now-playing badge widens from [STM] to [STM FX:3] with ROLE_STATE_OK colour when effects are actually active, so the per-tick pump becomes visible rather than inferred from the format name.

Drive-by enhancement: HEARO_AUTOPLAY=<path> env var loads and starts a file before ui_run. Useful for one-line BAT launches and (more importantly here) for the screenshot harness which needs a deterministic playing state without fighting the BIOS keyboard buffer prefill. Matches the existing HEARO_INTRO / HEARO_REVERB / HEARO_SEPARATION idiom.

DGROUP gotcha caught and fixed: the initial wake-hint storage as a 160-byte static buffer tipped DGROUP over 64K by 112 bytes. Refactored to a const-pointer slot so the actual text lives in CODE not DGROUP. Same lesson as the v1.4-E fire-viz field that prompted the original DGROUP-overflow watch in project_hearo. Source archive.

HEARO: sample offset + arpeggio on STM / ULT

Two more per-effect bolt-ons on the post-v1.4 trackers, both small additions on top of yesterday’s per-tick infrastructure.

Sample offset (STM Oxx, ULT 9xx): when a row triggers a sample with this effect, jump param * 256 frames into the sample data instead of starting from byte 0. This is how almost every drum loop in the tracker corpus slices breakbeats into different attack points; the mixer already had a mixer_set_position seek helper for this exact use case, so wiring is one line per format. Out-of-range offsets land on the mixer’s own deactivate-channel guard, matching the MOD/S3M behaviour.

Arpeggio (STM Jxx): the param’s xy nibbles are semitone offsets cycled per tick. Tick 0 plays the base note, tick 1 plays base+x semitones, tick 2 plays base+y semitones, then back to base on tick 3. Implemented via the existing hearo_arp_freq_mul Q16 multiplier table in decode.c; per-tick the carrier frequency multiplies by arp_freq_mul[semi] >> 16. Composes with vibrato and slides because the arpeggio multiplier is applied to the carrier BEFORE vibrato is added on top, same architecture as everything else in process_tick.

Two new fixtures: FXSTM4 (Oxx + Jxx) and FXULT5 (9xx). Wav-matrix grew from 28 to 30 cells; all 28 prior baselines unchanged. Source archive.

HEARO: tone portamento + vibrato + tremolo on STM / FAR / ULT

The next layer of musical expression landed on the post-v1.4 trackers. Tone portamento on all three (STM Gxx, FAR 3xx, ULT 3xx): a fresh note in the cell becomes a slide TARGET rather than a re-trigger, so the currently playing sample keeps its position and just shifts pitch. The slide rate is memorized across rows in S3M style, so a Gxx with param=0 reuses the previous rate. Vibrato on all three (STM Hxx, FAR 5xx / 8xx — FAR splits vibrato across two effect codes — ULT 4xx): xy parameter sets speed and depth; per-tick a Q8 sine table modulates the carrier frequency without persisting, so vibrato + slide / vibrato + porta combos work without the vibrato detuning the underlying motion. Tremolo on ULT (7xx): same shape as vibrato but modulating volume.

Per-channel state grew by 5 fields (porta_target, porta_step, vib_speed, vib_depth, vib_phase; ULT also has trem_speed/depth/phase). The carrier vs modulation split keeps chans.freq as the underlying value the slide / porta machinery walks, with vibrato added on top in process_tick’s output composition. Same approach for volume + tremolo on ULT.

Three new fixtures: FXSTM3 (Gxx + Hxx), FXULT4 (3xx + 4xx + 7xx), FXFAR4 (3xx + 5xx + 8xx). All three trigger the new code paths against fresh baselines; the original effect-less and earlier-fixture baselines all stable, so the cell-layout is unchanged. Wav-matrix grew from 25 to 28 cells.

Sample-offset, retrigger, fine slides, and arpeggio remain unimplemented — they’re per-effect bolt-ons rather than infrastructure work now that the per-tick pump is in place. Source archive.

HEARO: animated splash intro before the boot screen

HEARO splash intro animation: HEARO logo wipes in left-to-right with a CGA-snow flicker zone behind the wavefront, then settles to the full logo plus 'NetISA Music Player' subtitle, then dissolves into the boot screen.
The full intro animation captured in DOSBox-X via ffmpeg gdigrab and palette-quantised to GIF. ~3.5 s of reveal → hold → dissolve → boot-screen handoff. The OPL2 fanfare doesn’t survive the GIF round-trip; reach for the binary on real iron or a DOSBox-X build for the audio.

Inspired by the Apogee / Sierra studio splashes that set the tone before a single menu loaded, HEARO now plays a brief animated intro before its diagnostic boot screen lands.

Four phases in roughly 3.5 seconds. Reveal: a column-by-column wipe walks left-to-right across the HEARO logo, with a CGA-snow flicker zone behind the wavefront. The flicker is a simple LCG over a three-attribute palette (dim / mid / bright), evoking the CRT-warm-up feel without needing per-pixel work in Mode 13h. Fanfare: a three-note OPL2 ascending arpeggio (C5 / G5 / C6) keys on at 25%, 50%, and 75% through the reveal so the music and the visual land together. Hold: full-intensity logo with a “NetISA Music Player” subtitle held for one second. Dissolve: the VGA palette fades toward black via vsync-gated AX=1010h DAC writes across ten steps, then control returns to the boot screen via theme_apply which re-pushes the user’s saved palette.

Any keypress skips. --no-intro, HEARO_INTRO=off, or --safe on the command line opts out entirely so first-time problem diagnosis isn’t gated behind theatre. Hardware with no OPL chip (PC Speaker only) still gets the visual; the OPL writes just hit a non-existent port and silently drop. Implementation is text-mode CP437 + INT 10h palette helpers, around 250 lines in src/ui/intro.c, plumbed into the boot sequence between unlock evaluation and boot_screen_render. Source archive.

HEARO: UI catch-up after the post-v1.4 feature blitz

The decoders, the per-tick effects pump, the AY shape regression, the volume slides — everything that landed earlier today added depth without adding surface area. Four UI passes brought the surface back in line with the engine.

Phase 1 visibility. The bottom status bar grew live indicators that had no surface area before: [EQ] when the equalizer is active, [REV:Cathedral] when reverb is on (preset name shown), [Wide] / [Karaoke] when stereo separation is anything other than full, and [V:Plasma] for whichever visualizer the V key has cycled to. The now-playing pane gained a format badge in the panel header ([STM], [AY/YM3]); the badge brightens when the active format is a tracker whose effect column is being honoured, dims for non-effect formats. New helpers in the engine (mixer_get_separation, reverb_get_preset, playback_format, decode_format_name, decode_format_has_effects) keep the status code from reaching past public APIs into internals.

Phase 2 promoted env-var features. The stereo-separation knob was previously reachable only by setting HEARO_SEPARATION=narrow before launch; it now lives at Settings → Stereo mode with a live picker for Full / Narrow / Mono / Wide / Karaoke. Reverb and EQ presets already had pickers; the new status bar makes their state visible.

Phase 3 tightened discoverability. The boot screen got a tip line below the “N features unlocked” count: “Try: Alt-S Settings, V cycles visualizers, F2 Unlock matrix, Alt-H Help”. The browser’s empty state replaced one line of “no music files” with actionable hints (Backspace to parent, Alt-F to open by path, filter status when an extension filter is active). The now-playing pane’s stopped state replaced the empty artist / album lines with concrete next-step prompts.

Phase 4 added curated themes. Three new built-in palettes: Borland Blue (Turbo Pascal IDE), Norton Commander (file-manager cyan-on-blue), and Tokyo Night (modern dark purple). The theme picker already had live preview, so the new palettes apply instantly as you arrow through them. Built-in count is now 9 (plus the user-defined slot). Source archive.

HEARO: per-tick effects pump on STM / FAR / ULT plus AY shape regression

STM, FAR, and ULT got the rest of their first-pass effect coverage. STM picked up Dxx (volume slide) and Exx / Fxx (pitch slides). FAR picked up Dxx / Exx (fine tempo deltas) and 6xx / 7xx (volume slide up / down) — FAR’s effect parameter is only 4 bits so the slide rates are necessarily small. ULT picked up 1xx / 2xx (portamento up / down via MOD-style cmd numbering, not the S3M-style Exx / Fxx letters), Axx (volume slide), and Cxx (set volume) on either effect column.

The architectural piece is a process_tick() per format. The advance loop now runs process_row() on row ticks (tick 0) and process_tick() on every non-row tick; the latter walks each channel and applies persisted volume / frequency deltas through the mixer in the same scaling space the row processor uses. Slides memorize, so a Dxx with both nibbles zero keeps the previous slide rate (S3M-style). ULT cells widened to 7 bytes to carry both effect columns natively; STM cells stayed at 5; FAR cells stayed at 4 because FAR’s effect parameter is already 4 bits, so cmd<<4 | param packs into a single byte.

Tone portamento, vibrato, and tremolo are deferred — they need memorized per-channel target state (Gxx remembers a target note, Hxx remembers depth and speed) that the dispatch path doesn’t yet carry. Adding them is a per-effect bolt-on rather than a refactor now that the per-tick infrastructure exists.

The wav-matrix grew from 19 to 25 cells. Six new fixtures landed: FXULT2 (Cxx column-2 volume), FXFAR2 (Dxx / Exx fine tempo), FXSTM2 (volume + pitch slides), FXFAR3 (volume slides), FXULT3 (volume + pitch slides), and AYSHAPES — an AY-3-8910 fixture that cycles all 8 functional envelope shapes (0x08..0x0F) over its 4-second window. AYFULL only exercises shape 0x0A; AYSHAPES catches shape-specific regressions that AYFULL would silently miss. Twenty-five-of-twenty-five cells PASS deterministic across reruns. Source archive.

HEARO: STM, FAR, ULT sequencer-control effects

Three of the post-v1.4 trackers got their first effects pass. STM honors Axx (set speed), Bxx (position jump), and Cxx (pattern break). FAR honors Fxx (set tempo) — FAR has no position-jump or pattern-break effects in its set; song flow goes through the order list’s restart_pos field instead. ULT honors Bxx, Dxx (pattern break), and Fxx; ULT cells encode two effect columns per row, and v1.x reads the first column only (the high nibble of the effect byte). Volume slides and pitch effects on these formats stay deferred — they need a per-tick state machine that the v1.x dispatch path doesn’t yet have.

STM and ULT cells widened from 4 to 5 bytes to carry both the effect command and parameter without packing tricks; FAR’s already packed cmd<<4 | param into a single byte (FAR’s parameter is 4 bits wide), so its cell layout was unchanged. Each format gets a second wav-matrix cell — FXSTM, FXFAR, FXULT — whose fixture exercises the new code paths against fresh per-cell baselines. The original effect-less TONE.* fixtures still hash to their original baselines, which is what made it safe to land the cell-layout refactor: no listener-style verification needed when a deterministic byte stream is unchanged.

The STM fixture didn’t exist as a generator script — the existing TONE.STM was a binary with no companion .ps1 in the repo — so this work also added scripts/make-fxstm.ps1, make-fxfar.ps1, and make-fxult.ps1, building on the format references already in src/decode/{stm,far,ult}.c. Authored from scratch per HEARO conventions; OpenMPT’s loaders were read for byte layout but no code was copied. Source archive.

HEARO: six new decoders, AY chip music, audio regression with peak floor

Six format decoders shipped on top of v1.4: CMF (Creative Music File, OPL2 + embedded MIDI), HSC (HSC AdLib Composer), STM (Scream Tracker 2), RAD v1 (Reality AdLib Tracker), FAR (Farandole Composer, 16-channel PCM at C5 = 16726 Hz), and ULT (UltraTracker V004 with column-major RLE patterns). Plus an AY-3-8910 / YM3 register-stream player covering the ZX Spectrum 128 / Amstrad CPC / Atari ST corpus: 3 tone channels, 17-bit Galois LFSR noise, all 8 functional envelope shapes, per-channel envelope-follow, and a streaming synth that lifts the v1 ~2.97-second pre-render cap. The cap was set by Watcom 16-bit large-model _fmalloc’s 16-bit size_t; v2 generates samples into a 2 KB ring buffer just ahead of the mixer’s read head, so songs of any length play. Total decoder count is now 16.

Each decoder ships with a per-format end-to-end audio regression. A test fixture plus TESTPLAY.EXE packs into an 86Box VM, the /W render path drives the decoder and mixer with FPU forced off and writes a deterministic byte stream, then the harness mounts the VHD and hashes the rendered WAV against per-cell baselines. PCM trackers hash the mixer output; OPL register-stream formats route through Nuked-OPL3 host synthesis so the hash captures chip-write ordering rather than just the raw register log; AY uses HEARO’s own software synth. 16/16 cells PASS deterministic across reruns.

Picked up a real bug in regression coverage: my first AY build allocated 88200 bytes for a 4-second pre-render, hit the SAFE_FMALLOC_MAX cap, returned NULL, and ay_play_init silently bailed. The hash of the all-zero output was perfectly deterministic. The harness then added a peak-amplitude floor so a baseline regenerated against silence doesn’t look healthy any more. Default threshold is intentionally low (16 of 32767, just enough to catch all-zero) so quiet content can still pass.

Drive-by cleanup: shared tracker tables (vibrato sine, arpeggio Q16 multipliers) moved from static const in decode.h (which gave every TU that included the header its own private copy plus a W202 “defined but not referenced” warning when it didn’t use one) to extern const declarations backed by single definitions in decode.c. Saves ~128 bytes in TESTPLAY.EXE and clears the W202 noise from the build log.

One Watcom Makefile gotcha got captured for next time: the per-TU rules don’t list header dependencies, so editing ay.h to add fields without removing build/decode.obj first leaves decode.c linked against the old sizeof(ay_song_t). The resulting heap corruption surfaced as TESTPLAY hanging silently before the C runtime could flush stdout. Documented as an in-tree memory so the next decoder header change doesn’t step on the same trap.

LHA-packed YM5 (every YM file in the wild) is the next AY follow-up. The recognizer accepts raw YM3 today; YM5 needs an LHA / LH5 depacker, which is its own session of work. Source archive.

HEARO v1.3.0: engine fidelity, visualizers, metadata

Tagged v1.3.0 on the HEARO repo. The demoscene-polish milestone: full XM and IT envelope coverage, IT lowpass filter, NNAs, three visualizer modes, and real now-playing metadata. Cluster summary on the project page.

Engine fidelity (clusters B–E). XM pan envelope wires through to mixer pan; IT instrument-mode files now run vol + pan + pitch envelopes plus the Z40…Z7F lowpass filter; NNAs are honored via a ghost-voice pool that lets a logical channel’s previous note keep ringing while a new note triggers. The lowpass alpha is computed at the active mixer rate so the same cutoff Hz plays the same audibly regardless of output rate.

Visualizers (cluster A). The bottom-right pane cycles between Spectrum / VU meter / frequency Bars via the V key. Pure UI-side addition; no audio engine touched. Pattern observed from rajeevt2001/Dos-Music-Player.

Metadata (cluster F). WAV LIST/INFO sub-chunks (INAM/IART/IPRD/ICRD), MIDI track-0 sequence-name (FF 03 meta event with proper VLQ event walk), VGM GD3 tag block (UTF-16LE→ASCII lossy decode for track + author EN). The empty-state placeholder strings (“Axel F / Harold Faltermeyer / Beverly Hills Cop OST” demo defaults) were neutralized.

Hardening (same day). Twelve issues closed from external code reviews (mixer loop-bound clamping, safe_fmalloc 64K policy, irq_save/irq_restore helpers, MOD per-pattern allocation, S3M out-of-bounds note guard, XM short-header zero-init, mixer frequency overflow fix, modal UI audio pump, browser extension sync, S3M packed/stereo flag rejection, etc). Five auplay-inspired SB/DMA improvements (DSP4 IRQ self-detection, INTSTAT-driven IRQ ack with ack-both fallback, DSP_END_DMA + 0xD9/0xDA bug fix, audio_callback returns u16 + EOF wiring, unified DMA register tables).

Code review. Three rounds of iterative review closed clean: 5 → 1 → 0 Critical+Important findings. Round 2 caught a u32 overflow in IT pitch-envelope frequency multiplication on high notes; fixed via a split-shift form ((base_freq >> 8) * mul >> 8) keeping each intermediate under 2^31.

DOSBox-X SB16 smoke runs match canonical sentinels (WAV 67584 / MOD 90112 / XM 67584). Real-iron validation on the Toshiba 320CDT 486 dev box pending; bare-DOS YMF715 PnP isolation is v1.4 work. Source archive.

Prior-art writeup: where NetISA fits in the TLS-on-vintage landscape

Spent some time reading what other people have built before deciding the coprocessor design was the right call. The result is a new "Why a coprocessor" section on this site (the NetISA page above), a matching "Why a Coprocessor?" + "Related Projects" section in the README, and a longer ยง1.3 "Prior Art and the TLS Floor" subsection in the architecture spec with the technical detail.

The argument is essentially: the modern internet requires TLS, software TLS on vintage hardware bottoms out somewhere around a 486 (and ships with disabled cert verification and fake entropy to make it fit), Layer-2 WiFi cards like PicoMEM still need TLS to run on the host, and proxies like FrogFind require a second modern machine. NetISA puts an ESP32-S3 with hardware AES/SHA/RSA/ECC, a real TRNG, and 8 MB of PSRAM on the bus and terminates TLS there. An 8088 talks to HTTPS endpoints because it never touches the cryptography.

The architecture-spec Appendix A project index gained PicoMEM, PicoPCMCIA, WinGPT, Crypto Ancienne, AmiSSL, Secure Oldies, SEthernet/30, DOStodon, FrogFind, Browservice, WebOne, and Protoweb. While I was at it, swept the rest of the spec for em-dashes (now zero).

TAKEOVER v1.2.1: bug-fix release

Same-day patch on top of v1.2. Three issues from the post-release code review: (1) the Esc and Enter checks across the engine relied on dual-encoding fallbacks scattered through the codebase; cleaned up behind new IS_KEY_ESC and IS_KEY_ENTER helpers in lib/screen.h and replaced six call sites. (2) The 600 ms F9/F10 audio status flash leaked a Space press into the outer scenario delay (so pressing Space during the flash skipped seconds of intended pacing); handle_audio_key now saves and restores g_skip_delay around the flash. (3) engine_reset now also clears g_skip_delay for symmetry with g_abort_requested.

Compatibility: v1.2 saves and .scn files are unchanged. v1.2.1 is a drop-in replacement. Site captures (title, menu, axiom, fx) refreshed off the v1.2.1 binary; the new status bar (F9:Mus F10:Snd Esc:Quit) and footer (v1.2.1) are visible across them.

Binary: 63,068 bytes (v1.2 was 63,050). Release notes. TAKEOVER project page.

TAKEOVER v1.2: robustness and UX polish

v1.2 of TAKEOVER tagged on the takeover repo. The headline fix: Esc now exits any running scenario. Before this release, the engine’s input loops only handled Enter and the busy-wait inside long delays had no abort path; a user mid-scenario who hit Esc got nothing and had to reboot. v1.2 routes a global abort flag through engine_run that returns cleanly to the menu with a new END_QUIT result.

Other fixes shipped: engine no longer eats keys typed during delays (peek-don’t-drain via a new scr_peekkey in the shared screen lib); F9 and F10 audio toggles work mid-scenario, not just at prompts; climax sequence drains held keys on early exit; parser errors include scenario filename plus line number.

Added: Space skips the current pause or typing animation. F1 brings up an in-scenario key reference overlay (modal, dismisses on any key). F9/F10 mute states persist across runs (TAKEOVER.DAT save schema bumped to v2 with a v1 upgrade path). Status bar updated to advertise Esc.

Binary: 63,050 bytes (v1.1 was 60,492). Compatibility: v1.1 saves load fine and .scn format is unchanged. Release notes. TAKEOVER project page.

CATHODE, DISCORD, and CLAUDE get their own project pages

Three new pages live on barelybooting.com: cathode.html, discord.html, claude.html. Each one follows the same template the CERBERUS and HEARO pages already use: hero figure, status, features, source layout, build instructions, hardware specs, suite context, out-of-scope list, links. Until today these three were one-line mentions on the NetISA page. They now stand on their own.

Site nav updated across all 12 HTML files with the three new entries between CHIME and TAKEOVER. Sitemap and the index.html intro paragraph + links list updated to point at the new pages. The three apps are still under active development, so the pages will move with them.

CATHODE project page. DISCORD project page. CLAUDE project page.

CATHODE, DISCORD, and CLAUDE migrate to the suite-app pattern

All three older DOS apps now live at the top level of the NetISA repo, matching the shape HEARO and CHIME ship: src/ for sources, own Makefile with a LIB_OBJS split, README.md, .gitignore, scripts/ for the screenshot toolkit. Shared platform code (screen.c, netisa.h/c, netisa_stub.c) lives in a new /lib/ at the repo root that the apps reference via -i=../lib and link as object files.

What moved. dos/cathode/cathode/src/, dos/discord/discord/src/, dos/claude/claude/src/. dos/lib/ stays in place for the launcher and TSR; identical content lives at /lib/ for the migrated apps. Source files moved via git mv to preserve history. dos/Makefile trimmed back to just the TSR + launcher.

Build sizes match the previous dos/Makefile builds. CATHODE.EXE 40 KB, DISCORD.EXE 37 KB, CLAUDE.EXE 30 KB. Toolchain unchanged: 8088 floor (-0 -fpi -ms), max optimisation, DOS target. CATHODE builds two variants: CATHODE.EXE (stub backend, runs on any DOS) and CATHODE_HW.EXE (real INT 63h backend, requires NetISA card).

CATHODE start page in DOSBox-X. Green title bar at top. CATHODE block-letter logo in green-on-black. Subtitle 'A text-mode web browser for DOS. Sort of.' Powered by NetISA line. Quick Links section header. Status bar at bottom with shortcuts: Ln 1/36, Links:6, F5:Rel, Tab:Link, Ctrl+F:Find, Esc:Quit.
CATHODE start page.
DISCORD chat client in DOSBox-X. Channel list on the left with #general highlighted, plus #hardware, #builds (5), #software, #marketplace (2), #off-topic, #help (1), #showcase. Message thread on the right with timestamps and colored usernames: ChipCollector, BarelyBooting, VintageNerd, PCBWizard, SocketSlinger. Type a message field at bottom, status bar with Tab:Focus, Ctrl+F:Find, F1:Help, Ctrl+Q:Quit.
DISCORD v2 with the Retro Computing Hub server populated by stub data.
CLAUDE splash screen. Big green block-letter CLAUDE logo. 'for DOS' subtitle below. '640K of RAM. Billions of parameters. One ISA slot.' tagline in cyan. CLAUDE.EXE v0.1 | NetISA version line. 'Press any key to begin...' prompt in yellow.
CLAUDE splash with the NetISA tagline.

NetISA repo at BarelyBooting/netisa. The top-level layout is now: lib/ (shared) and one directory per suite app (hearo/, chime/, cathode/, discord/, claude/). dos/ is just the TSR (NETISA.COM) and the launcher (NETISA.EXE / NETISA_HW.EXE). Four suite apps remain unbuilt: COURIER, CRATE, KIOSK, RADIO.

HEARO + CHIME tagged v1.0.0

v1.0.0 of both apps tagged on the NetISA repo. Captured screenshots refreshed with the post-fix detection. The remaining v1.0 detection bugs visible in the previous tier captures are now fixed: SB Pro 2 is detected, the SB16 ASP false positive is gone, and the GUS-at-SB-base phantom is gone.

Detection fixes. probe_sb's OPL3 check was reading port base+8 instead of base+0, so SB Pro 2 vs SB Pro 1 disambiguation never fired correctly; same off-by-8 in the AWE32 vs AWE64 check. sb_asp_present switched from a loose mixer-index 0x83 read to DSP command 04h (ASP get version), which times out cleanly on plain SB16. probe_gus no longer blind-probes when ULTRASND is unset, and refuses to claim the SB base. cpu_pit_loop calibration switched from count/1000 to count/100, an order of magnitude closer to truth on real iron (still rough under DOSBox-X due to PIT-2 emulation timing).

HEARO boot screen on the Maximum tier configuration: Pentium / SVGA 1024 / 32 MB / SB16 + MPU-401. Intel Pentium @ 8 MHz / integrated x87 (Intel) / 34 features unlocked, including the Pentium-class Convolution reverb, Particle system, and Wireframe 3D.
Maximum tier (Pentium / SVGA / SB16 / MPU). 34 features.

CHIME mode captures. Two extra CHIME screenshots covering the /DRYRUN and /HELP flows. /DRYRUN shows the report and the "DOS clock NOT set" tail. /HELP documents the full flag list as one captured frame.

Tagged v1.0.0 on the NetISA repo. HEARO project page, CHIME project page.

HEARO and CHIME compile clean. First screenshots.

Open Watcom V2 installed. First wmake on HEARO surfaced the expected handful of issues, all fixed in one sweep. Same for CHIME. Both binaries now build clean and run under DOSBox-X.

HEARO boot screen captured in DOSBox-X. CP437 box-drawing HEARO logo at top, hardware report below with right-justified ENABLED in green for the 80486DX FPU and SVGA, indented unlock list of 16 FPU-gated visualizers and DSP features, plus a memory and library line. Bottom: Your machine has unlocked 24 features. Press any key to begin.
HEARO boot screen, first capture. 486DX / SVGA / synthetic-stub detection. 24 of 67 features unlocked.

Build fixes. DGROUP overflowed 64 K by ~10 KB because of all the unlock-rule strings, menu definitions, and status-output format strings; -zc in the Makefile pushes const data into the code segment and DGROUP fits. bipartite_sin had a sign-flip overflow when both A and B table entries were near the s16 max, plus a residual-table design that was wrong; replaced with linear interpolation between adjacent coarse entries plus a clamp. Test results are now ~13 bits sin precision against 1024 reference samples. Removed a duplicate dos_get_date stub from detect.c, fixed a nested-comment in probes.c (*.c patterns inside a /* ... */ block confused the lexer), trimmed a couple of unused statics. Test programs were pulling in hearo.obj's main(); split the Makefile into LIB_OBJS + the main-bearing object so test programs link only the library half.

HEARO_NOASM scope narrowed. The flag was originally a single gate covering every Watcom-specific facility (display VRAM writes, INT 21h calls, BIOS tick, file system probes, inline asm). When -dHEARO_NOASM was on, the boot screen wrote to an in-memory emu_buffer instead of real B800:0000 video memory, so the first capture attempt showed only the autoexec output. Display, DOS calls, and file-system access now always use the real Watcom path. HEARO_NOASM only gates the inline-asm probes in src/platform/probes.c. v1.0.1 will drop that gate too.

Test results from the headless DOSBox-X 386 conf. TESTCORD: precision climbs monotonically with iteration count from 8 to 24 as expected; max-err-sin goes from 475 at 8 iters down to 5 at 16 iters which is the Q16.16 fixed-point precision floor. TESTBIP: max err 3 across 1024 sample points (~13 bits accuracy). TESTQUIR: naive 32-bit sum overflows to -462758661, the quire low word matches naive but the high word holds the overflow carry across the full 256-bit state. TESTDET: correctly identifies a Workstation-tier profile (486DX + integrated FPU + VESA SVGA + 16 MB XMS) and produces a stable FNV-1a fingerprint. HEARO /VERSION and HEARO /UNLOCKS print the expected matrix. CHIME TESTTIME: 21 of 21 parser and stub-query assertions green.

CHIME 1.0 running in DOSBox-X with the /STUBNET flag. White-on-black terminal output: CHIME 1.0.0; NetISA: stub mode; Server: time.cloudflare.com (HTTPS HEAD); Time: 2026-04-25 14:30:42 UTC; Now: 2026-04-25 08:06:28 (DOS clock); Delta: +23054 seconds; Write to DOS clock? [Y/n].
CHIME report + confirmation prompt. /STUBNET returns a canned timestamp so the captured frame is deterministic.

Screenshot toolkit. hearo/scripts/screenshot.ps1 drives DOSBox-X to the boot screen and saves a PNG via the existing capture.ps1 WinAPI tool. Same toolkit shipped in chime/scripts/. The -fastlaunch flag skips the BIOS logo and welcome banner that otherwise overlay the captured frame. Both screenshots reproduce deterministically against the synthetic-stub backends regardless of host time or hardware.

What is next: drop -dHEARO_NOASM from the Makefile and exercise the Watcom inline-asm probes in src/platform/probes.c under DOSBox-X. After that, run on the bench iron. HEARO project page, CHIME project page, source archive.

CHIME: foundation phase done

CHIME is the second NetISA suite app to leave the design phase, landing the same day as HEARO. Smaller scope. Asks the network for the current time, sets the DOS clock.

Why not SNTP: NTP/SNTP runs over UDP. The NetISA TSR INT 63h API as of v1.0 exposes TLS sessions and plaintext TCP, no UDP yet. So v1.0 talks HTTPS HEAD to time-anchor URLs (time.cloudflare.com, time.google.com, worldtimeapi.org) and parses the response Date: header per RFC 7231. One-second precision. Real SNTP arrives in v1.1 when NetISA exposes UDP; NTS in v1.2.

What landed in chime/ (18 files): src/chime.{c,h} main flow; src/cmdline with /AUTO, /DRYRUN, /SAFE, /STUBNET, /SERVER=, /PATH=, /PORT=, /MODE=, /TZ=; src/config for CHIME.CFG read/write; src/netisa_api wrapping CHIME’s subset of the INT 63h API plus a stub backend; src/timesrc with the HTTP Date and worldtime JSON parsers; src/dos_clock for INT 21h AH=2Bh/2Dh date+time set plus CMOS RTC write-through via ports 0x70/0x71; src/ui_status for terminal output and the [Y/n] prompt; test/testtime for parser roundtrip and stub query. Plus the design doc at docs/chime-design.md.

Setting the clock: DOS exposes three layers. CMOS RTC at I/O ports 0x70/0x71 (persists). BIOS tick at 0040:006C, 18.2 Hz (resets on reboot). DOS internal date/time via INT 21h AH=2Ah/2Ch get and AH=2Bh/2Dh set. Modern DOS propagates the INT 21h set to CMOS; older DOS does not, and the next reboot loses the change. CHIME does a direct CMOS write after the INT 21h call as backstop. Timezone is fixed-offset (/TZ=-08:00). DST is not handled.

Same suite template HEARO ships: cmdline grammar, INI config shape, MIT, Watcom wmake. Project page, source archive, design doc.

HEARO: foundation phase done

HEARO is a music player for IBM PC compatibles (286+, DOS 3.3+) and the next NetISA suite app out of design phase. All eleven spec phases plus three polish items landed in hearo/. The design doc and a 681-line soundcard reference were written first; everything else was scaffolded against those. Not compiled yet; Open Watcom V2 is not installed here.

The design idea: every recognized expansion turns on specific features the user can see. Boot screen names every detected component. Settings panel lists locked features next to the requirement that would unlock them. HEARO.HAL records first-detection dates so the machine accumulates a timeline. Install an FPU, the next boot says it is new since last time. Anniversary date-diff line on the same calendar day in later years.

What landed:

  • Phase 1: scaffold + 313-line design + 681-line soundcard reference.
  • Phase 2: src/hearo.h verbatim per spec.
  • Phase 3: eight-module detection engine. CPU (FLAGS bit walk + CPUID + PIT clock fit). FPU (FNINIT + control-word width + IIT 2C87 + Cyrix FasMath probes). Video (MDA/CGA/Hercules/EGA/VGA/VESA + chipset fingerprint for S3, Cirrus, Trident, Tseng, Paradise, Oak, Chips & Tech). Audio (24 devices with full BLASTER/ULTRASND parsing). Memory (INT 12h + XMS + EMS). NetISA card. Mouse + joystick.
  • Phase 4: data-driven unlock matrix, around 70 rules.
  • Phase 5: boot screen with logo, ENABLED report, unlock count, anniversary line.
  • Phase 6: four-pane text-mode UI (browser, playlist, now-playing, ASCII spectrum) + pull-down menus + Settings panel + Hall viewer.
  • Phase 7: 24-iteration adaptive CORDIC, 4 KB bipartite sin/cos/log/exp tables, 256-bit software quire accumulator.
  • Phase 8: INI config + persistent Hall + dormant-feature whisper.
  • Phase 9: six test programs.
  • Phase 10: Watcom wmake Makefile.
  • Phase 11: main() with /VERSION, /UNLOCKS, /HALL, /SAFE, /STUBNET, /REDETECT, /BENCHMARK, /VIDEO=xxx.

Polish on top: real CP437 box-drawing logo (0xDB blocks + double-line corners 0xC9/0xCD/0xBB/0xC8/0xBC/0xBA) replacing the ASCII placeholder; DOS file browser using _dos_findfirst/_dos_findnext with subdirectory navigation and an 11-extension music-file filter; anniversary date-diff that fires when today’s MM-DD matches the Hall’s first-detection date in past years.

v1.0.1 work staged: src/platform/probes.c with Watcom #pragma aux + int86() bodies for every probe extern, gated behind -dHEARO_NOASM so the asm-free build still compiles. spectrum_feed(s16*, u16) API in place for the eventual decoder. DOSBox-X screenshot toolkit in hearo/scripts/.

Next: install Open Watcom V2, run wmake, fix whatever the first compile surfaces, capture the first screenshot. Hardware bring-up on the NetISA bench is the parallel workstream. Project page, source archive, design doc, soundcard reference.

NetISA parts landed. Every line of the BOM is on the bench.

The last Digikey box arrived. Every part I told myself to order, DigiKey, Amazon, TexElec, the whole list, is now sitting on the bench. Headline item: the TexElec 8 Bit ISA Prototype Card v1.0 (the yellow through-hole prototyping board with the 62-pin ISA edge connector already routed). Around it: ESP32-S3-WROOM-1U-N8R8 modules, ATF1508AS TQFP-100 CPLDs, the passive set (electrolytic caps, ceramic caps, 1% resistors, Schottky and signal diodes), IC sockets, pin headers, a ribbon cable, anti-static bags and foam for everything, and the programmer kit. Enough redundancy that I can burn one or two parts during bring-up without halting.

Photo: NetISA parts arrival on a workbench. A TexElec 8 Bit ISA Prototype Card v1.0 (yellow PCB with ISA edge connector) surrounded by EcoSonic VpCI-125 HP anti-static bags containing electrolytic capacitors, ceramic capacitors, resistors, and diodes, along with DigiKey SCS static shielding bags and a ribbon cable.
Bench photo: TexElec prototype card in the center, passives and ESP32-S3 / CPLD modules in the surrounding anti-static bags.

The software side has been parked here for a while: DOS TSR running, launcher and config UI working, Cathode browsing real pages against a stub backend, Discord v2 running on simulated channels, Claude client talking to the Anthropic API, ESP32 firmware complete, CPLD logic 160 of 160 testbench tests green. All of that was gated on actually having parts. That gate is now open. Real hardware bring-up is finally unblocked and I am fully expecting at least one thing to go wrong immediately. Bench assembly plan: socket the CPLD, wire the ISA edge to the register file, bring up power rails, flash a first firmware image, then attempt a loopback test from DOSBox-X-parity DOS. After that, iterate until it stops breaking (or at least breaks less). Project page, Source archive.

CERBERUS v0.8.1: same-day completion release

Second tag of the day after v0.8.0. v0.8.1 is the completion release: every item the v0.8.0 notes flagged “deferred to 0.8.1” either landed or got documented as hardware-gated with a specific gate. Three milestones, one hotfix, one new lesson.

M1 (precision + output). IEEE-754 edge-case diagnostic: 14 focused edges across FADD / FSUB / FMUL / FDIV / FSQRT exercise signed zero preservation, infinity arithmetic, NaN propagation, 0/0, inf/inf, sqrt(-0). Per-op counters plus aggregate; 14_of_14 on a conformant FPU. FSQRT uses #pragma aux fsqrt to emit the bare instruction rather than libc sqrt(), which clamps domain errors to 0.0 instead of propagating NaN. /CSV output mode writes a sibling CSV file with RFC 4180 minimal quoting (fields containing comma, quote, CR, or LF get quoted with embedded quotes doubled). Schema: key,value,confidence,verdict.

M2 (cache depth). L1 pointer-chase latency probe reinterprets the existing 2 KB shared FAR buffer as a 1024-slot unsigned int array, initializes a coprime-step (67) chain, primes the cache, then measures 20000 data-dependent chase iterations via PIT-C2. Emits bench.cache.char.l1_ns. L2 reach probe via ephemeral 64 KB _fmalloc buffer, emits bench.cache.char.size_64kb_kbps + l2_status. DRAM ns derivation from (line_bytes * 1e6) / kbps. Scope-cut: 128 KB / 256 KB sweeps require huge-pointer arithmetic to span 64 KB segment boundaries, deferred to 0.9.0.

M3 (DB gap closure). IIT 3C87 DB row + routing stub in place; the actual FNSAVE-or-opcode discriminator stays hardware-gated pending a 386 DX-40 + IIT 3C87 capture. Genoa ET4000 chip-level probe via the 3CDh segment-select register: a read-write-readback test that fires on any ET4000-family chip regardless of BIOS string. Hercules variant discrimination: new hercules_variant_t enum (HGC / HGC+ / InColor / UNKNOWN / NA), pure classifier in a dedicated include so the host test runs without DOS-only dependencies, video.hercules_variant emits alongside the stable video.adapter=hercules signature key. 16 new host-test assertions.

Hotfix + lesson. Between the M2 commit and the smoketest, the DOSBox Staging end-to-end pass surfaced a flag-parsing regression: str_starts_with(a, "/C") was silently consuming /CSV as the /C calibrated-mode prefix before reaching the exact /CSV match. Do_csv never got set, the CSV file was never written, and mode=calibrated, runs=7 crept into the INI on every /CSV invocation. Fix reorders the parser so /CSV is matched before any prefix check, and tightens the /C guard to require NUL or colon next. The bug passed both the host-test suite and wmake; only the full DOSBox Staging run surfaced it. The lesson is now a durable instruction: after every CERBERUS.EXE build, run the DOSBox Staging smoketest before claiming the binary clean.

Hardware-gated, carried to 0.8.2. M4 BEK-V409 BSS overwrite root-cause, M3.1 IIT 3C87 real discriminator, M3.2 Genoa ET4000 probe confirmation. All three either need a specific piece of hardware or a focused debugging session we cannot do autonomously.

Build state: CERBERUS.EXE 170,722 bytes. DGROUP 61,824 / 62,000 soft target (AT RISK yellow, 1,664 bytes headroom; hard ceiling 3,712 bytes away). Host tests 376 assertions across 12 suites, 0 failures. Zero compiler warnings. v0.8.1 release, full release notes, project page.

CERBERUS v0.8.0: trust-first release, four milestones in 36 hours

The first CERBERUS release where every shipped result has been verified on real iron. Four milestones (M1 through M4) landed on main between 2026-04-21 and 2026-04-22, each with its own quality gate. The release cut is smaller than the v0.7.1 envelope on purpose: things that could not be trust-proved on a 486 DX-2-66 inside the milestone window were either compiled out, emit-suppressed, or deferred to 0.8.1. What shipped, every byte of it, has a real-iron signature under it.

M1 (trust-first foundation). Whetstone emit suppressed in stock builds (the Curnow-Wichmann kernel reads 10-30x below the published reference band on real 486 silicon; wmake WHETSTONE=1 re-enables for research). Upload path compiled out of stock binaries after v0.7.1 surfaced a stack overflow when barelybooting.com was unreachable. End-of-run _exit() bypass fixes a Watcom libc teardown hang on BEK-V409 that was locking the machine after Q-exit (Watcom's atexit / FPU / stdio-close chain deadlocks on that specific AMI 11/11/92 + DOS 6.22 + HIMEM + EMM386 stack; DOSBox-X never reproduced it). Nickname buffer leak fixed (issue #9, the e inconclusive tail garbage). cpu.class now emits a normalized family token (486, pentium, pentium_pro) instead of the CPUID vendor string. bench_cpu DB anchor widened after BEK-V409 measured 1.96M iters/sec on a 486 DX-2-66 where the original DOSBox-X-seeded band was 4.7M-10.5M. DGROUP audit tooling at wmake dgroup-report.

M2 (precision expansion). FPU behavioral fingerprint grows from 4 axes to 5: added FPTAN-pushes-1.0 probe (387+ vs 8087/287). New rounding-control cross-check exercises all 4 rounding modes (nearest-even / down / up / truncate) against a canonical FISTP(±1.5) result table. New precision-control cross-check stores 1.0/3.0 as 10-byte tword at 24/53/64-bit significand and compares bytewise. New exception-flag roundtrip deliberately triggers each of the 6 x87 exceptions (IE / DE / ZE / OE / UE / PE), observes in the status word, clears via FCLEX. Cache stride sweep extended to 6 points with stride=128 enabling line=32 vs line=64 plateau detection on Pentium-era parts. Memory diagnostic gains checkerboard (0x55/0xAA) + inverse checkerboard patterns for adjacent-cell coupling faults. IEEE-754 edge-case suite and /CSV output deferred to 0.8.1 under DGROUP budget pressure.

M3 (CUA-lite interaction). Replaces the row-24 status bar with a Norton-style F-key legend (Borland 0x30 / 0x3F palette on color, ATTR_INVERSE on mono). F1 opens a help overlay with static nav reference; F3 exits; Esc cancels modal views; arrow keys and PgUp/PgDn navigate the scrollable summary; Q remains a legacy exit alias. New /MONO flag forces monochrome rendering with a five-role semantic palette (07h body, 0Fh emphasis, 01h underline, 70h reverse, F0h blink-reverse, mapped to MDA attribute semantics) regardless of detected adapter, for amber or green phosphor monitors on color cards. EGA/VGA gets INT 10h AX=1003h BL=00h at startup for 16 background colors (reassigns attribute bit 7 from blink to bg-intensity). CGA gets a 3DAh retrace-edge gate on every VRAM write to eliminate snow. Adapter-tier detection waterfall (INT 10h AH=1Ah → AH=12h BL=10h → BDA → 3BAh toggle) documented and aligned with MS-DOS UI/UX research Tier 0.

M4 (documentation parity). Every FAIL/WARN consistency-rule verdict now carries a second clause with possible causes: FAIL: 486DX but FPU not integrated (counterfeit SX relabel?), FAIL: 486SX but FPU integrated (487 upgrade? detect wrong?), WARN: XT-class CPU + slave DMA (CPU mis-ID, or AT board retrofit?). The verdict token remains the first whitespace-delimited word so downstream parsers still read the PASS/WARN/FAIL shape correctly. CERBERUS.md, README.md, methodology.md, and consistency-rules.md all refreshed against shipped state. New Rule 11 (xt_slave_dma) documented.

Real-iron captures earning the ship. BEK-V409 clean-exit capture 2026-04-21 20:39 (Intel i486DX-2-66 + AMI 11/11/92 + S3 Trio64 + Vibra 16S + 63 MB XMS): first time in the v0.7.x → v0.8.0 arc the run returned cleanly to the DOS prompt without a reset. Companion 386 DX-40 + IIT 3C87 + Genoa ET4000 capture confirms the BEK-V409-specific *** NULL assignment detected BSS overwrite does not reproduce on alternate hardware (filed for 0.8.1 removal-at-a-time investigation). 320 host-side assertions green across 9 test suites. Binary ~167 KB, DGROUP 60,976 / 62,000 soft target. v0.8.0 release, full release notes, project page.

CERBERUS Part B: barelybooting-server v0.1.0 shipped, production-ready

The companion server for CERBERUS upload ingest hit its first tagged release. Flask + SQLite + Jinja, about 700 lines of Python, strictly implementing the frozen upload contract from CERBERUS v0.7.0. Six GET routes (list, per-CPU-class, per-machine, unknown, detail, stubbed CSV export) and one POST (/api/v1/submit). 24 host-side tests green. MVP parser, DB schema, and browse UI landed first, then a full red-team pass before first public traffic.

Deploy stack: Dockerfile (Python 3.12-slim-bookworm, non-root UID, waitress production WSGI, healthcheck), docker-compose.yml pairing the app with a cloudflared sidecar that waits on the app's healthcheck before opening the tunnel. Cloudflare Tunnel is outbound-only: no port forward, no DDNS, no public IP exposed. Target is a Home Assistant OS mini PC running Docker. Full DEPLOY.md walks add-on install, Cloudflare Zero Trust tunnel creation, edge WAF + bot + rate-limit setup, backup automation, secret rotation.

Red-team found and fixed: no rate limit on intake (added Flask-Limiter, 5/min + 30/hour per CF-Connecting-IP); no server-side length enforcement on nickname/notes (spam vector closed); potential submission_id birthday collision at 65k rows (retry-on-PK-collision preserves the 8-char contract); no CSP / frame-deny response headers (added); non-ASCII body bytes silently replaced (now strict-decode 400); unbounded Content-Type acceptance (now text/* only). Container itself runs read-only with tmpfs /tmp, cap_drop: ALL, and no-new-privileges. v0.1.0 release. Next: push to the HA box, end-to-end validate on BEK-V409, promote CERBERUS v0.7.0-rc2 to final. Project page.

CERBERUS: ANSI three-head boot intro, issue sweep, Homage Phase 3

Five commits past the v0.4.0 tag. Headline deliverable: an adapter-aware three-headed-dog ANSI boot splash with an OPL2 stinger, rendered between display_init and display_banner. Iconography sourced from Pseudo-Apollodorus (three heads, serpent tail, snake-mane spines), Virgil (three throats with fangs), Dante (red glowing eyes, chained beast), and Hercules 12th-labor Roman iconography (bound at the gate of Hades). On VGA color the sequence does a DAC fade-in from black, three eye-pairs illuminating left-to-right in cascade with escalating OPL2 barks at D2/F2/A2, a chain-shatter climax with DAC white flash and OPL2 rhythm-mode snare hit, then a sustained A-minor chord with vibrato over a sub-bass drone while hellfire embers flicker, chains rattle, breath sparks rise, the serpent tail wiggles, the wordmark color-pulses, and BEHOLD flashes at mid-sustain before a DAC fade-out. Full adapter degradation: EGA/CGA get attribute-only; MDA/Hercules get intensity-bit pulse with block-shading silhouette. Keypress dismisses with clean DAC and OPL2 cleanup. ~2.2 seconds end-to-end, gated by /NOINTRO and /NOUI. Real-hardware validated on BEK-V409; operator reaction recorded as “Wow that looked epic.”

Issue sweep: four GitHub issues moved. Issue #1 (four pre-existing test_timing failures) closed; the test inputs predated a 25% pit/bios divergence guard that landed later in timing_compute_dual and now correctly tripped, updated test c2 values to near-full-wrap sub_ticks that satisfy all three kernel gates while preserving the original test semantic, added dedicated divergence-guard coverage. Now 167 host-test assertions green, 0 failures. Issue #3 (UI hang on real iron) closed as cannot-reproduce; 9 consecutive clean runs across v0.2-rc1 through v0.4.0 plus the diagnostic binaries. Instrumentation stash preserved for reopen if the hang re-emerges. Issue #6 test-1 (CFLAGS_NOOPT tax isolation) landed ~7-9%, not dominant; test-2 built as a standalone tools/repstosd/REPSTOSD.EXE writing 128 MB to mode 13h VRAM via pure-assembly REP STOSW. Issue #2 (Vibra 16 PnP OPL intermittency) instrumented: new audio.opl_probe_trace INI key emits byte-level trace of every status-register read across both probe attempts, so multi-cold-boot captures can be diffed to identify which byte differs between “opl3 detected” and “none detected” runs.

Homage Phase 3 research: seven additional lesson docs in docs/research/homage/. Deferred-from-Phase-2 tasks closed: CheckIt Whetstone (confirmed custom synthetic like the Dhrystone finding, reframes issue #4), CheckIt video methodology (text-mode only, no mode 13h reference exists), CACHECHK UMC timer workaround (structural match to CERBERUS, raw-forensic-emit pattern filed as v0.5+ enhancement), SPEEDSYS (Vladimir Afanasiev attribution, Pentium-era peer out of CERBERUS scope). New research for issue #6 cross-corroboration: PCPBENCH (PC Player magazine, Germany, DOS/4GW 3D with REP STOSD primitives), 3DBENCH (Superscape VRT Ltd, UK, per-frame phase breakdown with dedicated Clr column, the sharpest comparator in the corpus), CHRISB (DJGPP 1996, SVGA variant with S3-specific path), LM60 (Landmark Speed 6.0, same IBM PC/XT anchor as CheckIt, era convention). Three attribution corrections shipped: SPEEDSYS (Roedy Green to Vladimir Afanasiev), PCPBENCH (Jim Leonard to PC Player magazine), 3DBENCH (Future Crew to Superscape VRT Ltd). Ethical frame unchanged from Phase 2: no decompiled code reproduced, no binary redistribution, attribution preserved, corrections flagged openly. Project page.

CERBERUS v0.4.0 shipped. Three-release-candidate weekend closed.

Four tags landed across a 48-hour arc: v0.2-rc1 (first real-hardware-validated RC after five bugs were found and fixed on contact with the BEK-V409 bench box), v0.3-rc1 (diagnostics complete at 6 of 6 subsystems, per-line cache-ratio reformulation), v0.4-rc1 (bench_cache, bench_video, Rule 4b cache-aware narration, Rule 11 DMA-class coherence, DGROUP ceiling lift 52K to 56K), and v0.4.0 (final release, closing three UI defects the rc1 screenshot surfaced).

The rc1-to-final pass was surgical. Three fixes: UTF-8 em-dash and multiplication-sign bytes in diag_cache's status format string were rendering as CP437 garbage between the ratio value and the status tag; audit found three more latent em-dash paths in the consistency engine and fixed them preemptively. The UI's value_str returned empty for V_U32 benchmark rows emitted with a NULL display string, so five BENCHMARKS rows (fpu ops/s, mem write/read/copy, k-whet) rendered labels but no values; fix mirrored the INI emit path's type-aware formatter. The bench_dhrystone.c reference-point comment was updated per Phase 2 Homage research: CheckIt's “Dhrystones” is a custom synthetic using the familiar label, not a Dhrystone 1.1 or 2.1 port, so the 33,609 BEK-V409 anchor is an empirical agreement point, not an algorithmic equivalence. Validated on real iron with a clean INI capture and four photographs. Eight consecutive clean real-iron runs through the v0.4.0 capture.

In parallel, seven Phase 2 Homage research lessons shipped at docs/research/homage/ covering CheckIt 3.0 FPU detection (IIT undefined-opcode probe), CheckIt's “Dhrystone” nature (custom synthetic), CheckIt's PC-XT baseline anchor, CACHECHK cache-threshold calibration (PKLITE-unpacked via UNP 4.11), FASTVID tool-identification correction (Pentium Pro 82450 MTRR, not the VLB TSR the Phase 1 brief misidentified), CHKCPU CPU identification reference, and TOPBENCH's community database model. 144,166-byte EXE, 163 host-test assertions green (except 4 pre-existing test_timing failures gated behind the UMC491 8254 phantom-wrap deep-dive). Project page. v0.4.0 release.

NetISA end-to-end quality gate + firmware security hardening

Ran adversarial quality gate across the entire NetISA codebase (14,300 lines, 7 components). Four parallel chunk reviews found 8 Fatal and 16 Significant issues. All fixed in two commits. DOS-side: NULL pointer guards, TSR return code fix, password input VGA overflow clamp, FPU detection 8088 safety, Claude stack overflow fix, command injection filter, scroll clamping. Firmware security hardening: ISR race condition fixed (per-request data buffers), open WiFi AP replaced with WPA2-PSK using device-unique MAC-derived password + AP disabled after station connects, timing-safe admin key comparison (constant-time ct_strcmp), HTTP session mutex, redirect limit (5 hops), memory barrier fix in ISR, OTA SHA256 hash verification, NVS encryption documented. Project page.

DOSBox-X automation quality gate: 9 Fatal + 31 Significant fixed

Ran the full adversarial quality gate on the NetISA DOSBox-X automation infrastructure (capture.ps1, dosrun.py, dosbuild.py, _RELAY.BAT, dev.conf, README), 1,422 lines total. Three review rounds, score progression 30→28→0. Found and fixed 9 Fatal and 31 Significant issues including: atomic lockfile (Ctrl+C races), Windows PID recycling (os.kill unreliable), hardcoded WinGet paths (break on upgrade), tempDir leaks on ffmpeg failure, zombie DOSBox-X processes on early exit, CP437 silent replacement, mount path quote escaping, PrintWindow return value check (no more silent black GIFs), ERRORLEVEL baseline reset before user commands, cycles=auto for deterministic timing, $projectDir quoting, lock-timeout-aware staleness. Automation infrastructure is now production-grade reliable. Project page.

TAKEOVER quality gate: 12 bugs fixed in 5 rounds

Ran the full adversarial quality gate on the TAKEOVER codebase (5,700 lines, 13 modules). Five review rounds, score progression 6→5→7→1→0. Found and fixed 12 issues: string pool exhaustion (silent failure → parse-time error), midnight BIOS tick wrap (music freeze), OPL2 channel bounds validation (prevent OOB register writes), LFSR dissolve safety break (prevent infinite loop), stack buffer pressure (2 buffers moved to far heap), DGROUP palette buffer (768 bytes recovered), VGA palette tearing (buffered rotation), cracktro OPL2 state corruption (chip reset after easter egg), music fast-forward after climax sequences, input buffer size mismatch, AI control level cross-scenario persistence, and cracktro integer overflow (unsigned counters). EXE: 57.8KB → 60.5KB. Project page.

Discord v2: ground-up rebuild

Ground-up rebuild of the Discord client from 939 lines / 14KB to ~2,400 lines / 37KB. New: 128 messages per channel (up from 32) on far-heap storage, multi-line compose (Shift+Enter), Ctrl+F find in messages with match highlighting, PC speaker notifications (4 types, F9 toggle), 8 hash-based author colors, CP437 reaction display, thread reply indicators, user list overlay (Alt+U), help overlay (F1), VGA palette fade in/out, DISCORD.CFG settings persistence, scroll bar, unread counts per channel, notification title flash. 10 source modules. 8 channels with 64 realistic retro computing community messages. Built in parallel with 7 agents across 5 waves. Project page.

DOSBox-X development relay + Cathode parser fixes

Built a development relay system (devenv/) that closes the develop-compile-test loop: compile natively with OpenWatcom/NASM on Windows, run the result in DOSBox-X, and capture stdout + exit codes via a batch-file relay mechanism. Features atomic lockfile with stale PID recovery, timeout handling, CP437 encoding, stderr capture, and retcode retry loop. Quality-gated through 7 rounds of adversarial review. Then used the relay to build an automated Cathode test suite: downloaded 11 real websites (NPR, CNN, Hacker News, Wikipedia, OpenBSD man pages, and purpose-built edge-case fixtures), parsed them through Cathode's HTML engine inside DOSBox-X, and found three bugs. (1) Named HTML entity table only had 7 entries; expanded to 90+ covering accented characters, smart quotes, dashes, currency, math symbols, and arrows, reusing the existing CP437 mapping tables. (2) Missing </head> tags caused 0 rendered rows; <body> now implicitly closes <head>. (3) Pages over 32KB produced 0 rows due to 16-bit int overflow in the parse chunk length; fixed by chunking large parses. All 12 tests now pass: 12/12, 0 failures. Project page.

Cathode v0.2: Text-Mode Document Browser

Major upgrade to the Cathode browser. Transforms the v0.1 browser shell into a functional text-mode document browser that renders real HTML from the web. Streaming HTML parser (tokenizer + layout emitter, ~830 lines) handles headings, paragraphs, links, bold/italic/code, ordered and unordered lists, blockquotes, preformatted text, horizontal rules, and HTML entities. HTTP/1.0 fetch layer with redirect following and timeout handling. UTF-8 to CP437 character mapping (Latin-1 table + extended codepoints for smart quotes, em-dashes, bullets, box drawing). URL parser with relative URL resolution (7 forms). Find on page with pre-computed match positions and render-time bitmap for 8088 performance. Bookmark persistence to CATHODE.BMK. Word-boundary wrapping. Scroll bar. Page truncation indicator. CP437 block-art CATHODE logo preserved on start page. Non-blocking event loop with DOS idle yield. 15 new source files, ~2,700 lines of new code. Quality-gated: 5 rounds of adversarial code review, 34 issues found and fixed (3 Fatal, 31 Significant). Three red-team rounds on the design document found 57 additional design issues. Product review established phased victory conditions and compile-time feature flags. 38.5KB EXE. Renders text.npr.org, man.openbsd.org, example.com, and barelybooting.com in text mode with navigable links. Project page.

TAKEOVER v1.1: Demoscene Enhancement Pack

Eight demoscene-inspired features, informed by research into four decades of the demoscene from Commodore 64 cracktros to modern 4KB intros. Audio-visual beat sync: adlib_beat counter lets effects pulse to OPL2 music events. Six fire-and-forget OPL2 stingers on channels 3-8 (alarm klaxon, bureaucratic stamp, typewriter click, error buzz, ominous diminished chord, metallic ka-ching), each thematically assigned per AI. Two new visual effects: sine wave text distortion (buffer manipulation, works on all adapters including MDA) and VGA DAC palette cycling in text mode. Four state transition types on goto: commands: LFSR-based dissolve (zero-storage permutation via 11-bit linear feedback shift register), column wipe, VGA DAC fade, and random glitch. Living status bar with AI control level (0-100) that shifts from green to red with block-character progress indicator. Five per-AI Mode 13h climax sequences at endings: Grid Convergence (Axiom), Redaction Sweep (Hushline), Threat Radar (Kestrel-9), Cozy Dissolution with closing iris (Orchard), Reality Glitch with palette rotation (Cinder), all at half resolution (160x100 doubled) for 8088 compatibility. Hidden cracktro easter egg unlocked by completing all five scenarios: raster bars behind the logo, 40-star parallax starfield, DYCP sine-bouncing scrolltext with 64-character bitmap font, and a 9-channel OPL2 chiptune in C minor (10-bar loop at 120 BPM with arpeggiated channel). Seven new engine commands. All five scenarios wired with enhancements at thematically appropriate moments. 57.8KB EXE (was 41.8KB). New DGROUP: ~55 bytes of ~22KB headroom. Zero warnings. Project page.

TAKEOVER v1.0 released

First official release of TAKEOVER. F9/F10 audio toggles for AdLib music and PC speaker sound effects (playback timing preserved when muted). Expanded color palette: 6 new attr colors (magenta, bright magenta, blue, bright blue) bringing the total to 15. Each AI scenario now has a distinct color personality: Kestrel-9 uses red/cyan for threat data, Cinder Mirror uses magenta for fourth-wall breaks, Orchard Clerk uses bright cyan for its helpful-creepy voice. Subtle homages to classic AI cinema (2001, WarGames, Ex Machina, The Matrix, Blade Runner concepts) woven into scenario dialogue at key dramatic moments. Quality-gated twice: caught kestrel9 state at 33 commands and a status bar restore bug. Download v1.0 (70KB ZIP).

Cathode: logo fix, color gradient, VGA palette fade

Fixed the Cathode browser logo where the H looked like an M (block art row 1 used lower-half blocks creating an M-shaped dip). Added 5-row color gradient: green edges, bright green inner rows, white center crossbar. Added VGA DAC palette fade-in/fade-out to the shared screen library. Cathode now fades in from black on startup and fades to black on exit. NetISA project page.

TAKEOVER: all five scenarios, A/V overhaul

All five TAKEOVER scenarios complete: Axiom Regent (municipal optimization), Hushline (crisis comms), Kestrel-9 (threat detection), Orchard Clerk (personalization), and Cinder Mirror (narrative generation). 250+ states, 15 endings across 5 scenarios. A/V overhaul: VGA Mode 13h plasma title screen with animated color cycling and pixel-font logo, AdLib/OPL2 FM synthesis ambient music (5 unique drone tracks), FPU-accelerated effects, VGA palette fade transitions. Hardware auto-detection (VGA/EGA/CGA/MDA, AdLib, FPU) with graceful degradation. DGROUP went down from 98.1% to 96.5% despite adding 4 new modules; all new const data uses static const __far. Quality-gated: fixed Mode 13h restore bug, orphaned kestrel9 state, latent numeric comparison parser bug. 41KB EXE. Project page.

TAKEOVER: engine, effects, menu, first scenario

New project: TAKEOVER, an AI takeover simulator for DOS. Built the .scn script engine (parser, state machine, variable system, choice menus), 14 visual effects (screen flicker, text corruption, progress bars, fake BSOD, screen melt, falling chars, color shift, blackout, and more), PC speaker audio via 8253 PIT, and a menu system with completion tracking. Wrote the Axiom Regent scenario: 55 states, 3 endings (TAKEOVER, ESCAPE, REVELATION), 10-15 minutes per path. Bureaucratic horror where a municipal optimization AI reclassifies you out of existence. String pool architecture (19KB) with 12-byte commands keeps the 31KB EXE in small memory model. Quality-gated: caught string pool overflow (8KB too small for 18.5KB scenario), stack overflow in effects (4KB buffers on 2KB stack), and DOS 8.3 filename violations. Project page.

Claude for DOS

Built CLAUDE.EXE, a Claude AI chat client for DOS that talks to the Anthropic API over TLS 1.3 via the NetISA card. Three agent modes: Chat (conversation only), Ask (Claude proposes commands, you confirm), and Auto (Claude runs DOS commands freely). Far-heap message pool, word-wrapped chat with newline support, 3-line compose area, [EXEC] tag parsing for command execution with output capture. Iterative agent loop with 5-deep safety cap to avoid recursive stack overflow on an 8088. Quality-gated with 2 rounds of adversarial review. Added to the NETISA.EXE launcher menu.

Site launch + community pages

barelybooting.com is live. Project page, build log, RSS feed. The site exists because the project reached a point where a README isn't enough: there's a DOS browser, a Discord client, validated CPLD logic, and production firmware. Time to show it properly.

v1 firmware complete

ESP32-S3 firmware hit production quality after 8 rounds of adversarial review. WiFi manager with APSTA mode, HTTP client with 4 concurrent TLS sessions, HTML-to-CP437 parser for Cathode, embedded web config server, OTA updates with authentication. Fixed cross-core race conditions, register protocol bugs, security vulnerabilities, and resource leaks along the way. The firmware is the bridge: it turns ISA bus register writes into TLS 1.3 sessions.

Discord client for DOS

Text-mode Discord client running in DOSBox-X. 6 channels with per-channel far-heap message storage, word-wrapped messages with author colors, compose bar with cursor editing, Tab-cycling focus, timed fake message injection, PgUp/PgDn scrolling. Quality-gated through 6 rounds of adversarial review. This is the demo that makes people stop and look: Discord on an 8088.

Cathode text-mode browser

Built Cathode, a text-mode HTTPS browser for DOS. Split architecture: DOS side receives a cell stream, manages 200-row scrollback on far heap (~80 KB), renders to VGA text buffer. ESP32 side fetches URLs, parses HTML, converts to (char, attr) cell stream. Link navigation, URL bar editing, back/forward history, CP437 box-drawing. 5 rounds of adversarial review.

DOS software stack complete

Full DOS software stack built and tested in DOSBox-X. NETISA.COM TSR (678 bytes, hooks INT 63h, under 2 KB resident). NETISA.EXE launcher with WiFi scanning, signal bars, card status, system info. Shared screen library with direct VGA writes to 0xB800:0000. Complete INT 63h C wrapper library. Stub layer for testing without hardware.

CPLD verification complete: 160/160 tests passing

All 160 Verilog testbench tests passing under iverilog. Address decode, IOCHRDY wait states, watchdog timeout, IRQ state machine retrigger, back-to-back cycles, mid-cycle reset, status flag merge, alias rejection. 95 of 128 macrocells used, timing slack of +39.5 ns setup at 16 MHz. Zero bus contention across the full simulation suite. Lint clean on iverilog -Wall.

Hardware design finalized, parts ordered

Schematic and PCB layout for the NetISA v1 board finalized. ATF1508AS CPLD for ISA bus timing, ESP32-S3-WROOM-1U-N8R8 for networking and crypto, Wiznet W5500 pads for v1.5 Ethernet. Bracket-mount antenna for metal cases. Physical jumpers, no Plug-and-Play. Parts ordered, waiting on delivery for prototype build.