日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > windows >内容正文

windows

《一个操作系统的实现》——pmtest1.asm详解

發(fā)布時(shí)間:2025/6/15 windows 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《一个操作系统的实现》——pmtest1.asm详解 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

段機(jī)制輕松體驗(yàn)?
內(nèi)存尋址:?
實(shí)模式下的內(nèi)存尋址:?
讓我們首先來(lái)回顧實(shí)模式下的尋址方式?
段首地址×16+偏移量 = 物理地址?
為什么要×16?因?yàn)樵?086CPU中,地址線是20位,但寄存器是16位的,最高尋址64KB,它無(wú)法尋址到1M內(nèi)存。于是,Intel設(shè)計(jì)了這種尋址 方式,先縮小4位成16位放入到段寄存器,用到時(shí)候,再將其擴(kuò)大到20位,這也造成了段的首地址必須是16的倍數(shù)的限制。?
公式:xxxx:yyyy?
保護(hù)模式下分段機(jī)制的內(nèi)存尋址:?
分段機(jī)制是利用一個(gè)稱作段選擇符的偏移量,從而到描述符表找到需要的段描述符,而這個(gè)段描述符中就存放著真正的段的物理首地址,再加上偏移量?
一段話,出現(xiàn)了三個(gè)新名詞:?
段選擇子?
描述符表?
段描述符?
================================??
我們現(xiàn)在可以這樣來(lái)理解這段話:?
有一個(gè)結(jié)構(gòu)體類型,它有三個(gè)成員變量:?
段物理首地址?
段界限?
段屬性?
內(nèi)存中,維護(hù)一個(gè)該結(jié)構(gòu)體類型的數(shù)組。?
而分段機(jī)制就是利用一個(gè)索引,找到該數(shù)組對(duì)應(yīng)的結(jié)構(gòu)體,從而得到段的物理首地址,然后加上偏移量,得到真正的物理地址。?
公式:xxxx:yyyyyyyy?
其中,xxxx也就是索引,yyyyyyyy是偏移量(因?yàn)?2位寄存器,所以8個(gè)16進(jìn)制)xxxx存放在段寄存器中。?
================================?
現(xiàn)在,我們來(lái)到過(guò)來(lái)分析一下那三個(gè)新名詞:?
段描述符:一個(gè)結(jié)構(gòu)體,它有三個(gè)成員變量:?
段物理首地址?
段界限?
段屬性?
描述符表:也就是一個(gè)數(shù)組,什么樣的數(shù)組呢?是一個(gè)段描述符組成的數(shù)組。?
段選擇子:也就是數(shù)組的索引,但這時(shí)候的索引不在是高級(jí)語(yǔ)言中數(shù)組的下標(biāo),而是我們將要找的那個(gè)段描述符相對(duì)于數(shù)組首地址(也就是全局描述表的首地址)偏移位置。?
就這么簡(jiǎn)單,如圖:

