在前面的代码上进行修改后,测试读写大地址内存(实模式下的1MB限制),而且从保护模式再调回实模式.
上代码分析(省略前面重复的代码部分):
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
|
; ==========================================
; pmtest2.asm
; 编译方法:nasm pmtest2.asm -o pmtest2.com
; ==========================================
%include
"pm.inc"
; 常量, 宏, 以及一些说明
org 0100h ;参看DOS系统中.EXE文件的加载过程
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 ;5M基址
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] ; 32位数据段
ALIGN 32
[BITS 32]
LABEL_DATA:
SPValueInRealMode dw 0 ;保存实模式下的SP
; 字符串
PMMessage: db
"In Protect Mode now. ^-^"
, 0 ; 在保护模式中显示
OffsetPMMessage equ PMMessage - $$ ;PMMessage在LABEL_DATA节中的偏移
StrTest: db
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
, 0
OffsetStrTest equ StrTest - $$ ;StrTest在LABEL_DATA节中中的偏移
DataLen equ $ - LABEL_DATA ;LABEL_DATA节的长度
; END of [SECTION .data1]
; 全局堆栈段
[SECTION .gs]
ALIGN 32
[BITS 32]
LABEL_STACK:
times 512 db 0 ;512字节的堆栈段
TopOfStack equ $ - LABEL_STACK – 1 ;栈顶(偏移)
; END of [SECTION .gs]
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0100h
mov [LABEL_GO_BACK_TO_REAL+3], ax ;保存实模式下的CS
|
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
229
230
231
232
233
234
235
236
237
238
239
240
|
mov [SPValueInRealMode], 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
; 初始化数据段描述符
xor eax, eax
mov ax, ds
shl eax, 4
add eax, 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 作准备
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
; 准备切换到保护模式
mov eax, cr0
or eax, 1
mov cr0, eax
; 真正进入保护模式
jmp dword SelectorCode32:0 ; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0 处
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LABEL_REAL_ENTRY: ; 从保护模式跳回到实模式就到了这里
mov ax, cs ;jmp 0:LABEL_REAL_ENTRY 跳转到此处,恢复cs
mov ds, ax ;使用cs恢复ds,es,ss
mov es, ax
mov ss, ax
mov sp, [SPValueInRealMode] ;恢复实模式下的sp
in
al, 92h ; `.
and al, 11111101b ; | 关闭 A20 地址线
out
92h, al ; /
sti ; 开中断
mov ax, 4c00h ; `.
int
21h ; / 回到 DOS
; END of [SECTION .s16]
[SECTION .s32]; 32 位代码段. 由实模式跳入.
[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
mov ss, ax ; 堆栈段选择子
mov esp, TopOfStack
; 下面显示一个字符串
mov ah, 0Ch ; 0000: 黑底 1100: 红字
xor esi, esi ; esi重置
xor edi, edi ; edi重置
mov esi, OffsetPMMessage ; 源数据偏移
mov edi, (80 * 10 + 0) * 2 ; 目的数据偏移。屏幕第 10 行, 第 0 列。
cld
.1:
lodsb ;逐个加载ds:esi对应的字符到al中,其中DS中的选择子SelectData指向LABEL_DATA
test al, al ;判断al字符是否为0(只是为了测试,请看PMMeesage的定义最后是0)
jz .2 ;为0跳转.2显示完毕
mov [gs:edi], ax ;非0将ax中的字符移到视频选择子对应的区域gs:edi
add edi, 2 ;设置gs:edi指向下个字符,edi加2(ax为16位)
jmp .1 ;跳转到.1处继续
.2: ; 显示完毕
call DispReturn ;显示完毕后,换行显示
call TestRead ;读5M地址内容
call TestWrite ;写5M地址内容
call TestRead ;在此读5M地址荣,测试写5M地址内容是否成功
; 到此停止
jmp SelectorCode16:0 ;跳转到LABEL_SEG_CODE16处执行(调回实模式)
; ------------------------------------------------------------------------
TestRead:
xor esi, esi ;esi重置
mov ecx, 8 ;设置读取字节数位8
.loop:
mov al, [es:esi] ;将es:esi中的字符移到al中(5M地址)
call DispAL ;显示al中的值
inc esi ;增加esi的值,指向下个es:esi的下个字符
loop .loop ;继续读取
call DispReturn ;读取完毕显示回车
ret ;返回调用处
; TestRead 结束-----------------------------------------------------------
; ------------------------------------------------------------------------
TestWrite:
push esi ;esi入栈
push edi ;edi入栈
xor esi, esi ;esi重置
xor edi, edi ;edi重置
mov esi, OffsetStrTest ;源数据偏移
cld
.1:
lodsb ;将OffsetStrTest对应的字符串逐个加载到al中
test al, al ;是否为0
jz .2 ;为0,跳转.2读取完毕
mov [es:edi], al ;将al中的值写入测试大地址(5M)
inc edi ;增加EDI,下个字符的位置
jmp .1 ;继续读取
.2:
pop edi ;恢复edi,esi
pop esi
ret
; TestWrite 结束----------------------------------------------------------
; ------------------------------------------------------------------------
; 显示 AL 中的数字(将二进制转换为16进制显示)
; 默认地:
; 数字已经存在 AL 中
; edi 始终指向要显示的下一个字符的位置
; 被改变的寄存器:
; ax, edi
; ------------------------------------------------------------------------
DispAL:
push ecx ;ecx,edx入栈
push edx
mov ah, 0Ch ; 0000: 黑底 1100: 红字
mov dl, al ; al暂存入dl中
shr al, 4 ; al右移4位
mov ecx, 2 ;
.begin:
and al, 01111b ; 将al中的高4位清0,保留低四位(原高四位)
cmp al, 9 ; 是否大于9
ja .1 ; 大于则跳转.1
add al,
'0'
;
jmp .2
.1:
sub al, 0Ah ;大于9将数字扣除0Ah(10)
add al,
'A'
;加上
'A'
(65)转换为A到F 例如11 – 10 + 65 = 66 =
'B'
.2:
mov [gs:edi], ax ;移入显存区域
add edi, 2 ;edi+2指向下个位置
mov al, dl ;dl移入al中,进行低4位的16进制转换
loop .begin ;跳转.begin
add edi, 2 ;edi加2
pop edx ;恢复edx,ecx
pop ecx
ret
; DispAL 结束-------------------------------------------------------------
; ------------------------------------------------------------------------
DispReturn:
push eax ;eax,ebx入栈
push ebx
mov eax, edi ;edi移入eax
mov bl, 160 ;bl设置为160(设定每行显示为(80*10 + 0)*2)
div bl ;al/bl = edi/bl al位商 ah为余数
and eax, 0FFh ;清除eax的高16位,
inc eax ;eax+1
mov bl, 160 ;
mul bl ;ah = al*160=(edi/160的商+1)*160-----达到换行的效果
mov edi, eax ;
pop ebx
pop eax
ret
; DispReturn 结束---------------------------------------------------------
SegCode32Len equ $ - LABEL_SEG_CODE32
; END of [SECTION .s32]
; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式
[SECTION .s16code]
ALIGN 32
[BITS 16]
LABEL_SEG_CODE16:
; 跳回实模式:
mov ax, SelectorNormal ;将SelectorNormal加载到ds,es,fs,gs,ss,去除在32保护模式中的段属性
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
;使描述符寄存器含有合适的段界限和属性(实模式下32段属性不符合实模式的要求)
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov eax, cr0 ;设置cr0的PE为0
and al, 11111110b
mov cr0, eax
LABEL_GO_BACK_TO_REAL:
jmp 0:LABEL_REAL_ENTRY ; 段地址会在程序开始处被设置成正确的值
|
1
|
;jmp segment:offset 根据段间转移(长转移),汇编指令为:
|
1
|
;0EAh Offset(2字节) Segment(2字节)
|
1
|
;这就是代码mov [LABEL_GO_BACK_TO_REAL+3], ax的作用
|
1
|
;将实模式下的cs值,写入LABEL_GO_BACK_TO_REAL地址偏移+3的位置,
|
1
2
3
4
5
|
;刚好就是jmp指令EA的Segment处,请参看实模式下的长跳转指令图示
;所以此的指令为JPM cs(实模式):LABEL_REAL_ENRY
Code16Len equ $ - LABEL_SEG_CODE16
; END of [SECTION .s16code]
|
1
|
|