ARM汇编详细

ARM指令集

ARM状态有四种:指令集状态,执行状态,安全状态,调试状态

ARM指令集常用

image-20220324102318751.png

数据转送指令

MOV指令:它的转送指令只能是把一个寄存器的值(要能用立即数表示)赋给了一个寄存器,或者将一个常量赋给寄存器,将后边的量赋给前边的量

MOV指令格式:

  1. MOV{指令}{S} 目的寄存器,源操作数]() 把源操作数给到目的寄存器

MOV指令中,条件缺省时指令无条件执行;S选项决定指令的操作是否影响CPSR(程序状态寄存器)中条件标志位的值,当没有S时指令不更新CPSR中条件标志位的值

  1. 指令示例:
  2. MOV R1 , R0  ;将寄存器R0的值转送到寄存器R
  3. MOV PC , R14 ;将寄存器的R14(保存了返回地址)的值转送到PC(全局寄存器),常用于子程序返回
  4. MOV R1, R0 ,LSL #3 ;将寄存器R0的值左移3位后转送到R1(即乘8)
  5. MOVS PC , R14 ;将寄存器R14的值转送到PC中,返回调用代码并恢复标志位

算数运算指令

加法指令:ADD

ADD指令格式为:

  1. ADD{条件}{S}目的寄存器, 操作数1, 操作数2

ADD指令用于把两个操作数相加,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器或一个立即数。

  1. 指令示例:
  2. ADD R0, R1, R2 ;R0 = R1+R2
  3. ADD R0, R1,#256 ;R0 = R1+256
  4. ADD R0,R2,R3,LSL#1 ;R0=R2+(R3 >> 1)

带进位的加法指令:ADC

ADC指令格式为:

  1. ADC{条件}{S} 目的寄存器, 操作数1, 操作数2

ADC指令用于把两个操作数相加,再加上CPSR中的C条件标志位的值,并将结果存放到目的寄存器中,他使用一个进位标志位,这样就可以做比32位大的数的加法,注意不要忘记设置S后缀更改进位标志。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器或一个立即数

带借位减法指令:SBC

SBC指令格式为:

  1. SUB {条件}{S} 目的寄存器, 操作数1, 操作数2

BC指令用于把操作1减去操作数2,再减去CPSR中的C条件标记位反码,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令使用进位标志来表示借位,这样就可以做大于32位的剑法,注意不要忘记设置S后缀来更改进位标志。该指令可用于有符号数或无符号数的减法运算

比较指令

直接比较指令:CMP

CMP指令的格式为:

  1. CMP {条件} 操作数1, 操作数2

CMP指令默认会操作CPSR

CMP指令用于把一个寄存器的内容和另一个寄存器的内容或立即数进行比较,同时更新CPSR中条件标志位的值。该指令进行一次减法运算,但不存储结果,只更改条件标志位。标志位表示的是操作数1于操作数2的关系(大,小,相等),例如,当操作数1大于操作数2,则此后的有GT后缀指令将可以执行

  1. 指令示例:
  2. CMP R1, R0 ;将寄存器R1的值与寄存器R0的值相减,并根据结果设置CPSR的标志位
  3. CMP R1, #100 ;将寄存器R1的值与立即数100相减,并根据结果设置CPSR的标志位

位测试指令:TST

TST指令的格式为:

  1. TST{条件}操作数1, 操作数2

TST指令用于把一个寄存器的内容和另一个寄存器的内容还或立即数进行按位的与运算,并根据运算结果更新CPSR中条件标志位的值。操作数1是要测试的数据,而操作数2是一个位掩码,该指令一般用来检测是否设置了特定的位。

  1. 指令示例:
  2. TST R1, # % 1 ;用于测试在寄存器R1中是否设置了最低位(%表示二进制数)
  3. TST R1, # 0xffe ;将寄存器R1的值与立即数0xffe按位与,并根据结果设置CPSR的标志位

位测试指令:TEQ

TEQ指令的格式为:

  1. TEQ {条件} 操作数1, 操作数2

TEQ指令用于把一个寄存器的内容和另一个寄存器的内容或立即数进行按位的异或运算,并根据运算结果更新CPSR中条件标志位的值。该指令通常用于比较操作数1和操作数2是否相等。

  1. 指令示例:
  2. TEQ R1,R2 ;将寄存器R1的值与寄存器R2的值按位异或,并根据结果设置CPSR的标志位

逻辑运算指令

逻辑与指令:AND

AND指令的格式为:

  1. AND {条件} {S} 目的寄存器, 操作数1, 操作数2

