欢迎光临亿道电子技术有限公司官网!

全国服务热线:

400-821-3806

您的位置:首页 > 新闻资讯 > 公司新闻
导航栏目

深圳(总部)

深圳市宝安区33区大宝路83号美生慧谷科技园 美谷5栋(邮编:518101)

电 话: 0755-23706296

邮箱:shenzhen@emdoor.com


北京

北京市朝阳区阜通东大街1号院 望京SOHO T1-B-1709室(100102)

电 话:010-82359258

邮箱:beijing@emdoor.com


上海

上海市杨浦区国定路335号复旦科技创业园2号楼10层(200433)

电话:021-52653259 021-62650520

邮箱:shanghai@emdoor.com

公司新闻

联系我们

Thumb指令集之: ARM和Thumb的混合编程

发布时间:2020-07-17

浏览次数:44

[导读]Thumb以其较高的代码密度和在窄存储器上的性能,使得它在很多系统中得到广泛应用。但在很多情况下,还是不得不使用ARM指令,这是因为:

11.10ARMThumb的混合编程11.10.1互交工作基础

Thumb以其较高的代码密度和在窄存储器上的性能,使得它在很多系统中得到广泛应用。但在很多情况下,还是不得不使用ARM指令,这是因为:

ARM代码比Thumb代码有更快的执行速度;

ARM处理器的一些特定功能必须由ARM指令实现,其中包括PSR指令、协处理器指令;

③异常发生时,处理器自动进入ARM状态,如果异常处理程序需要使用Thumb指令也必须通用一个ARM程序头(ARMassemblerheader)。

基于以上原因,即使程序需要由Thumb代码实现,也必须通过ARM-Thumb互交(ARM-Thumbinterworking)进入Thumb状态。

ARM-Thumb互交是指对汇编语言和C/C++语言的ARMThumb代码进行连接的方法,它进行两种状态(ARMThumb状态)间的切换。在进行这种切换时,有时需使用额外的代码,这些代码被称为VeneerAAPCS定义了ARMThumb过程调用的标准。

从一个ARM例程调用一个Thumb例程,内核必须进行状态切换。状态的变化由CPSRT位来显示。在跳转到一个例程时BX指令可用于ARMThumb状态切换,具体用法如下。

Thumb状态调用ARM例程时,采用:

BXRn

ARM状态调用Thumb例程时,采用:

BX{cond}Rn;

其中,Rn可以是r0r15中的任意寄存器。

这种带状态切换的跳转指令BX,将寄存器Rn的内容拷贝到程序计数器寄存器PC,因此可以实现4G空间的跳转。指令根据寄存器Rnbit[0]来决定处理器是否进行状态切换,详细内容参见ARM指令一节。

下面是一段ARM程序,该程序调用虚拟的SWI_writeC子程序从存储器的固定地址取出字符串“helloworld”并输出。

AREAHello,CODE,READONLY

SWI_WriteCEQU&0 ;软中断调用参数

SWI_ExitEQU&11 ;程序退出软中断调用参数

ENTRY

STARTADRr1,TEXT ;取字符串地址

LOOPLDRBr0,[r1],#1 ;取下一字节内容

CMPr0,#0 ;判断是否为字符串尾

SWINESWI_WriteC ;软中断调用打印字符

BENLOOP ;循环

SWISWI_Exit ;软中断调用退出程序执行

TEXT=“HelloWorld”,&0a,&0d,0

END

下面的代码将上面的ARM代码转换成等价的Thumb代码。

AREAHelloW_Thumb,CODE,READONLY

SWI_WriteCEQU&0 ;软中断调用参数

SWI_ExitEQU&11 ;程序退出软中断调用参数

ENTRY ;程序入口点

CODE32进入ARM状态

ADRr0,START+1 ;取得Thumb代码入口地址

BXr0 ;进入Thumb代码

CODE16 ;Thumb代码入口点

STARTADRr1,TEXT ;r1->"HelloWorld"

LOOPLDRBr0,[r1] ;取下一字节内容

ADDr1,r1,#1 ;地址指针加1**T

CMPr0,#0 ;判断是否为字符串尾

BEQDONE ;完成?**T

SWISWI_WriteC ;如果不是字符串尾

BLOOP ;继续循环

DONESWISWI_Exit ;程序退出

ALIGN ;字对齐

TEXTDATA

"HelloWorld",&0a,&0d,&00

END

上例中,ARM代码到Thumb代码转换过程中新增加的指令用“**T”标注。

在实现ARM代码和Thumb代码转换时,大部分的ARM指令有等价的Thumb指令,只有少数指令没有。如加载字节指令(LDR)不支持自动变址,软中断指令不能条件执行。

在编写Thumb代码时要注意以下几点。

①汇编器需要知道什么时候产生ARM代码、什么时候产生Thumb代码,程序中使用CODE32CODE16伪操作提供给编译器这些信息。

