在第三篇文章我想用nasm与c来开发我们的操作系统,但当时最终还是用了作者现成的工具做的镜像文件。在学完汇编语言后,再来尝试。
1.汇编语言的引导程序。第二天虽然已经实现了用汇编语言编写hello word,但这个显示只是引导区(0扇区),软盘启动都会读取。第三天我们把引导文件拆成了ipl10.nas与haribote.nas。同样今天我们今天还用这两个文件,不过我们编译工具换成nasm,生成映像文件工具改成我们自己用java写的工具。
ipl10.nas与第三天的一样:
; haribote-ipl
; TAB=4
CYLS EQU 10 ; 读取到哪里?
ORG 0x7c00 ; 这个程序要读取到哪里?
; 以下是标准FAT 12格式软盘的描述
JMP entry
DB 0x90
DB "HARIBOTE" ; 可以自由写启动扇区的名字(8字节)
DW 512 ; 一个扇区的大小(必须是512)
DB 1 ; 集群大小(必须是一个扇区)
DW 1 ; FAT从哪里开始(一般从第一扇区开始)
DB 2 ; FAT的个数(必须设为2)
DW 224 ; 根目录区域的大小(通常为224个条目,FAT里的概念)
DW 2880 ; 该驱动器的大小(必须是2880扇区)
DB 0xf0 ; 媒体类型(必须为0xf0)
DW 9 ; FAT区域的长度(必须是9个扇区)
DW 18 ; 每个柱面上有几个扇区(必须要18个扇区)
DW 2 ; 头的数量(必须是2)
DD 0 ; 因为没有使用分区,所以这里必须是0
DD 2880 ; 再写一次这个驱动器的大小
DB 0,0,0x29 ; 不清楚,最好是这样
DD 0xffffffff ; 可能是卷序列号
DB "HARIBOTEOS " ; 磁盘名称(11字节)
DB "FAT12 " ; 格式名称(8字节)
RESB 18 ; 总之先空出18个字节
; 程序主体
entry:
MOV AX,0 ; 寄存器初始化
MOV SS,AX
MOV SP,0x7c00
MOV DS,AX
; 读磁盘
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 ; 如果不发生错误,请向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
ADD AX,0x0020
MOV ES,AX ; ADD ES,0x020 没有这样的命令,所以这样做
ADD CL,1 ; 在CL上加1
CMP CL,18 ; 比较CL和18
JBE readloop ; CL <= 18 那就去readloop吧
MOV CL,1
ADD DH,1
CMP DH,2
JB readloop ; DH < 2 那就去readloop吧
MOV DH,0
ADD CH,1
CMP CH,CYLS
JB readloop ; CH < CYLS 那就去readloop吧
; 读完了10柱面,开始读haribote.sys
MOV [0x0ff0],CH ; 记下IPL读到哪里了
JMP 0xc200
error:
MOV SI,msg
putloop:
MOV AL,[SI]
ADD SI,1 ; 在SI上加1
CMP AL,0
JE fin
MOV AH,0x0e ; 单字显示功能
MOV BX,15 ; 彩色代码
INT 0x10 ; 视频BIOS调用
JMP putloop
fin:
HLT ; 让CPU停止,直到发生什么。
JMP fin ; 无限循环
msg:
DB 0x0a, 0x0a ; 两个换行符
DB "load error"
DB 0x0a ; 改行
DB 0
;RESB 0x7dfe-$ ;
times 510-($-$$) db 0 ; 以0x00填充到0x7dfe为止的命令
DB 0x55, 0xaa
haribote.nas源码如下:
; haribote-os
; TAB=4
ORG 0xc200 ; このプログラムがどこに読み込まれるのか
MOV AL,0x13 ; VGA图形,320x200x8 bit颜色
MOV AH,0x00
INT 0x10
fin:
HLT
JMP fin
这个程序就是定位到内存的0xc200,然后显示个黑屏。int 0x10是显示器中断,关于BIOS基本的中断编号与功能,可参考《汇编语言程序设计-从DOS到Windows》下图:
这三行调用,就是让显示器清屏幕,13是模式号。生成目标文件用以下两句命令:
nasm ipl10.asm -o ipl10.bin
nasm haribote.asm -o haribote.bin
然后我们写一个生成镜像文件的java程序,这两个目标文件作为输入:
package day3;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
public class CreateOSImg3 {
public static void main(String[] args) throws Exception {
//写入0磁道,从这里加载
FileInputStream fis=new FileInputStream(new File("/Users/chengzhong/work/30_workspace/03_day/cz_work/ipl10.bin"));
//写入0x4200,启动区,系统入口
FileInputStream fis2=new FileInputStream(new File("/Users/chengzhong/work/30_workspace/03_day/cz_work/haribote2.bin"));
FileOutputStream fos=new FileOutputStream(new File("/Users/chengzhong/work/30_workspace/03_day/cz_work/javaos3.img"));
int totalLen=1474560;
byte src[]=new byte[fis.available()];
fis.read(src);
//总共有1474560个字节,
for(int i=0;i
if(i
fos.write(src[i]);
}else if(i>=0x4200&&i
fos.write(fis2.read());
}else {
fos.write(0);
}
}
fis.close();
fis2.close();
fos.close();
}
}
运行映像文件,可以看到效果与作者的工具生成的文件效果一样。
2.把c语言的机器码放进软盘c语言生成的文件放到映像文件的过程比较复杂,《30天自制操作系统》是这样做的:
我们现在要做的事,使用通用的开源软件来替换作者提供的cc1,nask等工具.因为这些工具屏蔽了一些操作,且没有文档,不利于我们后面自主开发。当然对于和我一样c语言与汇编开发经验不足的人来说是有些难度。但经过几天的研究,初步实现了这一目标。首先,我们用gcc生成obj2bim.exe与bim2hrb.exe这两文件。这两个文件涉及链接过程,但我们目前不用了解编译原理及其内部细节也能干活。这两个文件用作者提供的源码和MinGW就可以正常的生成,真是太好了。
这里先说明下环境:
win7,64位,MinGW32位,nasm x86安装包。环境变量已配好,可使用gcc,nasm命令。cd 光盘文件/omake/tolsrc/bim2hrb
gcc bim2hrb.c
这样就生成了bim2hrb.exe
cd 光盘文件/omake/tolsrc/obj2bi4d
gcc -c obj2bim.c
gcc -c autodec_.c
gcc obj2bim.o autodec_.o -o obj2bim.exe
这样就生成了obj2bim.exe
然后,我们处理c语言如何编译与链接的问题。作者的做法如上图,都转成了nas,再用他的nask那一套工具来生成。我尝试成功的方案是这样,先用gcc生成汇编文件,再用as工具(gcc带的)生成目标文件。
还有对汇编文件的编译,对于ipl10与asmhead,直接用以下命令生成最终机器语言文件即可:
nasm asmhead.asm -o asmhead.bin
nasm ipl10.asm -o ipl10.bin
但对于naskfunc(一个汇编程序文件),因为c语言要调用他的函数,所以不能直接生成机器语言文件。而要先生成目标文件,才能保留里面的函数名等信息。命令如下:
nasm -f coff naskfunc.asm -o naskfunc.obj
文件都准备好了就可以用obj2bim把c语言与汇编文件链接到一起了,命令如下:
obj2bim.exe @光盘文件\tools\haribote\haribote.rul out:bootpack.bim stack:3136k map:bootpack.map bootpack.obj naskfunc.obj
看到这儿有人可能会问haribote.rul是什么?我打开发现是个配置文件,他依赖了两个lib
harilibc.lib与golibc.lib 。这是两静态连接库文件。有人就要说了,你这搞得不彻底啊,怎么还有未知(无源码)文件?其实我是研究过这两文件的生成的,但c语言功底不行,没有搞定。他的生成依赖了cc1等,如果要彻底搞清楚,先得把cc1编译一下,这个c的编译器太麻烦了。对C语言熟悉的同学相信能搞定的。
之后,再用这两工具:
bim2hrb.exe bootpack.bim bootpack.hrb 0
copy /B asmhead.bin+bootpack.hrb haribote.sys
生成haribote.sys文件
最后是把这ipl10.bin与haribote.sys生成为一软盘映像文件,本来我以为可以用上面写的Java工具做这个事。但事与愿违,对比两个img文件,才想起来32位的寻址方式与16位不一样,还要考虑GDT等东西。edimg.exe这个工具生成:
cd 光盘文件/omake/tolsrc/edimg0j
gcc -c autodec_.c
gcc -c edimg.c
gcc edimg.o autodec_.o -o edimg.exe
edimg.exe的使用:
edimg.exe imgin:光盘文件/z_tools/fdimg0at.tek wbinimg src:ipl10.bin len:512 from:0 to:0 copy from:haribote.sys to:@: imgout:haribote.img
效果与第三天的一样:
现在文件已经不少了,可以用make管理起来了,我们用之前介绍的gnumake来打包。
把文件整理一下,工具放进tools文件夹,生成的文件放到bin文件夹:
写如下Makefile文件:
default:
gcc -S bootpack.c -o bin/bootpack.s
as -o bin/bootpack.obj bin/bootpack.s
nasm asmhead.asm -o bin/asmhead.bin
nasm -f coff naskfunc.asm -o bin/naskfunc.obj
nasm ipl10.asm -o bin/ipl10.bin
tools/obj2bim.exe @tools\haribote\haribote.rul out:bin/bootpack.bim stack:3136k map:bin/bootpack.map bin/bootpack.obj bin/naskfunc.obj
tools/bim2hrb.exe bin/bootpack.bim bin/bootpack.hrb 0
copy /B bin\asmhead.bin+bin\bootpack.hrb bin\haribote.sys
tools/edimg.exe imgin:tools/fdimg0at.tek wbinimg src:bin/ipl10.bin len:512 from:0 to:0 copy from:bin/haribote.sys to:@: imgout:bin/haribote.img
clean:
-del bin\bootpack.s
-del bin\bootpack.obj
-del bin\asmhead.bin
-del bin\naskfunc.obj
-del bin\ipl10.bin
-del bin\bootpack.bim
-del bin\bootpack.map
-del bin\bootpack.hrb
-del bin\haribote.sys
-del bin\haribote.img
下面用图总结一下,我们改造工具的方法,有颜色的代表对作者的工具还有依赖的步骤:
改造的这几天,都是摸着石头过河,说不定那一步就搞不定了。还好,其本都完成了。
这几天踩了一些坑在这记录下,不要在linux下或者windows的Cygwin使用gcc生成汇编文件,他们生成的都是linux风格的。后面再在window的nasm下生成obj文件,就不兼容了。
winxp已经安装不了MinGW了,不过可以用安装好的MinGW打个zip包放到xp里运行。可能win7 32位更适合本书相关代码的学习。
gas2nask工具的笔记,本文没用到,只为记录。
这步作者的工具是gas2nask.exe。这个工具比nask.exe的构建要简单些,于是我用作者提供的源码制作一个gas2nask.exe。
首先进入光盘文件\omake\tolsrc\go_0023s\toolstdc,键入命令:cl gas2nask.c就可以生成gas2nask.exe。这个比较简单,可以把gas2nask.c相关的源码文件转移一个新文件夹来生成。