arm汇编编程(示例)

一、arm的认知及基本概念

().arm的基本概念

1. 什么是arm

   arm是一家英国电子公司的名字,全名是Advanced RISC Machine

   这家企业设计了大量高性能、廉价、耗能低的RISC(精简指令集)处理器,ARM公司只设计芯片而不生产,它将

技术授权给世界上许多公司和厂商。目前采用arm技术知识产权内核的微处理器,即通常所说的arm微处理器

   

   所以arm也是对一类微处理器的通称。

   

   arm指令集体系版本号(软件)为V1 ~ V7 目前V1 ~ V3已很少见。从V4版不再与以前的版本兼容。

   armCPU系列(硬件)主要有 ARM7 ~ ARM11

   

   

2. 典型的嵌入式处理器

   arm        占市场79.5%  ARM

   mips       占市场13.9%  MIPS

   microSPARC 占市场3.1%   SUN 

   PowerPc    占市场2.8%   IBM

   其它       占市场0.8%

   

3. arm的应用范围:

   工业控制:如机床、自动控制等

   无线通信:如手机 

   网络应用:如

   电子产品:如音视频播放噐、机顶盒、游戏机、数码相机、打印机

   其它各领域:如军事、医疗、机器人、智能家居等

4.计算机体系结构

  见图:冯.诺依曼计算机体系图

  

  冯.诺依曼体系结构

     处理器使用同一个存储器,经由同一个总线传输

     完成一条指令需要3个步骤:即取指令->指令译码->执行指令

     指令和数据共享同一总线的结构

  哈佛体系结构

     将程序指令存储和数据存储分开

     中央处理器首先到程序指令存储器中读取程序指令。解码后到数据地址,再到相应的数据存储器读取数据,然后执行指令

     程序指令存储与数据存储分开,可以使指令和数据有不同的数据宽度。   

5.复杂指令集与精简指令集

  CISC 复杂指令集:采用冯.诺依曼体系结构。数据线和指令线分时复用(只能通过一辆车)。

       存储器操作指令多 汇编程序相对简单 指令结束后响应中断 CPU电路丰富 面积大功耗大

  

  RISC 精简指令集:采用哈佛体系结构。数据线和指令线分离(同时能通过多辆车)。

       对存储器操作有限 汇编程序占空间大 在适当地方响应中断 CPU电路较少 体积小功耗低

       ARM采用RISC精简指令集  

  ThumbARM体系结构中一种16位的指令集。

       从ARMv4T之后,的ARM处理器有一种16-bit指令模式,叫做Thumb,较短的指令码提供整体更佳的编码密度,更有效地使用有限的内存带宽。所有 ARM9 和后来的家族,包括 XScale 都纳入了 Thumb 技术。

       即ARM有两种指令集:RISCThumb

6. arm的思想

1) arm体系的总思想:

   在不牺牲性能的同时,尽量简化处理器。同时从体系结构上灵活支持处理器扩展。采用RISC结构。RISC处理器简化了处理器结构,减少复杂功能指令的同时,提高了处理器速度。

   ARMMIPS都是典型的RISC处理器

2) arm的流水线结构

   

   arm处理器使用流水线来增加处理器指令流的速度,这样可以使几个操作同时进行。并使处理和存储器系统连续操作。

   arm处理器分为三级:取指->译码->执行

      取指:指令从存储器中取出

      译码:对指令使用的寄存器进行译码

      执行:从寄存器组中读取寄存器,执行移位和ALU操作,寄存器被写回到寄存器组中

   

3) ARM处理器支持的类型

      字节  8

      半字  16

      字    32

      

      **所有数据操作都以字为单位

      **ARM指令的长度刚好是一个字,Thumb指令长度刚好是半个字

  

4) ARM处理器状态

      ARM处理器内核使用ARM结构,该结构包含32位的ARM指令集和16Thumb指令集,因此ARM有两种操作状态

      ARM状态:32

      Thumb状态:16

  

5) 处理器模式

      ARM处理器共有7种运行模式:

      用户:  正常程序工作模式,不能直接切换到其它模式

      系统:  用于支持操作系统的特权任务,可以直接切换到其它模式

      快中断:支持高速数据传输及通道处理,FIQ异常响应时进入此模式

      中断:  用于通用中断处理,IRQ异常响应时进入此模式

      管理:  操作系统保护代码,系统复位和软件中断响应时进入此模式

      中止:  用于支持虚拟内存或存储器保护,用于MMU

      未定义:支持硬件协处理器的软件仿真,未定义指令异常响应时进入此模式。

