Lesson 36 of 48 intermediate

DOS/BIOS Interrupts for Console I/O

Put interrupts to work — use DOS and BIOS services for keyboard input, screen output, and program control

Open interactive version (quiz + challenge)

Real-world analogy

DOS and BIOS interrupts are like a restaurant menu. INT 21h is the main menu (DOS services) — you pick a dish number (function in AH) and the kitchen prepares it. INT 10h is the dessert menu (BIOS video). INT 16h is the drink menu (BIOS keyboard). You do not need to know how the kitchen works (hardware details) — you just tell the waiter (CPU) your order (AH = function number) and get the result in a register.

What is it?

DOS interrupts (INT 21h) and BIOS interrupts (INT 10h, INT 16h) provide ready-made services for console I/O without needing to program hardware directly. INT 21h functions handle character input (01h, 08h), string input (0Ah), character output (02h), string output (09h), and program exit (4Ch). INT 10h provides video control (cursor, colors, modes). INT 16h provides keyboard access. These are the system API of the 8086 DOS environment.

Real-world relevance

Every DOS program — from WordPerfect to early games like DOOM — used these interrupts for I/O. BIOS interrupts are still used in modern PC boot sequences (UEFI calls are the successor). Understanding these services teaches you how system calls work: user programs request OS services through a controlled interrupt interface, which is exactly how Linux (INT 80h / SYSCALL) and Windows (INT 2Eh / SYSENTER) work today.

Key points

Code example

; Program: Interactive menu with colored output
; Demonstrates INT 21h and INT 10h together
.MODEL SMALL
.STACK 100h
.DATA
  menu   DB 0Dh, 0Ah
         DB '=== 8086 I/O Demo ===', 0Dh, 0Ah
         DB '1. Say Hello', 0Dh, 0Ah
         DB '2. Echo input', 0Dh, 0Ah
         DB '3. Colored text', 0Dh, 0Ah
         DB '4. Exit', 0Dh, 0Ah
         DB 'Choice: $'
  hello  DB 0Dh, 0Ah, 'Hello, 8086 World!', 0Dh, 0Ah, '$'
  echo_p DB 0Dh, 0Ah, 'Type a char: $'
  echo_r DB 0Dh, 0Ah, 'You typed: $'
  color_msg DB 'COLORFUL!', 0
  bye    DB 0Dh, 0Ah, 'Goodbye!', 0Dh, 0Ah, '$'

.CODE
MAIN PROC
  MOV AX, @DATA
  MOV DS, AX

menu_loop:
  ; Display menu
  MOV AH, 09h
  LEA DX, menu
  INT 21h

  ; Read choice (no echo)
  MOV AH, 08h
  INT 21h

  CMP AL, '1'
  JE opt_hello
  CMP AL, '2'
  JE opt_echo
  CMP AL, '3'
  JE opt_color
  CMP AL, '4'
  JE opt_exit
  JMP menu_loop       ; invalid — show menu again

opt_hello:
  MOV AH, 09h
  LEA DX, hello
  INT 21h
  JMP menu_loop

opt_echo:
  MOV AH, 09h
  LEA DX, echo_p
  INT 21h

  MOV AH, 01h         ; read with echo
  INT 21h              ; AL = typed character
  PUSH AX

  MOV AH, 09h
  LEA DX, echo_r
  INT 21h

  POP AX
  MOV DL, AL
  MOV AH, 02h
  INT 21h              ; display the character again
  JMP menu_loop

opt_color:
  ; Set cursor to row 12, col 30
  MOV AH, 02h
  MOV BH, 0
  MOV DH, 12           ; row
  MOV DL, 30           ; column
  INT 10h

  ; Print each char in different color
  LEA SI, color_msg
  MOV BL, 09h          ; starting color (bright blue)
