BX LR ; 跳转回LR地址处,既可以是ARM模式也可以是Thumb模式
A
一、现在先区分下ARM指令集与Thumb指令集
Thumb 指令可以看作是 ARM 指令压缩形式的子集,是针对代码密度的问题而提出的,它具有 16 位的代码密度但是它不如ARM指令的效率高 .Thumb 不是一个完整的体系结构,不能指望处理只执行Thumb 指令而不支持 ARM 指令集.因此,Thumb 指令只需要支持通用功能,必要时可以借助于完善的 ARM 指令集,比如,所有异常自动进入 ARM 状态.在编写 Thumb 指令时,先要使用伪指令 CODE16 声明,而且在 ARM 指令中要使用 BX指令跳转到 Thumb 指令,以切换处理器状态.编写 ARM 指令时,则可使用伪指令 CODE32声明.
流水线处理:
不同于微编码的处理器,ARM (保持它的 RISC 性)是完全硬布线的。
为了加速 ARM 2 和 3 的执行使用 3 阶段流水线。第一阶段持有从内存中取回的指令。第二阶段开始解码,而第三阶段实际执行它。故此,程序计数器总是超出当前执行的指令两个指令。(在为分支指令计算偏移量时必须计算在内)。
因为有这个流水线,在分支时丢失 2 个指令周期(因为要重新添满流水线)。所以最好利用条件执行指令来避免浪费周期。例如:
...
CMP R0,#0
BEQ over
MOV R1,#1
MOV R2,#2
over
...
可以写为更有效的:
...
CMP R0,#0
MOVNE R1,#1
MOVNE R2,#2
二、Thumb 指令集与 ARM 指令集的区别
Thumb 指令集没有协处理器指令,信号量指令以及访问 CPSR 或 SPSR 的指令,没有乘加指令及 64 位乘法指令等,且指令的第二操作数受到限制;除了跳转指令 B 有条件执行功能外,其它指令均为无条件执行;大多数 Thumb 数据处理指令采用 2 地址格式.Thumb指令集与 ARM 指令的区别一般有如下几点:
跳转指令
程序相对转移,特别是条件跳转与 ARM 代码下的跳转相比,在范围上有更多的限制,转向子程序是无条件的转移.
数据处理指令
数据处理指令是对通用寄存器进行操作,在大多数情况下,操作的结果须放入其中一个操作数寄存器中,而不是第 3 个寄存器中.数据处理操作比 ARM 状态的更少,访问寄存器 R8~R15 受到一定限制.除 MOV 和 ADD 指令访问器 R8~R15 外,其它数据处理指令总是更新 CPSR 中的 ALU 状态标志.访问寄存器 R8~R15 的 Thumb 数据处理指令不能更新 CPSR 中的 ALU 状态标志.
单寄存器加载和存储指令
在 Thumb 状态下,单寄存器加载和存储指令只能访问寄存器 R0~R7
批量寄存器加载和存储指令
LDM 和 STM 指令可以将任何范围为 R0~R7 的寄存器子集加载或存储. PUSH 和 POP 指令使用堆栈指令 R13 作为基址实现满递减堆栈.除 R0~R7 外,PUSH 指令还可以存储链接寄存器 R14,并且 POP 指令可以加载程序指令PC
ARM指令分为以下几种:
一、ARM 存储器访问指令
助记符 说明 操作 条件码位置
LDR Rd,addressing 加载字数据 Rd←[addressing],addressing 索引 LDR{cond}
LDRB Rd,addressing 加载无符字节数据 Rd←[addressing],addressing 索引 LDR{cond}B
LDRT Rd,addressing 以用户模式加载字数据 Rd←[addressing],addressing 索引 LDR{cond}T
LDRBT Rd,addressing 以用户模式加载无符号字数据 Rd←[addressing],addressing 索引 LDR{cond}BT
LDRH Rd,addressing 加载无符半字数据 Rd←[addressing],addressing 索引 LDR{cond}H
LDRSB Rd,addressing 加载有符字节数据 Rd←[addressing],addressing 索引 LDR{cond}SB
LDRSH Rd,addressing 加载有符半字数据 Rd←[addressing],addressing 索引 LDR{cond}SH
STR Rd,addressing 存储字数据 [addressing]←Rd,addressing 索引 STR{cond}
STRB Rd,addressing 存储字节数据 [addressing]←Rd,addressing 索引 STR{cond}B
STRT Rd,addressing 以用户模式存储字数据 [addressing]←Rd,addressing 索引 STR{cond}T
SRTBT Rd,addressing 以用户模式存储字节数据 [addressing]←Rd,addressing 索引 STR{cond}BT
STRH Rd,addressing 存储半字数据 [addressing]←Rd,addressing 索引 STR{cond}H
LDM{mode} Rn{!},reglist 批量(寄存器)加载 reglist←[Rn…],Rn 回存等 LDM{cond}{more}
STM{mode} Rn{!},rtglist 批量(寄存器)存储 [Rn…]← reglist,Rn 回存等 STM{cond}{more}
SWP Rd,Rm,Rn 寄存器和存储器字数据交换 Rd←[Rd],[Rn]←[Rm](Rn≠Rd 或 Rm) SWP{cond}
SWPB Rd,Rm,Rn 寄存器和存储器字节数据交换 Rd←[Rd],[Rn]←[Rm](Rn≠Rd 或 Rm) SWP{cond}B
二、ARM 数据处理指令
助记符号 说明 操作 条件码位置
MOV Rd ,operand2 数据转送 Rd←operand2 MOV {cond}{S}
MVN Rd ,operand2 数据非转送 Rd←(operand2) MVN {cond}{S}
ADD Rd,Rn operand2 加法运算指令 Rd←Rn+operand2 ADD {cond}{S}
SUB Rd,Rn operand2 减法运算指令 Rd←Rn-operand2 SUB {cond}{S}
RSB Rd,Rn operand2 逆向减法指令 Rd←operand2-Rn RSB {cond}{S}
ADC Rd,Rn operand2 带进位加法 Rd←Rn+operand2+carry ADC {cond}{S}
SBC Rd,Rn operand2 带进位减法指令 Rd←Rn-operand2-(NOT)Carry SBC {cond}{S}
RSC Rd,Rn operand2 带进位逆向减法指令 Rd←operand2-Rn-(NOT)Carry RSC {cond}{S}
AND Rd,Rn operand2 逻辑与操作指令 Rd←Rn&operand2 AND {cond}{S}
ORR Rd,Rn operand2 逻辑或操作指令 Rd←Rn|operand2 ORR {cond}{S}
EOR Rd,Rn operand2 逻辑异或操作指令 Rd←Rn^operand2 EOR {cond}{S}
BIC Rd,Rn operand2 位清除指令 Rd←Rn&(~operand2) BIC {cond}{S}
CMP Rn,operand2 比较指令 标志 N、Z、C、V←Rn-operand2 CMP {cond}
CMN Rn,operand2 负数比较指令 标志 N、Z、C、V←Rn+operand2 CMN {cond}
TST Rn,operand2 位测试指令 标志 N、Z、C、V←Rn&operand2 TST {cond}
TEQ Rn,operand2 相等测试指令 标志 N、Z、C、V←Rn^operand2 TEQ {cond}
三、乘法指令
具有 32×32 乘法指令,32×32 乘加指令,32×32 结果为 64 位的乘/乘法指令.
助记符 说明 操作 条件码位置
MUL Rd,Rm,Rs 32 位乘法指令 Rd←Rm*Rs (Rd≠Rm) MUL{cond}{S}
MLA Rd,Rm,Rs,Rn 32 位乘加指令 Rd←Rm*Rs+Rn (Rd≠Rm) MLA{cond}{S}
UMULL RdLo,RdHi,Rm,Rs 64 位无符号乘法指令 (RdLo,RdHi)←Rm*Rs UMULL{cond}{S}
UMLAL RdLo,RdHi,Rm,Rs 64 位无符号乘加指令 (RdLo,RdHi)←Rm*Rs+(RdLo,RdHi) UMLAL{cond}{S}
SMULL RdLo,RdHi,Rm,Rs 64 位有符号乘法指令 (RdLo,RdHi)←Rm*Rs SMULL{cond}{S}
SMLAL RdLo,RdHi,Rm,Rs 64 位有符号乘加指令 (RdLo,RdHi)←Rm*Rs+(RdLo,RdHi) SMLAL{cond}{S}
四、跳转指令
在 ARM 中有两种方式可以实现程序的跳转,一种是使用跳转指令直接跳转,另一种则是直接向 PC 寄存器赋值实现跳转.
助记符 说明 操作 条件码位置
B label 跳转指令 Pc←label B{cond}
BL label 带链接的跳转指令 LR←PC-4, PC←label BL{cond}
BX Rm 带状态切换的跳转指令 PC←label,切换处理状态 BX{cond}
五、ARM协处理器指令
ARM 支持协处理器操作,协处理器的控制要通过协处理器命令实现.
助记符 说明 操作 条件码位置
CDP
coproc,opcodel,CRd,CRn,CRm{,opcode2} 协处理器数据操作指令 取决于协处理器 CDP{cond}
LDC{L} coproc,CRd〈地址〉 协处理器数据读取指令 取决于协处理器 LDC{cond}{L}
STC{L} coproc,CRd,〈地址〉 协处理器数据写入指令 取决于协处理器 STC{cond}{L}
ARM 寄存器到协处理器
MCR coproc, opcodel,Rd,CRn,{,opcode2} 寄存器的数据传送指令 取决于协处理器 MCR{cond}
协处理器寄存器到 ARM
MRC coproc, opcodel,Rd,CRn,{,opcode2} 寄存器到数据传送指令 取决于协处理器MCR{cond}
五、ARM 杂项指令
助记符 说明 操作 条件码位置
SWI immed_24 软中断指令 产生软中断,处理器进入管理模式 SWI{cond}
MRS Rd,psr 读状态寄存器指令 Rd←psr,psr 为 CPSR 或 SPSR MRS{cond}
MSR psr_fields,Rd/#immed_8r 写状态寄存器指令 psr_fields←Rd/#immed_8r,psr 为 CPSR 或 SPSR MSR{cond}
B
由于Thumb指令在某些特殊情况下可能比ARM指令更有效,所以它在很多方面得到了广泛的应用。但是Thumb知识ARM指令集的一个子集,它不能独立组成一个应用系统,所以在很多情况下应用程序需要二者的混合编程,这就必然存在ARM与Thumb状态之间函数调用的问题。下面将分别详细介绍。 状态切换的实现ARM/Thumb之间的状态切换是通过一条专用的转移交换指令BX来实现的。BX指令以通用寄存器位操作数,通过拷贝Rn到PC来实现4GB空间范围内的一个绝对跳转。BX利用Rn寄存器中目的地址值的最后一位来判断跳转后的状态。当最后一位为0时,表示转移到ARM状态;当最后一位为1时,表示转移到Thumb状态。如图所示无论是ARM还是Thumb,其指令在存储器中都是边界对齐的。(2字节或者4字节对齐,最低位不起作用!)因此,在执行跳转过程中,PC寄存器中的最低位被舍弃,不起作用。在BX指令的执行过程中,最低位正好被用作状态判断的标志,不会造成存储器访问不对齐的错误。下面是一段直接进入状态切换的例程: ;从ARM状态开始 CODE32 ;表明一下是ARM指令 ADR R0,Into_Thumb+1 ;得到目标地址,末位置1,表示转移到Thumb BX R0 ;转向Thumb … CODE16 ;表明以下是Thumb指令Into_Thumb … ADR R5,Back_to_ARM ;得到目标地址,末位缺省为0 ,转移到ARM BX R5 ;转向ARM … CODE32 Back_to_ARM ;ARM代码段起始地址举例:从ARM态进入thumb态① 将在Makefile中将.c文件编译指定为thumb态参考:https://www.cnblogs.com/QuLory/archive/2012/10/23/2735226.html%.o:%.c arm-linux-gcc -mthumb -c -o $@ $^ -fno-builtin %.o:%.S arm-linux-gcc -c -o $@ $^.S文件直接在文件中修改来实现态的转化② 修改.S文件从arm转化到thumb态.text.global _start.code 32_start: /* 怎样从arm_state -> thumb_state */ adr r0,thumb_func add r0,r0,#1 bx r0 .code 16thumb_func: bl sdram_init //bl sdram_init2 //用到有初始值的数组,不是位置无关码 bl copy2sdram bl clean_bss //bl main //使用BL命令相对跳转,程序仍然在Nor/sram执行 ldr r0,=main //绝对跳转,跳到SDRAM mov pc,r0halt: b halt-----------------------------------------------------------------------------------------------------------------------------------------------------问题:①:start.S: Assembler messages:start.S:60: Error: lo register required -- `ldr pc,=main'Makefile:11: recipe for target 'start.o' failedthumb不像arm态那样,不能直接将标号赋值给pc,需要通过寄存器转化:ldr r0,=mainmov pc,r0②:init.o(.text+0x58): In function `sdram_init2':: undefined reference to `memcpy'thumb中自动给我们将数组中的值通过memcpy复制到了数组,我们可以通过static来解决这个问题:参考Getting GCC to compile without inserting call to memcpy:https://stackoverflow.com/questions/6410595/getting-gcc-to-compile-without-inserting-call-to-memcpyunsigned int arr[] = {0x02000000, //BWSCON0x00000700, //BANKCON00x00000700, //BANKCON10x00000700, //BANKCON20x00000700, //BANKCON3 0x00000700, //BANKCON40x00000700, //BANKCON50x00018001, //BANKCON60x00018005, //BANKCON70x008404f5, //REFRESH 0x000000b1, //BANKSIZE0x00000020, //MRSRB60x00000020, //MRSRB7};//把那些值全部放在数组里面 ->>>>>>const static unsigned int arr[] = {0x02000000, //BWSCON0x00000700, //BANKCON00x00000700, //BANKCON10x00000700, //BANKCON20x00000700, //BANKCON3 0x00000700, //BANKCON40x00000700, //BANKCON50x00018001, //BANKCON60x00018005, //BANKCON70x008404f5, //REFRESH 0x000000b1, //BANKSIZE0x00000020, //MRSRB60x00000020, //MRSRB7};//把那些值全部放在数组里面③:warning: conflicting types for built-in function解决:在编译的时候加上-fno-builtin不使用内建函数arm-linux-gcc -mthumb -c -o $@ $^ -fno-builtin
C
1 thumb指令集概述
为兼容数据总线宽度为16位的应用系统,ARM体系结构除了支持执行效率很高的32位ARM指令集以外,同时支持16位的Thumb指令集。
Thumb指令集是ARM指令集的一个子集,是针对代码密度问题而提出的,它具有16位的代码宽度。与等价的32位代码相比较,Thumb指令集在保留32位代码优势的同时,大大的节省了系统的存储空间。Thumb不是一个完整的体系结构,不能指望处理器只执行Thumb指令集而不支持ARM指令集。
当处理器在执行ARM程序段时,称ARM处理器处于ARM工作状态,当处理器在执行Thumb程序段时,称ARM处理器处于Thumb工作状态(CPSR的T=0:arm,T=1:thumb)。Thumb指令集并没有改变ARM体系底层的编程模型,只是在该模型上增加了一些限制条件,只要遵循一定的调用规则,Thumb子程序和ARM子程序就可以互相调用。
与ARM指令集相比较,Thumb指令集中的数据处理指令的操作数仍然是32位,指令地址也为32位,但Thumb指令集为实现16位的指令长度,舍弃了ARM指令集的一些特性,相比之下从指令集上看thumb和arm主要有以下不同:
l 跳转指令。条件跳转在范围上有更多的限制,转向子程序只具有无条件转移。
l 数据处理指令。对通用寄存器进行操作,操作结果需放入其中一个操作数寄存器,而不是第三个寄存器。
l 单寄存器加载和存储指令。Thumb状态下,单寄存器加载和存储指令只能访问寄存器R0~R7。
l 批量寄存器加载和存储指令。LDM和STM指令可以将任何范围为R0~R7的寄存器子集加载或存储,PUSH和POP指令使用堆栈指针R13作为基址实现满递减堆栈,除R0~R7外,PUSH指令还可以存储链接寄存器R14,并且POP指令可以加载程序指令PC。
Thumb指令集没有包含进行异常处理时需要的一些指令,因此,在异常中断时还是需要使用ARM指令。这种限制决定了Thumb指令不能单独使用需要与ARM指令配合使用。
2 thumb寄存器和arm寄存器的关系
1)、Thumb 状态寄存器集是ARM 状态寄存器集的子集
程序员可直接访问8 个通用寄存器R0~R7、PC、堆栈指针SP、链接寄存器LR和CPSR。每个特权模式都有分组的SP、LR和SPSR。
2)、Thumb状态寄存器与ARM 状态寄存器的关系
Thumb状态寄存器与ARM状态寄存器有如下关系:
l Thumb状态R0~R7与ARM状态R0~R7相同。
l Thumb状态CPSR和SPSR与ARM状态CPSR和SPSR 相同。
l Thumb状态SP映射到ARM状态R13。
l Thumb状态LR映射到ARM状态R14。
l Thumb状态PC映射到ARM状态PC(R15)。
3)、在Thumb状态中访问高寄存器
在Thumb状态中高寄存器(寄存器R0~R7为低寄存器,寄存器R8~R15为高寄存器)不是标准寄存器集的一部分,汇编语言程序员对它们的访问受到限制,但可以将它们用于快速暂存。
可以使用MOV指令的特殊变量将一个值从低寄存器R0~R7转移到高寄存器R8~R15,或者从高寄存器到低寄存器。CMP指令可用于比较高寄存器和低寄存器的值。ADD 指令可用于将高寄存器的值与低寄存器的值相加。
在编写Thumb指令时,先要使用伪指令CODE16声明,编写ARM指令时,则可使用CODE32伪指令声明。
1、Thumb指令集没有协处理器指令、信号量指令、以及访问CPSR或SPSR的指令,没有乘加指令及64位乘法指令等,且指令的第二操作数受到限制;
2、大多数的Thumb数据处理指令采用2地址格式;
3、除了跳转指令B有条件执行功能之外,其他指令均为无条件执行,而且分支指令的跳转范围有更多限制;
4、数据处理指令是对通用寄存器进行操作,在大多数情况下,操作的结果放入其中一个操作数寄存器中,而不是放入第3个寄存器中;访问寄存器R8~R15受到一定的限制,除MOV、ADD指令访问R8~R15外,其他数据处理指令总是更新CPSR中ALU状态标志,访问寄存器R8~R15的Thumb数据处理指令不能更新CPSR中的ALU状态指示。
5、Thumb状态下,单寄存器加载和存储指令只能访问寄存器R0~R7;
6、LDM、STM指令可以将任何范围为R0~R7的寄存器子集加载或存储;
7、PUSH、POP指令使用栈寄存器R13作为基址堆栈操作。
大多数ARM数据处理指令采用的是3地址格式(除了64位乘法指令外)。
所有异常都会使微处理器返回到ARM模式状态,并在ARM的编程模式中处理。由于ARM微处理器字传送地址必须可被4整除(即字对准),半字传送地址必须可被2整除(即半字对准)。而Thumb指令是2个字节长,而不是4个字节,所以,由Thumb执行状态进入异常时其自然偏移与ARM不同。
16位Thumb指令集是从32位ARM指令集提取指令格式的,每条Thumb指令有相同处理器模型所对应的32位ARM指令。
只要遵循ATPCS调用规则,Thumb子程序和ARM子程序就可以互相调用