这一章是有些复杂的,我不太懂作者为什么要把这么多内容都放进一天。
1读入了十个柱面
2从启动区执行操作系统
3进入32位
4导入C语言
makefile的内容:
TOOLPATH = ../z_tools/ INCPATH = ../z_tools/haribote/ MAKE = $(TOOLPATH)make.exe -r NASK = $(TOOLPATH)nask.exe CC1 = $(TOOLPATH)cc1.exe -I$(INCPATH) -Os -Wall -quiet GAS2NASK = $(TOOLPATH)gas2nask.exe -a OBJ2BIM = $(TOOLPATH)obj2bim.exe BIM2HRB = $(TOOLPATH)bim2hrb.exe RULEFILE = $(TOOLPATH)haribote/haribote.rul EDIMG = $(TOOLPATH)edimg.exe IMGTOL = $(TOOLPATH)imgtol.com COPY = copy DEL = del default : $(MAKE) img ipl10.bin : ipl10.nas Makefile $(NASK) ipl10.nas ipl10.bin ipl10.lst asmhead.bin : asmhead.nas Makefile $(NASK) asmhead.nas asmhead.bin asmhead.lst bootpack.gas : bootpack.c Makefile $(CC1) -o bootpack.gas bootpack.c bootpack.nas : bootpack.gas Makefile $(GAS2NASK) bootpack.gas bootpack.nas bootpack.obj : bootpack.nas Makefile $(NASK) bootpack.nas bootpack.obj bootpack.lst naskfunc.obj : naskfunc.nas Makefile $(NASK) naskfunc.nas naskfunc.obj naskfunc.lst bootpack.bim : bootpack.obj naskfunc.obj Makefile $(OBJ2BIM) @$(RULEFILE) out:bootpack.bim stack:3136k map:bootpack.map bootpack.obj naskfunc.obj # 3MB+64KB=3136KB bootpack.hrb : bootpack.bim Makefile $(BIM2HRB) bootpack.bim bootpack.hrb 0 haribote.sys : asmhead.bin bootpack.hrb Makefile copy /B asmhead.bin+bootpack.hrb haribote.sys haribote.img : ipl10.bin haribote.sys Makefile $(EDIMG) imgin:../z_tools/fdimg0at.tek wbinimg src:ipl10.bin len:512 from:0 to:0 copy from:haribote.sys to:@: imgout:haribote.img img : $(MAKE) haribote.img run : $(MAKE) img $(COPY) haribote.img ..\z_tools\qemu\fdimage0.bin $(MAKE) -C ../z_tools/qemu install : $(MAKE) img $(IMGTOL) w a: haribote.img clean : -$(DEL) *.bin -$(DEL) *.lst -$(DEL) *.gas -$(DEL) *.obj -$(DEL) bootpack.nas -$(DEL) bootpack.map -$(DEL) bootpack.bim -$(DEL) bootpack.hrb -$(DEL) haribote.sys src_only : $(MAKE) clean -$(DEL) haribote.img
注意end.exe,在第二天,作者提到:自己开发的磁盘映像管理工具end.exe,先读入一个空白的磁盘映像文件,然后在开头写入ipl.bin的内容,最后将结果输出为名为helloos.img的磁盘映像文件。
这样一来,逻辑上就很简单了,用makefile,调用end.exe,将ipl10.nas和操作系统代码,制成一个img文件。ipl10.nas是启动区。最后操作系统的启动是,在启动区启动。
然后我们用工具观察制成的img,会发现:
作者得出的结论:
一般向一个空软盘保存文件时
(1)文件名会写在0x002600以后的地方;
(2)文件内容会写在0x004200以后的地方。
所以,要执行磁盘映像上位于0x004200号地址的程序,现在的程序是从启动区开始,把磁盘上的内容装载到内存0x8000号地址,所以磁盘0x4200处的内容就应该位于内存0x8000+0x4200=0xc200号地址。
所以从启动区,启动操作系统就只需要jmp到0xc200地址即可。
ipl10.nas:
; haribote-ipl ; TAB=4 CYLS EQU 10 ; どこまで読み込むか ORG 0x7c00 ; このプログラムがどこに読み込まれるのか ; 以下は標準的なFAT12フォーマットフロッピーディスクのための記述 JMP entry DB 0x90 DB "HARIBOTE" ; ブートセクタの名前を自由に書いてよい(8バイト) DW 512 ; 1セクタの大きさ(512にしなければいけない) DB 1 ; クラスタの大きさ(1セクタにしなければいけない) DW 1 ; FATがどこから始まるか(普通は1セクタ目からにする) DB 2 ; FATの個数(2にしなければいけない) DW 224 ; ルートディレクトリ領域の大きさ(普通は224エントリにする) DW 2880 ; このドライブの大きさ(2880セクタにしなければいけない) DB 0xf0 ; メディアのタイプ(0xf0にしなければいけない) DW 9 ; FAT領域の長さ(9セクタにしなければいけない) DW 18 ; 1トラックにいくつのセクタがあるか(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へ ; 読み終わったので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 ; 改行を2つ DB "load error" DB 0x0a ; 改行 DB 0 RESB 0x7dfe-$ ; 0x7dfeまでを0x00で埋める命令 DB 0x55, 0xaa
asmhead.nas
; haribote-os boot asm ; TAB=4 BOTPAK EQU 0x00280000 ; bootpackのロード先 DSKCAC EQU 0x00100000 ; ディスクキャッシュの場所 DSKCAC0 EQU 0x00008000 ; ディスクキャッシュの場所(リアルモード) ; BOOT_INFO関係 CYLS EQU 0x0ff0 ; ブートセクタが設定する LEDS EQU 0x0ff1 VMODE EQU 0x0ff2 ; 色数に関する情報。何ビットカラーか? SCRNX EQU 0x0ff4 ; 解像度のX SCRNY EQU 0x0ff6 ; 解像度のY VRAM EQU 0x0ff8 ; グラフィックバッファの開始番地 ORG 0xc200 ; このプログラムがどこに読み込まれるのか ; 画面モードを設定 MOV AL,0x13 ; VGAグラフィックス、320x200x8bitカラー MOV AH,0x00 INT 0x10 MOV BYTE [VMODE],8 ; 画面モードをメモする(C言語が参照する) MOV WORD [SCRNX],320 MOV WORD [SCRNY],200 MOV DWORD [VRAM],0x000a0000 ; キーボードのLED状態をBIOSに教えてもらう MOV AH,0x02 INT 0x16 ; keyboard BIOS MOV [LEDS],AL ; PICが一切の割り込みを受け付けないようにする ; AT互換機の仕様では、PICの初期化をするなら、 ; こいつをCLI前にやっておかないと、たまにハングアップする ; PICの初期化はあとでやる MOV AL,0xff OUT 0x21,AL NOP ; OUT命令を連続させるとうまくいかない機種があるらしいので OUT 0xa1,AL CLI ; さらにCPUレベルでも割り込み禁止 ; CPUから1MB以上のメモリにアクセスできるように、A20GATEを設定 CALL waitkbdout MOV AL,0xd1 OUT 0x64,AL CALL waitkbdout MOV AL,0xdf ; enable A20 OUT 0x60,AL CALL waitkbdout ; プロテクトモード移行 [INSTRSET "i486p"] ; 486の命令まで使いたいという記述 LGDT [GDTR0] ; 暫定GDTを設定 MOV EAX,CR0 AND EAX,0x7fffffff ; bit31を0にする(ページング禁止のため) OR EAX,0x00000001 ; bit0を1にする(プロテクトモード移行のため) MOV CR0,EAX JMP pipelineflush pipelineflush: MOV AX,1*8 ; 読み書き可能セグメント32bit MOV DS,AX MOV ES,AX MOV FS,AX MOV GS,AX MOV SS,AX ; bootpackの転送 MOV ESI,bootpack ; 転送元 MOV EDI,BOTPAK ; 転送先 MOV ECX,512*1024/4 CALL memcpy ; ついでにディスクデータも本来の位置へ転送 ; まずはブートセクタから MOV ESI,0x7c00 ; 転送元 MOV EDI,DSKCAC ; 転送先 MOV ECX,512/4 CALL memcpy ; 残り全部 MOV ESI,DSKCAC0+512 ; 転送元 MOV EDI,DSKCAC+512 ; 転送先 MOV ECX,0 MOV CL,BYTE [CYLS] IMUL ECX,512*18*2/4 ; シリンダ数からバイト数/4に変換 SUB ECX,512/4 ; IPLの分だけ差し引く CALL memcpy ; asmheadでしなければいけないことは全部し終わったので、 ; あとはbootpackに任せる ; bootpackの起動 MOV EBX,BOTPAK MOV ECX,[EBX+16] ADD ECX,3 ; ECX += 3; SHR ECX,2 ; ECX /= 4; JZ skip ; 転送するべきものがない MOV ESI,[EBX+20] ; 転送元 ADD ESI,EBX MOV EDI,[EBX+12] ; 転送先 CALL memcpy skip: MOV ESP,[EBX+12] ; スタック初期値 JMP DWORD 2*8:0x0000001b waitkbdout: IN AL,0x64 AND AL,0x02 JNZ waitkbdout ; ANDの結果が0でなければwaitkbdoutへ RET memcpy: MOV EAX,[ESI] ADD ESI,4 MOV [EDI],EAX ADD EDI,4 SUB ECX,1 JNZ memcpy ; 引き算した結果が0でなければmemcpyへ RET ; memcpyはアドレスサイズプリフィクスを入れ忘れなければ、ストリング命令でも書ける ALIGNB 16 GDT0: RESB 8 ; ヌルセレクタ DW 0xffff,0x0000,0x9200,0x00cf ; 読み書き可能セグメント32bit DW 0xffff,0x0000,0x9a28,0x0047 ; 実行可能セグメント32bit(bootpack用) DW 0 GDTR0: DW 8*3-1 DD GDT0 ALIGNB 16 bootpack:
这里前一部分是对32位模式的启动,后一部分是对C语言的准备。
bootpack.c:
/* 他のファイルで作った関数がありますとCコンパイラに教える */ void io_hlt(void); /* 関数宣言なのに、{}がなくていきなり;を書くと、 他のファイルにあるからよろしくね、という意味になるのです。 */ void HariMain(void) { fin: io_hlt(); /* これでnaskfunc.nasの_io_hltが実行されます */ goto fin; }
时间: 2024-11-02 15:33:57