前言
在学习汇编前需要掌握一定的C/C++编程基础,计算机组成原理、操作系统等课程知识。
此文为复习所总结,主要介绍和总结汇编的常用指令,参考教材为《新概念汇编语言》
目录
一、通用寄存器简单指令
(一)简单传送指令
1. 传送指令(MOV)
格式:MOV DEST,SRC
将源操作数SRC送至目的操作数DEST,即:
DEST <= SRC
2. 交换指令(XCHG)
格式: XCHG OPRD1,OPRD2
将操作数OPRD1的内容与操作数OPRD2的内容交换;
(二)简单加减指令
1. 加法指令(ADD)
格式: ADD DEST,SRC
此指令完成两个操作数相加,结果送到目的操作数DEST,即:
DEST <= DEST + SRC
2. 减法指令(SUB)
格式: SUB DEST,SRC
此指令完成两个操作数相减,结果送到目的操作数DEST,即:
DEST <= DEST - SRC
3. 加 1 指令(INC)
格式: INC DEST
此指令完成对操作数DEST加1,然后把结果送回DEST,即:
DEST <= DEST + 1
4. 减 1 指令(DEC)
格式: DEC DEST
此指令完成对操作数DEST减1,然后把结果送回DEST,即:
DEST <= DEST - 1
5. 取补指令(NEG)
格式: NEG OPRD
此指令对操作数取补,就是用0减去操作数OPRD,再把结果送回OPRD,即:
OPRD <= 0 - OPRD
二、 标志寄存器的标志及指令
(一)标志寄存器
首先需要了解一下标志寄存器,IA-32系列CPU有一个32位的标志寄存器(EFLAGS Register)。这个标志寄存器含有一组状态标志、一组系统标志和一个控制标志。
(图:标志寄存器中的标志位)
(二)状态标志
1. 进位标志(CF)
进位标志主要反映算数运算是否产生进位或借位。
如果运算结果的最高位产生一个进位或借位,则CF被置1,否则CF被清0。
2. 零标志(ZF)
零标志反映运算结果是否为0。
如果运算结果为0,则ZF被置1,否则ZF被清零。
3. 符号标志(SF)
符号标志反映运算结果的符号位。
SF与运算结果的最高位相同,如果运算结果最高位为1,则SF被置1,否则SF被清0。
(由于在IA-32系列CPU中,有符号数采用补码的形式表示,因此SF反映了运算结果的符号:如果运算结果为正,则SF被清0,否则SF被置1。)
4. 溢出标志(OF)
溢出标志反映有符号数加减运算是否引起溢出。
如果运算结果超出了8位、16位或32位有符号数的表示范围,则OF被置1,否则OF被清0。
5. 奇偶标志(PF)
奇偶标志反映运算结果的最低字节中含有“1”的位数是偶数还是奇数。
如果“1”的位数是偶数,则PF被置1,否则PF被清0。
6. 辅助进位标志(AF)
辅助进位标志反映算术运算中第3位是否产生进位或借位,或者最低的4位是否有进位或借位。
如果产生进位或借位,则AF被置1,否则AF被清0。
(三)状态标志操作指令
1. 进位标志操作指令(CLC、STR、CMC)
在前述的6个状态标志中,进位标志CF的用途最为广泛。CPU具有单独调整CF的指令:
清进位标志指令(CLC)
格式: CLC
这条指令使进位标志CF为0。
置进位标志指令(STR)
格式: STC
这条指令使进位标志CF为1。
进位标志取反指令(CMC)
格式: CMC
这条指令使进位标志CF取反。
如CF为1,则使CF为0;如CF为0,则使CF为1。
上述 3 条进位标志操作指令仅影响CF,对其他标志没有影响。
2. 获取状态标志操作指令(LAHF)
格式: LAHF
这条指令把标志寄存器的低8位,送到通用寄存器AH中。
3. 设置状态标志操作指令(SAHF)
格式: SAHF
这条指令对标志寄存器中低8位的状态标志产生影响,使得状态标志SF、ZF、AF、PF和CF分别成为来自寄存器AH中对应位的值,但保留位不受影响。
(四)带进位加减指令
1. 带进位加法指令(ADC)
格式: ADC DEST,SRC
这条指令与ADD指令类似,完成两个操作数相加,但还要把进位标志CF的当前值加上,把结果送至目的操作数DEST,即:
DEST <= DEST + SRC + CF
2. 带借位减法指令(SBB)
格式: SBB DEST,SRC
这条指令与SUB指令类似,完成两个操作数相减,但还要把借位标志CF的当前值减去,把结果送至目的操作数DEST,即:
DEST <= DEST - SRC - CF
三、堆栈和堆栈操作
(一)堆栈
1. 什么是堆栈
- 汇编中的堆栈就是一段内存区域,只是对它的访问操作仅限于一端进行;
- 地址较大的一端称为栈底,地址较小的一端称为栈顶;
- 堆栈操作遵循“后进先出”的原则,所有数据的存入和取出都在栈顶进行;
- 存入数据的操作称为进栈操作(压栈操作),取出数据的操作称为出栈操作(弹出操作)。
(图:堆栈和堆栈操作示意图)
2. 堆栈的主要用途
- 保护寄存器内容或者保护现场;
- 保存返回地址;
- 传递参数;
- 安排局部变量或者临时变量。
(二)堆栈操作指令
1. 进栈指令(PUSH)
格式: PUSH SRC
该指令把源操作数SRC压入堆栈。
2. 出栈指令(POP)
格式: POP DEST
该指令从栈顶弹出一个双字或者字数据到目的操作数DEST。
注意:出栈指令的操作数不同于进栈指令,它不能是立即数和代码段寄存器CS!
3. 通用寄存器全进栈指令和全出栈指令
以下4条指令都没有显式的操作数,这些指令也不影响标志。
16位通用寄存器全进栈和全出栈指令
- PUSHA
将8个16位通用寄存器的内容压入堆栈,压入顺序为AX,CX,DX,BX,SP,BP,SI,DI,然后堆栈指针寄存器SP的值减16。 - POPA
从堆栈弹出内容,以PUSHA相反的顺序送到这些通用寄存器,从而恢复PUSHA之前的寄存器内容。然后SP的值通过增加16来恢复。
这两个指令提供了压入或弹出8个16位通用寄存器的有效手段。
32位通用寄存器全进栈和全出栈指令
- PUSHAD
将8个32位通用寄存器的内容压入堆栈,压入顺序为EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI,然后堆栈指针寄存器SP的值减32。 - POPAD
从堆栈弹出内容,以PUSHAD相反的顺序送到这些通用寄存器,从而恢复PUSHAD之前的寄存器内容。然后SP的值通过增加32来恢复。
这两个指令提供了压入或弹出8个32位通用寄存器的有效手段。
(三)子程序(过程)
1. 过程调用指令(CALL)
格式: CALL LABEL
(标号 LABEL 可以是程序中的一个标号,也可以是一个过程名)
此指令是段内直接调用指令,此指令的具体操作是:先把返回地址偏移压入堆栈,使得EIP的内容为目标地址偏移,从而实现转移。
2. 过程返回指令(RET)
格式: RET
该指令从堆栈弹出地址偏移,送到指令指针寄存器EIP。
四、算术逻辑运算指令
(一)乘除运算指令
1. 无符号数乘法指令(MUL)
格式: MUL OPRD
此指令实现两个无符号操作数的乘法运算。
指令看似只有一个操作数OPRD,实际上,另一个操作数是隐含的,位于寄存器AL、AX或者EAX中(这取决于OPRD的尺寸):
- 如果OPRD是字节操作数(8位),则把AL中的无符号数与OPRD相乘,16位结果送到AX中。
- 如果OPRD是字操作数(16位)则把AX中的无符号数与OPRD相乘,32位结果送到寄存器对DX:AX中,DX含高 16 位,AX含低 16 位。
- 如果OPRD是双字操作数(32位),则把EAX中的无符号数与OPRD相乘,64位结果送到寄存器对EDX:EAX中,EDX含高 32 位,EAX含低 32 位。
2. 有符号数乘法指令(IMUL)
格式:
- IMUL OPRD
单操作数形式。单操作数乘法指令实际上有一个隐含的操作数,位于寄存器AL、AX或者EAX中(这取决于操作数OPRD的尺寸)。 - IMUL DEST,SRC
双操作数形式。即:DEST <= DEST * SRC - IMUL DEST,SRC1,SRC2
三操作数形式。即:DEST <= SRC1 * SRC2
3. 无符号数除法指令(DIV)
格式: DIV OPRD
此指令实行两个无符号操作数的除法运算。指令看似只有一个操作数OPRD(作为除数),实际上,另一个操作数(作为被除数)是隐含的,位于寄存器AX、寄存器对DX:AX或者寄存器对EDX:EAX中(DX含有被除数的高16位,或者EDX含有被除数的高32位)。
- 如果OPRD是字节操作数(8位),则把AX中的无符号数除以OPRD,所得商送到AL中,余数送到AH中;
- 如果OPRD是字操作数(16位),则把寄存器对DX:AX中的无符号数除以OPRD,所得商送到AX,余数送到DX中;
- 如果OPRD是双字操作数(32位),则把寄存器对EDX:EAX中的无符号数除以OPRD,所得商送到EAX中,余数送到EDX中。
4. 有符号数除法指令(IDIV)
格式: IDVIV OPRD
此指令实现两个有符号操作数的除法运算。指令看似只有一个作为除数的操作数OPRD,实际上,作为被除数的另一个操作数是隐含的,位于寄存器AX、寄存器对DX:AX或者寄存器对EDX:EAX中(取决于操作数OPRD的尺寸)。
5. 符号扩展指令
-
字节转换字指令(CBW)
格式: CBW
此指令把寄存器AL中的符号拓展到寄存器AH。 -
字转换为双字指令(CWD)
格式: CWD
此指令把寄存器AX中的符号扩展到寄存器DX。 -
双字转换为四字指令(CDQ)
格式: CDQ
此指令把寄存器EAX中的符号扩展到寄存器EDX。 -
另一条字转换为双字指令(CWDE)
格式: CWDX
此指令把寄存器AX中的符号扩展到寄存器EAX的高16位。
6. 扩展传送指令
-
符号扩展传送指令(MOVSX)
格式: MOVSX DEST,SRC
此指令把源操作数SRC符号扩展后送至目的操作数DEST。 -
零扩展传送指令(MOVZX)
格式: MOVZX DEST,SRC
此指令把源操作数SRC零扩展后送至目的操作数DEST。
(二)逻辑运算指令
1. 否运算指令(NOT)
格式: NOT OPRD
此指令把操作数OPRD按位“取反”,然后送回OPRD;
按位“取反”是指把为 0 的位设置为 1,把为 1 的位清为 0。
2. 与运算指令(AND)
格式: AND DEST,SRC
此指令对两个操作数进行按位的逻辑“与”运算,结果送到目的操作数DEST。按位“与”是指当两个操作数对应位都为 1 时,把结果的对应位设置成 1 ,否则清成 0。
3. 或运算指令(OR)
格式: OR DEST,SRC
此指令对两个操作数进行按位的逻辑“或”运算,结果送到目的操作数DEST。
4. 异或运算指令(XOR)
格式: XOR DEST,SRC
此指令对两个操作数进行按位的逻辑“异或”运算,结果送到目的操作数DEST。
5. 测试指令(TEST)
格式: TEST DEST,SRC
此指令和AND指令类似,也是把两个操作数进行按位“与”,但结果不送回目的操作数DEST,仅仅影响状态标志。该指令执行后,标志ZF、PF和SF反映运算结果,标志CF和OF被清为0。
(三)移位指令
1. 移位指令
-
算术左移指令(同逻辑左移指令)(SAL)
格式: SAL OPRD,count
-
逻辑左移指令(同算术左移指令)(SHL)
格式: SHL OPRD,count
-
算术右移指令(SAR)
格式: SAR OPRD,count
-
逻辑右移指令(SHR)
格式: SHR OPRD,count
(图:一般移位指令执行示意图)
2. 循环移位指令
-
左循环移位指令(ROL)
格式: ROL OPRD,count
-
有循环移位指令(ROR)
格式: ROR OPRD,count
-
带进位左循环移位指令(RCL)
格式: RCL OPRD,count
-
带进位右循环移位指令(RCR)
格式: RCR OPRD,count
3. 双精度移位指令
-
双精度左移指令(SHLD)
格式: SHLD OPRD1,OPRD2,count
-
双精度右移指令(SHRD)
格式: SHRD OPRD1,OPRD2,count
五、转移指令
(一)条件转移指令
条件转移指令通常根据标志寄存器中的状态标志来判断条件是否满足,但条件转移指令本身的执行不影响状态标志。条件转移指令仅限于段内转移。
因为条件转移指令非常多,但是只要记住字母如下字母对应的意思,也可以轻松推导指令意思或者根据自己的要求写出指令。
大于(G) | 等于(E) | 小于(L) |
高于(A) | 等于(E) | 低于(B) |
(图:条件转移指令一览表)
(二)无条件转移指令
无条件转移指令可分为 4 种:
- 段内直接转移
- 段内间接转移
- 段间直接转移
- 段间间接转移
无条件转移指令均不影响标志寄存器中的状态标志。
1. 无条件段内直接转移指令
JMP LABEL
标号 LABEL 用于表示转移的目标位置,或者说转移目的地。
2. 无条件段内间接转移指令
JMP OPRD
此指令使控制无条件转移到操作数 OPRD 的内容给定的目标地址处。
六、循环指令
利用上面的条件转移指令和无条件转移指令可以实现循环,但是为了更加方便地实现循环,IA-32系列CPU还专门提供了循环指令。
(一)循环指令
1. 计数循环指令(LOOP)
格式: LOOP LABEL
此指令使寄存器ECX的值减 1,如果结果不等于 0,则转移到标号 LABEL 处,否则顺序执行LOOP指令后的指令。
2. 等于/全零循环指令(LOOPE / LOOPZ)
等于/全零循环指令有两个助记符,
格式: LOOPE LABEL
LOOPZ LABEL
此指令使寄存器 ECX 的值减 1,如果结果不等于 0,并且零标志 ZF 等于 1(表示相等),那么就转移到标号 LABEL 处,否则顺序执行。
3. 不等于/非零循环指令(LOOPNE / LOOPNZ)
不等于/非零循环指令也有两个助记符,
格式: LOOPEN JABEL
LOOPNZ JABEL
此指令使寄存器 ECX 的值减 1,如果结果不等于 0,并且零标志 ZF 等于 0(表示不相等),那么就转移到标号 LABEL 处,否则顺序执行。
(二)计数器转移指令
计数器转移指令(JECXZ)
格式: JECXZ LABEL
此指令实现当寄存器 ECX 的值等于 0 时转移到标号 LABEL 处,否则顺序执行。
七、子程序调用方法
在前文已介绍总结过,这里不再赘述。
(一)过程调用指令
1. 段内直接调用指令
格式: CALL LABEL
2. 段内间接调用指令
格式: CALL OPRD
3. 段间调用指令
(二)过程返回指令
1. 段内返回指令
2. 段内带立即数返回指令
格式: RET count
3. 段间返回指令
八、字符串操作
(一)串操作指令
常用的串操作指令有 5 种,分别是:串装入指令、串存储指令、串传送指令、串扫描指令和串比较指令。此外,还有串输入操作指令和串输出操作指令。每种串指令包括 3 条具体指令,分别对应 3 种字符尺寸,即字节、字和双字。
字符串操作的方向由标志寄存器中的方向标志 DF 控制。
- 清方向标志 DF 的指令(CLD)
- 设置方向标志 DF 的指令(STD)
利用这两条指令可以根据需要调整字符串操作指令处理字符串的方向。
1. 字符串装入指令(LOAD String)
格式:
- LODSB ;装入字节
- LODSW ;装入字
- LODSD ;装入双字
2. 字符串存储指令(STOre String)
格式:
- STOSB ;存储字节
- STOSW ;存储字
- STOSD ;存储双字
3. 字符串传送指令(MOVe String)
格式:
- MOVSB ;字节传送
- MOVSW ;字传送
- MOVSD ;双字传送
4. 字符串扫描指令(SCAn String)
格式:
- SCASB ;串字节扫描
- SCASW ;串字扫描
- SCASD ;串双字扫描
5. 字符串比较指令(CoMPare String)
格式:
- CMPSB ;串字节比较
- CMPSW ;串字比较
- CMPSD ;串双字比较
(二)重复操作前缀
1. 重复前缀 (REP)
它重复其后的串操作指令动作;
每一次重复都先判断寄存器 ECX 是否为 0,如果为 0 就结束重复,否则 ECX 的值减 1,重复其后的串操作指令。
(它类似于 LOOP 指令,但 LOOP 指令使先把 ECX 的值减 1 后再判断是否为 0)
2. 重复前缀 (REPE/REPZ)
REPE 和 REPZ 是一个前缀的两个助记符,下面以 REPE 为代表进行说明。
REPE 重复其后的串操作指令动作。每重复一次,ECX 的值减 1,重复一直进行到 ECX 为 0 或串操作指令使零标志 ZF 为 0 时为止。
3. 重复前缀 (REPNE/REPNZ)
REPNE 和 REPNZ 是一个前缀的两个助记符,下面以 REPNE 为代表进行说明。
REPNE 与 REPE 类似,所不同的是重复一直进行到 ECX 为 0 或串操作指令使零标志 ZF 为 1 时止。
九、位操作
(一)位操作指令
1. 位测试及设置指令组
位测试和设置指令组含有 4 条指令:
- 位测试指令(BT)
格式: BT OPRD1,OPRD2
功能是把被测试位的值送到进位标志 CF。
- 位测试并取反指令(BTC)
格式: BTC OPRD1,OPRD2
功能是把被测试位的值送到进位标志 CF,并且把被测试位取反。
- 位测试并复位指令(BTR)
格式: BTR OPRD1,OPRD2
功能是把被测试位的值送到进位标志 CF,并且把被测试位复位,即清 0。
- 位测试并置位指令(BTS)
格式: BTS OPRD1,OPRD2
功能是把被测试位的值送到进位标志 CF,并且把被测试位置位,即置 1。
2. 位扫描指令组
位扫描指令组含有 2 条指令:
- 顺向位扫描指令(BSF)
格式: BSF OPRD1,OPRD2
功能是从右往左(位 0 至位 15 或位 31)扫描字或者双字操作数 OPRD2 中的第一个含“1”的位,并把扫描到的第一个含“1”的位的位号送至操作数 OPRD1。
- 逆向位扫描指令(BSR)
格式: BSR OPRD1,OPRD2
功能是从左往右(位 15 至位 31 或位 0)扫描字或者双字操作数 OPRD2 中的第一个含“1”的位,并把扫描到的第一个含“1”的位的位号送至操作数 OPRD1。
(二)条件设置字节指令
条件设计字节指令的一般格式:
格式: SETcc OPRD
其中,符号cc是代表各种条件的缩写,是指令助记符的一部分。
(图:条件设置字节指令)
总结
以上就是汇编基本指令汇总的内容,本文仅仅简单介绍了汇编的常用指令,而对进一步学习汇编语言来说,掌握以上是非常有必要的。