本文介绍操作系统类指令。其中的某些指令始于286。通常只在操作系统代码中使用这些指令,而不在应用程序中使用这些指令。这是把它们称为操作系统类指令的原因。为了保证操作系统的安全,保护模式下的80386支持四个特权级。相应地,这些操作系统类指令也可分为三种:实模式和任何特权级下可执行的指令、实模式及特权级0下可执行的指令和仅在保护模式下执行的指令。
<一>实模式和任何特权级下可执行的指令
1.存储全局和中断描述符表寄存器指令
全局描述符表GDT和中断描述符表IDT包含着系统的重要数据,对应的两个描述符表寄存器GDTR和IDTR含有这两张表的定位信息。利用存储描述符表寄存器指令能把描述符表寄存器的内容保存到指定的存储单元。这样,访问这些存储单元就可获得描述符表的定位信息。与GDT和IDT被所有任务共享不同,LDT是每个任务私有的,所以存储局部描述符表寄存器LDTR的指令不在所列。
(1)存储全局描述符表寄存器指令
存储全局描述符表寄存器指令的格式如下:
SGDT QWORD PTR DST
其中操作数DST是48位(6字节)的存储器操作数。该指令的功能是把全局描述符表寄存器GDTR的内容存储到存储单元DST。GDTR中的16位界限存入DST的低字,GDTR中的32位基地址存入DST的高双字。该指令对标志没有影响。
(2)存储中断描述符表寄存器指令
存储中断描述符表寄存器指令的格式如下:
SIDT QWORD PTR DST
其中操作数DST是48位(6字节)的存储器操作数。该指令的功能是把中断描述符表寄存器IDTR的内容存储到存储单元DST。IDTR中的16位界限存入DST的低字,IDTR中的32位基地址存入DST的高双字。该指令对标志没有影响。
2.存储机器状态字指令
存储机器状态字指令的格式如下:
SMSW DST
其中操作数DST可以是16位(字)的存储器操作数或寄存器。该指令的功能是把机器状态字的内容存储到DST。该指令对标志没有影响
80386有此指令是为了包含80286的指令集,以便与其兼容。由于80386的控制寄存器CR0的低16位等同于80286的机器状态字,所以,在为80386编程时,如果需要存储机器状态字,那么最好使用存储CR0寄存器的指令。
<二>实模式及特权级0下可执行的指令
下列指令涉及设置关键的寄存器,所以只能在实模式和保护模式的特权级0下执行。为了从初始时的实模式转入保护模式,必须做基本的准备工作。例如,设置妥全局描述符表寄存器GDTR等。这是允许下列指令在实模式下工作的原因。
在保护模式下,如果当前特权级(CPL)不为0,执行这些指令将引起错误码为0的通用保护故障。在虚拟8086方式下,因为其CPL为3,所以执行这些指令也将会引起出错码为0的通用保护故障。
1.清任务切换标志指令
每当任务切换时,控制寄存器CR0中的任务切换标志TS被自动置1。这样安排的原因已在前文中说明过。清任务切换标志指令的功能是把TS标志清0,该指令的格式如下:
CLTS
该指令仅影响TS标志,对其它标志没有影响。
2.暂停指令
暂停指令的格式如下:
HLT
该指令使处理器暂停执行。暂停之后的系统,只有在接受一个已经启用的中断或让系统复位,才能重新启动。该指令对标志没有影响。
3.装载全局描述符表和中断描述符表寄存器的指令
(1)装载全局描述符表寄存器指令
装载全局描述符表寄存器指令的格式如下:
LGDT QWORD PTR SRC
其中操作数SRC是48位(6字节)的存储器操作数。该指令的功能是把存储器中的伪描述符装入到全局描述符表寄存器GDTR中。伪描述符SRC的结构如前文所述结构类型PDESC所示,低字是以字节为单位的段界限,高双字是段基地址。该指令对标志没有影响。
(2)装载中断描述符表寄存器指令
装载中断描述符表寄存器指令的格式如下:
LIDT QWORD PTR SRC
其中操作数SRC是48位(6字节)的存储器操作数。该指令的功能是把存储器中的伪描述符装入到中断描述符表寄存器IDTR中。伪描述符SRC的结构如前文所述结构类型PDESC所示,低字是以字节为单位的段界限,高双字是段基地址。该指令对标志没有影响。
4.装载机器状态字指令
装载机器状态字指令的格式如下:
LMSW DST
其中操作数DST可以是16位(字)的存储器操作数或寄存器。该指令的功能是把DST的内容装载到机器状态字。该指令对标志没有影响, 将PE位置为1,便进入保护模式。在80286中,没有控制寄存器,为进入保护模式需要通过该指令把MSW中的PE位置为1。如果的确是这样,那么LMSW指令后面必须紧跟一条转移指令。
80386有此指令是为了包含80286的指令集,以便与其兼容。由于80386的控制寄存器CR0的低16位等同于80286的机器状态字,所以,在为80386编程时,如果需要装载机器状态字,那么最好使用控制寄存器传送指令。
5.控制寄存器数据传送指令
控制寄存器数据传送指令的一般格式如下:
MOV DST,SRC
控制寄存器数据传送指令实现80386的控制寄存器和32位通用寄存器之间的数据传送。所以,操作数SRC和DST可以是80386使用的三个控制寄存器和任一32位通用寄存器,但不能同时是控制寄存器。该指令对标志没有影响。
6.调试寄存器数据传送指令指令
调试寄存器数据传送指令的一般格式与上面的控制寄存器数据传送指令的格式相同。功能是实现80386的调试寄存器和32位通用寄存器之间的数据传送。操作数SRC和DST可以是80386使用的6个调试寄存器和任一32位通用寄存器,但不能同时为调试寄存器。该指令不影响标志。
80386可使用的6个调试寄存器记为:DR0、DR1、DRW、DR3、DR6和DR7。其它说明与控制寄存器数据传送指令相同。
7.测试寄存器数据传送指令
测试寄存器数据传送指令的一般格式与上面的控制寄存器数据传送指令的格式相同。功能是实现80386的测试寄存器和32位通用寄存器之间的数据传送。80386使用的2个测试寄存器是TR6和TR7。其它说明与控制寄存器数据传送指令相同。
<三>只能在保护模式下执行的指令
下面介绍的指令只能在保护模式下执行,如果在实模式下执行这些指令,将引起非法操作码故障(向量号为6)。
1.装载和存储局部描述符表寄存器指令
(1)装载局部描述符表寄存器指令
装载局部描述符表寄存器指令的格式如下:
LLDT SRC
其中,操作数SRC可以是16位通用寄存器或存储单元。该指令的功能是把SRC中的内容作为指示局部描述符表LDT的选择子装入到LDTR寄存器。该指令不影响标志。
操作数SRC给定的选择子应该指示GDT中的类型为LDT的描述符。但LRC也可是一个空选择子,如果这样的话,表示暂时不使用局部描述符表LDT。
若CPL不为0,那么执行该指令将产生出错码为0的通用保护故障。若被装载的选择子不指示GDT中的描述符,或者描述符类型不是LDT描述符,那么产生通用保护故障,错误码由该选择子构成。
象段寄存器那样,LDTR也有两部分。在把指示LDT的选择子装入到LDTR可见部分时,处理器自动把选择子所索引的LDT描述符中的段基地址等信息保存到不可见的高速缓冲寄存器中。
(2)存储局部描述符表寄存器指令
存储局部描述符表寄存器指令的格式如下:
SLDT DST
其中,操作数DST可以是16位通用寄存器或存储单元。该指令的功能是把局部描述符表寄存器 LDTR的内容存储到存储单元DST中,也即把指向当前任务LDT的选择子存储到DST中。该指令不影响标志。
2.装载和存储任务寄存器指令
任务寄存器TR指示当前任务状态段TSS。随着任务的切换,TR的内容也随之改变;如果任务嵌套,那么TR的原值作为链接字保存到新任务的TSS中。但有时候需要直接地装载或者保存TR,这就需要使用装载TR指令和存储TR指令。
(1)装载任务寄存器指令
装载任务寄存器指令的格式如下:
LTR SRC
其中,操作数SRC可以是16位通用寄存器或存储单元。该指令的功能是将SRC作为指示TSS描述符的选择子装载到任务寄存器TR。由前文可知,TR分为两部分,即程序员可见部分和不可见的高速缓冲寄存器部分。在把TSS的选择子装入到TR可见部分时,处理器自动把选择子所索引的描述符中的段基地址等信息保存到不可见的高速缓冲寄存器中。所以,SRC表示的选择子不能为空,必须索引位于GDT中的描述符,并且描述符类型必须是可用TSS,该加载的TSS被处理器自动标为 “忙”。该指令对标志没有影响。
若CPL不为0,那么执行该指令将产生错误码为0的通用保护故障。若被加载的选择子不指示GDT中的可用TSS描述符,那么产生通用保护故障,错误码由该选择子构成。
(2)存储任务寄存器指令
存储任务寄存器指令的格式如下:
STR DST
其中,操作数DST可以是16位通用寄存器或存储单元。该指令的功能是把TR所含的指示当前任务TSS描述符的选择子存储到DST。该指令不影响标志。
3.调整申请特权级指令
调整申请特权级指令的格式如下:
ARPL OPRD1,OPRD2
其中,操作数OPRD1可以是16位通用寄存器或存储单元,操作数OPRD2是16位通用寄存器。该指令把操作数OPRD1和OPRD2视为两个选择子,用OPRD2的申请特权级(RPL)去检查OPRD1的RPL。选择子OPRD1和OPRD2的RPL分别由它们的最低2个位规定。如果OPRD1的RPL小于OPRD2的RPL,那么零标志ZF被置1,并把OPRD2的RPL值赋予OPRD1的RPL(使两个操作数的最低2位相等);否则,零标志ZF被清0。OPRD1和OPRD2都可为空选择子。该指令只影响ZF标志。
4.装载存取权指令
装载存取权指令的格式如下:
LAR OPRD1,OPRD2
其中,操作数OPRD1可以是16位或32位通用寄存器,操作数OPRD2是16位通用寄存器或存储单元,也可以是32位通用寄存器或存储单元。操作数OPRD1和OPRD2的尺寸必须一致。该指令把操作数OPRD2视为选择子(当为32位时,仅使用低16位),如果OPRD2所指示的描述符满足如下条件,那么零标志ZF被置1,并把描述符内的属性字段装入OPRD1;否则,ZF清0,OPRD1保持不变。
(1)在描述符表的范围内;
(2)是存储段描述符或系统段描述符或任务门描述符或调用门描述符;
(3)CPL和OPRD2的RPL都不大于DPL。
在满足条件的情况下,装入到OPRD1的由OPRD2所指示的描述符中的属性字段是指描述符的高4字节和00FXFF00H相与的结果,其中X表示第16位到第19位无定义。注意,如果指令使用16位操作数,那么只有高4字节中的低字被装入到OPRD1,即装入到OPRD1的属性字段不包括G位和AVL位等。该指令只影响ZF标志。
5.装载段界限指令
装载界限指令的格式如下:
LSL OPRD1,OPRD2
其中,操作数OPRD1可以是16位或32位通用寄存器,操作数OPRD2是16位通用寄存器或存储单元,也可以是32位通用寄存器或存储单元。操作数OPRD1和OPRD2的尺寸必须一致。该指令把操作数OPRD2视为选择子(当为32位时,仅使用低16位),如果OPRD2所指示的描述符满足如下条件,那么零标志ZF被置1,并把描述符内的界限字段装入OPRD1;否则,ZF清0,OPRD1保持不变。
(1)在描述符表的范围内;
(2)是存储段描述符或系统段描述符,而非门描述符;
(3)CPL和OPRD2的RPL都不大于DPL。
在满足条件的情况下,装入到OPRD1的由OPRD2所指示的描述符中的界限字段以字节位为单位。如果描述符中的界限字段以4K字节为单位(G=1),那么装入到OPRD1时被左移12位,空出的低位全部填成1。注意,如果指令使用16位操作数,那么只有段界限的低16位被装入到OPRD1。该指令只影响ZF标志。
6.读写检验指令
利用读检验指令和写检验指令可分别检查在当前特权级上指定的段能否读或写,从而避免引起不必要的异常。
(1)读检验指令
读检验指令的一般格式如下:
VERR OPRD
其中,操作数OPRD可以是16位通用寄存器或存储单元,也可以是32位通用寄存器或存储单元。该指令的功能是把OPRD的内容作为一个选择子(当32位时仅使用低16位),判断在当前特权级上该选择子所指示的段是否可读。如果该选择子指示合法的一个存储段描述符,并且在当前特权级上可读所描述的段,那么零标志ZF被置为1,否则ZF被清0。该指令只影响ZF标志。
(2)写检验指令
写读检验指令的一般格式如下:
VERW OPRD
其中,操作数OPRD可以是16位通用寄存器或存储单元,也可以是32位通用寄存器或存储单元。该指令的功能是把OPRD的内容作为一个选择子(当32位时仅使用低16位),判断在当前特权级上该选择子所指示的段是否可写。如果该选择子指示合法的一个存储段描述符,并且在当前特权级上可写所描述的段,那么零标志ZF被置为1,否则ZF被清0。该指令只影响ZF标志。
<四>显示关键寄存器内容的实例(实例八)
为了更好地说明操作系统类指令的使用,下面给出一个显示80386关键寄存器内容的实例。该实例的逻辑功能是,显示系统中GDTR、IDTR、LDTR和TR等关键寄存器的当前内容。实例八的源程序清单如下:
;名称:ASM8.ASM
;功能:显示关键寄存器内容及说明操作系统类指令的使用
;编译:TASM ASM8.ASM
;连接:TLINK ASM8.OBJ
;----------------------------------------------------------------------------
INCLUDE 386SCD.INC
;----------------------------------------------------------------------------
GDTSeg SEGMENT PARA USE16 ;全局描述符表数据段(16位)
;----------------------------------------------------------------------------
GDT LABEL BYTE
;空描述符
DUMMY Desc <>
;规范段描述符及选择子
Normal Desc <0ffffh,,,ATDW,,>
Normal_Sel = Normal-GDT
;----------------------------------------------------------------------------
EFFGDT LABEL BYTE
;临时任务代码段描述符及选择子
TempCode Desc <0ffffh,TempCodeSeg,,ATCE,,>
TempCode_Sel = TempCode-GDT
;缓冲区段描述符及选择子
Buffer Desc <BufferLen-1,BufferSeg,,ATDW,,>
Buffer_Sel = Buffer-GDT
;测试描述符1及选择子
Test1 Desc <1111h,,,92h,87h,>
Test1_Sel = Test1-GDT
TestR_Sel = Test1-GDT+RPL3
;测试描述符2及选择子
Test2 Desc <2222h,,,82h,17h,>
Test2_Sel = Test1-GDT
;----------------------------------------------------------------------------
GDNum = ($-EFFGDT)/(SIZE Desc) ;需特殊处理的描述符数
GDTLen = $-GDT ;全局描述符表长度
;----------------------------------------------------------------------------
GDTSeg ENDS ;全局描述符表段定义结束
;----------------------------------------------------------------------------
BufferSeg SEGMENT PARA USE16 ;缓冲区数据段
;----------------------------------------------------------------------------
GDTR_V PDesc <> ;存放GDTR
IDTR_V PDesc <> ;存放IDTR
;----------------------------------------------------------------------------
MSW_V DW 0 ;存放机器状态字
LDTR_V DW 0 ;存放LDTR选择子
TR_V DW 0 ;存放TR选择子
CR0_V DD 0 ;存放控制寄存器CR0
CR3_V DD 0 ;存放控制寄存器CR3
DR7_V DD 0 ;存放调试寄存器DR7
Test_RPL DW 0
;----------------------------------------------------------------------------
Test1_SLD DD 0 ;演示用变量
Test1_ARD DD 0
Test1_SLW DW 0
Test1_ARW DW 0
Test1_RF DW 0
Test1_WF DW 0
;----------------------------------------------------------------------------
Test2_SLD DD 0 ;演示用变量
Test2_ARD DD 0
Test2_SLW DW 0
Test2_ARW DW 0
Test2_RF DW 0
Test2_WF DW 0
;----------------------------------------------------------------------------
BufferLen = $
BufferSeg ENDS
;----------------------------------------------------------------------------
TempCodeSeg SEGMENT PARA USE16 ;临时代码段
ASSUME CS:TempCodeSeg,DS:BufferSeg
;----------------------------------------------------------------------------
Virtual PROC FAR
mov ax,Buffer_Sel
mov ds,ax
mov eax,cr0 ;存储CR0
mov CR0_V,eax
mov eax,cr3 ;存储CR3
mov CR3_V,eax
mov eax,DR7 ;存储DR7
mov DR7_V,eax
str TR_V ;存储TR
sldt LDTR_V ;存储LDTR
mov Test_RPL,Test1_Sel
mov ax,TestR_Sel
arpl Test_RPL,ax ;说明调整申请特权及指令
mov bx,0
mov ax,Test1_Sel
Lab1: mov edx,0
mov cx,0
lsl edx,eax ;说明装载段界限指令
lsl cx,ax
mov Test1_SLD[bx],edx
mov Test1_SLW[bx],cx
mov edx,0
mov cx,0
lar edx,eax ;说明装载存取权指令
lar cx,ax
mov Test1_ARD[bx],edx
mov Test1_ARW[bx],cx
mov Test1_RF[bx],0
verr ax ;说明读检验指令
jnz Lab2
mov Test1_RF[bx],1
Lab2: mov Test1_WF[bx],0
verw ax ;说明写检验指令
jnz Lab3
mov Test1_WF[bx],1
Lab3: add bx,16
mov ax,Test2_Sel
cmp bx,32
jb Lab1
;准备返回实方式
mov ax,Normal_Sel
mov ds,ax
mov eax,cr0
and al,11111110b
mov cr0,eax ;返回实方式
JUMP16 <SEG Real>,<OFFSET Real>
Virtual ENDP
;----------------------------------------------------------------------------
TempCodeSeg ENDS
;----------------------------------------------------------------------------
RCodeSeg SEGMENT PARA USE16
ASSUME CS:RCodeSeg,DS:BufferSeg
;----------------------------------------------------------------------------
VGDTR PDesc <GDTLen-1,>
;----------------------------------------------------------------------------
Start PROC
mov ax,BufferSeg
mov ds,ax
sgdt GDTR_V
sidt IDTR_V
smsw MSW_V
;准备转入保护方式
push cs
pop ds
cld
call InitGDT
mov bx,OFFSET VGDTR
lgdt [bx]
cli
mov eax,cr0
or al,1
;转入保护方式
mov cr0,eax
JUMP16 <TempCode_Sel>,<OFFSET Virtual>
Real: ;回到实方式
sti
;为了简单,略去了显示相关变量内容的部分代码
mov ax,4c00h
int 21h
Start ENDP
;----------------------------------------------------------------------------
InitGDT PROC
push ds
mov ax,GDTSeg
mov ds,ax
mov cx,GDNum
mov si,OFFSET EFFGDT
InitG: mov ax,[si].BaseL
movzx eax,ax
shl eax,4
shld edx,eax,16
mov WORD PTR [si].BaseL,ax
mov BYTE PTR [si].BaseM,dl
mov BYTE PTR [si].BaseH,dh
add si,SIZE Desc
loop InitG
pop ds
mov bx,16
mov ax,GDTSeg
mul bx
mov WORD PTR VGDTR.Base,ax
mov WORD PTR VGDTR.Base+2,dx
ret
InitGDT ENDP
;----------------------------------------------------------------------------
RCodeSeg ENDS
END Start
<五>特权指令
特权指令是指保护方式下只有当前特权级CPL=0时,才可执行的指令。如果CPL不等于0而执行它们,那么会引起通用保护异常。从上面介绍的操作系统类指令可归纳出如下表所示的80386特权指令。这些特权指令在构成完善的保护机制方面起了重要的作用。
特
权
指
令 指 令 功 能 指 令 功 能
CLTS 清除CR0中的TS位 LTR 装入TR寄存器
HLT 停机 MOV CRn,reg 装入控制寄存器
LGDT 装入GDTR寄存器 MOV reg,CRn 保存控制寄存器
LIDT 装入IDTR寄存器 MOV DRn,reg 装入调试寄存器
LLDT 装入LDTR寄存器 MOV reg,DRn 保存调试寄存器
LMSW 装入MSW寄存器(CR0的低16位)
从上表可见,装入GDTR、IDTR、LDTR、TR和MSW的指令都是特权指令,而存储上述寄存器的指令不是特权指令。这表示,保护模式下任何程序可获得这些寄存器的值,但只有特权级0的程序才能够改变这些寄存器的值。从上表还可以看出,设置和存储控制寄存器及调试寄存器的指令都是特权指令。