p.s.看《汇编语言(第三版)》--王爽,暂时看到第11章,所以可能不太全面,接下来发现有缺漏、错误的,会继续更新修正。
然后下面写的是部分书上的内容加上我个人的理解,正因如此,由于我知识的局限,可能会出现比较多的错误。
前提简介:
>寄存器是CPU中程序员可以用指令读写的部件。程序员通过改变各种寄存器中的内容来实现对CPU的控制。------《汇编语言(第三版)》第2章
简单来说,我认为寄存器粗略看有点像控制台,用户输入命令(修改寄存器中的数据),然后操作系统执行用户的命令(CPU执行命令)。除此之外,我认为寄存器还有存储功能,能有效提高CPU运算速度(不需要再从内存读取数据)。
既然说到寄存器控制CPU,就说说是如何控制CPU的,在之前,先简单说说一些和这个没有多大关系,但作为前提的内容(当课外阅读吧):
前提:首先所有的程序简单点说都是由代码组成,而且不管最初是什么语言的代码,到最后都转化成二进制数字,然后其中固定的二进制数字串对应着该CPU中相应的动作(就是遇到这串二进制数字,CPU就会执行相应的操作),我们就称这样一个固定的二进制数字串为一条指令吧。然后这些指令都存储在内存中(暂时当它都存储在内存中,实际上是部分在内存,部分在磁盘),然后运行的时候,就是CPU一条一条地从内存提取指令、执行指令。
小结:看完上面,我们再来整理一下上面内容,其实与CPU相关(换句话说CPU的动作)只有最后一句,从内存提取指令,然后执行指令,好啦,问题来啦,内存那么大,CPU怎么知道从哪里提取指令?怎么提取指令?然后怎么执行指令?
然后后面两个问题的答案和控制CPU已经没什么大关系了,因为这两个我们都不能控制(推荐看《深入了解计算机系统》),所以说到最后,我们控制CPU实际上就是控制CPU从哪里读取指令(数据)。
说到这里又来说说前提(课外阅读...),其实这个也是上面第二个问题(怎么提取指令)的答案,让我们先来看看图片(所有图片均来自《汇编语言(第三版)-王爽》)
从内存读取数据基础版:
1.把需要读写的内存地址传递给内存(送上地址总线)
2.内存把相应地址的数据传递回给内存
但由于当时条件限制,当时8086CPU是16位结构的CPU,就是说,在8086CPU内部,能够一次性处理、传输、暂时存储的信息的最大长度是16位。然后内存单元的地址在送上地址总线之前,必须在CPU中处理、传输、暂时存放,而对于16位CPU只能一次性处理、传输、暂时存储16位的地址。
而实际上8086CPU外的地址总线有20位,而从内部只能传送出16位地址,这使20位地址总线不能得到充分利用因此采用由2个16位地址合成的方法形成一个20位的物理地址。至于如何合成,又来看看图片:
物理地址=段地址x16+偏移地址
p.s.图片中的数字是16进制,4个二进制数字等于一个16进制数字
从内存读取数据完整版:
1.CPU中的相关部件提供两个16位的地址:段地址,偏移地址
2.段地址和偏移地址通过内部总线送入一个为地址加法器的部件
3.地址加法器将两个16位地址合成一个20位的地址
4.地址加法器通过内部总线将20位物理地址送入输入输出控制电路
5.输入输出控制电路将20位物理地址送上地址总线
6.地址总线将20位物理地址送到存储器
7.存储器将相应的地址上的数据传出
第三个前提:
有一个硬件部分叫时钟,然后在每一个时钟周期,CPU都从内存读取进一条指令
我们现在来总结一下前面的3个前提:在每一个时钟周期,CPU根据当前的段地址和偏移地址读取一条指令进来,然后执行。(前面一大段最后总结剩下一句。。。)
好啦,问题又来啦,CPU怎么知道当前的段地址和偏移地址是什么?说到现在,终于和我们的主题套上关系啦,对的,就是通过我们的主角---寄存器,来得知,段地址和偏移地址都有相应的寄存器进行存储,特别是要执行的指令的段地址和偏移地址更是有固定的寄存器(cs,ip)进行存储,然后CPU发出地址,就是直接把cs,ip中的数据发出去。
寄存器:
在8086CPU中一共有14个寄存器:
ax,bx,cx,dx,si,di,sp,bp,ip,cs,ss,ds,es,flag
每个寄存器都是16位,在下面对各个寄存器的逐一介绍中,根据功能或者其特点可能会将几个寄存器划分在一起介绍(所以一个寄存器可能会出现在多个地方,最后会给出总结表)
通用寄存器(ax,bx,cx,dx)
将它们4个划分一起是由于它们的一个共同特性:
都可以分为独立使用的2个8位寄存器来使用
ax:ah,al
bx:bh,bl
cx:ch,cl
dx,dh,dl
h结束的为高位,l的为低位,而且这两个8位寄存器之间相互独立,例如:ah本来为0000,0000,当你向al(其余一样)传入一个数据,但这个数据已经超8位,就假设为1,0000,0000,最后结果为ah:0000,0000,al:0000,0000。传进去的数据的最高位(超出的一位),将会被舍弃,而不是往ah进位。
好吧,实际上除了这个,它们也就没什么其它共同特点了(读写功能,那个所有寄存器都可以干啦)。。。
地址相关寄存器:段地址+偏移地址(cs,ss,ds,es,ip,sp,bp,bx,si,di)
在上面的前提简介的最后一部分中,提到过段地址和偏移地址存储在寄存器中,在这部分中将会详细介绍:
先来大体了解一下里面哪些用来存储段地址,哪些存储偏移地址:
段地址寄存器(段寄存器):cs,ss,ds,es
可用来存储偏移地址的寄存器:ip,sp,bp,si,di
可以看出它们往往是成对出现的,而且可以说都是固定组合,我们就一对对来看吧:
cs:ip:指示指令地址
cs和ip是8086CPU中两个最关键的寄存器,它们指示了CPU当前要读取指令的地址。
在8086CPU机中,任意时刻,设cs中的内容为M,ip中的内容为N,8086CPU将从内存Mx16+N单元开始,读取一条指令执行,若该条指令不是跳转指令,则执行完后,L为刚执行完的指令的长度(所占内存单元的个数),N=N+L,然后继续读取下一条指令执行。
换句话说,8086机中,任意时刻,CPU将cs:ip指向的内容当做指令执行,就是说假如你原来存储在某个内存单元的是数据,但是如果你的cs:ip指向了该内存单元,CPU依然会把内存单元中的内容当做指令来执行。
而且,cs,ip也是唯二的两个寄存器不能被mov指令修改,只能被转移指令修改。
ss:sp:指示栈顶地址
8086CPU提供了相关的指令来以栈的方式访问内存空间,也就是说,基于8086CPU编程时,可将一段内存当做栈来使用,然后这时,ss:sp指示的就是栈顶的地址。
栈中的一个单元大小是两个内存单元,就是16位,就是每次入栈,不管是8位还是16位,最后在栈中都占据16位大小也就是2个内存单元。
在8086CPU中,任意时刻,设ss中的内容为M,sp中的内容为N,每次入栈后,N=N-2(栈底在高地址,栈顶在低地址),每次出栈后,N=N+2。当使用push,pop指令时,CPU就会按照当前ss:sp指示的位置,进行入栈,出栈操作。
ds(es):idata(bx,bp,si,di):指示代码段地址
这里和前面的两对都不同,它们不用一一对应,而且除了用寄存器中的数据,还可以直接用立即数来作为偏移地址。它们用于对内存单元中的内容以数据形式读取。
使用格式为:ds(es):[idata(bx,bp,si,di)]
p.s.接下来用sa代表段地址,表示ds或者es均可
上面代表的是:段地址为ds中的内容,偏移地址为idata 的内存单元中的数据。
而且还有几点必须注意:
1.只有这4个寄存器可以作为偏移地址放进[]中,其余的寄存器如ax,cx...放进
去就是错误的指令
2.在[]中,这4个寄存器可以单个出现,或只能以4种组合出现:
sa:[bx]
sa:[bp]
sa:[si]
sa:[di]
sa:[bx+si]
sa:[bx+di]
sa:[bp+si]
sa:[bp+di]
3.两个内存单元之间不可以直接使用mov进行数据传输 最后来总结一下代码段
的合法寻址方式,也可以说是偏移地址中寄存器的组合方式:
-----------------------------------------------------
寻址方式名称
sa:[idata]直接寻址
sa:[bx]寄存器间接寻址
sa:[bp]寄存器间接寻址
sa:[si]寄存器间接寻址
sa:[di]寄存器间接寻址
sa:[bx+idata]寄存器相对寻址
sa:[bp+idata] 寄存器相对寻址
sa:[si+idata] 寄存器相对寻址
sa:[di+idata] 寄存器相对寻址
sa:[bx+si]基址变址寻址
sa:[bx+di] 基址变址寻址
sa:[bp+si] 基址变址寻址
sa:[bp+di] 基址变址寻址
sa:[bx+si+idata]相对基址变址寻址
sa:[bx+di+idata] 相对基址变址寻址
sa:[bp+si+idata] 相对基址变址寻址
sa:[bp+di+idata] 相对基址变址寻址
---------------------------------------------------
循环(loop)相关寄存器(cx)
cx表示的是循环的次数,设cx中的内容为N,每执行一次loop指令后,N=N-1,当N>0时,执行跳转(继续循环),否则跳出循环,执行下一条指令(内存上向邻的下一条)。
除法(div)相关寄存器(ax,dx)
除法指令:
div 寄存器
div 内存单元
div后面跟着的是除数,如果除数为8位,则被除数为16位,如果除数为16位,则被除数为32位;如果被除数为16位,则默认存放在ax,结果的商存放在al,余数存放ah;如果被除数为32位,则高16位存放在dx,低16位存放在ax;除得的商存放在ax,余数存放在dx
乘法(mul)相关寄存器(ax,dx)
转移指令(jcxz)相关寄存器(cx)
复制指令(movsb,movsw)相关寄存器(ds,si,es,di)
标志寄存器(flag)
使用标志寄存器的相关指令
寄存器功能总结:
---------------------------------------------------------------------
ax:
特性:
可被拆分为2个8位寄存器
功能:
(1)除法:1.被除数为16位,ax存放16位被除数,ax存放商。2.被除数为 32位,ax存放被除数的低16位,ax存放商
(2)乘法:1.相乘的数为8位,al存放一个相乘的数,且结果存放在ax中。2.相乘的数为16位,ax存放一个相乘的数
,且结果的低位存放在ax中
-----
bx:
特性:
可被拆分为2个8位寄存器
功能:
用作对内存单元的寻址中的偏移地址
-----
cx:
特性:
可被拆分为2个8位寄存器
功能:
1.用来存放loop、rep的循环次数
2.用jcxz的条件判断
-----
dx:
特性:
可被拆分为2个8位寄存器
功能:
(1)除法:1.被除数为16位,dx存放余数。2.被除数为32位,dx存放被除数的高16位,dx存放余数
(2)乘法:相乘的数为16位,相乘结果的高位存放在dx中
-----
cs:特性:
不能被mov修改,只有转移指令能修改
功能:
存放指令段地址
-----
es:
特性:
...
功能:
可用作ds的替补|有多个数据段的时候,用es顶上
-----
ip:
特性:
不能被mov修改,只有转移指令能修改
功能:
存放指令偏移地址
-----
bp:
特性:
...
功能:
存放内存单元的偏移地址
-----
sp:
特性:
...
功能:
存放栈顶的偏移地址
----
si:
特性:
...
功能:
存放内存单元的偏移地址
-----
di:
特性:
...
功能:
存放内存单元的偏移地址
---
psw:
...
-------------------------------------------------------------------------