Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2295965
  • 博文数量: 395
  • 博客积分: 10994
  • 博客等级: 上将
  • 技术积分: 5586
  • 用 户 组: 普通用户
  • 注册时间: 2010-12-17 19:49
文章存档

2014年(1)

2013年(10)

2012年(74)

2011年(303)

2010年(7)

分类: 嵌入式

2011-05-11 22:45:34

(注,转载的arm指令集,不过感觉其中有些地方有错误) 转: 第3章 ARM指令集

学习笔记 2010-10-07 19:19:04 阅读79 评论0   字号: 

进入第3章,ARM指令集。

总的来说ARM指令集里都是32位的指令。ARM指令只对放在寄存器里的数据处理,在存储器里的数据用store load指令

ARM指令集分为:

1数据处理指令

2分支指令

3load store指令

4软件中断指令

5程序状态指令

 

3.1数据处理指令

它又可以细分:

1move传送指令

2算术指令

3比较指令

4乘法指令

 

3.1.1MOVE指令

《指令》{cond}{S} Rd,N

其中cond是条件 如EQ相等,NE不等

加S可以改变cpsr状态

Rd是目标寄存器

N可以是立即数(立即数要#num格式)或者Rm 或者进过桶形移位器的预处理之后的寄存器。

那么来介绍具体move指令

MOV  把一个32位数送到一个寄存器  Rd=N

MVN  把一个32位数的非送到一个寄存器 Rd=~N

 

3.1.2桶形移位器

数据处理指令是在算术逻辑单元ALU处理的,在此之前Rm寄存器可以进过桶形移位器预处理。

具体操作如下。

mov r7 r5,LSL #2 含义是r5左移2位然后放到r7

LSL         逻辑左移            x LSL y              x《y

LSR        逻辑右移            x LSR y            (unsigned)x》y

ASR        算术右移            x ASR y           (signed)x》y

ROR        循环右移           x ROR y             ((unsigned)x》y)|(x《(32-y))

RRX         扩展的循环右移 x RRX                 (c flag 《31)|(unsigned)x》1)

注:为什么没有ASL,因为ASL不涉及符号 所以ASL和LSL性质一样,所以只有R方向才有算术移动。

 

3.1.3算术指令

《指令》《cond》{S}Rd,Rn,N

ADC  32位带进位加法  Rd=Rn+N+carry

ADD  32位加法  Rd=Rn+N

SUB  32位减法   Rd=Rn-N

SBC  32位带进位的减法 Rd=Rn-N-!(carry flag)

RSB  32位逆向减法 Rd=N-Rn

RSC 32位带进位的逆向减法 Rd=N-Rn-!(carry flag)

 

3.1.4算术指令使用桶形移位置器

在算术指令和逻辑指令中,广泛使用第2操作数的移位功能

 

3.1.5逻辑指令

《指令》《cond》{S}Rd,Rn,N

AND  32位逻辑与    Rd=Rn&N

ORR 32位逻辑或    RD=Rn|N

EOR  32位逻辑异或 Rd=Rn^N

BIC    32为逻辑与非 Rd=Rn&~N

BIC在位操作时候很有用

 

3.1.6比较指令

比较指令通常把一个寄存器与一个32位的进行比较,对于比较指令,不需要使用S后缀就可以改变标志位。

《指令》《cond》Rn,N

CMN   取负比较   Rn+N

CMP  比较           Rn-N

TEQ  等比较     Rn^N

TST  位测试        Rn&N

它不改变寄存器中的 改变cpsr

 

3.1.7乘法指令

乘法指令比较特殊,由于是2个32位的乘法,所以有一般情况下,需要64位长整形。

RdLo表示低32位,RdHi表示高32位。

普通32位指令

MUL Rd,Rm,Rs

MUL:  乘法  Rd=Rm*Rn

MLA Rd,Rm,Rs,Rn

MLA    乘累加  Rd=(Rm*Rs)+Rn

以下是64位的乘法指令。

《指令《cond》《s》RdLo,RdHi,Rm,Rs

SMULL    长整形有符号乘法  [RdHi,RdLo]=Rm*Rs

UMULL    长整形无符号乘法   [RdHi,RdLo]=Rm*Rs

