理论讲解:Bootloader
1. Bootloader的职责
引导扇区只有512字节,能做的事情非常有限。Bootloader的核心职责是为后续加载内核做准备:
① 初始化段寄存器和栈
↓
② 打印启动信息(让用户知道启动中)
↓
③ 从磁盘加载更多数据到内存
↓
④ 准备进入保护模式(下一章)
↓
⑤ 跳转到内核代码
2. 屏幕打印函数
上一章我们逐个字符打印,现在封装成可复用函数:
; 打印以0结尾的字符串
; 输入: SI = 字符串地址
print_string:
pusha ; 保存所有通用寄存器
mov ah, 0x0E ; BIOS teletype功能
.loop:
lodsb ; AL = [SI], SI++
cmp al, 0 ; 到字符串结尾了吗?
je .done
int 0x10 ; 打印字符
jmp .loop
.done:
popa ; 恢复所有寄存器
retpusha 和 popa — 保存/恢复所有通用寄存器。好习惯:函数不应该修改调用者的寄存器。
3. 磁盘读取
BIOS磁盘服务 INT 0x13
INT 0x13, AH=0x02 — 读取磁盘扇区
输入:
AH = 0x02 功能号(读取)
AL = 扇区数量 要读取多少个扇区
CH = 柱面号 CHS寻址的柱面
CL = 扇区号 CHS寻址的扇区(从1开始)
DH = 磁头号 CHS寻址的磁头
DL = 驱动器号 0x00=软驱A, 0x80=第一个硬盘
ES:BX = 目标地址 数据读取到的内存位置
输出:
CF = 0 成功, 1 失败
AH = 状态码
AL = 实际读取的扇区数
CHS vs LBA
- CHS(柱面-磁头-扇区): 传统的磁盘寻址方式
- LBA(逻辑块寻址): 从0开始的线性扇区编号
BIOS INT 0x13 AH=0x02 使用CHS寻址。扇区号从1开始。
磁盘布局
扇区0: 引导扇区(Bootloader)← BIOS已加载到0x7C00
扇区1: 内核代码
扇区2: 内核代码(续)
...
我们的Bootloader需要把扇区1开始的数据加载到内存中。
4. 代码组织
本章将代码拆分为多个文件:
code/
├── boot.asm 主引导程序
├── print.asm 字符串打印函数
├── disk.asm 磁盘读取函数
└── Makefile 构建脚本
使用 %include 指令将多个文件组合:
%include "print.asm"
%include "disk.asm"5. 内存布局
0x0000 ┌──────────────────┐
│ 中断向量表 │
0x0400 ├──────────────────┤
│ BIOS数据区 │
0x0500 ├──────────────────┤
│ │
│ 可用内存(栈区) │ ← 栈从0x7C00向下增长
│ │
0x7C00 ├──────────────────┤
│ 引导扇区(512B) │ ← 我们的Bootloader
0x7E00 ├──────────────────┤
│ │
│ 可用内存 │
│ │
│ 内核加载到这里 → │ ← 0x1000(我们选的地址)
│ │
└──────────────────┘