今天就来完成到C语言的过度。
首先说,映像永远是1440KB,按照软盘的格式来,就算你用了512字节的img,只有启动区,然后BIOS只从头开始检查,它的确可以执行,然后就执行了512字节的,然后完了,也不会出什么错误,但是最好还是1440KB的,因为剩下的整个系统都放在这里,把IPL制成了一个映像,然后再编写其他的脚本往映像里面添加就好了。
但是,一般C语言程序都是32位程序,而BIOS是16位的,因此二者不能互相调用了,一旦进入32位很难进入16位了,因此如果什么事情想要BIOS做,就放到开头去做。我们用BIOS设置画面模式,获取键盘信息后,直接找到C语言编写的二进制代码的函数地址,调用就可以了。
因此现在的问题是,如何能将C语言编写的二进制代码,放到img映像里,这里有准备一个程序edimg.exe,但是我没有,怎么办呢?鉴于也十分好做,自己造一个也未尝不可。
因为启动区被放在了0x8000~0x81ff,因此把数据装在到0x8200~0x83ff吧,0x7c00~0x7dff留给启动区,0x7e00以后直到0x9fbff的区域都没有什么特别的用途,操作系统可以随便使用。
因为我们的程序是从内存0x8000号地址上开始的,因此我们就直接跳到0x8000+512那个地址,就可以直接执行程序了。
现在简要说一下步骤:
首先将启动区代码稍微改一下,最后JMP到系统程序JMP 0xc200,这个0xc200就是系统程序所在地址。
这个文件的文件名设为IPL.asm
ORG 0x7c00 ; 程序的内存装载地址
JMP entry
CYLS EQU 10 ; 声明10个柱面
; 标准FAT12格式软盘专用的代码
DB "MyOS_IPL" ; 启动扇区名称(8字节)
DW 512 ; 每个扇区(sector)大小(必须512字节)
DB 1 ; 簇(cluster)大小(必须为1个扇区)
DW 1 ; FAT起始位置(一般为第一个扇区)
DB 2 ; FAT个数(必须为2)
DW 224 ; 根目录大小(一般为224项)
DW 2880 ; 该磁盘大小(必须为2880扇区1440*1024/512)
DB 0xf0 ; 磁盘类型(必须为0xf0)
DW 9 ; FAT的长度(必须是9扇区)
DW 18 ; 一个磁道(track)有几个扇区(必须为18)
DW 2 ; 磁头数(必须是2)
DD 0 ; 不使用分区,必须是0
DD 2880 ; 重写一次磁盘大小
DB 0,0,0x29 ; 意义不明(固定)
DD 0xffffffff ; (可能是)卷标号码
DB "MyOS_disk " ; 磁盘的名称(必须为11字节,不足填空格)
DB "FAT12 " ; 磁盘格式名称(必须是8字节,不足填空格)
RESB 18 ; 先空出18字节
; 程序主体
entry:
MOV AX, 0 ; 初始化
MOV SS, AX ; 堆栈地址为0
MOV SP, 0x7c00 ; 堆栈指针附了特定值
MOV DS, AX ; DS, ES地址必须置0
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 ; 一个扇区
MOV BX, 0
MOV DL, 0x00 ; A驱动器
INT 0x13 ; 读入
JNC next ; 没出错跳出
ADD SI, 1 ; 计数加1
CMP SI, 5 ; 重复5次
JAE error ; 5次还不行就出错
MOV AH, 0x00
MOV DL, 0x00 ; A驱动器
INT 0x13 ; 重置驱动器
JMP retry
next:
MOV AX, ES
ADD AX, 0x0020
MOV ES, AX ; 把内存地址后移512
ADD CL, 1 ; CL的加1
CMP CL, 18 ; 读完18个扇区
JBE readloop ; 嗯,到这里
MOV CL, 1
ADD DH, 1
CMP DH, 2
JB readloop ; 两个正反面
MOV DH, 0
ADD CH, 1
CMP CH, CYLS
JB readloop ; 读完10个柱面
;JMP main
JMP 0xc200 ; 跳转到系统执行
error:
MOV SI, msg
putloop:
MOV AL, [SI] ; 把si中的内容放到AL中
ADD SI, 1
CMP AL, 0
JE halt
MOV AH, 0x0e
MOV BX, 15
INT 0x10
JMP putloop
halt:
HLT
JMP halt
; 信息显示部分
msg:
DB 0x0a, 0x0a ; 换行两次
DB "load error"
DB 0x0a ; 换行
DB 0
RESB 0x7dfe-$
DB 0x55, 0xaa
紧接着将IPL.asm编译成IPL.bin,语句为nask IPL.asm IPL.bin
然后设计主程序代码,这里让它输出一个loaded的信号,注意一开始要注明程序的内存地址ORG 0xc200
ORG 0xc200
mov SI, msg1
putloop1:
MOV AL, [SI] ; 把si中的内容放到AL中
ADD SI, 1
CMP AL, 0
JE halt
MOV AH, 0x0e
MOV BX, 15
INT 0x10
JMP putloop1
halt:
HLT
JMP halt
msg1:
DB 0x0a, 0x0a ; 换行两次
DB "loaded"
DB 0x0a ; 换行
DB 0
RESB 0x7dfe-$
DB 0x55, 0xaa
再将这个文件编译成MyOS.bin,语句为nask MyOS.asm MyOS.bin
然后下载软件WinImage(共享软件),安装后点击菜单栏的新建按钮,新建一个1.44mb的软盘
然后点击菜单栏Image->Boot sector properties->open...,将IPL.bin选入,点击确定。
然后点击工具栏中的Inject,将MyOS.bin选入。
最后保存该镜像,镜像名为MyOS.img,方便Virtual Box读取。
启动Virtual Box出现如下画面证明成功:
采坑记:
关于RESB 0x7dfe-$这一段,注意在之前并不是这个数值,之前是0x1fe,这里会有一个问题,那就是nasm不认识这个,而nask认识这个,倘若用nasm的话,需改成times,还不一定对,而且关于编译,nasm的语句是:
nasm MyOS.asm -o MyOS.iso
而nask的语句是:
nask MyOS.asm MyOS.iso
没有那个-o参数,如果填上,会报NASK : LSTBUF is not enough这个错误,坑得很。
nask是《30天自制操作系统》作者编写的汇编编译器。