HALICERY

free-time coding, hardware dev, articles

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

Last modified: Thu Jun 18 07:11:46 UTC+0200 2026 © A. Tarpai


x86 String Instructions and REP

8086 String Instructions and REP

For fast memcopy and memfill (MOVS, STOS) of bytes or words.
Compare strings (CMPS), scan string (SCAS) f. ex. to find trailing zero, fast strlen.

One-byte opcodes:

+----------+-+                      +----------+-+
|          |z|                      |          |w|
+----------+-+                      +----------+-+
  REP PREFIX                    MOVS CMPS STOS LODS SCAS

F2   z=0 REPNE/REPNZ                w = 0 BYTE
F3   z=1 REP/REPE/REPZ              w = 1 WORD

Implicit use of SI, DI pointers with default SEGREG:

(DS):SI --> ES:DI

SI/DI automatically incremented or decremented by operand size of 1 or 2 after the operation (post-inc/dec). The only instructions on Intel with this addressing behaviour. Based on DF flag, 0 means increment (CLD/STD).

REP: ZF and 'z' has only meaning for CMPS and SCAS. Mnemonics are such that it is for repeat, i.e. REPNE repeats when comparison is not equal (repeats when ZF=0).

Operation:

prev instr                    prev instr                                  prev instr
    |                             |                                           |
    |                           OP REP                                      OP REPz
    |                             |         CX=0                              |         CX=0
    |               +---------> TEST CX  ------>----+           +---------> TEST CX  ------>----+
    |               |             |*                |           |             |*                |
    |               |           DEC CX              |           |           DEC CX              |
    |               |             |                 |           |             |                 |
  OP STR            |           OP STR              |           |           OP STR              |
    |               |             |                 |           |             |                 |
  adjust            |           adjust              |           |           adjust              |
  SI/DI             |           SI/DI               |           |           SI/DI               |
    |               |             |                 |           | z==ZF       |                 |
    |               +------<------+                 |           +------<--- TEST ZF             |
    |                                               |                         |                 |
next instr                    next instr <----------+                     next instr <----------+


  MOVS  [DI,SI]           REP   MOVS  [DI,SI]                        REPz   CMPS  [SI,DI]
  STOS  [DI]              REP   STOS  [DI]                           REPz   SCAS  [AX,DI]
  LODS  [SI]              REP   LODS  [SI]
  CMPS  [SI,DI]
  SCAS  [AX,DI]

Showing instructions with unnecessary implicit operands
* Interrupts served here
  A repeating string operation can be suspended by an exception or interrupt.


MOVS: move from source to destination.

LODS (Load string): Loads (DS):SI --> ACCU. With REP a little meaningless, but gets last value.

STOS (Store string): moves content of ACCU --> ES:DI

CMPS (Compare Strings): subtracts the destination(!) string element at ES:DI
  from the source string element (DS):SI and updates the flags AF, SF, ZF, PF, CF and OF.

SCAS (Scan String): subtracts the destination(!) string element at ES:DI
  from AX/AL and updates the flags AF, SF, ZF, PF, CF and OF.

Repeated CMPS and SCAS instructions can be exited if the count is exhausted or if the zero flag fails the repeat condition. Tests CX or ZF for termination condition. That is find a value (ZF=1) and maximize compare length (CX).

Note: when the flag fails and REPz exits - SI/DI already adjusted to next item. Eg. find trailing zero with SCAS:

OR CX, -1     <-- set CX to max
MOV AL, 0
REPNE SCASB   <-- repeat for not equal


h  e  l  l  o  \0  .

^                  ^
|                  |
DI                DI points
             to byte following \0


NOT CX        <-- CX=-7 --> CX=6: can be used to strcpy including trailing zero
set si/di..
REP MOVSB

386 String Instructions and REP

Both address- (67) and operand size (66) matters. Orthogonal extension, eg. we can memcopy anywhere using 16-bit RM code (after limit extend) by using 66/67.

rep movs (w=1) with NASM examples:

[BITS 16] [BITS 32] operand-size address-size
opcode syntax opcode syntax Move Adjust Register by Address Register REP test and decrement
F3 A5 REP MOVSW 67 66 F3 A5 a16 rep movsw WORD 2 SI/DI CX
66 F3 A5 REP MOVSD 67 F3 A5 a16 rep movsd DWORD 4 SI/DI CX
67 F3 A5 A32 REP MOVSW 66 F3 A5 rep movsw WORD 2 ESI/EDI ECX
67 66 F3 A5 A32 REP MOVSD F3 A5 rep movsd DWORD 4 ESI/EDI ECX

66 operand-size attribute:

67 address-size attribute:

Operation:

         address-size = 32                    address-size = 16
         D=1 or D=0 and 67h                   D=0 or D=1 and 67h

REP

        +-------------------+                +---------+---------+
        |                   | ECX            | . . . . |    CX   | ECX
        +-------------------+                +---------+---------+

         Test ECX                             Test CX
         Decrement ECX                        Decrement CX
                                              ECX HI unchanged (.)

STRING OP

        +-------------------+                +---------+---------+
        |                   | ESI/EDI        | . . . . |  SI/DI  | ESI/EDI
        +-------------------+                +---------+---------+

                  |                                    |
                  v                                    v
        +-------------------+                +---------+---------+
        |                   |                | 0 0 0 0 |         |
        +-------------------+                +---------+---------+
        +-------------------+                +-------------------+
        |      SR:BASE      |                |      SR:BASE      |
    +   +-------------------+                +-------------------+
    ________________________________________________________________
        +-------------------+                +-------------------+
        | EFFECTIVE ADDRESS |                | EFFECTIVE ADDRESS |
        +-------------------+                +-------------------+


ADJUST

        +-------------------+                +---------+---------+
        |                   | ESI/EDI        | . . . . |  SI/DI  | ESI/EDI
        +-------------------+                +---------+---------+

         Inc/Dec ESI/EDI                      Inc/Dec SI/DI
                                              SI/DI HI unchanged (.)

Note that REP is also a prefix - not an instruction. So CX/SI/DI or ECX/ESI/EDI are affected together by address-size.

186/286 INS/OUTS

186/286 added INS/OUTS string I/O and can be used with REP: pumping bytes or words through an I/O port and continuous memory area pointed by SI/DI.

One-byte opcodes, variable port in DX only. Thus every operand is implicit:

0 1 1 0 1 1 0 W    INS   [ES:DI], [DX=PORT]
0 1 1 0 1 1 1 W    OUTS  [DX=PORT], [DS:SI]

Same operation as string instructions:

Assembler syntax:

6C/6D     [REP]  INSB/INSW
6E/6F     [REP]  OUTSB/OUTSW

The only thing changeable is segment-override for DS:SI (then full syntax is accepted).

386 INS/OUTS

Honors both operand- and address-size (similar to other string instructions).

Operand-size determines port width, similar to IN/OUT:

[rep]  insb/outsb      8-bit I/O port   W=0
[rep]  insw/outsw     16-bit I/O port   W=1  D=0 or D=1 and 66h
[rep]  insd/outsd     32-bit I/O port   W=1  D=1 or D=0 and 66h

Address-size attribute determines DI/EDI (and CX/ECX for REP) similar to string instructions.

Assembler syntax

VC++. Eg. writing ins defaults to ins byte ptr es:[edi],dx:

ins
6C         ins byte ptr es:[edi],dx

insb
6C         ins byte ptr es:[edi],dx

insw
66 6D      ins word ptr es:[edi],dx

insd
6D         ins dword ptr es:[edi],dx