1. 初始化硬件信息存放空间
- 先说在前,setup在0x90200位置
代码:
start:
mov ax,#0x9000 ; this is done in bootsect already, but...
mov ds,ax
mov ah,#0x03 ; read cursor pos
xor bh,bh
int 0x10 ; save it in known place, con_init fetches
mov [0],dx ; it from 0x90000.
一开始setup就将ax寄存器放入0x9000地址,这里是之前bootsetc复制过来的位置,此时bootsetc没用了,这块空间将用于存放硬件信息
2. 读取硬件信息
通过中断操作读取硬件信息,int命令就是中断,中断会读取ah寄存器的数据,数据映射了所有中断操作,0x03就是读取光标的操作
通过诸如此类的方法,读取其他硬件信息:
获取内存信息。
; Get memory size (extended mem, kB)
mov ah,#0x88
int 0x15
mov [2],ax
获取显卡显示模式。
; Get video-card data:
mov ah,#0x0f
int 0x10
mov [4],bx ; bh = display page
mov [6],ax ; al = video mode, ah = window width
检查显示方式并取参数
; check for EGA/VGA and some config parameters
mov ah,#0x12
mov bl,#0x10
int 0x10
mov [8],ax
mov [10],bx
mov [12],cx
获取第一块硬盘的信息。
; Get hd0 data
mov ax,#0x0000
mov ds,ax
lds si,[4*0x41]
mov ax,#INITSEG
mov es,ax
mov di,#0x0080
mov cx,#0x10
rep
movsb
获取第二块硬盘的信息。
; Get hd1 data
mov ax,#0x0000
mov ds,ax
lds si,[4*0x46]
mov ax,#INITSEG
mov es,ax
mov di,#0x0090
mov cx,#0x10
rep
movsb
最终读取结果如下: | 内存地址 | 长度(字节) | 名称 | | —- | —- | —- | | 0x90000 | 2 | 光标位置 | | 0x90002 | 2
| 扩展内存数 | | 0x90004 | 2 | 显示页面 | | 0x90006 | 1
| 显示模式 | | 0x90007 | 1 | 字符列数 | | 0x90008 | 2 | 未知 | | 0x9000A | 1
| 显示内存 | | 0x9000B | 1
| 显示状态 | | 0x9000C | 2 | 显卡特性参数 | | 0x9000E | 1
| 屏幕行数 | | 0x9000F | 1 | 屏幕列数 | | 0x90080 | 16
| 硬盘1参数表 | | 0x90090 | 16 | 硬盘2参数表 | | 0x901FC | 2
| 根设备号 |
3. 关闭中断
cli ; no interrupts allowed 表示关闭中断;
- 因为后面我们要把原本是 BIOS 写好的中断向量表给覆盖掉,也就是给破坏掉了,写上我们自己的中断向量表,所以这个时候是不允许中断进来的。
4. 整理内存,完成实模式
- 将system的那块内存复制彻底覆盖掉boots(此时boots还有块BIOS存放的位置)
- 现在只setup和system了
5. 实模式和保护模式物理地址转换
实模式是16位的,因为一开始CPU是16位的,现在都是32位或64位CPU了,保护模式就是32位或64位了
实模式地址转换
先说在前,目前还没进入分页模式,所有地址都是基于段的
- 之前说过,实模式地址就是段基址(ds寄存器存的地址就是段基址)左移后加上偏移量,很简单
保护模式地址转换
- ds不再直接存段基址,而是段描述符
- 从段描述符表中,ds指向的位置去除段基址
- 段描述符表(gdt)位置,在setup中有一个标签,setup的起始地址(即0x90200) + gdt标签就是gdt的地址
-
6. 进入保护模式
setup初始化gdt,把地址存入寄存器,就是开启保护模式第一步
- 与gdt相同原理,还有个idt,即中断表,是Linux自己定义的中断信号的变量值
- 此时内存是这样:
- 打开A20地址线用于兼容20位地址(传统操作)
- 对可编程中断控制器 8259 芯片进行编程,设置0x00 ~ 0x19的20个保留中断信号(必须设置,兼容传统)
中断号是不能冲突的, Intel 把 0 到 0x19 号中断都作为保留中断,比如 0 号中断就规定为除零异常,软件自定义的中断都应该放在这之后,但是 IBM 在原 PC 机中搞砸了,跟保留中断号发生了冲突,以后也没有纠正过来,所以我们得重新对其进行编程,不得不做,却又一点意思也没有。这是 Linus 在上面注释上的原话。
PIC 请求号 | 中断号 | 用途 |
---|---|---|
IRQ0 | 0x20 | 时钟中断 |
IRQ1 | 0x21 | 键盘中断 |
IRQ2 | 0x22 | 接连从芯片 |
IRQ3 | 0x23 | 串口2 |
IRQ4 | 0x24 | 串口1 |
IRQ5 | 0x25 | 并口2 |
IRQ6 | 0x26 | 软盘驱动器 |
IRQ7 | 0x27 | 并口1 |
IRQ8 | 0x28 | 实时钟中断 |
IRQ9 | 0x29 | 保留 |
IRQ10 | 0x2a | 保留 |
IRQ11 | 0x2b | 保留 |
IRQ12 | 0x2c | 鼠标中断 |
IRQ13 | 0x2d | 数学协处理器 |
IRQ14 | 0x2e | 硬盘中断 |
IRQ15 | 0x2f | 保留 |
- 设置cr0寄存器第0位(PE位)的值置1,就代表正式进入了保护模式
7. setup的最后
jmpi命令跳转,根据gdt的定义找到第一项,值是0即跳转到内存0位置,也就是system代码的起始地址
由此,setup结束了,开始了head.s