汇编指令总结

发布于 2021-09-10  124 次阅读


字节单位划分

在 x86 计算机中,所有数据存储的基本单位都是字节(byte),一个字节有 8 位二进制数。其他的存储单位还有字(word)(2 个字节),双字(doubleword)(4 个字节)和四字(quadword)(8 个字节)。

标志寄存器

cmp

cmp是比较指令,相当于减法指令(sub),不同的是,他不储存结果,只影响相应的标志寄存器。其他的指令通过识别这些标志寄存器来判断比较的结果。

使用格式: cmp oprd1,oprd2

相当于oprd1 - oprd2

cmp影响标志寄存器的CF,ZF,OF,AF,PF

怎么判断大小呢?

   若执行指令后:ZF=1,则说明两个数相等,因为zero为1说明结果为0.

   当无符号时:

   若CF=1,则说明了有进位或借位,cmp是进行的减操作,故可以看出为借位,所以,此时oprd1<oprd2;

   CF=0,则说明了无借位,但此时要注意ZF是否为0,若为0,则说明结果不为0,故此时oprd1>oprd2.

   当有符号时:

   若SF=0,OF=0 则说明了此时的值为正数,没有溢出,可以直观的看出,oprd1>oprd2;

   若SF=1,OF=0 则说明了此时的值为负数,没有溢出,则为oprd1<oprd2;

   若SF=0,OF=1 则说明了此时的值为正数,有溢出,可以看出oprd1<oprd2;

   若SF=1,OF=1则说明了此时的值为负数,有溢出,可以看出oprd1>oprd2;

   最后两个可以作出这种判断的原因是,溢出的本质问题:

   两数同为正,相加,值为负,则说明溢出

   两数同为负,相加,值为正,则说明溢出

   故有,正正得负则溢出,负负得正则溢出

Test和cmp指令的区别


test属于逻辑运算指令
Test对两个参数(目标,源)执行AND逻辑操作,并根据结果设置标志寄存器,结果本身不会保存。
举例:
Test的一个非常普遍的用法是用来测试一方寄存器是否为空:
test ecx, ecx
jz somewhere

如果ecx为零,设置ZF零标志为1,Jz跳转

CMP属于算术运算指令
CMP比较.(两操作数作减法,仅修改标志位,不回送结果).
cmp实际上是只设置标志不保存结构的减法,并设置Z-flag(零标志).
举例:
Cmp eax, 2; 如果eax-2=0即eax=2就设置零标志为1
Jz ****; 如果设置了零标志就跳转

============================================

得出的结论
test逻辑与运算结果为零,就把ZF(零标志)置1;
cmp 算术减法运算结果为零,就把ZF(零标志)置1.

offset

找到对应的标签,一般配合mov使用取出标签地址

跳转指令

  • jmp 无条件转移

段内短转移(移动范围:-128~127) JMP SHORT 标号
段内近转移(移动范围:-32768~32767) JMP NEAR PTR 标号
段间远转移(移动范围:内存中任意位置) JMP FAR PTR 标号

  • jcxz 如果寄存器cx的值为零,跳转到指定位置

有条件转移指令。段内段转移,范围为:-128~127

  • jnz 如果寄存器ZF的值为零,跳转到指定位置

表达式求值

乘法

分为有符号乘法(imul)和无符号乘法(mul)

无符号乘法

32 位模式下,MUL(无符号数乘法)指令有三种类型:

第一种执行 8 位操作数与 AL 寄存器的乘法;
第二种执行 16 位操作数与 AX 寄存器的乘法;
第三种执行 32 位操作数与 EAX 寄存器的乘法。

乘数和被乘数的大小必须保持一致,乘积的大小则是它们的一倍。这三种类型都可以使用寄存器和内存操作数,但不能使用立即数:

MUL reg/mem8 MUL reg/meml6 MUL reg/mem32 MUL 指令中的单操作数是乘数。下表按照乘数的大小,列出了默认的被乘数和乘积。由于目的操作数是被乘数和乘数大小的两倍,因此不会发生溢岀。

被乘数 乘数 乘积 AL reg/mem8 AX AX reg/mem16 DX:AX EAX reg/mem32 EDX:EAX

如果乘积的高半部分不为零,则 MUL 会把进位标志位和溢出标志位置 1。因为进位标志位常常用于无符号数的算术运算,在此我们也主要说明这种情况。例如,当 AX 乘以一个 16 位操作数时,乘积存放在 DX 和 AX 寄存器对中。其中,乘积的高 16 位存放在 DX,低 16 位存放在 AX。如果 DX 不等于零,则进位标志位置 1,这就意味着隐含的目的操作数的低半部分容纳不了整个乘积。