AND指令用于在两个操作数上进行逻辑与运算,并把结果放置到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令常用于屏蔽操作数1的某些位

  1. 指令示例:
  2. AND R0, R0, #3 ;该指令保持R0的0,1位,其余位清零。

逻辑与指令:EOR

EOR指令的格式为:

  1. EOR {条件} {S} 目的寄存器, 操作数1, 操作数2

EOR指令用于在两个操作数上进行逻辑异或运算,并把结果放置到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令常用于反转操作数1的某些位。

  1. 指令示例:
  2. EOR R0,R0,#3 ; 该指令反转R0的0,1位,其余位保持不变

逻辑与指令:BIC

BIC指令的格式为:

  1. BIC {条件} {S} 目的寄存器, 操作数1, 操作数2

BIC指令用于清除操作数1的某些位,并把结果放置到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。操作数2为32位的掩码,如果在掩码中设置了某一位,则凊除这一位。未设置的掩码位保持不变。

  1. 指令示例:
  2. BIC R0,R0,#%1011 ;将指令清除R0中的位 0、1、和3,其他位保持不变

跳转指令

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

image-20220324121058169.png

ARM指令集中的跳转指令可以完成从当前指令先前或向后的32MB的地址空间的跳转,包括以下4条指令

B跳转指令

BLX带返回和状态切换的跳转指令

BL带返回的跳转指令

BX带状态切换的跳转指令

B指令

B指令的格式为:

  1. B{条件} 目标地址
  1. B指令是最简单的跳转指令。一旦遇到一个B指令,ARM处理器将立即跳转到给定的目标地址,从那里继续执行。注意存储在跳转指令中的实际值是相对当前PC值的个偏移量,而不是一个绝对地址,它的值由汇编器来计算(参考寻址方式中的相对寻址)。它是24位有符号数,左移两位后有符号扩展为32位,表示的有效偏移为26位(前后32MB的地址空间)。
  1. 指令示例:
  2. B Label ;程序无条件跳转到标号Label处执行
  3. CMP R1, #0 ;当CPSR寄存器中的Z条件码置位时,程序跳转到标号Label处执行
  4. BEQ Label

BL指令

BL指令的格式为:

  1. BL {条件} 目标地址

BL是另一个跳转指令,但跳转之前,会在寄存器R14中保存PC的当前内容,因此,可以通过将R14的内容重新加载到PC中,来返回到跳转指令之后的那个指令处执行。该指令是实现子程序调用的一个基本但常用的手段。

  1. 指令示例:
  2. BL Label ;当程序无条件跳转到标号Label处执行时,同时将当前的PC值保存到R14中

BLX指令

BLX指令的格式为:

  1. BLX 目标地址
  1. BLX指令从ARM指令集跳转到指令中所指定的目标地址,并将处理器的工作状态有ARM状态切换到Thumb状态,该指令同时将PC的当前内容保存到寄存器R14中。因此,当子程序使用Thumb指令集,而调用者使用ARM指令集时,可以通过BLX指令实现子程序的调用和处理器工作状态的切换。同时,子程序的返回可以通过将寄存器R14值复制到PC中来完成

BX指令

BX指令的格式为:

  1. BX {条件} 目标地址
  1. BX指令跳转到指令中所指定的目标地址,目标地址处的指令既可以是ARM指令,也可以是 Thumb指令。

状态寄存器访问指令

MRS指令的格式为:MRS {条件} 通用寄存器,程序状态寄存器(CPSR或SPSR)

一、当需要改变程序状态寄存器的内容时,可用MRS将程序状态寄存器的内容读入通用寄存器,修改后再写回程序状态寄存

二、当在异常处理或进程切换时,需要保存程序状态寄存器的值,可先用该指令读出程序状态寄存器的值,然后保存。

MSR指令

MSR指令格式为:

  1. MSR {条件} 程序状态寄存器(CPSR或SPSR)_<域>,操作数
  1. MR指令用于将操作数的内容传送到程序状态寄存器的特定域中。其中,操作数可以为通用寄存器或立即数。<域>用于设置程序状态寄存器中需要操作的位,32位的程序状态寄存器可分为4个域:

位[31:24]为条件标志域,用f表示;

位[23:16]为状态位域,用s表示;

位[15:8]为拓展位域,用x表示;

位[7:0]为控制位域,用c表示;

加载/存储指令

  1. ARM微处理器支持加载/存储指令用于在寄存器和存储器之间传送数据,加载指令用于将存储器中的数据传送到寄存器,存储指令则完成相反的操作。

