arm裸机【1】 --- ARM的汇编指令

目录

    • 一.ARM汇编的特点
      • ARM汇编特点1:`LDR/STR架构`
      • ARM汇编特点2:8种寻址方式
      • ARM汇编特点3:指令后缀
      • ARM汇编特点4:条件执行后缀
      • ARM汇编特点5:多级指令流水线
    • 二.常用的ARM指令
      • 1.数据处理指令:
      • 2.程序状态寄存器CPSR访问指令:
      • 3.跳转指令
      • 4.访存指令
      • 5.软中断指令
    • 三.ARM中的立即数
    • 四.协处理器与指令
      • 1.协处理器
      • 2.协处理器指令
      • 3.ldm,stm和栈
        • 后缀的使用:
        • 堆栈(栈)
      • 4.汇编语言中的符号
    • 五.ARM汇编伪指令
      • 1.GNU汇编符号
      • 2.常用GNU伪指令
      • 3.重要的几个伪指令

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

(指令可以类比为米,编译过程就是相当于煮饭的过程,最后生成的饭就是相当于1010的机器码,让CPU读取,而伪指令就相当于煮饭用的锅,只参与煮饭(编译过程),最终锅是不会成为米饭的)

一.ARM汇编的特点

ARM汇编特点1:LDR/STR架构

ARM采用RISC架构,这种架构CPU本身不能直接读取内存,而需要先将内存中内容加载入CPU中通用寄存器中才能被CPU处理。

ldr(load register)指令将内存内容加载入通用寄存器。
str(store register)指令将寄存器内容存入内存空间中。
ldr/str 组合用来实现 ARM CPU和内存数据交换

arm裸机【1】 --- ARM的汇编指令_第1张图片

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