()、经典平台硬件组成

    见图:arm硬件组成图

    开发板一般是由一块组成的,有核心器件和外围器件接口等,但是有的是由两块板子组成,主版和核心板,主版上主要是外围接口,外围器件等,核心板上主要是核心器件,还有一些晶振电路等 

      

  1.核心板(天嵌 2440)

    

    CPU处理器  S3C2440AL,主频400MHz(最高可达533MHz) 

    SDRAM内存  板载64MB SDRAM(标准配置), 32bit数据总线SDRAM时钟频率高达100MHz(支持运行133MHz

    Nand Flash 板载64MB Nand Flash256MB Nand Flash(标准配置)

    Nor Flash  板载2MB Nor Flash(最高可升级到8MB

    CorePower  专业1.25V核心电压供电

    Power      核心板采用3.3V供电 

    Powerled   核心板电源指示灯 

    核心板接口 接口型号为DC-2.0双列直插

    SDRAM:随机存储器,普遍使用的内存。用作主存。

    NOR Flash和 NAND Flash是现在市场上两种主要的非易失闪存。

          NOR的特点是芯片内执行,应用程序可以直接在flash 闪存内运行,不必再把代码读到系统RAM中。

          NAND结构能提供极高的单元密度,可以达到高存储密度,并且写入和擦除的速度也很快。

  

  2.主板

     电源

     并口线

     复位

     RTC电源

     RS232电平转换DB9插座

     音频IIS,AC97

     按键、PS/2IC接口

     数码管

     触摸屏

     以太网卡

     主USB HUB 14

  3.寄存器 

    见图:ARM模块和内核框图

    寄存器是中央处理器内的组成部份。

    寄存器是有限存贮容量的高速存贮部件,用来暂存指令、数据和位址。在中央处理器的控制部件中,包含的寄存器有指令寄存器(IR)和程序计数器(PC)。在中央处理器的算术及逻辑部件中,包含的寄存器有累加器(ACC)

    IR 用于存储指令

    PC 用于存储程序运行的地址(即当前指令在内存中的位置)

    寄存器是由一个指令的输出或输入可以直接索引到的暂存器群组。所有的计算机指令都是进入寄存器后被直接读取

    ARM的汇编编程,本质上就是针对CPU寄存器的编程。

    

    //*******重点  需要背讼*************************************************

    ARM寄存器分为2类:普通寄存器和状态寄存器

   

      (1)通用寄存器和计数器:共32个,15个通用寄存器

         R0 -R7 未备份寄存器

                R0(a1)  R1(a1) R2(a3) R3(a4) R4(v1) R5(v2) R6(v3) R7(v4) 

         R8 -R12 备份寄存器

                R8(v5)  R9 (SB,v6) R10(SL,v7) R11(EP,v8) R12(IP)    数据寄存器

         R15(PC)    程序计数器 它的值是当前正在执行的指令在内存中的位置。

                    当指令执行结束后,CPU会自动将PC值加上一个单位,PC值指向下一条即将执行的指令的地址

                    如果通过汇编指令对PC寄存器赋值,就会完成一次程序的跳转(如从子函数跳转回主函数内)

                    

         R14(LR)    链接寄存器 存放子程序的返回地址

                    例如:在主函数内,如果调用子函数,程序会进入到子函数内执行。当子函数执行完毕后,需要回到

                主函数内,所以,在子函数调用前需要将这个地址先保存起来,否则无法找到这个地址。

                    LR用于保存这个地址,这个地址也称为子程序返回地址。当子函数结束后,再将LR内的地址赋给PC即可。

                    

                    如果子程序再调用孙程序,LR如何保存地址呢?

                    先把当前LR内的值压入内存的栈区,然后LR再保存孙程序的返回地址。当孙程序执行完后通过PC跳转到

                子程序内,此时将栈区内的子程序返回地址取出保存在LR内。当子程序执行完后,再通过PC跳转到主函数内。

         

         R13(SP)    栈指针寄存器 用于存放堆栈的栈顶地址。

                    SP相当于指针变量,保存的是栈顶的地址,出栈时,从SP指向的内存中取出数据,入栈时将新的内存地址

                压入栈顶,而SP相当于链表的头指针(head)

                

                    

         原则上说R0-R12可以保存任何数据。其中R0-R7用来临时存储数据,R8-R12  系统没有用来做任何特殊用途,常用于中断

         

         而在汇编与C语言的交互中,定制了ATPCS标准

            寄存器:R4-R11用来保存局部变量

            参数:  参数小于等于4,用R0-R3保存参数,参数多于4,剩余的传入堆栈

            函数返回:结果为32位整数,通过R0返回

                      结果为64位整数,通过R0R1返回

                      对于位数更多的结果,通过内存传递

      (2)状态寄存器:

         状态寄存器用于保存程序的当前状态

         

         CPSR   当前程序状态寄存器

                一个寄存器为32位,每一位数据代表不同的状态。分为三个部分(条件代码标志位、控制位、保留区位)

                

                      31 32 29 28 .... 7 6 5 4  3  2  1  0

                      N  Z  C  V       I F T M4 M3 M2 M1 M0

                      

                      其中N Z C V称为条件标志位(即保存的是条件的运算结果,真和假)

                      N=1 表示运算结果为负数,N=0 表示运算结果为正数。               

                      Z=1 表示运算结果为0, Z=0 表示运算结果为非零。

                      C=1 表示运算结果产生了进位。

                      V=1 运算结果的符号位发生了溢出。

                      

                      这4个位的组合,代表了各种条件,如下:

                      0000 EQ Z置位 相等/等于0

                      0001 NE Z0 不等

                      0010 CS/HS C置位 进位/无符号高于或等于

                      0011 CC/LO C0 无进位/无符号低于

                      0100 MI N置位 负数

                      0101 PL N0 非负数

                      0110 VS V置位 溢出

                      0111 VC V0 无溢出

                      1000 HI C置位且Z0 无符号高于

                      1001 LS C0Z置位 无符号低于或等于

                      1010 GE N等于V 有符号大于或等于

                      1011 LT N不等于V 有符号小于

                      1100 GT Z0N等于V 有符号大于

                      1101 LE Z置位或N不等于V 有符号小于或等于

                      1110 AL 任何状态 总是(always

                      1111 NV 无 从不(never

                      

                      

                      IFT 称为控制位

                      I   I=1 禁用IRO中断

                      F   F=1 禁用FIQ中断

                      T   表示CPU当前的状态,代表正在Thumb指令集状态,0表示正在ARM指令集状态。

                      M0M4表示中断类型(控制位内的模式位)

                      0b10000      User        用户中断

                      0b10001      FIQ         快速中断

                      0b10010      IRQ         声卡、调制解调器等外部设备产生的中断

                      0b10011      Supervisor  管理程序或监控程序产生的中断

                      0b10111      Abort       异常中断 

                      0b11011      Undefined   未定义中断

                      0b11111      System      系统中断

          SPSR   保存的程序状态寄存器,结构与CPSR完全一样,用来保存CPSR的值。以便出现异常时恢复CPSR的值

      (3)流水线对pc值的影响 

         

         CPU内部的组成部分:指令寄存器,指令译码器,指令执行单元(包括ALU和通用寄存器组)

         CPU执行指令的步骤:取指->译码->执行

              取指:将指令从内存或指令cache中取入指令寄存器

              译码指令译码器对指令寄存器中的指令进行译码操作,辨识add,或是sub等操作

              执行:指令执行单元根据译码的结果进行运算并保存结果

         流水线操作:并发多条流水线(以3条为例)

         1 取指    译码     执行

         2         取指     译码    执行

         3                  取指    译码    执行

      

         提高时间效率

    

()、学习内容

    1.汇编(对裸板机的控制,以及驱动程序控制)

    2.内核移植(uboot移植、内核编译、文件系统移植、应用程序移植)

    3.驱动程序编写

//^^^^^^^^^^^^^^^^^^^^^^^^^^ 下午 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

()ADS的使用

        ADS是汇编或C语言编译调试工具

        可生成的文件:.axf 含调试信息的可执行ELF文件

                      .bin 可烧写的二进制映像文件    

                      .hex 可烧写的十六进制映像文件

        ADS配置

        在磁盘中新建一个目录 D:\arm,用来存储所写的代码

        

        点击目录:File->new,创建一个可执行的ARM映象工程

            ARM Executable Image 生成ELF格式映像(bin)

            ARM Object Library 生成armar格式目标库文件

            Empty Project 创建不包含任何库或源文件的工程

            Makefile Importer Wizard 用于Vc

            Thumb ARM Interworking Image 用于ARM thumb指令混合代码生成的ELF映像

            Thumb Executable image thumb指令生成ELF格式映像

            Thumb Object Library 用于Thumb指令的代码生成的armar格式文件

        

            选 ARM Executable Image,工程文件名2440ART   

        加源文件

            project -> Add Files

            新建

            填加已有

        生成目标的配置

            2440ART.mcp内双击

            TargetSettins:   post-linker选择ArM fromELF

            Language Settins:Architecture or Processor选择相应的编译器ARM920T

            Arm Linker:output RO 0x30000000

                       optionsImage entry point 设为0x30000000 

                       layout Object 2440init.o Section Init   

                       Listings Image map

            Arm fromELF: output format内 Plain binary

                         output filename***.bin             

        编译

            make

        调试  AXD是调试器

            设置,debug ->打开AXD调试界面,选择option->config target 选项

                 选ARMUL(模拟调试器),然后选择确定.进入调试界面.

                 ARMUL是虚拟调试环境(虚拟开发板)

                 如果用开发板真实环境调试,则需要使用JTAG连开发板后,在此处选H-JTAG

            用file-<lode image项,加载刚才编译所的的 .axf调试文件.

                 execute->run to cousor 项.使程序进入用户主程序

                 

            可以用F8来一条一条执行语句,也可用F10,可以设置断点.

        

().汇编语言基本结构

      例:

          AREA Init,CODE,READONLY  ;AREA定义代码段,段名Init;代码段,只读

          ENTRY                     ;伪操作,第一条指令的入口

      Start                         ;标号,一段代码的开始,用于标记,无意义,必须顶格

          MOV  r0,#10               ;10存入r0寄存器,整型常量前面用#号 

          MOV  r1,#3                ;3存入r1寄存器,r0r1相当于两个变量,只是名称固定,在寄存器的存储空间内

          ADD  r0,r0,r1             ;r0内数据与r1内数据相加,相加后数据放在r0

      ;Stop                          ;停止标号,下面的代码用于停止运行程序

      ;    MOV  r0,#0x18             ;软件异常中断响应

      ;    LDR  r1,=0x20026          ;ADP停止运行,应用退出

      ;    SWI  0x123456             ;ARM半主机软件中断

      END

      

  1).基本概念    

   (2)寄存器:如 R0R1等  

      ARM的汇编编程,本质上就是针对CPU寄存器的编程。

   (3)指令:即操作码,直接控制CPU,如MOV

      包括跳转指令、数据处理指令、乘法指令、PSR访问指令、加载或存储指令、数据交换指令、移位指令等

   (4)伪操作:作用于编译器,大多用于定义和控制。如AREA

      包括符号定义、数据定义、控制等

   (5)标号:仅是一种标识。在跳转语句中,可以指向要跳转到的标识号位置

            在ARM 汇编中,标号代表一个地址,段内标号的地址在汇编时确定,段外标号的地址值在连接时确定

   (6)符号:即标号(代表地址)、变量名、数字常量名等。符号的命名规则如下:

        a. 符号由大小写字母、数字以及下划线组成;

        b. 除局部标号以数字开头外,其它的符号不能以数字开头;

        c. 符号区分大小写,且所有字符都是有意义的;

        d. 符号在其作用域范围你必须是唯一的;

        e. 符号不能与系统内部或系统预定义的符号同名;

        f. 符号不要与指令助记符、伪指令同名。

  2).段定义

     在汇编语言中,以相对独立的指令或数据序列的程序段组成程序代码

     段的划分:数据段、代码段。一个汇编程序至少有一个代码段

     (1)代码段

        上面的例子为代码段。

        AREA  定义一个段,并说明所定义段的相关属性,CODE 用以指明为代码段

        ENTRY 标识程序的入口点。END为程序结束。

     (2)数据段

        AREA   DATAAREADATA,BIINIT,ALLGN=2

        DISPBUF SPACE 200

        RCVBUF  SPACE 200

          

        DATA用以指明为数据段,

        SPACE分配200字节的存储单元并初始化为0

        

        指令和伪操作在后面详细描述

  3).汇编语言结构

      [标号] [指令或伪操作]

    

      所有标号必须在一行的顶格书写,其后不加冒号

      所有指令均不能顶格写

      指令助记符大小写敏感,不能大小写混合,只能全部大写或全部小写

      ;为注释

      @代码行注释,同;

      #整行注释或直接操作数前缀

      \为换行符 

      ENTRY 为程序的入口

      END  为程序的结束

//************************************* arm体系结构 第二天***************************************

二、ARM的寻址方式

    最简单的汇编指令格式是操作码和操作数

    如:MOV  r0,#10

    操作码:即CPU指令 如MOV ADD

    操作数:即表示数据是在寄存器中还是在内存中,是绝对地址还是相对地址

            操作数部分要解决的问题是,到哪里去获取操作数,获取操作数的方式就是寻址方式。

    ARM每一条指令都是32位机器码,对应CPU的位数

    ARM指令格式:                 

       在32位中分为7个位域,每个位域分别存储不同意义的编码(二进制数)

        

       31 ~ 28 27~25   24 ~ 21  20   19~16    15~12    11 ~ 0 

       -----------------------------------------------------------

       | Cond | 001  | Opcode  | S |   Rn   |   Rd   |  Operand2 | 

       -----------------------------------------------------------

       对应输入的一条指令为:

       <Opcode>{<cond>}{s} <Rd>,<Rn>,<Operand2>

       

       Opcode:指令操作码   用 4个位存储,如MOV,ADD  每一个操作码和操作数最终都是以二进制形式存在

       Cond  :指令的条件码 用 4个位来储,可省略

              如:ADD   r0,r0,#1   ;计算r01后的值,再写入到r0

                  ADDEQ r0,r0,#1   ;只有在CPSR寄存器条件标志位满足指定条件时,才计算r01后的值,再写入到r0

                  

              其中条件助记符如下:

                 EQ     相等

                 NE     不相等

                 MI     负数

                 VS     溢出

                 PL     正数或零

                 HI     无符号大于

                 LS     无符号小于或等于

                 CS     无符号大于或等于

                 CC     无符号小于

                 GT     有符号大于

                 GE     有符号大于或等于

                 LE     有符号小于或等于

                 LT     有符号小于

                 AL     无条件执行

 

       S     :决定指令的操作是否影响CPSR的值,用一个位存储,省略则表示为0值,否则为1

              如SUBS  R0,R0,#1   ;R01,结果放入R0,同时响影CPSR的值

                SUB   R0,R0,#1   ;R01,结果放入R0,不影响CPSR的值

       Rd    :目标寄存器编码   用4位存储   

       Rn    :1个操作数的寄存器编码 用4位存储

       Operand2: 2个操作数   用12位存储      

    寻址方式:是根据指令中给出的地址码字段来实现寻找真实操作数地址的方式,8种寻址方式:

         寄存器寻址、立即寻址、寄存器间接寻址、基址寻址、多寄存器寻址、堆栈寻址、相对寻址、寄存器移位寻址

         

    //*******寻址方式是重点  需要理解背讼*************************************************

    

1.立即寻址

    操作数是常量,用#表示常量。例

    MOV  R0,#0xFF000   ;指令省略了第1个操作数寄存器。将立即数0xFF000(2操作数)装入R0寄存器  

    SUB  R0,R0,#64     ;R064,结果放入R0

    #表示立即数   0x&表示16进制数  否则表示十进制数 

    立即数寻址指令中的地址码就是操作数本身,可以立即使用的操作数。

    其中,#0xFF000#64都是立即数。该立即数位于32位机器码中,占低位的12位。也就是说在ARM指令中以12位存储一个数据

 那么,32位的数据(longfloat)如何存储?

    32位的数据以一种特殊的方式来处理

    其中:高4位表示的无符号整数

          低8位补0扩展为32位,然后循环右移x位来代表一个数。x=4位整数*2

          所以,不是每一个32位数都是合法的立即数,只有能通过上述构造得到的才是合法的立好数。如:

          合法立即数:0xff,0x104,0xff0

          不合法立即数:ox1010x102,0xff1

2.寄存器寻址

    操作数的值在寄存器中,指令执行时直接取出寄存器值来操作

      例:MOV R1,R2         ;R2的值存入R1     在第1个操作数寄存器的位置存放R2编码

          SUB R0,R1,R2      ;R1的值减去R2的值,结果保存到R0   在第2操作数位置,存放的是寄存器R2的编码

          

    寄存器寻址是根据寄存器编码获取寄存器内存储的操作数    

3.寄存器间接寻址

    操作数从寄存器所指向的内存中取出,寄存地存储的是内存地址

      例:LDR R1,[R2]          ;R2指向的存储单元的数据读出,保存在R1中 R2相当于指针变量

          STR R1,[R2]          ;R1的值写入到R2所指向的内存

          SWP R1,R1,[R2]       ;将寄存器R1的值和R2指定的存储单元的内容交换

      [R2]表示寄存器所指向的内存,相当于 *p

      LDR 指令用于读取内存数据

      STR 指令用于写入内存数据      

//-----------------------------------------------------

4.寄存器基址寻址

    操作数从内存址偏移后读取数据。常用于查表、数组操作、功能部件寄存器访问等。

    基址:相当于首地址,地址偏移后取值作为操作数

    基址寄存器:用来存放基址的寄存器    

    变址寻址:基址寻址也称为变址寻址

    

    1)前索引寻址

      例:LDR R2,[R3,#4]     ;读取R3+4地址上的存储单元的内容,放入R2

          LDR R2,[R3,#4]!    ;读取R3+4地址上的存储单元的内容,放入R2,然后R3内的地址变为R3+4,R3=R3+4

      

      [R3,#4] 表示地址偏移后取值,相当于*(p+4) 或 p[4]R3内保存的地址不变

      [R3,#4]! 表示地址偏移后取值,相当于*(p+4)  或 p[4],!表示回写,即R3=R3-4 ,R3的值发生了改变 

    

     2)后索引寻址 

      例:LDR R2,[R3],#4     ;先读取R3地址上的存储单元的内容,放入R2,然后R3内的地址变为R3+4,R3=R3+4

        

      [R3],#4     类似于 *p++ 只不过自加的不是,而是指定的

      [R3,#4]!    类似于 *++p 只不过自加的不是1,而是指定的4                            

     

     3)寄存器的值作索引

      例:LDR R2,[R3,R0]     ;R0内存储索引值,R3内存地址,读取R3+R0地址上的存储单元的内容,放入R2

      

      [R3,R0] 表示地址偏移后取值,相当于*(p+i) 或 p[i]

      

      

5.多寄存器寻址

    一次可传送多个寄存器的值,也称为块拷贝寻址

        

    例:LDMIA R1!,{R2-R7,R12} ;R1指向的存储单元中的数据读写到R2R7R12中,然后R1自加1

        STMIA R1!,{R2-R7,R12} ;将寄存器R2R7R12的值保存到R1指向的存储单元中,然后R1自加1

        

        其中 R1是基址寄存器,用来存基址,R2-R7R12用来存数据 赋值编号小的寄存器与低地址相对应,与寄存器列表顺序无关

             ! 为可选后缀,表示改变R1的值,则当数据传送完毕之后,将最后的地址写入基址寄存器

             基址寄存器不允许为R15,寄存器列表可以为R0R15 的任意组合。

             这里R1没有写成[R1]!,是因为这个位不是操作数位,而是寄存器位

             LDMIA 和 STMIA  是块拷贝指令,  LDMIA是从R1所指向的内存中读数据,STMIA是向R1所指向的内存写入数据

             

             R1指向的是连续地址空间

6.寄存器堆栈寻址

    是按特定顺序存取存储区,按后进先出原则,使用专门的寄存器SP(堆栈指针)指向一块存储区

    例:LDMIA SP!,{R2-R7,R12} ;将栈内的数据,读写到R2R7R12中,然后下一个地址成为栈顶

        STMIA SP!,{R2-R7,R12} ;将寄存器R2R7R12的值保存到SP指向的栈中

        

        SP指向的是栈顶

7.相对寻址

    即读取指令本身在内存中的地址。是相对于PC内指令地址偏移后的地址。

    

    由程序计数器PC提供基准地址,指令中的地址码字段作为偏移量,两者相加后得到的地址即为操作数的有效地址。

    例:

      B LOOP    ;B指令用于跳转到标号LOOP指令处执行代码

      ...

    LOOP

      MOV R6 #1

      

      其中LOOP仅仅是标号,而不是地址,不是CPU指令,所以在指令的机器码中没有标号的机器码,而是

  计算出了从B指令到MOV指令之间内存地址的差值,这个差值相当于PC的偏移量,即相当于:add pc,pc,#偏移量

      B 指令引起了PC寄存器值的变化,PC内永远保存将要运行指令在内存中的地址。

      

8.寄存器移位寻址

      操作数在被使用前进行移位运算

      例:MOV R0,R2,LSL #3     ;R2的值左移3位,结果放入R0,       ;即是R0=R2×8  

      

      LSL 是移位指令,用于将前面寄存器的数据 左移

//^^^^^^^^^^^^^^^^^^^^^^^^^^ 下午 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

   ARM汇编语言语句由 指令、伪指令、伪操作、宏指令组成

.ARM指令集

    共包括6种类型:数据处理指令、跳转指令、程序状态指令、加载存取储指令、协处理指令、软中断指令

(一)、数据处理指令

     数据处理指令,只能对寄存器内容进行操作,而不能对内存进行操作,所有数据处理指令均可使用S后缀,并影响状态标志

 包括:数据传送指令、算述指令、乘法指令、逻辑运算指令、比较指令、移位指令

1. 数据传输指令

  1) MOV 数据传送指令 

     格式:MOV Rd,<op1>

     功能:Rd=操作数操作数可以是寄存器、被移位的寄存器或立即数。

     例:

      MOV  R0,#0xFF000  ;立即寻址,将立即数0xFF000(2操作数)装入R0寄存器  

      MOV R1,R2         ;寄存器寻址,将R2的值存入R1 

      MOV R0,R2,LSL #3  ;移位寻址,R2的值左移3位,结果放入R0

  2) MVN 数据取反传送指令

     格式:MVN <Rd>,<op1>; 

     功能:将操作数传送到目的寄存器Rd中,但该值在传送前被按位取反,即Rd=!op1

     例:

       MVN R0,0 ;R0=-1

       

     mvn 意为“取反传输”,它把源寄存器的每一位取反,将得到的结果写入结果寄存器。

     movsmvns指令对pc寄存器赋值时有特殊含义,表示要求在赋值的同时从spsr中恢复cpsr

     movmvn指令,编译器会进行智能的转化。比如指令“mov r1, 0xffffff00”中的立即数是非法的。

  在编译时,编译器将其转化为“mvn r1, 0xff”,这样就不违背立即数的要求。所以对于movmvn指令,

  可以认为:合法的立即数反码也是合法的立即数。

      

2.算术指令

  1) ADD 加法指令 

     格式:ADD{<cond>}{S} <Rd>,<Rn>,<op2>; 

     功能:RdRn+op2

     例: 

       ADD R0,R1,5       ;R0=R1+5 

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

       ADD R0,R1,R2,LSL5 ;R0=R1+R2左移5位 

  2) ADC 带进位加法指令 

     格式:ADC{<cond>}{S} <Rd>,<Rn>,<op2>; 

     功能:RdRn+op2+carry ,carry为进位标志值。该指令用于实现超过32位的数的加法。 

     例如: 

         第一个64位操作数存放在寄存器R2R3中; 

         第二个64位操作数存放在寄存器R4R5中; 

         64位结果存放在R0R1中。 

        

         ADDS R0,R2,R4 ;32位相加,S表示结果影响条件标志位的值 

         ADC R1,R3,R5 ;32位相加 

  3) SUB 减法指令

     格式:SUB{<cond>}{S} <Rd>,<Rn>,<op2>; 

     功能:RdRn-op2 

     例: 

       SUB R0,R1,5 ;R0=R1-5 

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

       SUB R0,R1,R2,LSL5 ;R0=R1-R2左移5位 

  4) SBC 带借位减法指令 

     格式:SBC{<cond>}{S} <Rd>,<Rn>,<op2>; 

     功能:RdRn-op2-!carry 

     SUBSBC生成进位标志的方式不同于常规,如果需要借位则清除进位标志,所以指令要对进位标志进行一个非操作。 

     例: 

        第一个64位操作数存放在寄存器R2R3中; 

        第二个64位操作数存放在寄存器R4R5中; 

        64位结果存放在R0R1中。 

        SUBS R0,R2,R4; 32位相减,S表示结果影响条件标志位的值 

        SBC R1,R3,R5; 32位相减 

   5) 其它减法指令

      RSB 反向减法指令,SUB指令,但倒换了两操作数的前后位置,即Rdop2-Rn

      RSC 带借位的反向减法指令,SBC指令,但倒换了两操作数的前后位置,即Rdop2-Rn-!carry。 

      rsb r0, r1, r2 /* r0 = r2 – r1 */

      rsc r0, r1, r2 /* r0 = r2 – r1 + carry – 1 */

    

      addsadcs在进位时将cpsrC标志置1;否则置0

      subssbcs在产生借位时将cpsrC标志置0;否则置1

