寄存器

概述

一个典型的CPU主要由运算器,控制器,寄存器等器件构成,它们靠内部总线相连(内部总线实现CPU内部各机器件间的联系,外部总线实现CPU和主板上其他器件之间的联系)。

本篇博文叙述CPU中的寄存器,程序员可以通过指令读写寄存器,从而实现对CPU的控制。不同的CPU其寄存器的个数与结构也不相同,以8086CPU为例,其有14各寄存器,每个寄存器有一个名称,且所有寄存器都是16位的,可以存放两个字节,分别为:AX、BX、CX、DX、SI、DI、SP、BP、IP、CS、SS、DS、ES、PSW,后面将一一进行介绍。

通用寄存器

AX,BX,CX,DX为四个通用寄存器,用于存放一般性数据(所谓一般性数据指的是那些并非用于CPU控制的数据)。如前所述,8086CPU使用的是16位寄存器,为了兼容之前针对8位CPU编写的程序,每个通用寄存器都可以分为两个8位寄存器来独立使用,即AX可分为AH和AL,BX可分为BH和BL,CX可分为CH和CL,DX可分为DH和DL。如下图所示: img

通过汇编语言使用通用寄存器:(汇编语言中不能直接相加两个数字,而是要通过寄存器来做加法)

1
2
3
4
5
6
7
// 18 + 2
MOV AX 18 // 将18送入寄存器AX
add ax 8  // 将AX中的数值加上8
// 或如下写法
MOV AX 18
MOV BX 2
add ax bx // 将AX和BX中的数值相加,结果保存在AX中

**【注】:**在写一条汇编指令或一个寄存器名称时不区分大小写。

代码段寄存器与指令指针寄存器

要说明这两个寄存器,首先我们要理解一下几个概念。

物理地址

每个内存单元都有一个编号,而这就是内存单元唯一的地址,也就是物理地址。

16位结构的CPU

一个16位结构的CPU意味着该CPU具有以下特征:

  • 运算器一次最多可以处理16位的数据
  • 寄存器的最大宽度位16位
  • 寄存器与运算器之间的通路为16位

8086CPU给出的物理地址方法(分段结构)

8086CPU的物理总线的宽度为20,而CPU内部只能处理和传输16位地址,那么如果将地址从内部简单的发出,那么只能送出16位的地址,表现出的寻址能力只有64KB,因此,8080CPU添加了了一个地址加法器,将两个16位地址合并为一个20位的物理地址。其中两个16位地址分别位段地址和偏移地址,地址加法器的计算方法为:物理地址 = 段地址 * 16 + 偏移地址, 即物理地址 = 段地址 << (20 - 16) + 偏移地址。如下图所示:

img

代码段寄存器CS,指令指针寄存器IP

8086CPU含有4个段寄存器(存放段地址):CS,DS,SS,ES。本节只说明CS。CS为代码段寄存器,IP为指令指针寄存器,8086CPU从CS*16 + IP单元开始读取一条指令并执行,即通过CS寄存器找到段地址,IP则记录的是偏移地址,通过二者可以找到要执行的指令代码。

**其过程如下:**将段地址和偏移地址送至地址加法器,之后通过地址总线送至内存芯片,随后内存芯片将对应地址空间中的内容(指令)通过数据总线送达CPU,此时CPU将增加IP寄存器的值(增加值为所读取指令的长度),以便读入下一条指令。

修改CS、IP的指令

mov指令可以用于修改大部分寄存器的值,但不可以用于修改CS、IP的内容。若想修改,可以使用如下语法:*jmp 段地址 : 偏移地址*,例如:jmp 2AE3:3 执行后 cs = 2AE3H, IP = 0003H。若仅想修改IP的内容,可用形如:*jmp 某一合法寄存器* 来完成,比如 jmp axjmp bx ,那么该语句执行后便会用寄存器中的值修改IP寄存器。

数据段寄存器DS和偏移地址[address]

8086CPU中有一个DS寄存器,通常用于存放要访问数据的段地址,在配合偏移地址[address]来使用,则可以读取指定内存单元中的数据,如:

1
2
3
4
5
// 读取10000H单元的内容
mov bx, 1000H
mov ds, bx
mov al, [0]  // [adderss]表示偏移地址
mov [0] cx;

上述代码描述了mov指令可以完成的四种操作:1)將数据直接写入寄存器;2)将一个寄存器中的内容送至另一个寄存器;3)将一个内存单元中的内容送至寄存器;4)将寄存器中的数据送入内存单元。

