Update to v86 HEAD with IDE shared-register fix

Patches src/ide.js before building: restores the dual master+slave writes
for ATA Command Block registers (Features, Sector Count, LBA Low/Mid/High)
that 1b90d2e7 changed to current_interface-only. Those registers are
channel-shared per ATA spec; Win95's ESDI_506.PDR writes them then switches
drive-select expecting the values to persist. Found via JS-only bisect.

Boots fresh in ~32s with the same sporadic-bluescreen rate as the prod build.
The new build has the tcp-connection bus event so SMB uses the clean path
instead of the connection-theft hack (guard added for the old-API monkeypatch
to skip if the bus handler already accepted).

tools/update-v86.js applies the source patch automatically. v86 checkout
left clean (patch stashed).
This commit is contained in:
Felix Rieseberg
2026-04-11 07:40:49 -07:00
parent c243ebbbcc
commit 55c4fbb27e
5 changed files with 763 additions and 686 deletions

1
.gitignore vendored
View File

@@ -15,3 +15,4 @@ trusted-signing-metadata.json
.env
electron-windows-sign.log
.npmrc
/.claude/

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -148,6 +148,10 @@ export function setupSmbShare(emulator: V86, hostPath: string) {
const orig = adapter.on_tcp_connection.bind(adapter);
adapter.on_tcp_connection = function (packet: any, tuple: string): boolean {
if (packet.tcp.dport !== 139) return orig(packet, tuple);
// New v86 fires the tcp-connection bus event BEFORE this callback;
// if our bus handler already accepted the conn, it's in tcp_conn —
// claim it so the original (which would otherwise RST) doesn't run.
if (adapter.tcp_conn[tuple]) return true;
const adapterAny = adapter as any;
adapterAny.receive = () => {};

View File

@@ -51,10 +51,48 @@ function download(url, dest) {
});
}
/**
* v86 commit 1b90d2e7 (May 2025) changed ATA Command Block register writes
* to only target current_interface instead of both master and slave. Those
* registers (ports 0x1F1-0x1F6) are channel-shared per the ATA spec — both
* drives on the cable see the same register file. Win95's ESDI_506.PDR
* writes them, switches drive-select, expects them to still be there.
* Result: IDE IRQ never fires, splash screen hang.
*
* Found via JS-only bisect: prod wasm + freshly-built libv86.js, parent
* 3c944a02 boots, 1b90d2e7 hangs deterministically.
*/
function patchIdeSharedRegisters(ideJsPath) {
let s = fs.readFileSync(ideJsPath, 'utf-8');
const re = /this\.current_interface\.(\w+_reg) = \(this\.current_interface\.\1 << 8 \| data\) & 0xFFFF;/g;
const matches = [...s.matchAll(re)];
if (matches.length === 0) {
console.log(' ide.js: shared-register patch already applied or upstream fixed it');
return;
}
if (matches.length < 5) {
throw new Error(`ide.js: expected ≥5 register write sites, found ${matches.length} — pattern changed`);
}
s = s.replace(re, (_, reg) =>
`this.master.${reg} = (this.master.${reg} << 8 | data) & 0xFFFF;\n` +
` this.slave.${reg} = (this.slave.${reg} << 8 | data) & 0xFFFF;`
);
fs.writeFileSync(ideJsPath, s);
console.log(` ide.js: restored shared-register writes (${matches.length} sites)`);
}
async function main() {
const jsDest = path.join(LIB_DIR, 'libv86.js');
const wasmDest = path.join(LIB_DIR, 'build/v86.wasm');
// ─── source patch (before any build) ─────────────────────────────────────
if (!JS_ONLY) {
const ideJs = path.join(V86_DIR, 'src/ide.js');
if (fs.existsSync(ideJs)) {
patchIdeSharedRegisters(ideJs);
}
}
// ─── wasm ────────────────────────────────────────────────────────────────
let wasmDate;
if (JS_ONLY) {