* Fix guest TCP recv() stalling under concurrent traffic
v86's fake_network stores TCPConnection routing fields (hsrc/hdest/
psrc/pdest) as zero-copy subarrays of the SYN frame, which is itself a
view into the NE2000 TX ring. Win95's driver uses a 12-slot ring; once
it wraps (any concurrent SMB/NBNS/ping while waiting for an upstream
reply), pump() emits segments with whatever IP now occupies that slot,
the guest RSTs them, and recv() blocks forever.
- libv86.js: copy the four address arrays at TCPConnection construction
(matches felixrieseberg/v86@dd13099c on fake-network-copy-tcp-addrs,
now merged into windows95-base)
- tools/probe-tcp.sh + net/tcp-trace.ts + tcp-relay.ts test stub +
debug-harness WIN95_PROBE_RUN2: end-to-end regression harness
(boot → ping -t → telnet → async write after ring wrap → assert ACK).
All env-gated, no production-path change.
- docs/v86-patches.md: tracker for all fork patches + upstream PR state
- update-v86 SKILL.md: cross-link and new fork-branch entry
* Drop checked-in upstream PR description
Belongs on the GitHub PR, not in the repo.
Three layers:
v86 — src/vmware.js gains the legacy text-clipboard backdoor commands
(GETSELLENGTH/GETNEXTPIECE/SETSELLENGTH/SETNEXTPIECE, 6–9). The host
stages bytes via the vmware-clipboard-host bus event; the guest pushes
via 8/9 and the device emits vmware-clipboard-guest when the buffer
fills. Same wire protocol as open-vm-tools' pre-RPC copy/paste.
Committed on the windows95-base fork branch; libv86.js rebuilt here.
renderer — src/renderer/clipboard.ts polls Electron's clipboard (no
change event exists), translates host UTF-8/LF ↔ guest CP-1252/CRLF,
and bounces bytes through the two bus events. Echo-suppressed so a
value we just wrote does not come back as a change.
guest — guest-tools/agent/W95TOOLS.EXE is a 22 KB hidden-window agent
that joins the Win32 clipboard-viewer chain (push-on-copy) and polls
the backdoor on a 250 ms timer (pull-from-host). Win9x runs ring-3
with the I/O bitmap wide open, so a plain IN EAX,DX from a user
process reaches the port — no driver needed. Named for growth: time
sync and host-initiated shutdown will live here too. Built with Open
Watcom v2 inside Docker (Makefile + Dockerfile alongside the source);
subsystem 4.0, no msvcrt, runs on Win95 RTM.
Install: copy \\HOST\TOOLS\agent\W95TOOLS.EXE into the guest and drop a
shortcut in StartUp. Text only, 64 KB cap.
v86's async loaders leave the ATAPI drive in BSY across an event-loop
turn after a READ(10) CDB. Win95's ESDI_506 reads status twice, sees
BSY both times, and issues DEVICE RESET ~165 instructions later, which
cancels the in-flight read — the drive enumerates but D: never mounts.
Serve the ISO through a small fs.readSync-backed buffer so the data is
available before the next emulated instruction runs, and re-enable the
CD-ROM settings tab.
Also: WIN95_PROBE_CDROM / WIN95_PROBE_CDTRACE harness hooks, and pump
one screen-adapter frame before screenshotting so probe captures work
when the Electron window is occluded.
Rebuilds libv86.js from felixrieseberg/v86@windows95-base, which now
carries vga-defer-vbe-disable-v86: when a windowed DOS VM's vgabios
writes dispi[4]=0, Win9x's VDD passes that through (it doesn't know
about ports 1CE/1CF) while virtualising the rest of the mode-set, so
v86 used to drop out of LFB rendering with the legacy registers still
holding SVGA values and the screen turned to planar garbage. The fix
defers the disable until a legacy attribute-mode write actually
reaches the hardware.
debug-harness: WIN95_PROBE_DOSBOX=1 opens command from Run, types
dir, optionally Alt+Enters (WIN95_PROBE_DOSBOX_ALTENTER=1).
WIN95_PROBE_VGATRACE=1 wraps the VGA io.ports[] entries (not the
VGAScreen methods, which are captured by-value at registration) and
dumps [port, op, value, eip+VM/PE/CPL] tuples to
/tmp/win95-vgatrace.json — that EIP/mode column is what pinned the
leak on V86-mode vgabios at C000:2C8x.
* Seamless mouse via VMware backdoor + fs-backed TOOLS share
v86 (rebuilt libv86.js from fork branch vmware-mouse): new VMwareMouse
device on port 0x5658 implementing GETVERSION/ABSPOINTER_* fed by the
existing mouse-absolute bus event. Move-only packets are coalesced so
the guest cursor never falls more than one frame behind. Emits
vmware-absolute-mouse on the bus when the guest driver toggles mode.
Renderer: listens for that event, keeps the v86 mouse enabled without
pointer lock, drops the startup auto-capture, and hides the host cursor
over the canvas (.seamless-mouse) while the driver is active. Falls back
to click-to-capture when no driver is present.
SMB: TOOLS share is now backed by the bundled guest-tools/ directory
(subdirectories work) with the synthetic README.TXT/_MAPZ.BAT overlaid
at the root. resolve() routes by tid; SEARCH and FIND_FIRST2 share a
single listForSearch helper.
guest-tools/mouse-driver/: VBMOUSE.EXE + VBMOUSE.DRV from VBADOS
(Javier S. Pedro, GPLv2). Load the TSR from AUTOEXEC.BAT and set
mouse.drv=vbmouse.drv in SYSTEM.INI to enable seamless mouse.
Also: tsconfig rootDir "." for TS 6.0 (preserves dist/src/ layout).
* docs: windows95-base now includes vmware-abspointer
* Rewrite update-v86.js for the current build pipeline
Both v86 fixes we've been carrying are now real branches on the fork
(PR #1540 electron-renderer-fs-loader, PR #1541 ide-shared-registers)
combined as felixrieseberg/v86:windows95-base. update-v86.js no longer
needs to patch sources at build time — it just builds whatever's checked
out and copies the result.
Gone: the fallback-to-copy.sh path, the skew-day check, the structural
regex patches for load_file/exportSymbol/fetch-bind, the phantom-slave
guard (both are in the branch), the --js-only flag. If you don't have
cargo/clang/java/closure, the script fails loudly — no silent fallbacks.
Added: sanity checks against the installed libv86.js for the invariants
our SMB integration and parcel-build shim depend on, so if upstream
changes something load-bearing we see it as a WARN at update time
instead of a runtime failure.
Tested end-to-end: 5/5 sanity checks, fresh boot SUCCESS in 32s.
* docs and skills: capture SMB/v86/testing knowledge from the session
- docs/smb-share.md: user-facing SMB integration overview (how to mount in
Win95, what's implemented, what's not). Points at the protocol-level
README inside src/renderer/smb/ for wire-level gotchas.
- .claude/skills/probe-win95: how to boot and test the VM without a human.
Env vars, file locations, failure modes, the XT scancode keyboard trick,
bisect rules of thumb.
- .claude/skills/update-v86: how to pull upstream v86 changes, what the
five sanity-check WARNs mean, how to retire the fork branches when the
PRs merge upstream.
.gitignore narrowed to exclude only the runtime dirs (scheduled_tasks.lock,
worktrees) instead of the whole .claude/ tree, so skills can be committed.