《30天自制操作系統》讀書筆記(3) 引入C語言


這一次的學習相當曲折, 主要是因為粗心, Makefile里面的錯誤導致了文件生成出現各種奇奇怪怪的問題, 弄得心力交瘁, 因此制作過程還是盡量按着作者的路子來吧.

作者提供的源碼的注釋在中文系統下是亂碼, 而且代碼的分隔用了兩個Tab, 在這里要處理一下:

:%s/;.*//g 刪除所有的注釋;

:%s/\t\t/\t 把兩個Tab替換為一個Tab;

要讓作者的nas文件和asm文件擁有相同的語法規則, 在_vimrc文件的最后一行添加

au BufNewFile,BufRead *.nas set filetype=asm

  • 真正的IPL

    之前我們寫的只是軟盤啟動區代碼, 所有代碼只能放在512字節里, 根本不夠用,

所以我們要利用這一段代碼讀入軟盤的其他內容, 並將控制權移交給它, 所以叫做IPL(Initial Program Loader).

  • 需要的知識點:

int 0x13 磁盤中斷

AH = 0x02    ; 讀盤

AH = 0x03    ; 寫盤

AH = 0x04    ; 校驗

AH = 0x0c    ; 尋道

AH = 0x00    ;大概是重置磁盤, 配合DH = 0x00 使用

AL = 連續處理的扇區數

CH = 柱面號

CL = 扇區號

DH = 磁頭號

DL = 驅動器號

ES:BX = 緩沖地址, 尋道及校驗不使用

C = 0 沒有錯誤, AH = 0;

C = 1 錯誤號碼存入AH.

這里只會用到0x02和0x00.

軟盤的結構:

    用柱面(C)-磁頭(H)-扇區(S) 來標識軟盤上的扇區, 我們的啟動扇區的標號是C0-H0-S1, 值得注意的是下一個扇區是C0-H0-S2, 這個例子里作者要讀入10個柱面, 從C0-H0-S1 讀到 C9-H1-S18, 這樣算來應該是18*2*10 = 360 個扇區, 大小是360*512/1024 = 180 KB, 從我們現在小的可憐的需求看來, 180KB絕對夠了.(按這樣算的話不是一個柱面有36個扇區嗎? 此處不解).

 

以下的代碼能將軟盤除啟動區外開始的180KB的空間讀入內存中, 起始地址是0x8200, 占用的空間是0x2d000.

  • 執行軟盤中的內容

    編寫Haribote.nas文件如下, 結構很簡單:

    1 fin:
    2     HLT
    3     JMP    fin 

     

    問題是如何把編譯好的程序和IPL結合在一起?

    我們可以先編譯好IPL, 和之前一樣做成一個img, 然后把程序寫入img.

    最直觀的方法是把img文件寫入U盤, 把程序寫入U盤, 再把U盤打包成img,

雖然有些迂回, 但是我們可以借助WinHex來直接寫入, 用WinHex直接打開作者做好的Haribote.img ,可以發現它的內部格式如下:

發現Haribote.SYS處在0x4200的位置處 , 這是不是真的Haribote.sys?

也從Winhex里面打開Hraibote.sys:

只有一句, F4 EB FD, 和img文件0x4200處的完全一樣, 和作者的總結一樣:

向一個空軟盤保存文件的時候,

1.文件名會寫在0x2600以后的地方(這里我沒有去驗證);

2.文件的內容會寫入到0x4200以后的地方.

非空軟盤的情況沒有考慮(這里開始我們就真正意義上使用了FAT12的文件系統了, 而不是自己設計一個文件系統).

知道了這個我們就可以知道怎么調用這個程序了, 使用作者提供的edimg.exe 可以方便地將程序寫入到img中, 所以Makefile也要做相應的改動, 具體看Project文件夾day3里面的e文件夾.

這個Haribote.nas 所做的只是不斷HLT, 我們並不知道這段代碼是否真的執行, 作者這里使用了0x10來切換畫面模式, 使得光標消失,

參數是AL = 0x13, AH = 0x00, 效果是屏幕全黑, 連光標也不能看到.

