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
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.
Related Links:
- 8086 Assembly Program to Print ‘hello’ using 09H — INT 21h function 09h used in the PRINT macro
- 8086 Assembly Program to Display String ‘hello’
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.
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

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!” 😆
This program is not working….plzz don’t try…
working
Yep..