继推出80386之后,Intel又推出了80386、Pentium和Pentium PRO。这些处理器都具有
实模式和保护模式两种工作方式。前面已介绍过,实模式与8086兼容,可以运行DOS及
以其为平台的几乎所有软件;但在实模式下,处理器不能发挥自身的优越性能,不能支
持多用户、多任务操作系统的运行。为了充分发挥处理器的功能,同时使DOS及以其为
平台的软件继续有效地运行,从80386开始增加了虚拟8086模式。本文将介绍虚拟8086模式。
<一>V86模式
1.V86模式
虚拟8086模式是保护模式下的一种工作方式,也称为V8086模式,或者简称为V86模式。在
虚拟8086模式下,处理器类似于8086。寻址的地址空间是1M字节;段寄存器的内容作为段
值解释;20位存储单元地址由段值乘以16加偏移构成。在V86模式下,代码段总是可写的,
这与实模式相同,同理, 数据段也是可执行的,只不过可能会发生异常。所以,在虚
拟8086模式下,可以运行DOS及以其为平台的软件。但V86模式毕竟是虚拟8086的一种方
式,所以不完全等同于8086。
当标志寄存器中的VM位为1时,处理器就处于V86模式。此时,其当前特权级由处理器 自动
设置为3。
2.V86任务
8086程序可以直接在V86模式下运行,而V86模式受到称为V86监控程序的控制。V86监控程
序和在V86模式下的8086程序构成的任务称为虚拟8086任务,或者简称为V86任务。V86任务
形成一个由处理器硬件和属于系统软件的监控程序组成的“虚拟8086机”。V86监控程序控
制V86外部界面、中断和I/O。硬件提供该任务最低端1M字节线性地址空间的虚拟存储空间,
包含虚拟寄存器的TSS,并执行处理这些寄存器和地址空间的指令。
80386把V86任务作为与其它任务具有同等地位的一个任务。它可以支持多个V86任务,每
个V86任务是相对独立的。所以,通过V86模式这种形式,运行8086程序可充分发挥处理器
的能力和充分利用系统资源。
<二>进入和离开V86模式
保护模式和V86模式之间的切换情形如下图所示。图中左面部分为V86任务。从图中可
见,V86模式与保护模式的切换可发生在V86任务之内,这种切换是V86模式下的8086程序
与保护模式下的监控程序之间的转换;V86模式与保护模式的切换可发生在任务之间,这
种切换是V86任务与其它任务的切换。此外,V86监控程序与其它任务之间的切换是普通的
任务切换。
由于80386没有提供直接改变VM标志的指令,并且只有当前特权级CPL=0时,对VM的改变才
有效,所以V86模式与保护模式的切换不能简单地通过改变VM位而进行。下面介绍V86模式
与保护模式之间的切换,也就是如何进入和离开V86模式。为了方便,先介绍如何离开V86模式。
1.离开V86模式
在V86模式下,如果处理器响应中断/异常,那么就会退出当前V86任务的V86模式。
在V86模式下,处理器对中断/异常的响应处理不同于真正的8086,而仍然采用保护模式下对
中断/异常响应处理的方法。所以,在V86模式下,不是根据位于线性地址空间最低端的中断
向量表内的对应中断向量转入处理程序,而是根据中断描述符表IDT内的对应门描述符的指示
转入处理程序。
(1)在V86任务内离开V86模式
如果对应的门描述符是386中断门或386陷阱门,那么就发生在当前V86任务内从V86模式到保
护模式的转换。80386要求执行这种中断/异常处理程序时的CPL必须等于0。
由于V86模式下的CPL=3,而转换到保护模式后的CPL=0,所以这种转换包含了特权级的变换。
在转入处理程序之前,处理器先将V86模式下的段寄存器GS、FS、DS及ES压入0级堆栈,并在
进入保护模式下的处理程序之前装入空选择子。为保持使堆栈对齐,把段寄存器压入堆栈时
一律按32位值压入,低16位是段寄存器的值,高16位为空。于是,转换后的0级堆栈如下图所
示。其中,段寄存器SS和CS的值也是V86模式下的段值。图(a)是没有出错码的情形;图(b)是
有出错码的情形。
在这种V86任务内从V86模式转换到保护模式的过程中,为了保证中断/异常处理程序工作于
特权级0,对目标代码段描述符特权级进行检查,如果由目标代码段描述符特权级决定的CPL不
等于0,将引起通用保护异常。此外,标志寄存器EFLAGS中的VM位被清0,从而使得中断/异常
处理在保护模式下进行,也即离开V86模式。
这种情况下,相应的中断/异常处理在当前V86任务之内进行。中断异常处理程序可以检查保
存在堆栈中的EFLAGS映象,根据VM位的值来确定被中断程序的工作模式。如果VM=1,那么被
中断的程序工作于V86模式,是8086程序;否则,被中断的程序工作于保护模式,是V86监控
程序。
(2)任务切换离开V86模式
如果对应的门描述符是任务门,那么就发生从当前V86任务到其它任务的切换,也就离开了当
前V86任务的V86方式。象普通任务切换一样,V86模式的各通用寄存器、段寄存器、指令指针
和标志寄存器EFLAGS等保存到原V86任务的386TSS中。被保存的段寄存器的内容是V86模式下
的段值。被保存的EFLAGS内的VM=1。
这种情况下,相应的中断异常处理在另一个任务内进行。目标任务可以是普通任务,也可以是
另一个V86任务。如果目标任务TSS内的EFLAGS字段内的VM=1,那么就转入另一个V86任务
的V86模式。
2.进入V86模式
与离开V86模式的两条途径相对应,有两条进入V86模式的途径。
(1)通过IRET指令进入V86模式
通过在中断/异常处理结束时使用IRET指令返回被中断的程序继续执行。指令IRET的执行步骤
如下所示:
(1)若NT=1,则进行任务切换,然后转步骤6;
(2)否则从堆栈中弹出EIP、CS和EFLAGS;
(3)若VM=1且CPL=0,则恢复外层堆栈及其它段寄存器,然后转步骤6;
(4)若无特权级变换则转步骤6;
(5)否则恢复外层堆栈;
(6)结束
尽管上述步骤不够细致和没包括异常情况,但还是体现了指令IRET执行时所处理的三种情形。
第一种情形是当前EFLAGS中的NT=1,也即嵌套任务返回,那么就进行任务切换,指向目标任
务TSS的选择子在当前任务TSS的连接字段。NT=0表示当前中断/异常处理程序与被中断程序属
于同一任务,于是就从堆栈弹出EIP、CS和EFLAGS。第二和第三中情形是NT=0的条件下产生的。
第二种情形是弹出的EFLAGS中VM=0,表示被中断的程序是普通保护模式程序,那么就考虑特
权级变换,如果向外层返回,那么就恢复外层堆栈指针。不允许向内层返回,否则将会引起
通用保护异常。前文中介绍的IRET指令的动作只考虑了情形一和情形二,并不是IRET指令的
完整动作。
第三种情形是弹出的EFLAGS中VM=1且当前正运行程序的CPL=0,表示被中断的程序是V86模式
下的8086程序,当前是从同一V86任务下的中断/异常处理程序返回。由于V86模式的特权级
是3,所以要进行堆栈切换,也即从堆栈中弹出3级堆栈的指针(ESP和SS)。此外,还从堆栈中
弹出段寄存器ES、DS、FS和GS。在这种情形下,弹到各段寄存器(包括CS和SS)的内容都作为
段值,而非选择子。这种处理动作对应于上述第一种离开V86模式的情形,有关堆栈操作与上
图所示的堆栈内容相符。当然,如果产生异常时提供出错码,那么异常处理程序在利用IRET指
令返回时,必须确保堆栈指针指向如上图所示保存EIP的单元。简单的实现方法是,异常处理
程序在执行IRET前,先从堆栈中弹出出错码。
利用指令IRET处理的这第三种情形,可以方便地从V86任务下的中断/异常处理程序返回到V86模
式下的8086程序。利用这条途径还可以直接进入V86模式。为此,先在0级堆栈中形成如上图(a)所
示的栈顶。对应EIP值是V86模式下要执行的8086程序入口点的16位偏移;对应CS值是V86模式下要
执行的8086程序入口点的段值;对应EFLAGS值中的VM位必须是1;对应SS和ESP的值是要执行
的8086程序的堆栈指针;对应ES、DS、FS和GS的值是相应的段值。然后,在CPL=0和NT=0的情况
下,执行IRET指令。实际上,这种进入V86模式的途径是,先建立一个V86模式下执行的8086程序
被中断而离开V86模式的环境,然后再返回。需要注意的是,若当前正执行程序的CPL不为0,
则再执行IRET指令时不会进入V86模式,但也不产生异常,EFLAGS中的VM位被处理器 自动清0。
不能通过RET指令进入V86模式,因为它不改变EFLAGS的内容。任何不能修改EFLAGS中VM位的指令
均不能切换到V86模式。
(2)通过任务切换进入V86模式
通过任务切换的途径,可以从其它任务进入V86任务内的V86模式。
利用在前文介绍的任务切换的方法可以进行任务切换。如果目标任务由386TSS描述,并且其
中EFLAGS字段内的VM位为1,那么在切换到目标任务时,也就进入了V86模式。在切换到V86模式
时,CPL被规定为3。目标任务TSS中的各段寄存器字段被解释为8086可以接受的段值,而不是选
择子。任务切换时也将装载LDTR和CR3。
程序在V86模式下执行时,EFLAGS寄存器中的NT位被忽略,所以,不能在V86模式下用IRET指令
完成一个任务切换,并使其工作于V86以外的工作模式。在V86模式下执行IRET指令时,将弹出
IP、CS及FLAGS寄存器,以恢复被中断的程序,而不考虑NT位的值。
如果利用这条途径建立V86任务并进入V86模式,那么主要是把对应386TSS中EFLAGS字段内的VM位
置1,把8086程序的有关段值填入对应386TSS中的相应段寄存器字段。此外,如果V86监控程序需
要用到LDT,那么还要填写LDTR字段;如果需要采用分页机制,那么还要填写CR3字段(当新任务
为V86模式的任务时,只装入段寄存器,而没有装入描述符投影寄存器的动作)。
<三>演示进入和离开V86模式的实例(实例十一)
下面给出一个用于演示进入和离开V86模式的实例。该实例的逻辑功能是,以驻留方式结束程序,
退出时已处于V86模式。该实例演示内容包括:两种方式进入V86模式和两种方式离开V86模式;
V86模式下的8086程序如何调用实模式下的软中断处理程序。
1.演示步骤和源程序清单
为了便于演示,本实例含有三个任务:临时任务,V86任务和INTFF任务。在实模式下做必要的
初始化工作后切换到保护模式,也即进入临时任务,开始演示。演示分两个阶段:第一阶段进
入V86任务的V86模式,并驻留退出;第二阶段进入INTFF任务,切换到临时任务,并返回实模式。
第一阶段的演示步骤如下:
(1)开始临时任务后,作切换到V86任务的准备;
(2)切换到V86任务,由于V86任务TSS中的EFLAGS字段内的VM=1,所以伴随着任务切换就进入
了V86模式。
(3)进入V86任务的V86模式后,显示提示信息,驻留结束,出现DOS提示符,第一阶段至此结束。
在V86模式下,可进行各种操作,运行其它8086程序。如果8086程序引起通用保护异常,那么在
屏幕上显示提示信息,并中止该8086程序。如果在8086程序中执行“INT 0FFH”指令,开始第二
阶段。第二阶段的演示步骤如下:
(1)进入INTFF任务后,显示提示信息,切换到临时任务;
(2)在临时任务内切换到实模式;
(3)在实模式下中止发出“INT 0FFH”的程序
源程序由如下几部分组成:
(1)全局描述符表GDT;
(2)中断描述符表IDT(只适用于V86任务);
(3)INTFF任务的TSS段、LDT段、0级堆栈段和代码段;
(4)V86任务的TSS段、LDT段、0级堆栈段、3级堆栈段及 数据段,通用保护异常处理程序
段和其它中断/异常处理程序段,V86模式下的8086程序段;
(5)临时任务的TSS段和代码段;
(6)实模式下的初始化代码段及有关过程。
源程序清单如下:
;名称:ASM11.ASM
;功能:演示进入和离开V86方式
;编译:TASM ASM11.ASM
;连接:TLINK ASM11.OBJ
;============================================================================
INCLUDE 386SCD.INC
;============================================================================
GDTSeg SEGMENT PARA USE16 ;全局描述符表数据段(16位)
;----------------------------------------------------------------------------
;全局描述符表
GDT LABEL BYTE
;空描述符
DUMMY Desc <>
;规范段描述符及选择子
Normal Desc <0ffffh,,,ATDW,,>
Normal_Sel = Normal-GDT
;显示缓冲区数据段描述符及选择子
Video Desc <07fffh,8000h,0bh,ATDW,,>
Video_Sel = Video-GDT
;----------------------------------------------------------------------------
EFFGDT LABEL BYTE
;V86任务TSS段描述符及选择子
V86TSS Desc <V86TSSLen-1,V86TSSSeg,,AT386TSS,,>
V86TSS_Sel = V86TSS-GDT
;V86任务局部描述符表的描述符及选择子[1] [2] [3] [4] [5] [6] 下一页 |