(实模式+保护模式)模式切换的过程步骤(代码+文字解析)

【0】写在前面
文末的个人总结是干货,前面代码仅供参考的,且source code from orange’s implemention of a os.

; ==========================================
; pmtest2.asm
; 编译方法:nasm pmtest2.asm -o pmtest2.com
; ==========================================

%include    "pm.inc"    ; 常量,, 以及一些说明

 org    0100h
 jmp    LABEL_BEGIN

;全局描述符表定义
[SECTION .gdt]

; GDT
;                            段基址,        段界限 , 属性
    LABEL_GDT:         Descriptor    0,              0, 0         ; 空描述符
    LABEL_DESC_NORMAL: Descriptor    0,         0ffffh, DA_DRW    ; Normal 描述符
    LABEL_DESC_CODE32: Descriptor    0, SegCode32Len-1, DA_C+DA_32; 非一致代码段, 32
    LABEL_DESC_CODE16: Descriptor    0,         0ffffh, DA_C      ; 非一致代码段, 16
    LABEL_DESC_DATA:   Descriptor    0,      DataLen-1, DA_DRW    ; Data
    LABEL_DESC_STACK:  Descriptor    0,     TopOfStack, DA_DRWA+DA_32; Stack, 32 位
    LABEL_DESC_TEST:   Descriptor 0500000h,     0ffffh, DA_DRW
    LABEL_DESC_VIDEO:  Descriptor  0B8000h,     0ffffh, DA_DRW    ; 显存首地址
; GDT 结束

GdtLen   equ    $ - LABEL_GDT  ; GDT长度
GdtPtr   dw GdtLen - 1  ; GDT界限
  dd    0    ; GDT基地址

;GDT 选择子定义

SelectorNormal   equ    LABEL_DESC_NORMAL   - LABEL_GDT
SelectorCode32   equ    LABEL_DESC_CODE32   - LABEL_GDT
SelectorCode16   equ    LABEL_DESC_CODE16   - LABEL_GDT
SelectorData     equ    LABEL_DESC_DATA  - LABEL_GDT
SelectorStack    equ    LABEL_DESC_STACK    - LABEL_GDT
SelectorTest     equ    LABEL_DESC_TEST  - LABEL_GDT
SelectorVideo    equ    LABEL_DESC_VIDEO    - LABEL_GDT

; END of [SECTION .gdt]
;数据段定义
[SECTION .data1] ; 数据段

ALIGN   32
[BITS   32]
LABEL_DATA:
SPValueInRealMode   dw  0
; 字符串
PMMessage:   db "In Protect Mode now. ^-^", 0   ; 在保护模式中显示 ;Mine【后面的零作为字符串的结束标志,Line189、232的test会用到】
    OffsetPMMessage  equ    PMMessage - $$  ; Mine【$$ == LABEL_DATA 的基地址,相对于 LABEL_DATA 的偏移地址】
    StrTest:     db "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0   ;Mine【后面的零作为字符串的结束标志】
OffsetStrTest    equ    StrTest - $$
DataLen  equ    $ - LABEL_DATA

; END of [SECTION .data1]
;全局堆栈段
[SECTION .gs]

ALIGN   32
[BITS   32]
LABEL_STACK:
times 512 db 0
TopOfStack  equ $ - LABEL_STACK - 1    

; END of [SECTION .gs]
;16位代码段, CPU运行在实模式下,为什么只有在16位代码段下才能修改GDT中的值;
[SECTION .s16] ; Mine【为从实模式跳转到保护模式所做的准备工作】

[BITS   16]
LABEL_BEGIN:
 mov    ax, cs
 mov    ds, ax
 mov    es, ax
 mov    ss, ax
 mov    sp, 0100h

; 下一行指令的解说:
; 待会要跳回到实模式,这里是保存跳转前的相关 value
; mov [label_backto_real + 3], ax, 这是在代码已经加载到了内存之后再执行的,为什么加上3,一位jmp指令占用3个字节, 而jmp后面跟着跳转地址;
; 他的作用是在段基地址存储单元中去重新设置新值 去 覆盖原先的段基地址,作者真心很是聪明啊。

 mov    [LABEL_GO_BACK_TO_REAL+3], ax
 mov    [SPValueInRealMode], sp      ; Mine【保存sp,然后返回实模式后,再将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

;初始化数据段描述符

扫描二维码关注公众号,回复: 9714235 查看本文章
 xor    eax, eax
 mov    ax, ds
 shl    eax, 4
 add    eax, LABEL_DATA        ; Mine【数据段的基址便是 LABEL_DATA 的物理地址 
                                          ; 所以 OffsetStrTest 既是字符串相对于 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 作准备,填充GDT基地址的数据结构

 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

; 真正进入保护模式

 jmp dword SelectorCode32:0  ; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0

; 从保护模式跳回到实模式就到了这里(注意:从保护模式跳转到实模式,即本标识符下,本标识符是存在于 初始化描述符的16位代码段的末尾的)

LABEL_REAL_ENTRY: 
 mov    ax, cs
 mov    ds, ax
 mov    es, ax
 mov    ss, ax

 mov    sp, [SPValueInRealMode]

; 关闭 A20 地址线

 in al, 92h  ; 
 and    al, 11111101b 
 out    92h, al  ; /

 sti     ; 开中断

 mov    ax, 4c00h   ; `.
 int    21h  ; /  回到 DOS

; END of [SECTION .s16]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 32 位代码段. 由实模式跳入
[SECTION .s32]

[BITS   32]

LABEL_SEG_CODE32:
 mov    ax, SelectorData
 mov    ds, ax   ; 数据段选择子
 mov    ax, SelectorTest
 mov    es, ax   ; 测试段选择子
 mov    ax, SelectorVideo
 mov    gs, ax   ; 视频段选择子

 mov    ax, SelectorStack        ; Mine【改变了ss和esp, 则在32位代码段中所有的堆栈操作将会在新增的堆栈段中进行】
 mov    ss, ax   ; 堆栈段选择子

 mov    esp, TopOfStack

待续…

转载自:https://blog.csdn.net/PacosonSWJTU/article/details/48009165

发布了23 篇原创文章 · 获赞 13 · 访问量 3005

猜你喜欢

转载自blog.csdn.net/qq_29856169/article/details/104046131
今日推荐