《30天自制操作系统》第三天

Posted NONE

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《30天自制操作系统》第三天相关的知识,希望对你有一定的参考价值。

这一章是有些复杂的,我不太懂作者为什么要把这么多内容都放进一天。

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;

}

 

以上是关于《30天自制操作系统》第三天的主要内容,如果未能解决你的问题,请参考以下文章

「七天自制PHP框架」第三天:PHP实现的设计模式

《30天自制操作系统》第二天

30天python从零到一---第三天:python基础语法

《30天自制操作系统》第9天

30天自制操作系统——第十七天制作命令行窗口

《30天自制操作系统》第一天