汇编笔记
寄存器register
学习汇编语言,首先必须了解两个知识点:寄存器和内存模型。
先来看寄存器。CPU
本身只负责运算,不负责储存数据。数据一般都储存在内存之中,CPU
要用的时候就去内存读写数据。但是,CPU
的运算速度远高于内存的读写速度,为了避免被拖慢,CPU
都自带一级缓存和二级缓存。基本上,CPU
缓存可以看作是读写速度较快的内存。
但是,CPU
缓存还是不够快,另外数据在缓存里面的地址是不固定的,CPU
每次读写都要寻址也会拖慢速度。因此,除了缓存之外,CPU
还自带了寄存器(register
),用来储存最常用的数据。也就是说,那些最频繁读写的数据(比如循环变量),都会放在寄存器里面,CPU
优先读写寄存器,再由寄存器跟内存交换数据。
内存模型:Heap
-
程序运行过程中,对于动态的内存占用请求(比如新建对象,或者使用
malloc
命令),系统就会从预先分配好的那段内存之中,划出一部分给用户,具体规则是从起始地址开始划分(实际上,起始地址会有一段静态数据,这里忽略)。举例来说,用户要求得到10个字节内存,那么从起始地址0x1000
开始给他分配,一直分配到地址0x100A
,如果再要求得到22个字节,那么就分配到0x1020
。 -
这种因为用户主动请求而划分出来的内存区域,叫做
Heap
(堆)。它由起始地址开始,从低位(地址)向高位(地址)增长。\(Heap\) 的一个重要特点就是不会自动消失,必须手动释放,或者由垃圾回收机制来回收
内存模型: Stack
-
除了
Heap
以外,其他的内存占用叫做Stack
(栈)。简单说,Stack
是由于函数运行而临时占用的内存区域。 -
系统开始执行
main
函数时,会为它在内存里面建立一个帧(frame
),所有main
的内部变量(比如a
和b
)都保存在这个帧里面。main
函数执行结束后,该帧就会被回收,释放所有的内部变量,不再占用空间。 -
在函数内部在调用一个函数
process()
的时候,系统也会为该函数再新建一个帧,一般来说调用栈有多少层,就有多少帧. -
当函数运行结束之后,它的帧会被回收,系统回到函数中断执行的地方继续执行上一个帧的流程.通过这个机制,可实现函数的层层调用,且每一层函数都可以使用自己的本地变量
-
Stack
是由内存区域的结束地址开始分配,从高位到地位分配
CPU
指令
使用 g++ file.cpp -S out.s
或者gcc file.cpp -S out.s
,可用C/C++
生成相应的汇编代码
#include<stdio.h>
void hello()
{
int a=1,b=2;
a=a+b;
printf("hello world\n");
}
int main()
{
int a=1,b=2;
a=a+b;
hello() ;
return 0;
}
.LC0:
.string "hello world"
hello():
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], 1
mov DWORD PTR [rbp-8], 2
mov eax, DWORD PTR [rbp-8]
add DWORD PTR [rbp-4], eax
mov edi, OFFSET FLAT:.LC0
call puts
nop
leave
ret
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], 1
mov DWORD PTR [rbp-8], 2
mov eax, DWORD PTR [rbp-8]
add DWORD PTR [rbp-4], eax
call hello()
mov eax, 0
leave
ret
push
指令
push rbp
push
是CPU
指令,rbp
是该指令的算子.一个CPU
指令可以有0到多个算子
寄存器
IA-32
体系结构中有10个32位和6个16位处理器寄存器.寄存器分三类:
-
通用寄存器
-
数据寄存器
-
指针寄存器
-
索引寄存器
-
-
控制寄存器
-
段寄存器
指令系统
80x86寻址方式
-
寻址:寻找操作数的地址.
-
寻址方式:寻找操作数的方式
80x86
指令格式
- 指令助记符 +操作数1,+操作数2,+操作数3
- 指令的操作数个数可以是0,1,2,3个
如何确定偏移地址的值
- 与数据有关的十种方式,
- 立即寻址
- 寄存器寻址
- 直接寻址
- 寄存器间接寻址
- 寄存器相对寻址
- 基址变址寻址
- 相对基址变址寻址
- 比例变址寻址
- 基址比例变址寻址
- 相对基址比例变址寻址
- 与转移地址有关的4种寻址方式
- 段内直接寻址
- 段内间接寻址
- 段间直接寻址
- 段间间接寻址
寻址方式
立即寻址方式
MOV AX, 5
MOV AX, 05H
把十六进制数05H
移动到通用寄存器AX
上
寄存器寻址方式(无EA)
- 指令所要的操作数已存储在寄存器中或者把目标数存入寄存器
MOV AX , BX
- 指令中可以引用的寄存器及其符号名称:
8位寄存器 | AH、AL、BH、BL、CH、CL、DH、DL |
8位寄存器 | AX、BX、CX、DX、SI、DI、SP、BP和段寄存器等 |
8位寄存器 | EAX、EBX、ECX、EDX、ESI、EDI、ESP、EBP。 |
-
DST 和SRC的字长一致
-
CS不能用MOV指令改变
直接寻址方式
-
指令所要的操作数存放在内存中,在指令中直接给出该操作数的有效地址
-
物理地址=\((DS)\times16d+EA\)
-
MOV AX , [2000H]
-
MOV AX, VALUE
-
MOV AX , [VALUE]
-
-
物理地址=\((ES)\times16d+EA\)
-
MOV AX , ES:[2000H]
-
MOV AX, ES:VALUE
-
MOV AX , ES:[VALUE]
-
在通常情况下,操作数存放在数据段中,所以,其物理地址将有数据段寄存器DS和指令中的有效地址直接形成但如果使用段超越前缀,那么操作数可存放在其他段中.如:
MOV ES:[1000H], AX
-
立即寻址:1234H
-
直接寻址;[1234H]
-
寄存器间接寻址方式→EA基址/变址
-
MOV AX
,[BX]或MOV AX,ES:[BX]
寄存器相对寻址方式→EA=基址/变址+位移量
-
MOV AX,COUNT[SI]
或MOV AX,3000H[SI]
-
MOV AX,[COUNT+SI]
或MOV AX,[3000H+SI]
-
MOV AX,ES:COUNT[SI]
或MOV AX,ES:[COUNT+SI]
-
操作数在存储器中,其有效地址是一个基址寄存器(
BX
、BP
)或变址寄存器(SI
、DI
的内容和指令中的8位/16位偏移量之和。
基址变址寻址方式→EA=
基址+变址
-
操作数在存储器中,其有效地址是一个基址寄存器(BX、BP)和一个变址寄存器(
SI
、DI
)的内容之和。-
MOV AX,[BX][SI]
或MOV AX,[BX+SI]
-
MOV AX,ES:[BX][SI]
或MOV AX,ES:[BX+SI]
-
相对基址变址寻址方式→EA=基址+变址+位移量
-
MOV AX,COUNT[BX][SI]
或MOV AX,-46H[BX][SI]
-
MOV AX,COUNT[BX+SI]
或MOV AX,0246H[BX+SI]
-
MOV AX,[COUNT+BX+SI]
或MOV AX,[-56H+BX+SI]
-
MOV AX,ES:COUNT[BX][SI]
或MOV AX,ES:[0246H+BX+SI]
相对比例变址寻址→EA=变址×比例因子+位移量
-
MOV EBX,[EAX][EDX*8]
或MOV EBX,
-
[EAX+EDX\*8]
或MOV EBX,[ESP][EAX*2]
-
MOV EAX,TABLE[EBP][EDI*4]
或MOV EAX,[TABLE+EBP+EDI\*4]
8位/16位/32位的位移量
注意1:比例因子只能与32位变址寄存器:EAX、EBX、ECX、EDX、EBP、ESI、
EDI联用;且比例因子只能为1、2、4或8;
注意2: 8)、9)、10)只能是32位寻址,没有16位寻址;