ARM汇编指令

ARM汇编程序特点:

l         所有运算处理都是发生通用寄存器(一般是R0~R14)的之中.所有存储器空间(如C语言变量的本质就是一个存储器空间上的几个BYTE).的值的处理,都是要传送到通用寄存器来完成.因此代码中大量看到LDR,STR指令来传送值.

l         ARM汇编语句中.当前语句很多时候要隐含的使用上一句的执行结果.而且上一句的执行结果,是放在CPSR寄存器里,(比如说进位,为0,为负…)

CMP R0,R1

BNE NoMatch

比如上一句,BNE隐含的使用的上一句CMP执行结果.NE后缀表示使用Z标志位.两句合起来的意思就是,如果R0,R1的值不相等,就跳转到NoMatch处执行.

注意,PC=R15,CPSR=R16

 

ARM常用指令,伪指令

 

ARM常用指令并不太多,因此使用阅读ARM汇编代码,并不太困难.以下是使用频率最高的指令和伪指令,并不是完整的指令集的教材。详细指令参见参考资料。

l         B,BL

l         MOV,MVN

l         LDR,STR

l         ADD,SUB,ADC,SBC,MUL

l         AND,ORR,XOR,TST,BIC

l         CMP

l         LDM/STM

l         nop

 

1.        跳转语句 B,BL

 程序流程的跳转,在 ARM 程序中有两种方法可以实现程序流程的跳转指令用于实现

 使用专门的跳转指令 B

 直接向程序计数器PC 写入跳转地址值

 这是几乎是任何一种CPU必备的机器,PC表示CPU当前执行语句位置,改变PC的值,相当于实现程序跳转

 如实现类似C语言的Return 语句,就是用MOV PC,LR

 这里可以在任意4G的空间进行跳转

 

B指令(Branch)表示无条件跳转.

   B main;跳转到标号为main地代码处

 

BL指令(Branch with Link)表示带返回值的跳转.

   BL比B多做一步,在跳转前,BL会把当前位置保存在R14(即LR寄存器),当跳转代码结束后,用MOV PC,LR指令跳回来,这实际上就是C语言执行函数的用法,

   汇编里调子程序都用BL,执行完子函数后,可以用MOV PC,LR跳回来.

   BL delay ;执行子函数或代码段delay ,delay可以为C函数.

 

与MOV PC,XXX能在4G空间跳转不同,B语句只能32M空间跳转,(因为偏移量是一个有符号26bit的数值=32M)

 

2.        传输数据指令MOV,MVN

MOV(MOVE)指令可完成从另一个寄存器、被移位的寄存器或将一个立即数加载到目的寄存器

MOV R0,R1 ; 把R1的值传到R0

MOV R3,#3 ;把常数3传给R3,MOV中用#表示常数,这个值不能超过

MVN( MOVE Negative)取反后再传值,比MOV多了一步取反

MVN R0, #0 ;把0取反(即-1)传给R0

MVN R1,R2  ;把R2的值取反传给R1

 

3.        加载/存储指令,LDR,STR

