学习笔记:一个操作系统的实现--认识保护模式

; ==========================================
; pmtest1.asm
; 编译方法:nasm pmtest1.asm -o pmtest1.bin
; ==========================================

%include	"pm.inc"	; 常量, 宏, 以及一些说明

org	07c00h
	jmp	LABEL_BEGIN

[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]

[SECTION .s16]
[BITS	16]
LABEL_BEGIN:
	mov	ax, cs
	mov	ds, ax
	mov	es, ax
	mov	ss, ax
	mov	sp, 0100h

	; 初始化 32 位代码段描述符
	xor	eax, eax
	mov	ax, cs
	shl	eax, 4
	add	eax, LABEL_SEG_CODE32
	mov	word [LABEL_DESC_CODE32 + 2], ax
	shr	eax, 16
	mov	byte [LABEL_DESC_CODE32 + 4], al
	mov	byte [LABEL_DESC_CODE32 + 7], ah

	; 为加载 GDTR 作准备
	xor	eax, eax
	mov	ax, ds
	shl	eax, 4
	add	eax, LABEL_GDT		; eax <- gdt 基地址
	mov	dword [GdtPtr + 2], eax	; [GdtPtr + 2] <- gdt 基地址

	; 加载 GDTR
	lgdt	[GdtPtr]

	; 关中断
	cli

	; 打开地址线A20
	in	al, 92h
	or	al, 00000010b
	out	92h, al

	; 准备切换到保护模式
	mov	eax, cr0
	or	eax, 1
	mov	cr0, eax

	; 真正进入保护模式
	jmp	dword SelectorCode32:0	; 执行这一句会把 SelectorCode32 装入 cs,
					; 并跳转到 Code32Selector:0  处
; END of [SECTION .s16]


[SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS	32]

LABEL_SEG_CODE32:
	mov	ax, SelectorVideo
	mov	gs, ax			; 视频段选择子(目的)

	mov	edi, (80 * 11 + 79) * 2	; 屏幕第 11 行, 第 79 列。
	mov	ah, 0Ch			; 0000: 黑底    1100: 红字
	mov	al, 'P'
	mov	[gs:edi], ax

	; 到此停止
	jmp	$

SegCode32Len	equ	$ - LABEL_SEG_CODE32
; END of [SECTION .s32]

       11行中[SECTION .gdt]  表示一个段,通过[SECTION .###]将程序分成几个模块,使得程序结构更为清晰。

       14-16行是全局描述符表GDT,其中包含三个描述符。

       20行的结构与全局描述符表寄存器GDTR对应的数据结构一致,包含16位的界限和32位基地址。

       38-45行用于初始化代码段描述符,确切地说是初始化代码段描述符中的基地址部分,其基地址部分是第2、3、4、7几个字节。到此为止,描述符LABEL_DESC_CODE32对应的即为[SECTION .s32]段的信息。

      48-55行中将全局描述符表GDT的信息填充GdtPtr数据结构,并用lgdt载入GDTR中。

      58行关中断是因为保护模式下的中断机制发生了变化,不关中断会发生异常。

      61-63行用于打开地址线A20。这是历史问题,在8086中只有20位地址线,只能寻址1MB,如果试图访问超过1MB的地址时,会重新从地址开始寻址,造成兼容性问题。这里使用8042键盘控制器来控制A20地址位,默认情况是关闭的,现在将其打开。

      67-69行中的cr0是内部寄存器,cr0的0位是PE位,当PE=0时,CPU为实模式,当PE=1时,CPU为保护模式,这三行代码就是修改CPU的运行模式为保护模式。也就是说,这三行代码之后的代码要根据保护模式的规矩办事了。

      71行的dword是修饰偏移地址0的,告诉CPU启用32位程序计数器EIP。同时,装入CS的SelectorCode32是选择子,执行Jmp之后,系统将该选择子对应的描述符中的段基址装入隐藏在CS之后的寄存器中。

总结:

      进入保护模式的步骤如下:

      1  构筑GDT表项,并建立选择子对应关系,初始化段描述符。

      2  准备GDT信息,并用lgdt加载入GDTR

      3  打开A20

      4  置cr0的PE位,跳转进入保护模式

 

      就本节而言,保护模式的保护体现在段界限、段属性中,应该有所体会。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值