2.3 寄存器
寄存器可分为程序可见寄存器组和程序不可见寄存器组两部分。所谓程序可见寄存器,是指汇编语言程序设计人员在应用程序设计期间可以使用的寄存器,它可以在指令中出现。程序可见寄存器的应用是本课程必须熟练掌握的。而程序不可见寄存器则是指在应用程序设计时不能直接使用,但系统程序或系统运行程序期间可能要用到的寄存器。80386以上CPU才包含程序不可见寄存器组。
2.3.1 程序可见寄存器
80386及其以上型号的CPU能够处理32位数据,其寄存器长度是32位的。但为了与早期的8086等16位机的CPU保持良好的兼容性,80386以上型号的CPU中程序可见寄存器组包括多个8位、16位和32位寄存器,64位寄存器同样如此。Intel 80x86系列程序可见寄存器组包括通用寄存器、段寄存器和控制寄存器。
图2-4中给出了Intel 8086~Core2(含64位扩展)的CPU寄存器组。
图2-4 Intel 8086~Core2(含64位扩展)寄存器组
其中阴影部分为8086~80286中使用的16位寄存器模型。80386~Core2使用6个16位的寄存器(CS, DS, SS, ES, FS和GS)和其他32位的寄存器,包括4个数据寄存器EAX、EBX、ECX、EDX;3个指针寄存器EIP、ESP和EBP,其中实模式下使用16位IP/SP/BP,保护模式下使用EIP/ESP/EBP;2个变址寄存器SI/ESI和DI/EDI;1个标志寄存器EFlags, R8~R15只用于Pentium 4和Core 2中。64位扩展允许的情况下,这8个64位寄存器可以按照64、32、16、8位大小寻址。
1.通用寄存器
通用寄存器(General-Purpose Registers, GPRs)可以被程序访问,常用于传送和暂存数据,也可参与算术逻辑运算,并保存运算结果。除此之外,它们还各自具有一些特殊功能,表2-1中列举出了通用寄存器功能。
表2-1 通用寄存器功能
表2-1所列通用寄存器中,RAX、RBX、RCX和RDX可以作为64位、32位、16位和8位寄存器使用,微处理器访问不同长度数据时,可以直接引用表中的命名。如利用MOV指令修改DX的值:MOV DX,0010h。指令给DX寄存器赋值0010h,指令执行结果只会改变寄存器低16位,RDX的其他部分保持不变。又如,EAX的值为6BC30E9FH,那么, AX=0E9FH(其中AH=0EH, AL=9FH)。
64位系统中新增了8个64位寄存器:R8~R15。这些寄存器可以按照字节、字、双字或者四字方式寻址,见表2-2。可以通过添加控制字的方式访问R8~R15寄存器的低位,B表示低位字节,指7~0位,W表示低位字,指15~0位,D表示低位双字,指31~0位。对于完整的64位寄存器访问时,不需要任何控制字。值得注意的是,为了保证32位程序无须修改就能工作,Intel规定,在64位模式中,一个合法的高字节寄存器(AH、BH、CH或者DH)不能够与一个由R8~R15的寄存器所表示的字节在同一个指令中寻址。
表2-2 R8~R15寄存器8、16、32和64位访问控制字
2.段寄存器
80x86系列CPU对存储器采用分段或分页的方式管理,存储器寻址时,要用到段寄存器。段寄存器用来确定一个存储段在内存中的起始地址。80286以前的CPU有4个段寄存器,分别称为代码段寄存器CS(Code Segment)、数据段寄存器DS(Data Segment)、堆栈段寄存器SS(Stack Segment)、附加数据段寄存器ES(Extra Segment)。自80386 CPU开始,增加了FS和GS两个段寄存器。
一个程序可以由多个段组成,但对于8086~80286 CPU,由于只有4个段寄存器,所以,在某一时刻正在运行的程序,只可以访问4个当前段,而对于80386及其以上的机器,由于有6个段寄存器,因此可以访问6个当前段。在实模式下,段寄存器存放当前正在运行程序的段基址的高16位,在保护模式下,存放当前正在运行程序的段选择符。段选择符用以选择描述符表中的一个描述符,描述符描述段的基地址、长度和访问权限。在保护模式下,段寄存器仍然是选择一个内存段,只是不像实模式那样直接存放段基址的高16位。
CS指定当前代码段的段基址,代码段中存放正在运行的程序。SS指定当前堆栈段的段基址。堆栈段是在内存开辟的一块特殊区域,其中的数据访问按照后进先出(LIFO)的原则进行,允许插入和删除的一端叫作栈顶。SP(32位下为ESP)指向栈顶,SS指向堆栈段基地址。DS指定当前运行程序所使用的数据段。ES指定当前运行程序所使用的附加数据段。段寄存器FS和GS只对80386以上机器有效,用于指定当前运行程序的另外两个存放数据的存储段。虽然DS、ES、FS、GS(甚至CS、SS)所指定的段中都可以存放数据,但DS是主数据段寄存器,在默认情况下使用DS所指向段的数据。若要引用其他段中的数据,需要使用段超越前缀显式地说明。
3.专用寄存器
Intel 8086~Core2专用型寄存器包括指令指针寄存器、堆栈指针寄存器和标志寄存器等。
(1)指令指针寄存器IP/EIP/RIP
指令指针指向程序的下一条指令。当微处理器为8086/8088、80286或者工作在实模式下时,使用16位IP;80386及更高型号的微处理工作于保护模式下时,使用32位EIP。指令指针可以由转移指令或者调用指令修改。在64位模式中,RIP包含40位地址总线,可以寻址240=1 TB平坦模式地址空间。
(2)堆栈指针寄存器SP/ESP/RSP
堆栈指针,指向栈顶单元。这个寄存器作为16位寄存器时,使用SP;作为32位寄存器时,使用ESP;作为64位寄存器时,使用RSP。
(3)标志寄存器FLAG/EFLAG/RFLAG
状态标志寄存器,用来指示微处理器状态并控制它的操作。图2-5为Intel 80x86~Pentium全系列的微处理器EFLAG和FLAG寄存器。从8086开始,直到Pentium微处理器,向下兼容。8086/8088和80286使用16位FLAG寄存器。80386及更高微处理器使用32位EFLAG寄存器。64位版本中的RFLAG包含EFLAG和FLAG寄存器。
图2-5 Intel微处理器EFLAG和FLAG寄存器
32位EFLAG和16位FLAG标志寄存器用于指示微处理器的状态并控制它的操作。标志寄存器中最右边的5个标志和溢出标志(OF)根据算术运算或者逻辑运算指令结果改变。16位FLAG标志寄存器有效位含义如下。
① D0:进位标志CF(Carry Flag)。当结果的最高位(字节操作时的第7位或字操作时的第15位)产生一个进位或借位时,CF=1;否则CF=0。在移位或循环移位指令中,会把操作数的最高位(左移时)或最低位(右移时)移入CF中。
② D2:奇偶标志PF(Parity Flag)。Intel微处理器中采用奇校验,当执行结果的低8位中二进制1的个数为奇数时,PF为0;否则为1。例如,6DH+6DH=DAH完成后,DAH=1101 1010B,结果中有5个1,因此PF为0。
③ D4:辅助进位标志AF(Auxiliary Carry Flag)。在字节操作时,若低半字节(一个字节的低4位)向高半字节有进位或借位;在字操作时,若低位字节向高位字节有进位或借位,则AF=1,否则AF=0。这个标志用于十进制算术运算指令中,即通过二进制数运算调整为十进制数表示的结果。
④ D6:零标志ZF(Zero Flag)。当运算结果为零时,ZF为1;否则为0。
⑤ D7:符号标志SF(Sign Flag)。它与运算结果的最高位相同。对字节操作(8位运算)来说,是结果的第7位;对字操作(16位运算)来说,是结果的第15位。当SF=0时,结果为正数或0;当SF=1时,结果为负数。
⑥ D8:单步标志TF(Trap Enable Flag)。当TF=1时,CPU进入单步方式,在每条指令执行以后产生一个内部中断(单步中断);当TF=0时,CPU执行指令后,不产生单步中断。
⑦ D9:中断允许标志IF(Interrupt Enable Flag)。当IF=1时,允许CPU接收外部中断请求,此时为“开中断”状态;当IF=0时,屏蔽外部中断请求,此时为“关中断”状态。
⑧ D10:方向标志DF(Direction Flag)。在字符串操作指令中,当DF=0时,串操作为自动增址;当DF=1时,串操作为自动减址。STD指令置位DF, CLD指令清除DF。
⑨ D11:溢出标志OF(Overflow Flag)。带符号数运算时,当其运算结果超出了8位或16位带符号数所能表达的范围时,将产生溢出,置OF为1,否则OF清0。溢出OF与进位CF是两个不同性质的标志。
为了保证兼容性,80386的EFLAG中位11到位0和8086的FLAG完全相同。80386 CPU中新增了四个标志位:IOPL、NT、RF和VM。
① D13~D12:IOPL(I/O Privilege Level),表示I/O特权级,IOPL占2位,取值为0、1、2、3,对应四个特权级。只有特权级高于IOPL的程序才能够执行I/O指令,否则会产生异常,并将任务挂起。
② D14:NT(Nest Task),嵌套任务位。此位只用于保护模式,如果保护模式下当前的任务嵌套在其他任务中,此位为1,否则为0。IRET指令会检测NT的值。若NT=0,则执行中断的正常返回;若NT=1,则执行任务切换操作。
③ D15:RF(Resume Flag),与程序调试有关的一个控制位,在程序调试时使用。
④ D17:VM(Virtual 8086 Mode), V86模式位。VM=1时,表示当前CPU正工作在V86模式下;VM=0时,表示当前CPU工作在实模式或保护模式下。VM位只能在保护方式中由IRET指令置位(如果当前特权级为0)或在任意特权级上通过任务切换而置位。
Pentium系列新增了四个标志位:ID、VIP、VIF和AC。
① D18:AC(Alignment Check),地址对齐检查位。寻址一个字或者双字时,当地址不在字或者双字的边界上时,AC=1,否则AC=0。80486以上微处理器支持这个标志。
② D19:VIF(Virtual Interrupt Flag),虚拟中断标志。与VIP一起使用,在虚拟方式下提供中断允许标志位IF的副本。只有Pentium~Pentium 4处理器才有。
③ D20:VIP(Virtual Interrupt Pending),虚拟中断挂起标志。为Pentium~Pentium 4处理器提供有关虚拟模式中断的信息。它用于多任务环境下,为操作系统提供虚拟中断标志和中断挂起信息。
④ D21:ID(Identification),微处理器标识标志,用来指示Pentium~Pentium 4处理器,支持CPUID指令。CPUID指令是Intel IA-32架构下获得CPU信息的汇编指令,可以得到CPU类型、型号、制造商、商标、序列号、缓存等一系列CPU相关的信息。
2.3.2 保护模式下的寄存器
80386新增了一系列寄存器用于支持CPU工作于保护模式下,除了控制寄存器组有一些新增加的特性和变化外,其他更高级的处理器与80386微处理器本质上相同,因此本书主要介绍80386相关的新增寄存器,其他更高级的微处理器中的变化可以通过查看相关的微处理器手册获得。
1.控制寄存器
80386中有四个系统控制寄存器:CR0~CR3(Control Register),如图2-6所示。CR0的低5位是系统控制标志,被称为机器状态字(Machine Status Word, MSW),分页机制中用到CR3、CR2和CR0。
图2-6 80386微处理器的控制寄存器结构
CR0中机器状态字的含义如下:
① PE(Protection Mode Enable):保护模式允许标志。PE=0为实模式,CPU复位时自动进入实模式;PE=1为保护模式,可以通过软件设置PE进入或退出保护模式。
② MP(Monitor Coprocessor Extension):运算协处理器存在位。MP=1表示系统中有协处理器。
③ EM(Emulate Processor Extension):仿真位。设置该位可以使每条ESC指令引起7号中断(ESCape指令用来对80387协处理器指令编码)。EM=1时,可以利用7号中断,用软件来仿真协处理器的功能;EM=0时,用硬件控制浮点指令。
④ TS(Task Switched):任务切换标志。TS=1时,表明任务已经切换,在保护模式下, TR的内容改变将自动设置此位为1。
⑤ ET(Extension Type):协处理器选择标志。早期80386里面没有80387协处理器,因此设置此位。当处理器复位时,ET位被初始化,以指示系统中数字协处理器的类型。如果系统中存在80387协处理器,那么ET位置1;如果系统中存在80287协处理器或者不存在协处理器,那么ET位清0。80386以后的系统中,ET位被置为1,表示系统中存在协处理器。
⑥ PG(Paging Enable):分页标志。PG=1时,存储器管理单元允许分页,线性地址通过页表转换获得物理地址。PG=0时,分页功能被关闭,线性地址等于物理地址。当PG=0时,CR2和CR3寄存器无效;PG=1时,二者用于分页管理机制。
80386首次将分页机制引入80x86结构,每页大小为4KB。CR3也被称作页目录基址寄存器PDBR(Page Directory Base Register),它的高20位用于保存页目录表的起始物理地址的高20位。值得注意的是,向CR3中装入一个新值时,低12位必须为0,这是由于页目录是页对齐的,所以仅高20位有效,低12位保留未用;从CR3中取值时,低12位被忽略。
CR2也被称作页面故障线性地址寄存器(Page Fault Linear Address Register),用于发生页异常时报告出错信息。如在访问某个线性地址时,该地址的所在页没有在内存中,则发生页异常,处理器把引起页异常的线性地址保存在CR2中。操作系统中的页异常处理程序可以检查CR2的内容,定位故障页。Pentium以后的微处理器还增加了CR4控制寄存器,用于新增的几个控制位。
2.调试和测试寄存器
80386提供8个32位的调试寄存器和8个32位的测试寄存器。调试寄存器为调试软件错误提供硬件支持,其中DR0~DR3保存线性断点地址供分析;DR4、DR5为保留位;DR6为调试状态寄存器,通过该寄存器的内容可以检测异常,并允许或禁止进入异常处理程序;DR7为调试控制寄存器,用来规定断点字段的长度、断点访问类型、“允许”断点和“允许”所选择的调试条件。
测试寄存器中TR0~TR5保留,用户可见的只有TR6、TR7。它们用于转换检测缓冲区(Translation Look-aside Buffer, TLB)中测试相关存储器。
3.全局描述符表寄存器
全局存储器是一种共享系统资源,该存储器可以被所有任务访问。全局描述符表(Global Descriptor Table, GDT)是用来定义全局存储器空间的一种机制,它用段描述符来描述一个全局存储器中的段,每个GDT最多含有213=8192个描述符(8192×8=64KB)。全局描述符表可以存储在内存的任何位置,通过全局描述符表寄存器(Global Descriptor Table Register, GDTR)给出它的位置和大小。LGDT指令可以将描述符表的起始位置装入GDTR。GDTR中的32位基址是线性地址,经过分页部件转换为物理地址。
如图2-7所示,GDTR是48位的寄存器。其最低16位是限长,给出GDT的字节大小(其值比GDT的长度少1);其高32位是基址,指出GDT在物理存储器中存放的基地址。
图2-7 GDTR寄存器
例2.1 已知GDTR=0E003F0003FFH,则全局描述符表的基址是多少?这个全局描述符表有多大?里面有多少个描述符?
GDT的地址为0E003F00H;长度为3FFH+1=400H;可容纳400H/8=80H个段描述符。
4.中断描述符表寄存器
同GDTR一样,中断描述符表寄存器(Interrupt Descriptor Table Register, IDTR)也在存储器中定义了一个表,该表称为中断描述符表(IDT)。IDT中保存的不是段描述符,而是中断门描述符。每个门描述符也包含8字节,IDT最多包含256个门描述符,因为CPU最多支持256个中断。中断门指向的是中断服务程序的入口。
如图2-8所示,IDTR是48位的寄存器。其最低16位是限长,给出IDT的字节大小(其值比IDT的长度少1),其高32位是线性地址,经过分页部件转换为物理地址,指出IDT在物理存储器中存放的基地址。
图2-8 IDTR寄存器
例2.2 已知IDTR=0E003F40007FFH,则中断描述符表的基址是多少?这个中断描述符表有多大?里面有多少个描述符?
IDT的地址为0E003F400H;长度为7FFH+1=800H;可容纳800H/8=100H个描述符。
保护模式下的中断描述符表的功能,类似于实模式下的中断向量表,所不同的是,IDT的位置是可变的,由相应的描述符说明,而实模式下的中断向量表的地址是固定在物理地址00000H处的。
GDTR和IDTR的值必须在进入保护模式之前装入。在实模式下执行LGDT和LIDT指令装入GDTR和IDTR。
5.局部描述符表寄存器
保护模式提供了多任务的环境,系统中除了一个公用的全局描述符表GDT外,还为每个任务建一个局部描述符表(Local Descriptor Table, LDT)。LDT只含有与系统中某一个任务相关的各个段的描述符,即LDT定义的是某项任务用到的局部存储器地址空间。这样就可以使每一任务的代码段、数据段、堆栈段与系统其他部分隔离开。多任务环境下,由于每项任务都有自己的LDT(且每项任务最多只能有一个LDT),因此,保护模式下可以有多个LDT,而和所有任务有关的公用段的描述符放在全局描述符表GDT中。
与GDTR不同,由于LDT是面向某个任务的,局部描述符表寄存器LDTR并不直接指出LDT的位置和大小。LDTR的内容是一个16位的选择符,指向一个LDT描述符。LDT描述符指出LDT的位置和大小。在任务切换时,CPU从新任务的任务状态段中装入LDTR,从而使用新任务的局部存储器空间。
由LDTR寄存器确定LDT的位置和限长的过程如图2-9所示。①和②步由GDTR确定了GDT表在存储器中的位置和限长。LDTR中是一个选择符,它包含了LDT描述符在GDT中的索引。③步是依据LDTR在GDT中取出LDT描述符的过程。在LDT描述符中,包含有LDT的位置和限长,即④和⑤步。
图2-9 由LDTR寄存器确定LDT的位置和限长
6.任务寄存器
任务寄存器(Task Register, TR)在保护模式的任务切换机制中使用。TR是16位的选择符,其内容为索引值,它选中的是TSS描述符。TR的初值由软件装入,当执行任务切换指令时,TR的内容自动修改。
在多任务环境下,每个任务都有属于自己的任务状态段(Task State Segment, TSS), TSS中包含启动任务所必需的信息。任务状态段TSS在存储器的基地址和限长(大小)由TSS描述符指出。TSS描述符放在全局描述符表GDT中,TR内容为选择符,它指出了TSS描述符在GDT中的顺序号。由于保护模式支持多任务,每个任务都有自己的TSS,所以TR的功能是用于选择TSS描述符,由描述符说明各TSS的位置和限长。
例2.3 假定全局描述符表的基址为00011000H, TR为2108H,问TSS描述符的起始范围是多少?
TSS起始地址=00011000H+2108H=00013108H。
由于描述符为8字节,故TSS终止位置为00013108H+7H=0001310FH。
由任务寄存器TR取得TSS的过程如图2-10所示。①和②步由GDTR确定了GDT表在存储器中的位置和限长。TR是一个选择符,这个选择符中包含了TSS描述符在GDT中的索引。③步依据TR在GDT中取出TSS描述符。在第④和⑤步中,在TSS描述符中取得TSS的基址和限长。
图2-10 由TR寄存器确定TSS的位置和限长
7.段选择符
段寄存器在实模式、V86模式和保护模式下有不同的内容。实模式和V86模式下,它们的用法兼容16位CPU,即段寄存器保存20位段基址的高16位,段基址的低4位为0。在保护模式下,段寄存器的意义与实模式下的段寄存器则完全不同。它不直接存放段基址,而是存放一个索引,称为段选择符(Segment Selector)。由段选择符从全局描述符表或局部描述符表中找到8个字节长的段描述符,从而确定关于这个段的全部描述信息。选择符格式如图2-11所示。
图2-11 选择符的格式
段选择符中各字段含义如下:
① RPL(Requestor Privilege Level):请求特权级,表示将要访问的段的特权级。取值范围为0~3。00~11分别代表特权级0、1、2和3。
② TI(Table Indicator):表指示符。为0时,从全局描述符表(GDT)中选择描述符;为1时,从局部描述符表(LDT)中选择描述符。
③ Index:索引。指出要访问描述符在段描述符表中的顺序号,Index占13位。因此,顺序号的范围是0~8191。每个段描述符表(GDT或LDT)中最多有8192=213个描述符。
如图2-12所示,当DS=0023H=0000 0000 0010 0011B时,可知:Index=0 0000 0000 0100B=4, TI=0, RPL=11B=3。因为TI=0, DS的段描述符在GDT中。Index×8=4×8=0020H,该描述符在GDT表中的位置是0020H~0027H,占8个字节,RPL=3,请求特权级为3。
图2-12 由选择符确定一个描述符