汇编语言详细内容
最近在学习汇编语言。 由于有很多指令,所以边学习变做个笔记。
汇编语言基础
三种基本的汇编语言成员:
指令:
[name:] mnemonic [[dest][,src]][;comment]
伪指令
[name] directive [[operand][,operand,...]][;comment]
macros 宏
命名规则和基本的c/c++命名规则差不多,主要关注以下可以使用哪些符号
- ? _ @ $ 可以使用
- 长度不要超过31个字符。 第一个字符不能是数字。
数据类型:
| 类型 | 简写 | 大小 |
|---|---|---|
| BYTE | DB | 1B |
| WORD | DW | 2B |
| DWORD | DD | 4B |
| FWORD | DF | 6B |
| QWORD | DQ | 8B |
| TBYTE | DT | 10B |
| REAL4 | DD | 4B |
| REAL8 | DQ | 8B |
| REAL10 | DT | 10B |
汇编语言编译过程
src.asm —(汇编器)—-> src.obj —(连接器)—> xxxx.exe
伪指令
变量声明
label WORD [值,‘?’]
算了,还是直接举例子把:
a BYTE 'A' |
DUP
a BYTE 30 DUP(5) ;重复 30个5 |
OFFSET 和 SEG
这是两个比较常用的,
MOV AX, OFFSET VAR1 |
一个是取偏移地址,一个是取段地址。
算数运算
+,-,*,/ 。。。 比较弱的功能,好像只支持 常数之间的运算和变量之间的地址运算,如:
MOV DX, BLOCK +(6-1)*2 |
PTR
常用,用于类型转换
MOV AL, BYTE PTR var |
SHORT
后面跳转会用到,不常用
EQU 和 =
都是复制,只不过EQU 类似于 c系语言的宏定义,不可改变, =类似于=,可以改变。
ORG
常用,用于将当前程序指针指向某一个位置,与$搭配使用。
ORG expression |
汇编代码框架
.8086 |
汇编指令
转移指令
MOV 指令
MOV DST, SRC |
将数据从 SRC 拷贝到 DST。MOV的使用有以下限制:
- SRC 和 DST 不能都在主存或者都是段寄存器。
- 不能将立即数移动到段寄存器(可以用通用寄存器间接来)
- 不能对 FLAG 寄存器做此操作。
- 不能将任何数据移动到CS和IP寄存器。
- 不能再不同位长的寄存器之间移动。
PUSH 和 POP
PUSH SRC |
将数据压入栈,操作步骤一般为:
- SP上移,即 SP ← SP -2
- ((SP)+1,(SP) ) ← (SRC) 这里是小端对齐, 高位存在高地址。
另外,不要忘记, 可以将 PUSH 的对象可以为: 立即数,内存数,寄存器数(包括FLAG寄存器)都可以。
POP DST |
将栈顶元素弹出到DST。操作顺序:
- DST ← ((SP+1),(SP))
- SP ← (SP) + 2
需要注意的是,DST不可以是CS寄存器。
加载地址指令
加载有效地址
LEA reg16/32, mem8/16/32 |
LEA: load effective address 加载有效地址到 寄存器。 有效地址就是偏移地址。例如:
if (DS) = 2000H, (BX) = 1234H. 则 LEA DI, [BX] 的执行结果为:
DI ← 1234H
if Symbol variable BUFFER is in 2000H:1234H. 则 LEA DI, BUFFER 的执行结果为:
DI ← 1234H
加载段地址 + 有效地址
LDS reg, mem |
这是 LEA 的 升级版本,但是, 千万要注意的是, 这些指令不会做像 LEA 一样的翻译的效果,而是直接将 mem内的数据载入 段寄存器和reg。例如:
if (DS) = 2000H, (BX) = 1000H。 内存中 (21000H,21001H,21002H,21003H)= (45H,D6H,00H,50H)
那么, LDS DI,[BX] 的效果为:
DI ← D645H
DS ← 5000H
交换指令
XCHG A , B |
交换两者内部的值,值得注意的是:
- 两者长度要一致。
- 两者不能同时是段寄存器或者同时为内存数。
LAHF 和 SAHF
LAHF |
Transfers Flag寄存器最右面的8位到AH寄存器。
SAHF |
Transfers AH寄存器到Flag寄存器的最右面8位。
查表指令
XLAT |
将AL 寄存器的值转换成 一个表里面的值。
表的偏移地址必须事先存在 BX 寄存器中。并且表元素的类型是byte。例如:
MOV BX, OFFSET TABLE
MOV AL, 4
XLAT
执行结果: AL ← 34H 34H 是表中第四个元素,表从0开始计数。
I/O 指令
IN AL/AX , I/O DEV |
与I/O 设备的交互。 IO设备的寻址有两种方式存在:
- 固定端口寻址。 有的IO端口有固定的8bits的 端口地址。例如 IN AL, 52H
- 变动端口寻址。 端口地址存在DX。 例如 OUT DX, AL
算数指令
加法指令
ADD DST, SRC |
DST ← (DST) + (SRC) 需要注意的是:
- 两个操作数位数要相同。
- 可以两个寄存器,也可以是寄存器和立即数,寄存器和内存数,内存数和立即数。 但不能是两个内存数。
- 更值得注意的是: 它的执行会影响:CF, SF, OF, PF ,ZF 和 AF。
ADC DST, SRC |
DST ← (DST) + (SRC) + Carry。 考虑进位的加法。 同样会影响FLAG寄存器。
INC DST |
DST ← (DST) + 1
需要注意:
- 也可以是内存数。
- 会对FLAG产生影响,但是! 不会对CF产生影响!!!!!!!!
减法指令
SUB DST, SRC |
DST ← (DST) - (SRC) 需要注意的和 ADD 相同。
SBB DST, SRC |
DST ← (DST) - (SRC) - Carry。
DEC DST |
自减。 不会对CF产生影响。
NEG DST |
DST ← 0 - (DST) 效果, 取反并加一。 对于负数来说, 就是求补。
CMP DST, SRC |
通过 (DST) - (SRC) 来比较 两个操作数。 但是, DST 不会被改变!!!!!!!!! 通过对FLAG的影响来得到结果。
无符号数结果的判断:
- ZF = 1. 相等。
- CF = 0. DST 大于等于 SRC。
- CF = 1. DST 小于 SRC。
有符号数结果的判断:
- ZF = 1. 相等。
- SF = OF, DST 大于等于 SRC。
- SF != OF, DST < SRC。
乘除法指令
MUL SRC ; 无符号数 |
- SRC 可以是 寄存器数也可以是内存数。 被乘数存在 AX/AL寄存器当中。
- 对于8位数。 AX ← AL * SRC
- 对于16位数。DX : AX ← AX * SRC
需要注意的是:
- 结果的位数总是操作数的两倍
- 这个指令对 CF 和 OF 有影响。
- CF = OF = 1. 表明 结果的 高一半不是0
- CF = OF = 0. 表明 结果的 高一半 是 0
- 不会改变其他寄存器。
IMUL REG, SRC |
REG ← (REG) * (SRC) - (IMM) 注意, 在这里, 它们的尺寸是相同的。 16/32
DIV SRC |
同MUL。被除数在AX里面。需要注意:
- 不要除0
- 商太大放不下。
- 会改变FLAG的值
- 商的位数是被除数位数的一半。 商存在低一半。 余数存在高一半。
还有就是不能乘除立即数。可以加减立即数。
符号扩展指令
CBW ; AL to AX |
BCD 码的调节
- 加法结束后,1010B ~ 1111B 对于BCD码来说是没有意义的。因此需要调节。 即进位。
AAA |
例子:
MOV AL, 9H |
所以最后, 整个结果扩展到 AH:AL。 并且改变 CF 和 AF 【= 1】(adjust flag 第三位有无进位)的值。
- 减法结束后,的调节。
AAS |
同上, AF = CF = 1. 且 将正确结果整合到了AL。
- BCD 乘法结束后的调节
AAM ; 和AAA 差不多 |
- 除法结束后调节
AAD ; 和 AAM 差不多 |
以上都是unpacked BCD 码的调节。 packed BCD 码如有需要,自查资料。
逻辑指令
与或非
AND DST,SRC |
需要注意如下:
- 结果存在DST当中
- DST和SRC 都可以是MEM, 但不可同时是MEM。
它们的使用技巧:
- AND 转换 ASCII 到 digit integer
- OR 转换 digit integer 到 ASCII
- XOR 某一位取反 例如 : XOR CL, 00100000B . 因为0/1 与0异或不会改变。 0/1 异或会取反。
TEST 指令
和 AND 的效果一样,但是不会改变 DST 的值。
注意:
- AND, OR , XOR, TEST 会改变 FLAG 的值。
- CF = 0
- OF = 0
- AF undefined
- PF, SF, ZF 和结果有关。
NOT 指令
求反指令
MOV AL, 1 |
SHIFT 和 ROTATE
SHL DST, CNT ; 逻辑左移 |
值得注意的是:
- 逻辑左移和算数左移没有什么区别
- 逻辑右移左面补0,算数右移左面补符(最高位)。
- DST 可以是MEM数或者寄存器。 CNT 是8位立即数,1,或者CL
- SHIFT 操作将会改变 CF, OF ZF ,PF, AF。 注意OF, 当shift 一位的时候,如果结果的符号和原来一样,OF = 0. 否则为1.
ROL DST, CNT |
注意:
- ROL, ROR 只是将出的位,保留到CF一份, CF不会对结果产生影响
- RCL, RCR 是移动到CF, 在将原来CF的值填到另一端。
- 只会影响CF, OF(至对于一位ROTATE,如果符号变化,OF=1)
跳转指令
无条件跳转
JMP SHORT OPR ; OPR 可以是一个label 例如 |
SHORT 的范围是同一个段的8位, +127 ~ -128
JMP OPR ; 16位。 +32767~-32768 当前段 |
JMP FAR PTR OPR ; can be another 段 32bits |
JMP REG16/32 |
条件跳转
| 跳转指令 | 跳转条件 |
|---|---|
| JE / JNE | 是否相等(其实考察的是ZF) |
| JAE/ JGE | A >= B |
| JBE/ JLE | A <= B |
| JC/JZ/JS/JO/JP | 当相应的CF, ZF, SF, OF, PF 寄存器为1时跳转 |
跳转表
传言,switch 于 else if 的区别就是, switch 会被编译器编译成跳转表。跳转表的概念比较好理解但难解释,直接上实例吧!
.8086⇠ |
意思就是指,数据段把相应的cluster 的名字写入,而具体的实现写在下面,跳转的时候:
>...JMP TABLE[DI]⇠ |
就会跳转到表中该项对应的簇!
常用标志位的置位与复位
| 指令 | 作用 |
|---|---|
| CLC | Let CF = 0 |
| CMC | Let CF = ~CF |
| STC | Let CF = 1 |
| CLD | Let DF = 0 |
| STD | Let DF = 1 |
| CLI | Let IF = 0 |
| STI | Let IF = 1 |
Loop
普通Loop
MOV CX, [circles] |
特殊Loop
- 当相等的时候就LOOP
LOOPZ/E |
- 当不相等的时候就LOOP
LOOPNZ |
串操作**
DI 和 SI 以及串操作思想
串操作是对一些列字节进行一次性操作,涉及到以下三个寄存器:
- SI: 串操作源地址, 段地址参考 DS
- DI: 串操作目的地址 , 段地址参考 ES
- DF: 串操作的方向, DF = 0 , DI , SI 执行完一次会++, DF=1, DI,SI执行完会–
常用的串指令
- LODS 指令, 从内存加载到寄存器AL,AX,EAX。。。 有LODSB, LODSW, LODSD, 最后一位是Byte,Word,DWord。 决定了执行完一次后,DI,SI加/减多少个Byte。
- STOS 指令, 将AL,AX 存到DI 指向的内存空间。
上面的指令都是默认执行一次,如果要重复,就需要:
LEA DI, BUFF ; BUFF is a array |
- MOVS 常用,将 DS:SI 地址内容 移动(复制)到 ES:DI对应的地址, 同样需要配合 CX, 以及 REP指令
- INS 与 OUTS 主要用于与IO设备的串交互,同样需要配合 CX, 以及 REP指令
- SCAS 指令 串比较,将ES:DI指向的内容与AL,AX,EAX内容比较。 配合 REPNE(相等就停止) / REPE (不想等就停止),当然硬性条件是cx!=0.
- CMPS 串指令, 将SI,DI指向的内容进行比较,和SCAS 类型,配合RExxP使用。
串指令在汇编中作用很大,妥善使用能省去很多麻烦, 实验中有一个回文串的判断,我是用循环进行判断,十分麻烦,现在看来,利用STOSB将会非常简单,具体思路可能是:
先用MOVSB ,DF = 1, 将内容反向复制, 在利用CMPS 进行判断即可!
汇编过程的编写
LABEL PROC [NEAR/FAR,far常用于中断处理函数] |
REP 指令
所有过程都需要,一般没有参数,直接调用即可,但是当函数有返回值时,就需要了解下面的用法了:
REP EXP |
这个指令做了些啥呢,
IP <--- (SP+1,SP) |
EXP 可以自己想办法用, 反正效果就是让SP多向下移动了EXP个字节而已。
函数传参
- 通过寄存器传参,这个比较好弄
- 通过堆栈传参,这个比较常用,需要掌握以下。 假如要传递两个个参数:
PUSH AX ;PARAM1 |
现在明白了把, 具体情况还要根据函数类型来呀,因为有些函数可不止默认保留IP,还有CS什么的。。
总结
我所学到的有关汇编的全部内容就到这里啦, 有疑问可以联系作者zenhox@163.com