**【注】:**8086CPU不支持将数据直接送入段寄存器,因此修改DS寄存器需要从另一个寄存器移入。

栈寄存器

出栈入栈指令

汇编语言的出栈入栈指令为PUSH和POP,其使用方式如下:

1
2
3
push/pop 寄存器
push/pop 段寄存器
push/pop 内存单元

栈段寄存器SS与偏移寄存器SP

栈顶通过段寄存器ss与偏移寄存器SP指出。在任意时刻SS:SP总是指向栈顶元素,即最后入栈的数据。每次入栈时,push指令都将先移动栈顶指针再写入数据。当栈为空时,栈顶指针SS:SP指向比栈底地址高一个元素大小的位置(栈由高地址向低地址增长)。

**【注】:**将一段地址当作栈段仅仅是编程时的一种安排,而CPU并不会区别的对待这段内存地址空间。

Debug工具

Debug是供程序员使用的程序调试工具,可以用它检查内存中任何地方的字节以及修改任何地方的字节。

-r命令

r命令用于查看和改变CPU寄存器的内容,使用如下图所示

img

-d命令

可以通过"d 段地址:偏移地址"的格式查看从指定地址起的内存块中的内容。

-e命令

改写内存单元的内容

-a命令

以汇编指令的形式改写指定的内存单元。如下所示:

img

-t命令

执行-t命令后,CPU将执行CS:IP指向的指令。如下图所示:

img

小结

  • CPU访问内存时,必须向内存提供内存单元的物理地址。8086CPU在内部用段地址和偏移地址移位相加的方法形成最终的物理地址

结论:CPU可以使用不同的段地址和偏移地址形成同一个物理地址

  • 偏移地址16位,变化范围为0~FFFFH,仅用偏移地址来寻址最多可寻64kb个内存单元

  • 在8086PC机,存储单元的地址用两个元素来表述————段地址和偏移地址

  • 可以根据需要,将地址连续,起始地址为16的倍数的一组内存单元定义为一个段

  • 段地址在8086CPU的段寄存器中存放。当8086CPU要访问内存时,由段寄存器提供内存单元的段地址。8086CPU有四个段寄存器,其中CS用来存放指令的段地址

  • CS存放指令的段地址,IP存放指令的偏移地址

8086机中,任意时刻,CPU将CS:IP指向的内容当作指令来执行

  • 8086CPU的工作执行过程:

    1. 从CS:IP指向的内存单元读取指令,读取的指令进入指令缓冲器
    2. IP指向下一条指令
    3. 执行指令(转到步骤1,重复这个过程)
  • 8086CPU提供转移指令修改CS:IP的值(jmp指令)

  • 字在内存里存储时,要用两个连续的地址单元来存储,字的低位字节存放在低地址单元中,高位字节存放在地址单元中

  • 用mov指令访问内存单元,可以在mov指令中只给出内存单元的偏移地址,此时,段地址默认在DS寄存器中

  • [address]表示一个偏移地址为address的内存单元

  • 在内存和寄存器之间传送字型数据时,高地址单元和高8位寄存器,低地址单元和低8位寄存器相对应

  • mov、add、sub是具有两个操作对象的指令。jmp是具有一个操作对象的指令

  • 在8086CPU提供了栈操作机制:SS段寄存器存放栈顶的段地址,在SP寄存器里存放栈顶的偏移地址。提供入栈和出栈的指令,它们根据SS:IP指示的地址,按照栈的方式访问内存单元

  • push:①SP=SP-2;②向SS:IP指向的字单元中送入数据

  • pop:①从SS:IP指向的字单元中读取数据;②SP=SP+2

  • 任意时刻,SS:IP指向的栈顶元素

  • 8086CPU只记录栈顶,栈顶空间大小,是否栈溢出等需要我们自己管理

  • 可以用栈来保存需要恢复寄存器的内容

  • 我们可以将一段内存定义为一个段,用一个段地址指示段,用偏移地址访问段内的内存单元。

我们可以用一个段存放数据,将他定义为“数据段”

我们可以用一个段存放代码,将他定义为“代码段”

我们可以用一个段当作栈,将他定义为“栈段”

不管我们如何安排,CPU将内存中的某些内容当作代码,是因为CS:IP指向了那里;CPU将内存中的某些内容当作栈,是因为CS:IP指向了那里

所以,一段内存既可以是代码的存储空间,又可以是数据的存储空间,还可以是栈空间,也可以是栈空间,也可以什么都不是。关键在于CPU寄存器的设置,即CS、IP、SS、SP、DS的指向。