arm汇编

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

  ARM官方的ARM汇编风格:指令一般用大写、Windows中IDE开发环境(如ADS、MDK等)常用。如: LDR R0, [R1]

  GNU风格的ARM汇编:指令一般用小写字母、linux中常用。如:ldr r0, [r1]

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

  寄存器寻址 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}

  相对寻址 beq flag

  同一指令经常附带不同后缀,变成不同的指令。经常使用的后缀有:
  B(byte)功能不变,操作长度变为8位
  H(half word)功能不变,长度变为16位
  S(signed)功能不变,操作数变为有符号
  如 ldr ldrb ldrh ldrsb ldrsh
  S(S标志)功能不变,影响CPSR标志位
  如 mov和movs movs r0, #0

  mov r0, r1 @ 相当于C语言中的r0 = r1;
  moveq r0, r1	@ 如果eq后缀成立,则直接执行mov r0, r1;如果eq不成立则本句代码直接作废,相当于没有
  @ 类似于C语言中 if (eq){r0 = r1;}

  条件后缀执行注意2点:
1、条件后缀是否成立,不是取决于本句代码,而是取决于这句代码之前的代码运行后的结果。
2、条件后缀决定了本句代码是否被执行,而不会影响上一句和下一句代码是否被执行。

  

GT greater than
LT less than

数据传输指令 mov mvn
算术指令 add sub rsb adc sbc rsc
逻辑指令 and orr eor bic
比较指令 cmp cmn tst teq
乘法指令 mvl mla umull umlal smull smlal
前导零计数 clz

mrs & msr

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

  b & bl & bx

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

ldr/str & ldm/stm & swp

单个字/半字/字节访问 ldr/str
多字批量访问 ldm/stm
swp r1, r2, [r0]
swp r1, r1, [r0]

合法立即数与非法立即数

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

  swi(software interrupt)

  软中断指令用来实现操作系统中系统调用

mov(move) mov r1, r0 @两个寄存器之间数据传递
mov r1, #0xff	@ 将立即数赋值给寄存器

mvn和mov用法一样,区别是mov是原封不动的传递,而mvn是按位取反后传递
按位取反的含义:
譬如r1 = 0x000000ff,然后mov r0, r1 后,r0 = 0xff	但是我mvn r0, r1后,r0=0xffffff00

and	逻辑与
orr	逻辑或
eor	裸机异或

bic	位清除指令

bic	r0,r1,#0x1f	@ 将r1中的数的bit0到bit4清零后赋值给r0	0x1f = 0x0000001f=0x0000```11111

比较指令:
cmp	cmp r0, r1 等价于 sub r2, r0, r1 (r2 = r0 - r1)
cmn	cmn r0, r1 等价于 add r0, r1
tst	tst r0, #0xf	@测试r0的bit0~bit3是否全为0
teq
比较指令用来比较2个寄存器中的数
注意:比较指令不用后加s后缀就可以影响cpsr中的标志位。

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

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

  

mcr & mrc

mrc用于读取CP15中的寄存器
mcr用于写入CP15中的寄存器

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

mcr{<cond>} p15, <opcode_1>, <Rd>, <Crn>, <Crm>, {<opcode_2>}
opcode_1:对于cp15永远为0
Rd:ARM的普通寄存器
Crn:cp15的寄存器,合法值是c0~c15
Crm:cp15的寄存器,一般均设为c0
opcode_2:一般省略或为0

举例(来自于uboot)

mrc p15, 0, r0, c1, c0, 0
orr r0, r0, #1
mcr p15, 0, r0, c1, c0, 0

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

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

8种后缀
  ia(increase after)先传输,再地址+4
  ib(increase before)先地址+4,再传输
  da(decrease after)先传输,再地址-4
  db(decrease before)先地址-4,再传输
  fd(full decrease)满递减堆栈
  ed(empty decrease)空递减堆栈
  fa(·······) 满递增堆栈
  ea(·······)空递增堆栈
四种栈
  空栈:栈指针指向空位,每次存入时可以直接存入然后栈指针移动一格;而取出时需要先移动一格才能取出
  满栈:栈指针指向栈中最后一格数据,每次存入时需要先移动栈指针一格再存入;取出时可以直接取出,然后再移动栈指针
  增栈:栈指针移动时向地址增加的方向移动的栈
  减栈:栈指针移动时向地址减小的方向移动的栈

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,一般用于从异常模式返回。

总结

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

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

gnu汇编中的一些符号

@ 用来做注释。可以在行首也可以在代码后面同一行直接跟,和C语言中//类似
# 做注释,一般放在行首,表示这一行都是注释而不是代码。
:以冒号结尾的是标号
. 点号在gnu汇编中表示当前指令的地址
# 立即数前面要加#或$,表示这是个立即数

常用gnu伪指令

.global _start @ 给_start外部链接属性
.section .text @ 指定当前段为代码段
.ascii .byte .short .long .word
.quad .float .string @ 定义数据
.align 4 @ 以16字节对齐
.balignl 16 0xabcdefgh @ 16字节对齐填充

偶尔

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

最重要的几个伪指令

ldr 大范围的地址加载指令
adr 小范围的地址加载指令
adrl 中等范围的地址加载指令
nop 空操作

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

adr与ldr

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

int a;

while(1);

flag:
b flag

b .

IRQ_STACK_START:
.word	0x0badc0de
等价于 unsigned int IRQ_STACK_START = 0x0badc0de;

.align 4	@ 16字节对齐
.align 2	@ 4字节对齐

.balignl 16, 0xdeadbeef	@ 对齐 + 填充
b表示位填充;align表示要对齐;l表示long,以4字节为单位填充;16表示16字节对齐;0xdeadbeef是用来填充的原料。

0x00000008:	.balignl 16, 0xdeadbeef
0x0000000c	0xdeadbeef
0x00000010: 下一条指令

ldr指令:	ldr r0, #0xff
伪指令:	ldr r0, =0xfffl	@涉及到合法/非法立即数,涉及到ARM文字池

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

  

时间: 2024-10-13 02:45:28

arm汇编的相关文章

如何实现对ARM汇编指令的调试?

学习ARM汇编语言时,少不了对ARM汇编指令的调试.作为支持多语言的调试器,gdb自然是较好的选择.调试器工作时,一般通过修改代码段的内容构造trap软中断指令,实现程序的暂停和程序执行状态的监控.为了在x86平台上执行ARM指令,可以使用qemu模拟器执行ARM汇编指令,具体的调试方法,一起来看看吧. 一.准备ARM汇编程序 首先,我们构造一段简单的ARM汇编程序作为测试代码main.s. .globl _start _start: mov R0,#0 swi 0x00900001 以上汇编指

ARM汇编指令集1

(汇编)指令是CPU机器指令的助记符,经过编译过会得到一串0011组成的机器码,可以由CPU读取执行. (汇编)伪指令本质不是指令(只是和指令一起写在代码中),它是编译器环境提供的,目的是用来指导编译过程,经过编译后伪指令最终不会生成机器码. 两种不同风格的ARM指令 --ARM官方的ARM汇编风格:指令一般用大写,Windows中的IDE开发环境(如ADS.MDK)常用.如:LDR  R0,[R1] --GNU风格的ARM汇编:指令一般用小写字母.Linux中常用.如:ldr  r0,[r1]

ARM汇编基础(1)--ADS1.2安装与第一个ARM汇编程序

前言 最近的工作是与逆向相关, 用到ARM汇编, 以前在大学时学过x86汇编和ARM开发, 自我感觉当时学的还挺好, 很久没用也生疏了. 正好趁着这个机会也复习一下ARM汇编. 关于ARM的介绍和理论知识,此处就不再赘述. 进入正题: 安装ADS1.2 安装过程很简单, 没什么技术含量. 但是, win7可能会碰到卡在100%的问题, 网上有很多解决的办法, 但好像都不管用. 我用XP的虚拟机装的ADS1.2, 在XP系统上安装很顺利. 第一个ARM汇编程序 新建一个工程, 然后再按照一下步骤设

