##通用寄存器
CPU中主要部件是寄存器。寄存器是CPU中程序员可以用指令读写的部件。程序员通过改变各种寄存器的内容来实现对CPU的控制。如果你有调试过或者开发过单片机的,这章节一定了解的很清楚!
不同的CPU,寄存器的个数、结构是不同的。8086CPU有14个寄存器,每个寄存器有一个名称。这些寄存器是:AX、BX、CX、DX、SI、BP、IP、CS、SS、DS、ES、PSW。在今后的学习中我们用到这些寄存器时就对这些寄存器进行介绍。
一般而言都是通过查阅CPU datasheet 得知寄存器的名称,定义,配置等
我已经下载了文档
80x86_Instruction_Set_Reference
80x86_System_Programming_Guide
AX、BX、CX、DX四个寄存器可以存放一般性的数据,所以这四个寄存器称为通用寄存器。在8086CPU中,寄存器都是16位的,可以存放两个字节的数据,所以表示的最大值是2^16-1。
8086CPU的上一代CPU使用的是8位的寄存器,所以为了保证程序的兼容性,使的针对上一代CPU开发的汇编程序能够在8086CPU上运行,8086将通用寄存器分成两个8位的寄存器:
- AX可以分为AH和AL
- BX可以分为BH和BL
- CX可以分为CH和CL
- DX可以分为DH和DL
这里以AX 为例
几条汇编指令
如图先看看汇编中两个最常用的指令mov和add,下表展示了它们的用法
注意:汇编指令是不区分大小写的,所以写成ADD AX,8和add ax,8的效果是一样的。
如果有C 语言基础的,看高级语言的语法就能看得懂了!
接下来我们要看看每一条汇编指令是如何改变寄存器中的内容的。
我们假定开始时,AX和BX中的数据都是0000H。
这里有一个问题,其实有调试或者有工作经验的都知道。明显相加导致数据溢出。详细解释如下: 最后一条指令执行之前AX=8226H,BX=8226H,如果执行ADD操作,则A的值应该是1044CH,但是AX是16位的寄存器,最大值就为FFFFH,所以超出16位的部分就会被省略,所以AX=044CH。
我们还是假设,开始时AX和BX中的数据为0000H。
可以算算值是多少,ADD AL,93H执行后AL的数值为158H,但是AL为8位寄存器最大只能存储FFH,所以超过8位的部分会自动省略,于是AX中数据就变成了0058H。
注意,AL 是作为独立的8位数寄存器使用的,和AH没有关系,所以不要认为进位会存在AH的.
在进行数据传送或者运算的时候,要注意指令的两个操作对象的位数是一致的。 例如:
mov ax,bx
mov bx,cx
mov ax,18H
mov al,18H
add ax,bx
add ax,20000
等等都是正确的指令,而:
mov ax,bl
mov bh,ax
mov al,20000
add al,100H
等,都是错误的指令,错误的原因是指令的两个操作位数不一致。
物理地址的形成
8086CPU是16位的结构,这就意味着字长是16位,寄存器的最大宽度为16位,运算器一次可以处理16位的数据,寄存器和运算器之间的数据通路为16位。
然而8086CPU却有20位地址总线,可以传送20位地址,所以能够达到1MB的寻址空间,但是由于是16位架构的CPU,所以如果仅仅从CPU内部简单的将地址送出则只能形成16位的地址,寻址能力也只有64KB。 为了解决上述问题,8086CPU采用了一种使用两个16位地址合成一个20位地址的方法来形成一个20位的物理地址,从而扩大了寻址能力。
物理地址=段地址*16+偏移地址
地址加法器采用物理地址=段地址*16+偏移地址的方法用段地址和偏移地址合成物理地址。例如,8086CPU想要访问123C8H的内存单元,此时,加法器就利用1230H和00C8H两个地址形成123C8H这个地址。
段地址*16,在c语言的编写时左移4位。
看到书上有个例子解释的挺直观,直接照搬了。
假如你有一张可以写4位数的纸条,那么图书馆的位置可以被表述为2826,如果不幸你没有4位的纸条只有两张三位的纸条,那么图书馆的位置就必须借助上面的思想,我们可以用200和826两个三位数字来表示,图书馆的位置就在200*10+826=2826m上。 因为我们经常用的是十进制,所以就是直接乘以10。 计算机处理程序,我们都用的是16进制,所以直接乘以16。
段寄存器
关于段地址,但实际上内存中并没有分段,段的划分来自CPU,由于8086CPU用基础地址(段地址)*16+偏移地址=物理地址的方式给出内存的物理地址,使得我们可以用分段的方式来管理内存。我们可以认为10000H~100FFH的内存单元为一个段,段的起始地址是10000H,段地址为1000H,大小为100H;我们也可以认为10000H~1007FH、10080H~100FFH的内存单元组成两个段,它们的起始地址为10000H和10080H,段地址为1000H和1008H,段大小为80H。
既然地址加法部件要用段地址和偏移地址形成物理地址,那么这两个地址就必须都被保存下来。8086CPU有四个段寄存器:CS、DS、SS、ES。
CS和IP是8086CPU中两个关键的寄存器,他们指示了CPU当前要读取指令的地址。CS为代码段寄存器,IP为指令指针寄存器。如果CS中的内容为M,IP中的内容为N,那么CPU就将从内存M*16+N单元开始,读取一条指令并执行。也可以表述为如下:
8086机中,任意时刻,CPU将CS:IP指向的内容当做指令执行。
下面通过一组图的方式展示8086CPU读取、执行一条指令的过程。
图2.10说明如下:
初始状态,CS:2000H,IP:0000H。
内存20000H~20009H 单元存放可执行的机器码
内存20000H~20009H 单元存放机器码 对应的汇编指令如下。 地址: 20000H ~ 20002H ,内容: B8 23 01 长度3 byte, 对应汇编指令: mov ax,0123H 地址: 20003H ~ 20005H ,内容: BB 03 00 长度3 byte, 对应汇编指令: mov bx,0003H 地址: 20006H ~ 20007H ,内容: 89 D8 长度2 byte, 对应汇编指令: mov ax,bx 地址: 20008H ~ 20009H ,内容: 01 D8 长度2 byte, 对应汇编指令: add ax,bx
以下图是详细一步一步的执行图,展示指令执行的过程,这样有个直观的理解和认识
初始状态,CS:2000H,IP:0000H。
地址加法器利用CS和IP中的地址形成物理地址。 地址是20000H
地址加法器将物理地址送入输入输出控制电路。
输入输出电路将地址送上地址总线
从内存20000H单元开始存放的机器指令B8 23 01通过数据总线被送入CPU。
输入输出电路将机器指令送入指令缓冲器。
读取一条指令后,IP中值会自动增加,以使CPU可以读取下一条指令,因为当前读入的指令为3个字节,所以IP的值加3。
执行控制器执行指令。
AX中的内容被改变
后面的过程与这个过程是相同的,这里不再画出来了,因为文章的篇幅已经很长了。
注意:在8086CPU加电启动后或复位后,CS和IP被设置为CS=FFFFH,IP=00000H,即8086CPU在刚启动时,CPU从内存FFFF0H单元中读取指令执行。
注: CPU 将CP 、 IP 中的内容当做指令的段地址和偏移地址。然后合成物理地址,到该地址内存中读取指令码,执行!
##修改CS、IP的指令
那么我们是否能够通过指令改变CS和IP的值呢?答案是肯定的,但是不是通过MOV指令,8086提供了单独的指令来改变这两个寄存器的值。
1、若想同时改变CS和IP的值,可以用“JMP 段地址:偏移地址”的指令来完成。
例如:JMP 2AE3:3,执行后:CS=2AE3H,IP=0003H,CPU将从2AE33H单元读取指令。
2、若想仅修改IP的内容,可以使用形如“JMP 某个合法寄存器”的指令来完成。
例如:JMP AX,指令执行前:AX=1000H,CS=2000H,IP=00003H,指令执行后:AX=1000H,CS=20000H,IP=1000H 其实挺像mov IP,AX
代码段
可以根据需要,我们可以将一段内存定义为一个段,用一个段地址指示段,用偏移地址访问段内的单元,可以用分段的方式来管理内存。
用一个段存放数据,将它定义为“数据段”;
用一个段存放代码,将它定义为“代码段”;
用一个段当作栈,将它定义为“栈段”。 注意:
一个段的起始地址一定是16的倍数; 偏移地址为16位,变化范围为0-FFFFH,所以一个段的长度最大为64KB。 CPU可以用不同的段地址和偏移地址形成同一个物理地址。 段寄存器:8086CPU有4个段寄存器:CS、DS、SS、ES,提供内存单元的段地址。
CS(Code Segment):代码段寄存器; DS(Data Segment):数据段寄存器; SS(Stack Segment):堆栈段寄存器; ES(Extra Segment):附加段寄存器。
DS是段寄存器 一般放的是数据段的段地址至于BX 是一个灵活的寄存器 可以用它来做许多事情 当然也可以用来当指针 楼主所谓数据段的基地址是这个段的起始地址 要说他是基地址也没有错 但是bx里放的是某个字节或字的地址用[bx]来访问。DS放段地址,BX是通用寄存器.
ds与bx配合,es与dx配合,cx作为计数器。这既是cpu硬件设计使然,也是软件设计的标准用法,就如围棋中的定式,你必须这么用。bx是提供偏移地址。
- IP(Instruction Pointer):指令指针寄存器,与CS配合使用,可跟踪程序的执行过程;
- SP(Stack Pointer):堆栈指针,与SS配合使用,可指向目前的堆栈位置。
- BP(Base Pointer):基址指针寄存器,可用作SS的一个相对基址位置;
- SI(Source Index):源变址寄存器可用来存放相对于DS段之源变址指针;
- DI(Destination Index):目的变址寄存器,可用来存放相对于 ES 段之目的变址指针。
debug 信息请参考汇编语言第二版书籍说明
实践如图所示