保护模式编程实例

前面介绍了保护模式编程的重要指令,接下来需要掌握的是几个重要的保护模式编程要领。保护模式编程最重要的是切换,向保护模式切换的主要步骤是:(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)保护模式下的数据传送

 

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

 

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位。由于串操作指令使用的指针寄存器是ESIEDILOOP指令使用的计数器是ECX,所以,后面的例子中,在代码段CSEG2中,为了使用串操作指令,对ESIEDI等寄存器赋初值。请比较代码段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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值