目錄:
1、用0-1編寫最簡單的操作系統
2、用匯編改寫上面0-1程序
2.1 只用DB的匯編改寫版 2.2 加入RESB匯編的改寫版 2.3 進一步使用匯編替換0-1文件 2.4 核心程序也用匯編改寫 2.5 向匯編程序中加入IPL(啟動程序裝載器) 2.6 從啟動區執行操作系統(讀盤的應用)
3、匯編和C語言混合開發
3.1 32位開發及C語言混合開發引入 3.2 匯編引入C語言(用匯編寫C語言函數) 3.3 C語言實現內存寫入 3.4 C語言指針的強大 3.5 色號設定與調色板 3.6 簡單界面實現
1、用0-1編寫最簡單的操作系統
>_<" 用二進制編輯器(Binary Editor),輸入下面的內容(這里是16進制的)
PS: 如上圖,藍色部分要仔細,下面一直到168000這個地址都是00,在0001F0和001400附近還有些地方不是0,要把他們改過來,檢查一遍,保存為helloos.img
>_<" 將這個文件寫入軟盤用來啟動電腦就能顯示“hello,world”這個字符串。
PS: 上面界面是用了一個PC模擬器
2、用匯編改寫上面0-1程序
>_<" 這里就把上述超長的0-1文件改成匯編語言文件helloos.nas,然后調用nask.exe編譯器將.nas文件編譯為.img~剩下的就不用說了~
2.1 只用DB的匯編改寫版
1 DB 0xeb, 0x4e, 0x90, 0x48, 0x45, 0x4c, 0x4c, 0x4f 2 DB 0x49, 0x50, 0x4c, 0x00, 0x02, 0x01, 0x01, 0x00 3 DB 0x02, 0xe0, 0x00, 0x40, 0x0b, 0xf0, 0x09, 0x00 4 DB 0x12, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 5 DB 0x40, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x29, 0xff 6 (為節省紙張,這里省略18萬4314行) 7 DB 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
PS:DB是data byte的縮寫,就是往文件里直接寫入1字節的指令~這樣就實現了匯編實現最上面的0-1文件,但是貌似看起來更長了....(如果足夠牛B的話,只用DB就能做出任何文件)
2.2 加入RESB匯編的改寫版
1 DB 0xeb, 0x4e, 0x90, 0x48, 0x45, 0x4c, 0x4c, 0x4f 2 DB 0x49, 0x50, 0x4c, 0x00, 0x02, 0x01, 0x01, 0x00 3 DB 0x02, 0xe0, 0x00, 0x40, 0x0b, 0xf0, 0x09, 0x00 4 DB 0x12, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 5 DB 0x40, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x29, 0xff 6 DB 0xff, 0xff, 0xff, 0x48, 0x45, 0x4c, 0x4c, 0x4f 7 DB 0x2d, 0x4f, 0x53, 0x20, 0x20, 0x20, 0x46, 0x41 8 DB 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00 9 RESB 16 10 DB 0xb8, 0x00, 0x00, 0x8e, 0xd0, 0xbc, 0x00, 0x7c 11 DB 0x8e, 0xd8, 0x8e, 0xc0, 0xbe, 0x74, 0x7c, 0x8a 12 DB 0x04, 0x83, 0xc6, 0x01, 0x3c, 0x00, 0x74, 0x09 13 DB 0xb4, 0x0e, 0xbb, 0x0f, 0x00, 0xcd, 0x10, 0xeb 14 DB 0xee, 0xf4, 0xeb, 0xfd, 0x0a, 0x0a, 0x68, 0x65 15 DB 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72 16 DB 0x6c, 0x64, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 ;到000080前8個的內容,后8個和000090之后填0,部分做修改 17 RESB 368 18 DB 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa ;這就是0001F0處非0的內容 19 DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 20 RESB 4600 21 DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 ;伙計發現沒,這就是001400地址處的內容 22 RESB 1469432
PS: 這里多加入了一個匯編指令RESB,就使文件大致清新多了~這里RESB指令是reserve byte的縮寫,如果想從某個字節開始空出10個字節就能寫成RESB 10,這樣我們就節約了大約10萬行代碼~
2.3 進一步使用匯編替換0-1文件
>_<" 雖然上面把10幾萬行的0-1改寫的只有20幾行,但是還是無法理解其意思,下面進行優化,使我們能理解代碼的意思~
1 ; hello-os 2 ; TAB=4 3 4 ; 以下段落是標准FAT12格式軟盤專用代碼 5 DB 0xeb, 0x4e, 0x90 ; 注意和我們寫的0-1代碼,以及上面幾個版本的對比一下,你就會明白的 6 DB "HELLOIPL" ; 啟動區的名稱可以是任意字符串(8字節) 7 DW 512 ; 每個扇區(sector)的大小必須為512字節 8 DB 1 ; 簇(cluster)的大小必須為1個扇區 9 DW 1 ; FAT的起始位置(一般從第一個扇區開始) 10 DB 2 ; FAT的個數(必須為2) 11 DW 224 ; 根目錄的大小(一般設為244項) 12 DW 2880 ; 該磁盤的的大小(必須為2880扇區) 13 DB 0xf0 ; 磁盤的種類(必須為0xfd) 14 DW 9 ; FAT的長度(必須為9扇區) 15 DW 18 ; 一個磁道(track)有幾個扇區(必須為18) 16 DW 2 ; 磁頭數(必須為2) 17 DD 0 ; 不使用分區(必須為0) 18 DD 2880 ; 重寫一次磁盤大小 19 DB 0,0,0x29 ; 意義不明,固定 20 DD 0xffffffff ; (可能是)卷標號碼 21 DB "HELLO-OS " ; 磁盤名稱(11字節) 22 DB "FAT12 " ; 磁盤格式名稱(8字節) 23 RESB 18 ; 先騰出18字節 24 25 ; 程序主體 26 DB 0xb8, 0x00, 0x00, 0x8e, 0xd0, 0xbc, 0x00, 0x7c 27 DB 0x8e, 0xd8, 0x8e, 0xc0, 0xbe, 0x74, 0x7c, 0x8a 28 DB 0x04, 0x83, 0xc6, 0x01, 0x3c, 0x00, 0x74, 0x09 29 DB 0xb4, 0x0e, 0xbb, 0x0f, 0x00, 0xcd, 0x10, 0xeb 30 DB 0xee, 0xf4, 0xeb, 0xfd 31 32 ; 信息顯示部分 33 DB 0x0a, 0x0a ; 2個換行 34 DB "hello, world" 35 DB 0x0a ; 換行 36 DB 0 37 38 RESB 0x1fe-$ ; 填寫0x00直到0x001fe 39 DB 0x55, 0xaa 40 41 ; 以下是啟動區以外部分的輸出 42 DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 43 RESB 4600 44 DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 45 RESB 1469432
PS: 不用解釋,程序主體放在下面講,如何轉換為容易理解的匯編~
2.4 核心程序也用匯編改寫
>_<" 這里主要對上面改進版匯編的25~36行進行改寫為更容易理解的匯編程序(最開頭也改動一點點),其他一樣:
1 ; hello-os 2 ; TAB=4 3 ORG 0x7c00 ; 指明程序的裝載地址 4 ; 以下段落是標准FAT12格式軟盤專用代碼 5 JMP entry 6 DB 0x90 7 DB "HELLOIPL" ; 啟動區的名稱可以是任意字符串(8字節) 8 DW 512 ; 每個扇區(sector)的大小必須為512字節 9 DB 1 ; 簇(cluster)的大小必須為1個扇區 10 DW 1 ; FAT的起始位置(一般從第一個扇區開始) 11 DB 2 ; FAT的個數(必須為2) 12 DW 224 ; 根目錄的大小(一般設為244項) 13 DW 2880 ; 該磁盤的的大小(必須為2880扇區) 14 DB 0xf0 ; 磁盤的種類(必須為0xfd) 15 DW 9 ; FAT的長度(必須為9扇區) 16 DW 18 ; 一個磁道(track)有幾個扇區(必須為18) 17 DW 2 ; 磁頭數(必須為2) 18 DD 0 ; 不使用分區(必須為0) 19 DD 2880 ; 重寫一次磁盤大小 20 DB 0,0,0x29 ; 意義不明,固定 21 DD 0xffffffff ; (可能是)卷標號碼 22 DB "HELLO-OS " ; 磁盤名稱(11字節) 23 DB "FAT12 " ; 磁盤格式名稱(8字節) 24 RESB 18 ; 先騰出18字節 25 26 ; 程序核心 27 entry: 28 MOV AX,0 ; 初始化寄存器 29 MOV SS,AX 30 MOV SP,0x7c00 31 MOV DS,AX 32 MOV ES,AX 33 34 MOV SI,msg ;主程序就是把msg的內容給SI然后循環逐個輸出 35 putloop: 36 MOV AL,[SI] 37 ADD SI,1 ; SI++ 38 CMP AL,0 39 JE fin 40 MOV AH,0x0e ; 顯示一個文字 41 MOV BX,15 ; 指定字符顏色 42 INT 0x10 ; 調用顯卡BIOS(匯編中經常會用到的一種調用形式) 43 JMP putloop 44 fin: 45 HLT ; 讓CPU停止等待指令(和在main函數的結尾寫一個getchar()類似) 46 JMP fin ; 無限循環 47 48 msg: 49 DB 0x0a, 0x0a ; 2個回車 50 DB "hello, world" 51 DB 0x0a ; 回車 52 DB 0 53 54 RESB 0x7dfe-$ ; 填充0 55 56 DB 0x55, 0xaa 57 58 ; 以下是啟動區以外部分的輸出 59 DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 60 RESB 4600 61 DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 62 RESB 1469432
PS: 這里我們可以方便的看出來,原來主程序的功能就是把msg里面的字符先傳給SI然后SI++遍歷循環輸出的意思,哈哈,終於能完整的從0-1轉到匯編然后理解0-1的意思啦~
PS: 這里剛開始的125字節就是啟動區125字節內容,其地址為0x00007c00-0x00007dff,所以第3行指明程序加載地址~
2.5 向匯編程序中加入IPL(啟動程序裝載器)
>_<" 上面的匯編實現了將msg的字符串顯示到顯示屏上,但是這里的msg是本來保存在內存中的,一般CPU的內存是很小的,如果把大量的信息(如整個操作系統)都保存在這里肯定是不合理的,因此就需要把這部分內容存儲在其他上(如內存條,這里采用軟盤)!因此,我們就要在核心程序初始化之后從軟盤讀取數據放到內存在執行~(下面是調用INT 0x13讀取磁盤的相關代碼)
1 ; 讀磁盤(從軟盤中讀數據裝到內存中0x8200--0x83ff 125字節的地方) 2 MOV AX,0x0820 ;ES:BX=緩沖地址 3 MOV ES,AX 4 MOV CH,0 ; 柱面0 5 MOV DH,0 ; 磁頭0 6 MOV CL,2 ; 扇區2 7 8 MOV AH,0x02 ; AH=0x02 : 讀盤 9 MOV AL,1 ; 1個扇區 10 MOV BX,0 11 MOV DL,0x00 ; A驅動器 12 INT 0x13 ; 調用磁盤BIOS 13 JC error
>_<" 修改后的匯編代碼如下:(這里從磁盤讀取數據裝到0x8200--0x83ff 125字節的地方,如果讀取出錯就跳轉到error,顯示讀取出錯這個內容~

