上次完成了启动盘,我们已经成功把启动区加载到了内存中,当然了,这512字节的空间肯定是不可能够我们写一个完整的操作系统的,所以,我们操作系统的其他部分肯定得在别的地方写,那么,ipl的任务就应该是把软盘这1440KB的内容全部加载到内存中,不过这里有一个相当严重的问题,就是我们在实模式下,只有20位寻址空间,换句话说,我们只能访问1MB的内存,所以暂时还没办法把所有的数据都放到内存中,暂时我们先只放10个柱面的内容到内存中。交给操作系统的内存部分是0x8000到0x9fbff,不过,虽然ipl的部分事先被加载到了0x7c00的位置,但是,它仍然作为软盘数据的一部分,这部分的数据在以后是有可能会用到的,因此,我们还是把0x8000到0x81ff的内存空间留给它,把剩下的内容从0x8200开始储存。
我们按照扇区-磁头-柱面的顺序变更来读取软盘(也就是C0-H0-S1, C0-H0-S2, ……C0-H0-S18, C0-H1-S1, …… C0-H1-S18, C1-H0-S1, ……这样的顺序)一直读到C9-H1-S18。下面是改过后的ipl。
org 0x7c00
jmp entry
db 0x90
db "HELLOIPL"
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
db 0xffffffff
db "HELLO-OS "
db "FAT12 "
resb 18
entry:
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 ; 扇区号
readloop:
mov si, 0
retry:
mov ah, 0x02
mov al, 1 ; 处理1个扇区
mov bx, 0 ; 缓冲地址基址
mov dl, 0 ; 驱动器号
int 0x13 ; 0x13号中断,ah=0x2时读盘,内容读取到es:bx中
jnc succeed ; 如果进位符是1表示读取错误,jnc表示进位符为0
add si, 1
cmp si, 5
jae error
mov ah, 0
mov dl, 0
int 0x13 ; 0x13号中断 ah=0, dl=0时系统复位
jmp retry
next:
mov ax, es
add ax, 0x20
mov es, ax
add cl, 1
cmp cl, 18
jbe readloop
mov cl, 1
add dh, 1
cmp dh, 2
jb readloop
mov dh, 0
add ch, 1
cmp ch, 10
jb readloop
mov [0xff0], ch
jmp 0xc200
succeed:
mov si, msg_succeed
jmp putloop
error:
mov si,msg_error
putloop:
mov al, [si]
add si, 1
cmp al, 0
je next
mov ah, 0x0e
mov bx, 15
int 0x10
jmp putloop
msg_error:
db 0x0d, 0x0a
db "load error"
db 0x0a, 0
msg_succeed:
db 0x0d, 0x0a
db "load success "
db 0x0a, 0
resb 0x7dfe-$
db 0x55, 0xaa
24-27行是寄存器的初始化,ss和sp这两个寄存器在本程序里还用不到,只不过写上合适的值方便以后添加程序。29-30行初始化了缓冲地址寄存器es,这是因为我们要设置数据的起点,由于我们要把数据加载到0x8200位置,所以数据的起点就是这里,段寄存器就指向这里。13号BIOS中断时有关软盘操作的,当al为1时进行读盘操作,将会把dl表示的驱动器中的内容存放到[es:bx]中;当al为0时进行重新读盘操作。36-49行尝试读盘,si寄存器存放的是尝试读盘的次数,所以在35行初始化为0,当尝试5次都失败时将放弃读取,输出错误信息,而读取成功后,就会跳转到next标签下。50-64行通过不断循环36-49行的指令,把10个扇区的数据全部载入内存中。65行向内存0xff0处保存了一个数据,这个数据是目前读取的扇区数,这也是为了以后拓展这个程序用的,可以方便地知道之前加载了多少数据。执行到66行时,程序应当将10个扇区都加载完毕了,跳转到0xc200处,没错,0xc200已经超出ipl的范围了,这就是给操作系统的一个引子,我们就应当把操作系统加载到这个位置,由于ipl之前已经把这些内容都加载到内存中了,所以,接下来就可以直接执行操作系统的内容了,接下来我们来写操作系统,很简单的,输出一行文字就好。
org 0xc200
osEntry:
mov ax, 0
mov ds, ax
mov si, msg
putloop:
mov al, [si]
add si, 1
cmp al, 0
je fin
mov ah, 0xe
mov bx, 15
int 0x10
jmp putloop
fin:
hlt
jmp fin
msg:
db 0x0d, 0x0a
db "Hello World!"
db 0x0d, 0x0a, 0
在输出了一系列的加载成功以后,又输出了Hello World!,这就表示,我们的操作系统已经加载出来了。
所以,ipl的使命基本就是这样,但是现在还不是很完善,这个以后再去修改,在较长的一段时间就会保持这样,接下来的任务就要转向我们的操作系统内核了。下次将介绍在操作系统加载后如何进入保护模式,以便利用32位寻址空间,访问4GB的内存。