上一篇讲了如何使用虚拟机运行我们写好的操作系统,但上一篇的操作系统其实仅仅存在于引导扇区中,接下来我们将看到程序如何从引导扇区跳到其他位置。
引导区内容的装载位置为0x00007c00-0x00007dff,规定该位置的人是当年开发IBM-PC的工程师们。0x7e00-0x9fbff是操作系统可以随便使用的内存位置。按照《30天自制操作系统》中所讲,我们把前10个磁道的数据都读入内存,内存位置从0x8000开始。因此磁盘镜像文件中,位于地址x的字符加载到内存的位置就为0x8000+x。现在设我们需要执行磁盘镜像上位于0x004200号地址的程序,那么其内存地址就为0x8000+0x4200=0xc200。
程序一、ipl.nas
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; hello-os
; TAB=8
ORG 0x7C00
; 以下的记述用于标准FAT12格式的软盘
start:
JMP entry
DB "HELLOIPL" ; 启动区名称(8字节)
DW 512 ; 扇区大小(512字节)
DB 1 ; 簇大小(1扇区)
DW 1 ; FAT起始位置
DB 2 ; FAT个数
DW 224 ; 根目录大小(224项)
DW 2880 ; 磁盘大小(2880扇区)
DB 0xf0 ; 磁盘种类
DW 9 ; FAT长度
DW 18 ; 每个磁道扇区数
DW 2 ; 磁头数
DD 0 ; 不使用分区
DD 2880 ; 重写一次磁盘大小
DB 0,0,0x29 ; 意义不明
DD 0xffffffff ; 可能是卷标号码
DB "HELLO-OS " ; 磁盘名称(11字节)
DB "FAT12 " ; 格式名称(8字节)
RESB 18 ; 空出18字节
entry:
MOV AX,0 ; 初始化寄存器
MOV SS,AX
MOV SP,0x7c00
MOV DS,AX
MOV ES,AX
; 读磁盘
CYLS EQU 10
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 ; 读盘
MOV AL,1 ; 1个扇区
MOV BX,0
MOV DL,0x00 ; A驱动器
INT 0x13 ; 调用磁盘BIOS
JNC next ; 没出错跳转fin
ADD SI,1
CMP SI,5 ; 比较SI与5
JAE error ; SI >= 5时,跳转到error
MOV AH,0x00
MOV DL,0x00
INT 0x13 ; 重置驱动器
JMP retry
next:
MOV AX,ES
ADD AX,0x0020 ; 把内存地址后移0x200
MOV ES,AX ; 因为没有ADD ES,0x20
ADD CL,1
CMP CL,18
JBE readloop ; 如果CL <= 18,跳转至readloop
MOV CL,1
ADD DH,1 ; 读磁盘另一面
CMP DH,2
JB readloop
MOV DH,0
ADD CH,1
CMP CH,CYLS ; 读CYLS个柱面
JB readloop
; 输出helloworld
MOV SI,msg
putloop:
MOV AL,[SI]
ADD SI,1 ; 给SI加1
CMP AL,0
JE 0xc200 ; 跳到0xc200,即hanbote.nas程序地址
MOV AH,0x0e ; 显示一个文字
MOV BX,15 ; 指定字符颜色
INT 0x10 ; 调用显卡BIOS
JMP putloop
JMP 0xc200
fin:
HLT
JMP fin
error:
MOV SI,errmsg
errloop:
MOV AL,[SI]
ADD SI,1 ; 给SI加1
CMP AL,0
JE fin
MOV AH,0x0e ; 显示一个文字
MOV BX,15 ; 指定字符颜色
INT 0x10 ; 调用显卡BIOS
JMP errloop
msg:
DB 0x0a, 0x0a ; 换行2次
DB "hello, world"
DB 0x0a ; 换行
DB 0
errmsg:
DB 0x0a, 0x0a ; 换行2次
DB "disk error"
DB 0x0a ; 换行
DB 0
marker:
RESB 0x1fe-(marker-start)
DB 0x55, 0xaa
; 以下是磁盘其他内容
DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
RESB 4600
DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
RESB 1469432
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
程序二、hanbote.nas
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; haribote-os
; TAB=8
ORG 0xc200 ; 程序开头在内存中的地址
MOV SI,msg
putloop:
MOV AL,[SI]
ADD SI,1 ; 给SI加1
CMP AL,0
JE black
; JE fin
MOV AH,0x0e ; 显示一个文字
MOV BX,15 ; 指定字符颜色
INT 0x10 ; 调用显卡BIOS
JMP putloop
black:
MOV AL,0x13 ; VGA显卡
MOV AH,0x00
INT 0x10
fin:
HLT
JMP fin
msg:
DB 0x0d, 0x0a, 0x0a ; 换行2次
DB "haha"
DB 0x0a ; 换行
DB 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
程序3、Makefile
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
default:
make img
img: hanbote.bin ipl.bin
dd if=hanbote.bin of=ipl.img bs=512 seek=33 count=1 conv=notrunc
ipl.bin: ipl.nas
nasm -f bin ipl.nas -o ipl.img -l ipl.lst
hanbote.bin: hanbote.nas
nasm -f bin hanbote.nas -o hanbote.bin
run: img
qemu-system-i386 -fda ipl.img -boot a
clean:
rm ipl.lst
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
这样你只要在终端中输入make run就能看到运行结果了。
其中我们使用dd来把hanbote.bin写入ipl.nas中我们希望的位置。
hanbote.bin在ipl.nas中的位置从0x4200开始,因为dd以512字节为一块,所以用seek把输出文件定位到0x4200/512=0x21=33,notrunc保证ipl.img不被截断。