第5章Thumb指令 在ARM体系结构中,ARM指令是32位的,具有很高的执行效率。但是对于嵌入式设备而言,其存储空间极其有限,由于每条ARM指令都要占用4字节,对存储空间的要求较高。为了压缩代码的存储,增加代码存储密度,ARM公司设计了16位的Thumb指令。因此在程序状态寄存器中有一个T标志位,用来标志处理器运行的指令类型。当处理器在执行ARM程序段时,称ARM处理器处于ARM工作状态; 当处理器在执行Thumb程序段时,称ARM处理器处于Thumb工作状态。 严格来讲,Thumb指令集是ARM指令集的一个子集,所有的Thumb指令都有对应的ARM指令,在应用程序的编写过程中,只要遵循一定调用的规则,Thumb子程序和ARM子程序就可以互相调用。 编程时,可根据任务需求和存储资源合理地选择应用哪种类型的指令: 若对系统的性能有较高要求,应使用32位的存储系统和ARM指令集; 若对系统的成本及功耗有较高要求,则应使用16位的存储系统和Thumb指令集。本章将对Thumb指令做详细介绍。 5.1Thumb数据处理指令 Thumb数据处理操作包括数据传送、算术运算、逻辑运算、移位操作和比较操作等。具体的Thumb数据处理指令如表51所示。 表51Thumb数据处理指令 指 令 类 型汇编语法格式操作 数据传送指令 MOV Rd,#exprRd←expr,Rd为R0~R7 MOV Rd,RmRd←Rm, Rd、Rm均可为R0~R7 数据非传送指令MVN Rd,RmRd←(~Rm),Rd、Rm均为R0~R7 数据取负指令NEG Rd,RmRd←(-Rm),Rd、Rm均为R0~R7 续表 指 令 类 型汇编语法格式操作 加法运算指令 ADD Rd,Rn,RmRd←Rn+Rm,Rd、Rn、Rm为R0~R7 ADD Rd,Rn,#expr3Rd←Rn+expr3,Rd、Rn均为R0~R7 ADD Rd,#expr8Rd←Rd+expr8,Rd为R0~R7 ADD Rd,RmRd←Rd+Rm,Rd、Rm均可为R0~R15 SP/PC加法运算指令ADD Rd,Rp,#exprRd←SP+expr或PC+expr,Rd为R0~R7 SP加法运算指令ADD SP,#exprSP←SP+expr 减法运算指令 SUB Rd,Rn,RmRd←Rn-Rm,Rd、Rn、Rm均为R0~R7 SUB Rd,Rn,#expr3Rd←Rn-expr3,Rd、Rn均为R0~R7 SUB Rd,#expr8Rd←Rn-expr8,Rd为R0~R7 SP减法运算指令SUB SP,#exprSP←SP-expr 带进位加法指令ADC Rd,RmRd←Rd+Rm+Carry,Rd、Rm为R0~R7 带进位减法指令SBC Rd,RmRd←Rd-Rm-(NOT)Carry,Rd、Rm为R0~R7 乘法运算指令MUL Rd,RmRd←Rd*Rm,Rd、Rm为R0~R7 逻辑“与”操作指令AND Rd,RmRd←Rd & Rm,Rd、Rm为R0~R7 逻辑“或”操作指令ORR Rd,RmRd←Rd | Rm,Rd、Rm为R0~R7 逻辑“异或”操作指令EOR Rd,RmRd←Rd ∧ Rm,Rd、Rm为R0~R7 位清除指令BIC Rd,RmRd←Rd & (~Rm),Rd、Rm为R0~R7 算术右移指令 ASR Rd,RsRd←Rd算术右移Rs位,Rd、Rs为R0~R7 ASR Rd,Rm,#exprRd←Rm算术右移expr位,Rd、Rm为R0~R7 逻辑左移指令 LSL Rd, RsRd←Rd<>Rs, Rd、Rs为R0~R7 LSR Rd, Rm, #exprRd←Rm>>expr, Rd、Rm为R0~R7 循环右移指令ROR Rd, RsRd←Rd循环右移Rs位,Rd、Rs为R0~R7 比较指令 CMP Rn, Rm标识状态←Rn-Rm, Rn、Rm均可为R0~R7 CMP Rn, #expr标识状态←Rn-expr, Rn为R0~R7 负数比较指令CMN Rn, Rm标识状态←Rn+Rm, Rn、Rm为R0~R7 位测试指令TST Rn, Rm标识状态←Rn & Rm, Rn、Rm为R0~R7 5.1.1寄存器移位指令 Thumb指令将移位操作作为独立的指令进行操作,可以对寄存器内容直接进行移位操作。 这类指令的汇编语法格式为: opcode Rd, Rs, #shift_immed_5 Thumb寄存器移位指令16位编码格式如下: 其中: Rd: 目标寄存器; Rs: 源操作数寄存器; shift_immed_5: 寄存器移位的数值,取值范围为0~31; opcode: 指令操作码,其编码类型如表52所示。 表52Thumb寄存器移位指令操作码编码类型 opcodeThumb汇编语法格式实现的操作 00LSL Rd,Rs,#shift_immed_5将Rs逻辑左移shift_immed_5位,并将结果传送到Rd中 01LSR Rd,Rs,#shift_immed_5将Rs逻辑右移shift_immed_5位,并将结果传送到Rd中 10ASR Rd,Rs,#shift_immed_5将Rs算术右移shift_immed_5位,并将结果传送到Rd中 注意事项:  Rd、Rs必须在R0~R7中选取。  指令执行结果影响N、Z、C标志位。 【例51】指令功能解析。 LSR R2, R5, #10; 将R5逻辑右移10位,并将结果传送到R2中,并影响N、Z、C标志位 LSL R0, R3, #8; 将R3逻辑左移8位,并将结果传送到R0中,并影响N、Z、C标志位 ASR R1, R2, #3; 将R2算术右移3位,并将结果传送到R1中,并影响N、Z、C标志位 5.1.2低位寄存器算术运算指令 低位寄存器算术运算指令所操作的寄存器选取范围必须在R0~R7中选取。 1. 加法与减法运算指令(对R0~R7操作) 这类指令的汇编语法格式为: opcode Rd,Rs,Rm opcode Rd,Rs,#immed_3 Thumb加法减法运算指令16位编码格式如下: 其中: Rd: 目标寄存器; Rs: 源操作数寄存器; Rm: 第二操作数寄存器; immed_3: 第二操作数为立即数; I: 决定第二操作数是寄存器还是立即数; opcode: 指令操作码,其编码类型如表53所示。 表53Thumb加法与减法运算指令操作码编码类型 opcodeIThumb汇编语法格式实现的操作 00ADDRd,Rs,RmRd←Rs+Rm 0 1ADDRd,Rs,#immed_3Rd←Rs+immed_3 10SUBRd,Rs,RmRd←Rn-Rm 11SUBRd,Rs,#immed_3Rd←Rn-immed_3 注意事项:  Rd、Rs、Rm必须在R0~R7中选取。  指令执行结果影响N、Z、C、V标志位。 【例52】指令功能解析。 ADD R0, R3, R2; R0←R3 + R2,指令执行结果影响N、Z、C、V标志位 SUB R1, R2, #4 ; R1←R2 - 4,指令执行结果影响N、Z、C、V标志位 2. MOV、CMP、ADD与SUB指令(对R0~R7操作) 这类指令的汇编语法格式为: opcode Rd, # immed_8 Thumb MOV、CMP、ADD与SUB指令16位编码格式如下: 其中: immed_8: 寄存器移位的数值; Rd: 目标寄存器; opcode: 指令操作码,其编码类型如表54所示。 表54Thumb MOV、CMP、ADD与SUB指令操作码编码类型 opcodeThumb汇编语法格式实现的操作 0 0MOVRd,# immed_8Rd←immed_8 01CMPRd,# immed_8标识状态←Rd-immed_8 10ADDRd,# immed_8Rd←Rd+immed_8 11SUBRd,# immed_8Rd←Rn-immed_8 注意事项:  Rd必须在R0~R7中选取。  MOV指令执行结果影响N、Z标志位,CMP、ADD、SUB指令执行结果影响N、Z、C、V标志位。 【例53】指令功能解析。 MOV R1, #50; R0←50,指令执行结果影响标志位 CMP R2, #0x30; 根据R2 - 0x30的结果来影响标志位 ADD R1, #0xAA; R1←R1 + 0xAA,指令执行结果影响标志位 SUB R6, #0xAA ; R6←R6 - 0xAA,指令执行结果影响标志位 5.1.3ALU操作指令 这类指令的汇编语法格式为: opcode Rd, Rs 对应的16位Thumb指令编码格式如下: 其中: Rd: 目标寄存器; Rs: 源操作数寄存器; opcode: 指令操作码,其编码类型如表55所示。 表55Thumb ALU操作指令操作码编码类型 opcodeThumb汇编语法格式实现的操作 0000ANDRd,RsRd←Rd & Rs 0001EORRd,RsRd←Rd ∧ Rs 0010LSL Rd,RsRd←Rd<>Rs 0100ASR Rd,RsRd←Rd算术右移Rs位 0101ADC Rd,RsRd←Rd+Rs+Carry 0110SBC Rd,RsRd←Rd-Rs-(NOT)Carry 0111ROR Rd,RsRd←Rd循环右移Rs位 1000TST Rd,Rs标识状态←Rd & Rs 1001NEG Rd,RsRd←(-Rs) 1010CMP Rd,Rs标识状态←Rn-Rs 1011CMN Rd,Rs标识状态←Rn+Rs 1100ORR Rd,RsRd←Rd | Rs 1101MUL Rd,RsRd←Rd * Rs 1110BICRd,RsRd←Rd & (~Rs) 1111MVN Rd,RsRd←(~Rs) 注意事项:  Rd、Rs必须在R0~R7中选取。  AND、EOR、ORR、MUL、BIC、MVN指令执行结果影响N、Z标志位。  LSL、LSR、ASR、ROR指令执行结果影响N、Z、C标志位。  ADC、SBC、TST、NEG、CMP、CMN指令执行结果影响N、Z、C、V标志位。 【例54】指令功能解析。 EOR R3, R4 ; R3←R3 EOR R4,指令执行结果影响标志位 ROR R1, R0 ; R1←R1循环右移R0位,指令执行结果影响标志位 MUL R0, R7 ; R0←R7 * R0,指令执行结果影响标志位 NEG R5, R3; R5←(-R3),指令执行结果影响标志位 CMP R2, R6 ; 根据R2 - R6 来影响标志位 5.1.4带高位寄存器操作的Thumb指令 带高位寄存器操作的Thumb指令所操作的寄存器可以在R8~R15中选取。 这类指令的汇编语法格式为: opcodeRd, Hs opcodeHd, Rs opcodeHd, Hs 对应的16位Thumb指令编码格式如下: 其中: Rd: 目标寄存器,寄存器在R0~R7中选取; Hd: 目标寄存器; 寄存器在R8~R15中选取; Rs: 源操作数寄存器; 寄存器在R0~R7中选取; Hs: 源操作数寄存器; 寄存器在R8~R15中选取; opcode: 指令操作码,其编码类型如表56所示。 表56带高位寄存器操作的Thumb指令操作码编码类型 opcodeH1H2Thumb汇编语法格式实现的操作 0001ADD Rd,HsRd←Rd+Hs 0010ADD Hd,RsHd←Hd+Rs 0011ADD Hd,HsHd←Hd+Hs 0101CMP Rd,Hs影响标识状态←Rd-Hs 0110CMP Hd,Rs影响标识状态←Hd-Rs 0111CMP Hd,Hs影响标识状态←Hd-Hs 1001MOV Rd,HsRd←Hs 1010MOV Hd,RsHd←Rs 1011MOV Hd,HsHd←Hs 注意事项:  带高位寄存器的Thumb中,ADD、MOV指令不影响标志位。  CMP指令执行结果影响N、Z、C、V标志位。 【例55】指令功能解析。 ADD PC, R1; PC←PC + R1,指令执行结果不影响标志位 MOV R13, R14 ; R13←R14,指令执行结果不影响标志位 CMPR4, R12 ; 根据R4 - R12来影响标志位 CMP PC, R12; 根据PC - R12来影响标志位 5.1.5带SP/PC的算术运算指令 带SP/PC的算术运算指令的操作数中包含SP或PC,分为两种类型: SP/PC加法运算指令、SP加法/减法运算指令。 1. SP/PC加法运算指令 这类指令的汇编语法格式为: ADDRd,PC,#immed_8 × 4 ADDRd,SP,#immed_8 × 4 对应的16位Thumb指令编码格式如下: 其中: immed_8: 偏移量,它是一个无符号的立即数表达式,取值范围是0~255; Rd: 目标寄存器; X: 区别SP、PC操作,其编码类型如表57所示。 表57Thumb SP/PC加法运算指令操作码编码类型 XThumb汇编语法格式实现的操作 0ADD Rd,PC,#immed_8 × 4Rd←PC+ immed_8 × 4 1ADD Rd,SP,#immed_8 × 4Rd←SP+ immed_8 × 4 注意事项:  寄存器Rd必须在R0~R7中选取;  指令执行结果不影响标志位。 【例56】指令功能解析。 ADDR1,PC,#0x40; R1←PC + 0x40,指令执行结果不影响标志位 ADDR1,SP,#576; R1←SP +576,指令执行结果不影响标志位 2. SP加法/减法运算指令 这类指令的汇编语法格式为: ADDSP,#immed_7 × 4 SUBSP,#immed_7 × 4 对应的16位Thumb指令编码格式如下: 其中: SP: 目标寄存器; immed_7: 偏移量,它是一个无符号的立即数表达式,取值范围是0~127; S: 用于区别ADD、SUB操作,其编码类型如表58所示。 表58Thumb SP加法/减法运算指令操作码编码类型 SThumb汇编语法格式实现的操作 0ADD SP,#immed_7 × 4SP←PC+immed_7 × 4 1SUB SP,#immed_7 × 4SP←SP-immed_7 × 4 注意事项: 指令执行结果不影响标志位。 【例57】指令功能解析。 ADDSP,#0x40; SP←SP+0x40,指令执行结果不影响标志位 SUBSP,#0x20; SP←SP-0x20,指令执行结果不影响标志位 5.2Thumb存储器操作指令 Thumb存储器操作指令包括无符号字节、半字和字数据的存储与加载,多寄存器的加载与存储指令和栈操作指令。对存储器操作的Thumb指令如表59所示。 表59对存储器操作的Thumb指令 汇编语法格式说明操作 LDR Rd,[Rn,#immed_5×4]加载字数据Rd←[Rn,#immed_5×4],Rd、Rn为R0~R7 LDRHRd,[Rn,#immed_5×2]加载无符号半字数据Rd←[Rn,#immed_5×2],Rd、Rn为R0~R7 LDRBRd,[Rn,#immed_5×1]加载无符号字节数据Rd←[Rn,#immed_5×1],Rd、Rn为R0~R7 STR Rd,[Rn,#immed_5×4]存储字数据[Rn,#immed_5×4]←Rd,Rd、Rn为R0~R7 STRHRd,[Rn,#immed_5×2]存储无符号半字数据[Rn,#immed_5×2]←Rd,Rd、Rn为R0~R7 STRBRd,[Rn,#immed_5×1]加载无符号字节数据[Rn,#immed_5×1]←Rd,Rd、Rn为R0~R7 LDRRd,[Rn,Rm]加载字数据Rd←[Rn,Rm],Rd、Rn、Rm为R0~R7 续表 汇编语法格式说明操作 LDRH Rd,[Rn,Rm]加载无符号半字数据Rd←[Rn,Rm],Rd、Rn、Rm为R0~R7 LDRB Rd,[Rn,Rm]加载无符号字节数据Rd←[Rn,Rm],Rd、Rn、Rm为R0~R7 LDRSHRd,[Rn,Rm]加载有符号半字数据Rd←[Rn,Rm],Rd、Rn、Rm为R0~R7 LDRSBRd,[Rn,Rm]加载有符号字节数据Rd←[Rn,Rm],Rd、Rn、Rm为R0~R7 STRRd,[Rn,Rm]存储字数据[Rn,Rm]←Rd,Rd、Rn、Rm为R0~R7 STRHRd,[Rn,Rm]存储无符号半字数据[Rn,Rm]←Rd,Rd、Rn、Rm为R0~R7 STRBRd,[Rn,Rm]存储无符号字节数据[Rn,Rm]←Rd,Rd、Rn、Rm为R0~R7 LDR Rd,[Pc,#immed_8×4]基于PC加载字数据Rd←[PC,#immed_8×4],Rd为R0~R7 LDR Rd,label基于PC加载字数据Rd←[label],Rd为R0~R7 LDR Rd,[SP,#immed_8×4]基于SP加载字数据Rd←[SP,#immed_8×4],Rd为R0~R7 STR Rd,[SP,#immed_8×4]基于SP存储字数据[SP,#immed_8×4]←Rd,Rd为R0~R7 LDMIARn{!},reglist多寄存器加载Reglist←[Rn…],Rn回写等(R0~R7) STMIARn{!},reglist多寄存器存储[Rn…]←reglist,Rn回写等(R0~R7) PUSH{reglist,LR}寄存器入栈指令[SP…]←reglist,LR,SP回写等(R0~R7、LR) POP{reglist,PC}寄存器出栈指令reglist,PC←[SP…],SP回写等(R0~R7、PC) 5.2.1字节、半字和字的加载/存储指令 1. 立即数偏移量的加载/存储指令 这类指令的汇编语法格式为: LDR{B}Rd,[Rn,#immed_5 × 4] STR{B}Rd,[Rn,#immed_5 × 4] 对应的16位Thumb指令编码格式如下: 其中: Rd: 目标/源寄存器; Rn: 基址寄存器; immed_5: 偏移量,它是一个无符号的立即数表达式,取值范围是0~31; L: 区分是LDR指令还是STR指令; B: 区分存储的是无符号的字节数据还是字数据。 根据L、B的编码的不同,所对应的汇编指令如表510所示。 表510立即数偏移量的加载/存储指令 LBThumb汇编语法格式实现的操作 00STRRd,[Rn,#immed_5 × 4][Rn+immed_5×4]←Rd 10LDRRd,[Rn,#immed_5 × 4]Rd←[Rn+immed_5×4]字数据单元 01STRB Rd,[Rn,#immed_5 × 1][Rn+immed_5×1]←Rd低8位 11LDRB Rd,[Rn,#immed_5 × 1]Rd←[Rn+immed_5×1]字节数据单元 注意事项:  Rd、Rn寄存器在R0~R7中选取。  Thumb立即数偏移量的加载/存储指令不影响标志位。 【例58】指令功能解析。 LDR R2, [R5,#116]; 将内存地址R5+116开始的字数据单元加载到R2中 LDRB R0, [R3,#8] ; 将内存地址为R3+8的字节数据单元加载到R0中,高24位清零 STR R1, [R0,#13] ; 将R1存储到内存地址为R0+13开始的字数据单元 STRB R1, [R0,#13] ; 将R1的低8位存储到内存地址为R0+13的字节数据单元 2. 寄存器偏移量的加载/存储指令 寄存器偏移量的加载/存储指令的汇编语法格式为: LDR{B}Rd,[Rn,Rm] STR{B}Rd,[Rn,Rm] 对应的16位Thumb指令编码格式如下: 其中: Rd: 目标/源寄存器; Rn: 基址寄存器; Rm: 偏移量寄存器; L: 区分是LDR指令还是STR指令; B: 区分存储的是无符号的字节数据还是字数据。 根据L、B的编码的不同,所对应的汇编指令如表511所示。 表511Thumb寄存器偏移量的加载/存储指令 LBThumb汇编语法格式实现的操作 00STRRd,[Rn,Rm][Rn+Rm]←Rd 10LDRRd,[Rn,Rm]Rd←[Rn+Rm]字数据单元 01STRB Rd,[Rn,Rm][Rn+Rm]←Rd低8位 11LDRB Rd,[Rn,Rm]Rd←[Rn+Rm]字节数据单元 注意事项:  Rd、Rn、Rm寄存器在R0~R7中选取。  Thumb寄存器偏移量的加载/存储指令不影响标志位。 【例59】指令功能解析。 LDR R2, [R5,R0] ; 将内存地址R5+R0开始的字数据单元加载到R2中 LDRB R0, [R3,R1] ; 将内存地址为R3+R1的字节数据单元加载到R0中 STR R1, [R0,R2] ; 将R1存储到内存地址为R0+R2开始的字数据单元 STRB R1, [R0,R2] ; 将R1的低8位存储到内存地址为R0+R2的字节数据单元 3. 有符号字节/半字的加载/存储指令 有符号字节/半字的加载/存储指令的汇编语法格式为: LDRHRd,[Rn,Rm] STRHRd,[Rn,Rm] LDRSBRd,[Rn,Rm] LDRSHRd,[Rn,Rm] 对应的16位Thumb指令编码格式如下: 其中: Rd: 目标/源寄存器; Rn: 基址寄存器; Rm: 偏移量寄存器; S: 区分是对有符号数操作还是对无符号数操作; H: 区分是对有符号字节数据操作还是对半字数据操作。 根据S、H的编码的不同,所对应的汇编指令如表512所示。 表512Thumb有符号字节/半字的加载/存储指令 SHThumb汇编语法格式实现的操作 00STRHRd, [Rn,Rm][Rn+Rm]←Rd的低16位 01LDRHRd,[Rn,Rm]Rd←[Rn,Rm] 半字数据单元,Rd高16位清零 10LDRSBRd,[Rn,Rm]Rd←[Rn,Rm] 字节数据单元,Rd高24位扩展为符号位 11LDRSHRd,[Rn,Rm]Rd←[Rn,Rm] 半字数据单元,Rd高16位扩展为符号位 注意事项:  Rd、Rn、Rm寄存器在R0~R7中选取。  指令执行不影响标志位。 【例510】指令功能解析。 LDRH R2, [R5,R0] ; 将内存地址R5+R0开始的半字数据单元加载到R2中,R2的高16位清零 LDRSH R0, [R3,R1] ; 将内存地址为R3+R1的半字数据单元加载到R0中,R0的高16位用 ; 符号位扩展 STRH R1, [R0,R2] ; 将R1的低16位存储到内存地址为R0+R2开始的半字数据单元 LDRSB R1, [R0,R2] ; 将内存地址为R0+R2的字节数据单元加载到R1中,R1的高24位用 ; 符号位扩展 4. 偏移量为立即数的半字加载/存储指令 偏移量为立即数的半字加载/存储指令的汇编语法格式为: LDRH Rd,[Rn,#immed_5 × 2] STRH Rd,[Rn,#immed_5 × 2] 对应的16位Thumb指令编码格式如下: 其中: Rd: 目标/源寄存器; Rn: 基址寄存器; immed_5: 偏移量,它是一个无符号的立即数表达式,取值范围是0~31; L: 区分是LDR指令还是STR指令。 根据L的编码的不同,所对应的汇编指令如表513所示。 表513偏移量为立即数的半字操作指令 LThumb汇编语法格式实现的操作 0STRHRd, [Rn,# immed_5 × 2][Rn+immed_5 × 2]←Rd的低16位 1LDRHRd,[Rn,#immed_5 × 2]Rd←[Rn,immed_5 × 2] 半字数据单元,Rd高16位清零 注意事项:  Rd、Rn寄存器在R0~R7中选取。  指令执行不影响标志位。 【例511】指令功能解析。 LDRH R2, [R5, #0x10] ; 将内存地址R5+0x10开始的半字数据单元加载到R2中 ; R2的高16位清零 STRH R1, [R0, #0x20]; 将R1的低16位存储到内存地址为R0+0x20开始的半字 ; 数据单元 5. PC作为基址寄存器的字加载指令 PC作为基址寄存器的加载指令的汇编语法格式为: LDR Rd, [PC,# immed_8×4] 对应的16位Thumb指令编码格式如下: 其中: immed_8: 偏移量,它是一个8位的无符号的立即数表达式,取值范围是0~255; Rd: 目标/源寄存器; 功能描述: 将内存地址PC + immed_8×4为基地址的字数据单元加载到Rd中。 注意事项:  Rd寄存器在R0~R7中选取。  指令执行不影响标志位。 【例512】指令功能解析。 LDR R2, [PC, #0x10] ; 将内存地址PC + 0x10开始的字数据单元加载到R2中 LDR R0, [R15, #0x40]; 将内存地址PC + 0x40开始的字数据单元加载到R0中 6. SP作为基址寄存器的加载/存储指令 SP作为基址寄存器的加载/存储指令的汇编语法格式为: LDRRd, [SP,# immed_8×4] STRRd, [SP,# immed_8×4] 对应的16位Thumb指令编码格式如下: 其中: immed_8: 偏移量,它是8位的一个无符号的立即数表达式,取值范围是0~255; Rd: 目标/源寄存器; L: 区分是LDR指令还是STR指令。 根据L的编码的不同,所对应的汇编指令如表514所示。 表514SP作为基址寄存器的加载/存储指令 LThumb汇编语法格式实现的操作 0STRRd, [SP,#immed_8 × 2][SP+immed_8 × 4]←Rd 1LDRRd,[SP,#immed_8 × 2]Rd←[SP+immed_8 × 4] 字数据单元 注意事项:  Rd 寄存器在R0~R7中选取。  指令执行不影响标志位。 【例513】指令功能解析。 LDR R2, [SP, #0x10] ; 将内存地址SP+0x10开始的字数据单元加载到R2中 STR R1, [SP, #0x20] ; 将R1存储到内存地址为SP+0x20开始的字数据单元 5.2.2批量加载/存储指令 1. 寄存器入栈出栈指令 ARM处理器进行异常处理服务或子程序调用时,可使用寄存器入栈指令PUSH和寄存器出栈指令POP来保存和恢复现场信息。堆栈地址由SP寄存器设置,堆栈是满递减类型。这类指令的汇编语法格式为: PUSH{reglist} POP {reglist} PUSH{reglist, LR} POP{reglist, PC} 对应的16位Thumb指令编码格式如下: 其中: reglist: 入栈/出栈寄存器列表; R: 程序计数器PC和链接寄存器LR是否出现在寄存器列表中,由指令编码的该位来决定; L: 区分是PUSH指令还是POP指令。 根据L、R的编码的不同,所对应的汇编指令如表515所示。 表515Thumb寄存器入栈、出栈指令 LRThumb汇编语法格式实现的操作 00PUSH{ reglist }将reglist中的寄存器顺序入栈,并更新SP 即[SP…]←reglist,SP回写 01PUSH { reglist,LR }将reglist中的寄存器和LR顺序入栈,并更新SP 即[SP…]←reglist,LR,SP回写 10POP{ reglist }将堆栈中的数据顺序弹出到reglist中的寄存器中,并更新SP。即reglist←[SP…] ,SP回写 11POP{ reglist,PC }将堆栈中的数据顺序弹出到reglist中的寄存器和PC中,并更新SP。即reglist,PC←[SP…] ,SP回写 注意事项:  reglist 为低编号寄存器(R0~R7)的全部或子集,编号低的寄存器对应内存低地址单元。  指令执行不影响标志位。 【例514】指令功能解析。 PUSH{R0R3,R5}; 将寄存器R0、R1、R2、R3、R5压栈 POP{R0R3,R5}; 将堆栈中的数据弹出到寄存器R0、R1、R2、R3、R5中 PUSH{R0R3,R5,LR}; 将寄存器R0、R1、R2、R3、R5和LR顺序压栈 POP{R0R3,R5,PC}; 将堆栈中的数据弹出到寄存器R0、R1、R2、R3、R5和PC中 2. 多寄存器加载/存储指令 在Thumb多寄存器加载/存储指令中,只使用后增的形式(IA),可以将低编号寄存器(R0~R7)的全部或子集存储到内存中,还可以将内存中的数据加载到低编号寄存器(R0~R7)的全部或子集中。这类指令的汇编语法格式为: LDMIARn! {reglist} STMIARn! {reglist} 对应的16位Thumb指令编码格式如下: 其中: reglist: 入栈/出栈寄存器列表; L: 区分是LDM指令还是STM指令。 根据L的编码的不同,所对应的汇编指令如表516所示。 表516Thumb多寄存器加载/存储指令 LThumb汇编语法格式实现的操作 0STMIA Rn!, { reglist }将reglist中的寄存器以后增(IA)的方式顺序存储到以Rn为基地址的内存单元中,并更新Rn。即[Rn…]←reglist, Rn回写 1LDMIA Rn!, { reglist }将以Rn为基地址的内存单元数据以后增(IA)的方式顺序加载到reglist中的寄存器中, 并更新Rn。即reglist←[Rn…] , Rn回写 注意事项:  reglist 为低编号寄存器(R0~R7)的全部或子集,编号低的寄存器对应内存低地址单元。  指令执行不影响标志位。 【例515】指令功能解析。 STMIAR4!,{R0R3,R5}; 将R0、R1、R2、R3、R5中的数据以后增的方式顺序 ; 存储到以R4为基地址的内存单元中,并更新R4 LDMIAR4!,{R0R3,R5}; 将以R4为基地址的内存单元数据以后增的方式顺序 ; 加载到R0、R1、R2、R3、R5中, 并更新R4 5.3Thumb分支指令 与ARM指令一样,在Thumb指令集中,也有相应的分支指令实现程序的跳转和子程序的调用。Thumb分支指令可分为B分支指令、带链接的分支指令和带状态切换的分支指令。 5.3.1B分支指令 在Thumb指令中,B分支指令又分为条件分支指令和无条件分支指令。 1. 条件分支指令 当指令执行的条件成立时,条件分支指令跳转到指定的地址执行程序。这是Thumb指令中唯一的有条件执行指令。这类指令的汇编语法格式为: B{cond}label 对应的16位Thumb指令编码格式如下: 其中: signed_immed_8: 8位有符号的立即数,由汇编语法格式中的label右移一位得到; cond: 指令执行的条件码。 根据cond的编码的不同,所对应的标志位及描述如表517所示。 表517Thumb分支指令对应的标志位及描述 条件码条件码助记符描述PSR中的标志位 0000EQ相等Z=1 0001NE不相等Z=0 0010CS无符号大于或等于C=1 0011CC无等号小于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 【例516】指令功能解析。 CMPR0,#0x10 BEQstop; 当R0等于16时,程序跳转到stop标号处 … stop … … 2. 无条件分支指令 这类指令的汇编语法格式为: Blabel 对应的16位Thumb指令编码格式如下: 其中, signed_immed_11为11位有符号的立即数,由汇编语法格式中的label右移一位得到,程序可以跳转为-2KB~2KB。 【例517】指令功能解析。 Bstop; 程序无条件跳转到stop标号处 … stop: … … 5.3.2带链接的分支指令 带链接的分支指令BL将下一条指令的地址拷贝到R14(LR)链接寄存器中,然后跳转到指定的地址运行程序。指令的汇编语法格式为: BLlabel 对应的16位Thumb指令编码格式如下: 其中: immed_11: 11位立即数,作为长跳转的高11位/低11位; H: 区别+/-4MB范围的高11位偏移或低11位偏移,其对应的汇编指令如表518 所示。 表518Thumb带链接的分支指令 HThumb汇编语法格式实现的操作 0LR←PC+高11位偏移量<<12 BLlabelPC←LR+低11位偏移量<<1 1LR←下一条指令地址|1 由于BL指令通常需要大的地址范围,无法用一条16位的指令格式来实现,因此,Thumb指令采用两条指令来实现此功能。具体做法是: 两条指令中的immed_11组合成22位的偏移量,并有符号扩展为32位,使指令转移范围为-4MB~+4MB。 注意事项:  BL指令为无条件执行指令。  带链接的分支指令BL提供了一种在Thumb状态下程序间相互调用的方法,当从子程序返回时,通常使用下面的方式之一: MOVPC,LR BXLR POP{PC} 5.3.3带状态切换的分支指令 带状态切换的分支指令的汇编语法格式为: BXRs BXHs Thumb寄存器移位指令16位编码格式如下: 其中: Rs: 源操作数寄存器; 寄存器在R0~R7中选取; Hs: 源操作数寄存器; 寄存器在R7~R15中选取; H: 区分源操作数寄存器是高编号寄存器还是低编号寄存器,其对应的汇编指令如表519所示。 表519Thumb带状态切换的分支指令 H2Thumb汇编语法格式实现的操作 0BX RsPC←Rs,切换处理器状态 1BX HsPC←Hs,切换处理器状态 注意事项: 如果R[1:0]=0b10,处理器切换为ARM状态时,指令的执行结果不可预知。 【例518】指令功能解析。 ADRR1, go_to_ARM BXR1; 程序跳转到go_to_ARM标号处,处理器切换到ARM状态 … ALIGN CODE32 go_to_ARM … 5.4Thumb软中断指令 Thumb软中断指令SWI与ARM指令集下的软中断指令相似,用于使处理器产生软件异常,使用这种机制实现在用户模式下对操作系统中特权模式的程序调用。SWI指令的汇编语法格式为: SWIimmed_8 SWI指令16位编码格式如下: 其中, immed_8是8位无符号的立即数,它是软中断的请求号。 注意事项:  进行SWI软中断时,处理器切换到ARM状态,并处于管理模式,CPSR保存到管理模式的SPSR中,程序跳转到地址0x08处(SWI中断向量地址)。  在执行SWI时,immed_8被处理器忽略,但在指令编码中,它用来确定所请求的服务号。  SWI指令执行不影响条件标志位。 【例519】指令功能解析。 SWI0x10; 产生软中断,所请求的软中断服务号为16 5.5Thumb指令功能码段分析 5.5.1Thumb与ARM实现功能比较 1. 实现乘法的功能段 1) 与 2n (1,2,4,8,…)相乘 Thumb : LSL Ra, Rb, LSL #n ARM: MOV Ra, Rb, LSL #n 2) 与 2n +1(3,5,9,17,…)相乘 Thumb : LSL Rt, Rb, #n ADD Ra, Rt, Rb ARM: ADD Ra, Rb, Rb, LSL #n 3) 与2n-1(3,7,15,…) 相乘 Thumb : LSL Rt, Rb, #n SUB Ra, Rt, Rb ARM: RSB Ra, Rb, Rb, LSL #n 4) 与-2n(-2, -4, -8, …)相乘 Thumb : LSL Ra, Rb, #n MVN Ra, Ra ARM: MOV Ra, Rb, LSL #n RSB Ra, Ra, #0 5) 与-2n+1(-1,-3, -7, -15, …)相乘 Thumb : LSL Rt, Rb, #n SUB Ra, Rb, Rt ARM: SUB Ra, Rb, Rb, LSL #n 2. 实现除法的功能段 下面的ARM代码实现除法运算,R0用来存放被除数,R1用来存放除数,R2用来存放两数除法运算结果的商,R3用来存放两数除法运算结果的余数。 MOVR0,#0x0F; (4字节) MOV R1,#0x06; (4字节) MOV R2,#0x0; (4字节) Loop SUBSR0,R0,R1; (4字节) ADDGE R2,R2,#1; (4字节) BGELoop; (4字节) ADDR3,R0,R1; (4字节) 上面程序代码的7条ARM指令占用的存储空间大小为 7×4字节=28字节 如果用Thumb代码来实现相同的功能,程序代码如下: MOVR0,#0x0F; (2字节) MOV R1,#0x06; (2字节) MOV R2,#0x0; (2字节) Loop ADDR2,#1; (2字节) SUBR0,R1; (2字节) BGELoop; (2字节) SUBR2,#1; (2字节) ADDR3,R0,R1; (2字节) 上面程序代码的8条Thumb指令占用的存储空间大小为8×2字节=16字节,可见Thumb代码在存储密度上高于ARM代码。 5.5.2Thumb与ARM性能比较 Thumb指令具有较高的代码密度,因为Thumb指令的长度为16位,即只用ARM指令一半的位数来实现同样的功能。但是,要实现特定的程序功能,一般所需的Thumb指令的条数较ARM指令多。在一般的情况下,Thumb指令与ARM指令的时间效率和空间效率关系为:  Thumb代码所需的存储空间为ARM代码的60%~70%。  Thumb代码使用的指令数比ARM代码多30%~40%。  若使用32位的存储器,ARM代码比Thumb代码快约40%。  若使用16位的存储器,Thumb代码比ARM代码快40%~50%。  与ARM代码相比较,使用Thumb代码,存储器的功耗会降低约30%。 在实际应用中,一般将二者结合起来,也就是ARM、Thumb混合编程,充分发挥各自的优势。 思考与练习题 1. 与32位的ARM指令集相比较,16位的Thumb指令集具有哪些优势? 2. Thumb指令可分为哪几类?Thumb指令有条件执行指令吗?如果有,请说明哪些指令是条件执行的。 3. 分析下面的Thumb指令程序代码,指出程序所完成的功能。 .global _start .text .equnum,20 _start: .arm MOVSP, #0x400 ADRR0, Thumb_start + 1 BXR0 .thumb Thumb_start: ASRR2, R0, #31 EORR0, R2 SUBR3, R0, R2 stop: Bstop .end 4. 用多种方法实现将寄存器R0 中的数据乘以10。 5. 带链接的分支指令BL提供了一种在Thumb状态下程序间相互调用的方法,当从子程序返回时,可以采用哪几种返回方式? 6. 指出下列的Thumb程序代码所完成的功能: ASR R0, R1, #31 EOR R1, R0 SUB R1, R0