LOOP subtracts 1 from CX and then jumps if CX is not zero. The decrement does not change any flag. Load CX with the count, put the body between a label and the LOOP, and the body runs CX times:

Prints 54321: CX counts down, and the body never sees 0.
MOV CX, 5
AGAIN1:
MOV AL, CL ; the counter itself
ADD AL, "0" ; digit -> character
MOV DL, AL
MOV AH, 02h ; DOS: print the character in DL
INT 21h
LOOP AGAIN1 ; CX - 1, back while CX <> 0

It behaves like DEC CX / JNZ except for the flags: LOOP touches none, so a flag set inside the body survives to the next round. That makes it the natural partner of ADC in multi-word additions, where CF must carry from one word to the next.

Pitfalls

Entered with CX = 0, the first decrement gives FFFFh and the loop runs 65536 times; guard with JCXZ. LOOP is SHORT only (opcode E2h, −128..+127 bytes back) and MASM will not lengthen it; when the body grows too big, switch to DEC CX / JNZ TOP1, which it can. Nested loops share CX, so save the outer count:

MOV CX, 10 ; outer count
OUTER1:
PUSH CX ; keep it
MOV CX, 20 ; inner count
INNER1:
; ... inner body ...
LOOP INNER1
POP CX ; outer count back
LOOP OUTER1

Later CPUs

In 32-bit code the 386 counts in ECX instead of CX. The instruction is otherwise unchanged and still short-range only.