【我所認知的BIOS】—>實模式&保護模式切换实例
LightSeed
2009-6-23
上一章從基礎概念上我談了談我的理解。素不知“紙上得來終覺淺,絕知此事要躬行”呀!不多說二話,我們來詳細剖析實模式和保護模式的相互切換。(我儘量解釋清楚每一句話,以下舉的例子是楊季文老師書里一個最簡單的例子)
Let us go!
1、這個例子中code做的事
下麵我們看到的這個code做了這樣的操作:
① 在實模式進入保護模式
② 在保護模式里把高端內存的value copy到低端內存中來(buffer中)
③ 返回到實模式
④ 在實模式下,顯示buffer中的內存數值。
對此需要說明的一點是這個例子是一個很簡單很簡單地實模式和保護模式的切換,中間很多東西都沒有考慮,不過拿來作為我們學習保護模式的入門我想到是再好不過了。
2、原程式加詳細註釋
;-------------这段代码copy form<80X86汇编语言程序设计教程>--------
;-------------只是一个最简单的实模式与保护模式的相互切换----------
;-------------切忌,本程序编译连接后生成的exe文件在纯DOS下--------
;-------------才能够执行LGDT这个命令,才能顺利进入保护模式--------
;-------------宏定义区域开始--------------------------------------
;16位偏移的段间直接转移指令的宏定义
JUMP macro selector,offsetv
db 0eah ;操作码 jmp
dw offsetv;16位偏移
dw selector ;段值(real mode下)或者选择子(protect mode下)
endm
;字符显示宏指令的定义
ECHOCH macro ascii
mov ah, 2 ;选功能号
mov dl, ascii ;填将要显示的ASCII码给DL
int 21h ;调用DOS中断来显示ASCII码
endm
;-------------宏定义区域结束-------------------------------------
;-------------结构体定义区域开始---------------------------------
;存储段描述符结构类型的定义
DESCRIPTOR struc
Limitl dw 0 ;段界限(0~15)
Basel dw 0 ;段基地址(0~15)
Basem db 0 ;段基地址(16~23)
Attributes dw 0 ;段属性
Baseh db 0 ;段基地址(24~31)
DESCRIPTOR ENDS
;伪描述符结果类型的定义
PDESC struc
Limit dw 0 ;16界限
Base dd 0 ;基地址
PDESC ENDS
;-------------结构体定义区域结束---------------------------------
;常量定义
ATDW = 92H ;存在的可读写数据段属性值
ATCE = 98H ;存在的只执行代码段属性值
.386P
;--------------实模式下数据段定义开始----------------------------
dseg segment use16
align 16 ;16位段
GDT label byte ;全局描述符表GDT标志
DUMMY DESCRIPTOR <> ;空描述符【鉴于仅仅说明实模式与保护模式切换,为什么为空如果有兴趣我们再探讨】
CODE DESCRIPTOR <0FFFFH,,,ATCE,>;代码段描述符
CDDE_SEL = CODE - GDT ;代码段描述的选择子
DATAS DESCRIPTOR <0FFFFH,8eacH,20H,ATDW,0>;源数据段描述符
DATAS_SEL = DATAS - GDT ;源数据段描述符的选择子
DATAD DESCRIPTOR <0FFFFH,,,ATDW,>;目标数据段描述符
DATAD_SEL = DATAD - GDT ;目标数据段描述符的选择子
GDTLEN = $ - GDT ;全局描述符表长度
;
VGDTR PDESC <GDTLEN-1,>;①伪描述符
;
BUFFERLEN = 256 ;缓冲区字节长度
BUFFER DB BUFFERLEN DUP(0);缓冲区
dseg ends
;---------------实模式下数据段定义结束---------------------------
;---------------实模式下代码段定义开始---------------------------
cseg segment use16 ;16位段
assume cs:cseg,ds:dseg ;声明代码段和数据段
start:
mov ax,dseg
mov ds,ax ;初始化数据段
;准备要加载到GDTR的伪描述符
mov bx,16 ;乘数为16,是为了在实模式中计算地址
mul bx ;计算并设置GDT基地址
add ax,offset GDT ;此时AX中为GDT在实模式中的地址,界限在已定义时设置妥当
adc dx,0 ;如果有进位那么ADC加上
mov word ptr VGDTR.Base,ax;填入GDT的实际地址的低word到伪描述符结构体中
mov word ptr VGDTR.Base+2,dx ;填入GDT的实际地址的高word到伪描述符结构体中
;设置代码段描述符
mov ax,cs
mul bx ;计算代码段在实模式中的实际地址
mov code.Basel,ax ;代码段开始偏移为0
mov code.Basem,dl ;代码段界限已在定义时设置妥当
mov code.Baseh,dh
;设置目标数据段描述符
mov ax,ds
mul bx ;计算数据段在实模式中的实际地址
;计算并设置目标数据段基地址
add ax,offset BUFFER ;加上offset
adc dx,0 ;如果有进位那么ADC加上
mov DATAD.Basel,ax ;不解释了,同上
mov DATAD.Basem,dl
mov DATAD.Baseh,dh
;加载GDTR
DB 66H ; execute a 32 bit LGDT
LGDT VGDTR ;命令不熟悉的话去查查
cli ;关中断
call EnableA20 ;打开地址线A20
;切换到保护模式
mov eax,cr0
or eax,1
mov cr0,eax
;清指令预取队列,并真正进入保护方式
JUMP <CDDE_SEL>,<offset VIRTUAL> ;②far jmp目的是显示地修改CS,刷新段选择子的hidden部分(此步骤后面我会详细回头探讨)
VIRTUAL:
MOV AX,DATAS_SEL ;源数据段选择子
MOV DS,AX ;加载源数据段描述符
MOV AX,DATAD_SEL ;源数据段选择子
mov es,ax ;加载目标数据段描述符
cld ;指针(si & di)累加
xor si,si ;设置指针初值
xor di,di
mov cx,BUFFERLEN/4 ;设置4字节为单位的缓冲区长度
repz movsd ;传送
;切回到实方式
mov eax,cr0
and eax,0fffffffeh
mov cr0,eax
;清指令预取队列,进入实方式
;JUMP <seg REAL>,<OFFSET REAL> ;far jmp目的是显示地修改CS,刷新段寄存器的hidden部分(此步骤后面我会详回头探讨)
PUSH ss
push sp
REAL: ;现在又回到实方式
call DisableA20 ;③关闭地址线A20
sti ;开中断
mov ax,dseg ;重置数据段寄存器
mov ds,ax
mov si,offset BUFFER
cld ;显示缓冲区内容
mov bp,BUFFERLEN/16 ;bp作为显示的行数的计数器
Nextline:
mov cx,16 ;每行只显示16*2个字符
NextCH:
lodsb ;mov al,ds:[si],si++
push ax ;保存ax
shr al,4 ;准备显示高4bit中的值
call ToASCII ;把al中的值转换成ASCII
ECHOCH al ;显示之
pop ax ;回复ax
call ToASCII
ECHOCH al
ECHOCH ' ' ;在字符于字符之间显示空格
loop NextCH ;处理下一个字符
ECHOCH 0dh
ECHOCH 0ah ;显示这两个ASCII回车+换行
dec bp ;显示完了否?
jnz Nextline ;bp = 0显示完了
mov ax,4c00h ;结束
int 21h
;---------------实模式下代码段定义结束--------------------------
;---------------子程式定义开始----------------------------------
;***************************************************************
;子程序名 :HtoASC
;功 能 :十六进制数转换成 ASCII
;入口参数 :al=8位二进制数
;出口参数 :无
;说 明 :无
;***************************************************************
toASCII proc
and al,0fh ;屏蔽al的高4 bits
cmp al,9 ;compare 9
jbe toASCII1 ;小于9,直接+30H
add al,37h ;否则,al+37H
ret
toASCII1:
add al,30h
ret
toASCII endp
;***************************************************************
;打开a20地址线
;***************************************************************
EnableA20 proc
push ax
in al,92h
or al,00000010b
out 92h,al
pop ax
ret
EnableA20 endp
;***************************************************************
;关闭a20地址线
;***************************************************************
DisableA20 proc
push ax
in al,92h
and al,11111101b
out 92h,al
pop ax
ret
DisableA20 endp
;---------------子程式定义结束----------------------------------
cseg ends
;---------------实模式下代码段定义结束--------------------------
end start ;指明程序入口
3、說明程序中我標記(一定需要說明的)
3.1 ①伪描述符
爲什麽我們要專門拿一個結構體來給LDGT這個命令用呢?原因是這樣的,由於GDT不能由GDT本身之類的描述符進行描述定義,所以處理器採用GDTR為GDT這一特色的系統段提供一個偽描述符。它的數據格式如圖1。

圖1 GDTR給定GDT的地址
在用GDTR的是,我們必須要注意到,由於CPU取的時候是以word的形式來讀取的,所以GDTR的地址必須是以word對齊的。
那麼我們怎麼用這個GDTR呢?用programming guide里的原話說“Before the GDT can be used, the base address and limit for the GDT must be loaded into the GDTR register using an LGDT instruction”
並且我們在用LGDT這個命令的時候必須要始終保證是word對齊的。於是我們就用一個結構體來存儲這個特殊的偽描述符了。(當然啦,你是可以不用定義結構體的,只要能夠讓CPU識別的到就沒有任何問題啦。)
3.2 ②far jmp的作用
3.2.1 提出疑問,爲什麽要用far jmp?
爲什麽我要專門把這個提出來呢?是因為在當時學的時候,就有個疑問。“爲什麽在programming guide”裏面和《80X86彙編語言程序設計教程》里都“淺淺”地說,“清指令預取隊列(prefetch queue)”。
3.2.2 清prefetch queue的實質
我們再詳細查資料可以知道,在mov cr0,eax执行后,处理器实际上已经处于保护模式。然而,Prefetch Queue值仍为实模式下的值。(其中最重要的是cs)注意,段寄存器不仅包括16位可见部分,还包括48位高速缓冲(也就是我们programming guide里的,“visible” part and a “hidden” part)。在进入保护模式后,这些段寄存器高速缓冲中的内容仍然存在,而且CPU为了其执行指令的效率只要CS或者其他的DS等等段寄存器如果没有改变,那么CPU都直接从这些高速缓冲器中去取CS和DS的值。(但是进入保护模式后,CPU预取的指令却全是实模式下的地址,当然会在保护模式下寻址时出问题。)那么我们就需要改变CS等等段寄存器的值(不過在保护模式下应该叫做段选择子了)需要显式(就是直接修改CS的意思)地重新加载。加载其他段寄存器可以mov,但是只有li
分享到:
相关推荐
在计算机系统中,BIOS(基本输入输出系统)扮演着至关重要的角色,它是一组固化在计算机主板上ROM芯片中...通过阅读《我所认知的 BIOS 》实模式&保护模式切换实例文档,可以更直观地学习这一过程,并通过实践加深理解。
<br/><br/>《x86汇编语言:从实模式到保护模式》采用开源的NASM汇编语言编译器和VirtualBox虚拟机软件,以个人计算机广泛采用的Intel处理器为基础,详细讲解了Intel处理器的指令系统和工作模式,以大量的代码演示了16...
从实模式切换到保护模式,通常在BIOS启动过程中完成,通过执行特定的中断指令,如INT 15H,来触发CPU的模式切换。这一过程涉及到对控制寄存器的设置,比如CR0寄存器的PG(分页)和PE(保护模式启用)位的设置。 在...
《x86汇编语言++从实模式到保护模式》一书深入探讨了x86架构下计算机系统从实模式到保护模式的转变过程,这是一段从早期个人计算机到现代操作系统核心的基础演变历程。汇编语言是低级编程语言,它与硬件紧密相连,...
本文将深入探讨实模式与保护模式的区别,以及两者之间的切换过程。 #### 二、实模式详解 ##### 1. 定义 - **实模式**是x86架构中的一种工作模式,这种模式下,处理器遵循早期8086和8088的设计规范。 - 在实模式下...
《x86汇编语言-从实模式到保护模式》这本书深入浅出地介绍了x86架构下的汇编语言编程,涵盖了从基础的实模式到高级的保护模式的转换过程。汇编语言是计算机硬件与软件之间的桥梁,理解它对于底层系统开发、优化和...
《x86汇编语言:从实模式到保护模式》采用开源的NASM汇编语言编译器和VirtualBox虚拟机软件,以个人计算机广泛采用的Intel处理器为基础,详细讲解了Intel处理器的指令系统和工作模式,以大量的代码演示了16/32/64位...