您的位置:首页 > 编程语言

保护模式编程实例

2010-10-08 14:03 176 查看
前面介绍了保护模式编程的重要指令,接下来需要掌握的是几个重要的保护模式编程要领。保护模式编程最重要的是切换,向保护模式切换的主要步骤是:(1)作切换到保护方式的准备;(2)切换到保护方式;(3)把指定内存区域的内容传送到位于常规内存的缓冲区中;(4)切换回实模式;(5)显示缓冲区内容。

本节,我们通过一个具体实例来开始接触保护模式编程,这个实例的其逻辑功能是,以十六进制数的形式显示从内存地址110000H开始的256个字节的值。本实例指定该内存区域的目的仅仅是想说明切换到保护模式的必要性,因为在实模式下不能访问该指定内存区域,只有在保护模式下才能访问到该指定区域。这也是Linux这类操作系统保护内核态代码和数据的一般方式。

1. 包含文件

386保护模式汇编语言程序用到的包含文件如下所示,该包含文件在Linux中是以C语言头文件的形式存在,有兴趣的同志可以对其进行专门的研究。

1 ;名称:386SCD.INC
2 ;功能:符号常量等的定义
3 ;----------------------------------------------------------------------------
4 ;IFNDEF __386SCD_INC
5 ;__386SCD_INC EQU 1
6 ;----------------------------------------------------------------------------
7 .386P
8 ;----------------------------------------------------------------------------
9 ;打开A20地址线
10 ;----------------------------------------------------------------------------
11 EnableA20 MACRO
12 push ax
13 in al,92h
14 or al,00000010b
15 out 92h,al
16 pop ax
17 ENDM
18 ;----------------------------------------------------------------------------
19 ;关闭A20地址线
20 ;----------------------------------------------------------------------------
21 DisableA20 MACRO
22 push ax
23 in al,92h
24 and al,11111101b
25 out 92h,al
26 pop ax
27 ENDM
28 ;----------------------------------------------------------------------------
29 ;16位偏移的段间直接转移指令的宏定义(在16位代码段中使用)
30 ;----------------------------------------------------------------------------
31 JUMP16 MACRO Selector,Offset
32 DB 0eah ;操作码
33 DW Offset ;16位偏移量
34 DW Selector ;段值或段选择子
35 ENDM
36 ;----------------------------------------------------------------------------
37 ;32位偏移的段间直接转移指令的宏定义(在32位代码段中使用)
38 ;----------------------------------------------------------------------------
39 COMMENT <JUMP32>
40 JUMP32 MACRO Selector,Offset
41 DB 0eah ;操作码
42 DD OFFSET
43 DW Selector ;段值或段选择子
44 ENDM
45 <JUMP32>
46 ;-------------------------------------------------
47 JUMP32 MACRO Selector,Offset
48 DB 0eah ;操作码
49 DW OFFSET
50 DW 0
51 DW Selector ;段值或段选择子
52 ENDM
53 ;----------------------------------------------------------------------------
54 ;16位偏移的段间调用指令的宏定义(在16位代码段中使用)
55 ;----------------------------------------------------------------------------
56 CALL16 MACRO Selector,Offset
57 DB 9ah ;操作码
58 DW Offset ;16位偏移量
59 DW Selector ;段值或段选择子
60 ENDM
61 ;----------------------------------------------------------------------------
62 ;32位偏移的段间调用指令的宏定义(在32位代码段中使用)
63 ;----------------------------------------------------------------------------
64 COMMENT <CALL32>
65 CALL32 MACRO Selector,Offset
66 DB 9ah ;操作码
67 DD Offset
68 DW Selector ;段值或段选择子
69 ENDM
70 <CALL32>
71 ;-------------------------------------------------
72 CALL32 MACRO Selector,Offset
73 DB 9ah ;操作码
74 DW Offset
75 DW 0
76 DW Selector ;段值或段选择子
77 ENDM
78 ;----------------------------------------------------------------------------
79 ;存储段描述符结构类型定义
80 ;----------------------------------------------------------------------------
81 Desc STRUC
82 LimitL DW 0 ;段界限(BIT0-15)
83 BaseL DW 0 ;段基地址(BIT0-15)
84 BaseM DB 0 ;段基地址(BIT16-23)
85 Attributes DB 0 ;段属性
86 LimitH DB 0 ;段界限(BIT16-19)(含段属性的高4位)
87 BaseH DB 0 ;段基地址(BIT24-31)
88 Desc ENDS
89 ;----------------------------------------------------------------------------
90 ;门描述符结构类型定义
91 ;----------------------------------------------------------------------------
92 Gate STRUC
93 OffsetL DW 0 ;32位偏移的低16位
94 Selector DW 0 ;选择子
95 DCount DB 0 ;双字计数
96 GType DB 0 ;类型
97 OffsetH DW 0 ;32位偏移的高16位
98 Gate ENDS
99 ;----------------------------------------------------------------------------
100 ;伪描述符结构类型定义(用于装入全局或中断描述符表寄存器)
101 ;----------------------------------------------------------------------------
102 PDesc STRUC
103 Limit DW 0 ;16位界限
104 Base DD 0 ;32位基地址
105 PDesc ENDS
106 ;----------------------------------------------------------------------------
107 ;任务状态段结构类型定义
108 ;----------------------------------------------------------------------------
109 TSS STRUC
110 TRLink DW 0 ;链接字段
111 DW 0 ;不使用,置为0
112 TRESP0 DD 0 ;0级堆栈指针
113 TRSS0 DW 0 ;0级堆栈段寄存器
114 DW 0 ;不使用,置为0
115 TRESP1 DD 0 ;1级堆栈指针
116 TRSS1 DW 0 ;1级堆栈段寄存器
117 DW 0 ;不使用,置为0
118 TRESP2 DD 0 ;2级堆栈指针
119 TRSS2 DW 0 ;2级堆栈段寄存器
120 DW 0 ;不使用,置为0
121 TRCR3 DD 0 ;CR3
122 TREIP DD 0 ;EIP
123 TREFlag DD 0 ;EFLAGS
124 TREAX DD 0 ;EAX
125 TRECX DD 0 ;ECX
126 TREDX DD 0 ;EDX
127 TREBX DD 0 ;EBX
128 TRESP DD 0 ;ESP
129 TREBP DD 0 ;EBP
130 TRESI DD 0 ;ESI
131 TREDI DD 0 ;EDI
132 TRES DW 0 ;ES
133 DW 0 ;不使用,置为0
134 TRCS DW 0 ;CS
135 DW 0 ;不使用,置为0
136 TRSS DW 0 ;SS
137 DW 0 ;不使用,置为0
138 TRDS DW 0 ;DS
139 DW 0 ;不使用,置为0
140 TRFS DW 0 ;FS
141 DW 0 ;不使用,置为0
142 TRGS DW 0 ;GS
143 DW 0 ;不使用,置为0
144 TRLDTR DW 0 ;LDTR
145 DW 0 ;不使用,置为0
146 TRTrip DW 0 ;调试陷阱标志(只用位0)
147 TRIOMap DW $+2 ;指向I/O许可位图区的段内偏移
148 TSS ENDS
149 ;----------------------------------------------------------------------------
150 ;存储段描述符类型值说明
151 ;----------------------------------------------------------------------------
152 ATDR EQU 90h ;存在的只读数据段类型值
153 ATDW EQU 92h ;存在的可读写数据段属性值
154 ATDWA EQU 93h ;存在的已访问可读写数据段类型值
155 ATCE EQU 98h ;存在的只执行代码段属性值
156 ATCER EQU 9ah ;存在的可执行可读代码段属性值
157 ATCCO EQU 9ch ;存在的只执行一致代码段属性值
158 ATCCOR EQU 9eh ;存在的可执行可读一致代码段属性值
159 ;----------------------------------------------------------------------------
160 ;系统段描述符类型值说明
161 ;----------------------------------------------------------------------------
162 ATLDT EQU 82h ;局部描述符表段类型值
163 ATTaskGate EQU 85h ;任务门类型值
164 AT386TSS EQU 89h ;可用386任务状态段类型值
165 AT386CGate EQU 8ch ;386调用门类型值
166 AT386IGate EQU 8eh ;386中断门类型值
167 AT386TGate EQU 8fh ;386陷阱门类型值
168 ;----------------------------------------------------------------------------
169 ;DPL值说明
170 ;----------------------------------------------------------------------------
171 DPL0 EQU 00h ;DPL=0
172 DPL1 EQU 20h ;DPL=1
173 DPL2 EQU 40h ;DPL=2
174 DPL3 EQU 60h ;DPL=3
175 ;----------------------------------------------------------------------------
176 ;RPL值说明
177 ;----------------------------------------------------------------------------
178 RPL0 EQU 00h ;RPL=0
179 RPL1 EQU 01h ;RPL=1
180 RPL2 EQU 02h ;RPL=2
181 RPL3 EQU 03h ;RPL=3
182 ;----------------------------------------------------------------------------
183 ;IOPL值说明
184 ;----------------------------------------------------------------------------
185 IOPL0 EQU 0000h ;IOPL=0
186 IOPL1 EQU 1000h ;IOPL=1
187 IOPL2 EQU 2000h ;IOPL=2
188 IOPL3 EQU 3000h ;IOPL=3
189 ;----------------------------------------------------------------------------
190 ;其它常量值说明
191 ;----------------------------------------------------------------------------
192 D32 EQU 40h ;32位代码段标志
193 GL EQU 80h ;段界限以4K为单位标志
194 TIL EQU 04h ;TI=1(局部描述符表标志)
195 VMFL EQU 00020000h ;VMF=1
196 VMFLW EQU 0002h
197 IFL EQU 00000200h ;IF=1
198 RFL EQU 00010000h ;RF=1(重启动标志,为1表示忽略调试故障)
199 RFLW EQU 0001h
200 NTL EQU 00004000h ;NT=1
201 ;----------------------------------------------------------------------------
202 ;分页机制使用的常量说明
203 ;----------------------------------------------------------------------------
204 PL EQU 1 ;页存在属性位
205 RWR EQU 0 ;R/W属性位值,读/执行
206 RWW EQU 2 ;R/W属性位值,读/写/执行
207 USS EQU 0 ;U/S属性位值,系统级
208 USU EQU 4 ;U/S属性位值,用户级
209 ;----------------------------------------------------------------------------
210 ;ENDIF

