8086的内存分段机制

1,559 阅读7分钟

如图所示,8086 内部有 8 个 16 位的通用寄存器,分别是 AX、BX、CX、DX、SI、DI、BP、SP。其中,前四个寄存器中的每一个,都还可以当成两个 8 位的寄存器来使用,分别是 AH、AL、BH、BL、CH、CL、DH、DL

8086处理器内部组成框图.png

在进行数据传送或者算术逻辑运算的时候,使用算术逻辑部件(ALU)。比如,将 AX 的内容和 CX 的内容相加,结果仍在 AX 中,那么,在相加的结果返回到 AX 之前,需要通过一个叫数据暂存器的寄存器中转。

处理器能够自动运行,这是控制器的功劳。为了加快指令执行速度,8086 内部有一个 6 字节的指令预取队列,在处理器忙着执行那些不需要访问内存的指令时,指令预取部件可以趁机访问内存预取指令。这时,多达 6 个字节的指令流可以排队等待解码和执行。

8086 内部有 4 个段寄存器。其中,CS 是代码段寄存器,DS 是数据段寄存器,ES 是附加段(Extra Segment)寄存器。附加段的意思是,它是额外赠送的礼物,当需要在程序中同时使用两个数据段时,DS 指向一个,ES 指向另一个。可以在指令中指定使用 DSES 中的哪一个,如果没有指定,则默认使用 DSSS 是栈段寄存器,以后会讲到,而且非常重要。

IP 是指令指针(Instruction Pointer)寄存器,它只和 CS 一起使用,而且只有处理器才能直接改变它的内容。当一段代码开始执行时,CS 指向代码段的起始地址,IP 则指向段内偏移。这样,由 CSIP 共同形成逻辑地址,并由总线接口部件变换成物理地址来取得指令。然后,处理器会自动根据当前指令的长度来改变 IP 的值,使它指向下一条指令。

当然,如果在指令的执行过程中需要访问内存单元,那么,处理器将用 DS 的值和指令中提供的偏移地址相加,来形成访问内存所需的物理地址。

8086 的段寄存器和 IP 寄存器都是 16 位的,如果按照原先的方式,把段寄存器的内容和偏移地址直接相加来形成物理地址的话,也只能得到 16 位的物理地址。麻烦的是,8086 却提供了 20 根地址线。换句话说,它提供的是20位的物理地址。

提供 20 位地址的原因很简单,16 位的物理地址只能访问 64KB 的内存,地址范围是 0000H~FFFFH,共 65536 个字节。这样的容量,即使在那个年代(8086 是 Intel 在 1978 年发布的第一款 16 位的处理器),也显得捉襟见肘。65536 个字节就是 64KB,而 20 位的物理地址则可以访问多达 1MB 的内存,地址范围从 00000HFFFFFH。问题是,16 位的段地址和 16 位的偏移地址相加,只能形成 16 位的物理地址,怎么得到这 20 位的物理地址呢?

为了解决这个问题,8086 处理器在形成物理地址时,先将段寄存器的内容左移 4 位(相当于乘以十六进制的 10,或者十进制的 16),形成 20 位的段地址,然后再同 16 位的偏移地址相加,得到 20 位的物理地址。比如,对于逻辑地址 F000H:052DH,处理器在形成物理地址时,将段地址 F000H 左移 4 位,变成 F0000H,再加上偏移地址 052DH,就形成了 20 位的物理地址 F052DH

这样,因为段寄存器是 16 位的,在段不重叠的情况下,最多可以将 1MB 的内存分成 65536 个段,段地址分别是 0000H、0001H、0002H、0003H,......,一直到 FFFFH。在这种情况下,如图 2-13 所示,每个段正好 16 个字节,偏移地址从 0000H000FH

1MB内存可以划分为65536个16字节的段.png

同样在不允许段之间重叠的情况下,每个段的最大长度是 64KB,这是因为偏移地址也是 16 位的,从 0000HFFFFH。在这种情况下,1MB 的内存,最多只能划分成 16 个段,每段长 64KB,段地址分别是 0000H、1000H、2000H、3000H,...,一直到 F000H

以上所说的只是两种最典型的情况(边界情况)。通常情况下,段地址的选择取决于内存中哪些区域是空闲的。举个例子来说,假如从物理地址 00000H 开始,一直到 82251H 处都被其它程序占用着,而后面一直到 FFFFFH 的地址空间都是自由的,那么,你可以从物理内存地址 82251H 之后的地方加载你的程序。

接着,你的任务是定义段地址并设置处理器的段寄存器,其中最重要的是段地址的选取。因为偏移地址总是要求从 0000H 开始,而 82260H 是第一个符合该条件的物理地址,因为它恰好对应着逻辑地址 8226H:0000H,符合偏移地址的条件,所以完全可以将段地址定为 8226H

但是,举个例子来说,如果你从物理内存地址 82255H 处加载程序,由于它根本无法表示成一个偏移地址为 0000H 的逻辑地址,所以不符合要求,段不能从这里开始划分。这里的区别在于,82260H 可以被十进制数 16(或者十六进制数 10H)整除,而 82255H 不能。通过这里例子可以看出,8086 处理器的逻辑分段,起始地址都是 16 的倍数,这称为是按 16 字节对齐的。

段的划分是自由的,它可以起始于任何 16 字节对齐的位置,也可以是任意长度,只要不超过 64KB。比如,段地址可以是 82260H,段的长度可以是 64KB。在这种情况下,该段所对应的逻辑地址范围是 8226H:0000H~8226H:FFFFH,其所对应的物理地址范围是 82260~9225FH

同时,正是由于段的划分非常自由,使得 8086 的内存访问也非常随意。同一个物理地址,或者同一片内存区域,根据需要,可以随意指定一个段来访问它,前提是那个物理地址位于该段的 64KB 范围内。也就是说,同一个物理地址,实际上对应着多个逻辑地址。

练习题

  1. 在段与段之间互不重叠的前提下,1MB 内存可以完整地划分为多少个 16KB 的段?

答:只需拿 1MB 除以 16KB 即可,也就是可以完整地划分为 64 个 16KB 的段。

  1. 数据段寄存器 DS 的值为 25BCH 时,计算 Intel 8086 可以访问的物理地址范围。

答:因为 8086 是 16 位的 CPU,所以,偏移地址的范围为 0000H~FFFFH。 所能访问的范围即为:25BCH:0000H~25BCH:FFFFH,转换为物理地址时为:25BC0H~35BBFH

参考资料:

[x86汇编语言-从实模式到保护模式 第二章 2.5.4 8086的内存分段机制]