x86汇编学习笔记
cbw:把AL扩充成AX,扩充时要考虑负数
cwd:把AX扩充成DX:AX,扩充时要考虑负数
cdq:把EAX扩充成EDX:EAX,扩充时要考虑负数
一般用于放大被除数,为之后的除法做准备
movsx:符号扩充
movzx:零扩充
1 | movsx ax, al ;将al符号扩充成ax |
rol: rotate left 循环左移
把16位整数转化成16进制格式输出
1 | mov ah, 9Ah |
通过下列方式可以使用32位寄存器
1 | .386 |
段首地址(5位16进制数)必须以0结尾
0000:0000~9000:FFFF dos操作系统及用户代码占用的内存空间,总共640K内存空间
A000:0000~F000:FFFF 保留给显卡及ROM
显卡:text mode 与 graphic mode
text mode:80*25
assume ds:data,编译器会把data替换成ds:
若写成assume es:data,编译器则会把data替换成es:
同一个段与多个寄存器有关联时:ds > ss > es > cs
如,若写成assume ds:data, es:data,则会将data替换成ds:
cs:ip和ss:sp会被操作系统初始化
四个段寄存器中只有cs无法通过mov来改变
ds和es也会被操作系统初始化,ds=es=首段地址-10h
首段地址-10h:0000指向一块长度为100h字节的内存块,称为PSP(program segment prefix),PSP是操作系统自动分配给正在运行的程序的,里面存放了命令行参数等信息
图形模式编程
用int 10h把显卡切换到图形模式
如若要切换到分辨率320*200,颜色为256色的图形模式:
mov ah, 0h
mov al, 13h
int 10h
例如:
mov ax, 0a000h
mov es, ax
mov bx, 0
mov byte ptr es:[bx], 4
一个点只需要填一个字节
(x, y)对应的显卡偏移地址=y*320+x,段地址=A000h
mov ah, 0
int 16h
能读取上下左右方向键,并且读取键盘时不回显
mov ah, 1
int 16h
检测键盘缓冲区中有没有曾经按下的键,如果有则zf=0,没有则zf=1
jz nokey
mov ah, 0
int 16h
…
nokey:
继续刷新游戏画面
堆栈段定义
stk segment stack
db 200h dup(0)
stk ends
加上ss:stk
当源代码中并没有定义堆栈段时,编译器会自动生成一个堆栈段,ss=首段的段地址,sp=0
FLAG寄存器
FL共16位,但只用其中9位,包括6个状态标志和3个控制标志
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
X X X X OF DF IF TF SF ZF X AF X PF X CF
0 0 0 0 0 0 1
mov指令不会影响标志位的状态
CF:进位标志
移位指令也会影响CF的值,最后移出去的那一位会自动保存到CF中
- jc 有进位则跳
- jnc 无进位则跳
- adc 带进位加
- clc CF=0
- stc CF=1
ZF:零标志
结果为0时ZF=1
jz==je jnz==jne
cmp ax, ax
jz或je next会跳转到next
SF:运算结果的最高位
mov ah, 7fh
add ah, 1 ;AH=80h=10000000B, SF=1
sub ah, 1 l;AH=7F=01111111B, SF=0
js与jns
OF:溢出标志
在补码规则下,正数加正数得到负数时,OF=1;或者负数加负数得到正数时,OF=1;减法同理
jo与jno
PF:奇偶标志位
统计第八位中1的个数,偶数为1,奇数为0
jp/jpe与jnp/jpo
AF:辅助进位标志
第四位向高四位产生进位或借位
AF跟BCD码有关
1
2
3mov al, 29h
add al, 8
daa ;加法的十进制调整,这条指令会根据AF=1做AL=AL+6运算,使得AL=37h1
2
3mov al, 29h
add al, 1
daa ;AL=AL+6=2AH+6=30hDF:方向标志
控制字符串运算的方向
当DF=0时为正方向,当DF=1时是反方向
cld:使DF=0
std:使DF=1
源首地址<目标首地址时,复制按反方向
源首地址>目标首地址时,复制按正方向
只有源地址快和目标地址快有部分重叠时,才需要注意复制的正方向与反方向
IF:中断标志
IF=1时,允许硬件中断;IF=0时,禁止硬件中断
cli:使IF=0
sti:使IF=1
软件中断:在代码中用int n形式来调用某个函数集中的子函数
硬件中断:有硬件的某个事件触发,并由CPU自动插入并调用一个隐式的int n指令来调用某个中断服务子函数
TF:跟踪/陷阱标志
当TF=1时,CPU会进入单步模式,CPU在每执行一条指令后,会自动在该条指令与下条指令之间插入一条int 1h指令并执行它
当某条指令执行前TF=1,则该条指令执行后会自动执行int 1h单步中断
1
2
3
4
5
6pushf ;push FL
pop ax; AX=FL
or ax, 100000000B; 或100h
; and ax, not 100h; 或0FEFFh,把AX的第8位清零
push ax
popf ;TF=1jg,jl,jge,jle是符号数比较相关的跳转指令
- jg:jump if greater
- SF == OF 且 ZF == 0
- jge:SF == OF
- jl:SF != OF 不需要考虑ZF的状态
- jle:SF != OF || (SF == OF && ZF == 1)
- jg:jump if greater
jcxz:当cx=0时跳转
端口
端口地址范围:[0000h, 0FFFFh],共65536个端口
对端口操作使用指令in和out实现
通过60h端口,CPU与键盘之间可以建立通讯
1 | in al, 60h ;端口号<=FFh时可之间用常数 |
32位间接寻址方式
32位比16位多了以下这种寻址方式:
[寄存器+寄存器*n+常数]
其中n=2、4、8
如mov eax, [ebx+esi*4]
16位中只有4个寄存器可以用来放在[]内:[bx] [bp] [si] [di]
32位中对[]中的两个寄存器几乎不加限制
xchg
用于交换两个寄存器之间的值
或者交换寄存器和地址之间的值
1 | mov ax, 1 |
乘法指令:mul
8位乘法:被乘数为AL,乘积为AX
- mul bh表示AX=AL*BH
- mul后面所跟的操作数必须是8位寄存器或8位变量,不能是常数
16位乘法:被乘数为AX,乘积为DX:AX
- mul bx表示DX:AX=AX*BX
32位乘法:被乘数为EAX,乘积为EDX:EAX
- mul ebx表示EDX:EAX=EAX*EBX
带符号乘法为imul
- 第一类用法跟mul指令一样
mul及imul的第二类用法可以包含2个或3个操作数
1
2①mul eax, ebx ; eax=eax*ebx
②imul eax, ebx, 3 ;eax=ebx*3- ①②中的第2个操作数可以是寄存器也可以是变量
- ②中的第3个操作数只能是常数
除法指令:div
- 16位除以8位得8位
- ax为被除数,al为商,ah为余数
- ax / 除数 = AL…AH
- 32位除以16位得16位
- dx:ax / 除数 = ax…dx
64位除以32位得32位
- edx:eax / 除数 = eax…edx
idiv:带符号除法
地址传送指令:lea lds les
lea 取变量的编译地址
1
2
3
4lea dx, ds[bx+si+3] ;dx=bx+si+3
mov dx, bx+si+3 ;语法错误!
lea eax, [eax+eax*4] ;eax=eax*5 用lea做乘法[]中*后所跟的数只能是2的n次方
远指针
- 近指针:某个变量的偏移地址
- 远指针:某个变量的段地址和偏移地址
- C语言中的指针都为近指针
- les di, dword ptr ds:[bx] 取出ds:[bx]处的32位,高16位在es,低16位在di
- lds si, dword ptr ds:[bx] 与les类似,但高16位在ds,低16位在si
- fword ptr 特指48位宽度的变量
换码指令:在xlat执行前必须让ds:bx指向表,al必须赋值为数组的下标。执行xlat后,al=ds:[bx+al]
inc和dec不改变CF位
adc带进位加法
sbb带借位减法
neg ax ;即求ax=-ax
小数运算
fadd fsub fmul fdiv 小数的运算
1 | pi dd 3.14; 32位小数,相当于float |
CPU内部一共有8个小数寄存器,分别叫做st(0) st(1) st(2) … st(7)
其中st(0)可以简写成st
这八个寄存器的宽度都为80位
在载入数据时,先载入的值比如3.14进入st(0)后,再载入的值比如2.0并不是进入st(1),而是先把st(0)中的3.14存到st(1)中,再将2.0载入st(0)中
fld [a]:将[a]中值载入st中
fstp [a]:将st(0)中的内容取到[a]中,然后将st(0)弹出(st[1]中的内容会移动到st[0]中)
fstp st(0)会将st(0)中的内容清空
每一个小数运算之前都会自动插入一条wait指令
fild [a]:将a中的整数值当作小数载入到st中
逻辑运算指令
AND,OR,XOR,NOT,TEST
test ax, 8000h ;将即做ax&8000h,但不保存结果,只改变标志寄存器的值
移位指令
shl, shr, sal, sar, rol, ror, rcl, rcr
移出去的位都会放到CF中
sal是算术左移
sar为算术右移
shl为逻辑左移
shr为逻辑右移
算术左移(sal)及算术右移(sar)的对象是符号数
逻辑左移(shl)及逻辑右移(shr)的对象是非符号数
rcl:带进位循环左移
rcr:带进位循环右移
1 | mov ah, 0b6h |
字符串复制指令
movsb
rep movsb原理如下:
1 | again: |
单独的movsb指令所做的操作如下:
1 | byte ptr es:[di] = byte ptr ds:[si] |
movsw
rep movsw原理如下:
1 | again: |
单独的movsw指令与movsb指令类似
movsd
rep movsd原理如下:
1 | again: |
单独的movsd指令与movsb类似
32位系统
在32位系统中,为ds:esi与es:edi
字符串比较指令:cmpsb, cmpsw, cmpsd
cmpsb:
比较byte ptr ds:[si]与byte ptr es:[di]
当df=0时,si++,di++
当df=1时,si—,di—
rep cmpsb:连续进行比较
repe cmpsb:若本次相等则继续比较下一个
1 | again: |
repne cmpsb:若本次不想等则继续比较下一个
可以根据zf=1推出两个字符串全等
可以根据zf=0推出两个字符串全不等
cmpsw和cmpsd同理
字符串扫描指令:scasb, scasw, scasd
scasb:
1 | cmp al, es:[di] |
repne scasb:
1 | next: |
repe scasb:相等则进行下一次扫描
字符串操作指令:stosb, lodsb
stosb:
1 | es[di] = al |
rep stosb:循环cx次stosb
stosw:储存ax
stosd:储存eax
lodsb:
1 | al = ds:[si] |
lodsb通常没有rep前缀
lodsw:取出ax
lodsd:取出eax
控制转移指令:jmp, call, int
byte ptr
word ptr
dword ptr
fword ptr
qword ptr
tbyte ptr
短跳指令:机器码由2字节构成
第1个字节=EB,第二个字节=目标地址-下调指令的偏移地址
所有条件跳转都是近跳
- call 近跳,仅push ip,返回用ret
- call dword ptr,远跳,push cs, push ip,返回用retf
- int,远眺,pushf, push cs, push ip,返回用iret
汇编语言中的三种参数传递方式
寄存器传递
1
2
3
4
5
6f:
add ax, ax
ret
main:
mov ax, 3
call f变量传递
1
2
3
4
5
6
7f:
mov ax, var
add ax, ax
ret
main:
mov var, 3
call f
寄存器传递可以多线程,因为操作系统在进行多线程时会自动保存寄存器的值
变量传递不支持多线程
构造堆栈结构
1
2
3
4
5
6
7
8
9
10
11
12
13f:
push bp
mov bp, sp
mov ax, [bp + 4]
add ax, ax
pop bp
ret
main:
mov ax, 3
push ax
call f
back:
add sp, 2
动态变量的构造
将如下c语言函数转化成汇编:
1 | int f(int a, int b) { |
1 | f: |
C语言函数中需要保护bp, bx, si, di(32位为ebp, ebx, esi, edi):
1 | f: |
32位以上的cpu允许[esp+n]或[esp-n]来引用堆栈中的内容
递归
1 | int f(int n) { |
上述C语言递归函数可翻译成以下汇编代码:
1 | f: |
结束程序运行但保留内存块
1 | mov dx, 内存块的长度 |
PSP:在code段之前,100字节,由操作系统自动分配
调用int 21h/ah=3h功能时,需要把PSP的占用的空间长度也计算到当前程序占用的内存块长度中
dx = ((100h + code段的长度) + 0fh ) / 10h
除以10h是因为长度单位为节(1节=10h字节)
加上0fh是考虑到code段长度无法被10h整除
1 | final label byte |
int、iret
中断指令格式:int n; 其中n的范围是[0, 0FFh]