1 ; haribote-ipl 2 ; TAB=4 3 ORG 0x7c00 ; 指明程序裝載地址 4 5 ; 以下這段是FAT12格式軟盤專用代碼 0x7c00--0x7dff 125字節 用於啟動區 6 JMP entry 7 DB 0x90 8 DB "HARIBOTE" ; 啟動區的名字可以是任意的,但必須是8字節 9 DW 512 ; 每個扇區(sector)的大小必須為512字節 10 DB 1 ; 簇(cluster)的大小必須為1個扇區 11 DW 1 ; FAT的起始位置(一般從第一個扇區開始) 12 DB 2 ; FAT的個數(必須為2) 13 DW 224 ; 根目錄的大小(一般設為244項) 14 DW 2880 ; 該磁盤的的大小(必須為2880扇區) 15 DB 0xf0 ; 磁盤的種類(必須為0xfd) 16 DW 9 ; FAT的長度(必須為9扇區) 17 DW 18 ; 一個磁道(track)有幾個扇區(必須為18) 18 DW 2 ; 磁頭數(必須為2) 19 DD 0 ; 不使用分區(必須為0) 20 DD 2880 ; 重寫一次磁盤大小 21 DB 0,0,0x29 ; 意義不明,固定 22 DD 0xffffffff ; (可能是)卷標號碼 23 DB "HARIBOTEOS " ; 磁盤名稱(11字節) 24 DB "FAT12 " ; 磁盤格式名稱(8字節) 25 RESB 18 ; 先騰出18字節 26 27 ; 程序核心 28 entry: 29 MOV AX,0 ; 初始化寄存器 30 MOV SS,AX 31 MOV SP,0x7c00 32 MOV DS,AX 33 34 ; 讀磁盤(從軟盤中讀數據裝到內存中0x8200--0x83ff 125字節的地方) 35 MOV AX,0x0820 ;ES:BX=緩沖地址 36 MOV ES,AX 37 MOV CH,0 ; 柱面0 38 MOV DH,0 ; 磁頭0 39 MOV CL,2 ; 扇區2 40 41 MOV AH,0x02 ; AH=0x02 : 讀盤 42 MOV AL,1 ; 1個扇區 43 MOV BX,0 44 MOV DL,0x00 ; A驅動器 45 INT 0x13 ; 調用磁盤BIOS 46 JC error 47 48 fin: 49 HLT ; 讓CPU停止等待指令 50 JMP fin ; 無限循環 51 52 error: 53 MOV SI,msg ; 循環輸出msg里面的內容 54 55 putloop: 56 MOV AL,[SI] 57 ADD SI,1 ; 給SI加1 58 CMP AL,0 59 JE fin 60 MOV AH,0x0e ; 顯示一個文字 61 MOV BX,15 ; 指定字符顏色 62 INT 0x10 ; 調用顯卡BIOS 63 JMP putloop 64 65 msg: 66 DB 0x0a, 0x0a ; 換行2次 67 DB "load error" 68 DB 0x0a ; 換行 69 DB 0 70 71 RESB 0x7dfe-$ ; 0x7dfe傑偱傪0x00偱杽傔傞柦椷 72 73 DB 0x55, 0xaa
2.6 從啟動區執行操作系統(讀盤的應用)
>_<" 軟盤這種東西不可靠,因此我們要多次讀盤嘗試,如果多次之后還不行再放棄讀盤,讀盤代碼改動如下:

1 ; 讀磁盤(從軟盤中讀數據裝到內存中0x8200--0x83ff 125字節的地方) 2 MOV AX,0x0820 ;ES:BX=緩沖地址 3 MOV ES,AX 4 MOV CH,0 ; 柱面0 5 MOV DH,0 ; 磁頭0 6 MOV CL,2 ; 扇區2 7 8 MOv SI,0 ; 記錄失敗次數的寄存器 9 retry: 10 MOV AH,0x02 ; AH=0x02 : 讀盤 11 MOV AL,1 ; 1個扇區 12 MOV BX,0 13 MOV DL,0x00 ; A驅動器 14 INT 0x13 ; 調用磁盤BIOS 15 JNC fin ; 沒出錯就跳轉到fin 16 ADD SI,1 ; SI++ 17 CMP SI,5 ; 比較SI和5 18 JAE error ; SI>=5時,跳轉到error 19 MOV AH,0x00 20 MOV DL,0x00 ; A驅動器 21 INT 0x13 ; 重置驅動器 22 JMP retry

1 ; haribote-ipl 2 ; TAB=4 3 ORG 0x7c00 ; 指明程序裝載地址 4 5 ; 以下這段是FAT12格式軟盤專用代碼 0x7c00--0x7dff 125字節 用於啟動區 6 JMP entry 7 DB 0x90 8 DB "HARIBOTE" ; 啟動區的名字可以是任意的,但必須是8字節 9 DW 512 ; 每個扇區(sector)的大小必須為512字節 10 DB 1 ; 簇(cluster)的大小必須為1個扇區 11 DW 1 ; FAT的起始位置(一般從第一個扇區開始) 12 DB 2 ; FAT的個數(必須為2) 13 DW 224 ; 根目錄的大小(一般設為244項) 14 DW 2880 ; 該磁盤的的大小(必須為2880扇區) 15 DB 0xf0 ; 磁盤的種類(必須為0xfd) 16 DW 9 ; FAT的長度(必須為9扇區) 17 DW 18 ; 一個磁道(track)有幾個扇區(必須為18) 18 DW 2 ; 磁頭數(必須為2) 19 DD 0 ; 不使用分區(必須為0) 20 DD 2880 ; 重寫一次磁盤大小 21 DB 0,0,0x29 ; 意義不明,固定 22 DD 0xffffffff ; (可能是)卷標號碼 23 DB "HARIBOTEOS " ; 磁盤名稱(11字節) 24 DB "FAT12 " ; 磁盤格式名稱(8字節) 25 RESB 18 ; 先騰出18字節 26 27 ; 程序核心 28 entry: 29 MOV AX,0 ; 初始化寄存器 30 MOV SS,AX 31 MOV SP,0x7c00 32 MOV DS,AX 33 34 ; 讀磁盤(從軟盤中讀數據裝到內存中0x8200--0x83ff 125字節的地方) 35 MOV AX,0x0820 ;ES:BX=緩沖地址 36 MOV ES,AX 37 MOV CH,0 ; 柱面0 38 MOV DH,0 ; 磁頭0 39 MOV CL,2 ; 扇區2 40 41 MOv SI,0 ; 記錄失敗次數的寄存器 42 retry: 43 MOV AH,0x02 ; AH=0x02 : 讀盤 44 MOV AL,1 ; 1個扇區 45 MOV BX,0 46 MOV DL,0x00 ; A驅動器 47 INT 0x13 ; 調用磁盤BIOS 48 JNC fin ; 沒出錯就跳轉到fin 49 ADD SI,1 ; SI++ 50 CMP SI,5 ; 比較SI和5 51 JAE error ; SI>=5時,跳轉到error 52 MOV AH,0x00 53 MOV DL,0x00 ; A驅動器 54 INT 0x13 ; 重置驅動器 55 JMP retry 56 57 fin: 58 HLT ; 讓CPU停止等待指令 59 JMP fin ; 無限循環 60 61 error: 62 MOV SI,msg ; 循環輸出msg里面的內容 63 64 putloop: 65 MOV AL,[SI] 66 ADD SI,1 ; 給SI加1 67 CMP AL,0 68 JE fin 69 MOV AH,0x0e ; 顯示一個文字 70 MOV BX,15 ; 指定字符顏色 71 INT 0x10 ; 調用顯卡BIOS 72 JMP putloop 73 74 msg: 75 DB 0x0a, 0x0a ; 換行2次 76 DB "load error" 77 DB 0x0a ; 換行 78 DB 0 79 80 RESB 0x7dfe-$ ; 0x7dfe傑偱傪0x00偱杽傔傞柦椷 81 82 DB 0x55, 0xaa 83 84 完整的代碼
>_<" 趁着上面的盡頭,咱們直接寫個從扇區2讀到扇區18的代碼吧!
PS:這里采用的思路是:把扇區2->18的內容讀到內存,內存剛開始的地址為0x0820,每次讀一個扇區向后移動0x0200個地址(即:125字節)

