欢迎光临
我们一直在努力

ARM嵌入式开发 --- 汇编基础

一、立即数

1. 立即数:直接写在指令里的常数,不是寄存器的值,也不是内存里的数据。

2. 指令格式:#数字,#是立即数标记。

立即数支持 8 位、10 位、11 位、12 位等不同位宽,以下为常见立即数的二进制表示示例。

3. 12位立即数判断条件:

1)如果某个数的数值范围是0~0xFF之间,那么这个数一定是立即数
2)把某个数展开成2进制,这个数的最高位1至最低位1之间的二进制数序列的位数不能超过8位(包含这两位)
3)这个数的二进制序列凑够8位之后的的右边必须为偶数个连续的0

示例:

(1)0x234 = 0000 0000 0000 0000 0000 0010 0011 0100

最高位1至最低位1之间的二进制数序列:1000 1101没有超过8位

末尾1的右边有2个0,所以0x234是立即数

(2)0x3f4 = 0000 0000 0000 0000 0000 0011 1111 0100

最高位1至最低位1之间的二进制数序列:1111 1101 从第一个1开始到最后一个1之间没有超过8位

末尾1的右边有2个0,所以0x3f4是立即数

(3)旋转位 0xFA00
0x0000FA00
循环右移位数:24位 / 2 = 12
0xC 1100

注:常量既可以当作无符号数也可以当作有符号数,由编译器决定

二、指令

1. mov

加载12位立即数到寄存器或转移一个寄存器的值到另外一个寄存器

MOV{S}<c> <Rd>, #<const>
mov r0, #2 ;加载立即数2到寄存器r0

MOV{S}<c> <Rd>, <Rm>
mov r1, r0 ;将r0寄存器的值加载到r1

注:大小写均可,但必须保持统一

大多数指令的格式为opcode rd, rn ,rm,其中,rd是目标寄存器,rn是第一操作数寄存器

示例:

mov r0, #1 ;r0 = 1
mov r1, #2 ;r1 = 2
mov r2, r1 ;r2 = r1
mov r3, r1, lsl #2 ;r3 = r1 << 2
mov r4, r0, lsl r1 ;r4 = r0 << r1

2. ldr

(1)将非立即数加载到通用寄存器

例:ldr r0, =1314

(2)将RAM中的数据加载到通用寄存器

ldr r0, =0x40000000 ;i
ldr r1, [r0] ;r1 = *r0
ldr r0, =0x40000004 ;j
ldr r2, [r0] ;r2 = *r0
add r3, r1, r2
ldr r0, =0x40000008 ;sum
str r3, [r0] ;*r0 = r3

