2.3 寄存器

80386中应用程序员感兴趣的有16个寄存器。如图2-5所示,这些寄存器被分成以下几个基本类型:

1.通用寄存器。这些32为通用寄存器主要用来数学和逻辑运算。 2.段寄存器。这些特殊目的寄存器允许系统软件设计者选择平坦模式或是段模式。这六个寄存器决定了,任何时候,哪段存储器可以被寻址。 3.状态和指令寄存器。这些特殊目的寄存器用于记录和改变80386处理器状态的一些特征。

2.3.1 通用寄存器

80386的32位通用寄存器包括EAX, EBX,ECX, EDX, EBP, ESP, ESI以及EDI。这些寄存器可以互换使用,存储逻辑和算术操作数。也可以互换地用于地址计算(有个例外,ESP不能被用作索引操作数)。

如图2-5所示,这8个寄存器中的低位字都有单独的名称,可以单独使用。这有利于处理16位数据项,以及和8086和80286保持兼容。字寄存器被命名为AX, BX, CX, DX, BP, SP, SI以及DI。

图2-5同样表明,16为寄存器AX, BX, CX和DX的每个字节都有单独的名称,可以独立使用。这有利于处理字符和8位数据项。字节寄存器被称为AH, BH, CH, DH(高位字节);AL, BL, CL, DL(低位字节)。

所有这些寄存器均可以用来地址计算,作为大多数算术和逻辑计算的结果;然而,一些功能要求使用特定的寄存器。通过隐式的使用这些寄存器,80386架构可以使编码变得更紧凑。使用特定寄存器的指令包括:双精度乘法和除法,I/O,字符串指令,变换,循环,变量移位和循环,堆栈操作。

2.3.2 段寄存器

段寄存器给了系统软件设计人员在各种存储器组织模式之间选择的自由。存储器模式的实现是第II部分的主题-系统编程。设计人员可以选择一种模式,这种模式下,应用程序不需要改变段寄存器,在这种情况下,应用程序员可以跳过这章。

完整的程序通常包含许多不同的模块,每个由指令和数据构成。然而,在任何给定的程序执行时间段,只有一小部分程序模块的子集在使用。80386架构可以利用了这一点,它提供直接访问当前模块环境的手段,在有需要时访问其他段。

在任何给定的时间,六个存储器段可以在程序执行期间被立即访问。段寄存器CS, DS, SS, ES, FS和GS用来标识这六个当前段。这些寄存器每个都表示一个特殊类型的段,就像图2-6中关联助记符(”code”, “data”, 或者”stack”)表示的那样。每个寄存器唯一地确定一个特殊段,这些段组成了程序,在稍后以最快的速度被立即访问。

包含当前指令执行序列的段称为当前代码段;它通过CS寄存器来声明。80386将指令指针的内容作为偏移量,从这个段来提取所有当前指令。CS寄存器在段内控制传输指令执行(例如,CALL和JMP)后被隐式的改变。

子程序,参数和程序活动记录通常要求在堆栈上分配一块存储空间。所有堆栈操作都用SS来定位堆栈。与CS不同,SS可以被显示的加载,从而允许程序动态的定义堆栈。

DS, ES, FS和GS寄存器允许声明4个数据段,每个都可以被正在执行的程序寻址。访问4个独立的数据区帮助程序高效的访问不同的数据类型;例如,一个数据段寄存器指向当前模块的数据结构,另一个执行上层模块导出的数据,另一个指向动态创建的数据,而另一个指向和其他任务共享的数据。段内的操作数可以在指令中声明偏移量或通过通用寄存器来间接访问。

取决于数据结构(比如说,数据被放到一个或更多段内的方式),一个程序可能需要访问多于4个数据段。要想访问额外的段,DS, ES, FS和GS可以在程序执行期间在控制下改变。只需要在访问数据的指令前面执行一条指令来加载合适的段寄存器。

处理器会把一个基地址和每个由段寄存器选择的段联系起来。要想寻址段内的数据项,32位的偏移量被加到段基址上。一旦选择了一个段(通过加载段选择符到段寄存器),数据操作指令只需要声明偏移量。如果只声明了偏移量,使用一个简单的规则来确定使用哪个段寄存器。

2.3.3 堆栈的实现

堆栈操作通过下面的三个寄存器变得容易些:

1.堆栈段寄存器(SS)。堆栈的实现在存储器中。系统可以有多个堆栈,只受限于堆栈总数的上限值。堆栈可以有4G空间,堆栈的最大长度。在同一时刻只能有一个堆栈可以访问-SS定位的那个。这个是当前堆栈,常被简称为“这个“堆栈。处理器的所有堆栈操作自动使用SS。 2.堆栈指针寄存器(ESP)。ESP指向向下增长堆栈(TOS)的顶部。ESP被下面的操作间接引用:PUSH和POP操作,子程序调用和返回,以及中断操作。当数据被放入堆栈时(见图2-7),处理器减小ESP,然后将数据写入新的TOS。当数据从堆栈弹出时,处理器从TOS中拷贝数据,然后增加ESP。换句话说,堆栈在内存中项低位地址方向增长。 3.堆栈帧基指针(EBP)。EBP是访问堆栈中的数据结构,变量和动态分配的工作空间的最好选择。EBP经常通过相对堆栈的一个固定参考点而不是当前TOS来访问数据项。它的典型用法是标识为当前进程建立的当前堆栈帧的基地址。当EBP用作偏移计算的基址寄存器是,偏移量自动在当前堆栈段内(即,由SS选择的当前段)计算。因为SS不用显示的声明,这种编码指令更有效。EBP也可以用作是通过其他寻址段寄存器的索引。

2.3.4 标志位寄存器

标志位寄存器为32为寄存器,命名为EFLAGS。图2-8定义了寄存器中位。这些标志空着特定的操作并表示80386的状态。

EFLAGS的低16位被命名为FLAGS,可以单独使用。该特性在执行8086和80286代码时非常有用,因为EFLAGS的这部分和8086和80286的FLAGS寄存器是一样的。

标志位可以分为3组:状态标志位,控制标志位,以及系统标志位。系统标志位的讨论推迟到第II部分。

2.3.4.1 状态标志位

EFLAGS寄存器的状态标志位允许一条指令的结果影响下一条指令。算术指令使用OF, SF, ZF, AF, PF和CF。SCAS(扫描字符串),CMPS(比较字符串),以及LOOP指令使用ZF来通知它们的动作已结束。有些指令可以在算术指令执行之前设置,清除以及取反CF。每个状态标志位的定义参见附录C。

2.3.4.2 控制标志位

EFLAGS的控制标志位DF控制着字符串指令。

DF(方向标志位,位10)

设置DF标志位使字符串指令自动递减;也就是,从高位地址到低位地址处理字符串。清除DF使字符串指令自动递增,从低位地址到高位地址处理字符串。

2.3.4.3 指令指针

指令指针寄存器(EIP)包含相对于当前代码段内下一个将执行的指令序列的地址偏移量。对于程序员来说,指令指针不是直观可见的;它被控制传输指令,中断以及异常隐式控制。

如图2-9所示,EIP的低位16位被命名位IP,可以单独使用。该特性对于执行位8086额80286设计的指令非常有用。