2. 实例源程序

1 ;名称:ASM1.ASM
2 ;功能:演示实方式和保护方式切换(切换到16位代码段)
3 ;----------------------------------------------------------------------------
4 INCLUDE 386SCD.INC
5 ;----------------------------------------------------------------------------
6 ;字符显示宏指令的定义
7 ;----------------------------------------------------------------------------
8 EchoCh MACRO ascii
9 mov ah,2
10 mov dl,ascii
11 int 21h
12 ENDM
13 ;----------------------------------------------------------------------------
14 DSEG SEGMENT USE16 ;16位数据段
15 ;----------------------Desc来自头文件81行------------------------
16 GDT LABEL BYTE ;全局描述符表,LABEL伪指令设置属性
17 DUMMY LP Desc <> ;空描述符,Desc伪指令建6个字节的表
18 Code Desc <0ffffh,,,ATCE,,> ;代码段描述子,只执行
19 DataS Desc <0ffffh,0,11h,ATDW,,> ;源数据段描述子,可读写
20 DataD Desc <0ffffh,,,ATDW,,> ;目标数据段描述子,可读写
21 ;-------------------- PDesc来自头文件102行----------------------
22 GDTLen = $-GDT ;全局描述符表长度
23 VGDTR PDesc <GDTLen-1,> ;伪描述符指针(地址)
24 ;-----------------------------三个选择子-------------------------------
25 Code_Sel = Code-GDT ;代码段选择子
26 DataS_Sel = Datas-GDT ;源数据段选择子
27 DataD_Sel = DataD-GDT ;目标数据段选择子
28 ;----------------------------------------------------------------------------
29 BufLen = 256 ;缓冲区字节长度
30 Buffer DB BufLen DUP(0) ;缓冲区
31 ;----------------------------------------------------------------------------
32 DSEG ENDS ;数据段定义结束
33 ;----------------------------------------------------------------------------
34 CSEG SEGMENT USE16 ;16位代码段
35 ASSUME CS:CSEG,DS:DSEG
36 ;----------------------------------------------------------------------------
37 Start PROC
38 mov ax,DSEG ;实模式下16位数据段基址
39 mov ds,ax
40 ;准备要加载到GDTR的伪描述符
41 mov bx,16
42 mul bx ;ax左移4位(24=16),ax+dx
43 add ax,OFFSET GDT ;计算并设置GDT的基地址
44 adc dx,0 ;有可能有进位,此时ax+dx为段基址
45 mov WORD PTR VGDTR.Base,ax ;填充数据结构VGDTR低字
46 mov WORD PTR VGDTR.Base+2,dx ;填充数据结构VGDTR高字
47 ;设置代码段描述符
48 mov ax,cs
49 mul bx ; ax再左移4位,ax+dx
50 mov WORD PTR Code.BaseL,ax ;设置代码段描述子基地址
51 mov BYTE PTR Code.BaseM,dl ;设置代码段描述子基地址
52 mov BYTE PTR Code.BaseH,dh ;设置代码段描述子基地址
53 ;设置目标数据段描述符
54 mov ax,ds
55 mul bx ;ds作为目标数据的段基址
56 add ax,OFFSET Buffer ;加上Buffer的偏移量
57 adc dx,0 ;所以Buffer就是我们的目标段
58 mov WORD PTR DataD.BaseL,ax ;设置目标段描述子基地址
59 mov BYTE PTR DataD.BaseM,dl ;设置目标段描述子基地址
60 mov BYTE PTR DataD.BaseH,dh ;设置目标段描述子基地址
61 ;加载GDTR
62 lgdt QWORD PTR VGDTR ;将VGDTR加载到 GDTR
63 cli ;关中断
64 EnableA20 ;打开地址线A20
65 ;切换到保护方式
66 mov eax,cr0
67 or eax,1 ;PE置位,从此进入保护模式
68 mov cr0,eax
69 ;清指令预取队列,并真正进入保护方式,注意对照头文件31行的宏
70 JUMP16 Code_Sel,<OFFSET Virtual>
71 Virtual: ;现在开始在保护方式下运行
72 mov ax,DataS_Sel
73 mov ds,ax ;加载源数据段描述子
74 mov ax,DataD_Sel
75 mov es,ax ;加载目标数据段描述子
76 cld
77 xor si,si
78 xor di,di ;设置指针初值
79 mov cx,BufLen/4 ;设置4字节为单位的缓冲区长度
80 repz movsd ;传送
81 ;切换回实模式
82 mov eax,cr0
83 and al,11111110b
84 mov cr0,eax
85 ;清指令预取队列,进入实方式
86 JUMP16 <SEG Real>,<OFFSET Real>
87 Real: ;现在又回到实模式
88 DisableA20
89 sti
90 mov ax,DSEG
91 mov ds,ax
92 mov si,OFFSET Buffer
93 cld
94 mov bp,BufLen/16
95 NextLine: mov cx,16
96 NextCh: lodsb
97 push ax
98 shr al,1
99 call ToASCII
100 EchoCh al
101 pop ax
102 call ToASCII
103 EchoCh al
104 EchoCh ' '
105 loop NextCh
106 EchoCh 0dh
107 EchoCh 0ah
108 dec bp
109 jnz NextLine
110 mov ax,4c00h
111 int 21h
112 Start ENDP
113 ;----------------------------------------------------------------------------
114 ToASCII PROC
115 and al,0fh
116 add al,90h
117 daa
118 adc al,40h
119 daa
120 ret
121 ToASCII ENDP
122 ;----------------------------------------------------------------------------
123 CSEG ENDS ;代码段定义结束
124 ;----------------------------------------------------------------------------
125 END Start

3. 关于实例步骤的注释

在源程序的开头首先包含了文件“386SCD.INC”,在此包含文件中定义了保护模式程序设计要用到的一些结构、宏及常量。下面对各实现步骤作些说明。

(1)切换到保护方式的准备工作

在从实模式切换到保护模式之前,必须作必要的准备。准备工作的内容根据实际而定。最起码的准备工作是建立合适的全局描述符表,并使用GDTR指向该GDT。因为在切换到保护方式时,至少要把代码段的选择子装载到CS,所以GDT中至少含有代码段的描述符。

从本实例源程序可见,全局描述符表GDT仅有四个描述符:第一个是空描述符;第二个是代码段描述符;第三个和第四个分别为源数据段及目标数据段描述符。本实例各描述符中的段界限是在定义时设置的,并且除伪描述符VGDTR中的界限按GDT的实际长度设置外,各使用的存储段描述符的界限都规定为0FFFFH。

另外,描述符中的段属性也根据所描述段的类型被预置,各属性的定义在包含文件386SCD.INC中均有说明。从属性值可知,这三个段都是16位段。

由于在切换到保护方式后就要引用GDT,所以在切换到保护方式前必须装载GDTR。实例中使用如下指令装载GDTR:
LGDT QWORD PTR VGDTR

该指令的功能是把存储器中的伪描述符VGDTR装入到全局描述符表寄存器GDTR中。伪描述符VGDTR的结构如前所述结构类型PDESC所示,低字是以字节位单位的全局描述符表段的界限,高双字为描述符表段的线性基地址(本实例不启用分页机制,所以线性地址等同于物理地址)。注意,本实例中未涉及到局部描述符表及中断描述符表,这两个表类似,我们就不做说明了。

