第3章ARM指令集寻址方式







ARM指令集寻址方式可分为4大类: 数据处理指令寻址、Load/Store指令的寻址、批量Load/Store指令的寻址和协处理指令寻址。
3.1ARM指令的编码格式
1. 一般编码格式

每条ARM指令占有4字节,其指令长度为32位。典型的ARM指令编码格式如下。



其中: 
cond(bit[31:28]): 指令执行的条件码。
type(bit[27:26]): 指令类型码,根据其编码的不同,所代表各类型如表31所示。


表31指令类型码描述


type(bit[27:26])描述


00数据处理指令及杂类Load/Store指令

01Load/Store指令

10批量Load/Store指令及分支指令

11协处理指令与软中断指令

I(bit[25]): 第二操作数类型标志码。在数据处理指令里I=1时表示第二操作数是立即数,I=0时表示第二操作数是寄存器或寄存器移位形式。
opcode(bit[24:21]): 指令操作码。
S(bit[20]): 决定指令的操作结果是否影响CPSR。
Rn(bit[19:16]): 包含第一个操作数的寄存器编码。
Rd(bit[15:12]): 目标寄存器编码。
operand2(bit[11:0]): 指令第二个操作数。
ARM汇编指令语法格式: 

<opcode>{<cond>}{S}<Rd>,<Rn>,<operand2>

2. 指令条件码
条件码cond(bit[31:28])在指令中共占4位,其组合形式共有16种,如表32所示。


表32条件码


条件码条件码助记符描述PSR中的标志位


0000EQ相等Z=1
0001NE不相等Z=0
0010CS/HS无符号大于或等于C=1
0011CC/LO无符号小于C=0
0100MIè´Ÿæ•°N=1
0101PL非负数N=0
0110VS上溢出V=1
0111VC没有上溢出V=0
1000HI无符号数大于C=1且Z=0
1001LS无符号小于或等于C=0或Z=1
1010GE有符号数大于或等于N=1且V=1或N=0且V=0
1011LT有符号数小于N=1且V=0或N=0且V=1
1100GT有符号数大于Z=0且N=V
1101LE有符号数小于或等于Z=1或N!=V
1110AL无条件执行


3.2数据处理指令寻址方式
1. 数据处理指令第二操作数的构成方式

数据处理指令第二操作数operand2的构成有如下3种格式。
1) 立即数方式
每个立即数由一个8位的常数进行32位循环右移偶数位得到,其中循环右移的位数由一个4位二进制的两倍表示,即

<immediate>=immed_8进行32位循环右移(2*rotate_4)位

合法的立即数,例如: 0xff,0x104。不合法的立即数,例如: 0x101,0x102。
规则: 当立即数值在0~0xff范围时,令immed_8=immediate,rotate_4=0; 在其他情况下,汇编编译器选择使rotate_4数值最小的编码方式。
2) 寄存器方式
操作数即为寄存器的数值,例如: 

MOV R3,R2

ADD R0,R1,R2

3) 寄存器移位方式
操作数为寄存器的数值做相应的移位而得到。在ARM指令中移位操作包括逻辑左移、逻辑右移、算术左移、算术右移、循环右移和带扩展的循环右移,这些操作的功能如图31所示。


图31移位操作功能示意


LSL逻辑左移: 空出的最低有效位用0填充; 
LSR逻辑右移: 空出的最高有效位用0填充; 
ASL算术左移: 同LSL; 
ASR算术右移: 空出的最高有效位用“符号位”填充; 
ROR循环右移: 移出的最低有效位依次填入空出的最高有效位; 
RRX带扩展的循环右移: 将寄存器内容循环右移1位,空位用原来的C标志位填充,移出的最低有效位填入C标志位。
2. 具体寻址类型
数据处理指令寻址方式具体可分为5种类型,下面分别加以介绍。
1) 第二操作数为立即数
汇编语法格式: 

#<immediate>

指令编码格式如下: 



其中,由一个8位的常数immed_8进行32位循环右移rotate_4的2倍位得到的立即数immediate作为数据处理指令的第二操作数。例如: 

MOV R0,#0xFC0; R0<—0xFC0

2) 第二操作数为寄存器
汇编语法格式: 

<Rm>

指令编码格式如下: 


例如: 

