Rust
JIT & CPU core
JS
Device controllers
4 GB
Linear WASM memory
65 536
I/O ports
14+
Emulated devices
Browser Layer
User-facing adapters — bridge between browser APIs and emulator
Keyboard
Mouse
Screen
Audio
Network
Serial
V86 API
bus.js)
JavaScript Core
Device models, I/O dispatch, CPU wrapper — all device state lives here
CPU (cpu.js)
I/O Bus
PCI
VGA
IDE
PS/2
PIT
RTC
DMA
UART
NE2000
Virtio
SB16
Floppy
v86.wasm)
WebAssembly Runtime
Rust-compiled CPU engine — JIT compiles x86 blocks to WASM at runtime
JIT Engine
Interpreter
CPU State
TLB / Paging
Linear Memory
FPU / SSE
APIC
Key Data Flows
1
User presses a keyBrowser fires
keydown event — keyboard.js adapter reads the scancode.2
Adapter sends to bus
bus.send("keyboard-code", scancode) — routes through BusConnector pair.3
PS/2 device receives itPS/2 controller (port 0x60) queues the scancode in its internal FIFO buffer.
4
IRQ1 raisedPS/2 calls
cpu.device_raise_irq(1) — signals keyboard interrupt to PIC.5
CPU handles interruptWASM CPU checks pending interrupts, suspends current instruction, vectors to IDT[33] (IRQ1 handler).
6
Guest OS reads port 0x60Kernel interrupt handler executes
in al, 0x60 → WASM calls JS io_port_read8(0x60) → PS/2 dequeues scancode.1
Guest writes to video memoryGuest OS writes pixel/text data to 0xA0000 (VGA) or VBE framebuffer at 0xE0000000+.
2
MMAP handler firesWASM calls
mmap_write8(addr, value) → JS dispatches to VGA device's memory write handler.3
VGA marks dirty regionvga.js records which scanlines changed and schedules a redraw on the next timer tick.
4
VGA pushes pixel data
bus.send("screen-fill-buffer", {x, y, w, h, buffer}) — sends changed pixels to adapter.5
Screen adapter rendersscreen.js draws the pixel buffer into an HTML
<canvas> using putImageData().1
Guest issues ATA commandGuest OS writes command
0x20 (READ SECTORS) to IDE command register at port 0x1F7.2
IDE device processes commandide.js reads the LBA address and sector count from ports 0x1F3–0x1F6, locates data in the disk image buffer.
3
DMA transfer to guest memoryIDE uses DMA controller to write sector data directly into guest RAM. DMA channel 3 maps physical pages.
4
IRQ14 raisedIDE raises IRQ14 signaling transfer complete — CPU vectors to disk interrupt handler.
5
Guest OS reads dataKernel's DMA callback finds the sector data now in guest RAM and resumes the blocked process.
1
V86 constructor calledstarter.js creates BusConnector, loads WASM binary (v86.wasm), sets up adapters.
2
Devices initializedcpu.init() creates all devices in order: PCI → RTC → DMA → VGA → PS/2 → UART → Floppy → IDE → PIT → Network.
3
BIOS loaded into ROMBIOS binary mapped at physical address 0xF0000. VGA BIOS mapped at 0xC0000. CMOS filled with boot config.
4
CPU starts at reset vectorWASM CPU initializes EIP to 0xFFFFFFF0 (16-byte alias into BIOS ROM). First instruction runs.
5
JIT warms upFrequently-executed x86 basic blocks get JIT-compiled to WASM functions. Subsequent runs are much faster.
6
Emulator-ready eventOnce BIOS finishes POST and loads bootloader,
bus.send("emulator-ready") fires for the application.Explore Components
CPU & JIT Engine
How x86 instructions execute — the JIT compilation pipeline, instruction interpreter, and WASM bridge callbacks.
WASM / RustMemory System
4 GB linear WASM address space, virtual-to-physical paging, TLB, MMIO regions, and CPU state layout.
Memory / PagingI/O Port Bus
All 65 536 ISA I/O ports, device port assignments, and the read/write dispatch pipeline from WASM to JS.
I/O / PortsHardware Devices
Every emulated device — PIC, PIT, VGA, IDE, PS/2, UART, SB16, NE2000, Virtio — with port ranges and IRQs.
DevicesEvent Bus
The BusConnector pub/sub system that decouples browser adapters from emulator devices.
bus.js