忆杰的博客

忆杰的博客

保护模式2:保护模式下32位代码段和16位代码段切换

为了兼容, 386的保护模式可以在16位代码段和32位代码段之间随意转换, 用于标示是32位或者16位代码段的是段描述符内第6字节的第6位标示出来的. 叫做D/B位.

这个D/B位还有些郁闷, 如果是描述代码段时, 该位被称为D位, 如果标志位1, 那么模式使用32位地址, 所有的16位指令都会加前缀, 如果为0则默认是16位地址, 和指令. 指令前缀66H可以指定操作符的长度而不使用缺省长度, 67H可以用来指定地址值的长度.

下面这个实例就做到了在16位代码段和32位代码段之间的切换. 当然这个代码在逻辑上的含义是先显示线性地址100000h处16个字节的内容, 然后再显示一个数据段中的一个字符串.. 相对上一篇进入保护模式并没有复杂太多..

代码功能首先是为切换到保护模式做准备, 初始化VGDTR描述符, 还有各个段描述符, 然后将PE置位, 开启分段切换到保护模式. 然后将线性地址100000h处的16个字节内容. 写入到B80000H处, 也就是显示在屏幕上. 然后切换到16位段. 又显示了一个字符串. 然后再切换回实模式.

你可以从这里下载这个源码, 也是MASM9+link5写的..
        http://www.joenchen.com/JoenTools/CodeSwitch.rar

;============================================================================
	;保护模式下32位段和16位段的转换
	;编译方法参加makefile, MASM9 + link5.6 TAB=8
;============================================================================
	.686p
	Include pm.inc		;重要头文件定义
	option casemap:none
;============================================================================
	;16位数据段, 存储了一些非常重要的数据
;============================================================================
DataSeg		Segment		use16
GDT	label	byte				;全局描述符表
Dummy:		Descriptor	0, 0, 0		;空的描述符
;----------------------------------------------------------------------------
;				;段基址	  	;段界限		;属性
Normal:		Descriptor	0, 		0ffffh,		DA_DRW	;规范选择字
Code16Desc:	Descriptor 	0,		0ffffh, 	DA_C	;非一致代码段16位
Code32Desc:	Descriptor 	0,		Code32Len, DA_C or DA_32;非一致代码段32位
VideoDesc:	Descriptor	0b8000h,	0ffffh, 	DA_DRW	;显存段(可读写)
DataSDesc:	Descriptor	100000h,	16,		DA_DR	;只读数据段
DataMessDesc:	Descriptor	0,	sizeof SzMessage, 	DA_DRW	;保护模式下使用的数据段
StackDesc:	Descriptor	0, 		256,		DA_DRW	;堆栈段(可读写)
;----------------------------------------------------------------------------
GDT_Len		equ	$ - 	GDT		;GDT长度
GDT_Ptr		word	GDT_Len-1		;VGDT
		dword	0

_RegSs		word	0			;用于保存旧的SS:SP
_RegSp		word	0
;----------------------------------------------------------------------------
NormalSelector	equ	Normal - GDT		;规范段描述符选择子
Code16Selector	equ	Code16Desc - GDT	;16位代码段选择子
Code32Selector	equ	Code32Desc - GDT	;32位代码段选种子
DataSrcSelector	equ	DataSDesc- GDT		;源数据段选择子
VideoSelector	equ	VideoDesc - GDT		;视频段选择子
DataMessSelector equ	DataMessDesc - GDT	;显示消息段的选择子
StackSelector	equ	StackDesc - GDT		;堆栈段选择子
DataSeg		Ends

DataMess	Segment	use16
	SzMessage	byte	"I entered a protected mode I'm Joen", 0
DataMess	Ends

;============================================================================
	;保护模式下使用的堆栈段
;============================================================================
StackSeg	Segment	para Stack use16	;堆栈段
	byte	256 dup( 0 )
StackSeg	Ends

;============================================================================
	;16位代码段, 由保护模式跳入, 然后返回到实模式
;============================================================================
Code16Seg	Segment	use16

_RetReal	Proc

;----------------------------------------------------------------------------
	;在屏幕上显示一个字符串
	mov	ax, DataMessSelector
	mov	ds, ax				;ds-->消息段

	mov	si, offset SzMessage

	mov	ax, VideoSelector
	mov	es, ax				;es-->视频段
	mov	di, 80*2*11+10*2		;15行10列

	mov	cx, sizeof SzMessage
@@:	lodsb
	mov	ah, 07h				;属性
	stosw
	loop	@b

;----------------------------------------------------------------------------
	;关闭保护模式, 准备返回实模式
	mov	ax, NormalSelector
	mov	ds, ax
	mov	es, ax
	mov	ss, ax				;规范选择子

	mov	eax, cr0
	and	al, 0feh
	mov	cr0, eax			;关闭分段标记

	;通过这个跳转刷新段选择子影子寄存器, 真正进入实模式
	jmp	far ptr _ToReal			

_RetReal 	Endp	

Code16Seg	Ends

;============================================================================
	;32位代码段, 由实模式跳入
;============================================================================
Code32Seg	Segment	use32
;============================================================================
	;将_dwValue中的值最低位转换成ASCII并填写上属性, 在EAX中返回
;============================================================================
_ToAscii	Proc  _dwValue
	local	_byBuf[4]:byte

	mov	eax, _dwValue
	and	al, 0fh
	.if	al > 9
		add	al, 'A'			;转换低位
	.Else
		add	al, '0'
	.Endif

	mov	byte ptr [_byBuf], al
	mov	byte ptr [_byBuf+1], 7		;属性位

	xchg	ah, al
	.if	al > 9
		add	al, 'A'			;转换高位
	.Else
		add	al, '0'
	.Endif
	mov	byte ptr [_byBuf+2], al
	mov	byte ptr [_byBuf+3], 7		;属性
	mov	eax, dword ptr [_byBuf]
	ret
_ToAscii 	Endp

_Entry		Proc				;由实模式跳入

	mov	ax, StackSelector
	mov	ss, ax
	mov	esp, 256			;设置ss->esp

	mov	ax, DataSrcSelector
	mov	ds, ax				;设置源数据段

	mov	ax, VideoSelector
	mov	es, ax				;设置目标视频段

	xor	esi, esi
	mov	edi, 80*2*10+10*2		;第10行10列
	mov	ecx, 16				;限长是16
	cld
;----------------------------------------------------------------------------
@@:	lodsb					;转换100000h开始的20个字节数据
	Invoke	_ToAscii, eax
	stosd
	mov	ah, 7				;属性
	mov	al, 20h				;空格
	stosw
	loop	@b
;----------------------------------------------------------------------------
	;跳到16位代码段, 准备返回Dos
	Jmp32	Code16Selector, <offset _RetReal>
_Entry 		Endp
Code32Seg_Entry	= _Entry - Code32Seg		;定义段内入口
Code32Len	= $ -  Code32Seg		;段长度
Code32Seg	Ends
;============================================================================
	;16位段, 由这里进入保护模式
;============================================================================
StartCode	Segment	use16 

Jmain	Proc
;----------------------------------------------------------------------------
	mov	eax, DataSeg
	mov	ds, ax
	shl	eax, 4
	add	eax, offset GDT
	mov	dword ptr ds:[GDT_Ptr+2], eax	;初始化VGDT描述符
;----------------------------------------------------------------------------
	mov	eax, Code32Seg			;初始化32位代码段
	shl	eax, 4
	mov	word ptr ds:[Code32Desc+2], ax	;段基址低地址
	shr	eax, 16
	mov	byte ptr ds:[Code32Desc+4], al	;段基址高地址低位
	mov	byte ptr ds:[Code32Desc+7], ah	;段基址高地址高位
;----------------------------------------------------------------------------
	mov	eax, Code16Seg			;初始化16位代码段
	shl	eax, 4
	mov	word ptr ds:[Code16Desc+2], ax	;段基址低地址
	shr	eax, 16
	mov	byte ptr ds:[Code16Desc+4], al	;段基址高地址低位
	mov	byte ptr ds:[Code16Desc+7], ah	;段基址高地址高位
;----------------------------------------------------------------------------
	mov	eax, DataMess			;初始化一个数据段, 用于显示消息
	shl	eax, 4
	mov	word ptr ds:[DataMessDesc+2], ax;段基址低地址
	shr	eax, 16
	mov	byte ptr ds:[DataMessDesc+4], al;段基址高地址低位
	mov	byte ptr ds:[DataMessDesc+7], ah;段基址高地址高位
;----------------------------------------------------------------------------
	mov	word ptr ds:[_RegSs], ss
	mov	word ptr ds:[_RegSp], sp	;保存下SS:SP

	mov	eax, StackSeg			;初始化堆栈段
	shl	eax, 4
	mov	word ptr ds:[StackDesc+2], ax	;段基址低地址
	shr	eax, 16
	mov	byte ptr ds:[StackDesc+4],al	;段基址高地址低位
	mov	byte ptr ds:[StackDesc+7], ah	;段基址高地址高位
;----------------------------------------------------------------------------
	lgdt	fword ptr ds:[GDT_Ptr]		;装在VGDTR
	cli
	_EnableA20				;关中断 开A20地址线
	mov	eax, cr0
	or	eax, 1
	mov	cr0, eax			;开启分段进入保护模式
;----------------------------------------------------------------------------
	;刷新指令预读取序列, 真正进入保护模式
	Jmp16	Code32Selector,  < Code32Seg_Entry >

_ToReal::;从保护模式又回来了
	mov	ax, DataSeg
	mov	ds, ax
	mov	sp, ds:[_RegSp]
	mov	ss, ds:[_RegSs]			;恢复SS:SP
	_DisableA20				;关闭A20地址线
	sti
	mov	ax, 4c00h
	int	21h
Jmain 	Endp

StartCode	Ends

End	Jmain

发表评论


Warning: Undefined variable $user_ID in /www/wwwroot/joenchen.com/wp-content/themes/agan/comments.php on line 66

您必须登录 才能进行评论。