汇编语言:基于x86处理器-学习笔记-第七章
第七章学习笔记
移位和循环移位指令
《汇编语言:基于x86处理器(原书第7版)》Page 192
移动操作数的位有两种方法。
- 第一种是逻辑移位 (logic shift),空出来的位用 0 填充。如下图所示,一个字节的数据向右移动一位。也就是说,每一位都被移动到其旁边的低位上。
- 另一种移位的方法是算术移位 (arithmetic shift),空出来的位用原数据的符号位填充。
SHL 指令
逻辑左移。SHL 的第一个操作数是目的操作数,第二个操作数是移位次数:
$$ SHL \space \space destination,count $$ 该指令可用的操作数类型如下所示:1 |
|
x86处理器允许 imm8 为 0~255 中的任何整数。另外,CL 寄存器包含的是移位计数。上述格式同样适用于 SHR、SAL、SAR、ROR、ROL、RCR 和 RCL 指令。
SHR 指令
逻辑右移。SHR 的指令格式与 SHL 相同。
SAL 和 SAR 指令
SAL(算术左移)指令的操作与SHL指令一样。
每次移动时,SAL 都将目的操作数中的每一位移动到下一个最高位上。最低位用0填充;最高位移入进位标志位,该标志位原来的值被丢弃:
SAR 指令为算术右移,指令格式与 SHL 相同。
ROL 指令
以循环方式来移位即为位元循环 (Bitwise Rotation)。一些操作中,从数的一端移出的位立即复制到该数的另一端。还有一种类型则是把进位标志位当作移动位的中间点。
ROL(循环左移)指令把所有位都向左移。最高位复制到进位标志位和最低位。该指令格式与 SHL 指令相同:
位循环不会丢弃位。从数的一端循环出去的位会出现在该数的另一端。
特点
- 当循环计数值大于 1 时,进位标志位保存的是最后循环移出 MSB 的位。
- 位组交换利用 ROL 可以交换一个字节的高四位(位 4~7)和低四位(位 0~3)。例如,26h 向任何方向循环移动 4 位就变为 62h。当多字节整数以四位为单位进行循环移位时,其效果相当于一次向右或向左移动一个十六进制位。例如,将 6A4Bh 反复循环左移四位,最后就会回到初始值。
ROR 指令
ROR(循环右移)指令把所有位都向右移,最低位复制到进位标志位和最高位。该指令格式与 SHL 指令相同。
当循环计数值大于 1 时,进位标志位保存的是最后循环移出 LSB 的位。
RCL 和 RCR 指令
RCL(带进位循环左移)指令把每一位都向左移,进位标志位复制到 LSB,而 MSB 复制到进位标志位:
RCR(带进位循环右移)指令把每一位都向右移,进位标志位复制到 MSB,而 LSB 复制到进位标志位:
SHLD 和 SHRD 指令
SHLD(双精度左移)指令将目的操作数向左移动指定位数。移动形成的空位由源操作数的高位填充。源操作数不变,但是符号标志位、零标志位、辅助进位标志位、奇偶标志位和进位标志位会受影响:
$$
SHLD \space \space dest, source, count
$$
下图展示的是 SHLD 执行移动一位的过程。源操作数的最高位复制到目的操作数的最低位上。目的操作数的所有位都向左移动:
SHRD(双精度右移)指令将目的操作数向右移动指定位数。移动形成的空位由源操作数的低位填充:
SHLD 和 SHRD 的指令格式中,目标操作数可以是寄存器或内存操作数;源操作数必须是寄存器;移位次数可以是 CL 寄存器或者 8 位立即数。
程序示例
《汇编语言:基于x86处理器(原书第7版)》Page 200
乘法和除法指令
《汇编语言:基于x86处理器(原书第7版)》Page 201
MUL 指令
32位模式下,MUL(无符号数乘法)指令有三种类型:
- 第一种执行 8 位操作数与 AL 寄存器的乘法
- 第二种执行16位操作数与 AX 寄存器的乘法
- 第三种执行 32 位操作数与 EAX 寄存器的乘法
乘数和被乘数的大小必须保持一致,乘积的大小则是它们的一倍。这三种类型都可以使用寄存器和内存操作数,但不能使用立即数。
1 |
|
MUL 指令中的单操作数是乘数。下图按照乘数的大小,列出了默认的被乘数和乘积。
由于目的操作数是被乘数和乘数大小的两倍,因此不会发生溢出。
如果乘积的高半部分不为零,则 MUL 会把进位标志位和溢出标志位置 1。因为进位标志位常常用于无符号数的算术运算。
例如,当 AX 乘以一个 16 位操作数时,乘积存放在 DX 和 AX 寄存器对中。其中,乘积的高 16 位存放在 DX,低 16 位存放在 AX。如果 DX 不等于零,则进位标志位置 1,这就意味着隐含的目的操作数的低半部分容纳不了整个乘积。
IMUL 指令
IMUL(有符号数乘法)指令执行有符号整数乘法。与 MUL 指令不同,IMUL 会保留乘积的符号,实现的方法是,将乘积低半部分的最高位符号扩展到高半部分。x86指令集支持三种格式的 IMUL 指令:单操作数、双操作数和三操作数。
单操作数格式
单操作数格式中,乘数和被乘数大小相同,而乘积的大小是它们的两倍。单操作数格式将乘积存放在 AX、DX:AX 或 EDX: EAX 中:
和 MUL 指令一样,其乘积的存储大小使得溢出不会发生。同时,如果乘积的高半部分不是其低半部分的符号扩展,则进位标志位和溢出标志位置1。利用这个特点可以决定是否忽略乘积的高半部分。
双操作数格式(32位模式)
32 位模式中的双操作数 IMUL 指令把乘积存放在第一个操作数中,这个操作数必须是寄存器。第二个操作数(乘数)可以是寄存器、内存操作数和立即数。
$$
IMUL \space \space reg, \space reg/mem/imm
$$
双操作数格式会按照目的操作数的大小来截取乘积。如果被丢弃的是有效位则溢出标志位和进位标志位置 1。因此,在执行了有两个操作数的 IMUL 操作后,必须检查这些标志位中的一个。
三操作数格式
32 位模式下的三操作数格式将乘积保存在第一个操作数中。第二个操作数可以是16位寄存器或内存操作数,它与第三个操作数相乘,该操作数是一个8位或16位立即数。
$$
IMUL \space \space reg, \space reg/mem, \space imm
$$
IMUL 执行时,若乘积有效位被丢弃,则溢出标志位和进位标志位置 1。因此,在执行了有三个操作数的 IMUL 操作后,必须检查这些标志位中的一个。
DIV 指令
32 位模式下,DIV(无符号除法)指令执行 8 位、16 位和 32 位无符号数除法。其中,单寄存器或内存操作数是除数。格式如下:
$$
DIV\space\space reg/mem
$$
下图给出了被除数、除数、商和余数之间的关系:
64 位模式下,DIV 指令用 RDX:RAX 作被除数,用 64 位寄存器和内存操作数作除数商存放到 RAX,余数存放在 RDX 中。
IDIV 指令
IDIV(有符号除法)指令执行有符号整数除法,其操作数与 DIV 指令相同。执行 8 位除法之前,被除数 (AX) 必须完成符号扩展。余数的符号总是与被除数相同。
扩展加减法
《汇编语言:基于x86处理器(原书第7版)》Page 212
ADC 指令
ADC(带进位加法)指令将源操作数和进位标志位的值都与目的操作数相加。该指令格式与 ADD 指令一样,且操作数大小必须相同:
1 |
|
程序示例
下面的代码示例调用 Extended_Add,并向其传递两个 8 字节的整数。要注意为和数多分配一个字节:
上述程序的输出,加法产生了一个进位:0122C32B0674BB5736
SBB 指令
SBB(带借位减法)指令从目的操作数中减去源操作数和进位标志位的值。允许使用的操作数与 ADC 指令相同。