寻址方式是计算机的主要性能特征表现之一。所谓寻址方式就是微处理器在指令码中寻找操作数地址的方式。标准的ARM都是32位的指令,它们可以有3个操作数,其中第1个操作数一般为基本操作数寻址方式,第2个、第3个操作数可采用复合寻址方式。以下介绍ARM微处理器的9种寻址方式。
立即数寻址即操作数就在指令的代码中。立即寻址指令的操作码字段后面的地址码部分就是操作数本身,取出指令也就取出了可以立即使用的操作数(也称为立即数)。立即数要以“#”为前缀,表示十六进制数值时以“0x”表示。
应用示例:
注意 :指令中的立即数是用12位表示的。前面已介绍它是由一个8位的常数(图3-2中的immed_8)乘以一个4位二进制数(图3-2中的rotate_imm)的2倍后获得的。即8位常数×(0~15)×2。并不是所有的32位都可以作为合法的立即数。具体描述如图3-2所示。
图3-2 立即数寻址时的ARM指令编码格式
书写立即数时,必须以“#”开头。对于不同进制的立即数有不同的书写方式。
对于十六进制数,“#”后加“0x”或“&”,如#0xaf或&af。
对于二进制数,“#”后加“0b”或“%”,如#0b10101011或#%10101011。
对于十进制数,“#”后加“0d”或省略,如#0d123或567。
寄存器寻址的操作数就是寄存器的内容。指令中的地址码字段给出的是寄存器编号,寄存器的内容就是操作数,指令执行时直接取出寄存器值操作。
应用示例:
寄存器偏移寻址是ARM指令集特有的寻址方式。当第2个操作数是寄存器偏移方式时,它在与第1个操作数结合之前,可以按指令中的操作码进行移位操作。以下用其中的一条指令进行说明。
其中,Rm被称为第2操作数寄存器。
<shift>被用来指定移位类型和移位位数,有两种形式:一是5位二进制构成的立即数,其值的范围在0~31之间;二是使用寄存器的内容,其值的范围也在0~31之间。
应用示例:
第2操作数的移位方式共有6种:逻辑左移LSL、逻辑右移LSR、算术左移ASL、算术右移ASR、循环右移ROR、带扩展的循环右移RRX。它们的操作示意图如下。
1) 逻辑左移 LSL:向左移位时低端空出位补0,在不溢出情况下等价于乘2,示意图如图3-3所示。
图3-3 逻辑左移LSL移位示意图
2) 逻辑右移 LSR:向右移位时高端空出的位补0,等价于整除2,舍去余数,示意图如图3-4所示。
图3-4 逻辑右移LSR移位示意图
应用示例:
3) 算术左移 ASL:向左移位时低端空出的位补0,最高符号位保持不变,示意图如图3-5所示。
图3-5 算术左移ASL移位示意图
4) 算术右移 ASR:向右移位时若为正数,最高位为0,移出高端空出位补0;若为负数,最高位为1,移出高端空出位补1;算术右移ASR等价于整除2,舍去余数,示意图如图3-6所示。
图3-6 算术右移ASR移位示意图
应用示例:
5) 循环右移 ROR:由字的b0输出,进入字的b31,依次进行,示意图如图3-7所示。
图3-7 循环右移ROR示意图
6) 带扩展的循环右移 RRX:就是带进位循环右移操作一位,高端空出位使用进位位C填充,示意图如图3-8所示。
图3-8 带扩展的循环右移RRX示意图
应用示例:
移位位数可以用立即数表示或由寄存器方式给出,其值的范围在0~31之间。上述的许多应用示例使用的是立即数或由寄存器给出,但是一定要注意,给定的数值必须在指定的范围之内。
寄存器间接寻址就是将寄存器的内容作为操作数的地址。指令中的地址码给出的是一个通用寄存器编号,所需要的操作数保存在寄存器指定地址的存储单元中,即寄存器为操作数的地址指针,操作数存放在存储器中。
应用示例:
基址+变址寻址方式([Rn,偏移量]){!}也叫作变址寻址方式,它是将基址寄存器Rn的内容与指令中给出的地址偏移量相加,形成操作数的有效地址。若使用后缀“!”,则有效地址最后写回Rn中,称为自动修改指针,且Rn不允许使用R15。变址寻址方式用于访问基址附近的存储单元,常用于查表、数组操作、功能部件寄存器访问等。
变址寻址方式分为3种,即前变址模式、自动变址模式和后变址模式;偏移量有立即数偏移量、寄存器偏移量和寄存器移位偏移量3种形式。ARM指令中使用的是它们的组合体,这样前变址寻址模式[Rn,偏移量]就包括以下几种形式。自动变址模式不再单独介绍,同其他两种模式一并介绍。以下通过应用示例进行介绍。
如果在这对双括号后加上“!”,则完成后R0←R0+R2。
该条指令的功能是将基址寄存器R1的内容加上R2的内容乘8作为有效地址的存储单元内容传送到R0寄存器中,R1的内容保持不变。方括号后若有“!”,则R1地址指针自动修改。
注意 :对于后变址偏移寻址模式([Rn],偏移量),Rn的值用作传送数据的存储器地址。在操作完数据后,Rn+偏移量送到Rn,即修改了Rn中的地址指针。同样,后变址模式也有立即数寻址方式、寄存器寻址方式和寄存器移位寻址方式3种情况,且Rn不允许使用R15寄存器。
注意 :后变址模式不需要加“!”就可修改地址指针。以下列举几个应用示例。
此指令是将以基址寄存器R1内容作为有效地址单元的存储器内容加载到R0中,之后修改R1的内容,即R1内容+4送R1。
此指令是将R0的内容写到以R3内容作为有效地址的内存单元中,之后R3内容减去R8内容送R3中。其他组合这里不再赘述,后面会有更多的应用示例。
多寄存器寻址是ARM微处理器独有的寻址方式。多寄存器寻址方式就是一条指令可以完成多个寄存器值的传送,这种寻址方式用一条指令最多可以完成16个寄存器值的传送。
应用示例:
该条指令以R0的内容作为存储器有效基地址,取出内容送入R1,R0+4为地址单元的内容送R2;R0+8为地址单元的内容送R3;R0+12为地址单元的内容送R5。操作完成后R0的内容不变。
若要实时改变R0的内容,则执行的指令是:LDMIA R0!,{R1, R2, R3, R5},执行这条指令,R1、R2、R3、R5的内容同前,但最后R0的内容等于R0+12。
注意 :花括号中是16个寄存器R0~R15的子集,寄存器的编号从小到大排列,使用“,”隔开,编号连续时可以使用“-”连接,例如:
第1条指令将R1的内容作为有效字存储单元首地址,字内容分别送入R2~R9、R12,最后R1的内容是R1+4×8。
第2条指令将R3~R8、R10这7个寄存器的内容保存到以R0的初值为首地址的字存储单元中,最后R0的内容是R0+4×6。
注意 :指令中的后缀IA(Increment After)为操作模式,意思是传送数据之后,再增加地址指针,即传送完成后地址加4。另外还有如下后缀。
IB(Increment Before):指传送前地址先加4。
DA(Decrement After):指传送后地址减4。
DB(Decrement Before):指传送前地址先减4。
堆栈是一种数据结构,堆栈是按特定顺序进行存取的存储区,操作顺序分为“后进先出”和“先进后出”,堆栈寻址是隐含的,它使用一个专门的寄存器(堆栈指针寄存器R13)指向一块存储区域(堆栈),指针所指向的存储单元就是堆栈的栈顶。
● 递增堆栈:堆栈区由低地址向高地址方向生长,称为递增堆栈(Ascending Stack)。
● 递减堆栈:堆栈区由高地址向低地址方向生长,称为递减堆栈(Descending Stack)。
● 满堆栈:堆栈指针指向最后压入堆栈的有效数据项,称为满堆栈(Full Stack)。对于满堆栈的操作,在进行压栈时要先修改堆栈指针,再压入数据;在弹栈时要先弹出数据,再修改指针。否则,就会发生错误。
● 空堆栈:堆栈指针指向下一个要放入的空位置,称为空堆栈(Empty Stack)。对于空堆栈的操作,在进行压栈时要先压入数据,再修改堆栈指针;在弹栈时要先修改指针,再弹出数据。否则,就会发生错误。
● 满递增堆栈(Full & Ascending stack,FA):堆栈指针指向最后压入的数据,且栈区由低地址向高地址生成。如指令LDMFA、STMFA等。
● 满递减堆栈(Full & Descending stack,FD):堆栈指针指向最后压入的数据,且栈区由高地址向低地址生成。如指令LDMFD、STMFD等。
● 空递增堆栈(Empty & Ascending stack,EA):堆栈指针指向下一个将要放入数据的空位置,且栈区由低地址向高地址生成。如指令LDMEA、STMEA等。
● 空递减堆栈(Empty & Descending stack,ED):堆栈指针指向下一个将要放入数据的空位置,且栈区由高地址向低地址生成。如指令LDMED、STMED等。
注意 :上述每种堆栈工作方式的压栈指令和弹栈指令要成对使用,千万不能配错;压栈和弹栈的多寄存器列表必须一一对应,而且排列次序也是寄存器下标从小到大。需要说明的是,按照堆栈操作的原则,正确的方法是“先进后出,后进先出”,那么弹栈的寄存器列表顺序应该从大到小,实际书写编辑时仍然是从小到大的顺序,程序员不必担心,指令在操作时会自动地按照堆栈区的操作原则进行。
应用示例:
块复制寻址方式就是把一块从存储器的某一位置开始的数据复制到多个寄存器中,或者把多个寄存器的内容复制到存储器的某一块中。它是多地址多寄存器寻址方式的一种应用,实际上要完成的是将从存储器的某一块开始的数据复制到存储器的另外一块中去,这里中间的过渡缓冲区就是这多个寄存器列表。
它与堆栈的操作基本相同,也有4组配对的操作模式指令。
LDMIA/STMIA:在传送数据之后增加地址指针,块中的首地址值最小。
LDMIB/STMIB:在传送数据之前增加地址指针,块中的首地址值最小。
LDMDA/STMDA:在传送数据之后减小地址指针,块中的首地址值最大。
LDMDB/STMDB:在传送数据之前减小地址指针,块中的首地址值最大。
应用示例:
第1条指令是将以R0为首地址的字单元存储器的内容加载到R2~R12中。存储器指针R0在每加载一个值之后增加,增加步长为4,增长方向为向上增长。R0的内容自动修改。
第2条指令是将R2~R12的数据保存到以R1初值为首地址的字存储单元中,存储器指针R1每保存一个值之后增加,增加步长为4,增长方向也为向上增长。R1的内容自动修改。
这2条指令结合在一起实现的功能就是将以R0为首地址的字单元存储器的内容复制到以R1为首地址的字存储单元块中,共11×4=44字节数据。
注意 :寄存器间接寻址方式、多寄存器寻址方式、堆栈寻址方式和块复制寻址方式4种指令格式的差异和完成操作的特点说明如下:
1)寻址方式大都是寄存器间接寻址,但是寄存器间接寻址方式中的寄存器需要方括号[],而后3种不需要。而且其后的偏移量范围很大,可正可负,只要与基址累加不超出ARM存储器的地址范围就行。
2)多寄存器寻址方式中的基址寄存器不需要方括号[],且根据“!”的有无来决定指针是否自动修改。有则改;无则不改。修改值由指令的后缀模式来决定,模式为IA、IB时地址指针为+4;模式为DA、DB时地址指针为-4,上下偏移量只有±4。
3)堆栈寻址方式和块复制寻址方式实际上是多寄存器寻址方式的特殊应用,它们的基址寄存器也不需要使用方括号[],修改的偏移量只有±4,并且正负号也由指令码后缀模式(FA、EA、FD、ED)确定。模式为FA、EA时偏移量为+4;模式为FD、ED时偏移量为-4。堆栈寻址方式指向存储器(栈区)的寄存器必须是SP(R13)!。“!”必须有,因为压栈与弹栈必须有机配合才能保证堆栈操作的正确使用。
块复制寻址方式与堆栈寻址方式基本相同,差异在于将指令格式中的SP!修改为Rn!,n=1,2,…,11,12。需要注意的是,如果基址寄存器使用了哪个寄存器,就不要出现在指令的花括号{}中。
相对寻址是变址寻址的一种变通,由程序计数器PC提供基准地址,指令中的地址码字段作为偏移量,两者相加后得到的地址即为操作数的有效地址。
应用示例: