实模式跳转保护模式,代码全注释

最近正在阅读《orange's,一个操作系统的实现》,真是相见恨晚啊,那么多年就没有一本真正深入浅出弄点代码让我们入门和实践一下的书,感谢作者于渊。

下面是第3章pmtest1.asm的全注释

总结执行过程如下:

  1. 初始化32位代码段的段基址,并存储到GDT中对应的描述符中
  2. 准备GDT的基地址,并通过LGDT指令加载到GDTR寄存器中
  3. cli关中断
  4. 打开地址线A20
  5. 将CR0寄存器的PE位置1,进入保护模式
  6. 已经进入保护模式,跳转到32位代码段继续执行
  7. 写显存
  8. game over
  1. ; ==========================================  
  2. ; pmtest1.asm  
  3. ; 编译方法:nasm pmtest1.asm -o pmtest1.bin  
  4. ; ==========================================  
  5.   
  6. %include "pm.inc" ; 常量, 宏, 以及一些说明  
  7.   
  8. org 07c00h  
  9. jmp LABEL_BEGIN  
  10.   
  11.   
  12. ; 用户自定义段  
  13. [SECTION .gdt]  
  14. ; GDT  
  15. ; 段基址, 段界限 , 属性  
  16. LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符  
  17. LABEL_DESC_CODE32: Descriptor 0, SegCode32Len - 1, DA_C + DA_32; 非一致代码段  
  18. LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ; 显存首地址  
  19. ; GDT 结束  
  20.   
  21. GdtLen equ $ - LABEL_GDT ; GDT长度  
  22. GdtPtr dw GdtLen - 1 ; GDT界限  
  23.        dd 0 ; GDT基地址  
  24.   
  25.   
  26.   
  27. ; GDT 选择子  
  28. ; 选择子其实就是GDT中的偏移,因为GDT的每项都是8字节,因此选择子的低3位肯定为0  
  29. SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT  
  30. SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT  
  31. ; END of [SECTION .gdt]  
  32.   
  33. [SECTION .s16]  
  34. [BITS 16]  
  35. LABEL_BEGIN:  
  36.     ; 下面这几行代码貌似没有实际意义的说,尤其是那个0100h,不是DOS PSP的偏移嘛。  
  37.     ; 估计就是保护现场框架代码,哪位知道可以告诉我。  
  38.     mov ax, cs  
  39.     mov ds, ax  
  40.     mov es, ax  
  41.     mov ss, ax  
  42.     mov sp, 0100h  
  43.   
  44.   
  45.     ; 初始化 32 位代码段描述符  
  46.     ; 将32位代码段的基地址存到对应的描述符  
  47.     xor eax, eax  
  48.   
  49.     ; 代码段左移4位 + 32位代码段的偏移 = 32位代码段的段起始物理地址(段基址)  
  50.     mov ax, cs  
  51.     shl eax, 4  
  52.     add eax, LABEL_SEG_CODE32  
  53.   
  54.     ; 如果使用纯段式内存管理,段描述符中的地址就是段的物理地址  
  55.     ; 描述符低32位  
  56.   
  57.     ; 31      16 15       0  
  58.     ; | 段基址 | | 段界限 |  
  59.     ; 存储32位段基址的低16位  
  60.     mov word [LABEL_DESC_CODE32 + 2], ax  
  61.   
  62.     ; 存储32位段基址的高16位  
  63.     shr eax, 16  
  64.     mov byte [LABEL_DESC_CODE32 + 4], al  
  65.     mov byte [LABEL_DESC_CODE32 + 7], ah  
  66.   
  67.     ; 为加载 GDTR 作准备  
  68.     ; GDTR寄存器(48bit)中存储的是:GDT的线性基地址和GDT的界限  
  69.     ; 将GDT的地址存入GdtPtr  
  70.     ; GdtPtr对应48bit的GDTR寄存器,  
  71.     ; 47                 16 15        0  
  72.     ; | GDT32位线性基地址 | | GDT界限 |  
  73.     xor eax, eax  
  74.   
  75.     ; GDT在数据段中  
  76.     mov ax, ds  
  77.     shl eax, 4  
  78.     add eax, LABEL_GDT ; eax <- gdt 基地址  
  79.     mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基地址  
  80.   
  81.     ; 加载 GDTR  
  82.     ; 将GdtPtr指向的6个字节拷贝到GDTR寄存器中  
  83.     lgdt [GdtPtr]  
  84.   
  85.     ; 关中断  
  86.     cli  
  87.   
  88.     ; 打开地址线A20  
  89.     in al, 92h  
  90.     or al, 00000010b  
  91.     out 92h, al  
  92.   
  93.     ; 准备切换到保护模式  
  94.     ; cr0的第0位PE,PE=0为实模式,PE=1为保护模式  
  95.     mov eax, cr0  
  96.     or eax, 1  
  97.       
  98.     ; 一旦将cr0中的PE位设置为1,就进入了保护模式!  
  99.     ; 也就是说,执行完下面这句,我们就已经进入保护模式了  
  100.     mov cr0, eax  
  101.   
  102.     ; 真正进入保护模式  
  103.     ; 虽然已经进入保护模式,但我们现在还在16位代码段中,因为CS仍然是16位代码段  
  104.     ; 的基地址(如果我们此时执行不带段前缀的JMP指令,CS不会被解释为段基址,  
  105.     ; 而是被解释为GDT的选择子,不要忘记我们已经在保护模式中了),  
  106.     ; 因此我们必须跳入32位代码段,跳入以后,CS中保存的就是GDT的选择子了.  
  107.     ; 必须加上dword,因为我们还在16位代码段中,偏移也是16位的。  
  108.     ; 如果偏移不是0,而是0x12345678,则高位的0x1234会被截断  
  109.     jmp dword SelectorCode32:0 ; 执行这一句会把 SelectorCode32 装入 cs,并跳转到 Code32Selector:0 处  
  110.   
  111.     ; END of [SECTION .s16]  
  112.   
  113. [SECTION .s32]; 32 位代码段. 由实模式跳入.  
  114. [BITS 32]  
  115. LABEL_SEG_CODE32:  
  116.     mov ax, SelectorVideo  
  117.     mov gs, ax ; 视频段选择子(目的)  
  118.   
  119.     mov edi, (80 * 11 + 79) * 2 ; 屏幕第 11 行, 第 79 列。  
  120.     mov ah, 0Ch ; 0000: 黑底 1100: 红字  
  121.     mov al, 'P'  
  122.     mov [gs:edi], ax  
  123.   
  124.     ; 到此停止  
  125.     jmp $  
  126.   
  127.     SegCode32Len equ $ - LABEL_SEG_CODE32  
  128.   
  129. ; END of [SECTION .s32]  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值