C语言实现无条件状态切换,ARM指令集详解

ARM

可以用两套指令集:ARM指令集和Thumb指令集。本文介绍ARM指令集。在介绍ARM指令集之前,先介绍指令的格式。

1

指令格式

(1)基本格式

{}{S} ,{,}

其中,<>内的项是必须的,{}内的项是可选的,如是指令助记符,是必须的,而{}为

指令执行条件,是可选的,如果不写则使用默认条件AL(无条件执行)。

opcode  指令助记符,如LDR,STR 等

cond  执行条件,如EQ,NE 等

S  是否影响CPSR 寄存器的值,书写时影响CPSR,否则不影响

Rd  目标寄存器

Rn  第一个操作数的寄存器

operand2  第二个操作数

指令格式举例如下:

LDR R0,[R1] ;读取R1 地址上的存储器单元内容,执行条件AL

BEQ DATAEVEN ;跳转指令,执行条件EQ,即相等跳转到DATAEVEN

ADDS R1,R1,#1

;加法指令,R1+1=R1 影响CPSR 寄存器,带有S

SUBNES

R1,R1,#0xD;条件执行减法运算(NE),R1-0xD=>R1,影响CPSR 寄存器,带有S

(2)第2个操作数

在ARM 指令中,灵活的使用第2个操作数能提高代码效率,第2个操作数的形式如下:

#immed_8r

常数表达式,该常数必须对应8 位位图,即常数是由一个8 位的常数循环移位偶数位得到。

合法常量

0x3FC、0、0xF0000000、200、0xF0000001等都是合法常量。

非法常量

0x1FE、511、0xFFFF、0x1010、0xF0000010等都是非法常量。

常数表达式应用举例如下:

MOV R0,#1 ;R0=1

AND R1,R2,#0x0F ;R2

与0x0F,结果保存在R1

LDR R0,[R1],#-4 ;读取R1 地址上的存储器单元内容,且R1=R1-4

Rm

寄存器方式,在寄存器方式下操作数即为寄存器的数值。

寄存器方式应用举例:

SUB R1,R1,R2 ;R1-R2=>R1

MOV PC,R0 ;PC=R0,程序跳转到指定地址

LDR R0,[R1],-R2 ;读取R1 地址上的存储器单元内容并存入R0,且R1=R1-R2

Rm, shift

寄存器移位方式。将寄存器的移位结果作为操作数,但RM 值保存不变,移位方法如下:

ASR #n  算术右移n

位(1≤n≤32)

LSL #n  逻辑左移n 位(1≤n≤31)

LSR #n  逻辑左移n

位(1≤n≤32)

ROR #n  循环右移n 位(1≤n≤31)

RRX  带扩展的循环右移1位

type Rs  其中,type 为ASR,LSL,和ROR 中的一种;Rs 偏移量寄存器,低8位有效,若其值大于或等于32,则第2

个操作数的结果为0(ASR、ROR例外)。

寄存器偏移方式应用举例:

ADD

R1,R1,R1,LSL #3 ;R1=R1*9

SUB R1,R1,R2,LSR#2 ;R1=R1-R2*4

R15 为处理器的程序计数器PC,一般不要对其进行操作,而且有些指令是不允许使用R15,如UMULL 指令。

(3)条件码

使用指令条件码,可实现高效的逻辑操作,提高代码效率。表A-1给出条件码表。

表A-1  条件码表

4670178_1.jpg

对于Thumb指令集,只有B

指令具有条件码执行功能,此指令条件码同表A-?,但如果为无条件执行时,条件码助记符“AL”不能在指令中书写。

条件码应用举例如下:

比较两个值大小,并进行相应加1 处理,C 代码为:

if(a>b)a++

;

else b++            ;

对应的ARM

指令如下。其中R0为a,R1为b。

CMP R0,R1         ; R0 与R1 比较

ADDHI R0,R0,#1         ; 若R0>R1,则R0=R0+1

ADDLS R1,R1,#1

;若R0<=R1,则R1=R1+1

若两个条件均成立,则将这两个数值相加,C代码为:

If((a!=10)&&(b!=20)) a=a+b;

对应的ARM 指令如下,其中R0 为a,R1

为b。

CMP R0,#10             ; 比较R0 是否为10

CMPNE

R1,#20         ; 若R0 不为10,则比较R1 是否20

ADDNE R0,R0,R1         ;

若R0 不为10 且R1 不为20,指令执行,R0=R0+R1

2  ARM 存储器访问指令

ARM 处理是加载/存储体系结构的典型的RISC 处理器,对存储器的访问只能使用加载和存储指令实现。ARM

的加载/存储指令是可以实现字、半字、无符/有符字节操作;批量加载/存储指令可实现一条指令加载/存储多个寄存器的内容,大大提高效率;SWP指令是一

条寄存器和存储器内容交换的指令,可用于信号量操作等。ARM 处理器是冯?诺依曼存储结构,程序空间、RAM 空间及IO

映射空间统一编址,除对对RAM 操作以外,对外围IO、程序数据的访问均要通过加载/存储指令进行。表A-2给出ARM 存储访问指令表。

表A-2  ARM 存储访问指令表

4670178_2.jpg

LDR 和STR

加载/存储字和无符号字节指令。使用单一数据传送指令(STR 和LDR)来装载和存储单一字节或字的数据从/到内存。LDR

指令用于从内存中读取数据放入寄存器中;STR 指令用于将寄存器中的数据保存到内存。指令格式如下:

LDR{cond}{T}

Rd,;加载指定地址上的数据(字),放入Rd 中