②由于处理器上电执行是在ARM状态下完成的,所以要使用Thumb指令必须由ARM指令调用Thumb指令,这一过程是通过“BXLR”指令来实现的。需要注意的是,在使用“BXLR”指令前,要对寄存器LR做正确的初始化。

③在ARMThumb混合编程时,常使用ALIGN伪操作保证内存地址对齐。

11.10.2互交子程序

编写ARM/Thumb互交代码时,下面两点需要注意。

①对于C/C++子程序而言,只要在编译时指定--apcs/interwork选项,汇编器会生成合适的返回代码,使得程序返回到和调用程序相同的状态。

②在汇编语言子程序中,用户必须自己编写相应的返回代码,使得程序返回到和调用程序相同的状态。

如果目标代码包含以下内容,应该在编译或汇编时使用--apcs/interwork选项使处理器能够在ARMThumb代码间进行正确的切换,这种情况包含以下4种。

①需要返回到ARM状态的Thumb子程序。

②需要返回到Thumb状态的ARM子程序。

③间接调用ARM子程序的Thumb子程序。

④间接调用Thumb子程序的ARM子程序。

如果在程序连接阶段,连接器发现ARM子程序和Thumb子程序间存在相互调用,而源文件在编译时没有使用--apcs/interwork选项,则连接器将报告以下错误。

Error:L6239E:CannotcallARMsymbol'arm_function'innon-interworkingobject

armsub.ofromTHUMBcodeinthumbmain.o(.text)

其中,arm_function”为需要进行状态切换的子程序名。

在这种情况下,用户必须使用--apcs/interwork选项重新对源文件进行编译。

但在下面两种情况下,不必指定--apcs/interwork选项。

①在Thumb状态下,发生异常中断时,处理器自动切换到ARM状态,这时不需要添加状态切换代码。

②当异常发生在Thumb状态时,从异常返回不需要添加状态切换的Veneer代码。

1.使用汇编语言实现互交

对于汇编程序来说,可以有两种方法来实现程序状态的切换。第一种方法是利用连接器提供的交互子程序Veneer来实现程序状态的切换,这时用户可以使用指令BL来调用子程序;另一种方法是用户自己编写状态切换的程序。

ARMv4版本及其以前的版本中,可以使用BX指令实现程序状态的切换。

ARMv5版本开始,下面的指令也可以用来实现程序的状态切换。

·BXBranchandeXchange

·BLXLDRLDMPOP

下面的两个伪操作用来区分源程序中的ARM代码和Thumb代码。

·CODE16

·CODE32

下面简单介绍用于状态切换的指令和伪操作,更详细的信息请分别参见相关章节。

1BX指令

ARM状态下的BX指令,使程序跳转到指令中指定的参数Rm指定的地址执行程序,Rm的第0位拷贝到CPSRT位,位[311]移入PC。若Rmbit[0]1,则跳转时自动将CPSR中的标志位T置位,即把目标地址的代码解释为Thumb代码;若Rm的位bit[0]0,则跳转时自动将CPSR中的标志位T复位,即把目标地址代码解释为ARM代码。指令的语法格式如下。

BX{}

为指令编码中的条件域。它指示指令在什么条件下执行。当忽略时,指令为无条件执行(cond=ALAlway))。

包含跳转指令的目标地址。如果Rmbit[0]=0,目标地址处指令为ARM指令;如果Rmbit[0]=1,目标地址处指令为Thumb指令。

指令操作的伪代码。

指令操作的伪代码如下面程序段所示。

IfconditionPassed{cond}then

TFlag=Rm[0]

PC=RmAND0xfffffffe

Thumb状态下的BX指令,用于ARMThumb代码间的相互调用。

指令的语法格式。

BX

其中为目标地址寄存器,包含程序的跳转地址。BX指令的目标地址寄存器可以是r0r15中的任意寄存器。

注意

如果Rm[10]=0b10,不满足ARM指令的内存对齐方式。指令的执行结果不可预知。如果该指令使用r15作为目标寄存器,其操作方式和使用其他寄存器相同。

指令操作的伪代码如下所示。

TFlag=Rm[0]

PC=Rm[311]<<1

ARM指令集中的BX指令和Thumb指令集中的BX指令相差较大,它们分别为不同方向的跳转。当r15作为目的寄存器使用时,要特别注意该指令在两个指令集中的区别。

2BLX指令

ARM状态下的BLX指令使用一个寄存器中的绝对地址或标号,用于使程序跳转到Thumb状态或从Thumb状态返回,该指令用分支寄存器的最低位来更新CPSR中的T位,并将返回地址写入到连接寄存器LR中。

指令的语法格式如下所示:

BLX

BLX{}

第一种格式中,转移目标按下述方法计算。将指令中指定的24位偏移量进行符号扩展,左移两位形成字偏移量,然后将其累加进程序计数器PC中。这时,程序计数器的内容为BX指令地址加8字节。位Hbit[24])也加到结果地址的第一位(bit[1]),使目标地址为半字地址,以执行接下来的Thumb指令。计算偏移量的工作一般由ARM汇编器来完成。这种形式的跳转指令只能实现±32MB空间的跳转。

