DEC subtracts 1 from a byte or word register or memory operand. Like INC, it has a one-byte encoding for the 16-bit registers, and a memory operand needs BYTE PTR or WORD PTR when its size is not otherwise known: DEC WORD PTR [BX].
Flags
OF, SF, ZF, AF and PF are set as SUB x, 1 would set them; CF is left unchanged. ZF = 1 when the result is 0, which is what counting loops test. SF = 1 when the value has gone below zero (as signed), and OF = 1 when a signed value steps from 80h to 7Fh (or 8000h to 7FFFh). Going from 0 to FFh does not set CF.
Counting down
DEC followed by JNZ is the basic counted loop. It works with any register, not only CX, and the counter can be a byte. The example prints 54321.
MOV CL, 5AGAIN1:MOV AL, CLADD AL, '0' ; digit -> characterMOV DL, ALMOV AH, 02h ; DOS: print DLINT 21hDEC CL ; ZF = 1 when it reaches 0JNZ AGAIN1
With the counter in CX, LOOP does the same decrement and jump in one instruction (and does not change any flags at all). DEC CX / JNZ is useful when the loop body needs the flags, or when another register holds the count.
Pitfalls
A loop that starts with the counter at 0 runs 256 (byte) or 65536 (word) times, because the first DEC wraps it to FFh or FFFFh. Check for 0 before entering the loop when that can happen. And since CF is not set on wrap-around, JC after DEC tests a stale flag; use JS or JZ instead.
Later CPUs
As with INC, the one-byte register forms (48h–4Fh) became REX prefixes in 64-bit mode.