尽管 MOV 指令不能直接将较小的操作数复制到较大的操作数中,但是程序员可以想办法解决这个问题。假设要将 count(无符号,16 位)传送到 ECX(32 位),可以先将 ECX 设置为 0,然后将 count 传送到 CX:
.data count WORD 1 .code mov ecx,0 mov cx,count
如果对一个有符号整数 -16 进行同样的操作会发生什么呢?
.data signedVal SWORD -16 ; FFF0h (-16) .code mov ecx,0 mov cx,signedVal ; ECX = 0000FFF0h(+ 65,52 0)
ECX 中的值(+65 520)与 -16 完全不同。但是,如果先将 ECX 设置为 FFFFFFFFh,然后再把 signedVal 复制到 CX,那么最后的值就是完全正确的:
mov ecx,0FFFFFFFFh mov cx,signedVal ;ECX = FFFFFFF0h(-16)
本例的有效结果是用源操作数的最高位(1)来填充目的操作数 ECX 的高 16 位,这种技术称为符号扩展(sign extension)。当然,不能总是假设源操作数的最高位是 1。幸运的是,Intel 的工程师在设计指令集时已经预见到了这个问题,因此,设置了 MOVZX 和 MOVSX 指令来分别处理无符号整数和有符号整数。
MOVZX 指令
MOVZX 指令(进行全零扩展并传送)将源操作数复制到目的操作数,并把目的操作数 0 扩展到 16 位或 32 位。这条指令只用于无符号整数,有三种不同的形式:
MOVZX reg32,reg/mem8
MOVZX reg32,reg/mem16
MOVZX reg16,reg/mem8
在三种形式中,第一个操作数(寄存器)是目的操作数,第二个操作数是源操作数。注意,源操作数不能是常数。下例将二进制数 1000 1111 进行全零扩展并传送到 AX:
.data byteVal BYTE 10001111b .code movzx ax,byteVal ;AX = 0000000010001111b
下图展示了如何将源操作数进行全零扩展,并送入 16 位目的操作数。
下面例子的操作数是各种大小的寄存器:
mov bx, 0A69Bh movzx eax, bx ;EAX = 0000A69Bh movzx edx, bl ;EDX = 0000009Bh movzx cx, bl ;CX = 009Bh
下面例子的源操作数是内存操作数,执行结果是一样的:
.data byte1 BYTE 9Bh word1 WORD 0A69Bh .code movzx eax, word1 ;EAX = 0000A69Bh movzx edx, byte1 ;EDX = 0000009Bh movzx ex, byte1 ;CX = 009Bh
MOVSX 指令
MOVSX 指令(进行符号扩展并传送)将源操作数内容复制到目的操作数,并把目的操作数符号扩展到 16 位或 32 位。这条指令只用于有符号整数,有三种不同的形式:
MOVSX reg32, reg/mem8
MOVSX reg32, reg/mem16
MOVSX reg16, reg/mem8
操作数进行符号扩展时,在目的操作数的全部扩展位上重复(复制)长度较小操作数的最高位。下面的例子是将二进制数 1000 1111b 进行符号扩展并传送到 AX:
.data byteVal BYTE 10001111b .code movsx ax,byteVal ;AX = 1111111110001111b
如下图所示,复制最低 8 位,同时,将源操作数的最高位复制到目的操作数高 8 位的每一位上。
如果一个十六进制常数的最大有效数字大于 7,那么它的最高位等于 1。如下例所示,传送到 BX 的十六进制数值为 A69B,因此,数字“A”就意味着最高位是 1。(A69B 前面的 0 是一种方便的表示法,用于防止汇编器将常数误认为标识符。)
4.1 操作数类型
4.2 MOV指令
4.3 MOVZX和MOVSX指令
4.4 LAHF和SAHF指令
4.5 XCHG指令
4.6 直接偏移量操作数
4.7 汇编语言数据传送示例
4.8 加法和减法详解
4.9 OFFSET运算符
4.10 ALIGN伪指令
4.11 PTR运算符
4.12 TYPE运算符
4.13 LENGTHOF运算符
4.14 LABEL伪指令
4.15 间接寻址
4.16 JMP和LOOP指令
4.17 64位MOV指令
4.18 64位加法和减法