(2)由实模式切换到保护模式

在做好准备后,从实模式切换到保护模式并不难。原则上只要把控制寄存器CR0中的PE位置1即可。本实例采用如下三条指令设置PE位:
mov eax,cr0
or eax,1
mov cr0,eax

实际情况要比这复杂些。执行上面的三条指令后,处理器转入保护模式,但CS中的内容还是实模式下代码段的段值,而不是保护模式下代码段的选择子,所以在取指令之前得把代码段的选择子装入CS。为此,紧接着这三条指令,安排一条如下所示的段间转移指令:
JUMP16 Code_Sel,<OFFSET Virtual>

这条段间转移指令在实模式下被预取并在保护方式下被执行。利用这条段间转移指令可把保护模式下代码段的选择子装入CS,同时也刷新指令预取队列。从此真正进入保护模式。

(3)由保护模式切换到实模式

在80386上,从保护模式切换到实模式的过程类似于从实模式切换到保护模式。原则上只要把控制寄存器CR0中的PE位清0即可。实际上,在此之后也要安排一条段间转移指令,一方面清指令预取队列,另一方面把实模式下代码段的段值送CS。这条段间转移指令在保护方式下被预取并在实模式下被执行。

(4)保护模式下的数据传送

首先,把源数据段和目标数据段的选择子装入DS和ES寄存器,这两个描述符已在实模式下设置好,把选择子装入段寄存器就意味着把包括基地址在内的段信息装入到了段描述符高速缓冲寄存器。然后设置指针寄存器SI和DI的初值,也设置计数器CX的初值。根据预置的段属性,在保护方式下,代码段也仅是16位段,串操作指令只使用16位的SI、DI和CX等寄存器。最后利用串操作指令实施传送。

