第四章 ARM指令集 ARM指令集概述 ARM寻址方式 ARM指令详细介绍 一些基本的ARM指令功能段
(一)ARM指令集概述 1.1 ARM指令分类 ARM指令集总体分为6类指令 数据处理指令:完成寄存器中数据的算术和逻辑运算操作。 程序状态寄存器处理指令:mrs和msr。 跳转指令:b和bl。 Load/Store指令:唯一用于寄存器和存储器之间进行数据传送的指令。 异常中断产生指令:swi和bkpt。 协处理器指令。
1.2 ARM指令的特点: 所有指令都是32bit。 大多数指令都在单周期内完成。 所有指令都可以条件执行。 load/store体系结构。 指令集可以通过协处理器扩展
1.3 ARM指令的格式 一条典型的ARM指令编码格式为: 一条典型的ARM指令语法格式为: Cond 001 Opcode S Rn Rd Operand2 11 12 15 16 19 20 21 24 25 27 28 31 7 8 一条典型的ARM指令语法格式为: <Opcode>{<cond>}{s} <Rd>, <Rn>{, <Operand2>} Opcode:指令操作码。 cond:指令的条件码。 S:决定指令的操作是否影响cpsr的值。 Rd:目标寄存器编码。 Rn:包含第一个操作数的寄存器编码。 Operand2:第2操作数。 例:ADDS R2,R1,#1 SUBNES R2,R1,#0x20 LDR R0,[R1]
1.4 条件执行 每条ARM指令包含4位条件码域< cond >,它占用指令编码的最高四位[31:28]。 条件编码共 24 =16 种,其中,15种用于指令的条件码。每种条件码用2个英文缩写字符表示。(见P.101 表3-1) ARM处理器根据指令的执行条件是否满足,决定当前指令是否执行。 只有在cpsr中的条件标志位满足指定的条件时,指令才会被执行。不符合条件的代码依然占用一个时钟周期(相当于一个NOP指令)。 书写时,条件码的位置在指令助记符的后面(因此也称为条件后缀。),例如: MOVEQ R0, R1; 指令条件后缀如下表所示:(见P.101 表3-1)
NV 无 从不(未使用)
默认情况下,数据处理指令不影响条件码标志位,但可以选择通过添加“S”来影响标志位。 CMP不需要增加 “S”就可改变相应的标志位。 loop … SUBS r1,r1,#1 BNE loop 如果 Z标志清零则跳转 R1减1,并设置标志位
(二)ARM寻址方式 寻址方式: 根据指令编码中给出的地址码字段来寻找真实操作数的方式。 立即寻址 寄存器寻址 寄存器间接寻址 基址加偏址寻址 堆栈寻址 块拷贝寻址 相对寻址
2.1 立即寻址 立即寻址 ——操作数本身在指令中直接给出(立即数由指令的后12位给定),这个操作数被称为立即数,对应的寻址方式也就叫做立即寻址。 例如以下指令: ADD R0,R0,#1 ;R0←R0+1 MOV R0,#0x3f ;R0←#0x3f 书写立即数时,要求以“#”为前缀。 十六进制数,#后加 0x或&,如 #0x3f,#&3f. 二进制数,#后加 0b, 如 #0b1011 十进制数,#后加 0d或缺省,如#0d678,#789 如何构造32位立即数?
因此有效立即数immediate可以表示成: <immediate>=immed_8 循环右移(2×rot) 数据处理指令格式中,第二个操作数有12位 因此有效立即数immediate可以表示成: <immediate>=immed_8 循环右移(2×rot) 4 bit 移位值 (0-15)乘于2,得到一个范围在0-30,步长为 2的移位值。 记住一条准则: “最后8位一定要移动偶数位”。 11 8 7 rot immed_8 x2 Shifter ROR
例: 下列命令中,汇编器把立即数转换为移位操作: MOV r0,#4096 ; uses 0x40 ror 26 31 7 ror #0 ror #8 ror #30 下列命令中,汇编器把立即数转换为移位操作: MOV r0,#4096 ; uses 0x40 ror 26 ADD r1,r2,#0xFF0000 ; uses 0xFF ror 16 带有立即数的MOV 指令的二进制编码为: MOV R0,0xF200 ;E3A00CF2 MOV R1,0x110000 ;E3A01811 MOV R4,0x12800 ;E3A04B4A 0xF200 =0xF2循环右移(2*C) 0x110000 =0x11循环右移(2*8) 0x12800 =0x4A循环右移(2*B)
0x3F0 只有能够通过此构造方法得到的才是合法的立即数。 合法立即数: 非法立即数: 0xFF;0x104;0xFF0;0xFF00 非法立即数: 0x101;0x102;0xFF1 一个合法的立即数可能有多种编码方法,将使某些指令的执行产生不同的结果。 0x3F0 immed_8=0x3F,rot=0xE immed_8=0xFC,rot=0xF ARM汇编编译器生成立即数的规则为: 当立即数数值在0到0xFF范围时,令immed_8=<immediate>,rot=0。 其它情况下,汇编编译器选择使rot数值最小的编码方式。
2.2 寄存器寻址 寄存器寻址: 这种寻址方式是各类微处理器经常采用的一种方式,也是一种执行效率较高的寻址方式。 如指令: ——利用寄存器中的数值作为操作数。 这种寻址方式是各类微处理器经常采用的一种方式,也是一种执行效率较高的寻址方式。 如指令: ADD R0,R1,R2 ;R0←R1+R2
寄存器移位寻址 ——当第二操作数为寄存器型时,在执行寄存器寻址操作时,可以选择是否对第二操作数进行移位,即第二操作数形如: MOV Rd, Rn, Rm,{<shift>} 其中: Rm 称为第二操作数寄存器 <shift> 用来指定移位类型和移位位数。移位 位数可以用5位立即数<#shift> 或寄存 器(Rs)方式。
在指令执行时将寄存器移位后的内容作为第二操作数参与运算。例如指令: ADD R3,R2,R1,LSR #2 ;R3←R2+(R1右移2位) ADD R3,R2,R1,LSR R0 ;R3←R2+(R1右移R0位)
第二操作数移位方式 LSL:逻辑左移,空出的最低有效位用0填充。 LSR:逻辑右移,空出的最高有效位用0填充。 31 0 31 0
ASL:算术左移,由于左移空出的有效位用0填充,因此 它与LSL同义。 ASR:算术右移 (Arithmetic Shift Right) 。算术移位的对象是带符号数,移位过程中必须保持操作数的符号不变。如果源操作数是正数,空出的最高有效位用0 填充,如果是负数用1填充。 30 0
31 0 ROR:循环右移(Rotate Right),移出的字的最低有效位依次填入空出的最高有效位。 31 0 RRX:带扩展的循环右移(Rotate Right Extended) 。将寄存器的内容循环右移1位,空位用原来C标志位填充。 31 0 C
第二操作数的移位位数 移位位数可以用立即数方式或者寄存器方式给出, 如下所示: ADD R3,R2,R1,LSR #2 ;R3R2+(R1右移2位) ADD R3,R2,R1,LSR R4 ;R3R2+(R1右移R4位) 寄存器R1的内容分别逻辑右移2位、R4位,再与寄 存器R2的内容相加,结果放入R3中。
2.3 寄存器间接寻址 寄存器间接寻址 ——就是以寄存器中的值作为操作数的地址,而操作数本身存放在存储单元中。例如以下指令: LDR R0,[R1] ;R0←[R1] STR R0,[R1] ;[R1]←R0 第一条指令将以R1的值为地址的存储单元中的内容加载到寄存器R0中。 第二条指令将R0的内容存储到以R1的值为地址的存储单元中。 R1——基址寄存器 R1的内容——基地址
2.4 基址加偏址寻址(变址寻址) 基址加偏址寻址 ——将基址寄存器的内容与指令中给出的地址偏移量相加,得到操作数所在的存储器的有效地址。 变址寻址方式常用于访问某基地址附近的地址单元。(4K) 例如: LDR R0,[R1,#4] ;R0←mem32[R1+4]
有三种加偏址的方式: 前变址模式(不修改基址寄存器): ——先基址+偏址,生成操作数地址,做指令指定的操作。 Pre-indexed: STR r0,[r1,#12] 偏移量 r0 源寄存器 for STR 12 0x5 0x5 0x20c r1 基址 寄存器 0x200 0x200
自动变址模式(修改基址寄存器): ——①先基址+偏移,生成操作数地址,做指令指定的操作。②然后自动修改基址寄存器。 例如: LDR R0,[R1,#4]! ;R0←mem32 [R1+4] ;R1←R1+4 ! ——表示更新基址寄存器。
Post-indexed: STR r0,[r1],#12 后变址模式(修改基址寄存器): ——①基址寄存器不加偏移作为操作数地址。 ②完成指令操作后,用(基址+偏移)的值修改基址寄存器。 Post-indexed: STR r0,[r1],#12 更新 基址寄存器 r1 偏移量 0x20c 12 0x20c r0 原基址 寄存器 r1 源寄存器 for STR 0x5 0x200 0x5 0x200
偏移地址 常用的是立即数偏移的形式。 ——可以是一个立即数,也可以是另一个寄存器,并且在加到基址寄存器前还可以经过移位操作,如下所示: LDR r0,[r1,r2] ;r0<—mem32[r1+r2] LDR r0,[r1,r2,LSL #2] ;r0<—mem32[r1+r2*4] 常用的是立即数偏移的形式。
2.5 堆栈寻址 堆栈寻址 ——堆栈寻址是隐含的,它使用一个专门的寄存器(堆栈指针SP)指向一块存储区域(堆栈)。 堆栈可分为两种增长方式: ◆ 向上生长:向高地址方向生长,称为递增堆栈。 ◆ 向下生长:向低地址方向生长,称为递减堆栈。
根据堆栈指针指向的数据位置的不同,可分为: 满堆栈 ——堆栈指针指向最后压入堆栈的有效数据项,称为满堆栈; 空堆栈 ——堆栈指针指向下一个待压入数据的空位置,称为空堆栈。 这样就有4种类型的堆栈表示递增和递减的满和空堆栈的各种组合。
四种类型的堆栈工作方式 满递增堆栈FA: ——堆栈指针指向最后压入的数据,且由低地址向高地址生长。 满递减堆栈FD: ——堆栈指针指向最后压入的数据,且由高地址向低地址生长。 空递增堆栈EA: ——堆栈指针指向下一个将要放入数据的空位置,且由低地址向高地址生长。 空递减堆栈ED: ——堆栈指针指向下一个将要放入数据的空位置,且由高地址向低地址生长。
STMFD sp!,{r4-r7,lr} 满递减 LDMFA sp!,{r4-r7,pc} 满递增 100 FF 1234 AOBE 8034 1010 8420 9753 r4 1 r5 14544 r6 r7 12 lr 9048 pc 9020 r4 100 r5 FF r6 1234 r7 A0BE lr 8034 ABCD 8765 102E 16 FFFF 1010 8420 9753 高地址 SP 8034 pc SP Old SP 100 FF 1234 A0BE 8034 SP 8034 A0BE A0BE r7 1234 1234 r6 FF FF r5 100 r4 100
2.6 块拷贝寻址 块拷贝寻址 ——把存储器中的一个数据块加载到多个寄存器中,也可以把多个寄存器中的内容保存到存储器中。是多寄存器传送指令LDM/STM的寻址方式。 寻址操作中的寄存器可以是R0-R15这16个寄存器的子集或是所有寄存器。
4 中寻址操作: LDMIA / STMIA Increment After(先传送,后地址加4) LDMIB / STMIB Increment Before(先地址加4 ,后传送) LDMDA / STMDA Decrement After (先传送,后地址减4) LDMDB / STMDB Decrement Before (先地址减4,后传送) IA IB DA DB STMxx r10, {r0,r1,r4} r4 r4 r1 r1 r0 地址 增加 基址寄存器 (Rb) r10 r0 r4 r1 r4 r0 r1 r0
2.7 相对寻址 相对寻址 —— 与基址变址寻址方式相类似,相对寻址以程序计数器PC的当前值为基地址,指令中的地址标号作为偏移量,将两者相加之后得到操作数的有效地址。 相对寻址指令举例如下: BL SUBRl ;调用到SUBRl子程序 . . . SUBR1… MOV PC, LR ;返回
(三) ARM指令详细介绍 数据处理指令; Load/Store指令; 程序状态寄存器处理指令; 跳转指令; 异常中断产生指令; 协处理器指令。
3.1 数据处理指令 ARM的数据处理指令主要完成寄存器中数据的算术和逻辑运算操作。 ARM数据处理指令的特点: 所有的操作数要么来自寄存器,要么来自立即数,不会来自存储器。 如果有结果,则结果一定是为32位宽,并且放在一个寄存器中,不会写入存储器。(有一个例外:长乘法指令产生64位结果) 每一个操作数寄存器和结果寄存器都在指令中独立指出,即:ARM指令采用3地址模式: <Operation> Rd, Rn, Rm
ARM数据处理指令大致可分为6类: 算术运算指令: ADD ADC SUB SBC RSB RSC 逻辑运算指令: AND ORR EOR BIC 数据传送指令: MOV MVN 比较指令: CMP CMN 测试指令: TST TEQ 乘法指令:MUL MLA UMULL UMLAL SMULL SMLAL 上述指令只能对寄存器操作,不能针对存储器。
后缀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。
当选择后缀S时: 如果结果为负,则N标志位置1;否则清0。 如果结果为0,则Z标志位置1;否则清0。 如果是算术运算指令或比较指令时,C标志位设置为ALU的进位输出;否则设置为移位器的进位输出。如果不需要移位,则C保持不变。 在非算术操作中,V标志位保持原值。在算术操作中,如果有溢出,则置1;不发生溢出,则清0。
结果 操作数1 Barrel Shifter 操作数2 ALU
数据处理指令的二进制编码如下: add r0, r1, #0xff add r0, r1, r1, LSL #31 add r0, r1, r1, LSL r2
数据处理指令的详细列表如下: 操作码[24:21] 助记符 意义 效果 0000 AND 逻辑位与 Rd = Rn AND Op2 0001 EOR 逻辑位异或 Rd = Rn EOR Op2 0010 SUB 减 Rd = Rn - Op2 0011 RSB 反向减 Rd = Op2 – Rn 0100 ADD 加 Rd = Rn + Op2 0101 ADC 带进位加 Rd = Rn + Op2 + C 0110 SBC 带进位减 Rd = Rn - Op2 + C -1 0111 RSC 反向带进位减 Rd = Op2 - Rn + C -1 1000 TST 测试 根据Rn AND Op2设置条件码 1001 TEQ 测试相等 根据Rn EOR Op2设置条件 1010 CMP 比较 根据Rn - Op2设置条件码 1011 CMN 负数比较 根据Rn + Op2设置条件码 1100 ORR 逻辑位或 Rd = Rn OR Op2 1101 MOV 传送 Rd = Op2 1110 BIC 位清零 Rd = Rn AND NOT Op2 1111 MVN 求反 Rd = NOT Op2
算术运算指令: ① ADD——加法运算指令 ADD指令将operand2的数据与Rn的值相加,结果保存到Rd寄存器。 指令格式如下: 指令格式如下: ADD{cond}{S} Rd,Rn,operand2 指令举例如下: ADDS R1,R1,#1 ;R1=R1+1 ADDS R3,R1,R2,LSL #2 ; R3=R1+R2<<2
ADC指令将operand2的数据与Rn的值相加,再加上CPSR中的C条件标志位,结果保存到Rd寄存器。指令格式如下: ADC{cond}{S} Rd,Rn,operand2 指令举例如下: ADDS R4,R0,R2 ;使用ADC实现64位加法, ADC R5,R1,R3 ;(R5、R4)=(R1、R0)+(R3、R2)
SUB指令用寄存器Rn减去operand2,结果保存到Rd中。 指令格式如下: SUB{cond}{S} Rd,Rn,operand2 指令举例如下: SUBS R0,R0,#l ;R0=R0-1 SUB R6,R7,#0x10 ;R6=R7-0x10
④SBC——带进位减法指令 SBC指令用寄存器Rn减去operand2,再减去CPSR中的C条件标志位的反码,结果保存到Rd中。 指令格式如下: SBC{cond}{S} Rd,Rn,operand2 指令举例如下: SUBS R4,R0,R2 ;使用SBC实现64位减法, SBC R5,R1,R3 ;(R5,R4)=(R1,R0)-(R3,R2)
RSB指令用寄存器operand2减去Rn,结果保存到Rd中。 指令格式如下: RSB{cond}{S} Rd,Rn,operand2 指令举例如下: RSB R3,R1,#0xFF00 ;R3=0xFF00-R1 RSBS R1,R2,R2,LSL #2 ;R1R2<<2-R2 ;(R1 =R2×3)
⑥ RSC——带进位反向减法指令 RSC 指令用寄存器operand2减去Rn,再减去CPSR中的C条件标志位的反码,结果保存到Rd中。 指令格式如下: RSC{cond}{S} Rd,Rn,operand2 指令举例如下: RSBS R2,R0,#0 RSC R3,R1,#0 ;使用RSC指令实现 ;求64位数值的负数
逻辑运算指令: AND指令可用于提取寄存器中某些位的值。 ①AND——逻辑“与”操作指令 AND指令将operand2的值与寄存器Rn的值按位逻辑“与”操作,结果保存到Rd中。 指令格式如下: AND{cond}{S} Rd,Rn,operand2 指令举例如下: ANDS R0,R0,#0x01 ;R0=R0&0x01 ;取出最低位数据 AND R2,R1,R3 ;R2=R1&R3 AND指令可用于提取寄存器中某些位的值。
ORR指令将operand2的值与寄存器Rn的值按位逻辑“或”操作,结果保存到Rd中。 指令格式如下: ORR{cond}{S} Rd,Rn,operand2 指令举例如下: ORR R0,R0,#0x0F ;将R0的低4位置1 ORR指令用于将寄存器中某些位的值设置成1。
EOR指令将operand2的值与寄存器Rn的值按位逻辑“异或”操作,结果保存到Rd中。 指令格式如下: EOR{cond}{S} Rd,Rn,operand2 指令举例如下: EOR R1,R1,#0x0F ;将Rl的低4位取反 EORS R0,R5,#0x01 ;将R0R5异或0x01, ;并影响标志位 EOR指令可用于将寄存器中某些位的值取反。将某一位与0异或,该位值不变;与1异或,该位值被求反。
④BIC——位清除指令 BIC指令将寄存器Rn的值与operand2的值的反码按位逻辑“与”操作,结果保存到Rd中。 指令格式如下: BIC{cond}{S} Rd,Rn,operand2 指令举例如下: BIC R1,R1,#0x0F ;将R1的低4位清0, ;其它位不变 BIC指令可用于将寄存器中某些位的值设置成0。将某一位与1做BIC操作,该位值被设置成0;将某一位与0做BIC操作,该位值不变。
数据传送指令: ① MOV——数据传送指令 MOV指令将operand2传送到目标寄存器Rd中。 指令格式如下: MOV{cond}{S} Rd,operand2 指令举例如下: MOVS R3,R1,LSL #2 ;R3=R1<<2 ;影响标志位 MOV PC,LR ;PCLR,子程序返回
MOV指令可以完成以下功能: 将数据从一个寄存器传送到另一个寄存器中。 将一个常数传送到一个寄存器中。 实现单纯的移位操作。 当PC寄存器作为目标寄存器时可以实现程序跳转。这种跳转可以实现子程序调用以及从子程序中返回。 当PC寄存器作为目标寄存器且指令中S位被设置时,指令在执行跳转操作的同时,将当前处理器模式的SPSR寄存器内容复制到CPSR中。这样可以实现从某些异常中断中返回。
MVN指令将operand2按位取反后传送到目标寄存器Rd中。 指令格式如下: MVN{cond}{S} Rd,operand2 指令举例如下: MVN R1,#0xFF ;R10xFFFFFF00 MVN R1,R2 ; Rl R2取反
比较指令: ① CMP——比较指令 CMP指令将寄存器Rn的值减去operand2的值,根据操作的结果更新CPSR中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。 指令格式如下: CMP{cond} Rn,operand2 指令举例如下: CMP R1,#10 ;R1与10比较,设置相关标志位 CMP指令与SUBS指令的区别?
CMN R0,#1 ;R0+1,判断R0是否为1的补码。 ;若是,则Z位置1。 CMN指令将寄存器Rn的值加上operand2的值,根据操作的结果更新CPSR中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。 指令格式如下: CMN{cond} Rn,operand2 指令举例如下: CMN R0,#1 ;R0+1,判断R0是否为1的补码。 ;若是,则Z位置1。 CMN指令与ADDS指令的区别在于CMN指令不保存运算结果。CMN指令可用于负数比较,比如“CMN R0,#1”指令则表示R0与-1比较。若R0为-1(即1的补码),则Z置位;否则Z复位
测试指令: ① TST——位测试指令 TST指令将寄存器Rn的值与operand2的值按位逻辑“与”操作,根据操作的结果更新CPSR中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。 指令格式如下: TST{cond} Rn,operand2 指令举例如下: TST R0,#0x01 ;判断R0的最低位是否为0 TST Rl,#0x0F ;判断R1的低4位是否为0 TST指令与ANDS指令的区别在于TST指令不保存运算结果。TST指令通常与EQ、NE条件码配合使用。当所有测试位均为0时,EQ有效。而只要有一个测试位不为0,则NE有效。
② TEQ——测试相等指令 TEQ指令将寄存器Rn的值与operand2的值按位逻辑“异或”操作,根据操作的结果更新CPSR中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。 指令格式如下: TEQ{cond} Rn,operand2 指令举例如下: TEQ R0,R1 ;比较R0与R1是否相等 ;(不影响V位和C位) TEQ指令与EORS指令的区别在于TEQ指令不保存运算结果。使用TEQ进行相等测试时,常与EQ、NE条件码配合使用。当两个数据相等时,EQ有效;否则NE有效。
乘法指令 ARM有两类乘法指令: 32位的乘法指令,即乘法操作的结果为32位; 64位的乘法指令,即乘法操作的结果为64位。
MUL指令将Rm和Rs中的值相乘,结果的低32位保存到Rd中。 指令格式如下: MUL{cond}{S} Rd,Rm,Rs ;RdRm*Rs 指令举例如下: MUL R1,R2,R3 ;R1=R2×R3 MULS R0,R3,R7 ;R0=R3×R7, ;设置CPSR的N位和Z位
MLA指令将Rm和Rs中的值相乘,再将乘积加上第3个操作数,结果的低32位保存到Rd中。 指令格式如下: MLA{cond}{S} Rd,Rm,Rs,Rn ; RdRm*Rs+Rn 指令举例如下: MLA R1,R2,R3,R0 ;R1=R2×R3+R0
③ UMULL—64位无符号乘法指令 UMULL指令将Rm和Rs中的值作无符号数相乘,结果的低32位保存到RdLo中,高32位保存到RdHi中。 指令格式如下: UMULL{cond}{S} RdLo,RdHi,Rm,Rs ; RdHi, RdLo Rm*Rs 指令举例如下: UMULL R0,R1,R5,R8 ;(R1,R0)R5×R8
④ UMLAL—64位无符号乘加指令 UMLAL指令将Rm和Rs中的值作无符号数相乘,64位乘积与RdHi、RdLo相加,结果的低32位保存到RdLo中,而高32位保存到RdHi中。 指令格式如下: UMLAL{cond}{S} RdLo,RdHi,Rm,Rs ;RdHi, RdLo Rm*Rs+ RdHi, RdLo 指令举例如下: UMLAL R0,R1,R5,R8 ;(R1,R0)R5×R8+(R1,R0)
SMULL指令将Rm和Rs中的值作有符号数相乘,结果的低32位保存到RdLo中,而高32位保存到RdHi中。 指令格式如下: SMULL{cond}{S} RdLo,RdHi,Rm,Rs ; RdHi, RdLo Rm*Rs 指令举例如下: SMULL R2,R3,R7,R6 ;(R3,R2)R7×R6
⑥ SMLAL—64位有符号乘加指令 SMLAL指令将Rm和Rs中的值作有符号数相乘,64位乘积与RdHi、RdLo相加,结果的低32位保存到RdLo中,高32位保存到RdHi中。 指令格式如下: SMLAL{cond}{S} RdLo,RdHi,Rm,Rs ; RdHi, RdLo Rm*Rs+ RdHi, RdLo 指令举例如下: SMLAL R2,R3,R7,R6 ; ;(R3,R2)R7×R6+(R3,R2)
乘法指令的特点: 不支持第2操作数为立即数。 结果寄存器不能与第一源寄存器相同。 Rd、RdHi、RdLo不能与Rm为同一寄存器。 早期的ARM处理器仅支持32位乘法指令。ARM7版本和后续的在名字中有M的处理器才支持64位乘法指令。
当在乘法指令中设置了位S时,则: 对于产生32位结果的指令形式,将标志位N设置为Rd的第31位的值;对于产生长结果的指令形式,将其设置为RdHi的第31位的值。 如果Rd或RdHi、RdLo为0,则标志位Z置位。 乘法指令不影响V标志位。 ARM v5及以上的版本不影响C标志位; ARM v5以前的版本,C标志位数值不确定。
3.2 Load/Store指令 ——加载/存储指令 对于存储器的读/写只能用此类指令。 因为ARM处理器对外设寄存器、I/O映射空间与存储器统一编址,对外围设备的I/O操作也用此类指令。 共有三种加载/存储指令: 单寄存器的存取指令(LDR,STR) 多寄存器存取指令(LDM,STM) 单寄存器交换指令(SWP)
1. 单寄存器的存取指令 单寄存器加载/存储指令是ARM在寄存器和存储器间传送单个字节和字的最灵活方式。根据传送数据的类型不同,单个寄存器存取指令又可以分为以下两类: 单字和无符号字节的加载/存储指令 半字和有符号字节的加载/存储指令
(1)单字和无符号字节的加载/存储指令 LDR指令从内存中取32位字或8位无符号字节数据放入寄存器; STR指令将寄存器中的32位字或8位无符号字节数据保存到存储器中。 注意: 字节传送时用0将8位的操作数扩展到32位。
指令格式如下: LDR{cond}{T} Rd,<地址> ;加载指定地址上的字数据,放入Rd中。 STR{cond}{T} Rd,<地址> ;存储Rd中字数据,到指定地址的存储单元。 LDR{cond}B{T} Rd,<地址> ;加载字节数据到Rd中, Rd最低字节有效,高24位为0。 STR{cond}B{T} Rd,<地址> ;存储Rd中字节数据, Rd中最低字节为传送数据。
T后缀 T为可选后缀,若指令有T,那么即使处理器是在特权模式下,存储系统也将访问看成是处理器是在用户模式下。 用于存储器保护。 不能与前变址模式、自动变址模式一起使用。 T在用户模式下无效。
指令寻址方式: LDR/STR指令寻址非常灵活,由两部分组成: 地址偏移量有以下3种格式: 一部分为一个基址寄存器,可以为任一个通用寄存器; 另一部分为一个地址偏移量。 地址偏移量有以下3种格式: 立即数 寄存器 寄存器及移位常数。
①立即数 指令举例如下: LDR R1,[R0,#0x12] LDR R1,[R0,# -0x12] ——12位立即数可以是一个无符号的数值。这个数据可以加到基址寄存器,也可以从基址寄存器中减去这个数值。 指令举例如下: LDR R1,[R0,#0x12] ;将R0+0x12地址处的数据读出,保存到R1中(R0的值不变) LDR R1,[R0,# -0x12] ;将R0-0x12地址处的数据读出,保存到R1中(R0的值不变)
②寄存器 ——寄存器中的数值可以加到基址寄存器,也可以从基址寄存器中减去这个数值。指令举例如下: LDR R1,[R0,R2]
;将R0+R2×4地址处的数据读出,保存到R1中(R0、R2的值不变) ③寄存器及移位常数 ——寄存器移位后的值可以加到基址寄存器,也可以从基址寄存器中减去这个数值。 指令举例如下: LDR R1,[R0,R2,LSL #2] ;将R0+R2×4地址处的数据读出,保存到R1中(R0、R2的值不变) LDR R1,[R0,-R2,LSL #2] ;将R0-R2×4地址处的数据读出,保存到R1中(R0、R2的值不变) 注意:移位位数只能是5位的立即数,不能使用寄存器指定移位位数。
PC的使用: 使用PC作为基址时,使用的数值是指令的地址加8个字节。 PC不能用做偏移寄存器,也不能用于任何变址寻址模式。
(2)半字和有符号字节的加载/存储指令 这类LDR/STR指令可实现半字(有符号和无符号)、有符号字节数据的传送。 特点: 偏移量格式、寻址方式与加载/存储字和无符号字节指令基本相同。 立即数偏移量限定在8位,寄存器偏移量不可经过移位得到。
;加载无符号半字数据到Rd的低16位,高16位清零。 指令格式如下: LDR {cond}H Rd,<地址> ;加载无符号半字数据到Rd的低16位,高16位清零。 LDR {cond}SB Rd,<地址> ;加载指定地址上有符号字节到Rd中,高24位用符号位扩展 LDR {cond}SH Rd,<地址> ;加载指定地址上的有符号半字到Rd中,高16位用符号位扩展。 STR{cond}H Rd,<地址> ;存储Rd中的低16位半字数据。 存储有符号数据和无符号数据之间没有差别。
说明: 有符号位字节或有符号半字的传送是用“符号位”扩展到32位;无符号半字传送是用0扩展到32位。 地址对齐——对半字传送的地址必须为偶数。非半字对齐的半字加载将使Rd内容不可靠;非半字对齐的半字存储将使指定地址的2字节存储内容不可靠。
指令举例: LDRSB R1,[R0,R3] ;将R0+R3地址上的字节数据读到R1,高24位用符号位扩展 LDRSH R1,[R9] LDRH R6,[R2],#2 ;将R2地址上的半字数据读出到R6,高16位用零扩展,R2=R2+2 STRH R1,[Ro,#2]! ;将R1的数据保存到R0+2地址中,只存储低2字节数据,R0=R0+2
2. 多寄存器的存取指令 LDM和STM指令可以实现在一组寄存器和一块连续的内存单元之间存/取数据。LDM为加载多个寄存器;STM为存储多个寄存器。允许一条指令传送16个寄存器的任何子集或所有寄存器。 指令格式如下: LDM{cond}<模式> Rn{!},<reglist>{^} STM{cond}<模式> Rn{!},<reglist>{^}
指令格式中, 寄存器Rn为基址寄存器,装有传送数据的初始地址,Rn不允许为R15。 后缀“!”表示最后的地址写回到Rn中。 寄存器列表reglist可包含多个寄存器或包含寄存器的范围,使用“,”分开,如{R1,R2,R6~R9} 。其中寄存器和存储器的对应关系满足规则:编号低的寄存器对应于存储器中低地址单元,编号高的寄存器对应于存储器中高地址单元。
后缀“^” 使用后缀“^”进行数据传送且寄存器列表不包含PC时,加载/存储的是用户模式的寄存器,而不是当前模式的寄存器。 后缀“^”不允许在用户模式或系统模式下使用。 若在LDM指令中使用后缀“^”且寄存器列表中包含有PC时,除了正常的多寄存器传送外,将SPSR也拷贝到CPSR中,此用法可用于异常处理返回。
地址字对齐——这些指令忽略地址位[1:0]。 当Rn在寄存器列表中且使用后缀“!”时: 对于STM指令,若Rn为寄存器列表中的最低数字的寄存器,则会将Rn的初值保存; 其它情况下Rn的加载值和存储值不可预知。 地址字对齐——这些指令忽略地址位[1:0]。
LDM/STM的主要用途是现场保护、数据复制和参数传送等。其模式有如下8种(前面4种用于数据块的传输,后面4种是堆栈操作): IA: 每次传送后地址加4; IB: 每次传送前地址加4; DA: 每次传送后地址减4; DB: 每次传送前地址减4; FD: 满递减堆栈; ED: 空递减堆栈; FA: 满递增堆栈; EA: 空递增堆栈。
举例如下: LDMIA R0!,{R3 - R9} ;加载R0指向地址上的多字数据,保存到R3~R9中,R0值更新 STMIA R1!,{R3 - R9} ;将R3~R9的数据存储到R1指向的地址上,R1值更新 STMFD SP!,{R0 - R7,LR} ;现场保存,将R0~R7、LR入栈 LDMFD SP!,{R0 - R7,PC} ;恢复现场,异常处理返回
3. 单寄存器交换指令(SWP) SWP指令用于将一个存储单元(该单元地址放在寄存器Rn中)的内容读取到一个寄存器Rd中,同时将另一个寄存器Rm的内容写入到该存储单元中。 指令格式如下: SWP{cond}{B} Rd,Rm,[Rn] 其中,B为可选后缀,若有B,则交换字节,否则交换32位字; Rd为被加载的寄存器;Rm的数据用于存储到Rn所指的地址中。若Rm与Rd相同,则为寄存器与存储器内容进行交换;Rn为要进行数据交换的存储器地址,Rn不能与Rd和Rm相同。
指令举例如下: SWP R1,R1,[R0] ;将R1的内容与R0指向的存储单元的内容进行交换。 SWPB R1,R2,[R0] ;将R0指向的存储单元的内容读取1字节数据到R1中(高24位清零),并将R2的内容写入到该内存单元中(最低字节有效)
3.3 程序状态寄存器处理指令 ARM指令中有两条指令,用于在状态寄存器和通用寄存器之间传送数据。修改状态寄存器一般是通过“读取-修改-写回”三个步骤的操作来实现的。 这两条指令分别是: 状态寄存器到通用寄存器的传送指令(MRS) 通用寄存器到状态寄存器的传送指令(MSR)
MRS 在ARM处理器中,只有MRS指令可以将状态寄存器CPSR或SPSR读出到通用寄存器中。 指令格式如下: MRS{cond} Rd,psr ; Rd psr 其中: Rd —— 目标寄存器。Rd不允许为R15。 psr —— CPSR或SPSR。 指令举例如下: MRS R1,CPSR ; R1 CPSR MRS R2,SPSR ; R2 SPSR
MRS指令读取CPSR,可用来判断ALU的状态标志,或IRQ、FIQ中断是否允许等。 MRS指令读取CPSR,可用来判断ALU的状态标志,或IRQ、FIQ中断是否允许等。 在异常处理程序中,读SPSR可知道进行异常前的处理器状态等。 MRS与MSR配合使用,实现CPSR或SPSR寄存器的读—修改—写操作,可用来进行处理器模式切换、允许/禁止IRQ/FIQ中断等设置。
MSR{cond} psr_fields,Rm 在ARM处理器中,只有MSR指令可以直接设置状态寄存器CPSR或SPSR。 指令格式如下: MSR{cond} psr_fields,#immed MSR{cond} psr_fields,Rm 注:不可以使用S后缀。
immed: 要传送到状态寄存器指定域的立即数。 Rm: 要传送到状态寄存器指定域的数据的源寄 存器。 其中: psr: CPSR或SPSR。 immed: 要传送到状态寄存器指定域的立即数。 Rm: 要传送到状态寄存器指定域的数据的源寄 存器。 fields 指定传送的区域。fields可以是以下的一种或多种(字母必须为小写): c 控制域 (psr[7…0]); x 扩展域(psr[15…8]);(暂未用) s 状态域 (psr[23…16]);(暂未用) f 标志位域 (psr[31…24])。
程序状态寄存器 N Z C V Q J U n d e f i n e d I F T mode f s c x 31 28 27 24 23 16 15 8 7 6 5 4 N Z C V Q J U n d e f i n e d I F T mode f s c x
指令举例如下: MSR CPSR_f,#0xF0000000 ; 当使用立即数操作数时,只可选择更新标志位[31:24] 。 CPSR[31:28]=0xF(0b1111),即N,Z,C,V均被置1。 当使用立即数操作数时,只可选择更新标志位[31:24] 。 修改状态寄存器一般是通过“读取-修改-写回”三个步骤的操作来实现的。
CPSR的读—修改—写操作举例如下: 例1:设置进位位C MRS R0, CPSR ;R0CPSR ORR R0,R0,#0X20000000 ;置1进位位C MSR CPSR_f, R0 ;CPSRR0 例2:从管理模式切换到IRQ模式 MRS R0, CPSR ;R0CPSR BIC R0,R0,#0X1F ;低5位清零 ORR R0,R0,#0X12 ;设置为IRQ模式 MSR CPSR_c, R0 ;传送回CPSR
注意: 只有在特权模式下才能修改状态寄存器的控制域[7:0],以实现处理器模式转换,或设置开/关异常中断 。 程序中不能通过MSR指令直接修改CPSR中的T控制位来实现ARM状态/Thumb状态的切换,必须使用BX指令完成处理器状态的切换。 用户模式下不能对CPSR[23:0]做修改。
例 MRS R0,CPSR BIC R0, R0, #0x1F ; R0后5位清0 ORR R0, R0, #0x1F ; R0后5位赋值为0b11111 MSR CPSR, R0 ;根据模式位[4 : 0]切换工作模式到系统模式 MOV R13, #1 MOV R14, #2 BIC R0, R0, #0x1F ; R0后5位清0 ORR R0, R0, #0x11 ; R0后5位赋值为0b10001 MSR CPSR, R0 ;根据模式位[4 : 0]切换工作模式到FIQ模式 MOV R13, #33 MOV R14, #44 BIC R0, R0, #0x1F ; R0后5位清0 ORR R0, R0, #0x10 ; R0后5位赋值为0b10000 MSR CPSR, R0 ;根据模式位[4 : 0]切换工作模式到用户模式
3.4 跳转指令 在ARM中有两种方式可以实现程序的跳转: 一种是使用转移指令直接跳转; 另一种则是直接向PC寄存器赋值来实现跳转。 ARM的转移指令可以从当前指令向前或向后的32MB的地址空间跳转,根据完成的功能它可以分为以下4种 : B 转移指令 BL 带链接的转移指令 BX 带状态切换的转移指令 BLX 带链接和状态切换的转移指令
① B——转移指令 B指令跳转到指定的地址执行程序。 指令格式如下: B{cond} label 指令举例如下: B WAITA ;跳转到WAITA标号处 B 0x1234 ;跳转到绝对地址0x1234处 转移指令B限制在当前指令的±32 MB的范围内。
指令举例如下: 无条件跳转: B label …… label …… 执行10次循环: MOV R0, #10 LOOP: SUBS R0, #1 BNE LOOP
BL指令先将下一条指令的地址拷贝到LR 链接寄存器中,然后跳转到指定地址运行程序。 指令格式如下: BL{cond} label 指令举例如下: BL SUB1 ;LR下条指令地址 ;转至子程序SUB1处 … SUB1 … MOV PC, LR 注意:转移地址限制在当前指令的±32 MB的范围内。BL指令用于子程序调用。
指令举例如下: CMP R1, #5 BLLT ADD11 ;< BLGE SUB22 ; ≧ … ADD11: SUB22: 注:如果R1<5,只有ADD11不改变条件码,本例才能正常工作。
BL SUB1 …… SUB1 …… BL SUB2 STMFD R13!,{R0-R3,R14} SUB2 ……
③BX——带状态切换的转移指令 BX指令跳转到Rm指定的地址执行程序。若Rm的位[0]为1,则跳转时自动将CPSR中的标志T置位,即把目标地址的代码解释为Thumb代码;若Rm的位[0]为0,则跳转时自动将CPSR中的标志T复位,即把目标地址的代码解释为ARM代码。 指令格式如下: BX{cond} Rm ; Rm:该寄存器中为跳转的目标地址。当Rm寄存器的bit[0]为0时,目标地址处的指令为ARM指令;当bit[0]为1时,目标地址处的指令为Thumb指令。
指令举例如下: ADRL R0,ThumbFun+1 BX R0 ;跳转到R0指定的地址,并 根据R0的最低位来切换处 理器到Thumb状态。 ThumbFun: …
④ BLX ——带链接和状态切换的转移指令 BLX指令先将下一条指令的地址拷贝到R14 (即LR)连接寄存器中,然后跳转到指定地址处执行程序。(目前只有V5T ARM 支持BLX) 指令格式如下: BLX <target address> 转移地址限制在当前指令的±32 MB的范围内。
3.5 异常中断产生指令 异常中断指令可以分为以下几种: 软件中断指令(SWI) 断点指令(BKPT—仅用于v5T体系) 前导0计数(CLZ—仅用于v5T体系)
SWI {<cond>} <24位立即数> 说明: 软件中断指令SWI产生SWI异常中断,用来实现用户模式到特权模式的切换。用于在用户模式下对操作系统中特权模式的程序的调用;它将处理器置于管理(_svc)模式,中断矢量地址为0x08。 汇编格式如下: SWI {<cond>} <24位立即数> 说明: 主要用于用户程序调用操作系统的API。 参数传递通常有两种方法: 指令中的24bit立即数指定API号,其它参数通过寄存器传递。 忽略指令中的24bit立即数,r0指定API号,其它参数通过其它寄存器传递。
断点中断指令BKPT用于产生软件断点,供调试程序用。仅用于v5T体系。 汇编格式如下: BKPT { immed_16} immed_16:16位的立即数。该立即数被调试软件用 来保存额外的断点信息。 断点指令用于软件调试;它使处理器停止执行正常指令而进入相应的调试程序。
CLZ{<cond>} Rd, Rm 前导0计数指令CLZ 对Rm中的前导0的个数进行计数,结果放到Rd中。(仅用于v5T体系。) 汇编格式: CLZ{<cond>} Rd, Rm 举例如下: MOV R2, #0X17F00 ;R2=0b0000 0000 0000 0001 0111 1111 0000 0000 CLZ R3, R2 ;R3=15
3.6 协处理器指令 ARM支持16个协处理器,用于各种协处理器操作,最常使用的协处理器是用于控制片上功能的系统协处理器,例如控制高速缓存和存储器的管理单元,浮点ARM协处理器等,还可以开发专用的协处理器。ARM协处理器指令根据其用途主要分为以下三类: 数据操作指令; 协处理器寄存器和内存单元之间数据存/取指令; ARM寄存器与协处理器寄存器的数据传送指令。
① CDP——协处理器数据操作指令 ARM处理器通过CDP指令通知ARM协处理器执行特定的操作。协处理器数据操作完全是协处理器内部的操作,用于初始化ARM协处理器,完成协处理器寄存器的状态改变。 指令特点: 该操作由协处理器完成,即对命令参数的解释与协处理器有关,指令的使用取决于协处理器。 若协处理器不能成功地执行该操作,将产生未定义指令异常中断。
指令格式如下: 其中: CP# 指令操作的协处理器名。标准名为pn,n为0~15。 Cop1 协处理器的特定操作码。 CDP{<cond>} <CP#>,<Cop1>,CRd,CRn,CRm{,<Cop2>} 其中: CP# 指令操作的协处理器名。标准名为pn,n为0~15。 Cop1 协处理器的特定操作码。 CRd 作为目标寄存器的协处理器寄存器。 CRn 存放第1个操作数的协处理器寄存器。 CRm 存放第2个操作数的协处理器寄存器。 Cop2 可选的协处理器特定操作码。 指令举例如下: CDP p7,0,c0,c2,c3,0 ;协处理器7执行操作码1为0和可选操作码2为0的操作。 CDP p6,1,c3,c4,c5 ;协处理器6执行操作码为1的操作
② LDC/STC——协处理器数据存/取指令 协处理器数据存/取指令从存储器读取数据装入协处理器寄存器,或将协处理器寄存器的数据存入存储器。 LDC——协处理器数据读取指令 LDC指令从某一连续的内存单元将数据读取到协处理器的寄存器中。进行协处理器数据的数据传送时,由协处理器来控制传送的字数。若协处理器不能成功地执行该操作,将产生未定义指令异常中断。
指令格式如下: LDC{cond}{L} <CP#>,CRd, <地址> 其中: L 可选后缀,指明是长整数传送。 CP# 指令操作的协处理器名。标准名为pn,n为0~15。 CRd 作为目标寄存的协处理器寄存器。 <地址> 指定的内存地址。 指令举例如下: LDC p5,c2,[R2,#4] ;读取R2+4指向的内存单元的数据,传送到协处理器p5的c2寄存器中。 LDC p6,c2,[R1] ;读取R1指向的内存单元的数据,传送到协处理器p6的c2寄存器中。
STC——协处理器数据存入指令。 将协处理器的寄存器数据存入到某一连续的内存单元中,由协处理器来控制写入的字数。 若协处理器不能成功地执行该操作,将产生未定义指令异常中断。 指令格式如下: STC{cond}{L} CP#,CRd,<地址> 其中格式说明同 LDC 指令。 指令举例如下: STC p5,c1,[R0] STC p5,c1,[R0,#0x04]
③ MCR/MRC——ARM寄存器与协处理器寄存器的数据传送指令 MCR ——ARM寄存器到协处理器寄存器的数据传送指令 MCR指令将ARM处理器的寄存器中的数据传送到协处理器的寄存器中。 若协处理器不能成功地执行该操作,将产生未定义指令异常中断。 指令格式如下: MCR{cond} CP# , Cop1, Rd, CRn, CRm{, <Cop2>} 其中格式说明同 CDP 指令。 指令举例如下: MCR p14,3,R7,c7,c11,6 ;从ARM寄存器中将数据传送到协处理器p14的寄存器中,其中R7为ARM寄存器,存放源操作数;c7和c11位协处理器寄存器,为目标寄存器;操作码1为3;操作码2为6。
MRC——协处理器寄存器到ARM寄存器的数据传送指令 指令格式如下: MRC{cond} CP# , Cop1, Rd,CRn,CRm{, <Cop2>} 其中格式说明同 CDP 指令。 指令举例如下: MRC p5,2,R2,c3,c2
(四)一些基本的ARM指令功能段 4.1 算数逻辑运算指令的应用 例1: 实现乘法的指令段 MOV R0,R0,LSL #n ;R0=R0<<n;R0= R0*2n ADD R0,R0,R0,LSL #n ;R0=R0+R0*2n= R0*(2n+1) RSB R0,R0,R0,LSL #n ;R0=R0*2n-R0= R0*(2n-1)
例2:64位数据运算 假设R0和R1存放一个64位数据,R0中存放数据的低32位;R2和R3中存放另一个64位数据,R2中存放数据的低32位。 ①两个64位数据的加法运算,结果保存到R0和R1中。 ADDS R0,R0,R2 ;低32位相加,设置CPSR的C标志位。 ADC R1,R1,R3 ;高32位的带位相加 ②两个64位数据的减法运算,结果保存到R0和R1中。 SUBS R0,R0,R2 ;低32位相减,设置CPSR的C标志位。 SBC R1,R1,R3 ;高32位的带位相减
③两个64位数据的比较操作,并设置CPSR中的条件标志位。 CMP R1,R3 ;比较高32位 CMPEQ R0,R2 ;如果高32位相等,比较低32位
例3:转换内存中数据存储方式 将寄存器R0中的数据存储方式转换成另一种存储方式。指令执行前R0中数据存储方式为:R0=A,B,C,D;指令执行后R0中数据存储方式为:R0=D,C,B,A。 EOR R1,R0,R0, ROR #16 ;R1=A^C,B^D,C^A,D^B BIC R1,R1,#OxFF0000 ;R1=A^C,0,C^A,D^B MOV R0,R0,ROR #8 ;R0=D,A,B,C EOR RO,RO,R1,LSR #8 ;R0=D,C,B,A
4.2 跳转指令的应用 例1:子程序的调用 BL指令在执行跳转操作的同时保存下一条指令的地址,用于从被调用的子程序中返回。 …… BL function ;调用子程序function …… ;子程序结束后,程序将返回到这里执行 function ;子程序的程序体 MOV PC,LR ;子程序中的返回语句
例2:条件执行 实现类似于C语言中的if-else功能的代码段。下例的功能为求最大公约数。 C语言代码为: int gcb (int a,int b) { while (a!=b) { if (a>b) a=a-b; else b=b-a; } return a; 对应的ARM代码段。(代码执行前R0中存放a,R1中存放b;代码执行后R0中存放最大公约数。 gcb CMP R0,R1 ;比较a和b的大小 SUBGT R0,R0,R1 ;if(a>b) a=a-b SUBLT R1,R1,R0 ;if(b>a) b=b-a BNE gcb ;if(a!=b)跳转到gcb继续执行 MOV PC,LR ;子程序结束,返回
例3:循环语句 下面代码段实现了程序循环执行。 MOV R0,#loopcount ;初始化循环次数 loop ;循环体 …… SUBS R0,R0,#1 ;循环计数器减1,设置条件标志 BNE loop ;循环计数器不为0,跳到loop继续执行 …… ;循环计数器为0,程序继续执行
4.3 load/store指令的应用 例:链表操作 下面代码段在链表中搜索与某一数据相等的元素。链表的每个元素包括两个字,第1个字中包含一个字节数据;第2个字中包含指向下一个链表元素的指针,当这个指针为0时表示链表结束。代码执行前R0指向链表的头元素,R1中存放将要搜索的数据;代码执行后R0指向第1个匹配的元素,或者当没有匹配元素时,R0为0。 search CMP R0,#0 ;R0指针是否为空 LDRNEB R2,[R0] ;读取当前元素中的字节数据 CMPNE R1,R2 ;判断数据是否为搜索的数据 LDRNE R0,[R0,#4] ;如果不是,指针R0指向下一个元素 BNE search ;跳转到search执行 MOV PC,LR ;搜索完成,程序返回