x86 / DOSBox
The x86 preset in the IDE gives you a DOSBox environment running DOS 6.22 — a complete MS-DOS system in your browser, with VGA graphics support and the NASM assembler for writing 8086/286/386 assembly code. This is the retro PC experience: COM programs, INT 21h for DOS calls, and INT 10h for BIOS video.
If you grew up with a DOS PC in the late 80s or early 90s, this will feel very familiar.
Languages in the IDE
| Language | Notes |
|---|---|
| x86 assembly (NASM) | Netwide Assembler — clean Intel syntax |
Quick start
- Select x86 (DOSBox) as the platform.
- Load the Assembly template.
- Build — NASM compiles your source, the resulting COM file runs in DOSBox automatically.
COM vs EXE
The IDE template produces .COM files — the simplest DOS executable format. A COM file is a flat binary loaded at offset $100 in a 64 KB segment. This is perfect for learning and small programs. Larger programs needing multiple segments require the EXE format.
The x86 in context
The Intel 8086 (1978) is the CPU at the base of the x86 family — every Intel/AMD processor today is a backward-compatible descendant. The 8086 is a 16-bit processor with a segmented memory architecture. DOSBox emulates a PC in this era running at a speed appropriate for the software.
Registers
The 8086 has 14 programmer-visible registers:
| Register | Size | Purpose |
|---|---|---|
| AX | 16-bit | Accumulator (AH=high byte, AL=low byte) |
| BX | 16-bit | Base — general purpose, indirect addressing |
| CX | 16-bit | Counter — used by LOOP, SHIFT, REP instructions |
| DX | 16-bit | Data — used with AX for 32-bit ops, I/O port |
| SI | 16-bit | Source Index |
| DI | 16-bit | Destination Index |
| SP | 16-bit | Stack Pointer |
| BP | 16-bit | Base Pointer (stack frame access) |
| CS | 16-bit | Code Segment |
| DS | 16-bit | Data Segment |
| ES | 16-bit | Extra Segment |
| SS | 16-bit | Stack Segment |
| IP | 16-bit | Instruction Pointer |
| FLAGS | 16-bit | Status flags |
The 8-bit halves of AX, BX, CX, DX are accessible individually: AL, AH, BL, BH, CL, CH, DL, DH.
Your first program
; Hello World — x86 COM program (NASM syntax)
; Build: nasm hello.asm -f bin -o hello.com
BITS 16
ORG 100h ; COM files start at offset 0x100
start:
mov dx, msg ; DX = address of message
mov ah, 09h ; DOS INT 21h function 09 = print string
int 21h ; Call DOS
mov ax, 4C00h ; DOS INT 21h function 4C = exit, AL=return code
int 21h
msg DB "Hello from x86 DOS!", 13, 10, "$" ; $ terminates the string for INT 21h/09
Core instructions
Moving data
mov ax, 42 ; AX = 42 (immediate)
mov bx, ax ; BX = AX (register to register)
mov ax, [bx] ; AX = word at address BX (memory)
mov [bx], cx ; Word at BX = CX
mov al, [1234h] ; AL = byte at address 1234h
mov word [bx], 100 ; Write 100 to word at BX
Arithmetic
add ax, bx ; AX = AX + BX
add ax, 10 ; AX = AX + 10
sub bx, cx ; BX = BX - CX
inc ax ; AX++
dec bx ; BX--
mul bx ; AX = AX * BX (unsigned, result in DX:AX)
div bx ; AX = DX:AX / BX, DX = remainder
neg ax ; AX = -AX
Logic
and ax, 0x00FF ; AX = AX AND 0x00FF (isolate low byte)
or ax, 0x8000 ; AX = AX OR 0x8000 (set bit 15)
xor ax, ax ; AX = 0 (fastest way to zero a register)
not ax ; AX = ~AX
shl ax, 1 ; AX = AX << 1 (multiply by 2)
shr ax, 1 ; AX = AX >> 1 (divide by 2, unsigned)
Comparisons and jumps
cmp ax, 10 ; Compare AX with 10 (sets flags)
je equal ; Jump if equal (ZF=1)
jne not_equal ; Jump if not equal
jl less ; Jump if less than (signed)
jg greater ; Jump if greater than (signed)
jb below ; Jump if below (unsigned less-than)
ja above ; Jump if above (unsigned greater-than)
jmp label ; Unconditional jump
Stack
push ax ; Push AX onto stack
push bx
pop bx ; Pop into BX
pop ax
; PUSH and POP all registers at once
pusha ; Push AX,CX,DX,BX,SP,BP,SI,DI (286+)
popa ; Pop them back
Subroutines
Loops
The LOOP instruction decrements CX and jumps if CX ≠ 0:
Segmented memory
The 8086 addresses memory using segment:offset pairs. The physical address = (segment × 16) + offset. This gives access to up to 1 MB of RAM.
In a COM file, all four segment registers (CS, DS, ES, SS) point to the same 64 KB segment — the program segment. You can mostly ignore segmentation in small COM programs.
For programs accessing more than 64 KB of data, you'd need to switch DS/ES — but the IDE templates stay within COM constraints.
; COM file memory layout
; CS = DS = ES = SS = PSP segment
; ORG 100h because:
; $0000 to $00FF = PSP (Program Segment Prefix, set up by DOS)
; Your code starts at $0100
mov ax, ds ; DS already set correctly for COM files
DOS interrupts (INT 21h)
DOS provides services via INT 21h. Set AH to the function number, fill in other registers, then int 21h:
| AH | Function | Notes |
|---|---|---|
| 01h | Read character (echo) | Returns char in AL |
| 02h | Write character | DL = character |
| 09h | Write string | DX = address, string ends with $ |
| 0Ah | Buffered input | DX = buffer |
| 0Bh | Check key status | AL = 0 (no key) or $FF |
| 2Ch | Get system time | CH=hr, CL=min, DH=sec, DL=hundredths |
| 4Ch | Exit program | AL = return code |
; Write character 'A' to stdout
mov dl, 'A'
mov ah, 02h
int 21h
; Read a character (wait for keypress)
mov ah, 01h
int 21h ; AL = character pressed
BIOS interrupts
The PC BIOS provides low-level services below DOS:
| Interrupt | Service | Notes |
|---|---|---|
| INT 10h | Video | Set mode, write char, scroll, etc. |
| INT 16h | Keyboard | Read key, check status |
| INT 1Ah | Timer/clock | Get/set time |
INT 10h — Video BIOS
; Set video mode — 320x200, 256 colours (Mode 13h)
mov ax, 0013h ; AH=00 (set mode), AL=13h (mode 13)
int 10h
; Back to text mode
mov ax, 0003h ; Mode 3 = 80x25 text
int 10h
; Write character at cursor position
mov ah, 0Eh ; BIOS teletype output
mov al, 'A' ; Character
int 10h
Mode 13h — 320×200 256 colours
Mode 13h is the beloved VGA mode used by nearly every DOS game from 1990 onwards. The screen is a linear 64000-byte framebuffer at segment $A000:
; Set mode 13h
mov ax, 0013h
int 10h
; Point ES at video memory ($A000:0000)
mov ax, 0A000h
mov es, ax
; Write pixel — colour 15 (white) at centre (160, 100)
; Pixel address = y*320 + x
mov bx, 100*320 + 160
mov byte [es:bx], 15 ; Colour index 15 in the VGA palette
Mode 13h has a 256-colour palette that you can customise via VGA DAC registers (ports $3C8/$3C9).
VGA palette programming
; Set palette entry 15 to red (RGB each 0-63 in VGA DAC)
mov dx, 3C8h ; VGA DAC write address port
mov al, 15 ; Palette entry 15
out dx, al
mov dx, 3C9h ; VGA DAC data port
mov al, 63 ; Red = maximum
out dx, al
mov al, 0 ; Green = 0
out dx, al
mov al, 0 ; Blue = 0
out dx, al
Reading the keyboard (INT 16h)
; Wait for a keypress
mov ah, 00h
int 16h ; AH = scan code, AL = ASCII character
; Check if key is available (non-blocking)
mov ah, 01h
int 16h
jz no_key ; ZF set = no key waiting
; If key available: AH=scan code, AL=ASCII (call AH=00 to consume it)
Minimal Mode 13h demo
; Plasma-style colour cycling in Mode 13h
BITS 16
ORG 100h
; Set mode 13h
mov ax, 0013h
int 10h
; Point ES at video memory
mov ax, 0A000h
mov es, ax
main_loop:
; Fill screen with colour based on pixel position
xor di, di ; DI = 0 (start of video buffer)
xor bx, bx ; BX = pixel counter (y*320+x)
fill:
mov al, bl ; Use low byte of position as colour
stosb ; Write AL to ES:[DI] and DI++
inc bx
cmp bx, 64000 ; Done when 320*200 pixels drawn
jb fill
; Check for keypress — exit on any key
mov ah, 01h
int 16h
jz main_loop ; No key — loop
; Exit
mov ax, 4C00h
int 21h
Common mistakes
Forgetting ORG 100h — COM files must start at offset 0x100 because the first 256 bytes ($000–$0FF) of the segment are the PSP (Program Segment Prefix) set up by DOS. If you forget this, all your addresses are wrong.
Word vs byte operations — mov ax, [bx] reads a 16-bit word (two bytes). mov al, [bx] reads one byte. Getting these mixed up is the most common x86 beginner bug.
Segment register surprises — in a COM file, CS=DS=ES=SS. But if you ever change DS or ES (e.g., pointing ES at video memory), remember to reload it from CS when accessing your own data.
String terminator for INT 21h/09h — the print-string function expects the string to end with $ (ASCII 36), not a null byte. NULL-terminated strings are for C; DOS uses $-terminated ones.
Stack direction — the x86 stack grows downward. PUSH AX decrements SP by 2, then writes AX. This is the same direction as the 6502 and Z80.
See also
- 6502 assembly — for comparison with the 8-bit world
- Z80 assembly — Z80 for more cross-reference
- IDE getting started