3.乘法指令 

   1) MUL 32位乘法指令 

      格式:MUL{<cond>}{S} <Rd>,<Rn>,<op2>; 

      功能:RdRn×op2 

      该指令根据S标志,决定操作是否影响CPSR的值;其中op2必须为寄存器。Rnop2的值为32位的有符号数或无符号数。 

      例: 

         MULS R0,R1,R2 ;R0R1×R2,结果影响寄存器CPSR的值 

   2) MLA 32位乘加指令 

      格式:MLA{<cond>}{S} <Rd>,<Rn>,<op2>,<op3>; 

      功能:RdRn×op2+op3  op2op3必须为寄存器。Rnop2op3的值为32位的有符号数或无符号数。 

      例: 

         MLA R0,R1,R2,R3 ;R0R1×R2+R3 

   3) SMULL 64位有符号数乘法指令 

      格式:SMULL{<cond>}{S} <Rdl>,<Rdh>,<Rn>,<op2>; 

      功能:Rdh RdlRn×op2   RdhRdlop2均为寄存器。Rnop2的值为32位的有符号数。 

      例: 

         SMULL R0,R1,R2,R3 ;R0R2×R3的低32位 R1R2×R3的高32位 

   4) SMLAL 64位有符号数乘加指令 

      格式:SMLAL{<cond>}{S} <Rdl>,<Rdh>,<Rn>,<op2>; 

      功能:Rdh RdlRn×op2+Rdh Rdl  ;RdhRdlop2均为寄存器。Rnop2的值为32位的有符号数,Rdh Rdl的值为64位的加数。 

      例: 

         SMLAL R0,R1,R2,R3   ;R0R2×R3的低32+R0 R1R2×R3的高32+R1 

   5) UMULL 64位无符号数乘法指令 

      格式:UMULL{<cond>}{S} <Rdl>,<Rdh>,<Rn>,<op2>; 

      功能:同SMULL指令,但指令中Rnop2的值为32位的无符号数。 

      例: 

         UMULL R0,R1,R2,R3   ;R0R2×R3的低32位 R1R2×R3的高32位 其中R2R3的值为无符号数 

   6) UMLAL 64位无符号数乘加指令 

      格式:UMLAL {<cond>}{S} <Rdl>,<Rdh>,<Rn>,<op2>; 

      功能:同SMLAL指令,但指令中Rnop2的值为32位的无符号数,Rdh Rdl的值为64位无符号数。 

      例: 

         UMLAL R0,R1,R2,R3  ;R0R2×R3的低32+R0 R1R2×R3的高32+R1  

                            ;其中R2R3的值为32位无符号数 R1R0的值为64位无符号数 

4. 逻辑指令:andorreorbic

   1) AND 逻辑与指令 

      格式:AND{<cond>}{S} <Rd>,<Rn>,<op2>; 

      功能:RdRn AND op2  一般用于清除Rn的特定几位。

      例: 

         AND R0,R0,5  ;保持R0的第0位和第2位,其余位清

   2) ORR 逻辑或指令 

      格式:ORR{<cond>}{S} <Rd>,<Rn>,<op2>; 

      功能:RdRn OR op2 一般用于设置Rn的特定几位。

      例: 

        ORR R0,R0,5  ;R0的第0位和第2位设置为1,其余位不变 

   3) EOR 逻辑异或指令 

      格式:EOR{<cond>}{S} <Rd>,<Rn>,<op2>;

      功能:RdRn EOR op2  一般用于将Rn的特定几位取反。 

      例: 

        EOR R0,R0,5  ;R0的第0位和第2位取反,其余位不变 

   4) BIC 位清除指令 

      格式:BIC{<cond>}{S} <Rd>,<Rn>,<op2>; 

      功能:RdRn AND (!op2) 用于清除寄存器Rn中的某些位,并把结果存放到目的寄存器Rd

      例: 

        BIC R0,R0,5 ;R0中第0位和第2位清0,其余位不变

5. 比较指令

   1) CMP 比较指令

      格式:CMP{<cond>} <Rn>,<op1>; 

      功能:Rn-op1,根据结果更新CPSR中条件标志位的值。

      例:

         CMP R0,5        ;计算R0-5,根据结果设置条件标志位 

         ADDGT R0,R0,5   ;ADD为加法指令,GT为判断条件标志位是 否大于5, 如果R0>5,则执行ADD指令

   2) CMN 反值比较指令 

      格式:CMN{<cond>} <Rn>,<op1>; 

      功能:同CMP指令,但寄存器Rn的值是和op1取负的值进行比较。

      例: 

         CMN R0,5 ;R0-5进行比较 

   3) TST 位测试指令

      格式:TST{<cond>} <Rn>,<op1>; 

      功能: Rn AND op1 按位与后,更新CPSR中条件标志位,用于检查寄存器Rn是否设置了op1中相应的位。

      例: 

         TST R0,#5 ;测试R0中第0位和第2位是否为

   4) TEQ 相等测试指令 

      格式:TEQ{<cond>} <Rn>,<op1>; 

      功能:Rn EOR op1  按位异或后,更新CPSR中条件标志位,用于检查寄存器Rn的值是否和op1所表示的值相等。

      例: 

         TEQ R0,5 ;判断R0的值是否和5相等 

6.移位指令(位运算指令)

   1) LSL(ASL) 左移

      格式:寄存器,LSL(ASL ) 操作数

      功能:将寄存器内的数据左移,操作数是移位的位数在0-31之间

      例:

        MOV R0, R1, LSL#2;将 R1中的内容左移两位后传送到 R0中。

   2) LSR操作 右移

      格式寄存器 LSR 操作数

      功能将寄存嚣内的数据右移

      例:

        MOV R0, R1, LSR#2;将 R1中的内容右移两位后传送到 R0中,左端用零来填充。

   3)其它移位

      ASR  右移,左端用第31位值来填充

      ROR  右移,循环右移,左端用右端移出的位来填充

      RRX  右移,循环右移,左端用进位标志位C来填充

      

//************************************* arm体系结构 第三天***************************************

