問題1、怎麼理解界限 = 大小 - 1?
數組
界限就是邊界、分界線,就是終止的地方,內存的界限就是一段內存的終址相對於基址的偏移號(不是偏移量,偏移號 = 偏移量 -1)。數據結構
爲何要減一?由於內存地址編號從 0 開始,第一個內存地址對應內存地址編號 0 ,第 n 個內存地址對應內存地址編號 n - 1。內存地址序號的上限減一對應內存地址編號的上限,而內存地址的最大序號就是這段內存的大小,即一段內存的大小 - 1 = 這段內存的界限。ide
問題二:描述符是什麼?學習
不要被「符」字迷惑了,說白了「符」就是個數據結構(在 C語言裏就是個 struct)。「描述」兩個字從字面理解就行,就是對一個東西進行詳細說明。咱們這裏學習的保護模式,描述的就是內存的分段管理模式。那麼「描述符」就是說明一段內存的詳細狀況的數據結構。實際上,描述符就是定義一段內存的起始地址、界限、訪問屬性的一個數據結構,描述符表就是一個由描述符爲元素組成的數組,看起來像一個表格而已。spa
問題三:GDT 表第一個爲何要留空呢?code
由於選擇子的結構是這樣的:orm
; ┏━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┓
索引
; ┃15┃14┃13┃12┃11┃10┃09┃08┃07┃06┃05┃04┃03┃02┃01┃0 ┃ip
; ┣━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━╋━━╋━━┻━━┫內存
; ┃ 描述符索引 ┃TI ┃ RPL ┃
; ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━┻━━━━━┛
選擇子中正真索引 GDT 表的索引號只在高 13 位,低 3 位要留給 TI 和 RPL,即索引號實際上從第 3 位開始,就是0b0000000000001000 = 8;索引號每次增長的值(步進)也不是 1 ,而是 0b1000 = 8 。
恰好 GDT 表中,一個描述符也是佔用 8 個字節。
如今簡單了:把第一個描述符留空不用,只是佔據 8 個字節的空間,在 TI 和 RPL 都等於 0 的狀況下,選擇子就能夠簡單等同於描述符在 GDT 表中的偏移了!即:
第一個選擇子 = 一號描述符在 GDT 表中的偏移 = 8
第 n 個選擇子 = n 號描述符在 GDT 表中的偏移 = n * 8
打開地址線 A20、打開保護模式標誌都是固定格式,照抄就行。lgdt 和 cli 是 CPU 指令,看看其指令說明就行。再來讀下面的代碼,就沒啥難度了。
; TestPM.nas ; 初識保護模式 ; 四彩 ; 2015-11-15 ; ======================================================================================== ; ---------------------------------------------------------------------------------------- org 0x7C00 jmp Label_RM_main ; **************************************************************************************** ; ======================================================================================== %include "./INC/Descriptor.inc" ; ; ---------------------------------------------------------------------------------------- ; GDT 段基址 段界限 屬性 Label_Desc_Empty : Descriptor 0, 0, 0 ; 空描述符 Label_Desc_PM : Descriptor 0, 0xFFFF, DA_32 + DA_CS_E ; 保護模式代碼段描述符 Label_Desc_Video : Descriptor 0xB8000, 0xFFFF, DA_DS_RW ; 顯存段描述符 ; ; ---------------------------------------------------------------------------------------- ; GDTPtr GDTLen equ $ - Label_Desc_Empty GDTPtr dw GDTLen - 1 ; 界限 dd 0 ; 基址 ; ; ---------------------------------------------------------------------------------------- ; 選擇子 SelectorPM equ Label_Desc_PM - Label_Desc_Empty SelectorVideo equ Label_Desc_Video - Label_Desc_Empty ; ; **************************************************************************************** [BITS 16] ; ======================================================================================== ; 實模式下開啓保護模式 ; ---------------------------------------------------------------------------------------- ; 程序入口,實模式代碼段 Label_RM_main: ; 填上保護模式代碼段描述符的基址(界限、屬性在定義時已初始化) xor eax, eax mov ax, cs shl eax, 4 add eax, Label_PM_main ; 保護模式程序入口的絕對內存地址,即爲其基址 mov word[Label_Desc_PM + 2], ax ; 拆分紅 3 部分存入相應位置 shr eax, 16 mov byte[Label_Desc_PM + 4], al mov byte[Label_Desc_PM + 7], ah ; 填上 GDTPtr 的基址(界限在定義時已初始化) xor eax, eax mov ax, cs shl eax, 4 add eax, Label_Desc_Empty mov dword[GDTPtr + 2], eax ; 加載 GDT lgdt [GDTPtr] ; 屏蔽中斷 cli ; 打開地址線 A20 in al, 0x92 or al, 0b10 out 0x92, al ; 打開保護模式標誌 mov eax, cr0 or eax, 1 mov cr0, eax ; 修改 CS : EIP jmp dword SelectorPM : 0 ; **************************************************************************************** [BITS 32] ; ======================================================================================== ; 保護模式代碼段,由實模式跳入 Label_PM_main: mov ax, SelectorVideo mov gs, ax mov edi, (80 * 5 + 35) * 2 mov ah, 0xC mov al, 'O' mov [gs : edi], ax mov al, 'K' mov [gs : edi + 2], ax jmp $ ; **************************************************************************************** ; ======================================================================================== ; FAT12 文件系統引導扇區引導代碼的剩餘部分用 0 填滿 times 510 - ($ - $$) db 0 ; **************************************************************************************** ; ======================================================================================== ; FAT12 文件系統引導扇區的的結束標誌(最後 2 字節,必須是 0xAA55) dw 0xAA55 ; **************************************************************************************** ; ======================================================================================== CodeLenOfPM equ $ - Label_PM_main ; ****************************************************************************************