前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >arm(2)| 汇编指令和伪指令

arm(2)| 汇编指令和伪指令

作者头像
飞哥
发布2020-07-10 10:26:48
2.5K0
发布2020-07-10 10:26:48
举报

今天我们来说一下arm的汇编指令和伪指令。

一、指令和伪指令

我们首先来了解一下什么叫做指令和伪指令。

指令是CPU机器指令的助记符,经过编译后会得到一串10组成的机器码,可以由CPU读取执行。伪指令本质上不是指令(只是和指令一起写在代码中),它是编译器环境提供的,目的是用来指导编译过程,经过编译后伪指令最终不会生成机器码。所以指令和伪指令最大区别就是编译完之后会不会生成机器码。

arm汇编有两种风格,ARM官方的ARM汇编风格:指令一般用大写、Windows中IDE开发环境(如ADS、MDK等)常用。如:LDR R0, [R1]。GNU风格的ARM汇编:指令一般用小写字母、linux中常用。如:ldr r0, [r1]。

二、arm汇编特点

ARM汇编特点1:LDR/STR架构。ARM采用RISC架构,CPU本身不能直接读取内存,而需要先将内存中内容加载入CPU中通用寄存器中才能被CPU处理。ldr(load register)指令将内存内容加载入通用寄存器。str(store register)指令将寄存器内容存入内存空间中。ldr/str组合用来实现 ARM CPU和内存数据交换。

ARM汇编特点2:8种寻址方式

寄存器寻址 mov r1, r2

立即寻址 mov r0, #0xFF00

寄存器移位寻址 mov r0, r1, lsl #3

寄存器间接寻址 ldr r1, [r2]

