經過 【自制操做系統01】硬核講解計算機的啓動過程 和 【自制操做系統02】環境準備與啓動區實現 的講解,咱們已經實現了一個最簡單的操做系統(僅僅一條機器指令)。html
今天咱們要再往前進一步,逐漸將這個最簡單的操做系統完善起來。以前最簡單的操做系統是寫在啓動區的 512 字節裏,這麼小的空間之後確定不能所有用來寫操做系統的代碼,因此它的主要任務就是將硬盤中更多的數據讀取到內存裏,並跳轉到內存的那個位置開始運行。git
這裏不得不回顧一下每節課都說到的四次跳躍:ide
其實咱們能夠無限跳躍下去,只要感受某一個環節的任務複雜了,就能夠分紅兩步來走。但也徹底能夠從第三跳開始就不再跳轉了,把全部操做系統須要的指令和數據都從硬盤中加載到內存,而後執行,但這樣顯然很差。oop
先不說別的,先發上來一份本章內容的所有代碼學習
;----BIOS把啓動區加載到內存的該位置,因此需設置地址偏移量 section mbr vstart=0x7c00 ;----設置堆棧地址 mov sp,0x7c00 ;----卷屏中斷,目的是清屏 mov ax,0x0600 mov bx,0x0700 mov cx,0 mov dx,0x184f int 0x10 ;----直接往顯存中寫數據 mov ax,0xb800 mov gs,ax mov byte [gs:0x00],'m' mov byte [gs:0x02],'b' mov byte [gs:0x04],'r' ;----讀取硬盤(第2扇區)並加載到內存(0x900) mov eax,0x02 ;起始扇區lba地址,LBA=(柱面號*磁頭數+磁頭號)*扇區數+扇區編號-1 mov bx,0x900 ;寫入的內存地址,以後用 mov cx,4 ;待讀入的扇區數 call read_disk jmp 0x900 ;----讀硬盤方法,eax爲lba扇區號,bx爲待寫入內存地址,cx爲讀入的扇區數 read_disk: mov esi,eax ;備份 mov di,cx ;備份 ;第一步,設置要讀取的扇區數 mov dx,0x1f2 mov al,cl out dx,al mov eax,esi ;恢復 ;第二步,設置LBA地址 mov cl,8 ;0-7位寫入0x1f3 mov dx,0x1f3 out dx,al ;8-15位寫入0x1f4 mov dx,0x1f4 shr eax,cl out dx,al ;16-23位寫入0x1f5 mov dx,0x1f5 shr eax,cl out dx,al ;24-27位寫入0x1f6 mov dx,0x1f6 shr eax,cl and al,0x0f ;lba的24-27位 or al,0xe0 ;另外4位爲1110,表示lba模式 out dx,al ;第三步,寫入讀命令 mov dx,0x1f7 mov al,0x20 out dx,al ;第四步,檢測硬盤狀態 .not_ready: nop in al,dx and al,0x88 ;第4位爲1表示準備好,第7位爲1表示忙 cmp al,0x08 jnz .not_ready ;第五步,讀數據 mov ax,di mov dx,256 mul dx mov cx,ax mov dx,0x1f0 .go_on_read: in ax,dx mov [bx],ax add bx,2 loop .go_on_read ret ;----512字節的最後兩字節是啓動區標識 times 510-($-$$) db 0 db 0x55,0xaa
section loader vstart=0x900 mov byte [gs:0xa0],'l' mov byte [gs:0xa2],'o' mov byte [gs:0xa4],'a' mov byte [gs:0xa6],'d' mov byte [gs:0xa8],'e' mov byte [gs:0xaa],'r'
mbr.bin: mbr.asm nasm -I include/ -o out/mbr.bin mbr.asm -l out/mbr.lst loader.bin: loader.asm nasm -I include/ -o out/loader.bin loader.asm -l out/loader.lst os.raw: mbr.bin loader.bin ../bochs/bin/bximage -hd -mode="flat" -size=60 -q target/os.raw dd if=out/mbr.bin of=target/os.raw bs=512 count=1 dd if=out/loader.bin of=target/os.raw bs=512 count=4 seek=2 brun: make install make only-bochs-run only-bochs-run: ../bochs/bin/bochs -f ../bochs/bochsrc.disk -q install: make clean make -r os.raw clean: rm -rf target/* rm -rf out/*
若是你粗略地讀了一下代碼,起碼能夠知道 mbr.asm 中的代碼,前半部分是在屏幕上輸出一個 mbr 字符串,這是上節課爲了作最小操做系統而用直觀方式寫的代碼,無關緊要。後半部分僅僅是讀取了幾個扇區的硬盤數據,加載到內存中的某個位置,而後跳轉到此位置,這部分是關鍵,也是 mbr 的職責所在。操作系統
那怎麼讀取硬盤中的數據呢,這就要從磁盤的結構提及。硬件的東西並非很懂,因此也只能說個大概。硬盤屬於磁盤的一種,磁盤分爲硬盤和軟盤。但他們的邏輯結構是同樣的:code
盤片(platter)
磁頭(head)
磁道(track)
扇區(sector)
柱面(cylinder)htm
我不想管它怎麼動的,我只須要想明白,肯定一個磁頭、柱面、扇區,就肯定了一個 512 字節大小的區域,這就夠了。這也就是硬盤的 CHS 表示法,即 Cylinder(柱面)、Head(磁頭)、Sector(扇區),只要知道了硬盤的 CHS 的數目,便可肯定硬盤的容量,硬盤的容量 = 柱面數 × 磁頭數 × 扇區數 × 512B。blog
若是不考慮這個物理結構,其實硬盤就是 n 多個 512 字節的區域構成的,咱們徹底能夠從 0 開始編號,每 512 字節加一,這樣就能夠徹底不用考慮什麼扇區啦,柱面啦,這種是我比較喜歡的(看來仍是軟件工程師思想呀),這種方式叫作 LBA 表示法。接口
LBA = (柱面號 * 磁頭數 + 磁頭號) * 扇區數 + 扇區編號 - 1
因此 CPU 要和硬盤打交道,要麼用這個 CHS 表示法,就至少要告訴硬盤柱面、磁頭、扇區號是多少,要麼用 LBA 表示法告訴硬盤一個 LBA 號碼,而後再給硬盤一個是讀仍是寫的信號。硬盤製做廠商千千萬,CPU製做廠商也是各不相同,天然就會想到必定有一個硬盤接口標準,這個標準就叫作 ATA 標準,也能夠俗稱爲 IDE 硬盤接口技術標準。這個標準能夠下載 AT_Attachment_with_Packet_Interface 共三冊的內容,但咱們用不到那麼多,我這裏找到了一個還算原汁原味的中文版的論文 《IDE接口硬盤讀寫技術》 ,看這個基本就夠用了。
CPU 與外設是經過 IO 接口交互的,因此最核心的就是這個技術標準定義的 IO 接口都有哪些,分別有什麼做用
I/O地址 | 讀(主機從硬盤讀數據) | 寫(主機數據寫入硬盤) |
---|---|---|
1F0H | 數據寄存器 | 數據寄存器 |
1F1H | 錯誤寄存器(只讀寄存器) | 特徵寄存器 |
1F2H | 扇區計數寄存器 | 扇區計數寄存器 |
1F3H | 扇區號寄存器或 LBA 塊地址 0~7 | 扇區號或 LBA 塊地址 0~7 |
1F4H | 磁道數低 8 位或 LBA 塊地址 8~15 | 磁道數低 8 位或 LBA 塊地址 8~15 |
1F5H | 磁道數高 8 位或 LBA 塊地址 16~23 | 磁道數高 8 位或 LBA 塊地址 16~23 |
1F6H | 驅動器/磁頭或 LBA 塊地址 24~27 | 驅動器/磁頭或 LBA 塊地址 24~27 |
1F7H | 命令寄存器或狀態寄存器 | 命令寄存器 |
因此若是要寫一個程序來讀文件的話,不難分析出整個過程就是:
這五步剛恰好對應着上面的代碼
最後,別忘了咱們這些代碼仍然是要加載到啓動區的,因此最後兩個字節依然要是啓動區標識符 0x55 0xaa
寫好了 mbr.asm,咱們再寫一個 loader.asm,設置其起始地址爲 0x900(由於讀寫磁盤後存入的內存位置就是這個,這是咱們本身定義的),並把它放在磁盤的第二扇區(這也是咱們本身定的,只要和讀盤的代碼保持一致就行)
#### loader.asm
section loader vstart=0x900 mov byte [gs:0xa0],'l' mov byte [gs:0xa2],'o' mov byte [gs:0xa4],'a' mov byte [gs:0xa6],'d' mov byte [gs:0xa8],'e' mov byte [gs:0xaa],'r'
剩下的精華就在於咱們的 Makefile 文件了,能夠參考下上面的代碼
執行 make brun,能夠看到以下效果,說明加載磁盤中的 loader 代碼到內存這個過程生效了。
若是你對自制一個操做系統感興趣,不妨跟隨這個系列課程看下去,甚至加入咱們,一塊兒來開發。
《操做系統真相還原》這本書真的贊!強烈推薦
當你看到該文章時,代碼可能已經比文章中的又多寫了一些部分了。你能夠經過提交記錄歷史來查看歷史的代碼,我會慢慢梳理提交歷史以及項目說明文檔,爭取給每一課都準備一個可執行的代碼。固然文章中的代碼也是全的,採用複製粘貼的方式也是徹底能夠的。
若是你有興趣加入這個自制操做系統的大軍,也能夠在留言區留下您的聯繫方式,或者在 gitee 私信我您的聯繫方式。
本課程打算出系列課程,我寫到哪以爲能夠寫成一篇文章了就寫出來分享給你們,最終會完成一個功能全面的操做系統,我以爲這是最好的學習操做系統的方式了。因此中間遇到的各類坎也會寫進去,若是你能持續跟進,跟着我一塊寫,必然會有很好的收貨。即便沒有,交個朋友也是好的哈哈。
目前的系列包括