8086 assembly playground
Write 8086 assembly in NASM syntax and step through it as a DOS .COM program: every instruction's bytes split into prefix, opcode, ModR/M, displacement and immediate with each field explained, registers and flags with changes highlighted, segment:offset addresses with the 20-bit physical address, Intel's clock count for every instruction (8086 or 8088), an 80 × 25 colour text screen at B800:0000, BIOS and DOS interrupts, and the keyboard.
Added
How to use it
An assembler and a simulator for the Intel 8086 — the CPU of the IBM PC family (the original PC
had its 8-bit-bus sibling, the 8088) — written for this page and running entirely in it. You
write NASM-style assembly, and it runs as a DOS .COM program would: loaded at offset 100h, with
DOS and the BIOS answering int 21h, int 10h and int 16h, and a text screen you can write
to directly.
How to use it
Pick an example or type in the Source pane; it assembles as you type and errors appear under the source (click one to jump to the line). The Machine code pane shows each instruction’s offset, its bytes, the disassembly and its clock count.
-
Step (F10) runs one instruction, Back undoes it, Run (F5) runs until the program exits, hits a breakpoint or waits for a key. Click the gutter to the left of an address for a breakpoint. Speed runs flat out, at the 4.77 MHz of the original PC, or ten steps a second so you can watch.
-
Registers shows AX–DX (with their high and low bytes), SI, DI, BP, SP, the four segment registers, IP and the flags (O D I T S Z A P C); whatever the last step changed is highlighted. CS:IP and SS:SP are also shown as 20-bit physical addresses.
-
Screen is video memory at B800:0000: 80 × 25 cells of a character and an attribute (colours and blink), in code page 437. Click it and type to send keys to the program (
int 16hand the DOS input calls); on a phone use the box under it. -
Encoding breaks the selected instruction into its bytes — prefixes, the opcode with its d, w, s or register bits, ModR/M as mod / reg / r/m, the displacement and the immediate — bit by bit, says what each field means, which segment the memory operand uses and what it costs to compute the address, and, at the current instruction, the actual segment:offset and physical address it will touch.
-
Memory dumps DS, the stack around SS:SP, ES:DI, video memory, the interrupt vector table and the code, each row with its segment:offset and physical address.
-
Clocks: every instruction shows Intel’s published 8086 count, such as
9+EAfor an add from memory,16/4for a conditional jump (taken / not taken) or9+17/repforrep movsb. The running total uses the actual operands: the address calculation, how many times a string instruction repeated, how far a shift by CL went, the data-dependentmulanddivtimes (estimated within Intel’s range), and 4 extra clocks per word moved at an odd address — or, with 8088 selected, for every word, since that chip’s bus is 8 bits wide. Real chips also stall on the prefetch queue, so measured times run a little longer than these.
The examples: hello world through DOS, writing coloured text straight into video memory,
reversing a string with lodsb and stosb, a recursive factorial printed in decimal, a bubble
sort, an interactive loop that reads keys and moves a character with the arrow keys, flags and
BCD arithmetic, and hooking the divide-error interrupt with your own handler.
The source is kept in this browser; the share link carries it.
What is supported
-
Every 8086 instruction, with the 8086’s own behaviour:
push sppushes the decremented value, shifts by CL are not masked to 5 bits,idivfaults on a quotient of −128 (or −32768), and the divide-error interrupt returns to the instruction after thediv. Nothing from the 186 or later: conditional jumps reach only ±128 bytes, shifts are by 1 or by CL, there is nopush 5— the assembler says what to write instead. Undocumented opcodes stop the program. -
Flags: the ones Intel documents are exact; the ones it calls undefined (such as SF and ZF after
mul) are left as they were. -
NASM syntax: labels and
.locallabels,db dw dd resb resw resd times equ org align,section .text/.data/.bss,%define,$and$$,byte/word/short/near/far(MASM’sptris accepted too), segment overrides as[es:di], and numbers as0x1F,1Fh,0b101or101b.jmppicks the short or near form by itself. -
Interrupts go through the vector table at 0000:0000, so your own handlers work (
int 21hAH=25h sets one). The BIOS and DOS functions in the Reference tab are handled by the page:int 21h01h 02h 06h 07h 08h 09h 0Ah 0Bh 25h 2Ah 2Ch 30h 35h 4Ch,int 20h,int 10h00h–03h 06h–0Ah 0Eh 0Fh 13h,int 16h00h–02h andint 1Ah00h. There are no files, no timer interrupt and no I/O ports (inreads FFh).
Feedback is a gift. Something to improve, a feature to add, or a bug? Tell me.
Runs entirely in your browser; nothing you type leaves this page.