圖中,通過(guò)Selector(段選擇子)找到存儲(chǔ)在Descriptor Table(描述符表)中某個(gè)Descriptor(段描述符),該段描述符中存放有該段的物理首地址,所以就可以找到內(nèi)存中真正的物理段首地址Segment?
Offset(偏移量):就是相對(duì)該段的偏移量?
物理首地址 + 偏移量 就得到了物理地址 本圖就是DATA?
但這時(shí),心細(xì)的朋友就發(fā)現(xiàn)了一個(gè)GDTR這個(gè)家伙還沒(méi)有提到!?
我們來(lái)看一下什么是GDTR?
Global Descriptor Table Register(全局描述符表寄存器)?
但是這個(gè)寄存器有什么用呢 ??
大家想一下,段描述符表現(xiàn)在是存放在內(nèi)存中,那CPU是如何知道它在哪里呢?所以,Iterl公司設(shè)計(jì)了一個(gè)全局描述符表寄存器,專門用來(lái)存放段描述符表的首地址,以便找到內(nèi)存中段描述符表。?
這時(shí),段描述符表地址被存到GDTR寄存器中了。?
=================================?
好了,分析就到這,我們來(lái)看一下正式的定義:?
當(dāng)x86 CPU 工作在保護(hù)模式時(shí),可以使用全部32根地址線訪問(wèn)4GB的內(nèi)存,因?yàn)?0386的所有通用寄存器都是32位的,所以用任何一個(gè)通用寄存器來(lái)間接尋址,不比分段就可以訪問(wèn)4G空間中任意的內(nèi)存地址。?
但 這并不意味著,此時(shí)段寄存器就不再有用了。實(shí)際上,段寄存器更加有用了,雖然再尋址上沒(méi)有分段的限制了,但在保護(hù)模式下,一個(gè)地址空間是否可以被寫入,可 以被多少優(yōu)先級(jí)的代碼寫入,是不是允許執(zhí)行等等涉及保護(hù)的問(wèn)題就出來(lái)了。要解決這些問(wèn)題,必須對(duì)一個(gè)地址空間定義一些安全上的屬性。段寄存器這時(shí)就派上了 用場(chǎng)。但是設(shè)計(jì)屬性和保護(hù)模式下段的參數(shù),要表示的信息太多了,要用64位長(zhǎng)的數(shù)據(jù)才能表示。我們把著64位的屬性數(shù)據(jù)叫做段描述符,上面說(shuō)過(guò),它包含3 個(gè)變量:?
段物理首地址、段界限、段屬性?
80386的段寄存器是16位(注意:通用寄存器在保護(hù)模式下都是32位,但段寄存器沒(méi)有被改 變)的,無(wú)法放下保護(hù)模式下64位的段描述符。如何解決這個(gè)問(wèn)題呢?方法是把所有段的段描述符順序存放在內(nèi)存中的指定位置,組成一個(gè)段描述符表 (Descriptor Table);而段寄存器中的16位用來(lái)做索引信息,這時(shí),段寄存器中的信息不再是段地址了,而是段選擇子(Selector)。可以通過(guò)它在段描述符表 中“選擇”一個(gè)項(xiàng)目已得到段的全部信息。?
那么段描述符表存放在哪里呢?80386引入了兩個(gè)新的寄存器來(lái)管理段描述符,就是GDTR和LDTR,(LDTR大家先忘記它,隨著學(xué)習(xí)的深入,我們會(huì)在以后學(xué)習(xí))。?
這樣,用以下幾步來(lái)總體體驗(yàn)下保護(hù)模式下尋址的機(jī)制?
1、段寄存器中存放段選擇子Selector?
2、GDTR中存放著段描述符表的首地址?
3、通過(guò)選擇子根據(jù)GDTR中的首地址,就能找到對(duì)應(yīng)的段描述符?
4、段描述符中有段的物理首地址,就得到段在內(nèi)存中的首地址?
5、加上偏移量,就找到在這個(gè)段中存放的數(shù)據(jù)的真正物理地址。?
好的,那我們開(kāi)始編碼,看看如何實(shí)現(xiàn)先前描述的內(nèi)容?
=================================?
首先,既然我們需要一個(gè)數(shù)組,全局描述符表,那我們就定義一塊連續(xù)的結(jié)構(gòu)體:?
[SECTION .gdt] ;為了代碼可讀性,我們將這個(gè)數(shù)組放到一個(gè)節(jié)中?
;由一塊連續(xù)的地址組成的,不就是一個(gè)數(shù)組嗎?看下面代碼,^_^?
段基地址 段界限 段屬性?
GDT_BEGIN: Descriptor 0,?? 0, 0?
GDT_CODE32: Descriptor 0, 0, DA_C?
;上面,我定義了二個(gè)連續(xù)地址的結(jié)構(gòu)體,大家先認(rèn)為Descriptor就是一個(gè)結(jié)構(gòu)體類型,我們會(huì)在以后詳細(xì)講述?
;第一個(gè)結(jié)構(gòu)體,全部是0,是為了遵循Interl規(guī)范,先記得就OK?
;第二個(gè)定義了一個(gè)代碼段,段基地址和段界限我們暫且還不知道,先初始化為0,但是因?yàn)槭莻€(gè)代碼段,代碼段具備執(zhí)行的屬性,那么DA_C就代表是一個(gè)可執(zhí)行代碼段,DA_C是一個(gè)預(yù)先定義好的常量,我們會(huì)在詳細(xì)講解段描述符中講解。?
=================================?
我們繼續(xù)來(lái)實(shí)現(xiàn),那么下面,我們就需要設(shè)計(jì)段選擇子了,因?yàn)樯厦娲a已經(jīng)包含了段描述符和全局描述符表?
還記得選擇子是個(gè)什么東西嗎 ??
段選擇子:?? 也就是數(shù)組的索引,但這時(shí)候的索引不在是高級(jí)語(yǔ)言中數(shù)組的下標(biāo),而是我們將要找的那個(gè)段描述符相對(duì)于數(shù)組首地址(也就是全局描述表的首地址)偏移位置。?
看我代碼怎么實(shí)現(xiàn),包含以上代碼不再說(shuō)明:?
[SECTION .gdt]?
GDT_BEGIN: Descriptor 0, 0, 0?
GDT_CODE32: Descriptor 0, 0, DA_C?
;下面是定義代碼段選擇子,它就是相對(duì)數(shù)組首地址的偏移量?
SelectorCode32 equ GDT_CODE32 - GDT_BEGIN?
;因?yàn)榈谝粋€(gè)段描述符,不被使用,所以就不比設(shè)置段選擇子了。?
=================================?
偏移地址:?
注意一點(diǎn)我們?cè)诔绦蛑惺褂玫亩际瞧频刂?#xff0c;相對(duì)于段的偏移地址,用上面的例子來(lái)說(shuō),象?GDT_CODE32 GDT_BEGIN 這些結(jié)構(gòu)體的首地址都是相對(duì)于數(shù)據(jù)段的偏移量。什么意思呢 ??
因?yàn)槲覀兊某绦虻降准虞d到內(nèi)存的哪個(gè)地方是不固定,不知道的,只需使用偏移地址操作就行了,如:?
SelectorCode32 ,它本身就是一個(gè)偏移地址?
但是SelectorCode32 equ GDT_CODE32 - GDT_BEGIN?
怎么解釋呢 ??
GDT_CODE32是相對(duì)于數(shù)據(jù)段的偏移量,?
GDT_BEGIN也是相對(duì)于數(shù)據(jù)段的偏移量,雖然它是數(shù)組的首地址,說(shuō)的羅索一些,GDT_BEGIN是數(shù)組的首地址(用數(shù)組的概念來(lái)理解頁(yè)不錯(cuò)哦可以看作數(shù)組下標(biāo)0),但是它是相對(duì)于數(shù)據(jù)段的偏移量?
那么兩個(gè)偏移量相減就是GDT_CODE32 相對(duì)于GDT_BEGIN的偏移量 (這個(gè)記住就行了,同時(shí)也是兩個(gè)偏移量的長(zhǎng)度)

