依旧直接贴代码:
%macro Descriptor 3 dw %2 & 0FFFFh ; 段界限 1 (2 字节) dw %1 & 0FFFFh ; 段基址 1 (2 字节) db (%1 >> 16) & 0FFh ; 段基址 2 (1 字节) dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ; 属性 1 + 段界限 2 + 属性 2 (2 字节) db (%1 >> 24) & 0FFh ; 段基址 3 (1 字节) %endmacro ; 共 8 字节 SA_RPL0 EQU 0 ; ┓ SA_RPL1 EQU 1 ; ┣ RPL SA_RPL2 EQU 2 ; ┃ SA_RPL3 EQU 3 ; ┛ DA_32 EQU 4000h ; 32 位段 DA_LIMIT_4K EQU 8000h ; 段界限粒度为 4K 字节 DA_DPL0 EQU 00h ; DPL = 0 DA_DPL1 EQU 20h ; DPL = 1 DA_DPL2 EQU 40h ; DPL = 2 DA_DPL3 EQU 60h ; DPL = 3 DA_DR EQU 90h ; 存在的仅仅读数据段类型值 DA_DRW EQU 92h ; 存在的可读写数据段属性值 DA_DRWA EQU 93h ; 存在的已訪问可读写数据段类型值 DA_C EQU 98h ; 存在的仅仅运行代码段属性值 DA_CR EQU 9Ah ; 存在的可运行可读代码段属性值 DA_CCO EQU 9Ch ; 存在的仅仅运行一致代码段属性值 DA_CCOR EQU 9Eh ; 存在的可运行可读一致代码段属性值 ; GDT ---------------------------------------------------------------------------------------------------------------------------------------------------- ; 段基址 段界限 , 属性 LABEL_GDT: Descriptor 0, 0, 0 ; 空描写叙述符 LABEL_DESC_FLAT_C: Descriptor 0, 0fffffh, DA_CR | DA_32 | DA_LIMIT_4K ; 0 ~ 4G LABEL_DESC_FLAT_RW: Descriptor 0, 0fffffh, DA_DRW | DA_32 | DA_LIMIT_4K ; 0 ~ 4G LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW | DA_DPL3 ; 显存首地址 ; GDT ---------------------------------------------------------------------------------------------------------------------------------------------------- GdtLen EQU $ - LABEL_GDT GdtPtr DW GdtLen - 1 ; 段界限 DD 0x80000 + LABEL_GDT ; GDT 选择子 ---------------------------------------------------------------------------------- SelectorFlatC EQU LABEL_DESC_FLAT_C - LABEL_GDT SelectorFlatRW EQU LABEL_DESC_FLAT_RW - LABEL_GDT SelectorVideo EQU LABEL_DESC_VIDEO - LABEL_GDT + SA_RPL3 ; GDT 选择子 ---------------------------------------------------------------------------------- ;准备切换到保护模式 MOV EAX,CR0 ;获取CR0配置 OR EAX,1 ;改动CR0的第0位(PE位),进入保护模式 MOV <span style="white-space:pre"> </span>CR0,EAX ;将改动过的配置送回CR0 JMP dword SelectorFlatC:(0x80000 + Protect_Mode)
[bits 32] Protect_Mode: MOV AX,SelectorFlatRW MOV DS,AX MOV SS,AX MOV ES,AX MOV FS,AX MOV AX,SelectorVideo MOV GS,AX JMP $
嗯哼,读者朋友一定会问:前面那一大段干虾米的?
答:那但是著名的段选择子,那但是要载入到各个段里面的,在这里不细说,我们将在下一篇文章来讨论段选择子。当然,前面另一个数据结构,类似于C语言中的struct,这是为了简化程序,用最简便的方法来完毕传递參数。
废话不多说,直接进入正题。在这里介绍一下CR0
CR0,用于控制和确定处理器的操作模式以及当前运行任务的特性,(事实上CR0——CR3都是),我们在这里不介绍其它位,仅仅介绍PE位(保护模式位)。
我们先取出CR0的配置。然后用OR运算符把PE位(第0位)置1。接着把CR0的配置送回。
(完了?)当然没有。我们另一个历史性的JMP。
在这里笔者建议读者朋友看《X86汇编语言:从实模式到保护模式》(假设没有学习过长跳转)。为什么要看呢?由于接下来讲的可能读者朋友有些吸收不了。
JMP dword SelectorFlatC:(0x80000 + Protect_Mode)
如今我列出几个读者可能会问的问题。
一:为什么要加上那个0x80000:
答:由于Nasm和C语言的标识符默认没有加上当前的物理地址,(即把当前程序的開始地址当做0)
二:为什么要加上一个dword
答:我们先做一个实验,”JMP 8:0x12345678“。这一句没有加dword把。可是他执行出来的结果是”JMP 8:0x5678“。如今能明确为什么要加上dword了吧。
三:为什么前面有SelectorFlatC标识符呢?
答:我们在后面会讲到,这是个段选择子。
四:为什么一定要有这个JMP呢?
答:由于这个JMP(事关重大太严重了。嘻嘻)一要更新CS,二要跳转到32位段。三。
。。。
就这么简单。
假设还有问题能够联系我:Email:2608184397@qq.com
假设读者朋友也有开发操作系统的想法,能够联系我。