(5)显示缓冲区中的内容

由于缓冲区在常规内存中,所以在实模式下根据要求按十六进制显示其内容是很容易理解的,这里就不再多说。

4. 特别说明

作为一个实模式和保护模式切换的例子,本实为了演示保护模式编程的基本规定动作,例作了大量的简化处理。通常,由实模式切换到保护模式的准备工作还应包含建立中断描述符表。但本实例没有建立中断描述符表。为此,要求整个过程在关中断的情况下进行;要求不使用软中断指令;假设不发生任何异常。否则会导致系统崩溃。

本实例未使用局部描述符表,所以在进入保护模式后没有设置局部描述符表寄存器LDTR。为此,在保护模式下使用的段选择子都指定GDT中的描述符。

本实例未定义保护模式下的堆栈段,GDT中没有堆栈段描述符,在保护模式下没有设置SS,所以在保护方式下没有涉及堆栈操作的指令。本实例各描述符特权级DPL和各选择子的请求特权级RPL均为0,在保护方式下运行时的当前特权级CPL也是0。本实例没有采用分页管理机制,也即CR0中的PG位为0,线性地址就是存储单元的物理地址。

PC及其兼容机的第21根地址线(A20)较特殊,这就是前面提到的PC中安排的一个 “门”控制该地址线是否有效。为了访问地址在1M以上的存储单元,应先打开控制地址线A20的“门”。这种设置与实模式下只使用最低端的1M字节存储空间有关,与处理器是否工作在实模式或保护方式无关,即使在关闭地址线A20时,也可进入保护模式。

