前言
由于上一章中的budOS.img都是用十六進(jìn)制寫的依痊,里面很多數(shù)據(jù)意義不明,而且大量的0x00即使復(fù)制粘貼都很花時(shí)間怎披,而且容易出錯(cuò)胸嘁。這一章中,我們將使用匯編來重寫這個(gè)系統(tǒng)钳枕,并解釋系統(tǒng)中各個(gè)參數(shù)的含義。
budOS-0.0.2
這次版本中還是只有兩個(gè)文件:
其中budOS.asm就是我們的操作系統(tǒng)啦赏壹。.asm是匯編文件的后綴鱼炒,可以用文本編輯器直接打開。但是計(jì)算機(jī)無法直接理解匯編語言蝌借,我們需要一款匯編器來將匯編語言編譯成機(jī)器語言昔瞧,這里推薦使用開源軟件NASM(原書中作者使用了自己改造的匯編器,改動(dòng)了匯編語言的語法菩佑,為了學(xué)習(xí)真正通用的匯編自晰,這里使用最為通用的NASM),下載地址NASM稍坯,請(qǐng)根據(jù)自己計(jì)算機(jī)的操作系統(tǒng)安裝合適的版本酬荞。
接下來開一下budOS.asm里面的內(nèi)容搓劫。這個(gè)文件也不是我自己寫的,只是參照《30天自制操作系統(tǒng)》以及這篇文章鏈接混巧。我也是因?yàn)榭吹竭@篇文章寫的很好枪向,才想自己也寫一下博客,可惜作者只更新了前三章就沒有再更新了咧党。這里面作者描述的很詳細(xì)秘蛔,推薦看一下,這里我只簡單地過一遍傍衡。
這里的所謂扇區(qū)深员,是指磁盤上的一段弧形區(qū)域。磁盤被劃分成一個(gè)個(gè)同心圓(即磁道)蛙埂,每個(gè)圓又以512個(gè)字節(jié)為單位劃分為一段段圓弧倦畅,這些圓弧就是扇區(qū),扇區(qū)是最小的讀寫單元箱残。磁盤的磁道從0開始計(jì)數(shù)滔迈,0磁道是同心圓里最大的,也就是最外面的圓被辑。扇區(qū)從1開始累加計(jì)數(shù)燎悍,0磁道的第一個(gè)扇區(qū)為1扇區(qū),第二個(gè)為2扇區(qū)盼理。假如0磁道有32個(gè)扇區(qū)谈山,則1磁道的第一個(gè)扇區(qū)即為33扇區(qū)。不難得到磁盤容量為扇區(qū)數(shù)*512字節(jié)宏怔。
下面直接貼上budOS.asm的代碼:
; hello-os
; TAB=4
; 下面實(shí)現(xiàn)FAT12格式軟盤代碼
DB 0xeb, 0x4e, 0x90
DB "budOSIPL" ;啟動(dòng)區(qū)的名稱可以是任意的字符串奏路,但長度必須是8字節(jié)
DW 512; 每一個(gè)扇區(qū)的大小,必須是512字節(jié)
DB 1 ;簇的大须铩(必須為1個(gè)扇區(qū))
DW 1 ;FAT的起始位置(一般從第一個(gè)扇區(qū)開始)
DB 2 ;FAT的個(gè)數(shù) 必須是2
DW 224;根目錄的大小 一般是224項(xiàng)
DW 2880; 該磁盤的大小 必須是2880扇區(qū)
DB 0xf0;磁盤的種類 必須是0xf0
DW 9;FAT的長度 必須是9扇區(qū)
DW 18;1個(gè)磁道(track) 有幾個(gè)扇區(qū) 必須是18
DW 2; 磁頭個(gè)數(shù) 必須是2
DD 0; 不使用分區(qū)鸽粉,必須是0
DD 2880; 重寫一次磁盤大小
DB 0,0,0x29 ;擴(kuò)展引導(dǎo)標(biāo)記 固定0x29
DD 0xffffffff ;卷列序號(hào)
DB "budOS " ;磁盤的名稱(11個(gè)字節(jié))
DB "FAT12 " ;磁盤的格式名稱(8字節(jié))
TIMES 18 DB 0; 先空出18字節(jié) 這里與原文寫法不同
;程序主體
DB 0xb8, 0x00, 0x00, 0x8e, 0xd0, 0xbc, 0x00, 0x7c
DB 0x8e, 0xd8, 0x8e, 0xc0, 0xbe, 0x74, 0x7c, 0x8a
DB 0x04, 0x83, 0xc6, 0x01, 0x3c, 0x00, 0x74, 0x09
DB 0xb4, 0x0e, 0xbb, 0x0f, 0x00, 0xcd, 0x10, 0xeb
DB 0xee, 0xf4, 0xeb, 0xfd
;信息顯示部分
DB 0x0a, 0x0a ;兩次換行
DB "Hello, I'm buddingpop!"
DB 0x0a ; 換行
DB 0
TIMES 0x1fe-($-$$) DB 0 ;填寫0x00,知道0x001fe
DB 0x55,0xaa
;啟動(dòng)區(qū)以外的輸出
DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
TIMES 4600 DB 0
DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
TIMES 1469432 DB 0
可見代碼量很小,已經(jīng)可以全部貼上來了抓艳。代碼中一些常見指令:
;:注釋
DB:define byte触机,定義一個(gè)字節(jié)(8bit)
DW:define word,定義一個(gè)字(16bit)
DD:define double word玷或,定義兩個(gè)字(32bit)
TIMES n DB m:重復(fù)DB m操作n次儡首,DB也可以是其他操作,這是我們代碼量大大減少的原因
$:當(dāng)前位置
$$:當(dāng)前扇區(qū)起始地址
對(duì)照budOS-0.0.1中的budOS.img文件偏友,不難發(fā)現(xiàn)蔬胯,我們只是用匯編的方法將他重寫了一遍。至于為什么要這么寫呢位他?看可以參照上面的windows讀軟盤第一個(gè)扇區(qū)的讀法圖氛濒,都是能夠一一對(duì)應(yīng)的产场。當(dāng)然這只是第一個(gè)扇區(qū),從0x0000到0x01ff這512個(gè)字節(jié)的內(nèi)容泼橘,后面的我們暫時(shí)不要管涝动。最后兩個(gè)字節(jié)的內(nèi)容如果是0x55與0xaa,則表示該扇區(qū)內(nèi)包含啟動(dòng)代碼炬灭。
我們按照一定規(guī)則(匯編語法)用budOS.asm重寫了budOS.img醋粟,但是計(jì)算機(jī)是看不懂.asm這種文本文件的,因此需要使用上面提到的匯編器將其翻譯成機(jī)器語言重归。打開build.bat文件米愿,可以發(fā)現(xiàn)里面有三條命令:
nasm budOS.asm -o budOS.img
qemu-system-x86_64 budOS.img
pause
其中第一條的含義為使用nasm將源文件budOS.asm編譯為名為budOS.img的目標(biāo)文件,這條命令執(zhí)行完成后會(huì)在當(dāng)前文件夾下生成文件budOS.img鼻吮,之后的流程就與上一章的一樣啦育苟。雙擊build.bat文件,我們的操作系統(tǒng)又可以整場地運(yùn)行了椎木。我們可以對(duì)比一下budOS-0.0.1和0.0.2兩個(gè)文件的內(nèi)容违柏,用二進(jìn)制編輯器打開兩個(gè).img文件,發(fā)現(xiàn)是完全一樣的(除了我改了一點(diǎn)打印的字符之外)香椎。有了匯編器以后漱竖,我們就可以不用瘋狂的敲0啦,而.img文件也可以從我們的源代碼里刪掉了畜伐。
budOS-0.0.3
以上我們只是用匯編將.img重新寫了一遍馍惹,但其中有很多地方為什么要這么寫還不是很了解,比如程序主體部分÷杲纾現(xiàn)在我們用更加清楚的匯編語法來描述這段代碼究竟在干什么万矾。從這里開始大家就可以自己手敲代碼啦。
budOS-0.0.3相對(duì)于0.0.2只是修改了匯編程序慎框,具體代碼如下:
; hello-os
; TAB=4
ORG 0x7c00 ;程序的裝載地址
; 下面實(shí)現(xiàn)FAT12格式軟盤代碼
JMP entry
DB 0x90
DB "budOSIPL" ;啟動(dòng)區(qū)的名稱可以是任意的字符串良狈,但長度必須是8字節(jié)
DW 512; 每一個(gè)扇區(qū)的大小,必須是512字節(jié)
DB 1 ;簇的大斜靠荨(必須為1個(gè)扇區(qū))
DW 1 ;FAT的起始位置(一般從第一個(gè)扇區(qū)開始)
DB 2 ;FAT的個(gè)數(shù) 必須是2
DW 224;根目錄的大小 一般是224項(xiàng)
DW 2880; 該磁盤的大小 必須是2880扇區(qū)
DB 0xf0;磁盤的種類 必須是0xf0
DW 9;FAT的長度 必須是9扇區(qū)
DW 18;1個(gè)磁道(track) 有幾個(gè)扇區(qū) 必須是18
DW 2; 磁頭個(gè)數(shù) 必須是2
DD 0; 不使用分區(qū)薪丁,必須是0
DD 2880; 重寫一次磁盤大小
DB 0,0,0x29 ;擴(kuò)展引導(dǎo)標(biāo)記 固定0x29
DD 0xffffffff ;卷列序號(hào)
DB "budOS " ;磁盤的名稱(11個(gè)字節(jié))
DB "FAT12 " ;磁盤的格式名稱(8字節(jié))
TIMES 18 DB 0; 先空出18字節(jié) 這里與原文寫法不同
;程序核心
entry:
MOV AX,0 ;初始化寄存器
MOV SS,AX
MOV SP,0x7c00
MOV DS,AX
MOV ES,AX
MOV SI,msg
putloop:
MOV AL,[SI]
ADD SI,1 ;SI加一
CMP AL,0
JE fin
MOV AH,0x0e ;顯示一個(gè)文字
MOV BX,15 ;指定字符顏色
INT 0x10 ;調(diào)用顯卡BIOS
JMP putloop
fin:
HLT ;讓cpu停止,等待指令
JMP fin ;無限循環(huán)
msg:
DB 0x0a, 0x0a ;換行兩次
DB "Hello, I am buddingpop!"
DB 0x0a ;換行
DB 0
TIMES 0x1fe-($-$$) DB 0 ;填寫0x00,直到0x001fe
DB 0x55,0xaa
這一段里面又多了一些新指令:
ORG:origin猎醇,告訴編譯器這段代碼要被搬運(yùn)至內(nèi)存的起始地址窥突;
JMP:jump努溃,跳轉(zhuǎn)指令硫嘶,跳轉(zhuǎn)至tag處;
MOV:move梧税,賦值指令沦疾;
[]:取內(nèi)存地址中的內(nèi)容称近;
ADD:加法指令;
CMP:compare哮塞, 比較刨秆;
JE:jump equal,相等則跳轉(zhuǎn)忆畅;
INT:interrupt衡未,中斷;
HLT:halt家凯,停止指令缓醋。
這里還有一些寄存器的符號(hào),寄存器屬于cpu绊诲,主要包括:
其中AX送粱,BX,CX掂之,DX抗俄,SP,BP世舰,SI动雹,DI均是16位寄存器,AX冯乘,BX洽胶,CX,DX又可以分為低8位與高8位裆馒,例如AX就可以分為AL和AH姊氓。在32與64位cpu中,這些寄存器會(huì)被擴(kuò)展到32位(EAX等)與64位(RAX等)喷好,但仍然可以只使用低位翔横,因此向下兼容。CS梗搅,DS禾唁,SS,ES稱為段寄存器无切,不管是32位與64位cpu下都是16位的荡短。
這樣上面的代碼就很清楚啦,運(yùn)行build.bat可以發(fā)現(xiàn)我們的操作系統(tǒng)能夠正常運(yùn)行哆键。打開生成的budOS.img掘托,會(huì)發(fā)現(xiàn)這次的有一些不一樣,只包含了一個(gè)扇區(qū)籍嘹,即512字節(jié)的內(nèi)容闪盔,后面的都丟失了弯院,但程序依然正常運(yùn)行。這是為什么呢泪掀?我也不知道哈哈听绳,不過繼續(xù)看下去就知道了。
總結(jié)
我們使用匯編指令替代了十六進(jìn)制异赫。匯編器能夠幫助我們將匯編翻譯成機(jī)器語言椅挣,但機(jī)器語言具體是怎么調(diào)用顯卡的呢?以及這些寄存器為什么要這樣用呢塔拳?我們暫時(shí)不得而知贴妻,不過后面就會(huì)學(xué)習(xí)到啦。下一次我們就要開始引入c語言啦蝙斜。
P.S. 感覺每次程序更新的比較少名惩,現(xiàn)在開始我每個(gè)版本盡量多加一點(diǎn)內(nèi)容進(jìn)去。