问题1、怎么理解界限 = 大小 - 1?
数组
界限就是边界、分界线,就是终止的地方,内存的界限就是一段内存的终址相对于基址的偏移号(不是偏移量,偏移号 = 偏移量 -1)。数据结构
为何要减一?由于内存地址编号从 0 开始,第一个内存地址对应内存地址编号 0 ,第 n 个内存地址对应内存地址编号 n - 1。内存地址序号的上限减一对应内存地址编号的上限,而内存地址的最大序号就是这段内存的大小,即一段内存的大小 - 1 = 这段内存的界限。ide
问题二:描述符是什么?学习
不要被“符”字迷惑了,说白了“符”就是个数据结构(在 C语言里就是个 struct)。“描述”两个字从字面理解就行,就是对一个东西进行详细说明。咱们这里学习的保护模式,描述的就是内存的分段管理模式。那么“描述符”就是说明一段内存的详细状况的数据结构。实际上,描述符就是定义一段内存的起始地址、界限、访问属性的一个数据结构,描述符表就是一个由描述符为元素组成的数组,看起来像一个表格而已。spa
问题三:GDT 表第一个为何要留空呢?code
由于选择子的结构是这样的:orm
; ┏━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┓
索引
; ┃15┃14┃13┃12┃11┃10┃09┃08┃07┃06┃05┃04┃03┃02┃01┃0 ┃ip
; ┣━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━╋━━╋━━┻━━┫内存
; ┃ 描述符索引 ┃TI ┃ RPL ┃
; ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━┻━━━━━┛
选择子中正真索引 GDT 表的索引号只在高 13 位,低 3 位要留给 TI 和 RPL,即索引号实际上从第 3 位开始,就是0b0000000000001000 = 8;索引号每次增长的值(步进)也不是 1 ,而是 0b1000 = 8 。
恰好 GDT 表中,一个描述符也是占用 8 个字节。
如今简单了:把第一个描述符留空不用,只是占据 8 个字节的空间,在 TI 和 RPL 都等于 0 的状况下,选择子就能够简单等同于描述符在 GDT 表中的偏移了!即:
第一个选择子 = 一号描述符在 GDT 表中的偏移 = 8
第 n 个选择子 = n 号描述符在 GDT 表中的偏移 = n * 8
打开地址线 A20、打开保护模式标志都是固定格式,照抄就行。lgdt 和 cli 是 CPU 指令,看看其指令说明就行。再来读下面的代码,就没啥难度了。
; TestPM.nas ; 初识保护模式 ; 四彩 ; 2015-11-15 ; ======================================================================================== ; ---------------------------------------------------------------------------------------- org 0x7C00 jmp Label_RM_main ; **************************************************************************************** ; ======================================================================================== %include "./INC/Descriptor.inc" ; ; ---------------------------------------------------------------------------------------- ; GDT 段基址 段界限 属性 Label_Desc_Empty : Descriptor 0, 0, 0 ; 空描述符 Label_Desc_PM : Descriptor 0, 0xFFFF, DA_32 + DA_CS_E ; 保护模式代码段描述符 Label_Desc_Video : Descriptor 0xB8000, 0xFFFF, DA_DS_RW ; 显存段描述符 ; ; ---------------------------------------------------------------------------------------- ; GDTPtr GDTLen equ $ - Label_Desc_Empty GDTPtr dw GDTLen - 1 ; 界限 dd 0 ; 基址 ; ; ---------------------------------------------------------------------------------------- ; 选择子 SelectorPM equ Label_Desc_PM - Label_Desc_Empty SelectorVideo equ Label_Desc_Video - Label_Desc_Empty ; ; **************************************************************************************** [BITS 16] ; ======================================================================================== ; 实模式下开启保护模式 ; ---------------------------------------------------------------------------------------- ; 程序入口,实模式代码段 Label_RM_main: ; 填上保护模式代码段描述符的基址(界限、属性在定义时已初始化) xor eax, eax mov ax, cs shl eax, 4 add eax, Label_PM_main ; 保护模式程序入口的绝对内存地址,即为其基址 mov word[Label_Desc_PM + 2], ax ; 拆分红 3 部分存入相应位置 shr eax, 16 mov byte[Label_Desc_PM + 4], al mov byte[Label_Desc_PM + 7], ah ; 填上 GDTPtr 的基址(界限在定义时已初始化) xor eax, eax mov ax, cs shl eax, 4 add eax, Label_Desc_Empty mov dword[GDTPtr + 2], eax ; 加载 GDT lgdt [GDTPtr] ; 屏蔽中断 cli ; 打开地址线 A20 in al, 0x92 or al, 0b10 out 0x92, al ; 打开保护模式标志 mov eax, cr0 or eax, 1 mov cr0, eax ; 修改 CS : EIP jmp dword SelectorPM : 0 ; **************************************************************************************** [BITS 32] ; ======================================================================================== ; 保护模式代码段,由实模式跳入 Label_PM_main: mov ax, SelectorVideo mov gs, ax mov edi, (80 * 5 + 35) * 2 mov ah, 0xC mov al, 'O' mov [gs : edi], ax mov al, 'K' mov [gs : edi + 2], ax jmp $ ; **************************************************************************************** ; ======================================================================================== ; FAT12 文件系统引导扇区引导代码的剩余部分用 0 填满 times 510 - ($ - $$) db 0 ; **************************************************************************************** ; ======================================================================================== ; FAT12 文件系统引导扇区的的结束标志(最后 2 字节,必须是 0xAA55) dw 0xAA55 ; **************************************************************************************** ; ======================================================================================== CodeLenOfPM equ $ - Label_PM_main ; ****************************************************************************************