内存寻址方式
内存中字的存储
8086CPU采用小端方式进行存储,一个字的低位字节存储在内存中的低地址字节,高位字节存储在内存中的高地址字节
物理地址
前面我们了解了什么是内存地址空间,而每一个内存单元在内存地址空间中都有唯一的地址,这个唯一的地址就是物理地址。
16位结构的CPU
基本内容介绍里面有提到,8086CPU的地址线共有20bit宽度,但我们也同样知道,8086CPU是一个16bit字长的CPU,这意味:
- 寄存器是16bit的
- 运算器一次最多进行16bit的运算
- 运算器和寄存器之间的通路是16bit的
从上述几点可以得知:若由CPU内部简单给出地址,最多一次性给出16bit宽度的地址,其寻址能力为64KB。
8086CPU给出物理地址的方法
为了能够得到20bit的地址,达到8086设计的寻址能力,其CPU内部采用两个16bit地址来生成一个20bit地址。
相关部件的示意图:
从图中可以得知:生成物理地址的步骤
- CPU内部将段地址和偏移地址送入地址加法器
- 算出物理地址后送入输入输出电路
- 输入输出电路将物理地址送入地址线
在地址加法器中,运算公式为:物理地址 = 段地址 16 + 偏移地址。从公式中可以看出,若想算出物理地址,须有存储器件存储段地址和偏移地址,这里就需要用到段寄存器。
段的概念
从上面的物理地址计算公式,我们难免会去认为,内存中物理划分为了一段段,但实际上内存并没有分段。段的概念就是从公式中出现的,而非实际上存在。
那么段的意义在哪?段的意义在于给程序员更好地访问内存。程序员可以将若干连续的内存单元视为一个段,将不同类型的信息(如数据和代码)放在不同的段中,用偏移地址来访问段中的信息。
这里我们要注意三点:
- 段地址 16必然是16的倍数,所以一个段的起始地址也一定是16的倍数
- 偏移地址为16bit,这说明一个段最大为64KB。
- 段地址 16 + 偏移地址 可以表示的数值范围已经超过了 ,这意味着一个物理地址可以对应多组段地址和偏移地址。
程序员在编程过程中,将一段内存空间视为一个段是程序员的心里所想,实际上这里并没有进行内存分段。但是由上面的注意点我们又可以得知,一个段的起始地址为16的倍数,这又是一个实际的要求,为了保证这一点,我们需要利用汇编段定义伪指令
寻址方式
我们上面提到了物理地址的计算方法,段地址一般保存于段寄存器中,其值往往不变,我们通常利用不同的寻址方式得到不同的偏移地址,从而应用于不同的场景之中。
内存地址的写法
我们常用[......]来表示一个内存单元,[......]中填入内存单元的偏移地址,具体可以填什么内容涉及到下面所讲的不同的寻址方式。而若是没有指定段寄存器,就默认ds作为段寄存器。
有时我们想要显示地指定段寄存器,这时候就可以使用段前缀,格式为:段寄存器:[偏移地址]
常用寄存器
在8086CPU中,只有bx、si、di、bp这四个寄存器可以用于寻址
它们可以单独用来寻址,也可以组合起来寻址,组合方式只有四种
- bx + si
- bx + di
- bp + si
- 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,这是不允许的组合方式。
作用:常用于访问结构体中的数组,或用于二维数组