HALICERY

free-time coding, hardware dev, articles

Top
Home 8042 Blogs About
Home IntelEssential 16/32-bit Instructions LOOP

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

  1. LOOP decrements the count register without changing any of the flags (not to alter ZF in case)
  2. 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
  3. 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:

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