LDR指令

LDR指令的格式为:

  1. LDR {条件} 目的寄存器,<存储器地址>
  1. LDR指令用于从存储器中将一个32位的字数据传送到目的寄存器中。难该指令通常用于从存储器中读取32位的字数据到通用寄存器,然后对数据进行处理。当程序计数器PC作为目的寄存器时,指令从存储器中读取的字数据被当作目的地址,从而可以实现程序流程的跳转。
  1. 指令示例:
  2. LDR R0,[R1] ; 将存储器地址为R1的字数据读入寄存器R0
  3. LDR R0,[R1,R2] ;将存储器地址为R1+R2的子数据读入寄存器R0
  4. LDR R0,[R1, #8] ;将储存器地址为R1+8的子数据读入寄存器R0
  5. LDR  R0, [R1, R2] ! ;将存储器地址为R1+R2的字数据读入寄存器R0,并将新地址R1+R2写入R1
  6. LDR  R0, [R1,  #8] ! ;将存储器地址为R1+8的字数据读入寄存器R0,并将新地址R1+8写入R1
  7. LDR  R0, [R1], R2将存储器地址为R1的字数据读入寄存器R8,并将新地址R1+R2写入R1
  8. LDR R0,[R1,R2,LSL#2] ! ;将存储器地址为R1+R2×4的字数据读入寄存器R,并将新地址R1+R2×4写入R1
  9. LDR  R0,[R1],R2,LSL#2 ;将存储器地址为R1的字数据读入寄存器R0,并将新地址R1+R2×4写入R1。

LDRB指令

LDRB指令的格式为:

LDRB {条件} 目的寄存器,<存储器地址>

LDRB指令用于从存储器中将一个8位的字节数据传送到目的寄存器中,同时将寄存器的高24位清零。该指令通常用于从存储器中读取8位的字节数据到通用寄存器,然后对数据进行处理。当程序计数器PC作为目的寄存器时,指令从存储器中读取的字数据被当作目的地址,从而可以实现程序流程的跳转。

指令示例:

LDRB R0,[R1]    ;将存储器地址为R1的字节数据读入寄存器R0的高24位清零。

LDRB R0,[R1, #8]    ;将储存器地址为R1+8的字节数据读入寄存器R0,并将R0的高24位清零。

LDRH指令

LDRH指令的格式为:

LDRH {条件} 目的寄存器,<寄存器地址>
LDRH指令用于从存储器中将一个16位的半字数据传送到目的寄存器中,同时将寄存器的高16位清零。该指令通常用于从存储器中读取16位的半字数据到通用寄存器,然后对数据进行处理。当程序计数器PC作为目的寄存器时,指令从存储器中读取的字数据被当作目的地址,从而可以实现程序流程的跳转。

指令示例:

LDRH R0,[R1]    ;将存储器地址为R1的版字节数据读入寄存器R0,并将R0的高16位清零

LDRH R0, [R1, #8]  ;将存储器地址为R1+8的半字数据读入寄存器R8,并将Rθ的高16位清零。

LDRH R0,[R1, R2] ;将存储器地址为R1+R2的半字数据渎入杏存器R8,并将R0的高16位清零

STR指令

STR指令的格式为:

STR {条件} 源寄存器,<存储器地址>
STR指令用于丛源寄存器中将一个32位的字数据传送到存储器中。该指令在程序设计中比较常用,且寻址方式灵活多样,使用方式可参考指令LDR

指令示例:

[R1] 取的是内存对应的地址 相当于指针

STR R0,[R1], #8 ;将R8中的字数据写入以R1为地址的存储器中,并将新地址R1+8写入R1
STR R0,[R1,#8] ;将R8中的字数据写入以R1+8为地址的存储器中

STRB和STRH指令 原理都是一样的

批量数据加载/存储指令

ARM微处理器所支持批量数据加载/存储指令可以一次在一片连续的存储器单元和多个寄存器之间传送数据,批量加载指令用于将一片连续的存储器中的数据传送到多个寄存器,批量数据存储指令则完成相反的操作。常用的加载存储指令如下:
LDM  批量数据加载指令

STM 批量数据存储指令

LDM或STM指令

LDM(或STM)的指令格式为:

LDM(或STM) {条件}{类型} 基址寄存器{!},寄存器列表{∧}

{!}为可选后缀,若选用该后缀,则当数据传送完毕之后,将最后的地址写入基址寄存器,否则基址寄存器的内容不改变。
基址寄存器不允许为R15,寄存器列表可以为R0~R15的任意组合。

{∧}为可选后缀,当指令为LDM且寄存器列表中包含R15,选用该后缀时表示:除了正常的数据传送之外,还将SPSR复制到CPSR。同时,该后缀还表示传入或传出的是用户模式下的寄存器,而不是当前模式下的寄存器。

指令示例:

STMFD R13! ,{R0,R4-R12, LR} ;将寄存器列表中的寄存器(R0,R4到R12,LR)存入堆栈

LDMFD R13! ,{R0,R4-R12, PC} ;将堆栈内容恢复到寄存器(R0, R4到R12, LR)

异常产生指令

SWI 指令

SWI指令格式为:

SWI {条件} 24位的立即数
SWI指令用于产生软件中断,以便用户程序能调用操作系统的系统例程。操作系统在SWI的异常处理程序中提供相应的系统服务,指令中24位的立即数指定用户程序调用系统例程的类型,相关参数通过通用寄存器传递,当指令中24位的立即数被忽略时,用户程序调用系统例程的类型由通用寄存器R0的内容决定,同时,参数通过其他通用寄存器传递。
指令示例:

SWI  0x02 ;将指令调用操作系统编号位02的系统列程

伪代码

AREA

一个汇编程序至少要包含一个段,当程序太长时,也可以将程序分为多个代码段和数据段,因此在汇编程序的开头,一般的语句会用到AREA。

语法格式:AREA 段名 属性1, 属性2,....
AREA伪指令用于定义一个代码段或数据段。其中,段名若以数字开头,则该段名需用“丨”括起来,如 l1 testl。属性字段表示该代码段(或数据段)的相关属性,多个属性用逗号分隔。常用的属性如下
CODE 属性:用于定义代码段,默认为READONLY

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

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

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

ALIGN

ALIGN语法格式

ALIGN {表达式{ , 偏移量}}
ALIGN伪指令可通过添加填充字节的方式,使当前位置满足一定的对其方式其中,表达式的值用于指定对齐方式,可能的取值为2的幂,如1、2、4、8、16等。若未指定表达式,则将当前位置对齐到下一个字的位置。偏移量也为个数字表达式,若使用该字段,则当前位置的对齐方式为:2的表达式次幂+偏移量。
使用示例:

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

'''

;指令序列

'''

END

CODE16、CODE32

语法格式:CODE16(或CODE32)
CODE16 伪指令通知编译器,其后的指令序列为16位的Thumb指令

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

若在汇编源程序中同时包含ARM指令和 Thumb指令时,可用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 ; 程序结束

ENTRY

语法格式:ENTRY
ENTRY伪指令用于指定汇编程序的入口点。在一个完整的汇编程序中至少要有个 ENTRY(也可以有多个,当有多个 ENTRY时,程序的真正入口点由链接器指定),但在一个源文件里最多只能有一个 ENTRY

使用示例:

AREA Init, CODE, READONLY

ENTRY ;指定应用程序的入口点

‘’‘’

END

语法格式:END

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

使用示例:

AREA Init, CODE, READONLY

''''

END ;指定应用程序的结尾

ARM环境搭建

image-20220324162426766.png
可以到https://www.keil.com/download/product/ 下载,C51用于单片机…
image-20220324162653212.png

ARM实战

如何创建项目可参考:https://blog.csdn.net/qq_45237293/article/details/111825193

需要注意的是,目前是本地的项目,还未涉及真正的项目,此案例按如下进行设置

image-20220325105117241.png

目前用的两个:CMSIS和DEVICE,选择后点击OK即可
image-20220325105257224.png

    AREA Lab, CODE, READONLY    ;一个只读的代码段
    ENTRY    ;入口
    EXPORT main    ;导出main,编译完可执行后别的程序也可以调用main函数
main    ;标号
    MOVS R0, #0x1    ;0x1给了R0
    MOVS R1, #0x2    ;0x2给了R1

    BL, ADD_SUM    ;跳转到了ADD_SUM的位子
    MOVS R3, R0    ;把R0给到R3

    MOVS R0, #0    ;把R0清零

    B STOP    ;跳到STOP

ADD_SUM
    ADD R0, R0, R1    ;将R1和R0相加给到R0里面

    BX LR    ;LR是返回地址,BL进来后会把地址放到LR寄存器里面,在跳转回去

STOP    ;标号
    END    ;程序结束符

理解完后需要编译他,这里选择Rebuild all target files

image-20220325105508744.png

完后后需要了解debug
image-20220325141438308.png
配置完后点击如下即可进行调试
image-20220325141509572.png