寄存器基本原理(下篇)

上篇文章说到了通用寄存器,而寄存器在逆向中是非常重要的环节。关于通用寄存器的指令,我们主要是通过汇编指令来控制 CPU 进行工作。

以上是几条最简单也最重要的汇编指令,大部分汇编语言都说是由这几个简单指令完成 ,还有就是汇编指令不区分大小写,例如 MOV AX、221CH 和 mov ax 、221CH 是同一条汇编指令。

接下来看几条指令执行的前后变化:

上面的指令都很简单,着重看最后一条,add ax,bx 在执行前,ax 和 bx 中的数据都为 8226H,所得的值应该是 1044CH,这里就涉及溢出问题,上篇文章讲过,AX 是 16 位寄存器,只能存放 4 位 16 进制数据,所以要舍弃最高位的 1,最后值为 044CH。

接下来看几条 8 位寄存器的数值:

同样,这也涉及到溢出的问题,AL 是一个独立的八位寄存器,和 AH 没有关系,在执行这条语句时,CPU 会默认这是两个不同的寄存器,溢出的结果不会从低位进入高位。在进行汇编指令操作时,一定要注意两个寄存的位数要一样,8 位都是 8 位,16 位都是 16 位,不要进行越界操作。

物理地址

接下来看一下物理地址,先介绍一条概念,段地址ⅹ16+ 偏移地址 = 物理地址,这是一条十分重要的概念,接下来详细分析一下。

在此之前先说一下物理地址,CPU 在访问内存时,要给出内存单元的地址,存储空间是一个个线性空间,每一个存储单元在这个线性空间中,都有一个地址,这个地址是唯一的,就是物理地址。这可以理解为门牌号,CPU 就是根据门牌号存入数据。

8086 是 16 位的 CPU,在此之前也有 8 位的,比如 8080,8085 就是八位机,这些就不在这里研究了,主要研究的是 16 位 CPU。16 位机器主要有以下几种特征:

1. 运算器一次最多可以处理 16 位数据。

2. 寄存器的最大宽度是 16 位。

3. 寄存器和运算器之间的通路是 16 位,注意这个通路不是总线。

8086 是 16 位的 CPU,也就是说在 8086 CPU 内部一次性可以处理的数据就是 16 位

的,当然也包括传输和暂时存储,这些都是 16 位的。8086 CPU 是 20 位地址总线,可以传送 20 位地址,寻址能力达到了 1MB,但是 8086 又是 16 位的结构,在内部一次性处理、传输和暂时存储的地址是 16 位,那么寻址能力应该是 64KB。

这就说到了咱们之前说的内容,8086 CPU 采用两个 16 位地址合成的方法形成一个 20 位地址。

CPU 的某一个部件提供两个 16 位的地址,这两个地址,一个称为段地址,一个称为偏移地址。段地址和偏移地址通过内部总线路由一个名为地址加法器的部件,称之为“血汗工厂“。然后,地址加法器将两个 16 位地址合称为一个 20 位的物理地址并将物理地址通过内部总线送到输入输出的控制电路。这个输入输出的控制电路,将 20 位的物理地址送上了地址总线,最后,20 位物理地址通过地址总线送入存储器,这就是一个大概的工作流程。

上图是一个简易的控制流程,(画的不好,尽力了)

至于为什么会有这么一个运行机制,各有各的说法,大多数都是猜测,理据最高的一个说法是 CPU 机制源于本身的设计缺陷。

地址加法器采用上面说的方法,物理地址 = 段地址ⅹ16+ 偏移地址的方法合成物理地址,这就是把地址加法器称为“血汗工厂”的原因,这个过程确实不容易。

接下来仔细分析下“段地址ⅹ16+ 偏移地址 = 物理地址”的本质含义。本质含义是 CPU 在访问内存时,用一个基础地址,就是我们所说的短地址ⅹ16,和一个相与基础地址的偏移地址相加,给出内存单元的物理地址。

更形象一点就是基础地址加段地址等于物理地址,在 8086CPU 中,段地址ⅹ16 可以被认为是基础地址。举例来说,小明的家距离公交站 500 米,距离学校 2000 米,假设他们三点之间的位置在同一条直线上,那么从公交站到学校就是 1500 米。

现在小明要去上学有两种办法,第一,步行 500 米到公交站,乘坐公交车在行进 1500 米,到达学校。但是有一天小明睡觉睡过头了,没赶上公交车,就只能用第二种办法,就是步行 2000 米到学校。

这就很好解释了段地址和偏移地址的问题,一种是直达,也就是“物理地址”,也就是小明迟到的时候,另一种就是偏移地址 + 基础地址的情况,就是坐上公交车的时候。