30天自制作業系統——第3天實驗總結

Nuclesome0發表於2020-11-28
實驗日期實驗專案
2020.10.15第3天 進入32位模式並匯入C語言

一、實驗主要內容

1、 內容1 製作真正的IPL

(1).內容概要

  • 實驗內容:認識軟盤的結構,並使用IPL來裝載程式;優化Makefile內容,使用變數定義去簡化Makefile的寫法。
  • 實驗重點:能夠使用匯編去讀取磁碟,清楚呼叫0x13號BIOS的相關值的設定。使用IPL來裝載程式。

軟盤的結構:軟盤為多層環狀磁條組成。一共有80個柱面(0-79號),每個柱面有18個扇區(1-18號),讀取時區分正反面,其中正面為磁頭0,反面為磁頭1。描述磁碟的某個位置時 可以用Cx-Hy-Sz表示,例如本次實驗中含有IPL的啟動區就位於C0-H0-S1(柱面0,磁頭0,扇區1)磁碟結構示意圖如下所示:
在這裡插入圖片描述
Makefile檔案中的變數表示:在Makefile檔案中將一些經常用到的路徑或者命令設定為變數,定義在檔案開頭,當編寫命令時,用到這些變數時,只需要將變數名外加上括號和$即可。簡化了Makefile的書寫。

記憶體地址的裝載:啟動區在記憶體地址0x7c00 ~ 0x7dff,用於啟動載入程式,軟盤的啟動區在0x8000~0x81ff,軟盤的資料從0x8200開始裝載,即第2扇區開始,每個扇區所佔空間為512個位元組

(2).關鍵程式碼分析

;讀取磁碟
MOV		AX,0x0820
		MOV		ES,AX
		MOV		CH,0			; 柱面0
		MOV		DH,0			; 磁頭0
		MOV		CL,2			; 扇區2
		MOV		AH,0x02			; AH=0x02 : 表示讀取磁碟操作
		MOV		AL,1			; 1個磁碟
		MOV		BX,0
		MOV		DL,0x00			; A驅動器
		INT		0x13			;呼叫磁碟BIOS
		JC		error           ;jump if carry  如果進位值為1,說明出現了錯誤跳轉到error輸出錯誤資訊

這部分程式碼是在第2天內容的基礎上新增的讀取磁碟的操作,讀取磁碟相關的暫存器的設定根據筆者的書總結如下:

暫存器設定的值表示的含義
AH0x02讀取磁碟
AH0x03寫入磁碟
AH0x04校驗
AH0x0c尋道
ALxx表示處理的扇區數,這裡只能是連續的扇區
CH柱面號&0xff軟盤的柱面號
CL扇區號(0-5位)(柱面號&0x300)>>2
DH0或1表示軟盤的磁頭的正面或者反面
DL驅動器號驅動器號
ES:BX緩衝地址表示的實際地址為ES*16+BX

上述暫存器的值在每次讀取磁碟時都需要進行相應的設定。另外,返回值在這裡是用來檢測是否有錯誤的,如果FLAGS.CF為1說明有錯誤,錯誤號碼存入AH內,否則AH=0。

其餘程式碼部分和第2天的程式碼一致。

2、 內容2 試錯

(1).內容概要

  • 實驗內容:軟盤在讀取過程中可能會出現一些意料之外的錯誤,本次實驗將重複讀取磁碟多次,避免偶爾磁碟出錯帶來的影響。

(2).關鍵程式碼分析

;讀取磁碟
		MOV		AX,0x0820
		MOV		ES,AX
		MOV		CH,0			; 柱面0
		MOV		DH,0			; 磁頭0
		MOV		CL,2			; 扇區2
		MOV		SI,0			; 記錄讀取失敗的次數,如果次數超過5次,則輸出錯誤資訊
retry:
		MOV		AH,0x02			; AH=0x02 : 讀入磁碟
		MOV		AL,1			; 1個扇區
		MOV		BX,0
		MOV		DL,0x00			; A驅動器
		INT		0x13			; 呼叫磁碟BIOS
		JNC		fin				; 沒有出錯則跳轉到fin
		ADD		SI,1			; 出錯了,SI記錄的次數+1
		CMP		SI,5			; SI和5比較
		JAE		error			; SI >= 5 跳轉到error
		MOV		AH,0x00
		MOV		DL,0x00			; A驅動器
		INT		0x13			; 重置驅動器
		JMP		retry           ;繼續嘗試