寻址方式 汇编语句 内容解析
寄存器寻址 mov r1, r2 r2的值赋值给r1
立即寻址 mov r0, #0xFF00 把16进制数#0xFF00赋值给r0
寄存器移位寻址 mov r0, r1, lsl #3 把r1里面的数左移3位后赋值给r0 (右移lsr)
寄存器间接寻址 ldr r1, [r2] 把r2里面存的地址相对应的内容赋值给r1(r2相当于是一个指针)
基址变址寻址 ldr r1, [r2, #4] 把r2l里面存的地址加4后相对应的内容赋值给r1
多寄存器寻址 ldmia r1!, {r2-r5, r12} r1作为基地址 r2 <-[r1] , r3 <-[r1+04] ,r4 <-[r1+08] , r5 <- [r1+12] , r12 <-[r1+16]
堆栈寻址 stmfd sp!, {r2-r7, lr} 把r2 - r7寄存器里面的内容压到内存的栈上
相对寻址 beq flag flag: flag: 标号用于标记后面那句指令的地址,常用于表示入口点,类似于C语言中的函数名;C语言中的goto可以跳转到一个标号,在arm汇编中用指令b flag:j就可以跳转到flag:对应的标号处执行

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汇编特点4:条件执行后缀

arm裸机【1】 --- ARM的汇编指令_第2张图片

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

条件后缀执行注意2点:
1、条件后缀和程序状态寄存器CPSR的标志位相关,条件后缀是否成立,不是取决于本句代码,而是取决于这句代码之前的代码运行后的结果。
2、条件后缀决定了本句代码是否被执行,而不会影响上一句和下一句代码是否被执行。

ARM汇编特点5:多级指令流水线

PC指向正被取指的指令,而非正在执行的指令为增加处理器指令流的速度,ARM使用多级流水线.,(S5PV210使用13级流水线,ARM11为8级)允许多个操作同时处理,而非顺序执行,一般中断返回到PC时需要注意流水线的问题。

下图为3级流水线工作原理示意图。
arm裸机【1】 --- ARM的汇编指令_第3张图片

二.常用的ARM指令

6类指令:
数据处理指令:
——数据传输指令, MOV MVN
——算术指令, ADD SUB RSB ADC SBC RSC
——逻辑指令, AND ORR EOR BIC
——比较指令, CMP CMN TST TEQ
——乘法指令, MUL MLA UMULL UMLAL SMULL SMLAL
——前导零计数。
程序状态寄存器访问指令:MRS MSR
跳转指令: B BL BX
访存指令: LDR/STR LDM/STM SWP
异常中断产生指令: SWI
协处理器指令: mcr mrc

1.数据处理指令:

指令名称 指令语句 内容解析
数据传输指令 mov mvn (1) mov(move): mov r1,r0 @两个寄存器之间数据传递; (2) mov r1,#0xff @将立即数赋值给目标寄存器; (3) mvn和mov用法一样,区别是mov原封不动传递,而mvn是按位取反后传递。
算术指令 add sub rsb adc sbc rsc add(加法运算) sub(减法运算) rsb(反减运算) adc(带进位的加法运算) sbc(带进位的减法运算) rsc(带进位的反减指令)
逻辑指令 and orr eor bic and(逻辑与) orr(逻辑或) eor(逻辑异或) bic(位清除指令)
比较指令 cmp cmn tst teq cmp(等价于sub r2,r0,r1(r2=r0-r1)) cmn(等价于add r0,r1) tst(对特定位进行操作) teq(对两个数进行异或);比较指令不用加s后缀就可以影响spsr中的标志位
乘法指令 mvl mla umull umlal 实际编程比较少用
前导零计数 clz 统计一个数的二进制位前面有几个0

2.程序状态寄存器CPSR访问指令:

由于cpsr寄存器比较特殊,需要专门的访问指令:
mrs: 用来读cpsr
msr: 用来写cpsr
cpsr和spsr的区别: cpsr是程序状态寄存器,整个soc中只有一个,而spsr有5个,分别在5中异常模式下,作用是当进入异常模式时,用来保存之前普通模式下的cpsr,以在返回普通模式时恢复原来的cpsr。

3.跳转指令

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

4.访存指令

ldr: 加载指定内存地址的数据到寄存器,按照字节访问
str: 加载指定寄存器数据到内存地址中,按照字节访问
ldm: 和ldr功能一样,一次多字节多寄存器访问
stm: 和str功能一样,一次多字节多寄存器访问
swp: 内存和寄存器互换指令,一边读一边写,例如:swp r1,r2,[r0]把r0地址中的数赋给r1,再把r2赋给r0地址中,相当于做了一个交换;而指令swp r1,r1,[r0] 也可以表示内存和寄存器交换内容。

5.软中断指令

swi(software interrupt):在软件层模拟产生一个中断,这个中断会传送给CPU,常用于实现系统调用

三.ARM中的立即数

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

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

四.协处理器与指令

1.协处理器

什么是协处理器?SOC内部另一处理核心,协助主CPU实现某些功能,被主CPU调用执行一定任务。
CPU中的寄存器都是以R开头的,协处理器中的寄存器都是以C开头的。

2.协处理器指令

mrc: 读取CP15中的寄存器
mcr: 向CP15中的寄存器写数据
指令用法: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。

3.ldm,stm和栈

ldr与str只能访问4个字节,当数据较大的时候,就会明显的降低效率,这时就需要使用到ldm和stm,ldm与stm是大量的从寄存器与内存交换数据的方式,常用于在内存和寄存器之间大量读取和写入数据。
ldm(load register multiple) :读指令
stm(store register multiple):写指令

后缀的使用:

ia:increase after,后增加,表示每个操作的时候,先传输数据,后增加内存地址
ib:increase before,先增加,表示在每个操作的时候,先增加内存地址,再进行数据传输
da:decrease after:和ia一样,差别在于减少地址
db:decrease before:和ib一样,差别在于减少地址
fd:full decrease:满递减堆栈,查看栈的描述
ed:empty decrease:空递减堆栈
fa:满递增堆栈
ea:空递增堆栈
操作栈时使用相同的后缀就不会出错

堆栈(栈)

空栈:栈指针指向空位,每次可以直接存入,然后栈指针(SP)递增或者递减1格,取的时候要递增或者递减1格才能取出
满栈:SP指向栈最后1格数据,存入的时候需要先移动1格才能存入,取的时候可以直接取出
增栈:SP向地址增加的方向移动
减栈:SP向地址减少的方向移动

4.汇编语言中的符号

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汇编伪指令

1.GNU汇编符号

[@,#,//,/~/] 注释,和C语言的//是一样的
冒号,在汇编中以冒号结尾的是标号,标号标记标号后面的指令的地址,举例汇编的死
循环: flag: b flag
. 点号,代表当前指令的地址,例如:b .指令会进入死循环
#立即数前面要加#或$,代表一个立即数(不区分进制)

2.常用GNU伪指令

.global_start: 给_start外部链接属性
.section .text: 指定当前段为代码段
.ascii .byte .short .long .word .quad .float .string: 定义各种类型的数据
举例:

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

.align 4: 以16字节对齐(2的4次方)
uboot里的(.balignl 16 0x3C)解析: b表示填充,align表示对齐,l表示long,以4字节为单位填充,16表示以16字节对齐,0x3C是用来填充的原料
如下:

0x00000008:		.balignl 16, 0xdeadbeef        //(假设起始地址)
0x0000000c		0xdeadbeef                    //0xdeadbeef  填充到此地址
0x00000010: 	                             //    下一条指令存放的地址

.equ: 宏定义
.end: 表示一个文件的结束
.include: 用于包含头文件
.arm / .code32: 声明以下的代码是arm指令
.thumb / .code16: 声明以下的代码是thumb指令

3.重要的几个伪指令

ldr: 大范围的地址加载指令
adr: 小范围的地址加载指令
adrl: 中等范围的地址加载指令
nop: 空操作
adr和ldr的差别:ldr加载的地址在链接时确定,而adr加载的地址在运行时确定;所以我们可以通过adr和ldr加载的地址比较来判断当前程序是否在链接时指定的地址运行。
ARM中有一个ldr指令,还有一个ldr伪指令,在一般情况下使用的是伪指令。

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

本文链接:https://blog.csdn.net/qq_45544223/article/details/104863706

你可能感兴趣的:(#,arm裸机初体验,arm)