Download - ARM 指令集与编程
ARM指令集与编程 1
ARM 指令集与编程
1. 总体介绍:指令分类,特点,格式,条件码2. 数据处理指令:数据传输指令,算术指令,逻辑指令,比
较指令,乘法指令 ( 略 ) ,前导零计数 ( 略 )3. 程序状态访问指令4. 跳转指令5. 单数据访存指令6. 多数据访存指令7. 其它指令:信号量操作指令,异常中断产生指令,协处理
器指令 ( 略 )8. 伪指令 ( 略 )9. 基于 ARM 的编程
1 总体介绍
指令分类、特点、格式、条件码
ARM指令集与编程 3
1.1 指令分类 ARM 指令集总体分为 6 类指令
– 数据处理指令:数据传输指令,算术指令,逻辑指令,比较指令,乘法指令,前导零计数。
– 程序状态访问指令: mrs 和 msr 。– 跳转指令: b 和 bl 。– 访存指令:单数据访存指令,多数据访存指令,信号
量操作指令。– 异常中断产生指令: swi 和 bkpt 。– 协处理器指令。
ARM指令集与编程 4
1.2 指令特点• 所有指令都是 32bit 。• 大多数指令都在单周期内完成。• 所有指令都可以条件执行。• load/store 体系结构。• 指令集可以通过协处理器扩展
ARM指令集与编程 5
1.3 ARM 指令的格式
<Opcode>{<cond>}{s} <Rd>, <Rn>, <Operand2>
• Cond :指令的条件码。• Opcode :指令操作码。• S :操作是否影响 cpsr 。• Rn :包含第一个操作数的寄存器编码。• Rd :目标寄存器编码。• Operand2 :第 2 操作数。
Cond 001 Opcode S Rn Rd Operand2
0111215161920212425272831 78
ARM指令集与编程 6
1.3 ARM 指令的格式 ( 续 )
ARM指令集与编程 7
1.4 ARM 指令的条件执行所有的 ARM 指令可包含一个可选的条件码,只有在 cpsr 中的条件标志位满足指定的条件时,指令才会被执行。不符合条件的代码依然占用一个时钟周期(相当于一个NOP 指令)。
// r0, r1, r2, r3, r4 : a, b, c, d, ecmp r0, r1cmpeq r2, r3addeq r4, r4, #1
if ( (a == b) && (c == d) ) {
e++;}
ARM指令集与编程 8
1.4.1 ARM 指令的条件域
• EQ/NE: 等于 / 不等于 (equal / not equal)• HS/LO: 无符号数高于或等于 / 无符号数小于 (higher or same/lower)• HI/LS: 无符号数高于 / 无符号数低于或等于 (higher/lower or same)• GE/LT: 有符号数大于或等于 / 有符号数小于 (greater or equal / less
than)• GT/LE: 有符号数大于 / 有符号数小于或等于 (greater than / less or
equal)• MI/PL: 负 / 非负• VS/VC: 溢出 / 不溢出 (overflow set / overflow clear)• CS/CC: 进位 / 无进位 (carry set / carry clear)
ARM指令集与编程 9
1.4.2 ARM 指令的条件码
0000 EQ Z 置位 相等 / 等于 0
0001 NE Z 清 0 不等0010 CS/HS C 置位 进位 / 无符号高于或等
于0011 CC/LO C 清 0 无进位 / 无符号低于0100 MI N 置位 负数0101 PL N 清 0 非负数0110 VS V 置位 溢出0111 VC V 清 0 无溢出
ARM指令集与编程 10
1.4.2 ARM 指令的条件码 ( 续 )
1000 HI C 置位且 Z 清 0 无符号高于
1001 LS C 清 0 或 Z 置位 无符号低于或等于1010 GE N 等于 V 有符号大于或等于1011 LT N 不等于 V 有符号小于1100 GT Z 清 0 且 N 等于 V 有符号大于1101 LE Z 置位或 N 不等于 V 有符号小于或等于1110 AL 任何状态 总是( always )1111 NV 无 从不( never )
注: AL 是默认的, NV 不建议使用。
ARM指令集与编程 11
目录1. 总体介绍:指令分类,特点,格式,条件码2. 数据处理指令:数据传输指令,算术指令,逻辑指令,比
较指令,乘法指令 ( 略 ) ,前导零计数 ( 略 )3. 程序状态访问指令4. 跳转指令5. 单数据访存指令6. 多数据访存指令7. 其它指令:信号量操作指令,异常中断产生指令,协处理
器指令 ( 略 )8. 伪指令 ( 略 )9. 基于 ARM 的编程
ARM指令集与编程 12
2 数据处理指令
• 数据传输指令: mov 和 mvn
• 算数指令: add 、 adc 、 sub 、 sbc , rsb 和 rsc
• 逻辑指令: and 、 orr 、 eor 和 bic
• 比较指令: cmp 、 cmn 、 tst 和 teq
ARM指令集与编程 13
2.1 数据处理指令的特点• 所有的操作数要么来自寄存器,要么来自立即数,
不会来自内存。• 如果有结果,则结果一定是为 32 位宽,并且放在
一个寄存器中,不会写入内存。(有一个例外:长乘法指令产生 64 位结果)
• 每一个操作数寄存器和结果寄存器都在指令中独立指出,即: ARM 指令采用 3 地址模式: <Operation> Rd, Rn, Rm
ARM指令集与编程 14
2.2 四种寻址方式和 s 后缀
add r0, r1, r2
add r0, r1, #0xff
add r0, r1, r1, LSL r2
Cond S Rn Rd 第 2 操作数078111215161920
opcode
2124
00X
25272831
#immed_8#rot
07811
0 0 0 0 0 0 0 0 Rm
03411
方式 1 : #immed
方式 2 : Rm
0 0 0 0 Rm
03411
Rs
78
方式 3 : Rm LSL Rs
shift Rm
03411
#immed_5
67方式 4 : Rm LSL #immed_5( #immed_5 取值范围 0-31 )add r0, r1, r1, LSL #31
ARM指令集与编程 15
2.2.1 四种寻址方式的硬件支持
•寄存器,可能需要移位。•如需移位,移位值可为:
•5bit 的无符号整数( 0-31 )•在指令的最低字节指定的另一寄存器
•立即数• 8 比特数•8 比特数循环右移偶数位•右移位数由汇编器自动计算
ARM指令集与编程 16
2.2.2 立即数寻址• ARM 指令中的立即数,由一个 8bit 的常数
循环右移偶数位得到:立即数 = ( 0->255) 循环右移 2N 位
• 例子:– 合法立即数: 0x3fc , 0x0 , 0xf0000000 , 0
xf0000001– 非法立即数: 0x1fe , 0xffff , 0x1010 , 0xf0
000010
ARM指令集与编程 17
2.2.2 立即数寻址 ( 续 )
• 同一个立即数可能有多个表示方法。如:– 0x3f0 = 0x3f 循环右移 28 位– 0x3f0 = 0xfc 循环右移 30 位
• 对立即数的编码规则:– 如果立即数在 0 – 0xff 之间,移位数为 0 。– 否则,就取决于编译器了。指令“ mov r0, #0x3f
0” 在 ADS1.2 中被编译为 0xe3a00ffc ,在 arm-elf-gcc-2.95.3 中被编译为 0xe3a00e3f 。
ARM指令集与编程 18
2.2.2 立即数寻址 ( 续 )
• 对于有互补操作的指令,编译器可以做智能的转换,比如:– mvn r1, 0xffffff00 -> mov r1, 0xff– add r1, r1, #0xffffff00 -> sub r1, r1, #0x100– adc r1, r1, #0xffffff00 -> sbc r1, r1, #0xff– and r1, r1,#0xffffff00-> bic r1, r1, #0xff
这样,一些原本非法的立即数也可以正常编译通过。如果一个立即数,经过上述转换后是合法的,那么它也可以用在数据操作指令中。
ARM指令集与编程 19
2.2.3 寄存器移位寻址
ASR 算术右移
LSL 逻辑左移
LSR 逻辑右移
ROR 循环右移
RRX 扩展的循环右移
ARM指令集与编程 20
2.2.3 寄存器移位寻址(续)
• 如果移位的位数由立即数( 5bit ,取值范围 0 - 31 )给出,就叫作 immediate specified shift ;如果由 Rs 的低 5 位决定,就叫做 register specified shift 。
• Register specified shift 的两点问题:– 不能使用 pc :如果将 pc 寄存器用在 Rn , Rd , R
m 和 Rs 的位置上时,会产生不可预知的结果。– 额外代价( overhead ):需要更多的周期才能完成
指令,因为 ARM没有能力一次读取 3 个寄存器。• Immediate specified shift 没有上述问题。
ARM指令集与编程 21
2.2.3 寄存器移位寻址(续)• 在 register specified shift 寻址方式下使用 p
c 寄存器,编译器提示如下警告:– 在 ADS1.2 种编译产生如下警告之一:
• Warning A1477W : This register combination results in UNPREDICTABLE behavior
• Warning : A1320E: Undefined effect (using PC as Rn or Rm in register specified shift)
• Warning : A1319E: Undefined effect (using PC as Rs)
–但是在 arm-elf-gcc-2.95.3 中没有报告错误。
ARM指令集与编程 22
2.2.4 后缀 s
• 数据处理指令可以选择 s 后缀,以影响状态标志。但是比较指令( cmp 、 cmn 、 tst 和 teq )不需要后缀 s ,它们总会直接影响 cpsr 中的状态标志。
• 在数据操作指令中,除了比较指令以外,其它的指令如果带有 s 后缀,同时又以 pc 为目标寄存器进行操作,则操作的同时从 spsr恢复 cpsr 。比如:– movs pc, #0xff /* cpsr = spsr; pc = 0xff */– adds pc, r1, #0xffffff00 /* cpsr = spsr; pc = r1 + 0xffffff00 */– ands pc, r1, r2 /* cpsr = spsr; pc = r1 & r2; */
• 如果在 user 或者 system 模式下使用带有 s 后缀的数据操作指令,同时以 pc 为目标寄存器,那么会产生不可预料的结果。因为 user 和 system 模式下没有 spsr 。
ARM指令集与编程 23
2.3 数据传输指令
• 语法– <Operation>{<cond>}{s} <Rd>, #<immed>– <Operation>{<cond>}{s} <Rd>, <Rm>– <Operation>{<cond>}{s} <Rd>, <Rm>, LSL #<immed_5>– <Operation>{<cond>}{s} <Rd>, <Rm>, LSL <Rs>
• 伪代码if ConditionPassed(cond) then
Rd = 第 2 操作数if s == 1 and Rd == pc then
cpsr == spsrelse if s == 1 then
set NZCV flags in cpsr
ARM指令集与编程 24
2.3 数据传输指令(续)
• 举例– mov r0, r1 /* r0 = r1 ,不修改 cpsr */– mov r0, #0x0 /* r0 = 0 ,不修改 cpsr */– movs r0, #0x0 /* r0 = 0 ,同时设置 cpsr 的 Z 位 *
/– movs r0, #-10 /* r0 = 0xfffffff6 ,同时设置 cpsr 的
N 位 */– mvn r0, r2 /* r0 = NOT r2 ,不修改 cpsr */– mvn r0, 0xffffffff /* r0 = 0x0 ,不修改 cpsr */– mvns r0, 0xffffffff /* r0 = 0x0 ,同时设置 cpsr 的 Z 位 */– mov r0, r1, LSL #1 /* r0 = r1 << 1 */– mov r0, r1, LSR r2 /* r0 = r1 >> r2 */
ARM指令集与编程 25
2.3 数据传输指令(续)• 说明
– mvn意为“取反传输”,它把源寄存器的每一位取反,将得到的结果写入结果寄存器。
– movs 和 mvns 指令对 pc 寄存器赋值时有特殊含义,表示要求在赋值的同时从 spsr 中恢复 cpsr 。
– 对于 mov 和 mvn 指令,编译器会进行智能的转化。比如指令“ mov r1, 0xffffff00” 中的立即数是非法的。在编译时,编译器将其转化为“ mvn r1, 0xff” ,这样就不违背立即数的要求。所以对于 mov 和 mvn 指令,可以认为:合法的立即数反码也是合法的立即数。
ARM指令集与编程 26
2.4 算术指令
• 语法– <Operation>{<cond>}{s} <Rd>, <Rn>, #<immed>– <Operation>{<cond>}{s} <Rd>, <Rn>, <Rm>– <Operation>{<cond>}{s} <Rd>, <Rn>, <Rm>, LSL #<immed_5>– <Operation>{<cond>}{s} <Rd>, <Rn>, <Rm>, LSL <Rs>
• 伪代码(以加法 add 为例)if ConditionPassed(cond) then
Rd = Rn + 第 2 操作数if s == 1 and Rd == pc then
cpsr = spsrelse if s == 1 then
set NZCV flags in cpsr
ARM指令集与编程 27
2.4 算术指令(续)
• 举例– add r0, r1, r2 /* r0 = r1 + r2 */– adc r0, r1, r2 /* r0 = r1 + r2 + carry */– sub r0, r1, r2 /* r0 = r1 – r2 */– sbc r0, r1, r2 /* r0 = r1 – r2 + carry -1 */– rsb r0, r1, r2 /* r0 = r2 – r1 */– rsc r0, r1, r2 /* r0 = r2 – r1 + carry – 1 */– add r0, r1, r1, LSL #31 /* r0 = r1 + r1 << 31 */– add r0, r1, r1, LSL r2 /* r0 = r1 + r1 << r2 */
• 说明– adds 和 adcs 在进位时将 cpsr 的 C 标志置 1 ;否则置 0 。– subs 和 sbcs 在产生借位时将 cpsr 的 C 标志置 0 ;否则置 1 。
ARM指令集与编程 28
2.5 逻辑指令• 语法
– <Operation>{<cond>}{s} <Rd>, <Rn>, #<immed>– <Operation>{<cond>}{s} <Rd>, <Rn>, <Rm>– <Operation>{<cond>}{s} <Rd>, <Rn>, <Rm> LSL #<immed_5>– <Operation>{<cond>}{s} <Rd>, <Rn>, <Rm> LSL <Rs>
• 伪代码(以 and 为例)If ConditionPassed(cond) then
Rd = Rn AND 第 2 操作数if s == 1 and Rd == pc then
cpsr = spsrelse if s == 1 then
set NZCV flags in cpsr
ARM指令集与编程 29
2.5 逻辑指令(续)
• 举例– and r0, r1, r2 /* r0 = r1 AND r2 */– and r1, r1,#0xffffff00 /* r1 = r1 AND 0xffffff00 */– orr r0, r1, r2 /* r0 = r1 OR r2 */– eor r0, r1, r2 /* r0 = r1 XOR r2 */– bic r0, r1, r2 /* r0 = r1 AND NOT r2 */– bic r1, r1, #0x0f /* 清空 r1 的低 4 位 */– and r0, r1, r1, LSL #31 /* r0 = r1 AND (r1 << 31) */– and r0, r1, r1, LSR r2 /* r0 = r1 AND (r1 >> r2) */
ARM指令集与编程 30
2.6 比较指令
• 语法– <Operation>{<cond>} <Rn>, #<immed>– <Operation>{<cond>} <Rn>, <Rm>– <Operation>{<cond>} <Rn>, <Rm>, LSL #<immed_5
>– <Operation>{<cond>} <Rn>, <Rm>, LSL <Rs>
• 伪代码(以 cmp 为例)If ConditionPassed(cond) then
alu_out = Rn – 第 2 操作数 set NZCV flags in cpsr
ARM指令集与编程 31
2.6 比较指令(续)
• 举例– cmp r1, r2 /*根据 r1 – r2 的结果设置 cpsr ,结果不写回 */– cmn r1, r2 /*根据 r1 + r2 的结果设置 cpsr ,结果不写回 */– tst r1, r2 /*根据 r1 AND r2 的结果设置 cpsr ,结果不写回 */– teq r1, r2 /*根据 r1 XOR r2 的结果设置 cpsr ,结果不写回 */– cmp r2, #5 /*根据 r2 – 5 的结果设置 cpsr ,结果不写回 */– cmp r1, r2, LSL #5 /* 根据 r1 – (r2 << 5)设置 cpsr */– cmp r1, r2, LSL r3 /* 根据 r1 – (r2 << r3)设置 cpsr */
• 说明– 如果不考虑结果的写回, cmp 、 cmn 、 tst 和 teq 分别等价于 sub
s 、 adds 、 ands 和 eors 。
ARM指令集与编程 32
目录
1. 总体介绍:指令分类,特点,格式,条件码2. 数据处理指令:数据传输指令,算术指令,逻辑指令,比
较指令,乘法指令 ( 略 ) ,前导零计数 ( 略 )3. 程序状态访问指令4. 跳转指令5. 单数据访存指令6. 多数据访存指令7. 其它指令:信号量操作指令,异常中断产生指令,协处理
器指令 ( 略 )8. 伪指令 ( 略 )9. 基于 ARM 的编程
ARM指令集与编程 33
3 程序状态访问指令• 当需要修改 cpsr/spsr 的内容时,首先要读
取它的值到一个通用寄存器,然后修改某些位,最后将数据写回到状态寄存器。
• cpsr/spsr 不是通用寄存器,不能使用 mov指令来读写。在 ARM 处理器中,只有 mrs指令可以读取 cpsr/spsr ;只有 msr 可以写cpsr/spsr 。
ARM指令集与编程 34
3.1 读指令 mrs
• 语法– mrs{<cond>} <Rd>, cpsr|spsr
• 伪代码if ConditionPassed(cond) then
if R == 1 thenRd = spsr
else Rd = cpsr
ARM指令集与编程 35
3.1读指令 mrs (续)• 举例
– mrs r0, cpsr /* 读取 cpsr 到 r0 */– mrs r3, spsr /* 读取 spsr 到 r3 */
• 说明– user 和 system 模式没有 spsr ,因此这些模式下不能读取 spsr 。
ARM指令集与编程 36
3.2 写指令 msr 的二进制格式
0
cond R 1 0 f s x c 1111 操作数01112151619202122
1 0
2324
#
25
0 0
26272831
1 8 位立即数#rot
07811
0 0 0 0 0 0 0 0 Rm
03411
cpsr/spsr
域屏蔽
立即数对准
操作数寄存器
ARM指令集与编程 37
3.2.1 写指令 msr 的语法
• msr{<cond>} <psr>_<fields>, #<immed>• msr{<cond>} <psr>_<fields>, <Rm>
– <immed> 表示合法的立即数: 8bit 循环右移偶数位– <psr> 代表 cpsr 或 spsr– <fields> 指定传送的区域,可进一步细分 ( 只能小
写 )• c 控制域字节( psr[7:0] )• x 扩展域字节( psr[15:8] )• s 状态域字节( psr[23:16] )• f 标志域字节( psr[31:24] )
ARM指令集与编程 38
3.2.2 写指令 msr 的伪代码
• 伪代码if ConditionPassed(cond) then
if opcode[25] == 1operand = 8 立即数 Rotate_Right (#rot * 2)
else /* opcode[25] == 0 */operand = Rm
if R == 0 thenif field_mask[0] == 1 and InAPrivilegeMode() then cpsr[7:0] = operand[7:0]if field_mask[1] == 1 and InAPrivilegeMode() then cpsr[15:8] == operand[15:8]if field_mask[2] == 1 and InAPrivilegeMode() then cpsr[23:16] == operand[23:16]if field_mask[3] == 1 then cpsr[31:24] = operand[31:24]
else /* R == 1 */if field_mask[0] == 1 and CurrentModeHasSPSR() then spsr[7:0] = operand[7:0]if field_mask[1] == 1 and CurrentModeHasSPSR() then spsr[15:8] == operand[15:8]if field_mask[2] == 1 and CurrentModeHasSPSR() then spsr[23:16] == operand[23:16]if field_mask[3] == 1 and CurrentModeHasSPSR() then spsr[31:24] = operand[31:24]
ARM指令集与编程 39
3.2.3 msr举例和说明
• 举例– msr cpsr_c, #0xd3 /* 切换到 SVC 模式 */– msr cpsr_cxsf, r3 /* cpsr = r3 */
• 说明① user 和 system 模式没有 spsr ,因此这些模式下不能对 spsr
操作。② 由于权限问题,在 user 模式下对 cpsr[23:0]修改无效。③ 如果使用立即数,要使用合法的立即数。④ 程序不能同过“ msr修改 cpsr 的 T 位”来完成 ARM/Thumb
态的切换。必须使用 bx 指令,因为 bx属于分支指令,它会打断流水线,实现处理器状态切换。
⑤ 如果要修改读出的值,仅修改必要的位,其它位保持不变,这样保持了最大兼容性。
ARM指令集与编程 40
目录
1. 总体介绍:指令分类,特点,格式,条件码2. 数据处理指令:数据传输指令,算术指令,逻辑指令,比
较指令,乘法指令 ( 略 ) ,前导零计数 ( 略 )3. 程序状态访问指令4. 跳转指令5. 单数据访存指令6. 多数据访存指令7. 其它指令:信号量操作指令,异常中断产生指令,协处理
器指令 ( 略 )8. 伪指令 ( 略 )9. 基于 ARM 的编程
ARM指令集与编程 41
4 跳转指令• 语法
– b{<cond>} label– bl{<cond>} label
• 说明– 寻址范围 +32MB
ARM指令集与编程 42
4 跳转指令(续)• 当转移指令执行时,处理器将指令中的 offset(24b
it) 左移 2bit ,变成 26bit ,表示 + 32M 的范围。• pc 从新的地址执行,流水线重新填充。• 如果是“ bl” 指令,将返回地址写入 lr 寄存器。子
程序返回时只需要用 lr恢复 pc 就可以:– mov pc, lr
• “b” 指令不影响 lr 寄存器
ARM指令集与编程 43
目录
1. 总体介绍:指令分类,特点,格式,条件码2. 数据处理指令:数据传输指令,算术指令,逻辑指令,比
较指令,乘法指令 ( 略 ) ,前导零计数 ( 略 )3. 程序状态访问指令4. 跳转指令5. 单数据访存指令6. 多数据访存指令7. 其它指令:信号量操作指令,异常中断产生指令,协处理
器指令 ( 略 )8. 伪指令 ( 略 )9. 基于 ARM 的编程
ARM指令集与编程 44
5 单数据访存指令• 第一类:
–读写字 : ldr / str–读写无符号字节 : ldrb / strb
• 第二类:–读写无符号半字: ldrh / strh–读有符号半字: ldrsh–读有符号字节: ldrsb
ARM指令集与编程 45
5.1 第一类指令的指令格式
0 0 0 0 0 0 0 0 0 0 Rm
03411方式 2 : Rm例子: ldr r0, [r1, +r2]
011
#immed_12方式 1 : #immed_12 (取值范围 0 – 0xfff )例子: ldr r0, [r1, #+0xfff]
shift Rm
03411
#immed_5
67方式 3 : Rm LSL #immed_5 (取值范围 0-31 )例子: ldr r0, [r1, r2 LSL #31] 0
5
0/1 : str/ldr
0/1 : 无 / 有 (!)0/1 : 后变址 / 前变址
0/1 : 字 / 无符号字节
cond Rn Rd 第 2 操作数07811121516192022
01
272831 2324
U B W L
2126
PI
25
0/1 : 加 /减 ( 第 2 操作数 )
0/1 : 立即数 ( 方式 1)/ 寄存器 ( 方式 2,3)
ARM指令集与编程 46
5.2 第一类指令的语法
• 语法– ldr|str{<cond>}{b} <Rd>, [<Rn>, #+<immed_12]{!}– ldr|str{<cond>}{b} <Rd>, [<Rn>, +<Rm>]{!}– ldr|str{<cond>}{b} <Rd>, [<Rn>, +<Rm>, <shift> #<immed_5>]{!}– ldr|str{<cond>}{b} <Rd>, [<Rn>], #+<immed_12– ldr|str{<cond>}{b} <Rd>, [<Rn>], +<Rm>– ldr|str{<cond>}{b} <Rd>, [<Rn>], +<Rm>, <shift> #<immed_5>
• 举例– ldrb r0, [r1, #+0xfff] /* 把 r1+0xfff 地址的字节读入 r0 */– ldr r0, [r1, +r2]! /*把 r1+r2 地址的 32 比特数读入 r0 ,然后 r1= r1+r2 */– str r0, [r1, +r2, LSL #31] /* 把 r0 ( 32bit )写到地址 r1+(r2<<31) */– ldr r0, [r1], #+0xfff /* 把 r1 地址的数读入 r0 ,然后 r1=r1+0xfff */– ldr r0, [r1], +r2 /* 把 r1 地址的数读入 r0 ,然后 r1=r1+r2 */– ldr r0, [r1], +r2, LSL #31 /* 把 r1 地址的数读入 r0 ,然后 r1=r1+(r2<<31) */
ARM指令集与编程 47
5.2 第一类指令说明• 说明
– ldr / str 读 / 写一个 32bit 字到 / 从一个 32bit 的寄存器,要求读 / 写地址字对齐。
– ldrb :读一个 8bit 字节到一个 32bit 的寄存器,不要求地址对齐,寄存器的高 24 位清零。
– strb :将寄存器的低 8 位,写入内存的某个地址。不要求地址对齐。
ARM指令集与编程 48
5.3 第二类指令• 语法
– 同第一类指令• 说明
– ldrh :读取 16bit半字到一个 32bit 寄存器,要求地址半字对齐,目标寄存的高 16bit 清零。
– strh :将寄存器的低 16bit 存放到内存中,要求地址半字对齐。– ldrsh :将内存中的一个 16bit半字读到一个 32bit 寄存器中,要求
地址半字对齐。寄存器高 16bit根据符号位扩展。– ldrsb :将内存中的一个 8bit 字节读到一个 32bit 寄存器中,寄存
器高 24bit根据符号位扩展。不要求地址对齐。
ARM指令集与编程 49
5.4 前 / 后变址
0 0 0 0 0 0 0 0 0 0 Rm
03411方式 2 : Rm例子: ldr r0, [r1, +r2]
011
#immed_12方式 1 : #immed_12 (取值范围 0 – 0xfff )例子: ldr r0, [r1, #+0xfff]
shift Rm
03411
#immed_5
67方式 3 : Rm LSL #immed_5 (取值范围 0-31 )例子: ldr r0, [r1, r2 LSL #31] 0
5
0/1 : str/ldr
0/1 : 无 / 有 (!)0/1 : 后变址 / 前变址
0/1 : 字 / 无符号字节
cond Rn Rd 第 2 操作数07811121516192022
01
272831 2324
U B W L
2126
PI
25
0/1 : 加 /减 ( 第 2 操作数 )
0/1 : 立即数 ( 方式 1)/ 寄存器 ( 方式 2,3)
ARM指令集与编程 50
5.4.1 简单的基址寻址• 基址寄存器数据访问
– str r0, [r1] ldr r2, [r1]
ARM指令集与编程 51
5.4.2 前变址寻址• str r0,[r1,#12]{! } ;
• mem32[r1+12] = r0;{r1 = r1 + 12;}
ARM指令集与编程 52
5.4.3 后变址寻址数据访问• str r0, [r1], #12
• mem32[r1] = r0; r1 = r1 + 12;
ARM指令集与编程 53
5.5 三种寻址方式 ( 第一类 )
0 0 0 0 0 0 0 0 0 0 Rm
03411方式 2 : Rm例子: ldr r0, [r1, +r2]
011
#immed_12方式 1 : #immed_12 (取值范围 0 – 0xfff )例子: ldr r0, [r1, #+0xfff]
shift Rm
03411
#immed_5
67方式 3 : Rm LSL #immed_5 (取值范围 0-31 )例子: ldr r0, [r1, r2 LSL #31] 0
5
0/1 : str/ldr
0/1 : 无 / 有 (!)0/1 : 后变址 / 前变址
0/1 : 字 / 无符号字节
cond Rn Rd 第 2 操作数07811121516192022
01
272831 2324
U B W L
2126
PI
25
0/1 : 加 /减 ( 第 2 操作数 )
0/1 : 立即数 ( 方式 1)/ 寄存器 ( 方式 2,3)
ARM指令集与编程 54
5.5 寻址方式 1— 立即数寻址• ldr|str{<cond>}{b} <Rd>, [<Rn>, #+<immed_12]{!}
• ldr|str{<cond>}{b} <Rd>, [<Rn>], #+<immed_12
• 说明:– 无叹号的前变址,当 pc 作为 Rn 时,内存基地址为当前指令地址加 8 字节偏移 ;
后变址或有叹号的前变址,当 pc 作为 Rn 时,会产生不可预知的结果• 在 ADS1.2 中,提示错误 Error : A1324E Undefined effect (PC + writeback)
• 在 arm-elf-gcc-2.95.3 中,没有提示– 后变址或有叹号的前变址,如果 Rn 和 Rd 是同一个寄存器,编译出错
• 在 ADS1.2 中,提示错误 Error : A1325E: Undefined effect (destination same as written-back base)
• 在 arm-elf-gcc-2.95.3 中,提示 Warning: destination register same as write-back base
– 如果 immed_12超过 0xfff ,编译会出错• 在 ADS1.2 中,提示错误 Error : A1174E: Data transfer offset out of range
• 在 arm-elf-gcc-2.95.3 中,提示 Error: address offset too large
ARM指令集与编程 55
5.5 寻址方式 2— 寄存器寻址• ldr|str{<cond>}{B} <Rd>, [<Rn>, +<Rm>]{!}• ldr|str{<cond>}{B} <Rd>, [<Rn>], +<Rm>• 说明:
– 无论如何, pc 都不能作 Rm 。– 无叹号的前变址,当 pc 作为 Rn 时,内存基地址为当前指令地址加 8 字
节偏移;后变址或有叹号的前变址,当 pc 作为 Rn 时,会产生不可预知的结果。
– 后变址或有叹号的前变址,如果 Rn 和 Rd 是同一个寄存器,会出现不可预知的结果。
ARM指令集与编程 56
5.5 寻址方式 3- 寄存器移位寻址
• ldr|str{<cond>}{B} <Rd>, [<Rn>, +<Rm>, <shift> #<immed_5>]{!}• ldr|str{<cond>}{B} <Rd>, [<Rn>], +<Rm>, <shift> #<immed_5>• 说明:
– 无论如何, pc 都不能作 Rm 。– 无叹号的前变址,当 pc 作为 Rn 时,内存基地址为当前指令地址加 8 字
节偏移;后变址或有叹号的前变址,当 pc 作为 Rn 时,会产生不可预知的结果。
– 后变址或有叹号的前变址,如果 Rn 和 Rd 是同一个寄存器,会出现不可预知的结果。
– 如果 immed_5超过 31 ,编译器报错。
ARM指令集与编程 57
目录
1. 总体介绍:指令分类,特点,格式,条件码2. 数据处理指令:数据传输指令,算术指令,逻辑指令,比
较指令,乘法指令 ( 略 ) ,前导零计数 ( 略 )3. 程序状态访问指令4. 跳转指令5. 单数据访存指令6. 多数据访存指令7. 其它指令:信号量操作指令,异常中断产生指令,协处理
器指令 ( 略 )8. 伪指令 ( 略 )9. 基于 ARM 的编程
ARM指令集与编程 58
6 多数据访存指令• 批量访存指令可以实现一组( 1-16 )寄
存器和一块( 4-64 字节)连续内存单元之间的数据传输。
ARM指令集与编程 59
6.1 指令格式
ARM指令集与编程 60
6.2 ldm/stm 指令的语法
• 语法– 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 个字读取到 r5~r8 的 4 个寄存器中 */
ARM指令集与编程 61
6.2 ldm/stm 指令的语法 ( 续 )
• 说明– 递增方式和递减方式处理寄存器列表和内存地址对应关系时采用不同的顺序,见前面的例子。可以总结为:编号低的寄存器对应低内存地址。
– pc 不能作为 stm 指令的 <Rn> ,否则结果不可预知。– 感叹号“!”表示执行后将更新 <Rn> 。– “^” 不能在 usr 和 system 模式下使用,否则结果不可预
知。在其它模式下使用时,含义和寄存器列表是否包含 pc 有关,见后面的详述。
ARM指令集与编程 62
6.3 ldm 指令的三种用法
编号! ^ pc ldm 指令不同的功能 用
法1 是 是 是 同 (5) ,但是更新 <Rn> 3
2 是 是 否 编译警告 (ADS1.2) : Warning : A1329W: Unsafe instruction (forced user mode xfer with write-back to base)
2
3 是 否 是 同 (7) ,但是更新 <Rn> 1
4 是 否 否 同 (8) ,但是更新 <Rn> ,且 <Rn> 不能出现在 <registers> 中
1
5 否 是 是 数据读取的同时,拷贝 spsr 到 cpsr 3
6 否 是 否 <registers> 采用 user 模态下的寄存器 2
7 否 否 是 同 (8) ,可用于程序跳转。 1
8 否 否 否 正常读取 1
ARM指令集与编程 63
6.4 stm 指令的两种用法
编号! ^ ldm 指令不同的功能 用
法1 是 是 同 (3) ,但是更新 <Rn> 2
2 是 否 同 (4) ,但是更新 <Rn> 。当 <Rn> 是 <registers> 中编号最小的寄存器时,指令将 <Rn> 的初值保存;否则结果不
可预测。
1
3 否 是 <registers> 采用 user 模态下的寄存器 2
4 否 否 正常写入 1
ARM指令集与编程 64
6.5 多数据访存的程序例子
/* r12 指向源数据起点 */
/* r14 指向源数据终点 */
/* r13 指向目标地址 */
loop:
ldmia r12!, {r0-r11} /* 读 48 字节 */
stmia r13!, {r0-r11} /* 写 48 字节 */
cmp r12, r14 /* 是否到末尾 */
bne loop /* 重复循环 */
ARM指令集与编程 65
6.6 模拟栈• ARM 指令集中没有用于栈的操作指令,但
是可以用多数据访存指令来模拟。
ARM指令集与编程 66
6.6.1 栈的分类
• 栈的分类(按指针) :– 栈指针指向最后一个被占用的地址 (Full Stack)
• 在 push 前需要先将栈指针减小– 栈指针指向下一个被占用的地址 (Empty Stack)
• 在 push 之后需要将栈指针减小
• 栈的分类(按方向):– 上升的栈( Ascending Stack )
• 向高地址扩展– 下降的栈( Descending Stack )
• 向低地址扩展
ARM指令集与编程 67
6.6.2 模拟压栈操作
块数据访问指令 栈访问指令 说明stmda stmed 下降型空栈stmia stmea 上升型空栈stmdb stmfd 下降型满栈stmib stmfa 上升型满栈
ARM指令集与编程 68
6.6.3 模拟退栈操作
块数据访问指令 栈访问指令 说明ldmda ldmfa 上升型满栈ldmia ldmfd 下降型满栈ldmdb ldmea 上升型空栈ldmib ldmed 下降型空栈
ARM指令集与编程 69
6.6.4 模拟栈操作• 模拟 stack 的访问
– stmfd / ldmfd : Full Descending Stack– stmfa / ldmfa : Full Ascending Stack.– stmed / ldmed : Empty Descending Stack– stmea / ldmea : Empty Ascending Stack
• 常见的情况– stmfd sp!, {r0-r12, lr} // 保存所有有寄存器(包括返回地址)
……
– ldmfd sp!, {r0-r12, pc} // 恢复所有寄存器(包括 pc )
ARM指令集与编程 70
6.6.5 栈操作图示
ARM指令集与编程 71
目录
1. 总体介绍:指令分类,特点,格式,条件码2. 数据处理指令:数据传输指令,算术指令,逻辑指令,比
较指令,乘法指令 ( 略 ) ,前导零计数 ( 略 )3. 程序状态访问指令4. 跳转指令5. 单数据访存指令6. 多数据访存指令7. 其它指令:信号量操作指令,异常中断产生指令,协处理
器指令 ( 略 )8. 伪指令 ( 略 )9. 基于 ARM 的编程
ARM指令集与编程 72
7 其它指令• 信号量操作指令:用于进程间的同步互斥,提供对信号量的原子操作。
• 异常中断产生指令:用于系统调用和调试。
ARM指令集与编程 73
7.1 信号量操作指令• 语法
– swp|swpb{<cond>} <Rd>, <Rm>, [<Rn>]
• 例子– swp r1, r2, [r3] /* 将内存单元 (r3) 中的字读取到 r
1 ,同时将 r2 中的数据写入内存单元 (r3) 中 */– swp r1, r1, [r2] /* 将 r1 寄存器内容和内存单元 (r
2) 的内容互换 */
• 说明– swpb读取 8bit 数据到寄存器后会对高 24 位清零,写
到内存的 8bit 数来自寄存器低 8 位
ARM指令集与编程 74
7.1 信号量操作指令 ( 续 )
ARM指令集与编程 75
7.2 异常产生指令• 指令格式
• 语法– swi{<cond>} <immed_24>
• 说明– 主要用于用户程序调用操作系统的 API 。参数传递通
常有两种方法:• 指令中的 24bit 立即数指定 API 号,参数通过寄存器传递。• 忽略指令中的 24bit 立即数, r0 指定 API 号,其它参数通过其
它寄存器传递。
cond Immed_24
023
1 1 1 1
272831 24
ARM指令集与编程 76
7.3 中断产生指令• 语法
– bkpt <immed_16>
• 说明– 用于产生软件断点中断。–主要用于调试。– ARMv5及以上版本支持。
ARM指令集与编程 77
目录
1. 总体介绍:指令分类,特点,格式,条件码2. 数据处理指令:数据传输指令,算术指令,逻辑指令,比
较指令,乘法指令 ( 略 ) ,前导零计数 ( 略 )3. 程序状态访问指令4. 跳转指令5. 单数据访存指令6. 多数据访存指令7. 其它指令:信号量操作指令,异常中断产生指令,协处理
器指令 ( 略 )8. 伪指令 ( 略 )9. 基于 ARM 的编程
ARM指令集与编程 78
8 伪指令• 略
ARM指令集与编程 79
目录
1. 总体介绍:指令分类,特点,格式,条件码2. 数据处理指令:数据传输指令,算术指令,逻辑指令,比
较指令,乘法指令 ( 略 ) ,前导零计数 ( 略 )3. 程序状态访问指令4. 跳转指令5. 单数据访存指令6. 多数据访存指令7. 其它指令:信号量操作指令,异常中断产生指令,协处理
器指令 ( 略 )8. 伪指令 ( 略 )9. 基于 ARM 的编程
ARM指令集与编程 80
• 编程工具• 编程语言
9 基于 ARM 的编程
ARM指令集与编程 81
9.1 编程工具• 直接面向硬件的开发
– SDT 、 ADS (集成开发环境)– CodeWorrier
• 面向特定操作系统的应用– 面向WinCE 的 Embeded VC – 面向 Symbian 的 C/C++ 集成开发环境– 面向 Linux 的 GNU Tools (主要是 GCC/G++ )
• 操作系统开发– GCC/C++ ( 支持 uClinux, Linux, ECOS, uC/OS-II 等 )
• 其它– J2ME 等基于 JAVA 的编程
ARM指令集与编程 82
9.2 编程语言• 汇编语言• C/C++语言
– C 与汇编混合编程• Java语言
ARM指令集与编程 83
9.2.1 C 与汇编混合编程( 1 )
• C/ASM 相互调用基于 ATPCS(ARM/Thumb Procdure Call Standard) ,也可简称 APCS
• C 中内嵌汇编 基于 ADS__asm{ 指令 [; 指令 ] /* 注释 */ ….. [ 指令 ]}
基于 GNU C__asm__ __volatile__ (“ 指令 @ 注释 ….. [ 指令 ]:属性设置 1:属性设置 2)
Asm(“ 指令” :属性 1:属性 2)
ARM指令集与编程 84
• 内嵌汇编的语法– 操作数:作为操作数的寄存器和产量可以是 ch
ar\short\int型的 C 表达式。–物理寄存器:不要使用复杂的 C 表达式;一般
不要使用 R0~R3,R12(IP),R14(LR)– 不要使用寄存器代替变量
9.2.1 C 与汇编混合编程( 2 )
ARM指令集与编程 85
9.2.1 C 与汇编混合编程( 3 )
• 交互规则:– 寄存器规则: v1-v8 ( R4-R11 )用来保存局部变量– 堆栈规则: FD 类型(满递减)堆栈– 参数传递规则:如果参数个数小于等于 4 ,用 R0-R3保存参数;参数个数多于 4 的情况下,剩余的参数传入堆栈
– 子程序结果返回规则:• 结果为一个 32 位整数,通过 R0返回;• 结果为一个 64 位整数,通过 R0 、 R1返回;• 对于位数更多的结果,通过内存传递。
ARM指令集与编程 86
9.2.1 C 与汇编混合编程( 4 )• C调用汇编
C 代码
…
extern void scopy(char *d, char *s);
…
scopy(dststr,srcstr
汇编代码
AREA example, CODE, READONLY
EXPORT scopy
scopy
LDRB R2, [R1],#1
STRB R2, [R1], #1
CMP R2, #0
BNE scopy
MOV pc,lr
END
ARM指令集与编程 87
9.2.1 C 与汇编混合编程( 5 )• 汇编调用 C
汇编代码EXPORT callsum5
AREA example, CODE, READONLY
IMPORT sum5
callsum5
STMFD SP!,{LR} ;LR 入栈
ADD R1,R0,R0 ;R0=a,R1=b,R2=c
ADD R2,R1,R0
ADD R3,R1,R2
STR R3,[SP,#-4]! ;e in stack
ADD R3,R1,R1 ;R3=d
BL sum5 ;call R5, R0=result
ADD SP,SP,#4
LDMFD SP!,{PC}
END
C 代码
Int sum5(int a, int b ,int c, int d, int e)
{
return (a+b+c+d+e);
}