1 ; 讀磁盤(從軟盤中讀數據裝到內存中0x8200--0x83ff 125字節的地方) 2 MOV AX,0x0820 ; ES:BX=緩沖地址 3 MOV ES,AX 4 MOV CH,0 ; 柱面0 5 MOV DH,0 ; 磁頭0 6 MOV CL,2 ; 扇區2 7 readloop: 8 MOv SI,0 ; 記錄失敗次數的寄存器 9 retry: 10 MOV AH,0x02 ; AH=0x02 : 讀盤 11 MOV AL,1 ; 1個扇區 12 MOV BX,0 13 MOV DL,0x00 ; A驅動器 14 INT 0x13 ; 調用磁盤BIOS 15 JNC next ; 沒出錯就跳轉到next繼續讀下一個做准備 16 ADD SI,1 ; SI++ 17 CMP SI,5 ; 比較SI和5 18 JAE error ; SI>=5時,跳轉到error 19 MOV AH,0x00 20 MOV DL,0x00 ; A驅動器 21 INT 0x13 ; 重置驅動器 22 JMP retry 23 next: 24 MOV AX,ES ; 把內存地址后移0x200 25 ADD AX,0x0020 26 MOV ES,AX ; 因為沒有ADD ES,0x200指令,所以這里繞個彎 27 ADD CL,1 ; 往CL里加1 (所讀扇區標號,開始是2,見初始化部分) 28 CMP CL,18 ; 和18比較 29 JBE readloop ; 小於18就跳轉到readloop繼續讀

1 ; haribote-ipl 2 ; TAB=4 3 ORG 0x7c00 ; 指明程序裝載地址 4 5 ; 以下這段是FAT12格式軟盤專用代碼 0x7c00--0x7dff 125字節 用於啟動區 6 JMP entry 7 DB 0x90 8 DB "HARIBOTE" ; 啟動區的名字可以是任意的,但必須是8字節 9 DW 512 ; 每個扇區(sector)的大小必須為512字節 10 DB 1 ; 簇(cluster)的大小必須為1個扇區 11 DW 1 ; FAT的起始位置(一般從第一個扇區開始) 12 DB 2 ; FAT的個數(必須為2) 13 DW 224 ; 根目錄的大小(一般設為244項) 14 DW 2880 ; 該磁盤的的大小(必須為2880扇區) 15 DB 0xf0 ; 磁盤的種類(必須為0xfd) 16 DW 9 ; FAT的長度(必須為9扇區) 17 DW 18 ; 一個磁道(track)有幾個扇區(必須為18) 18 DW 2 ; 磁頭數(必須為2) 19 DD 0 ; 不使用分區(必須為0) 20 DD 2880 ; 重寫一次磁盤大小 21 DB 0,0,0x29 ; 意義不明,固定 22 DD 0xffffffff ; (可能是)卷標號碼 23 DB "HARIBOTEOS " ; 磁盤名稱(11字節) 24 DB "FAT12 " ; 磁盤格式名稱(8字節) 25 RESB 18 ; 先騰出18字節 26 27 ; 程序核心 28 entry: 29 MOV AX,0 ; 初始化寄存器 30 MOV SS,AX 31 MOV SP,0x7c00 32 MOV DS,AX 33 34 ; 讀磁盤(從軟盤中讀數據裝到內存中0x8200--0x83ff 125字節的地方) 35 MOV AX,0x0820 ; ES:BX=緩沖地址 36 MOV ES,AX 37 MOV CH,0 ; 柱面0 38 MOV DH,0 ; 磁頭0 39 MOV CL,2 ; 扇區2 40 readloop: 41 MOv SI,0 ; 記錄失敗次數的寄存器 42 retry: 43 MOV AH,0x02 ; AH=0x02 : 讀盤 44 MOV AL,1 ; 1個扇區 45 MOV BX,0 46 MOV DL,0x00 ; A驅動器 47 INT 0x13 ; 調用磁盤BIOS 48 JNC next ; 沒出錯就跳轉到next繼續讀下一個做准備 49 ADD SI,1 ; SI++ 50 CMP SI,5 ; 比較SI和5 51 JAE error ; SI>=5時,跳轉到error 52 MOV AH,0x00 53 MOV DL,0x00 ; A驅動器 54 INT 0x13 ; 重置驅動器 55 JMP retry 56 next: 57 MOV AX,ES ; 把內存地址后移0x200 58 ADD AX,0x0020 59 MOV ES,AX ; 因為沒有ADD ES,0x200指令,所以這里繞個彎 60 ADD CL,1 ; 往CL里加1 (所讀扇區標號,開始是2,見初始化部分) 61 CMP CL,18 ; 和18比較 62 JBE readloop ; 小於18就跳轉到readloop繼續讀 63 64 fin: 65 HLT ; 讓CPU停止等待指令 66 JMP fin ; 無限循環 67 68 error: 69 MOV SI,msg ; 循環輸出msg里面的內容 70 71 putloop: 72 MOV AL,[SI] 73 ADD SI,1 ; 給SI加1 74 CMP AL,0 75 JE fin 76 MOV AH,0x0e ; 顯示一個文字 77 MOV BX,15 ; 指定字符顏色 78 INT 0x10 ; 調用顯卡BIOS 79 JMP putloop 80 81 msg: 82 DB 0x0a, 0x0a ; 換行2次 83 DB "load error" 84 DB 0x0a ; 換行 85 DB 0 86 87 RESB 0x7dfe-$ ; 0x7dfe傑偱傪0x00偱杽傔傞柦椷 88 89 DB 0x55, 0xaa
>_<" 趁熱打鐵,C0-H0-S18的下一扇區就是磁盤的反面C0-H1-S1,這次是要從C0-H0-S2---->C0-H0-S3----->C9-H1-S18

1 ; 讀磁盤(從軟盤中讀數據裝到內存中0x8200--0x83ff 125字節的地方) 2 MOV AX,0x0820 ; ES:BX=緩沖地址 3 MOV ES,AX 4 MOV CH,0 ; 柱面0 5 MOV DH,0 ; 磁頭0 6 MOV CL,2 ; 扇區2 7 readloop: 8 MOv SI,0 ; 記錄失敗次數的寄存器 9 retry: 10 MOV AH,0x02 ; AH=0x02 : 讀盤 11 MOV AL,1 ; 1個扇區 12 MOV BX,0 13 MOV DL,0x00 ; A驅動器 14 INT 0x13 ; 調用磁盤BIOS 15 JNC next ; 沒出錯就跳轉到next繼續讀下一個做准備 16 ADD SI,1 ; SI++ 17 CMP SI,5 ; 比較SI和5 18 JAE error ; SI>=5時,跳轉到error 19 MOV AH,0x00 20 MOV DL,0x00 ; A驅動器 21 INT 0x13 ; 重置驅動器 22 JMP retry 23 next: 24 MOV AX,ES ; 把內存地址后移0x200 25 ADD AX,0x0020 26 MOV ES,AX ; 因為沒有ADD ES,0x200指令,所以這里繞個彎 27 ADD CL,1 ; 往CL里加1 (所讀扇區標號,開始是2,見初始化部分) 28 CMP CL,18 ; 和18比較 29 JBE readloop ; 小於18就跳轉到readloop繼續讀 30 MOV CL,1 ; 扇區1 31 ADD DH,1 ; 磁頭+1 32 CMP DH,2 ; 判斷磁頭是否超過2 33 JB readloop ; 沒有超過就繼續讀 34 MOV DH,0 ; 超過2就轉為0 35 ADD CH,1 ; CH記錄讀取的柱面數 36 CMP CH,CYLS ; CYLS在前面定義 CYLS EQU 10 37 JB readloop

