3.认识8086处理器

3.认识8086处理器

1.8086的通用寄存器

  介绍8086内部的通用寄存器

(查阅资料:AH&AL=AX(accumulator):累加寄存器

AH&AL=AX(accumulator):累加寄存器
BH&BL=BX(base):基址寄存器
CH&CL=CX(count):计数寄存器
DH&DL=DX(data):数据寄存器
SP(Stack Pointer):堆栈指针寄存器
BP(Base Pointer):基址指针寄存器
SI(Source Index):源变址寄存器
DI(Destination Index):目的变址寄存器
IP(Instruction Pointer):指令指针寄存器
CS(Code Segment)代码段寄存器
DS(Data Segment):数据段寄存器
SS(Stack Segment):堆栈段寄存器
ES(Extra Segment):附加段寄存器
OF overflow flag 溢出标志 操作数超出机器能表示的范围表示溢出,溢出时为1.
SF sign Flag 符号标志 记录运算结果的符号,结果负时为1.
ZF zero flag 零标志 运算结果等于0时为1,否则为0.
CF carry flag 进位标志 最高有效位产生进位时为1,否则为0.
AF auxiliary carry flag 辅助进位标志 运算时,第3位向第4位产生进位时为1,否则为0.
PF parity flag 奇偶标志 运算结果操作数位为1的个数为偶数个时为1,否则为0.
DF direcion flag 方向标志 用于串处理.DF=1时,每次操作后使SI和DI减小.DF=0时则增大.
IF interrupt flag 中断标志 IF=1时,允许CPU响应可屏蔽中断,否则关闭中断.
TF trap flag 陷阱标志 用于调试单步操作.
————————————————
版权声明:本文为CSDN博主「j448749903」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/j448749903/article/details/107172087

这一课我们的任务时 认识INTEL 8086处理器内部的通用寄存器

 

 在因特尔8086处理器内部。

有八个十六位的通用寄存器。

分别命名为AX BX CX DX SI DI SP BP 

通用的意思是它们都可以根据需要用于多种目的。

比如可以在这些寄存器之间互相传送数据,或者做各种算数逻辑运算。

可以在这些寄存器和内存之间,传送数据。或者做各种算数逻辑运算

 

 

这八个寄存器都时十六位的。由十六个比特组成,  

为了描述方便,每一个bite都能描述为0 , 1  , 2 ,3 ,。。。 15;

其中最右边的比特,称为最低为

最左边的比特,叫做最高位。

十六位的寄存器可以保存十六位的二进制数,

比如:寄存器里保存了2进制数

 

 这个二进制数等于十进制的23235

同时等于16进制的5AC3

就时说这个二进制数,等于十进制的23235,或者十六进制的5AC3

使用二进制很不方便和直观。

所以,以后我们不在画出寄存器的每一个比特

也不在用二进制来表示数值,因为这样很麻烦。

而是使用图中的方法。

这里,寄存器直接使用矩形来表示。

这就表示了一个寄存器。

寄存器的内容采用十六进制给出。

 

 5AC3就是寄存器所保存的内容

为什么计算机领域的人,都偏爱使用十六进制呢?

这时有原因的

从一个十六进制数,可以很容易它的二进制形式。

反过来,从一个二进制数可以非常直观的看出它的十六进制的形式。

 

 

举例来说:请看这个二进制数,它的十六进制时852F

这个二进制数一共十六个比特

第一个四位,第二个四位,第三个四位,第四个四位

 

 

 

在8086中,八个通用寄存器的前四个。

也就是AX BX CX DX

可以拆成两个八位寄存器来使用,总共可以提供两个八位寄存器

他们分别时

AH AL

BH BL

CH CL

DH DL

这样一来,当需要的寄存器和寄存器之间

或者寄存器和内存之间进行八位的数据传送

或者八位的算数逻辑运算时,使用他们就狠方便,

以寄存器AX为例子

 

 

 

 在计算机里,一个字等于两个字节。

以寄存器AX为例子。

因为寄存器AX的长度,是十六个比特,可以说寄存器AX的长度是两个字节。

也可以说,寄存器AX的长度是一个字。

引入了一个新的概念 字

一个字等于两个字节

所以寄存器AX的长度是一个字

但是我们知道,寄存器AX是由两个独立的寄存器AH 和 AL组成

这两个寄存器都是一个字节

因此我们说寄存器AH是寄存器AX的高字节部分,

寄存器 AL是寄存器AL的低字节部分

高字节和低字节

寄存器AX的低字节是由寄存器AL组成

寄存器AX的高字节是由寄存器AH组成


 

在以寄存器AX为例

 

 

寄存器AX的值等于3E2F

 

 其次如果我们改变了AH寄存器的值

 

将他修改为00

那么这对于寄存器AL,没有任何映像

但是寄存器AX的值也跟着改变,变成了002f

这是显而易见的

 

 毕竟AX是由AH和AL组成

一旦改变了AH的值,同时改变了AX的值。

如果我们改变了AL的值,将他修改为5A,

这对于寄存器AH没有任何影响还是00

但是寄存器AX的值变成了005A

 

 这也是显而易见的

 

 

 

 

 

 

 

2.8086的内存访问和字节序

  8086的寄存器如何与内存交换数据

这一课,我们将了解8086如何访问内存以及内存访问的字节序

 

 

因特尔8086有十六根数据线,这十六根数据线和内存相连,可以向内存写数据。或者从内存里面读数据,它是双向的。

数据线的宽度,和8086的宽度一致,以寄存器SI为例。这是一个十六位的寄存器,比特编号从0到15.

 

 

假定S寄存器里保存的是C05B这个数字。那么如何将他写入内存呢?

要知道,虽然内存有16跟数据线,但是内存是由字节单元组成,而每个字节单元都是8位的。

容纳不下十六位的二进制数。

void Mymain{

 

内存是由字节单元组成的,每个字节单元8位

 

 SI寄存器是十六位构成的,。

 

将SI寄存器寄存器里的内容,放到内存单元内,是放不下的。

 }

将SI寄存器寄存器里的内容,放到内存单元内,是放不下的。

 不过这也算不上严重的问题,因为我们可以将十六位的数据拆开,分成两个字节,然后分别写入两个相邻的字节单元,。
假定在写入内存时,指定的地址是0002,那么寄存器SI的内容C05B在通过十六位的数据线,到达内存之后。
被拆分成两个8位,
其中C0是高字节部分。
5B是低字节部分

 

 然后

5B,被写入到地址为0002的单元,

C0,被写入到地址为0003的单元,

地址为0002的单元是第一地址单元,地址为0003的单元是高地址单元。

当我们写入一个字时,

如果

低字节写入内存的低地址单元

高字节写入内存的高地址单元

那我们称之为低端字节序端。

这就引入了一个概念。低端字节序。

读的时候也时一样,如果我们要从内存地址为0002的地方,开始读取一个字。

并传送到寄存器SI,那么会怎么样 。

因为时读取一个字,所以低地址单元里的5B

高地址单元里的C0

被分别取出,然后在数据线上合并成一个字C05B

最后传送到寄存器SI

 

 


 

 

在8086里,有些寄存器时8位的。比如寄存器AH 和 寄存器AL

假定寄存器AH的内容时8D,要将他写入内存地址为0002的单元

会怎么样呢?

寄存器AH和内存单元的长度相同,都是一个字节,匹配的

寄存器AH的长度时一个字节

内存单元的长度时一个字节

问题在于

数据线的宽度是十六位的

不过这没有关系,在传送八位的数据时,数据线只使用了一半,

因此

寄存器AH的8D,通过十六位数据线的一半,进入内存,然后写入地址位0002的单元。

 

 

读出的时候,也是这样, 如果我们要从地址位0002的单元,读取一个字节 ,并传送到寄存器AL

那么这个单元里边数字8D,会通过十六根数据线中的一半。给送出,然后接着传送到寄存器AL

 

 这十六根数据线的另一半是不用的。

 

 

 

 寄存器BX的内容是,55AA,写入内存的时候,指定的地址是0008.采用低端字节序,写入后,肯定会占用2个内存单元。

因为寄存器BX是2B,将占用2个内存单元,。它的地址是0008和0009.

占用这2个单元,

他们的内容是 

因为寄存器是BX的内容是55AA,其中55是高字节,AA是低字节。

当我们采用低端字节序写入内存时,低字节占据内存的低地址部分,高字节占据内存的高地址部分。

他们的内容分别时AA和55

0008 AA

0009 55 

3.程序分段

  介绍程序中的代码和数据段

这一课,我们来了解程序在内存中时如何分段存放的。

 

 处理器时可以自动取指令,并可以执行指令的器件。

为了解决问题,我们需要编排指令,这个过程叫编程

编程的结果是生成了一个程序。

来看这幅图。

我们在内存中编排一个程序

这个程序是从内存地址位零的部分开始存放的。

就是从这里0000开始存放

处理器是按顺序并执行指令,所以指令必须是一条一条的集中存放

在图中的黄色部分,是集中存放的指令。

指令很多,我们仅仅是列出了最开始的部分。

后面还有很多,用省略号来表示。

很自然的这些指令集中在一起形成了。程序代码部分。或者说指令部分

由于手代码或者指令在内存中占据了,一个连续的区段,

我们称之为程序的代码段。

从这幅图可以看出,这个代码段的起始地址是零。

指令在执行时,需要用到数据,这些数据也是放到内存中的。

蓝色部分是集中存放的数据,数据很多,列出 最开始的部分,

这些数据放在一起,形成了程序的数据部分,。

由于这些数据,在内存里,占据了一个连续的区段,。

我们称之为数据段。

从图中可以看出,数据段的起始地址是0C00


现在,我们来看程序的指令   第一条指令

 

 通常来说,指令是由操作码和操作数共同组成,

对于处理器来说,操作码隐含了如何执行该指令的信息。

比如指令是做什么的?以及怎么去做?

第一条指令,是在内存地址为零的地方开始存放的

一共是三个字节,

这三个字节是这个此程序的第一条指令。

其中,第一个字节的A1是操作码,它隐含了3个方面的信息

第一:这条指令是一个传送指令。

第二:被传送的目标位置是AX寄存器

第三:被传送的数字,位于内存中的另一个地方,它的地址是十六位的000C。而且仅仅跟在操作码的后边

操作码的后边两个字节是00和0C

他们共同组成了这个地址,8086处理器是低端字节序的。

可以这个地址是0C00

换而言之,这一条指令的功能是,将内存地址0C00处的一个字传送到AX寄存器

猜测    A1 Ax 0C00 

由于被传送的目标位置是AX寄存器,AX寄存器的字长是十六位,所以

这条指令在执行的时候,将再次访问内存

从内存地址0C00处,取的一个字

在这个地方是两个字节3C和05 

这两个字节将合并成合并成一个数字053C

然后在传送给AX寄存器,

传送之后AX的内从是053C

这是第一条指令。


 

再来看第二条指令

 

 第二条指令是从0003的地方开始存放的,一共是4个字节,其中前两个字节03和06是操作码,显然操作码很长,它由两个字节组成的

操作码0306所隐含的信息是,

第一,这条指令是一条加法指令;

第二,第一个相加数字是位于AX寄存器里,而且相加的结果也要保存在AX寄存器;

第三,第二个相加的数据位于内存中第二个地方,它的地址紧紧跟在操作码的后边,由两个字节组成 020C

 020C用于组成十六位的地址,因为8086处理器是低端字节序的,所以地址是0C02

总而言之 这条指令的功能是,将寄存器AX的内容和内存地址0C02处的字相加。

结果在AX中,那么这条指令在执行时,将再次访问内存。

从内存地址0C02处取出一个字,0F8B,

将0F8B和寄存器R原有的数据053C相加,

做加法,结果在存回AX寄存器,相加之后的结果是14C7 


 

4.程序的重定位难题

  程序在内存中浮动时出现的问题

这一课 继续上一课的话题

来研究一个有关程序重定位的困境

 

 

上一节课,已经知道了程序的各种细节以及他的执行过程。

为了自动的取指令和执行指令,处理器需要一个寄存器来。自动跟踪每一条指令。

我们假定它的名字叫做ipr

IPR始终是下一条将要执行的指令,

在程序开始执行之前,

我们需要将第一条执行指令的地址

传送到这个寄存器、

我们的程序是从内存地址为零的地方,开始存放的。

第一条指令的地址就是程序开始的地方。

也就是0.

 

因此我们把0传送到IPR

这个时候IPR的内容是0000

 

 

 也就是0,当程序开始执行时,处理器把ipr的内容,放到地址线上。第一次,发出的地址是零。

所以是从内存地址为零的地方,取出第一条指令,并加以执行。那么第一次取出的是第一条指令。

也就是

 

 第一条指令的内容,将内存地址0C00处的一个字传送到AX寄存器。

这一条指令在执行的时候,从内存地址0C00处,

 

 取出一个字053c,然后传送到AX寄存器。

 

 与此同时,寄存器IPR的内容自动更新为下一条指令的地址0003

 

 这个地址是怎么来的呢?

当前指令的地址加上,当前指令的长度,就可以得到下一条指令的地址。

当前指令的地址是0000,他的长度是3,三个字节。那么0+3=3,则下一条指令的地址,自然就是3.

这是IPR的新内容。

在第一条指令执行之后,处理器在用ipr的内容取出第二条指令。

因为IPR的当前内容,是0003,所以是从内存地址为0003的地方取指令,。这将取得第二条指令,并加以执行。

那么,这是第二条指令。

 

 地址是0003,处理器从这里取出指令,并加以执行

第二条指令的内容是,将寄存器AX的内容和内存地址0C02处的字相加,结果存放在寄存器AX中。

那么这一条指令执行的时候,从内存地址0C02处取得一个字  0F8B

然后和寄存器AX里的原来的内容相加 ,相加之后的结果,重新写回到AX寄存器。

 

 相加之后的结果,重新写回到AX寄存器。

IPR的内容,被自动更新为下一条指令的地址0007

 

 这个地址是如何来的呢?

用当前指令的地址,加上当前指令的长度,就可以得到下一条指令的地址

当前指令的地址是0003长度是是4个字节,3+4等于7,则下一条指令的地址自然就是0007了。

目前来看程序工作的很好,问题是,如果我们把程序在内存中的位置挪一挪,换一个地方就不灵了。

比如我们可以把这个程序,从内存地址为0的这个地方,整体移动到内存地址为1000的地方。

 

 

移动之后,程序中的指令和数据都没有发生任何变化。

只不过每一条指令和每一条数据在内存中的地址发生了改变。

第一条指令原来的地址是零,现在是1000

第二条指令源来的地址是0003,现在是1003.

那么在程序的数据段,

第一个字他原来的地址是0c00现在是1C02

第二个字他原来的地址是0C02现在是1C02

地址都发生了变化,他是内容没有变化。

为了从新的地方取指令和自动执行指令。

IPR的内容也要更新,也要在程序开始执行前设置成1000

因为程序的新地址是1000

从这里开始的,

所以要重新设置IPR的地址为1000

当程序开始执行的时候

处理器用IPR的内容,发出地址1000,去取指令,取的第一条指令,

第一条指令的意思是,将内存地址0C00处的一个字传送到AX寄存器

但是由于程序刚刚改变了位置,那个数字的地址,已经不是0C00

而是1C00在这里,但是这个指令在执行的时候,依然要从老地方0C00处来取。

它取得是别人地盘里的数 ,这就麻烦了。

程序出错了,发生这样的事情吗,是因为我们在指令中使用了绝对指令地址

也就是物理地址,比如第一条指令中的0C00 和第二条指令的0C02这是物理地址

这样的程序时无法在内存中自由浮动的,用术语来说,是无法重定位的。、

换一个地方就不零了。

要解决这样问题,

可以在程序加载之后,根据当前加载的位置来修改每一条指令

把0C00修改为1C00

把程序加载之后进行临时改变。

但这样这,非常的荒谬,

一个程序的指令少则几十条,多则几千几十万条

几千万条都有可能

这么多指令怎么改呢?

能够重定位

这是对程序的基本要求

在我们的计算机里,运行着各种各样的程序,

每个程序在启动时 ,都是随机的,。哪里由空闲的地方?

它就会被加载到哪里?比如说,当你在网上下载一个程序的时候,亲爱的,我只能在地址为零的地方工作

你给我换个位置呗,。从来没有发生过这样的事情

因此一个良好的程序,必须能在内存中自由浮动。

而不会影响它的正确执行,这就要求我们在指令中不能使用绝对地址或者说是物理地址,

那么到底应该怎么做?

悬念

下一课说:

5.段地址和偏移地址

  引入段地址和偏移地址的概念

这一课我们通过引入数据段寄存器来解决程序的重定位问题

在上一级里指令中不能使用物理地址,否则程序无法重定位。

那么如何解决这个问题呢?

先来观察数据段,数据段的起始地址是1C00

这个地址也是段内第一个字053C的地址

 

 段内第一个字053C和数据段起始的距离是0个字节。

所以我们可以说第一个字053C距离段起始地址的偏移量是零。

这个零是偏移量,距离数据段起始位置的偏移量是零。

再来看第二个字0F8B,它的物理地址是1C02

在这个地方,换个角度来看。这个数据段距离起始位置的距离是两个字节。

所以我们可以说这个字距离段起始地址的偏移量,是2.

 

 因为这个原因,在数据段内,每个字都有两个属性,一个是他们的物理地址,分别是1C00和1C02

另一个是相对他们的起始段的相对偏移量

 

 分别是0和2,从现在开始,我们把这2个字相对于段起始地址的偏移量,叫做偏移地址

新概念偏移地址。 

因此,这两个字的偏移地址,分别是0000 和0002

我们把偏移地址标注在右侧,并且使用加号来表明他们是偏移地址,

你可能会说,这只是看待事物的角度不同。

看代码段的指令也已经修改。

在第一条指令中,原先的物理地址,已经改成了第一个字的偏移地址0000

这条指令现在的意思是,将偏移地址为0000处的一个字传送到AX寄存器。

 

 再来看第二条指令,原先的物理地址,已经改成了第二个字的偏移地址。

那么这一条指令现在的意思是,将寄存器AX的内容和偏移地址为0002处的字相加。

结果在AX中。指令也改变了,指令中的地址都改成了偏移地址

为了配合这一种改变,增加了数据段寄存器DSR,用来保存数据段的起始物理地址。

 

 那么在程序开始执行前,我们先将第一条指令的物理地址,1000传送到IPR(指令指针寄存器)

然后,将数据段的起始物理地址1C00,传送到数据段寄存器DSR,传送之后,DSR的内容是1C00

现在开始处理器取指令并且执行指令。

它先用IPR的内容发出地址1000去访问内存,取得第一条指令,这一条指令是将偏移地址0000处的一个字传送到AX寄存器,

在执行指令的时候,访问内存,需要用到数据段寄存器DSR,此时是将DSR的内容1C 00,和指令中的0000相加。做加法

相加之后,形成物理地址,物理地址还是1C00,因为是用1C00和0000相加还是1C00,

于是处理器将1C00作为地址发送给内存。

然后从内存地址1C00处,取得一个字053C

然后传送给AX寄存器。

 

 

这是第一条指令的执行过程。


 

于此同时IPR的内容被更新,变成了下一条指令的执行地址1003,那么在第一条指令执行之后,处理器再次利用,IPR的内容,向 内存发出地址1003,去取指令

这将取得第二条指令。将寄存器AX的内容和偏移地址为0002的字相加,结果在AX中。

 

 在指令执行的时候,访问内存,需要用到数据段DSR,此时是将DSR的内容,1C00 和指令中的偏移量相加0002以形成物理地址。

地址是1C02,然后用1C02去访问内存,

从内存地址为1C02处取得一个字0F8B,最后将0F8B和寄存器AX的原有内容相加。结果在存回AX寄存器。

这是第二条指令的执行过程。

 

 


 

显然经过这样的软硬件改变之后,程序不进行修改,就可以放到程序里的任何地方。

加以执行,都没有问题。

6.8086内存访问的看困境

  8086内存访问的困境

我们来了解INTEL8086处理器 

因硬件设计的特点而在访问内存时所面临的挑战

 

 8086有十六根数据线

一次最大可以访问16位,也就是2个字节的数据。

或者说一次最大可以访问一个字,因为一个字等于2个字节

为了访问内存,它还需要地址线和内存相连,用来指定的时哪个内存单元

8086有20根地址线

20根地址线,可以访问多少个内存呢?

20根地址线,可以访问20位的二进制数

20位的二进制数,可以表示的最小内存地址。

是20个零。

这是二进制形式等于十六进制的0.

 

 

 

 这里一共有多少个地址呢?

2的20次幂 =1024x1024=1,048,576个字节。

 

 

1,048,576B=1024KB=1MB

1MB个地址

每个地址都对应一个内存单元

这20个地址线,可以访问1048576个内存单元

每个单元1个字节

因此我们可以说20根地址线,可以访问,1048576个字节

这就是说啊,20根地址线,最多可以访问的数量是1048576个字节

因特尔8086的20根地址线可以访问1M字节的内存。


 

要取指令和数据,就必须发出地址,因特尔8086处理器,内部集成了,一些和内存访问的一些寄存器。

其中就包括,代码段寄存器CS和数据段寄存器DS 原则上代码段寄存器CS用于存放代码段的起始物理地址

 

起始物理地址存放在寄存器CS里。

 

 

通过代码段寄存器CS,可以跟踪每一个指令的地址,处理器可以用它取指令和执行指令。

 

 

原则上,数据段寄存器DS,用于保存数据段的起始物理地址,

 

在指令执行期间数据段的内容和指令中的偏移地址相加就得到了物理地址。

通过物理地址可以取得操作数,但是非常遗憾,这些寄存器都是十六位的,容纳不了20位的内存地址。

也就是说,像DS CS的寄存器都是十六位的,但是地址线是20位的。

这十六位的段寄存器容纳不了20位的地址。

这一下就麻烦了。

怎么办呢?

7.8086选择段地址的策略

  介绍8086处理器访问内存时,选择段地址的策略

这一课,我们来了解INTEL 8086处理器

在选择段地址时,所采取的策略和方法。

 

 

我们已经知道,程序在内存中时分段的。

8086处理器内部有代码段寄存器CS

和数据段寄存器DS

他们用来保存代码段的起始地址和数据段的起始地址

原则上内存段,可以起始于 任何内存地址。

或者说,任何内存地址,都可以用做段地址。

但是很遗憾,代码段寄存器CS, 和数据段寄存器DS,他们都时十六位的,容纳不了20位的物理地址

不过我们发现有些内存地址的十六进制形式,是以0结尾的。

。比如最低端的0000

FFF00

A0000

00020

00010

如果我们将这些地址的末尾零去掉。剩下的 部分就可以放到段寄存器里了。

比如

FFF0

A000

0002

0001

已经去掉了末尾的0。变成了16位,可以放到段寄存器里了。

 

 

如果我们给定一个以零结尾的内存地址,12560

那么将他末尾的0去掉,剩下一个十六位的部分1256,这很容易理解。

12560是一个十六进制数

将他除以十六进制数10,同样可以1256

1256+0=1256

1256+1=1257

1256+2=1258

1256+3=1259

1256+4=125A

1256+5=125B

1256+6=125C

1256+7=125D

1256+8=125E

1256+9=125F

1256+A=1230

那么加多少会进位呢?

这一样非常理解。

但是在很多书中,将他描述成,将段的物理地址,除以16或者右移4位。

这时怎么个意思呢?

请看将段地址除以16,指的是十进制的十六

要这样算的话,需要将12560换算成十进制数。75104,然后将他除以十进制数16 等于十进制数商4694

十进制数 4694等于十六进制数 1256,他们是相等的/

右移4位

12560,是一个十六进制数。

转换成二进制数之后,0001 0010 0101 0110 0000 

这是一个二十位的二进制数

将他放入到一个20位的寄存器

然后,将这个寄存器的内容右移4位

这些内容整体上右移动4位

 

 最左边的四个零被挤掉了。

是最右边的把???

右边空出来四个空白,用零来替换

是最左边的把???。

移动之后剩余的部分,因为左边四位是零这个无关紧要,实际上有效的部分, 只有十六位,

 

 移动之后的这个二进制数,等于十进制数4694

或者等于十六进制数1256

现在在8086的系统中,由于段寄存器长度的限制,并不是所有的内存地址都可以称为内存地址。

只有那些以零结尾的地址才有可能成为段地址。

 

查阅段地址

DS     数据段地址

CS     代码段地址

比如程序的代码段起始于物理地址30CE0,那么很好, 将末尾的0去掉,剩下的30C3就可以传送到寄存器CS

则CS的内容是30CE

程序的数据段,起始于物理地址 33CE0,那么很好,将末尾的0去掉,剩下的33C3就可以传送到数据段DS

那么此时DS的内容是33CE

 

 反过来再用。段寄存器的内容访问内存时,可以把末尾的零加上。

这样就还原到原来的20位物理地址。

在很多书上的逆过程是把段寄存器的内容左移动四位,或者说乘以16,按照我们原先的设想,段寄存器CS用来跟踪每一条指令的物理地址。

比如说,第一条指令的物理地址是30CE0,长度是3个字节。

 

在第一条指令执行期间,

处理器必须将CS的内容,

修改为第二条指令的内容30CE3

这是用第一条指令的物理地址  30CE0加上第一条指令的长度3得到的。

所以必须把CS的内容修改为第二条指令的物理地址30CE3

但是CS的长度是十六位,存不下,总不能把末尾的3给抹掉把。

这如何是好?

请听下一课分解

8.8086的内存访问过程

  介绍8086使用段寄存器访问内存的过程

这一课来解释8086处理器,用段地址和偏移地址访问内存的过程。

 

 仅仅依靠段寄存器CS,是无法取指令的

、因为它的宽度不够,只有十六位

可是请考虑一下,在代码段内,虽然每一条指令 都有一个物理地址,但是它都有一个相对于段起始处的偏移地址。

比如这是一个代码段,第一条指令 的偏移地址是0000,因为它的物理地址是代码段的起始地址。

 

 第二条指令的偏移地址是0003

因为它相对于段起始处的偏移量是3个字节。

因为这个原因8086处理器内部集成了一个指令指针寄存器IP。’

 

 这个寄存器,专门用于保存指令的偏移地址。

下面我们通过跟踪这个程序的执行过程来了解这个寄存器是如何工作的,以及8086是如何取指令和执行指令的。

在程序开始执行前。

我们需要先把代码段的起始物理地址30CE0右移4位,这时得到30CE(直观上去掉了0),并传送到代码段CS

 此时寄存器CS的内容时30CE

 

接着把数据段的起始物理地址33CE0右移4位,(直观上去掉了0)或者说把末尾的0去掉,这将得到33CE,然后把33C3传送到寄存器DS,此时段寄存器DS的内容是33CE

最后我们把第一条指令的偏移地址0000 传送到指令指针寄存器IP,此时,IP的内容是0000     也就是0.

 

 

 那么现在处理器开始取指令,它首先将段寄存器CS的内容30CE左移4位,得到一个20位的数字,30CE0,然后用30CE0,和指令指针寄存器IP里的0000相加,做加法、这将得到20位的物理地址

30CE0,并送入地址线。

把这个地址送到地址线上,去访问内存。

 

 这将从物理地址30CE0处取得第一条指令,并加以执行。

这条指令是把偏移地址0000处的一个字传送到AX寄存器

这将再次访问内存,在指令执行的时候,需要用到数据段寄存器DS,

此时是把段寄存器DS的内容,33CE左移动4位,得到一个20位的数字33CE0,然后用33CE0和指令中的偏移地址相加,做加法

这将得到一个20位的物理量地址33CE0

然后把这个地址送入地址线,去访问内存,

 

 这将从物理地址33CE0处,也就是这里,得到一个字053C,送入AX寄存器,与此同时

指令指针寄存器,将自动修改位下一条指令的偏移地址0003

这个数字是用IP里原有的数字0加上第一条指令的长度3得到的。

 

 

注意寄存器IP里的内容已经是0003

 

 执行完第一条指令之后,处理器再次进入,取指令阶段,将段寄存器 内容30CE左移4位

得到20位的数字,30CE0,然后用30CE0和指令指针寄存器IP里的0003数字相加,得到20位的物理地址30CE3

然后把这个地址送入地址线取访问内存,这将从物理地址30CE3处,取得第二条指令,然后加以执行

这条指令是把寄存器AX的内容和偏移地址0002处的字相加,结果在AX中

这将再次访问内存

指令执行的时候再次访问,需要用到数据段寄存器DS。

此时,是把段寄存器DS的内容,33CE左移4位,得到20位的数字33CE0,然后用33CE0和指令中的偏移地址0002做加法,得到一个物理地址33CE2

然后把这个地址送入地址线,取访问内存。

这将从物理地址33CE2处取出一个字0F8B,将 0F8B和寄存器AX的内容一起做加法。

结果在存回AX。
于此同时,指令指针寄存器IP的内容,将会自动修改下一条指令的偏移地址,0007,这个数字是用IP里的原有数字三,加上第二条指令的长度4来得到的。

 

 

 

 

30CE 左移四位变成30CE0

30CE0 + 0做加法 

 

 

十六进制

0X0000 乘以10 = 0X0010

why?

0x0000

0x0001

0x0002

0x0003

0x0004

0x0005

0x0006

0x0007

0x0008

0x0009

0x000A

0x000B

0x000C

0x000D

0x000E

0x000F

0x0010

十六进制来看  个数*10=10,直观上进位填0.

从0-F 十六个

 

 

9.逻辑地址和分段的灵活性

  介绍8086分段时,逻辑地址和分段的灵活性

这一课 我们来总结一下,先总结一下,先引入逻辑地址的概念

再来阐述一下8086处理器分段的灵活性

当然了,还得整点习题做做,巩固一下

通过以上的分析,你已经明白了8086内存分段的原理

 

 

 比如在一副的图中,给出了内存单元的地址。

就是这些地址,那么在这些地址中,哪些可以作为8086的段地址呢?

经过观察已经知道,65C70这个地址是可以的。

 因为它是以零结尾的。

从65C70开始,是几个连续的内存单元。

他们的物理地址是65C71  65C72 65C73 

如果我们将这一部分看成一个段,那么在这个段呢,这个内存单元的偏移地址,将会是0000 0001 

为了方便的描述内存单元的地址,和段地址的关系

我们推荐用一种新的方法来标注内存单元的地址

 

 

 

可以清楚的知道段的起始地址

以及

这个内存单元在段内的偏移量

或者说偏移地址

用这种方法表示的地址叫做逻辑地址。

同样的方法,

后面的几个的内存单元的逻辑地址 

65C7:000F

65C7:0006

65C7:0005

65C7:0004

65C7:0003

65C7:0002

65C7:0001

 

 

在8086系统中,也就是8086处理器组成的计算机系统中,访问任何一个内存单元,都是用段寄存器乘以16形成20位的段地址,在和偏移地址相加。

8086这个系统的特点又有着,您要想访问一个内存单元,光知道,它的物理地址还不行,还必须将它转换成十六位的段地址,和十六位的偏移地址。

(光看20位的十六进制,是无法访问内存单元的。因为这20位的地址,有16位表示段地址,而剩下的四位表示偏移地址,所以要区分开)

8086的分段是很灵活的。

 

 

 这个内存单元的地址是22567

如何将这个物理地址转换成段地址和偏移地址呢?

或者说如何将这个物理地址转换成逻辑地址呢?

唯一的办法,想象这个内存单元,位于某个段中/

位于哪个段中呢?

从这个地址往下寻找,寻找那些以零为结尾的物理地址。

 

 

 这样的地址就是段的起始地址。

首先可以认为这个内存单元位于起始物理地址为22560段

此时这个内存单元距离段起始地址的偏移量是7个字节

 

 

 所以偏移地址是7,并因此它的逻辑地址是2256:0007

这个是可以理解的。

同理我们也可以认为这个内存单元位于起始物理地址为22550的段

此时这个内存单元,距离段起始地址的偏移量是23个字节  23十进制

换算成十六进制是17,因此它的逻辑地址是 2255:0017

 

 

 一个物理地址,可以对应着多个物理地址,因此我们也可以认为这个内存单元位于起始地址为22540或者22530等等等

或者12570的段

我们可以认为这个单元位于这些段中。/

 

 

 那么此时,这个内存单元的逻辑地址,还可以分别被认为是

2254:0027

2254:0037 

等等

1257:FFF7

 请问这个内存单元22567,能否位于12560的段吗?

因为在这种情况下的这个内存单元,距离段首的偏移量是10007.十六进制的

在8086的系统中,只能是十六位的。

十六位的偏移地址,最大只能是4个FFFF

但是10007,是无法用十六位来表示的。

所以这样的逻辑地址是非法的。

 

 

 

因为10007是20位的,而不是十六位的

在8086系统中,偏移地址只能是十六位的。


从前面的叙述中,可以看出

 

 

因为段内偏移地址最小是零,最大只能是FFFF

所以段的长度最大只能是64K.

也就是64千字节

或者说65536

这个绿色的框是一个段

段的起始地址是A0370

在这个段内,第一个内存单元的逻辑地址是

A037:0000

最后一个单元的逻辑地址是

A037:   FFFF
那么这个段的长度是多少?65536

FFFF=65536

请问,最后一个单元的物理地址是多少?

????? A037:FFFF

A0370   A037:0000

计算:

A037*16=A0370

然后在加上逻辑偏移地址

A0370+FFFF=B036F

 

 

 

 所以最后一个内存单元的物理地址是 B036F

换句话说,这个段,起始于物理地址A0370,终止于物理地址B036F

 

myGuess main{

   内存单元物理地址 22567

  起始物理地址位22560的段

  起始物理地址位22550的段

  起始物理地址位22540的段

  起始物理地址位22530的段

  起始物理地址位12570的段

  起始物理地址位12560的段

  

   内存单元物理地址 22567

  2256:0007       HX:67-60=7

  2255:0017  HX:67-50=17

  2254:0027  HX:67-40=27

  2253:0037  HX:67-30=37

....

  1257:FFF7  HX:22567-12570=FFF7    (22567-FFFF=12568)

  1256:10007   HX:2567-10007=1 0007  (超过了FFFF) 

  证明:得最大的物理偏移地址为FFFF  ,(2的四次幂)* (2的四次幂)*(2的四次幂)*(2的四次幂)=2的十六次幂= 2的6次幂*2的10次幂= 2的三次幂*2的三次幂*2的10次幂=8*8*1024=64K

}

 

 

  B800*10 + 0028 = B8000

  B8000    + 0028 = B8028

 

 

 

 

 

 

 内存单元的物理地址是B0375,表示成三种不同的逻辑地址。

离这个物理地址最近,且比它小的物理段地址为B0370,B0360,B0350

偏移距离分别为 5,15,25 (十六进制)

B037:0005

B036:0015

B035:0025

 

 

 

 

8086处理器,可以访问1MB内存

内存单元=1B

de1MB=0x1024KB=1024*1024B= 1,048,576B

0x10 0000

0X0000

 Q1.不浪费内存空间可以划分多少个16字节的段?65536个段

 

 Q2,可以划分呢多少个64KB呢?64K=64*1024=16

 

 


 

自我猜测

因为寄存器的位数是16位,

而地址线的总数是20位

那么我们为了取指令,需要将地址线保存起来,而寄存器的位数不足以保存。所以我们将地址线进行分开保存。

那么寄存器位数能保存的数值最大是十六位。

所以将地址线中的十六位保存。那么究竟应该是保存哪些十六位呢?

由于内存地址的编号是以十六进制来进行编号的。也就是逢F进1.

所以观察所有的地址编号比如

65C9F

65C9E

...

65C91

65C90

65C8F

65C8E

...

65C81

65C80

65C7F

65C7E

65C7D

...

65C77

65C76

65C75

65C74

65C73

65C72

65C71

65C70

可以看出65C70   65C80 65C90之间共有16个内存单元。

所以将

65C70---65C7F 作为一个段

65C80---65C8F 作为一个段

65C90---65C9F 作为一个段

而段的起始编号分别为

65C70-

65C80-

65C90-  

而在段中的每一个偏移的内存单元,都有相同的偏移地址。

65C71相对于段首65C70的偏移地址 +0001

65C72相对于段首65C70的偏移地址 +0001

同理

65C81相对于段首65C80的偏移地址 +0001

65C82相对于段首65C80的偏移地址 +0001

同理

65C91相对于段首65C90的偏移地址 +0001

65C92相对于段首65C90的偏移地址 +0001

可以从上分析处,共同的东西有:

一个段共有16个内存单元

段首的内存单元地址 末尾为零

段的内存单元数为十六个

每一个段的内存单元的偏移地址都是从0到F的。

所以

将20为的地址线分段表示

其中,将20位的地址线中的以零为末尾的内存单元编号作为段首,并保存寄存器???

将每一个段的内存单元的偏移地址进行保存在寄存器???

问题,哪一个寄存器作为保存偏移地址还是段首地址呢?

内存中有指令【操作码 立即数】

数据段,其中数据段是操作数【10+9里面的10还有9】

代码段 ,操作码+立即数 【10+9里面的+,而立即数是10的保存地址,以及9的保存地址】
段首是指向操作码加立即数的地址 而偏移地址指向立即数的地址

有点懵?为什么呢?

对过程不了解。所以要从过程出发。

比如10+9=19

第一件事情是取指令


 

posted @ 2022-06-08 11:19  昵称可修改  阅读(952)  评论(0)    收藏  举报