《一个操作系统的实现》读书笔记--第三章--“实模式--保护模式--实模式”转换过程

下面以pmtest2.asm为例,来讲述“实模式--保护模式--实模式”的转换过程。

1、“实模式--保护模式--实模式”的转换过程。
2、介绍段描述符属性
3、pm.inc 中的宏定义
4、pmtest2.asm源代码

一、“实模式--保护模式--实模式”的转换过程
1、“实模式--保护模式”的跳转
(1)关中断
(2)打开地址线A20
(3)置cr0寄存器的末位为1
(4)实现跳转,进入到保护模式

2、“保护模式--实模式”的跳转
(1)从保护模式下的32位代码段跳转到16位代码段
(2)在16位代码段下初始化所有段寄存器
(3)置cr0的末位为0
(4)实现跳转,返回到实模式

3、pmtest2.asm中“实模式--保护模式--实模式”的跳转过程

(1)下面是“实模式--保护模式--实模式”的跳转过程图

《一个操作系统的实现》读书笔记--第三章--“实模式--保护模式--实模式”转换过程_第1张图片

其中 LABEL_BEGIN、LABEL_REAL_ENTRY运行于实模式下;LEBEL_SEG_CODE32、LABEL_SEG_CODE16运行于保护模式下。所以该程序实现了“实模式--保护模式--实模式”的跳转。

(2)“实模式--保护模式”的跳转

这个跳转过程主要存在于LABEL_BEGIN代码段中:
70- 71行:保存实模式下的SP内的值
73-111行:初始化段描述符
113-121行:加载GDTR
123-124行:关中断
126-129行:打开地址线A20
141-144行:准备切换到保护模式,置cr0的末位为1
146-147行:跳转到保护模式

(3)“保护模式--实模式”的跳转

208-209行:从保护模式下的32位代码段跳转到16位代码段
316-322行:为跳回实模式做准备,将段寄存器初始化为符合实模式下的代码段规范。即用SelectorNormal来初始化段寄存器。
324-326行:置cr0的末位为0
328-329行:实现跳转,进入到实模式下的 LABEL_REAL_ENTRY段
158-158行:恢复实模式下的SP
160-162行:关闭A20地址线
164-164行:开中断
166-168行:返回DOS

二、段描述符的属性

段描述符属性占5、6字节,其具体特性如下:

《一个操作系统的实现》读书笔记--第三章--“实模式--保护模式--实模式”转换过程_第2张图片

在这里主要介绍第5字节的内容。
1、 P:存在(Present)位。
1 表示段在内存中存在
0 表示段在内存中不存在
2、 DPL:表示描述符特权级(Descriptor Privilege level),共2位。
它规定了所描述段的特权级,用于特权检查,以决定对该段能否访问。
3、 S:说明描述符的类型。
1 数据段和代码段描述符
0系统段描述符和门描述符
4、 TYPE:说明存储段描述符所描述的存储段的具体属性
数据段类型:
类型值              说明
---------------------------------
0                只读
1                只读、已访问
2                读/写
3                读/写、已访问
4                只读、向下扩展
5                只读、向下扩展、已访问
6                读/写、向下扩展
7                读/写、向下扩展、已访问
代码段类型:
类型值              说明
---------------------------------
8                只执行
9                只执行、已访问
A                执行/读
B                执行/读、已访问
C                只执行、一致码段
D                只执行、一致码段、已访问
E                执行/读、一致码段
F                执行/读、一致码段、已访问
系统段类型:
类型编码         说明
----------------------------------
0                <未定义>
1                可用286TSS
2                LDT
3                忙的286TSS
4                286调用门
5                任务门
6                286中断门
7                286陷阱门
8                未定义
9                可用386TSS
A                <未定义>
B                忙的386TSS
C                386调用门
D                <未定义>
E                386中断门
F                 386陷阱门

三、pm.inc中的宏定义

在程序中,我们定义属性时,使用了pm.inc中的宏定义DA_DRW、DA_C、DA_32、DA_DRW、DA_DRWA。下面让我来向大家解释这些宏定义。
DA   : Descriptor Attribute
D    : 数据段
C    : 代码段
S    : 系统段
R    : 只读
RW   : 读写
A    : 已访问
-----------------------------------------------------------
DA_32               EQU 4000h       ; 32 位段
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  ; 存在的可执行可读一致代码段属性值
-----------------------------------------------------------
 系统段描述符类型值说明
