LOOPNE subtracts 1 from CX, leaving the flags alone, and jumps if CX ≠ 0 and ZF = 0. With a CMP just before it, the loop runs while the items do not match and stops on the first match or when the count is used up: a search. LOOPNZ is the same instruction (opcode E0h).
CLDMOV SI, OFFSET LETTERS1MOV CX, 5SCAN1:LODSB ; AL = next letter (flags untouched)CMP AL, "D"LOOPNE SCAN1 ; on while not found and CX <> 0JNE NOTF1 ; ZF = 0: ran out without a matchMOV AL, 5SUB AL, CL ; position of the D: 4ADD AL, "0" ; digit -> characterMOV DL, ALMOV AH, 02h ; DOS: print the character in DLINT 21hJMP DONE1NOTF1:MOV DL, "-"MOV AH, 02h ; DOS: print the character in DLINT 21hDONE1:LETTERS1 DB "ABCDE"
Pitfalls
Decide found or not found with ZF (JE / JNE right after the loop). CX is 0 both when nothing matched and when the match was the last item. Anything between the CMP and the LOOPNE that changes ZF, such as INC SI, breaks the loop; that is why LODSB is convenient. Guard a count that may be 0 with JCXZ, and keep the body within the short range. For bytes in memory, REPNE SCASB performs the same search in one instruction.