【嵌入式Linux+ARM】ARM体系结构与编程(ARM汇编指令)

自己的一些简单的总结,也是最常用的ARM汇编指令,之后也会不断的补充完善. 1. 汇编系统预定义的段名 .text    @代码段 .data   @初始化数据段 .bss    @未初始化数据段 需要注意的是,源程序中.bss段应该在.text之前. 2.定义入口点 汇编程序的缺省入口是 start标号,用户也可以在连接脚本文件中用ENTRY标志指明其它入口点. .text .global _start _start: 3 .word用法 word expression就是在当前位置放一个wo

GNU ARM汇编语法

http://wenku.baidu.com/view/812a698c84868762caaed55d.html?re=view ARM汇编源程序有两种风格: ARM官方推荐的风格,所有的指令都大写.常用在windows下的IDE中. GNU风格的汇编风格,所有的指令都小写.常用在与Linux相关的工具中. 注释符号: GNU arm汇编所采用的注释符号是@符号,同样c语言中的 // 和 /* */ 两种类型的注释方法在GNU arm汇编中也被支持. 宏定义: 在GNU arm汇编程序中可以使

linux驱动系列之arm汇编

在arm平台学习linux时,会遇到arm汇编指令,arm汇编指令与8086汇编指令很多地方都不同,在此记下来以免后面忘了,同时在学习了汇编指令之后分析一些汇编指令编写的代码. 一.相对跳转指令b.bl b.bl指令都实现短跳转,bl指令执行后会在链接寄存器r14中保存下一条指令的地址. 二.数据传送指令mov mov指令会把一个寄存器的数赋值给另一个寄存器,或者把一个常数传递给另一个寄存器. 如:mov  r0,r1  //将r1中的值传递给r0,mov r0,#0xff //将常数0xff传

ARM汇编的一般形式和汇编调用C语言

.text //代码段.global _start //表明程序入口_start: //入口函数 BL main //跳转到c语言中的main,不一定要转跳到main,也可以执行其他的汇编指令 一般工程中,纯汇编复杂,而且效率比C语言并没有提高多少,所以在没有操作系统的工程中,最好的开发方式就是用汇编调用C语言,使用C语言来完成所需要的工作(只有在对效率要求极高的时候才会使用汇编编写代码块),当然裸机开发没有现成的库,就算是简单的printf函数也是没法调用的,其实就是就是汇编的高级语言版本.个

GNU风格 ARM汇编语法指南

汇编源程序一般用于系统最基本的初始化:初始化堆栈指针.设置页表.操作 ARM的协处理器等.这些初始化工作完成后就可以跳转到C代码main函数中执行. 1.  GNU汇编语言语句格式 任何Linux汇编行都是如下结构:[<label>:][<instruction or directive or pseudo-instruction>} @comment l         instruction为指令 l         directive为伪操作 l         pseudo

ARM 汇编指令集

转载,方便查找用. ARM汇编指令集 一. 跳转指令    跳转指令用于实现程序流程的跳转,在ARM程序中有两种方法可以实现程序流程的跳转: Ⅰ.使用专门的跳转指令. Ⅱ.直接向程序计数器PC写入跳转地址值. 通过向程序计数器PC写入跳转地址值,可以实现在4GB的地址空间中的任意跳转,在跳转之前结合使用 MOV LR,PC 等类似指令,可以保存将来的返回地址值,从而实现在4GB连续的线性地址空间的子程序调用. ARM指令集中的跳转指令可以完成从当前指令向前或向后的32MB的地址空间的跳转,包括以

GNU风格ARM汇编编程实战之一 &lt;C与汇编混合编程&gt;

一.参考资料 1. <ARM GCC内联汇编手册>:http://www.ethernut.de/en/documents/arm-inline-asm.html 2. <__asm__ __volatile__内嵌汇编用法简述>:http://www.embedu.org/Column/Column28.htm 3. <A?R?M?内?嵌?汇?编?示?例>:http://wenku.baidu.com/view/72c12e4133687e21af45a990.htm