汇编复习笔记
导语
起源
助记符
首先我们来聊聊为什么会有汇编这么个东西。
老师肯定给你讲了,汇编语言是机器语言的助记符,但是我想只听这句话应该完全不理解它是什么东西。首先下面这张图应该能理解:
-
机器语言出来的时候还没有操作系统
-
操作系统被开发出来,那时候纯机器语言写,有了操作系统方便了很多,但写程序还是机器语言写
-
人们觉得用机器语言写一堆 0 和 1 实在太麻烦了,想找个办法省略一点
-
汇编语言就诞生了
第一版汇编语言的编译器是用纯机器语言写的,然后后面就轻松了。
所以说,汇编语言是机器语言的助记符。
总结:懒是人类创新的第一驱动力。
关于 ISA(Instruction set architecture,指令集体系结构)
当然,你可能看到了操作系统下面还有个 ISA,所以这就是我接下来要给你说的东西。
芯片最先是 Intel 那帮子人搞出来的(最先诞生了 386,486,所以这也就是 x86 的由来),芯片里面是一堆集成的门电路。所以为了把指令,数据,等等翻译成相应的二进制,搞出了一个指令集系统。
类似于英语和汉语的关系,汉语一个词,英语里面一个单词。
比如他们把加法指令 ADD
翻译成 00000011
。这个 Intel 公司有文档的。
后来 AMD 那帮子搞机器的,看 Intel 搞得风生水起,盆满钵满,也想赚钱,所以自己就新搞了一个新的芯片体系架构出来,叫 arm 架构,你应该听过。性能好像有时候还比 Intel 好,有时候又不好,反正能用,是个玄学问题,所以用户就有两家芯片厂商可以选择。
这个时候他们也必然涉及到这个问题,把指令翻译成相应的二进制,所以他们自己又有一种指令集系统。(其实你如果想,也可以自己设计一个指令集系统出来,只是没人用,笑)
所以就诞生了两个汇编语言格式。
一个 Intel 格式,一个 AT&T 格式。
; AT&T 格式汇编代码
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %edx
movl 12(%ebp), %eax
addl (%edx), %eax
movl %eax, (%edx)
popl %ebp
ret
; Intel 格式汇编代码
push ebp
mov ebp, esp
mov edx, dword ptr [ebp+8]
mov eax, dword ptr [ebp+12]
add eax, dword ptr [edx]
mov dword ptr [edx], eax
pop ebp
ret
详情可以参考这篇:https://blog.csdn.net/goodcrony/article/details/92794938
由于我对这两家公司的爱恨情仇不是很了解,只知道大概是这么来的,你感兴趣的话可以自己搜一下。
寄存器
汇编当中有个绕不开的东西,叫做寄存器,这里简单说一下。
CPU 运算速度非常快,为了性能,CPU 在内部开辟一小块临时存储区域,并在进行运算时先将数据从内存复制到这一小块临时存储区域中,运算时就在这一小块临时存储区域内进行。我们称这一小块临时存储区域为寄存器。
这块空间也不用特别大,够放执行一次的数据和指令就行了。寄存器就是搞这个的。可以反复利用。
其实后面还有个叫高速缓存(Cache)的东西,这个我们之后有时间再说。
data-instruction=>start: 指令 / 数据
register=>operation: 寄存器
cpu=>operation: CPU 运算
re-register=>operation: 寄存器
memory=>end: 存储
data-instruction->register->cpu->re-register->memory
当然,如果可以直接送入内存/CPU 的话,就不用寄存器了,所以寄存器不是必须的,是可选的,只是大部分情况下都要用。
寻址方式
就是给你一个指定地点,要你跑到那个地点去拿东西。然后这个地点有很多种不同的方式告诉你。
比如:
; AX:我要的数据在 BX 寄存器里
MOV AX, BX
; AX:我要的数据在 DS 寄存器的值加上 2000H 得到的计算结果里,这个结果是个地址,你去这个地址上把它取过来
; 至于为什么要加上 DS,这个是硬性规定,只能靠记忆
MOV AX, [2000H]
Intel 格式汇编源程序讲解
; segment 是关键字,表示定义一个段,至于这个是数据段还是代码段,要看之后的伪指令,暂时先不管
; segment 之前是该段的名字,所以这里的 data 不是关键字,只是一个名字,想取什么名字都可以
data segment
; 定义一个代号(用在写代码里,类似于变量名)叫 x 的字,值为 1234h
; dw 意思为 Define Word 所以缩写为 dw,类似还有 db(Define byte), dd(Define Double Word)
; 1234h 后面的 h 意为 HEX(十六进制),表明这是十六进制的 1234,而非十进制的一千二百三十四
x dw 1234h
; 与上同理
y dw 5678h
; 这里 ? 表示我暂时不知道这个值是多少,只是用来占个位置。也用于之后写数据到这个位置(把数据写进去当然不用知道这个值是多少,要计算机算了之后才能知道)。
z dw ?
; ends 表示结束这个 data 段,有始有终
data ends
; 定义一个段,名字叫 code,见名知意,用来存放代码的
code segment
; assume 是汇编伪指令,类似于 C 当中的 #include,是给编译器看的东西
; assume 意为假设
; 告诉编译器,假设 code 段与寄存器 cs 有联系,data 段同理
; 寄存器 cs, ds 具体指什么请看后面
assume cs:code,ds:data
; 标识代码段真正开始,之前的都是伪指令
start:
; 将 data 段的首地址赋值给 ax 寄存器,记住是首地址
; mov 表示 move,赋值的意思
mov ax,data
; 将 ax 寄存器中的值赋值给 ds 寄存器
; 与上面那条指令连起来,就是把 data 段的首地址赋值给 ds 寄存器
; 至于为什么要用 ax 寄存器中转,是因为英特尔不准将地址直接存进 ds
; 可能是安全原因吧,我不清楚,反正比较麻烦,所以汇编语言经常是几条语句连在一块看
mov ds,ax
; 将 data 段中名字叫 x 的那个值,存进 ax
mov ax,x
; 将 ax 当中的值(也即 x,也即 1234h),与 y 相加(add)
; 然后重新赋值给 ax 寄存器,此时 ax 存放的就是 1234h+5678h
add ax,y
; 将 ax 寄存器中的值赋值给 z 所在的地址(那个问号?)
; 意思就是存进内存
mov z,ax
; ah 是 ax 寄存器高位的简称(AX's High byte)
; ax 是十六位寄存器,而 4c 是八位,所以不能直接存进 ax,汇编语言里面必须时刻注意数据与寄存器的位数是否保持一致
; 不过这里是另外一个原因:与下面 int 21h 结合在一起,表示中断程序,让 cpu 转而去执行 ah 里面存的代号(也即 4c)所代表的功能(即是 带返回码终止程序)
; 关于 ah 里面的代号表示什么意思,详情请看书 471 页及其以后
mov ah,4ch
; 这里的 int 并非数据类型 int,而是打断,中断(interrupt)的前三个字母简写
; 中断程序,中断类型为 21h
; 该类型指的是 DOS 系统功能调用,详情看书 470 页
; 所以需要先把 ah 里面的系统功能调用代号赋值好,再执行这句话
int 21h
; 代码段结束
code ends
; 代码行数真正在这里结束(其实我也不知道为啥,硬记吧,反正就是一个框架性质的东西)
; 注意 end 与之前的 ends 的区别
end start
学习建议
-
理解了上述框架之后,就只用学习指令了
-
理解每一个指令是什么意思,谁加减谁,又牵涉到哪个寄存器
-
然后记指令的时候,务必请和英语联系,否则硬记十分痛苦
-
说的就是跳转指令这些
-
JNB(Jump if Not Below,如果不低于则跳转,也即大于等于时跳转)
-
JAE(Jump if Above or Equal,如果大于或者等于则跳转,所以跟上面那个是一样的)
-
JNC(Jump if Not Carry,进位标志符不变时则跳转,跟上面意思也是一样的,进位标志符:标志寄存器 / 程序状态寄存器 里面的一个位之一,之后的笔记你会看到)
-
如果不了解每个指令是干啥的,建议与上下语句连在一块儿看,可能就会懂了
-
之后就是我复习的时候做的笔记,也是考点和比较重要的地方,可以参照看一下
-
看完之后,看到汇编其实也就觉得没什么难的,只是麻烦了一点
-
没啥了
第一章
-
进制相互之间转换
-
同进制运算
-
正反补码
-
补码运算
-
无符号整数
-
字符表示法(ASCII)
-
逻辑运算
第二章
硬件和软件
中央处理器
寄存器
通用寄存器
-
AX(Accumulator,累加)
-
BX(Base,基址)
-
CX(Count,计数)
-
DX(Data,数据)
-
以上四个可以 16 位访问,也可以 8 位访问
-
BP(Base Pointer,基址指针)
-
SI(Source Index,源变址)
-
DI(Destination Index,目的变址)
-
以上三个只能以 16 位访问
专用寄存器
-
IP(Instruction Pointer,指令指针)
-
SP(Stack Pointer,堆栈指针)
-
FLAGS / PSW(Program Status Word,程序状态)
-
OF(Overflow Flag,溢出标志)
-
SF(Sign Flag,符号标志)
-
ZF(Zero Flag,零位标志)
-
CF(Carry Flag,进位标志)
-
AF(Auxiliary Carry Flag,辅助进位标志)
-
PF(Parity Flag,奇偶标志)
-
DF(Direction Flag,方向标志)
-
TF(Trap Flag,陷阱标志)
-
IF(Interrupt Flag,中断标志)
段寄存器
-
CS(Code Segment,代码段)
-
DS(Data Segment,数据段)
-
SS(Stack Segment,堆栈段)
-
ES(Extra Segment,附加段)
存储器
存储器的无穷大空间数组,可以通过字节,字和双字等不同形式访问。
最大寻址范围取决于地址总线长度。
外部设备
第三章
寻址方式
; 立即寻址(Immediate Addressing):使用的是立即数,所以叫立即寻址
MOV AL, 5
; 寄存器寻址(Register Addressing):使用的是寄存器,所以叫寄存器寻址
MOV AX, BX
; 直接寻址(Direct Addressing):直接到相应地址上找数据,所以叫直接寻址
; 需要加上 DS 里面的内容
MOV AX, [2000H]
; 寄存器间接寻址(Register Indirect Addressing)
MOV AX, [BX]
; 寄存器相对寻址 / 直接变址寻址(Register Relative Addressing)
MOV AX, COUNT[SI]
MOV AX, [COUNT+SI]
; 基址变址寻址(Based Indexed Addressing)
MOV AX, [BX][DI]
MOV AX, [BX+DI]
; 相对基址变址寻址(Relative Based Indexed Addressing)
MOV AX, MASK[BX][SI]
MOV AX, MASK[BX+SI]
MOV AX, [MASK+BX+SI]
; 比例变址寻址(Scaled Indexed Addressing)
MOV EAX, COUNT[ESI*4]
; 基址比例变址寻址(Based Scaled Indexed Addressing)
MOV EAX, [EAX],[EDX*8]
; 相对基址比例变址寻址(Relative Based Scaled Indexed Addressing)
MOV EAX, TABLE[EBP][EDI*4]
; 段内直接寻址(Intrasegment Direct Addressing)
; PROGIA 和 QUEST 均为转向的符号地址
; 如果位移量为 16 位,则在符号地址前加操作符 NEAR PTR
; 如果位移量为 8 位,则在符号地址前加操作符 SHORT
JMP NEAR PTR PROGIA
JMP SHORT QUEST
; 段内间接寻址(Intrasegment Indirect Addressing)
JMP BX
JMP WORD PTR[BP+TABLE]
JMP TABLE[BX]
JMP [BX][SI]
JMP ECX
; 段间直接寻址(Intersegment Direct Addressing)
JMP FAR PTR NEXTROUTINT
; 段间间接寻址(Intersegment Indirect Addressing)
JMP DWORD PTR[INTERS+BX]
JMP DWORD PTR [EDI]
数据传送指令
-
通用数据传送指令
-
MOV
-
PUSH
-
POP
-
PUSHA / PUSHAD
-
POPA / POPAD
MOV AX, DATA_SEGMENT
MOV DS, AX
MOV AL, 'E'
MOV BX, OFFSET TABLE
; PUSH 和 POP 的指针和数据存储的先后顺序
PUSH SRC
POP DST
PUSHA
POPAD
-
地址传送指令
-
LEA
LEA REG, SRC
-
算术指令
-
ADD
-
INC
-
SUB
-
DEC
-
NEG
-
CMP
-
MUL
-
DIV
; (DST) + (SRC) --> (DST)
ADD DST, SRC
; (OPR) + 1 --> (OPR)
INC OPR
; (DST) - (SRC) --> (DST)
SUB DST, SRC
; (OPR) - 1 --> (OPR)
DEC OPR
; (OPR) --> (OPR)
; 作用是求补码
NEG OPR
; 比较指令
; 跟 SUB 执行一样的操作,但是不保存结果
; 只保存 FLAGS
CMP OPR1, OPR2
; 注意:
; 目的操作数只能为 AX / AL
; 结果存放在 AX / DX+AX / EDX+EAX 中
; 其中 (E)AX 存低位,(E)DX 存高位
MUL SRC
; 注意:
; 16 位被除数在 AX 中,8 位除数为源操作数。8 位商存在 AL 中,8 位余数在 AH 中
; 32 位被除数在 DX,AX 中,其中 DX 为高位字。16 位除数为源操作数,16 位商存在 AX 中,16 位余数在 DX 中
; 64 位被除数在 EDX,EAX 中,其中 EDX 为高位双字。32 位除数为源操作数,32 位商存在 EAX 中,32 位余数在 EDX 中
DIV SRC
-
逻辑运算指令
-
AND
-
OR
-
NOT
-
XOR
-
TEST
-
移位指令
-
SHL:逻辑左移
-
SAL:算术左移
-
SHR:逻辑右移
-
SAR:算术右移
-
ROL:循环左移
-
ROR:循环右移
-
通用用法:OPTION OPR, CNT
-
注意:
- OPTION 为上述移位指令中的任何一个;
- OPR 用除了立即数以外的任何寻址方式;
- 移位次数由 CNT 决定,可以是 1 - 31。但是如果需要移位的次数大于 1,需要将其存在 CL 寄存器中,移位指令中的 CNT 直接写为 CL 即可。如果为 1,啧不受此限制
-
串处理指令
-
MOVS:串传送
-
CMPS:串比较
-
SCAS:串扫描
-
LODS:从串取
-
STOS:存入串
-
INS:串输入
-
OUTS:串输出
-
与上述基本指令配合使用的前缀:
-
REP:重复
-
REPE / REPZ:相等 / 为零则重复
-
REPNE / REPNZ:不相等 / 不为零则重复
-
控制转移指令
-
JMP
-
段内直接短转移:
JMP SHORT OPR
-
段内直接近转移:
JMP NEAR PTR OPR
-
段内间接近转移:
JMP WORD PTR OPR
-
段间直接远转移:
JMP FAR PTR OPR
-
段间间接远转移:
JMP DWORD PTR OPR
-
JE / JZ
-
JNE / JNZ
-
JS / JN
-
JO / JNO
-
比较无符号数:(B: Below, E:Equal, A:Above, C:Carry, N:Not)
-
JB / JNAE / JC
-
JNB / JAE / JNC
-
JBE / JNA
-
JA / JNBE
-
比较有符号数:(G: Greater, E:Equal, L:Less, C:Carry, N:Not)
-
JL / JNGE
-
JNL / JGE
-
JLE / JNG
-
JG / JNLE
循环
子程序
-
CALL
-
RET
中断
第四章
伪操作
-
地址计数器 $:
-
用在指令中时,表示本条指令的第一个字节的地址。
-
在指令中用到 $ 时,它只代表该指令的首地址,而与 $ 本身所在的字节无关。
-
用在伪操作的参数字段时,表示的是地址计数器的当前值。
-
ORG:
-
用来设置当前计数器的值。