舉個(gè)例子:0 1 2 3,一個(gè)偏移0表示占據(jù) 0這個(gè)地址,一個(gè)偏移3表示占據(jù)3這個(gè)地址(談偏移要把前面要偏移的那個(gè)參照物拿掉,去掉要偏移的參照物(其實(shí)就可以數(shù)學(xué)表示成減前一個(gè)偏移參照物),剩下的就是偏移量),3這個(gè)地址相對(duì)與0這個(gè)地址的偏移量是把0這個(gè)地址先起掉后再算。(再結(jié)合數(shù)組來(lái)理解就可以了)
所以,我們要時(shí)時(shí)刻刻記得在程序中,我們永遠(yuǎn)使用的是偏移量,因?yàn)槲覀儾恢莱绦驅(qū)⒁患虞d內(nèi)存那塊地方。?
好了,基礎(chǔ)也學(xué)的差不多了,下面我們要自己動(dòng)手寫一段程序,實(shí)現(xiàn)實(shí)模式到保護(hù)模式之間的跳轉(zhuǎn)?
=====================================================================?
;實(shí)現(xiàn)從實(shí)模式到保護(hù)模式之間的跳轉(zhuǎn)?
;參考:《自己動(dòng)手寫操作系統(tǒng)》?
----------------------------------------------------------------------?
%include "pm.inc"

