第94章 8086的寻址方式

在MS-DOS及Win16(参见本书78.3节和53.5节)平台的16位应用程序中,我们可以看到程序指针由两个16位值构成。这是什么情况?不得不说这是MS-DOS和8086的另一大怪异的特色。

虽然8086/8088属于16位CPU,但是它却有20位的RAM地址空间(内存总线有20个引脚);即,它可直接寻址的存储空间只有1MB。这1MB的外部内存空间又被划分为RAM(最大640kB)、ROM、显卡内存、EMS卡,等等。

16位的8086/8088 CPU实际上由8080 CPU发展而来。8080 CPU的地址空间只有16位,所以可直接控制的内存只有64KB。大概是8086的设计者认为64KB空间不够用,而且8086还要兼容8080平台的程序,所以就把20位/1MB的内存划分为若干个段使用。这就是早期玩具级的虚拟内存技术的思路。而8086的寄存器又只是16位寄存器,为了进行更大范围(20位)的寻址,它就得借助新推出的段寄存器。从此CPU就有了CS、DS、ES和SS寄存器。20位的内存指针由短寄存器和地址寄存器对(DS:BX)混合计算而来:

real\underline{\;}address=(segment\underline{\;}register\ll 4)+address\underline{\;}register

举例来说,过去IBM PC兼容的主机,其显卡(EGA、VGA)的显存都只有64KB。要读写显存,就要在某个段寄存器里(例如DS)写入0xA000。如此一来,程序就可使用DS:0~DS:0xFFFF访问整个显存。虽然地址总线是20位的,超过了16位寄存器的表达范围,但是CPU可借助段寄存器毫无障碍地访问0xA0000~0AFFFF。

程序还可能直接访问固定的内存地址,例如0x1234,但是操作系统加载应用程序到起始地址却不是固定的。段寄存器的出现,解决了这种问题——它可由段寄存器进行相对寻址,应用程序不必关心自己到底被加载到了什么RAM地址上。

实际上,MS-DOS系统下的指针由段地址和段内地址构成,可由两个16位的数值表示。20位地址总线足以满足这种寻址方式的需要。不过,程序员就需要重新计算内存地址了:他们要不停地考虑空间和效率的平衡,仔细规划数据栈的分配情况。

另外,8086的寻址方式决定了每个内存块不能大于64KB。

80286平台仍然继承了段寄存器(segment registers),只是用途不同而已。

在支持更大RAM的80386 CPU问世时,市面上流行的仍然是MS-DOS。更有一大批叫作DOS extenders的DOS粉丝在Windows系统问世以后继续坚守DOS阵地。他们甚至开发出各种像模像样的OS系统。这种系统不仅实现了CPU保护模式的切换功能,而且大幅度地改进了内存API,可继续支持MS-DOS的应用程序。著名的有:DOS/4GW(游戏巨作DOOM就是面向它编译的)、Phar Lap和PMODE。

在Win32之前,16位的Windows 3.x仍然沿用了这种寻址方式。