如何打开和关闭地址线A20与计算机系统的具体设置有关。在本文中介绍的包含文件386SCD.INC中定义了两个宏,打开地址线A20的宏EnableA20和关闭地址线A20的宏DisableA20,此两个宏指令在一般的PC兼容机上都是可行的。

5. 关于32位代码段程序设计的说明

在32位代码段中,缺省的操作数大小是32位,缺省的存储单元地址大小是32位。由于串操作指令使用的指针寄存器是ESI和EDI,LOOP指令使用的计数器是ECX,所以,后面的例子中,在代码段CSEG2中,为了使用串操作指令,对ESI和EDI等寄存器赋初值。请比较代码段CSEG3中的相关片段和实例一中的相关片段,它们是16位代码段。

下面是个32位保护模式切换的例子,其目的与前一个例子一样,具体的就详细介绍了,感兴趣的童鞋可以自己去研究一下:

;名称:ASM2.ASM
;功能:演示实方式和保护方式切换(切换到32位代码段)
;----------------------------------------------------------------------------
INCLUDE 386SCD.INC
;----------------------------------------------------------------------------
DSEG SEGMENT USE16 ;16位数据段
;----------------------------------------------------------------------------
GDT LABEL BYTE ;全局描述符表
DUMMY Desc <> ;空描述符
Normal Desc <0ffffh,,,ATDW,,> ;规范段描述符
Code32 Desc <C32Len-1,,,ATCE,D32,> ;32位代码段描述符
Code16 Desc <0ffffh,,,ATCE,,> ;16位代码段描述符
DataS Desc <DataLen-1,0,10h,ATDR,,> ;源数据段描述符
DataD Desc <3999,8000h,0bh,ATDW,,> ;显示缓冲区描述符
Stacks Desc <StackLen-1,,,ATDW,,> ;堆栈段描述符
;----------------------------------------------------------------------------
GDTLen = $-GDT ;全局描述符表长度
VGDTR PDesc <GDTLen-1,> ;伪描述符
;----------------------------------------------------------------------------
SaveSP DW ? ;用于保存SP寄存器
SaveSS DW ? ;用于保存SS寄存器
;----------------------------------------------------------------------------
Normal_Sel = Normal-GDT ;规范段描述符选择子
Code32_Sel = Code32-GDT ;32位代码段选择子
Code16_Sel = Code16-GDT ;16位代码段选择子
DataS_Sel = Datas-GDT ;源数据段选择子
DataD_Sel = DataD-GDT ;目标数据段选择子
Stacks_Sel = Stacks-GDT ;堆栈段描述符选择子
;----------------------------------------------------------------------------
DataLen = 16
;----------------------------------------------------------------------------
DSEG ENDS ;数据段定义结束
;----------------------------------------------------------------------------
StackSeg SEGMENT PARA STACK USE16
StackLen = 256
DB StackLen DUP(0)
StackSeg ENDS
;----------------------------------------------------------------------------
CSEG1 SEGMENT USE16 'REAL' ;16位代码段
ASSUME CS:CSEG1,DS:DSEG
;----------------------------------------------------------------------------
Start PROC
mov ax,DSEG
mov ds,ax
;准备要加载到GDTR的伪描述符
mov bx,16
mul bx
add ax,OFFSET GDT ;计算并设置基地址
adc dx,0 ;界限已在定义时设置好
mov WORD PTR VGDTR.Base,ax
mov WORD PTR VGDTR.Base+2,dx
;设置32位代码段描述符
mov ax,CSEG2
mul bx
mov WORD PTR Code32.BaseL,ax
mov BYTE PTR Code32.BaseM,dl
mov BYTE PTR Code32.BaseH,dh
;设置16位代码段描述符
mov ax,CSEG3
mul bx
mov WORD PTR Code16.BaseL,ax ;代码段开始偏移为0
mov BYTE PTR Code16.BaseM,dl ;代码段界限已在定义时设置好
mov BYTE PTR Code16.BaseH,dh
;设置堆栈段描述符
mov ax,ss
mov WORD PTR SaveSS,ax
mov WORD PTR SaveSP,sp
mov ax,StackSeg
mul bx
mov WORD PTR Stacks.BaseL,ax
mov BYTE PTR Stacks.BaseM,dl
mov BYTE PTR Stacks.BaseH,dh
;加载GDTR
lgdt QWORD PTR VGDTR
cli ;关中断
EnableA20 ;打开地址线A20
;切换到保护方式
mov eax,cr0
or al,1
mov cr0,eax
;清指令预取队列,并真正进入保护方式
JUMP16 Code32_Sel,<OFFSET SPM32>
ToReal: ;现在又回到实方式
mov ax,DSEG
mov ds,ax
mov sp,SaveSP
mov ss,SaveSS
DisableA20
sti
mov ax,4c00h
int 21h
Start ENDP
;----------------------------------------------------------------------------
CSEG1 ENDS ;代码段定义结束
;----------------------------------------------------------------------------
CSEG2 SEGMENT USE32 'PM32'
ASSUME CS:CSEG2
;----------------------------------------------------------------------------
SPM32 PROC
mov ax,Stacks_Sel
mov ss,ax
mov esp,StackLen
mov ax,DataS_Sel
mov ds,ax
mov ax,DataD_Sel
mov es,ax
xor esi,esi
xor edi,edi
mov ecx,DataLen
cld
Next: lodsb
push ax
CALL ToASCII
mov ah,7
shl eax,16
pop ax
shr al,4
CALL ToASCII
mov ah,7
stosd
mov al,20h
stosw
loop Next
JUMP32 Code16_Sel,<OFFSET SPM16>
SPM32 ENDP
;----------------------------------------------------------------------------
ToASCII PROC
and al,00001111b
add al,30h
cmp al,39h
jbe Isdig
add al,7
IsDig: ret
ToASCII ENDP
;----------------------------------------------------------------------------
C32Len = $
;----------------------------------------------------------------------------
CSEG2 ENDS
;----------------------------------------------------------------------------
CSEG3 SEGMENT USE16 'PM16'
ASSUME CS:CSEG3
;----------------------------------------------------------------------------
SPM16 PROC
xor si,si
mov di,DataLen*3*2
mov ah,7
mov cx,DataLen
AGain: lodsb
stosw
loop AGain
mov ax,Normal_sel
mov ds,ax
mov es,ax
mov ss,ax
mov eax,cr0
and al,11111110b
mov cr0,eax
jmp FAR PTR ToReal
SPM16 ENDP
;----------------------------------------------------------------------------
CSEG3 ENDS
;----------------------------------------------------------------------------
END Start
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: