8086 Assembly Program to Search an Element in an Array

Array search is where assembly starts feeling like real programming — you need a pointer, a counter, a comparison, and a conditional branch, all working together. This program searches a five-element byte array for a target value and prints either “FOUND” or “NOT FOUND” using a reusable MACRO that wraps the DOS print call.

Prerequisites: Familiarity with 8086 registers, conditional jumps, and INT 21h function 09h. Read 8086 Assembly Program to Print ‘hello’ using 09H first if needed.

DATA SEGMENT
STRING1 DB 11H,22H,33H,44H,55H  ; array of 5 bytes
MSG1    DB "FOUND$"
MSG2    DB "NOT FOUND$"
SE      DB 33H                   ; element to search for
DATA ENDS

PRINT MACRO MSG
    MOV AH, 09H
    LEA DX, MSG
    INT 21H
ENDM

CODE SEGMENT
ASSUME CS:CODE, DS:DATA
START:
    MOV AX, DATA
    MOV DS, AX
    MOV AL, SE           ; AL = search target (33H)
    LEA SI, STRING1      ; SI points to start of array
    MOV CX, 05H          ; loop counter = 5 (all elements)
UP:
    MOV BL, [SI]         ; load current array element
    CMP AL, BL           ; compare with target
    JZ  FO               ; match found? jump to FO
    INC SI               ; advance to next element
    DEC CX
    JNZ UP               ; loop if more elements remain
    PRINT MSG2           ; not found
    JMP END1
FO:
    PRINT MSG1           ; found
END1:
    INT 3
CODE ENDS
END START
⚠️ Bug fixed — original used MOV CX, 04H instead of 05H: The original code set CX to 4, which means the loop only checks the first four elements (11H, 22H, 33H, 44H). The fifth element (55H) is never examined. Searching for SE=55H would incorrectly print “NOT FOUND”. Since the array has 5 elements, the correct counter is 05H. This has been fixed in all three versions.


Breaking down the code

The data segment defines the array STRING1 with five hex values, two messages terminated with $ (required by INT 21h function 09h), and the search target SE=33H.

The PRINT macro is a reusable block that takes a message label, loads it into DX, sets AH=09h, and calls INT 21h. Macros in MASM expand inline — every time you write PRINT MSG1, the assembler replaces it with those three instructions in place. This avoids repeating the same three lines every time you need to print something.

The search itself is textbook linear search. AL holds the target (33H). SI is a pointer that starts at the beginning of the array. Each pass: load the byte at [SI] into BL, compare with AL, jump to FO if they match, otherwise advance SI and decrement CX. After 5 iterations without a match, execution falls through to the NOT FOUND branch.

💡 Side note — why BL instead of comparing AL with [SI] directly? The 8086 CMP instruction can compare a register with a memory operand directly: CMP AL, [SI] would work and eliminates the MOV BL, [SI] step. The two-step version exists in this program probably because the author wanted a register copy for clarity, but the direct comparison is equally valid and saves a register.

Flowchart

Flowchart for 8086 array search program

Output

C:TASM>debug se.exe
-G
FOUND
AX=0924  BX=0033  CX=0002  DX=0005  SP=0000  BP=0000  SI=0002  DI=0000
DS=0B97  ES=0B87  SS=0B97  CS=0B99  IP=002D   NV UP EI PL ZR NA PE NC
0B99:002D CC            INT     3
-Q

SI=0002 tells you the match was found at offset 2 within the array — the third byte (0-indexed), which is 33H. CX=0002 means 2 iterations remained when the match was found (3 had already run). BX=0033 is the matched value still in BL.


emu8086 version

Tested with: emu8086 v4.08, Windows 10. Note: emu8086 supports MASM-style macros.

; emu8086 -- search element in array
#make_COM#
org 100h

PRINT MACRO MSG
    MOV AH, 09H
    LEA DX, MSG
    INT 21H
ENDM

start:
    MOV AL, se_val       ; load search target
    LEA SI, arr          ; SI = start of array
    MOV CX, 05H          ; 5 elements to check
up:
    MOV BL, [SI]
    CMP AL, BL
    JZ  found
    INC SI
    DEC CX
    JNZ up
    PRINT msg2
    JMP done
found:
    PRINT msg1
done:
    MOV AX, 4C00H
    INT 21H

; --- Data ---
arr    DB 11H,22H,33H,44H,55H
msg1   DB "FOUND$"
msg2   DB "NOT FOUND$"
se_val DB 33H

NASM version

NASM uses %macro / %endmacro instead of MASM’s MACRO / ENDM. The search logic is identical.

Tested with: NASM 2.16.01, DOSBox 0.74-3.

; NASM -- search element in array
; nasm -f bin search.asm -o search.com
bits 16
org  100h

; NASM macro syntax
%macro PRINT 1
    mov ah, 09h
    mov dx, %1           ; %1 = first macro argument (the label)
    int 21h
%endmacro

start:
    mov al, [se_val]     ; load search target into AL
    mov si, arr          ; SI = start of array
    mov cx, 5            ; 5 elements
up:
    mov bl, [si]
    cmp al, bl
    jz  found
    inc si
    dec cx
    jnz up
    PRINT msg2
    jmp done
found:
    PRINT msg1
done:
    mov ax, 4c00h
    int 21h

; --- Data ---
arr    db 11h,22h,33h,44h,55h
msg1   db "FOUND$"
msg2   db "NOT FOUND$"
se_val db 33h

Frequently Asked Questions

Why does the loop check 5 elements when CX starts at 5 but the array has 5 elements — does it check the last one?

Yes, all 5 are checked. The loop runs while CX > 0. On the first iteration CX=5, check element 0; CX decremented to 4. On the fifth iteration CX=1, check element 4 (55H); CX decremented to 0. JNZ UP does not fire, and execution falls to NOT FOUND (assuming no match). The original code used CX=4, which means element at index 4 (55H) was never checked — that was the bug.

What happens if the same value appears multiple times in the array?

The program prints FOUND on the first match and stops — it doesn’t continue scanning for additional occurrences. If you needed to find all positions of a value, you’d remove the JMP END1 after the print, or restructure the loop to continue after a match and accumulate results.

What is a MACRO in MASM and how is it different from a procedure?

A macro is a text-substitution mechanism — every invocation of PRINT MSG1 expands to the three instructions inline at assembly time. There is no CALL or RET; the instructions are literally copied into the code at each use site. A procedure (subroutine) uses CALL and RET: execution jumps to the subroutine, runs it, then returns. Macros have no call overhead but increase code size with each use. Procedures add a small call/return overhead but exist only once in the binary.

“Searching for a byte in an array — because even assembly programs need a little hide and seek!” 😆

3 thoughts on “8086 Assembly Program to Search an Element in an Array”

Leave a Reply

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