ARM 汇编指令教程

原文地址:ARM 汇编指令 作者:bluedrum
Andrew Huang bluedrum@163.com



一、ARM汇编程序特点

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


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

CMP R0,R1
BNE NoMatch

比如上一句,BNE隐含的使用的上一句CMP执行结果.NE后缀表示使用Z标志位.两句合起来的意思就是,如果R0,R1的值不相等,就跳转到NoMatch处执行.
注意:PC=R15,CPSR=R16,
在这里插入图片描述

3、ARM伪指令不是必须的,但是一个完整没有伪指令几乎很难写出来。
比如一个程序至少包含READONLY,AREA和ENTRY。否则CPU都无法知道从哪里开始运行


4、ARM的属于RISC,指令并不多,但是可以带后缀表示扩展出不同用法,这里与X86汇编完全不同风格。
如BNE实际上是B指令的变种,本质还同一类指令。只是多一个对CPSR的Z标志位的判断。



二、ARM常用指令,伪指令

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

B,BL
MOV,MVN
LDR,STR
ADD,SUB,ADC,SBC,MUL
AND,ORR,XOR,TST,BIC
CMP
LDM/STM
nop

1、跳转语句 B,BL

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

  1. 使用专门的跳转指令 B

  2. 直接向程序计数器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可以采用多种寻址方式,以下只举出使用频率最高几种用法:

1、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) ; 用位与的方法赋值

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

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

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

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

4.1、ADD/ADC加法指令

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

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

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

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

以下是做64Bit的加法

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

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

4.2、SUB/SBC减法指令

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

4.3、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

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,其余的位保持

Ps:%表示是二进制,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表示


三、ARM指令的变形

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

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

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

BEQ 实际表示当EQ条件满足时才执行B
ADDNE 实际表示当NE条件满足时才执行ADD
在这里插入图片描述


四、ARM指令的寻址方式

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

1、寄存器寻址

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

MOV R1,R2 ;R2->R

2、立即数寻址

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

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

注意与SUBS R0,R0,1区别


3、寄存器偏移寻址

这是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带扩展的循环右移
在这里插入图片描述


4、寄存器间接寻址

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

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

5、 基址寻址

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

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

6、相对寻址

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

BL NEXT

NEXT

MOV PC,LR ;从子程序返回


五、ADS ARM的伪指令

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

1、符号定义指令

全局变量定义 GBLA ,GBLL,GBLS

局域变量定义 LCLA,LCLL,LCLS

变量赋值 SETA,SETL,SETS

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

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

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

LCLA bitno;  声明一个算术变量

Bitno SETA 8 ;设变量值为8

2、数据定义伪指令

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};

DCU定义的一段字的内存空间(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;

3、杂项的伪指令

字节对齐 ALIGN
ALIGN; 声明4字节对齐


定义一个数字常量定义 EQU

NAME EQU expr {type}

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

包含文件 GETINCLUDE

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**

指令集定义 CODE16CODE32

指示是Thumb 指令集(压缩指令集,每个指令16位)。还是普通32位指令集


汇编结束:END

程序入口ENTRY



六、一个基本ARM程序结构

1、ARM汇编程序结构

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

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

  • 汇编源程序,后缀名是.S
  • 汇编包含文件,后缀名是.inc
  • 如果是与C混和编程…C,.h也能识别

2、ARM 汇编语句格式

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

重点:

  1. 所有标号顶格写,而指令和伪指令不能顶格写
  2. 标识符(标号,指令)大小写敏感,所以要在标号和指令时书写一致,一般伪3指令,指令,寄存器名可以全部为大写
  3. 注释以;开头,可以顶格写
  4. 可以使用\来分行写太长语句
  5. 变量,常量的定义必须在一行顶格写

3、常量的书写

数字常量

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

字符常量

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

HELLO SETS “hello,the world!”

逻辑常量

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

Testno SETS {TURE}

4、汇编程序的段定义

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

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

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

  3. 每一个段用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

6、小结

ADS ARM汇编程序格式要求:

  1. 所有标号要顶格写.
  2. 所有指令不能顶格写,一般插入Tab键在行首
  3. ADS ARM中,是大小写敏感的.建议标号,指令,伪指令,寄存器名全部为大写
  4. 注释采用;打头
  5. 每个程序至少有一个AREA在代码里(READONLY)
  6. 每个段都要用END结束(不能顶格)

最常见几个伪指令:
AREA,EQU,DCB,END ,ENTRY,EXPORT,GOBEL,IMPORT


常见伪定义:

DCB 定义字符
如Str DCB “hello, world “

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ARM汇编指令转换为机器码是将人类可读的汇编指令翻译成机器可执行的二进制码的过程。由于机器码是计算机硬件能够直接理解的指令形式,所以在执行指令时,计算机需要将汇编指令转换为机器码。下面是一种简单的方法将ARM汇编指令转换为机器码。 首先,需要根据ARM指令的格式和规范理解每个指令的含义和操作码。ARM指令由32位组成,其中包含操作码、操作数和其他参数。 其次,根据具体指令的格式,将每个部分转换为对应的二进制表示形式。例如,操作码通常表示为特定的二进制位模式。 然后,根据指令的不同寻找对应的编码方式。不同的指令有不同的编码方式,例如立即数、寄存器和内存地址等等。根据具体的指令要求和参数,转换相应的部分。 最后,将所有部分组合成一个32位的二进制序列,这就是转换后的机器码。转换后的机器码可以被计算机硬件直接识别和执行。 需要注意的是,ARM汇编指令转换为机器码的过程需要具备一定的知识和经验。对于复杂的指令或有特殊要求的指令,可能需要查阅相关的ARM体系结构手册或其他资料来获得准确的转换方式。此外,在转换过程中还应该注意指令的字节顺序(即大端序或小端序)以及特殊的对齐要求。 总之,将ARM汇编指令转换为机器码是一项需要仔细思考和操作的任务。掌握适当的知识和技巧可以帮助我们完成这个过程,以便更好地理解和使用ARM指令集。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值