1 ; haribote-ipl 2 ; TAB=4 3 CYLS EQU 10 ; 定義讀的柱面數 4 ORG 0x7c00 ; 指明程序裝載地址 5 6 ; 以下這段是FAT12格式軟盤專用代碼 0x7c00--0x7dff 125字節 用於啟動區 7 JMP entry 8 DB 0x90 9 DB "HARIBOTE" ; 啟動區的名字可以是任意的,但必須是8字節 10 DW 512 ; 每個扇區(sector)的大小必須為512字節 11 DB 1 ; 簇(cluster)的大小必須為1個扇區 12 DW 1 ; FAT的起始位置(一般從第一個扇區開始) 13 DB 2 ; FAT的個數(必須為2) 14 DW 224 ; 根目錄的大小(一般設為244項) 15 DW 2880 ; 該磁盤的的大小(必須為2880扇區) 16 DB 0xf0 ; 磁盤的種類(必須為0xfd) 17 DW 9 ; FAT的長度(必須為9扇區) 18 DW 18 ; 一個磁道(track)有幾個扇區(必須為18) 19 DW 2 ; 磁頭數(必須為2) 20 DD 0 ; 不使用分區(必須為0) 21 DD 2880 ; 重寫一次磁盤大小 22 DB 0,0,0x29 ; 意義不明,固定 23 DD 0xffffffff ; (可能是)卷標號碼 24 DB "HARIBOTEOS " ; 磁盤名稱(11字節) 25 DB "FAT12 " ; 磁盤格式名稱(8字節) 26 RESB 18 ; 先騰出18字節 27 28 ; 程序核心 29 entry: 30 MOV AX,0 ; 初始化寄存器 31 MOV SS,AX 32 MOV SP,0x7c00 33 MOV DS,AX 34 35 ; 讀磁盤(從軟盤中讀數據裝到內存中0x8200--0x83ff 125字節的地方) 36 MOV AX,0x0820 ; ES:BX=緩沖地址 37 MOV ES,AX 38 MOV CH,0 ; 柱面0 39 MOV DH,0 ; 磁頭0 40 MOV CL,2 ; 扇區2 41 readloop: 42 MOv SI,0 ; 記錄失敗次數的寄存器 43 retry: 44 MOV AH,0x02 ; AH=0x02 : 讀盤 45 MOV AL,1 ; 1個扇區 46 MOV BX,0 47 MOV DL,0x00 ; A驅動器 48 INT 0x13 ; 調用磁盤BIOS 49 JNC next ; 沒出錯就跳轉到next繼續讀下一個做准備 50 ADD SI,1 ; SI++ 51 CMP SI,5 ; 比較SI和5 52 JAE error ; SI>=5時,跳轉到error 53 MOV AH,0x00 54 MOV DL,0x00 ; A驅動器 55 INT 0x13 ; 重置驅動器 56 JMP retry 57 next: 58 MOV AX,ES ; 把內存地址后移0x200 59 ADD AX,0x0020 60 MOV ES,AX ; 因為沒有ADD ES,0x200指令,所以這里繞個彎 61 ADD CL,1 ; 往CL里加1 (所讀扇區標號,開始是2,見初始化部分) 62 CMP CL,18 ; 和18比較 63 JBE readloop ; 小於18就跳轉到readloop繼續讀 64 MOV CL,1 ; 扇區1 65 ADD DH,1 ; 磁頭+1 66 CMP DH,2 ; 判斷磁頭是否超過2 67 JB readloop ; 沒有超過就繼續讀 68 MOV DH,0 ; 超過2就轉為0 69 ADD CH,1 ; CH記錄讀取的柱面數 70 CMP CH,CYLS ; CYLS在前面定義 CYLS EQU 10 71 JB readloop 72 73 fin: 74 HLT ; 讓CPU停止等待指令 75 JMP fin ; 無限循環 76 77 error: 78 MOV SI,msg ; 循環輸出msg里面的內容 79 80 putloop: 81 MOV AL,[SI] 82 ADD SI,1 ; 給SI加1 83 CMP AL,0 84 JE fin 85 MOV AH,0x0e ; 顯示一個文字 86 MOV BX,15 ; 指定字符顏色 87 INT 0x10 ; 調用顯卡BIOS 88 JMP putloop 89 90 msg: 91 DB 0x0a, 0x0a ; 換行2次 92 DB "load error" 93 DB 0x0a ; 換行 94 DB 0 95 96 RESB 0x7dfe-$ ; 0x7dfe傑偱傪0x00偱杽傔傞柦椷 97 98 DB 0x55, 0xaa
3、匯編和C語言混合開發
3.1 32位開發及C語言混合開發引入
>_<" 現在我們已經學會如何從磁盤讀數據放到內存中了,那么下面就是如何將操作系統本身內容寫進磁盤映像,然后我們從啟動區執行這個內容就行了~
>_<" 前兩部分我們已經把操作系統的引導程序寫好了,即是最終的讀取10個柱面的全部代碼,我們通過編譯就能把其編譯為磁盤鏡像文件(即最上面看的0-1文件);接下來新建一個haribote.nas匯編文件,在里面先寫上如下代碼,這就是一個簡單的操作系統內容文件,用nask編譯為.sys格式的文件,保存到磁盤映像.img里(這個過程比較復雜,其實有比較好的軟件可以幫助我們直接解決上述保存到磁盤映像的問題:edimg.exe) 。這里文件保存到映像文件時有一個規律:
- 文件名會在0x002600以后的地方出現
- 文件的內容會寫在0x004200后的地方
根據上面的規律,我們就可以將操作系統本身的內容寫到名為haribote.sys文件中,再把他們保存到磁盤映像里,然后我們從啟動區執行這個haribotes.sys就行了。
1 fin: 2 HLT ; 讓CPU停止等待指令 3 JMP fin ; 無限循環
>_<" 那么該怎樣才能執行磁盤映像上位於0x004200號地址的程序呢?我們在第二節已經介紹,程序是從啟動區開始執行,把磁盤上的內容裝在到內存0x8000號地址,所以磁盤0x4200處的內容位於內存0x8000+0x4200=0xc200號地址處。 這樣我們在haribote.nas里加上ORG 0xc200,然后在第二節最后讀取10個柱面的全部代碼(下面命名為ipl.nas)加上JMP 0xc200。
1 ; haribote-os 2 ; TAB=4 3 4 ORG 0xc200 5 fin: 6 HLT 7 JMP fin
這樣將上面兩個文件編譯並合並為映像文件並運行,什么都沒有發生,那么我們想看看程序到底有沒有運行,該則么辦呢?其實很簡單,就像我們剛學C++的時候讓程序輸出個hello world,但是這里,因為我們上兩節已經實現了這個功能,再玩一遍沒啥意思,於是,這里來個高級一點的:這次切換一下畫面模式(因為我們最終要做成windows那樣地畫面),因此需要把在我們的haribote.nas內添加一些內容(主要是調用顯卡BIOS的函數,具體搜索INT 0x10):
1 ; haribote-os 2 ; TAB=4 3 4 ORG 0xc200 ; 這個程序要被裝在到程序的什么位置 5 6 MOV AL,0x13 ; VGA顯卡,320X200X8位彩色 7 MOV AH,0x00 8 INT 0x10 9 fin: 10 HLT 11 JMP fin
這樣如果程序正常運行,畫面將是一片漆黑,光標會消失。另外,我們將ipl.nas的文件名改成ipl10.nas(提醒大家,這個程序只能讀入10個柱面),另外,想要把磁盤裝在內容的結束地址告訴haribote.sys,所以我們要在JMP 0xc200之前加上一行命令,將CYLS的值寫到地址0x0ff0中,這樣啟動程序就修改好了~
1 ; 磁盤內容裝載內容的結束地址告訴haribote.sys 2 3 MOV [0x0ff0],CH ; 將CYLS的值寫到內存地址0x0ff0中 4 JMP 0xc200

1 ; haribote-ipl 2 ; TAB=4 3 CYLS EQU 10 ; 定義讀的柱面數 4 ORG 0x7c00 ; 指明程序裝載地址 5 6 ; 以下這段是FAT12格式軟盤專用代碼 0x7c00--0x7dff 125字節 用於啟動區 7 JMP entry 8 DB 0x90 9 DB "HARIBOTE" ; 啟動區的名字可以是任意的,但必須是8字節 10 DW 512 ; 每個扇區(sector)的大小必須為512字節 11 DB 1 ; 簇(cluster)的大小必須為1個扇區 12 DW 1 ; FAT的起始位置(一般從第一個扇區開始) 13 DB 2 ; FAT的個數(必須為2) 14 DW 224 ; 根目錄的大小(一般設為244項) 15 DW 2880 ; 該磁盤的的大小(必須為2880扇區) 16 DB 0xf0 ; 磁盤的種類(必須為0xfd) 17 DW 9 ; FAT的長度(必須為9扇區) 18 DW 18 ; 一個磁道(track)有幾個扇區(必須為18) 19 DW 2 ; 磁頭數(必須為2) 20 DD 0 ; 不使用分區(必須為0) 21 DD 2880 ; 重寫一次磁盤大小 22 DB 0,0,0x29 ; 意義不明,固定 23 DD 0xffffffff ; (可能是)卷標號碼 24 DB "HARIBOTEOS " ; 磁盤名稱(11字節) 25 DB "FAT12 " ; 磁盤格式名稱(8字節) 26 RESB 18 ; 先騰出18字節 27 28 ; 程序核心 29 entry: 30 MOV AX,0 ; 初始化寄存器 31 MOV SS,AX 32 MOV SP,0x7c00 33 MOV DS,AX 34 35 ; 讀磁盤(從軟盤中讀數據裝到內存中0x8200--0x83ff 125字節的地方) 36 MOV AX,0x0820 ; ES:BX=緩沖地址 37 MOV ES,AX 38 MOV CH,0 ; 柱面0 39 MOV DH,0 ; 磁頭0 40 MOV CL,2 ; 扇區2 41 readloop: 42 MOv SI,0 ; 記錄失敗次數的寄存器 43 retry: 44 MOV AH,0x02 ; AH=0x02 : 讀盤 45 MOV AL,1 ; 1個扇區 46 MOV BX,0 47 MOV DL,0x00 ; A驅動器 48 INT 0x13 ; 調用磁盤BIOS 49 JNC next ; 沒出錯就跳轉到next繼續讀下一個做准備 50 ADD SI,1 ; SI++ 51 CMP SI,5 ; 比較SI和5 52 JAE error ; SI>=5時,跳轉到error 53 MOV AH,0x00 54 MOV DL,0x00 ; A驅動器 55 INT 0x13 ; 重置驅動器 56 JMP retry 57 next: 58 MOV AX,ES ; 把內存地址后移0x200 59 ADD AX,0x0020 60 MOV ES,AX ; 因為沒有ADD ES,0x200指令,所以這里繞個彎 61 ADD CL,1 ; 往CL里加1 (所讀扇區標號,開始是2,見初始化部分) 62 CMP CL,18 ; 和18比較 63 JBE readloop ; 小於18就跳轉到readloop繼續讀 64 MOV CL,1 ; 扇區1 65 ADD DH,1 ; 磁頭+1 66 CMP DH,2 ; 判斷磁頭是否超過2 67 JB readloop ; 沒有超過就繼續讀 68 MOV DH,0 ; 超過2就轉為0 69 ADD CH,1 ; CH記錄讀取的柱面數 70 CMP CH,CYLS ; CYLS在前面定義 CYLS EQU 10 71 JB readloop 72 73 ; 磁盤內容裝載內容的結束地址告訴haribote.sys 74 MOV [0x0ff0],CH ; 將CYLS的值寫到內存地址0x0ff0中 75 JMP 0xc200 76 77 error: 78 MOV SI,msg ; 循環輸出msg里面的內容 79 80 putloop: 81 MOV AL,[SI] 82 ADD SI,1 ; 給SI加1 83 CMP AL,0 84 JE fin 85 MOV AH,0x0e ; 顯示一個文字 86 MOV BX,15 ; 指定字符顏色 87 INT 0x10 ; 調用顯卡BIOS 88 JMP putloop 89 fin: 90 HLT ; 讓CPU停止等待指令 91 JMP fin ; 無限循環 92 93 msg: 94 DB 0x0a, 0x0a ; 換行2次 95 DB "load error" 96 DB 0x0a ; 換行 97 DB 0 98 99 RESB 0x7dfe-$ ; 0x7dfe傑偱傪0x00偱杽傔傞柦椷 100 101 DB 0x55, 0xaa
PS: 這里我們把啟動區里與haribote.sys無關的前后部分也讀了進來,所以啟動很慢,但是以后會有用的~
>_<" 現在,匯編語言開發終於可以告一段落啦~,接下來我們主要以C語言開發為 主了~因為這里用的是32位C語言編譯器(32位模式比16位的要好很多,這里不詳細說明了),但是32位模式就不能調用BIOS功能了(這是因為BIOS是由16位機器語言寫的,如果我們有什么事情想用BIOS來做,就要全部放在開頭做,一旦進入32位模式就不能調用BIOS函數了(其實也有32位返回16位的方法)。再回頭說說使用BIOS的事情,在上面我們已經把畫面模式設定已經做完了,接下來還想從BIOS獲取鍵盤狀態(即:NumLock是ON還是OFF),這次只修改了haribote.nas:
1 ; haribote-os 2 ; TAB=4 3 4 ; BOOT_INFO相關 5 CYLS EQU 0x0ff0 ; 設定啟動區 6 LEDS EQU 0x0ff1 7 VMODE EQU 0x0ff2 ; 關於顏色的信息顏色的位數 8 SCRNX EQU 0x0ff4 ; 分辨率X 9 SCRNY EQU 0x0ff6 ; 分辨率Y 10 VRAM EQU 0x0ff8 ; 圖像緩沖區的開始地址 11 12 ORG 0xc200 ; 這個程序要裝載到什么地方 13 14 MOV AL,0x13 ; VGA顯卡,320X200X8位色彩 15 MOV AH,0x00 16 INT 0x10 17 MOV BYTE [VMODE],8 ; 記錄畫面模式 18 MOV WORD [SCRNX],320 19 MOV WORD [SCRNY],200 20 MOV DWORD [VRAM],0x000a0000 21 22 ; 用BIOS獲得鍵盤上各種LED指示燈的狀態 23 24 MOV AH,0x02 25 INT 0x16 ; keyboard BIOS 26 MOV [LEDS],AL 27 28 fin: 29 HLT 30 JMP fin
這個程序一看就明白,其實就是設置畫面模式之后,將設置的信息保存到內存中(以后可能要設置不同的畫面模式,就要把現在的設置信息保存起來以備后用);[VRAM]里保存的是0xa0000,在電腦的世界里,VRAM是指顯卡內存(Video RAM)也就是顯示畫面的內存,這一塊可以像一般的內存一樣存儲數據,但VRAM的功能不僅限於此,它的各個地址都對應着畫面上的像素,可以利用這一機制在畫面上繪制出五顏六色的圖案~
3.2 匯編引入C語言(用匯編寫C語言函數)
>_<" 終於准備就緒了,現在我們直接切換到32位模式,然后運行C語言程序,這次要修改很多:首先是haribote.sys,它的前半部分是用匯編寫的,后半部分是用C語言寫的,所以為了方便把文件名也從haribote.nas也隨之改成了asmhead.nas,並且為了調用C語言在前面加了100行左右的匯編代碼(今后再詳細介紹這100行代碼)下面先把到這里全部的代碼貼出來(我猜很多人已經暈了,不知道到底是哪些文件了)

