本学期的内容其实都是挺新鲜的知识,秉持着不听课的优良传统看书自学。但是汇编的书编写的太过分了,头次看实在难以直观的看到重点和思考角度,复习也没法一目了然的看到知识结构。于是顺手就写了。
二进制运算
学了无数遍了好烦啊,回头看考研课的时候统一写一下吧。
寄存器
常见的寄存器如下:
通用寄存器:寄存运行时产生的数字
AX,BX,CX,DX,他们全是16位,支持双字读取,也支持单字节读取,这时写为AH,AL表示高位,低位。
这几个寄存器又有各自的特殊作用复用:
A(accumulate)用作累加器,IO功能使用
B(base)常用作基址寄存器
C(count)保存计数值,计数器
D(data)双字长运算的时候做高位,做IO端口地址
在四个通用寄存器复用中,BX寄存器可以作为基址寄存器是架构上的允许特性,其他三个则是约定俗成的规则,不具有硬性约束力。
16位寄存器:常做地址相关
SP,BP,SI,DI:他们也是16位,不同于其他通用寄存器,没有单字访问的能力。因此常用于表示地址相关的内容,具体如下
SP(stack pointer)堆栈指针
BP (base pointer)基址指针
DI (destination index)目的变址寄存器
SI (sourse index)源变址寄存器
IP(instraction pointer)指令指针寄存器,指向下一条指令的偏移量
他们往往配合以下段寄存器使用
CS:代码段寄存器,与IP连用获得下一条指令的位置
DS:数据段寄存器,指向数据段头与DI,SI连用,表达数据段中内存位置。
SS:堆栈段寄存器,与SP连用表示堆栈中内存位置。
ES:额外段寄存器,与DI连用,指向额外数据段。
es寄存器有很多位,功能各不相同,需要时查表吧。
还有一个表示状态的特殊寄存器
FLAG:表示程序状态的寄存器,位数同样很多而且各有作用。
命名好乱……背后定有历史包袱。
常见的内存类型记号
教材这里写的超级晚……附录也没声明,想找还是散装的……
mem:储存元件
reg:寄存器
ac:累加器
segreg:段寄存器
MOV指令
书上写了一大堆屋檐了,穷举了一堆之后才给形式化声明,简直倒反天罡。
形式化指令格式
$$
MOV ,DST , SRC
$$
将SRC内容塞进DST里面
其中DST:mem,reg,ac,segreg,
SRC:mem,reg,data,segreg
寻址方式
要说mov,对于学习者最重要的就是理解汇编的内存寻址方式,因为汇编是非常底层的语言,可以按照物理地址来选定内存。寻址方式如下:
实际上,汇编的寻址方式和C语言十分类似。采用基准+偏移量的形式寻找,类比到C语言中,就是指针访问数组与下标访问等价,a[5]== *(a+5)
对于指定定点,有如下表示方法:
- 直接指定常量(不调用内存而是记录常量)
- 寄存器:寄存器是有名字的内存,可以直接指名访问
- 基址+(变址*比例因子)+位移量
其中最后一种较为常见,对以下术语做出阐释
基址:指定的其实位置,可以是段/寄存器等,放在基址寄存器里(类比数组名)
比例因子:扩大访问范围的缩放因子,可以一定程度上扩大16位的限制。
变址:也就是所谓的偏移量(类比下标)
位移量:查询的数据字长,指定位0,8,16(16位),0,8,32(32位)(类比数据类型)
- 必须明白的事实:每一个程序包括的寄存器一般是独立的(系统足够复杂的情况下),于是各有自己的指令域,数据域,堆栈
因此,所有的位移量都是在DS(数据段寄存器)作为默认基址的前提下进行的。如果基址不是DS,可以显式的指定为其他的段寄存器。如ES:2000H即表示在ES作为基址的前提下偏移2000H个字长。 - 基址寄存器(有默认)+(变址寄存器)*(比例因子)+(指定偏移常量)+(指定位移量)
段与偏移量
8086只有16位字长,也就是说对于最多访问2e16的内存,也就是64K的内存单元,但是这样显然不够,因此,对于1M的内存单元,将其拆为64K份,每一份称为段。所以段的定义就是内存的种寻址上的单位,因为1M=2e20=2e16*2e4,所以一个段内有16位,这样,使用两个16位数就可以指出更多内存单元,计算公式为物理地址=段首地址<<4+偏移量。当然,这样可能会出现一个内存单元有多种表示的问题,这是完全合法的。
因此,对于一个内存就有了新的存取单位和表示方法,所以设置寄存器记录段首位置,一个寄存器计算偏移量。
命名
存在变址寄存器:在名字里加一个“变址”
存在常量偏移量:在名字里加一个“相对”
只有一个常量偏移:直接寻址
存在基址+变址的格式:基址变址寻址
换言之,“直接”就是指定明确的位置,以当前数据段为基准的偏移,”相对“就是依照某个变址寄存器的位置作为偏移基准的常量偏移。两者的分别是以谁为基准。可以类比数轴。
特殊的,如果没有常量偏移量,只有两个寄存器之间运算,则是单独命名为”基址变址寻址“,此时再加入常量偏移,则引入了”相对“。
还有一种80086不支持的比例写法,就是引入上述的“比例因子”,这里8086不支持乘法,但是依然介绍,在名字里引入“比例”
写法
这里注意一个运算符号“[]”
在汇编中,这个符号表示:括号内部的内容看作内存地址。(类比C中的指针解引用)
- 有两种等价写法:[A][B][C]== [A+B+C]
- 对于8086,[]不支持三运算符,所以最多COUNT[BX][DI]的两寄存器常数的形式。
- 括号内寄存器仅限基址和变址寄存器
栈相关指令
PUSH
形式化格式: PUSH SRC
SRC:mem reg segreg data
将SRC进栈,值得一说的是,汇编中的栈入栈顺序是由高位到低位,即完成以下步骤:
sp<- sp-2
sp+1,sp <- src
可见栈方向由高到低
POP
形式化格式: POP DST
DST: mem reg seg
- 不允许立即直接寻址
- 出于保护程序的目的,当DST是segreg时,不能为CS
这里包括了取得栈顶元素并弹出两个步骤。
等价到CPP是如下代码
stack<T> st;
st.push(x)
T temp=st.head();
st.pop()
AD命令:全部寄存器出入栈
仅作标记存在。
累加器指令
IN 输入
OUT 输出
XLAT(translate) 换码
形式化格式
$$
\begin{align}
IN ,,, AX/AL ,,, PORT \newline
OUT ,,, PORT,,, AX/AL
\end{align}
$$
两者都用来进行IO操作
XLAT指令用于多种编码格式之间的相互转换,诸如2进制转ACSII等,需要提前存在一个对照表。
形式化指令格式:XLAT
实际操作: AL <- ((BX)+(AL))
BX,AL是指定实际存在的两具体寄存器,等于查表。
实际上可以等价于若干条MOV指令
下一篇: LEA等地址运算