Device controllers live entirely in JavaScript (src/*.js). They register handlers on the I/O bus and respond to WASM callbacks. Device state (registers, FIFOs, buffers) is stored in JS objects. Interrupt signals are sent back to the WASM CPU via cpu.device_raise_irq(n).
Device Cards
Click any card to expand details.
Two cascaded 8259A PICs manage the 16 hardware interrupt lines (IRQ0â15). Master PIC at 0x20 handles IRQ0â7; Slave PIC at 0xA0 handles IRQ8â15 (cascaded via IRQ2).
Flow: Device calls cpu.device_raise_irq(n) â PIC checks mask register â if not masked, signals CPU â CPU checks IF flag â dispatches to IDT[vector].
Software sends EOI (End of Interrupt, 0x20) to dismiss the IRQ after handling. Non-specific EOI clears the highest-priority ISR bit.
Three independent countdown channels clocked at 1.193182 MHz. Channel 0 drives IRQ0 (system tick); Channel 2 drives the PC speaker pitch.
The BIOS programs channel 0 to mode 3 (square wave) at ~18.2 Hz. Linux reprograms it to a higher rate for better timer resolution. Modern OSes use HPET/APIC timer instead.
Speaker: Port 0x61 bit 0 gates channel 2 output to the speaker. The speaker adapter listens for "pcspeaker-update" bus messages.
Indexed CMOS register set accessed via port 0x70 (index) and 0x71 (data). Stores time, date, and machine configuration that persists across reboots.
Key CMOS fields v86 pre-fills: base memory size, extended memory size, boot order (disk/CDROM), floppy drive type, hard disk geometry, equipment flags.
Status register C contains IRQ8 interrupt flags: periodic interrupt (PIE), alarm interrupt (AIE), update-ended interrupt (UIE). Linux uses periodic interrupts for scheduling.
Two cascaded 8237A DMA controllers (8 channels total). DMA transfers data between device FIFOs and guest RAM without CPU involvement.
Channel allocation: Ch0=free, Ch1=SB16 8-bit, Ch2=Floppy, Ch3=SB16 16-bit alt, Ch4=cascade, Ch5â7=16-bit ISA.
Page registers (0x80â0x8F) extend the 16-bit DMA address to 20 bits. Device calls dma.do_read() which copies data from guest RAM into the device buffer, then triggers the device callback.
The largest device â emulates a Bochs VBE SVGA-compatible card. Supports VGA text mode (80Ã25, 25 colors), CGA graphics mode (320Ã200 or 640Ã480), and SVGA modes up to 1280Ã1024 via Bochs VBE extensions (ports 0x1CE/0x1CF).
Text mode: Reads character+attribute bytes from MMIO at 0xB8000, renders font glyphs into a pixel buffer, sends to screen adapter.
SVGA mode: Guest writes pixels directly to linear framebuffer at 0xE0000000. VGA marks dirty rows; next tick flushes changed scanlines via bus message.
MMIO regions: 0xA0000â0xBFFFF (planar), 0xE0000000+ (linear framebuffer), PCI BAR for VBE registers.
Emulates the Intel 8042 microcontroller that bridges the CPU to keyboard and mouse. Maintains separate output FIFOs for keyboard and mouse data.
Keyboard scancodes arrive via bus message "keyboard-code". The PS/2 device enqueues the scancode and raises IRQ1. Guest reads the byte from port 0x60.
Mouse packets are 3 bytes (button state + X delta + Y delta), delivered one byte at a time. IRQ12 fires after each byte. Mouse data arrives via bus messages "mouse-delta" and "mouse-click".
Command 0xA9 (test aux port), 0xAD/0xAE (disable/enable keyboard), 0xD4 (write to mouse) are implemented for OS compatibility.
Up to 4 UART 16550A serial controllers. Each has a 16-byte RX/TX FIFO. The divisor latch sets baud rate (typical: 115200 baud = divisor 1).
Linux commonly uses COM1 as a serial console. Output bytes go to the serial adapter via bus and appear in a terminal. Input from the terminal is queued in the RX FIFO and triggers UART interrupts.
UART0 (COM1) is always enabled. UART1â3 are created only if enabled in v86 settings. Virtio console can replace UART for better throughput.
PCI IDE controller with two channels (primary/secondary), each supporting master and slave devices. Primary master = hard disk (hda), secondary master = CD-ROM.
Hard disk: ATA PIO transfers for small reads, DMA for large blocks. Disk images are JS ArrayBuffers. Async I/O reads in chunks and signals completion via IRQ14.
CD-ROM: ATAPI protocol (ATA Packet Interface). PACKET command (0xA0) sends a 12-byte CDB. Supports READ(10), REQUEST SENSE, INQUIRY, MODE SENSE, START/STOP UNIT. ISO 9660 image support built-in.
Disk state (dirty sectors) can be persisted to IndexedDB via the buffer abstraction layer (src/buffer.js).
Intel 8272A Floppy Disk Controller emulation. Supports two drives (fda/fdb), standard 1.44 MB geometry: 80 tracks, 2 heads, 18 sectors/track, 512 bytes/sector.
Command sequence: write command bytes to port 0x3F5, wait for interrupt, read result bytes. BIOS uses INT 13h to abstract floppy access.
DMA channel 2 transfers data. The BIOS typically reads the boot sector (track 0, head 0, sector 1) first during the boot sequence.
Creative SoundBlaster 16 ISA audio card. DSP version 4.x. Supports 8-bit and 16-bit PCM playback and recording via DMA transfer. Mixer chip at 0x224 controls volume, channel balance, and source selection.
Playback flow: guest programs DMA address/length, writes PLAY DMA command to DSP â DMA reads audio samples from guest RAM â samples sent to Web Audio API via speaker bus adapter â IRQ5 fires when buffer exhausted.
Also emulates OPL2/OPL3 FM synthesis chip (Yamaha YM3812) at 0x388 for MIDI-quality music synthesis used by DOS games.
ISA NE2000-compatible Ethernet adapter using the NS DP8390 controller. Implements register pages 0 and 1 for command, transmit, receive, and interrupt management.
Outgoing packets: guest writes frame to NIC ring buffer â network-send bus message â network adapter sends to relay or fetch backend.
Incoming packets: network adapter fires network-receive â NIC copies frame into ring buffer â raises IRQ9 â guest's driver reads the frame.
MAC address is configurable. Can be replaced by Virtio-net for better performance and simpler guest driver requirements.
Paravirtualized devices using the Virtio standard (PCI transport). Much more efficient than emulating legacy hardware â the guest driver knows it's in a VM and cooperates.
virtio-net: Network card replacement. Virtqueue-based packet I/O. Requires virtio-net driver (standard in Linux since 2.6.25).
virtio-9p: Filesystem passthrough via the 9P protocol. Maps a JS filesystem (backed by lib/9p.js) into the guest. Linux mounts it with mount -t 9p.
virtio-console: High-performance serial console replacement. Shares virtqueues with the serial adapter.
virtio-balloon: Memory pressure signaling â allows the host to reclaim guest memory pages.
PCI configuration space â 256 "slots" (bus 0, devices 0â31, functions 0â7). Each device has a 256-byte config space with standard header: vendor ID (0x0), device ID (0x2), command (0x4), status (0x6), class code (0x8), BARs (0x10â0x24), IRQ line (0x3C), IRQ pin (0x3D).
Access via CONFIG_ADDRESS (0xCF8) and CONFIG_DATA (0xCFC). Writing a 1 to a BAR causes the device to return its required size on the next read â used by BIOS to assign addresses.
v86 devices that use PCI: VGA, IDE, NE2000 (optional), all Virtio devices.
Basic ACPI power management support. Implements the ACPI timer (PMTMR at 0xB008) â a 24-bit counter running at 3.579545 MHz, used by Linux for high-resolution timing when HPET is unavailable.
ACPI tables (RSDT, FADT, MADT, HPET) are provided by SeaBIOS in the BIOS ROM and describe the hardware topology to the OS.
Shutdown via ACPI: guest writes 0x2000 to PM1a_CNT register (SLP_TYP=5, SLP_EN=1) â v86 stops the main loop.
Initialization Order
Devices are created in a specific order in cpu.init() because some depend on others already existing (e.g. DMA must exist before Floppy, PCI before Virtio).
IRQ Routing
| IRQ | Vector | Device | Trigger condition |
|---|---|---|---|
| IRQ 0 | 0x08 | PIT Channel 0 | Countdown reaches zero (~18.2 Hz or programmed rate) |
| IRQ 1 | 0x09 | PS/2 Keyboard | Scancode enqueued from keyboard FIFO |
| IRQ 2 | 0x0A | PIC cascade | Cascade line from slave PIC (not maskable directly) |
| IRQ 3 | 0x0B | COM2 UART | TX empty, RX data ready, or line status change |
| IRQ 4 | 0x0C | COM1 UART | TX empty, RX data ready, or line status change |
| IRQ 5 | 0x0D | SoundBlaster 16 | DMA buffer half/full, or DSP command completion |
| IRQ 6 | 0x0E | Floppy FDC | Command completion (seek done, transfer done) |
| IRQ 7 | 0x0F | LPT1 (unused) | Parallel port â not used in v86 |
| IRQ 8 | 0x70 | RTC | Periodic interrupt (configurable rate) or alarm |
| IRQ 9 | 0x71 | NE2000 / free | Packet received or transmitted |
| IRQ 10 | 0x72 | free / Virtio | PCI device interrupt (shared) |
| IRQ 11 | 0x73 | free / Virtio | PCI device interrupt (shared) |
| IRQ 12 | 0x74 | PS/2 Mouse | Mouse packet byte ready |
| IRQ 13 | 0x75 | FPU / coprocessor | FPU error (rarely used with x87 exceptions) |
| IRQ 14 | 0x76 | IDE Primary | Disk read/write or ATAPI command complete |
| IRQ 15 | 0x77 | IDE Secondary | CD-ROM read or ATAPI command complete |