汇编学习笔记
进行一些笔记的整理
前言
上年暑假就学了 x86 汇编的实模式部分,当时用 Onenote 记了一些笔记,后来发现了 Markdown 这个好东西,就决定搬迁一下,也方便我上课查(bushi
格式有待整理(逃
寄存器
通用寄存器
寄存器 | 全称 | 中文名称 |
---|---|---|
CS | Code Segment | 代码段寄存器 |
DS | Data Segment | 数据段寄存器 |
ES | Extra Segment | 附加段寄存器 |
SS | Stack Segment | 栈段 |
SP | Stack Pointer | 栈指针寄存器 |
IP | Instruction Pointer | 指令指针寄存器 |
SI | Source Index | 源索引(变址)寄存器 |
DI | Destination Index | 目标索引寄存器 |
AX | Accumulator | 累加器 |
BX | Base Address Register | 基地址寄存器 |
CX | Counter | 计数器 |
DX | Data | 数据寄存器 |
标志寄存器
Index | 名称 | 说明 |
---|---|---|
第0位 | CF | 进位标志,进行算术操作时,如果最高位有向前进位或借位的情况发生,则CF=1,否则CF=0,少数指令除外(如inc和dec) |
第2位 | PF | 奇偶标志,运算结果最低8位,有偶数个为1的比特则PF=1,否则PF=0 |
第4位 | AF | 无 |
第6位 | ZF(Zero Flag) | 零标志,当处理器执行一条算数或者逻辑运算指令后,抓住逻辑部件送出的结果除了送到指令中指定位置,还送到一个或非门,如果计算结果为0,ZF被置成1,表示计算结果为零是“真”的,否则清除此位(0) |
第7位 | SF(Sign Flag) | 符号位,如dec计算结果的最高位是比特“0”,SF置“0”,否则置“1” |
第8位 | TF | 无 |
第9位 | IF | 无 |
第10位 | DF(Direction Flag) | 方向标志,可控制movsb和movsw的传送方向 |
第11位 | OF | 即溢出标志,用于指示两个有符号数的运算结果是否错误,如果结果正确,则OF=0,否则OF=1 |
指令
mov
示例:
mov ah,bh
mov ax,dx
mov [0x02],bl
mov ax,[0x06]
mov ah,0x05
mov word [0x1c],0xf000
db(Declare Byte)
伪指令,跟在后面的操作数都占一个字节的长度,如果要声明超过一个以上的数据,各个操作数之间必须以逗号隔开
div
- 用16位的二进制数除以8位的二进制数:
被除数必须在寄存器AX中,必须先传送到AX寄存器里,除数可以由8位的通用寄存器或者内存单元提供
执行后:商在AL中,余数在AH中 - 用32位的二进制数除以16位的二进制数:
被除数的高16位在DX中,低16位在AX中
执行后:商在AX中,余数在DX中
xor
0 xor 0 = 0
0 xor 1 = 1
1 xor 0 = 1
1 xor 1 = 0
常用于清零
times
伪指令,重复后面的指令若干次
例:times 100 db 0
movsb和movsw
原始数据串的段地址由 DS 指定,偏移地址由 SI 指定,简写为 DS:SI ,目的地址为 ES:DI ,传送的字节数或者字数由 CX 指定,每传送一次, CX 内容减一
正向传送时传送操作方向是从内存区域低地址端到高地址端,每传送一个字节或一个字时, SI 和 DI 加 1 或者加 2 ,反向传送则相反
cld和std
cld:将 DF 标志清零,指示传送是正方向的
std:与 cld
相反
loop
执行时 CX 减一,若 CX 内容不为零,转移到指定的位置执行,否则顺序后面的指令
rep
指令前缀,即 repeat
CX 不为零则重复
inc和dec
inc:加一指令,inc bx
和 add bx,1
功能一样,但前者机器码更短,速度更快
dec:减一指令,与 inc
格式相同
neg
用 0 减去指令中指定的操作数
cbw和cwd
两条指令后都没有操作数
cbw(Convert Byte to Word):将 AL 中的有符号数扩展到整个 AX
cwd(Convert Word to Double-word):将 AX 中的有符号数扩展到 DX:AX 中
idiv
div 为无符号除尘指令(Unsigned Divide)
idiv 指令格式与 div 相同,专门用于计算有符号数
sub
与加法指令 add
类似,但处理器没有减法运算电路,故 sub ah,al
等效
neg al
add ah,al
cmp
功能上和 sub
指令相同,但仅影响相应标志位(CF、OF、SF、ZF、AF和PF),而不保留计算结果,因此不会改变两个操作数的原有内容
or和and
or | and |
---|---|
0 xor 0 = 0 | 0 xor 0 = 0 |
0 xor 1 = 1 | 0 xor 1 = 0 |
1 xor 0 = 1 | 1 xor 0 = 0 |
1 xor 1 = 1 | 1 xor 1 = 1 |
对标志寄存器影响: OF 和 CF 位被清零, SF、ZF、PF 的状态依计算结果而定
push和pop
操作数可以是寄存器或者内存单元,逻辑地址为 SS:SP ,不影响任何标志位
push:执行时将 SP 内容减去操作数的字长,从高地址端向低地址端推进
pop:执行时将 SP 内容加上操作数的字长
in和out
in
指令是从端口读,一般形式是
in al,dx
in ax,dx
in 指令的操作数必须是寄存器 AL 或者 AX ,用于访问 8 位或 16 位的端口,源操作数应当是寄存器 DX , in
指令为 2 字节形式时,后一字节是立即数
out
指令和 in
指令相反
两个指令均不影响任何标志位
call、ref和retf指令
8086处理器支持四种调用方式
i. 16位相对近调用。近调用的意思是被调用的目标过程位于当前代码段内,而非另一个不同的代码段,故只需得到偏移地址即可,计算过程为:用目标过程的汇编地址减去当前call指令的汇编地址,再减去当前call指令以字节为单位的长度(3),保留16位的结果,近调用的特征是在指令中使用关键字”near”,若没有提供任何关键字,则编译器认为该指令是近调用,其机器指令操作数是一个16位的有符号数,被调用过程的首地址必须位于距离当前call指令-32768~32767字节的地方
ii. 16位间接绝对近调用。这种调用也是近调用,只能调用当前代码段内的过程,指令中的操作数不是偏移量,而是被调用过程的真实偏移地址,故称为绝对地址,该偏移地址不直接出现在指令中,而是由16位的通用寄存器或者16位的内存单元间接提供,机器指令的操作是16位的绝对地址,故可以调用当前代码段任何位置处的过程
iii. 16位直接绝对远调用。这种调用属于段间调用,即调用另一个代码段的过程,”16位”是针对偏移地址来说的,而不是限定段地址,”直接”的意思,段地接和偏移地址直接在call指令中给出,先后将CS和IP进行压栈和出栈
iv. 16位间接绝对远调用。同样属于段间调用,”16位”同样用于限定偏移地址,必须使用关键字”far”,例:
proc_1 dw 0x0102,0x2000
call far [proc_1]
0x0102是偏移地址,0x2000是段地址,指令执行时,处理器访问DS指向的数据段,从指令中指定的偏移地址处取得两个字(段地址0x2000和偏移地址0x0102),再将CS和IP分别压栈和取代
ref是近返回指令,从栈中弹出一个字到IP中
retf是远返回指令,分别从栈中弹出两个字到IP和CS中
shr、shl、ror、rol
shr(Shift logical Right),即逻辑右移指令,将AX中的内容右移4位,执行时将操作数连续右移指定次数,每次溢出的比特被移到CF位,空出的位置用“0”填充,目的操作数可以是8位或16位通用寄存器/内存单元,源操作数可以是1或8位立即数或寄存器CL,当使用CL时,对于目的操作数是内存地址的情况,须使用关键字byte或word等来加限定,如:
shr al,cl
shr byte [bx],cl
ror(Rotate Right),即循环右移指令,执行时每右移一次,移出的比特既送到CF位,也送进左边空出的位
jmp
若JMP之后是标号,则编译为相对转移指令0xE9,操作数为相对偏移题,执行时IP+操作数+长度,此时为相对近转移
8086处理器的无条件转移指令(“16位”意为要转移到的目标位置的偏移地址是16位的)
- 相对短转移。操作码为0xEB,操作数是相对于目标位置的偏移量,仅1字节,且为有符号数,故该指令属于段内转移指令,且只允许转移到距离当前-128~127字节的地方,该指令须使用关键字“short”,例:
jmp shor infinite - 16位相对近转移。转移范围较相对短转移稍大,操作码为0xE9,操作数为2字节,属于段内转移,可转移到距当前指令-32768~32767字节的地方,该指令应使用关键字“near”
- 16位间接绝对近转移。转移到的目标偏移地址不是在指令中直接给出,而是用一个16位的通用寄存器或者内存地址间接给出,关键字“near“可以省略,例:
jmp near bx
jmp near cx
jump_dest dw 0xc000
jmp [jump_dest]
jmp [bx] - 16位直接绝对远转移。在指令中直接给出段地址和偏移地址的转移指令,例:
jmp 0x0000:0x7c00 - 16位间接绝对远转移。该指令要使用关键字”far”,例:
jump_far dw 0x33c0,0xf000
jmp far [jump_far]
resb(REServe Byte)、resw和resd指令
resb:从当前位置开始,保留指定数量的字节,但不进行初始化
mul
可以用8位的通用寄存器或者内存单元中的数和寄存器AL中的内容相乘,结果是16位,在AX寄存器中,也可以用16位的通用寄存器或者内存单元中的数和寄存器AX中的内容相乘,结果是32位,高16位和低16位分别在DX和AX中,指令执行后,若结果的高一半全为0,则OF和CF清零,否则置1
参考
《x86汇编语言——从实模式到保护模式》——李忠 著