1 ; haribote-ipl 2 ; TAB=4 3 CYLS EQU 10 ; 定義讀的柱面數 4 ORG 0x7c00 ; 指明程序裝載地址 5 6 ; 以下這段是FAT12格式軟盤專用代碼 0x7c00--0x7dff 125字節 用於啟動區 7 JMP entry 8 DB 0x90 9 DB "HARIBOTE" ; 啟動區的名字可以是任意的,但必須是8字節 10 DW 512 ; 每個扇區(sector)的大小必須為512字節 11 DB 1 ; 簇(cluster)的大小必須為1個扇區 12 DW 1 ; FAT的起始位置(一般從第一個扇區開始) 13 DB 2 ; FAT的個數(必須為2) 14 DW 224 ; 根目錄的大小(一般設為244項) 15 DW 2880 ; 該磁盤的的大小(必須為2880扇區) 16 DB 0xf0 ; 磁盤的種類(必須為0xfd) 17 DW 9 ; FAT的長度(必須為9扇區) 18 DW 18 ; 一個磁道(track)有幾個扇區(必須為18) 19 DW 2 ; 磁頭數(必須為2) 20 DD 0 ; 不使用分區(必須為0) 21 DD 2880 ; 重寫一次磁盤大小 22 DB 0,0,0x29 ; 意義不明,固定 23 DD 0xffffffff ; (可能是)卷標號碼 24 DB "HARIBOTEOS " ; 磁盤名稱(11字節) 25 DB "FAT12 " ; 磁盤格式名稱(8字節) 26 RESB 18 ; 先騰出18字節 27 28 ; 程序核心 29 entry: 30 MOV AX,0 ; 初始化寄存器 31 MOV SS,AX 32 MOV SP,0x7c00 33 MOV DS,AX 34 35 ; 讀磁盤(從軟盤中讀數據裝到內存中0x8200--0x83ff 125字節的地方) 36 MOV AX,0x0820 ; ES:BX=緩沖地址 37 MOV ES,AX 38 MOV CH,0 ; 柱面0 39 MOV DH,0 ; 磁頭0 40 MOV CL,2 ; 扇區2 41 readloop: 42 MOv SI,0 ; 記錄失敗次數的寄存器 43 retry: 44 MOV AH,0x02 ; AH=0x02 : 讀盤 45 MOV AL,1 ; 1個扇區 46 MOV BX,0 47 MOV DL,0x00 ; A驅動器 48 INT 0x13 ; 調用磁盤BIOS 49 JNC next ; 沒出錯就跳轉到next繼續讀下一個做准備 50 ADD SI,1 ; SI++ 51 CMP SI,5 ; 比較SI和5 52 JAE error ; SI>=5時,跳轉到error 53 MOV AH,0x00 54 MOV DL,0x00 ; A驅動器 55 INT 0x13 ; 重置驅動器 56 JMP retry 57 next: 58 MOV AX,ES ; 把內存地址后移0x200 59 ADD AX,0x0020 60 MOV ES,AX ; 因為沒有ADD ES,0x200指令,所以這里繞個彎 61 ADD CL,1 ; 往CL里加1 (所讀扇區標號,開始是2,見初始化部分) 62 CMP CL,18 ; 和18比較 63 JBE readloop ; 小於18就跳轉到readloop繼續讀 64 MOV CL,1 ; 扇區1 65 ADD DH,1 ; 磁頭+1 66 CMP DH,2 ; 判斷磁頭是否超過2 67 JB readloop ; 沒有超過就繼續讀 68 MOV DH,0 ; 超過2就轉為0 69 ADD CH,1 ; CH記錄讀取的柱面數 70 CMP CH,CYLS ; CYLS在前面定義 CYLS EQU 10 71 JB readloop 72 73 ; 磁盤內容裝載內容的結束地址告訴haribote.sys 74 MOV [0x0ff0],CH ; 將CYLS的值寫到內存地址0x0ff0中 75 JMP 0xc200 76 77 error: 78 MOV SI,msg ; 循環輸出msg里面的內容 79 80 putloop: 81 MOV AL,[SI] 82 ADD SI,1 ; 給SI加1 83 CMP AL,0 84 JE fin 85 MOV AH,0x0e ; 顯示一個文字 86 MOV BX,15 ; 指定字符顏色 87 INT 0x10 ; 調用顯卡BIOS 88 JMP putloop 89 fin: 90 HLT ; 讓CPU停止等待指令 91 JMP fin ; 無限循環 92 93 msg: 94 DB 0x0a, 0x0a ; 換行2次 95 DB "load error" 96 DB 0x0a ; 換行 97 DB 0 98 99 RESB 0x7dfe-$ ; 0x7dfe傑偱傪0x00偱杽傔傞柦椷 100 101 DB 0x55, 0xaa

