处理器:Inte lCeleron(R) Dual-Core CPU
操作系统:Windows7 专业版 x86
阅读《30天自制操作系统》—川合秀实[2015.03.15 – 03.16],[03.27整理]笔记。
将《30天自制操作系统》简称为“书”。对于书中的工具,可以专门对其笔记。
工具:../toolset/
软盘的第一个扇区之所以有那么多死板的规定,是因为软盘的第一个扇区有特殊的作用:计算机首先从磁头0面最初一个扇区开始读软盘,然后检查这个扇区最后2个字节的内容。如果这最后2个字节不是55AAH,计算机会认为这张盘上没有所需的启动程序,就会报一个不能启动的错误;如果计算机确认了第一个扇区的最后2个字节正好是55AAH,那它就认为这个扇区的开头是启动程序,并开始执行这个程序。故而软盘的第一个扇区被称为启动区(boot sector)。
计算机(BIOS)只会读软盘的第一个扇区的内容到内存,软盘其它扇区的内容靠执行第一个扇区内的程序载入。
IPL(Initial program loader,启动程序加载器)是软盘启动区内的程序,这个程序的功能为加载软盘各个扇区的内容到内存中。在“[OS-I]计算机开机从软盘启动显示字符串Hello World”中,第一个扇区内的二进制程序的作用是显示字符串“Hello World”,并没有加载软盘扇区内容到内存的功能。像这样的软盘的第一个扇区可以不叫“启动区”,其内的程序也可不叫“IPL”,分别叫它们“Hello World扇区”和“Hello World程序”比较合适。
制作IPL时需要遵循2点:
[1] BIOS 13h对启动区的格式要求(非程序运行所必须的东西);
[2] 在启动区内的代码要有载入其它扇区内容到内存的功能。
磁头0和磁头1所在面的磁道号相同的两个磁道被称为一个柱面。软盘内用于存储二进制信息的(黑色)介质被称为磁盘。
启动区的内容被加载到内存的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次都读不成功则放弃这次的程序加载操作。
如果此时将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 - $
打开“!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指令,进入休眠。
@ hutao1101175783 我重新运行了一遍,可以显示“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]