這一次的學習相當曲折, 主要是因為粗心, 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.
這次更新慢了, 下次爭取早一點…