1 ; haribote-os boot asm 2 ; TAB=4 3 4 BOTPAK EQU 0x00280000 ; bootpack偺儘乕僪愭 5 DSKCAC EQU 0x00100000 ; 僨傿僗僋僉儍僢僔儏偺応強 6 DSKCAC0 EQU 0x00008000 ; 僨傿僗僋僉儍僢僔儏偺応強乮儕傾儖儌乕僪乯 7 8 ; BOOT_INFO相關 9 CYLS EQU 0x0ff0 ; 設定啟動區 10 LEDS EQU 0x0ff1 11 VMODE EQU 0x0ff2 ; 關於顏色的信息顏色的位數 12 SCRNX EQU 0x0ff4 ; 分辨率X 13 SCRNY EQU 0x0ff6 ; 分辨率Y 14 VRAM EQU 0x0ff8 ; 圖像緩沖區的開始地址 15 16 ORG 0xc200 ; 這個程序要裝在到什么位置 17 18 MOV AL,0x13 ; VGA顯卡,320X200X8位色彩 19 MOV AH,0x00 20 INT 0x10 21 MOV BYTE [VMODE],8 ; 記錄畫面模式 22 MOV WORD [SCRNX],320 23 MOV WORD [SCRNY],200 24 MOV DWORD [VRAM],0x000a0000 25 26 ; 用BIOS獲得鍵盤上各種LED指示燈的狀態 27 MOV AH,0x02 28 INT 0x16 ; keyboard BIOS 29 MOV [LEDS],AL 30 31 ; PIC偑堦愗偺妱傝崬傒傪庴偗晅偗側偄傛偆偵偡傞 32 ; AT屳姺婡偺巇條偱偼丄PIC偺弶婜壔傪偡傞側傜丄 33 ; 偙偄偮傪CLI慜偵傗偭偰偍偐側偄偲丄偨傑偵僴儞僌傾僢僾偡傞 34 ; PIC偺弶婜壔偼偁偲偱傗傞 35 36 MOV AL,0xff 37 OUT 0x21,AL 38 NOP ; OUT柦椷傪楢懕偝偣傞偲偆傑偔偄偐側偄婡庬偑偁傞傜偟偄偺偱 39 OUT 0xa1,AL 40 41 CLI ; 偝傜偵CPU儗儀儖偱傕妱傝崬傒嬛巭 42 43 ; CPU偐傜1MB埲忋偺儊儌儕偵傾僋僙僗偱偒傞傛偆偵丄A20GATE傪愝掕 44 45 CALL waitkbdout 46 MOV AL,0xd1 47 OUT 0x64,AL 48 CALL waitkbdout 49 MOV AL,0xdf ; enable A20 50 OUT 0x60,AL 51 CALL waitkbdout 52 53 ; 僾儘僥僋僩儌乕僪堏峴 54 55 [INSTRSET "i486p"] ; 486偺柦椷傑偱巊偄偨偄偲偄偆婰弎 56 57 LGDT [GDTR0] ; 巄掕GDT傪愝掕 58 MOV EAX,CR0 59 AND EAX,0x7fffffff ; bit31傪0偵偡傞乮儁乕僕儞僌嬛巭偺偨傔乯 60 OR EAX,0x00000001 ; bit0傪1偵偡傞乮僾儘僥僋僩儌乕僪堏峴偺偨傔乯 61 MOV CR0,EAX 62 JMP pipelineflush 63 pipelineflush: 64 MOV AX,1*8 ; 撉傒彂偒壜擻僙僌儊儞僩32bit 65 MOV DS,AX 66 MOV ES,AX 67 MOV FS,AX 68 MOV GS,AX 69 MOV SS,AX 70 71 ; bootpack偺揮憲 72 73 MOV ESI,bootpack ; 揮憲尦 74 MOV EDI,BOTPAK ; 揮憲愭 75 MOV ECX,512*1024/4 76 CALL memcpy 77 78 ; 偮偄偱偵僨傿僗僋僨乕僞傕杮棃偺埵抲傊揮憲 79 80 ; 傑偢偼僽乕僩僙僋僞偐傜 81 82 MOV ESI,0x7c00 ; 揮憲尦 83 MOV EDI,DSKCAC ; 揮憲愭 84 MOV ECX,512/4 85 CALL memcpy 86 87 ; 巆傝慡晹 88 89 MOV ESI,DSKCAC0+512 ; 揮憲尦 90 MOV EDI,DSKCAC+512 ; 揮憲愭 91 MOV ECX,0 92 MOV CL,BYTE [CYLS] 93 IMUL ECX,512*18*2/4 ; 僔儕儞僟悢偐傜僶僀僩悢/4偵曄姺 94 SUB ECX,512/4 ; IPL偺暘偩偗嵎偟堷偔 95 CALL memcpy 96 97 ; asmhead偱偟側偗傟偽偄偗側偄偙偲偼慡晹偟廔傢偭偨偺偱丄 98 ; 偁偲偼bootpack偵擟偣傞 99 100 ; bootpack偺婲摦 101 102 MOV EBX,BOTPAK 103 MOV ECX,[EBX+16] 104 ADD ECX,3 ; ECX += 3; 105 SHR ECX,2 ; ECX /= 4; 106 JZ skip ; 揮憲偡傞傋偒傕偺偑側偄 107 MOV ESI,[EBX+20] ; 揮憲尦 108 ADD ESI,EBX 109 MOV EDI,[EBX+12] ; 揮憲愭 110 CALL memcpy 111 skip: 112 MOV ESP,[EBX+12] ; 僗僞僢僋弶婜抣 113 JMP DWORD 2*8:0x0000001b 114 115 waitkbdout: 116 IN AL,0x64 117 AND AL,0x02 118 JNZ waitkbdout ; AND偺寢壥偑0偱側偗傟偽waitkbdout傊 119 RET 120 121 memcpy: 122 MOV EAX,[ESI] 123 ADD ESI,4 124 MOV [EDI],EAX 125 ADD EDI,4 126 SUB ECX,1 127 JNZ memcpy ; 堷偒嶼偟偨寢壥偑0偱側偗傟偽memcpy傊 128 RET 129 ; memcpy偼傾僪儗僗僒僀僘僾儕僼傿僋僗傪擖傟朰傟側偗傟偽丄僗僩儕儞僌柦椷偱傕彂偗傞 130 131 ALIGNB 16 132 GDT0: 133 RESB 8 ; 僰儖僙儗僋僞 134 DW 0xffff,0x0000,0x9200,0x00cf ; 撉傒彂偒壜擻僙僌儊儞僩32bit 135 DW 0xffff,0x0000,0x9a28,0x0047 ; 幚峴壜擻僙僌儊儞僩32bit乮bootpack梡乯 136 137 DW 0 138 GDTR0: 139 DW 8*3-1 140 DD GDT0 141 142 ALIGNB 16 143 bootpack:

1 void HariMain(void) 2 { 3 fin: 4 /* 這里想寫上HLT,但是C語言中不能使用HLT! */ 5 goto fin; 6 }
下面說一下C語言部分:(以后為了啟動操作系統還需要很多處理函數,所以就把這些處理函數打成一個包,為了好理解就取了bootpack名字),這里有c語言基礎的人一眼就懂這個簡單程序的意思了~關鍵是它如何變成機器語言的呢?下面就是詳細過程:
- 首先:使用cc1.exe從bootpack.c生成bootpack.gas[這一步主要實現將c語言編譯為gas匯編]
- 第二步:使用gas2nask.exe從bootpack.gas生成bootpack.nas[這一步主要實現從gas匯編到nas匯編]
- 第三步:使用nask.exe從bootpack.nas生成bootpack.obj[將nas匯編編譯為機器語言]
- 第四步:使用bim2bim.exe從bootpack.obj生成bootpack.bim[制成二進制映像文件]
- 第五步:使用bim2hrb.exe從bootpack.bim生成bootpack.hrb[將映像制成對應操作系統所需要的文件]
- 最后:這樣就做成了機器語言,再適用copy指令將asmhead.bin和bootpack.hrb單純結合起來,就制成了haribote.sys[合並形成系統文件]
PS: 1) C語言編譯生成的匯編不是nask匯編,所以要有1、2兩步將c語言轉換為nask匯編
2) 我們用從C語言轉換來的nask匯編然后轉換成的機器語言是一種特殊的obj機器語言,必須與其他文件鏈接(link)之后才能形成真正可以運行的機器語言
3) 這里我們把obj轉換為bim映像其實就是將各個分部分鏈接在一起,做成一個完整的機器文件
4) 此外,我們為了能讓上面完整的機器語言實際使用,還要針對不同操作系統進行必要的加工,比如說加上識別文件頭、壓縮等
PS: 有人在windows和linux上也做過很多次C語言開發,但是也沒有這么煩,說到底,是人家的編譯器已經很成熟啦!我們為了要開發出適用不同平台的操作系統甚至是自己的操作系統,就沒有將中間的過程省略~
PS: 這里的HariMain不要改動名字[因為程序要從這個地方開始運行,就像main函數的main]
>_<" 這里又要新建一個文件了:naskfunc.nas 它的功能就是用匯編寫一些函數,供c語言調用(因為上面已經說過,32位模式下無法適用BIOS等功能),這個文件里主要寫一些要用匯編實現的功能函數~
1 ; naskfunc 2 ; TAB=4 3 4 [FORMAT "WCOFF"] ; 制成目標文件的形式 5 [BITS 32] ; 制作32模式用的機器語言 6 7 8 ; 制作目標文件信息 9 10 [FILE "naskfunc.nas"] ; 源文件名信息 11 12 GLOBAL _io_hlt ; 程序中包含的函數名 13 14 15 ; 以下是實際的函數 16 17 [SECTION .text] ; 目標文件中寫了這些后再寫程序 18 19 _io_hlt: ; void io_hlt(void); 20 HLT 21 RET
PS: 用匯編寫的函數,之后還要與bootpack.obj鏈接,所以也需要編譯成目標文件。因此將輸出格式設置為WCOFF模式,另外還要設置為32位機器語言模式。在nask目標文件的模式下,必須設定文件名信息,然后再寫明下面函數的名,注意這里的函數名前面要加”-“並且聲明為GLOBAL。再往下就是實際的函數了,這里很簡單~這里在bootpack.c要調用這個函數很簡單,如下:
1 /* 告訴c編譯器,有一個函數在別的文件里 */ 2 3 void io_hlt(void); 4 5 void HariMain(void) 6 { 7 8 fin: 9 io_hlt(); /*執行naskfunc.nas里的_io_hlt*/ 10 goto fin; 11 }
3.3 C語言實現內存寫入
>_<" 上面已經實現了讓畫面黑屏,但是只做到這一點沒意思,還是在畫面上畫點東西比較有趣。想要畫東西的話,只要往VRAM里寫點東西就可以了,但是c語言中又沒有直接寫入指定內存地址的語句(其實是有的,一步步來)。所以我們直接用匯編寫一個函數,在naskfunc.nas里添加如下部分:
1 _write_mem8: ; void write_mem8(int addr, int data); 2 MOV ECX,[ESP+4] ; [ESP+4]中存放的是地址,將其讀入ECX 3 MOV AL,[ESP+8] ; [ESP+8]中存放的是數據,將其讀入AL 4 MOV [ECX],AL 5 RET

