Build and install v86 (wasm + libv86.js + BIOS) into windows95. Use when pulling upstream v86 changes, fixing a broken build, verifying the fork branches are still in sync, or setting up a fresh v86 checkout.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
Build and install v86 (wasm + libv86.js + BIOS) into windows95. Use when pulling upstream v86 changes, fixing a broken build, verifying the fork branches are still in sync, or setting up a fresh v86 checkout.
Updating v86
windows95 builds v86 from source โ not from copy.sh. Two small bugfix
patches ride along on a fork branch until the upstream PRs land.
Sources
File
Built from
src/renderer/lib/libv86.js
make build/libv86.js in ../v86
src/renderer/lib/build/v86.wasm
make build/v86.wasm
bios/seabios.bin, bios/vgabios.bin
copied from ../v86/bios/
tools/update-v86.js runs those targets, copies the artifacts, runs 5
sanity checks, and fails loudly if any prerequisite is missing. No
fallbacks, no fetching from copy.sh.
The fork branch
v86 should be checked out on felixrieseberg/v86:windows95-base.
That branch merges the feature branches tracked in
docs/v86-patches.md โ keep that table
in sync with this list. Each is upstreamable on its own:
electron-renderer-fs-loader (PR #1540) โ src/lib.js uses
require("fs") instead of await import("node:fs/promises"). Dynamic
import of node: URLs doesn't work in an Electron renderer.
ide-shared-registers (PR #1541) โ writes ATA Command
Block registers (Features, Sector Count, LBA Low/Mid/High) to both
master and slave. Without this, Win95/98 hang at the splash screen on
any disk >~535MiB. Root cause: v86 commit changed those
writes to target only , but per ATA spec they're
channel-shared (one register file on the IDE cable; both drives latch
the same value).
src/ide.js
1b90d2e7
current_interface
vmware-abspointer โ src/vmware.js implements the VMware
backdoor (port 0x5658): GETVERSION + ABSPOINTER_* so a guest driver
(VBADOS VBMOUSE) can read absolute cursor position and track the host
cursor 1:1 without pointer lock, and the legacy text-clipboard commands
6โ9 so W95TOOLS.EXE (guest-tools/agent) can sync CF_TEXT with
the host. Consumes mouse-absolute and vmware-clipboard-host bus
events; emits vmware-absolute-mouse and vmware-clipboard-guest.
vmware-gettime(stacked on the clipboard branch) โ
src/vmware.js adds backdoor command GETTIME (23): EAX = host UTC
seconds, EBX = microseconds, ECX = max time lag, EDX = host UTC offset
in minutes. W95TOOLS.EXE polls it and calls SetLocalTime, so a
resumed guest (which never re-reads the RTC) snaps back to host time.
fake-network-copy-tcp-addrs โ src/browser/fake_network.js
copies the four address subarrays (hsrc/hdest/psrc/pdest) when a
TCPConnection is created from an inbound SYN. Upstream stores them
as zero-copy views into the NE2000 TX ring; once the guest's 12-slot
TX ring wraps (any concurrent traffic โ SMB, NBNS, ping), pump()
builds segments with whatever IP now occupies that slot, the guest
RSTs them as belonging to no TCB, and recv() blocks forever.
Exercised by tools/probe-tcp.sh.
vga-defer-vbe-disable-v86 โ src/vga.js defers dispi[4]=0
written from V86 mode until a legacy attribute-mode write reaches the
hardware. Win9x's VDD virtualises ports 3B0โ3DF for a windowed DOS VM
but not 1CE/1CF, so vgabios's VBE-disable leaks through while the rest
of its mode-set is captured into the VM's virtual register file โ
without this the screen turns to planar garbage the moment you open a
DOS box.
Closure must be v20210601 โ newer versions hit
closure-compiler#3972
on v86's source. The pin is in v86's Makefile.
Steps
cd ../v86
git fetch fork origin
git checkout windows95-base
git rebase fork/windows95-base # in case fork was updated elsewherecd ../windows95
node tools/update-v86.js
That's it. Script runs both make targets, copies, verifies.
Sanity-check WARNs
The 5 checks assert invariants src/renderer/smb/index.ts and
tools/parcel-build.js depend on. A WARN means upstream changed
something load-bearing โ don't ignore it:
await import("node:...") still present โ PR #1540 was reverted
or the pattern moved. Electron renderer will fail to load disk images.
master.features_reg= missing in minified โ PR #1541 was reverted
or windows95-base lost the commit. Win95 will hang at splash on
disks >535MiB. Check cd ../v86 && git log --oneline windows95-base.
Export pattern changed โ tools/parcel-build.js shim needs
updating. Look for module.exports.V86= and window.V86=.
tcp-connection event gone โ SMB falls back to the old-API theft
hack in src/renderer/smb/index.ts โ still works, but surprising.
on_tcp_connection gone โ old-API fallback is dead. SMB integration
only works via the tcp-connection bus event now. Harmless; update
the comment in index.ts and retire the theft code.
After updating, probe-test
node tools/update-v86.js && tools/probe-boot.sh
Should land SUCCESS in ~40s. If FAIL_SPLASH_HANG, the IDE fix didn't
take โ check grep master.features_reg src/renderer/lib/libv86.js. If
FAIL_VXDLINK, retry โ sporadic bluescreens are normal (see the
probe-win95 skill).
When a PR merges upstream
Rebase windows95-base to drop the now-redundant commit:
cd ../v86
git fetch origin
git checkout windows95-base
git rebase origin/master # drops the merged commit cleanly
git push fork windows95-base --force-with-lease
If both PRs are upstream, retire the fork branch entirely:
Point tools/update-v86.js default at origin/master (it already
uses ../v86, so just git checkout master there)
Delete fork/windows95-base
Remove this skill's "The fork branch" section
Confirm the 5 sanity checks still pass โ they're version-agnostic
Integration contract with SMB
The SMB server sits on top of v86's network adapter. Details in
src/renderer/smb/README.md. Short version: the new path uses the
tcp-connection bus event; the fallback path uses
adapter.on_tcp_connection callback + connection-theft (stealing a
TCPConnection the HTTP probe builds for us). Both use .on_data on
the conn, not .on("data"), because Closure dead-code-eliminates the
event emitter plumbing.
If any v86 update breaks these assumptions, src/renderer/smb/index.ts
needs updating, not just tools/update-v86.js.