8086 Assembly Program to Display String ‘hello’

There are two ways to print a string in 8086 DOS assembly. One is the clean, one-call approach using INT 21h function 09h. The other — shown here — is the character-by-character loop using function 02h. It’s more verbose, but it gives you a clear picture of how string output actually works at the hardware level: load a character into DL, call INT 21h, move to the next character, repeat. Once you understand this version, the shortcut function 09h makes a lot more sense.

Prerequisites: Basic familiarity with 8086 registers and DOS interrupts. New to assembly? Read Understanding DW and DB in 8086 Assembly first.

data segment
    msg1 db 'hello'      ; string to print (no $ terminator needed here)
data ends

code segment
assume cs:code, ds:data
start:
    mov ax, data
    mov ds, ax
    mov sp, 0d000h           ; set up stack pointer
    mov bx, offset msg1      ; BX points to first character of string
    mov cx, 0005h            ; loop 5 times (length of 'hello')
move:
    mov ah, 02h              ; INT 21h function 02h = display character in DL
    mov dl, [bx]             ; load current character into DL
    int 21h                  ; print the character
    inc bx                   ; advance pointer to next character
    dec cx                   ; decrement counter
    jnz move                 ; if CX != 0, loop back
    mov ax, 4c00h
    int 21h
code ends
end start


How it works

The data segment holds the string 'hello' as five consecutive bytes — the ASCII codes for h, e, l, l, o. There’s no $ terminator because this version doesn’t use function 09h; it counts characters manually with CX instead.

After segment setup, BX is loaded with the address of the first character (offset msg1) and CX is set to 5. Each loop iteration does three things: loads the byte at [bx] into DL, calls INT 21h with AH=02h to print that character, then advances BX and decrements CX. The jnz move keeps looping until CX hits zero.

💡 Side note — mov ah, 02h must be inside the loop: AH gets clobbered by INT 21h during execution. If you move mov ah, 02h outside the loop (before move:), it works on the first iteration but AH will be trashed by the interrupt on subsequent passes, and the wrong DOS function gets called. Keep it inside the loop.

Flowchart

Flowchart for 8086 hello string program

Output

C:TASM>debug an_hello.exe
-g
hello
Program terminated normally
-q


emu8086 version

Tested with: emu8086 v4.08, Windows 10.

; emu8086 -- print 'hello' character by character using INT 21h function 02h
#make_COM#
org 100h

start:
    mov bx, offset msg1  ; BX = address of first character
    mov cx, 0005h        ; loop counter = 5 (length of 'hello')
move:
    mov ah, 02h          ; function 02h: display character in DL
    mov dl, [bx]         ; load current character
    int 21h              ; print it
    inc bx               ; next character
    dec cx
    jnz move             ; loop until CX = 0
    mov ax, 4c00h
    int 21h

; --- Data after code ---
msg1 db 'hello'

NASM version

Tested with: NASM 2.16.01, DOSBox 0.74-3.

; NASM -- print 'hello' character by character
; nasm -f bin hello.asm -o hello.com
bits 16
org  100h

start:
    mov bx, msg1         ; BX = offset of first character
    mov cx, 5            ; 5 characters to print
move:
    mov ah, 02h
    mov dl, [bx]         ; load character from memory
    int 21h
    inc bx
    dec cx
    jnz move
    mov ax, 4c00h
    int 21h

; --- Data after code ---
msg1 db 'hello'

Function 02h vs 09h — which should you use?

Function 02h (character output) is useful when you’re printing characters one at a time — useful for formatted output, building strings dynamically, or printing values you compute at runtime. Function 09h (string output) is simpler for fixed strings since it handles the loop internally, but it requires a $ terminator and expects DX to already point to the string. For beginners, the function 02h loop is worth understanding first because it makes the mechanics visible. The function 09h version is covered in this post.


Frequently Asked Questions

Why does the string not need a $ terminator?

Because this program doesn’t use function 09h. The $ terminator is only required by INT 21h function 09h, which scans forward from DX until it finds a $ byte. This version uses a manual counter in CX instead — it prints exactly 5 characters and stops, regardless of what byte follows the string.

What does INT 21h function 02h actually do?

It outputs a single character to the standard output. The character is whatever is in DL when the interrupt is called. The function also echoes the character to the screen and handles special characters like backspace (08h) and carriage return (0Dh). After the call, AX contains the character that was printed (for confirmation), which is why AH gets modified and must be reset inside the loop.

Can I use SI instead of BX as the pointer register?

Yes. SI (Source Index) is specifically designed for indexed memory access and works just as well here. You’d write mov si, offset msg1, then mov dl, [si], then inc si. Using SI would arguably be more idiomatic for a string loop, since LODS-type thinking naturally uses SI. BX (Base Register) is more general-purpose. Both work on the 8086 — pick whichever is more readable to you.


Conclusion

The function 02h character loop is the long way to print a string — but it is also the most instructive. You can see exactly what is happening at each step: load a byte, call the interrupt, move the pointer, count down. Once you are comfortable with this pattern you will understand every other DOS output routine. The one rule to never forget: mov ah, 02h goes inside the loop, not before it, because INT 21h clobbers AH on return.


See Also

One thought on “8086 Assembly Program to Display String ‘hello’”

  1. I don’t why this stuff has to be so difficult, Everyone writes different and difficult codes, I think I will never understand this. I think I have to start from very Basic.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.