org 0100h?
jmp LABEL_BEGIN?
[SECTION .gdt]?
GDT_BEGIN: Descriptor 0, 0,?? 0?
GDT_CODE32: Descriptor?0, LenOfCode32 - 1, DA_C + DA_32???//程序段描述符的基地址首先置位0,以后還要重置為32位程序段物理首地址
GDT_VIDEO: Descriptor 0B8000H, 0FFFFH,?? DA_DRW???????????????//這個(gè)32位程序段的物理首地址是在實(shí)模式下計(jì)算得到的。
GdtLen equ $ - GDT_BEGIN???????//長(zhǎng)度=偏移量1---偏移量2 。如偏移4-偏移2得到長(zhǎng)度為2?。? $表示當(dāng)前的偏移量
GdtPtr dw GdtLen - 1??????????????????//定義了一個(gè)Gdtptr的數(shù)據(jù)結(jié)構(gòu),低16位dw部分為位段界限,高32位為0,一共48位,高32位以后還要重置

dd?0?????????????????????????????????????????????//0,1為低16位,高32位是從2開(kāi)始,所以GdtPtr+2。高32位應(yīng)該放GDT的物理地址
;定義段選擇子?
SelectorCode32 equ GDT_CODE32 - GDT_BEGIN?
SelectorVideo equ GDT_VIDEO - GDT_BEGIN?
[SECTION .main]?
[BITS 16]?
LABEL_BEGIN:?
mov ax, cs?
mov ds, ax???????????????????????????? //這個(gè)ds es ss等于cs 表示代碼段和數(shù)據(jù)段在同一個(gè)街道上,只是偏移量不一樣。
mov es, ax?
mov ss, ax?????????????????????????? //段寄存器就相當(dāng)于街道號(hào),偏移量就相當(dāng)于門牌號(hào)。只有兩者組合起來(lái)才能形成真正的物理地址。

????????????????????????????????????????? //看到段寄存器就應(yīng)該想象成街道號(hào),看到偏移量就應(yīng)該想象成門牌號(hào)

????????????????????????????????????????? //如果代碼中只出現(xiàn)偏移量,實(shí)際上也是和操作系統(tǒng)所默認(rèn)的這個(gè)偏移量的段寄存器(只是代碼沒(méi)有顯式給出而已)一起組成物理地址,(如ip它默認(rèn)的段寄存 器就是cs),代碼也可以顯式給出段寄存器和偏移量,這個(gè)時(shí)候的段寄存器就不一定是這個(gè)偏移量所默認(rèn)的段寄存器。

1、初始化32位代碼段描述符的段基址
;我們可以在實(shí)模式下通過(guò)段寄存器×16 + 偏移兩 得到物理地址,?
;那么,我們就可以將這個(gè)物理地址放到段描述符中,以供保護(hù)模式下使用,?
;因?yàn)楸Wo(hù)模式下只能通過(guò)段選擇子 + 偏移量?
xor eax, eax ? //同或運(yùn)算,這里是將eax清零
mov ax,?cs? ? ?
shl eax, 4 ? ? ?//左移四位,相當(dāng)于乘以16,實(shí)模式下計(jì)算物理地址
add eax, LABEL_CODE32 ? ? ? ? ? ?//加上段相對(duì)代碼段的偏移地址,等于段的基地址,eax中為32位段的物理地址
mov word [GDT_CODE32 + 2],ax???? //?物理地址的ax放在段基址 2,3字節(jié)
shr eax, 16 ?//將eax向右移動(dòng)16位,低位被拋棄,高位變成了低位
mov byte [GDT_CODE32 + 4],al ? ??//低16位又可以分為al,和 ah,那么現(xiàn)在我們就將al放到4位置,ah放到7位置
mov byte [GDT_CODE32 + 7],ah?

2、得到段描述符表的物理地址,并將其放到GdtPtr中?
xor eax, eax?
mov ax,?ds???????????????????????????????????????//?GDT的段地址為數(shù)據(jù)寄存器DS,
shl eax, 4?
add eax, GDT_BEGIN?????????????????????//DS加上偏移量GDT_BEGIN就是GDT的物理地址?
mov dword [GdtPtr + 2],eax????????????//dword 表示是雙字所以為32位,eax也是32位啊。

