跳到主要内容

内存寻址方式

内存中字的存储

8086CPU采用小端方式进行存储,一个字的低位字节存储在内存中的低地址字节,高位字节存储在内存中的高地址字节

物理地址

前面我们了解了什么是内存地址空间,而每一个内存单元在内存地址空间中都有唯一的地址,这个唯一的地址就是物理地址。

16位结构的CPU

基本内容介绍里面有提到,8086CPU的地址线共有20bit宽度,但我们也同样知道,8086CPU是一个16bit字长的CPU,这意味:

  1. 寄存器是16bit的
  2. 运算器一次最多进行16bit的运算
  3. 运算器和寄存器之间的通路是16bit的

从上述几点可以得知:若由CPU内部简单给出地址,最多一次性给出16bit宽度的地址,其寻址能力为64KB。

8086CPU给出物理地址的方法

为了能够得到20bit的地址,达到8086设计的寻址能力,其CPU内部采用两个16bit地址来生成一个20bit地址。

相关部件的示意图:

从图中可以得知:生成物理地址的步骤

  1. CPU内部将段地址和偏移地址送入地址加法器
  2. 算出物理地址后送入输入输出电路
  3. 输入输出电路将物理地址送入地址线

在地址加法器中,运算公式为:物理地址 = 段地址 ×\times 16 + 偏移地址。从公式中可以看出,若想算出物理地址,须有存储器件存储段地址和偏移地址,这里就需要用到段寄存器。

段的概念

从上面的物理地址计算公式,我们难免会去认为,内存中物理划分为了一段段,但实际上内存并没有分段。段的概念就是从公式中出现的,而非实际上存在。

那么段的意义在哪?段的意义在于给程序员更好地访问内存。程序员可以将若干连续的内存单元视为一个段,将不同类型的信息(如数据和代码)放在不同的段中,用偏移地址来访问段中的信息。

这里我们要注意三点:

  1. 段地址 ×\times 16必然是16的倍数,所以一个段的起始地址也一定是16的倍数
  2. 偏移地址为16bit,这说明一个段最大为64KB。
  3. 段地址 ×\times 16 + 偏移地址 可以表示的数值范围已经超过了 2202^{20},这意味着一个物理地址可以对应多组段地址和偏移地址。

程序员在编程过程中,将一段内存空间视为一个段是程序员的心里所想,实际上这里并没有进行内存分段。但是由上面的注意点我们又可以得知,一个段的起始地址为16的倍数,这又是一个实际的要求,为了保证这一点,我们需要利用汇编段定义伪指令

寻址方式

我们上面提到了物理地址的计算方法,段地址一般保存于段寄存器中,其值往往不变,我们通常利用不同的寻址方式得到不同的偏移地址,从而应用于不同的场景之中。

内存地址的写法

我们常用[......]来表示一个内存单元,[......]中填入内存单元的偏移地址,具体可以填什么内容涉及到下面所讲的不同的寻址方式。而若是没有指定段寄存器,就默认ds作为段寄存器。

有时我们想要显示地指定段寄存器,这时候就可以使用段前缀,格式为:段寄存器:[偏移地址]

常用寄存器

在8086CPU中,只有bx、si、di、bp这四个寄存器可以用于寻址

它们可以单独用来寻址,也可以组合起来寻址,组合方式只有四种

  1. bx + si
  2. bx + di
  3. bp + si
  4. bp + di

若是出现了bp寄存器的寻址方式,且没有显示指定段前缀,就默认段寄存器是ss。

不同的寻址方式

我们可以采用多种方法给定一个内存单元的偏移地址,这种定位内存单元的方法称为寻址方法

直接寻址

格式为:[idata],其中idata是一个16bit的数,直接指出了偏移地址

寄存器间接寻址

格式为:[bx] / [si] / [di] / [bp],这里的寄存器中存储着偏移地址

作用:常用于loop循环来访问数组

寄存器相对寻址

格式为:[bx+idata] / [bx].idata / idata[bx] / [bx][idata] 可将bx换成si, di, bp中任意一个。

作用为:访问起始地址为idata的数组,或者是一个二位数组的固定行列等等。

基址变址寻址

格式为:[bx + si] / [bx][si],可将bx换成bp,si换成di,但不可以将bx换成di,将si换成bp,这是不允许的组合方式。

作用:常用于二维数组

相对基址变址寻址

格式为:[bx + si + idata] / idata[bx][si] / [bx].idata[si],可将bx换成bp,si换成di,但不可以将bx换成di,将si换成bp,这是不允许的组合方式。

作用:常用于访问结构体中的数组,或用于二维数组