基址变址寻址 ldr r1, [r2, #4]

多寄存器寻址 ldmia r1!, {r2-r7,r12}

堆栈寻址 stmfd sp!, {r2-r7,lr}

相对寻址 beqflag

ARM汇编特点3:指令后缀。同一指令经常附带不同后缀,变成不同的指令。经常使用的后缀有:

B(byte)功能不变,操作长度变为8位

H(half word)功能不变,长度变为16位

S(signed)功能不变,操作数变为有符号,如 ldr ldrb ldrh ldrsb ldrsh

S(S标志)功能不变,影响CPSR标志位,如 mov和movs movs r0, #0

ARM汇编特点5:多级指令流水线。为增加处理器指令流的速度,ARM使用多级流水线。所以要注意PC指向正被取指的指令,而非正在执行的指令。

三、常用指令

接下来介绍几个常用的指令,对于不常用的指令就不多说了,请自行搜索。

1、mov和mvn

mov r1, r0 把r0寄存器的值加载到r1当中。

mov r1, #0xff 将立即数的值赋给r1。

mov指令是最简单也是最重要的指令了,mvn用法和mov一样,区别在于mov是原封不动的传递,而mvn是按位取反后传递。

2、几个逻辑运算指令

and 逻辑与

orr 逻辑或

eor 逻辑异或

bic 位清除指令

例:bic r0,r1,#0x1f @将r1中的数的bit0到bit4清零后赋值给r0。

3、比较指令cmp

cmp r0, r1 比较r0和r1寄存器值是否相等,若相等,会改变cpsr寄存器对应的零标志位。

4、cpsr访问指令

mrs用来读cpsr或spsr,msr用来写cpsr或spsr

5、跳转指令b、bl、bx

b 直接跳转(就没打开算返回)。

bl branch and link,跳转前把返回地址放入lr中,以便返回,以便用于函数调用。

bx跳转同时切换到ARM模式,一般用于异常处理的跳转。

6、访存指令

ldr/str每周期只能访问4字节内存,如果需要批量读取、写入内存时太慢,解决方案是stm/ldm

关于这一部分内容可以参考https://blog.csdn.net/u013477200/article/details/50723555

这里重点提一下LDM指令和STM指令以及一些后缀。

LDM指令:

LDM指令是LDR指令的“升级”,其实就是加载多个字节到寄存器当中。虽然貌似是LDR的升级,但是,千万要注意,这个指令运行的方向和LDR是不一样的,是从左到右运行的。该指令是将内存中堆栈内的数据,批量的赋值给寄存器,即是出栈操作;其中堆栈指针一般对应于SP,注意SP是寄存器R13,实际用到的却是R13中的内存地址,只是该指令没有写为[R13]。

LDMFD SP! , {R0, R1, R2}

实际上可以理解为: LDMFD [SP]!, {R0, R1, R2}

意思为:把sp指向的3个连续地址段(应该是3*4=12字节(因为为r0,r1,r2都是32位))中的数据拷贝到r0,r1,r2这3个寄存器中去。

STM指令:

S的含义仍然是STORE,与LDM是配对使用的,其指令格式上也相似,即区别于STR,是将堆栈指针写在左边,而把寄存器组写在右边。

STMFD SP!, {R0}

同样的,该指令也可理解为: STMFD [SP]!, {R0}

意思是:把R0保存到堆栈(sp指向的地址)中。

这个可能不是很难理解,同时也注意到在指令后面有一个后缀FD,FD的意思是满栈递减,其实还有另外几个类似的后缀。这里我们需要想了解一下几种栈。

空栈:栈指针指向空位,每次存入时可以直接存入然后栈指针移动一格;而取出时需要先移动一格才能取出。

满栈:栈指针指向栈中最后一格数据,每次存入时需要先移动栈指针一格再存入;取出时可以直接取出,然后再移动栈指针。

增栈:栈指针移动时向地址增加的方向移动的栈。

减栈:栈指针移动时向地址减小的方向移动的栈。

这些不同的栈对应着要使用不同的指令后缀。

ia(increase after)先传输,再地址+4

ib(increase before)先地址+4,再传输

da(decrease after)先传输,再地址-4

db(decrease before)先地址-4,再传输

fd(full decrease)满递减堆栈

ed(empty decrease)空递减堆栈

fa(·······) 满递增堆栈

ea(·······)空递增堆栈

操作栈时使用相同的后缀就不会出错,不管是满栈还是空栈、增栈还是减栈。比如上面我们提到的STMFD和LDMFD,当使用STMFD时,SP指针会先向地址减小方向移动一格(四个字节),再把寄存器的值放进SP所指向的地址当中,因为SP指向的地址里面原本就是满的,当然要移动完之后才能放进去。而使用LDMFD时,就会直接从SP指向的地址把数据加载进寄存器当中,而不需要先移动一格,因为它里面本来就有内容,当然不需要移动了。其他的也是类似的操作,入栈和出栈采用相同的后缀就不会出错。最常见的是stmia和stmfd。

!的作用:

ldmia r0, {r2 - r3}

ldmia r0!, {r2 - r3}

感叹号的作用就是r0的值在ldm过程中发生的增加或者减少最后写回到r0去,也就是说ldm时会改变r0的值。

^的作用

ldmfd sp!, {r0 - r6, pc}

ldmfd sp!, {r0 - r6, pc}^

^的作用:在目标寄存器中有pc时,会同时将spsr写入到cpsr,一般用于从异常模式返回。

那么arm汇编指令就暂时介绍这几个,接下来介绍几个伪指令。

伪指令不是指令,伪指令和指令的根本区别是经过编译后会不会生成机器码。伪指令的意义在于指导编译过程。

ldr 大范围的地址加载指令

adr 小范围的地址加载指令

adrl 中等范围的地址加载指令

nop 空操作

ldr伪指令和ldr指令虽然名字一样,但是还是有区别的,这里涉及到合法立即数和非法立即数的概念。这一部分内容可以参考https://blog.csdn.net/wwwlyj123321/article/details/80802770

简言之,就是ldr指令只能操作合法立即数,ldr伪指令就没有这个限制,所以,我们通常使用的都是ldr伪指令。在写法上,ldr伪指令后面多一个等号,如:LDR R1,=0xFFF

总结:掌握一些常见的指令有助于我们看懂程序,并且进行简单的修改,对于arm汇编,通常只要能大概看懂就行,或者进行一些简单的修改,并不需要完全自己来写。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 电子技术研习社 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档