有个很好的理由要求在执行 MUL 后检查进位标志位,即,确认忽略乘积的高半部分是否安全。 MUL 示例 下述语句实现 AL 乘以 BL,乘积存放在 AX 中。由于 AH(乘积的高半部分)等于零,因此进位标志位被清除(CF=0):

mov al, 5h mov bl, 10h mul bl ; AX = 0050h, CF = 0 下图展示了寄存器内容的变化:

下述语句实现 16 位值 2000h 乘以 0100h。由于乘积的高半部分(存放于 DX)不等于零,因此进位标志位被置 1:

.data
val1 WORD 2000h
val2 WORD 0l00h
.code
mov ax, val1           ; AX = 2000h
mul val2               ; DX:AX = 00200000h, CF = 1

下述语句实现 12345h 乘以 1000h,产生的 64 位乘积存放在 EDX 和 EAX 寄存器对中。EDX 中存放的乘积高半部分为零,因此进位标志位被清除:

mov eax, 12345h mov ebx, 1000h mul ebx ; EDX:EAX = 0000000012345000h, CF = 0 下图展示了寄存器内容的变化:

在 64 位模式下使用 MUL 64 位模式下,MUL 指令可以使用 64 位操作数。一个 64 位寄存器或内存操作数与 RAX 相乘,产生的 128 位乘积存放到 RDX:RAX 寄存器中。下例中,RAX 乘以 2,就是将 RAX 中的每一位都左移一位。RAX 的最高位溢出到 RDX 寄存器,使得 RDX 的值为 0000 0000 0000 0001h:

mov rax, 0FFFF0000FFFF0000h mov rbx, 2 mul rbx ; RDX:RAX = 0000000000000001FFFE0001FFFE0000 下面的例子中,RAX 乘以一个 64 位内存操作数。该寄存器的值乘以 16,因此,其中的每个十六进制数字都左移一位(一次移动 4 个二进制位就相当于乘以 16)。

.data
multiplier QWORD 10h
.code
mov rax, OAABBBBCCCCDDDDh
mul multiplier       ; RDX:RAX = 00000000000000000AABBBBCCCCDDDDOh
有符号乘法

IMUL(有符号数乘法)指令执行有符号整数乘法。与 MUL 指令不同,IMUL 会保留乘 积的符号,实现的方法是,将乘积低半部分的最高位符号扩展到高半部分。

x86 指令集支持三种格式的 IMUL 指令:单操作数、双操作数和三操作数。单操作数格式中,乘数和被乘数大小相同,而乘积的大小是它们的两倍。 单操作数格式 单操作数格式将乘积存放在 AX、DX:AX 或 EDX:EAX 中:

IMUL reg/mem8 ; AX = AL reg/mem8 IMUL reg/meml6 ; DX:AX = AX reg/meml6 IMUL reg/mem32 ; EDX:EAX = EAX * reg/mem32 和 MUL 指令一样,其乘积的存储大小使得溢出不会发生。同时,如果乘积的高半部分不是其低半部分的符号扩展,则进位标志位和溢出标志位置 1。利用这个特点可以决定是否忽略乘积的高半部分。 双操作数格式(32位模式) 32 位模式中的双操作数 IMUL 指令把乘积存放在第一个操作数中,这个操作数必须是寄存器。第二个操作数(乘数)可以是寄存器、内存操作数和立 即数。16位格式如下所示:

IMUL regl6, reg/meml6 IMUL regl6, imm8 IMUL regl6, imml6 32 位操作数类型如下所示,乘数可以是 32 位寄存器、32 位内存操作数或立即数(8 位 或 32 位):

IMUL reg32, reg/mem32 IMUL reg32, inun8 IMUL reg32, imm32 双操作数格式会按照目的操作数的大小来截取乘积。如果被丢弃的是有效位,则溢出标志位和进位标志位置 1。因此,在执行了有两个操作数的 IMUL 操作后,必须检查这些标志位中的一个。 三操作数格式 32 位模式下的三操作数格式将乘积保存在第一个操作数中。第二个操作数可以是 16 位寄存器或内存操作数,它与第三个操作数相乘,该操作数是一个8位或16 位立即数:

IMUL regl6, reg/meml6,imm8 IMUL regl6, reg/meml6, iirrnl6 而 32 位寄存器或内存操作数可以与 8 位或 32 位立即数相乘:

IMUL reg32, reg/mem32, imm8 IMUL reg32, reg/mem32, imm32 IMUL 执行时,若乘积有效位被丢弃,则溢出标志位和进位标志位置 1。因此,在执行了有三个操作数的 IMUL 操作后,必须检查这些标志位中的一个。 在 64 位模式下执行 IMUL 在 64 位模式下,IMUL 指令可以使用 64 位操作数。在单操作数格式中,64 位寄存器或内存操作数与 RAX 相乘,产生一个 128 位且符号扩展的乘积存放到 RDX:RAX 寄存器中。在下面的例子中,RBX 与 RAX 相乘,产生 128 位的乘积 -16。

