认识保护模式
为什么需要保护模式
- Intel 8086是16位CPU,它有着16位的寄存器,16位的数据总线以及20位的地址总线和1MB的寻址能力。
 - 从80386开始CPU进入32位时代,寻址能力达到4GB,无法使用16位寄存器完成寻址
 
GDT(global descriptor table)
而保护模式下,虽然段值仍然由原来16位的cs、ds等寄存器表示,但此时它仅仅变成了一个索引,这个索引指向一个数据结构的一个表项,表项中详细定义了段的起始地址、界限、属性等内容。这个数据结构,就是GDT.
描述符(Descriptor)
GDT的作用是用来提供段式存储机制,这种机制是通过段寄存器和GDT中的描述符共同提供的。
描述符的结构

- 段基址:规定线性地址空间中段的开始地址。在保护模式下,段基地址长32位。因为基地址长度与寻址地址的长度相同,所以段基地址可以是 0~4GB 范围内的任意地址,而不像实方式下规定的边界必须被16整除。
 - 段界限:段界限规定段的大小。在保护模式下,段界限决定了偏移量的最大值。对于向下扩展的段,如堆栈段来说, 段界限决定了偏移量的最小值。
 
[SECTION .gdt]
; GDT
;                              段基址 ,       段界限     , 属性
LABEL_GDT:	   Descriptor       0    ,            0	   , 0           ; 空描述符
LABEL_DESC_CODE32: Descriptor       0, SegCode32Len - 1, DA_C + DA_32; 非一致代码段
LABEL_DESC_VIDEO:  Descriptor 0B8000h,           0ffffh, DA_DRW	     ; 显存首地址
; GDT 结束
GdtLen		equ	$ - LABEL_GDT	; GDT长度
GdtPtr		dw	GdtLen - 1	; GDT界限
		dd	0		; GDT基地址
; GDT 选择子
SelectorCode32		equ	LABEL_DESC_CODE32	- LABEL_GDT
SelectorVideo		equ	LABEL_DESC_VIDEO	- LABEL_GDT
; END of [SECTION .gdt]
 
在pmtest1.asm中定义了如上的GDT,包括:
- 三个描述符:
空描述符非一致代码段显存首地址,并赋予段基址段界限属性的初值 - GDT长度
 - GDT指针:包括GDT界限和GDT基地址
 - GDT选择子:包括
非一致代码段选择子显存首地址选择子 
选择子(selector)

; GDT 选择子
SelectorCode32		equ	LABEL_DESC_CODE32	- LABEL_GDT
SelectorVideo		equ	LABEL_DESC_VIDEO	- LABEL_GDT
 
对于上面的代码片段,在Ti和RPL都是0的时候,选择子可以看做是描述符相对于GDT基地址的偏移
段式寻址
理解[SECTION .s16]程序的关键是要明白此时CPU仍然处于实模式,此时最大的寻址范围仍然是1M,因此可以用cs寄存器存储
[SECTION .s16]
[BITS	16]
LABEL_BEGIN:
	mov	ax, cs				;cs为0
	mov	ds, ax
	mov	es, ax
	mov	ss, ax
	mov	sp, 0100h
 
这一节程序是用cs中存储的段基址(即LABEL_BEGIN标签在内存中对应的地址)初始化ds es ss寄存器,并初始化sp寄存器
	; 初始化 32 位代码段描述符
	xor	eax, eax
	mov	ax, cs
	shl	eax, 4				  ;段基址(CS)左移四位
	add	eax, LABEL_SEG_CODE32 ;加上偏移地址(LABEL_SEG_CODE32)
 
这段程序利用如下公式算出LABEL_SEG_CODE32真实的逻辑地址并存储在eax中(此时仍然处于实模式,计算机的最大寻址空间仍然是1M)
  
      
       
        
        
          逻辑地址 
         
        
          = 
         
        
          段基址 
         
        
          ∗ 
         
        
          16 
         
        
          + 
         
        
          偏移地址 
         
        
       
         逻辑地址=段基址*16+偏移地址 
        
       
     逻辑地址=段基址∗16+偏移地址
	;对照图3.4阅读
	mov	word [LABEL_DESC_CODE32 + 2], ax;BYTE2,3
	shr	eax, 16
	mov	byte [LABEL_DESC_CODE32 + 4], al;BYTE4
	mov	byte [LABEL_DESC_CODE32 + 7], ah;BYTE7
 
这段程序则是将eax(LABEL_SEG_CODE32的真实逻辑地址)赋值给BYTE2 BYTE3 BYTE4 BYTE7作为保护模式下描述符LABEL_DESC_CODE32的基地址
	; 为加载 GDTR 作准备
	xor	eax, eax
	mov	ax, ds					;ds = cs
	shl	eax, 4
	add	eax, LABEL_GDT			; eax <- gdt 基地址
	mov	dword [GdtPtr + 2], eax	; GDT基地址([GdtPtr + 2]) <- gdt 基地址
 
这段程序是将GDT的信息写入GDTPtr指针
首先计算出LABEL_GDT对应的内存地址,然后将GdtPtr对应的基地址赋值为LABEL_GDT的真实的内存地址
	lgdt	[GdtPtr]
 
这段程序是将GdtPtr加载到gdtr寄存器当中,gdtr寄存器的格式如下图所示

	; 在保护模式下,如果不关终端程序会发生错误
	cli
	; 打开地址线A20,才能访问所有的内存空间
	in	al, 92h
	or	al, 00000010b
	out	92h, al
 
- 问:什么是A20?
 - 答:这是一个历史问题。 
  
- 8086中,“段:偏移”这样的模式能表示的最大内存是FFFF:FFFF,即10FFEFh。可是8086只有20位的地址总线,只能寻址到1MB,那么如果试图访问超过1MB的地址时会怎样呢?实际上系统并不会发生异常,而是回卷(wrap)回去,重新从地址零开始寻址。可是,
 - 到了80286时,真的可以访问到1MB以上的内存了,如果遇到同样的情况,系统不会再回卷寻址,这就造成了向上不兼容.
 - 为了保证百分之百兼容,IBM想出一个办法,使用8042键盘控制器来控制第20个(从零开始数)地址位,这就是A20地址线,如果不被打开,第20个地址位将会总是零。
 
 
	; 准备切换到保护模式
	mov	eax, cr0
	or	eax, 1
	mov	cr0, eax
	; 真正进入保护模式
	jmp	dword SelectorCode32:0	; 执行这一句会把 SelectorCode32 装入 cs,
								; 并跳转到 Code32Selector:0  处
 
这段程序首先通过控制cr0寄存器的第0位(PE标志位:1为实模式,0为保护模式)切换到保护模式
然后使用jmp dword SelectorCode32:0跳转到实模式,将SelectorCode32即LABEL_SEG_CODE32标志的真实物理地址当做保护模式的段基址


