我這里采用的是0x10中斷的另一個功能, 能夠顯示帶顏色的字符串, 代碼如下:

 1 ; Program
 2     ORG    0xc200    ; 加載到 0x8200 + 0x4200 – 0x200(啟動區沒有被讀入)
 3     MOV    AX,0
 4     MOV    ES,AX
 5     MOV    AX,loaded
 6     MOV    BP,AX    ; ES:BP = 串地址
 7     MOV    CX,10        ; 串長度
 8     MOV    AX,0x1301    ;AH = 0x13,AL = 0x01
 9     MOV    BX,0x000b    ; 頁號BH = 0 黑底藍字 BL = 0x0b
10     MOV    DL,0
11     INT    0x10
12     JMP fin
13 loaded:
14     DB    0x0a, 0x0a    
15     DB    "Loaded."
16     DB    0x0a        
17     DB    0
18  
19 fin:    
20     HLT
21     JMP fin 

 

 

效果如下, 可以確定我們Haribote.sys里面的代碼已經被執行.

 

  • 進入32位模式&導入C語言*

這里作者開始用我們看不懂的東西了, 給出了一個長長的asmhead.nas, 並且表示"先跳過這一部分", 讓我有點不爽, 但是畢竟不能半途而廢不是? 這里的Makefile變得越來越復雜, 在這里我第一次碰到了困難, 由於粗心Makefile寫少了幾行, 導致編譯莫名其妙地出錯, 糾結了兩天才發現, 抄代碼都能抄錯 = =, 廢話略過, 作者還增加了一個Bootpack.c 大概就是我們以后的主戰場了, 這里編譯過程異常繁瑣(作者啊你可不可以不要用你自己寫的工具!? (╯‵□′)╯︵┻━┻),

以上所有的工具都是川和先生寫的或者改寫的(╯‵□′)╯︵┻━┻.

需要強調的是HariMain這個Main函數的函數名已經寫死了, 不能夠更換, 為了在C

語言里面使用HLT, (事實上現在C語言里面根本就沒有函數可用), 增加naskfunc.nas.

所有文件均如下:

 

IPL.nas:

; hello-os
; TAB=4
; const

CYLS    EQU    10
    
    ORG    0x7c00    

    JMP    entry
    DB    0x90
    DB    "OS 0.01 "    
    DW    512    
    DB    1
    DW    1    
    DB    2
    DW    224    
    DW    2880    
    DB    0xf0    
    DW    9
    DW    18        
    DW    2        
    DD    0    
    DD    2880    
    DB    0,0,0x29    
    DD    0xffffffff    
    DB    "OS ver 0.01"    
    DB    "FAT12 "    
    RESB    18        

    
entry:
    MOV    AX,0
    MOV    SS,AX
    MOV    SP,0x7c00
    MOV    DS,AX

   
    MOV    AX,0x0820    ; 緩存位置 = ES:BX
    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
    CMP    SI,5    ; 失敗5次跳出
    JAE    error
    MOV    AH,0x00
    MOV    DL,0x00    ; 指定驅動器A:
    INT    0x13    ; 重置並重試
    JMP    retry   
next: MOV AX,ES ADD AX,0x0020 MOV ES,AX ; 段寄存器往后移0x0020 實際偏移 0x0020*0x0010 = 0x0200 = 512d ADD 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 ; 柱面+1 CMP CH,CYLS JB readloop MOV [0x0ff0],CH JMP 0xc200 error: ; 錯誤提示 MOV AX,0 MOV ES,AX MOV AX,msg MOV BP,AX ; ES:BP = 串地址 MOV CX,14 ; 串長度 MOV AX,0x1301 ;AH = 0x13,AL = 0x01 MOV BX,0x000c ; 頁號BH = 0 黑底紅字 BL = 0x0c MOV DL,0 INT 0x10 fin: HLT JMP fin msg: DB 0x0a, 0x0a DB "Load error." DB 0x0a DB 0 RESB 0x7dfe-$ DB 0x55, 0xaa

 

 

Asmhead.nas:

BOTPAK    EQU    0x00280000    
DSKCAC    EQU    0x00100000    
DSKCAC0    EQU    0x00008000    

 