-----------------------------------------------------------
DA_LDT             EQU   82h       ; 局部描述符表段类型值
DA_TaskGate    EQU   85h       ; 任务门类型值
DA_386TSS      EQU   89h       ; 可用 386 任务状态段类型值
DA_386CGate   EQU   8Ch       ; 386 调用门类型值
DA_386IGate     EQU   8Eh       ; 386 中断门类型值
DA_386TGate    EQU   8Fh       ; 386 陷阱门类型值

四、pmtest2.asm源代码:

;=====================================================
;pmtest2.asm
;编译方法:nasm pmtest2.asm -o pmtest2.com

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

org 0100h
    jmp LABEL_BEGIN

[SECTION .gdt]
;GDT
;				      段基址  段界限          段属性
LABEL_GDT:		Descriptor	0,	0,		0		;空描述符
LABEL_DESC_NORMAL:	Descriptor	0,	0ffffh,		DA_DRW		;Normal描述符
LABEL_DESC_CODE32:	Descriptor	0,	SegCode32Len-1,	DA_C + DA_32	;非一致代码段,32
LABEL_DESC_CODE16:	Descriptor	0,	0ffffh,		DA_C		;非一致代码段,16
LABEL_DESC_DATA:	Descriptor	0,	DataLen-1,	DA_DRW + DA_DPL1		;Data
LABEL_DESC_STACK:	Descriptor	0,	TopOfStack,	DA_DRWA + DA_32	;Stack,32位
LABEL_DESC_TEST:	Descriptor   0500000h,	0ffffh,		DA_DRW		;测试代码段
LABEL_DESC_VIDEO:	Descriptor   0B8000h,	0ffffh,		DA_DRW		;显存首地址
;GDT 结束

GdtLen	equ	$ - LABEL_GDT		;GDT长度
GdtPtr	dw	GdtLen - 1		;GDT界限
	dd	0			;GDT基地址

;GDT选择子
SelectorNormal		equ	LABEL_DESC_NORMAL - LABEL_GDT
SelectorCode32		equ	LABEL_DESC_CODE32 - LABEL_GDT
SelectorCode16		equ	LABEL_DESC_CODE16 - LABEL_GDT
SelectorData		equ	LABEL_DESC_DATA - LABEL_GDT + SA_RPL3
SelectorStack		equ	LABEL_DESC_STACK - LABEL_GDT
SelectorTest		equ	LABEL_DESC_TEST - LABEL_GDT
SelectorVideo		equ	LABEL_DESC_VIDEO - LABEL_GDT
;END of [SECTION .gdt]

[SECTION .data1]	;数据段
ALIGN 32
[BITS 32]
LABEL_DATA:
SPValueInRealMode	dw	0
;字符串
PMMessage:	db	"In Protect Mode now.",	0	;进入保护模式后显示该字符串
OffsetPMMessage	equ	PMMessage - $

StrTest:	db	"ABCDEFGHIJKLMNOPQRSTUVWXYZ",	0	
OffsetStrTest	equ	StrTest - $

DataLen		equ	$ - LABEL_DATA
;END of [SECTION .data1]
		
;全局堆栈段
[SECTION .gs]
ALIGN 32
[BITS 32]
LABEL_STACK:
	times 512 db 0
