跳到主要内容

寄存器

一个典型的CPU中,往往由以下几个部分组成:

  • 运算器进行信息处理
  • 控制器控制各个器件进行工作
  • 寄存器进行信息存储
  • 内部总线连接各种器件

**对于一个汇编程序员而言,CPU中主要部件就是寄存器。**寄存器是程序员可以用指令读写的部件,程序员可以通过修改各种寄存器的值来实现对CPU的控制。

8086CPU中有14个寄存器

  • 通用寄存器:AX,BX,CX,DX,SI,DI,SP,BP
  • 特殊用途寄存器:IP,FLAGS
  • 段寄存器:CS,SS,DS,ES

通用寄存器

用途

  1. AX:累加寄存器,常用于存储计算结果,如mul, div指令的最终结果,也常用于存储函数的返回值
  2. BX:基址寄存器,常用于存储基址(8086内存寻址方式相关内容)
  3. CX:计数器,常用于存储循环次数,函数长度等内容
  4. DX:数据寄存器,常用于存储运算过程中的数据
  5. SI:源变址寄存器,常用于存储源数据的地址中的变址部分
  6. DI:目的变址寄存器,常用于存储目的数据地址中的变址部分
  7. SP:堆栈指针,常用于指向栈顶
  8. BP:基址指针

这些通用寄存器均为16bit,仅有AX,BX,CX,DX这四个寄存器可以分为两个8bit寄存器。AX可以分为AH(High)和AL(Low),AH为高位字节,AL为低位字节,其余同AX。

数据在寄存器中的存储

在8086CPU中,可以一次性处理的数据有两种:

  1. 字节型数据(8bit)
  2. 字型数据(16bit)

这里主要讲字型数据的存储,以20000存储到AX中为例子

  1. 20000的二进制为 01001110 00100000
  2. 这里可以看到高8位和低8位,实际存储的时候,会将高8位存储在AH中,将低8位存储在AL中。

段寄存器

用途

在8086CPU计算物理地址的时候,需要提供两个16bit的地址:段地址,偏移地址。公式是:物理地址 = 段地址 ×\times 16 + 偏移地址 的方式来计算出20bit的物理地址(详细部分在内存那一部分)。

那么我们可以清楚地知道,需要存储部件来存储段地址,这就是段寄存器的用途。

CS和IP

用途

CS存储代码段的段地址,IP存储代码段的偏移地址。

在任意时刻,设CS的内容为M,IP中的内容为N,则8086CPU将从内存M×16+NM\times 16+N处读取一条指令并执行。也就是说:在任意时刻,8086机的CPU将从CS:IP指向的内容当作指令执行。

之前提到的数据和指令在内存中都是二进制,没有任何区别。CPU在工作的时候会把有的信息看作指令,有的看作数据,如何实现的呢?

我们可以回答说:CPU将从CS:IP指向的内容当作指令执行。

CPU读取、执行指令的工作过程

  1. CPU从CS:IP处读取出指令,送入IR寄存器中
  2. IP = IP + 所读取指令长度
  3. 执行指令
  4. 跳转到到步骤(1),重复执行

步骤图例可以看书上P26

修改CS和IP的指令

我们常常可以利用mov指令来修改寄存器的值,但是我们无法用mov指令修改CS和IP的值。我们只能利用转移指令来修改。(详情去看汇编指令部分)

代码段

在内存那一部分我们提到:**编程的时候,可以根据需要将一组连续的内存地址空间定义为一个段。**我们可以将长度小于64KB的一组代码,存在一组地址连续、起始地址为16的倍数的内存单元中,我们可以认为这段内存是用来存储代码的,从而定义了一个代码段。

将这一段内存当作代码段,仅仅是我们编程时候的安排,CPU并不会因为这种安排就自动地将我们所定义的代码段中的指令视为指令来执行。CPU只认被CS:IP指向的内存单元为指令,因此我们若想CPU执行我们代码段中的指令,要让CS:IP指向代码中第一条指令的首地址。

DS

用途

DS寄存器常用来存放要访问的数据的段地址。

数据段

我们可以将长度小于64KB,地址连续、起始地址为16的倍数的一组内存单元当作一个段,我们可以认为这段内存是用来存储数据的,从而定义了一个数据段。

把这一段内存当作数据段,只是我们编程时的一种安排,要使CPU能访问这段数据,可以在具体操作的时候,用ds来存放数据段的段地址。

SS和SP

用途

栈是一块具有先进后出性质的内存区,我们可以用push和pop指令来入栈和出栈。

在实现栈的时候,我们最需要关心的就是栈顶的位置,根据栈顶的移动来实现入栈和出栈。 SS 用来存储栈顶的段地址,SP用来存储栈顶的偏移地址。

push和pop指令的描述

  • push ax的执行有以下两步:

    1. SP = SP - 2,SS:SP指向当前栈顶前面的单元,让其成为新的栈顶
    2. 将ax中的内容送入SS:SP指向的内存单元处
  • pop ax的执行有以下两步:

    1. 将SS:SP指向的内存单元中的数据送入ax中
    2. SP = SP + 2,SS:SP成为新的栈顶。

栈顶超界的问题

我们只用了SS和SP来描述栈顶的位置,并没有记录栈底和栈的大小。在栈满的时候push,在栈空的时候pop都将发生栈顶超界的问题。栈顶超界会覆盖掉内存中其他位置的数据,这些位置可能存放了其他用途的代码和数据,这将会导致意料之外的错误。

而8086CPU并不提供保护栈顶不超界的机制,这需要我们自己操心栈顶超界的问题,在编程的时候要注意。

栈段

我们可以将长度小于64KB,地址连续、起始地址为16的倍数的一组内存单元当作一个栈段。

把这一段内存当作栈段,只是我们编程时的一种安排,要使CPU能访问这段数据,可以在具体操作的时候,将SS:SP指向我们定义的栈段。

标志寄存器

在CPU内部寄存器中,有一个标志寄存器,其作用是:

  1. 存储某些指令的执行结果
  2. 用来为CPU执行相关指令提供依据
  3. 用来控制CPU的相关工作方式

PSW是按照位起作用的,也就是说,它的每一位都有专门的含义。其结构如图

ZF标志

ZF(zero flag),若是结果为0,那么zf = 1,若是结果不为0,那么zf = 0

PF标志

PF(parity flag),奇偶标志位,如果结果所有bit中1的个数为偶数,则pf = 1,如果所有bit中1的个数为奇数,则pf = 0

SF标志

SF(sign flag),符号标志位,如果结果为负,则sf = 1,如果结果非负,则sf = 0

CF标志

CF(carry flag),进位标志位,在进行无符号运算的时候,记录了运算结果的最高有效位向更高位的进位,或从更高位的借位。

对于位数为N的无符号数来说,其对应的二进制的最高位,即N-1位,就是它的最高有效位。当两个无符号数相加,最高位发生进位,或者是两个无符号数相减,需要向更高位借位的时候,CF = 1;其余情况,CF = 0

OF标志

OF(Overflow flag),溢出标志位,在有有符号数运算的时候,若出现了正正得负,负负得正的情况,OF = 1,其余情况,OF = 0

DF标志

DF(Direction flag),方向标志位。在串处理指令中,控制每次操作后si,di的增减。

  • df = 0,每次操作后si,di递增
  • df = 1,每次操作后si,di递减

如串传送指令movsb,其功能是:

  1. 将ds:si指向的内存单元中的字节送入es:di
  2. 如果df = 0,则di = di + 1, si = si + 1;如果df = 1,则di = di - 1, si = si - 1

8086CPU提供两条指令对DF标志位进行设置

  1. cld(clear direction)将df设为0
  2. std(set direction)将df设为1