上一篇:http://t.csdn.cn/XfUKt 讲到这个启动设备的第一个扇区:引导扇区。那么引导扇区的代码长什么样子?
这里得看引导扇区代码源文件bootsect.s(.s后缀文件为用汇编语言编写的源代码文件)。
另外为什么是用汇编语言写的?
为什么用底层的汇编语言,而不用对应上更高级的C语言,因为如果是C语言,它是编译型语言,要经过编译。而编译的过程中可能就会出现一些我们无法控制的事情。比如int i,我们是无法用C语言去控制i存放的地址哪个位置。而汇编可以。汇编的指令都会变成真正的机器指令。
bootsect.s形成的机器指令最后就会实现在引导扇区上。
BOOTSEG: boot-sector的初始地址(通电后的初始地址) |
INITSEG:bootsect把自身搬运到0x90000 |
SETUPSEG:setup模块被加载到 0x90200 |
.globl begtext,begdata,begbss,endtext,enddata,endbss
// .globl用于定义随后的标识符是外部或者全局的,全局标识符,供ld86链使用
.text //文本段
begtext:
.data //数据段
begdata:
.bss //未初始化数据段
begbss:
// BOOTSEG = 0x07c0 // 0x就是16进制
// INITSEG = INITSEC
// SETUPSEG = 0x9020
entry start // 关键字entry告诉链接器“程序入口”
start:
mov ax, #BOOTSEG mov ds, ax // mov 变量A 变量B 将变量B的值赋给变量A
mov ax, #INITSEC mov es, ax // ds 7c0 es 9000
mov cx, # 256 // 循环控制字节,512字节
sub si, si sub di,di // 这里的值都等于0
// sub是减的意思,这里自己减自己,结果都为0 si清零,ds:si即0x07c00 di清零,es:si即0x90000
rep movw // 循环直到cx==0,将ds:si复制到es:di,0x07c0:0x0000移动256位到0x9000 :0x0000
jmpi go, INITSEC // 跳转到go标志处(一个标号地址),jmp是跳转,加i,jmpi是间接跳转,go是后面的一个标志,INITSEC即INITSEC【CPU就会跳到 0x9000:go】,INITSEG 是段地址,go 是偏移地址。
0x13是BIOS读磁盘扇区的中断:ah=0x02-读磁盘,al=扇区数量(SETUPLEN=4),ch=柱面号,cl=开始扇区,dh=磁头号,dl=驱动器号,es:bx=内存地址。
go: mov ax,cs // cs就是INITSEC=0x9000
mov ds,ax
mov es,ax
mov ss,ax // cs = ds = es = ss = 0x9000
mov sp,#0xFF00 // es:sp = 0x9000:0xff00
load_setup:
mov dx,#0x0000 // 动器号(DL)0,磁头号(DH)0
mov cx,#0x0002 // 起始扇区号2(从这里开始读), 磁道号0
mov bx,#0x0200 // 偏移地址0x200
mov ax,#0x0200+SETUPLEN // AH=0x02,al(SETUPLEN=4) ,这里就是从第二个扇区开始读4个扇区
int 0x13 // BIOS中断
jnc ok_load_setup // CF=0操作成功,CF=1操作失败。若CF为0则重载
mov dx,#0x0000 // 需要复位的驱动器号=DL=0
mov ax,#0x0000 // 复位
int 0x13 // 复位磁盘
j load_setup // 重读
读入setup模块后:ok_load_setup
SYSSEG = 0x1000
ok_load_setup: // 载入setup模块
mov dl,#0x00 // 驱动器号为0,说明是软盘
mov ax,#0x0800 // AH=8获取磁盘参数
int 0x13
mov ch,#0x00
mov sectors,cx
mov ah,#0x03
xor bh,bh
int 0x10 // 读光标
mov cx,#24 // cx为24,表示输出24个字符
mov bx,#0x0007 // 7是显示属性
mov bp,#msg1
mov ax,#0x1301
int 0x10 // 显示字符
mov ax,#SYSSEG // 0X1000
mov es,ax // es=0x1000
call read_it // 读入system模块
jmpi 0,SETUPSEG // 跳入0x9020:0x0000执行setup.s,cs跳四位为0x9020
比如
msg1: .byte 13,10
.ascii "Loading system..."
.byte 13,10,13,10
我们也可以改的,把这里的Loading system...比如改成My OS,那么就需要把cx的输出字符数调成我们这里要输出的字符数,而这个loading system...是cx为24个字符,按ASCII的规则数一下即可。其他就没有需要改的。
读入system模块
read_it(system模块可能很大,需跨磁道,ENDSEG=SYSSEG+SYSSIZE,其中SYSSIZE=0x8000该变量可在编译操作系统时,根据image大小设定)
read_it: mov ax,es
cmp ax,#ENDSEG
jb ok1_read
ret
ok1_read:
mov ax,sectors
sub ax,sread // sread是当前磁道已读扇区,ax是未读扇区
call read_track // 读磁道
引导扇区末尾
BIOS用以识别引导扇区
.org 510
.word 0xAA55 // 扇区的最后两个字节
这里就跳回setup执行最后的语句:jmpi 0,SETUPSEG(IP只成0,cs=SETUPSEG 0x9000,然后cs跳四位为0x9020,最后结果:0x9020:0x0000,控制器交给setup.s)
这里bootsect.s就执行结束了,接下来就是setup.s。
学习参考:
https://www.bbsmax.com/A/Gkz1qNj6zR/
https://www.xiaolincoding.com/
【哈工大】操作系统 李治军