汇编语言MOVZX和MOVSX指令

< 上一页MOV指令 LAHF和SAHF指令下一页 >
尽管 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 是一种方便的表示法,用于防止汇编器将常数误认为标识符。)
< 上一页MOV指令 LAHF和SAHF指令下一页 >