一、真正的内存不用1MB
8086具有20根地址线,它的寻址范围是0x00000到0xFFFFF,可以寻址1M的内存空间。实际上这20根地址线并没有全部用于内存寻址,只不过是大部分地址线用于内存,还有一部分地址线用于只读存储器ROM(存放BIOS)和外围设备。
在8086中1M寻址空间分布如下:
1、较低端的640KB,物理内存占用。物理地址范围是0x00000-0x9FFFF。
2、中间还剩余320KB,由外设占用。其中就包括显卡的显存部分。由于历史原因,一直以来0xB8000-0xBFFFF这段物理地址空间,是留给显卡的。
3、最顶端的64KB,ROM占用。物理地址的范围是0xF0000-0xFFFFF。这一段空间固化了开机时要执行的指令。
二、实模式下1M地址分布
起始 | 结束 | 大小 | 用途 |
FFFF0 | FFFFF | 16B | BIOS入口地址,此地址也属于BIOS代码,
同样属于顶部的640KB字节。只是为了 强调其入口地址才单独贴出来。此处16字节 的内容是跳转指令jmp f000:e05b |
F0000 | FFFEF | 64KB-16B | 系统BIOS范围是F0000~FFFFF共640KB,
为说明入口地址,将最上面的16字节从 此处去掉了,所以此处终止地址是0XFFFEF |
C8000 | EFFFF | 160KB | 映射硬件适配器的ROM或内存映射式I/O |
C0000 | C7FFF | 32KB | 显示适配器BIOS |
B8000 | BFFFF | 32KB | 用于文本模式显示适配器 |
B0000 | B7FFF | 32KB | 用于黑白显示适配器 |
A0000 | AFFFF | 64KB | 用于彩色显示适配器 |
9FC00 | 9FFFF | 1KB | EBDA(Extended BIOS Data Area)
扩展BIOS数据区 |
7E00 | 9FBFF | 622080B约608KB | 可用区域 |
7C00 | 7DFF | 512B | MBR被BIOS加载到此处,共512字节 |
500 | 7BFF | 30464B约30KB | 可用区域 |
400 | 4FF | 256B | BIOS Data Area(BIOS数据区) |
000 | 3FF | 1KB | Interrupt Vector Table(中断向量表) |
三、两个字符操作显存
显存就是用来控制显示器要输出的内容。需要输入两个字节,第一个是字符,第二个是字符的样式(比如颜色,背景等等)【简单总结,可能不够准确】
四、86-DOS 将开机码载入200H 的理由
在86-DOS 中会将储存在磁碟开头的开机码(bootstrap) 载入到200H,而200H 这个值刚好在8086 的中断向量所在的位址范围内(0H – 3FFH)。
根据Tim Peterson 的说明,中央处理器的中断向量所在区域在规格上属于预定好的保留区,也就是说0H-3FFH 这个区域是绝对不会载入作业系统的「安全地带」,因此选择了200H这个位址。
摘录Tim Paterson 的说明如下:
At SCP, I chose 0x200 because it was in the interrupt vector space (0 – 3FFH). This means it needed to be reserved and couldn’t be in the way of an OS, no matter where it wanted to load.
整理一下:
- 0x0 – 0x3FF 是8086 在规格上用来放置中断向量的区域
- 0x400 以后的位址会载入86-DOS 系统
- 0x200 – 0x3FF 之间的的中断向量在86-DOS 时代并未被使用
根据以上的理由使用了200H – 3FFH 之间的空间。顺带一提,100H – 200H 之间的空间似乎也被用来备份暂存器内容或作为Monitor/开机码所使用的堆叠空间。
五、MBR 载入到7C00H的理由
先说结论,”0x7C00″最早出现于1981年8月发表的IBM Personal Computer (PC) 5150的ROM BIOS。
首先,所谓的7C00H 就是:
32KB (8000H) - 1024B
最前面的32KB 到刚好1024 Bytes 的位置,就是7C00H 了。首先是第一个疑问:这里的”32KB – 1024 = 7C00″ 是有意义的,为什么要订在这个位址呢?
第二个疑问是:IBM PC 5150 发表时,搭载最少记忆体的机种只有16KB (4000H) 的RAM。换句话说,只有16KB 的RAM 是无法将MBR 载入到7C00H 这个位址的。这样说来,16KB 的机种不就没办法执行DOS 了吗?
整理一下后是以下两点:
- 0x7C00 刚好是32KB – 1024B 的位置,为什么是这个位置?
- 5150 搭载最少记忆体的机种只有16KB 的RAM,那不就无法启动OS 了吗?(无法存取到0x7C00)
针对这两个疑问,试着写信询问了IBM PC 5150 的ROM BIOS 的开发者,David Bradley。同时他也因为设计了以”Ctrl-Alt-Delete” 重置系统而为人所知。
另外,在信件中也问了「86-DOS 会载入到200H,为什么要改变呢?」
I don’t know anything about 86-DOS.
得到这样的回应。也就是说,David 没看过86-DOS 的程式,在不晓得「86-DOS 将开机码载入200H」这件事的情况下开发了ROM BIOS。
那么,先从David Bradley 的回信中解答关于16KB 机型的疑问:
It had to boot on a 32KB machine. DOS 1.0 required a minimum of 32KB, so we weren’t concerned about attempting a boot in 16KB.
要执行DOS 1.0 至少需要32KB,因此没考虑过16KB 机型的问题。
接着是「为什么是32KB – 1024B?」的回答:
We wanted to leave as much room as possible for the OS to load itself within the 32KB. The 808x Intel architecture used up the first portion of the memory range for software interrupts, and the BIOS data area was after it. So we put the bootstrap load at 0x7C00 (32KB-1KB) to leave all the room in between for the OS to load. The boot sector was 512 bytes, and when it executes it’ll need some room for data and a stack, so that’s the other 512 bytes . So the memory map looks like this after INT 19H executes:
- 考虑到OS 需要的最少记忆体,希望能在32KB 中尽量空出空间
- 从位址0 开始,一直到3FFH 为止规划为中断向量,因此无法使用
- 所以,只能载入到32KB 的后半段了
- MBR 中的开机码需要空间以便存放资料或作为堆叠使用,要多保留512 bytes
- 根据上述,32KB – 512B (MBR) – 512B (资料与堆叠) 就得到了7C00H
根据以上的说明,执行INT 19h 后的记忆体规划如下图:
+--------------------- 0x0 | Interrupts vectors +--------------------- 0x400 | BIOS data area +--------------------- 0x5?? | OS load area +--------------------- 0x7C00 | Boot sector +--------------------- 0x7E00 | Boot data/stack +--------------------- 0x7FFF | (not used) +--------------------- (...)
以上就是超过25 年,一直控制着x86 IBM PC/AT 相容机上的OS 开机码的0x7C00、7C00H 的由来。
六、32位系统下实模式的差异
计算机刚刚启动时的内存布局如图:
地址 | 用途 |
---|---|
(4GB – 64KB) ~ 4GB | 实际BIOS ROM |
1MB ~ (4GB – 64KB) | 空闲空间 |
640KB ~ 1MB | 视频内存,BIOS启动固件(映射) |
0 ~ 640KB | 空闲空间 |
(这是一个非常简略的示意图,具体请见Memory Map (x86))
这一复杂的映射机制是为了保证向后兼容而设计的。在8086时代,内存只有1MB大小,此时,BIOS的代码固化在EPROM中,且EPROM被编址在1MB内存地址空间的最高64KB中。PC加电后,CS寄存器初始化为0xF000,IP寄存器初始化为0xFFF0,所以CPU要执行的第一条指令的地址为
CS:IP=0xF000:0XFFF0
( Segment:Offset表示) =0xFFFF0
( Linear表示) 。这个地址位于被固化的EPROM中,该地址存储了一条指令,它是一个长跳转指令JMP F000:E05B
。这样就开启了BIOS的执行过程。
到了32位的80386 CPU时代,内存空间扩大到了4G,多了段机制和页机制。如果仍然把BIOS启动固件编址在0xF0000起始的64KB内存地址空间内,就会把整个物理内存地址空间隔离成不连续的两段,一段是0xF0000以前的地址,一段是1MB以后的地址,这很不协调。为此,intel采用了一个折中的方案:默认将执行BIOS ROM编址在32位内存地址空间的最高端,即位于4GB地址的最后一个64KB内。在PC系统开机复位时,CPU进入实模式,并将CS寄存器设置成0xF000,将它的shadow register的Base值初始化设置为0xFFFF0000,EIP寄存器初始化设置为0x0000FFF0。所以机器执行的第一条指令的物理地址是0xFFFFFFF0。80386的BIOS代码也要和以前8086的BIOS代码兼容,故地址0xFFFFFFF0处的指令还是一条长跳转指令jmp F000:E05B
。注意,这个长跳转指令会更新CS寄存器和它的shadowregister,即执行jmp F000:E05B
后,CS将被更新成0xF000
。表面上看CS其实没有变化,但CS的shadow register被更新为另外一个值了,它的Base域被更新成0x000F0000
,此时形成的物理地址为Base+EIP=0x000FE05B
,这就是CPU执行的第二条指令的地址。此时这条指令的地址已经是1M以内了,且此地址不再位于BIOS ROM中,而是位于RAM空间中。由于Intel设计了一种映射机制,将内存高端的BIOS ROM映射到1MB以内的RAM空间里,并且可以使这一段被映射的RAM空间具有与ROM类似的只读属性。所以PC机启动时将开启这种映射机制,让4GB地址空间的最高一个64KB的内容等同于1MB地址空间的最高一个64K的内容,从而使得执行了长跳转指令后,其实是回到了早期的8086 CPU初始化控制流,保证了向下兼容。
上述说明指出,在CPU启动之后,它处于实模式之下,执行的第一条指令是jmp F000:E05B
,跳转到BIOS程序中。此时,PC = 16*CS + IP
,系统地址空间只有20位(1MB)。
参考:
https://gist.github.com/letoh/2790559
http://book.51cto.com/art/201604/509566.htm
https://zhanghuimeng.github.io/post/os-mooc-lecture-3-summary/