()、跳转指令

   1) B 跳转指令 

      格式:B{<cond>} <addr>; 

      功能:跳转到addr地址。

      例:

         B exit; 程序跳转到标号exit处 

   2) BL 带返回的跳转指令 

      格式:BL{<cond>} <addr>; 

      功能:同B指令,但BL指令执行跳转操作的同时,还将PC(寄存器R15)的值保存到LR寄存器(寄存器R14)中。

            该指令用于实现子程序调用,程序的返回可通过把LR寄存器的值复制到PC寄存器中来实现。 

      例: 

      BL func; 调用子程序func 

      … 

      func 

      … 

      MOV R15,R14; 子程序返回        

   3) 其它跳转指令

      BLX 带返回和状态切换的跳转指令,用于子程序调用和程序Thumb状态的切换。 

      BX 带状态切换的跳转指令,处理器跳转到目标地址处,目标地址处的指令可以是ARM指令,也可以是Thumb 指令。 

   跳转指令用于实现程序的跳转和程序状态的切换。 

   ARM程序设计中,实现程序跳转有两种方式:跳转指令、直接向程序寄存器PC中写入目标地址值。

//----------------------------------------------------------------------------------------

(三)、程序状态指令

      用于状态寄存器和通用寄存器间传送数据。总共有两条指令:MRSMSR。两者结合可用来修改程序状态寄存器的值。

   1) MRS 程序状态寄存器到通用寄存器的数据传送指令

      格式:MRS{<cond>} <Rd>,CPSR/SPSR; 

      功能:用于将程序状态寄存器的内容传送到目标寄存器Rd中。

      例: 

          MRS R0,CPSR  ;状态寄存器CPSR的值存入寄存器R0中 

   2) MSR 通用寄存器到程序状态寄存器的数据传送指令 

      格式:MSR{<cond>} CPSR/SPSR_<field>,<op1>; 

      功能:用于将寄存器Rd的值传送到程序状态寄存器中。 

      <field>:用来设置状态寄存器中需要操作的位。 

               32位的状态寄存器可以分为4个域: 

               位[3124]为条件标志位域,用f表示。 

               位[2316]为状态位域,用s表示。 

               位[158]为扩展位域,用x表示。 

               位[70]为控制位域,用c表示。 

      例:

          MSR CPSR_f,R0 ;R0的值修改CPSR的条件标志域 

          MSR CPSR_fsxc,#5; CPSR的值修改为

(四)、加载存储指令

    该集合的指令使用频繁,当数据存放在内存中时,必须先把数据从内存装载到寄存器,执行完后再把寄存器

中的数据存储到内存中

1.单数据访存指令

  1)单数据加载指令 

      格式:LDR(LDRBLDRBTLDRHLDRSBLDRSHLDRTSTRSTRBSTRBTSTRHSTRT) <Rd>,<addr>; 

      功能:内存地址中的数据装载到目标寄存器Rd中,同时还可以把合成的有效地址写回到基址寄存器。 

      寻址方式:Rn:基址寄存器。Rm:变址寄存器。Index:偏移量,12位的无符号数。

            LDR Rd,[Rn]             ;把内存中地址为Rn的字数据装入寄存器Rd中 

            LDR Rd,[Rn,Rm]          ;将内存中地址为Rn+Rm的字数据装入寄存器Rd中 

            LDR Rd,[Rn,index]     ;将内存中地址为Rn+index的字数据装入Rd中 

            LDR Rd,[Rn,Rm,LSL5]   ;将内存中地址为Rn+Rm×32的字数据装入Rd 

            LDR Rd,[Rn,Rm]!         ;将内存中地址为Rn+Rm的字数据装入Rd,并将新地址Rn+Rm写入Rn 

            LDR Rd,[Rn,index]!    ;将内存中地址为Rn+index的字数据装入Rd,并将新地址Rn+index写入Rn 

            LDR Rd,[Rn,RmLSL5]! ;将内存中地址为Rn+Rm×32的字数据装入Rd,并将新地址Rn+Rm×32写入Rn 

            LDR Rd,[Rn],Rm          ;将内存中地址为Rn的字数据装入寄存器Rd,并将新地址Rn+Rm写入Rn 

            LDR Rd,[Rn],index     ;将内存中地址为Rn的字数据装入寄存器Rd,并将新地址Rn+index写入Rn 

            LDR Rd,[Rn],Rm,LSL5   ;将内存中地址为Rn的字数据装入寄存器Rd,并将新地址Rn+Rm×32写入Rn 

      各指令的区别:

      (1) LDR  字数据加载指令  

          将内存地址中的字数据装载到目标寄存器Rd

          例: LDR R0,[R1,R2,LSL5]! ;将内存中地址为R1+R2×32的字数据装入寄存器R0,并将新地址R1+R2×32写入R1 

          

      (2) LDRB 字节数据加载指令

          同LDR,只是从内存读取一个8位的字节数据而不是一个32位的字数据,并将Rd的高24位清0。        

          例: LDRB R0,[R1]           ;将内存中起始地址为R1的一个字节数据装入R0中 

      

      (3) LDRBT 用户模式的字节数据加载指令

          同LDRB指令,但无论处理器处于何种模式,都将该指令当作一般用户模式下的内存操作。 

      (4) LDRH 半字数据加载指令

          同LDR指令,但该指令只是从内存读取一个16位的半字数据而不是一个32位的字数据,并将Rd的高16位清0

          例:LDRH R0,[R1]            ;将内存中起始地址为R1的一个半字数据装入R0中 

      (5) LDRSB 有符号的字节数据加载指令

          同LDRB指令,但该指令将寄存器Rd的高24位设置成所装载的字节数据符号位的值。 

          例:LDRSB R0,[R1] ;将内存中起始地址为R1的一个字节数据装入R0中,R0的高24位设置成该字节数据的符号位 

      (6) LDRSH 有符号的半字数据加载指令

          同LDRH指令,但该指令将寄存器Rd的高16位设置成所装载的半字数据符号位的值。

          例:LDRSH R0,[R1] ;将内存中起始地址为R1的一个16位半字数据装入R0中,R0的高16位设置成该半字数据的符号位 

      (7) LDRT 用户模式的字数据加载指令

          同LDR指令,但无论处理器处于何种模式,都将该指令当作一般用户模式下的内存操作。有效地址必须是字对齐的

  2)单数据存储指令

      格式:STR(STRSTRBSTRBTSTRHSTRT) <Rd>,<addr>; 

      功能:将寄存器数据写入到内存中

      寻址方式:Rn:基址寄存器。Rm:变址寄存器。Index:偏移量,12位的无符号数。

            STR Rd,[Rn]             ;将寄存器Rd中的字数据写入到内存中地址为Rn内存中

            STR Rd,[Rn,Rm]          ;将寄存器Rd中的字数据写入到内存中地址为Rn+Rm的内存中

            STR Rd,[Rn,index]     ;将寄存器Rd中的字数据写入到内存中地址为Rn+index内存中

            STR Rd,[Rn,Rm,LSL5]   ;将寄存器Rd中的字数据写入到内存中地址为Rn+Rm×32内存中

            STR Rd,[Rn,Rm]!         ;将寄存器Rd中的字数据写入到内存中地址为Rn+Rm的内存中

            STR Rd,[Rn,index]!    ;将寄存器Rd中的字数据写入到内存中地址为Rn+index的内存中,并将新地址Rn+index写入Rn 

            STR Rd,[Rn,RmLSL5]! ;将寄存器Rd中的字数据写入到内存中地址为Rn+Rm×32的内存中,并将新地址Rn+Rm×32写入Rn 

            STR Rd,[Rn],Rm          ;将寄存器Rd中的字数据写入到内存中地址为Rn的内存中,并将新地址Rn+Rm写入Rn 

            STR Rd,[Rn],index     ;将寄存器Rd中的字数据写入到内存中地址为Rn的内存中,并将新地址Rn+index写入Rn 

            STR Rd,[Rn],Rm,LSL5   ;将寄存器Rd中的字数据写入到内存中地址为Rn的内存中,并将新地址Rn+Rm×32写入Rn 

   

      (1) STR 字数据存储指令

          把寄存器Rd中的字数据(32位)保存到addr所表示的内存地址中,同时还可以把合成的有效地址写回到基址寄存器。

          例: STR R0,[R1,5]!  ;R0中的字数据保存到以R1+5为地址的内存中,然后R1R1+5 

      (2) STRB 字节数据存储指令

          把寄存器Rd中的低8位字节数据保存到addr所表示的内存地址中。

          例: STRB R0,[R1]  ;将寄存器R0中的低8位数据存入R1表示的内存地址中 

      (3) STRBT 用户模式的字节数据存储指令 

          同STRB指令,但无论处理器处于何种模式,该指令都将被当作一般用户模式下的内存操作。

      (4) STRH 半字数据存储指令

          把寄存器Rd中的低16位半字数据保存到addr所表示的内存地址中,而且addr所表示的地址必须是半字对齐的。 

          例:STRH R0,[R1]  ;将寄存器R0中的低16位数据存入R1表示的内存地址中 

      (5) STRT 用户模式的字数据存储指令

          同STR指令,但无论处理器处于何种模式,该指令都将被当作一般用户模式下的内存操作。 

2.多数据访存指令

   1) 批量数据加载指令 

      格式:LDM{<cond>}{<type>} <Rn>{!},<regs>{^}; 

      功能:从一片连续的内存单元读取数据到各个寄存器中,内存单元的起始地址为基址寄存器Rn的值,各个寄存器由寄存

            器列表regs表示。 

      该指令一般用于多个寄存器数据的出栈。 

      type字段种类: 

          IA:每次传送后地址加1。 

          IB:每次传送前地址加1。 

          DA:每次传送后地址减1。 

          DB:每次传送前地址减1。 

          FD:满递减堆栈。 

          ED:空递减堆栈。 

          FA:满递增堆栈。 

          EA:空递增堆栈。 

      堆栈寻址的命令LDMFA/STMFALDMEA/STMEALDMFD/STMFDLDMED/STMED

      LDMSTM表示多寄存器寻址,即一次可以传送多个寄存器值。

      LDM:一次装载多个,这里用来出栈。

      STM:一次存储多个,这里用来入栈。

      F/E表示指针指向的位置

      Ffull满堆栈,表示堆栈指针指向最后一个入栈的有效数据项。

      Eempty空堆栈,表示堆栈指针指向下一个要放入的空地址。

      A/D表示堆栈的生长方式

      A:堆栈向高地址生长,即递增堆栈。

      D:堆栈向低地址生长,即递减堆栈。

      注意:有一个约定,编号低的寄存器在存储数据或者加载数据时对应于存储器的低地址。

            FDEDFAEA指定是满栈还是空栈,是升序栈还是降序栈,用于堆栈寻址。

                一个满栈的栈指针指向上次写的最后一个数据单元

                空栈的栈指针指向第一个空闲单元。

                一个降序栈是在内存中反向增长而升序栈在内存中正向增长。

            {!}:若选用了此后缀,则当指令执行完毕后,将最后的地址写入基址寄存器。 

            {^}:当regs中不包含PC时,该后缀用于指示指令所用的寄存器为用户模式下的寄存器,

                 否则指示指令执行时,将寄存器SPSR的值复制到CPSR中。

                 

   2) 批量数据存储指令 

      格式:STM{<cond>}{<type>} <Rn>{!},<regs>{^}; 

      功能:将各个寄存器的值存入一片连续的内存单元中,内存单元的起始地址为基址寄存器Rn的值

            各个寄存器由寄存器列表regs表示。 该指令一般用于多个寄存器数据的入栈。 

      {^}:指示指令所用的寄存器为用户模式下的寄存器。其他参数用法同LDM指令。

      

      例:STMEA R13!,{R0-R12,PC}  ;将寄存器R0~R12以及程序计数器PC的值保存到R13指示的堆栈中 

3.数据交换指令

   1) 字数据交换指令

      格式:SWP <Rd>,<op1>,[<op2>]; 

      功能:Rd[op2][op2]op1 

            从op2所表示的内存装载一个字并把这个字放置到目的寄存器Rd中,然后把寄存器op1的内容存储到同一内存地址中。 

      

      例:SWP R0,R1,[R2] ;R2所表示的内存单元中的字数据装载到R0,然后将R1中的字数据保存到R2所表示的内存单元中 

   2) 字节数据交换指令

      格式:SWPB <Rd>,<op1>,[<op2>]; 

      功能:从op2所表示的内存装载一个字节并把这个字节放置到目的寄存器Rd的低8位中,Rd的高24位设置为0

            然后将寄存器op1的低8位数据存储到同一内存地址中。 

      例:  SWPB R0,R1,[R2] ;R2所表示的内存单元中的一个字节数据装载到R0的低8位,然后将R1中的低8位字节

         数据保存到R2所表示的内存单元中 

