《Orange’s 一个操作系统的实现》3.保护模式3----保护模式进阶

在前面的代码上进行修改后,测试读写大地址内存(实模式下的1MB限制),而且从保护模式再调回实模式.

上代码分析(省略前面重复的代码部分):

; ==========================================

; pmtest2.asm

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

; ==========================================



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



org	0100h                   ;参看DOS系统中.EXE文件的加载过程

	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    ; Data

LABEL_DESC_STACK:  Descriptor    0,     TopOfStack, DA_DRWA+DA_32; Stack, 32 位

LABEL_DESC_TEST:   Descriptor 0500000h,     0ffffh, DA_DRW    ;5M基址

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

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]	 ; 32位数据段

ALIGN	32

[BITS	32]

LABEL_DATA:

SPValueInRealMode	dw	0                               ;保存实模式下的SP

; 字符串

PMMessage:		db	"In Protect Mode now. ^-^", 0	; 在保护模式中显示

OffsetPMMessage		equ	PMMessage - $$                  ;PMMessage在LABEL_DATA节中的偏移

StrTest:		db	"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0

OffsetStrTest		equ	StrTest - $$                    ;StrTest在LABEL_DATA节中中的偏移

DataLen			equ	$ - LABEL_DATA                  ;LABEL_DATA节的长度

; END of [SECTION .data1]





; 全局堆栈段

[SECTION .gs]

ALIGN	32

[BITS	32]

LABEL_STACK:

	times 512 db 0                      ;512字节的堆栈段



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 ;保存实模式下的CS
	mov	[SPValueInRealMode], sp       ;保存实模式下的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	eax, cr0

	or	eax, 1

	mov	cr0, eax



	; 真正进入保护模式

	jmp	dword SelectorCode32:0	; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0  处



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;



LABEL_REAL_ENTRY:		; 从保护模式跳回到实模式就到了这里

	mov	ax, cs          ;jmp 0:LABEL_REAL_ENTRY 跳转到此处,恢复cs

	mov	ds, ax          ;使用cs恢复ds,es,ss

	mov	es, ax

	mov	ss, ax



	mov	sp, [SPValueInRealMode] ;恢复实模式下的sp



	in	al, 92h		; `. 

	and	al, 11111101b	;  | 关闭 A20 地址线

	out	92h, al		; /



	sti			; 开中断



	mov	ax, 4c00h	; `.

	int	21h		; /  回到 DOS

; 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                ; esi重置

	xor	edi, edi                ; edi重置

	mov	esi, OffsetPMMessage	; 源数据偏移

	mov	edi, (80 * 10 + 0) * 2	; 目的数据偏移。屏幕第 10 行, 第 0 列。

	cld

.1:

	lodsb                   ;逐个加载ds:esi对应的字符到al中,其中DS中的选择子SelectData指向LABEL_DATA

	test	al, al          ;判断al字符是否为0(只是为了测试,请看PMMeesage的定义最后是0)

	jz	.2              ;为0跳转.2显示完毕

	mov	[gs:edi], ax    ;非0将ax中的字符移到视频选择子对应的区域gs:edi

	add	edi, 2          ;设置gs:edi指向下个字符,edi加2(ax为16位)

	jmp	.1              ;跳转到.1处继续

.2:	; 显示完毕



	call	DispReturn      ;显示完毕后,换行显示



	call	TestRead        ;读5M地址内容

	call	TestWrite       ;写5M地址内容

	call	TestRead        ;在此读5M地址荣,测试写5M地址内容是否成功



	; 到此停止

	jmp	SelectorCode16:0 ;跳转到LABEL_SEG_CODE16处执行(调回实模式)



; ------------------------------------------------------------------------

TestRead:

	xor	esi, esi      ;esi重置

	mov	ecx, 8        ;设置读取字节数位8

.loop:

	mov	al, [es:esi]  ;将es:esi中的字符移到al中(5M地址)

	call	DispAL        ;显示al中的值

	inc	esi           ;增加esi的值,指向下个es:esi的下个字符

	loop	.loop         ;继续读取



	call	DispReturn    ;读取完毕显示回车



	ret                   ;返回调用处

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





; ------------------------------------------------------------------------

TestWrite:

	push	esi                     ;esi入栈

	push	edi                     ;edi入栈

	xor	esi, esi                ;esi重置

	xor	edi, edi                ;edi重置

	mov	esi, OffsetStrTest	;源数据偏移

	cld

.1:

	lodsb                           ;将OffsetStrTest对应的字符串逐个加载到al中

	test	al, al                  ;是否为0

	jz	.2                      ;为0,跳转.2读取完毕

	mov	[es:edi], al            ;将al中的值写入测试大地址(5M)

	inc	edi                     ;增加EDI,下个字符的位置

	jmp	.1                      ;继续读取

.2:



	pop	edi                     ;恢复edi,esi

	pop	esi



	ret

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





; ------------------------------------------------------------------------

; 显示 AL 中的数字(将二进制转换为16进制显示)

; 默认地:

;	数字已经存在 AL 中

;	edi 始终指向要显示的下一个字符的位置

; 被改变的寄存器:

;	ax, edi

; ------------------------------------------------------------------------

DispAL:

	push	ecx                     ;ecx,edx入栈

	push	edx



	mov	ah, 0Ch			; 0000: 黑底    1100: 红字

	mov	dl, al                  ; al暂存入dl中

	shr	al, 4                   ; al右移4位

	mov	ecx, 2                  ;   

.begin:

	and	al, 01111b              ; 将al中的高4位清0,保留低四位(原高四位)

	cmp	al, 9                   ; 是否大于9

	ja	.1                      ; 大于则跳转.1

	add	al, '0'                 ; 

	jmp	.2

.1:

	sub	al, 0Ah                 ;大于9将数字扣除0Ah(10)

	add	al, 'A'                 ;加上'A'(65)转换为A到F  例如11 – 10 + 65 = 66 = 'B'

.2:

	mov	[gs:edi], ax            ;移入显存区域

	add	edi, 2                  ;edi+2指向下个位置

    

	mov	al, dl                  ;dl移入al中,进行低4位的16进制转换

	loop	.begin                  ;跳转.begin

	add	edi, 2                  ;edi加2



	pop	edx                     ;恢复edx,ecx

	pop	ecx



	ret

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





; ------------------------------------------------------------------------

DispReturn:

	push	eax             ;eax,ebx入栈

	push	ebx        

	mov	eax, edi        ;edi移入eax     

	mov	bl, 160         ;bl设置为160(设定每行显示为(80*10 + 0)*2)

	div	bl              ;al/bl = edi/bl   al位商   ah为余数

	and	eax, 0FFh       ;清除eax的高16位,

	inc	eax             ;eax+1

	mov	bl, 160         ;

	mul	bl              ;ah = al*160=(edi/160的商+1)*160-----达到换行的效果

	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 ;将SelectorNormal加载到ds,es,fs,gs,ss,去除在32保护模式中的段属性
                                   ;使描述符寄存器含有合适的段界限和属性(实模式下32段属性不符合实模式的要求)

	mov	ds, ax

	mov	es, ax

	mov	fs, ax

	mov	gs, ax

	mov	ss, ax



	mov	eax, cr0           ;设置cr0的PE为0

	and	al, 11111110b

	mov	cr0, eax 



LABEL_GO_BACK_TO_REAL:

	jmp	0:LABEL_REAL_ENTRY	; 段地址会在程序开始处被设置成正确的值
                                              ;jmp segment:offset 根据段间转移(长转移),汇编指令为:
                                        ;0EAh Offset(2字节) Segment(2字节)   
                                        ;这就是代码mov [LABEL_GO_BACK_TO_REAL+3], ax的作用
                                        ;将实模式下的cs值,写入LABEL_GO_BACK_TO_REAL地址偏移+3的位置,
                                        ;刚好就是jmp指令EA的Segment处,请参看实模式下的长跳转指令图示          

                                        ;所以此的指令为JPM  cs(实模式):LABEL_REAL_ENRY

Code16Len	equ	$ - LABEL_SEG_CODE16



; END of [SECTION .s16code]
 
 
 

相关:org 0100h 请参看DOS加载.EXE过程《Orange’s 一个操作系统的实现》4.保护模式3----DOS加载.EXE过程

你可能感兴趣的:(操作系统)