汇编语言符号扩展指令及应用示例

1. 什么是符号扩展?为什么要用符号扩展?

       所谓符号扩展,就是将数据的表示大小加倍,数值仍保持不变,即将符号位扩展到同样大小的寄存器空间中去,由两部分构成一个比原值表示大一倍的数。正数必须要0扩展,负数必须用1扩展。

为什么要进行符号扩展呢?有些指令对操作数位数的要求,例如倍长于除数的被除数,再如将数据位数加长以减少计算过程中的误差。另外,除法运算中规定

除数是BYTE,被除数AX,商存放在AL,余数放在AH

除数是WORD,被除数DX:AX,商存放在AX,余数放在DX

除数是DWORD,被除数EDX:EAX,商存放在EAX,余数放在EDX

被除数总是除数和商的两倍大小,由于余数的符号取决于被除数的符号,计算之后的余数存放在AH,DX,或者EDX中,那么执行除法之前,先将被除数的符号扩展到余数存放寄存器中,这也要求符号扩展。

        事实上,在汇编语言里面,有符号数与无符号数计算,很多地方都分成了两套指令,进位与符号操作,都交由程序员来判断和操作,符号扩展正是这一设计思想的体现。

2. 符号扩展指令及应用示例

2.1  CBW(Convert Byte to Word):

规定将AL中的符号扩展到AX中,这是规定死的,执行这个指令就将AL中的符号扩展到AX中了。通过符号扩展,将AL的操作数大小扩大1倍,字节表示的数用字表示,将结果存入AX中。AL中的符号位第7位,复制到AH中的每一位。例如

XOR EAX,EAX

MOV AL,-128

此时EAX=0x80二进制10000000

CBW

此时EAX=0xFF80进制11111111 10000000符号位1,高8位用符号位1全部填充。

2.2  CWD(Convert Word to Doubleword):

规定将AX中的符号扩展到DX中,执行这个指令就将AX中的符号扩展到DX中了,那为什么不直接将AX中的符号扩展到EAX中,而要那么麻烦的扩展到DX中,再用DX:AX来表示一个有符号数呢,例如,后面的CWDE就是这么干的?估计是在符号扩展指令出来时,还没有32位机,如8086就是16cpu,也就没有EAX寄存器,只能用两个16位寄存器来表示32位数了,后续32位为了兼容16位时写的程序,也就保留了这种操作方法。另外用于在有符号除法运算中,AX存放商,DX存放余数,不将符号扩展到DX中,IDIV指令是不会将负号自动填进去的,会使用计算结果不正确。

通过符号扩展,将AX中的操作数大小扩大1倍,字表示的数用双字表示,将结果存入DX:AX中,用两个字寄存器存储1个双字的数。例如

XOR EAX,EAX

XOR EDX,EDX

MOV AX,-32768

此时AX=0x8000二进制10000000 00000000

CWD

此时AX=0x8000进制10000000 00000000符号位1,而DX=0xFFFF,即符号位1被填充到了DX。

2.3  CDQ(Convert Doubleword to Quadword):

规定将EAX中的符号扩展到EDX中,执行这个指令就将EAX中的符号扩展到EDX中了。同样,当两个32位有符号数相乘时,符号位就超出了32位表示的范围,像前一样,用两个寄存器EDX:EAX来表示这个乘法的结果,从而得到正确的计算结果。同理,64位出现后,为了兼容32位的程序,也保留了这种操作。同样,在有符号除法运算中,EAX存放商,EDX存放余数,不将符号扩展到EDX中,IDIV指令是不会将负号自动填进去的,会使用计算结果不正确。通过符号扩展,将EAX中的操作数大小扩大1倍,双字表示的数用四字表示,将结果存入EDX:EAX中,用两个双字寄存器存储1个四字的数。例如

XOR EAX,EAX

XOR EDX,EDX

MOV EAX, -2147483648

此时EAX=0x8000 0000二进制10000000 00000000 00000000 00000000

CWD

此时EAX=0x80000000进制10000000 00000000 00000000 000000000符号位1,而EDX=0xFFFFFFFF,即符号位1被填充到了EDX。

2.4  CQO(Convert Quadword to )

     规定将RAX中的符号扩展到RDX中去,这是64cpu模式下使用的指令。用以表示128位的数据,因为两个64位数操乘法运算超过64位的范围,采用以上的表示方法。同样,在有符号除法运算中,RAX存放商,RDX存放余数,不将符号扩展到RDX中,IDIV指令是不会将负号自动填进去的,会使用计算结果不正确。通过符号扩展,将RAX中的操作数大小扩大1倍,四字表示的数用八字表示,将结果存入RDX:RAX中,用两个四字寄存器存储1个八字的数。例如

XOR RAX,RAX

XOR RDX,RDX

MOV RAX, -9223372036854775808

此时RAX=0x8000000000000000二进制

1000000000000000000000000000000000000000000000000000000000000000

CQO

此时RAX=0x8000000000000000 符号位1,而RDX=0xFFFFFFFFFFFFFFFF,即符号位1被填充到了RDX。

 

2.5  CWDE(Convert Word to Doubleword):

这个E(Extension)应该是指相对于CWD的扩展,是稍后面加的指令,CWD指令出现的时间比CWD早。规定将AX中的符号扩展到EAX中,执行这个指令就将AX中的符号扩展到EAX中了。

CWDE同CWD,只是符号位是直扩展到EAX的高16位中了。

2.6  CDQE(Convert Doubleword to Quadword):

    这个E(Extension)应该是指相对于CWQ的扩展,同样,CWD指令出现的时间比CDQE早。

规定将EAX中的符号扩展到RAX中,执行这个指令就将EAX中的符号扩展到RAX中了。

    CDQE同CDQ,符号被扩展到RAX的高32位中了。

2.7 MOVZX指令将源操作数的内容复制到目的操作数中,并将该值零扩展至16位或32位。该指令仅适用于无符号整数。

为什么要有这个指令呢,因为MOV指令不能将一个较小的操作数复制到一个较大的操作数。

类为MOV指令要求两个操作数必须一样大。比如将一个较小的操作数在寄存上执行MOV操作,则寄存器的高位没有被覆盖,这个时候我们取整个寄存器值作为MOV的结果是不对的,例如

data1 BYTE 1

data2 WORD ?

XOR EAX,EAX

MOV AX,0FFFFH

MOV AL,data1

MOV data2,AX

这个时候,data2变成了FF01,而不是1。要得到正确的结果,可以有多种方法

比如,先将高位清零

XOR EAX,EAX

MOV AL,data1

MOV data2,AX

这样data2=1

或者就直接使用这个指令

MOV AX,0FFFFH

MOVZX AX,data1

MOV data2,AX

2.8 MOVSX指令将源操作数的内容复制到目的操作数中,并将该值符号扩展至16位或32位。该指令仅适用于有符号整数。例如

data1 SBYTE -1

data2 SWORD ?

XOR RAX,RAX

MOV AL,data1

MOV data2,AX

这个时候,data2的值为255了,而不是-1,也就是MOV并没有把FF当成有符号数,而是当作值了,那么用MOVSZ就不一样了。

XOR RAX,RAX

MOVSX AX,data1

MOV data2,AX

这个时候data2=-1,是正确的结果。

你可能感兴趣的:(汇编语言)