Data Transfers, Addressing, and Arithmetic
4.1 Data Transfer Instructions
4.1.2 Operand Types
Chapter 3 introduced x86 instruction formats:
[label:] mnemonic [operands][ ; comment ]
Instructions can have zero, one, two, or three operands. Here, we omit the label and comment fields for clarity: mnemonic mnemonic [destination] mnemonic [destination],[source] mnemonic [destination],[source-1],[source-2]
There are three basic types of operands:
• Immediate—uses a numeric literal expression
• Register—uses a named register in the CPU
• Memory—references a memory location
4.1.3 Direct Memory Operands
Variable names are references to offsets within the data segment. For example, the following declaration for a variable named var1 says that its size attribute is byte and it contains the value 10 hexadecimal:
var1 BYTE 10h
We can write instructions that dereference (look up) memory operands using their addresses. Suppose var1 were located at offset 10400h. The following instruction copies its value into the AL register:
mov al var1
It would be assembled into the following machine instruction:
The first byte in the machine instruction is the operation code (known as the opcode). The remaining part is the 32-bit hexadecimal address of var1. Although it might be possible to write programs using only numeric addresses, symbolic names such as var1 make it easier to reference memory.
Alternative Notation. Some programmers prefer to use the following notation with direct operands because the brackets imply a dereference operation:
MASM permits this notation, so you can use it in your own programs if you want. Because so many programs (including those from Microsoft) are printed without the brackets, we will only use them in this book when an arithmetic expression is involved:
mov al,[var1 + 5]
(This is called a direct-offset operand, a subject discussed at length in Section 4.1.8.)
4.1.4 MOV Instruction
MOV is very flexible in its use of operands, as long as the following rules are observed:
• Both operands must be the same size.
• Both operands cannot be memory operands.
• The instruction pointer register (IP, EIP, or RIP) cannot be a destination operand.
Here is a list of the standard MOV instruction formats:
Memory to Memory a single MOV instruction cannot be used to move data directly from one memory location to another. Instead, you must move the source operand’s value to a register before assigning its value to a memory operand:
var1 WORD ? var2 WORD ?
mov ax,var1 mov var2,ax
You must consider the minimum number of bytes required by an integer constant when copying it to a variable or register. For unsigned integer constant sizes, refer to Table 1-4 in Chapter 1. For signed integer constants, refer to Table 1-7.
Overlapping Values (page 98 skipped)
4.1.5 Zero/Sign Extension of Integers
Copying Smaller Values to Larger Ones
Although MOV cannot directly copy data from a smaller operand to a larger one, programmers can create workarounds. Suppose count (unsigned, 16 bits) must be moved to ECX (32 bits). We can set ECX to zero and move count to CX:
count WORD 1
mov ecx,0 mov cx,count
What happens if we try the same approach with a signed integer equal to -16?
.data signedVal SWORD -16 ; FFF0h (-16)
mov ecx,0 mov cx,signedVal ; ECX = 0000FFF0h (+65,520)
The value in ECX (-65,520) is completely different from -16. On the other hand, if we had filled ECX first with FFFFFFFFh and then copied signedVal to CX, the final value would have been correct:
mov ecx,0FFFFFFFFh mov cx,signedVal ; ECX = FFFFFFF0h (-16)
The effective result of this example was to use the highest bit of the source operand (1) to fill the upper 16 bits of the destination operand, ECX. This technique is called sign extension. Of course, we cannot always assume that the highest bit of the source is a 1.