汇编复习笔记

导语

起源

助记符

  首先我们来聊聊为什么会有汇编这么个东西。

  老师肯定给你讲了,汇编语言是机器语言的助记符,但是我想只听这句话应该完全不理解它是什么东西。首先下面这张图应该能理解:

架构图

  1. 机器语言出来的时候还没有操作系统

  2. 操作系统被开发出来,那时候纯机器语言写,有了操作系统方便了很多,但写程序还是机器语言写

  3. 人们觉得用机器语言写一堆 0 和 1 实在太麻烦了,想找个办法省略一点

  4. 汇编语言就诞生了

第一版汇编语言的编译器是用纯机器语言写的,然后后面就轻松了。

所以说,汇编语言是机器语言的助记符

总结:懒是人类创新的第一驱动力

关于 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

  • 注意:

  1. OPTION 为上述移位指令中的任何一个;
  1. OPR 用除了立即数以外的任何寻址方式;
  1. 移位次数由 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:

  • 用来设置当前计数器的值。