Last modified: Mon Jun 15 23:27:22 UTC+0200 2026 © A. Tarpai
LOOP and JCXZ
Since 8086.
8086 LOOP CX Times and JCXZ
LOOP-s are made for short, back jump loops: 8-bit relative displacement only. Can be used to execute a short iteration exactly CX times. CX=0 terminates the loop. CX is the implicit loop-counter and cannot be changed. LOOPz also checks the ZF-flag and exits when the Z-condition is not met.
Watch for CX=0, when entering the block. JCXZ – Jump if CX Zero – can be used to prevent entering the iteration with CX=0:
prev instr prev instr
| CX=0 | CX=0
JCXZ ---------->----+ JCXZ ---------->----+
| | | |
+-------> instr | +-------> instr |
| instr | | instr |
| instr | | instr |
| instr | | instr |
| | | | | |
| CX>0 | | | CX>0 | |
+------<--- LOOP | +------<--- LOOPz |
| | AND ZF=z | |
|CX=0 | | |
| | | |
next instr <----------+ next instr <----------+
LOOP rel8 LOOPz rel8
Related OPCODES:
+--------+-+-+ +------------+ | E0/E1 |0|z| | REL8 | LOOPNZ/LOOPNE <-- DEC CX and jump if CX>0 AND ZF=0 +--------+-+-+ +------------+ LOOPZ/LOOPE <-- DEC CX and jump if CX>0 AND ZF=1 +--------+-+-+ +------------+ | E2 |1|0| | REL8 | LOOP <-- DEC CX and jump if CX>0 +--------+-+-+ +------------+ +--------+-+-+ +------------+ | E3 |1|1| | REL8 | JCXZ <-- jump if CX Zero +--------+-+-+ +------------+ LOOPNZ/LOOPNE and LOOPZ/LOOPE are synonyms
LOOP Operation
- LOOP decrements the count register without changing any of the flags (not to alter ZF in case)
- Conditions are checked for the form of LOOP being used. If the conditions are met, a short jump is made to the label given by the operand to LOOP
- Otherwise fetch next instruction
prev instr prev instr
| |
______|___________ ______|___________________________
| | | | | |
| DEC CX | | DEC CX |
| | CX>0 | | | CX>0 ZF=z |
| TEST CX ------|--> JMP rel8 | TEST CX ------> TEST ZF -------|--> JMP rel8
| | | | | | |
| |CX=0 | | |CX=0 |ZF<>z |
| | | | | | |
| | | | +---------<-----+ |
|______|___________| |______|___________________________|
| |
| |
next instr next instr
LOOP LOOPz
386 LOOP ECX Times and JECXZ
Same old opcodes. Still only short jumps. Except that ECX is used in native 32-bit mode as the count register.
Both address-size- and operand-size are honored:
Operand-size determines EIP or IP used for the relative short jump (EIP HI zeroed) just like for JMP:
operand-size = 32 operand-size = 16
D=1 or D=0 and 66h D=0 or D=1 and 66h
+-------------------+ +-------------------+
| DWORD | EIP | DWORD | EIP
+-------------------+ +-------------------+
+--------------+----+ +--------------+----+
+ |ssssssssssss<-|s | REL8 + |ssssssssssss<-|s | REL8
+--------------+----+ +--------------+----+
| |
v v
+-------------------+ +---------+---------+
| | EIP | 0 0 0 0 | | EIP
+-------------------+ +---------+---------+
This is for 8086-emulation to wrap-aroung 64K, not something we frequently override in code.. but it is possible and the operation confirmed (see GitHub). Now this can be fun to jump almost 64KB (+/- 128 bytes) with a REL8 IP-relativ (when IP is around 0 – or around FFFF).
Address-size attribute determines ECX register portion used to decrement and test (ECX or CX):
address-size = 32 address-size = 16
D=1 or D=0 and 67h D=0 or D=1 and 67h
+-------------------+ +---------+---------+
| | ECX | . . . . | CX | ECX
+-------------------+ +---------+---------+
Decrement ECX Decrement CX
And Test ECX And Test CX
ECX HI unchanged
; LOOP on ECX ; LOOP on CX
[BITS 32] [BITS 16]
E2 FF loop -1 E2 FF LOOP -1
[BITS 16] [BITS 32]
67 E2 FF A32 LOOP -1 67 E2 FF a16 loop -1
Makes it is possible to use a 32-bit ECX counter in 16-bit code:
[BITS 16]
66 B9 05000100 MOV ECX, 0x0001_0005
.e:
67 E2 FD A32 LOOP .e
ECX = 0000_0000
Or use CX part only in 32-bit code (leaving ECX HI unchanged):
[BITS 32]
B9 05000100 mov ecx, 0x0001_0005
.e:
67 E2 FD a16 loop .e
ecx = 0001_0000
386 JECXZ
Same opcode, E3. Still only short jumps. Here we have different mnemonics (no need to specify 67h prefix).
JCXZ - Jump on CX Zero
JECXZ - Jump on ECX Zero
Similar to LOOP both address-size- and operand-size are honored:
- Operand size determines EIP or IP used for the relative short jump (EIP HI zeroed) just like for JMP (see GitHub).
- Address-size attribute determines ECX register portion used to test (ECX or CX)
Can be handy to test CX/ECX for zero in both 16/32-bit code:
address-size = 32 address-size = 16
D=1 or D=0 and 67h D=0 or D=1 and 67h
+-------------------+ +---------+---------+
| | ECX | . . . . | CX | ECX
+-------------------+ +---------+---------+
Test ECX Test CX
; Jump on ECX Zero ; Jump on CX Zero
[BITS 32] [BITS 16]
E3 FF jecxz -1 E3 FF JCXZ -1
[BITS 16] [BITS 32]
67 E3 FF JECXZ -1 67 E3 FF jcxz -1