1. 程式人生 > >19-再談8086CPU的分段機制

19-再談8086CPU的分段機制

前面已經簡單的討論過8086CPU的分段機制了,如果已經忘了的同學趕快回去複習一遍(傳送門:3-淺談8086CPU的記憶體分段機制),在這一篇我們將針對前面的知識進行補充和應用。

8086處理器的工作模式是邏輯上對記憶體分成各個段(程式設計師自己抽象),8086處理器在執行指令處理資料時,例如獲取下一條指令或者獲取某個資料資訊,肯定會涉及到定址,而在8086中一律按照“段地址 x 16 +偏移地址 ”的方式進行。

通常一個標準的8086程式要在作業系統上跑起來,這個程式包含程式碼段,資料段,附加段和棧段等。換句話說,在8086程式設計中,段與段之間的界限早在程式載入到記憶體之前就已經準備好了(你可以理解為這相當於一個程式的框架,因為你想要在作業系統上執行起來,就必須遵守作業系統的規則)。

有同學可能會問了,那為啥之前我們寫的程式沒有各種段呢?

這是因為我們之前編寫的彙編程式是基於沒有作業系統的環境下跑起來的,也就是說沒有作業系統的限制,你想怎麼寫都行,甚至你可以自己定義規則:在一個程式中定義兩個程式碼段,三個資料段等。

那8086CPU設計程式的時候如何分段呢?先來了解一下Nasm編譯器提供的關鍵字:section(一個偽指令)。但是Nasm編譯器並不關心段的用途,也不知道什麼是段,CPU也不知道。這些所謂分段都是計算機工程師抽象出來的,方便計算機工程師們編寫程式,用來區分程式中的不同內容,如果一個程式的程式碼段和資料段等都放在一起的話,那寫程式的時候就會非常糟糕,所以一切都是為了方便管理。你可以只有一個段,也可以分割成很多個段。但每個段的用途,你自己應該非常清楚,並且給每個段起個有意義的名字,這是必須的。

 

使用section偽指令進行分段,程式碼如下:

section data1 align=16
db 0x11

section data2 align=16
db 0x22

section data3 align=16
db 0x33

在上述程式碼中我們定義了三個段,data1、data2、data3分別是這三個段的名稱,並且每個段只有一個位元組的資料,即0x11、0x22、0x33。align=16是告訴Nasm編譯器,這個段的大小為16的倍數,如果不是16的倍數,自動填充0x00湊夠成16的倍數為止。

以段data1為例,data1之前沒有任何內容,那麼段data1的相對起始彙編地址是0,我們知道地址0是以16位元組對齊的,因此在段data1中的0x11資料的起始彙編地址就是0x00000。

段data2也是以16位元組對齊的,而0x00000的下一個能被16整除的數字只能是0x00010,所以編譯器會把0x00010作為段data2的相對起始彙編地址,並在兩個段之間填充15位元組的0x00。

段data3以此類推,我們可以得出它的起始彙編地址為0x00020。

上述程式碼被載入到主引導扇區的位置,即記憶體地址0x07C00,那麼:

data1的絕對起始彙編地址就是:0x07C00+0x00000

data2的絕對起始彙編地址就是:0x07C00+0x00010

data3的絕對起始彙編地址就是:0x07C00+0x00020

 

對於每個段都有一個彙編地址,它是相當於整個程式開頭(0)的(相對起始彙編地址),為了方便取得該段的彙編地址,Nasm編譯器提供了以下偽指令,可以用在你的程式中加section.段名稱.start

jmp near Code

section Data1 align=16
AA:
db 0x11

section Data2 align=16
BB:
db 0x22

section Data3 align=16
CC:
db 0x33

;計算每個段的相對起始彙編地址
Code:
mov ax,section.Data1.start
mov ax,section.Data2.start
mov ax,section.Data3.start

 

執行結果:

因為段data1之前有一個jmp指令也會以16位元組對齊,所以data1是從0x0010開始的。