//^^^^^^^^^^^^^^^^^^^^^^^^^^ 下午 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

()、协处理指令

   1) CDP协处理器操作指令 

      格式:CDP{<cond>}<p>,<opcode1>,<CRd>,<CRm>,<CRn>,<opcode2>; 

      功能:用于传递指令给协处理器p,要求其在寄存器CRnCRm上,进行操作opcode1,并把结果存放到CRd中,

            可以使用opcode2提供与操作有关的补充信息。指令中的所有寄存器均为协处理器的寄存器,操作由协处理器完成。 

      指令中

            P为协处理器编号; 

            CRd为目的寄存器的协处理器寄存器; 

            CRmCRn为存放操作数的协处理器寄存器; 

            Opcode1opcode2为协处理器即将执行的操作。

             

      例:CDP p55c0c1c29;该指令用于通知协处理器p5,在c1c2上执行操作59,并将结果存放到c0中。 

   2) LDC协处理器数据读取指令 

      格式:LDC {<cond>}{L}<p>,<CRd>,<addr>; 

      功能:将addr表示的内存地址中的连续数据传送到目的寄存器CRd中。 

            L表示指令为长读取操作,比如用于双精度数据的传输; 

            目的寄存器CRd为协处理器的寄存器; 

            addr的寻址方式同LDR指令,其寄存器为ARM处理器的寄存器。 

     

      例:LDC p5c1[R1+5]:该指令用于将R15所对应的存储单元中的数据,传送到协处理器p5的寄存器c1中。 

   

   3) STC协处理器数据存储指令 

      格式:STC{<cond>}{L}<p>,<CRd>,<addr>; 

      功能:将寄存器CRd的值传送到addr表示的内存地址中。指令中各参数用法同LDC。 

      例如:STC p5c1[R1+5]  ;该指令用于将协处理器p5中寄存器c1的数据传送到R15所对应的存储单元中。 

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

      格式:MCR{<cond>}<p>,<op1>,<Rd>,<CRn>,<CRm>{,op2}; 

      功能:将ARM处理器的寄存器Rd中的数据传送到协处理器p的寄存器CRnCRm中;op1op2为协处理器将要执行的操作。 

      例:MCR p5,5,R1,C1,C2,9;该指令将R1中的数据传送到协处理器p5的寄存器C1C2中,协处理器执行操作59。 

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

      格式:MRC {<cond>}<p>,<op1>,<Rd>,<CRn>,<CRm>{,op2}; 

      功能:将协处理器p的寄存器CRnCRm的数据传送到ARM处理器的寄存器Rd中;op1op2为协处理器将要执行的操作。 

      例:MRC p5,5,R1,C1,C2,9;该指令将寄存器C1C2中的数据传送到R1中,协处理器p5协处理器执行操作59。 

()、异常中断指令

    异常中断产生指令:用于系统调用和调试。 

   1) SWI 软件中断指令 

      格式:SWI {<cond>} 24位的立即数; 

      功能:用于产生软件中断,以使用户程序调用操作系统的系统例程。 

            指令中24位的立即数指定用户程序调用系统例程的类型,其参数通过通用寄存器传递。当24位的立即数

            被忽略时,系统例程类型由寄存器R0指定,其参数通过其他通用寄存器传递。 

      例:SWI 0X05; 调用编号为05的系统例程。 

   2) BKPT 断点中断指令 

      格式:BKPT 16位的立即数; 

      功能:用于产生软件断点中断,以便软件调试时使用。16位的立即数用于保存软件调试中额外的断点信息。 

      指令操作的伪代码: 

()、信号量操作指令

      信号量操作指令:用于进程间的同步互斥,提供对信号量的原子操作。

()ARM程序常见结构

1.子函数和主函数

       使用BL 指令进行调用,该指令会把返回的PC 值保存在LR

           AREA Example,CODE,READONLY ;声明代码段Example

           ENTRY                      ;程序入口

       Start 

           MOV  R0,#0                 ;设置实参,将传递给子程骗子的实参存放在r0r1

           MOV  R1,#10

           BL   ADD_SUM               ;调用子程序ADD_SUM

           B    OVER                  ;跳转到OVER标号处,进入结尾

       ADD_SUM

           ADD  R0,R0,R1              ;实现两数相加

           MOV  PC,LR                 ;子程序返回,R0内为返回的结果

       OVER

           END

      运行过程

      (1)程序从ENTRY后面指令处开始运行(即主函数)

         R0R1先准备好数据令子函数运算时使用

      (2)BL  为跳转指令,用于调用子函数 后面为函数标号。

         在调用函数的过程中,自动将PC内的值保存到LR(R14)内备份,PC内当前的值为下一条要执行的指令地址

         (即B OVER指令地址),在子函数结束前将该地址恢复到PC

      (3)B   为跳转指令,用于跳转到指定的标号后,此处跳转到程序结尾

      (4)MOV PC,LR 是子函数内的最后一条语句,用于将LR内保存的地址恢复到PC

         PC(R15)  程序计数器 存储要执行的指令在内存中的地址

                  PC的值 当前正在执行指令在内存中的地址 + 8

2.条件跳转语句

        AREA Example,CODE,READONLY ;声明代码段Example

        ENTRY                      ;程序入口

    Start

        MOV  R0,#2                 ;R0赋初值

        MOV  R1,#5                 ;R1赋初值5

        ADD  R5,R0,R1              ;R0R1内的值相加并存入R5

        CMP  R5,#10                

        BEQ  DOEQUAL               ;R510,则跳转到DOEQUAL标签处

    WAIT    

        CMP  R0,R1 

        ADDHI R2,R0,#10            ;R0 >R1 R2=R0+10

        ADDLS R2,R1,#5             ;R1<=R2 R2=R1+5

    DOEQUAL

        ANDS R1,R1,#0x80           ;R1=R1& 0x80,并设置相应标志位

        BNE  WAIT                  ;R1 d7位为1则跳转到WAIT标签

    OVER

        END 

        

    运行过程

      (1)程序从ENTRY后面指令处开始运行(即主函数)

         R0R1赋初值25

         将R0R1相加后存入R5

      (2)CMP 用于比较两个数据,指令格式如下:

         CMP 操作数1,操作数2

         CMP用于把一个寄存器的内容和另一个寄存器或立即数进行比较,同时

      更新CPSR中条件标志位的值。标志位表示操作数1和操作数2的关系。然后执行后面的语句

      (3)条件助记符

         BEQ   B为跳转指令,EQ为条件相等,读取CPSR内的条件标志位,如相等则跳转到所指定的标号处

         BNE   B为跳转指令,NE为不相等(0),如不相等则跳转到所指定的标号处

         ADDHI ADD为相加指令,HI为无符号大于,如大于则执行相加

         ADDLS ADD为相加指令,LS为无符号小于或等于,如小于或等于则相加

      (4)位运算

         ANDS  AND 按位与,0x80取出第7位, S为将该位与的值写入标志位

