之前的IPL(Intial Program Loader)并没有装载程序,接下来通过之前的IPL装载程序,读入磁盘内容,完成真正的IPL。
; hello-os
; TAB=4
CYLS EQU 10 ; 相当于c语言中的#define,声明常数,CYLS = 10
ORG 0x7c00 ; 指明程序的装载地址
; 以下这段是标准FAT32格式软盘专用的代码
JMP entry
DB 0x90
DB "HELLOIPL" ; freeparam 启动区的名称可以是任意的字符串(8字节)
DW 512 ; 每个扇区(sector)的大小(必须为512字节)
DB 1 ; 簇(cluster)的大小(必须为1个扇区)
DW 1 ; FAT的起始位置(一般从第一个扇区开始)
DB 2 ; FAT的个数(必须为2)
DW 224 ; 根目录的大小(一般设成224项)
DW 2880 ; 该磁盘的大小(必须是2880扇区)
DB 0xf0 ; 磁盘的种类(必须是0xf0)
DW 9 ; FAT的长度(必须是9扇区)
DW 18 ; 1个磁道(track)有几个扇区(必须是18)
DW 2 ; 磁头数(必须是2)
DD 0 ; 不使用分区,必须是0
DD 2880 ; 重写一次磁盘大小
DB 0,0,0x29 ; 意义不明,固定
DD 0xffffffff ; (可能是)卷标号码
DB "HELLO-OS " ; freeparam 磁盘的名称(11字节)
DB "FAT12 " ; 磁盘格式名称(8字节)
RESB 18 ; 先空出18字节
; 程序核心
entry:
MOV AX,0 ; 初始化寄存器
MOV SS,AX
MOV SP,0x7c00 ; 0x7c00到0x7dff为启动区内容的装载地址
MOV DS,AX ; DS必须指定为0,因为它是默认的段寄存器,地址的值会加上这个值的16倍
; 读磁盘,读入第二个扇区 C0-H0-S1(柱面0,磁头0,扇区1的缩写)第一个扇区存储IPL
MOV AX,0x0820
MOV ES,AX ; [ES:BX] = ES * 16 + BX 中的数据被加载到0x8200到0x83ff的地方,0x8200到0x81ff为读入启动区后放置的地方
MOV CH,0 ; 柱面0,CH代表柱面号
MOV DH,0 ; 磁头0,DH代表磁头号,0表示正面
MOV CL,2 ; 扇区2,CL代表扇区号
readloop:
MOV SI,0 ; 记录失败次数的寄存器
;如果装载不成功,重试最多5次
retry:
MOV AH,0x02 ; AH=0x02 : 读盘,AH=0x03:写盘
MOV AL,1 ; 1个扇区
MOV BX,0
MOV DL,0x00 ; A驱动器,DL代表驱动器号
INT 0x13 ; 调用磁盘BIOS
JNC next ; 没出错时跳转到next,进位标志为0时跳转
ADD SI,1 ; SI加1
CMP SI,5 ; 比较SI与5
JAE error ; SI >= 5时,跳转到 error
MOV AH,0x00 ; 连同下面两句是系统复位,复位软盘状态
MOV DL,0x00 ; A驱动器
INT 0x13 ; 重置驱动器
JMP retry
;读到第18扇区
next:
MOV AX,ES ; 把内存地址后移0x200(0x200 = 512)
ADD AX,0x0020 ; ADD AX, 512 / 16 ,下一个扇区地址
MOV ES,AX ; 因为没有ADD ES,0x020 指令,所以这里稍微绕个弯,ES指定读入地址
ADD CL,1 ; CL加1,CL是扇区号
CMP CL,18 ; 比较CL与18
JBE readloop ; 如果CL<=18,跳转至readloop
;至此已读完一个柱面,接下来再读入9个柱面(1个柱面18个扇区)
MOV CL,1 ; 扇区号
ADD DH,1 ; 磁头号,1表示反面,C0-H0-S18扇区的下一扇区是磁盘反面的C0-H1-S1
CMP DH,2
JB readloop ; 如果DH < 2,则跳转到readloop
MOV DH,0
ADD CH,1 ; 柱面号加1
CMP CH,CYLS
JB readloop ; 如果CH < CYLS,则跳转到readloop,CYLS为声明的常数,为10
; 读完所有数据后,调到0x8200位置,即haribote.sys中的指令
MOV [0x0ff0],CH ; 将CYLS的值写到内存地址0x0ff0中。
JMP 0xc200 ; naskfunc.nas开始执行的地方
fin:
HLT ; 让CPU停止;等待指令
JMP fin ; 无限循环
error:
MOV SI,msg
putloop:
MOV AL,[SI]
ADD SI,1 ; 给SI加1
CMP AL,0
JE fin
MOV AH,0x0e ; 显示一个文字
MOV BX,15 ; 指定字符颜色
INT 0x10 ; 调用显卡BIOS
JMP putloop
msg: ; 信息显示部分
DB 0x0a, 0x0a ; 换行2次
DB "load, error!" ; freeparam
DB 0x0a ; 换行
DB 0
RESB 0x7dfe-$ ; 填写0x00,直到0x001fe,是为了保证第510字节(即第0x1fe字节)开始的地方是55AA,
DB 0x55, 0xaa ;如果启动区最后2字节不是0x55aa,计算机会认为盘上没有需要启动的程序。
现在已经把软盘中的10 * 2 * 18 * 512 = 180KB 装载入内存0x8200到0x34fff的地方了。至此,已完成启动区的制作。
双击!cons_nt.bat,输入make run后,出现如下画面:
着手开发操作系统
编写一个OS程序,保存为haribote.nas
;haribote-os
;TAB = 4
ORG 0xc200 ;程序要装载的地方
MOV AL,0x13 ;VGA显卡,32x00x8位颜色
MOV AH,0x00 ;设置AH=0x00,调用显卡BIOS函数,就可以切换显示模式了,AL中设置模式
INT 0x10
fin:
HLT ; 使CPU基本处于睡眠状态
JMP fin
用nask编译,输出成二进制文件haribote.sys:
双击!cons_nt.bat,输入:..\z_tools\nask.exe haribote.nas haribote.sys
接下来将这个文件保存到磁盘映像hribote.img中。
Makefile中的内容更新:
TOOLPATH = ../z_tools/
MAKE = $(TOOLPATH)make.exe -r
NASK = $(TOOLPATH)nask.exe
EDIMG = $(TOOLPATH)edimg.exe
IMGTOL = $(TOOLPATH)imgtol.com
COPY = copy
DEL = del
default :
$(MAKE) img
#制作文件ipl.bin
ipl.bin : ipl.nas Makefile #要制作文件ipl.bin,需先检查ipl.nas和Makefile这两个文件是否准备好
$(NASK) ipl.nas ipl.bin ipl.lst
haribote.sys : haribote.nas Makefile
$(NASK) haribote.nas haribote.sys haribote.lst
haribote.img : ipl.bin haribote.sys Makefile
$(EDIMG) imgin:../z_tools/fdimg0at.tek \
wbinimg src:ipl.bin len:512 from:0 to:0 \
copy from:haribote.sys to:@: \
imgout:haribote.img
#生成 helloos.img
img :
$(MAKE) haribote.img
run :
$(MAKE) img
$(COPY) haribote.img ..\z_tools\qemu\fdimage0.bin
$(MAKE) -C ../z_tools/qemu
install :
$(MAKE) img
$(IMGTOL) w a: haribote.img
#删除最终成果以外的所有中间生成文件,将硬盘清理干净
clean :
-$(DEL) ipl.bin
-$(DEL) ipl.lst
-$(DEL) haribote.sys
-$(DEL) haribote.lst
-del ipl.lst
#把源程序以外的文件全部删干净
src_only :
$(MAKE) clean
-$(DEL) haribote.img
双击!cons_nt.bat,输入make img后,生成haribote.img,此时hanbote.sys已经写入hairbote.img中
说明:
文件名写在0x002600以后的地方
文件的内容写在0x004200以后的地方
从启动区执行操作系统
目的:执行磁盘映像上位于0x4200号地址的程序。
现况:从启动区开始,磁盘上的内容转载到了内存0x8000号地址。
结果:0x4200处的内容位于内存0x8000+0x4200=0xc200处
操作:haribote.nas中加入ORG 0xc200
ipl.nas中处理的最后加JMP 0xc200
输入make run后的画面:
32位模式前期准备
修改harbote.nas
;haribote-os
;TAB = 4
; 有关BOOT_INFO
CYLS EQU 0x0ff0 ;设定启动区
LEDS EQU 0x0ff1
VMODE EQU 0x0ff2 ;有关颜色数目的信息
SCRNX EQU 0x0ff4 ;分辨率的x
SCRNY EQU 0x0ff6 ;分辨率的y
VRAM EQU 0x0ff8 ;图像缓冲区的开始地址,VRAM指显卡内存
ORG 0xc200 ;程序要装载的地方
MOV AL,0x13 ;VGA显卡,32x00x8位颜色
MOV AH,0x00 ;设置AH=0x00,调用显卡BIOS函数,就可以切换显示模式了,AL中设置模式
INT 0x10
MOV BYTE [VMODE],8 ; 记录画面模式
MOV WORD [SCRNX],320
MOV WORD [SCRNY],200
MOV DWORD [VRAM],0x000a0000 ;这种画面模式下"VRAM是0xa0000到0xaffff的64KB
; 用BIOS取得键盘上各种LED指示灯的状态
MOV AH,0x02
INT 0x16 ; keyboard BIOS
MOV [LEDS],AL
fin:
HLT
JMP fin
开始导入C语言
haribote.nas改成了asmhead.nas,为调用C语言,添加了100行左右的汇编代码。它的功能是承接IPL程序,调用bootpack.c中的主函数。这部分后面再讲。
最重要的OS核心部分为C语言程序bootpack.c,从现在开始,将越来越多的采用C语言编写OS。
bootpack.c :
//告诉C编译器,有一个函数在别的文件里
void io_hlt(void); //是函数声明,意思是函数在别的文件中,自己找一下吧
void HariMain(void) //程序从此处开始运行,函数名不能改
{
fin:
io_hlt(); //执行naskfunc.nas里的_io_hlt
goto fin;
}
接下来用汇编写一个函数naskfunc.nas,因为C语言无法实现HLT,所以需要写成汇编语言,供C语言函数调用:
; naskfunc
; TAB=4
[FORMAT "WCOFF"] ; 制作目标文件的模式,因为该函数之后要与bootpack.obj链接,所以也需要编译成目标文件。
[BITS 32] ; 制作32位模式用的机器语言
; 制作目标文件的信息
[FILE "naskfunc.nas"] ; 源文件名信息
GLOBAL _io_hlt ; 程序中包含的函数名,必须在函数前加"_",否则不能很好地与C语言函数链接
;需要链接的函数名,都需要用GLOBAL声明
; 以下是实际的函数
[SECTION .text] ; 目标文件中写了这些后再写程序
_io_hlt: ; 先写一个用GLOBAL声明的函数名相同的编号,表示函数void io_hlt(void)
HLT ; 使CPU基本处于睡眠状态
RET ; 相当于return
Makefile中的内容也变化了,将ipl.nas、asmhead.nas、bootpack.c、naskfunc.nas组合成为一个OS程序。
TOOLPATH = ../z_tools/
INCPATH = ../z_tools/haribote/
MAKE = $(TOOLPATH)make.exe -r
NASK = $(TOOLPATH)nask.exe
CC1 = $(TOOLPATH)cc1.exe -I$(INCPATH) -Os -Wall -quiet
GAS2NASK = $(TOOLPATH)gas2nask.exe -a
OBJ2BIM = $(TOOLPATH)obj2bim.exe
BIM2HRB = $(TOOLPATH)bim2hrb.exe
RULEFILE = $(TOOLPATH)haribote/haribote.rul
EDIMG = $(TOOLPATH)edimg.exe
IMGTOL = $(TOOLPATH)imgtol.com
COPY = copy
DEL = del
default :
$(MAKE) img
# 启动区
ipl.bin : ipl.nas Makefile
$(NASK) ipl.nas ipl.bin ipl.lst
asmhead.bin : asmhead.nas Makefile
$(NASK) asmhead.nas asmhead.bin asmhead.lst
bootpack.gas : bootpack.c Makefile
$(CC1) -o bootpack.gas bootpack.c
bootpack.nas : bootpack.gas Makefile
$(GAS2NASK) bootpack.gas bootpack.nas
bootpack.obj : bootpack.nas Makefile
$(NASK) bootpack.nas bootpack.obj bootpack.lst
naskfunc.obj : naskfunc.nas Makefile
$(NASK) naskfunc.nas naskfunc.obj naskfunc.lst
bootpack.bim : bootpack.obj naskfunc.obj Makefile
$(OBJ2BIM) @$(RULEFILE) out:bootpack.bim stack:3136k map:bootpack.map \
bootpack.obj naskfunc.obj
# 3MB+64KB=3136KB
bootpack.hrb : bootpack.bim Makefile
$(BIM2HRB) bootpack.bim bootpack.hrb 0
haribote.sys : asmhead.bin bootpack.hrb Makefile
copy /B asmhead.bin+bootpack.hrb haribote.sys
haribote.img : ipl.bin haribote.sys Makefile
$(EDIMG) imgin:../z_tools/fdimg0at.tek \
wbinimg src:ipl.bin len:512 from:0 to:0 \
copy from:haribote.sys to:@: \
imgout:haribote.img
#生成 helloos.img
img :
$(MAKE) haribote.img
run :
$(MAKE) img
$(COPY) haribote.img ..\z_tools\qemu\fdimage0.bin
$(MAKE) -C ../z_tools/qemu
install :
$(MAKE) img
$(IMGTOL) w a: haribote.img
#删除最终成果以外的所有中间生成文件,将硬盘清理干净
clean :
-$(DEL) *.bin
-$(DEL) *.lst
-$(DEL) *.gas
-$(DEL) *.obj
-$(DEL) bootpack.nas
-$(DEL) bootpack.map
-$(DEL) bootpack.bim
-$(DEL) bootpack.hrb
-$(DEL) haribote.sys
#把源程序以外的文件全部删干净
src_only :
$(MAKE) clean
-$(DEL) haribote.img
bootpack.c变成机器语言的过程:
使用cc1.exe从bootpack.c生成gas汇编语言bootpack.gas
使用gas2nask,将gas汇编语言bootpack.gas变成nask能翻译的汇编语言bootpack.nas
使用nask.exe将bootpack.nas生成目标文件bootpack.obj(机器语言)
使用obj2bim.exe将bootpack.obj生成二进制映像文件bootpack.bim,这一步是因为C语言不能编写所有的程序,有一部分用汇编来写,然后链接到C语言程序上
使用bim2hrb.exe将bootpack.obj生成bootpack.hrb,这是因为映像文件只是将各部分全都链接在一起,做成了完整的机器语言文件,为了实际使用,还需要加工,如加上识别的文件头,或者压缩等。
使用make run命令后,结果没有变化