《一個操作系統的實現》(一):不到20行的操作系統代碼的解釋

最開始的一段操作系統代碼:

	org 07c00h		;告訴編譯器程序加載到7c00處
	mov ax, cs
	mov ds, ax
	mov es, ax
	call DispStr		;調用顯示字符串例程
	jmp $			;無限循環
DispStr:
	mov ax, BootMessage
	mov bp, ax		;ES:BP = 串地址
	mov cx, 16		;CX = 串長度
	mov ax, 01301h		;AH = 13, AL = 01h
	mov bx, 000ch		;頁號爲0(BH=0)黑底紅字(BL=0Ch,高亮)
	mov dl, 0
	int 10h			;10h號中斷
	ret
BootMessage:			db "Hello, OS world!"
	times 510-($-$$)	db 0	;填充剩下的空間,使生成的二進制代碼恰好爲512字節
	dw 0xaa55		;結束標誌


org規定程序的起始地址

cs:代碼段寄存器,一般用於存放代碼;ds:數據段寄存器,一般用於存放數據;es:擴展段寄存器,使用時可以看作ds的擴展寄存器;(ss:棧段寄存器,一般作爲棧使用)。段寄存器是爲了對內存進行分段管理。

ax:通用寄存器。通用寄存器有AX,BX,CX,DX,BP,SP,SI,DI,均可做普通數據寄存器使用。此外,AX爲累加器,CX爲計數器,BX,BP爲基址寄存器,SI,DI爲變址寄存器,BP還可以是基指針,SP爲堆棧指針。

2-4行的作用就是令ds和es兩個段寄存器指向與cs相同的段,以便在以後進行數據操作的時候能定位到正確位置。段寄存器都指向了相同的段,但並不代表內容重疊,只是概念上的重疊。雖然放到一個段中,但是相互可以區分開。比如某一段既有數據也有代碼,則代碼在執行到數據之前,需要用戶在編程時加上一個跳轉指令以跳過此段中的代碼。段寄存器一共有四個,爲什麼只將ds、es、cs指向同一段,ss呢?棧段寄存器比較特殊,不僅用戶會用,系統也會自動使用,而且用戶也許在不知道系統使用的情況下使用ss,這樣就會導致衝突。避免這種衝突的方法是採用逆向的棧段。

在網上查了一下,X86有實模式和保護模式兩種模式(書上第三章講保護模式,學習這章時我再寫篇以這兩種模式爲內容的文章),現在“Hello, OS world!”是實模式。

第五行所call的就是顯示字符串的代碼了,8-15行都是。jmp $ 代表無限循環,其中$代表當前行被彙編之後的地址。

8-9行將BootMessage的首地址賦給bp(前面說過啦 是基址寄存器,還可以是基指針)。對於DispStr中爲什麼要給ax、bx、dl賦值,BL=0Ch代表黑底紅字等問題,參見此材料此博文,這些在我看來應該算是int 10h的規定吧

下面說一下16-18行。db代表後面的數據以字節存放(dw代表以字存放,dd代表以雙字存放)。 db即“分配一片連續的字節單元並初始化”。而times 510-($-$$)代表以0填充剩餘空間。這裏times可以理解爲循環(PS:times、db、dw均屬於僞指令——用於告訴彙編程序如何進行彙編的指令,它既不控制機器的操作也不被彙編成機器代碼,只能爲彙編程序所識別並指導彙編如何進行)。而$$表示一個節的開始處被彙編後的地址,這裏程序只有1個節,所以$$實際上表示程序被編譯後的開始地址。所以$-$$表示本行距離程序開始處的相對距離。times循環過後程序有510字節,爲什麼不是512字節呢?因爲還要給結束標誌0xaa55留兩個字節。而dw基本含義與db相同,不同的是dw定義16位數據,高8位數據字節先存入低地址字節中,而低8位數據字節則再存入高地址字節中。


書上還說明了關於[]的使用問題,如下:

在NASM中,任何不被方括號[]括起來的標籤或變量名都被認爲是地址,訪問標籤中的內容必須使用[]。所以mov ax, BootMessage會把“Hello, OS world!”這個字符串的首地址傳給寄存器ax。又比如,如果有foo dw 1,則mov ax, foo將把foo的地址傳給ax,而mov bx, [foo]將把bx的值賦爲1.實際上NASM中變量和標籤一樣,即foo dw 1等價於foo: dw 1,而且Offset這個關鍵字在NASM中也是不需要的,因爲不加方括號時表示的就是Offset。總結爲NASM的一大優點:要地址就不加方括號,也無需額外用什麼Offset,想要訪問地址中的內容就必須加上方括號,代碼規則非常鮮明,一目瞭然。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章