3.循环语句

        AREA Example,CODE,READONLY ;声明代码段Example

        ENTRY                      ;程序入口

    Start

        MOV  R1,#0                 ;R1赋初值0

    LOOP     

        ADD  R1,R1,#1

        CMP  R1,#10

        BCC  LOOP                  ;R1小于10则执行跳转到LOOP处执行循环,即R1010后退出循环

        END 

    例:编写一具有完整汇编格式的程序,实现冒泡法排序功能。

        设无符号字数据存放在从0x400004开始的区域,字数据的数目字存放在0x400000中。

        AREA   SORTCODEREADONLY

    ENTRY

    START

    MOV     R1#0x400000

    LP

    SUBS     R1R1#1

    BEQ     EXIT

    MOV     R7R1

    LDR     R0=0x400004

    LP1

    LDR     R2[R0]#4

    LDR     R3[R0]

    CMP     R2R3

    STRLO   R3[R0, # -4]

        STRLO   R2[R0]

      SUBS      R7R7#1

      BNE     LP1

      B     LP

    EXIT

      END

    练习:

      1. 编写1+2+3++100的汇编程序。

      2. 实现子函数,该函数返回两个参数中的最大值,在主函数内调用。

//************************************* arm体系结构 第四天***************************************

.伪操作和宏指令

   伪指令——是汇编语言程序里的特殊指令助记符,在汇编时被合适的机器指令替代。

   伪操作——为汇编程序所用,在源程序进行汇编时由汇编程序处理,只在汇编过程起作用,不参与程序运行。

   宏指令——通过伪操作定义的一段独立的代码。在调用它时将宏体插入到源程序中。也就是常说的宏。

   

   说明:所有的伪指令、伪操作和宏指令,均与具体的开发工具中的编译器有关

1.宏定义(MACROMEND)    

    格式:MACRO

            {$标号名宏名{ $ 参数 , 参数 ,……}    

            指令序列    

          MEND 

          MACRO 、 MEND 伪指令可以将一段代码定义为一个整体,称为宏指令,在程序中通过宏指令多次调用该段代码。

          {}为可选项

          $ 标号在宏指令被展开时,标号会被替换为用户定义的符号

          在宏定义体的第一行应声明宏的原型(包含宏名、所需的参数),然后就可以在汇编程序中通过宏名来调用该指令序列

          

          写在代码段或数据段前面

          MEXIT 跳出宏

          

    例:没有参数的宏(实现子函数返回)

            MACRO

               MOV_PC_LR    ;宏名

               MOV PC,LR    ;子程序返回,R0内为返回的结果

            MEND

            

            AREA Example,CODE,READONLY ;声明代码段Example

            ENTRY                      ;程序入口

        Start 

            MOV  R0,#0                 ;设置实参,将传递给子程骗子的实参存放在r0r1

            MOV  R1,#10

            BL   ADD_NUM               ;调用子程序ADD_NUM

            BL   SUB_NUM               ;调用子程序SUB_NUM

            B    OVER                  ;跳转到OVER标号处,进入结尾

        

            EXPORT ADD_NUM             

        ADD_NUM

            ADD  R0,R0,R1              ;实现两数相加

            MOV_PC_LR                  ;调用宏,代表子函数结束

            

            EXPORT SUB_NUM             

        SUB_NUM

            SUB  R0,R1,R0              ;实现两数相减

            MOV_PC_LR                  ;调用宏,代表子函数结束                            

        OVER

            END          

    例:有参数宏

        宏定义从MACRO 伪指令开始,到MEND 结束,并可以使用参数。类似于C#define

        MACRO                ;宏定义

           CALL $Function,$dat1,$dat2 ;宏名称为CALL,个参数

           IMPORT $Function  ;声明外部子程序 宏开始

           MOV R0,$dat1      ;设置子程序参数,R0=$dat1

           MOV R1,$dat2

           BL $Function      ;调用子程序 宏最后一句

        MEND                 ;宏定义结束

        

        CALL FADD1,#3,#2     ;宏调用,后面是三个参数

        

        汇编预处理后,宏调用将被展开,程序清单如下:

        IMPORT FADD1

        MOV R0,#3

        MOV R1,#3

        BL FADD1          

2.符号定义伪操作    

  1)定义常量(EQU)   

     

    格式:标号名称 EQU 表达式 ,类型 }

          用于为程序中的常量、标号等定义一个等效的字符名称,类似于 语言中的# define 

          其中 EQU 可用 代替。

       

          标号名称:为常量名。

          表达式:寄存器的地址值、程序中的标号、32位地址常量、32位常量

                  当表达式为 32 位的常量时,可以指定表达式的数据类型,可以有以下三种类型:  

                  CODE16 、 CODE32 和 DATA

                      

    示例:EQU的使用

    X     EQU 45    ;#define X 45,必须顶格

    Y     EQU 64

    stack_top EQU 0x30200000CODE32    

     

         AREA Example,CODE,READONLY             

         CODE32     

         ENTRY                         

    Start     

         LDR SP,=stack_top ; stack_top内的值0x30200000是地址,=stack_top是取stack_top常量地址(即指针的指针

         MOV R0,#X    ;X替换为45

         STR R0,[SP]  ;R0内的45存入到SP所指向的内存中(SP此时是指针的指针)

         MOV R0,#Y

         LDR R1,[SP]  ;从内存中读取数据到R1

         ADD R0,R0,R1

         STR R0,[SP]         

     END 

     注:X,Y,stack_top为标号,必须顶格写,大多写在代码段外面

   

    

  2)定义变量

    常量:数字常量,有三种表示方式:十进制数、十六进制数、字符串常量、布尔常量(testno SETS {FALSE})

    变量:数字变量、逻辑变量、字符串变量

    (1)*** GBLAGBLLGBLS 定义全局变量

    格式:GBLA(GBLLGBLS) 全局变量名

           GBLA 伪指令用于定义一个全局的数字变量,并初始化为 ;    

           GBLL 伪指令用于定义一个全局的逻辑变量,并初始化为 F ();    

           GBLS 伪指令用于定义一个全局的字符串变量,并初始化为空;  

           

           由于以上三条伪指令用于定义全局变量,因此在整个程序范围内变量名必须唯一。 

             

    示例:全局变量的定义与赋值    

          GBLA count    ;定义全局变量

    count SETA 2        ;给全局变量赋值为2,必须顶格

         AREA Example,CODE,READONLY             

         CODE32     

         ENTRY                     

    Start          

         MOV R0,#count  ;count内的值写入R0

         ADD R0,R0,#2

         B Start

         END   

    注: 在赋值过程中,全局变量名必须顶格写,全局变量常在代码段外定义和赋值       

    

    示例:变量与内存地址 

        GBLA globv

    globv SETA 23

    

        AREA Example,CODE,READONLY ;声明代码段Example

        ENTRY                      ;程序入口

    Start 

        LDR R0,=globv  ;golbv是全局变量,将内存地址读入到R0

        LDR R1,[R0]    ;将内存数据值读入到R1

        ADD R1,R1,#2

        STR R1,[R0]    ;将修改后数据再赋给变量

        MOV R0,#0   

    OVER

        END            

                

        注:#取变量值  =取变量地址  [R0]读取R0内地址所指向的数据值

  

    (2)***LCLALCLL LCLS 定义局部变量

    格式:LCLA ( LCLL 或 LCLS ) 局部变量名    

                   LCLA 伪指令用于定义一个局部的数字变量,并初始化为 ;    

          LCLL 伪指令用于定义一个局部的逻辑变量,并初始化为 F ();    

          LCLS 伪指令用于定义一个局部的字符串变量,并初始化为空;    

          以上三条伪指令必须写在宏定义内,用于声明局部变量,宏结束,局部变量不再起作用

          

    示例:

         LCLA num ;声明一个局部的数字变量,变量名为 num    

    num  SETA 0xaa ;将该变量赋值为 0xaa    

         LCLL isOk ;声明一个局部的逻辑变量,变量名为 isOk    

    isOk SETL ;将该变量赋值为真    

         LCLS str1 ;定义一个局部的字符串变量,变量名为 str1    

    str1 SETS "Testing" ;将该变量赋值为"Testing"   

          

    示例:局部变量的定义与赋值

        MACRO

        MOV_START       ;宏名

            LCLA x      ;定义局部变量

            LCLA y

    x SETA 12           ;必须顶格写

    y SETA 24        

            MOV R0,#2   

            MOV R1,#3

            ADD R0,R0,R1

        MEND

        

        AREA Example,CODE,READONLY ;声明代码段Example

        ENTRY                      ;程序入口

    Start 

        MOV_START

        MOV  R0,#0   

    OVER

        END             

   

    注: 在赋值过程中,局部变量名必须顶格写,局部变量必须在宏定义内使用

         

    (3)***SETASETL SETS  用于给一个已经定义的全局变量或局部变量赋值。    

        SETA 伪指令用于给一个数学变量赋值;    

        SETL 伪指令用于给一个逻辑变量赋值;    

        SETS 伪指令用于给一个字符串变量赋值;    

        其中,变量名为已经定义过的全局变量或局部变量,表达式为将要赋给变量的值。

    (4)变量代换 $

        $在数字变量前,将变值转换为十六进制字符串

        $在逻辑变量前,将变量转换为真或假

        $在字符串变量前,替换后面变量的字符串

        如:

          LCLS  Y1  ;定义局部字符串变量Y1Y2

          LCLS  Y2  

          Y1    SETS "WORLD!"

          Y2    SETS "LELLO,$Y1" ;将字符串Y2的值替换$Y1,形成新的字符串

  3)、定义一个寄存器(RN)    

         格式:名称 RN 表达式    

               RN 伪指令用于给一个寄存器定义一个别名。

         示例:    

               Temp RN R0 ;将R0 定义一个别名Temp    

  

  4) 定义寄存器列表(RLIST)    

    格式:名称 RLIST { 寄存器列表 }  

          用于对一个通用寄存器列表定义名称,使用该伪指令定义的名称可在 ARM 指令 LDM/STM 中使用。

          在LDM/STM指令中,寄存器列表中的寄存器访问次序总是先访问编号较低的寄存器,再访问编号较高的寄存器,而不管寄存器列表中各寄存器的排列顺序。

    

    示例:RegList RLIST {R0-R5 , R8 , R10} ;将寄存器列表名称定义为 RegList,用于多寄存器寻址(后面详解)

    

  5) 定义协处理器寄存器(CN)

     格式:名称 CN  协处理器的寄存器编号

     

     示例:Power CN 6   ;将协处理器的寄存器6名称定义为Power

  6) 定义协处理器(CP)

     格式名称 CP  协处理器名

     示例:Dmu CP 6 ;将协处理器6名称定义为Dmu 

  7) 定义浮点或精度寄存器(DN,SN,FN)

     格式:名称 DN 双精度寄存器编号   ;DN 为双精度VFP寄存器定义名称

     格式:名称 SN 单精度寄存器编号   ;SN 为单精度VFP寄存器定义名称

     格式:名称 FN 浮点寄存器编号     ;FN 为浮点寄存器定义名称

           

     示例:

     height DN  6  ;VFP双精度寄存器6名称定义为height

     width  SN  16 ;VFP单精度寄存器16名称定义为width

     height FN  6  ;将浮点寄存器6名称定义为height

   

//^^^^^^^^^^^^^^^^^^^^^^^^^^ 下午 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    

3.数据定义伪操作(申请内存)

    数据定义伪指令用于为特定的数据分配存储单元,同时可完成已分配存储单元的初始化。

    

    1) 按类型分配内存

       格式:标号 伪指令 表达式

          标号 

          表达式:初始化的值,表达式可以为程序标号或字符、数字表达式

          伪指令:如下

            (1) DCB   用于分配一片连续的字节存储单元(字符数组),可用 =号代替

                Str DCB "This is a test!" ;分配一片连续的字节存储单元并初始化。 

            (2) DCW(DCWU) 用于分配一片连续的半字存储单元(16位 短整型数组)DCW 半字对齐,DCWU不严格半字对齐。

                DataTest DCW 1,2,3

            (3) DCD(DCDU) 用于分配一片连续的字存储单元(32位,整型数组),DCD 可用 &代替,DCD字对齐的

                DataTest DCD 4,5,6

            (4) DCFD(DCFDU) 为双精度的浮点数分配一片连续的字存储单元,每个双精度的浮点数占据两个字单元。

                FDataTest DCFD 2E115,-5E7

            (5) DCFS(DCFSU) 为单精度的浮点数分配一片连续的字存储单元,每个单精度的浮点数占据一个字单元。

                FDataTest DCFS 2E5,-5E-7

            (6) DCQ(DCQU) 用于分配一片以 个字节为单位的连续存储区域(8字节为一个数据的数组)

                DataTest DCQ 100 ;分配一片连续的存储单元并初始化为指定的值100

                

     2)申请连续内存

        (1) 申请一个连续内存(SPACE

             用于分配一片连续的存储区域并初始化为 0,可用%代替

              格式标号 SPACE 表达式

                    表达式为要分配的字节数

                    

              例:DataSpace SPACE 100  ;分配连续 100 字节的存储单元并初始化为 。    

       

        (2)声明一个数据缓冲池的开始(LTORG)

              通常,把数据缓冲池放在代码段的最后面,下一个代码段之前,或END之前

              

           示例:           

              AREA Example,CODE,READONLY ;声明代码段Example

              ENTRY                      ;程序入口

           Start

              BL funcl

           funcl    

              LDR R0,=0x12345678

              ADD R1,R1,R0

              MOV PC,LR

              LTORG           ;定义缓冲池0x12345678 LTORG根据LDR确定内存地址

           data SPACE 4200     ;从当前位置开始分配4200字节内存

              END

        (3)定义一个结构化的内存表首地址(MAP

           格式:  MAP 表达式 ,基址寄存器 

                  用于定义一个结构化的内存表的首地址。 可用 代替。

                  表达式可以为程序中的标号或数学表达式,基址寄存器为可选项.

                  ** 当基址寄存器选项不存在时,表达式的值即为内存表的首地址,

                  ** 当该选项存在时,内存表的首地址为表达式的值与基址寄存器的和。    

           例:

              Datastruc   SPACE    280             ;分配280个字节单元

              MAP         Datastruc                ;内存表的首地址为 Datastruc内存块

           

        (4) 定义一个结构化内存表的数据域(FILED

            用于定义一个结构化内存表中的数据域。可用#代替

            格式:标号 FILED  表达式

                   FIELD 伪指令常与 MAP 伪指令配合使用来定义结构化的内存表。表达式的值为当前数据域所占的字节数。

                   标号为数据域(字段、成员变量)名  

                   ** MAP 伪指令定义内存表的首地址, 

                   ** FIELD 伪指令定义内存表中的各个数据域,并可以为每个数据域指定一个标号供其他的指令引用。                (3)内存首地址 (MAP)

            MAP 伪指令通常与 FIELD 伪指令配合使用来定义结构化的内存表。    

           示例:    

              Datastruc   SPACE    280             ;分配280个字节单元

              MAP         Datastruc                ;内存表的首地址为 Datastruc内存块

              consta      FIELD    4               ;字段consta长度4字节,相对地址0

              constab     FIELD    4               ;字段constab长度4字节,相对地址4

              x           FIELD    8               ;字段x长度8字节,相对地址8

              y           FIELD    8               ;字段y长度8字节,相对地址16

              string      FIELD    256             ;字段string长度256字节,相对地址24

              LDR  R6[R9,consta] ;引用内存表中的数据域

           注意:MAP伪操作和FIELD伪操作仅仅是定义数据结构,他们并不实际分配内存单元,而SPACE用于分配内存

          

4.汇编控制伪操作

    用于控制汇编程序的执行流程,常用的汇编控制伪指令包括以下几条:

    

    (1) IF 逻辑表达式...ELSE...ENDIF 条件控制

    (2) WHILE 逻辑表达式 ...WEND 循环控制   

    

    例:条件编译

          AREA Example,CODE,READONLY 

          CODE32                     

    Data_in *   100              ;定义标号Data_in的值为100 在 ENTRY入口之前

          GBLA count             ;定义全局变量

    count SETA 20            

          ENTRY                      

    Start

          IF count < Data_in      ;条件编译

             MOV R0,#3

          ELSE                   

             MOV R1,#24

          ENDIF

          MOV R1,#12

          ADD R0,R0,R1      

          

          END 

   例:循环编译

       GBLA Counter ;声明一个全局的数学变量,变量名为 Counter    

       Counter SETA 3 ;由变量Counter 控制循环次数    

       ……    

       WHILE Counter < 10    

           指令序列    

           IF continue 

              MEXIT ;退出宏

           ENDIF

       WEND  

5. 其他常用的伪指令

    1)AREA    

       格式:AREA 段名 属性 ,属性 ,……

       AREA 伪指令用于定义一个代码段或数据段。其中,段名若以数字开头,则该段名需用 " | " 括起来,如 |1_test| 

       

       属性字段表示该代码段(或数据段)的相关属性,多个属性用逗号?

       — CODE 属性:用于定义代码段,默认为 READONLY 。    

       — DATA 属性:用于定义数据段,默认为 READWRITE 。    

       — READONLY 属性:指定本段为只读,代码段默认为 READONLY 。    

       — READWRITE 属性:指定本段为可读可写,数据段的默认属性为 READWRITE 。    

       — ALIGN 属性:使用方式为 ALIGN 表达式。在默认时, ELF (可执行连接文件)的代码段和数据段是按字对齐的

       — COMMON 属性:定义一个通用的段,不包含任何的用户代码和数据。各源文件中同名的 COMMON 段共享同一段存储单元

       

       一个汇编语言程序至少要包含一个段,当程序太长时,也可以将程序分为多个代码段和数据段。    

       使用示例:    

       AREA Init , CODE , READONLY    

       该伪指令定义了一个代码段,段名为 Init ,属性为只读    

    2)、 ENTRY    

       格式:ENTRY    

             用于指定汇编程序的入口点。一个源文件里最多只能有一个 ENTRY (可以没有)

    3)、 END    

       格式:END    

             用于通知编译器已经到了源程序的结尾。

             

    4)、 CODE16CODE32    

         格式:CODE16 (或 CODE32 )    

    

         CODE16 伪指令通知编译器,其后的指令序列为 16 位的 Thumb 指令。    

         CODE32 伪指令通知编译器,其后的指令序列为 32 位的 ARM 指令。    

         

         在使用 ARM 指令和 Thumb 指令混合编程的代码里,可用这两条伪指令进行切换

         示例:    

         AREA Init , CODE , READONLY    

         CODE32                        ;通知编译器其后的指令为 32 位的 ARM 指令    

         LDR R0 ,= NEXT + 1  ;将跳转地址放入寄存器 R0    

         BX R0                 ;程序跳转到新的位置执行,并将处理器切换到 Thumb 工作状态    

         ……    

         CODE16                ;通知编译器其后的指令为 16 位的 Thumb 指令    

         NEXT LDR R3,=0x3FF    

         ……    

         END                   ;程序结束 

    

    5)、 EXPORT(GLOBAL)    

         格式:EXPORT 标号

               export 伪指令用于在程序中声明一个全局的标号,该标号可在其他的文件中引用。

    6)、 IMPORT    

         格式:IMPORT 标号     

               IMPORT 伪指令用于通知编译器要使用的标号在其他的源文件中定义,但要在当前源文件中引用

               如果当前源文件实际并未引用该标号,该标号也会被加入到当前源文件的符号表中。

               

    7)、 EXTERN    

         格式:EXTERN 标号     

               EXTERN 伪指令用于通知编译器要使用的标号在其他的源文件中定义,但要在当前源文件中引用

               如果当前源文件实际并未引用该标号,该标号就不会被加入到当前源文件的符号表中。

   

    8)、 GET(INCLUDE)    

         格式:GET 文件名    

         GET 伪指令用于将一个源文件包含到当前的源文件中,并将被包含的源文件在当前位置进行汇编处理。

         

         汇编程序中常用的方法是在某源文件中定义一些宏指令,用 EQU 定义常量的符号名称,用 MAP和 FIELD 定义结构化的数据类型,然后用 GET 伪指令将这个源文件包含到其他的源文件中。使用方法与 语言中的 "include" 相似。

         GET 伪指令只能用于包含源文件,包含目标文件需要使用 INCBIN 伪指令 

            

        示例:    

            AREA Init , CODE , READONLY    

            GET a1.s     ;通知编译器当前源文件包含源文件a1.s    

            GET C\a2.s ;通知编译器当前源文件包含源文件C\ a2.s ……    

            END    

    9)、 INCBIN    

         格式:INCBIN 文件名

            INCBIN 伪指令用于将一个目标文件或数据文件包含到当前的源文件中

        示例:    

            AREA Init , CODE , READONLY    

            INCBIN a1.dat ;通知编译器当前源文件包含文件a1.dat    

            INCBIN C\a2.txt ;通知编译器当前源文件包含文件C\a2.txt……    

            END   

                

    10)ROUT    

        格式: 名称 } ROUT    

              ROUT 伪指令用于给一个局部变量定义作用范围。

              在程序中未使用该伪指令时,局部变量的作用范围为所在的 AREA 

              而使用 ROUT 后,局部变量的作为范围为当前 ROUT 和下一个 ROUT 之间。

    11)、 ALIGN    

        格式:ALIGN { 表达式 ,偏移量 }}    

              ALIGN 伪指令可通过添加填充字节的方式,使当前位置满足一定的对其方式 

              表达式的值用于指定对齐方式,可能的取值为 的幂,如 、 、 、 、 16 等。

              偏移量也为一个数字表达式,若使用该字段,则当前位置的对齐方式为: 的表达式次幂+偏移量。    

        示例:

              AREA Init , CODE , READONLY , ALIEN = ;指定后面的指令为 字节对齐。 

    

