两个概念:
指令与伪指令
指令
是CPU机器指令的助记符,经过编译后会得到一串1010…组成的机器码,可以由CPU读取执行。
伪指令
本质上不是指令(只是和指令一起写在代码中),它是编译器环境提供的,目的是用来指导编译过程,经过编译后伪指令最终不会生成机器码。
’
(指令可以类比为米,编译过程就是相当于煮饭的过程,最后生成的饭就是相当于1010的机器码,让CPU读取,而伪指令就相当于煮饭用的锅,只参与煮饭(编译过程),最终锅是不会成为米饭的)
LDR/STR架构
ARM采用RISC架构,这种架构CPU本身不能直接读取内存,而需要先将内存中内容加载入CPU中通用寄存器中才能被CPU处理。
ldr(load register)指令将内存内容加载入通用寄存器。
str(store register)指令将寄存器内容存入内存空间中。
ldr/str 组合用来实现 ARM CPU和内存数据交换
寻址方式 | 汇编语句 | 内容解析 |
---|---|---|
寄存器寻址 | 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:对应的标号处执行 |
同一指令经常附带不同后缀,变成不同的指令。经常使用的后缀有:
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、条件后缀和程序状态寄存器CPSR的标志位相关,条件后缀是否成立,不是取决于本句代码,而是取决于这句代码之前的代码运行后的结果。
2、条件后缀决定了本句代码是否被执行,而不会影响上一句和下一句代码是否被执行。
PC指向正被取指的指令,而非正在执行的指令为增加处理器指令流的速度,ARM使用多级流水线.,(S5PV210使用13级流水线,ARM11为8级)允许多个操作同时处理,而非顺序执行,一般中断返回到PC时需要注意流水线的问题。
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
指令名称 | 指令语句 | 内容解析 |
---|---|---|
数据传输指令 | 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 |
由于cpsr寄存器比较特殊,需要专门的访问指令:
mrs: 用来读cpsr
msr: 用来写cpsr
cpsr和spsr的区别: cpsr是程序状态寄存器,整个soc中只有一个,而spsr有5个,分别在5中异常模式下,作用是当进入异常模式时,用来保存之前普通模式下的cpsr,以在返回普通模式时恢复原来的cpsr。
b: 直接跳转。(就没打算返回)
bl(branch and link): 跳转前把返回地址放入lr寄存器中,以便返回,以便用于函数调用。
bx: 跳转同时切换到ARM模式,一般用于异常处理的跳转。
ldr: 加载指定内存地址的数据到寄存器,按照字节访问
str: 加载指定寄存器数据到内存地址中,按照字节访问
ldm: 和ldr功能一样,一次多字节多寄存器访问
stm: 和str功能一样,一次多字节多寄存器访问
swp: 内存和寄存器互换指令,一边读一边写,例如:swp r1,r2,[r0]把r0地址中的数赋给r1,再把r2赋给r0地址中,相当于做了一个交换;而指令swp r1,r1,[r0] 也可以表示内存和寄存器交换内容。
swi(software interrupt):在软件层模拟产生一个中断,这个中断会传送给CPU,常用于实现系统调用
ARM指令都是32位,除了指令标记和操作标记外,本身只能附带很少位数的立即数。因此立即数有合法和非法之分。
合法立即数:经过任意位数的移位后非零部分可以用8位表示的即为合法立即数
举列子:
合法立即数: 0x000000ff 0x00ff0000 0xf000000f
非法立即数: 0x000001ff
什么是协处理器?SOC内部另一处理核心,协助主CPU实现某些功能,被主CPU调用执行一定任务。
CPU中的寄存器都是以R开头的,协处理器中的寄存器都是以C开头的。
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。
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向地址减少的方向移动
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,一般用于从异常模式返回。
[@,#,//,/~/]
注释,和C语言的//是一样的
:
冒号,在汇编中以冒号结尾的是标号,标号标记标号后面的指令的地址,举例汇编的死
循环:flag: b flag
.
点号,代表当前指令的地址,例如:b .
指令会进入死循环
#
立即数前面要加#或$,代表一个立即数(不区分进制)
.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指令
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