数据寻址方式
文章目录
指令有
操作码
和操作数
两部分组成, 有些指令没有操作数, 但大部分指令有一个或两个操作数。
数据来自
主存
或外设
, 但也有可能事先已经保存在处理器的寄存器
中,也可能与指令操作码一起进入处理器。
主存和外设在汇编语言中被抽象为存储器地址和I/O地址,且在机器代码中用地址来区分寄存器,故指令的操作数需要通过地址指示。这种通过地址才能找到数据本身的方法,叫做数据寻址方法。
在汇编语言中,操作码(处理器要执行那种操作)有助记符表示,操作数(指令执行的参与者)由寻址方式体现。IA-32处理器只有输入输出指令与外设交换数据(I/O寻址:数据在外设I/O设备中,用I/O地址代表)。
另外还有3类外设数据外的数据寻址方式。
1.立即数寻址方式
- 用常量表达的具体数值(数据在指令代码中,用
常量表达
)
立即数寻址方式只用于源操作数
,在传送指令中常用来给寄存器和存储器赋值
。
例:将数据33221100传送到EAX寄存器的指令
mov eax,33221100H
这个指令的机器代码(十六进制)是
B8 00 11 22 33(小端方式)
,B8是操作码,后面是立即数。IA-32处理器规定:高对高,低对低
- 立即数也可以传递给数据
立即数(常量)没有类型,他的类型取决于另一个操作数(目的操作数)的类型。
2.寄存器寻址方式
- 用寄存器名表示的其中内容(数据在寄存器中,用寄存器表示)
指令的操作数存放在处理器的寄存器中,就是寄存器寻址方式,是最常用的寻址方式。。
凡是只使用寄存器名(无其它符号,如中括号、变量名等)的操作数都采用寄存器寻址方式。
寄存器既可以是源操作数也可以是目的操作数,另一个操作数可以是变量也可以是常量(源操作数)。
但要求,操作数类型必须一致,mov eax, dx 就是错的。
3.存储器寻址方式
- 用存储器地址代表的保存的数据(数据在主存中,用存储器地址代表)
数据很多时候都保存在主存储器中。尽管可以事先将他们取到寄存器中再进行处理,但指令也需要能够直接寻址存储单元进行数据处理。
寻址主存中存储的操作数就称为存储器寻址方式,也称为主存寻址方式。编程时,存储器地址使用段选择器和偏移地址的逻辑地址。
3.1段寄存器指示段基地址
绝大多数情况使用默认规定,无需表达:
- 读取指令:一定是代码段
CS
- 堆栈操作,针对堆栈段
SS
(如果EBP、BP或ESP、SP作为基地址指针,默认使用SS段寄存器指向堆栈段) - 读写数据,默认在数据段
DS
(一般是DS段寄存器指向的数据段)
在这里提一下3类基本段(应该能让你更好了解一些):代码段、数据段、堆栈段
1.代码段中存放程序的指令代码。
程序的指令代码必须安排在代码段,否则无法进行。
程序利用代码段寄存器CS
获得当前代码的段基地址,指令寄存器EIP
保存代段中指令的偏移地址。处理器(CPU)利用CS:EIP取得下一条要执行的指令。
CS和EIP不能人为的通过程序直接设置,只能通过执行控制转移指令
、外部中断
或内部异常
等间接控制。2.数据段存放当前运行程序所用的数据(例如全局变量),一个程序可以使用多个数据段。主要数据放在一个数据段,只读的数据放在另一个数据段,动态分配的数据再一个。
数据的偏移地址有各种存储器寻址方式计算得出(在下面会讲到。
3.堆栈段是程序所使用的堆栈所在的区域。程序利用
SS获得当前堆栈段的段基地址
,堆栈指针寄存器ESP保存堆栈栈顶的偏移地址
。处理器利用SS:ESP操作堆栈地址。可存放返回地址(高级语言中的调用完函数return到调用函数语句的下一句,就相当于通过将堆栈段上的返回目的地的地址设定在程序计数器上,来跳转到相应位置);还用临时变量。
但在某些特例下(不使用默认的段选择器),需要进行段超越寻址,应使用段超越指令前缀,段超越指令前缀是一种只能跟随在具有存储器操作数的指令之前的指令,其助记符是段寄存器后跟英文冒号,即CS:、SS:、ES:、FS:、或GS:。
【例】
MOV CX,ES:[BX]
这条指令是数据访问, 把ES段中的偏移地址为BX的单元中的字送CX,而不是到DS段去寻址。
无论用BX,SI,DI或者BP作为间接寄存器,都允许段超越。
3.2偏移地址的组成
因为段基地址有默认的指定的段寄存器指明,所以指令中只有偏移地址即可。
存储器操作数寻址使用的偏移地址
常称为有效地址(Effrctive Address,EA)
IA-32处理器的多种主存寻址方式可以统一表达如下:
3.3存储器的直接寻址
有效地址
(存储器操作数寻址使用的偏移地址,不要弄混)只有位移量部分,且直接包含在指令代码中,这就是存储器的直接寻址方式。直接寻址常用于存取变量
。
【例】将变量COUNT的内容传送给ECX:
mov ecx,count ;也可以表达为:mov ecx,[count]
在汇编中[]的作用可以分为两种情况:
1.对于
变量
来说[var1]和var1作用是一样的2.但是对于
寄存器
来说就有区别了,[eax]是eax的偏移地址(这里相当于c语言中的指针) 而不加中括号的eax就是c语言中的变量值了,理解有误之处望在评论区指正!
在汇编语言的指令代码中,直接书写变量名就是在其偏移地址(有效地址)的存储单元读写操作数。
3.4寄存器间接寻址
有效地址存放在寄存器中,就是采用寄存器间接寻址存储器操作数。MASM汇编程序使用英文中括号括起寄存器表示寄存器间接寻址。
IA-32处理器的8个32位通用寄存器都可以作为间接的寄存器,但建议主要使用EBX、ESI、EDI,访问堆栈数据时使用EBP。
例如,下面的前两条指令的源操作数、后两条指令的目的操作数都是寄存器间接寻址方式:
mov edx,[ebx] ;双字传送,间接寻址主存数据
mov[ebp],edx ;双字传送,EBP间接寻址主存堆栈段
mov [eax] , [edx] 是错误的,主存储器之间不能传送
在寄存器间接寻址中,寄存器的内容是偏移地址,相当于一个地址指针。指令“mov edx,[ebx]” 执行时,如果EBX=405000H,则该指令等同于“mov edx ,DS:[405000H]”。
利用寄存器间接寻址,可以方便地对数组的元素或字符串的字符进行操作。
【例:寄存器间接寻址程序–显示目标字符串】
;two0201.asm
include io32.inc
.data
srcmsg byte 'Try your best,why not.',0
dstmsg byte sizeof srcmsg dup(?)
.code
start:
mov ecx,lengthof srcmsg
mov esi,offset srcmsg ;esi=源字符串首地址
mov edi,offset dstmsg ;edi=目的字符串首地址
again:
mov al,[esi] ;取源串一个字符送AL
mov [edi],al ;将AL传送给目的串
add esi,1 ;源串指针加1,指向下一个字符
add edi,1 ;目的串指针加1,指向下一个字符
loop again ;字符个数ECX(默认计数器)减一,不为零,则转到AGAIN标号处执行
mov eax,offset dstmsg
call dispmsg
exit 0
end start
3.5寄存器相对寻址
寄存器相对寻址的有效地址是寄存器内容与位移量
之和。
【例】
mov esi,[ebx+4] ;位移量:4,源操作数也可以表达为:[4][ebx],或者:4[ebx]
mov edi,[ebp-08h] ;位移量:-08h,源操作数也可以表达为:[-80h][ebx],但不能是:-08h[ebx]
mov eax,count[esi] ;位移量:count偏移量还可以用变量所在的地址,可替换为:[esi+count]、[count][esi]
【寄存器相对寻址程序–显示目标字符串】
;two0202.asm
include io32.inc
.data
srcmsg byte 'Try your best,why not.',0
dstmsg byte sizeof srcmsg dup(?)
.code
start:
mov ecx,lengthof srcmsg
mov ebx,0 ;EBX指向首个字符
again:
mov al,srcmsg[ebx] ;取源串一个字符送AL
mov dstmsg[ebx],al ;将AL传送给目的串
add ebx,1
loop again ;字符个数ECX减一,不为零,则转到AGAIN标号处执行
mov eax,offset dstmsg
call dispmsg
exit 0
end start
3.6变址寻址
3.6.1基址变址寻址
3.6.2相对基址变址寻址
3.6.3带比例的变址寻址
重要的是计算有效地址,进而访问存储器操作数。