【PM复习】从保护模式切换回实模式

     在上几节中,我们在进入到保护模式后打印完一个字符后就死循环了,而现在我们想从保护模式切回实模式,先上代码:

Code:
  1. %include "pm.inc"  
  2.   
  3. org 0100h       
  4. jmp LABEL_BEGIN       
  5.       
  6. [section .gdt]       
  7. LABEL_DESC_DUMMY:       
  8.     Descriptor  0,0,0     
  9. LABEL_DESC_CODE32:    
  10.     Descriptor  0,0ffffh,DA_C | DA_32     
  11. LABEL_DESC_CODE16:   
  12.     Descriptor  0,0ffffh,DA_C   
  13. LABEL_DESC_VIDEO:       
  14.     Descriptor  0b8000h,0ffffh,DA_DRW   
  15. LABEL_DESC_NORMAL:   
  16.     Descriptor  0,0ffffh,DA_DRW   
  17.            
  18. GDT_Len equ $ - LABEL_DESC_DUMMY       
  19. GDT_Ptr:       
  20.     dw  GDT_Len - 1       
  21.     dd  0       
  22.       
  23. Selector_Code32 equ LABEL_DESC_CODE32 - LABEL_DESC_DUMMY       
  24. Selector_Code16 equ LABEL_DESC_CODE16 - LABEL_DESC_DUMMY   
  25. Selector_Video  equ LABEL_DESC_VIDEO - LABEL_DESC_DUMMY       
  26. Selector_Normal equ LABEL_DESC_NORMAL - LABEL_DESC_DUMMY   
  27.       
  28. [section .s16]       
  29. [bits 16]       
  30. LABEL_BEGIN:       
  31.   mov ax,cs       
  32.   mov ds,ax       
  33.   mov es,ax       
  34.      
  35.   mov   [LABEL_GO_BACK_TO_REAL + 3],ax   
  36.            
  37.   Fill_Descriptor   LABEL_DESC_CODE32,LABEL_CODE32   
  38.   Fill_Descriptor   LABEL_DESC_CODE16,LABEL_BEGIN   
  39.            
  40.   xor eax,eax       
  41.   mov ax,ds       
  42.   shl eax,4       
  43.   add eax,LABEL_DESC_DUMMY       
  44.   mov dword [GDT_Ptr + 2],eax       
  45.            
  46.   lgdt    [GDT_Ptr]       
  47.            
  48.   cli       
  49.            
  50.   in  al,92h       
  51.   or  al,00000010b       
  52.   out 92h,al       
  53.            
  54.   mov eax,cr0       
  55.   or  al,1       
  56.   mov cr0,eax       
  57.            
  58.   jmp Selector_Code32:0       
  59.        
  60. _LABEL_PREPARE_GO_BACK_TO_REAL:   
  61. LABEL_PREPARE_GO_BACK_TO_REAL equ   _LABEL_PREPARE_GO_BACK_TO_REAL - $$   
  62.     mov ax,Selector_Normal   
  63.     mov ds,ax   
  64.     mov es,ax   
  65.     mov gs,ax   
  66.     mov fs,ax   
  67.        
  68.     mov eax,cr0   
  69.     and al,11111110b   
  70.     mov cr0,eax   
  71.        
  72. LABEL_GO_BACK_TO_REAL:   
  73.     jmp 0:LABEL_ALREADY_REAL   
  74.        
  75. LABEL_ALREADY_REAL:   
  76.     in  al,92h   
  77.     and al,11111101b   
  78.     out 92h,al   
  79.        
  80.     sti   
  81.        
  82.     mov ax,4c00h   
  83.     int 21h   
  84.       
  85. [section .s32]       
  86. [bits 32]       
  87. LABEL_CODE32:       
  88.   mov ax,Selector_Video       
  89.   mov gs,ax       
  90.   mov ah,0ch       
  91.   mov al,'x'      
  92.   mov [gs:80 * 10],ax       
  93.        
  94.   jmp   Selector_Code16:LABEL_PREPARE_GO_BACK_TO_REAL    

     我们看到,94行进行了一个跳转,取代了原来的死循环。跳转到了16位代码段的_LABEL_PREPARE_GO_BACK_TO_REAL 处,这里要注意一点,_LABEL_PREPARE_GO_BACK_TO_REAL在保护模式下是不能用的,是在实模式下用的,我们又额外定义了一个LABEL_PREPARE_GO_BACK_TO_REAL 宏, 表示标号_LABEL_PREPARE_GO_BACK_TO_REAL在此16位代码段的偏移。为什么不就直接在这个32位代码段完成切换回实模式的工作而要先跳到16位代码段呢?这个等会儿要说到。

     让我们继续前进,控制权来到了跳转的标号处,我们把一个选择子赋给了各个段寄存器,我们看到了GDT多了此选择子对应的描述符,为什么要这样呢?先从段描述符高速缓冲寄存器说起:

     在实模式下,段寄存器含有段值,为访问存储器形成物理地址时,处理器引用相应的某个段寄存器并将其值乘以16,形成20位的段基地址。在保护模式下,段寄存器含有段选择子,如上所述,为了访问存储器形成线性地址时,处理器要使用选择子所指定的描述符中的基地址等信息。为了避免在每次存储器访问时,都要访问描述符表而获得对应的段描述符,从80286开始每个段寄存器都配有一个高速缓冲寄存器,称之为段描述符高速缓冲寄存器或描述符投影寄存器,对程序员而言它是不可见的。每当把一个选择子装入到某个段寄存器时,处理器自动从描述符表中取出相应的描述符,把描述符中的信息保存到对应的高速缓冲寄存器中。此后对该段访问时,处理器都使用对应高速缓冲寄存器中的描述符信息,而不用再从描述符表中取描述符。
 
     段描述符高速缓冲寄存器在处理器内,所以可对其进行快速访问。绝大多数情况下,对存储器的访问是在对应选择子装入到段寄存器之后进行的,所以,使用段描述符高速缓冲寄存器可以得到很好的执行性能。段描述符高速缓冲寄存器之内保存的描述符信息将一直保存到重新把选择子装载到段寄存器时再更新。程序员尽管不可见段描述符高速缓冲寄存器,但必须注意到它的存在和它的上述更新时机。例如,在改变了描述符表中的某个当前段的描述符后,也要更新对应的段描述符高速缓冲寄存器的内容,即使段选择子未作改变,这可通过重新装载段寄存器实现。
 
     这些高速缓冲寄存器在实方式下仍发挥作用,只是内容上与保护模式下有所不同。段基地址仍是 32位,其值是相应段寄存器值(段值)乘以16,在把段值装载到段寄存器时刷新。由于其值是16位段值乘上16,所以在实模式下基地址实际上有效位只有20位。每个段的32位段界限都固定为0FFFFH,段属性的许多位也是固定的。所谓固定是指在实方式下不可设置这些属性值,只能继续沿用保护方式下所设置的值。因此,在准备结束保护模式回到实模式之前,要通过加载一个合适的描述符选择子到有关段寄存器,以使得对应段描述符高速缓冲寄存器中含有合适的段界限和属性。本实例GDT中的描述符Normal就是这样一个描述符,在返回实模式之前把对应选择子Normal_Selector加载到DS和ES就是此目的。16位代码段描述符中的内容也符合实模式的需要,所以在通过16位代码段返回实模式时,CS段描述符中的内容也符合实模式的要求。需要注意的是,不能从32位代码段返回实模式,这是因为无法实现从32位代码段返回时CS高速缓冲寄存器中的属性符合实模式的要求(实模式不能改变段属性)。顺便说以下,实例中的描述符都是符合实模式要求的。段描述符高速缓冲寄存器中含有合适的段界限。
 
     这就是为什么要给各个选择子赋Selector_Normal和必须从16位代码段返回实模式的原因了。接着看代码,第73行的代码很奇怪,跳转的地址的段值居然是0,但看看35行就明白了,在程序一开始就把实模式下的段值赋给了跳转指令的段值,这样 就可以在实模式下正常的跳转到对应的代码段了。这种方法有一定的技巧。
 
     剩下的代码就很熟悉了,运行结果在显示了字符后成功回到了DOS,如图所示:
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值