30天自制操作系统 pdf_30天自制操作系统-引导读取磁盘内容

由于工(zi)作(shen)繁(lan)忙(duo),有几周没更新了,被大家催更,于是立了flag周末更新,咱不能鸽了哈,这篇文章继续介绍引导的部分。

上篇文章中,给大家实现了初步的引导,利用引导打印一串字符串。

VictorYXL:30天自制操作系统-汇编实现初版镜像​zhuanlan.zhihu.com

后面的内容自然是要用引导扇区实现对系统的引导,这其中读取磁盘内容就是不可缺少的一步。

磁盘结构

在开始完善我们的引导之前,需要知道磁盘结构一些内容,我们的系统是现在模拟软盘镜像中,不过结构是一样的。

磁盘结构图,取自原书

软盘和硬盘的组成结构是类似的,都是包含若干磁面,每个磁面都有唯一的磁头用来读取信息,磁面中一圈圈的是磁道,磁道又被分为扇区,所以磁盘的容量=磁面数(磁头数)*磁道数(柱面数)*扇区数*扇区大小,软盘包含2个磁头,80个磁道,18个扇区,每个扇区512,共计1440k,这也回答了我们呢之前遗留的问题-为什么我们的软盘大小是1440k。

预备知识

读取磁盘需要先知道两个汇编指令,一个是条件跳转,一个是磁盘中断。

CMP A,B
Jxx C

表示如果A,B满足某条件,则跳转到C,如

CMP     AL, 0
JE      jump

表示如果AL=0则跳转到jump处(写了一半突然发现上篇文章也说了这个,尴尬),JB表示小于则跳转,JBE表示小于等于则跳转等等。

磁盘相关操作的中断是0x13中断,其中AH=0x00, DL=0x00表示磁盘复位,AH=0x02表示读磁盘,此时参数如下

DL      磁盘驱动器号
DH      磁头号
CH      磁道号
CL      起始扇区号
AL      读取扇区数
ES:BX   读取数据后的缓冲区

调试信息

接着就是完善我们的IPL使得他们读取软盘内容,由于软盘的读写具有不可靠性,我们设定读某扇区超过5次则为失败。

在读磁盘内容前,我们先将之前IPL中msg改为成功或者失败的信息,以便与我们查看结果。

; error
error:
    MOV SI, error_msg
    JMP print
; error msg
error_msg:
    DB 0x0a,0x0a,0x0a
    DB "load error"
; succeed
succeed:
    MOV SI, succeed_msg
    JMP print
; succeed msg
succeed_msg:
    DB 0x0a,0x0a,0x0a
    DB "load successfully"

内容很简单,就是定义了成功和失败的字串,并配上调用的接口。

寄存器初始化

接下来是读取磁盘内容的部分,包括寄存器初始化,单个扇区的读写和多个扇区的读写。

; Init register
init:
    MOV    AX,0       
    MOV    SS,AX
    MOV    SP,0x7c00    
    MOV    DS,AX

    MOV    AX,0x0820
    MOV    ES,AX
    MOV    CH,0
    MOV    DH,0
    MOV    CL,2

将之前的entry改成了init,增添了后半段的初始化,其中ES:BX作为后面接受读取内容缓冲区的地址,这里对ES,CH,DH和CL初始化,这里寄存器的值将作用于上文所说的0x13中断。

读入单个扇区

read_sector:
    MOV    SI,0

try_read:
    MOV    AH,0x02
    MOV    AL,1
    MOV    BX,0
    MOV    DL,0x00

    INT    0x13
    JNC    next

    ADD    SI,1
    CMP    SI,5
    JAE    error
    MOV    AH,0x00
    MOV    DL,0x00
    INT    0x13
    JMP    try_read

这段逻辑还是比较清晰的,如果用C写就是一个循环。

SI记录读取次数,接着进入循环,每次将AH, AL, BX, DL这些寄存器也按照上文所说参数设定好,然后调用0x13中断,可能有些小伙伴不理解为什么这些参数不再初始化的时候设定,那是因为后面的代会使用这些寄存器,所以这里对这些需要初始化。

接着用0x13中断尝试读取扇区内容,如果成功则跳转到next,是后面要介绍的读取多段扇区的部分,否则就是失败,失败次数加1,并判断如果超过5次则跳转到前面定义的失败内容结束程序,如果未超过5次则用调用前面提到的磁盘复位中断,并尝试下一次读取。

读入前10柱面

接下来尝试读取更多扇区,可能有小伙伴会问为啥是读前10个柱面的扇区,而不是整个80个柱面,一方面是我懒,原书作者就是只读了10个柱面照搬了,另一方面还是我懒,之前定义引导扇区外空间写的4600个空白字节,(4600+8)/512=9,加上自身引导扇区,刚好是10个,也就不想改了。

next:
    MOV     AX,ES
    ADD     AX,0x0020
    MOV     ES,AX

    ADD     CL,1
    CMP     CL,18
    JBE     read_sector
    
    MOV     CL,1
    ADD     DH,1
    CMP     DH,1
    JBE     read_sector
    
    MOV     DH,0
    ADD     CH,1
    CMP     CH,9
    JBE     read_sector

    JMP     succeed

在读取单个扇区成功后进去多扇区的逻辑,其实也很简单,第一段将ES后移0x20个单位,因为BX是4位,最多表达到16,所以ES段寄存器需要增加32,以跳过已经读取的一个扇区。话说我在重写code的时候,把0x0020达成了00020,每次都只能读两个磁道就会死机,而且用虚拟机debug还关不了机,只能重复创建虚拟机,心累。

后面的逻辑就是重复读取扇区,当CL超过18表示该磁头的扇区读完了,扇区号归0磁头号+1,磁头超过1表示该柱面读完了,磁头号归0柱面号+1,直到柱面号超过9表示整个读完。

话说我在写到这的时候,突然感觉不能再懒惰了,遂改写了代码,支持读更多柱面。

将原本写引导扇区外的内容改写成

    DB      0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
    RESB    (n-1)*512-8
    DB      0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
    RESB    80*2*18*512-n*512-8

n为柱面数。

果然勤奋的人会被上天眷顾。80*2*18*512-512*10-8=1649432,上篇文章我居然鬼使神差的写成了1468432,也是佩服我自己。

就这样,我们的引导能够读取足够多的磁盘内容了,后面就是正式开始写系统,并且被我们的引导加载进内存了。

你可能感兴趣的:(30天自制操作系统,pdf,30天自制操作系统光盘)