操作系统的引导
实验内容
此次实验的基本内容是:
- 阅读《Linux内核完全注释》的第6章,对计算机和Linux 0.11的引导过程进行初步的了解;
- 按照下面的要求改写0.11的引导程序bootsect.s
- 有兴趣同学可以做做进入保护模式前的设置程序setup.s。
改写bootsect.s主要完成如下功能:
- bootsect.s能在屏幕上打印一段提示信息“XXX is booting…”,其中XXX是你给自己的操作系统起的名字,例如LZJos、Sunix等(可以上论坛上秀秀谁的OS名字最帅,也可以显示一个特色logo,以表示自己操作系统的与众不同。)
改写setup.s主要完成如下功能:
- bootsect.s能完成setup.s的载入,并跳转到setup.s开始地址执行。而setup.s向屏幕输出一行"Now we are in SETUP"。
- setup.s能获取至少一个基本的硬件参数(如内存参数、显卡参数、硬盘参数等),将其存放在内存的特定地址,并输出到屏幕上。
- setup.s不再加载Linux内核,保持上述信息显示在屏幕上即可。
实验结果
bootsect.s
.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text
! 设置初始段地址参数
SETUPLEN = 4 ! setup程序所占扇区
BOOTSEG = 0x07c0 ! bootsect原始内存地址
INITSEG = 0x9000 ! bootsect移动后内存地址
SETUPSEG = 0x9020 ! setup程序起始段地址
entry _start
_start:
! 移动bootsect 0x07c00 -> 0x90000
! 设置源地址 ds:si
mov ax, #BOOTSEG
mov ds, ax
sub si, si
! 设置目的地址 es:di
mov ax, #INITSEG
mov es, ax
sub di, di
! 移动512字节
mov cx, #256
rep
movw
! 跳转到go标号处开始,并将cs赋给ds,es
jmpi go, INITSEG
go:
mov ax, cs
mov ds, ax
mov es, ax
load_setup: ! 加载setup程序
! 将setup从磁盘中读到内存0x90200开始处
! setup在磁盘第2个扇区开始,共占4个扇区
! 磁道、磁头、驱动器均为0
! 使用BIOS int 0x13中断的0x02功能-读扇区
! ah = 0x02 选择中断功能 al = 要读扇区的数量
! ch = 磁道(柱面)号的低8位 cl = 开始扇区(0 - 5位),磁道号高两位(0 - 6位)
! dh = 磁头号 dl = 驱动器号(如果是硬盘则位7要置位)
! es:bx 指向数据缓冲区
! 若读写成功,CF = 0,ah = 0x00
! 若读写错误,则标志位置位,ah = 状态码
mov ax, #0x0200 + SETUPLEN
mov cx, #0x0002
mov dx, #0x0000
mov bx, #0x0200
int 0x13
jnc ok_load_setup !若读写完毕,转到ok_load_setup
! 若读写错误,则复位驱动器
! 使用BIOS int 0x13中断的0x00功能-磁盘系统复位
! ah = 0x00 选择中断功能
! dl = 驱动器, 0x00~0x7f -> 软盘, 0x80~0xff -> 硬盘
mov ax, #0x0000
mov dx, #0x0000
int 0x13
j load_setup !重新读写
ok_load_setup: ! setup加载完毕,在屏幕输出msg1中的字符串
! 读取显示屏幕第0页光标位置
! 使用BIOS int 0x10中断的 0x03功能-读取光标位置
! 输入: ah = 0x03 选择功能 bh = 页号
! 返回: ch = 光标开始行 cl = 光标结束行
! dh = 行 dl = 列
mov ah, #0x03
xor bh, bh
int 0x10
! 在当前光标位置显示msg1中的18个字符串
! 使用BIOS int 0x10中断的 0x13功能-显示字符串
! 输入: ah = 0x13 选择中断功能 al = 放置光标的方式及其规定属性
! al = 0x01 使用bl中的属性值,光标停留在字符转结尾处
! bh = 显示页面号 bl = 字符属性
! dh = 行号 dl = 列号
! es:bp 指向字符串起始处 cx = 字符数
! 属性: 0-2位 -> 文字RGB
! 3位 -> 高亮
! 4-6位 -> 背景RGB
! 7位 -> 闪烁
mov cx, #28 !显示18个字符
mov bx, #0x008a !第0页,闪烁高亮黑底绿字
mov bp, #msg1
mov ax, #0x1301
int 0x10
! 跳转到setup执行
jmpi 0, SETUPSEG
msg1: ! 共28个字符
.byte 13, 10 ! 换行+回车
.ascii "LYP's os is loading..."
.byte 13, 10, 13, 10
.org 510
boot_flag:
.word 0xAA55 !引导的必需
.text
endtext:
.data
enddata:
.bss
endbss:
setup.s
.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text
! 设置段地址常量
BOOTSEG = 0x07c0
INITSEG = 0x9000
SETUPSEG = 0x9020
entry _start
_start:
! 将cs赋给ds,es
mov ax, cs
mov ds, ax
mov es, ax
! 显示msg1中的字符串,以表示当前程序在setup部分
! 读光标
mov ah, #0x03
xor bh, bh
int 0x10
! 显示字符串
mov cx, #28
mov bp, #msg1
mov bx, #0x008a
mov ax, #0x1301
int 0x10
! 将ds置为0x9000
mov ax, #INITSEG
mov ds, ax
! 获取参数
! 获取光标位置,并存放在0x90000(2 byte)处
! 使用BIOS int 0x10中断的 ah = 0x03功能-读光标
! 输入:bh = 页号
! 返回:ch = 扫描开始线 cl = 扫描结束线
! dh = 行 dl = 列
mov ah, #0x03
xor bh, bh
int 0x10
mov [0], dx
! 获取扩展内存大小,并存放在0x90002(2 byte)处
! 使用BIOS int 0x15中断的 ah = 0x88功能-从0x100000处开始的扩展内存大小
! 返回:ax = 从0x100000处开始的扩展内存大小
mov ah, #0x88
int 0x15
mov [2], ax
! 获取硬盘信息,获取硬盘参数表的前16个byte
! 起始地址位为中断向量0x41的值
! 目的地址为0x9000:0x0080
! 设置源地址 ds:si
mov ax, #0x0000 ! 将ds中的数值清除
mov ds, ax
lds si, [4 * 0x41] !将中断向量的值放入ds:si
! 设置目标地址 es:di
mov ax, #INITSEG
mov es, ax
mov di, #0x0080
mov cx, #0x10 ! 传输16个字节
rep
movsb
! 重置ds,es
mov ax, #INITSEG
mov ds, ax
mov ax, #SETUPSEG
mov es, ax
! 显示参数
! 显示字符串 "CUrsor POS"
mov ah, #0x03
xor bh, bh
int 0x10
mov cx, #11
mov bx, #0x000a ! 黑底绿字高亮无闪
mov bp, #cur
mov ax, #0x1301
int 0x10
! 打印光标位置
mov ax, [0]
call print_hex
call print_nl
! 显示字符串 "Memory SIZE:"
mov ah, #0x03
xor bh, bh
int 0x10
mov cx, #12
mov bx, #0x000a
mov bp, #mem
mov ax, #0x1301
int 0x10
! 打印内存大小
mov ax, [2]
call print_hex
! 显示提示信息
mov ah, #0x03
xor bh, bh
int 0x10
mov cx, #25
mov bx, #0x000a
mov bp, #syl
mov ax, #0x1301
int 0x10
! 打印柱面数
mov ax, [0x80]
call print_hex
call print_nl
! 显示字符串 "Headers:"
mov ah, #0x03
xor bh, bh
int 0x10
mov cx, #8
mov bx, #0x000a
mov bp, #head
mov ax, #0x1301
int 0x10
! 打印磁头数
mov ax, [0x80 + 0x02]
call print_hex
call print_nl
! 显示字符串 "Sectors:"
mov ah, #0x03
xor bh, bh
int 0x10
mov cx, #8
mov bx, #0x000a
mov bp, #sect
mov ax, #0x1301
int 0x10
! 打印扇区数
mov ax, [0x80 + 0x0e]
call print_hex
call print_nl
! 死循环,使程序一直停留在setup中
l: jmp l
! 以16进制方式打印栈顶的16位数
! 使用BIOS int 0x10中断的 ah = 0x0e功能-显示一个字符
! 输入:al = 要显示字符的ASCII码
print_hex:
mov cx, #4 ! 打印4次,每次一个16进制位-4bit
mov dx, ax ! 将ax所指的值放入dx中
print_digit:
rol dx, #4 ! dx循环左移4位,每次只打印最低4位的内容
mov ax, #0xe0f ! ah = 中断功能, al = 显示的字符
and al, dl ! 取dl的低4位
add al, #0x30 ! 转化为ascii码,默认为数字
cmp al, #0x3a ! 判断是否是数字
jl outp ! 是数字
add al, #0x07 ! 不是数字,再加0x07,转为字母
outp:
int 0x10
loop print_digit
ret
!打印回车换行
print_nl:
mov ax, #0xe0d ! 回车
int 0x10
mov al, #0xa ! 换行
int 0x10
ret
msg1: ! 共25个字节
.byte 13, 10
.ascii "Now we are in setup..."
.byte 13, 10, 13, 10
cur: ! 光标位置
.ascii "Cursor POS:"
mem: ! 内存大小
.ascii "Memory SIZE:"
syl:
.ascii "KB"
.byte 13, 10, 13, 10
.ascii "HD Info"
.byte 13, 10
.ascii "Cylinders:"
head:
.ascii "Headers:"
sect:
.ascii "Secotors"
.text
endtext:
.data
enddata:
.bss
endbss: