ARM汇编指令

xiaoxiao2021-03-01  33

目录

一、ARM汇编指令集... 1

1、两个概念:指令和伪指令... 1

2、两种不同风格的ARM指令... 1

3、ARM汇编特点... 1

3.1、特点1:LDR/STR架构... 1

3.2、特点2:8种寻址方式... 1

3.3、特点3:指令后缀... 2

3.4、特点4:条件执行后缀(大于、小于、等于)... 3

3.5、特点5:多级指令流水线... 3

4、数据传输与跳转指令... 4

4.1、数据处理指令... 4

4.2、cpsr访问指令... 5

4.3、跳转指令  b & bl & bx. 5

4.4、访存指令(访问内存)  ldr/str & ldm/stm & swp. 5

4.5、软中断指令(用软件模拟中断)(用于操作系统)... 6

4.6、立即数... 6

二、协处理器和协处理器指令... 6

1、什么是协处理器... 6

2、协处理器cp15操作指令    mcr & mrc. 6

2.1、操作指令的作用... 6

2.2、操作指令的使用方法... 7

三、ldm/stm与栈的处理... 7

1、为什么需要多寄存器访问指令... 7

2、ldm/stm的8种后缀... 7

3、四种栈... 8

4、!的作用... 8

5、^的作用... 8

6、总结... 9

四、伪指令... 9

1、伪指令的意义... 9

2、gnu汇编中的一些符号... 9

3、常见的gnu伪指令... 9

4、偶尔用到的gnu伪指令... 10

5、最重要的伪指令... 10

6、adr和ldr. 11

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

一、ARM汇编指令集

1、两个概念:指令和伪指令

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

2、两种不同风格的ARM指令

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

3、ARM汇编特点

3.1、特点1:LDR/STR架构

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

注意:

CPU与内存之间的数据交换需要通过CPU中的通用寄存器才可以,所以访问效率会低一些。

3.2、特点2:8种寻址方式

Mov表示从寄存器到寄存器,ld和str表示从内存到寄存器。

寄存器寻址                 mov r1, r2                        

R1和r2都是寄存器名字,意思是将r2中的内容传送到r1

立即寻址                     mov r0, #0xFF00

就是立即数寻址,意思是将立即数#0xFF00给r0

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

意思是将r1左移3位后传送个r0(lsl表示左移)

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

[r2]表示r2中存储的所指向的内存的地址,意思是将r2中存储的所指向的内存中的数据给r1。

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

意思是将r2中存储的地址加上4后所指向的地址中的内容给r1。

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

意思是一次访问7个寄存器,{ r2-r7, r12}表示r2、r3、r4、r5、r6、r7、r12这7个寄存器。而r1!表示一串内存空间,相当于数组头地址,第二个参数有7个寄存器,它就有7个内存空间。

将r1所存地址所代表的内存及其后边的7个连续内存中存储的数据传给7个寄存器。

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

sp是堆栈指针,是一个确定的值。具体和多寄存器寻址类似。

 

相对寻址                 beq flag         以PC为标志来跳转。

在汇编中,“flag:”这种形式的写法叫做标号。用来标记后边指令的地址。这样可以直接跳转到标号出执行。和C语言中的goto一样。

3.3、特点3:指令后缀

同一指令经常附带不同后缀,变成不同的指令。

经常使用的后缀有:

B(byte)功能不变,操作长度变为8位H(half word)功能不变,长度变为16位S(signed)功能不变,操作数变为有符号,

如: ldr  ldrb  ldrh  ldrsb  ldrsh

S(S标志)功能不变,影响CPSR标志位

如 :mov和movs            

mov  r0, #0          将0这个数字赋给r0,且r0中的标志位不变。

movs  r0, #0        将0这个数字赋给r0,且r0中的Z标志位变为1。

3.4、特点4:条件执行后缀(大于、小于、等于)

例如:

mov r0,r1                   相当于C语言中的r0=r1;

moveq r0,r1                      如果eq后缀成立,则直接执行mov r0,r1;如果eq不成立,则此语句直接作废,相当于没有。类似于C语言中的

if ( eq )  { r0=r1; }

注意:

条件后缀是否成立不是取决于本句代码,而是取决于这句代码之前的代码运行的结果。

条件后缀决定了本句代码是否被执行,而不会影响上一句和下一句代码是否执行。

3.5、特点5:多级指令流水线

为增加处理器指令流的速度,ARM使用多级流水线.,下图为3级流水线工作原理示意图。(S5PV210使用13级流水线,ARM11为8级)允许多个操作同时处理,而非顺序执行。PC指向正被取指的指令,而非正在执行的指令。

 

4、数据传输与跳转指令

4.1、数据处理指令

数据传输指令     mov  mvn

mov(move)             mov r1,r0     在两个寄存器之间进行数据传递

                                     mov r1,#oxff              将立即数赋值给寄存器

mov和mvn用法一样,区别是mov是原封不动的传递,而mvn是按位取

反后传递。

例如:r1 = 0x000000ff,然后mov r0, r1 后,r0 = 0xff,但是我mvn r0, r

后,r0=0xffffff00

算术指令             add  sub  rsb  adc  sbc  rsc 逻辑指令             and  orr  eor  bic

and          逻辑与               

orr           逻辑或(位或)

eor           裸机异或

bic           位清除指令

bic    r0,r1,#0x1f        将r1中的数的bit0到bit4清零后赋值给r0          0x1f =0x0001 1111

比较指令(比较指令用来比较2个寄存器中的数)cmp  cmn  tst  teq

cmp         cmp r0, r1  判断r0-r1=0,等价于 sub r2, r0, r1 (r2 = r0 - r1)

cmn         cmn r0, r1  判断r0+r1=0,等价于 add r0, r1

tst            tst r0, #0xf    测试r0的bit0~bit3是否全为0

                tst r0,#0x8    测试r0的bit3是否为0。

teq         teq r0,r1       对两个数进行EOR(异或),比较是否相等。若两个数

                                   相等,结果为假,否则,结果为真。

注意:比较指令不用后加s后缀就可以影响cpsr中的标志位。

比较指令不需要保存结果,直接访问标志位即可知道比较指令的结果。

乘法指令             mvl  mla  umull  umlal  smull  smlal前导零计数         clz(基本用不到)

4.2、cpsr访问指令

CPSR寄存器比较特殊,需要专门的指令访问,这就是mrs和msr。mrs & msrmrs用来读psr,msr用来写psr

mrs  r0,cpsr         将cpsr读取到寄存器r0中

msr  cpsr,r0         将r0中的数读取到cpsr中。

cpsr和spsr的区别和联系:cpsr是程序状态寄存器,整个SoC中只有1个;而spsr有5个,分别在5种异常模式下,作用是当从普通模式进入异常模式时,用来保存之前普通模式下的cpsr的,以在返回普通模式时恢复原来的cpsr。

4.3、跳转指令         b & bl & bx

b (branch)直接跳转(就没打算返回),与C语言的goto类似。bl (branch and link),跳转前把返回地址放入lr中,以便返回,以便用于函数调用。bx 跳转同时切换到ARM模式,一般用于异常处理的跳转。

4.4、访存指令(访问内存)           ldr/str &      ldm/stm & swp

单个字/半字/字节访问    ldr/str多字批量访问  ldm/stmswp   同时读写

swp r1, r2, [r0]      将r0所指向的内存中的值读取到r1中,同时将r2中的值写入到r0所指向的内存中。

swp r1, r1, [r0]      将r0所指向的内存中的值读取到r1中,同时将r1之前的值写入到r0所指向的内存中。

4.5、软中断指令(用软件模拟中断)(用于操作系统)

swi(software interrupt)软中断指令用来实现操作系统中系统调用

4.6、立即数

合法立即数与非法立即数ARM指令都是32位,除了指令标记和操作标记外,本身只能附带很少位数的立即数。因此立即数有合法和非法之分。合法立即数:经过任意位数的移位后非零部分可以用8位表示的即为合法立即数

例如:

合法立即数: 0x000000ff       0x00ff0000   0xf000000f

非法立即数: 0x000001ff

二、协处理器和协处理器指令

1、什么是协处理器

SoC内部的另一个处理核心,协助主处理器CPU实现某些功能,被主处理器CPU调用执行一定任务。协处理器是通过编号来区分的,在使用时只需要知道他的编号就可以操作。ARM设计上支持多达16个协处理器,但是一般SoC只实现其中的CP15.(cp:coprocessor)协处理器和MMU、cache、TLB等处理有关,功能上和操作系统的虚拟地址映射(MMU)、cache管理等有关。

2、协处理器cp15操作指令             mcr & mrc

2.1、操作指令的作用

mrc用于读取CP15中的寄存器

mcr用于写入CP15中的寄存器

 

2.2、操作指令的使用方法

mcr{<cond>}   p15, <opcode_1>, <Rd>, <Crn>, <Crm>, {<opcode_2>}

opcode_1:对于cp15永远为0Rd:ARM的普通寄存器,不能是r15,否则结果未知Crn:cp15的寄存器,合法值是c0~c15Crm:cp15的寄存器,一般均设为c0opcode_2:一般省略或为0

例子:

       mrc  p15, 0, r0, c1, c0, 0

       bic   r0, r0, #0x00002000

       bic   r0, r0, #0x00000007

       orr   r0, r0, #0x00000002

       orr    r0, r0, #0x00000800

       mcr  p15, 0, r0, c1, c0, 0

三、ldm/stm与栈的处理

1、为什么需要多寄存器访问指令

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

2、ldm/stm的8种后缀

ia(increase after)先传输,再地址+4ib(increase before)先地址+4,再传输da(decrease after)先传输,再地址-4db(decrease before)先地址-4,再传输fd(full decrease)满递减堆栈ed(empty decrease)空递减堆栈fa(·······) 满递增堆栈ea(·······)空递增堆栈

举例

stmia     sp, {r0 - r12}

说明:

将r0存入sp指向的内存处(假设为0x30001000);然后地址+4(即指向0x30001004),将r1存入该地址;然后地址再+4(指向0x30001008),将r2存入该地址······直到r12内容放入(0x3001030),指令完成。一个访存周期同时完成13个寄存器的读写

3、四种栈

空栈:栈指针指向空位,每次存入时可以直接存入然后栈指针移动一格;而取出时需要先移动一格才能取出满栈:栈指针指向栈中最后一格数据,每次存入时需要先移动栈指针一格再存入;取出时可以直接取出,然后再移动栈指针增栈:栈指针移动时向地址增加的方向移动的栈减栈:栈指针移动时向地址减小的方向移动的栈

4、!的作用

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

5、^的作用

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

6、总结

批量读取或写入内存时要用ldm/stm指令各种后缀以理解为主,不需记忆,最常见的是stmia和stmfd谨记:操作栈时使用相同的后缀就不会出错,不管是满栈还是空栈、增栈还是减栈ARM默认的是满减栈(stmfd)

四、伪指令

1、伪指令的意义

伪指令不是指令,伪指令和指令的根本区别是经过编译后会不会生成机器码。指令编译后生产机器码,伪指令编译后消失。伪指令的意义在于指导编译过程。伪指令是和具体的编译器相关的,我们使用gnu工具链,因此学习gnu环境下的汇编伪指令。

2、gnu汇编中的一些符号

@ 用来做注释。可以在行首也可以在代码后面同一行直接跟,和C语言中//类似:以冒号结尾的是标号.  点号在gnu汇编中表示当前指令的地址

例子:死循环(相当于C语言中的while(1);)

flag:                     flag标号,表示一个地址

                      b flag            跳转到flag本身

               b .                 跳转到当前指令的地址(这一句相当于上边两行)

# 立即数前面要加#或$,表示这是个立即数

3、常见的gnu伪指令

.global _start         @ 给_start外部链接属性,为了让别的文件中访问.section  .text             @ 指定当前段为代码段.ascii  .byte  .short  .long  .word      @用于定义数据,相当于C语言中定义变量

例子:

IRQ_STACK_START:

         .word     0x0badc0de

其中IRQ_STACK_START为变量名,.word为变量类型,0x0badc0de为变量的值。相当于unsigned    int  IRQ_STACK_START = 0x0badc0de。

.quad  .float  .string @ 定义数据(双字,小数,字符串).align 4                  @ 以16字节对齐,2的4次方。

.align 2                  @ 以4字节对齐,2的2次方

.balignl 16 0xabcdefgh      @ 16字节对齐填充

B表示位填充;align表示对齐;l表示long,以4字节为单位填充;16表示以16字节对齐;0xabcdefgh是用来填充的原料。

例子:

地址                     

0x00000008          .balignl 16 0xabcdefgh

0x0000000c           0xabcdefgh

0x00000010          下一条指令

说明:

0x00000008为原地址,运行.balignl 16 0xabcdefgh伪指令后,需要16字节对齐,跳转到0x00000010,中间的地址0x0000000c用0xabcdefgh填充。

.equ                @ 类似于C中宏定义

4、偶尔用到的gnu伪指令

.end                @标识文件结束.include                 @ 头文件包含.arm / .code32      @声明以下为arm指令.thumb / .code16  @声明以下为thubm指令

5、最重要的伪指令

ldr            大范围的地址加载指令(将地址加载到寄存器中)adr           小范围的地址加载指令(将地址加载到寄存器中)adrl          中等范围的地址加载指令(将地址加载到寄存器中)nop          空操作

注意:

ARM中有一个ldr指令,还有一个ldr伪指令。一般都使用ldr伪指令而不

用ldr指令。

       如果是指令,立即数前边是#,如果是伪指令,立即数前边是=;涉及到合法/非法立即数,设计到ARM文字池。

       例如:

       ldr指令 :          ldr r0,#0xff          需要编程人员考虑立即数是否合法

       ldr伪指令:       ldr r0,=0xff          不用考虑立即数是否合法

6、adr和ldr

adr编译时会被1条sub或add指令替代,而ldr编译时会被一条mov指令替代或者文字池方式处理;adr总是以PC为基准来表示地址,因此指令本身和运行地址有关,可以用来检测程序当前的运行地址在哪里(相对寻址)ldr加载的地址和链接时给定的地址有关,由链接脚本决定。

adr和ldr的差别:

ldr加载的地址在链接时确定,而adr加载的地址在运行时确定;所以我们可以通过adr和ldr加载的地址比较来判断当前程序是否在链接时指定的地址运行。

 

转载请注明原文地址: https://www.6miu.com/read-3450196.html

最新回复(0)