Chapter4 Introduction to the Thumb Instruction Set

本章介绍了Thumb指令集。Thumb将32位ARM指令的子集编码为16位指令集空间。由于在具有16位数据总线的处理器上,Thumb性能优于ARM,但在32位数据总线上性能低于ARM,因此在内存受限的系统中使用Thumb。
Thumb具有更高的代码密度,即可执行程序在内存中占用的空间比ARM少。对于内存受限的嵌入式系统,例如移动电话和个人数码助理(PDA),代码密度非常重要。成本压力还限制了内存大小、宽度和速度。

平均而言,相同代码的Thumb实现占用的内存量比等效的ARM实现少约30%。例如,图4.1展示了在ARM和Thumb汇编代码中实现的相同除法代码例程。尽管Thumb实现使用了更多指令,但整体内存占用减少了。
代码密度是推动Thumb指令集开发的主要动力。由于它也被设计为编译器的目标,而不是手写汇编代码的目标,我们建议您使用高级语言(如C或C++)编写针对Thumb的代码。


每个Thumb指令与一个32位ARM指令相关。图4.2显示了简单的Thumb ADD指令如何解码为等效的ARM ADD指令。表4.1提供了在ARMv5TE架构中使用的THUMBv2架构中可用的完整Thumb指令列表。只有相对分支指令可以有条件地执行。16位中有限的空间导致Thumb ISA中的桶移位操作ASR、LSL、LSR和ROR成为单独的指令。
本章只描述了这些指令的一个子集,因为大多数代码都是从高级语言编译而来的。请参考附录A获取Thumb指令的完整列表。
本章涵盖了Thumb寄存器的使用、ARM-Thumb互操作、分支指令、数据处理指令、加载存储指令、堆栈操作和软件中断。
4.1 Thumb Register Usage

在Thumb状态下,您无法直接访问所有寄存器。只有低位寄存器r0到r7是完全可访问的,如表4.2所示。较高的寄存器r8到r12只能通过MOV、ADD或CMP指令进行访问。CMP和所有操作低位寄存器的数据处理指令会更新cpsr中的条件标志。
您可能已经从Thumb指令集列表和Thumb寄存器使用表中注意到,没有直接访问cpsr或spsr的方式。换句话说,Thumb指令集中没有等效于MSR和MRS的指令。
要更改cpsr或spsr,您必须切换到ARM状态使用MSR和MRS指令。同样,在Thumb状态下也没有协处理器指令。您需要进入ARM状态才能访问协处理器,以配置缓存和内存管理。
4.2 ARM-Thumb Interworking
ARM-Thumb互操作是将ARM和Thumb代码链接在一起的方法,适用于汇编语言和C/C++。它处理两种状态之间的转换。有时需要额外的代码(称为veneer)来执行状态转换。ATPCS定义了ARM和Thumb过程调用标准。
要从ARM例程调用Thumb例程,核心必须改变状态。这个状态改变显示在cpsr的T位上。BX和BLX分支指令在跳转到例程时引发ARM和Thumb状态之间的切换。BX lr指令也会返回例程,并在必要时进行状态切换。
BLX指令是在ARMv5T中引入的。在ARMv4T核心上,链接器在子例程调用时使用一个veneer来进行状态切换。链接器不直接调用例程,而是调用veneer,veneer使用BX指令切换到Thumb状态。
BX或BLX指令有两个版本:一个是ARM指令,另一个是Thumb指令。ARM BX指令只在Rn中的地址的第0位设置为二进制1时进入Thumb状态;否则进入ARM状态。Thumb BX指令执行相同的操作。
语法:BX Rm
BLX Rm | label

不同于ARM版本,Thumb BX指令无法按条件执行。
示例4.1:
这个示例展示了一个小的代码片段,使用了ARM和Thumb版本的BX指令。可以看到,跳转到Thumb的分支地址的最低位被设置为1,这将在cpsr中的T位上将状态设置为Thumb状态。
返回地址不会自动由BX指令保留。相反,代码在分支之前使用MOV指令显式地设置返回地址。

; ARM code
CODE32
; word aligned
LDR r0, =thumbCode+1 ; +1 to enter Thumb state
MOV lr, pc
; set the return address
BX r0
; branch to Thumb code & mode
; continue here
; Thumb code
CODE16
; halfword aligned
thumbCode
ADD r1, #1
BX lr
; return to ARM code & state

一个分支交换指令也可以作为绝对分支使用,只要不使用第0位来强制状态变化即可:

; address(thumbCode) = 0x00010000
; cpsr = nzcvqIFt_SVC
; r0 = 0x00000000
0x00009000 LDR r0, =thumbCode+1
; cpsr = nzcvqIFt_SVC
; r0 = 0x00010001
0x00009008 BX r0
; cpsr = nzcvqIFT_SVC
; r0 = 0x00010001
; pc = 0x00010000

