本文深入剖析兩個經典的 x86 組語範例,詳細說明如何在 80386+ 處理器上實現實模式與保護模式的雙向切換,並展示保護模式下的分段記憶體管理、GDT/LDT 建立、特權級控制等核心概念。這些技術是作業系統核心開發、Bootloader 編寫及底層系統程式設計的基礎。
LGDT 指令載入偽描述符指向 GDT。CLI)、開啟 A20 位址線、設定 CR0.PE=1、執行段間跳轉(JMP FAR)刷新預取隊列並載入 CS 選擇子。REP MOVSD 將高端記憶體(110000H)資料搬移至常規記憶體緩衝區。STI)、恢復實模式段寄存器、顯示結果。範例在 .data 區定義 GDT 結構,運行時動態計算各段的 32 位線性基址(段值 × 16 + 偏移),填入描述符的 BaseL/BaseM/BaseH 欄位。所有段界限設為 0xFFFF(64KB),屬性為 16 位代碼/數據段(D/B=0)。
設定 CR0.PE=1 後,處理器進入保護模式,但 CS 仍存放實模式段值。必須立即執行一條 段間直接轉移指令(JMP FAR),將保護模式代碼段選擇子載入 CS,同時指定新 EIP。此指令在實模式被預取、在保護模式執行,完成真正的模式切換。
透過鍵盤控制器端口(92H)開啟/關閉 A20 Gate,允許存取 1MB 以上記憶體。此操作與處理器模式無關,純屬平台硬體控制。
返回實模式前,需載入一個符合實模式需求的描述符(如範例中的 Normal 描述符)到 DS/ES,以更新段描述符快取中的界限(0xFFFF)與屬性,確保實模式下段寄存器行為正確。
此範例展示更進階的場景:從實模式進入 32 位保護模式代碼段,再切換至 16 位保護模式代碼段,最後返回實模式。
D32 EQU 40h),預設操作數/位址大小為 32 位,使用 ESI/EDI/ECX 進行串操作。CALL/RET/PUSH/POP。JMP FAR 指令。在 16 位保護模式代碼段中,執行 MOV CR0, EAX (PE=0) 後,必須立即執行 JMP FAR 回實模式入口。同樣需預先載入符合實模式語意的描述符選擇子到 DS/ES/SS,確保段快取狀態正確。
| 項目 | 實模式 | 保護模式 |
|---|---|---|
| 位址形成 | 段值×16+偏移(20位) | 選擇子→描述符→基址+偏移(32位線性位址) |
| 分段保護 | 無 | 特權級(DPL/RPL/CPL)、界限檢查、類型檢查 |
| 代碼段預設 | 16位 | 由描述符 D 位決定(16/32位) |
| 中斷處理 | IVT (0:0) | IDT(中斷/陷阱/任務門) |
| 切換關鍵 | CR0.PE=0 | CR0.PE=1 + 載入有效 CS 選擇子 |
兩個範例均為教學簡化模型:
CLI 關中斷運行,假設無異常發生。掌握這兩個範例,即掌握了 x86 保護模式程式設計的骨架。進階開發需深入研究 IDT、LDT、任務狀態段(TSS)、呼叫門、中斷門、分頁機制及多任務切換。