這部分程式碼設定了多次讀取磁碟的操作,如果沒有讀取成功,則一直重複retry部分的程式碼。JNC指令表示進位標誌位是0就跳轉;JAE表示大於或等於時跳轉。在重新讀取磁碟前,需要進行系統復位,即“AH=0x00,DL=0x00,INT 0x13”

3、 內容3 讀到18扇區和讀入10個柱面

(1).內容概要

  • 實驗內容: 學會讀入18個扇區和10個柱面的方法。

指定處理的扇區數,範圍在0x01~0xff(指定0x02以上數值時,需要特別注意能夠處理多個扇區的條件。如果是FD的話,就不能跨越多個磁軌,也不能超過64KB的界限。

(2).關鍵程式碼分析

readloop部分和retry部分
readloop:
		MOV SI ,0              ; 記錄失敗次數
retry:
		MOV AH,0x02
		MOV Al ,1              ;一個扇區,這裡是一個扇區一個扇區讀取
		MOV BX,0
		MOV DL,0x00           ;驅動器A
		INT 0x13              ;呼叫BIOS
		JNC next              ;沒有出錯就跳轉到next,next是讀取磁碟
		ADD SI ,1             ;SI 1,記錄失敗次數
		CMP SI ,5             ;和試錯允許次數5比較
		JAE error             ;超過允許的範圍,則跳轉到error,輸出錯誤資訊
		MOV AH, 0x00 
		MOV DL ,0x00
		INT 0x13              ;這三句表示重置驅動器
		JMP retry

這部分程式碼是多次讀取磁碟,防止偶爾磁碟出錯帶來的問題。如果沒有出錯,則跳轉到next讀取下一個扇區。

next部分:
next:
		MOV AX,ES          
		ADD AX ,0x0020     
		MOV ES ,AX          ;將記憶體地址後移0x200,相當於到下一個扇區起始地址
		ADD CL,1            ;下一個扇區
		CMP CL ,18          ;比較CL和18,即判斷是否讀到18扇區
		JBE readloop        ;每次讀取新的扇區前需要將記錄失敗次數的暫存器重複值為0
		MOV CL,1            ;從扇區1開始
		ADD DH,1            ;DH+1,表示從反面讀
		CMP DH ,2            
		JB  readloop            
		MOV DH,0            ;從正面開始讀
		ADD CH,1            ;下一個柱面
		CMP CH,CYLS         ;比較CH和CYLS,即判斷是否讀完10個柱面
		JB readloop

這部分程式碼的實現邏輯是先判斷是否讀到18個扇區,讀完18個扇區則從反面繼續讀,正反面讀取完成就開始讀取下一個柱面,直到讀取到指定的柱面。

4、 內容4 著手開發作業系統

(1).內容概要

  • 實驗內容: 學會將自己編寫的小程式編譯後得到的sys檔案儲存映像檔案中;從啟動區執行作業系統;確認作業系統的執行情況。
  • 實驗重點:瞭解從啟動區執行作業系統的簡單流程,並學會呼叫BOIS確認作業系統的實際執行情況。

儲存到映像中的步驟:

	a.使用make install指令,將磁碟映像檔案寫入磁碟。
	b.在Windows中開啟磁碟,將haribote.sys儲存到磁碟上。
	c.使用工具將磁碟備份為磁碟映像。

一般向一個空軟盤中儲存檔案時,檔名會寫在0x002600以後的地方,檔案的內容會寫在0x004200以後的地方。當我們將作業系統本身的內容寫到名為haribote.sys的檔案中,再將其儲存到磁碟映像裡,確定好磁碟映像0x4200在記憶體中對應的地址位置,即可從啟動區執行作業系統。例如將我們編寫的nas檔案製作映像檔案後開啟結果如下:
在這裡插入圖片描述
在這裡插入圖片描述
顯示卡模式設定:設定AH=0x00,AL根據具體的數值設定相應的模式。其中0x03表示16色字元模式,8025;0x12表示VGA模式,6404804位彩色模式,獨特的4面儲存模式;0x13表示VGA圖形模式,320200*8為彩色模式,調色盤模式;0x6a表示擴充套件的VGA圖形模式;無返回值。

記憶體的地址分佈,下圖是實驗過程中找的記憶體的地址分割槽圖,對於理解筆者彙編的涉及到的地址有一定的幫助。
在這裡插入圖片描述
(2).關鍵程式碼分析
在這裡插入圖片描述
這部分程式碼的功能是使得CPU進行待機功能的小程式。ORG表示將磁碟的內容裝載到0xc200處。在ipl.nas中讀取磁碟完成後加上JMP 0xc200表示跳轉到0xc200。當裝載啟動區執行時,讀取磁碟完成後,就會跳轉到0xc200處執行我們預先使用ORG指令裝載好的程式內容。INT 0x10表示呼叫顯示卡,對應的暫存器AH設定為0x00,AL 設定為對應模式。

5、 內容5 進入32位模式,並匯入C語言,實現HLT指令

(1).內容概要

  • 實驗內容: 瞭解進入32位模式下需要做哪些前期準備;學會將編寫的C語言程式匯入sys檔案;編寫實現特定功能的C語言程式,如HLT指令。
  • 實驗重點:32位模式和16位模式下的區別;如何將一個C程式檔案變成一個彙編程式,並匯入sys檔案。

32位模式和16位模式:32位模式,指的是CPU的模式。CPU有32位和16兩種模式。如果通過16位模式啟動,使用AX和CX等暫存器會十分方便,而EAX和ECX等32位的暫存器就會比較麻煩。在不同的模式下機器語言的命令程式碼不一樣,解釋方法不一樣,故16位模式的機器語言在32位模式下不能執行。另外,32位下可以使用CPU的自我保護功能(識別出可疑的機器語言並進行遮蔽,以避免破壞系統)。

C語言檔案變成機器語言,並生成haribote.sys的基本操作步驟:

	a.使用ccl.exe將bootpack.c生成bootpack.gas
	b.使用gas2nask.exe將bootpack.gas生成bootpack.nas
	c.使用nask.exe將bootpack.nas生成bootpack.obj
	d.使用obi2bim.exe將bootpack.obj生成bootpack.bim
	e.使用bim2hrb.exe將bootpack.bim生成bootpack.hrb
	f.使用copy指令將asmhead.bin與bootpack.hrb結合生成baribote.sys檔案

整個過程的流程圖如下所示:
在這裡插入圖片描述
在這裡插入圖片描述
編寫c程式和彙編程式時需要注意,在nask目標檔案的模式下,必須設定檔名資訊,寫明程式的函式名。特別在函式名前需要加上“_”,否則就不能很好地與C語言函式連結,連結的函式名必須使用GLOBAL宣告。

(2).關鍵程式碼分析

haribote.nas程式碼
; BOOT_INFO
CYLS	EQU		0x0ff0			;設定啟動區
LEDS	EQU		0x0ff1
VMODE	EQU		0x0ff2			; 關於顏色數目的資訊。顏色的位數
SCRNX	EQU		0x0ff4			; 解析度X
SCRNY	EQU		0x0ff6			; 解析度Y
VRAM	EQU		0x0ff8			; 圖形緩衝區的開始地址

		ORG		0xc200			; 程式被裝載到內容中的地址
		MOV		AL,0x13			; VGA顯示卡,320*200*8位彩色
		MOV		AH,0x00
		INT		0x10
		MOV		BYTE [VMODE],8	; 記錄畫面模式
		MOV		WORD [SCRNX],320
		MOV		WORD [SCRNY],200
		MOV		DWORD [VRAM],0x000a0000
; 用BIOS取得鍵盤上各種LED指示燈的狀態
		MOV		AH,0x02
		INT		0x16 			; keyboard BIOS
		MOV		[LEDS],AL	
fin:
		HLT
		JMP fin

這部分程式碼主要是設定畫面模式,畫面的畫素數,顏色數和從BIOS取得的鍵盤資訊都儲存起來,儲存在記憶體中0x0ff0的位置處。

naskfunc.nas程式碼:
[FORMAT "WCOFF"]				; 製作目標檔案的模式
[BITS 32]						; 製作32位模式用的機器語言
; 製作目標檔案的資訊
[FILE "naskfunc.nas"]			; 程式中包含的函式名
		GLOBAL	_io_hlt			; 
; 以下是實際的函式
[SECTION .text]		
_io_hlt:	; void io_hlt(void);
		HLT
		RET

這部分程式碼是彙編寫的HLT指令的實現函式

bootpack.c的程式碼:
void io_hlt(void);/* 函式宣告用;表示函式在別的檔案中 */
void HariMain(void)
{
fin:
	io_hlt(); /* 執行naskfunc.nas裡面的_io_hlt函式 */
	goto fin;
}

這部分程式碼是用C語言寫的具有停機功能的程式,裡面呼叫了彙編程式碼寫的HLT指令。

二、遇到的問題及解決方法

1、 描述問題1

  • 問題描述

在筆者書中提到,將軟盤內容讀到記憶體0x8200,記憶體結構中0x7c00 ~ 0x7dff用於載入ipl,0x7e00~0x7fff用於引導區執行時棧,為什麼計算0x4200對應位置時是從0x8000開始?

  • 解決方法

通過上網查閱資料,BIOS將磁碟0位置的內容拷貝到了記憶體0x7c00處,執行啟動區的內容後,會將軟盤內容讀入第2個扇區即0x8200的位置,而0x4200是相對於0號地址中第1個扇區的起始地址,所以0x4200對應記憶體中0x8200+0x4200-0x200=0x8000+0x4200。

2、 描述問題2

  • 問題描述:
    在這裡插入圖片描述
  • 解決方法

make –r img時沒有規則生成ipl10.bin檔案。仔細檢查後發現筆者Makefile檔案中使用的工具是去上一級目錄中z_tools裡面找,而我新建了一個資料夾,導致沒有工具生成ipl10.bin檔案。

修改Makefile檔案中所有對應路徑;將路徑修改為Makefile中對應的路徑,前者需要修改的地方太多,還是選擇後者吧。

三、程式設計創新點

1、 描述創新點1,關鍵程式碼及結果截圖

  • 創新點1

根據教材58頁上VRAM儲存著畫面的畫素,而每個畫素在地址0xa0000~0xaffff中,改變地址裡邊的畫素即可改變顯示畫面。

  • 關鍵程式碼
bootpack.c程式碼
void change(int s,int d);
void HariMain(void)
{
	int i;
	for(i=0xa0000;i<=0xaffff;i+=0x200)
	{
		if(i>0xaffff||i+10>0xaffff||i+20>0xaffff) break;
		change(i,45);
		change(i+10,10);
		change(i+20,8);
	}
}

這部分程式碼是用來修改制定地址中畫素,change函式用匯編程式碼編寫。

change程式碼
[FORMAT "WCOFF"]				; 製作目標檔案的模式
[INSTRSET "i486p"]
[BITS 32]						; 製作32位模式用的機器語言
; 製作目標檔案的資訊
[FILE "naskfunc.nas"]			; 程式中包含的函式名
		GLOBAL	_change			; 
[SECTION .text]		
_change:	; void change(int s,int d);
		MOV ECX,[ESP++4]
		MOV AL,[ESP+8]
		MOV [ECX],AL
		RET

函式呼叫過程中引數1,引數2放在棧中esp+4和esp+8的位置,故只需要將esp+8處的資料放到esp+4的地址中即可。

  • 結果截圖
    在這裡插入圖片描述

四、實驗心得體會

  • 本次實驗是自制作業系統的第3天,學習的內容比較多,包括繼續介紹組合語言的相關指令和BIOS的呼叫;講解讀取磁碟的彙編程式;從啟動區開始執行作業系統,並確認其執行情況;進入32位模式匯入C語言等內容。有了之前組合語言的基礎,在閱讀教材的時候比較容易理解筆者所寫的彙編程式碼,比較不清楚的是關於軟盤的結構,磁碟和記憶體地址的對應關係,通過查閱相關資料,解決了自己遇到的問題。總體來說,這次實驗內容多,但是都不難理解,跟著筆者的講解一步步深入,實驗很容易完成。
  • 在這次實驗中梳理C程式轉化為彙編程式碼的步驟時,用到了流程圖的方式描述,清晰簡潔,可以在以後學習過程中對繁雜步驟梳理,以便學習和理解。實驗中受到教材58頁VRAM儲存著畫面的畫素,於是想通過改變不同地址中畫素的大小來繪製簡單的圖案,這就是本次實驗我想到的創新點。另外,是否能用C程式實現其他功能,比如簡單加法輸出。

相關文章