;加載到gdtr,因?yàn)楝F(xiàn)在段描述符表在內(nèi)存中,我們必須要讓CPU知道段描述符 表在哪個(gè)位置?
;通過(guò)使用lgdtr就可以將源加載到gdtr寄存器中?
lgdt [GdtPtr]?

3、關(guān)中斷?
cli?

4、打開(kāi)A20線?
in al, 92h ?//從92h號(hào)端口讀入一個(gè)字節(jié)
or al, 00000010b ?
out 92h, al ?//向92h號(hào)端口寫入一個(gè)字節(jié)

5、準(zhǔn)備切換到保護(hù)模式,設(shè)置PE為1?
mov eax, cr0 ?//CR0也是一個(gè)寄存器,其中有個(gè)PE位,如果為0,就說(shuō)明為實(shí)模式,?
? ? ?//如果置1,說(shuō)明為保護(hù)模式。現(xiàn)在我們要進(jìn)入保護(hù)模式下工作,那么就要設(shè)置PE為1。?

or eax, 1?
mov cr0, eax?
;現(xiàn)在已經(jīng)處在保護(hù)模式分段機(jī)制下,所以尋址必須使用段選擇子:偏移量來(lái) 尋址?

6、跳轉(zhuǎn)到32位代碼段中?
;因?yàn)榇藭r(shí)偏移量位32位,所以必須dword告訴編譯器,不然,編譯器將編譯成16位?
jmp?dword?SelectorCode32:0 ? ? ? ?;跳轉(zhuǎn)到32位代碼段第一條指令開(kāi)始執(zhí)行

[SECTION .code32]?
[BITS 32]?
LABEL_CODE32:?
mov ax, SelectorVideo ? ? ? ??//視頻選擇子,用于找到顯存段的描述符
mov es, ax?
xor edi, edi?
mov edi, (80 * 10 + 10) ? //屏幕的第10行,第0列
mov ah, 0ch ? //0000:黑底 ? 1100:紅字
mov al, 'G' ???
mov [es:edi],ax?
jmp $?
LenOfCode32 equ $ - LABEL_CODE32?
===================================?


這段代碼的大概意思是:?
先 在16位代碼段,實(shí)模式下運(yùn)行,在實(shí)模式下,通過(guò)段寄存器×16+偏移量得到32位代碼的真正物理首地址,并將放入到段描述符表中,以供在保護(hù)模式下使 用,上面說(shuō)過(guò)了,保護(hù)模式下尋址,是通過(guò)段選擇子,段描述符表,段描述符一起工作尋址的。所以在實(shí)模式下所做的工作就是初始化段描述符表里的所有段描述 符。?
我們來(lái)看一下段描述符表,它有3個(gè)段描述符:?
GDT_BEGIN?
GDT_CODE32?
GDT_VIDEO?
GDT_BEGIN,遵循Intel公司規(guī)定,全部置0?
GDT_CODE32,32位代碼段描述符,供保護(hù)模式下使用?
GDT_VIDEO,顯存段首地址,我們知道,顯存首地址是0B8000H.?
回想一下,我們?cè)趯?shí)模式下往顯示器上輸出文字時(shí),我們?cè)O(shè)置段寄存器為?
0B800h,(注意后面比真正物理地址少一個(gè)0)。?
而我們現(xiàn)在在保護(hù)模式下訪問(wèn)顯存,那么0B8000h就可以直接放到段描述符中即可。因?yàn)槎蚊枋龇写娣诺氖嵌蔚恼嬲奈锢淼刂贰?
下面我們來(lái)逐行分析該代碼?

org 0100h?
這句話告訴加載器,將這段程序加載到偏移段首地址0100h處,即:偏移256字節(jié)處,為什么要加載到偏移256個(gè)字節(jié)處呢 ?

這是因?yàn)?#xff0c;在DOS中,需要留下256個(gè)字節(jié)和DOS系統(tǒng)進(jìn)行通信。?

jmp LABEL_BEGIN?
執(zhí)行這句話就跳轉(zhuǎn)到LABEL_BEGIN處開(kāi)始執(zhí)行。?
好,我們看一下LABEL_BEGIN在那塊,也就是16位代碼段?

