0. 自己試着在win7下用NASM和minGW改寫彙編和C混合編程,結果受挫了。還是先使用作者提供的工具構建吧。
1. 通過前2天的工作已經能使用NASM製作一個映像了,並且編寫的彙編代碼可以成爲引導扇區代碼。
2. 引導扇區代碼中可以調用BIOS中斷,讀取軟盤上其它扇區到內存中,根據FAT12文件系統格式得知,保存到軟盤內的第一個文件的文件名一定從19邏輯扇區開始,且該文件的內容從邏輯扇區33開始(見day01,圖一)。
3. 引導扇區可以將第一個保存的文件(asmhead.nas,功能是跳入保護模式並調用C語言編寫的函數代碼)讀入內存並使之執行,找這個文件用到了一個技巧(如2描述),不然通過文件系統結構分析出文件位置,並加載碼就太複雜了。
4. 引導扇區代碼如下ipl10.asm
CYLS EQU 10
ORG 0x7c00
JMP entry
DB 0x90
DB "HARIBOTE"
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 "HARIBOTEOS "
DB "FAT12 "
RESB 18
entry:
MOV AX,0
MOV SS,AX
MOV SP,0x7c00
MOV DS,AX
MOV AX,0x0820 ;目的地址
MOV ES,AX
MOV CH,0 ;柱面號
MOV DH,0 ;磁頭號
MOV CL,2 ;扇區號
readloop:
MOV SI,0
retry:
MOV AH,0x02 ;讀磁盤
MOV AL,1 ;讀1個扇區
MOV BX,0 ;目的地址
MOV DL,0x00 ;驅動器號
INT 0x13 ;來BIOS中斷
JNC next
ADD SI,1
CMP SI,5
JAE error ;讀五次還失敗就放棄
MOV AH,0x00 ;重置驅動器功能號
MOV DL,0x00 ;驅動器號
INT 0x13
JMP retry
next:
MOV AX,ES
ADD AX,0x0020 ;保存位置向後移動512字節,0x0020是段地址加偏移量後成爲0x0200了
MOV ES,AX
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
CMP CH,CYLS
JB readloop
MOV [0x0ff0],CH
JMP 0xc200 ;跳到軟盤kernel.sys(asmhead.nas+bootpack.c)文件內部執行
;該文件在加載軟盤文件基址0x8000+Fat12文件系統中文件出現位置0x004200處=0xc200H
error:
MOV SI,msg
putloop:
MOV AL,[SI]
ADD SI,1
CMP AL,0
JE fin
MOV AH,0x0e
MOV BX,15
INT 0x10
JMP putloop
fin:
HLT
JMP fin
msg:
DB 0x0a, 0x0a
DB "load error"
DB 0x0a
DB 0;
RESB 0x7dfe-$
times 510-($-$$) db 0
DB 0x55, 0xaa
BIOS 13中斷說明(功能有磁盤的讀、寫、扇區校驗、尋道)
AH=0x02 讀盤/0x03寫盤/0x04校驗/0x0c尋道
AL=處理連續扇區數
CH=柱面號&0xff
CL=扇區號(0~5位)|(柱面號&0x300)>>2
DH=磁頭號
DL=驅動器號
ES:BX=緩衝地址
返回值:FLAGS=0沒有錯誤AH=0,FLAGS=1有錯誤AH保存錯誤碼
5. 跳入保護模式代碼如下asmhead.nas(1)準備GDT(2)用LGDT加載gdtr(3)打開A20(4)設置CR0的PE位(5)跳轉進入保護模式
BOTPAK EQU 0x00280000
DSKCAC EQU 0x00100000
DSKCAC0 EQU 0x00008000
CYLS EQU 0x0ff0
LEDS EQU 0x0ff1
VMODE EQU 0x0ff2
SCRNX EQU 0x0ff4
SCRNY EQU 0x0ff6
VRAM EQU 0x0ff8
ORG 0xc200 ;讓引導扇區加載後從這裏開始運行,認爲它們在一個段中所以能直接跳轉過來
MOV AL,0x13 ;保存信息
MOV AH,0x00
INT 0x10
MOV BYTE [VMODE],8
MOV WORD [SCRNX],320
MOV WORD [SCRNY],200
MOV DWORD [VRAM],0x000a0000
MOV AH,0x02
INT 0x16 ; keyboard BIOS
MOV [LEDS],AL
MOV AL,0xff ;禁止PIC主從片的中斷
OUT 0x21,AL
NOP ;不能有兩個連續的OUT指令
OUT 0xa1,AL
CLI ;禁止PIC工作要在CLI之前
CALL waitkbdout ;等待鍵盤電路準備好,要設置A20使1MB以上內存能被訪問
MOV AL,0xd1
OUT 0x64,AL
CALL waitkbdout
MOV AL,0xdf ; enable A20
OUT 0x60,AL
CALL waitkbdout
[INSTRSET "i486p"]
LGDT [GDTR0] ;加載臨時的GDT表首地址到GDTR寄存器
MOV EAX,CR0
AND EAX,0x7fffffff
OR EAX,0x00000001
MOV CR0,EAX ;設置CR0寄存器PE標誌位
JMP pipelineflush ;設置標誌位之後馬上JMP
pipelineflush: ;從此尋址方式變了
MOV AX,1*8 ;段值的設置,用GDT中那個段(GDT+1段)
MOV DS,AX
MOV ES,AX
MOV FS,AX
MOV GS,AX
MOV SS,AX
MOV ESI,bootpack ;將bootpack.c生成的目標代碼移動到0x00280000,乾坤大挪移
MOV EDI,BOTPAK
MOV ECX,512*1024/4
CALL memcpy
MOV ESI,0x7c00 ;將啓動扇區複製到1MB以後的內存
MOV EDI,DSKCAC
MOV ECX,512/4
CALL memcpy
MOV ESI,DSKCAC0+512 ;將0x00008200數據複製到0x00100200
MOV EDI,DSKCAC+512
MOV ECX,0
MOV CL,BYTE [CYLS]
IMUL ECX,512*18*2/4
SUB ECX,512/4
CALL memcpy
;至此內存中0x00100000部分與磁盤內容就一樣了
;調用bootpack.c的初始化操作,解析bootpack.hrb的header並傳入參數
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 ;跳到指定段中跳過可執行文件頭p421
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: ;臨時設計的GDT表
RESB 8
DW 0xffff,0x0000,0x9200,0x00cf
DW 0xffff,0x0000,0x9a28,0x0047
DW 0
GDTR0: ;臨時設計的GDT選擇子
DW 8*3-1
DD GDT0
ALIGNB 16
bootpack:
6. C語言代碼如下bootpack.c
void io_hlt(void);
void write_mem8(int addr,int data);
void HariMain(void)
{
int i;
for(i=0xa0000;i<=0xaffff;i++)
{
write_mem8(i,15); /*向內存的0xa0000~0xaffff位置寫入信息,這塊內存是顯存空間,15是白色*/
}
for(;;)
{
io_hlt();
}
}
7. C語言中調用的io_hlt和write_mem8函數放到了如下代碼中func.asm
[FORMAT "WCOFF"]
[INSTRSET "i486p"]
[BITS 32]
[FILE "naskfunc.nas"]
global _io_hlt,_write_mem8
[section .text]
;void io_hlt(void);
_io_hlt:
HLT
RET
;void write_mem8(int addr,int data);
_write_mem8:
MOV ECX,[ESP+4]
MOV AL,[ESP+8]
MOV [ECX],AL
RET
8. 在toolset文件夾內建立一個新文件夾,將上面所有的文件放在裏邊,編譯鏈接接上面的文件,寫個bat文件如下
nasm -o ipl10.bin ipl10.asm ;生成引導扇區代碼
nasm -o img.img img.asm ;生成軟盤鏡像文件
..\z_tools\nask.exe asmhead.nas asmhead.bin
..\z_tools\cc1.exe -I..\z_tools\haribote\ -Os -Wall -quiet -o bootpack.gas bootpack.c
..\z_tools\gas2nask.exe -a bootpack.gas bootpack.nas
..\z_tools\nask.exe bootpack.nas bootpack.obj
..\z_tools\nask.exe func.asm func.obj
..\z_tools\obj2bim.exe @..\z_tools\haribote\haribote.rul out:bootpack.bim stack:3136k map:bootpack.map bootpack.obj func.obj
..\z_tools\bim2hrb.exe bootpack.bim bootpack.hrb 0
copy /B asmhead.bin+bootpack.hrb kernel.sys
9.這樣除了中間文件外,生成img.img文件和kernel.sys文件。使用winImage打開img.img文件將kernel.sys文件加入到該img文件中。
10. 啓動Bochs,呵呵看見屏幕白了,這可是從C代碼裏控制的啊!
11. asmhead中跳入保護模式的代碼慢慢在深入掌握,不然會掉入細節裏不能自拔了。
12. (這句很經典)asmhead和C代碼是通過copy /B進行鏈接的其中asmhead代碼最後留了個標號bootpack,在這個標號後面C的目標代碼被砍去文件頭直接將代碼鏈接到了這裏,所以能實現從彙編跳轉到C語言的目的。