可以看到,寄存器r0的最低有效位被用于设置cpsr中的T位。在执行BX指令之前,cpsr从IFt状态变为IFT状态。然后,将pc设置为指向Thumb例程的起始地址。
示例4.2:
通过将BX指令替换为BLX指令,可以简化对Thumb例程的调用,因为它会在链接寄存器lr中设置返回地址。    

CODE32
LDR r0, =thumbRoutine+1 ; enter Thumb state
BLX r0
; jump to Thumb code
; continue here
CODE16
thumbRoutine
ADD r1, #1
BX r14
; return to ARM code and state

4.3 Other Branch Instructions
有两种标准分支指令或B的变体。第一种类似于ARM版本,并且可以按条件执行;分支范围限制在有符号的8位立即数,即-256到+254字节之间。第二种版本去除了指令的条件部分,并将有效的分支范围扩展为有符号的11位立即数,即-2048到+2046字节之间。
条件分支指令是Thumb状态下唯一可以按条件执行的指令。
语法:
B<cond> label
B label
BL label

BL指令不是按条件执行的,并且具有约±4MB的近似范围。之所以有这个范围,是因为BL(和BLX)指令被转换成一对16位的Thumb指令。该对指令中的第一条指令保存分支偏移的高位,第二条指令保存低位。这些指令必须成对使用。
下面的代码展示了从BL子程序调用中返回所使用的各种指令:

要进行返回操作,我们将pc设置为lr中保存的值。关于栈指令POP的详细讨论将在第4.7节中进行。
4.4 Data Processing Instructions
数据处理指令用于在寄存器内操作数据。它们包括移动指令、算术指令、移位指令、逻辑指令、比较指令和乘法指令。Thumb数据处理指令是ARM数据处理指令的一个子集。
语法:
<ADC|ADD|AND|BIC|EOR|MOV|MUL|MVN|NEG|ORR|SBC|SUB> Rd, Rm
<ADD|ASR|LSL|LSR|ROR|SUB> Rd, Rn #立即数
<ADD|MOV|SUB> Rd,#立即数
<ADD|SUB> Rd,Rn,Rm
ADD Rd,pc,#立即数
ADD Rd,sp,#立即数
<ADD|SUB> sp, #立即数
<ASR|LSL|LSR|ROR> Rd,Rs
<CMN|CMP|TST> Rn,Rm
CMP Rn,#立即数
MOV Rd,Rn


这些指令的风格与相应的ARM指令相同。大多数Thumb数据处理指令操作低寄存器并更新cpsr。异常情况包括:
MOV Rd,Rn
ADD Rd,Rm
CMP Rn,Rm
ADD sp, #立即数
SUB sp, #立即数
ADD Rd,sp,#立即数
ADD Rd,pc,#立即数
它们可以在高寄存器r8-r14和pc上进行操作,但使用高寄存器时这些指令除了CMP外不会更新cpsr中的条件标志位。然而,CMP指令总是更新cpsr。
示例4.3
以下示例展示了一个简单的Thumb ADD指令。它将两个低寄存器r1和r2相加,然后将结果放入寄存器r0中,覆盖原始内容。同时也会更新cpsr。

PRE
cpsr = nzcvIFT_SVC
r1 = 0x80000000
r2 = 0x10000000
ADD r0, r1, r2
POST
r0 = 0x90000000
cpsr = NzcvIFT_SVC

示例4.4
Thumb与ARM风格不同之处在于位移操作(ASR,LSL,LSR和ROR)是单独的指令。这个示例展示了逻辑左移(LSL)指令,用于将寄存器r2乘以2。

PRE
r2 = 0x00000002
r4 = 0x00000001
LSL r2, r4
POST
r2 = 0x00000004
r4 = 0x00000001

