[Intel汇编-NASM]进入保护模式全过程

enter_pm.mbr

  1.             org     0x7C00          ; 该命令表示程序将被装在到偏移地址为0x7C00的地方  
  2.                                     ; 该命令效果是全局的,但只能使用一次,之后不得再用  
  3.                                     ; 从该位置开始到整个源代码结束之间的所有标号在被访问时都会隐式地自动加上0x7C00  
  4.                                     ; 但是和vstart=0x7C00不同,vstart会将整个段内所有指令的汇编地址都加上0x7C00  
  5.                                     ; 而org不影响汇编地址,仅仅就是在访问标号的时候临时加一个0x7C00  
  6.                                     ; 并将这个临时的和作为访问结果返回  
  7.   
  8.             jmp     start  
  9.   
  10. GDT_BEG: ; GDT表的定义  
  11. DESC_SG_NULL    dd  0x00000000, 0x00000000  
  12. DESC_SG_CODE    dd  0x7C0001FF, 0x00409A00 ; TYPE=1010,代码段必须可读,否则msg中的内容是无法读出并写到显卡中的  
  13. DESC_SG_VIDEO   dd  0x8000FFFF, 0x0040920B  
  14. DESC_SG_STACK   dd  0x00007A00, 0x00409600  
  15. GDT_END:  
  16.   
  17. ; 段选择子  
  18. SLCT_NULL       equ DESC_SG_NULL - GDT_BEG  
  19. SLCT_CODE       equ DESC_SG_CODE - GDT_BEG  
  20. SLCT_VIDEO      equ DESC_SG_VIDEO - GDT_BEG  
  21. SLCT_STACK      equ DESC_SG_STACK - GDT_BEG  
  22.   
  23. ; GDT总共有多少个双字  
  24. GDT_SIZE_DWORD  equ (GDT_END - GDT_BEG) / 4  
  25.   
  26. GDTR:   ; 以下48位内容需要加载到全局描述符表寄存器gdtr中  
  27. GDT_BOUND       dw  GDT_END - GDT_BEG - 1   ; 低16位是GDT的界限(即GDT总共多少字节)  
  28.                                             ; 如果把GDT看做以字节为单位的数组,则下标从0开始  
  29.                                             ; 所以该项为GDT总字节数减1  
  30.                                             ; 本程序总共有4个描述符,GDT每个表项都占8字节  
  31. GDT_BASE        dd  0x7E00          ; GDT加载在内存中的起始物理地址  
  32.                                     ; 由于在加载GDTR时还没有进入保护模式,因此只能用20位地址  
  33.                                     ; 这里就只能用32位来表示20位地址了  
  34.                                     ; 因此在进入保护模式前只能将GDT加载到1MB的内存空间中  
  35.                                     ; 在进入保护模式之后可以再将GDT移动到其它位置  
  36.                                     ; 由于MBR位于0x7C00开始的512字节空间中了  
  37.                                     ; 因此就将GDT放在后面一个新的512字节处(7E00H = 7C00H + 512)  
  38.   
  39. PORT_FAST_A20   equ 0x92            ; 快速设置A20地址线的端口  
  40. GATE_ALT_A20    equ 0x02            ; 开启A20地址线的位掩码(门控),也称作A20替代门控  
  41.                                     ; 即该8位端口的1号位置1即可打开A20地址线  
  42.   
  43. GATE_PE         equ 0x01            ; 控制寄存器(32位)的第0位——保护模式允许位(Protection Enable)  
  44.                                     ; 也称作PE门控  
  45.                                     ; 将其设置为1就真正进入保护模式了  
  46.                                     ; 从此之后都要按照保护模式的规矩来了  
  47.                                     ; 这是真正的保护模式的开关  
  48. ; 程序开始  
  49. start:      mov     ax, cs  
  50.             mov     ss, ax  
  51.             mov     sp, 0x7C00  
  52.   
  53.             ; ds:si指向本代码中的临时GDT  
  54.             mov     ax, cs  
  55.             mov     ds, ax  
  56.             mov     si, GDT_BEG  
  57.   
  58.             ; es:di指向GDT实际加载的位置0x7E00:0  
  59.             mov     ax, [cs: GDT_BASE]  
  60.             mov     dx, [cs: GDT_BASE+2]  
  61.             mov     bx, 16  
  62.             div     bx                      ; 获取实际物理地址对应的16位段地址  
  63.             mov     es, ax  
  64.             mov     di, 0  
  65.   
  66.             ; 将GDT加载到指定位置  
  67.             mov     cx, GDT_SIZE_DWORD  
  68.             cld  
  69.             rep     movsd  
  70.   
  71.             lgdt    [cs: GDTR]              ; load gdtr,将GDTR处的48位内容加载进gdtr中  
  72.   
  73.             ; 由于实模式下地址只有20位,为了使20位地址溢出时归0并产生进位CF就必须关闭  
  74.             ; 第21根地址线即A20(Address #20),如果不关闭则20位地址溢出时的进位会  
  75.             ; 出现在A20处而不产生CF进位标记,同时不能归0  
  76.             ; 因此在进入32位保护模式之前必须先开启A20(实模式下A20不工作,即一直为0)  
  77.             ; 从而使32位的每一位都能工作成为真正意义上的32位模式  
  78.             in      al, PORT_FAST_A20  
  79.             or      al, GATE_ALT_A20  
  80.             out     PORT_FAST_A20, al  
  81.   
  82.             ; 保护模式下段的定位和实模式不同  
  83.             ; 因此在保护模式下所有BIOS中断都不能使用(跳转到中断例程时需要定位)  
  84.             ; 因此在进入保护模式之后需要重新设置BIOS中断的定位  
  85.             ; 因此在这个问题解决之前不能相应任何中断  
  86.             ; 因此在进入保护模式之前到BIOS中断重新设置完毕的过程中必须要关中断以免发生未知异常  
  87.             cli         ; clear IF,将IF标志位(中断允许标志位)置0  
  88.                         ; 对应的sti,即set IF,将IF位置1重新允许中断  
  89.                         ; 由于本程序并不重新设置BIOS的定位,因此就无需sti  
  90.   
  91.             mov     eax, cr0            ; cr0是0号寄存器,还有cr1、cr2等  
  92.             or      eax, GATE_PE        ; 彻底进入保护模式  
  93.             mov     cr0, eax  
  94.   
  95.             ; 虽然是进入了保护模式,但cs的描述符高速缓冲寄存器中的内容还是20位模式下的内容  
  96.             ; 如果这个问题不解决会导致程序的错误,因此必须刷新cs  
  97.             ; 刷新的同时也会自动更新描述符高速缓冲寄存器,同时清空了流水线  
  98.             ; 只能通过转移、调用、返回、中断指令来修改cs  
  99.             jmp     dword SLCT_CODE:(pm32_start - 0x7C00)   ; 注意cs现在是段选择子  
  100.                                                             ; 由于所有标号被引用的时候会加0x7C00  
  101.                                                             ; 而段选择子中起始位置是从0x7C00开始算起的  
  102.                                                             ; 所以在这里跳转的时候偏移地址需要减去0x7C00  
  103.                                                             ; 使用dword主要是约束偏移地址,使之成为32位的  
  104.                                                             ; 这之后就会使用eip作为偏移指针了  
  105.     msg     db  'Already in protect mode...'  
  106.     len_msg equ $-msg  
  107.   
  108.             [bits 32]  
  109. pm32_start:  
  110.             mov     eax, SLCT_VIDEO             ; 段选择子  
  111.             mov     ds, eax  
  112.   
  113.             ; 显示msg  
  114.             mov     ebx, msg-0x7C00  
  115.             mov     esi, 0  
  116.             mov     edi, 0  
  117.     .lp:    mov     al, [cs: ebx+esi]  
  118.             mov     [edi], al  
  119.             inc     esi  
  120.             add     edi, 2  
  121.             loop    .lp  
  122.   
  123.   
  124.             ; 测试栈段  
  125.             mov     eax, SLCT_STACK  
  126.             mov     ss, eax  
  127.             mov     esp, 0x7C00  
  128.   
  129.             mov     ebp, esp        ; 备份esp  
  130.   
  131.             push    byte '!'        ; 立即数压入实验  
  132.             sub     ebp, 4  
  133.             cmp     ebp, esp        ; 考察一下压入的是否是32位数  
  134.             jne     .tail  
  135.             pop     eax             ; 事实证明32位模式下压入的立即数必定都是32为的  
  136.             mov     [0x1A], al  
  137.             mov     [0x1C], ' '     ; 抽马桶  
  138.   
  139.     .tail:  jmp     $  
  140.   
  141. times 510 - ($ - $$)    db  0  
  142.                         dw  0xAA55 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要下载NASM,需要按照以下步骤进行操作: 1. 打开您的Web浏览器并转到NASM官方网站(http://www.nasm.us/)。 2. 在网站的导航菜单中选择“Download”选项。 3. 在下载页面上,找到与您的操作系统相对应的NASM版本。例如,如果您的操作系统是Windows,选择适用于Windows的安装程序。 4. 单击选定的下载链接,然后保存安装程序到您的计算机上。 5. 找到保存的安装程序文件并双击打开它。 6. 按照安装向导的指示进行操作。对于Windows用户,可简单地按照默认选项进行安装。 7. 安装程序将自动下载和安装NASM到您的计算机中。 8. 安装完成后,您可以通过命令提示符或终端窗口执行NASM编译器。 要编写和编译x86从保护模式到实模式的代码,您需要一个文本编辑器来编写代码,以及NASM编译器将代码转换为可执行文件。在您下载并安装NASM之后,可以使用任何文本编辑器(如Notepad++、Sublime Text、Visual Studio等)打开并编写代码。然后,使用命令提示符或终端窗口将代码保存为以.asm为扩展名的文件,并使用NASM编译器编译它。 例如,假设您已经编写了一个名为example.asm的代码文件。在命令提示符或终端窗口中,导航到保存了example.asm文件的目录,并执行以下命令进行编译: ``` nasm -f bin example.asm -o example.bin ``` 上述命令会将example.asm文件编译为一个名为example.bin的可执行文件。现在,您可以执行该可执行文件来运行您的x86代码。 请注意,这里只提供了一种方式来下载和使用NASM编译器。如果您想了解更多关于x86从保护模式到实模式的编程知识,请参考相关的教程、书籍或在线资源。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值