转载:http://sleepycat.org/tech/os/protectmode
代码来自《自己动手写操作系统》一书. 在原有基础上加了些自己的注释.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
; ==========================================
; pmtest1.asm
; 编译方法:nasm pmtest1.asm -o pmtest1.bin
; ==========================================
%include "pm.inc" ; 常量, 宏, 以及一些说明
org 07c00h
jmp LABEL_BEGIN
[SECTION .gdt]
; GDT
; 段基址, 段界限, 属性
LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符
LABEL_DESC_CODE32: Descriptor 0, SegCode32Len - 1, DA_C + DA_32 ; 非一致代码段
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ; 显存首地址
GdtLen equ $ - LABEL_GDT ; GDT长度
;共6 byte, 结构与寄存器 gdtr 一致. 用于保存 GDT 表信息, 通过 lgdt 加载.
;后3-6 byte内容, 在下面的代码中会计算出并填入.
GdtPtr dw GdtLen - 1 ; GDT界限
dd 0 ; GDT基地址
; GDT 选择子
SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0100h
;此处计算出了 LABEL_SEG_CODE32 的真实物理地址,保存于 eax 中.
xor eax, eax
mov ax, cs
shl eax, 4 ;eax 中存储 cs 段地址, 并左移四位
add eax, LABEL_SEG_CODE32 ;段+偏移地址, 计算出其真实物理首地址
;此处将前面计算出的真实物理地址, 按规则放入GDT相应的段基址位置.
;注: 保护模式寻址步骤:
; 1). Selector 选择子 => 取得对应 GDT 的位置
; 2). GDT 的 段基址 => 取得 物理首地址
; 3). 物理首地址 + 偏移量 => 真实地址
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 ; eax 中存储 ds 段地址, 并左移四位
add eax, LABEL_GDT ; 段+偏移地址, 计算出真实物理首地址(即GDT开始处地址)
mov dword [GdtPtr + 2], eax ; 将此物理首地址放入 GdtPtr(共6 byte) 的3-6 byte 处.
; 加载到寄存器 gdtr.
lgdt [GdtPtr]
; 关中断
cli
; 打开地址线A20
in al, 92h
or al, 00000010b
out 92h, al
; 将寄存器 cr0 的第 0 位置设置为1, 表示 CPU 要运行于保护模式
mov eax, cr0
or eax, 1
mov cr0, eax
; 进入保护模式
jmp dword SelectorCode32:0 ; 执行这一句会把 SelectorCode32 装入 cs,
; 并跳转到 Code32Selector:0 处
; END of [SECTION .s16]
[SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS 32]
LABEL_SEG_CODE32:
mov ax, SelectorVideo
mov gs, ax ; 视频段选择子(目的)
mov edi, (80 * 11 + 79) * 2 ; 屏幕第 11 行, 第 79 列。
mov ah, 0Ch ; 0000: 黑底 1100: 红字
mov al, 'P'
mov [gs:edi], ax
; 到此停止
jmp $
SegCode32Len equ $ - LABEL_SEG_CODE32
; END of [SECTION .s32]
|
selector 是对应的 GDT 相对于 GDT 起始位置的偏移量. GDT 的长度都为 8 byte, 故 selector 的值为 8 的倍数. 也因此, 其最低 3 位正常情况下应该全为零, 但实际上, 最后 3 位被用作标志符, 标志 GDT 或 LDT 等, 故在使用 LDT 时, 需要直接修改对应的值。
[LABEL_DESC_CODE32 + 4] 表示 LABEL_DESC_CODE32 所在的地址, 加上 4 个内存单位(byte) mov byte [LABEL_DESC_CODE32 + 4], al 表示将 al 内容, 放置到 [LABEL_DESC_CODE32 + 4] 所在地址, 放置内容长度为 1 byte.
SelectorCode32 => 选择子, 16 位, 被装入 cs. dword => 表示后面的偏移量为 32 位. 按保护模式方式寻址.
http://www.360doc.com/content/11/0409/22/6580811_108479867.shtml