TopOfStack equ	$ - LABEL_STACK - 1
;END of [SECTION .gs]

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

     mov [LABEL_GO_BACK_TO_REAL + 3], ax
     mov [SPValueInRealMode], sp

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

     ;初始化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

     ;初始化数据段描述符
     xor eax, eax
     mov ax, ds
     shl eax, 4
     add eax, LABEL_DATA
     mov word[LABEL_DESC_DATA+2], ax
     shr eax, 16
     mov byte[LABEL_DESC_DATA+4], al
     mov byte[LABEL_DESC_DATA+7], ah

     ;初始化堆栈段描述符
     xor eax, eax
     mov ax, ds
     shl eax, 4
     add eax, LABEL_STACK
     mov word[LABEL_DESC_STACK+2], ax
     shr eax, 16
     mov byte[LABEL_DESC_STACK+4], al
     mov byte[LABEL_DESC_STACK+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 ah, 06H         ;功能06H和07H 
     mov ch, 00          ;功能描述:初始化屏幕或滚屏
     mov cl, 00          ;入口参数:AH=06H——向上滚屏,07H——向下滚屏
     mov dh, 24          ;AL=滚动行数(0——清窗口)
     mov dl, 79          ;BH=空白区域的缺省属性
     mov bh, 7           ;(CH、CL)=窗口的左上角位置(Y坐标,X坐标)
     mov al, 00          ;(DH、DL)=窗口的右下角位置(Y坐标,X坐标)
     int 10H             ;出口参数:无

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

     ;真正进入保护模式
     jmp dword SelectorCode32:0		;执行这一句会把SelectorCode32装入cs,并跳转到SelectorCode32:0处

;......................................................................
;,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,

LABEL_REAL_ENTRY:		;从保护模式跳回到实模式就到了这里
	mov ax, cs
	mov ds, ax
	mov es, ax
	mov ss, ax

	mov sp, [SPValueInRealMode]

	in al, 92h	;关闭A20地址线
	and al, 11111101b	
	out 92h, al

	sti		;开中断

	;回到DOS
	mov ax, 4C00h
	int 21h;
;END of [SECTION .s16]

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

LABEL_SEG_CODE32:
	mov ax, SelectorData
	mov ds, ax		;数据段选择子
	mov ax, SelectorTest
	mov es, ax		;测试段选择子
	mov ax, SelectorVideo	
	mov gs, ax		;视频段选择子

	mov ax, SelectorStack
	mov ss, ax		;堆栈段选择子
	mov esp, TopOfStack

	;下面显示一个字符串
	mov ah, 0Ch		;0000:黑底  1100:红字
	xor esi, esi
	xor edi, edi
	mov esi, OffsetPMMessage	;源数据偏移
	mov edi, (80*10+0)*2		;目的数据偏移。屏幕第10行,第0列
	cld
.1:
	lodsb
	test al, al
	jz .2
	mov [gs:edi], ax	;将ax中的数据送入到显存
	add edi, 2
	jmp .1
.2:			;显示完毕
	
	call DispReturn

	call TestRead
	call TestWrite
	Call TestRead

	;到此停止
	jmp SelectorCode16:0
;---------------------------------------------------------------------------

TestRead:
	xor esi, esi
	mov ecx, 8
.loop:
	mov al, [es:esi]
	call DispAL
	inc esi
	loop .loop

	call DispReturn

	ret
;TestRead结束--------------------------------------------------------------

;--------------------------------------------------------------------------
TestWrite:
	push esi
	push edi
	xor esi, esi
	xor edi, edi
	mov esi, OffsetStrTest		;源数据偏移

.1:
	lodsb
	test al, al
	jz .2
	mov [es:edi], al
	inc edi
	jmp .1
.2:
	pop edi
	pop esi

	ret

;TestWrite结束--------------------------------------------------------------

;---------------------------------------------------------------------------
;显示AL中的数字
;默认地:
;	数字已经存在AL中
;	edi始终指向要显示的下一个字符的位置
;被改变的寄存器
;	ax, edi
;---------------------------------------------------------------------------

DispAL:
	push ecx
	push edx

	mov ah, 0Ch
	mov dl, al
	shr al, 4
	mov ecx, 2
.begin:
	and al, 01111b
	cmp al, 9
	ja .1
	add al, '0'
	jmp .2
.1:
	sub al, 0Ah
	add al, 'A'
.2:
	mov [gs:edi], ax
	add edi, 2

	mov al, dl
	loop .begin
	add edi, 2
	
	pop edx
	pop ecx

	ret
;DispAL结束-----------------------------------------------------------------

;---------------------------------------------------------------------------
DispReturn:
	push eax
	push ebx
	mov eax, edi
	mov bl, 160
	div bl
	and eax, 0FFh
	inc eax
	mov bl, 160
	mul bl
	mov edi, eax
	pop ebx
	pop eax

	ret
;DispReturn结束--------------------------------------------------------------


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

;16位代码段,由32位代码段跳入,跳出后到实模式
[SECTION .s16code]
ALIGN 32
[BITS 16]
LABEL_SEG_CODE16:
	;跳回实模式:
	mov ax, SelectorNormal
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax
	mov ss, ax

	mov eax, cr0
	and al, 11111110b
	mov cr0, eax

LABEL_GO_BACK_TO_REAL:
	jmp 0:LABEL_REAL_ENTRY		;段地址会在程序开始处被设置成正确的值

Code16Len	equ	$-LABEL_SEG_CODE16
;END of [SECTION .s16code]

你可能感兴趣的:(dos,保护模式,实模式,段描述符)