LDR,STR是用于寄存器和外部存储器交换数据指令,注意与MOV的区别,后面只在寄存器或常数交换.

           LDR/STR可以采用多种寻址方式,以下只举出使用频率最高几种用法

    LDR(load)用于把一个32Bit的WORD数据从外部存储空间装入到寄存器中

      LDR R0,[R1]; R1的值当成地址,再从这个地址装入数据到R0 (R0=*R1)

      LDR R1,=0x30008000; 把地址0x30008000的值装入到R1中,LDR中用常数要用=打头.(注意跟MOV的区别,MOV是#)

      ldr  r0, =(0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0) 用位与的方法赋值

     STR(Store) 用于把一个寄存器的值存入外部存储空间,是LDR的逆操作.

      STR R0,[R1]; 把R0的值,存入到R1对应地址空间上(*R1 = R0)

      STR R0,=0x30008000 ;把R0中值存入到地址0x30008000

S2C2440的中CPU内核以外的模块的控制寄存器空间也是属于外部空间,所以也得用如下指令LDR R0,=GPFDAT

 

4.        算术运算指令,ADD/ADC,SUB/SBC ,MUL

ADD加法指令

  ADD R0,R1,R2; R0=R1+R2

  ADD R0,R1,#3 ;R0=R1+3

        ADC带进位加法指令,即除了加两个数以外,还要把CPSR的C值也要带进来

           通常用于大数(超过32Bit整数)相加,这时单用ADD不能处理,必须折成两步,其中一步用ADC, 以下是做64Bit的加法

  ADDS R0,R1,R2; R0=R1+R2,ADDS中S表示把进位结果写入CPSR

  ADC R5,R3,R4 ;R5=R3+R4+C

SUB减法指令

  SUB R0,R1,R2; R0=R1-R2

  SUB R0,R1,#3 ;R0=R1-3

         SBC带进位减法指令,即除了加两个数以外,还要把CPSR的C值也要带进来,类似ADC,以下是做64Bit的减法

  SUBS R0,R1,R2; R0=R1-R2,SUBS中S表示把进位结果写入CPSR

  SBC R5,R3,R4 ;R5=R3-R4-C

MUL 乘法指令

  MUL R0,R1,R2; R0=R1*R2

  MUL R0,R1,#3 ;R0=R1*3

 

5.        位操作指令 AND,ORR, TST,BIC

AND位与指令

  AND R0,R1,R2; R0=R1 & R2

  AND R0,R1,#0xFF ;R0=R1 & 0xFF

ANDS

  例程: 

  ANDS R1,R1,#0x0400  

  BEQ WAITOK  

  ==>

  R1&0x0400 => R1,若结果为 0(即 R1 为 0),则标志位 Z=1.  

  当 Z=1 时,BEQ WAITOK 有效执行.  

ORR位或指令

  ORR R0,R1,R2; R0=R1 | R2

  ORR R0,R1,#0xFF ;R0=R1 | 0xFF

TST测试某一位是否为1,并把结果写入CPSR,供下一句使用

  TST R1,#0xffe;   等同于if(R1 & 0xffe)

  TST R1,#%1;测试最低位是否为1,%表示二进制

BIC清位操作

  BIC   R0,R0,#0xF         ;等同于 R0&=~(0xF)

  BIC   R0,R0,#%1011   ;该指令清除 R0中的位 0 1  3,其余的位保持;   %表示是二进制,0x表示十六进制

 

6.        比较指令 CMP

CMP比较两个操作数,并把结果存入CPSR供下一句语句使用

  CMP R0,R1; 比较R0,R1

 

7.        多寄存器语句传输指令,LDM,STM

类似于一次传一个BUFFER到寄存器当中,或反过来.后面一般要接一个地址改变方法

LDM 从BUFFER传数据多个寄存器传输数据到

  LDMIA R0! ,{R3-R9} ;加R0指向的地址上连续空间的数据,保存到R3-R9当中,!表示R0值更新,IA后缀表示按WORD递增

  LDMFD SP!,{R0-R7,PC}^;恢复现场,异常处理返回,^表示不允许在用户模式下使用。

STM 从寄存器列表向存储空间传值。

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

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

    stmfd    sp!,{r8-r9},把SP寄存器对庆的地址的值存到R8,R9当中.!表示最后的值写入SP中。Fd表示

 

寻址方式 说明 pop =LDM push =STD
FA 递增满 LDMFA LDMDA STMFA STMIB
FD 递减满 LDMFD LDMIA STMFD STMDB
EA 递增空 LDMEA LDMDB STMEA STMIA
ED 递减空 LDMED LDMIB STMED  

 

*pop (出栈)即使用一条多寄存器指令的load操作,将栈中的数据pop到通用寄存器中,push(入栈)使用一条多寄存器指令的store操作,将寄存器中的数据存至栈中

*在使用一个堆栈时,需要确定堆栈在存储器空间中是向上生长还是向下生长的,一个堆栈或者是递增的(ascending,"A")--向上(高地址空间)生长,或者是递减的(descending,"D")--向下(低地址空间)生长

*满堆栈(full stack,"F")是指堆栈指针sp指向堆栈的最有一个已使用的地址或满位置(也就是sp指向堆栈的最有一个数据项位置);相反,空堆栈(empty stack,"E")是指sp指向堆栈的第一个没有使用的地址或空位置(也就是sp指向堆栈的最后一个数据项的下一个位置)

 

8.        ARM指令的变形

大部分指令后位可以接 <cond>与S两个特殊位来表示,对CPSR特殊的一些判断

S,表示当前指令执行后把结果改写CPSR

subs,Adds

<Cond>取决于具体条件,只有CPSR满足指定条件时才指这一指令

 

BEQ 实际上B+ EQ的条件执行.

addne 表示ADD +NE 才开始加

 

9.        ARM指令的寻址方式

寻址方式是根据指令中给出的地址码来定位真实的地址,ARM中有9种寻址方法

寄存器寻址

  直接用寄存器编号来寻址,最为常用

     MOV R1,R2 ;R2->R1

立即数寻址

  即指令中的地址码是操作数本身,可以立即取出使用,立即数前带一个#表示,否则表示一个地址

  SUBS R0,R0,#1  ;R0 -1 ->R0

  注意与SUBS R0,R0,1区别

寄存器偏移寻址

  这是ARM特有的寻址模式,当第2操作数是寄存器,在执行操作之前,可以做一次移位操作

  MOV R0,R2,LSL #3;R2的逻辑左移3位,结果放入R0,即R0=R2*8

  ANDS R1,R1,R2,LSL R3;RS的值左移R3位,然后和R1相与操作,结果放入R1

移位操作有LSL (逻辑左移),LSR(逻辑右移) ,ASR(算术右移),ROR(循环右移)RRX带扩展的循环右移 

寄存器间接寻址

  即寄存器中值是一个地址,用[]来取出定位到地址当中

  LDR R2,[R0] ;把R0的值当成地址,取出相应值,赋给R2

基址寻址

  把寄存器的地址值加上一个偏移量

  LDR R2,[R3,#0x0F]; R3中的值加上0x0F,从这个地址取出值赋给R@

相对寻址

  基址寻址的变形,由PC寄存器提供基准地址,指令中地址段作为偏移量.两者相加即是有效地址,以下是BL采用相对寻址

BL NEXT

NEXT

    …

MOV PC,LR;从子程序返回

 

10.     ADS ARM的伪指令

  类似于C语言的宏,由汇编程序预处理.

符号定义指令

  全局变量定义 GBLA ,GBLL,GBLS

  局域变量定义 LCLA,LCLL,LCLS

  变量赋值SETA,SETL,SETS

  其中上述伪指令中,最后面的A表示给一个算术变量赋值,L表示用于给一个逻辑变量赋值,s表示给一个字符串赋值

    GBLL codedbg;声明一个全局的逻辑变量

    Codebg SETL  {TRUE} ;设置变量为{TRUE}

    LCLA bitno; 声明一个算术变量

    Bitno SETA 8 ;设变量值为8,数据定义伪指令

  SPACE 定义一个内存空间,并用0初始化

    {label }  SPACE expr

    DataBuf SPACE 100 ;定义100字节长空间, unsigned char DataBuf[100];

  DCB 定义一个连续字节内存空间,用伪指令的表达式expr来初始化.一般可以用定义数据表格,或文字字符串.(这时等同于SETS),用于初始二进制BUFFER

        {label} DCB expr{,expr …}

         Dest DCB -120,20,36,55;等同于 unsigned char Dest[]={-120,20,36,55};

  DCDU(DCD)定义的一段字的内存空间(DCB是字节),并用后面表达式初始化

    _RESET DCU Reset ; 等同于 DWORD _RESET[]={Reset};

 

  MAP定一个结构化内存,相当于定义一个C结构

  FILED 定义一个结构化内存的成员

    MAP 0x00,R9 ; 定义内存表,地址为R9

    Timer   FIELD 4 ; 定义数据域Timer,长为4字

    Attrib  FIELD 4 ; 定义数据域Attrib,长为4字

    String  FILED 100  ; 定义数据域String ,长为100字

      相当于C语言的定义:

      struct {

      DWORD Timer ;

      DWORD Attrib ;

      Char String[100];

      } R9;

 

11.     杂项的伪指令

字节对齐 ALIGN

  ALIGN; 声明4字节对齐

定义一个数字常量定义 EQU

  NAME EQU expr {type}

    PLLCON EQU 0xE01FC080;定义PLLCON,类似于C的宏或C++的常量

包含文件 GET和INCLUDE

  INCLUDE lpc2106.inc

NOP 空指令

  在汇编时会被ARM的空操作代替,比如MOV R0,R0,一般用于延时与占位。

声明一个外部符符号 IMPORT,EXTERN

  IMPORT,EXTERN 向外部导入一个符号,一般是外部程序全局变量

 

条件编译:[]。类似于C的#ifdef 之类定义。

格式 :[ 条件表达式

        满足条件分支

        |

        不满足条件分支

      ]

示例1:

 [ ENTRY_BUS_WIDTH=32 ;类似#if ENTRY_BUS_WIDTH=32

      b   ChangeBigEndian     ;DCD 0xea000007

   ] ; 类似#endif 

示例2:   [ CLKDIV_VAL>1      ; 类似#if CLKDIV_VAL>1

bl MMU_SetAsyncBusMode

          |;类似#else 

        bl MMU_SetFastBusMode ; default value.

]; 类似#endif

          示例3 [ THUMBCODE 类似#ifdef  THUMBCODE

                                   bx lr

                                 | ;类似#else

                              mov   pc,lr

                               ] ;类似#endif

段定义 AREA

语法 AREA sectionname{,ATtr}{,ATtr}...

sectionname

  是将要指定的节名。可以为节选择任何名称。但是,以数字开始的名称必须包含在竖杠内,否则会产生一个缺失节名错误。 例如,|1_DATaArea|。有些名称是习惯性的名称。 例如,|.text| 用于表示由 C 编译器生成的代码节,或以某种方式与 C 库关联的代码节。

ATtr
  是一个或多个用逗号分隔的节属性。有效的属性有:    

    ALIGN=expression_r
      缺省情况下,ELF节在四字节边界上对齐。expression_r可以取值0到31之间的任何整数。节在2^expression_r字节边界上对齐。例如,如果expression_r是10,则节在 1KB 边界上对齐。这与 ALIGN 指令所指定的方式不同。请参阅ALIGN。

      注意:

        不要对ARM代码节使用ALIGN=0或ALIGN=1。

        不要对Thumb代码节使用ALIGN=0。

    ASSOC=section
      section 指定一个关联的ELF节。sectionname 必须包含在含有section 的任何链接中。

    CODE
      包含机器指令。READONLY 是缺省值。

    CODEALIGN
      当在节内的 ARM 或 Thumb 指令后使用 ALIGN 指令时,该属性导致汇编程序插入 NOP 指令,除非 ALIGN 指令指定了其他填充方式。

    COMDEF
      是一个公共节定义。 此 ELF 节可以包含代码或数据。 它必须等同于其他源文件中拥有相同名称的任何其他节。名称相同的同一 ELF 节在内存的同一节中被链接器覆盖。 如果有任何不同,则链接器会产生一个警告,并且不覆盖这些节。 请参阅 《RealView 编译工具链接器和实用程序指南》中的第 3 章 使用基本链接器功能。

    COMGROUP=symbol_name
      是一个公共组节。公共组中的所有节都是公共的。当对象被链接后,其他目标文件可能具有带有symbol_name 签名的一个 GROUP。最终映像中只包含一个组。

    COMMON
      是一个公共数据节。 不能在其中定义任何代码或数据。 它由链接器初始化为零。 名称相同的所有公共节在内存的同一节中被链接器覆盖。 它们并不都必须具有相同大小。 链接器按每个名称的最大公共节的需要分配空间。

     DATA
      包含数据,不包含指令。READWRITE 是缺省值。

    GROUP=symbol_name
      是组的签名,它必须由源文件或源文件中包含的文件定义。 具有相同 symbol_name 签名的所有 AREAS 都被置于同一组中。 组内的各节同时保存或显现。

    NOALLOC
      指示在目标系统上没有为此区域分配内存。

    NOINIT
      指示数据节未初始化,或初始化为零。 它只包含空间保留指令 SPACE 或初始化值为零的 DCB、DCD、DCDU、DCQ、DCQU、DCW 或 DCWU。 您可以在链接时决定某区域是未初始化还是初始化为零(请参阅 《RealView 编译工具链接器和实用程序指南》中的第 3 章 使用基本链接器功能)。

    READONLY
      指示不应向此节写入。 这是代码区域的缺省值。

    READWRITE

用法:
  指示可以读写此节。 这是数据区域的缺省值。

    使用AREA指令可将源文件细分为ELF节。 可以在多个 AREA 指令中使用相同的名称。 名称相同的所有区域都放在相同的 ELF 节中。 只有特定名称的第一个 AREA 指令的属性才会被应用。通常应对代码和数据使用不同的 ELF 节。 大型程序通常可方便地划分为多个代码节。 大量独立的数据集通常也最好放在不同的节中。局部标签的作用域是由 AREA 指令定义的,并可选择用 ROUT 指令细分(请参阅局部标签和ROUT)。

    一组汇编代码必须至少有一个 AREA 指令。

    示例
    下列示例定义名为 Example 的只读代码节。

    AREA    Example,CODE,READONLY   ; An example code section.

    ; code

 

一个基本ARM程序结构

ARM汇编程序结构

源代码由文本文件组成.按照汇编的编译器不同,分为两大量,一类是ADS的汇编程序,一类是GNU汇编格式,两者在指令集是完成一样,但是在伪指令.程序结构等方法各不同相同.本节主要是讲解ADS汇编格式.

ADS汇编程序,主要包含如下几类程序

  汇编源程序,后缀名是.S

  汇编包含文件,后缀名是.inc

  如果是与C混和编程..C,.h也能识别

 ARM 汇编语句格式     

  [标号]  <指令|条件|S> <操作数> [;注释]

    所有标号顶格写,而指令和伪指令不能顶格写

    标识符(标号,指令)大小写敏感,所以要在标号和指令时书写一致,一般伪指令,指令,寄存器名可以全部为大写

    注释以;开头,可以顶格写

    可以使用\来分行写太长语句

    变量,常量的定义必须在一行顶格写

常量的书写

  数字常量

    在程序中直接写数字 ,十进制 12,256,十六进制 0x1228,

  字符常量

    类似于C的定义,用SETS来定义字符常量

      HELLO SETS “hello,the world!”

  逻辑常量

    逻辑真为{TRUE},逻辑假为{FLASE}

      Testno SETS {TURE}

汇编程序的段定义

  任何一个程序都要分段,C语言一般由编译器自动分段,(分成.Text,.Data段之类),但在汇编程序这样的底层程序中,由开发者自行分段.  它包含如下段

    至少一个代码段,并且代码段是只读的,对应(.Text)

    数据段可以没有,也可以有多个.

    每一个段用END结束

AREA 定义一个段

  AREA  段名    属性1, 属性2,

    例子:AREA Init,CODE,READONLY

  ENTRY 指明一个段的入口

  END结束一个段

   

         ABC EQU 0x12

            AREA Example,CODE,READONLY

            ENTRY

        START MOV R7,#10

             MOV R6,#5

              ADD R6,R6,R7

              B   

              END

 

posted @ 2016-11-01 21:05  ZYVV  阅读(1326)  评论(0)    收藏  举报