ARM 汇编指令学习:[1]ARM指令寻址方式
一、数据处理指令的操作数的寻址方式
<opcode>{<cond>}{S} <Rd>,<Rn>,<shifter_operand>
<shifter_operand>通常具有下面3种格式:
1、 立即数方式
-
#<immediate>
其中,#<immediate>=immed_8循环右移(2*rotate_imm)。
使用说明
这里需要注意关于立即数的合法性以立即数编码的规则。
示例
MOV R0, #0xFC0
2、 寄存器方式
-
<Rm>
其中,<Rm>指定操作数所在的寄存器。
使用说明
当R15(PC)用作第一个源操作数Rn或者第二操作数Rm时,操作数即为当前指令地址加常数8。
示例
MOV R0, R1
3、 寄存器位移方式
-
<Rm>, LSL #<shift_imm>
其中:
<Rm> 为进行逻辑左移操作的寄存器。
LSL 逻辑左移操作。
#<shift_imm> 逻辑左移位数,范围0~31。
使用说明
当R15(PC)用作第一个源操作数Rn或者第二操作数Rm时,操作数即为当前指令地址加常数8。
示例
MOV R0, R0, LSL #n ;R0=R0<<n
-
<Rm>, LSL <Rs>
其中:
<Rm> 为进行逻辑左移操作的寄存器。
LSL 逻辑左移操作。
<Rs> 包含逻辑左移位数的寄存器。
使用说明
当R15(PC)用作Rn、Rm、Rd及Rs时,会产生不可预知效果(跑飞)。
示例
MOV R0, R0, LSL R2 ;R0=R0<<R2
-
<Rm>, LSR #<shift_imm>
其中:
<Rm> 为进行逻辑右移操作的寄存器。
LSR 逻辑右移操作。
#<shift_imm> 逻辑右移位数,范围1~32,shift_imm=0时位移数为32。
使用说明
当R15(PC)用作第一个源操作数Rn或者第二操作数Rm时,操作数即为当前指令地址加常数8。
示例
MOV R0, R0, LSR #n ;R0=R0>>n
-
<Rm>, LSR <Rs>
其中:
<Rm> 为进行逻辑右移操作的寄存器。
LSR 逻辑右移操作。
<Rs> 包含逻辑右移位数的寄存器。
使用说明
当R15(PC)用作Rn、Rm、Rd及Rs时,会产生不可预知效果(跑飞)。
示例
MOV R0, R0, LSR R2 ;R0=R0>>R2
-
<Rm>, ASR #<shift_imm>
其中:
<Rm> 为进行算术右移操作的寄存器。
ASR 算术右移操作。
#<shift_imm> 算术右移位数,范围1~32,shift_imm=0时位移数为32。
使用说明
当R15(PC)用作第一个源操作数Rn或者第二操作数Rm时,操作数即为当前指令地址加常数8。
ASR与LSR的区别是:各位依次右移指定位数,在左侧用原符号位补齐,即保持符号位不变。
示例
MOV R0, R0, ASR #n
-
<Rm>, ASR <Rs>
其中:
<Rm> 为进行算术右移操作的寄存器。
ASR 算术右移操作。
<Rs> 包含算术右移位数的寄存器。
使用说明
当R15(PC)用作Rn、Rm、Rd及Rs时,会产生不可预知效果(跑飞)。
示例
MOV R0, R0, ASR R2
-
<Rm>, ROR #<shift_imm>
其中:
<Rm> 为进行循环右移操作的寄存器。
ROR 循环右移操作。
#<shift_imm> 循环右移位数,范围0~31,shift_imm=0时将执行RRX操作。
使用说明
当R15(PC)用作第一个源操作数Rn或者第二操作数Rm时,操作数即为当前指令地址加常数8。
示例
MOV R0, R0, ROR #n
-
<Rm>, ROR <Rs>
其中:
<Rm> 为进行循环右移操作的寄存器。
ROR 循环右移操作。
<Rs> 包含循环右移位数的寄存器。
使用说明
当R15(PC)用作Rn、Rm、Rd及Rs时,会产生不可预知效果(跑飞)。
示例
MOV R0, R0, ROR R1
-
<Rm>, RRX
其中:
<Rm> 为进行移位操作的寄存器。
RRX 扩展的循环右移操作。
使用说明
当R15(PC)用作第一个源操作数Rn或者第二操作数Rm时,操作数即为当前指令地址加常数8。
扩展的循环右移与循环右移的区别是:首位补的是进位。(循环右移补的是0)
示例
MOV R0, R0, RRX
二、字及无符号字节的Load/Store指令的寻址方式
1、 立即数方式
- [<Rn>, #+/-<offset_12>]
- [<Rn>, #+/-<offset_12>]!
- [<Rn>], #+/-<offset_12>
2、 寄存器方式
- [<Rn>, +/-<Rm>]
- [<Rn>, +/-<Rm>]!
- [<Rn>], +/-<Rm>
3、 寄存器及一个移位常数方式
- [<Rn>, +/-<Rm>, <shift> #<shift_imm>]
- [<Rn>, +/-<Rm>, <shift> #<shift_imm>]!
- [<Rn>], +/-<Rm>, <shift> #<shift_imm>
每组第2个为事先访问,第3个为事后访问。
三、杂类Load/Store指令的寻址方式
操作数为半字(无符号数或者带符号数)数据的Load/Store指令;操作数为带符号的字节数据的Load指令;双字的Load/Store指令。
这类指令的语法格式为:
LDR|STR{<cond>}H|SH|SB|B <Rd>, <addressing_mode>
其中,<addressing_mode>是指令中的内存单元的寻址方式,具体有以下6种形式:
- [<Rn>, #+/-<offset_8>]
- [<Rn>, #+/-<offset_8>]!
- [<Rn>], #+/-<offset_8>
- [<Rn>, +/-<Rm>]
- [<Rn>, +/-<Rm>]!
- [<Rn>], +/-<Rm>
- LDRH 半字无符号Load,高16位填充0
- LDRSH 半字带符号Load,高16位填充该半字的符号位
- LDRSB 字节带符号Load,高24位填充该字节的符号位
- LDRB 字节无符号Load,高24位填充0
STR与此上类似。
四、批量Load/Store指令的寻址方式
一条批量Load/Store指令可以实现在一组寄存器和一块连续的内存单元之间传输数据。
这类指令的语法格式为:
LDM|STM{<cond>}<addressing_mode> <Rn>{!}, <registers>{^}
其中,<addressing_mode>表示地址的变化方式,具体有以下4种形式:
- IA(Increment After) 事后递增方式
- IB(Increment Before) 事先递增方式
- DA(Decrement After) 事后递减方式
- DB(Decrement Before) 事先递减方式
示例
LDMIA R0, {R5-R8} ; 将内存单元(R0)到(R0+12)4个字的数据读取到R5~R8的4个寄存器中
LDMIB R0, {R5-R8} ; 将内存单元(R0+4)到(R0+16)4个字的数据读取到R5~R8的4个寄存器中
LDMDA R0, {R5-R8} ; 将内存单元(R0-12)到(R0)4个字的数据读取到R5~R8的4个寄存器中
LDMDB R0, {R5-R8} ; 将内存单元(R0-16)到(R0-4)4个字的数据读取到R5~R8的4个寄存器中
对应于栈操作的寻址方式
- FD(Full Descending)
- ED(Empty Descending)
- FA(Full Ascending)
- EB(Empty Ascending)
根据栈指针的指向:
- 栈指针指向栈顶元素(即最后一个入栈的数据元素)时称为FULL栈
- 栈指针指向与栈顶元素相邻的一个可用数据单元时称为EMPTY栈
根据数据栈的增长方向:
- 当数据栈向内存地址减少的方向增长时,称为DESCENDING栈
- 当数据栈向内存地址增加的方向增长时,称为ASCENDING栈
五、协处理器Load/Store指令的寻址方式
一条协处理器Load/Store指令可以实现在ARM处理器和协处理器之间传输数据。
这类指令的语法格式为:
<opcode>{<cond>}{L} <coproc>, <CRd>, <addressing_mode>
其中,<addressing_mode>表示地址的变化方式,具体有以下4种形式:
- [<Rn>, #+/-<offset_8>*4]
- [<Rn>, #+/-<offset_8>*4]!
- [<Rn>], #+/-<offset_8>*4
- [<Rn>], <option>
参考自《ARM体系架构与编程》杜春雷
- 我的个人主页:http://www.techping.cn/
- 我的个人站点博客:http://www.techping.cn/blog/wordpress/
- 我的CSDN博客:http://blog.csdn.net/techping
- 我的简书:http://www.jianshu.com/users/b2a36e431d5e/timeline
- 我的GitHub:https://github.com/techping
欢迎相互follow~