平台
处理器:Inte lCeleron(R) Dual-Core CPU
操作系统:Windows7 专业版 x86
阅读《30天自制操作系统》—川合秀实[2015.03.15 – 03.16],[03.27整理]笔记。
将《30天自制操作系统》简称为“书”。对于书中的工具,可以专门对其笔记。
工具:../toolset/
1 了解IPL
1.1 启动区
软盘的第一个扇区之所以有那么多死板的规定,是因为软盘的第一个扇区有特殊的作用:计算机首先从磁头0面最初一个扇区开始读软盘,然后检查这个扇区最后2个字节的内容。如果这最后2个字节不是55AAH,计算机会认为这张盘上没有所需的启动程序,就会报一个不能启动的错误;如果计算机确认了第一个扇区的最后2个字节正好是55AAH,那它就认为这个扇区的开头是启动程序,并开始执行这个程序。故而软盘的第一个扇区被称为启动区(boot sector)。
计算机(BIOS)只会读软盘的第一个扇区的内容到内存,软盘其它扇区的内容靠执行第一个扇区内的程序载入。
1.2 IPL
IPL(Initial program loader,启动程序加载器)是软盘启动区内的程序,这个程序的功能为加载软盘各个扇区的内容到内存中。在“[OS-I]计算机开机从软盘启动显示字符串Hello World”中,第一个扇区内的二进制程序的作用是显示字符串“Hello World”,并没有加载软盘扇区内容到内存的功能。像这样的软盘的第一个扇区可以不叫“启动区”,其内的程序也可不叫“IPL”,分别叫它们“Hello World扇区”和“Hello World程序”比较合适。
2 制作IPL
制作IPL时需要遵循2点:
[1] BIOS 13h对启动区的格式要求(非程序运行所必须的东西);
[2] 在启动区内的代码要有载入其它扇区内容到内存的功能。
2.1 软盘的柱面
磁头0和磁头1所在面的磁道号相同的两个磁道被称为一个柱面。软盘内用于存储二进制信息的(黑色)介质被称为磁盘。
2.2 读取软盘10个柱面的IPL
启动区的内容被加载到内存的0x7c00~ 0x7dff,就让软盘后续区内容跟启动区内容相接,将后续内容拷贝到0x8000~ 0x34dff。
; ipl
; TAB=4
CYLS EQU 10 ;读磁盘10个柱面
ORG 0x7c00 ;后续程序从0x7c00开始装载
;FAT12格式软盘启动区规定内容(非必须)
JMP entry
DB 0x90
DB "HARIBOTE" ;启动区的名称,可以是任意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 "HARIBOTEOS " ;磁盘的名称(11字节)
DB "FAT12 " ;磁盘的格式名称(8字节)
RESB 18 ;空出18字节
;程序主体:读磁盘内容到内存
entry:
MOV AX,0 ;根据程序被加载的地址初始化寄存器
;MOV SS,AX
;MOV SP,0x7c00
MOV DS,AX
;读磁盘内容到内存的程序
MOV AX,0x0820
MOV ES,AX
MOV CH,0 ;磁盘的柱面,BIOS 0x13h中断程序参数
MOV DH,0 ;磁头0,BIOS 0x13h中断程序参数
MOV CL,2 ;扇区2,BIOS 0x13h中断程序参数
readloop:
MOV SI,0 ;读扇区失败的次数
retry:
MOV AH,0x02 ; AH=0x02:读磁盘;BIOS 0x13h中断程序参数
MOV AL,1 ;一个扇区;BIOS 0x13h中断程序参数
MOV BX,0 ;将磁盘内容读到ES:BX 处
MOV DL,0x00 ; A驱动器
INT 0x13 ;调用BIOS 0x13h中断程序
JNC next ;无出错就跳转到next处
ADD SI,1 ;记录操作磁盘失败的次数
CMP SI,5
JAE error ;如果SI >= 5就跳到error处
MOV AH,0x00
MOV DL,0x00
INT 0x13
JMP retry
next:
MOV AX,ES
ADD AX,0x0020
MOV ES,AX ;把内存地址后移0x200(512)
ADD CL,1 ;下一个扇区
CMP CL,18
JBE readloop ; CL <= 18 就继续读下一个扇区
MOV CL,1
ADD DH,1
CMP DH,2
JB readloop ; DH < 2(分别读磁盘两个面的CH柱面的1 ~ 18扇区)
MOV DH,0
ADD CH,1
CMP CH,CYLS
JB readloop ; CH < CYLS 磁盘每个面都读CYLS个柱面
;加载磁盘内容后、CPU执行完新任务后进入休眠状态
fin:
HLT
JMP fin
error:
MOV SI,msg ;用si访问字符串
putloop:
MOV AL,[SI]
ADD SI,1
CMP AL,0
JE fin
MOV AH,0x0e ;0x0e表示显示一个字符;BISO 0x10中断程序参数
MOV BX,15 ;显示字符的属性;BISO 0x10中断程序参数
INT 0x10 ;调用BIOS 0x10号中断程序
JMP putloop
msg:
DB 0x0a, 0x0a ;换行
DB "load error"
DB 0x0a
DB 0
RESB 0x7dfe-$ ;再填0直到0x7de - 1处
DB 0x55, 0xaa ;表明需要执行启动程序IPL
[1] ipl10.nas功能:将软盘前10个柱面(没有读启动区)的内容装载到内存地址空间0x08200~ 0x34fff中(从0x8000处开始装载不能被正确执行,估计是因为0x8000处有F0H FFH FFH 3字节内容)。([实模式]内存地址空间分布 CPU合成内存地址的方式 程序中段的加载)
[2] “读磁盘内容到内存”部分以磁盘的“磁头”、“柱面”、“扇区”作为“最外层”、“次外层”、“内层”循环的条件,在读完磁头0上柱面的一面时就切换到磁头1读柱面的另一面。
[3] ipl10.nas用si寄存器来记录读一个扇区不成功的次数,如果读一个扇区读5次都读不成功则放弃这次的程序加载操作。
2.3 准备其它扇区的内容
如果此时将ipl10.nas下载到软盘中并运行,IPL会将软盘前10个柱面的内容拷贝到内存地址空间0x08200~ 0x34fff中。虽然如果拷贝其它扇区内容不成功时会有“load error”的错误提示,但笔记还想执行一下IPL拷贝的程序。
需要保证源程序中的各偏移值跟程序在内存中的各偏移值一致,否则需要在用ORG伪指令。见“[实模式] 内存地址空间分布 CPU合成内存地址的方式 程序中段的加载”的ORG部分。
org 0x8200 ;让本程序中jmp指令的参数以0x8200位起始偏移计算,
;因为以下这段程序将会被加载到0x8200处
;后续扇区内容,从偏移0x8200开始
oth_sec:
MOV SI,ho_str
showloop:
MOV AL,[SI]
ADD SI,1 ;下一个字符
CMP AL,0
JE sleep
MOV AH,0x0e ;BIOS 10h程序的功能号参数,eh表示显示字符功能
MOV BX,15 ;AH = EH时,BL表字符前景色,AL表要显示的字符
INT 0x10 ;调用BIOS 10h号中断程序
JMP showloop
sleep:
HLT ;省电:让CPU进入休眠状态,当复位信号或者中断来临时,
JMP sleep ;CPU再执行HLT的下一条指令,或者转去执行中断程序,
;当中断处理完后又执行一次HLT,CPU将再次进入休眠状态
ho_str:
DB 0x0a, 0x0a ;0x0a表示换行
DB "hello, world"
DB 0x0a
DB 0
RESB 1474560 + 0x7c00 - $
只要IPL正确的将软盘10个柱面的内容加载到内存0x08200~ 0x34fff后,就应该跳到0x08200处执行拷贝的代码,最后让CPU进入休眠。如果IPL拷贝失败,则显示load error并让CPU休眠。
将这段程序和ipl10.nas结合在一起:
ipl
; TAB=4
CYLS EQU 10 ;读磁盘10个柱面
ORG 0x7c00 ;后续程序从0x7c00开始装载
;FAT12格式软盘启动区规定内容(非必须)
JMP entry
DB 0x90
DB "HARIBOTE" ;启动区的名称,可以是任意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 "HARIBOTEOS " ;磁盘的名称(11字节)
DB "FAT12 " ;磁盘的格式名称(8字节)
RESB 18 ;空出18字节
;程序主体:读磁盘内容到内存
entry:
MOV AX,0 ;根据程序被加载的地址初始化寄存器
;MOV SS,AX
;MOV SP,0x7c00
MOV DS,AX
;读磁盘内容到内存的程序
MOV AX,0x0820
MOV ES,AX
MOV CH,0 ;磁盘的柱面,BIOS 0x13h中断程序参数
MOV DH,0 ;磁头0,BIOS 0x13h中断程序参数
MOV CL,2 ;扇区2,BIOS 0x13h中断程序参数
readloop:
MOV SI,0 ;读扇区失败的次数
retry:
MOV AH,0x02 ; AH=0x02:读磁盘;BIOS 0x13h中断程序参数
MOV AL,1 ;一个扇区;BIOS 0x13h中断程序参数
MOV BX,0 ;将磁盘内容读到ES:BX 处
MOV DL,0x00 ; A驱动器
INT 0x13 ;调用BIOS 0x13h中断程序
JNC next ;无出错就跳转到next处
ADD SI,1 ;记录操作磁盘失败的次数
CMP SI,5
JAE error ;如果SI >= 5就跳到error处
MOV AH,0x00
MOV DL,0x00
INT 0x13
JMP retry
next:
MOV AX,ES
ADD AX,0x0020
MOV ES,AX ;把内存地址后移0x200(512)
ADD CL,1 ;下一个扇区
CMP CL,18
JBE readloop ; CL <= 18 就继续读下一个扇区
MOV CL,1
ADD DH,1
CMP DH,2
JB readloop ; DH < 2(分别读磁盘两个面的CH柱面的1 ~ 18扇区)
MOV DH,0
ADD CH,1
CMP CH,CYLS
JB readloop ; CH < CYLS 磁盘每个面都读CYLS个柱面
jmp oth_sec
;加载磁盘内容后、CPU执行完新任务后进入休眠状态
fin:
HLT
JMP fin
error:
MOV SI,msg ;用si访问字符串
putloop:
MOV AL,[SI]
ADD SI,1
CMP AL,0
JE fin
MOV AH,0x0e ;0x0e表示显示一个字符;BISO 0x10中断程序参数
MOV BX,15 ;显示字符的属性;BISO 0x10中断程序参数
INT 0x10 ;调用BIOS 0x10号中断程序
JMP putloop
msg:
DB 0x0a, 0x0a ;换行
DB "load error"
DB 0x0a
DB 0
RESB 0x7dfe-$ ;再填0直到0x7de - 1处
DB 0x55, 0xaa ;表明需要执行启动程序IPL
org 0x8200 ;让本程序中jmp指令的参数以0x8200位起始偏移计算,
;因为以下这段程序将会被加载到0x8200处
;后续扇区内容,从偏移0x8200开始
oth_sec:
MOV SI,ho_str
showloop:
MOV AL,[SI]
ADD SI,1 ;下一个字符
CMP AL,0
JE sleep
MOV AH,0x0e ;BIOS 10h程序的功能号参数,eh表示显示字符功能
MOV BX,15 ;AH = EH时,BL表字符前景色,AL表要显示的字符
INT 0x10 ;调用BIOS 10h号中断程序
JMP showloop
sleep:
HLT ;省电:让CPU进入休眠状态,当复位信号或者中断来临时,
JMP sleep ;CPU再执行HLT的下一条指令,或者转去执行中断程序,
;当中断处理完后又执行一次HLT,CPU将再次进入休眠状态
ho_str:
DB 0x0a, 0x0a ;0x0a表示换行
DB "hello, world"
DB 0x0a
DB 0
RESB 1474560 + 0x7c00 - $
2.4 执行
打开“!cons_ne.bat”,用nask.exe依据2.3中结合的程序输出helloos.img,再运行“install.bat”命令将helloos.img下载到软盘上。再将软盘插入另一台相同计算机中,以软盘方式启动,得到如下结果:
Figure1. IPL加载其他扇区程序后再执行其它扇区内容的结果
计算机首先根据IPL的最后2个字节55AA执行IPL程序,IPL将前10个柱面的内容加载到了内存0x08200 ~ 0x34fff处后执行jmp 0x8200,CPU就执行0x08200的代码,最后执行HLT指令,进入休眠。
我重新运行了一遍,可以显示“hello world”。
!cons_nt.bat install.bat ipl10.nas nask.exe run.bat |
总结
[1] 计算机(BIOS)将软盘启动区程序读到0x7c00~ 0x7dff,为了保证内存中的指令参数和编译器编译后的指令参数一直,需要在汇编程序中使用ORG0x7c00语句;IPL将启动区的程序加载到0x8000~ 0x81ff;IPL读启动区后一个扇区的内容到内存0x8200~0x83ff;以此类推… (装载启动区程序到内存地址空间0x7c00~ 0x7dff是计算机(BIOS)规定;从0x8000 内存地址空间存软盘程序是开发操作系统人的规定,因为这段内存没有被其它程序占用)。
[2] 从计算机开机到操作系统的启动,CPU的控制权的操纵者为:FFFF0H(实模式)-->BIOS(实模式)-->(操作系统)程序。
[x86OS] Note Over.
[2015.04.01]