.ARM汇编伪指令(读取内存地址)

  1)ADRADRL

    将PC相对偏移的地址或基于寄存器相对偏移的地址值读取到寄存器中

       格式:ADR(ADRL) 寄存器,地址表达式

             ADR 小范围的地址读取伪指令,

             ADRL 中等范围的地址读取伪指令

       例:

       查表

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

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

               ……

       D_TAB

          DCB   0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92

      

  2)LDR

    用于加载32位立即数或一个地址值到指定的寄存器,大范围的地址读取伪指令.

    LDR通常都是作加载指令,但是它也可以作伪指令。作用是装载一个32bit常数和一个地址到寄存器。

       

       格式:LDR  寄存器,=地址表达式

       COUNT EQU       0x56000054  ;COUNT是一个变量,地址为0x56000054

             LDR       R1,=COUNT   ;COUNT这个变量的值(地址),也就是0x56000054放到R1中。

             MOV       R0,#0

             STR       R0,[R1]     ;是一个典型的存储指令,将R0中的值放到以R1中的值为地址的存储单元去

       ;这三条指令是为了完成对变量COUNT赋值。

  3)NOP     

    空操作伪指令,可用于延时操作

       例:延时子程序

       Delay

              NOP       ;空操作

              NOP

              NOP

              SUBS   R1,R1,#1   ;循环次数减1

              BNE    Delay

              MOV    PC,LR

六、Thumb指令集

   有兴趣的自学

   

//************************************* arm体系结构 第五天***************************************

七、C语言与汇编混合编程

    完全使用汇编语言来编写程序会非常的繁琐

    通常,只是使用汇编程序来完成少量必须由汇编程序才能完成的工作,而其它工作则由C语言程序来完成。

()ATPCS规则  

    混合编程中,双方都须遵守ATPCS规则。这些基本规则包括:

    子程序调用过程中寄存器的使用规则、数据栈的使用规则和参数的传递规则。

1.寄存器使用规则

    寄存器:  R4-R11用来保存局部变量

        R0-R3 a1-a4)用于保存参数/返回结果/scratch(临时寄存器)

        R4-R11v1-v8)用于保存ARM状态局部变量

        R12IP) 子程序内部调用的scratch

        R13SP) 数据栈指针

        R14LR) 连接寄存器

        R15PC) 程序计数器

    

    R7 又可称为 wr 用于Thumb状态工作寄存器

    R9 又可称为 sb 在支持RWPI ATPCS中为静态基址寄存器

    R10又可称为 sl 在支持PWPI ATPCS中为数据栈限制指针

    R11又可称为 fp 用于帧指针

              

2.数据栈使用规则

    ATPCS标准规定,数据栈为FD(满递减类型),并且对数据栈的操作是8字节对齐。在进行出栈和入栈操作,则

必须使用ldmfdstrnfd指令(或ldmiastmdb)    

3.参数的传递规则

    参数:    参数小于等于4,用R0-R3保存参数,参数多于4,剩余的传入堆栈

    函数返回:结果为32位整数,通过R0返回

              结果为64位整数,通过R0R1返回

              对于位数更多的结果,通过内存传递

    例:参数传递及结果返回(r0-r3做参数,r0做返回值)

        AREA Example,CODE,READONLY ;声明代码段Example

        ENTRY                      ;程序入口

    Start 

        MOV  R3,#4                 ;设置实参,将参数写入R0-R3

        MOV  R2,#3

        MOV  R1,#2

        MOV  R0,#1    

        BL   func1                 ;调用子程序ADD_SUM

        B    OVER                  ;跳转到OVER标号处,进入结尾

    func1

        ADD  R0,R0,R1              ;实现两数相加

        ADD  R0,R0,R2

        ADD  R0,R0,R3    

        MOV  PC,LR                 ;子程序返回,R0内为返回的结果

    OVER

        END

        

    相当于如下C语言

    int func1(int a,int b,int c,int d){

        return a+b+c+d;

    }

    int main(){

        func1(1,2,3,4);

    }    

    例:多于4个参数,前4个通过寄存器R0-R3传递,其它参数通过数据栈传递

        AREA Example,CODE,READONLY ;声明代码段Example

        ENTRY                      ;程序入口

    Start 

        STMFD SP!,{R1-R4,LR}       ;先将R1-R4,及LR内原有数据压入栈,需要使用这五个寄存器

        MOV  R0,#1                 ;准备好7个寄存嚣存入7个数据 LR,IP,R4作临时寄存器使用

        MOV  IP,#2

        MOV  LR,#3

        MOV  R4,#4

        MOV  R1,#5

        MOV  R2,#6

        MOV  R3,#7

        STMFD SP!,{R1-R3}          ;先将R1-R3数据从前向后入栈,然后将IP,LR,R4内的数据装入R1-R3

        MOV  R3,R4                 ;其中IP,LR,R4 是临时使用的寄存器

        MOV  R2,LR

        MOV  R1,IP     

        BL   func1                 ;调用子程序funcl R0是返回结果

        LDMFD SP!,{R1-R4,PC}       ;从栈中取出最初的数据,恢复原始值

        B    OVER                  ;跳转到OVER标号处,进入结尾

    func1

        STMFD SP!,{R4,LR}          ;当调用函数时,LRR4都已发生了变化,其中LR是指令地址所以也压入栈

        LDR  R4,[SP,#0x10]         ;目前共压入5个数据,每一个数据占两个字节,当前栈顶偏移10为前5个数据7

        ADD  LR,SP,#8              ;将前第4个数据的地址(栈项+偏移)赋给LR

        LDMIA LR,{IP,LR}           ;连续将LR地址处的两个数据取出写入IPLR内,从右向左写,LDMIA即出栈指令

        ADD  R0,R0,R1              ;从此行开始相当于return a+b+c+d+e+f+g;   

        ADD  R0,R0,R2

        ADD  R0,R0,R3

        ADD  R0,R0,IP

        ADD  R0,R0,LR

        ADD  R0,R0,R4    

        LDMFD SP!,{R4,PC}          ;从栈内取数据加载入R4PC,PC跳转回主函数

    OVER

        END        

     

    下面是栈顶 

        

    相当于如下C语言

    int func1(int a,int b,int c,int d,int e,int f,int g){

        return a+b+c+d+e+f+g;

    }

    int main(){

        int a=1,b=2,c=3,d=4,e=5,f=6,g=7;

        func1(a,b,c,d,e,f,g);

    }        

()CARM汇编程序间的相互调用

1. 汇编程序调用C子程序

   为保证程序调用时参数正确传递,必须遵守ATPCS

   在C程序中函数不能定义为static函数。在汇编程序中需要在汇编语言中使用IMPORT伪操作来声明C子函数

   

   //C代码

   int sum5(int a, int b ,int c, int d)

   {

       return (a+b+c+d);

   }

      

   //汇编代码

         AREA Example,CODE,READONLY ;声明代码段Example

         IMPORT sum5                     ;

         ENTRY                      ;程序入口

   Start 

         MOV  R3,#4                 ;设置实参,将参数写入R0-R3

         MOV  R2,#3

         MOV  R1,#2

         MOV  R0,#1    

         BL   sum5                 ;调用子程序sum5

         B    OVER                  ;跳转到OVER标号处,进入结尾

   OVER

         END

        

2. 汇编程序访问全局C变量

   汇编程序中可以通过C全局变量的地址来间接访问C语言中定义的全局变量

   在编编程序中用IMPORT引入C全局变量,该C全局变量的名称在汇编程序中被认为是一个标号。通过ldrstr指令访问该编号所代表的地址

   //C代码    

   int i=3;

   int sum5(int a, int b ,int c, int d)

   {

       return (a+b+c+d+i);

   }   

   //汇编代码   

         AREA Example,CODE,READONLY ;声明代码段Example

         IMPORT sum5                     

         IMPORT i

         ENTRY                      ;程序入口

   Start 

         LDR  R1,i                  ;i读入R1

         MOV  R0,#2

         ADD  R0,R0,R1

         STR  R0,i                  ;将寄存器值写入i

         MOV  R3,#4                 ;设置实参,将参数写入R0-R3

         MOV  R2,#3

         MOV  R1,#2

         MOV  R0,#1    

         BL   sum5                 ;调用子程序ADD_SUM

         B    OVER                  ;跳转到OVER标号处,进入结尾

   OVER

         END

  

3. C语言中调用汇编子程序

   为保证程序调用时参数的正确传递,在汇编程序中需要使用EXPORT伪操作来声明汇编子程序,同时在C语言中使用extern扩展声明汇编子程序。

   

   //汇编代码

        EXPORT func1               ;func1为子函数名

        AREA Example,CODE,READONLY ;声明代码段Example

   func1

        ADD  R0,R0,R1              ;实现两数相加

        ADD  R0,R0,R2

        ADD  R0,R0,R3    

        MOV  PC,LR                 ;子程序返回,R0内为返回的结果

        END

   //C代码

   extern int func1(int a,int b,int c,int d);

   int main(int argc,char **argv){

       int a=1,b=2,c=3,d=4;

       int z=func1(a,b,c,d);

       printf("%d",z);   

       return 0;

   }

4. C语言中调用汇编全局变量

   汇编中用DCD为全局变量分配空间并赋值,并定义一个标号代表该存储位置。

   在汇编中用EXPORT导出标号(这个标号就是全局变量),在C程序中用extern扩展声明名该变量

   

   //汇编代码 

        EXPORT func1

        EXPORT tmp

        AREA Example,CODE,READONLY ;声明代码段Example

   tmp              ;全局变量名

        DCD 0x0005  ;全局变量创建内存空间及赋初值      

   func1            ;子函数名

        ADD  R0,R0,R1              ;实现两数相加

        ADD  R0,R0,R2

        ADD  R0,R0,R3    

        MOV  PC,LR                 ;子程序返回,R0内为返回的结果

        END

   //C代码     

   extern int func1(int a,int b,int c,int d);

   extern int tmp;

   int main(int argc,char **argv){

       int a=1,b=2,c=3,d=4;

       int z=func1(a,b,c,tmp);

       printf("%d",z);   

       return 0;

   }

5. C语言中内嵌汇编

   有些操作C语言程序无法实现,如改变CPSP寄存器值,初始化堆栈指针寄存器SP等,这些只能由汇编来完成。

   但出于编程简洁等一些因素,有时需要在C源代码中实现上述操作,此时就需要在C中嵌入少量汇编代码。

   内嵌的汇编不能直接引用C的变量定义,必须通过ATPCS进行,语法格式如下:

      __asm{ 

        //内嵌汇编

      }

      

   例:在C语言中嵌入汇编

   int f(){ //C函数

      __asm{ //内嵌汇编,禁用中断例子

         MRS R0,CPSR

         ORR R0,R0,#0x80

         MSR CPSR_c,R0

      }

      

   }

   int main(int argc,char **argv){

      int a;

      int z=f(a);

      printf("%d",z);

      return 0;

   }

   

   出地完整性考虑,内嵌汇编相对于一般汇编的不同特点如下:

   1)操作数可以是寄存器、常量或C表达式。可以是charshort、或int类型,而且是无符号数进行操作

   2)常量前的#号可以省略

   3)只有指令B可以使用C程序中的标号,指令BL不可以使用

   4)不支持汇编语言中用于内存分配的伪操作

   5)内嵌汇编不支持通过“.”指示符或PC获取当前指令地址

   6)不支持LDR Rn,=expression 伪指令,而使用MOV Rn,expression指令向寄存器赋值

   7)不支持标号表达式

   8)不支持ADRADRL伪指令

   9)不支持BXBLX指令

   10)不可以向PC赋值

   11)使用0x前缀替代 &表示十六进制数

   12)不使用寄存寻址变量

   13)ldmstm指令的寄存器列表只允许物理寄存器

   14)必须小心使用物理寄存器,如R0-R3,LRPC