1 ; naskfunc 2 ; TAB=4 3 4 [FORMAT "WCOFF"] ; 制成目標文件的形式 5 [INSTRSET "i486p"] ; 指明是486用的,EAX[新加的一條] 6 [BITS 32] ; 制作32模式用的機器語言 7 8 ; 制作目標文件信息 9 [FILE "naskfunc.nas"] ; 源文件名信息 10 GLOBAL _io_hlt,_write_mem8 ;程序中包含的函數名 11 12 ; 以下是實際的函數 13 [SECTION .text] ; 目標文件中寫了這些后再寫程序 14 15 _io_hlt: ; void io_hlt(void); 16 HLT 17 RET 18 19 _write_mem8: ; void write_mem8(int addr, int data); 20 MOV ECX,[ESP+4] ; [ESP+4]中存放的是地址,將其讀入ECX 21 MOV AL,[ESP+8] ; [ESP+8]中存放的是數據,將其讀入AL 22 MOV [ECX],AL 23 RET
上面的函數在c語言里調用write_mem8(0x1234,0x56);語句時,就相當於MOV BYTE[0x1234],0x56。如果C語言中調用了write_mem8函數,就會跳轉到_write_mem8。此時參數指定的數字就是放在內存里,分別是:
- 第一個數字的存放地址:[ESP+4]
- 第二個數字的存放地址:[ESP+8]
- 第三個數字的存放地址:[ESP+12]
- 第四個數字的存放地址:[ESP+16]
- (依次類推...)
那么在bootpack.c里面調用這個函數填寫VRAM內存的方法如下:
1 void io_hlt(void); 2 void write_mem8(int addr, int data); 3 4 void HariMain(void) 5 { 6 int i; /* 聲明變量i,i是一個32位的整數 */ 7 8 for (i = 0xa0000; i <= 0xaffff; i++) { //遍歷VRAM地址,每個寫入15 9 write_mem8(i, 15); /* MOV BYTE [i],15 */ 10 } 11 12 for (;;) { 13 io_hlt(); 14 } 15 }
效果如下:整個屏幕變成了白色,這是因為向VRAM內都寫入的15,意思是所有顏色都是第15個顏色,而第15個顏色正好是純白色!
現在我們稍微改動一下,寫入VRAM中的變量看看會有啥效果,我們將循環中的write_mem8(i,15)改成write_mem8(i,i & 0x0f),效果如下:我們這種寫法就相當於每次寫入i%16,所以每個16個像素,色號就反復一次,而整體呈現出如下的條紋圖案:
3.4 C語言指針的強大
>_<" 上一節開頭說過:c語言不能對內存進行讀寫,其實不是,c語言的指針能完美地完成內存讀寫的工作,這里就用c語言的指針代替上面用匯編寫的write_mem8函數實現內存寫入。
1 void io_hlt(void); 2 3 void HariMain(void) 4 { 5 int i; 6 char *p; /*用於BYTE型地址*/ 7 8 for (i = 0xa0000; i <= 0xaffff; i++) { 9 10 p = i; /* 帶入地址 */ 11 *p = (char*)i & 0x0f; 12 13 /* 代替 write_mem8(i, i & 0x0f); */ 14 } 15 16 for (;;) { 17 io_hlt(); 18 } 19 }
這樣我們就完美的代替了匯編寫的write_mem8函數,不得感嘆一句指針真實強大(其實關於指針的很多細節,這里實在說不了了,但是它很強大,很強大~)!這里只說幾點:
- char *p;是BYTE類地址--short *p;是用於WORD類地址--int *p是用於DWORD類地址
- 其實這樣寫就不用聲明個p了:((char *)i)=i & 0x0f ps:這種寫法和BYTE[i]=i & 0x0f有些像吧
- p表示地址,*p表示地址的內容
- char *p是聲明p,一般把*靠在p上是為了想聲明2個指針時更清晰:char *p,*q;
- *(p+i)也可以寫成p[i]形式,但是這里p[i]不是數組,因為這里*(p+i)可以*(i+p)也行,即i[p]也對
3.5 色號設定與調色板
>_<" 我們想通過上面的內存寫入可以畫出一些亂的圖案,但是我們想具體畫一些東西的時候就要考慮顏色問題了。這里我們采用的是320X200的8位顏色模式,色號使用8位(二進制數),也就是只能從0~255的數。熟悉電腦顏色的人應該知道這是很少的,像電腦里一般用6位16進制的數,即24位(二進制數)來指定顏色。那么該怎樣來指定顏色呢?
>_<" 這個8位色彩模式,是由程序員隨意指定0~255的數字所對應的顏色。比如:25號對應#ffffff,26號對應#123456,如果像剛才那樣程序員不做任何指定,0號就是#000000,15號就是#ffffff。這里我們想要制作個操作系統,因此由前輩的經驗知道,只要用以下16種顏色就夠了:
- #000000:黑 #00ffff:淺亮色 #000084:暗藍 #ff0000:亮紅 #ffffff:白 #840084:暗紫 #00ff00:亮綠 #c6c6c6:亮灰
- #008484:淺暗藍 #ffff00:亮黃 #840000:暗紅 #848484:暗灰 #0000ff:亮藍 #008400:暗綠 #ff00ff:亮紫 #848400:暗黃
所以要對bootpack.c進行較大的修改:

1 /*匯編里面的函數*/ 2 void io_hlt(void); 3 void io_cli(void); //匯編函數,禁止中斷 4 void io_out8(int port, int data); //向指定的設備傳送數據(第一參數是設備號,第二參數是傳送數據) 5 int io_load_eflags(void); //用匯編寫的函數,通過棧的處理獲得當前中斷標志 6 void io_store_eflags(int eflags); //用匯編寫的函數,通過棧的處理設置當前中斷標志 7 8 /*同一個文件中的函數*/ 9 void init_palette(void); 10 void set_palette(int start, int end, unsigned char *rgb); 11 12 void HariMain(void) 13 { 14 int i; 15 char *p; 16 17 init_palette(); /*設定調色板*/ 18 19 p = (char *) 0xa0000; /*指定地址*/ 20 21 for (i = 0; i <= 0xffff; i++) { 22 p[i] = i & 0x0f; 23 } 24 25 for (;;) { 26 io_hlt(); 27 } 28 } 29 30 void init_palette(void) 31 { 32 static unsigned char table_rgb[16 * 3] = { 33 0x00, 0x00, 0x00, /* 0:黑 */ 34 0xff, 0x00, 0x00, /* 1:亮紅 */ 35 0x00, 0xff, 0x00, /* 2:亮綠 */ 36 0xff, 0xff, 0x00, /* 3:亮黃 */ 37 0x00, 0x00, 0xff, /* 4:亮藍 */ 38 0xff, 0x00, 0xff, /* 5:亮紫 */ 39 0x00, 0xff, 0xff, /* 6:淺亮藍 */ 40 0xff, 0xff, 0xff, /* 7:白 */ 41 0xc6, 0xc6, 0xc6, /* 8:亮灰 */ 42 0x84, 0x00, 0x00, /* 9:暗紅 */ 43 0x00, 0x84, 0x00, /* 10:暗綠 */ 44 0x84, 0x84, 0x00, /* 11:暗黃 */ 45 0x00, 0x00, 0x84, /* 12:暗青 */ 46 0x84, 0x00, 0x84, /* 13:暗紫 */ 47 0x00, 0x84, 0x84, /* 14:淺暗藍 */ 48 0x84, 0x84, 0x84 /* 15:暗灰 */ 49 }; 50 set_palette(0, 15, table_rgb); 51 return; 52 /*C語言中static char只能用於數據,就像匯編中的DB指令 */ 53 } 54 55 void set_palette(int start, int end, unsigned char *rgb) 56 { 57 int i, eflags; 58 eflags = io_load_eflags(); /* 記錄中斷許可標志 */ 59 io_cli(); /* 將中斷許可標志置0,禁止中斷 */ 60 io_out8(0x03c8, start); 61 for (i = start; i <= end; i++) { 62 io_out8(0x03c9, rgb[0] / 4); 63 io_out8(0x03c9, rgb[1] / 4); 64 io_out8(0x03c9, rgb[2] / 4); 65 rgb += 3; 66 } 67 io_store_eflags(eflags); /* 恢復原中斷 */ 68 return; 69 }

1 ; naskfunc 2 ; TAB=4 3 4 [FORMAT "WCOFF"] 5 [INSTRSET "i486p"] 6 [BITS 32] 7 [FILE "naskfunc.nas"] 8 9 GLOBAL _io_hlt, _io_cli, _io_sti, _io_stihlt 10 GLOBAL _io_in8, _io_in16, _io_in32 11 GLOBAL _io_out8, _io_out16, _io_out32 12 GLOBAL _io_load_eflags, _io_store_eflags 13 14 [SECTION .text] 15 16 _io_hlt: ; void io_hlt(void); 17 HLT 18 RET 19 20 _io_cli: ; void io_cli(void); 21 CLI 22 RET 23 24 _io_sti: ; void io_sti(void); 25 STI 26 RET 27 28 _io_stihlt: ; void io_stihlt(void); 29 STI 30 HLT 31 RET 32 33 _io_in8: ; int io_in8(int port); 34 MOV EDX,[ESP+4] ; port 35 MOV EAX,0 36 IN AL,DX 37 RET 38 39 _io_in16: ; int io_in16(int port); 40 MOV EDX,[ESP+4] ; port 41 MOV EAX,0 42 IN AX,DX 43 RET 44 45 _io_in32: ; int io_in32(int port); 46 MOV EDX,[ESP+4] ; port 47 IN EAX,DX 48 RET 49 50 _io_out8: ; void io_out8(int port, int data); 51 MOV EDX,[ESP+4] ; port 52 MOV AL,[ESP+8] ; data 53 OUT DX,AL 54 RET 55 56 _io_out16: ; void io_out16(int port, int data); 57 MOV EDX,[ESP+4] ; port 58 MOV EAX,[ESP+8] ; data 59 OUT DX,AX 60 RET 61 62 _io_out32: ; void io_out32(int port, int data); 63 MOV EDX,[ESP+4] ; port 64 MOV EAX,[ESP+8] ; data 65 OUT DX,EAX 66 RET 67 68 _io_load_eflags: ; int io_load_eflags(void); 69 PUSHFD ; 指 PUSH EFLAGS 70 POP EAX 71 RET 72 73 _io_store_eflags: ; void io_store_eflags(int eflags); 74 MOV EAX,[ESP+4] 75 PUSH EAX 76 POPFD ; 指 POP EFLAGS 77 RET
PS: 這里要做幾點說明(這篇文章說的太多了,不多說還說不清,早知道不把這么多放在一起了!)
1)訪問調色板的步驟:(具體參照bootpack.c中的set_palette函數)
- 屏蔽中斷(CLI)
- 將想要設定的調色板號寫入0x03c8,緊接着按着R,G,B的順序寫入0x03c9,如果想繼續設定下一個調色板,省略調色板號,繼續按照RGB順序寫入0x03c9
- 想要讀出當前調色板的狀態,首先要將調色板號寫入0x03c7,再從0x03c9依次讀3次,分別是R,G,B,如果想繼續讀取下一個,也是省略調色板號的設定繼續讀取
- 如果最初執行了CLI那么要恢復中斷STI
2)中斷標志位的讀取和設置要涉及到棧的應用,這里看一下代碼就理解了(前提是你得懂棧這種數據結構)
3)在set_palette中要注意關閉中斷與恢復中斷,這個在操作系統中特別是嵌入式操作系統中應用較多
現在運行一下程序看看效果:[仔細看,和上面那個條紋圖有點不一樣吧]
3.6 簡單界面實現
>_<" 上面我們已經把調色板配好了,接下來看看我們的繪畫能力了,這里我們稍微改動一下bootpack.c里面的函數,實現畫矩形的功能:

