x86处理器除了段寄存器外,还有通用寄存器、指令指针寄存器和标志寄存器。
一.通用寄存器
1.1 8086的16位通用寄存器
为了协助运行指令,8086设计了8个16位通用寄存器,如下表所示。这8个寄存器虽然称为通用寄存器,其实最初都有专用的目的,且都是根据专用的目的命名的,但是,除了专门用来指向栈帧的SP和BP外,这些专用的目的已成为历史,在编程时我们可灵活使用。
AX、BX、CX和DX分别提供了字节的访问方式,例如可通过AL访问低8位,通过AH访问高8位。
序号 | 通用寄存器 | 作用 |
1 | AX | (1)累加寄存器。 (2)A就取自英文Accumulator(累加)的首字母。 |
2 | BX | (1)设计初衷是存储一个数据的基址,比如数组的首地址,然后基于这个基址,使用偏移访问数组中的元素。因此,被称为基址寄存器。 (2)B取自Base的首字母。 |
3 | CX | (1)一个典型用途是作为循环的计数,loop指令就使用了CX作为循环计数。因此,被称为计数寄存器。 (2)C取自英文单词Counter。 |
4 | DX | (1)用于算数相关的运算。 (2)D取自英文单词Data的首字母。 |
5 | SI | (1)为了访问一块连续的内存,x86设计了寄存器SI,称为源变址寄存器,用于指示内存地址。通过自增SI,就可以方便地实现连续访存。 (2)SI取自Source Index的首字母。 |
6 | DI | (1)和SI寄存器演对手戏,用来访问目的内存块,称为目的变址寄存器。 (2)DI取自Destination Index的首字母。 |
7 | SP | (1)程序运行时使用栈存储局部变量,因此需要一个寄存器指向栈顶,x86设计了栈指针寄存器SP。 (2)SP取自Stack Pointer的首字母。 |
8 | BP | (1)一个栈有栈顶和栈底,BP用于指向一个栈帧的底部,所以称为基指针寄存器,程序可使用BP引用栈中的数据。 (2)BP取自Base Pointer。 |
1.2 8036的32位通用寄存器
从80386开始,这些寄存器被扩展为32位,因此相应的名称也增加了一个前缀“e”,取自单词extended的首字母。32位处理器也支持在16位模式下访问32位寄存器。
1.3 64位模式的64位通用寄存器
在64位模式下,这些寄存器扩展为64位,前缀替换为“r”,取自单词register的首字母。64位x86还引入了另外8个通用寄存器R8~R15,总共16个通用寄存器。
事实上,RAX、EAX、AX、AL在处理器器中是一个寄存器,对应同一个ID,只是处理器在运行时会根据指令使用的操作数的宽度使用相应的位数。
二.指令指针寄存器
代码段的基址存储在代码段寄存器CS中,段内偏移值存储在指令指针寄存器IP中。
IP寄存器的内容不允许程序直接修改,比如如下指令是非法的:
mov $0x1000,%ip
但可通过执行转移指令,如jmp、call、ret等指令间接修改。
三.标志寄存器
处理器在运行时需要保存很多状态。例如进行加法运算时,处理器需要记录是否有进位;进行减法运算时,需要记录是否有借位等。
处理器将这些标志组织到一个寄存器中,称为标志寄存器。标志寄存器是由若干D触发器组成的,一个标志由一个或多个D触发器组成。
序号 | 标志位 | 全称 | 作用 |
1 | CF | Carry Flag | (1)进/借位标志 (2)在加法时用于记录是否发生了进位;在减法时,用于记录是否发生了借位。 |
2 | ZF | Zero Flag | 零标志,当两个数相减时,如果结果为0,处理器将设置这一位。 |
3 | IF | Interrupt enable Flag | 中断使能标志,用来控制处理器是否响应外部中断信号。 |
标志的设置方式:
1.通过指令控制
例如x86提供了sti和cli指令来打开和关闭中断。
2.指令执行时间接设置
例如使用指令cmp比较两个寄存器ax和bx中数据的大小,cmp根据2个数的差设置ZF和CF标志:
(1)若ax = bx,则ax – bx = 0,将设置ZF = 1。
(2)若ax > bx,则不会发生借位,将设置CF = 0。
(3)若ax < bx,则一定发生借位,将设置CF = 1。
有时将CF作为借位标志(Borrow Flag)。计算机不是使用补码将减法转换为加法吗,怎么还会有借位呢?
没错,计算机使用补码将减法转换为加法,但是Intel为了便于人们直观的理解,当执行减法时,将CF转换为借位标志了。