[SECTION .main]?
[BITS 16]?
LABEL_BEGIN: (意味著運(yùn)行在實(shí)模式
這樣程序就從.main節(jié)的第一段代碼開(kāi)始執(zhí)行。?
我們看一下上面的代碼,[BITS 16]告訴編譯器,這是一個(gè)16位代碼段,所使用的寄存器都是16位寄存器。?
該代碼段初始化所有段描述符表中的段物理首地址?
首先在實(shí)模式下計(jì)算出32位代碼段的物理首地址?
對(duì)照 段值 × 16 + 偏移量 = 物理地址?
1 mov ax, cs ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
2 shl eax, 4 ?// CS存儲(chǔ)著有操作體統(tǒng)分配的代碼段值,段值*16 得到代碼段的物理首地址(注意這是在實(shí)模? 式下)
;到現(xiàn)在為止,eax就是代碼段的物理首地址了,那么。。。看?
3 add eax, LABEL_CODE32??????????//? 所有的諸如LABEL_CODE32:這樣的都表示是偏移量,因?yàn)槲锢硎椎刂返亩沃凳怯刹僮飨到y(tǒng)分配的。
;為eax (代碼段首地址)加上 LABEL_CODE32偏移量,得到的不就是LABEL_CODE32的真正物理地址了嗎 ?

上面說(shuō)過(guò),代碼中,使用的變量,或者標(biāo)簽 都是相對(duì)程序物理首地址的偏移量。如:LABEL_CODE32 這個(gè)標(biāo)簽就是相對(duì)程序物理首地址的偏移量。
OK現(xiàn)在我們已經(jīng)知道了32位代碼段的物理首地址,那么將eax放入到段描述符中就行了?

我們先假設(shè)Descriptor就是一個(gè)結(jié)構(gòu)體類型,(實(shí)際它是一個(gè)宏定義的數(shù)據(jù)結(jié)構(gòu),為了不影響整體思路,我們放到以后講)?
看一下這個(gè)Descriptor段描述符的內(nèi)存模型:?
; 高地址………………………………………………………………………低地址?
; |?? 7?? |?? 6?? |?? 5?? |?? 4?? |?? 3?? |?? 2?? |?? 1?? |?? 0?? |?
共 8 字節(jié)?
; |--------========--------========--------========--------========|?
; ┏━━━┳━━━━━━━┳━━━━━━━━━━━┳━━━━━━━┓?
; ┃31..24┃?? 段屬性?? ┃?? 段基址(23..0)?? ┃ 段界限(15..0)┃?
; ┃?? ┃?????? ┃?? |?????? ┃?????? ┃?
; ┃ 基址2┃?????? ┃基址1b│?? 基址1a?? ┃?? 段界限1 ┃?
; ┣━━━╋━━━┳━━━╋━━━━━━━━━━━╋━━━━━━━┫?
; ┃?? %6 ┃ %5 ┃ %4 ┃ %3 ┃?? %2?? ┃?? %1?? ┃?
; ┗━━━┻━━━┻━━━┻━━━┻━━━━━━━┻━━━━━━━┛?
由于歷史原因,段描述符的內(nèi)存排列不是按照 段基地址 段界限 段屬性 這樣的來(lái)排列的,所以我們現(xiàn)在要想一種辦法,把eax里所存放的物理首地址拆開(kāi),分別放到2,3,4,7字節(jié)處?
那么很顯然,我們可以將eax寄存器中的ax先放到2,3字節(jié)處?
mov word [GDT_CODE32 + 2],ax??// 這種內(nèi)存訪問(wèn)方式也是很常見(jiàn)的,首地址為數(shù)據(jù)寄存器提供(這里數(shù)據(jù)寄存器等于代碼寄存器),GDT_CODE32為偏移量,再加2
因?yàn)樵谄?個(gè)字節(jié)處,所以,首地址 + 2,才能定位到下標(biāo)為2的字節(jié)開(kāi)頭處?
而,word 告訴編譯器,我要一次訪問(wèn)2個(gè)字節(jié)的內(nèi)存?
好,簡(jiǎn)單的搞定了,那么再看,我們現(xiàn)在要將eax高16字節(jié)分別放到下標(biāo)為4,7字節(jié)處。?
雖然eax的ax代表低16位,但是Intel并沒(méi)有給高位一個(gè)名字定義,(不會(huì)是high ax,呵呵),所以,我們沒(méi)有辦法去訪問(wèn)高位。但是我們可以將高16位放到低16位中,因?yàn)檫@時(shí),低16位我們已經(jīng)不關(guān)心它的值了。?
好,看代碼?
shr eax, 16?
這句代碼就將eax向右移動(dòng)16位,低位被拋棄,高位變成了低位。呵呵。。。?
現(xiàn)在好辦了,低16位又可以分為al,和 ah,那么現(xiàn)在我們就將al放到4位置,ah放到7位置吧?
mov byte [GDT_CODE32 + 4], AL?
mov byte [GDT_CODE32 + 7], AH?
不用我再解釋這段代碼了,自己去分析為什么吧。。。。