print_color:
  LODSB
  CMP AL, 0
  JE color_done
  MOV AH, 09h          ; BIOS write char + attrib
  MOV BH, 0
  MOV CX, 1
  INT 10h

  ; Advance cursor manually
  PUSH AX
  MOV AH, 03h          ; get cursor position
  MOV BH, 0
  INT 10h              ; DH=row, DL=col
  INC DL               ; move right
  MOV AH, 02h          ; set cursor position
  INT 10h
  POP AX

  INC BL               ; next color
  JMP print_color
color_done:
  JMP menu_loop

opt_exit:
  MOV AH, 09h
  LEA DX, bye
  INT 21h
  MOV AX, 4C00h
  INT 21h
MAIN ENDP
END MAIN

Line-by-line walkthrough

  1. 1. menu_loop: — the program loops back here after each menu option, creating an interactive menu-driven application
  2. 2. MOV AH, 08h / INT 21h — read choice without echo, so the typed digit is not shown (cleaner UI)
  3. 3. CMP AL, '1' through '4' — dispatch based on the ASCII code of the keypress. JE jumps to the matching handler
  4. 4. opt_echo: reads a character with function 01h (echo), then prints it again with function 02h. PUSH/POP AX preserves it across the DOS print call
  5. 5. INT 10h AH=02h — BIOS service to set the cursor position. DH=row, DL=column, BH=page. This positions our colored text
  6. 6. INT 10h AH=09h — BIOS write character with attribute. BL = color attribute (foreground + background), CX = repeat count. Unlike DOS, this does NOT advance the cursor
  7. 7. INT 10h AH=03h — get current cursor position so we can manually advance DL by 1 to move right after each colored character
  8. 8. INC BL — cycles through colors for each letter, creating a rainbow effect. BL wraps through 16 foreground colors
  9. 9. LODSB / CMP AL, 0 — the color_msg string is null-terminated (not '$') because we print character by character through BIOS, not DOS
  10. 10. MOV AX, 4C00h / INT 21h — clean exit with return code 0. AH=4Ch (function), AL=00h (return code), combined into one MOV

Spot the bug

.DATA
  buf DB 10, 0, 10 DUP(0)
  msg DB 'You said: $'
.CODE
  MOV AH, 0Ah
  LEA DX, buf
  INT 21h
  MOV AH, 09h
  LEA DX, buf+2
  INT 21h
Need a hint?
What terminator does function 0Ah put at the end of input? What does function 09h need?
Show answer
Bug: Function 0Ah terminates input with CR (0Dh), not '$'. When function 09h tries to print the buffer, it does not find '$' and prints garbage past the input until it randomly hits a '$' in memory. Fix: after INT 21h (0Ah), find the end of input using the count in buf[1], and place a '$' after the last character: MOV BL, buf[1] / XOR BH, BH / MOV buf[BX+2], '$'.

Explain like I'm 5

Imagine you want to talk to people in a big building. INT 21h is the reception desk — you say 'I want service number 9' (AH=09h, print a message) and the receptionist does it. INT 10h is the building maintenance office — they control the lights and signs (screen). INT 16h is the mailroom — they check if you have any letters (keyboard input). You do not need to know how the plumbing works (hardware) — you just ask the right office (interrupt) for the right service (function number).

Fun fact

DOS function 09h uses '$' as a string terminator because CP/M (the operating system DOS was based on) used '$'. This meant you could never print a literal '$' using function 09h. The workaround was to use function 02h to print '$' as a single character. This quirk survived through the entire DOS era — an example of backward compatibility trumping good design.

Hands-on challenge

Build a complete text-mode application that: (1) clears the screen using INT 10h function 06h, (2) draws a border made of box-drawing characters (like frame using +-|), (3) accepts a name via INT 21h function 0Ah, (4) displays a personalized greeting in a specific color using INT 10h function 09h, and (5) waits for a keypress then exits. Make the greeting centered on the screen.

More resources

Open interactive version (quiz + challenge) ← Back to course: Microprocessor A–Z