SMLAL    长整形有符号乘累加    [RdHi,RdLo]=Rm*Rs+[RdHi,RdLo]

UMLAL    长整形有符号乘累加    [RdHi,RdLo]=Rm*Rs+[RdHi,RdLo]

 

3.2分支指令

结束第一类数据处理指令,接下来第2类,分支处理指令。它可以改变程序执行的流程或者调用子程序,让pc指向一个新的地址。

B{cond} label

BL{cond} label

BX{cond}Rm

BLX {cond}label | Rm

地址label以一个有符号的相对于pc的偏移量保存在指令中,不过要在分支指令32M范围内

B  跳转  pc = label

BL 带返回的跳转 pc=label  lr=BL后的第一条指令

以上是arm'指令跳转到arm指令

如果是arm指令跳转到thumb指令 则要用

BX  跳转并切换状态  pc=RM &0xfffffffe,T=RM&1  (不明白为什么)

BLX 带返回的跳转并切换   pc=RM &0xfffffffe,T=RM&1   lr=BLX后的第一条指令

T对应与cpsr中的T位,由分支寄存器的最低位来更新。第4章中将有描述

BX指令使用一个存储在寄存器Rm中的绝对地址,主要用于跳转到thumb代码。

 

3.3load-store指令

第3种类型指令,load store 这个指令也可以分3大类

1单寄存器传输指令

2多寄存器传输指令

3交换指令

 

3.3.1单寄存器传送指令

这个指令是把单一的数据传入寄存器 或者从寄存器送入存储器。

它支持的数据类型有 8位 字节 16位 半字 32位 字

<LDR|STR>{B} Rd,addressing1

LDR SB|H|SH Rd,addressing2

STRH Rd,addressing2

 

其中没有STRSB与STRSH因为STRH完全实现了前2者的功能。

LDR是指从存储器load到寄存器

STR是从寄存器store到存储器

而且要求数据边界对齐,例如 32位的 要4字节的整数倍。

其中无符号字节 和 字 用 addressing1  它能使用桶形移位器。

而 有符号字节,半字,以及双字 用 addressing2 。它不能使用桶形移位器。

且addressing1中的立即数最大是12位

而addressing2中的立即数最大才8位

 

LDR     把一个字装入一个寄存器        Rd<-mem32[address]

STR     从寄存器中保存一个字到存储器  Rd-》mem32[address]

LDRB  字节

STRB   字节

LDRH    半字

STRH   半字

LDRSB  有符号字节

LDRSH  有符号半字

 

3.3.2单寄存器load store指令的寻址方式

以下介绍3种变址