ADD R0,R1,R2; R0<—R1+R2

3) 第二操作数为寄存器移位方式,且移位的位数为一个5位的立即数
汇编语法格式: 

<Rm>,<shift> #<shift_imm>

指令编码格式如下: 



其中: 
shift_amount表示移位数量; 
shift表示移位类型编码,bit[5]用H表示,bit[6]用S表示,其描述如表33所示。


表33shift编码描述


SH描述


00逻辑左移LSL

01逻辑右移LSR

10算术右移ASR

11循环右移ROR

指令的操作数<operand2>为寄存器Rm的数值按某种移位方式移动shift_amount位,这里shift_amount的范围是0~31,当shift_amount=0时,移位位数为32,则移位数范围为1~32。例如: 

MOV R0,R0,LSL #n ; R0<—R0 * (2n)  (n= 0~31)

4) 第二操作数为寄存器移位方式,且移位数值放在寄存器中
汇编语法格式: 

<Rm>,<shift>  <Rs>

指令编码格式如下: 



其中: 
寄存器Rs中存放着要移位的数量; 
shift表示移位类型编码,其描述如表33所示。
指令的操作数<operand2>为寄存器Rm的数值进行移位得到。移位的数由Rs的低8位bit[7:0]决定。

注意事项: 
当R15用作Rn、Rm、Rd、Rs中的任一个时,指令会产生不可预知的结果。

5) 第二操作数为寄存器进行RRX移位得到
汇编语法格式: 

<Rm>,RRX

指令编码格式如下: 


指令的操作数<operand2>为寄存器Rm中的数值进行带扩展的循环右移一位,并用CPSR中的C条件标志位填补空出的位,CPSR中的C条件标志位则用移出的位代替。
3.3Load/Store指令寻址
Load/Store指令是对内存进行存储/加载数据操作的指令,根据访问的数据格式的不同,将这类指令的寻址分为字、无符号字节的Load/Store指令寻址和半字、有符号字节的Load/Store指令寻址两大类。
3.3.1地址计算方法
1. 寄存器间接寻址

寄存器间接寻址就是以寄存器中的值作为操作数的地址,而操作数本身存放在存储器中。例如以下指令: 

LDRR0,[R1]; R0 ←[R1] 

STRR0,[R1]; [R1]← R0 

在第一条指令中,以寄存器R1的值作为操作数的地址,在存储器中取得一个操作数后将其送入R0中。
第二条指令将R0的值传送到以R1的值为地址的存储器中。
2. 基址加变址寻址
基址加变址寻址就是将寄存器(该寄存器一般称作基址寄存器)的内容与指令中给出的地址偏移量相加,从而得到一个操作数的有效地址。变址寻址方式常用于访问某基地址附近的地址单元。
根据访问存储单元和基址寄存器更新的先后顺序可以将基址加变址寻址分为以下两种。
(1) 前变址法: 基址寄存器中的值和地址偏移量先做加减运算,生成的操作数作为内存访问的地址。
(2) 后变址法: 将基址寄存器中的值直接作为内存访问的地址进行操作,内存访问完毕后基址寄存器中的值和地址偏移量做加减运算,并更新基址寄存器。
采用变址寻址方式的指令常见有以下几种形式: 

