为什么要了解寄存器
为什么了解寄存器?因为CPU是计算机的大脑,可以通过指令读写寄存器实现对CPU的控制,你试想一下如果你的大脑不能控制只能控制手和脚等,不就等于扯线木偶了?
寄存器只是CPU内部的一个器件,对于汇编来说CPU中比较重要的三个器件:
- 运算器:对数据进行处理
- 控制器:控制各种器件工作
- 寄存器:对数据进行储存
而在CPU内部,这三个器件包括其他器件都是靠CPU内部总线相连,这里的内部总线是指的CPU内部器件的总线,而上一章节说的总线是CPU与外部器件比如显卡、内存等器件的总线。
8086CPU
每个CPU寄存器和结构都是不相同的,而王爽《汇编语言》第三版主要讲的是在8086CPU上面操作的汇编,所以我这个笔记(教程)也是跟着王爽《汇编语言》第三版这本书学习。
在8086CPU中有14个寄存器,并且每个寄存器都是16位(2Byte),每个寄存器的名字不一样,这些寄存器的名字分别为:AX、BX、CX、DX、SI、DI、SP、BP、IP、CS、SS、DS、ES、PSW。而关于这些寄存器的作用,需要用的时候再去理解,现在就当认识一下啦~
通用寄存器
AX、BX、CX、DX这四个寄存器可以来存放一般的数据,所以通常称为通用寄存器。以AX寄存器为例,寄存器的逻辑结构为下图:
8086CPU每个寄存器都是16位,2而8086上一代CPU的寄存器为8位,所以8086CPU为了保证兼容性,AX、BX、CX、DX这四个寄存器都可以分为两个独立的8位寄存器来使用。
- AX可分为:AH和AL
- BX可分为:BH和BL
- CX可分为:CH和CL
- DX可分为:DH和DL
而这些命名也是有规律的,AH表示AX寄存器的高8位,AL表示AX寄存器的低8位。以AX为例,AX寄存器分为两个独立8为寄存器的逻辑结构为下图:
为了保证兼容性8086CPU同时也可以处理两种尺寸的数据:
- 字节:byte,一个字节由8个bit组成,可以存放在8位寄存器中,比如AH、AL
- 字:word,一个字由两个字节组成,可以直接放在16位寄存器中,比如AX
下图是数据20000(十六进制为4E20H)保存在寄存器AX当中的示意图:
如果把AX寄存器当作一个整体看,那么里面的值就是字型数据20000(4E20H),如果把寄存器分成两个8位寄存器看,那么AH就是字节型数据78(4EH),而AL就是字节型数据32(20H)。
几条汇编指令
通过下面几条简单的汇编指令来熟悉一下汇编指令,至于其他的一些汇编指令会在后期介绍。下面表中会列出汇编指令和相应的指令含义。
在汇编语言当中,如果你操作寄存器的时候需要运算的字节大小超过了寄存器能存储的大小,CPU会抛弃高位(但是CPU并不是真正的丢弃这个值,后面会说到为什么),保留相应的低位,什么意思呢?比如下面的汇编指令:
mov ah,88H
add ah,ah
上面代码运算后的结果为110H,但是因为操作的是8位寄存器,也就是只能保存两个16进制(16进制一个位等于4位二进制,两个16进制等于8位二进制),所以这里CPU丢去了高位1H,保留了10H。而这种情况同样在16位中也会有。
16位结构的CPU
之前提到过8086CPU上一代CPU是8位CPU,而8086CPU是16位CPU,那么什么是16位CPU?什么是8位CPU?有三个方面来描述CPU的结构特性:
- 运算器最多一次性可以计算16位数据
- 寄存器最多一次性能存储16位数据
- 寄存器和运算器之间最多一次性能传输16位数据
通过上面简单的说,就是说8086CPU能一次性处理、传输、暂存信息的长度为16位。那32位计算机呢?同样是一次性处理、传输、暂存信息的长度为32位。
物理地址
在上一章节说过CPU会把所有储存器看成一个一维线性空间,每个储存器在这个空间中都有自己的地址,我们将这个地址称为物理地址,比如就像下图一样:
而CPU访问存储器的时候是通过地址总线来寻址的,而寻址必须通过物理地址来寻址。
在CPU向地址总线发出物理地址之前需要先在内部形成这个一维的线性空间,那么物理地址是怎样形成的呢?不同的CPU可以有不同的形成物理地址的方式,下面来讨论一下8086CPU形成物理地址的方式。
8086CPU给出物理地址的方法
8086CPU有20位地址总线用于相连外部的存储器,最多可以传输20位地址。在将物理地址发出到地址总线寻址之前,必须在CPU中处理、传输、暂存,而8086CPU是16位结构的CPU,意味着8086只能一次性发出16位的地址,也意味着只有64KB的寻址能力,所以8086CPU在设计上采用了用两个16位地址来合成20位的物理地址的方法。
它的大概实现就是由CPU的相关部件提供两个16位地址,一个为段地址,一个为偏移地址,他们通过CPU内部的一个地址加法器计算这个地址得到一个20位的物理地址,然后通过CPU内部的地址总线传输到地址总线上进行寻址,比如下图:
而这个地址加法器采用的计算方式是物理地址=基础地址(段地址X16)+偏移地址,比如8086CPU要访问123C8H内存单元的时候,地址加法器的工作流程大致如下:
关于这样的计算方式,还有一种更为常见的说法是左移4位,因为上面的物理地址都是用16进制表示的,而一位16进制等于4位二进制。在计算机存储的任何信息都是以二进制存放的,所以这里指的左移4位,是指的二进制左移4位,而二进制左移4位相当于16进制X16,相当于10进制的该数乘以2的4次方,下图中可以很好的对比数据:
比如我们要访问一个内存单元,用描述来说“数据在21F60H当中”,这句话对于8086PC机一般不这样描述,一般用两种说法表示:
- 数据存在内存2000:1F60单元中
- 数据存在内存的2000段中的1F60单元中
段的概念
说到段,可能都会误认为储存器是分为一段一段的,实际上不是,因为CPU把所有的储存器看作一个一维的线性空间,所以CPU把所有的储存器看作的都是一个整体。
而这个划分来自于CPU的寻址方式,因为8086CPU用的是“基础地址(段地址X16+偏移地址)”得到的,所以使我们可以分段的管理这些内存,但是需要知道的是段地址X16必然是16的倍数,而偏移地址位16位,所以一个段最大长度为64KB,比如下图中表示的两个“段”:
在这个之前提到过,段地址是由相关部件提供的,这个部件也是寄存器,但是它只用来存放段地址,所以称为段寄存器,在8086中有4个段寄存器,分别有4个:CS、DS、SS、ES。当8086CPU要访问内存时,由这4个段寄存器提供内存单位的段地址,这里先看看CS。
CS段寄存器和IP指令指针寄存器
因为在计算机中储存的数据都是二进制,计算机并不知道这些二进制是代码还是数据,所以数据和代码是完全由程序员说了算,而怎么来让计算机知道这是代码?这是数据?通过CS和IP寄存器。
CS和IP是8086CPU中最为关键的两个寄存器,这两个寄存器指示了CPU当前要读取的指令地址。
CS为代码段寄存器,IP为指令指针寄存器,在8086CPU中通过CSX16+IP得到需读取执行的代码,当每次执行代码后IP的值会自动加上当前已经读取执行的代码长度,以使CPU可以正确的执行一下一条命令。
所以CS和IP是表示的CPU任意时刻当前需要读取执行的代码,下图中是一个CPU读取执行代码的过程:
在8086CPU加电或复位后(即CPU刚开始工作的时候)CS和IP被设置为CS=FFFFH,IP=0000H,也就是说8086PC机刚启动的时候,FFFF0H内存单元中的指令是8086PC机启动后执行的第一条指令。
修改CS和IP
程序员可以通过修改CS、IP到达控制CPU执行哪一段代码,我们之前可以通过传送指令MOV来修改通用寄存器,但是传送指令MOV并不能来修改CS、IP寄存器,而8086CPU提供了另外的指令来改变CS、IP的值,这一类指令我们统称为转移指令。
jmp指令就是一条简单的转移指令,它的作用是用指令修改当前CPU需要执行的代码内存单元地址,通过修改CS的段地址以及IP的偏移地址。它的使用方法是:
jmp 段地址:偏移地址
它可以直接修改CS和IP寄存器的值,比如下面这样:
jmp 2AE3:3,执行后:CS=2AE3H,IP=0003H,CPU将从内存单元2AE33H执行指令。
jmp 3:0B16,执行后:CS=0003H,IP=0B16H,CPU将从内存单元00B46H执行指令。
我们也可以只修改IP偏移地址:
jmp ax 执行后把ax中的数据传送到IP寄存器中
执行前:ax=1000H,CS=2000H,IP=0000H
执行后:ax=1000H,CS=2000H,IP=1000H
jmp bx 执行后把bx中的数据传送到IP寄存器中
执行前:bx=3000H,CS=2000H,IP=0000H
执行后:bx=3000H,CS=2000H,IP=3000H
8086CPU工作过程
- 从CS:IP指向内存单元读取指令,读取的指令进入指令缓冲区
- IP指向下一条指令
- 执行指令(转到步骤1,重复过程)
《汇编语言学习笔记(二):寄存器》留言数:0