1 void io_hlt(void); 2 void io_cli(void); 3 void io_out8(int port, int data); 4 int io_load_eflags(void); 5 void io_store_eflags(int eflags); 6 7 void init_palette(void); 8 void set_palette(int start, int end, unsigned char *rgb); 9 void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1); 10 11 #define COL8_000000 0 12 #define COL8_FF0000 1 13 #define COL8_00FF00 2 14 #define COL8_FFFF00 3 15 #define COL8_0000FF 4 16 #define COL8_FF00FF 5 17 #define COL8_00FFFF 6 18 #define COL8_FFFFFF 7 19 #define COL8_C6C6C6 8 20 #define COL8_840000 9 21 #define COL8_008400 10 22 #define COL8_848400 11 23 #define COL8_000084 12 24 #define COL8_840084 13 25 #define COL8_008484 14 26 #define COL8_848484 15 27 28 void HariMain(void) 29 { 30 char *p; 31 32 init_palette(); 33 34 p = (char *) 0xa0000; 35 36 boxfill8(p, 320, COL8_FF0000, 20, 20, 120, 120); 37 boxfill8(p, 320, COL8_00FF00, 70, 50, 170, 150); 38 boxfill8(p, 320, COL8_0000FF, 120, 80, 220, 180); 39 40 for (;;) { 41 io_hlt(); 42 } 43 } 44 45 void init_palette(void) 46 { 47 static unsigned char table_rgb[16 * 3] = { 48 0x00, 0x00, 0x00, 49 0xff, 0x00, 0x00, 50 0x00, 0xff, 0x00, 51 0xff, 0xff, 0x00, 52 0x00, 0x00, 0xff, 53 0xff, 0x00, 0xff, 54 0x00, 0xff, 0xff, 55 0xff, 0xff, 0xff, 56 0xc6, 0xc6, 0xc6, 57 0x84, 0x00, 0x00, 58 0x00, 0x84, 0x00, 59 0x84, 0x84, 0x00, 60 0x00, 0x00, 0x84, 61 0x84, 0x00, 0x84, 62 0x00, 0x84, 0x84, 63 0x84, 0x84, 0x84 64 }; 65 set_palette(0, 15, table_rgb); 66 return; 67 } 68 69 void set_palette(int start, int end, unsigned char *rgb) 70 { 71 int i, eflags; 72 eflags = io_load_eflags(); 73 io_cli(); 74 io_out8(0x03c8, start); 75 for (i = start; i <= end; i++) { 76 io_out8(0x03c9, rgb[0] / 4); 77 io_out8(0x03c9, rgb[1] / 4); 78 io_out8(0x03c9, rgb[2] / 4); 79 rgb += 3; 80 } 81 io_store_eflags(eflags); 82 return; 83 } 84 85 void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1) 86 { 87 int x, y; 88 for (y = y0; y <= y1; y++) { 89 for (x = x0; x <= x1; x++) 90 vram[y * xsize + x] = c; 91 } 92 return; 93 }
>_<" 進一步我們繪制一個像樣點的窗口:[哈哈,有點那個意思了吧!]

1 void io_hlt(void); 2 void io_cli(void); 3 void io_out8(int port, int data); 4 int io_load_eflags(void); 5 void io_store_eflags(int eflags); 6 7 void init_palette(void); 8 void set_palette(int start, int end, unsigned char *rgb); 9 void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1); 10 11 #define COL8_000000 0 12 #define COL8_FF0000 1 13 #define COL8_00FF00 2 14 #define COL8_FFFF00 3 15 #define COL8_0000FF 4 16 #define COL8_FF00FF 5 17 #define COL8_00FFFF 6 18 #define COL8_FFFFFF 7 19 #define COL8_C6C6C6 8 20 #define COL8_840000 9 21 #define COL8_008400 10 22 #define COL8_848400 11 23 #define COL8_000084 12 24 #define COL8_840084 13 25 #define COL8_008484 14 26 #define COL8_848484 15 27 28 void HariMain(void) 29 { 30 char *vram; 31 int xsize, ysize; 32 33 init_palette(); 34 vram = (char *) 0xa0000; 35 xsize = 320; 36 ysize = 200; 37 38 boxfill8(vram, xsize, COL8_008484, 0, 0, xsize - 1, ysize - 29); 39 boxfill8(vram, xsize, COL8_C6C6C6, 0, ysize - 28, xsize - 1, ysize - 28); 40 boxfill8(vram, xsize, COL8_FFFFFF, 0, ysize - 27, xsize - 1, ysize - 27); 41 boxfill8(vram, xsize, COL8_C6C6C6, 0, ysize - 26, xsize - 1, ysize - 1); 42 43 boxfill8(vram, xsize, COL8_FFFFFF, 3, ysize - 24, 59, ysize - 24); 44 boxfill8(vram, xsize, COL8_FFFFFF, 2, ysize - 24, 2, ysize - 4); 45 boxfill8(vram, xsize, COL8_848484, 3, ysize - 4, 59, ysize - 4); 46 boxfill8(vram, xsize, COL8_848484, 59, ysize - 23, 59, ysize - 5); 47 boxfill8(vram, xsize, COL8_000000, 2, ysize - 3, 59, ysize - 3); 48 boxfill8(vram, xsize, COL8_000000, 60, ysize - 24, 60, ysize - 3); 49 50 boxfill8(vram, xsize, COL8_848484, xsize - 47, ysize - 24, xsize - 4, ysize - 24); 51 boxfill8(vram, xsize, COL8_848484, xsize - 47, ysize - 23, xsize - 47, ysize - 4); 52 boxfill8(vram, xsize, COL8_FFFFFF, xsize - 47, ysize - 3, xsize - 4, ysize - 3); 53 boxfill8(vram, xsize, COL8_FFFFFF, xsize - 3, ysize - 24, xsize - 3, ysize - 3); 54 55 for (;;) { 56 io_hlt(); 57 } 58 } 59 60 void init_palette(void) 61 { 62 static unsigned char table_rgb[16 * 3] = { 63 0x00, 0x00, 0x00, 64 0xff, 0x00, 0x00, 65 0x00, 0xff, 0x00, 66 0xff, 0xff, 0x00, 67 0x00, 0x00, 0xff, 68 0xff, 0x00, 0xff, 69 0x00, 0xff, 0xff, 70 0xff, 0xff, 0xff, 71 0xc6, 0xc6, 0xc6, 72 0x84, 0x00, 0x00, 73 0x00, 0x84, 0x00, 74 0x84, 0x84, 0x00, 75 0x00, 0x00, 0x84, 76 0x84, 0x00, 0x84, 77 0x00, 0x84, 0x84, 78 0x84, 0x84, 0x84 79 }; 80 set_palette(0, 15, table_rgb); 81 return; 82 } 83 84 void set_palette(int start, int end, unsigned char *rgb) 85 { 86 int i, eflags; 87 eflags = io_load_eflags(); 88 io_cli(); 89 io_out8(0x03c8, start); 90 for (i = start; i <= end; i++) { 91 io_out8(0x03c9, rgb[0] / 4); 92 io_out8(0x03c9, rgb[1] / 4); 93 io_out8(0x03c9, rgb[2] / 4); 94 rgb += 3; 95 } 96 io_store_eflags(eflags); 97 return; 98 } 99 100 void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1) 101 { 102 int x, y; 103 for (y = y0; y <= y1; y++) { 104 for (x = x0; x <= x1; x++) 105 vram[y * xsize + x] = c; 106 } 107 return; 108 }