LDR R0, [R1 ,# 4] ; R0 ←[R1+4]

LDR R0, [R1 ,# 4] !; R0 ←[R1+4]且R1←R1+4 

LDR R0, [R1] ,# 4 ; R0 ←[R1]且R1←R1+4 

LDR R0, [R1 , R2] ; R0 ←[R1+R2] 

在第一条指令中,将寄存器R1的内容加上4形成操作数的有效地址,从而取得操作数存入寄存器R0中。
在第二条指令中,将寄存器R1的内容加上4形成操作数的有效地址,从而取得操作数存入寄存器R0中; 然后,R1的内容自增4(也就是R1的内容加4后写回到R1中)。
在第三条指令中,以寄存器R1的内容作为操作数的有效地址,从而取得操作数存入寄存器R0中; 然后,R1的内容自增4。
在第四条指令中,将寄存器R1的内容加上寄存器R2的内容形成操作数的有效地址,从而取得操作数存入寄存器R0中。
3.3.2字、无符号字节寻址
在Load/Store指令中,字与无符号字节操作指令编码格式如下: 


汇编指令语法格式如下。
加载指令: 

LDR {<cond>}{B}{T}<Rd>,<addressing_mode>

存储指令: 

STR {<cond>}{B}{T}<Rd>,<addressing_mode>

其中: 
cond为指令执行的条件,Rn为基址寄存器,Rd为源/目标寄存器,addressing_mode为内存地址构成格式。
I=1: 偏移量为寄存器或寄存器移位形式

0: 偏移量为12位立即数
P=1: 前变址操作

0: 后变址操作
U=1: 内存地址address为基址寄存器Rn值加上地址偏移量

0: 内存地址address为基址寄存器Rn值减去地址偏移量
B=1: 指令访问的是无符号的字节数据

0: 指令访问的是字数据
W=1: 执行基址寄存器回写操作

0: 不执行基址寄存器回写操作
L=1: 执行Load操作

0: 执行Store操作
下面我们将重点讨论内存地址构成格式addressing_mode。
根据指令编码和汇编语法格式的不同,归纳起来共有以下3种内存地址构成格式。
1. addressing_mode中的偏移量为立即数
addressing_mode中的偏移量为立即数的汇编指令寻址按编码格式可分为以下3种形式。
前变址不回写形式: 

[<Rn>,#+/-<immed_offset>]

前变址回写形式: 

[<Rn>,#+/-<immed_offset>]!

后变址回写形式: 

[<Rn>],#+/-<immed_offset>

其指令编码如下: 


P=1: 前变址操作
0: 后变址操作

U=1: 内存地址address为基址寄存器Rn值加上地址偏移量
0: 内存地址address为基址寄存器Rn值减去地址偏移量

B=1: 指令访问的是无符号的字节数据
0: 指令访问的是字数据

W=1: 执行基址寄存器回写操作
0: 不执行基址寄存器回写操作

L=1: 执行Load操作
0: 执行Store操作

addressing_mode中的偏移量为立即数的汇编指令寻址编码对应的汇编语法格式类型如表34所示。下面介绍各汇编语法格式下的地址计算方法。
1) [<Rn>,#+/-<immed_offset>]

内存地址为基址寄存器值加上/减去immed_offset,其加减由U的值来确定。


表34偏移量为立即数的指令编码类型


WP汇编语法格式


01[<Rn>,#+/-<immed_offset>]

10[<Rn>],#+/-<immed_offset>

11[<Rn>,#+/-<immed_offset>]!

2) [<Rn>,#+/-<immed_offset>]!

内存地址为基址寄存器值加上/减去immed_offset,其加减由U的值来确定。当指令执行时,生成的地址值将写入基址寄存器。
3) [<Rn>],#+/-<immed_offset>

内存地址为基址寄存器值,当存储器操作完毕后,将基址寄存器Rn值加上/减去immed_offset,将所得到的值写回到基址寄存器Rn(更新基址寄存器),其加减由U的值来确定。
【例31】指令功能解析。
LDR R0,[R1,#4];   R0<—[R1+4]

LDR R0,[R1,#-4];  R0<—[R1-4]

LDR R0,[R1,#4]!;   R0<—[R1+4],同时R1=R1+4

LDR R0,[R1],#4;   R0<—[R1],R1=R1+4

2. addressing_mode中的偏移量为寄存器的值
addressing_mode中的偏移量为寄存器的汇编指令寻址按编码格式可分为以下3种形式。
前变址不回写形式: 

[<Rn>,+/-<Rm>]

前变址回写形式: 

[<Rn>,+/-<Rm>]!

后变址回写形式: 

[<Rn>],+/-<Rm>

其指令编码如下: 


P=1: 前变址操作
0: 后变址操作

U=1: 内存地址address为基址寄存器Rn值加上地址偏移量
0: 内存地址address为基址寄存器Rn值减去地址偏移量

B=1: 指令访问的是无符号的字节数据
0: 指令访问的是字数据

W=1: 执行基址寄存器回写操作
0: 不执行基址寄存器回写操作

L=1: 执行Load操作
0: 执行Store操作


addressing_mode中的偏移量为寄存器的汇编指令寻址编码对应的汇编语法格式类型如表35所示。下面介绍各汇编语法格式下的地址计算方法。


表35偏移量为寄存器的指令编码类型对应关系


WP汇编语法格式


01[<Rn>,+/-<Rm>]

10[<Rn>],+/-<Rm>

11[<Rn>,+/-<Rm>]!

1) [<Rn>,+/-<Rm>]

内存地址为基址寄存器值加上/减去Rm,其加减由U的值来确定。
2) [<Rn>,+/-<Rm>]!

内存地址为基址寄存器值加上/减去Rm,其加减由U的值来确定。当指令执行时,生成的地址值将写入基址寄存器。
3) 
[<Rn>],+/-<Rm>

内存地址为基址寄存器值,当存储器操作完毕后,将基址寄存器Rn值加上/减去Rm,将所得到的值写回到基址寄存器Rn(更新基址寄存器),其加减由U的值来确定。

注意事项: 
 程序计数器PC(R15)用作索引寄存器Rm时,会产生不可预知的结果。
 当Rn 和 Rm为同一个寄存器时,会产生不可预知的结果。

【例32】指令功能解析。

LDR R0,[R1,R2];   R0<—[R1+R2]

LDR R0,[R1,-R2];  R0<—[R1-R2]

LDR R0,[R1,R2]!;   R0<—[R1+R2],且 R1=R1+R2

LDR R0,[R1],R2;   R0<—[R1], R1=R1+R2

3. addressing_mode中的偏移量通过寄存器移位得到
addressing_mode中的偏移量是通过寄存器移位所得到的,这类汇编指令寻址按编码格式可分为以下3种形式。
前变址不回写形式: 

[<Rn>,+/-<Rm>,<shift>#shift_amount]

前变址回写形式: 

[<Rn>,+/-<Rm>,<shift>#shift_amount]!

后变址回写形式: 

[<Rn>],+/-<Rm>,<shift>#shift_amount

其指令编码格式如下: 


P=1: 前变址操作
0: 后变址操作

U=1: 内存地址address为基址寄存器Rn值加上地址偏移量
0: 内存地址address为基址寄存器Rn值减去地址偏移量

B=1: 指令访问的是无符号的字节数据
0: 指令访问的是字数据

W=1: 执行基址寄存器回写操作
0: 不执行基址寄存器回写操作

L=1: 执行Load操作
0: 执行Store操作

通过寄存器移位得到地址偏移量指令编码对应的汇编语法格式类型如表36所示。下面介绍各汇编语法格式下的地址计算方法。


表36偏移量为移位寄存器的指令编码类型对应关系


WP汇编语法格式


01[<Rn>,+/-<Rm>,<shift>#shift_amount]

10[<Rn>],+/-<Rm>,<shift>#shift_amount

11[<Rn>,+/-<Rm>,<shift>#shift_amount]!

1) [<Rn>,+/-<Rm>,<shift>#shift_amount]

内存地址为基址寄存器值加上/减去Rm通过移位shift_amount后所得到的数值,其加减由U的值来确定。
2) 
[<Rn>,+/-<Rm>,<shift>#shift_amount]!

内存地址为基址寄存器值加上/减去Rm通过移位shift_amount后所得到的数值,其加减由U的值来确定。当指令执行时,生成的地址值将写入基址寄存器。
3) 
[<Rn>],+/-<Rm>,<shift>#shift_amount

内存地址为基址寄存器值,当存储器操作完毕后,将基址寄存器Rn值加上/减去Rm通过移位shift_amount后所得到的数值,将所得到的值写回到基址寄存器Rn(更新基址寄存器),其加减由U的值来确定。

注意事项: 
 程序计数器PC(R15)用作索引寄存器Rm时,会产生不可预知的结果。
 当Rn 和 Rm为同一个寄存器时,会产生不可预知的结果。


【例33】指令功能解析。

LDRR0,[R1,R2,LSL #2]; R0<—[R1+R2*4]

LDRR0,[R1,R2,LSL #2]!; R0<—[R1+R2*4]且 R1=R1+R2*4

LDRR0,[R1],R2,LSL #2; R0<—[R1],R1=R1+R2*4

3.3.3半字、有符号字节寻址
这类指令可用来加载有符号字节、加载有符号半字、加载/存储无符号半字。一般称这类指令为“杂类的Load/Store指令”。
Load/Store指令对半字、有符号字节操作指令编码格式如下。


汇编指令汇编语法格式如下。
加载有符号字节到寄存器: 

LDR {<cond>}SB <Rd>,<addressing_mode>

加载有符号半字到寄存器: 

LDR {<cond>}SH <Rd>,<addressing_mode>

加载无符号半字到寄存器: 

LDR {<cond>}H  <Rd>,<addressing_mode>

存储无符号半字到内存: 

STR {<cond>}H  <Rd>,<addressing_mode>

cond为指令执行的条件。
P=1: 前变址操作
0: 后变址操作

U=1: 内存地址address为基址寄存器Rn值加上地址偏移量
0: 内存地址address为基址寄存器Rn值减去地址偏移量

I=1: 偏移量为8位立即数
0: 偏移量为寄存器

W=1: 执行基址寄存器回写操作
0: 不执行基址寄存器回写操作

L=1: 执行Load操作
0: 执行Store操作

Rn为基址寄存器,Rd为源/目标寄存器,addressing_mode为内存地址构成格式,对应指令编码中的S、H位编码将在4.3.2节详细介绍。
下面我们将重点讨论内存地址构成格式addressing_mode。根据指令编码和汇编语法格式的不同,归纳起来共有以下两种内存地址构成格式。
1. addressing_mode中的偏移量为立即数	
addressing_mode中的偏移量为立即数的汇编语法格式有以下3种。
前变址不回写形式: 

[<Rn>,#+/-<immed_offset8>]

前变址回写形式: 

[<Rn>,#+/-<immed_offset8>]!

后变址回写形式: 

[<Rn>],#+/-<immed_offset8>

其指令编码如下: 


P=1: 前变址操作
0: 后变址操作

U=1: 内存地址address为基址寄存器Rn值加上地址偏移量
0: 内存地址address为基址寄存器Rn值减去地址偏移量

W=1: 执行基址寄存器回写操作
0: 不执行基址寄存器回写操作

L=1: 执行Load操作
0: 执行Store操作

从指令编码中可以看出,immed_offset8是一个8位的偏移量,这个8位的偏移量是由两个4位的偏移量(offset_H、offset_L)构成,其中offset_H为8位偏移量的bit[7:4]位,offset_L为8位偏移量的bit[3:0]位。
偏移量为立即数的指令编码对应的汇编语法格式类型如表37所示。下面介绍各汇编语法格式下的地址计算方法。


表37偏移量为立即数的指令编码类型


WP汇编语法格式


01[<Rn>,#+/-<immed_offset8>]

10[<Rn>],#+/-<immed_offset8>

11[<Rn>,#+/-<immed_offset8>]!

1) [<Rn>,#+/-<immed_offset8>]

内存地址为基址寄存器值加上/减去immed_offset8,其加减由U的值来确定。
2) 
[<Rn>,#+/-<immed_offset8>]!

内存地址为基址寄存器值加上/减去immed_offset8,其加减由U的值来确定。当指令执行时,生成的地址值将写入基址寄存器。
3) 
[<Rn>],#+/-<immed_offset8>

内存地址为基址寄存器值,当存储器操作完毕后,将基址寄存器Rn值加上/减去immed_offset8,将所得到的值写回到基址寄存器Rn(更新基址寄存器),其加减由U的值来确定。

注意事项: 
 程序计数器PC(R15)用作基址寄存器Rn时,会产生不可预知的结果。
 当S=1且L=0时,表示带符号数的存储指令,目前还没有实现此功能的ARM指令。

【例34】指令功能解析。

LDRSB R0,[R1,#4]; R0<—[R1+4]字节单元,R0有符号扩展为32位

LDRH R2,[R1,#-4]; R2<—[R1-4]半字单元,R2的高16位清零

STRH R0,[R1,#4]!; R0低16位—>[R1+4],同时R1=R1+4

LDRSH R0,[R1],#4; R0<—[R1]半字单元且R0有符号扩展为32位,R1=R1+4

2. addressing_mode中的偏移量为寄存器的值	
addressing_mode中的偏移量为寄存器值的汇编语法格式有以下3种。
前变址不回写形式: 

[<Rn>,+/-<Rm>]

前变址回写形式: 

[<Rn>,+/-< Rm >]!

后变址回写形式: 

[<Rn>],+/-< Rm >

其指令编码如下: 


P=1: 前变址操作
0: 后变址操作

U=1: 内存地址address为基址寄存器Rn值加上地址偏移量
0: 内存地址address为基址寄存器Rn值减去地址偏移量

W=1: 执行基址寄存器回写操作
0: 不执行基址寄存器回写操作

L=1: 执行Load操作
0: 执行Store操作


偏移量为寄存器值的指令编码对应的汇编语法格式类型如表38所示。下面介绍各汇编语法格式下的地址计算方法。


表38偏移量为寄存器值的指令编码类型对应关系


WP汇编语法格式


01[<Rn>,+/-< Rm >]

10[<Rn>],+/-< Rm >

11[<Rn>,+/-< Rm >]!

1) [<Rn>,+/-< Rm >]
内存地址为基址寄存器值加上/减去Rm,其加减由U的值来确定。
2) [<Rn>,+/-< Rm >]!
内存地址为基址寄存器值加上/减去Rm,其加减由U的值来确定。当指令执行时,生成的地址值将写入基址寄存器。
3) [<Rn>],+/-< Rm >
内存地址为基址寄存器值,当存储器操作完毕后,将基址寄存器Rn值加上/减去Rm,将所得到的值写回到基址寄存器Rn(更新基址寄存器),其加减由U的值来确定。

注意事项: 
 程序计数器PC(R15)用作基址寄存器Rn或索引寄存器Rm时,会产生不可预知的结果。
 基址寄存器Rn和索引寄存器Rm为同一个寄存器时,会产生不可预知的结果。

【例35】指令功能解析。

LDRSB R0,[R1,R5]; R0<—[R1+R5]字节单元,R0有符号扩展为32位

LDRH R2,[R1,-R5]; R2<—[R1-R5]半字单元,R2的高16位清零

STRH R0,[R1,R5]!; R0低16位—>[R1+R5],同时R1=R1+R5

LDRSH R0,[R1],R5; R0<—[R1]半字单元且R0有符号扩展为32位,R1=R1+R5

3.4批量Load/Store指令寻址方式
ARM指令系统提供了批量Load/Store指令寻址方式,即通常所说的多寄存器寻址,也就是一次可以传送几个寄存器的值,允许一条指令最多传送16个寄存器。
1. 编码格式
批量Load/Store指令汇编语法格式如下。
批量加载: 

LDM {<cond>}<addressing_mode> <Rn> {!},<register>{^^}

批量存储: 

STM {<cond>}<addressing_mode> <Rn> {!},<register>{^^}

批量Load/Store指令编码格式如下: 


cond为指令执行的条件。
P=1: 前变址操作
0: 后变址操作

U表示地址变化的方向。 
U=1: 地址向上变化(Upwards)
0: 地址向下变化(Downwards)

W=1: 执行基址寄存器回写操作
0: 不执行基址寄存器回写操作

L=1: 执行Load操作
0: 执行Store操作

Rn为基址寄存器,也就是内存地址块的最低地址值。
register_list表示要加载或存储的寄存器列表,bit[15:0]可以表示16个寄存器,如果某位为1,则该位的位置作为寄存器的编号,此寄存器参与加载或存储。例如,bit[8]为1,则代表R8参与加载或存储。
S用于恢复CPSR和强制用户位。当程序计数器PC包含在LDM指令的register_list中,且S为1时,则当前模式的SPSR被复制到CPSR中,使处理器的程序返回和状态的恢复成为一个原子操作。如果register_list中不包含程序计数器PC,S为1则加载或存储的是用户模式下的寄存器组。
addressing_mode表示地址的变化方式。

注意事项: 
指令中寄存器和连续内存地址单元的对应关系: 编号低的寄存器对应内存低地址单元,编号高的寄存器对应内存高地址单元。

2. 内存操作
批量Load/Store指令在实现寄存器组和连续的内存单元中数据传递时,地址的变化方式(addressing_mode)有以下4种类型。
 后增IA(Increment After): 每次数据传送后地址加4。
 先增IB(Increment Before): 每次数据传送前地址加4。
 后减DA(Decrement After): 每次数据传送后地址减4。
 先减DB(Decrement Before): 每次数据传送前地址减4。
它们与指令编码中P、U的对应关系如表39所示。


表39地址变化方式与指令编码中P、U的对应关系


addressing_modePU


DA00

IA01

DB10

IB11

3. 堆栈操作
堆栈是一种数据结构,按先进后出(First In Last Out,FILO)的方式工作,使用一个称作堆栈指针的专用寄存器指示当前的操作位置,堆栈指针总是指向栈顶,在ARM里常用R13作为栈指针(SP)。
根据堆栈指针的指向位置的不同可以将堆栈分为满堆栈和空堆栈。
 满堆栈(Full Stack): 当堆栈指针指向最后压入堆栈的数据时。 
 空堆栈(Empty Stack): 当堆栈指针指向下一个将要放入数据的空位置时。
根据堆栈的生成方式,又可以分为递增堆栈和递减堆栈。
 递增堆栈(Ascending Stack): 当堆栈由低地址向高地址生成时。 
 递减堆栈(Decending Stack): 当堆栈由高地址向低地址生成时。
4种类型的堆栈工作方式如下。
 满递增堆栈FA(Full Ascending): 堆栈指针指向最后压入的数据,且由低地址向高地址生成。
 满递减堆栈FD(Full Descending): 堆栈指针指向最后压入的数据,且由高地址向低地址生成。
 空递增堆栈EA(Empty Ascending): 堆栈指针指向下一个将要放入数据的空位置,且由低地址向高地址生成。
 空递减堆栈ED(Empty Descending): 堆栈指针指向下一个将要放入数据的空位置,且由高地址向低地址生成。
栈操作其实也是块拷贝操作,每一条栈操作指令都相应与一条块拷贝操作指令相对应,其对应关系如表310所示。


表310块拷贝与栈操作的对应关系


块拷贝


栈操作
地址变化方向

向上向下

满空满空



增

å…ˆSTMIB
STMFALDMIB
LDMED

后STMIA
STMEALDMIA
LDMFD


减

å…ˆLDMDB
LDMEASTMDB
STMFD

后LDMDA
LDMFASTMDA
STMED


例如: 

LDMIA R0,{R5R8}; 

功能解析: 将内存单元[R0]~[R0+15]以字为单位读取到R5~R8中,低地址编号的字数据内存单元对应低编号寄存器。
3.5协处理器指令寻址方式
ARM支持协处理器操作,其操作要通过协处理器命令来实现,下面讨论协处理指令具体的寻址方式。
1. 协处理器加载/存储指令的寻址方式
协处理器的加载存储指令可以用来实现ARM处理器与协处理器之间的数据传输。
其汇编语法格式如下: 

<opcode>{<cond>}{L} <coproc>,<CRd>,<addressing_mode>

其中: 
opcode为指令操作码; 
coproc为协处理器名称; 
addressing_mode为指令寻址模式。
根据内存地址的构成方式,可分为索引格式和非索引格式。
1) 内存地址索引格式
索引格式类似于LDR/STR指令寻址中的立即数作为地址偏移量的形式。
addressing_mode中的偏移量为8位立即数的汇编语法格式有以下3种。
前变址不回写形式: 

[<Rn>,#+/-<immed_offset8>*4 ]

前变址回写形式: 

[<Rn>,#+/-<immed_offset8>*4]!

后变址回写形式: 

[<Rn>],#+/-<immed_offset8>*4

指令编码格式如下: 


cond为指令执行的条件。
Cp_num为协处理器的编号。
P=1: 前变址操作
0: 后变址操作

U=1: 内存地址address为基址寄存器Rn值加上地址偏移量
0: 内存地址address为基址寄存器Rn值减去地址偏移量

N: 依赖于具体的协处理器,一般用来表示传输数据的大小。
W=1: 执行基址寄存器回写操作
0: 不执行基址寄存器回写操作

L=1: 执行Load操作
0: 执行Store操作

CRd作为目标寄存器的协处理器寄存器。
偏移量为8位立即数的指令编码对应的汇编语法格式类型如表311所示。下面介绍各汇编语法格式下的地址计算方法: 


表311协处理器加载/存储指令编码W、P与地址模式的关系


WP汇编语法格式


01[<Rn>,#+/-<immed_offset8>*4]

10[<Rn>],#+/-<immed_offset8>*4

11[<Rn>,#+/-<immed_offset8>4*]!


(1) [<Rn>,#+/-<immed_offset8>*4]
第一个内存地址编号为基址寄存器Rn值加上/减去immed_offset8的4倍(其加减由U的值来确定),后续的每一个地址是前一个内存地址加4,直到协处理器发出信号,结束本次数据传输为止。
(2) [<Rn>,#+/-<immed_offset8>*4]!
内存地址为基址寄存器Rn值加上/减去immed_offset8的4倍(其加减由U的值来确定),后续的每一个地址是前一个内存地址加4,直到协处理器发出信号,结束本次数据传输为止。当指令执行时,生成的地址值(也就是Rn值加上/减去immed_offset8的4倍)将写入基址寄存器。
(3) [<Rn>],#+/-<immed_offset8>*4
内存地址为基址寄存器Rn值,当存储器操作完毕后,将基址寄存器Rn值加上/减去immed_offset8的4倍(其加减由U的值来确定),后续的每一个地址是前一个内存地址加4,直到协处理器发出信号,结束本次数据传输为止。最后将Rn值加上/减去immed_offset8的4倍写回到基址寄存器Rn(更新基址寄存器)。
2) 内存地址非索引格式
这种指令寻址汇编语法格式为:

[<Rn>],<userdefined>

指令编码格式如下: 



其中:
cond为指令执行的条件;

U=1: 内存地址address为基址寄存器Rn值加上地址偏移量
0: 内存地址address为基址寄存器Rn值减去地址偏移量

N: 依赖于具体的协处理器,一般用来表示传输数据的大小;
L=1: 执行Load操作
0: 执行Store操作

CRd作为目标寄存器的协处理器寄存器;
Cp_num为协处理器的编号;
userdefined为用户自定义内容。
这种寻址方式用来产生一段连续的内存地址,第一个地址值为基址寄存器<Rn>的值,后续的每一个地址是前一个内存地址加4,直到协处理器发出信号,结束本次数据传输为止。

注意事项: 
 从索引格式和非索引格式寻址情况来看,它们有一个共同点: 数据传输的数目是由协处理器来决定的。
 在使用中,这两种寻址方式最大可以传输16个字数据。

2. 协处理器数据处理指令的寻址方式
协处理器数据处理指令的寻址方式主要通过寄存器寻址,根据寄存器编码来查找相应的寄存器,这部分内容在指令系统中进行详细介绍。
思考与练习题
1. 在指令编码中,条件码占有几位?最多有多少个条件?各个条件是如何形成的?
2. 指令条件码中,V标志位在什么情况下才能等于1?
3. 在ARM指令中,什么是合法的立即数?判断下面各立即数是否合法,如果合法则写出在指令中的编码格式(也就是8位常数和4位的移位数)。
0x54300x1080x3040x501
0xfb100000x3340000x3FC0000x1FE0000
0x55800000x7F8000x39C0000x1FE80000
4. 分析逻辑右移、算术右移、循环右移、带扩展的循环右移之间的区别。
5. ARM数据处理指令具体的寻址方式有哪些?如果程序计数器PC作为目标寄存器,会产生什么结果?
6. 在Load/Store指令寻址中,字、无符号字节的Load/Store指令寻址和半字、有符号字节的寻址之间有什么差别?
7. 块拷贝Load/Store指令在实现寄存器组和连续的内存单元中数据传递时,地址的变化方式有哪几种类型?分析它们的地址变化情况。
8. 栈操作指令地址的变化方式有哪几种类型?分析它们的地址变化情况,从而得出栈操作指令寻址和块拷贝Load/Store指令之间的对应关系。
9. 分析协处理器加载/存储指令的寻址方式中的内存地址索引格式中不同的汇编语法格式下内存地址的计算方法。
10. 写出下列指令的机器码,并分析指令操作功能。

MOVR0,R1

MOVR1,#0x198

ADDEQSR1,R2,#0xAB

CMPR2,#0Xab

LDRR0,[R1,#4]		

STRR0,[R1,R1,LSL#2]!

LDRHR0,[R1,#4]

LDRSBR0,[R2,#-2]!

STRBR1,[R2,#0xA0]

LDMIAR0,{R1,R2,R8}

STMDBR0!,{R1-R5,R10,R11}

STMEDSP!,{R0-R3,LR}