ldr r0, =0x40000000
ldr r1, [r0] ;r1 = *r0
ldr r1, =0x1A2B3C4D
str r1, [r0] ;*r0 = r1
str r1, [r0, #4] ;*(r0 + 1) = r1
str r1, [r0], #4 ;*r0++ = r1
str r1, [r0, #4]! ;*++r0 = r1

ADD 指令用于实现加法运算,支持立即数、寄存器以及带移位操作的寄存器三种形式。

3. str

将通用寄存器中的值写入到RAM中

str r1, [r0, #4] ;*(r0 + 1) = r1
str r1, [r0], #4 ;*r0++ = r1
str r1, [r0, #4]! ;*++r0 = r1

内存地址映射示例

区域地址范围说明
nor flash 0x00000000 – 0x1FFFFF 2MB
RAM 0x200000 – 0xFFFFFFFE 4KB
pc 0x40000000 – 0x40000FFF 程序计数器

4. add

寄存器加法:add Rd, Rm, Rn → Rd = Rm + Rn

可选加后缀 adds:加法并且更新 CPSR 状态标志位(不带 s 不更新标志)

mov r0, #2
mov r4, #1
add r0, r1, #3 ;r0 = r1 + 3
add r2, r0, r1 ;r2 = r0 + r1
add r3, r2, r1, lsl r4 ;r3 = r2 + (r1 << r4)

5. sub

寄存器减法:sub Rd, Rm, Rn → Rd = Rm – Rn

减法同时更新 CPSR 标志(cmp本质就是 subs R0, R1,结果不保存,只改标志)

add r0, r1, r2 ; r0 = r1 + r2

add r0, r1, #5 ; r0 = r1 + 5

6.cmp

做减法 Rm − Rn,不保存计算结果,只修改 CPSR 程序状态寄存器的标志位 (N,Z,C,V)

寄存器 Rm、Rn 本身的值不会被改变

求三个数中的最大值

mov r0, #3
mov r1, #5
mov r2, #7

cmp r0, r1
movlt r3, r1
movgt r3, r0

cmp r3, r2
movlt r3, r2

常用后缀:lt小于、gt大于、le小于等于、ge大于等于

7. bic

bit clear,将某个通用寄存器清0(操作数中为bit1的位清0),清0结果写回到通用寄存器

ldr r0, =0xFFFFFFF
bic r0, r0, #(1 << 7)

汇编指令的s后缀,几乎所有的汇编指令都可以在指令后面加上s后缀,s后缀的含义是在指令执行过程中会更新cpsr寄存器的N,V,C,Z位

  • N:在结果是有符号的二进制补码情况下,如果结果为负数,则N=1;如果结果为非负数,则N=0
  • Z:如果结果为0,则Z=1;如果结果为非零,否则Z=0
  • C:加法是针对无符号数最高有效位向更高位进位时C=1;减法中运算结果的最高有效位从更高位借位时C=0
  • V:该位是针对有符号数的操作,会在下面两种情形变为1,两个最高有效位均为0的数相加,得到的结果最高有效位为1;两个最高有效位均为1的数相加,得到的结果最高有效位为0;除了这两种情况以外V位为0

8. orr

OR Register,将某个通用寄存器置1(操作数中为bit1的位置1),置1结果写回到通用寄存器

mov r0, #0
orr r0, r0, #(1 << 7)

9. 跳转指令

(1)b

实现无条件跳转,可用于条件分支、循环和函数调用

1)条件分支

mov r0, #5
mov r1, #3
mov r2, #7

cmp r0, r1
bgt great
blt less

great
mov r3, r0
b label
less
mov r3, r1
b label

label
cmp r3, r2
bgt finished
movlt r3, r2

finished
b finished

2)循环

(1)do while循环
mov r0, #1 ;i
mov r1, #0 ;sum
loop
add r1, r0, r1
add r0, r0, #1
cmp r0, #100
ble loop
finished
b finished

(2)while循环
mov r0, #1
mov r1, #0
loop
cmp r0, #100
bgt finished
add r1, r0, r1
add r0, r0, #1
b loop
finished
b finished

3)函数调用

(2)bl

函数跳转前,会将函数返回地址保存到 lr 寄存器

(3)bx

bx lr 等价于 mov pc, lr,bx 指令后需要加一个地址

三、栈的分类

ARM 栈分为四种类型:满减栈、满增栈、空增栈、空减栈。

  • 增栈:入栈时,sp 指针往上走
  • 减栈:入栈时,sp 指针往下走
  • 空栈:先写数据,再挪指针
  • 满栈:先挪指针,再写数据

ARM 默认使用满减栈(*–sp)

1. 栈空间分配

栈大小
sp_svc 1K 栈(1024)
sp_user 2048
sp_irq

2. 入栈与出栈指令

STMFD:入栈保护

STMFD<c> <Rn>{!}, <registers>
示例:stmfd sp!, <r0,r1,lr>

LDMFD:出栈恢复

LDMFD<c> <Rn>{!}, <registers>
示例:ldmfd sp!, <r0,r1,lr>

示例:

ldr pc, =_main

_asm_max
mov r0, #20
mov r1, #10
;入栈保护
stmfd sp!,{r0,r1,lr}
bl _asm_min
;出栈恢复
ldmfd sp!,{r0,r1,lr}
cmp r0, r1
movgt r2, r0
movlt r2, r1
bx lr ;mov pc, lr
_asm_min
mov r0, #4
mov r1, #3
cmp r0, r1
movlt r2, r0
movgt r2, r1
bx lr
_main
ldr sp, =0x40001000
stmfd sp!,{r0-r12,lr}
bl _asm_max
ldmfd sp!,{r0-r12,lr}
mov r2, #10
mov r3, #11

finished
b finished

四、汇编与 C 语言混合编程

1. 汇编调用 C 函数

汇编如何向 C 语言传参:

  • 当参数小于等于 4 个时,通过通用寄存器进行传参(R0 – R3)
  • 当参数超过 4 个时,必须通过栈进行传参(入栈保护、出栈恢复)

C 语言将函数运行结果返回给 R0 寄存器。

2. C 调用汇编函数

  • C 语言向汇编函数传参,参数会被保存到通用寄存器。
  • 汇编函数的结果通过 R0 寄存器返回给 C 语言。

示例:调用C语言函数找最大值

main.c代码
int c_max(int a, int b)
{
return a > b ? a : b;
}

int main(void)
{

return 0;
}

汇编:
area reset, code, readonly
code32
preserve8
entry

ldr pc, =_main

_asm_max
mov r0, #20
mov r1, #10
;入栈保护
stmfd sp!,{r0,r1,lr}
bl _asm_min
;出栈恢复
ldmfd sp!,{r0,r1,lr}
cmp r0, r1
movgt r2, r0
movlt r2, r1
bx lr ;mov pc, lr
_asm_min
mov r0, #4
mov r1, #3
cmp r0, r1
movlt r2, r0
movgt r2, r1
bx lr
_main
ldr sp, =0x40001000
;import c_add
import c_max
mov r0, #2
mov r1, #3
;bl c_add
stmfd sp!,{r0,r1,lr}
bl c_max
ldmfd sp!,{r0,r1,lr}
下面是调用汇编
;stmfd sp!,{r0-r12,lr}
;bl _asm_max
;ldmfd sp!,{r0-r12,lr}
;mov r2, #10
;mov r3, #11

finished
b finished

end

五、处理器工作模式切换(USER 模式)

切换处理器工作模式为 USER 模式的步骤:

  • 将 cpsr 寄存器中的值读到 R0 寄存器。
  • 将 R0 寄存器中的值修改为 10000。
  • 将 R0 寄存器中的值写回到 cpsr 寄存器。

MRS 与 MSR 指令

MRS 指令:读取某个特殊寄存器(cpsr)的值到通用寄存器

MRS<c> <Rd>, <spec_reg>

MSR 指令:将通用寄存器中的值写入到特殊寄存器(cpsr)

MSR<c> <spec_reg>, <Rn>

示例:

;SVC stack
ldr sp, =0x40001000
;切换工作模式:USER模式
mrs r0, cpsr
bic r0, r0, #0x1F
orr r0, r0, #0x10
msr cpsr_c, r0

;USER stack
ldr sp, =0x40001000
sub sp, sp, #1024

import main
b main

赞(0)
未经允许不得转载:171主机测评 » ARM嵌入式开发 --- 汇编基础
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址