所谓微处理器的编程结构,即是在编程人员眼中看到的微处理器的软件结构模型。软件结构模型便于人们从软件的视角去了解计算机系统的操作和运行。从这一点上说,程序员可以不必知道微处理器内部极其复杂的电路结构、电气连接或开关特性,也不需要知道各个引脚上的信号功能和动作过程。对于编程人员来说,重要的是要了解微处理器所包含的各种寄存器的功能、操作和限制,以及在程序设计中如何使用它们。进一步,需要知道微处理器外部的存储器中数据是如何组织的,微处理器如何从存储器中取得指令和数据等。
所谓“程序可见(program visible)寄存器”,是指在应用程序设计时可以直接访问的寄存器。相比之下,“程序不可见(program invisible)寄存器”是指在应用程序设计时不能直接访问,但在进行系统程序设计(如编写操作系统软件)时可以被间接引用或通过特权指令才能访问的寄存器。在80x86微处理器系列中,通常在80286及其以上的微处理器中才包含程序不可见寄存器,主要用于保护模式下存储系统的管理和控制。
图3.2给出了80x86/Pentium微处理器的寄存器模型。它实际上是一个呈现在编程者面前的寄存器集合,所以也称微处理器的编程结构。早期的8086/8088及80286微处理器为16位结构,它们所包含的寄存器是图3.2所示寄存器集的一个子集;80386、80486及Pentium系列微处理器为32位结构,它们包括了图3.2所示寄存器的全部。图中阴影区域的寄存器在8086/8088及80286微处理器中是没有的,它们是80386、80486及Pentium系列微处理器中新增加的。
由图3.2可见,该寄存器模型包括8位、16位及32位寄存器组。8位寄存器有AH、AL、BH、BL、CH、CL、DH和DL,在指令中用双字母的寄存器名字来引用它们。例如“MOV AL,BL”指令,将8位寄存器BL的内容传送到AL中;16位寄存器有AX、BX、CX、DX、SP、BP、SI、DI、IP、FLAGS、CS、DS、ES、SS、FS和GS。这些寄存器也用双字母的名字来引用。例如,“MOV BX,CX”指令,将16位寄存器CX的内容传送到BX寄存器中;32位寄存器为EAX、EBX、ECX、EDX、ESP、EBP、EDI、ESI、EIP和EFLAGS。这些寄存器一般可用三字母的名字引用,例如“MOV EBX,ECX”指令,将32位寄存器ECX的内容传送到EBX中。
图3.2中的寄存器按功能的不同可分为通用寄存器、指令指针寄存器、标志寄存器和段寄存器4种类型。下面先对这些寄存器的基本功能予以概括说明,以便对它们有一个初步了解和认识。至于对这些寄存器的深入理解和正确使用,还需要一个过程,特别是通过后续章节中指令系统及汇编语言程序设计的学习过程。
图3.2 80x86/Pentium处理器的寄存器模型
通用寄存器也称多功能寄存器,在图3.2所示的寄存器模型中,共有8个通用寄存器,即EAX、EBX、ECX、EDX、ESP、EBP、ESI、EDI,按它们的功能差别,又可分为两组,即“通用数据寄存器”及“指针寄存器和变址寄存器”。
(1)通用数据寄存器。通用数据寄存器用来存放8位、16位或32位的操作数。大多数算术运算和逻辑运算指令都可以使用这些寄存器。共有4个通用数据寄存器,它们是EAX、EBX、ECX和EDX。
EAX(Accumulator,累加器):EAX可以作为32位寄存器(EAX)、16位寄存器(AX)或8位寄存器(AH或AL)引用。如果作为8位或16位寄存器引用,则只改变32位寄存器的一部分,其余部分不受影响。当累加器用于乘法、除法及一些调整指令时,它具有专门的用途,但通常仍称之为通用寄存器。在80386及更高型号的微处理器中,EAX寄存器也可以用来存放访问存储单元的偏移地址。
EBX(Base,基址):EBX是个通用寄存器,它可以作为32位寄存器(EBX)、16位寄存器(BX)或8位寄存器(BH或BL)引用。在80x86系列的各种型号微处理器中,均可以用BX存放访问存储单元的偏移地址。在80386及更高型号的微处理器中,EBX也可以用于存放访问存储单元的偏移地址。
ECX(Count,计数):ECX是个通用寄存器,它可以作为32位寄存器(ECX)、16位寄存器(CX)或8位寄存器(CH或CL)引用。ECX可用来作为多种指令的计数值。用于计数的指令是重复的串操作指令、移位指令、循环移位指令和LOOP/LOOPD指令。移位和循环移位指令用CL计数,重复的串操作指令用CX计数,LOOP/LOOPD指令用CX或ECX计数。在80386及更高型号的微处理器中,ECX也可用来存放访问存储单元的偏移地址。
EDX(Data,数据):EDX是个通用寄存器,用于保存乘法运算产生的部分积,或除法运算之前的部分被除数。对于80386及更高型号的微处理器,这个寄存器也可用来寻址存储器数据。
(2)指针寄存器和变址寄存器。这是另外4个通用寄存器,分别是:堆栈指针寄存器ESP、基址指针寄存器EBP、源变址寄存器ESI和目的变址寄存器EDI。这4个寄存器均可作为32位寄存器引用(ESP、EBP、ESI和EDI),也可作为16位寄存器引用(SP、BP、SI和DI),主要用于堆栈操作和串操作中形成操作数的有效地址。其中,ESP、EBP(或SP、BP)用于堆栈操作,ESI、EDI(或SI、DI)用于串操作。另外,这4个寄存器也可作为数据寄存器使用。
ESP(Stack Pointer,堆栈指针):ESP寻址一个称为堆栈的存储区。通过这个指针存取堆栈存储器数据。具体操作将在本章后面介绍堆栈及其操作时再做说明。这个寄存器作为16位寄存器引用时,为SP;作为32位寄存器引用时,则为ESP。
EBP(Base Pointer,基址指针):EBP用来存放访问堆栈段的一个数据区的“基地址”。它作为16位寄存器引用时,为BP;作为32位寄存器引用时,则为EBP。
ESI(Source Index,源变址):ESI用于寻址串操作指令的源数据串。它的另一个功能是作为32位(ESI)或16位(SI)的数据寄存器使用。
EDI(Destination Index,目的变址):EDI用于寻址串操作指令的目的数据串。如同ESI一样,EDI也可作为32位(EDI)或16位(DI)的数据寄存器使用。
EIP(Instruction Pointer)是一个专用寄存器,用于寻址当前需要取出的指令字节。当CPU从内存中取出一个指令字节后,EIP就自动加1,指向下一指令字节。当微处理器工作在实模式下时,这个寄存器为IP(16位);当80386及更高型号的微处理器工作于保护模式下时,则为EIP(32位)。
程序员不能对EIP/IP进行存取操作。程序中的转移指令、返回指令以及中断处理能对EIP/IP进行操作。
EFLAGS用于指示微处理器的状态并控制它的操作。图3.3展示了80x86/Pentium系列所有型号微处理器的标志寄存器的情况。注意,从8086/8088到PentiumⅡ微处理器是向前兼容的。随着微处理器功能的增强及型号的更新,相应的标志寄存器的位数也不断扩充。早期的8086/8088微处理器的标志寄存器FLAG为16位,且只定义了其中的9位;80286微处理器虽然仍为16位的标志寄存器,但定义的标志位已从原来的9位增加到12位(新增加了3个标志位);80386及更高型号的微处理器则采用32位的标志寄存器EFLAGS,所定义的标志位也有相应的扩充。
图3.3 80x86/Pentium系列微处理器的标志寄存器
下面着重介绍8086/8088系统中所定义的9个标志位——OF、DF、IF、TF、SF、ZF、AF、PF、CF。在这9个标志位中,有6位(即CF、PF、AF、ZF、SF和OF)为状态标志;其余3位(即TF、IF和DF)为控制标志。状态标志与控制标志的作用有所不同。顾名思义,状态标志反映微处理器的工作状态,如执行加法运算时是否产生进位,执行减法运算时是否产生借位,运算结果是否为0等;控制标志对微处理器的运行起特定的控制作用,如以单步方式运行还是以连续方式运行,在程序执行过程中是否允许响应外部中断请求等。
6个状态标志的功能简述如下:
(1)进位标志CF(Carry Flag):运算过程中最高位有进位或借位时,CF置1;否则置0。
(2)奇偶标志PF(Parity Flag):该标志位反映运算结果低8位中1的个数情况,若为偶数个1,则PF置1;否则置0。它是早期Intel微处理器在数据通信环境中校验数据的一种手段。今天,奇偶校验通常由数据存储和通信设备完成,而不是由微处理器完成。所以,这个标志位在现代程序设计中很少使用。
(3)辅助进位标志AF(Auxiliary carry Flag):辅助进位标志也称“半进位”标志。若运算结果低4位中的最高位有进位或借位,则AF置1;否则置0。AF一般用于BCD运算时是否进行十进制调整的依据。
(4)0标志ZF(Zero Flag):反映运算结果是否为0。若结果为0,则ZF置1;否则置0。
(5)符号标志SF(Sign Flag):记录运算结果的符号。若结果为负,则SF置1;否则置0。SF的取值总是与运算结果的最高位相同。
(6)溢出标志OF(Overflow Flag):反映有符号数运算结果是否发生溢出。若发生溢出,则OF置1;否则置0。所谓溢出,是指运算结果超出了计算装置所能表示的数值范围。例如,对于字节运算,数值表示范围为-128~+127;对于字运算,数值表示范围为-32768~+32767。若超过上述范围,则发生了溢出。溢出是一种差错,系统应做相应的处理。
在机器中,溢出标志的判断逻辑式为“OF=最高位进位⊕次高位进位”。
注意: “溢出”与“进位”是两个不同的概念。某次运算结果有“溢出”,不一定有“进位”;反之,有“进位”,也不一定发生“溢出”。另外,“溢出”标志实际上是针对有符号数运算而言;对于无符号数运算,溢出标志OF是无定义的,无符号数运算的溢出状态可通过进位标志CF来反映。
下面,通过具体例子来进一步熟悉这6个状态标志的功能定义(为了便于表示,在例子中提前使用了第4章中介绍的MOV指令及ADD指令)。
【 例3.1 】 指出执行如下指令后,标志寄存器中各状态标志值。
MOV AX, 31C3H ADD AX, 5264H
上述两条指令执行后,在CPU中将完成如下二进制运算:
所以,根据前面给出的6个状态标志的功能定义,可得:
OF=1 (最高位进位⊕次高位进位=0⊕1=1);
SF=1 (SF与运算结果的最高位相同);
ZF=0 (运算结果不为0);
AF=0 (运算结果低4位中的最高位无进位);
PF=1 (运算结果低8位中1的个数为偶数);
CF=0 (运算结果最高位无进位)。
在本例中可以看到溢出标志OF和进位标志CF的情况。这里,OF=1,说明有符号数运算时发生了溢出;但进位标志CF=0。
作为练习,请指出下述两条指令执行后的6个状态标志的情况:
MOV AX, 0E125H ADD AX, 0C25DH
3个控制标志的功能分述如下:
(1)方向标志DF(Direction Flag):用来控制串操作指令的执行。若DF=0,则串操作指令的地址自动增量修改,串数据的传送过程是从低地址到高地址的方向进行;若DF=1,则串操作指令的地址自动减量修改,串数据的传送过程是从高地址到低地址的方向进行。
(2)中断标志IF(Interrupt Flag):用来控制对外部可屏蔽中断请求的响应。若IF=1,则CPU响应外部可屏蔽中断请求;若IF=0,则CPU不响应外部可屏蔽中断请求。
(3)陷阱标志TF(Trap Flag):陷阱标志也称单步标志。当TF=1时,CPU处于单步方式;当TF=0时,则CPU处于连续方式。单步方式常用于程序的调试。
由图3.2可以清楚地看到,微处理器寄存器集合中的另一组寄存器为16位的段寄存器,用于与微处理器中的其他寄存器联合生成存储器地址。80x86/Pentium系列的微处理器中有4个或6个段寄存器。对于同一个微处理器而言,段寄存器的功能在实模式下和保护模式下是不相同的。本章主要针对实模式下段寄存器的基本功能进行介绍。下面概要列出每个段寄存器及其在系统中的功能:
(1)代码段寄存器(Code Segment,CS):代码段是一个存储区域,用以保存微处理器使用的程序代码。代码段寄存器CS定义代码段的起始地址。
(2)数据段寄存器(Data Segment,DS):数据段是包含程序所使用的大部分数据的存储区。与代码段寄存器CS类似,数据段寄存器DS用以定义数据段的起始地址。
(3)附加段寄存器(Extra Segment,ES):附加段是为某些串操作指令存放目的操作数而附加的一个数据段。附加段寄存器ES用以定义附加段的起始地址。
(4)堆栈段寄存器(Stack Segment,SS):堆栈是计算机存储器中的一个特殊存储区,用以暂时存放程序运行中的一些数据和地址信息。堆栈段寄存器SS定义堆栈段的首地址。通过堆栈段寄存器SS和堆栈指针寄存器ESP/SP可以访问堆栈栈顶的数据。另外,通过堆栈段寄存器SS和基址指针寄存器EBP/BP可以寻址堆栈栈顶下方的数据。具体的实现方法,将在后续章节介绍。
(5)段寄存器FS和GS:这两个段寄存器仅对80386及更高型号的微处理器有效,以便程序访问相应的两个附加的存储器段。