Your First 8086 Program
Write, understand, and run complete assembly programs — from Hello World to arithmetic
Open interactive version (quiz + challenge)Real-world analogy
What is it?
Your first 8086 programs combine everything learned so far: data definitions, register operations, arithmetic, and DOS interrupts for I/O. The core pattern is: initialize segments, read input, process data, display output, and exit cleanly. Even a 'Hello World' program teaches segment setup, string termination, and interrupt calling — foundational skills for all assembly programming.
Real-world relevance
Every developer's journey in any language starts with simple I/O programs. In assembly, these teach you the actual machine operations behind high-level abstractions. When you debug a crashed program and see register dumps, or write an embedded bootloader that must print diagnostics without an OS, these fundamentals are what you rely on.
Key points
- The Minimal 8086 Program — Every .EXE program needs at minimum: .MODEL, .STACK, .CODE, a PROC with DS initialization, and an exit (INT 21h function 4Ch). Even a program that does nothing needs this skeleton to assemble and run without crashing.
- Hello World — Printing a String — DOS function 09h (INT 21h) prints a string pointed to by DS:DX. The string must end with '$'. This is the 8086 equivalent of printf('Hello World') or console.log.
- Adding Two Numbers — The simplest arithmetic: load two values into registers, ADD them, and store the result. This is the assembly version of result = a + b. The result stays in a register or goes to a memory variable.
- Displaying a Single-Digit Number — To display a number (0-9) on screen, convert it to ASCII by adding 30h (48 decimal). ASCII '0' is 30h, '1' is 31h, etc. Then use DOS function 02h to print the character in DL.
- Displaying Multi-Digit Numbers — For numbers larger than 9, you must convert to individual digits using repeated division by 10. Each remainder is a digit (least significant first). Push digits onto the stack and pop them to reverse order for display.
- Reading a Character from Keyboard — DOS function 01h reads a single character with echo (displays it on screen). The ASCII code is returned in AL. Function 08h reads without echo (for passwords). This is the assembly version of getchar().
- Reading a Single-Digit Number — When the user types '5', AL receives 35h (ASCII for '5'). Subtract 30h to get the numeric value 5. This is the reverse of the display conversion.
- Complete Add-Two-Numbers Program — A practical program that reads two single-digit numbers from the user, adds them, and displays the result. This combines input, arithmetic, and output — the three pillars of any program.
- Program Exit with Return Code — INT 21h function 4Ch terminates the program and returns a code to DOS. AL holds the return code (0 = success). The parent process or batch file can check this with ERRORLEVEL.
- INT 21h Quick Reference — The most common DOS interrupts for beginners: 01h = read char (echo), 02h = print char, 08h = read char (no echo), 09h = print string, 0Ah = buffered input, 4Ch = exit. AH always holds the function number.
Code example
; Program: Calculator — add, subtract, multiply two single-digit numbers
.MODEL SMALL
.STACK 100h
.DATA
msg1 DB 'Enter first digit (0-9): $'
msg2 DB 0Dh, 0Ah, 'Enter second digit (0-9): $'
msg3 DB 0Dh, 0Ah, 'Sum: $'
msg4 DB 0Dh, 0Ah, 'Difference: $'
msg5 DB 0Dh, 0Ah, 'Product: $'
num1 DB ?
num2 DB ?
.CODE
MAIN PROC
MOV AX, @DATA
MOV DS, AX
; --- Read first number ---
MOV AH, 09h
LEA DX, msg1
INT 21h
MOV AH, 01h
INT 21h ; AL = ASCII digit
SUB AL, 30h ; convert to number
MOV [num1], AL
; --- Read second number ---
MOV AH, 09h
LEA DX, msg2
INT 21h
MOV AH, 01h
INT 21h
SUB AL, 30h
MOV [num2], AL
; --- Addition ---
MOV AH, 09h
LEA DX, msg3
INT 21h
MOV AL, [num1]
ADD AL, [num2] ; AL = sum (may be > 9)
CALL print_al ; display result
; --- Subtraction ---
MOV AH, 09h
LEA DX, msg4
INT 21h
MOV AL, [num1]
SUB AL, [num2] ; AL = difference
CALL print_al
; --- Multiplication ---
MOV AH, 09h
LEA DX, msg5
INT 21h
MOV AL, [num1]
MOV BL, [num2]
MUL BL ; AX = product
CALL print_al
; --- Exit ---
MOV AH, 4Ch
INT 21h
MAIN ENDP
; Procedure: print number in AL (0-99)
print_al PROC NEAR
CMP AL, 10
JB single_digit
; Two digits: divide by 10
XOR AH, AH ; clear AH
MOV BL, 10
DIV BL ; AL = tens, AH = ones
PUSH AX ; save ones in AH
ADD AL, 30h ; tens digit to ASCII
MOV DL, AL
MOV AH, 02h
INT 21h
POP AX
MOV AL, AH ; ones digit
single_digit:
ADD AL, 30h
MOV DL, AL
MOV AH, 02h
INT 21h
RET
print_al ENDP
END MAINLine-by-line walkthrough
- 1. MOV AX, @DATA / MOV DS, AX — initialize DS so we can access all variables defined in .DATA
- 2. MOV AH, 09h / LEA DX, msg1 / INT 21h — print the prompt 'Enter first digit (0-9): ' using DOS string output
- 3. MOV AH, 01h / INT 21h — wait for the user to type a character. The ASCII code goes into AL
- 4. SUB AL, 30h — convert ASCII to numeric value. If user typed '5' (ASCII 35h), AL becomes 5
- 5. MOV [num1], AL — save the numeric value into our data variable for later use
- 6. ADD AL, [num2] — add the two numbers. Result may exceed 9 (e.g., 7+8=15), so we need the two-digit display procedure
- 7. CALL print_al — call our procedure that handles both single and double digit display
- 8. In print_al: CMP AL, 10 / JB single_digit — if result is less than 10, just add 30h and print one character
- 9. For two digits: DIV BL divides AX by 10. AL gets the tens digit, AH gets the ones digit. Print each after converting to ASCII
- 10. MUL BL — unsigned multiply. AL * BL, result in AX. For single digits, the product fits in AL (max 9*9=81)
Spot the bug
MOV AH, 01h
INT 21h
MOV BL, AL ; BL = first digit ASCII
MOV AH, 01h
INT 21h
ADD AL, BL ; 'add' the two digits
MOV DL, AL
MOV AH, 02h
INT 21hNeed a hint?
Show answer
Explain like I'm 5
Fun fact
Hands-on challenge
More resources
- 8086 Assembly Hello World (GeeksforGeeks)
- First Assembly Program Tutorial (YouTube)
- DOS INT 21h Function Reference (Wikipedia)
- emu8086 Online Tutorials (Softonic)