前言
在编写汇编代码的时候,我们经常需要取出各种各样的数据,进行操作,
但是有的数据常数(这里叫立即数),还有的在寄存器中,都是很简单的,但是难免有一些在存储器中,这时我们就需要取出数据进行操作。
在分支结构或者循环结构中,我们可能需要跳过几行(if else语句)或者回到几行之间(循环),
所以我们需要修改存储当前指令的IP寄存器,为其赋值的过程中我们就需要赋值,道理同上。
在例子中,我们经常使用的是mov ax,……,这里要说明一下,寻址方式不一定非要是mov指令,而且mov指令这样的两个操作数都可以采用寻址方式。
寻址的准备工作
先讲一下地址的形成:
8086是20位的地址线,但是我们的寄存器是16位的,剩下的四位不能浪费了,所以人们就想出附加段寄存器的方式(下面的配对中,前面的都是附加段寄存器)。
将附加段寄存器的内容乘10H,然后加上偏移地址(EA),这样我们就得到了一个20位的地址。
我们将20位的地址叫做物理地址(PA)。
接下来需要求的就是EA。
给出常用的寄存器配对:
- CS+IP
- SS+SP or BP
- DS+BX、DI、SI或一个16位数
- ES+DI(用于串指令)
如果不是串部分,我们的DI寄存器都配合DS使用。
另外我们还有跨段寻址,就是不按照默认的寄存器来,如:MOV AX,DS:[BP]
立即数寻址和寄存器寻址
mov ax,10h
mov ax,bx
一个是将10h的内容赋值给ax,另一个是将bx的内容赋值给ax。
使用立即数或者是寄存器的数据赋值,都不需要在存储器中寻找数据,还是很快的。
五种存储器寻址方式
寻址说白了就是从一个有效地址中读取一个数据,赋值给对象(或者是将数据赋值给地址指向的地方)
直接寻址
mov al,[1000h]
直接将地址给出,然后我们将地址对应的内容赋值给al寄存器(不是直接塞地址过去)
我们经常使用一个符号来代替地址常数,所以可以写成:
mov ax,[buffer]或者mov ax,buffer
注意这里的buffer为偏移地址,我们还需要加上附加段地址(默认为DS),如果有跨段,则按照给出的段来就好。
注意区分一下mov ax,[1000h]和mov ax,1000h
寄存器间接寻址
间接就是不直接给出显式地址,而是存在寄存器中,我们需要自己去算有效地址(当然是计算机帮你算了)。
格式:mov ax,[bx] ,
这里能使用的寄存器只有几个:BX 、BP、SI、DI,只需要匹配默认的段寄存器然后算出20位地址,将地址上的数据送进ax就行。
例:
MOV BX, [DI],(DS)=1000H,(DI)=2345H
所以我们得到了一个12345h的地址,将地址的内容取出来,给bx就行。
区分mov ax,bx
寄存器相对寻址
相对,我们可以想象成一个数组,我们只知道其首地址是不能直接得到我们想要的内容,所以我们需要有一个相对地址来找到元素。
格式:mov ax,[bx+60h]或mov ax,60h[bx]
这里面的常数我们叫做偏移量,为八位或者十六位(注意是有符号数)
找到对应的附加段,然后将60h也加到物理地址上,得到我们最终的地址,然后取值、赋值即可。
这里我们区分一下四个寄存器,接下来会用到
基址寄存器:BX、BP
变址寄存器:SI、DI
基址变址寻址方式
MOV BX, [BX+SI]
这里我们需要用到一个基址寄存器和一个变址寄存器,将两者求和再加上对应的段寄存器就能找到物理地址。
那么我们是使用哪个寄存器的对应段寄存器呢?
解释一下两个寄存器的定义:
用基址表示首地址,
而用变址来表示数组,表格或者字符串中的某一个值
很明显,我们应该采取的是基址寄存器对应的段寄存器。
一直在用数组举例子,其实寄存器相对寻址和基址变址寻址是可以联系上一维数组的。
相对基址变址寻址方式
格式:MOV AX, [BX+SI+200H],(或者200H[BX+SI])
就是基址加变址加偏移量形成的地址。
那这个应该代表什么呢?
二维数组,
基址为首地址,变址确定哪一行,偏移地址确定哪一个位置,求和后还要加上附加段地址,然后就可以得到PA了。
总结
- 间接就是隐晦,就是不直接给出,需要我们自己读取一下;
- 相对就是有常数偏移量,变址就是要加上一个寄存器的内容(也算是偏移量),相当于一维数组
- 相对基址变址就是二维数组,首地址+变址+偏移量
- 寄存器操作数地址只能由BX、BP、SI、DI 给出, 它们的组合也不是任意的。
转移指令寻址
就是修改当前的指令地址(IP寄存器中),赋值方式和上面差不多
段内转移
分为直接和间接,也就是是否直接给出地址。
如果是直接转:JMP short next(next为结束在的位置,是一个地址)
short是8位位移量,near ptr是16位。(不是结束地址)
JMP是跳转指令。
虽然我们是直接给出跳转地址的位置,但是在机器指令中(我们写的是伪指令)显示的是位移量,也就是差值。
间接跳转的话,我们采取的是JMP word ptr +间接寻址。
这里的形式不止一种,比如JMP [BP]和JMP [BX][SI]都是有的。
这里我们添加word ptr的目的是因为我们需要在后面给出的地址中取值,其中取值的类型需要确定。
可能有一点绕,想一下我们的目的就是取出一个地址,而我们采用的是间接,所以就是找到一个地址,从该地址上取我们需要的地址。(地址为一个字的大小)
然后我们在找到地址之后,我们需要取出一个字的内容,作为我们跳转的目的地,但是如果是jmp+间接地址,其实计算机是不知道我们想取出的数据的类型的,这里就需要加上word ptr告诉计算机我们要取一个字的大小。
但如果是寄存器间接寻址,我们直接拿出寄存器的地址,可以确定大小是一个字,所以word ptr就不需要了。
段间转移
段内是只修改IP,如果是段间转移,我们则需要修改CS和IP。
也是分为直接和间接。
直接:在给出的物理地址,我们读取出偏移地址和附加段地址,分别赋值给IP和CS就行了。
怎么读?我们有指令的。
- offset +物理地址 得到偏移地址给IP
- seg +物理地址 得到附加段地址给CS
如果是段间间接寻址,那么我们需要先找到一个物理地址,还是采取读数的方式。
不过之前都是寄存器为几位就读取几位,这次我们采取不一样的方式。
例子:DS=3000H,BX=1200H,INTERS=0020H
JMP DWORD PTR [INTERS+BX]
我们得到的地址为31220h,然后在内存中寻找该地址:
从高到低,一共是8个16位数,先四个给CS,然后是IP。