下面开始我们新的一章的学习了。在这章开头,我先要吐槽一下这本书,我觉得这本在第三章,也就是第三天这里,感觉有些地方作者
讲的很含糊,有的地方需要深入的讲解却没有,但是有很多所谓的“风趣”的话,但是我倒不觉得有多风趣,纯属吐槽。
好了下面开始进入正文,在进入正文之前先讲解几个重要的概念
启动区:(boot sector)软盘第一个的扇区成为启动区,就是0面0道1扇区。那么什么是扇区呢?计算机读写软盘的时候,并不是一个字节
一个字节地读写的,而是以512字节为一个单位进行读写。因此软盘的512个字节就称为一个扇区,一张软盘的空间共有1440KB,也就是
1474560字节,除以512得2880,这也就是说一张软盘共有2880个扇区。那为什么第一个扇区称为启动区呢?那是因为计算机首先从最初一个
扇区开始读软盘,然后去检查这个扇区最后2个字节的内容。如果这最后2个字节不是55AA,计算机会认为这张软盘上没有所需的启动程序,就
会报一个不能启动的错误。如果计算机确认第一个扇区的最后两个字节正好是55AA,那它就认为这个扇区的开头是启动程序,并开始执行这个程序。
IPL:(initial program loader)的缩写。启动程序加载器,启动区只有区区512字节,实际的操作系统很大,根本装不进去,所以几乎所有
的操作系统,都是把加载操作系统本身的程序放在启动区里的。有鉴于此,有时也将启动区称为IPL。
概念说完,那就上代码了:
; haribote-ipl ; TAB=4 CYLS EQU 10 ORG 0x7c00 JMP entry DB 0x90 DB "HARIBOTE" DW 512 DB 1 DW 1 DB 2 DW 224 DW 2880 DB 0xf0 DW 9 DW 18 DW 2 DD 0 DD 2880 DB 0,0,0x29 DD 0xffffffff DB "HARIBOTEOS " DB "FAT12 " RESB 18 entry: MOV AX,0 MOV SS,AX MOV SP,0x7c00 MOV DS,AX MOV AX,0x0820 ;表示要把磁盘0柱面0磁头2扇区的数据加载到内存0x8200开始的位置 MOV ES,AX MOV CH,0 ;柱面0 MOV DH,0 ;磁头0 MOV CL,2 ;扇区2 readloop: MOV SI,0 retry: ;这里是读一个扇区的操作 MOV AH,0x02 ;AH=0x20:读盘 MOV AL,1 ;1个扇区(表示要处理的扇区数) MOV BX,0 MOV DL,0x00 ;A驱动器 INT 0x13 ;调用磁盘BIOS JNC next ;读盘没出错那就跳转到next标号 ;这里是试错操作,软盘这东西很不可靠,有时发生不能读数据的状况,这时候重新再读一次就行了。 ADD SI,1 CMP SI,5 JAE error MOV AH,0x00 MOV DL,0x00 INT 0x13 JMP retry next: ;移动地址 MOV AX,ES ;把内存地址后移0x200(为什么?下面讲解) ADD AX,0x0020 MOV ES,AX ;下面的操作就是从C0-H0-S1一直读到C9-H1-S18 ADD CL,1 CMP CL,18 JBE readloop ;循环18个扇区 MOV CL,1 ADD DH,1 CMP DH,2 JB readloop ;循环磁头 MOV DH,0 ADD CH,1 CMP CH,CYLS JB readloop ;循环柱面 MOV [0x0ff0],CH ;保存CYLS值 JMP 0xc200 ;跳转到0xc200地址开始执行指令 ;下面这串暂时没用,可以忽略 error: MOV SI,msg putloop: MOV AL,[SI] ADD SI,1 CMP AL,0 JE fin MOV AH,0x0e MOV BX,15 INT 0x10 JMP putloop fin: HLT JMP fin msg: DB 0x0a, 0x0a DB "load error" DB 0x0a DB 0 RESB 0x7dfe-$ DB 0x55, 0xaa
1,下面先来看看几个BIOS的函数调用
INT 0x13:磁盘读,写,扇区校验,以及寻道
AH = 0x02;(读盘)
AH = 0x03;(写盘)
AH = 0x04;(校验)
AH = 0x0c;(寻道)
AL = 处理对象的扇区数(只能同时处理连续的扇区)
CH = 柱面号 & 0xff;
CL = 扇区号(0-5位) | (柱面号&0x300) >> 2;
DH = 磁头号;
DL = 驱动器号
ES:BX=缓冲地址;(校验及寻道时不使用)
返回值: FLACS.CF == 0: 没有错误,AH==0
FLAGS.CF == 1: 有错误,错误号码存入AH内
INT 0x13
AH = 0x00
DL = 0x00
这个三个组合就是系统复位功能,它的功能是复位软盘状态,再读一次。
2,下面要总结几个跳转指令
(1),JC,是"jump if carry"的缩写,意思是如果进位(carry flag)是1的话,就跳转。
(2),JNC,是"jump if not carry"的缩写,意思是如果进位(carry flag)是0的话,就跳转。
(3),JAE,是"jump if above or equal"的缩写,意思是大于或等于时跳转。
(4),JB,是"jump if below"的缩写,如果小于的话,就跳转
3,讲解几个我觉得需要注意的地方
(1)就是程序开始时为什么要将ES指定为0x0820?作者的理由如下:
我们指定了ES=0x0820,BX=0,所以软盘的数据将被装载到内存中0x8200到0x83ff的地方,可能有人会想,怎么也不弄个整点的数,
比如0x8000什么的,那多好,但0x8000-0x81ff这512字节是留给启动区的,要将启动区的内容读到那里,所以就这样吧。
但在后面作者又说0x7c00-0x7dff用于启动区,0x7e00以后直到0x9fbff为止的区域没有特别的用途,操作系统可以随便使用。
所以这里就给我造成了疑问,但是是0x8000-0x81ff还是0x7c00-0x7dff用于启动区呢,BIOS规定为把0盘0面1扇区的内容加载到
0x7c00的位置,这肯定是无可厚非的。但是作者后面又说0x8000-0x81ff这512字节是留给启动区的呢?
但是,我觉得一定不是作者弄错了,只不过没表达清晰而已,因为我们的代码是从0盘0面2扇区的内容开始加载进来的,假如我们
的代码是从0盘0面1扇区的内容加载的,那么物理地址=0x8200+汇编地址(即是当前的代码相对于文件的偏移量),但是问题现在
作者是从0盘0面2扇区的内容,所以作者把0x8000-0x8lff预留给启动区的(即使作者这段地址实际上是没用的),那么现在的物理地址
=0x8000+汇编地址。这里有点难解释清楚,得自己看书好好体会。
(2)第二个问题我觉的是作者的疏忽。在第52页中作者写到:"CL是扇区号,ES指定读入地址。0x20是十六制512除以16的结果,如果
写成ADD AX,512/16或许更好懂,笔者在写的时候,直接在头脑中换算成了0x20,当然写成512/16也一样。可能有人会说:往BX里加上
512不是更简单吗",这里的代码不能直接往BX直接加上512,因为每次BX的值每次都被更新为0了,除非上面的代码需要改了。
那下面可以看看这个启动区的代码怎么来进行加载下面的helloworld程序。
; haribote-os ; TAB=4 ORG0xc200 entry: MOVAX,0 MOVSS,AX MOVSP,0xc200 MOVDS,AX MOVES,AX MOVSI,msg putloop: MOVAL,[SI] ADDSI,1 CMPAL,0 JEfin MOVAH,0x0e MOVBX,15 INT0x10 JMPputloop fin: HLT JMPfin msg: DB0x0a, 0x0a DB"hello, world" DB0x0a DB0
运行效果如下: