80386处理器新增了一组控制寄存器CR0,CR1,CR2,CR3和一组系统地址寄存器GDTR,LDTR,IDTR,TR,它们全部都是32位的。CR0包含了指定处理器工作方式的控制位,CR1保留未使用,CR2和CR3由分页管理部件使用,CR0中的5~30位和CR3中的0~11位必须为0,分别介绍如下:
___________________________________________________________________________
|PG|0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |ET|TS|EM|MP|PE| CR0
|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|_ |__|
| Reserved | CR1
|__________________________________________________________________________|
| 页故障线性地址 | CR2
|__________________________________________________________________________|
| 高20位页表的起始物理地址 |低12位为0 | CR3
|_____________________________________________________|____________________|
PE标记用于指定处理器的工作模式。PE=0,处理器处于实模式;PE=1,处理器处于保护模式
PG标记用于指定处理器是否启用分页管理机制。PG=0,禁用分页管理机制,此时由分段管理部件产生的线性地址就是物理地址。;PG=1,启用分页管理机制,此时由分段管理部件产生的线性地址须再经过分页管理机制才能得到最终的物理地址。
MP,EM,TS,ET用于控制浮点协处理器的操作。
CR2和CR3控制寄存器由分页管理机制使用。CR2用于发生页异常时报告出错信息。当发生页故障时,处理器会将当前的线性地址保存在CR2。CR3用于保存页表在内存中的起始物理地址,由于页表是对齐的,所以仅高20位有效,低12位必须为0。
全局描述符表GDT,局部描述符表LDT和中断描述符表IDT在保护模式下是特殊的段,也就是说处理器将这些线性表当段一个特殊的段来处理,它包含了对段机制所用的重要数据。为了能够更快速地进位这些段,386处理器采用特殊的寄存器保存这种段的基地址和界限,这种寄存器就是系统地址寄存。在80386下系统地址寄存器有:全局描述符表寄存器GDTR,局部描述符表寄存器LDTR,中断描述符表IDTR,任务状态段寄存器TR。全局描述符表寄存器GDTR,长度为48位,其中高32位是基址,低16位含界限。由于GDT本身不可以由GDT内的描述符来描述,所以处理使用GDTR寄存器为GDT这样的特殊段提供一个伪描述符,即是说:
| |
|________________| 全局描述符表寄存器GDTR
| | ________________________________
| GDT |______| | |
|________________|______| 32位基址 | 16界限 |
| | |___________________|___________|
| |
因为段选择子只用了13位来表描述表中的索引号,即是说最多可以有8192个描述符,而每个描述符是8个字节。而在80386处理器下将全局描述表作为一个特殊的系统段,那么段的界限实际上就是8192*8,所以段的界限用16位就可以了。通常情况下,如果GDT有N个描述符,那么GDT的段界限为N*8-1,这个伪描述符也就是全局描述符寄存器内容可以用结体定义成:
PreDesc STRUCT
BASE32 DD 0
LIMIT16 DW 0
PreDesc ENDS
局部描述表寄存器LDTR规定了当前任中使用的局部描述表LDT,LDTR类似于一个段寄存,它的长度为32位,一个16位的寄存器和对程序员来讲不可见的高速缓冲存储器。每一个任务的局部描述符表作为一个特殊的系统段,它由定义在全局描述符表GDT中的描述符来描述,前面已提到过一个任务只能有一张全局描述符表GDT和一张中断描述符表IDT,但可以有多张局部描述行表LDT,而每一张局部描述符表都由定义在GDT中的描述符来确定。通常将描述LDT的选择子装入到LDTR,LDTR根据选择子从全局描述符表中取出对应的描述符,并把LDT的基址及界限信息保存到对程序员来讲不可见的高速缓冲存储器,随后就可以对LDT进行访问。当前任务中的所有段都由GDT中的描述符来描述。
_________ ____________________________________________________
| |______| | | |
| LDTR |______| 32位基址 | 32位界限 |12位属性 |
|_______| |___________________|_____________________|_________|
中断描述符表和全局描述符表一样,长度为48位。32位段基址和16位界限。
如何从实式模式切换到保护模式下呢?通常来讲,要两个步骤:1.作好切换到保护模式下的准备;2.切换到保护模式。主要准备工作就是建立全局描述符表,并使GDTR指向GDT,因为切换到保护模式下,至少要将代码段的选择子装入到CS中,看程序片段:
;定义好描述符的结构
DESCRIPTOR STRUCT
LIMIT DW 0;段界限
BASEL DW 0;段基址的低16位
BASEM DB 0;段基址的16~23位
ATTRIBUTES DW 0;段属性
BASEH DB 0;段基址的高8位,24~31
DESCRIPTOR ENDS
;定义好伪描述符
PDESC STRUCT
LIMIT DW 0
BASE DD 0
PDESC ENDS
;通常要定义一个段间跳转的宏,这样的话就可以保证在进入保护模式时将代码段的选择子装入到CS寄存器
JUMP MACRO selector,offset
DB 0EAH
DW offsetv;段偏移
DW selector;段选择子
ENDM
;打开A20地址线
PUSH AX
IN AL,92H
OR AL,2
OUT 92H,AL
POP AX
;关闭A20地址线
PUSH AX
IN AL,92H
AND AL,0FDH
OUT 92H,AL
POP AX
;切换到保护模式下,将CR0寄存中的第0位置1
MOV EAX,CR0
OR CR0,1
MOV CR0,EAX
其它的部分就要根据具体的应用来写, 下面的例子是如何在保护模下访问820000H单元开始的内容,看程序:
.386P
data segment use16
GDT LABEL BYTE;定义全局描述符表
DUMMY DESCRIPTOR<>;空描述符,它有特定义的含义,空描述符可以保证GDT中的第1个描述符永远不会被访问
CODE DESCRIPTOR<0FFFFH,,,SAttr,>;代码段的描述符
CODE_SEL=CODE-GDT;代码段描述符的选择子
DATAS DESCRIPTOR<0FFFFH,0H,82H,DAttr,>;源数据段描述符,即820000H
DATAS_SEL=DATAS-GDT;源数据段选择子
GDTLEN=$-GDT
VGDTR DESCRIPTOR<GDTLEN-1,>
data ends
code segment use16
assume cs:code,ds:data
start:
mov ax,data
mov ds,ax
mov bx,16
mul bx;设置全局描述表GDT基址,因为现在还处在实模式下,所以段地址要左移4位
add ax,offset GDT
adc dx,0
mov word ptr VGDTR.BASE,ax;设置全局描述符表寄存器GDTR的内容
mov word ptr VGDTR.BASE+2,dx
;设置代码段描述符
mov ax,cs
mul bx
mov CODE.BASEL,ax
mov CODE.BASEM,dl
mov CODE.BASEH,dh
;以下部分你可以根据实际的应用来编写
.........
...........
;加载GDTR
LGDT QWORD PTR VGDTR
CLI;关中断
;打开A20地址线
;切换到保护模式
mov eax,cr0
or eax,1
mov cr0,eax
JUMP <CODE_SEL>,<OFFSET VIRTUAL>;清指令预取队列,真正进入保护模式
........
........
virutal:
;add your code here according to your needs
............
;回到实模式
;关闭A20地址线
STI;开中断
code ends
end start
上述的程序片段是随手写的,可根据需要自已加以调整,不过有点要说明。
a.通常来讲,从实模式下切换到保护模式下只要将CR0寄存器中的最低位设置为1就可以了。但是,此时CS的内容仍然是实模式下的内容,所以加了一条段间跳转指令JUMP <CODE_SEL>,<OFFSET VIRTUAL>,执行完这条指令就可以将代码段选择子CODE_SEL装入到段寄存器CS中,同时也可以刷新指令预取队列。
b.LGDT QWORD PTR VGDTR,该指令的功能是将VGDTR的内容装入到全局描述符表寄存器GDTR中。
c.上面的代码片段中并没有建立中断描述符表IDT,这样的话就要求整个程序必须运行在关中断情况下进行。
d.为了访问1M以上的存储单元,应该打开A20地址线,在WINDOWS下只需加载HIMEM.SYS就可以了。能不能进入保护模式只与是否加载HIMEM.SYS有关,与处理器工作在实方式下还是在保护方式下无关。也就是说,只要加载HIMEM.SYS,就算处理器当前处在实模式下,A20地址线关闭,处理器也一样可以进入保护模式。
下集预告:
80386ASM程序设计基础(十二)---任务切换
80386ASM程序设计基础(十三)---80386中断和异常
80386ASM程序设计基础(十四)---分页管理机制
80386ASM程序设计基础(十五)---V86模式
敬请关注,谢谢。