Art of Assembly/Win32 Edition is now available. Let me read that version.
PLEASE: Before emailing me asking how to get a hard copy of this text, read this.
Important Notice: As you have probably discovered by now, I am no longer updating this document. The reason is quite simple: I'm working on a Windows version of "The Art of Assembly Language Programming". In the past I have encouraged individuals to send me corrections to this text. However, as I am no longer updating this material, don't expect those correctioins to appear in a future release. I am collecting errata that I will post to Webster someday, so feel free to continue sending corrections to AoA/DOS (16-bit) to rhyde@cs.ucr.edu. If you're more interested in leading edge material, please see the information about the Win/32 edition, above.
movs
, cmps, scas, lods,
and stos[1]
. They are the string primitives since you can build most other string operations from these five instructions. How you use these five instructions is the topic of the next several sections.
movs
instruction moves a sequence of bytes from one memory location to another. The cmps
instruction compares two blocks of memory. The scas
instruction scans a block of memory for a particular value. These string instructions often require three operands, a destination block address, a source block address, and (optionally) an element count. For example, when using the movs
instruction to copy a string, you need a source address, a destination address, and a count (the number of string elements to move).si
(source index) register,
di
(destination index) register,
cx
(count) register,
ax
register, and
For example, one variant of the movs
(move string) instruction copies a string from the source address specified by ds:si
to the destination address specified by es:di
, of length cx
. Likewise, the cmps
instruction compares the string pointed at by ds:si
, of length cx
, to the string pointed at by es:di
.
Not all instructions have source and destination operands (only movs
and cmps
support them). For example, the scas
instruction (scan a string) compares the value in the accumulator to values in memory. Despite their differences, the 80x86's string instructions all have one thing in common - using them requires that you deal with two segments, the data segment and the extra segment.
movs
instruction, for example, will move a single byte, word, or double word. When executed by itself, the movs
instruction ignores the value in the cx
register. The repeat prefixes tell the 80x86 to do a multi-byte string operation. The syntax for the repeat prefix is:
Field: Label repeat mnemonic operand ;comment For MOVS: rep movs {operands} For CMPS: repe cmps {operands} repz cmps {operands} repne cmps {operands} repnz cmps {operands} For SCAS: repe scas {operands} repz scas {operands} repne scas {operands} repnz scas {operands} For STOS: rep stos {operands}
You don't normally use the repeat prefixes with the lods
instruction.
As you can see, the presence of the repeat prefixes introduces a new field in the source line - the repeat prefix field. This field appears only on source lines containing string instructions. In your source file:
When specifying the repeat prefix before a string instruction, the string instruction repeats cx
times[2]. Without the repeat prefix, the instruction operates only on a single byte, word, or double word.
You can use repeat prefixes to process entire strings with a single instruction. You can use the string instructions, without the repeat prefix, as string primitive operations to synthesize more powerful string operations.
The operand field is optional. If present, MASM simply uses it to determine the size of the string to operate on. If the operand field is the name of a byte variable, the string instruction operates on bytes. If the operand is a word address, the instruction operates on words. Likewise for double words. If the operand field is not present, you must append a "B", "W", or "D" to the end of the string instruction to denote the size, e.g., movsb
, movsw
, or movsd
.
si, di, si
, and ax
registers, one other register controls the 80x86's string instructions - the flags register. Specifically, the direction flag in the flags register controls how the CPU processes strings.si
and di
after operating upon each string element. For example, if the direction flag is clear, then executing movs
will move the byte, word, or double word at ds:si
to es:di
and will increment si
and di
by one, two, or four. When specifying the rep
prefix before this instruction, the CPU increments si
and di
for each element in the string. At completion, the si
and di
registers will be pointing at the first item beyond the string.si
and di
after processing each string element. After a repeated string operation, the si
and di
registers will be pointing at the first byte or word before the strings if the direction flag was set.cld
(clear direction flag) and std
(set direction flag) instructions. When using these instructions inside a procedure, keep in mind that they modify the machine state. Therefore, you may need to save the direction flag during the execution of that procedure. The following example exhibits the kinds of problems you might encounter:
StringStuff: cld <do some operations> call Str2 <do some string operations requiring D=0> . . . Str2 proc near std <Do some string operations> ret Str2 endp
This code will not work properly. The calling code assumes that the direction flag is clear after Str2
returns. However, this isn't true. Therefore, the string operations executed after the call to Str2
will not function properly.
There are a couple of ways to handle this problem. The first, and probably the most obvious, is always to insert the cld
or std
instructions immediately before executing a string instruction. The other alternative is to save and restore the direction flag using the pushf
and popf
instructions. Using these two techniques, the code above would look like this:
Always issuing cld
or std
before a string instruction:
StringStuff: cld <do some operations> call Str2 cld <do some string operations requiring D=0> . . . Str2 proc near std <Do some string operations> ret Str2 endp
Saving and restoring the flags register:
StringStuff: cld <do some operations> call Str2 <do some string operations requiring D=0> . . . Str2 proc near pushf std <Do some string operations> popf ret Str2 endp
If you use the pushf
and popf
instructions to save and restore the flags register, keep in mind that you're saving and restoring all the flags. Therefore, such subroutines cannot return any information in the flags. For example, you will not be able to return an error condition in the carry flag if you use pushf
and popf
.
cmps
instruction which repeats at most the number of times specified in the cx
register.