//上面程序的功能是把32位程序段的物理首地址放到程序段描述符的段基址中,以便跳轉(zhuǎn)到保護(hù)模式時(shí),可以使用選擇子選用程序段描述符,從而得到32位程序段的物理首地址。

好了,32位代碼段描述符設(shè)置好了,其界限設(shè)置看代碼吧,為什么要那樣設(shè)置,很簡(jiǎn)單的,界限 = 長(zhǎng)度 - 1,段屬性:?
DA_C: 98h?? 可執(zhí)行?
DA_32: 4000h 32位代碼段?
是個(gè)常量,換算成二進(jìn)制位,對(duì)照段描述符屬性位置去看吧,參考任意一本保護(hù)模式書(shū)。?
段描述符設(shè)置好了,但是,這段描述符表,還在內(nèi)存中,我們必須想辦法放到寄存器中,這時(shí),就用到了gdtr(Golbal Descriptor Table Register),使用一條指令?
lgdtr [GdtPtr]?
就可以將GdtPtr加載到gdtr中?
而gdtr的內(nèi)存模型是:?
高字節(jié)?????????????? 低字節(jié)

但GdtPtr是什么呢 ??
就是我們定義的和這個(gè)寄存器內(nèi)存模型一摸一樣的結(jié)構(gòu)體:?
GdtLen equ $ - LABEL_BEGIN?
GdtPtr dw GdtLen - 1?? ;界限?
dd 0?? ;真正物理地址?
那現(xiàn)在我們就要計(jì)算GdtPtr第二個(gè)字節(jié) 也就是真正物理地址了?
xor eax, eax?
mov ax, ds?
shl eax, 4?
add eax, GDT_BEGIN?
mov?dword?[GdtPtr + 2],eax??????????????// dword表示為32位
自己分析吧,和計(jì)算32位段首地址基本一樣的,?
搞定后,使用lgdt [GdtPtr]就將此加載到寄存器GDTR中了?
然后關(guān)中斷?
cli 實(shí)模式下的中斷和保護(hù)模式下的中斷處理不一樣,那就關(guān)吧,規(guī)矩?
開(kāi)啟A20線?
in al, 92h?
or al, 00000010b?
out 92h, al?
如果不開(kāi)啟A20線,就無(wú)辦法訪問(wèn)1M之上的內(nèi)存,沒(méi)辦法,開(kāi)啟吧,規(guī)矩,想知道歷史了,去查吧?
然后設(shè)置CR0的PE位?
mov eax, cr0?
or eax, 1?
mov cr0, eax?
這個(gè)簡(jiǎn)單說(shuō)一下,以后再詳細(xì)?
CR0也是一個(gè)寄存器,其中有個(gè)PE位,如果為0,就說(shuō)明為實(shí)模式,?
如果置1,說(shuō)明為保護(hù)模式?,F(xiàn)在我們要進(jìn)入保護(hù)模式下工作,那么就要設(shè)置PE為1。?
好了,看一下這個(gè)main節(jié)中的最后一個(gè)代碼?
jmp dword SelectorCode32 : 0?
哈哈,現(xiàn)在已經(jīng)再保護(hù)模式下了,當(dāng)然要使用段選擇子 + 偏移量來(lái)尋址啊,這樣不就是尋址到了32位代碼段中去了嗎,偏移量為0不就說(shuō)明從第一個(gè)代碼開(kāi)始執(zhí)行。?
不是嗎 ?呵呵,那dword了??
因?yàn)楝F(xiàn)在的代碼段是16位,編譯器只能將它編譯位16位,但處于保護(hù)模式下,它的偏移量應(yīng)該是32位,所以,要顯示告訴編譯器,我這里使用的是32位,把我這塊給編譯成32位的!!!?
如果不加dword,?
jmp SelectorCode32:0?
這句話不會(huì)出什么問(wèn)題,16位的0是0,32位的0還是0,但如果這樣呢?:?
jmp SelectorCode32:0x12345678?
跳轉(zhuǎn)到偏移0x12345678中,這時(shí)就錯(cuò)了?
如果不將dword,編譯器就將該地址截?cái)喑?6位,取低位,變成了0x5678?
你說(shuō)對(duì)嗎 ?哈哈?
所以我們必須這樣做:?
jmp dword SelectorCodde32:0x12345678?
OKEY,我們繼續(xù)追擊,執(zhí)行完上面那個(gè)跳轉(zhuǎn)后,?
代碼就跳到了32位代碼段的中,開(kāi)始執(zhí)行第一條指令?
mov ax, SelectorVideo?
再看?
mov es,ax?????????????????????????//現(xiàn)在已經(jīng)在保護(hù)模式下,通過(guò)選擇子找到顯存的基址放

