(2)(3)中介绍了ARM处理区的存储访问指令,数据处理指令,分支指令,协处理器指令等,本文对其余的指令及注意事项做一补充
在ARM指令集中杂项指令共有3条,它们非常重要,特别是与操作系统的使用息息相关:
1.软件中断产生指令:SWI
2. 程序状态寄存器读指令:MRS
3. 程序状态寄存器写指令:MSR
SWI指令用于产生软中断,主要用于用户程序调用操作系统的系统服务。执行该指令后,处理器将完成以下动作:
SWI有两种参数传递的方式
指令中的24位立即数指定了用户请求的服务类型,参数通过通用寄存器传递。 (最常用)
MOV R0,#34 ;设置子功能号为34
SWI 12 ;调用12号软中断
指令中的24位立即数被忽略,用户请求的服务类型由寄存器R0的值决定,参数通过其它的通用寄存器传递。
MOV R0,#12 ;调用12号软中断
MOV R1,#34 ;设置子功能号为34
SWI 0
在SWI异常中断处理程序中,取出SWI指令中立即数的步骤为:
1、CPSR的第六位即工作状态位,如果是1就是thumb指令,0即ARM指令。此时CPSR的内容已经复制到SPSR了。
2、【LR,#-2】是寄存器间接寻址,将-2加到LR上,取[LR-2]位置的操作数。
在ARM处理器中,只有MRS指令可以对状态寄存器CPSR和SPSR进行读操作。通过读CPSR可以了解当前处理器的工作状态。读SPSR寄存器可以了解到进入异常前的处理器状态。指令格式如下所示:
应用示例:
MRS R1,CPSR ; 读取CPSR状态寄存器到R1
MRS R2,SPSR ; 读取SPSR状态寄存器到R2
SPSR: 在每种异常模式下都有一个程序状态寄存器SPSR, SPSR用于保存CPSR状态,以便异常返回后恢复异常发生时的工作状态
在ARM处理器中,只有MSR指令可以对状态寄存器CPSR和SPSR进行写操作。与MRS配合使用,可以实现对CPSR或SPSR寄存器的读-修改-写操作,可以切换处理器模式等操作。使用时必须**精确到域,**取相应位数的数值进行写。
ARM伪指令不属于ARM指令集中的指令,是为了编程方便而定义的。伪指令可以像其它ARM指令一样使用,但在编译时这些指令将被等效的ARM指令代替
ARM伪指令有四条:
1.小范围地址读取指令:ADR
2.中等范围地址读取指令:ADRL
3.大等范围地址读取指令:LDR
4.空操作指令:NOP
ADR伪指令将基于PC相对偏移的地址值或基于寄存器相对偏移的地址值读取到寄存器中。在汇编编译器编译源程序时,ADR伪指令被编译器替换成一条合适的指令,若不能用一条指令实现,则产生错误,编译失败。
ADRL伪指令将基于PC相对偏移的地址值或基于寄存器相对偏移的地址值读取到寄存器中,比ADR伪指令可以读取更大范围的地址。在汇编编译器编译源程序时,ADRL伪指令被编译器替换成两条合适的指令(ADR只有一条)。若不能用两条指令实现,则产生错误,编译失败。
LDR伪指令用于加载32位的立即数或一个地址值到指定寄存器。在汇编编译源程序时,LDR伪指令被编译器替换成一条合适的指令。若加载的常数未超出MOV或MVN的范围,则使用MOV或MVN指令代替该LDR伪指令,否则汇编器将常量放入文字池,并使用一条程序相对偏移的LDR指令从文字池读出常量。
NOP伪指令在汇编时将会被代替成ARM中的空操作,比如可能是“MOV R0,R0”指令等。NOP可用于延时操作。
1.请使用NOP伪指令、比较指令、条件跳转指令等完成一个软件延时子程序,
延时长度由R0寄存器的数值控制?
Delay
NOP ;空操作
NOP
NOP
SUBS R0,R0,#1 ;循环次数减一
BNE Delay ;如果循环未结束,跳转Delay继续
MOV PC,LR ;子程序返回
所谓对齐就是数据在存储器中存放的规则,32位系统中一般有字节对齐(8bit)、半字对齐(16bit)、字对齐(32bit)三种方式,分别对齐到连续地址、偶数地址、被4整除的地址。
ARM体系结构中有ARM指令集和Thumb指令集2种,其中ARM指令为32位指令,按照4字节对齐存储,一条指令必须从4的整数倍地址来取;Thumb指令为16位指令,按2字节对齐存储,一条指令必须从偶数地址来取。
当如果一个字访问的地址是0x0003时,此时为非地址对准,将产生数据异常。
程序中可以使用一些措施保证地址对准,最方便的方法就是使用地址对准伪指令ALIGN;除此以外,也可以填充空指令NOP,空指令NOP被编译成2字节。
Cortex-M3虽然支持非对准的地址访问,但是只对下列指令可以:LDR,LDRT,LDRH,LDRHT,LDRSH,LDRSHT,STR,STRT,STRH,STRHT;而对于其他指令仍然不支持非对准的访问。因此需要注意地址对准的应用,不可以随意使用非地址对准方法。
Cortex-M3也可以实现位的操作,但并不是所有的存储区域都可以实现位操作,只有两个位操作区,也称为位域。
①LDR R2,[R3,#0x30] ②LDR R2,[R3],#0x30
③LDR R2,[R3,#-0x20] ④LDR R2,[R3],#-0x20
⑤LDR R2,[R3,#0x20+0x20/2] ⑥LDR R2,[R3],#0x20*4-0x10
第1条语句是地址前缀,这个前缀的含义是把R3+0x30地址处的数据加载给R2,注意先把R3加0x30;第2条语句是后缀,这个后缀的含义是把R3地址处的数据加载给R2,然后计算R3=R3+0x30,注意后把R3加0x30。前缀是不改变值的(想要改变必须加感叹号),后缀的地址是寄存器指向的地址,地址传输后再修改寄存器的值。
S后缀的含义是:使用S后缀时,指令执行后程序状态寄存器的条件标志位将刷新;不使用S后缀,指令执行后程序状态寄存器的条件标志位将不发生变化。
指令举例如下:
ADD R1,R2,R3
; 没有使用S后缀,条件标志位不刷新
ADDS R1,R2,R3
; 使用S后缀,条件标志位刷新
有些指令不需要加S后缀,在执行时同样刷新条件标志位,如比较指令CMP、CMN、测试指令TST等。
S后缀使用目的:在需要对条件进行测试,例如:是否有溢出,是否有进位,是否大于或小于等。
!后缀的含义是:在指令的地址表达式中含有!后缀时,指令执行后,基址寄存器中的地址将发生变化,变化的结果如下:
指令举例如下:
LDR R2,[R1,#02]
;没有!后缀,结果是把R1加2作为地址指针存储的数据赋给R2,R1值不变
LDR R2,[R1,#02]!
;有!后缀,结果是把R1加2作为地址指针存储的数据赋给R2,R1加2的结果送到R1中
B后缀的含义是:指令所涉及的数据是一个8位字节,不是一个字或半字。
H后缀的含义是:指令所涉及的数据是一个16位半字,不是一个字或字节。
指令举例:
LDR R2,[R0,#20] ;R2<-[R0+0x20]传送一个32位字
LDRB R2,[R0,#20] ;R2<-[R0+0x20]传送一个8位字节
LDRH R2,[R0,#20] ;R2<-[R0+0x20]传送一个半字
MOV R0,#0x500 (R0: 0x500:0000 1001 0000 0000)
ADD R1,R0,#0x06 (R1=R0+0x06=0000 1001 0000 1100)注意0x06的表示,前导0可以省略
AND R2, R0,#0x07 (R2=0x500&0x0007=0)
LDR R3,=0x12345678 (R3=0x12345678)
STR R3,[R0] (R3的内容存储到R0所在地址,R3有多个字节,遵循小断模式,以R0指向地址为起点,低位字节排放在内存的低地址端,见下图)
LDRB R4,[R0,#2] (R4=[R0+2]=[0x502]=0x34)
LDRB R5,[R0] (R5=[R0]=0x78)
Thumb指令集可以看作是ARM指令压缩形式的子集,它是为减小代码量而提出的,具有16位的代码密度。Thumb指令体系不完整,只支持通用功能。必要时仍需要使用ARM指令,如进入异常时。
Thumb指令集较ARM指令集有如下限制:
只有B指令可以条件执行,其它指令都不能条件执行;
分支指令的跳转范围有更多限制;
数据处理指令的操作结果必须放入其中一个操作数寄存器中,而不是第三个寄存器;
单寄存器访问指令,只能操作R0~R7;
LDM和STM指令可以对R0~R7的任何子集进行操作;