某人的汇编学习笔记
因为汇编太抽象了所以决定复活博客并记个笔记(
使用的教材是汇编语言程序设计,第五版,钱晓捷主编
1.基础知识
二进制,原码反码补码,BCD码,ASCII码,Unicode就不说了(x_x;) 网上都可以方便的查到,直接说8086
-
1.1 功能结构
等下我去盗张图去(x
图上可以看出,除了地址总线为20位外, 其他的均为16位(????H)另外有14个寄存器,多个标志寄存器算作一个。完成一条指令的过程为取指和执行,指令的地址由代码段CS寄存器和指令指针寄存器IP共同决定
CSCS CSCS CSCS CSCS 0000 + IPIP IPIP IPIP IPIP
取出这个指令之后进入指令队列等待执行。接下来EU从队列中取出指令,译码,由ALU完成计算,数据可以来自内部寄存器,内存或外部设备
-
1.2 寄存器
这部分除了记忆好像没办法
-
1.2.1 通用寄存器
- 数据寄存器(都被分为高/低(H/L)两部分,各8位,两个16进制数字)
- AX 累加器,在加法指令中被作为一个被加数使用
- BX 基址寄存器(存地址的)可以被[]符号括起来间接寻址,即以这个寄存器里面的值为地址,取这个地址上的数
- CX 计数器 一般来说就是个循环的时候用的计数器
- DX 数据寄存器 在输入输出指令中存放外设端口地址(?)
- 实际上这些都可以用来存数进行运算,只不过他们本身有些特殊功能
- 变址寄存器
- SI 源地址寄存器,可以被[]符号括起来间接寻址
- DI 目的地址寄存器,可以被[]符号括起来间接寻址
- 这两个相当与C++里的指针,可以与下面的段寄存器联合使用,作为偏移量指向数组或者栈中的元素
- 指针寄存器
- BP 基址指针 指向栈中某位置的寄存器,可以被[]符号括起来间接寻址
- SP 堆栈指针 只是用来指向栈顶, 随POP和PUSH操作自动+2或者-2
- 这两个都是用在与栈相关的操作上
- 数据寄存器(都被分为高/低(H/L)两部分,各8位,两个16进制数字)
-
1.2.2 标志寄存器
从高位到低位分别为
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 OF DF IF TF SF ZF AF PF CF - CF 进位/借位标志
- ZF 是0!
- OF 溢出了,注意和CF区分,CF是进位, 而溢出会导致答案错误比如1000+1111=[1]0111 两个负数相加溢出变成整数
-
1.2.3 指令指针寄存器
IP 是由CPU自己控制,进行语句的跳转,不能直接修改,需要借助跳转,分支调用和返回指令
-
1.2.4 段寄存器
- CS 代码段,放指令的内存段
- SS 堆栈段,表明程序的堆栈起始地址 和CS类似,由SS确定栈的起始位置,SS+SP作为栈顶,这样栈就有了一个确定的最大大小,一般为所需空间的120%,每加入一个数,SP就-2,当SP==SS的时候就是栈满了
- DS 数据段 存放数据, ES 附加段 处理数据串(?)
- 这些东西你可以自己指定,比如先给ax赋值,然后把ax赋给ds。但是你需要保证不能发生冲突。一般来说,都是编译器处理这些事情,比如你jmp far 到另外某个段中,编译器会处理出来这两个段之间的距离,然后在真正放入内存执行的过程中,操作系统会自己figure out具体的跳转地址(就相当于实际地址+你写的跳转的offset来完成跳转)。这样就解决了你和编译器都无法预测真正执行时这些代码到底放在内存中的哪里的问题
-
-
1.3 数据怎么存
-
1.3.1 一些名词
BIT 位,8位是BYTE,两个BYTE 16位是WORD,刚好是AX寄存器的大小 DWORD两个字32位,QWORD四个字64位
存储器是按字节组织的,所以一个地址对应的是1字节的存储区域
位编号是从右向左,由低到高,15-0,最低位0叫LSB(最低有效位),最高位叫MSB(最高有效位),可能是7,15,31,63
-
1.3.2 Little Endian
占用多个字节的WORD或者DWORD怎么存呢?连续占用多个字节,如果这个字的低字节存在地址低的地方(低对低,高对高),就叫做小端方式(Little Endian)一般x86都是这种存储方式
有些其他架构的CPU会采用Big Endian
-
1.3.3 内存好大≧ _ ≦,怎么管理
在早期的8086处理器上,一般采用分段管理,后来有了分页和分段,在8086上,每段大小最大为64k,so,每个存储空间就可以用 段基地址:段内偏移地址来表示了(不用想了,就是那个什么S+什么P,这个叫逻辑地址,具体算出来叫物理地址)具体见书P23
在分段之后,就可以把这些段按功能分类成代码段,堆栈段,数据段,附加段了,并且可以用CS,SS,DS,ES这四个寄存器来指向他们的位置!另外,各个段之间的内存区域是允许重叠的,但是绝不能发生冲突
那又有一个问题,不同的指令有默认的段寄存器,如何访问存储在其他段的数据呢?为此8086提供了段超越操作,格式为
?s:
,可以借此指明你要访问的数据处在哪个段中。[附表]段寄存器的使用规定:
访问存储器的方式 默认的段寄存器 可超越的段寄存器 偏移地址 取指令 CS None IP 堆栈操作 SS None SP 除下列以外一般数据访问 DS CS,ES,SS EA 串操作源操作数 DS CS,ES,SS SI 串操作目的操作数 ES None DI BP为基址的寻址方式 SS CS,ES,SS EA
-
-
1.4 数据怎么找
-
1.4.1 IMM立即数寻址方式
一般用于给变量赋值(一个数字),这个值是直接存放在操作码之后的,例如
1 2
mov al,05h mov ax,2333h
-
1.4.2 寄存器寻址方式
可以看作是C++里面的a=b,将一个寄存器中的值赋给另一个
mov bx, ax
就是让bx=ax,注意这两个寄存器位数一定要相等,不能不同,否则如果遇到有符号数比如0xff,就无法界定该是-1还是255了喵 -
1.4.3 存储器直接寻址方式
寄存器还是太少了,数据还是存在内存中方便,所以你可以直接用中括号括起来内存中的某地址,来代表直接取这个地址中的数,相当于C++的*
1
mov ax, [2333h]
在这个代码中,数据默认是存在DS段中的,所以2333h实际上相对于DS的偏移量,然后就是取出实际地址的低位放到AL,高位(低位地址+1)放到AH
如果涉及到段超越的话就直接改成比如ES:[2333h]就行
-
1.4.4 存储器(用)寄存器间接寻址方式
-
1.4.5 存储器(用)寄存器相对寻址方式
被用的寄存器就相当于C++中的数组下标,但是回忆一下寄存器们的用途,你会发现不是什么寄存器都可以被中括号括起来当数组下标的,只有BX,BP和SI,DI。相对寻址方式去掉给定的常量就变成了间接寻址方式。相对寻址方式算出的最终地址对64k取模 例子:
mov ax, [di + 06h] mov ax, [bp]
,如果DI是0xFFFE,那最终地址就是0x4 -
1.4.6 存储器(用)基址变址接寻址方式
就是BX/BP+SI/DI,若使用的是BX,默认数据段是DS,若使用的是BP,默认是堆栈段SS
-
2 指令系统
-
2.1 数据传送指令
- MOV
- XCHG
- XLAT
-
2.2 堆栈操作指令
- PUSH
- POP
-
2.3 标志传送指令
- LAHF/SAHF
- PUSHF/POPF
-
2.4 地址传送指令
- LEA
- LES
- LDS
-
2.5 算数运算指令
-
2.5.1 加法
- ADD
- ADC
- INC
- 多位加法,进位
-
2.5.2 减法
- SUB
- SBB
- DEC
- NEG
- CMP
-
2.5.3 乘法
- MUL
- IMUL
-
2.5.4 除法
- DIV
- IDIV
-