1. 制作真正的IPL
上次制作的IPL只是简单的取了512个字节,并没有真正的加载程序,因此今天我们要制作真正的IPL。
在加载程序之前,我们首先应该把程序装载到内存中,我们在上一次上添加的代码如下:
MOV AX,0x0820
MOV ES,AX
MOV CH,0 ;柱面0
MOV DH,0 ; 磁头0
MOV CL,2 ; 扇区2
MOV AH,0x02 ; AH=0x02 : 读盘
MOV AL,1 ; 1个扇区
MOV BX,0
MOV DL,0x00 ; A驱动器
INT 0x13 ; 调用磁盘BIOS
JC error
其中INT 0x13是调用BISO的磁盘中断,具体内容如下:
磁盘读、写
AH =0x02;(读盘) AH = 0x03(写盘)
AL=处理对象的扇区数(只能同时处理连续的扇区)
CH=柱面号 CL =扇区号(0-5位)|(柱面号&0x300)>>2
DH = 磁头号, DL= 驱动器号 ES:BX=缓冲区地址;
返回值:
FLAGS.CF ==0没有错误,AH=0
FLAGS.CF==1 有错误,错误号码卸载AH内
软盘的样子如下:
软盘中有两个磁头,对应着正反两面,有80个柱面,每个柱面有80个扇区,每个扇区有512字节,软盘的容量80*2*18*512=1.4M
C0-H0-S1(柱面0-磁头0-扇区1),启动扇区。
接下来就是缓冲区地址,是一个内存地址,表明我们要把从软盘上读出的数据装载到内存的那个位置。这里通过一个寄存器最大只有64K的内存,所以这里使用ES:BX这种形式来表示地址,MOV AL,[ES:BX] 它代表ES*16+BX的内存地址,这样最大就可以表示1M的字节内容。
这里指定ES=0x0820,BX=0,所以软盘的数据被装载到内存中0x8200到0x83ff的地方,为何要转载到这个地方呢,书上说,因为启动区装载到0x7c00-0x7dfff用于启动区,0x7e00以后直到0x9fbff为止的区域都没有特别用途,操作系统可以随便使用。其中0x8000~0x81ff这512字节是留给启动区的。
注意:需要注意的是之前开发程序时没有考虑段寄存器,但事实上,不管我们制定内存是什么地址,都需要制定段寄存器,一般如果省略的话就把“DS:”作为默认的寄存器。以前我们用“MOV CX,[1234]”,其实是“MOV CX,[DS:1234]”。
接着本节又加入了试错,并且读了又读了10个柱区。
注意:在读18个扇区时,没有直接赋予AL=18,而是一个一个扇区的读取。具体原因:书中解释为:
指定处理的扇区数,范围在0x01-0xff(指定0x02以上的数值时,要特别注意能够连续处理多个扇区的条件。如果是FD的话,似乎不能跨越多个磁道,也不能超过64KB的界限)。
读了10个柱面后,我们已经将10*2*18*512=180KB。
2.着手开发系统
我们把文件加入到磁盘映像中,可以同过WinImage来装入,之后我们可以通过二进制软件观察,可以发现在0x002600,可以发现我们向一个空软盘保存文件时:
(1)文件会写在0x002600以后的地方,
(2)文件的内容会写在0x004200以后的地方
为什么是如下两个地址,主要是根据FAT12的文件格式来,其中启动扇区占一个扇区,fat1,fat2各占9个扇区,根目录区所在位置时19扇区,19*512=0x002600,同样,在默认设置下,数据区会在0x004200处。
接着,由于文件内容在0x4200处,就应该位于内存0x8000+0x4200=0xc200,因此,我们在ipl.nas中加入jmp 0xc200,在haribote.nas 加上ORG 0xC200。
3. 确认操作系统的执行情况:
现在程序运行出来没有现象,因此我们加入一些屏幕显示来观察一下。在haribote.nas中加入程序如下:
MOV AL,0x13 ; VGA显卡,320*200*8位彩色
MOV AH,0x00
INT 0x10
设置显卡模式:
AH =0x00,AL=模式(省略了一些不重要的画面模式)
0x03:16字符模式;
0x12:VGA图形模式640*480*4,独特的4面存储模式
0x13:VGA图形模式320*200*8位彩色模式,调色板模式
0x6a:扩展VGA图形模式,800*600*4位彩色模式,独特的4面存储模式(有的显卡不支持这种模式)
返回值:无
这里我们采用0x13模式,因为8中彩色模式可以使用256中颜色,运行之后,我们可以发现画面变为一篇漆黑。
4. 32位模式的前期准备
由于本操作系统主要以C语言开发为主,由于本书使用的编译器只能生产32位的机器语言,如果要生成16位的机器语言也是可以做到的,但是很费事,其次,32位模式下可以使用的内存量远远大于1MB,而且,CPU的自我保护功能,在16位下不可用,但在32位下可用,并且,现在16位的模式几乎使用的很少,因此本书使用了32位的模式了。
可是如果用32位模式就不能调用BIOS功能了,这是因为BIOS是用16位机器语言写的,如果我们有什么事想用BIOS做的,那么就全部放在开头先做,一旦进入32位模式就不能调用BIOS函数了(当然,也可以从32位返回到16位的方法,但是非常费工夫)。
下面,我们就把画面模式,图像缓冲区地址,各个指示灯状态保存在内存中。
CYLS EQU 0x0ff0 ; 设定启动区
LEDS EQU 0x0ff1
VMODE EQU 0x0ff2 ; 关于颜色数目,位数
SCRNX EQU 0x0ff4 ; 分辨率X
SCRNY EQU 0x0ff6 ; 分辨率Y
VRAM EQU 0x0ff8 ; 图像缓冲区开始地址
ORG 0xc200 ; 装载到内存的位置
MOV AL,0x13 ; VGA模式
MOV AH,0x00
INT 0x10
MOV BYTE [VMODE],8 ; 记录画面模式
MOV WORD [SCRNX],320
MOV WORD [SCRNY],200
MOV DWORD [VRAM],0x000a0000
; 用BIOS获得键盘上各种LED指示灯的状态,
MOV AH,0x02
INT 0x16 ; keyboard BIOS
MOV [LEDS],AL
注意,[VRAM]中保存的是显卡内存,不同的画面模式的像素数不一样,使用的内存也不一样。而本文所用的画面模式对应的内存为“0xa0000~0xaffff”;共64KB。
5. 开始导入C语言
这个是本章中的重点,因为之后我们都要采用C语言进行系统的开发。程序中添加和修改了很多地方,在asmhead.nas中为了调用C语言程序,添加了100行代码。这部分作者也没有详细的去讲,我们暂时跳过。
添加了一个简单的C语言文件,bootpack.c。里面是一个无限循环。
文中又添加了c文件如何变成机器语言的步骤,写的很详细,大家可以看书。
下面主要讲解如何在C文件中调用汇编函数,首先我们编写一个汇编函数,如下:
[FORMAT "WCOFF"] ; 制作目标文件模式
[BITS 32] ; 制作32位模式用的机器语言
; 制作目标文件信息
[FILE "naskfunc.nas"] ; 源文件信息
GLOBAL _io_hlt ; 程序包含的函数名
; 以下是实际函数;
[SECTION .text] ; 目标文件写入这些后在写入程序
_io_hlt: ; void io_hlt(void);
HLT
RET
用汇编写的函数,之后还要与bootpack.obj连接,所以也需要编译成目标文件,因此将输出格式设定为WCOFF模式,另外,还要设定成32位机器语言模式。
注意要在函数名的前面加上“_”,否则就不能焊好的与C语言函数链接,需要链接的函数名都需要用GLOBAL指令声明。
在来看看C文件时如何调用的。
/*首先声明有一个函数在别的文件里;
void io_hlt(void);
/*用;结尾表示函数在别的文件里;
void HariMain(void)
{
fin:
Io_hlt(); /* 执行naskfunc.nas的_io_hlt函数 */
goto fin;
}
我们执行以下,正确,黑屏。
在本文的最后,我突然发现makefile的变化很大,有必要讲一讲,这里面现在采用变量的形式简化了里面的代码。里面按照代码的编译过程,增加了一些新的代码。这里有些代码我还有些不懂,就不说了。