概述
1.首先大概说一下KolibriOS这个系统引导的过程,然后是对其bootloder.asm文件的注释分析
引导程序被加载到0x7c00后,首先向屏幕输出“Starting system”字样,然后从fat12结构中获取需要的信息,将位于软盘镜像的根目录表(第20-第33扇区)加载至0x700处,之后寻找字符串为"KERNEL MNT"的表项(KERNEL.MNT为内核文件),找到后根据该表项的信息将内核文件(KERNEL.MNT)加载至0x10000处,最后利用retf指令跳转至0x10000处。
2.bootloder.asm源文件解读:
include "lang.inc"
;lang.inc的内容是对于语言的设定,这个是在编译时用luc脚本创建出来的,也可以自己设定
;lang="en"
;这里将语言设定为英语,主要是在开机时显示
lf
equ
0ah
cr
equ
0dh
pos_read_tmp
equ
0700h
;根目录表和fat被加载的地址
boot_program
equ
07c00h
;boot被加载到的地址
seg_read_kernel equ
01000h
;内核被加载到的段地址
jmp
start_program
;跳转到程序开始的地方
nop
include 'floppy1440.inc'
;在floppy1440.inc文件中包含的是1.44m软盘格式的设置,作者还提供了一些其他大小软盘的设置文件,可以根据需要修改
;include 'floppy2880.inc'
;include 'floppy1680.inc'
;include 'floppy1743.inc'
;下面是fat12文件系统的结构,这是floppy1440.inc文件的内容:
; BS_OEMName db 'KOLIBRI ' ; 启动扇区名称(必须为8字节)
; BPB_BytsPerSec dw 512
; 每个扇区(sector)大小(必须为512字节)
; BPB_SecPerClus db 1
; 簇(cluster)大小(必须为1个扇区)
; BPB_RsvdSecCnt dw 1
; FAT起始位置(一般为第一个扇区)
; BPB_NumFATs db 2
; FAT个数(必须为2)
; BPB_RootEntCnt dw 224
; 根目录文件数最大值(一般为224项)
; BPB_TotSec16 dw 2880
; 该磁盘大小(为2880扇区,即1440*1024/512)
; BPB_Media db 0f0h
; 磁盘类型(可移动介质,设置为0xf0)
; BPB_FATSz16 dw 9
; 每个FAT的扇区数(设为9扇区)
; BPB_SecPerTrk dw 18
; 一个磁道(track)的扇区数(最大为18)
; BPB_NumHeads dw 2
; 磁头数(软盘有两个面,所以设置2)
; BPB_HiddSec dd 0
; 隐藏扇区数,0
; BPB_TotSec32 dd 0
; 如果BPB_ToSec16是0,由这里记录扇区数
; BS_DrvNum db 0
; 中断13的驱动器号
; BS_Reserved db 0
; 未使用
; BS_BootSig db 29h
; 扩展引导标签29h
; BS_VolID dd 0
; 卷标序列号
; BS_VolLab db 'KOLIBRI
' ; 卷标(磁盘的名称)
; BS_FilSysType db 'FAT12
' ; 文件系统类型
start_program:
xor
ax, ax
;将ax清零
mov
ss, ax
;设置栈底寄存器ss指向0x0
mov
sp, boot_program
;设置栈顶寄存器sp指向0x7c00,即boot被夹在的地方
push
ss
pop
ds
;令数据段寄存器ds=0;
; print loading string
mov
si, loading+boot_program
;将loading的内容在屏幕上显示出来
loop_loading:
lodsb
;lodsb指令的作用是将ds:si指向的值存入al,此外还有lodsw指令,将ds:si指向的值存入ax
or
al, al
;or al,al用于判断al的值进行或运算,当为0时,置标志位zf=1,
jz
read_root_directory
;zf=1后,jz就会跳转到read_root_directory
mov
ah, 0eh
;调用bios中断号0x10,ah=0xe0,bx=为前景色,al=要显示的字符
mov
bx, 7
int
10h
jmp
loop_loading
read_root_directory:
push
ss
pop
es
mov
ax, word [BPB_FATSz16+boot_program]
;将每个fat表的扇区数9存入ax
xor
cx, cx
mov
cl, byte [BPB_NumFATs+boot_program]
;fat表数2存入cl
mul
cx
add
ax, word [BPB_RsvdSecCnt+boot_program]
;此时ax=18,再加上boot记录占用的扇区数1,ax=19
mov
word [FirstRootDirSecNum+boot_program], ax
; 将boot及文件表占用的长度存入FirstRootDirSecNum,其实就是根目录表的起始位置
mov
si, ax
; - count of sectors in RootDir
mov
bx, word [BPB_BytsPerSec+boot_program]
;每扇区字节数512存入bx
mov
cl, 5
; 将bx=512除以32(32=2^5),就是将ax的值右移5位,每条目录占用32字节,所以一个扇区(512 byte)有16条目录
shr
bx, cl
; bx = 每个扇区的目录条数 =16
mov
ax, word [BPB_RootEntCnt+boot_program]
;将根目录文件数最大值224存入ax
xor
dx, dx
div
bx
;被除数是ax=224,除数是bx=16,商为14,存入ax
mov
word [RootDirSecs+boot_program], ax
;ax=14,存入RootDirSecs指向的内存,也就是说根目录表的长度为14扇区,从19-32
; - data start
add
si, ax
; si=19+14=33,结果指向的是文件数据的起始扇区(即33*512=0x4200)
mov
word [data_start+boot_program], si
;将位置存入data_start
; reading root directory
; al=count root dir sectrors !!!! TODO: al, max 255 sectors !!!!
mov
ah, 2
;ax=14,现在令ah=2,ax=0x020e
push
ax
mov
ax, word [FirstRootDirSecNum+boot_program]
;[FirstRootDirSecNum]=19,存入ax,作为conv_abs_to_THS的参数,ax=19
call
conv_abs_to_THS
; 将扇区数(ax)转换为bios的参数(磁道号:磁头号:扇区),ch=00,cl=2,dh=,1,dl=0
pop
ax
;ax=0x20e
mov
bx, pos_read_tmp
; pos_read_tmp=700h,es:bx=0x0:0x700=0x700
call
read_sector
;将根目录表的14个扇区读到0x700处
mov
si, bx
;bx=0x700
mov
ax, [RootDirSecs+boot_program]
;将根目录表长度14存入ax
mul
word [BPB_BytsPerSec+boot_program]
;[BPB_BytsPerSec]=512(扇区大小512字节),ax=ax*512=14*512=0x1c00
add
ax, si
; ax=ax+si=0x1c00+0x700=0x2300,ax指向根目录表的结束位置
; 在根目录表中寻找内核文件
loop_find_dir_entry:
push
si
mov
cx, 11
mov
di, kernel_name+boot_program
;[kernel_name]='KERNEL
MNT'
rep cmpsb
;cmpsb是字符串比较指令,将es:si指向的数据与es:di指向的数据进行比较,rep用来重复后面的cmpsb指令,每进行一次,cx-1,直到cx=0
;si=0x700
pop
si
je
found_kernel_file
;如果找到'KERNEL
MNT'则跳转到found_kernel_file
add
si, 32
; 否则令si+32,即偏移到下一条目录
cmp
si, ax
; 判断是否到目录表文件尾。当si>=ax,cf=0,jb的跳转条件是cf=1,所以会退出循环
jb
loop_find_dir_entry
;下面是提示错误信息
file_error_message:
mov
si, error_message+boot_program
loop_error_message:
lodsb
or
al, al
jz
freeze_pc
mov
ah, 0eh
mov
bx, 7
int
10h
jmp
loop_error_message
freeze_pc:
jmp
$
; 打印错误字符串后,陷入死循环
; === KERNEL FOUND. LOADING... ===
;kernel内核文件
found_kernel_file:
mov
bp, [si+01ah]
;si=700h,si+01ah=71ah,bp=[71ah],0x1a=26
;在目录表项中,偏移量=0x1a的位置是内核文件在磁盘中的储存位置,(单位是簇,簇的值从2开始,就是说
;如果这个地方等于2,说明内核文件在文件数据区的第一个簇(33*512=0x4200处),在这里一个簇和一个扇区长度相等)
; <diamond>
mov
[cluster1st+boot_program], bp
; [cluster1st]指向文件内核的第一个簇
; <diamond>
; reading first FAT table
mov
ax, word [BPB_RsvdSecCnt+boot_program]
; fat1的位置存入ax,ax=1
call
conv_abs_to_THS
; 返回ch=0,cl=2,ax=0,bh=0,bl=0
mov
bx, pos_read_tmp
; bx=700
mov
ah, 2
; ah=2 (read)
mov
al, byte [BPB_FATSz16+boot_program]
; al=9=一个根目录表占的扇区数
call
read_sector
; 把第一个fat表读到0x700
jc
file_error_message
; 出错则跳到错误提示
mov
ax, seg_read_kernel
;seg_read_kernel=1000h,ax=1000h
mov
es, ax
;es=1000h
xor
bx, bx
; es:bx = 1000h:0000h
; reading kernel file
loop_obtains_kernel_data:
; read one cluster of file
call
obtain_cluster
;将内核文件的第一个扇区读取到es:bx = 1000h:0000h=0x10000
jc
file_error_message
; 当cf=1,说明读取失败
; add one cluster length to segment:offset
push
bx
mov
bx, es
;bx=1000h
mov
ax, word [BPB_BytsPerSec+boot_program]
; ax=512=一个扇区的大小=512字节
movsx
cx, byte [BPB_SecPerClus+boot_program]
; cx=1=一个簇的大小=1扇区
mul
cx
; ax=cx*cx=512*1=512
shr
ax, 4
; ax=ax/16=32=20h
add
bx, ax
;bx=bx+ax=1000h+20h=1020h
mov
es, bx
;es=1020h
pop
bx
;bx=0
mov
di, bp
;di=bp=2=内核文件的簇号
shr
di, 1
;di=1,cf=0
pushf
add
di, bp
; di = bp * 1.5==3
add
di, pos_read_tmp
; di=700h+di=700h+3=703h
mov
ax, [di]
; 将fat1的第4-5个字节读入,fat表的前3个字节没用
popf
jc
move_4_right
;有进位跳转到move_4_right
and
ax, 0fffh
;清掉ax前4位
jmp
verify_end_sector
move_4_right:
mov
cl, 4
shr
ax, cl
verify_end_sector:
cmp
ax, 0ff8h
;当值大于等于ff8h时,说明这是这个文件的最后一个簇,ff7h代表该簇损坏
jae
execute_kernel
;判断这是内核文件的最后一个簇时,跳转到execute_kernel
mov
bp, ax
;簇号是连续的,如果一个文件占了4个簇,第一个簇号为2,则它对应的fat表项为03h 40h 00h f5h f8h(12位一项,每项指向该文件下一个正确的簇)
jmp
loop_obtains_kernel_data
;继续读取内核文件
execute_kernel:
; <diamond>
mov
ax, 'KL'
;ax='KL'
push
0
pop
ds
;ds=0(数据段寄存器)
mov
si, loader_block+boot_program
; </diamond>
push
word seg_read_kernel
;seg_read_kernel=1000h
push
word 0
retf
; jmp far 1000:0000
;retf相当于pop ip ;pop cs;利用retf实现段间跳转
;------------------------------------------
; loading cluster from file to es:bx
obtain_cluster:
; bp=簇号(假设kernel文件的位置在文件数据区第一个扇区,则bp=2)
; cf = 0 -> 读取成功
; cf = 1 -> 读取失败
; print one dot
push
bx
mov
ax, 0e2eh
; ah=0eh (teletype), al='.'
xor
bh, bh
int
10h
;功能描述:在Teletype模式下显示字符
;入口参数:AH=0EH
;AL=字符
;BH=页码
;BL=前景色(图形模式)
;出口参数:无
pop
bx
writesec:
;将簇号变成扇区号
mov
ax, bp
; ax=bp=2
sub
ax, 2
;ax=0
xor
dx, dx
;dx=0
mov
dl, byte [BPB_SecPerClus+boot_program]
;dl=簇的大小=1(扇区)
mul
dx
;ax=0
add
ax, word [data_start+boot_program]
;ax=ax+文件数据区起始位置=0x4200
call
conv_abs_to_THS
; 将位置转换为bios可以处理的参数
patchhere:
mov
ah, 2
; ah=2 (read)
mov
al, byte [BPB_SecPerClus+boot_program]
; 读取一个扇区
call
read_sector
retn
;retn是段内跳转,近返回
;------------------------------------------
;------------------------------------------
; read sector from disk
read_sector:
push
bp
mov
bp, 20
;尝试20次,因为软盘读取时可能会发生错误,所以会多尝试几次
newread:
dec
bp
;dec指令将bp减1,
jz
file_error_message
;等于0则跳转到file_error_message
push
ax bx cx dx
;ax=0x020e,dx=0x0100,cx=0x0002,bx=0x0700;es=0;ch=00,cl=2,dh=,1,dl=0
int
13h
;参数:ah=2;作用:读扇区
;al=扇区数
;ch=柱面
;cl=扇区
;dh=磁头
;dl=驱动器,00H~7FH:软盘;80H~0FFH:硬盘
;es:bx=缓冲区的地址
;出口参数:CF=0——操作成功,AH=00H,AL=传输的扇区数,AH=状态代码,其定义如下:
;00H — 无错
01H — 非法命令
;02H — 地址目标未发现
03H — 磁盘写保护(软盘)
;04H — 扇区未发现
05H — 复位失败(硬盘)
;06H — 软盘取出(软盘)
07H — 错误的参数表(硬盘)
;08H — DMA越界(软盘)
09H — DMA超过64K界限
;0AH — 错误的扇区标志(硬盘)
0BH — 错误的磁道标志(硬盘)
;0CH — 介质类型未发现(软盘)
0DH — 格式化时非法扇区号(硬盘)
;0EH — 控制数据地址目标被发现(硬盘)
0FH — DMA仲裁越界(硬盘)
;10H — 不正确的CRC或ECC编码
11H — ECC校正数据错(硬盘)
pop
dx cx bx ax
jc
newread
pop
bp
retn
;------------------------------------------
; convert abs. sector number (AX) to BIOS T:H:S
; sector number = (abs.sector%BPB_SecPerTrk)+1
; pre.track number = (abs.sector/BPB_SecPerTrk)
; head number = pre.track number%BPB_NumHeads
; track number = pre.track number/BPB_NumHeads
; Return: cl - sector number
;
ch - track number
;
dl - drive number (0 = a:)
;
dh - head number
conv_abs_to_THS:
push
bx
;传入ax=19
mov
bx, word [BPB_SecPerTrk+boot_program]
;将磁道扇区数18存入bx,bx=18
xor
dx, dx
div
bx
;令ax/bx,19/18=1-----1,ax=1,dx=1
inc
dx
;dx=2;
mov
cl, dl
; cl=2 =扇区数
mov
bx, word [BPB_NumHeads+boot_program]
;将磁头数2存入bx
xor
dx, dx
div
bx
;1/2=0-----1,ax=0,dx=1
; !!!!!!! ax = 磁道号, dx = 磁头号
mov
ch, al
; ch=磁道号=0
xchg
dh, dl
; xchg交换两个寄存器的值,交换前dh=0,dl=1,交换后:dh=1,dl=0
mov
dl, 0
; dl=0 (drive 0 (a:)),即bios中断调用中驱动器0
pop
bx
retn
;------------------------------------------
if lang eq sp
loading
db
cr,lf,'Iniciando el sistema ',00h
else
loading
db
cr,lf,'Starting system ',00h
end if
error_message
db
13,10
kernel_name
db
'KERNEL
MNT ?',cr,lf,00h
FirstRootDirSecNum
dw
?
RootDirSecs
dw
?
data_start
dw
?
; <diamond>
write1st:
push
cs
pop
ds
mov
byte [patchhere+1+boot_program], 3
; change ah=2 to ah=3
mov
bp, [cluster1st+boot_program]
push
1000h
pop
es
xor
bx, bx
call
writesec
mov
byte [patchhere+1+boot_program], 2
; change back ah=3 to ah=2
retf
cluster1st
dw
?
loader_block:
db
1
dw
0
dw
write1st+boot_program
dw
0
; <diamond>
times
0x1fe-$ db 00h
db
55h,0aah
;boot signature
关于fat12文件系统的参考(侵删):https://yq.aliyun.com/wenji/275247
最后
以上就是呆萌帽子为你收集整理的对于KolibriOS系统引导部分的详细解读的全部内容,希望文章能够帮你解决对于KolibriOS系统引导部分的详细解读所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复