《自己動手寫作業系統》讀後感 http://blog.csdn.net/zgh1988/article/details/7059936
全面剖析《自己動手寫作業系統》第一章 http://blog.csdn.net/zgh1988/article/details/7060032
全面剖析《自己動手寫作業系統》第二章 http://blog.csdn.net/zgh1988/article/details/7062065
全面剖析《自己動手寫作業系統》第三章1 http://blog.csdn.net/zgh1988/article/details/7098981
期末考試,寒假過春節,8號回到學校,繼續學習。今天讀到了一句話:我們要做 象棋 中的兵卒,雖然行動緩慢,力量弱小,但是誰見過他後退一步?嗯,堅持一步步的前進,踏踏實實的學習編程技能!
在這裡,我還要繼續跟大家分享對保護模式的認識。我以pmtest2.asm為例,來主要講述“實模式———保護模式———實模式”的轉換過程。
1、“實模式——保護模式——實模式”的轉換過程。
2、結合執行個體pmtest2.asm來瞭解“實模式——保護模式——實模式”的轉換過程”。
3、介紹段描述符屬性
4、pm.inc 中的宏定義
下面我們開始詳細敘述上面4個問題。
一、“實模式——保護模式——實模式”的轉換過程1、實模式——保護模式
1)在實模式下,要有自己的堆棧段,所以在進入保護模式之前要暫存ss和sp的值至某記憶體處,以便從保護模式返回實模式後恢複到原先的堆棧。
2)初始化段描述符
3)為載入GDTR做準備
4)載入GDTR
5)關中斷
6)開啟地址線A20
7)置cr0寄存器的末位為1
8)實現跳轉,進入到保護模式
2、保護模式——實模式
1)需要注意事項:
返回實模式前,需要把各個段寄存器設定為符合實模式下的規範段,其中SS也要設定,段界限為0ffffh;
不能從32位程式碼片段返回實模式,只能從16位程式碼片段中返回,因為無法實現從32位程式碼片段返回時,cs高速緩衝寄存器中的屬性符合實模式下的要求
基於以上兩方面的原因為: 80286開始為每個段寄存器增加了段描述符高速緩衝寄存器,而這些緩衝寄存器對於程式員是不可見的,且在實模式下是不能修改的,要想改變這些高速緩衝寄存器的值必須通過在保護模式下修改相應的段寄存器的選擇來來實現,這也是為什麼在返回實模式之前必須把DS、ES、FS、GS、SS設定為規範段的原因。但是CS是一個特殊的段,不能通過常規方式修改,只能通過段間跳轉修改,而一旦跳到實模式後又不能修改了,所以就要求在返回實模式的段的段描述符必須符合實模式下的要求。實模式下的段長度是0FFFFH,也就是1M,如果用於返回實模式的段的段界限不是0FFFFH,會導致返回實模式後實模式下的CS段的高速緩衝寄存器的段長度還是保護模式時的段界限值,這是不正確的,所以要求用於返回實模式的段的段界限必須是0FFFFH。
2)從保護模式下的32位程式碼片段跳轉到16位程式碼片段
3)在16位程式碼片段下初始化所有段寄存器
4)置cr0的末位為0
5)實現跳轉,返回到實模式
二、結合執行個體pmtest2.asm來瞭解“實模式——保護模式——實模式”的轉換過程”
;=====================================================;pmtest2.asm;編譯方法:nasm pmtest2.asm -o pmtest2.com%include "pm.inc";常量,宏,以及一些說明org 0100h jmp LABEL_BEGIN[SECTION .gdt];GDT; 段基址 段界限 段屬性LABEL_GDT:Descriptor0,0,0;空描述符LABEL_DESC_NORMAL:Descriptor0,0ffffh,DA_DRW;Normal描述符LABEL_DESC_CODE32:Descriptor0,SegCode32Len-1,DA_C + DA_32;非一致程式碼片段,32LABEL_DESC_CODE16:Descriptor0,0ffffh,DA_C;非一致程式碼片段,16LABEL_DESC_DATA:Descriptor0,DataLen-1,DA_DRW + DA_DPL1;DataLABEL_DESC_STACK:Descriptor0,TopOfStack,DA_DRWA + DA_32;Stack,32位LABEL_DESC_TEST:Descriptor 0500000h,0ffffh,DA_DRW;測試程式碼片段LABEL_DESC_VIDEO:Descriptor 0B8000h,0ffffh,DA_DRW;顯存首地址;GDT 結束GdtLenequ$ - LABEL_GDT;GDT長度GdtPtrdwGdtLen - 1;GDT界限dd0;GDT基地址;GDT選擇子SelectorNormalequLABEL_DESC_NORMAL - LABEL_GDTSelectorCode32equLABEL_DESC_CODE32 - LABEL_GDTSelectorCode16equLABEL_DESC_CODE16 - LABEL_GDTSelectorDataequLABEL_DESC_DATA - LABEL_GDT + SA_RPL3SelectorStackequLABEL_DESC_STACK - LABEL_GDTSelectorTestequLABEL_DESC_TEST - LABEL_GDTSelectorVideoequLABEL_DESC_VIDEO - LABEL_GDT;END of [SECTION .gdt][SECTION .data1];資料區段ALIGN 32[BITS 32]LABEL_DATA:SPValueInRealModedw0;字串PMMessage:db"In Protect Mode now.",0;進入保護模式後顯示該字串OffsetPMMessageequPMMessage - $StrTest:db"ABCDEFGHIJKLMNOPQRSTUVWXYZ",0OffsetStrTestequStrTest - $DataLenequ$ - LABEL_DATA;END of [SECTION .data1];全域堆棧段[SECTION .gs]ALIGN 32[BITS 32]LABEL_STACK:times 512 db 0TopOfStack equ$ - LABEL_STACK - 1;END of [SECTION .gs][SECTION .s16][BITS 16]LABEL_BEGIN: mov ax, cs mov ds, ax mov es, ax mov ss, ax mov sp, 0100h mov [LABEL_GO_BACK_TO_REAL + 3], ax mov [SPValueInRealMode], sp ;初始化16位程式碼片段描述符 mov ax, cs movzx eax, ax shl eax, 4 add eax, LABEL_SEG_CODE16 mov word[LABEL_DESC_CODE16+2], ax shr eax, 16 mov byte[LABEL_DESC_CODE16+4], al mov byte[LABEL_DESC_CODE16+7], ah ;初始化32位程式碼片段描述符 xor eax, eax mov ax, cs shl eax, 4 add eax, LABEL_SEG_CODE32 mov word[LABEL_DESC_CODE32+2], ax shr eax, 16 mov byte[LABEL_DESC_CODE32+4], al mov byte[LABEL_DESC_CODE32+7], ah ;初始化資料區段描述符 xor eax, eax mov ax, ds shl eax, 4 add eax, LABEL_DATA mov word[LABEL_DESC_DATA+2], ax shr eax, 16 mov byte[LABEL_DESC_DATA+4], al mov byte[LABEL_DESC_DATA+7], ah ;初始化堆棧段描述符 xor eax, eax mov ax, ds shl eax, 4 add eax, LABEL_STACK mov word[LABEL_DESC_STACK+2], ax shr eax, 16 mov byte[LABEL_DESC_STACK+4], al mov byte[LABEL_DESC_STACK+7], ah ;為載入GDTR作準備 xor eax, eax mov ax, ds shl eax, 4 add eax, LABEL_GDT; eax <- gdt mov dword[GdtPtr+2], eax; [GdtPtr+2] <- gdt 基地址 ;載入GDTR lgdt [GdtPtr] ;關中斷 cli ;開啟地址線A20 in al, 92h or al, 00000010b out 92h, al ;清屏操作(用以指定色彩) mov ah, 06H ;功能06H和07H mov ch, 00 ;功能描述:初始化螢幕或滾屏 mov cl, 00 ;入口參數:AH=06H——向上滾屏,07H——向下滾屏 mov dh, 24 ;AL=滾動行數(0——清視窗) mov dl, 79 ;BH=空白地區的預設屬性 mov bh, 7 ;(CH、CL)=視窗的左上方位置(Y座標,X座標) mov al, 00 ;(DH、DL)=視窗的右下角位置(Y座標,X座標) int 10H ;出口參數:無 ;準備切換到保護模式 mov eax, cr0 or eax, 1 mov cr0, eax ;真正進入保護模式 jmp dword SelectorCode32:0;執行這一句會把SelectorCode32裝入cs,並跳轉到SelectorCode32:0處;......................................................................;,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,LABEL_REAL_ENTRY:;從保護模式跳回到實模式就到了這裡mov ax, csmov ds, axmov es, axmov ss, axmov sp, [SPValueInRealMode]in al, 92h;關閉A20地址線and al, 11111101bout 92h, alsti;開中斷;回到DOSmov ax, 4C00hint 21h;;END of [SECTION .s16][SECTION .s32];32位程式碼片段,由實模式跳入[BITS 32]LABEL_SEG_CODE32:mov ax, SelectorDatamov ds, ax;資料區段選擇子mov ax, SelectorTestmov es, ax;測試段選擇子mov ax, SelectorVideomov gs, ax;視頻段選擇子mov ax, SelectorStackmov ss, ax;堆棧段選擇子mov esp, TopOfStack;下面顯示一個字串mov ah, 0Ch;0000:黑底 1100:紅字xor esi, esixor edi, edimov esi, OffsetPMMessage;來源資料位移mov edi, (80*10+0)*2;目的資料位移。螢幕第10行,第0列cld.1:lodsbtest al, aljz .2mov [gs:edi], ax;將ax中的資料送入到顯存add edi, 2jmp .1.2:;顯示完畢call DispReturncall TestReadcall TestWriteCall TestRead;到此停止jmp SelectorCode16:0;---------------------------------------------------------------------------TestRead:xor esi, esimov ecx, 8.loop:mov al, [es:esi]call DispALinc esiloop .loopcall DispReturnret;TestRead結束--------------------------------------------------------------;--------------------------------------------------------------------------TestWrite:push esipush edixor esi, esixor edi, edimov esi, OffsetStrTest;來源資料位移.1:lodsbtest al, aljz .2mov [es:edi], alinc edijmp .1.2:pop edipop esiret;TestWrite結束--------------------------------------------------------------;---------------------------------------------------------------------------;顯示AL中的數字;預設地:;數字已經存在AL中;edi始終指向要顯示的下一個字元的位置;被改變的寄存器;ax, edi;---------------------------------------------------------------------------DispAL:push ecxpush edxmov ah, 0Chmov dl, alshr al, 4mov ecx, 2.begin:and al, 01111bcmp al, 9ja .1add al, '0'jmp .2.1:sub al, 0Ahadd al, 'A'.2:mov [gs:edi], axadd edi, 2mov al, dlloop .beginadd edi, 2pop edxpop ecxret;DispAL結束-----------------------------------------------------------------;---------------------------------------------------------------------------DispReturn:push eaxpush ebxmov eax, edimov bl, 160div bland eax, 0FFhinc eaxmov bl, 160mul blmov edi, eaxpop ebxpop eaxret;DispReturn結束--------------------------------------------------------------SegCode32Lenequ$ - LABEL_SEG_CODE32; END of [SECTION .s32];16位程式碼片段,由32位程式碼片段跳入,跳出後到實模式[SECTION .s16code]ALIGN 32[BITS 16]LABEL_SEG_CODE16:;跳回實模式:mov ax, SelectorNormalmov ds, axmov es, axmov fs, axmov gs, axmov ss, axmov eax, cr0and al, 11111110bmov cr0, eaxLABEL_GO_BACK_TO_REAL:jmp 0:LABEL_REAL_ENTRY;段地址會在程式開始處被設定成正確的值Code16Lenequ$-LABEL_SEG_CODE16;END of [SECTION .s16code]
下面是“實模式——保護模式——實模式”的跳轉過程圖
LABEL_BEGIN、LABEL_REAL_ENTRY運行於實模式下,LEBEL_SEG_CODE32、LABEL_SEG_CODE16運行於保護模式下。所以該程式實現了“實模式——保護模式——實模式”的跳轉。
1、實模式——保護模式
這個跳轉過程主要存在於LABEL_BEGIN程式碼片段中:
70——71:儲存實模式下的SP內的值
73——111:初始化段描述符
113——118:為載入GDTR做準備
120——121:載入GDTR
123——124:關中斷
126——129:開啟地址線A20
141——144:準備切換到保護模式,置cr0的末位為1
146——147:跳轉到保護模式
2、保護模式——實模式
208——209:從保護模式下的32位程式碼片段跳轉到16位程式碼片段
316——322:為跳回實模式做準備,將段寄存器初始化為符合實模式下的程式碼片段規範。即用SelectorNormal來初始化段寄存器。
324——326:置cr0的末位為0
328——329:實現跳轉,進入到實模式下的 LABEL_REAL_ENTRY段
158——158:恢複實模式下的SP
160——162:關閉A20地址線
164——164:開中斷
166——168:返回DOS
三、段描述符的屬性
段描述符屬性佔5、6位元組,其具體特性如下:
在這裡主要介紹第5位元組的內容。
(1) P:存在(Present)位。
1 表示段在記憶體中存在
0 表示段在記憶體中不存在
(2) DPL:表示描述符特權級(Descriptor Privilege level),共2位。
它規定了所描述段的特權級,用於特權檢查,以決定對該段能否訪問。
(3) S:說明描述符的類型。
1 資料區段和程式碼片段描述符
0系統段描述符和門描述符
(4) TYPE:說明儲存段描述符所描述的儲存段的具體屬性。
資料區段類型 類型值 說明
----------------------------------
0 唯讀
1 唯讀、已訪問
2 讀/寫
3 讀/寫、已訪問
4 唯讀、向下擴充
5 唯讀、向下擴充、已訪問
6 讀/寫、向下擴充
7 讀/寫、向下擴充、已訪問
程式碼片段類型 類型值 說明
----------------------------------
8 只執行
9 只執行、已訪問
A 執行/讀
B 執行/讀、已訪問
C 只執行、一致碼段
D 只執行、一致碼段、已訪問
E 執行/讀、一致碼段
F 執行/讀、一致碼段、已訪問
系統段類型 類型編碼 說明
----------------------------------
0 <未定義>
1 可用286TSS
2 LDT
3 忙的286TSS
4 286調用門
5 任務門
6 286中斷門
7 286陷阱門
8 未定義
9 可用386TSS
A <未定義>
B 忙的386TSS
C 386調用門
D <未定義>
E 386中斷門
F 386陷阱門
四、pm.inc中的宏定義
在程式中,我們定義屬性時,使用了pm.inc中的宏定義DA_DRW、DA_C、DA_32、DA_DRW、DA_DRWA。下面讓我來向大家解釋這些宏定義。
DA : Descriptor Attribute
D : 資料區段
C : 程式碼片段
S : 系統段
R : 唯讀
RW : 讀寫
A : 已訪問
----------------------------------------------------------------------------
DA_32 EQU 4000h ; 32 位段
DA_DPL0 EQU 00h ; DPL = 0
DA_DPL1 EQU 20h ; DPL = 1
DA_DPL2 EQU 40h ; DPL = 2
DA_DPL3 EQU 60h ; DPL = 3
----------------------------------------------------------------------------
儲存段描述符類型值說明
----------------------------------------------------------------------------
DA_DR EQU 90h ; 存在的唯讀資料區段類型值
DA_DRW EQU 92h ; 存在的可讀寫資料區段屬性值
DA_DRWA EQU 93h ; 存在的已訪問可讀寫資料區段類型值
DA_C EQU 98h ; 存在的只執行程式碼片段屬性值
DA_CR EQU 9Ah ; 存在的可執行可讀程式碼片段屬性值
DA_CCO EQU 9Ch ; 存在的只執行一致程式碼片段屬性值
DA_CCOR EQU 9Eh ; 存在的可執行可讀一致程式碼片段屬性值
----------------------------------------------------------------------------
系統段描述符類型值說明
----------------------------------------------------------------------------
DA_LDT EQU 82h ; 局部描述符表段類型值
DA_TaskGate EQU 85h ; 任務門類型值
DA_386TSS EQU 89h ; 可用 386 任務狀態段類型值
DA_386CGate EQU 8Ch ; 386 調用門類型值
DA_386IGate EQU 8Eh ; 386 中斷門類型值
DA_386TGate EQU 8Fh ; 386 陷阱門類型值