呵呵,實(shí)模式下,放的是16位的段值,而現(xiàn)在呢,不就是要將段選擇子放到段寄存器里嗎 ?然后通過(guò)段選擇子(偏移量)找到描述符表中對(duì)應(yīng)的段描述符的嗎 !!!!?
繼續(xù)看下面代碼?
xor edi, edi?
mov edi, (80 * 10 + 10)?
mov ah, 0ch?
mov al, 'G'?
跟實(shí)模式下差不多,設(shè)置目標(biāo)10行10列?
設(shè)置現(xiàn)實(shí)字符:G?
mov [es:edi],ax?
也和實(shí)模式下一樣,?
只不過(guò)實(shí)模式是這樣來(lái)尋址 :?
es×16 + edi?
而保護(hù)模式下呢?
es是一個(gè)偏移,根據(jù)這個(gè)偏移找到段描述符表中的對(duì)應(yīng)顯存段,然后這個(gè)顯存段里存放的就是0B8000h,然后在加上偏移 不就的了嗎!!!?
哈哈 。。。。程序分析完畢,細(xì)節(jié)之處,自己體會(huì)去?
總結(jié):?
1. 注意程序中使用的全部是偏移地址。注意兩種偏移地址?
A 對(duì)于程序的起始地址來(lái)說(shuō), 所有變量和標(biāo)簽都是相對(duì)于整個(gè)程序的偏移量?
B 對(duì)于段中定義的代碼,有兩種偏移:?
相對(duì)于程序起始地址的偏移?
相對(duì)于段標(biāo)簽的偏移。?
2.不管是實(shí)模式下的物理地址,還是保護(hù)模式下的物理地址,反正他們都是物理地址,呵呵,實(shí)模式下求的物理地址,也能在保護(hù)模式下使用,只是他們不同的是,如何尋址的方式不一樣。?
3.一個(gè)程序中可以包含多個(gè)不同位的段,32位或者16位,他們之間也可以互相跳轉(zhuǎn),只是32位段用的是32位寄存器,16位代碼段用的是16位寄存器,如果要在16位段下使用32位寄存器,必須象高級(jí)語(yǔ)言中強(qiáng)制類型轉(zhuǎn)換一樣,顯示的定義 dword?
參考: 《自動(dòng)動(dòng)手寫操作系統(tǒng)》?
《Undocument Windows 2000 Secrets》?
《Linux 內(nèi)核完全剖析》

總結(jié)

以上是生活随笔為你收集整理的《一个操作系统的实现》——pmtest1.asm详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。