STR{cond}{T}

Rd,;存储数据(字)到指定地址的存储单元,要存储的数据在Rd中

LDR{cond}B{T}

Rd,;加载字节数据,放入Rd中,即Rd最低字节有效,高24位清零

STR{cond}B{T}

Rd,;存储字节数据,要存储的数据在Rd,最低字节有效

其中,T

为可选后缀,若指令有T,那么即使处理器是在特权模式下,存储系统也将访问看成是处理器是在用户模式下。T

在用户模式下无效,不能与前索引偏移一起使用T。

LDR/STR

指令寻址是非常灵活的,由两部分组成,一部分为一个基址寄存器,可以为任一个通用寄存器,另一部分为一个地址偏移量。地址偏移量有以下3 种格式:

(1) 立即数。立即数可以是一个无符号数值,这个数据可以加到基址寄存器,也可以从基址寄存器中减去这个数值。指令举例如下:

LDR R1,[R0,#0x12] ;将R0+0x12 地址处的数据读出,保存到R1 中(R0 的值不变)

LDR

R1,[R0,#-0x12];将R0-0x12 地址处的数据读出,保存到R1 中(R0 的值不变)

LDR R1,[R0]

;将R0 地址处的数据读出,保存到R1 中(零偏移)

(2)寄存器。寄存器中的数值可以加到基址寄存器,也可以从基址寄存器中减去这个数值。指令举例值。指令举例如下:

LDR

R1,[R0,R2] ;将R0+R2 地址的数据计读出,保存到R1 中(R0 的值不变)

LDR R1,[R0,-R2]

;将R0-R2 地址处的数据计读出,保存到R1 中(R0 的值不变)

(3)寄存器及移位常数。寄存器移位后的值可以加到基址寄存器,也可以从基址寄存器中减去这个数值。指令举例如下:

LDR

R1,[R0,R2,LSL #2] ;将R0+R2*4地址处的数据读出,保存到R1中(R0,R2的值不变)

LDR

R1,[R0,-R2,LSL #2];将R0-R2*4地址处的数据计读出,保存到R1中(R0,R2的值不变)

从寻址方式的地址计算方法分,加载/存储指令有以下4 种形式:

(1)零偏移。Rn

的值作为传送数据的地址,即地址偏移量为0。指令举例如下:

LDR Rd,[Rn]

(2)前索引偏移。在数据传送之前,将偏移量加到Rn 中,其结果作为传送数据的存储地址。若使用后缀“!”,则结果写回到Rn 中,且Rn

值不允许为R15。指令举例如下:

LDR Rd,[Rn,#0x04]!

LDR

Rd,[Rn,#-0x04]

(3)程序相对偏移。程序相对偏移是索引形式的另一个版本。汇编器由PC

寄存器计算偏移量,并将PC 寄存器作为Rn 生成前索引指令。不能使用后缀“!”。指令举例如下:

LDR Rd,label

;label 为程序标号,label 必须是在当前指令的±4KB 范围内

(4) 后索引偏移。Rn

的值用做传送数据的存储地址。在数据传送后,将偏移量与Rn相加,结果写回到Rn 中。Rn 不允许是R15。指令举例如下:

LDR Rd,[Rn],#0x04

地址对准--大多数情况下,必须保证用于32 位传送的地址是32 位对准的。

加载/存储字和无符号字节指令举例如下:

LDR R2,[R5] ;加载R5 指定地址上的数据(字),放入R2 中

STR R1,[R0,#0x04] ;将R1 的数据存储到R0+0x04 存储单元,R0 值不变

LDRB

R3,[R2],#1 ;读取R2 地址上的一字节数据,并保存到R3 中,R2=R3+1

STRB R6,[R7] ;读R6

的数据保存到R7 指定的地址中,只存储一字节数据

加载/存储半字和带符号字节。这类LDR/STR

指令可能加载带符字节\加载带符号半字、加载/存储无符号半字。偏移量格式、寻址方式与加载/存储字和无符号字节指令相同。指令格式如下:

LDR{cond}SB Rd, ;加载指定地址上的数据(带符号字节),放入Rd 中

LDR{cond}SH Rd, ;加载指定地址上的数据(带符号字节),放入Rd 中

LDR{cond}H Rd, ;加载半字数据,放入Rd中,即Rd最低16 位有效,高16位清零

STR{cond}H Rd, ;存储半字数据,要存储的数据在Rd,最低16 位有效

说明:带符号位半字/字节加载是指带符号位加载扩展到32 位;无符号位半字加载是指零扩展到32 位。

地址对准--对半字传送的地址必须为偶数。非半字对准的半字加载将使Rd 内容不可靠,非半字对准的半字存储将使指定地址的2 字节存储内容不可靠。

加载/存储半字和带符号字节指令举例如下:

LDRSB R1[R0,R3] ;将R0+R3

地址上的字节数据读出到R1,高24 位用符号位扩展

LDRSH R1,[R9] ;将R9

地址上的半字数据读出到R1,高16 位用符号位扩展

LDRH R6,[R2],#2 ;将R2

地址上的半字数据读出到R6,高16 位用零扩展,R2=R2+1

SHRH R1,[R0,#2]!;将R1

的数据保存到R2+2 地址中,只存储低2 字节数据,R0=R0+2

LDR/STR

指令用于对内存变量的访问,内存缓冲区数据的访问、查表、外设的控制操作等等,若使用LDR 指令加载数据到PC

寄存器,则实现程序跳转功能,这样也就实现了程序散转。

变量的访问

NumCount EQU

0x40003000 ;定义变量NumCount

LDR R0,=NumCount ;使用LDR

伪指令装载NumCount 的地址到R0

LDR R1,[R0] ;取出变量值

ADD

R1,R1,#1 ;NumCount=NumCount+1

STR R1,[R0] ;保存变量值

GPIO 设置

GPIO-BASE EQU 0Xe0028000 ;定义GPIO 寄存器的基地址

LDR R0,=GPIO-BASE

LDR R1,=0x00FFFF00 ;装载32 位立即数,即设置值

STR R1,[R0,#0x0C] ;IODIR=0x00FFFF00, IODIR 的地址为0xE002800C

MOV R1,#0x00F00000

STR R1,[R0,#0x04] ;IOSET=0x00F00000,IOSET

的地址为0xE0028004

程序散转

MOV

R2,R2,LSL #2 ;功能号乘上4,以便查表

LDR PC,[PC,R2] ;查表取得对应功能子程序地址,并跳转

NOP

FUN-TAB DCD FUN-SUB0

DCD FUN-SUB1

DCD FUN-SUB2

LDM 和STM

批量加载/存储指令可以实现在一组寄存器和一块连续的内存单元之间传输数据。LDM 为加载多个寄存器,STM

为存储多个寄存器。允许一条指令传送16 个寄存器的任何子集或所有寄存器。指令格式如下:

LDM{cond} Rn{!},reglist{^}

STM{cond}

Rn{!},reglist{^}

LDM /STM 的主要用途是现场保护、数据复制、参数传送等。其模式有8

种,如下所列:(前面4 种用于数据块的传输,后面4 种是堆栈操作)。

(1) IA:每次传送后地址加4

(2) IB:每次传送前地址加4

(3) DA:每次传送后地址减4

(4)

DB:每次传送前地址减4

(5) FD:满递减堆栈

(6) ED:空递增堆栈

(7) FA:满递增堆栈

(8) EA:空递增堆栈

其中,寄存器Rn

为基址寄存器,装有传送数据的初始地址,Rn 不允许为R15;后缀“!”表示最后的地址写回到Rn 中;寄存器列表reglist

可包含多于一个寄存器或寄存器范围,使用“,”分开,如{R1,R2,R6-R9},寄存器排列由小到大排列;“^”后缀不允许在用户模式呈系统模式下使

用,若在LDM 指令用寄存器列表中包含有PC 时使用,那么除了正常的多寄存器传送外,将SPSR 拷贝到CPSR

中,这可用于异常处理返回;使用“^”后缀进行数据传送且寄存器列表不包含PC 时,加载/存储的是用户模式的寄存器,而不是当前模式的寄存器。

地址对准――这些指令忽略地址的位[1:0]。

批量加载/存储指令举例如下:

LDMIA

R0!,{R3-R9} ;加载R0 指向的地址上的多字数据,保存到R3~R9 中,R0 值更新

STMIA

R1!,{R3-R9} ;将R3~R9 的数据存储到R1 指向的地址上,R1 值更新

STMFD

SP!,{R0-R7,LR} ;现场保存,将R0~R7、LR 入栈

LDMFD

SP!,{R0-R7,PC}^;恢复现场,异常处理返回

在进行数据复制时,先设置好源数据指针,然后使用块拷贝寻址指令LDMIA/STMIA、LDMIB/STMIB、LDMDA/STMDA、LDMDB

/STMDB 进行读取和存储。而进行堆栈操作时,则要先设置堆栈指针,一般使用SP

然后使用堆栈寻址指令STMFD/LDMFD、STMED。LDMED、STMFA/LDMFA、STMEA/LDMEA 实现堆栈操作。

多寄存器传送指令示意图如图A-1所示,其中R1为指令执行前的基址寄存器,R1’则为指令执行完后的基址寄存器。

4670178_3.jpg

(a)指令STMIA R1!,{R5-R7}

(b)指令STMIB R1!,{R5-R7}

  4670178_4.jpg

(c)指令STMDA R1!, {R5-R7}

(d)指令STMDB R1!,{R5-R7}

图A-1  多寄存器传送指令示意图

数据是存储在基址寄存器的地址之上还是之下,地址是在存储第一个值之前还是之后增加还是减少。表A-3给出多寄存器传送指令映射示意表。

表A-3  多寄存器传送指令映射示意表

4670178_5.jpg

使用LDM/STM 进行数据复制例程如下:

LDR R0,=SrcData ;设置源数据地址

LDR R1,=DstData ;设置目标地址

LDMIA R0,{R2-R9} ;加载8 字数据到寄存器R2~R9

STMIA R1,{R2-R9}

;存储寄存器R2~R9 到目标地址

使用LDM/STM 进行现场寄存器保护,常在子程序中或异常处理使用:

SENDBYTE

STMFD SP!,{R0-R7,LR} ;寄存器入堆

BL DELAY ;调用DELAY 子程序

LDMFD SP!,{R0-R7,PC}

;恢复寄存器,并返回

SWP

寄存器和存储器交换指令。SWP指令用于将一个内存单元(该单元地址放在寄存器Rn中)的内容读取到一个寄存器Rd 中,同时将另一个寄存器Rm

的内容写入到该内存单元中。使用SWP 可实现信号量操作。

指令格式如下:

SWP{cond}{B}

Rd,Rm,[Rn]

其中,B 为可选后缀,若有B,则交换字节,否则交换32 位字:Rd

为数据从存储器加载到的寄存器;Rm 的数据用于存储到存储器中,若Rm 与Rn 相同,则为寄存器与存储器内容进行交换;Rn

为要进行数据交换的存储器地址,Rn 不能与Rd 和Rm 相同。

SWP 指令举例如下:

SWP

R1,R1,[R0] ; 将R1 的内容与R0 指向的存储单元的内容进行交换

SWP R1,R2,,[R0] ; 将R0

指向的存储单元内容读取一字节数据到R1 中(高24 位清零)

; 并将R2 的内容写入到该内存单元中(最低字节有效)

使用SWP 指令可以方便地进行信号量的操作:

12C_SEM EQU 0x40003000

12C_SEM_WAIT

MOV R0,#0

LDR R0,=12C_SEM

SWP R1,R1,[R0]

;取出信号量,并设置其为0

CMP R1,#0             ;判断是否有信号

BEQ

12C_SEM_WAIT      ;若没有信号,则等待

3  ARM 数据处理指令

数据处理指令大致可分为3

类;数据传送指令(如MOV、MVN),算术逻辑运算指令(如ADD,SUM,AND),比较指令(如CMP、TST)。数据处理指令只能对寄存器的内容

进行操作。

所有ARM 数据处理指令均可选择使用S

后缀,以影响状态标志。比较指令CMP、CMN、TST和TEQ不需要后缀S,它们会直接影响状态标志。ARM 数据处理指令列于表A-4中。

表A-4  ARM 数据处理指令

4670178_6.jpg

(1)数据传送指令

MOV

数据传送指令。将8 位图立即数或寄存器(operant2)传送到目标寄存器Rd,可用于移位运算等操作。指令格式如下:

MOV{cond}{S} Rd,operand2

MOV 指令举例如下:

MOV R1#0x10

;R1=0x10

MOV R0,R1 ;R0=R1

MOVS R3,R1,LSL #2

;R3=R1<<2,并影响标志位

MOV PC,LR   ;PC=LR ,子程序返回

MVN

数据非传送指令。将8

位图立即数或寄存器(operand2)按位取反后传送到目标寄存器(Rd),因为其具有取反功能,所以可以装载范围更广的立即数。指令格式如下:

MVN{cond}{S} Rd,operand2

MVN 指令举例如下:

MVN R1,#0xFF

;R1=0xFFFFFF00

MVN R1,R2 ;将R2 取反,结果存到R1

(2)算术逻辑运算指令

ADD

加法运算指令。将operand2

数据与Rn 的值相加,结果保存到Rd 寄存器。指令格式如下:

ADD{cond}{S} Rd,Rn,operand2

ADD 指令举例如下:

ADDS R1,R1,#1 ;R1=R1+1

ADD R1,R1,R2

;R1=R1+R2

ADDS R3,R1,R2,LSL #2 ;R3=R1+R2<<2

SUB

减法运算指令。用寄存器Rn 减去operand2。结果保存到Rd 中。指令格式如下:

SUB{cond}{S} Rd,Rn,operand2

SUB 指令举例如下:

SUBS

R0,R0,#1 ;R0=R0-1

SUBS R2,R1,R2 ;R2=R1-R2

SUB

R6,R7,#0x10 ;R6=R7-0x10

RSB

逆向减法指令。用寄存器operand2 减法Rn,结果保存到Rd 中。指令格式如下:

RSB{cond}{S}

Rd,Rn,operand2

SUB 指令举例如下:

RSB R3,R1,#0xFF00

;R3=0xFF00-R1

RSBS R1,R2,R2,LSL #2 ;R1=R2<<2-R2=R2×3

RSB R0,R1,#0 ;R0=-R1

ADC

带进位加法指令。将operand2 的数据与Rn 的值相加,再加上CPSR 中的C 条件标志位。结果保存到Rd 寄存器。指令格式如下:

ADC{cond}{S} Rd,Rn,operand2

ADC 指令举例如下:

ADDS

R0,R0,R2

ADC R1,R1,R3 ;使用ADC 实现64 位加法,(R1、R0)=(R1、R0)+(R3、R2)

SBC

带进位减法指令。用寄存器Rn 减去operand2,再减去CPSR 中的C

条件标志位的非(即若C 标志清零,则结果减去1),结果保存到Rd 中。指令格式如下:

SCB{cond}{S}Rd,Rn,operand2

SBC 指令举例如下:

SUBS

R0,R0,R2

SBC R1,R1,R3 ;使用SBC 实现64 位减法,(R1,R0)-(R3,R2)

RSC

带进位逆向减法指令。用寄存器operand2 减去Rn,再减去CPSR 中的C

条件标志位,结果保存到Rd 中。指令格式如下:

RSC{cond}{S} Rd,Rn,operand2

RSC 指令举例如下:

RSBS R2,R0,#0

RSC R3,R1,#0 ;使用RSC

指令实现求64 位数值的负数

AND

逻辑与操作指令。将operand2 值与寄存器Rn 的值按位作逻辑与操作,结果保存到Rd 中。指令格式如下:

AND{cond}{S} Rd,Rn,operand2

AND 指令举例如下:

ANDS

R0,R0,#x01 ;R0=R0&0x01,取出最低位数据

AND R2,R1,R3 ;R2=R1&R3ORR

逻辑或操作指令。将operand2 的值与寄存器Rn 的值按位作逻辑或操作,结果保存到Rd 中。指令格式如下:

ORR{cond}{S} Rd,Rn,operand2

ORR 指令举例如下:

ORR

R0,R0,#x0F ;将R0 的低4 位置1

MOV R1,R2,LSR #4

ORR

R3,R1,R3,LSL #8 ;使用ORR 指令将近R2 的高8 位数据移入到R3 低8 位中

EOR

逻辑异或操作指令。将operand2 的值与寄存器Rn 的值按位作逻辑异或操作,结果保存到Rd

中。指令格式如下:

EOR{cond}{S}Rd,Rn,operand2

EOR 指令举例如下:

EOR R1,R1,#0x0F ;将R1 的低4 位取反

EOR R2,R1,R0 ;R2=R1^R0

EORS R0,R5,#0x01 ;将R5 和0x01 进行逻辑异或,结果保存到R0,并影响标志位BIC

位清除指令。将寄存器Rn

的值与operand2 的值的反码按位作逻辑与操作,结果保存到Rd 中。指令格式如下:

BIC{cond}{S}Rd,Rn,operand2

BIC 指令举例如下:

BIC

R1,R1,#0x0F ;将R1 的低4 位清零,其它位不变

BIC R1,R2,R3 ;将拭的反码和R2

相逻辑与,结果保存到R1

(3)比较指令

CMP

比较指令。指令使用寄存器Rn 的值减去operand2

的值,根据操作的结果更新CPSR中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。指令格式如下:

CMP{cond} Rn,operand2

CMP 指令举例如下:

CMP R1,#10 ;R1

与10 比较,设置相关标志位

CMP R1,R2 ;R1 与R2 比较,设置相关标志位

CMP

指令与SUBS 指令的区别在于CMP 指令不保存运算结果。在进行两个数据大小判断时,常用CMP 指令及相应的条件码来操作。

CMN

负数比较指令。指令使用寄存器Rn 与值加上operand2 的值,根据操作的结果更新CPSR

中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行,指令格式如下:

CMN{cond}

Rn,operand2

CMN R0,#1 ;R0+1,判断R0 是否为1 的补码,若是Z 置位

CMN 指令与ADDS 指令的区别在于CMN 指令不保存运算结果。CMN 指令可用于负数比较,比如CMNR0,#1 指令则表示R0 与-1

比较,若R0 为-(即1 的补码),则Z 置位,否则Z复位。

TST

位测试指令。指令将寄存器Rn 的值与operand2 的值按位作逻辑与操作,根据操作的结果更新CPSR

中相应的条件标志位,以便后面指令根据相应的条件标志来判断是否执行。指令格式如下:

TST{cond}

Rn,operand2

TST 指令举例如下:

TST R0,#0x01 ;判断R0

的最低位是否为0

TST R1,#0x0F ;判断R1 的低4 位是否为0

TST 指令与ANDS

指令的区别在于TST4 指令不保存运算结果。TST 指令通常于EQ、NE条件码配合使用,当所有测试位均为0 时,EQ

有效,而只要有一个测试为不为0,则NE 有效。

TEQ

相等测试指令。指令寄存器Rn 的值与operand2 的值按位作逻辑异或操作,根据操作的结果更新CPSR

中相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。指令格式如下:

TEQ{cond}

Rn,operand2

TEQ 指令举例如下:

TEQ R0,R1 ;比较R0 与R1

是否相等(不影响V 位和C 位)

TST 指令与EORS 指令的区别在于TST 指令不保存运算结果。使用TEQ

进行相等测试,常与EQNE 条件码配合使用,当两个数据相等时,EQ 有效,否则NE 有效。

(4)乘法指令

ARM7TDMI(-S)具有32×32 乘法指令、32×32 乘加指令、32×32 结果为64 位的乘法指令。表A-5给出全部的ARM

乘法指令。

表A-5  全部的ARM 乘法指令

4670178_7.jpg

MUL

32

位乘法指令。指令将Rm 和Rs 中的值相乘,结果的低32 位保存到Rd 中。指令格式如下:

MUL{cond}{S}

Rd,Rm,Rs

MUL 指令举例如下:

MUL R1,R2,R3 ;R1=R2×R3

MULS R0,R3,R7 ;R0=R3×R7,同时设置CPSR 中的N 位和Z 位

MLA

32 位乘加指令。指令将Rm 和Rs 中的值相乘,再将乘积加上第3 个操作数,结果的低32 位保存到Rd 中。指令格式如下:

MLA{cond}{S} Rd,Rm,Rs,Rn

MLA 指令举例如下:

MLA

R1,R2,R3,R0 ;R1=R2×R3+10UMULL

64 位无符号乘法指令。指令将Rm 和Rs

中的值作无符号数相乘,结果的低32 位保存到RsLo 中,而高32 位保存到RdHi 中。指令格式如下:

UMULL{cond}{S} RdLo,RdHi,Rm,Rs

UMULL 指令举例如下:

UMULL

R0,R1,R5,R8 ;(R1、R0)=R5×R8

UMLAL

64 位无符号乘加指令。指令将Rm 和Rs 中的值作无符号数相乘,64 位乘积与RdHi、RdLo 相加,结果的低32 位保存到RdLo

中,而高32 位保存到RdHi 中。指令格式如下:

UMLAL{cond}{S} RdLo,RdHi,Rm,Rs

UMLAL 指令举例如下:

UMLAL R0,R1,R5,R8 ;(R1,R0)=R5×R8+(R1,R0)SMULL

64 位有符号乘法指令。指令将Rm

和Rs 中的值作有符号数相乘,结果的低32 位保存到RdLo 中,而高32 位保存到RdHi 中。指令格式如下:

SMULL{cond}{S} RdLo,RdHi,Rm,Rs

SMULL 指令举例如下:

SMULL

R2,R3,R7,R6 ;(R3,R2)=R7×R6

SMLAL

64 位有符号乘加指令。指令将Rm 和Rs 中的值作有符号数相乘,64 位乘积与RdHi、RdLo,相加,结果的低32 位保存到RdLo

中,而高32 位保存到RdHi 中。指令格式如下:

SMLAL{cond}{S} RdLo,RdHi,Rm,Rs

SMLAL 指令举例如下:

SMLAL R2,R3,R7,R6 ;(R3,R2)=R7×R6+(R3,R2)

4

ARM 跳转指令

在ARM

中有两种方式可以实现程序的跳转,一种是使用跳转指令直接跳转,另一种则是直接向PC 寄存器赋值实现跳转。跳转指令有跳转指令B,带链接的跳转指令BL

带状态切换的跳转指令BX。表A-6给出全部的ARM跳转指令。

表A-6  ARM跳转指令

4670178_8.jpg

B

跳转指令。跳转到指定的地址执行程序。指令格式如下:

B{cond} label

跳转指令B

举例如下:

B WAITA ;跳转到WAITA 标号处

B 0x1234

;跳转到绝对地址0x1234 处

跳转到指令B 限制在当前指令的±32Mb 的范围内。

BL

带链接的跳转指令。指令将下一条指令的地址拷贝到R14(即LR)链接寄存器中,然后跳转到指定地址运行程序。指令格式如下:

BL{cond} label

带链接的跳转指令BL 举例如下:

BL DELAY

跳转指令B 限制在当前指令的±32MB 的范围内。BL 指令用于子程序调用。

BX

带状态切换的跳转指令。跳转到Rm 指定的地址执行程序,若Rm 的位[0]为1,则跳转时自动将CPSR 中的标志T

置位,即把目标地址的代码解释为Thumb 代码;若Rm 的位[0]为0,则跳转时自动将CPSR 中的标志T 复位,即把目标地址的代码解释为ARM

代码。指令格式如下:

BX{cond} Rm

带状态切换的跳转指令BX 举例如下:

ADRL R0,ThumbFun+1

BX R0 ;跳转到R0 指定的地址,并根据R0 的最低位来切换处理器状态

5   ARM 协处理器指令        ARM

支持协处理器操作,协处理器的控制要通过协处理器命令实现。表A-7给出全部的ARM协处理器指令。

表A-7  ARM 协处理器指令

4670178_9.jpg

CDP

协处理器数据操作指令。ARM 处理器通过CDP 指令通知ARM

协处理器执行特定的操作。该操作由协处理器完成,即对命令的参数的解释与协处理器有关,指令的使用取决于协处理器。若协处理器不能成功地执行该操作,将产

生未定义指令异常中断。指令格式如下:

CDP{cond}

coproc,opcodel,CRd,CRn,CRm{,opcode2}

其中: coproc

指令操作的协处理器名。标准名为pn,n 为0~15。

opcodel 协处理器的特定操作码。

CRd

作为目标寄存器的协处理器寄存器。

CRN 存放第1 个操作数的协处理器寄存器。

CRm 存放第2

个操作数的协处理器寄存器。

Opcode2 可选的协处理器特定操作码。

CDP 指令举例如下:

CDP p7,0,c0,c2,c3,0 ;协处理器7 操作,操作码为0,可选操作码为0

CDP

p6,1,c3,c4,c5 ;协处理器操作,操作码为1

LDC

协处理器数据读取指令。LDC

指令从某一连续的内存单元将数据读取到协处理器的寄存器中。协处理器数据的数据的传送,由协处理器来控传送的字数。若协处理器不能成功地执行该操作,将产

生未定义指令异常中断。指令格式如下:

LDC{cond}{L} coproc,CRd,

其中: L 可选后缀,指明是长整数传送。

coproc 指令操作的协处理器名。标准名为pn,n 为0~15

CRd 作为目标寄存的协处理器寄存器。

指定的内存地址

LDC

指令举例如下:

LDC p5,c2,[R2,#4];读取R2+4指向的内存单元的数据,传送到协处理器p5的c2寄存器中

LDC p6,c2,[R1] ;读取是指向的内存单元的数据,传送到协处理器p6 的c2 寄存器中

STC

协处理器数据写入指令。STC

指令将协处理器的寄存器数据写入到某一连续的内存单元中。进行协处理器数据的数据传送,由协处理器来控制传送的字数。若协处理器不能成功地执行该操作,将

产生未定义指令异常中断。指令格式如下:

STC{cond}{L} coproc,CRd,

其中: L 可选后缀,指明是长整数传送。

coproc 指令操作的协处理器名。标准名为pn,n 为0~15

CRd 作为目标寄存的协处理器寄存器。

指定的内存地址

STC

指令举例如下:

STC p5,c1,[R0]

STC p5,c1,[Ro,#-0x04]

MCR

ARM 寄存器到协处理器寄存器的数据传送指令。MCR 指令将ARM

处理器的寄存器中的数据传送到协处理器的寄存器中。若协处理器不能成功地执行该操作,将产生未定义指令异常中断。指令格式如下:

MCR{cond} coproc,opcodel,Rd,CRn,CRm{,opcode2}

其中:coproc

指令操作的协处理器名。标准名为pn,n 为0~15。

cpcodel 协处理器的特定操作码。

CRD

作为目标寄存器的协处理器寄存器。

CRn 存放第1 个操作数的协处理器寄存器

CRm 存放第2

个操作数的协处理器寄存器。

Opcode2 可选的协处理器特定操作码。

MCR 指令举例如下:

MCR p6,2,R7,c1,c2,

MCR P7,0,R1,c3,c2,1,

MRC

协处理器寄存器到ARM 寄存器到的数据传送指令。MRC 指令将协处理器寄存器中的数据传送到ARM

处理器的寄存器中。若协处理器不能成功地执行该操作。将产生未定义异常中断。指令格式如下:

MRC {cond}

coproc,opcodel,Rd,CRn,CRm{,opcode2}

其中:coproc

指令操作的协处理器名。标准名为pn,n为0~15。

opcodel 协处理器的特定操作码。

CRd

作为目标寄存器的协处理器寄存器。

CRn 存放第1 个操作数的协处理器寄存器。

CRm 存放第2

个操作数的协处理器寄存器。

opcode2 可选的协处理器特定操作码。

MRC 指令举例如下:

MRC p5,2,R2,c3,c2

MRC p7,0,R0,c1,c2,1

6   ARM

杂项指令

表A-8给出全部的ARM协处理器指令。

表A-8 ARM杂项指令

4670178_10.jpg

SWI

软中断指令。SWI

指令用于产生软中断,从而实现在用户模式变换到管理模式,CPSR保存到管理模式的SPSR 中,执行转移到SWI 向量,在其它模式下也可使用SWI

指令,处理同样地切换到管理模式。指令格式如下:

SWI{cond} immed_24

其中:immed_24 24 位立即数,值为0~16777215 之间的整数。

SWI 指令举例如下:

SWI 0 ;软中断,中断立即数为0

SWI 0x123456 ;软中断,中断立即数为0x123456

使用SWI 指令时,通常使用以下两种方法进行传递参数,SWI 异常中断处理程序就可以提供相关的服务,这两种方法均是用户软件协定。SWI

异常中断处理程序要通过读取引起软中断的SWI 指令,以取得24 位立即数。

(A)指令24

位的立即数指定了用户请求的服务类型,参数通过用寄存器传递。

MOV R0,#34     ;设置了功能号为34

SWI 12         ;调用12 号软中断

(B)指令中的24 位立即数被忽略,用户请求的服务类型由寄存器R0

的值决定,参数通过其它的通用寄存器传递。

MOV R0,#12     ;调用12 号软中断

MOV

R1,#34     ;设置子功能号为34

SWI 0        ;

在SWI

异常中断处理程序中,取出SWI 立即数的步骤为:首先确定引起软中断的SWI指令是ARM 指令还时Thumb 指令,这可通过对SPSR

访问得到:然后要取得该SWI 指令的地址,这可通过访问LR 寄存器得到:接着读出指令,分解出立即数。

读出SWI 立即数:

T_bit EQU 0x20

SWI_Hander

STMFD

SP!,{R0_R3,R12,LR}         ;现场保护

MRS R0,SPSR

;读取SPSR

STMFD SP!,{R0}                     ;保存SPSR

TST R0,#T_bit                     ;测试T 标志位

LDRNEH

R0,[LR,#-2]                 ;若是Thumb 指令,读取指令码(16 位)

BICNE

R0,R0,#0xFF00             ;取得Thumb 指令的8 位立即数

LDREQ

R0,[LR,#-4]                 ;若是ARM 指令,读取指令码(32 位)

BICNQ

R0,R0,#0xFF00000             ;取得ARM 指令的24 位立即数

LDMFD SP!,{R0-R3,R12,PC}^ ;SWI 异常中断返回

MRS

读状态寄存器指令。在ARM 处理器中,只有MRS 指令可以状态寄存器CPSR 或SPSR读出到通用寄存器中。指令格式如下:

MRS{cond} Rd ,psr

其中: Rd 目标寄存器。Rd 不允许为R15。

psr

CPSR 或SPSR

MRS指令举例如下:

MRS R1,CPSR     ;将CPSR

状态寄存器读取,保存到R1 中

MRS R2,SPSR     ;将SPSR 状态寄存器读取,保存到R2 中

MRS 指令读取CPSR,可用来判断ALU 的状态标志,或IRQ、FIQ 中断是否允许等;在异常处理程序中,读SPSR

可知道进行异常前的处理器状态等。MRS 与MSR 配合使用,实现CPSR 或SPSR

寄存器的读—修改---写操作,可用来进行处理器模式切换(),允许/禁止IRQ/FIQ

中断等设置。另外,进程切换或允许异常中断嵌套时,也需要使用MRS 指令读取SPSR 状态值。保存起来。

使能IRQ

中断例程:

ENABLE_IRQ

MRS R0,CPSR

BIC

R0。R0,#0x80

MSR CPSR_c,R0

MOV PC,LR

禁能IRQ 中断例程:

DISABLE_IRQ

MRS R0,CPSR

ORR

R0,R0,#0x80

MSR CPSR_c,R0

MOV PC,LR

MSR

写状态寄存器指令。在ARM 处理器中。只有MSR

指令可以直接设置状态寄存器CPSR或SPSR。指令格式如下:

MSR{cond} psr_fields,#immed_8r

MSR{cond} psr_fields,Rm

其中: psr CPSR 或SPSR

fields

指定传送的区域。Fields 可以是以下的一种或多种(字母必须为小写):

c 控制域屏蔽字节(psr[7…0])

x 扩展域屏蔽字节(psr[15…8])

s 状态域屏蔽字节(psr[23。…16])

f

标志域屏蔽字节(psr[31…24])

immed_8r 要传送到状态寄存器指定域的立即数,8 位。

Rm 要传送到状态寄存器指定域的数据的源寄存器。

MSR 指令举例如下:

MSR

CPSR_c,#0xD3 ;CPSR[7…0]=0xD3,即切换到管理模式。

MSR CPSR_cxsf,R3

;CPSR=R3

只有在特权模式下才能修改状态寄存器。

程序中不能通过MSR 指令直接修改CPSR

中的T 控制位来实现ARM 状态/Thumb 状态的切换,必须使用BX 指令完成处理器状态的切换(因为BX

指令属转移指令,它会打断流水线状态,实现处理器状态切换)。MRS 与MSR 配合使用,实现CPSR 或SPSR

寄存器的读-修改-写操作,可用来进行处理器模式切换、允许/禁止IRQ/FIQ 中断等设置。

堆栈指令实始化例程:

INITSTACK

MOV R0,LR ;保存返回地址

;设置管理模式堆栈

MSR CPSR_c,#0xD3

LDR SP,StackSvc

;设置中断模式堆栈

MSR CPSR_c,#0xD2

LDR SP,StackIrq

7   ARM 伪指令

ARM 伪指令不是ARM

指令集中的指令,只是为了编程方便编译器定义了伪指令,使用时可以像其它ARM 指令一样使用,但在编译时这些指令将被等效的ARM 指令代替。ARM

伪指令有四条,分别为ADR 伪指令、ADRL 伪指令、LDR 伪指令和NOP 伪指令。ADR        小范围的地址读取伪指令。ADR 指令将基于PC

相对偏移的地址值读取到寄存器中。在汇编编译源程序时,ADR 伪指令被编译器替换成一条合适的指令。通常,编译器用一条ADD 指令或SUB

指令来实现该ADR 伪指令的功能,若不能用一条指令实现,则产生错误,编译失败。

ADR 伪指令格式如下:

ADR{cond} register,exper

其中:register 加载的目标寄存器。

exper 地址表达式。当地址值是非字对齐时,取值范围-255~255 字节之间;当地址是字对齐时,取值范围-1020~1020 字节之间。

对于基于PC 相对偏移的地址值时,给定范围是相对当前指令地址后两个字处(因为ARM7TDMI 为三级流水线)。

ADR

伪指令举例如下:

LOOP MOV R1, #0xF0

ADR R2,

LOOP         ;将LOOP 的地址放入R2

ADR R3, LOOP+4

可以用ADR

加载地址,实现查表:

ADR R0,DISP_TAB     ;加载转换表地址

LDRB R1,[R0,R2]     ;使用R2 作为参数,进行查表

DISP_TAB

DCB 0Xc0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90

ADRL

中等范围的地址读取伪指令。ADRL 指令将基于PC

相对偏移的地址值或基于寄存器相对偏移的地址值读取到寄存器中,比ADR 伪指令可以读取更大范围的地址。在汇编编译源程序时,ADRL

伪指令被编译器替换成两个条合适的指令。若不能用两条指令实现ADRL 伪指令功能,则产生错误,编译失败。ADRL 伪指令格式如下:

ADR{cond} register,exper

其中:register 加载的目标寄存器。

expr 地址表达式。当地址值是非字对齐时,取范围-64K~64K 字节之间;当地址值是字对齐时,取值范围-256K~256K 字节之间。

ADRL 伪指令举例如下:

ADRL R0,DATA_BUF

ADRL

R1 DATA_BUF+80

DATA_BUF

SPACE 100

;定义100 字节缓冲区

可以且用ADRL 加载地址,实现程序跳转,中等范围地址的加载:

ADR LR,RETURNI             ;设置返回地址

ADRL R1 Thumb_Sub+1

;取得了Thumb 子程序入口地址,且R1 的0 位置1

BX R1

;调用Thumb 子程序,并切换处理器状态

RETURNI

CODE16

Thumb_Sub

MOV R1,#10

LDR

大范围的地址读取伪指令。LDR 伪指令用于加载32 位的立即数或一个地址值到指定寄存器。在汇编编译源程序时,LDR

伪指令被编译器替换成一条合适的指令。若加载的常数未超出MOV 或MVN 的范围,则使用MOV 或MVN 指令代替该LDR

伪指令,否则汇编器将常量放入字池,并使用一条程序相对偏移的LDR 指令从文字池读出常量。LDR 伪指令格式如下:

LDR{cond} register,=expr/label_expr

其中:register 加载的目标寄存器

expr 32 位立即数。

label_expr 基于PC 的地址表达式或外部表达式。

LADR

伪指令举例如下:。

LDR R0,=0x123456         ;加载32 位立即数0x12345678

LDR R0,=DATA_BUF+60     ;加载DATA_BUF 地址+60

LTORG

;声明文字池

伪指令LDR 常用于加载芯片外围功能部件的寄存器地址(32

位立即数),以实现各种控制操作加载32 位立即数:

LDR R0,=IOPIN ;加载GPIO

寄存器IOPIN 的地址

LDR R1,[R0] ;读取IOPIN 寄存器的值

LDR R0,=IOSET

LDR R1,=0x00500500

STR R1,[R0]

;IOSET=0x00500500

从PC 到文字池的偏移量必须小于4KB。与ARM

指令的LDR 相比,伪指令的LDR 的参数有“=”号

NOP

空操作伪指令。NOP 伪指令在汇编时将会被代替成ARM 中的空操作,比如可能为“MOV  R0, R0”指令等,NOP 伪指令格式如下:

NOP

NOP

NOP

NOP

SUBS R1,

R1, #1

BNE DELAY1

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值