CYLS    EQU    0x0ff0    ; 設定啟動區    
LEDS    EQU    0x0ff1    
VMODE    EQU    0x0ff2    ; 關於顏色數目的信息,顏色的位數    
SCRNX    EQU    0x0ff4    ; 分辨率 X(Screen X)    
SCRNY    EQU    0x0ff6    ; 分辨率 Y(Screen Y)    
VRAM    EQU    0x0ff8    ; 圖像緩沖區的起始地址    

    ORG    0xc200       

    MOV    AL,0x13    ; VGA 顯卡    
    MOV    AH,0x00
    INT    0x10
MOV BYTE [VMODE],8 ; 記錄畫面模式 MOV WORD [SCRNX],320 MOV WORD [SCRNY],200 MOV DWORD [VRAM],0x000a0000 ; 取得鍵盤上的LED燈的狀態 MOV AH,0x02 INT 0x16 MOV [LEDS],AL MOV AL,0xff OUT 0x21,AL NOP OUT 0xa1,AL CLI CALL waitkbdout MOV AL,0xd1 OUT 0x64,AL CALL waitkbdout MOV AL,0xdf OUT 0x60,AL CALL waitkbdout [INSTRSET "i486p"] LGDT [GDTR0] MOV EAX,CR0 AND EAX,0x7fffffff OR EAX,0x00000001 MOV CR0,EAX JMP pipelineflush pipelineflush: MOV AX,1*8 MOV DS,AX MOV ES,AX MOV FS,AX MOV GS,AX MOV SS,AX 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 SUB ECX,512/4 CALL memcpy MOV EBX,BOTPAK MOV ECX,[EBX+16] ADD ECX,3 SHR ECX,2 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 RET memcpy: MOV EAX,[ESI] ADD ESI,4 MOV [EDI],EAX ADD EDI,4 SUB ECX,1 JNZ memcpy RET ALIGNB 16 GDT0: RESB 8 DW 0xffff,0x0000,0x9200,0x00cf DW 0xffff,0x0000,0x9a28,0x0047 DW 0 GDTR0: DW 8*3-1 DD GDT0 ALIGNB 16

 

bootpack.c:

 

void io_hlt(void);

void HariMain(void)
{

fin:
    io_hlt();
    goto fin;
}

 

 

 

Naskkfunc.nas:

;naskfunc
[FORMAT "WCOFF"]        ; 制作目標文件的模式
[BITS 32]            ; 制作32位模式用的機械語言

; 制作目標文件的信息

[FILE "naskfunc.nas"]        ; 源文件名信息
    GLOBAL    _io_hlt        ; 程序中包含的函數名

; 實際的函數
[SECTION .text]    ;
_io_hlt:    
    HLT
    RET
void io_hlt(void); void HariMain(void) { fin: io_hlt(); goto fin; }

 

 

萬惡的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
DEL = del
SHORTCUT = "D:\Program Files\Oracle\VirtualBox\VirtualBox.exe" --comment "OS1" --startvm "a5c4b0e6-e142-4720-98ee-056911204b29"

default :
    $(MAKE) install
    $(MAKE) run
    $(MAKE) clean


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 naskfucn.lst

bootpack.bim : bootpack.obj naskfunc.obj Makefile
    $(OBJ2BIM) @$(RULEFILE) out:bootpack.bim stack:3136k map:bootpack.map \
        bootpack.obj naskfunc.obj
    echo error

# 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

install:
    $(MAKE) haribote.img

run :
    echo Running...
    $(SHORTCUT)
    echo Finished

clean :
    -$(DEL) *.bin
    -$(DEL) *.lst
    -$(DEL) *.gas
    -$(DEL) *.obj
    -$(DEL) bootpack.nas
    -$(DEL) bootpack.map
    -$(DEL) bootpack.bim
    -$(DEL) bootpack.hrb
    -$(DEL) haribote.sys
    -$(DEL) *.*~
    -$(DEL) *~
    echo Finished. 

 

 

 

這次更新慢了, 下次爭取早一點…

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM