【PM复习】保护模式下的一点迷你应用(上)

来源:互联网 发布:淘宝店铺手机端网址 编辑:程序博客网 时间:2024/05/29 03:32

     前一节中我们进入保护模式后就只在显存中写入一个字符后就退出,还没有体验到保护模式下的种种强大的功能。那么现在我们就来体验一下保护模式里的一个强大的功能,就是强大的寻址。在这节的任务中是在5M地址处读取8个字节以16进制的方式显示出来,接着换行并在5M地址处连续写入8个字节,分别是从字符A开始的ASCII码,最后再从5M地址处读取8个字节以16进制的方式显示出来。

     首先把以16进制显示一个数字的功能写成一个函数,命名为Disp_Al,在调用此函数前把要显示的数字先送到al中,代码如下:

Code:
  1. Disp_Al:   
  2.     push    ebp   
  3.     mov ebp,esp   
  4.        
  5.     push    esi   
  6.     push    ecx   
  7.     push    eax   
  8.     mov ecx,2   
  9.     shr al,4   
  10.        
  11. .loop:   
  12.     and al,0fh   
  13.     cmp al,9   
  14.     jb  .1   
  15.     add al,7   
  16.        
  17. .1:   
  18.     add al,30h   
  19.     mov esi,[d_Disp_Pos]   
  20.     mov byte [gs:esi],al   
  21.     mov byte [gs:esi + 1],0ch   
  22.     add dword [d_Disp_Pos],2   
  23.        
  24.     dec ecx   
  25.     cmp ecx,0   
  26.     je  .2   
  27.     pop eax   
  28.     jmp .loop   
  29.        
  30. .2:   
  31.     pop ecx   
  32.     pop esi   
  33.     pop ebp   
  34.     ret  

     2到7行和31到33是一些例行的保护。al是8位,以16进制显示的话是2位16进制位,也就是要显示2个字符,所以用ecx来做计数器,赋2。先要把al的高4位显示出来,所以第9行是先把高4位移到低4位,那低4位不是丢失了吗,没事,第7行先把eax压到栈中保护了起来。接下来把al的高4位清零,跟9比较一下,如果大于9就要以字母显示,需要特殊处理一下,在把al中的数转化成ASCII码之后就要写入显存了,我们看到d_Disp_Pos变量,这是用来标示在显存中要显示的位置,为此我们要建立一个数据段,在GDT添加:

Code:
  1. LABEL_DESC_DATA:   
  2.     Descriptor  0,0ffffh,DA_DRW  

     并添加选择子:

Code:
  1. Selector_Data       equ LABEL_DESC_DATA - LABEL_DESC_DUMMY    

     并在16位段中添加初始化数据段的段基址的宏:

Code:
  1. Fill_Descriptor LABEL_DESC_DATA,LABEL_DATA  

     并添加一个段,在段中添加d_Disp_Pos的初始化,这里我们想在第5行处开始显示:

Code:
  1. [section .data]   
  2. LABEL_DATA:   
  3. _d_Disp_Pos dd  160 * 5   
  4. d_Disp_Pos  equ _d_Disp_Pos - $$  

     接着看Disp_Al函数,19到22行写入显存,并把d_Disp_Pos变量自增2,以指向下一个显存的位置,接下来ecx自减1,判断ecx是否等于0,等于就退出函数,不等于就把eax弹出,跳回标号loop处执行。这个函数就搞定了。

     不知你有没有注意到还有一个问题,在函数的调用和函数内部中都要对栈进行操作,而在GDT中却没有栈段,所以必须设立一个32位的栈段,在GDT中添加:

Code:
  1. LABEL_DESC_STACK32:    
  2.     Descriptor  0,Stack_Len - 1,DA_DRW | DA_32  

     添加选择子:

Code:
  1. Selector_Stack32 equ    LABEL_DESC_STACK32 - LABEL_DESC_DUMMY  

     添加栈段:

Code:
  1. [section .stack32]   
  2. [bits 32]   
  3. LABEL_STACK32:   
  4.     times 512 db 0   
  5. Stack_Len   equ $ - $$  

     添加初始化栈段段基址的宏:

Code:
  1. Fill_Descriptor LABEL_DESC_STACK32,LABEL_STACK32  

     另外,在进入保护模式之前,还要把保护模式下的sp值保存起来,在从保护模式回到实模式恢复,所以在数据段添加一个16位的变量:

Code:
  1. _w_SP_Value_In_Real_Mode    dw  0   
  2. w_SP_Value_In_Real_Mode equ _w_SP_Value_In_Real_Mode - $$  

     在进入保护模式前把sp值存到此变量:

Code:
  1. mov [w_SP_Value_In_Real_Mode],sp  

     返回实模式后恢复:

Code:
  1. mov ss,ax   
  2. mov sp,[w_SP_Value_In_Real_Mode]  

     接着是换行显示的功能,命名为Disp_Return,代码如下:

Code:
  1. Disp_Return:   
  2.     push    ebp   
  3.     mov ebp,esp   
  4.     push    eax   
  5.     push    ebx   
  6.        
  7.     mov eax,[d_Disp_Pos]   
  8.     mov bl,160   
  9.     div bl   
  10.     and ax,0ffh   
  11.     inc ax   
  12.     mov bl,160   
  13.     mul bl   
  14.     mov [d_Disp_Pos],eax   
  15.        
  16.     pop ebx   
  17.     pop eax   
  18.     pop ebp   
  19.     ret  

     此函数很简单,就是先用d_Disp_Pos的值除以160,160就是每行的字节数,div指令执行后,al保留着当前的行数,al自增1后,再乘以160,把结果赋给d_Disp_Pos,这样就指向下一行的起始处。

     OK,先暂停一下,添加一点测试代码来测试下是否实现了功能,在32位代码段中把写入一个字符的代码注释掉,添加以下新的测试代码:

Code:
  1. mov ax,Selector_Stack32   
  2. mov ss,ax   
  3. mov esp,Stack_Len   
  4.   
  5. mov ax,Selector_Data   
  6. mov ds,ax   
  7. mov ax,Selector_Video   
  8. mov gs,ax   
  9. mov al,4   
  10. call    Disp_Al   
  11. call    Disp_Return   
  12. mov al,14   
  13. call    Disp_Al  

     1到3行是对保护模式下的栈初始化,5到8行初始化DS和GS,接下来就是函数调用了,打印4换行后打印14。下面是所有代码:
 

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_DATA:   
  16.     Descriptor  0,0ffffh,DA_DRW   
  17. LABEL_DESC_STACK32:    
  18.     Descriptor  0,Stack_Len - 1,DA_DRW | DA_32   
  19. LABEL_DESC_NORMAL:   
  20.     Descriptor  0,0ffffh,DA_DRW   
  21.            
  22. GDT_Len equ $ - LABEL_DESC_DUMMY       
  23. GDT_Ptr:       
  24.     dw  GDT_Len - 1       
  25.     dd  0       
  26.       
  27. Selector_Code32 equ LABEL_DESC_CODE32 - LABEL_DESC_DUMMY       
  28. Selector_Code16 equ LABEL_DESC_CODE16 - LABEL_DESC_DUMMY   
  29. Selector_Video  equ LABEL_DESC_VIDEO - LABEL_DESC_DUMMY     
  30. Selector_Data       equ LABEL_DESC_DATA - LABEL_DESC_DUMMY     
  31. Selector_Stack32 equ    LABEL_DESC_STACK32 - LABEL_DESC_DUMMY   
  32. Selector_Normal equ LABEL_DESC_NORMAL - LABEL_DESC_DUMMY   
  33.   
  34. [section .stack32]   
  35. [bits 32]   
  36. LABEL_STACK32:   
  37.     times 512 db 0   
  38. Stack_Len   equ $ - $$   
  39.   
  40. [section .data]   
  41. LABEL_DATA:   
  42. _d_Disp_Pos dd  160 * 5   
  43. d_Disp_Pos  equ _d_Disp_Pos - $$   
  44. _w_SP_Value_In_Real_Mode    dw  0   
  45. w_SP_Value_In_Real_Mode equ _w_SP_Value_In_Real_Mode - $$   
  46.       
  47. [section .s16]       
  48. [bits 16]       
  49. LABEL_BEGIN:       
  50.     mov ax,cs       
  51.   mov ds,ax       
  52.   mov es,ax       
  53.   mov   ax,ss   
  54.   mov   sp,0100h   
  55.      
  56.   mov   [LABEL_GO_BACK_TO_REAL + 3],ax   
  57.            
  58.   Fill_Descriptor   LABEL_DESC_CODE32,LABEL_CODE32   
  59.   Fill_Descriptor   LABEL_DESC_CODE16,LABEL_BEGIN   
  60.   Fill_Descriptor   LABEL_DESC_DATA,LABEL_DATA   
  61.   Fill_Descriptor   LABEL_DESC_STACK32,LABEL_STACK32   
  62.            
  63.   xor eax,eax       
  64.   mov ax,ds       
  65.   shl eax,4       
  66.   add eax,LABEL_DESC_DUMMY       
  67.   mov dword [GDT_Ptr + 2],eax       
  68.            
  69.   lgdt    [GDT_Ptr]       
  70.      
  71.   mov   [w_SP_Value_In_Real_Mode],sp   
  72.            
  73.   cli       
  74.            
  75.   in  al,92h       
  76.   or  al,00000010b       
  77.   out 92h,al       
  78.            
  79.   mov eax,cr0       
  80.   or  al,1       
  81.   mov cr0,eax       
  82.            
  83.   jmp Selector_Code32:0       
  84.        
  85. _LABEL_PREPARE_GO_BACK_TO_REAL:   
  86. LABEL_PREPARE_GO_BACK_TO_REAL equ   _LABEL_PREPARE_GO_BACK_TO_REAL - $$   
  87.     mov ax,Selector_Normal   
  88.     mov ds,ax   
  89.     mov es,ax   
  90.     mov ss,ax   
  91.     mov sp,[w_SP_Value_In_Real_Mode]   
  92.     mov gs,ax   
  93.     mov fs,ax   
  94.        
  95.     mov eax,cr0   
  96.     and al,11111110b   
  97.     mov cr0,eax   
  98.        
  99. LABEL_GO_BACK_TO_REAL:   
  100.     jmp 0:LABEL_ALREADY_REAL   
  101.        
  102. LABEL_ALREADY_REAL:   
  103.     in  al,92h   
  104.     and al,11111101b   
  105.     out 92h,al   
  106.        
  107.     sti   
  108.        
  109.     mov ax,4c00h   
  110.     int 21h   
  111.       
  112. [section .s32]       
  113. [bits 32]       
  114. LABEL_CODE32:       
  115.   ;mov ax,Selector_Video       
  116.   ;mov gs,ax       
  117.   ;mov ah,0ch       
  118.   ;mov al,'x'      
  119.   ;mov [gs:80 * 10],ax       
  120.      
  121.   mov   ax,Selector_Stack32   
  122.   mov   ss,ax   
  123.   mov   esp,Stack_Len   
  124.      
  125.   mov   ax,Selector_Data   
  126.   mov   ds,ax   
  127.   mov   ax,Selector_Video   
  128.   mov   gs,ax   
  129.   mov   al,4   
  130.   call  Disp_Al   
  131.   call  Disp_Return   
  132.   mov   al,14   
  133.   call  Disp_Al   
  134.      
  135.   jmp   Selector_Code16:LABEL_PREPARE_GO_BACK_TO_REAL    
  136.   
  137. Disp_Al:   
  138.     push    ebp   
  139.     mov ebp,esp   
  140.        
  141.     push    esi   
  142.     push    ecx   
  143.     push    eax   
  144.     mov ecx,2   
  145.     shr al,4   
  146.        
  147. .loop:   
  148.     and al,0fh   
  149.     cmp al,9   
  150.     jb  .1   
  151.     add al,7   
  152.        
  153. .1:   
  154.     add al,30h   
  155.     mov esi,[d_Disp_Pos]   
  156.     mov byte [gs:esi],al   
  157.     mov byte [gs:esi + 1],0ch   
  158.     add dword [d_Disp_Pos],2   
  159.        
  160.     dec ecx   
  161.     cmp ecx,0   
  162.     je  .2   
  163.     pop eax   
  164.     jmp .loop   
  165.        
  166. .2:   
  167.     pop ecx   
  168.     pop esi   
  169.     pop ebp   
  170.     ret   
  171.        
  172. Disp_Return:   
  173.     push    ebp   
  174.     mov ebp,esp   
  175.     push    eax   
  176.     push    ebx   
  177.        
  178.     mov eax,[d_Disp_Pos]   
  179.     mov bl,160   
  180.     div bl   
  181.     and ax,0ffh   
  182.     inc ax   
  183.     mov bl,160   
  184.     mul bl   
  185.     mov [d_Disp_Pos],eax   
  186.        
  187.     pop ebx   
  188.     pop eax   
  189.     pop ebp   
  190.     ret   

     运行结果如下: