在第一篇的文章中有学习到,引导程序的作用实质是一个桥梁的作用。虽然它本身空间有限,能实现的功能也有限,然而它却是被BIOS看重的继承者,将大权传给引导程序手上。现在为了实现“大业”,识时务引导程序现在必须找一个文武双全的人将大权传与它,而这个人就是操作系统,当然准确地说应该是操作系统内核。
具体实现的原理是:
1.BIOS将引导调入0X7C00处;
2.BIOS隐退,引导程序执行;
3.引导程序做完自己的工作后,像BIOS寻找引导程序一样,将指定位置的程序读入内存。所以,引导程序需要知道内核在磁盘的什么位置,这样才能载入内存;
4.引导程序隐退,内核程序执行;
而上篇的代码都是实现了能让BIOS引导,即BIOS隐退时将大权给了引导程序,并未实现引导程序能够移交大权于内核。下面我就试着写并解读一下:
下面的文件是boot.asm引导文件
;; 文件:boot.asm
;; 工具:UltraEdit14.12编辑,Nasm2.02汇编
;; 创建日期:2009/02/2 HouRj
;; 作用:接管BIOS,引导kernel执行
;; 备注:没有文件系统,1.44M 512bits/80sec 软盘启动,
;; [email protected]
;;================================================
org 0x7c00
init:
mov ax,cs ;设置数据段
mov ds,ax
mov es,ax
jmp start
;定义意义串
welcome db 'Welcome numOS,the boot is running',13,10,0
print:
lodsb ;从SI地址处加载一个字节到al
or al,al ;检查要显示的串是否结束,因为如果结束,si给加载0x0进入al
jz pe ;判断是否相等即zf=0,则结束,跳至pe结束输出
mov ah,0x0e ;10号中断初始化,功能0EH 在Teletype模式下显示字符
mov bx,0x0002 ;显示方式
int 0x10 ;打开中断
jmp print ;中断响应,跳至print输出
pe:
ret
showboot: ;欢迎模块
mov si,welcome
call print
ret
start:
call showboot ;调用欢迎模块
jmp dkernel ;执行对内核的加载模块
dkernel:
reset:
mov ax,0 ;13号中断,00h为磁盘系统复位
mov dl,0 ;dl为驱动器,00h-7Fh是软盘,80-0FF是硬盘
int 13h ;中断调用
jc reset ;若复位成功,则CF=0,若CF !=0,则复位不成功,重新复位
loop:
mov ax,0x1000 ;0x1000为代码载入的段地址,自己可按照一定规则自己定义,
mov es,ax ;移送ES
mov bx,0x0 ;0为在ES(1000h)的段地址下的偏移地址
mov ah, 0x02 ;13号中断,02h为读磁盘数据,BIOS 读取扇区
mov al, 0x01 ;读取的空间为一个扇区,事先估计或规定好的
mov ch, 0h ;起始磁道 0
mov cl, 2h ;起始扇区 第二扇区
mov dh, 0h ;磁头号 1
mov dl, 0h ;dl为驱动器,00h-7Fh是软盘,80-0FF是硬盘
int 13h ;调用中断
jc loop ;同理,若CF位!=0,则失败,重新读写
jmp 1000h:0000h ;跳转到1000H:0000地址处执行,此时,cs:1000h ip:0000h
times 510-($-$$) db 0
dw 0xaa55
;;================================================
下面是汇编语言的内核文件(当然,简单内核,试验目的是看怎么加载)
;; 文件:kernel.asm
;; 工具:UltraEdit14.12编辑,Nasm2.02汇编
;; 创建日期:2009/02/2 HouRj
;; 作用:作为简单的操作系统核心,被boot引导
;; 备注:没有文件系统,1.44M 512bits/80sec 软盘启动,
;; [email protected]
;;================================================
org 0x0 ;从本段的偏移地址为0,即段首开始载入
init:
mov ax,cs ;设置数据段
mov ds,ax
mov es,ax
jmp start ;跳到start处执行
;定义意义串
welcome db 'OK,the kernel is running......',13,10,0
type db 'please input the char',13,10,0
print: ;打印字符的功能块
lodsb ;boot.asm 有详细解释
or al,al
jz pe
mov ah,0x0e
mov bx,0x0002
int 10h
jmp print
pe: ;显示结束,返回
ret
showkernel: ;欢迎功能块
mov si,welcome ;显示欢迎信息和提示信息
call print
mov si,type
call print
ret
echo: ;回显功能块
mov ah,0 ;调用键盘中断,等待按键,上篇文章有解释
int 0x16
mov ah,0x0e ;回显到屏幕
mov bx,0x0002
int 0x10
jmp echo
start: ;开始,主模块
call showkernel ;调用欢迎模块
call echo ;调用回显模块
times 510-($-$$) db 0
dw 0xaa55
;;================================================
详细的解答我都尽力下到代码里了,注释在详细不过了。下面是汇编和执行步骤:
1.进入cmd,cd到文件保存的目录
2.nasm boot.asm -o boot.img ;汇编boot.img二进制文件
3.nasm kernel.asm -o kernel.img ;汇编kernel.img二进制文件
4.parycopy kernel.img 0 200 boot.img 200 ;将kernel.img 无缝接到boot.img文件后面
5.将生成的boot.img载入到虚拟机,启动就可以看到效果
注:第4个步骤,我查看了一下网上的说法,说最后的200是200个字节,并且,好多文章都这样说。我无意看一片E文时发现,原来前面0 200是范围,后面200是拷入到boot.img的起始位置,并不是大小。我特地用UltraEdit打开了boot.img二进制文件,看到,原来512K大小的地址空间为0000h-01FFh,而下一个地址,也就是kernel.img无缝连在boot.img后面的起始地址为0200h。