mov rax, -4 mov rbx, 4 imul rbx ; RDX = 0FFFFFFFFFFFFFFFFh, RAX = -16 也就是说,十进制数 -16 在 RAX 中表示为十六进制 FFFF FFFF FFF0,而 RDX 只包含 TRAX 的高位扩展,即它的符号位。

三操作数格式也可以用于 64 位模式。如下例所示,被乘数 (-16) 乘以 4,生成 RAX 中的乘积 -64:

.data
multiplicand QWORD -16
.code
imul rax, multiplicand, 4       ; RAX = FFFFFFFFFFFFFFC0 (-64)

无符号乘法 由于有符号数和无符号数乘积的低半部分是相同的,因此双操作数和三操作数的 IMUL 指令也可以用于无符号乘法。但是这种做法也有一点不便的地方:进位标志位和溢出标志位将无法表示乘积的高半部分是否为零。 IMUL 示例 下述指令执行 48 乘以 4,乘积 +192 保存在 AX 中。虽然乘积是正确的,但是 AH 不是 AL 的符号扩展,因此溢出标志位置 1:

mov al,48 mov bl, 4 imul bl ; AX = 00C0h, OF = 1 下述指令执行 -4 乘以 4,乘积 -16 保存在 AX 中。AH 是 AL 的符号扩展,因此溢出标志位清零:

mov al, -4 mov bl, 4 imul bl ; AX = FFF0h, OF = 0 下述指令执行 48 乘以 4,乘积 +192 保存在 DX:AX 中。DX 是 AX 的符号扩展,因此溢出标志位清零:

mov ax, 48 mov bx, 4 imul bx ; DX:AX = 000000C0h, OF = 0 下述指令执行 32 位有符号乘法 (4 823 424*-423),乘积 -2 040 308 352 保存在 EDX:EAX 中。溢出标志位清零,因为 EDX 是 EAX 的符号扩展:

mov eax, +4823424 mov ebx, -423 imul ebx ; EDX:EAX = FFFFFFFF86635D80h, OF = 0 下述指令展示了双操作数格式:

.data
word1 SWORD 4
dword1 SDWORD 4
.code
mov ax, -16            ; AX = -16
mov bx, 2              ; BX = 2
imul bx, ax            ; BX = -32
imul bx, 2             ; BX = -64
imul bx, word1         ; BX = -256
mov eax, -16           ; EAX = -16
mov ebx, 2             ; EBX = 2
imul ebx, eax          ; EBX = -32
imul ebx, 2            ; EBX = -64
imul ebx, dword1       ; EBX = -256

双操作数和三操作数 IMUL 指令的目的操作数大小与乘数大小相同。因此,有可能发生有符号溢出。执行这些类型的 IMUL 指令后,总要检查溢岀标志位。下面的双操作数指令展示了有符号溢出,因为 -64000 不适合 16 位目的操作数:

mov ax, -32000 imul ax, 2 ; OF = 1 下面的指令展示的是三操作数格式,包括了有符号溢出的例子:

纯文本复制

.data
word1 SWORD 4
dword1 SDWORD 4
.code
imul bx, word1, -16             ; BX = word1 * -16
imul ebx, dword1, -16           ; EBX = dword1 * -16
imul ebx, dword1, -2000000000   ; 有符号溢出!

cdq 拓展高位指令

这里直接引用布衣僧的文章 CDQ 是一个让很多人感到困惑的指令。 这个指令把 EAX 的第 31 bit 复制到 EDX 的每一个 bit 上。 它大多出现在除法运算之前。它实际的作用只是把EDX的所有位都设成EAX最高位的值。也就是说,当EAX <80000000, EDX 为00000000;当EAX >= 80000000, EDX 则为FFFFFFFF。

例如 : 假设 EAX 是 FFFFFFFB (-5) ,它的第 31 bit (最左边) 是 1, 执行 CDQ 后, CDQ 把第 31 bit 复制至 EDX 所有 bit EDX 变成 FFFFFFFF 这时候, EDX:EAX 变成 FFFFFFFF FFFFFFFB ,它是一个 64 bit 的大型数字,数值依旧是 -5。

备注:

    EDX:EAX,这里表示EDX,EAX连用表示64位数

———————————————— 原文链接:https://blog.csdn.net/oBuYiSeng/article/details/50349139

赋值指令

lea 将一个地址直接赋值给寄存器

lea是“load effective address”的缩写,简单的说,lea指令可以用来将一个内存地址直接赋给目的操作数,

例如:lea eax,[ebx+8]就是将ebx+8这个值直接赋给eax,而不是把ebx+8处的内存地址里的数据赋给eax。