我依稀记得当初看到这一节标题的时候非常非常的兴奋,因为可以用C语言了。
首先是我们要来制作真正的IPL了,回顾一下,IPL就是那512个启动字节,让他来引导真正的操作系统,因为512个字节太少了,不能做太多逻辑。
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
这里又调用了一个中断,上一个中断是向显示器上输出一个字符,这个中断的作用就是对磁盘进行操作的,如果把他当作C语言或者其他高级语言来讲会很方便,就是这是个对磁盘操作的函数,然后调用前,你给这些寄存器传一些参数,然后会通过标志位的方式返回读取结果,即CF标志位。
介绍一下软盘,这玩意儿淘宝上还能买到,我最开始学的时候买过,实体店早已看不到了,一点几MB的内存,我小仓老师的照片都放不了。。。要它何用。软盘分正反,叫磁头。柱面,可以理解在这个软盘上画了更多的圆圈,然后再分成扇形,也就有了,柱面和扇区。磁盘号是指那么多个软盘,你读哪个。
我们的IPL就会放在C0-H0-S1(也就是柱面0,磁头0,扇区1),本人不太理解,为啥柱面从0开始而扇区不是。
这里要介绍个新玩意儿了段寄存器,我记得我在学汇编时很经典的一句话,物理地址=段地址 x 10 + 偏移地址 (基于10进制,如果是16进制x16就好了)。如果指定用法就得把段寄存器与偏移地址写在一块儿,如果不写默认以DS作为默认段寄存器,昨天就用到了[],暂时还没讲这个东西次。记得和C语言保持一个习惯,变量初始化,这里的DS也记得初始化,不然会读到其他的内存地址去。
by the way Makefile里面的变量特别像PHP里的变量。
;多式几次
MOV AX,0x0820
MOV ES,AX
MOV CH,0 ; 柱面0
MOV DH,0 ; 磁头0
MOV CL,2 ; 扇区2
MOV SI,0 ; 记录失败次数
retry:
MOV AH,0x02 ; AH=0x02 : 读盘
MOV AL,1 ; 1个扇区
MOV BX,0
MOV DL,0x00 ; A驱动器
INT 0x13 ; 调用磁盘BIOS
JNC fin ; 若OK则跳转至fin
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
JNC会去判断CF标志位的状态若是0则会跳转。
JAE大于等于则跳转
MOV AX,0x0820
MOV ES,AX
MOV CH,0 ; 柱面0
MOV DH,0 ; 磁头0
MOV CL,2 ; 扇区2
readloop:
MOV SI,0 ;
retry:
MOV AH,0x02 ; AH=0x02 : 读盘
MOV AL,1 ; 1个扇区
MOV BX,0
MOV DL,0x00 ; A驱动器
INT 0x13 ; 调用磁盘BIOS
JNC next ; 若OK则跳转至next
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
next:
MOV AX,ES ; 把内存地址后移0x200(512字节)
ADD AX,0x0020
MOV ES,AX ; ADD ES,0x020 因为没有这个指令所以只能找中介
ADD CL,1 ; 往CL里加1
CMP CL,18 ; 比较CL与18
JBE readloop ; 如果CL <= 18 跳转至readloop
JBE只要是拿来控制读取到18扇区的,小于等于则跳转。
我们知道磁盘读出来后的内容都放到了ES:BX了,然而我们分别设置了ES与BX,所以程序也就装载到相应位置啦。
接下来是读取是个柱面,这里就不贴代码了。直接说说新指令把。
JB就是小于等于跳转。还有EQU 这个非常建议写一些经常要改的东西这样写,因为稍微做过项目就知道这种玩意儿不写成这样那是找骂呀。
现在磁盘上的180KB字节就存储在ES:BX这个地方啦。没什么太多难度,就是需要每句去理解。
着手开发操作系统啦。。。
这里作者给了一个非常简单的操作系统。。
but...这是历史的转折点,这毕竟是在启动区之外了,我们要读取IPL之外的代码了,从harib00e(光盘目录)看得出来多了一个文件haribote.nas,这个就是我们的操作系统啦,由刚才的main函数(IPL),调用到了这边来,然后作者通过二进制软件一顿分析,得出了如下结论 1.文件名会写在0x002600以后的地方 2.文件的内容会写在0x004200以后的地方 其实我在纠结一个问题,要是0x002600-0x004200之间都写了文件名咋办。不过他说的是0x002600以后,并非之间,嗯,那我就放心了。知道这个文件的内存地址后,现在我们就要来读取这个文件啦。
从启动区执行操作系统
如果我们是把程序装载在0的位置那么直接使用0x004200就行了,但问题是操作系统有它应该放的位置,所以前面我们放在了0x0820的位置,一开始作者说0x8000我小楞了一下,不是放在0x0820位置的吗?(我还是解释一下,怕有些出血朋友不能理解到)首先,这是个段地址会进行偏移,偏移为0x8200,然后200,一想是512个字节,所以这就是我们的启动区嘛,所以正确,就是0x8000。所以我们的程序需要从0x8000+0x4200 = 0xc200地址读取我们的hairbote.nas文件。
这里又新调用了一个“函数” int 0x10,配合上参数这个的作用是把屏幕弄成VGA图形模式,所以我们要开始做图形界面啦,而非一部份书是做命令行模式的(当然这并不代表我对那种书的差评,我直是觉得一上来做图形界面,会让初学者更又兴趣)
马上要进入32位模式啦
优点当然是更多的内存空间可以使用,与安全性。
; 有关BOOT_INFO
CYLS EQU 0x0ff0 ; 设定启动项
LEDS EQU 0x0ff1
VMODE EQU 0x0ff2 ; 关于颜色数目的信息。颜色的位数
SCRNX EQU 0x0ff4 ; 分辨率的X
SCRNY EQU 0x0ff6 ; 分辨率的Y
VRAM EQU 0x0ff8 ; 图像缓冲区的开始地址
ORG 0xc200 ; 将程序装载到0xc200处
MOV AL,0x13 ; VGA显卡,320*200*8位彩色
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
这里呢,也没做啥实际的操作,就是把各种数据记录下来了,然后通过int 0x16取得了键盘上各种指示灯的状态
开始导入C语言
从一开始我就说要开始C语言了,结果到了今天快结束才开始说导入C语言
虽说导入吧,其实有很多原理性的东西,在这里不会讲,其实里面就是后面的GDT等内容,我也是看到后面的GDT开始有些卡主了,所以这里作者没有讲,而是直接来看bootpack.c吧。
void HariMain(void)
{
fin:
goto fin;
}
看到这种代码,我又重新找回了自信,毕竟我不是那个时代过来的人,没有天天写过汇编,所以汇编和C,当然是C更亲近些。我想这玩意儿C代码也不用解释太多。至于是如何调用到这里来的,后面再细谈。
关于对编译器的解释,也很简单。解释的那么清楚,然后与windows,mac上如此成熟的集成开发环境做了对比,意思是相对于那种,我们搞这么复杂,但是步奏是可控的。
最后作者一定要让计算机处于HALT那就来吧。
; naskfunc
; TAB=4
[FORMAT "WCOFF"] ; 制作目标文件的模式
[BITS 32] ; 制作32位模式用的机械语言
; 制作目标文件的信息
[FILE "naskfunc.nas"] ; 源文件名信息
GLOBAL _io_hlt ; 程序中包含的函数名
; 以下是实际的函数
[SECTION .text] ; 目标文件中写了这些之后再些程序
_io_hlt: ; void io_hlt(void);
HLT
RET
然后回来在C语言声明一下,就可以调用啦,不过至于今天许多的细节问题,后面会又深入讲解的。
总的来说我们可以用C语言写代码啦。