------------------------------------------------------------------------
setup.asm 模块文档说明
By 姜江 <Jiang Jiang>
E-mail:jznsmail@tom.com
http://blog.csdn.net/jznsmail
1.模块介绍
==========
该模块是被boot.asm代码加载到内存0x90200位置的一共24个扇区,该段代码完
成设备信息的采集,建立全局描述符表,开启A20门,并且设置好相应的寄存器然后跳
转到保护模式下执行.
2.模块功能
==========
1.获取启动设备号码,boot.asm代码将设备号码存储在0x90A00位置
2.获取当前光标位置
3.获取扩展内存大小
4.检查显卡数据
5.检查EGA/VGA参数
6.获取硬盘参数表
7.将系统模块从0x10000位置移动到0x00000位置,这将覆盖BIOS中断向量,意味
着我们将不能再使用BIOS中断了.
8.开启A20门
9.设置CR0寄存器,并且跳转到保护模式下运行
3.相关概念
==========
1>什么是硬盘参数表
在PC机中BIOS设定的中断向量表中int 0x41的中断向量位置(4*0x41 =
0x0000:0x0104)存放的并不是中断程序的地址,而是第一个硬盘的基本参数表
对于100%兼容的BIOS来说,这里存放着硬盘参数表阵列的首地址0xF000:0E401
第二个硬盘的基本参数表入口地址存于int 0x46中断向量位置处.每个硬盘参
数表有16个字节大小.
2>什么是A20门
在8086/8088中,只有20根地址总线,所以可以访问的地址是2^20=1M,但由
于8086/8088是16位地址模式,能够表示的地址范围是0-64K,所以为了在8086/
8088下能够访问1M内存,Intel采取了分段的模式:16位段基地址:16位偏移.
其绝对地址计算方法为:16位基地址左移4位+16位偏移=20位地址.
但这种方式引起了新的问题,通过上述分段模式,能够表示的最大内存为:
FFFFh:FFFFh=FFFF0h+FFFFh=10FFEFh=1M+64K-16Bytes(1M多余出来的部分被
称做高端内存区HMA).但8086/8088只有20位地址线,如果访问100000h-
10FFEFh之间的内存,则必须有第21根地址线.所以当程序员给出超过1M
(100000H-10FFEFH)的地址时,系统并不认为其访问越界而产生异常,而是自动
从重新0开始计算,也就是说系统计算实际地址的时候是按照对1M求模的方式
进行的,这种技术被称为wrap-around.
到了80286,系统的地址总线发展为24根,这样能够访问的内存可以达到
2^24=16M.Intel在设计80286时提出的目标是,在实模式下系统所表现的行为
应该和8086/8088所表现的完全一样,也就是说,在实模式下80286以及后续系
列,应该和8086/8088完全兼容.但最终,80286芯片却存在一个BUG:如果程序员
访问100000H-10FFEFH之间的内存,系统将实际访问这块内存,而不是象过去一
样重新从0开始.
为了解决上述问题,IBM使用键盘控制器上剩余的一些输出线来管理第21
根地址线(从0开始数是第20根),被称为A20 Gate.如果A20 Gate被打开,则当
程序员给出100000H-10FFEFH之间的地址的时候,系统将真正访问这块内存区
域;如果A20 Gate被禁止,则当程序员给出100000H-10FFEFH之间的地址的时候,
系统仍然使用8086/8088的方式.绝大多数IBM PC兼容机默认的A20 Gate是被
禁止的.由于在当时没有更好的方法来解决这个问题,所以IBM使用了键盘控制
器来操作A20 Gate.在80286以及更高系列的PC中,即使A20 Gate被打开,在实
模式下所能够访问的内存最大也只能为10FFEFH,尽管它们的地址总线所能够
访问的能力都大大超过这个限制.为了能够访问10FFEFH以上的内存,则必须进
入保护模式.
A20,其实它就是对于20-bit(从0开始数)的特殊处理(也就是对第21根地
址线的处理).如果A20 Gate被禁止,对于80286来说其地址为24bit,其地址表
示为EFFFFF;对于80386极其随后的32-bit芯片来说,其地址表示为FFEFFFFF.
这种表示的意思是如果A20 Gate被禁止,则其第20-bit在CPU做地址访问的时
候是无效的,永远只能被作为0;如果A20 Gate被打开,则其第20-bit是有效的,
其值既可以是0,又可以是1.
所以,在保护模式下,如果A20 Gate被禁止,则可以访问的内存只能是奇数
1M段,即1M,3M,5M…,也就是00000-FFFFF,200000-2FFFFF,300000-3FFFFF….
如果A20 Gate被打开,则可以访问的内存则是连续的.
3>如何开启A20地址线
多数PC都使用键盘控制器(8042芯片)来处理A20 Gate.从理论上讲,打开
A20 Gate的方法是通过设置8042芯片输出端口(64h)的2nd-bit,但事实上,当
你向8042芯片输出端口进行写操作的时候,在键盘缓冲区中,或许还有别的数
据尚未处理,因此你必须首先处理这些数据.
处理过程如下:
1. 禁止中断;
2. 等待,直到8042 Input buffer为空为止;
3. 发送禁止键盘操作命令到8042 Input buffer;
4. 等待,直到8042 Input buffer为空为止;
5. 发送读取8042 Output Port命令;
6. 等待,直到8042 Output buffer有数据为止;
7. 读取8042 Output buffer,并保存得到的字节;
8. 等待,直到8042 Input buffer为空为止;
9. 发送Write 8042 Output Port命令到8042 Input buffer;
10. 等待,直到8042 Input buffer为空为止;
11. 将从8042 Output Port得到的字节的第2位置1(OR 2),然后写入8042
Input buffer;
12. 等待,直到8042 Input buffer为空为止;
13. 发送允许键盘操作命令到8042 Input buffer;
14. 打开中断。
4>如何检测A20地址线是否开启
我们在之前已经提到,如果A20 Gate被打开了,则在实模式下,程序员可以
直接访问100000H~10FFEFH之间的内存,如果A20 Gate被禁止,则在实模式下,
若程序员访问100000H~10FFEFH之间的内存,则会被硬件自动转换为0H~0FFEFH
之间的内存,所以我们可以利用这个差异来检测A20 Gate是否被打开.
5>CR0寄存器
CR0寄存器是处理器4个控制寄存器之一,结构如下
+----+--------------------------+----+----+----+----+----+
| PG | Reserved | ET | TS | EM | MP | PE |
+----+--------------------------+----+----+----+----+----+
31 30 5 4 3 2 1 0
BIT 0:PE 如果该位被置位,则运行保护模式,否则运行实模式
BIT 1:MP 控制wait指令
BIT 2:EM 表示协处理器功能是否可以被仿真
BIT 3:TS 用于任务转换
BIT 4:ET 表示当前协处理器的类型(80287或80387)
BIT 31:PG表示处理器是否使用分页机制
6>描述符表
在保护模式下内存的寻址方式跟实模式下不同,它并不是简单的将段寄存
器的16位地址左移4位,然后加上偏移量来构成20位地址.在保护模式下,是可
以寻址4GB空间的,因为有32跟地址线,所以2^32 = 4GB.在该模式下是通过段
寄存器的索引值在描述符表(全局描述符表GDT或者局部描述符表LDT)内寻找
到相应的表项,然后通过获取表项里保存的段基地址,段基地址与偏移量相加
得到线性地址.如果系统没采用分页处理模式,那么线性地址就对应着实际的
物理地址了.分页处理模式放到内存管理文档里详细说明.
段寄存器格式:
+--------------------------------+------+-----+
| Index | TI | RPL |
+--------------------------------+------+-----+
15 32 1 0
RPL: 优先级别 0 - 4 LINUX只用了0核心态 3用户态
TI: 0使用全局描述符表(GDT),1使用局部描述符表(LDT)
描述符表是保存在gdtr或者ldtr寄存器里的,该寄存器共占用6个字节大
小.0-15位是表限,16-47位是表的基地址.表限是表示该表的大小,基地址是表
示描述符表所在的线性地址中的位置.
全局描述符号表和局部描述符号表的结构是一样的,如下:
+---------------------------------+----------+
| Base address | Limit |
+---------------------------------+----------+
47 1615 0
描述符表项是存储在描述符表里的内容.Intel公司将表项的第一个项设
为空,从第2个表项开始使用.描述符表项的结构定义如下:
63 5655 5251 4847 4039 3231 1615 87 0
+---------+----+-------+-----+---------+-------------+-----------------+
|B31 - B24| |L19-L16| |B23 - B16|基地址 B15-B0|段上限L15 - L0|
+---------+----+-------+-----+---------+-------------+-----------------+
+------+------+------+------+
| G | D/B | O | AV |
+------+------+------+------+
55 52
G: =1 段长以4K字节为单位 =0 段长以字节为单位
D/B: =1 表示对该段访问为32位指令 =0 表示为16位指令
O: 永远为0
AV: 可由软件使用,CPU忽略该位
+-----+----------+-----+-----+-------+-------+------+
| P | DPL | S | E | ED/C | R/W | A |
+-----+----------+-----+-----+-------+-------+------+
47 44 43 40
A: =1 已经被访问过 =0 还未被访问过
R/W: ---+
ED/C: ---|----+---> +---- E = 0 数据段
E: ---+ | |
| +---- ED = 0 向上伸(数据段) ED = 1 向下伸(堆栈段)
| |
| +---- W = 0不可写 W = 1 可写
|
+---> +---- E = 1代码段
|
+---- C = 0 忽略特权级 C = 不忽略特权级
|
+---- R = 0 不能读 R = 1可读
S = 0表示用于系统管理的系统段 = 1表示一般的代码段或者数据段
DPL: 特权级
P = 1该段在内存中 = 0 不在内存中
4.模块内存映象
==============
0x90090 - 0x9009F HD2硬盘参数表
0x90080 - 0x9008F HD1硬盘参数表
0x9000C 显卡特性参数
0x9000A 显卡内存和显示状态
0x90008 ???
0x90006 显示模式和字符列数
0x90004 显示页面号
0x90002 扩展内存大小
0x90000 光标位置
+--------------+
| |
| .... |
| |
+--------------+ 0x909FF
| |
| setup |
| |
| |
+--------------+ 0x90200
| ... |
+--------------+ 0x900A0
| 系统参数 |
+--------------+ 0x90000
| |
| ... |
| |
+--------------+ 0x13000
| |
| bak system |
| |
+--------------+ 0x10000
| |
| ... |
| |
+--------------+ 0x07E00
| boot |
+--------------+ 0x07C00
| ... |
+--------------+ 0x03000
| |
| system |
| |
+--------------+ 0x00000
5.模块使用的中断调用
====================
中断号: INT 0x10
AH = 0x03 读光标功能号
功能: 获取当前光标位置
输入: bh = 页号
返回: ch = 扫描开始线 cl = 扫描结束线
dh = 行号(0x00是顶端) dl = 列号(0x00是左边)
-----------------------------------------------------------------------
中断号: INT 0x15
ah = 0x88
功能: 获取扩展内存大小(KB)
返回: ax = 从0x100000(1MB)处开始的扩展内存大小(KB)
错误: FC置位, ax = 出错码
-----------------------------------------------------------------------
中断号: INT 0x10
ah = 0x0f
功能: 获取显卡当前显示模式
返回: ah = 字符列数 al = 显示模式
bh = 当前显示页
-----------------------------------------------------------------------
中断号: INT 0x10
ah = 0x12 bl = 0x10
功能: 检查显示方式(EGA/VGA)
返回: bh = 显示状态
0x00 - 彩色模式,I/O端口 = 0x3dX
0x01 - 单色模式,I/O端口 = 0x3bX
bl = 安装的显示内存
0x00 - 64k 0x01 - 128k
0x02 - 192k 0x03 - 256k
-----------------------------------------------------------------------
中断号: INT 0x15
ah = 0x15
功能: 检查系统是否存在第2个硬盘
输入: dl = 驱动器号(0x8X是硬盘:0x80第一个硬盘,0x81第二个硬盘)
输出: ah = 类型码(00 - 没有这个盘,CF = 1;01 - 软驱,没有change-line支持;
02 - 软驱(或其他可移动设备),有change-line支持, 03 - 硬盘)
-----------------------------------------------------------------------