//------------------------------------------

()、裸机硬件的控制方法

1.裸板开发环境搭建

    1)J-Link

      (1)安装Setup_JLinkARM_V408i.exe

      (2)连接开发板

         1 打开桌面快捷 J-Flash ARM V4.08i

         2 连接好开发板 开发板->jlink->pc(usb)

         3 将开发板置为NorFlash启动

         4 打开菜单 file->open project->选择TQ2440.jflash

         5 填加配置选项

           将Flash.csv复制到 安装目录的\ETC\JFlash

           打开菜单 options->project settings

           在弹出对话框内选 flash 后,点击按钮select flash device

           在弹出对话框内选择 EN29LV160AB

         6.连接开发板

           重启开发板,然后点击菜单 target->connect 查看联接信息

      (3)烧写方法

         将j-link连接好后,在菜单 file->open内选择要烧写的程序。

         如:烧写u-boot.bin

             然后在烧写地址对话框内输入烧写地址,u-boot的地址设为0

             再点击菜单 target->program进行烧写

      (4)调试方法

         连接JLINK和开发板。

         打开程序 J-LINK ARM V4.08i->J-link GDB Server

                  设置信息 JTAG speed 500KH

                           所有选项勾选

         设置AXD调试环境 options->configure target填加JlinkARM目录下的JLinkRDI.dll

                 然后在AXD内选则JLinkRDI.dll选项,同时点击右侧configure按钮

                 在弹出对话框内General标签:JTAG speed设为4000Khz

                               flash标签  去掉 Enable flash programming选项

                               Breakpoints: 去掉 Use software breakpoints选项

                               CPU标签    :勾选 Allowinstruction set simulation

                                                 Little endian

                                                 Reset strategy内选

                                                 Hardware,halt after reset(normal)1000ms

                 在AXD内,通过File->Load Image载入要调试的axf文件                                

                                                 

                 

                                    

()、软件控制硬件的编程原理

   每一种硬件,在其控制器芯片上都有物理寄存器(不是CPU寄存器,是硬件上的寄存器)

   这些寄存器分为三类:命令寄存器、状态寄存器、数据寄存器

   程序控制硬件的方法是,通过汇编str指令向命令寄存器写入指令即可完成对硬件的配置操作或执行物理操作。

                         通过汇编ldr指令从数据寄存器中获取数据,或从状态寄存器获取状态。

   

   程序控制硬件,其实质就是对硬件的寄存器进行读写操作。

   

   程序中需要解决的问题:

   1)硬件寄存器的内存地址是多少?

   2)向哪一个寄存器写入什么值?

   

   如想解决上述两个问题,需要熟练查看硬件的手册,阅读硬件连线原理图。

   

   数电的基本知识:

   1).电路符号

   2).基本原理

   

   以LED灯为例讲解如何使用硬件手册,和原理图

   在开发板上有4LED灯,如果想点亮,必须先看硬件连线图

   

1. 查看开发板LED管脚连线

   TQ2400开发板有两块电路板:核心板、主板

   LED灯在主板上,所以查看《TQ2440底板原理图.pdf》,找到LED模块(LED测试)

   从电路图可看到如果让LED1灯点亮,必须在nLED_1连线上输出低电屏(即通电)

   

   如何让LED1上输出低电屏,需查看CPU相关引脚连线

2. 查看TQ2440核心板原理图

   CPU在核心板上,所以查看《TQ2440_V2核心板原理图.pdf

   找到nLED1连线,如下

       nLED1 CUPGPB5引脚上

       nLED2        GPB6

       nLED3        GPB7

       nLED4        GPB8

   

   如何让CPUGPB5为低电屏,需查看CPU引脚模式

3.查看CPU用户手册

  TQ2440的微处理器芯片为S3C2440,所以查看《S3C2440.pdf》芯片手册,找到 GPB5的管脚模式

  1)控制寄存器

      GPB5 [11:10] 00 = Input   01 = Output   

                   10 = nXBACK 11 = Reserved

      

      GPBCON 控制寄存器

      GPBDAT 数据寄存器

      GPBUP  上拉使能寄存器

      

      [11:10] 是所在位,每个管脚都是占两个位

         

      其中 Input  是输入值

           Output   输出值

           reserved 保留值

           nXBACK  nXBACK/GPB5   

           

      即,如果将GPB5设为低电屏,则需要将控制寄存器设置为输出模式。

          即需要CPU寄存器GPBCON的 1110位设置为 01  11位为0  10位为1

  2)数据寄存器

      GPB[10:0] [10:0] 当端口配置为输入端口时,相应位为引脚状态。

                       当端口配置为输出端口时,相应位为引脚状态。

                       当端口配置为功能引脚时,将读取到未定义值  

      所以,当GPBCON为输入状态时,GPBDAT的相应位5则应为0才会输出低电屏

  3)查看寄存器地址

      GPBCON 0x56000010 R/W  端口配置寄存器        复位值 0x0

      GPBDAT 0x56000014 R/W  端口数据寄存器        复位值 -

      所以,需要将地址位0x56000010的寄存器作为GPBCON寄存器

                  地址位0x56000014的寄存器作为GPBDAT寄存器

4.代码编写

   //ledtest.c

     #define GPBCON (*(volatile unsigned long *)0x56000010)

     #define GPBDAT (*(volatile unsigned long *)0x56000014)

     //volatile 影响编译器编译的结果,表示变量是随时可能发生变化的

     //0x56000010是地址,强行转换为unsigned long *类型,

     //然后前面加*代表这个地址所指向的寄存器变量

     #define LEDS (1<<5 | 1<< 6 | 1<< 7| 1<<8)  //即 11110000

     #define DELAYVAL (0xFFFF)

     

     extern int delay(int time); //声明汇编函数

     int main(){

         int val;

         int i=0;

      GPBCON = (1<< 16 | 1<< 14 | 1<< 12 | 1<< 10); //010101010000000000 B5 B6 B7 B8的状态寄存器

      val =val +1;

      for(i=0;i<4;i++){

          GPBDAT=(GPBDAT & (~LEDS)) | (1 <<6 | 1 << 7 | 1 << 8);

          delay(DELAYVAL);

          GPBDAT=(GPBDAT & (~LEDS)) | (1 <<5 | 1 << 7 | 1 << 8);

          delay(DELAYVAL);

          GPBDAT=(GPBDAT & (~LEDS)) | (1 <<5 | 1 << 6 | 1 << 8);

          delay(DELAYVAL);

          GPBDAT=(GPBDAT & (~LEDS)) | (1 <<5 | 1 << 6 | 1 << 7);

          delay(DELAYVAL);

      }

      return 0;

     }

   //delay.s

     ;汇编指令延时程序                                 

      EXPORT delay                                     

      AREA DELAY,CODE,READONLY                                      

     ;下面是延时子程序                                       

     delay                                                                   

      sub R0,R0,#1    ;r0=r0-1                             

      cmp R0,#0x0     ;r00比较                                       

      bne delay       ;比较的结果不为0,则继续调用delay              

      mov PC,LR       ;返回                            

                                                       

      END                                              

   

5.调试代码

     

   2440ART.mcp内双击

            TargetSettins:   post-linker选择ArM fromELF

            Language Settins:Architecture or Processor选择相应的编译器ARM920T

            Arm Linker:output RO 0x30000000

                       optionsImage entry point 设为0x30000000 

                       layout Object 2440init.o Section Init   

                       Listings Image map

            Arm fromELF: output format内 Plain binary

                         output filename***.bin             

        编译

            make

        调试  AXD是调试器

            设置,debug ->打开AXD调试界面,选择option->config target 选项

                 选ARMUL(模拟调试器),然后选择确定.进入调试界面.

                 ARMUL是虚拟调试环境(虚拟开发板)

                 如果用开发板真实环境调试,则需要使用JTAG连开发板后,在此处选H-JTAG

            用file-<lode image项,加载刚才编译所的的 .axf调试文件.

                 execute->run to cousor 项.使程序进入用户主程序

                 

            可以用F8来一条一条执行语句,也可用F10,可以设置断点.  

 

arm汇编编程(示例)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值