1.回写前变址             数据:mem[base+offset]                基地址加上偏移量                  LDR r0,[r1,#4]!

其实就是实际送到寄存器r0中的数据是r1寄存器中的数据加上4作为地址寻址找到的数据,而r1中的数据也要改变

2前变址                     数据:mem[base+offset]                原先的                                LDR r0,[r1,#4]

与之前相比,送到r0中的数据还是要偏移后的地址得到,但r1中数据不会发生加4偏移

3后变址                     数据:mem[base]                           基地址加上偏移量                  LDR r0,[r1],#4

r0中的不变,r1中的要发生偏移4.(注:这个地方有些问题,//先进行操作然后R1+4->R1,操作完毕后,R1 = R1+4。不需要"!"号。

 

3.3.3多寄存器传送指令

可以用一条指令来传送多个寄存器的到内存。

语法:

<寻址模式>Rn,{r^}

首先说下Rn,指令是从Rn寄存器中的作为地址,复制到之后的Registers中。

而后面的!可选,如果选上,Rn的将随传送而改变。

而寻址模式有4类:

寻址模式                    描述                  起始地址                结束地址                Rn!

IA(increase after)  执行后增加        Rn                         Rn+4*N-4               Rn+4*N

IB                               执行前增加        Rn+4                     Rn+4*N                  Rn+4*N

DA                             执行后减少        Rn                         Rn-4*N+4               Rn-4*N

DB                             执行前减少        Rn-4                      Rn-4*N                   Rn-4*N

 

指令的配对:

STMIA         LDMDB

STMIB         LDMDA

STMDA       LDMIB

STMDB       LDMIA

例子:

r9存放源数据起始地址

r10存放目标起始地址

r11存放源的结束地址

loop:

LDMIA r9!,{r0-r7}

STMIA r10!,{r0-r7}

CMP r9,r11

BNE loop

简单说明下:把r9中的地址存放的数据复制到寄存器,再从寄存器复制到r10中的地址。本事就是拷贝数据。如果r9没达到r11的位置,就循环

 

注意:多寄存器的load store指令会增加中断的延迟,ARM通常不会打断正在执行的指令去响应中断

堆栈操作:

可以利用多寄存器的load store指令来实现push 与pop操作

ARM中有一些多寄存器loadstore指令的别名支持堆栈。

寻址方式     说明        pop         LDM         push       STM

FA               递增满    LDMFA   LDMDA     STMFA   STMIB

FD               递减满    LDMFD   LDMIA       STMFD   STMDA

EA               递增空    LDMEA    LDMDB     STMEA   STMIA

ED               递减空    LDMED    LDMIB      STMED   STMDA

F为满堆栈sp指向最后一个数据的位置。E为空堆栈sp执行最后一个数据的下一个位置。

A为向上,D为向下。

ARM制定了ARM-Thumb过程调用标准(ATPCS),定义了历程如何被调用,寄存器如何被分配。其中定义堆栈为递减满。

 

3.3.4交换指令

把一个存储单元的内容与寄存器内容相交换,这个是原子操作。

语法:

SWP  Rd,Rm,[Rn]

SWP    字交换   tmp=mem32[Rn]  mem32[Rn]=Rm  Rd=tmp

SWPB  字节交换

其实是【Rn】中的数,放入Rd,Rm中的数据放入【Rm】。

交换指令多用于实现操作系统中的信号量和互斥量。

例子:

spin

MOV R1 = semaphore (这是传一个semaphore信号量的32位地址偏移量)

MOV R2,#1  

SWP R3,R2,[R1]  把信号量送入R3,而自己的用R2=1来继续锁闭原来的锁。

CMP R3,#1

BEQ spin   等于1的话就继续循环,不等的话就能执行接下来的操作,这把锁就给这个程序。

 

3.4软件中断指令

软件中断指令可以产生一个软件中断异常。这为应用程序调用系统例程提供一直机制

语法:

SWI 《cond》SWI_number

SWI     软件中断         lr_svc=SWI的下一条指令。即原先没有切换到管理模式的时候,(一般是用户模式)的lr的

                                  spsr_svc=cpsr

                                  pc = vectors +0X8   pc切换到0x00000008或者0xffff0008;

                                  cpsr模式为SVC   I=1  (屏蔽IRQ中断)

其中SWI_number是用来给中断处理程序传参的,告诉它是软件的中断号。

中断处理程序利用原先模式下(一般为用户模式下)的lr来通过AND NOT 提取出来

SWI_number=《SWI 那条指令32位编码》AND NOT (0xff000000)

来看个例子吧

这是一个中断处理程序,环境是这样的,程序执行了SWI之后,进入到了管理模式中,在中断向量表+0x8的位置找到了我们下面写的这个程序的起始位置。

SWI-handler:

STMFD sp!,{r0 - r12,lr}把寄存器都压栈

LDR r10,[lr,#-4] 把用户模式下的lr减去4找到原先SWI指令的位置,并把存储器中的给r10

BIC r10,r10,#0xff000000  r10过滤出后24位,即中断码

BL service-routine  跳转去其他处理子程序并设返回地址lr-svc

LDMFD sp!,{r0-r12,pc}  出栈

 

3.5程序状态寄存器

ARM指令集提供2条指令,可以控制psr中的传到另一个寄存器,或者把寄存器中的或者立即数送到psr;

语法:

MRS《cond》 Rd,《cpsr|spsr》

MSR《cond》 Rd,《cpsr|spsr》—《field》,Rm或立即数

MRS   把程序状态寄存器送到一个通用寄存器

MSR   把寄存器中的或者立即数送到psr;

其中filed是域,如果选择了一个域就不改变其他域的,分4个域c控制(模式 中断) x扩展 s状态  f标志(NZCV)

 

3.5.1协处理器指令

协处理器指令用于扩展指令集,它即可以提供附加的计算能力,也可以控制包括cache和内存管理的存储子系统

语法

CDP cp,opcode1,Cd,Cn,{,opcode2}

  cp,opcode1,Rd,Cn,Cm,{,opcode2}

  cp,Cd,addressing

CDP     协处理器数据处理,在协处理器内部执行数据

MRC,MCR      协处理寄存器传输,把数据送入或取出协处理器寄存器

LDC, STC       协处理器内存传输,从协处理器装载,存储一个内存的数据块

cp是协处理器编号p0~p15,opcode1是要在协处理器中执行的操作 Cd Cn Cm是在协处理器里的寄存器的描述。

要看具体协处理器来操作。

 

3.6常量的装载

ARM中有2条伪指令,用于把32位立即数装载到寄存器里。因为指令都是32位编码的,里面不可能容下32位的立即数。所以增加了这个伪代码。

形式:

LDR  Rd,=constant  常量装载指令

ADR  Rd,label          32位相对地址装载指令

ADR是把一个标号的地址,与pc的地址相加或相减,放入寄存器

 

3.7ARMv5E扩展

它扩展了许多新的指令,其中最重要的是16位数据的有符号乘累加。

3.7.1零计数指令

这个指令用于计算最高符号位与第一个1之间的0的个数。

CLZ 《cond》 Rd,Rm

 

3.7.2饱和算术指令

就是大发生超过最大时,数据保持在最大,不会变成负数。并且改变cpsr中的Q位。

QADD 《cond》 Rd,Rm,Rn             Rd=Rn+Rm

QDADD 《cond》 Rd,Rm,Rn              Rd=Rn+(Rm*2)

QSUB 《cond》 Rd,Rm,Rn             Rd=Rn-Rm

QDSUB 《cond》 Rd,Rm,Rn             Rd=Rn-(Rm*2)

cspr中的Q位 只能手动清除。

 

3.7.3乘法指令

SMLAxy 《cond》 Rd,Rm,Rs,Rn      Rd=Rm(x)*Rs(y)+Rn

SMULxy  《cond》 Rd,Rm,Rs            Rd=Rm(x)*Rs(y)

如果x取T表示取Rm中的高16位,取B表示低16位

SMLALxy 《cond》 RdLo,RdHi,Rm,Rs      【RdHi,RdLo】+=Rm(x)*Rs(y)

 

SMLAWy 《cond》 Rd,Rm,Rs,Rn      Rd=((Rm*Rs(y))>>16)+Rn

SMULWy 《cond》 Rd,Rm,Rs           Rd=((Rm*Rs(y))>>16)

加个W表示 Rm取32位,与Rs的16位相乘后要把结果的高16位取出。

 

3.8条件执行

大多数ARM指令都支持条件执行,它是指指令的条件代码标志要与cpsr中的标志位相符合时才执行该指令,默认情况下是AL,就是无条件。

而在指令后面加上S表示改变cpsr,而CMP命令无需加S也能改变cspr

一个不错的例子:

求最大公约数

c代码

while(a!=b)

{

           if(a>b)  a-=b;else  b-=a;

}

ARM汇编

gcd

         CMP    r1, r2      比较a和b是不是相等

         SUBGT    r1,r1,r2     如果a大于b 执行a-=b

         SUBLT     r2,r2,r1     如果b大于a 执行b-=a

         BNE   gcd       分支指令,查看CMP之后的标志位 与 分支的条件是不是吻合。

 

这一章结束了,学了好几天了,主要是针对ARM指令集的叙述。从move传送指令开始,算术指令 逻辑指令 比较指令等等,Rm可以使用桶型移位器。 接下来就是3种load store指令的介绍 单寄存器的load store 其中分addressing1 和addressing2 这是因为发展的关系 只有老的一批 32位和 无符号8位才继承多一点的功能(桶型移位器)。之后多寄存器主要是堆栈服务的。最后的交换指令是一个原子操作,它提供了互斥量这些系统应用方面编程的接口。

之后就是软件中断指令SWI 为应用层提供编程的接口。 之后的MRS与MSR是改变psr用的。

协处理器的CDP MRC MCR LDC STC 等操作。之后是常量装载与增强指令  0技术 饱和加减法 16位乘法运算。最后说明条件执行。

接下去一章开始Thumb 16位指令集。

阅读(2411) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~