Ok6410裸机驱动学习(二)ARM基础知识
1.ARM工作模式
ARM微处理器支持7种工作模式,分别为:
l 用户模式(usr):ARM处理器正常的程序执行状态(Linux用户态程序)
l 快速中断模式(fiq):用于高速数据传输或通道处理
l 外部中断模式(irq):用于通用的中断处理
l 管理模式(svc):操作系统使用的保护模式(Linux内核)
l 中止模式(abt):当数据或指令预取终止时进入该模式,用于虚拟存储及存储保护
l 未定义指令模式(und):当未定义的指令执行时进入该模式,用于支持硬件协处理器的软件仿真
l 系统模式(sys):运行具有特权的操作系统任务
除用户模式外,其余6种模式称之为非用户模式或特权模式(Privileged Modes);其中除用户模式和系统模式外的5种又称为异常模式(Exception Modes),常用于处理中断或异常,以及需要访问受保护的系统资源等情况
2.ARM寄存器
ARM微处理器有37个32位寄存器,其中31个为通用寄存器,6个为状态寄存器,但是这些寄存器不能被同时访问,最多可有18个活动寄存器;16个数据寄存器和2个处理器状态寄存器。
3.通用寄存器
通用寄存器可以用来保存数据和地址,这些寄存器都是32位的, 通用寄存器包括R0~R15,可以分为3类,即未分组寄存器、分组寄存器及程序计数器。
未分组寄存器:
未分组寄存器包括R0~R7,在所有工作模式下,未分组寄存器都指向同一个物理寄存器,他们未被系统用作特殊用途,因此,在中断或异常处理进行工作模式转换时,由于不同的处理器工作模式均使用相同的物理寄存器,可能会造成寄存器中数据的破坏,这一点在程序设计的时候应引起注意。
分组寄存器:
分组寄存器包括R8~R14,对于分组寄存器,他们每一次所访问的物理寄存器都与处理器当前的工作模式有关
l 寄存器R13在ARM指令中常用作堆栈指针
由于处理器的每种工作模式均有自己独立的物理寄存器R13,在用户应用程序的初始化部分,一般要初始化每种模式下的R13,使其指向该工作模式的栈空间,这样,当程序的运行进入异常模式时,可以将需要保护的寄存器放入R13所指向的堆栈,而当程序从异常模式返回时,则从对应的堆栈中恢复,采用这种方式可以保证异常发生后程序的正常执行。
l R14也称作子程序连接寄存器(Subroutine Link Register)或连接寄存器LR,当执行BL子程序调用指令时,R14中得到R15(程序计数器PC)的备份,其他情况下R14用作通用寄存器。
l 寄存器R15用作程序计数器(PC),用于控制程序中指令的执行顺序,正常运行时,PC执行那个CPU运行的下一条指令每次取指后PC的值会自动修改以指向下一条指令,从而保证了指令按一定的顺序执行。当程序的执行顺序发生改变(如转移)时,需要修改PC的值。
在ARM状态下,任一时刻可以访问以上所讨论的16个通用寄存器和一到两个状态寄存器,在非用户模式(特权模式)下,可以访问到特定模式下的分组寄存器。
状态寄存器
ARM体系结构包含一个当前程序状态寄存器CPSR(R16)和5个备份的程序状态寄存器(SPSR)。CPSR可以在任何工作模式下被访问,用来保存ALU中的当前操作信息、控制允许和禁止中断、设置处理器的工作模式等。备份的程序状态寄存器用来进行异常处理。
l 条件码标志
标志位 |
含义 |
N |
正、负标志,N=1表示运算结果为负数,N=0表示结果为正数或0 |
Z |
零标志,Z=1表示运算结果为零,Z=0表示运算结果为非零 |
C |
进位标志:加法运算结果产生进位则C=1否则C=0,借位标志:减法运算产生借位则C=0否则C=1 |
V |
溢出标志,V=1表示有溢出,V=0表示无溢出 |
Q |
在ARM v5及以上版本的E系列处理器中用Q表示增强的DSP运算是否溢出 |
1.ARM寻址方式
所谓的寻址方式就是根据指令中操作数的信息来寻找操作数实际物理地址的方式,ARM指令系统支持一下几种常见的寻址方式:立即数寻址、寄存器寻址、寄存器间接寻址、寄存器移位寻址、基址变址寻址、多寄存器寻址、相对寻址、堆栈寻址。
立即数寻址
立即数寻址是一种特殊的寻址方式,操作数本身就在指令中给出,只要取出指令也就取到了操作数,这个操作数被称为立即数
ADD R0 , R0 , #0x3f; R0<-R0+0x3f
以上指令中,第二个操作数即为立即数,要求以#为前缀
寄存器寻址
寄存器寻址就是利用寄存器中的数值作为操作数,这种寻址方式是一种执行效率较高的寻址方式
ADD R0 , R1 , R2; R0<-R1+R2
该指令执行的效果是将寄存器R1和R2的内容相加,其结果存放在寄存器R0中
寄存器间接寻址
寄存器间接寻址就是寄存器中存放的是操作数在内存中的地址
LDR R0 , [R4]; R0<-[R4]
该指令以寄存器R4的值作为操作数的地址,在存储器中中取得一个操作数存入寄存器R0中
寄存器移位寻址
寄存器移位寻址的操作数由寄存器中的数值做相应的移位而得到,移位的方式在指令中以助记符的形式给出
ADD R0 , R1 ,R2 ,LSL #1
MOV R0 , R1 , LSL R3
第一条指令表示将R2中的值向左移一位,所得的值与R1中的值相加,结果存入R0中。
第二条指令表示将R1中的值向左移R3中的值位,结果存入R0中
基址变址寻址
基址变址寻址就是将寄存器(该寄存器一般称作基址寄存器)的内容与指令中给出的地址偏移量相加,从而得到一个操作数的有效地址,基址变址寻址方式常用于访问某基地址附近的地址单元
LDR R0 , [R1 , #4]; R0<-[R1+4]
LDR R0 , [R1 , #4!]; R0<-[R1+4]、R1<-R1+4
LDR R0 , [R1] , #4; R0<-[R1]、R1<-R1+4
LDR R0 , [R1 , R2]; R0<-[R1+R2]
多寄存器寻址
采用多寄存器寻址方式,一条指令可以完成多个寄存器值的传送,这种寻址方式中用一条指令最多可以传送16个通用寄存器的值,连续的寄存器件可以用 “–” 连接,否则用“,”连接。
LDMIA R0! , {R1 - R4}; R1<-[R0]、R2<-[R0+4]、R3<-[R0+8]、R4<-[R0+12]
相对寻址
与基址变址寻址方式相类似,相对寻址以程序计数器PC的当前值为基地址,指令中的地址标号作为偏移量,两者相加之后得到操作数的有效地址
BL NEXT ;跳转到子程序NEXT处执行
……
NEXT
……
MOV PC LR ;从子程序返回
堆栈寻址
堆栈是一种数据结构,按后进先出(Last In First Out , LIFO)的方式工作,使用一种称作堆栈指针的专用寄存器指示当前的操作位置,堆栈指针总是指向栈顶,ARM中分别采用LDMFD和STMFD 指令支持POP(出栈)和PUSH(压栈),R13作为堆栈指针。
STMFD R13! , {R0 – R14};
LDMFD R13! , {R0 R14};
第一条指令将R0 – R14中的数据压入堆栈,R13为堆栈指针;第二条指令将数据出栈,恢复R0 – R14原先的值,多用于中断处理时保护现场和恢复现场
2.ARM汇编指令
汇编程序框架
.text
.global _start
_start:
….
指令的可选后缀
ARM指令集中大多数指令都可以选加后缀,这些后缀使得ARM指令使用十分灵活。常见的可选后缀有“S”后缀和“!”后缀。
l “S”后缀
指令中使用“S”后缀时,指令执行后状态寄存器的条件标志位将被刷新,不使用“S”后缀时,指令执行后状态寄存器的条件标志将不会发生变化。“S”后缀通常用于对条件进行测试,如是否有溢出,是否进位等,根据这些变化,就可以进行一些判断,如是否大于,是否相等,是否进位等,从而可能影响指令的执行顺序。
SUB R1 , R0 , R3; R0的值减去R3的值,结果存入R1
SUBS R1 , R0 , R3 ; R0的值减去R3的值,结果存入R1,影响标志位
l “!”后缀
如果指令地址表达式中不含“!”后缀,则基址寄存器中的地址值不会发生变化,指令中的地址表达式中含有“!”后缀时,指令执行后,基址寄存器中的地址值将发生变化,变化的结果如下
基址寄存器中的值(指令执行后)=指令执行前的值+地址偏移量
LDR R3 , [R0 , #4]
LDR R3 , [R0 , #4]!
在上述指令中,第一条指令没有后缀“!”,指令的结果是吧R0加4作为地址指针,把这个指针所指向的地址单元所存储的数据读入R3,R0的值不变,第二条指令除了实现上述操作之外,还把R0+4的结果送入到R0中
使用“!”后缀注意事项:
①“!”后缀必须紧跟在地址表达式后面,而地址表达式要有明确的地址偏移量
②“!”后缀不能用于R15(PC)后面
③当用在单个地址寄存器后面时,必须确信这个寄存器有隐性的偏移量例如
STMFDB R1! {R3 , R5 , R7} 此时地址基址寄存器R1的隐性偏移量为4
指令的条件执行
程序要执行的指令都保存在存储器中,当计算机需要执行一条指令时,首先产生这条指令的地址,根据地址号打开相应的存储单元,取出指令代码,CPU根据指令代码的要求以及指令中的操作数,去执行相应的操作。
每一条ARM指令包含4位条件码,位于指令的高四位[31:28],条件码共有16种,每种条件码可用两个字符表示,这两个字符可以添加在指令助记符的后面和指令同时使用
条件码 |
助记符后缀 |
标志 |
含义 |
0000 |
EQ |
Z置位 |
相等 |
0001 |
NE |
Z清零 |
不相等 |
0010 |
CS |
C置位 |
无符号数大于或等于 |
0011 |
CC |
C清零 |
无符号数小于 |
0100 |
ML |
N置位 |
负数 |
0101 |
PL |
N清零 |
正数或0 |
0110 |
VS |
V置位 |
溢出 |
0111 |
VC |
V清零 |
未溢出 |
1000 |
HI |
C置位Z清零 |
无符号数大于 |
1001 |
LS |
C清零Z置位 |
无符号数小于或等于 |
1010 |
GE |
N等于V |
带符号数大于或等于 |
1011 |
LT |
N不等于V |
带符号数小于 |
1100 |
GT |
Z清零且N=V |
带符号数大于 |
1101 |
LT |
Z置位或N≠V |
带符号数小于或等于 |
1110 |
AL |
忽略 |
无条件执行 |
ADD R4 , R3 , #1
ADDEQ R4 , R3 , #1
ADDS R4 , R3 , #1
第一条指令不带条件标志(无条件AL),指令的执行不受条件标志位的影响,完成加法运算,第二条指令ADD加上了后缀EQ变为ADDEQ表示“相等则相加”,即当CPSR中Z标志置位时该指令执行,否则不执行,第三条指令的执行也不受条件标志的影响,但是由于附带了S后缀,这条指令的执行结果将影响CPSR中条件标志位的值
条件后缀只是影响指令是否执行,不影响指令的内容
条件后缀和S后缀的关系如下:
l 如果既有条件后缀又有S后缀,则书写时S排在后面
l 条件后缀是要测试条件标志位,而S后缀是要刷新条件标志位
l 条件后缀要测试的是执行前的标志位,而S后缀是根据指令结果改变条件标志
3.ARM指令分类
数据传送指令
l MOV指令
格式:MOV {<cond>} {S} Rd , operand2
功能:MOV指令将操作数operand2传送到目的寄存器Rd中,通常operand2是一个立即数、寄存器Rm或被移位的寄存器。S选项决定指令的操作是否影响CPSR中标志位的值,有S时指令执行后的结果影响CPSR中条件标志位N和Z的值,在计算第二操作数时更新标志C,不影响V标志
MOV后面如果跟的是立即数,立即数不能超过8位,往寄存器中装大于8位的可以用LDR伪指令
MOV R1 , R0 ;将寄存器R0的值传送到寄存器R1
MOV PC , R14(LR) ;将寄存器R14的值传送到PC,常用于子程序返回
MOV R1 , R0 , LSL #3 ;将寄存器R0的值左移3位后传送到R1
MOV R0,#5 ;将立即数5传送到寄存器R0
l MVN指令
格式:MVN {<cond>} {S} Rd , operand2
功能:MVN将一个寄存器、被移位的寄存器或将一个立即数传送到目的寄存器Rd,与MOV指令不同之处是:数据在传送之前被按位取反了,即把一个被取反的值传送到目的寄存器中是逻辑非操作而不是算术操作,这个取反的值加 1 才是它的取负的值:。
MVN R0 , #0 ;将立即数0取反送到寄存器R0中,完成后R0= -1
MVN R1 , R2 ;将R2取反,结果存到R1中
移位操作
ARM微处理器一个显著的特征是:在操作数进入ALU之前,对操作数进行预处理。如:指定位数的左移或右移,这种功能明显增强了数据处理操作的灵活性。这种预处理是通过ARM微处理器内嵌的桶形移位器(Barrel Shifter)来实现的。桶形移位器支持数据的各种移位操作,移位操作在ARM指令集中不作为单独的指令使用,它只能作为指令格式中的一个字段,在汇编语言中作为指令中的选项。例如,数据处理指令的第二个操作数为寄存器时,就可以加入移位操作选项对它进行各种移位操作。
l LSL(逻辑左移):
格式:Rm , LSL<op1>
功能:LSL指令可以完成对通用寄存器Rm中的内容进行逻辑左移操作,按操作数op1所指定的数量向左移位,低位用0来填充,逻辑左移一次相当于将无符号数据做乘2操作。
- op1有如下两种表示方式
①立即数控制方式 Rm , LSL#2 ,使用立即数方式时必须满足0≤立即数≤31
②寄存器控制方式,就是寄存器Rm移位的位数由另一个寄存器Rs来控制,Rs是通用寄存器,但不可以使用R15,Rm , LSL Rs
l LSR(逻辑右移)
格式:Rm , LSL<op1>
功能:LSR指令可完成对通用寄存器Rm中的数据按操作数op1所指定的数量向右移位,空出的最高位用0填充,逻辑右移一次相当于将无符号数据做除2操作。附加了S选项时,移位操作的结果还将影响CPSR中的标志位
l ASR(算术右移)
格式:Rm , ASR<op1>
功能:ASR可完成对通用寄存器中的内容按操作数所指定的数量向右移位,左端用第31位的值来填充在汇编语言中,对于算术右移,如果最高位为1,则补1,否则补0, 如将10000000算术右移7位,应该变成11111111,而逻辑右移7位,则不考虑符号位,变为00000001,这点就是算术右移和逻辑右移的区别。
l ROR(循环右移)
格式:Rm , ROR<op1>
功能:ROR可以完成对通用寄存器中的内容按操作数所指定的数量向右循环移位,左端用右端移出的位来填充
l RRX(带扩展的循环右移)
格式:Rm , RRX
功能:RRX可以完成对通用寄存器中的内容进行带扩展的循环右移一位的操作,右移时32位数据和C标志位共33位数据组成一个循环向右移一次
算术指令
l ADD(加法指令)
格式:ADD {<cond>}{S} Rd , Rn ,operand2
功能:ADD指令用于把寄存器Rn的值和操作数operand2相加,并将结果存放到Rd寄存器中,即Rd=Rn+operand2.其中Rd为目的寄存器,Rn为操作数1,要求必须是一个寄存器,operand2是操作数2,可以是一个寄存器、被移位的寄存器或一个立即数。有S时指令执行后影响CPSR中条件标志位N、Z、C、V标志。
ADD R0 , R1 , R2 ;R0=R1+R2
ADD R0 , R1 , #5 ;R0=R1+5
ADD R0 , R1 , R2 , LSL#2 ;R0=R1+(R2左移2位)
l ADC(带进位加法指令)
格式:ADC{<cond>}{S} Rd , Rn , operand2
功能:ADC指令用于把寄存器Rn的值和操作数operand2相加,再加上CPSR中的C条件标志位的值,并将结果存放到目的寄存器Rd中,即Rd=Rn+operand2+C ,Rn为操作数1,要求必须是一个寄存器,operand2是操作数2,可以是一个寄存器、被移位的寄存器或一个立即数。有S时指令执行后影响CPSR中条件标志位N、Z、C、V标志,该指令使用一个进位标志位,这样就可以做比32位大的数的加法,注意不要忘了设置S后缀来更改进位标志位。
l SUB(减法指令)
格式:SUB{<cond>}{S} Rd , Rn , operand2
功能:SUB指令用于把寄存器Rn的值减去操作数operand2,并将结果存放到目的寄存器Rd中即Rd=Rn-operand2 Rn为操作数1,要求必须是一个寄存器,operand2是操作数2,可以是一个寄存器、被移位的寄存器或一个立即数。该指令可以用于有符号数或无符号数的减法运算。有S时指令执行后影响CPSR中条件标志位N、Z、C、V标志。
SUB R0 , R1 , R2 ;R0=R1-R2
SUB R0 , R1 , #6 ;R0=R1-6
SUB R0 , R2 , R3 ,LSL#1 ;R0=R2-(R3左移一位)
l SBC(带借位减法指令)
格式:SBC{<cond>}{S} Rd , Rn , operand2
功能:SBC指令用于把寄存器Rn的值减去操作数operand2,再减去CPSR中的C条件标志位的反码,并将结果存放到目的寄存器Rd中,即Rd=Rn-operand2-!C
逻辑运算指令
l AND(逻辑与指令)
格式:AND{<cond>}{S} Rd , Rn , operand2
功能:AND指令将两个操作数按位进行逻辑与运算,结果放置到目的寄存器Rd中,即Rd=Rn AND operand2。有S时指令执行后结果影响标志位N和Z值,在计算第二个操作数时更新标志C,不影响V标志,该指令常用于将操作数1的特定为清零操作
AND R0 , R0 , #0xF ;该指令保持R0低4位其余位清零
l ORR(逻辑或指令)
格式:ORR{<cond>}{S} Rd , Rn , operand2
功能:ORR指令将两个操作数按位进行或运算,结果放置到目的寄存器Rd中,即Rd =Rn or operand2,该指令常用于将操作数1的特定位置位的操作。
ORR R0 , R0 , #5 ;将第0位和第3位置位
l EOR(逻辑异或指令)
格式:EOR{<cond>}{S} Rd , Rn , operand2
功能:EOR指令将两个操作数按位进行逻辑异或运算,并把结果放置到目的寄存器Rd中,即Rd=Rn EOR operand2。该指令常用于反转操作数1的某些位
EOR R0 , R0 , #0xF ;将R0低4位取反,其余位不变
l BIC(位清除指令)
格式:BIC{<cond>}{S} Rd , Rn , operand2
功能:BIC指令用于清除操作数Rn的某些位,并把结果放置到目的寄存器Rd中,即Rd=Rn AND (!operand2)
BIC R0 , R0 , #9 ;将R0的第0位和第3位清零
比较指令
l CMP(比较指令)
格式:CMP{<cond>} Rn , operand2
功能:CMP指令将寄存器Rn的内容和另一个操作数operand2进行比较,同时更新CPSR中的标志位的值,该指令实际上是进行一次减法运算但不存储结果,只更改条件标志位,后面的指令就可以根据条件标志位来决定是否执行
CMP R1 , #10 ;将寄存器R1的值与10相减,并设置CPSR的标志位
l CMN(反值比较指令)
格式:CMN{<cond>} Rn , operand2
功能:CMN指令用于把寄存器R0的内容和另一个操作数operand2取反后进行比较,同时更新CPSR中条件标志位的值,该指令实际上是将操作数Rn和操作数operand2相加,并根据结果更改条件标志位。
l TST(位测试指令)
格式:TST{<cond>} Rn , operand2
功能:TST指令用于把一个寄存器Rn的内容和另一个操作数operand2按位进行与运算,并根据运算结果更新CPSR中条件标志位的值,该指令通常用来检查是否设置了特定位。其中Rn是要测试的数据,这里操作数operand2可以看做一个32位的掩码,如果在掩码中设置了某一位,表示检查该位。
l TEQ(相等测试指令)
格式:TEQ{<cond>} Rn , operand2
功能:TEQ指令用于把一个寄存器Rn的内容和另一个操作数operand2按位进行异或运算,并根据运算结果更新CPSR中条件标志位,该指令通常用于比较操作数Rn和操作数operand2是否相等。
乘法指令
l MUL(32位乘法指令)
格式:MUL{<cond>}{S} Rd , Rm , Rs
功能:MUL指令完成操作数Rm与操作数Rs的乘法运算,并把结果放置到目的寄存器Rd中,即Rd=Rs*Rs
数据加载与存储指令
ARM处理器是加载/存储体系结构的处理器,对存储器的访问只能通过加载和存储指令实现,对于冯●诺依曼存储结构的ARM处理器,其程序空间、RAM空间及I/O映射空间统一编址,除了对RAM操作以外,对外围I/O、程序数据的访问都要通过加载/存储指令进行。
l LDR/STR(加载/存储字数据)
格式:LDR/STR{<cond>} {T} Rd , addr
功能:LDR指令用于从存储器中将一个32位的字数据加载到目的寄存器Rd中。该指令通常用于从存储器中读取32位的字数据到通用寄存器,然后对数据进行处理,当程序计数器PC作为目的寄存器时,指令从存储器中读取的字数据被当作目的地址,从而可以实现程序流程的跳转。
STR指令用于从元寄存器中将一个32位的字数据存储到存储器中。该指令在程序设计中比较常用,且寻址方式灵活多变,使用方式和指令LDR类似,区别在于数据的传送方向不一样。
①使用标号
LDR R4 , START;将存储地址为START的字数据读入R4
STR R5 , DATA1;将R5存入存储地址为DATA1中
②前索引
LDR R0 , [R1];将存储器地址为R1的字数据读入寄存器R0
LDR R0 , [R1 , R2];将存储器地址为R1+R2的字数据读入寄存器R0
LDR R0 , [R1 , #8];将存储器地址为R1+8的字数据读入寄存器R0
LDR R0 , [R1 , R2 , LSL#2];将存储器地址为R1+R2*4的字数据读入到寄存器R0
③自动索引
STR R0 , [R1 , R2]!;将R0字数据存入存储器地址为R1+R2的存储单元中并将新地
;址R1+R2写入到R1
STR R0 , [R1 , #8]!;将R0字数据存入存储器地址为R1+8的存储单元中并将新地
;址R1+8写入到R1
STR R0 , [R1 , R2 , LSL#2]!; 将R0字数据存入存储器地址为R1+R2*4的存储单元
;中并将新地R1+R2*4写入到R1
④后索引
LDR R0 , [R1] , #8;将存储器地址为R1的字数据读入寄存器R0,并将新地址R1+8
;写入R1
LDR R0 , [R1] , R2; 将存储器地址为R1的字数据读入寄存器R0,并将新地
;址R1+R2写入R1
LDR R0 , [R1] , R2 , LSL#2; 将存储器地址为R1的字数据读入寄存器R0,并将新
;地址R1+R2*4写入R1
堆栈操作
堆栈操作包括建栈、进栈、出栈3种基本操作
l 建栈:建栈就是规定堆栈底部在RAM存储器中的位置,例如,用户可以通过LDR伪指令设置SP的值来建立堆栈
LDR R13 , =0x90010
LDR SP , =0x90010
这时,SP指向地址0x90010,栈中无数据,堆栈底部与顶部重叠,是一个空栈
l 进栈:ARM体系结构中使用多寄存器指令完成堆栈操作,出栈用LDM指令,进栈用STM指令。LDM和STM指令往往结合下面一些参数实现堆栈的操作。
①FD满递减堆栈
②ED空递减堆栈
③FA满递增堆栈
④EA空递增堆栈
在使用堆栈的时候,要确定堆栈在存储空间中是向上生长还是向下生长的,向上称为递增(Ascending),向下称为递减(Descending)
满堆栈(Full Stack)是指堆栈指针SP(R13)指向堆栈的最后一个已使用的地址或满位置(也就是SP指向堆栈的最后一个数据项的位置);相反空堆栈(Empty Stack)是指SP指向对战的第一个没有使用的地址或空位置
在ATPCS中,堆栈被定义为满递减式堆栈,因此LDMFD和STMFD指令分别用来支持POP操作(出栈)和PUSH操作(进栈)
进栈(PUSH)就是把数据推入堆栈的操作,ARM中进栈或出栈都是以字(32位)为单位的
l 出栈:出栈(POP)就是把数据从堆栈中读出的操作
分支指令
l B(分支指令)
格式:B{<cond>} label
功能:B指令时最简单的分支指令,一旦遇到B指令,ARM处理器将立即跳转到给定的目标地址label,即PC=label,从那里继续执行。这里label表示一个符号地址,它实际上是相对当前PC的一个偏移量,而不是一个绝对地址,它由汇编器来计算,是一个24位的有符号数,左移两位后扩展为32位,然后与PC相加,即得到跳转的目的地址,跳转范围为 -32M ~ +32M
backword SUB R1 , R1 , #1
CMP R1 , #0;比较R1和0
BEQ forward;如果R1=0则跳转到forward处执行
SUB R1 , R3 , #4
SUB R1 , R1 , #1
forward ADD R1 , R2 , #4
ADD R2 , R3 , #2
B backword
l BL(带返回的分支指令)
格式:BL{<cond>} label
功能:BL指令是另一个跳转指令,与B指令不同的是,在跳转之前,将PC的当前内容保存在寄存器R14(LR)中,因此可以通过将R14的内容重新加载到PC中,返回到跳转指令之后的那个指令处执行,该指令用于实现子程序的调用,程序的返回可以通过把LR寄存器的值复制到PC寄存器来实现。
…
BL func;跳转到子程序
ADD R1 , R2 , #2;子程序调用完返回后执行的语句,返回地址
程序状态寄存器访问指令
l MRS(程序状态寄存器到通用寄存器的数据传送指令)
格式:MRS{<cond>} <Rd> , <CPSR|SPSR>
功能:MRS指令用于将程序状态寄存器的内容传送到通用寄存器中,该指令一般用在以下情况,当在异常处理或进程切换时,需要保存程序状态寄存器的值,可以先用该指令读出程序状态寄存器的值然后保存,当需要改变程序状态寄存器的内容时,可用MRS将程序状态寄存器的内容读入通用寄存器,修改后再写入程序状态寄存器。
MRS R0 , CPSR;传送CPSR的内容到R0
MRS R0 , SPSR;传送SPSR的内容到R0
l MSR(通用寄存器到程序状态寄存器的数据传送指令)
格式:MSR{<cond>} <CPSR|SPSR>_<fields> , <Rm |#immed>
功能:MSR指令用于将操作数的内容传送到程序状态寄存器的特定域中,其中状态寄存器是指CPSR或SPSR,一般指当前工作模式下的状态寄存器。操作数可以是通用寄存器Rm或立即数#immed。域<fields>用于设置程序状态寄存器中需要操作的位。
MSR CPSR , R0;传送R0的内容到CPSR
MSR SPSR , R0;传送R0的内容到SPSR
MSR CPSR_c , R1; 传送R0的内容到CPSR的控制位域
将MRS和MSR指令结合起来可以对程序状态寄存器进行修改,从而可以实现处理器模式转换,设置异常中断的开和关,正确的修改方法是对状态寄存器进行读,然后修改,再写入状态寄存器,下列例程说明了使能IRQ中断的过程
MRS R1 , CPSR
BIC R1 , R1 , #0x80
MSR CPSR _c , R1
协处理器指令
ARM微处理器可支持多达16个协处理器,用于各种协处理操作,在程序执行的过程中,每个协处理器只执行针对自身的协处理指令,忽略ARM处理器和其他协处理器的指令
l MCR(ARM处理器寄存器到协处理器寄存器的数据传送指令)
格式:MCR{<cond>}<p> , opcode1 , Rd , CRm , CRn , {opcode2}
功能:MCR指令用于将ARM处理器寄存器中的数据传送到协处理器寄存器CRm,CRn中,若不能完成操作,则产生未定义指令异常,其中协处理器操作码opcode1和opcode2为协处理器将要执行的操作,Rd为源寄存器,是ARM处理器寄存器,CRm,CRn为目的寄存器,均为协处理器的寄存器
MCR P5 , 5 , R1 , C1 , C2 , 9;将ARM处理器R1中的数据传送到协处理器P5的寄
;存器C1和C2中,协处理器执行操作5和9
l MRC(协处理器寄存器到ARM处理器寄存器的数据传送指令)
格式:MRC{<cond>}<p> , opcode1 , Rd , CRm , CRn , {opcode2}
功能:MCR指令用于将协处理器寄存器中的数据传送到ARM处理器寄存器中,若协处理器不能成功完成操作,则产生未定义指令异常,其中协处理器操作码opcode1和opcode2均为协处理器要执行的操作,Rd为目的寄存器,是ARM处理器的寄存器,CRm,CRn为源寄存器,均为协处理器的寄存器
MRC P3 , 3 , R0 , C4 , C5 , 6;协处理器P3执行操作3和6操作数为C4和C5,其
;操作的结果传送到ARM处理器寄存器R0中
ARM伪指令
l LDR(大范围的地址读取伪指令)
格式:LDR{cond} Rm ,=addr
功能:LDR指令用于加载32位立即数或一个地址值到指定寄存器,在汇编器编译源程序时,LDR伪指令被编译器替换成一条合适的指令,若加载的常数未超过MOV或MVN的范围,则使用MOV或MVN指令提花该LDR伪指令,否则,编译器将常量放入文字池,并使用一条相对偏移的LDR指令从文字池中读出常量
Rm表示要加载的目标寄存器
Addr为32位立即数或基于PC的地址表达式或外部表达式
LDR R0 ,=0x12345678;加载32位立即数0x12345678
LDR R0 ,=DATA_BUF+60
伪指令LDR常用于加载芯片外围功能部件的寄存器地址(32位立即数),以实现各种控制操作。
…
LDR R0 ,=IOPIN;加载GPIO的寄存器IOPIN的地址
…
LDR R1 , [R0];读取IOPIN寄存器的值
…
LDR R0 ,=IOSET
LDR R1 ,=0x00500500
STR R1 , [R0]; IOSET=0x00500500
l global
功能:把一个符号定义为全局的
.text
.global _start (GNU汇编中伪指令前面加点)
_start:
…
l data
功能:声明接下来的内容为数据段
.data
…
l 数据定义指令(在数据段使用)
①ascii:用来定义一个字符串
.data
hello:
.ascii “helloworld”
②byte:定义字节型的数据
.data
hello:
.byte 0x1
③word:用来定义字型变量
.data
hello:
.word 0xff
④equ:类似于C语言中的宏定义
.equ DA , 0x89
⑤align:用来控制对齐
.align 4 ;按四字节对齐