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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
|
;============================================================================
;保护模式下32位段和16位段的转换
;编译方法参加makefile, MASM9 + link5.6 TAB=8
;============================================================================
.686p
Include pm.inc ;重要头文件定义
option casemap:none
;============================================================================
;16位数据段, 存储了一些非常重要的数据
;============================================================================
DataSeg Segment use16
GDT label byte ;全局描述符表
Dummy: Descriptor 0, 0, 0 ;空的描述符
;----------------------------------------------------------------------------
; ;段基址 ;段界限 ;属性
Normal: Descriptor 0, 0ffffh, DA_DRW ;规范选择字
Code16Desc: Descriptor 0, 0ffffh, DA_C ;非一致代码段16位
Code32Desc: Descriptor 0, Code32Len, DA_C or DA_32;非一致代码段32位
VideoDesc: Descriptor 0b8000h, 0ffffh, DA_DRW ;显存段(可读写)
DataSDesc: Descriptor 100000h, 16, DA_DR ;只读数据段
DataMessDesc: Descriptor 0, sizeof SzMessage, DA_DRW ;保护模式下使用的数据段
StackDesc: Descriptor 0, 256, DA_DRW ;堆栈段(可读写)
;----------------------------------------------------------------------------
GDT_Len equ $ - GDT ;GDT长度
GDT_Ptr word GDT_Len-1 ;VGDT
dword 0
_RegSs word 0 ;用于保存旧的SS:SP
_RegSp word 0
;----------------------------------------------------------------------------
NormalSelector equ Normal - GDT ;规范段描述符选择子
Code16Selector equ Code16Desc - GDT ;16位代码段选择子
Code32Selector equ Code32Desc - GDT ;32位代码段选种子
DataSrcSelector equ DataSDesc- GDT ;源数据段选择子
VideoSelector equ VideoDesc - GDT ;视频段选择子
DataMessSelector equ DataMessDesc - GDT ;显示消息段的选择子
StackSelector equ StackDesc - GDT ;堆栈段选择子
DataSeg Ends
DataMess Segment use16
SzMessage byte "I entered a protected mode I'm Joen", 0
DataMess Ends
;============================================================================
;保护模式下使用的堆栈段
;============================================================================
StackSeg Segment para Stack use16 ;堆栈段
byte 256 dup( 0 )
StackSeg Ends
;============================================================================
;16位代码段, 由保护模式跳入, 然后返回到实模式
;============================================================================
Code16Seg Segment use16
_RetReal Proc
;----------------------------------------------------------------------------
;在屏幕上显示一个字符串
mov ax, DataMessSelector
mov ds, ax ;ds-->消息段
mov si, offset SzMessage
mov ax, VideoSelector
mov es, ax ;es-->视频段
mov di, 80*2*11+10*2 ;15行10列
mov cx, sizeof SzMessage
@@: lodsb
mov ah, 07h ;属性
stosw
loop @b
;----------------------------------------------------------------------------
;关闭保护模式, 准备返回实模式
mov ax, NormalSelector
mov ds, ax
mov es, ax
mov ss, ax ;规范选择子
mov eax, cr0
and al, 0feh
mov cr0, eax ;关闭分段标记
;通过这个跳转刷新段选择子影子寄存器, 真正进入实模式
jmp far ptr _ToReal
_RetReal Endp
Code16Seg Ends
;============================================================================
;32位代码段, 由实模式跳入
;============================================================================
Code32Seg Segment use32
;============================================================================
;将_dwValue中的值最低位转换成ASCII并填写上属性, 在EAX中返回
;============================================================================
_ToAscii Proc _dwValue
local _byBuf[4]:byte
mov eax, _dwValue
and al, 0fh
.if al > 9
add al, 'A' ;转换低位
.Else
add al, '0'
.Endif
mov byte ptr [_byBuf], al
mov byte ptr [_byBuf+1], 7 ;属性位
xchg ah, al
.if al > 9
add al, 'A' ;转换高位
.Else
add al, '0'
.Endif
mov byte ptr [_byBuf+2], al
mov byte ptr [_byBuf+3], 7 ;属性
mov eax, dword ptr [_byBuf]
ret
_ToAscii Endp
_Entry Proc ;由实模式跳入
mov ax, StackSelector
mov ss, ax
mov esp, 256 ;设置ss->esp
mov ax, DataSrcSelector
mov ds, ax ;设置源数据段
mov ax, VideoSelector
mov es, ax ;设置目标视频段
xor esi, esi
mov edi, 80*2*10+10*2 ;第10行10列
mov ecx, 16 ;限长是16
cld
;----------------------------------------------------------------------------
@@: lodsb ;转换100000h开始的20个字节数据
Invoke _ToAscii, eax
stosd
mov ah, 7 ;属性
mov al, 20h ;空格
stosw
loop @b
;----------------------------------------------------------------------------
;跳到16位代码段, 准备返回Dos
Jmp32 Code16Selector, <offset _RetReal>
_Entry Endp
Code32Seg_Entry = _Entry - Code32Seg ;定义段内入口
Code32Len = $ - Code32Seg ;段长度
Code32Seg Ends
;============================================================================
;16位段, 由这里进入保护模式
;============================================================================
StartCode Segment use16
Jmain Proc
;----------------------------------------------------------------------------
mov eax, DataSeg
mov ds, ax
shl eax, 4
add eax, offset GDT
mov dword ptr ds:[GDT_Ptr+2], eax ;初始化VGDT描述符
;----------------------------------------------------------------------------
mov eax, Code32Seg ;初始化32位代码段
shl eax, 4
mov word ptr ds:[Code32Desc+2], ax ;段基址低地址
shr eax, 16
mov byte ptr ds:[Code32Desc+4], al ;段基址高地址低位
mov byte ptr ds:[Code32Desc+7], ah ;段基址高地址高位
;----------------------------------------------------------------------------
mov eax, Code16Seg ;初始化16位代码段
shl eax, 4
mov word ptr ds:[Code16Desc+2], ax ;段基址低地址
shr eax, 16
mov byte ptr ds:[Code16Desc+4], al ;段基址高地址低位
mov byte ptr ds:[Code16Desc+7], ah ;段基址高地址高位
;----------------------------------------------------------------------------
mov eax, DataMess ;初始化一个数据段, 用于显示消息
shl eax, 4
mov word ptr ds:[DataMessDesc+2], ax;段基址低地址
shr eax, 16
mov byte ptr ds:[DataMessDesc+4], al;段基址高地址低位
mov byte ptr ds:[DataMessDesc+7], ah;段基址高地址高位
;----------------------------------------------------------------------------
mov word ptr ds:[_RegSs], ss
mov word ptr ds:[_RegSp], sp ;保存下SS:SP
mov eax, StackSeg ;初始化堆栈段
shl eax, 4
mov word ptr ds:[StackDesc+2], ax ;段基址低地址
shr eax, 16
mov byte ptr ds:[StackDesc+4],al ;段基址高地址低位
mov byte ptr ds:[StackDesc+7], ah ;段基址高地址高位
;----------------------------------------------------------------------------
lgdt fword ptr ds:[GDT_Ptr] ;装在VGDTR
cli
_EnableA20 ;关中断 开A20地址线
mov eax, cr0
or eax, 1
mov cr0, eax ;开启分段进入保护模式
;----------------------------------------------------------------------------
;刷新指令预读取序列, 真正进入保护模式
Jmp16 Code32Selector, < Code32Seg_Entry >
_ToReal::;从保护模式又回来了
mov ax, DataSeg
mov ds, ax
mov sp, ds:[_RegSp]
mov ss, ds:[_RegSs] ;恢复SS:SP
_DisableA20 ;关闭A20地址线
sti
mov ax, 4c00h
int 21h
Jmain Endp
StartCode Ends
End Jmain
|