请查看附录A,获取完整的Thumb数据处理指令列表。
4.5 Single-Register Load-Store Instructions
Thumb指令集支持加载和存储寄存器,即LDR和STR指令。这些指令使用两种预索引寻址模式:寄存器偏移和立即数偏移。
语法: <LDR|STR>{<B|H>} Rd, [Rn,#立即数] 
LDR{<H|SB|SH>} Rd,[Rn,Rm] 
STR{<B|H>} Rd,[Rn,Rm] 
LDR Rd,[pc,#立即数] 
<LDR|STR> Rd,[sp,#立即数]

在表4.3中可以看到不同的寻址模式。寄存器偏移使用基础寄存器Rn加上寄存器偏移量Rm。第二种方式使用相同的基础寄存器Rn加上一个5位立即数,或者一个与数据大小相关的值。指令中编码的5位偏移量对于字节访问乘以1,对于16位访问乘以2,对于32位访问乘以4。
示例4.5
这个示例展示了两个使用预索引寻址模式的Thumb指令。两者都使用相同的前提条件。

PRE
mem32[0x90000] = 0x00000001
mem32[0x90004] = 0x00000002
mem32[0x90008] = 0x00000003
r0 = 0x00000000
r1 = 0x00090000
r4 = 0x00000004
LDR r0, [r1, r4] ; register
POST r0 = 0x00000002
r1 = 0x00090000
r4 = 0x00000004
LDR r0, [r1, #0x4] ; immediate
POST r0 = 0x00000002

这两个指令执行相同的操作,唯一的区别在于第二个LDR指令使用一个固定的偏移量,而第一个LDR指令依赖于寄存器r4中的值。
4.6 Multiple-Register Load-Store Instructions
Thumb版本的加载存储多个指令是ARM加载存储多个指令的简化形式。它们只支持增量后(IA)寻址模式。
语法: <LDM|STM>IA Rn!, {低位寄存器列表}

这里的N是寄存器列表中寄存器的数量。可以看到,这些指令在执行后总是更新基址寄存器Rn。基址寄存器和寄存器列表仅限于低位寄存器r0到r7。
例如,在以下示例4.6中,将寄存器r1到r3保存到内存地址0x9000到0x900c中,并更新基址寄存器r4。需要注意的是,与ARM指令集不同,更新符号!不是一个选项。

PRE
r1 = 0x00000001
r2 = 0x00000002
r3 = 0x00000003
r4 = 0x9000
STMIA r4!,{r1,r2,r3}
POST
mem32[0x9000] = 0x00000001
mem32[0x9004] = 0x00000002
mem32[0x9008] = 0x00000003
r4 = 0x900c

4.7 Stack Instructions
Thumb堆栈操作与对应的ARM指令不同,因为它们使用更传统的POP和PUSH概念。
语法:POP {低位寄存器列表{, pc}} 
PUSH {低位寄存器列表{, lr}}

值得注意的一点是,在指令中没有堆栈指针。这是因为在Thumb操作中,堆栈指针固定为寄存器13,并且sp会自动更新。寄存器列表仅限于低位寄存器r0到r7。
PUSH寄存器列表也可以包括链接寄存器lr;同样,POP寄存器列表也可以包括程序计数器pc。这为子例程的进入和退出提供了支持,如示例4.7所示。
堆栈指令仅支持完全下降的堆栈操作。
示例4.7
在这个例子中,我们使用了POP和PUSH指令。使用带链接的分支(BL)指令调用了子例程ThumbRoutine。

; Call subroutine
BL ThumbRoutine
; continue
ThumbRoutine
PUSH {r1, lr} ; enter subroutine
MOV r0, #2
POP {r1, pc} ; return from subroutine

链接寄存器lr与寄存器r1一起被推入堆栈中。在返回时,寄存器r1从堆栈中弹出,同时返回地址被加载到pc寄存器中。这样就从子例程中返回了。
4.8 Software Interrupt Instruction
与ARM等效指令类似,Thumb软件中断(SWI)指令会引发一个软件中断异常。如果在Thumb状态下引发了任何中断或异常标志,处理器会自动返回到ARM状态以处理异常。
语法:SWI immediate

Thumb SWI指令与ARM等效指令具有相同的效果和几乎相同的语法。不同之处在于SWI编号的范围限制为0到255,并且不会有条件地执行。
示例4.8
这个例子展示了执行Thumb SWI指令的过程。请注意,在执行后,处理器从Thumb状态切换到ARM状态。

PRE
cpsr = nzcVqifT_USER
pc = 0x00008000
lr = 0x003fffff ; lr = r14
r0 = 0x12
0x00008000 SWI 0x45
POST
cpsr = nzcVqIft_SVC
spsr = nzcVqifT_USER
pc = 0x00000008
lr = 0x00008002
r0 = 0x12

4.9 Summary
在本章中,我们介绍了Thumb指令集。所有Thumb指令的长度都为16位。相比于ARM代码,Thumb提供了约30%的代码密度优势。大多数用于Thumb编写的代码是使用高级语言如C和C++编写的。
ATPCS定义了ARM和Thumb代码之间的调用方式,称为ARM-Thumb交互操作。交互操作使用分支交换(BX)指令和带链接的分支交换(BLX)指令来改变状态并跳转到特定的例程。
在Thumb中,只有分支指令具有条件执行的能力。移位操作(ASR、LSL、LSR和ROR)是单独的指令。
多寄存器加载/存储指令仅支持递增后(IA)寻址模式。Thumb指令集包含了作为堆栈操作的POP和PUSH指令。这些指令只支持完全下降的堆栈操作。
Thumb指令集中没有用于访问协处理器、cpsr和spsr的指令。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值