第二种格式中,寄存器Rm指定转移目标,Rm的第0位拷贝到CPSR中的T位,bit[310]移入PC

·如果Rmbit[0]=1,则跳转时自动将CPSR中的标志位T置位,即把目标地址的代码解释为Thumb代码。

·如果Rmbit[0]=0,则跳转时自动将CPSR中的标志位T复位,即把目标地址的代码解释为ARM代码。

指令操作的伪代码如下面程序段所示。

第一种格式BLX指令。

LR=addressoftheinstructionaftertheBLXinstruction

TFlag=1

PC=PC+PC=PC+(SignExtend(signed_immed_24)<<2)+(H<<1)

第二种格式BLX指令。

IfConditionPass{cond}then

LR=addressoftheinstructionafterthebranchinstruftion

TFlag=Rm[0]

PC=RmAND0xfffffffe

Thumb状态下带返回链接的跳转指令BLX1)提供了一种在Thumb状态下无条件调用ARM子程序的方法,当从子程序返回时,通常使用下面的方式之一:

·BXLR

·加载PCLDRLDM指令。

BLX指令不可条件执行,可以实现在大约±4MB的地址空间范围内跳转,实现方法是将一条BLX指令编译成两条16位的Thumb指令,从而实现上述跳转。对编译后的两条指令说明如下。

H=10的跳转指令。该跳转包含跳转偏移量的高位部分。

H=01的跳转指令。该跳转包含跳转偏移量的低位部分。

指令的语法格式

BL

指定程序跳转的目标地址。指令通过下面的方法计算目标地址。

·将H=10BL指令的offset_11域左移12位。

·将结果符号扩展为32位。

·将得到的值加到PC寄存器中。

·与H=11BL指令的offset_11域相加。

因此BL指令可以实现在大约±4MB的地址空间范围内跳转。

指令操作的伪代码为:

ifH==10then

LR=PC+(SignExtend(offset_11)<<12)

ElseifH==11then

PC=LR+(offset_11<<11)

LR=(addressofnextinstruction)|1

ElseifH==01then

PC=(LR+(offset_11<<1))AND0xFFFFFFFC

LR=(addressofnextinstruction)|1

ElseifH==01then

PC=(LR+(offset_11<<1))AND0Xfffffffc

LR=(addressofnextinstruction)|1

TFlag=0

另外Thumb状态下包含另一种格式的BLX指令,该BLX2)指令用于ARMThumb子程序间的相互调用。程序状态字的T标志位根据目的寄存器的bit[0]位而改变。

指令的语法格式为:

BLX

其中为目标地址寄存器,r0r14寄存器均可以作为目标地址寄存器。

注意

如果在此指令中使用r15作为目的寄存器,指令的执行结果不可预知。

此指令只在ARMv5版本以上指令集中被支持。

指令操作的伪代码为:

LR=(addressoftheinstructionafterthisBLX)|1

TFlag=Rm[0]

PC=Rm[31:1]<<1

3)汇编伪指令

汇编编译器可以产生ARM代码也可以产生Thumb代码。使用--thumb--16选项指示编译器产生Thumb代码。由于所有支持Thumb代码的ARM处理器都从ARM状态开始执行,所以必须人为地使用BXBLX指令,使处理器状态切换到Thumb状态并使用下面的伪操作使编译器产生Thumb代码。

·CODE16

·CODE32

CODE32伪操作指示汇编器后面的指令为32位的ARM指令。

ARMCODE32伪操作的意义相同。

当汇编器对源程序进行编译时,如果需要,将会在程序中插入空指令,以保证内存单元字对齐。

语法格式如下。

ARM

CODE32

使用在同时包含ARM指令和Thumb指令的源文件中。当需要从ARM指令序列切换到Thumb指令序列时,使用伪操作ARM(或CODE32);当需要从Thumb指令序列切换到ARM指令序列时使用Thumb伪操作。ARM(或CODE32)伪操作只是指示汇编器后面的指令类型是ARM指令。该伪操作本身并不进行程序状态的切换,要进行状态切换,可以使用BX指令操作。

CODE16伪指令通知编译器,其后的指令序列为16位的Thumb指令。

语法格式如下。

CODE16

若在汇编源程序中同时包含ARM指令和Thumb指令时,可用CODE16伪指令通知编译器其后的指令序列为16位的Thumb指令。

4)编程实例

PRESERVE8

AREAAddReg,CODE,READONLY ;段名为AddReg,属性为READONLY

ENTRY ;程序入口

;SECTION1

main

ADRr0,ThumbProg+1 ;确定跳转地址

;并将bit[0]1

;使程序切换到thumb状态

BXr0 ;程序跳转并执行状态切换

;SECTION2

CODE16 ;Thumb代码指示伪操作

ThumbProg

MOVr2,#2 ;r2=2

MOVr3,#3 ;r2=3

ADDr2,r2,r3 ;r2=r2+r3

ADRr0,ARMProg

BXr0 ;程序跳转并执行状态切换