ARM 裸机开发(2)——ARM 汇编与启动代码

ARM 裸机开发(2)------ARM 汇编与启动代码

学汇编的直接目的,是为了写启动代码。启动代码要在处理器复位以后,把 C 语言运行所必需的环境搭起来,再引导程序进入 main 函数。

一、启动代码要做的五件事

  1. 初始化异常向量表。
  2. 初始化各工作模式的栈指针寄存器。
  3. 开启 ARM 内核中断允许。
  4. 将工作模式切换为 User 模式。
  5. 引导程序进入 C 语言主函数。

后面学的指令,基本都是围绕这五件事展开的。

二、汇编程序的基本格式

汇编文件里同时存在两类东西:指令和伪操作。指令是 CPU 真正执行的,比如 MOV、ADD;伪操作是写给汇编器看的,用来指导汇编器怎么组织代码。

常见的格式是 AREA RESET, CODE, READONLY,加 CODE32 和 ENTRY,最后 END 结束。

AREA 是最重要的伪操作,用来定义一个段,程序、数据、堆栈都要被组织到不同的段里。RESET 是这个段的名字,命名成 RESET 有暗示性,通常表示复位向量段,也就是上电后最先执行的位置。CODE 表示这个段是可执行代码,READONLY 表示只读。

CODE32 表示后续指令使用 32 位 ARM 指令集,换成 THUMB 就是 16 位 Thumb 指令集。

三、数据传送和算术运算

MOV 除了直接送立即数或寄存器,还能带移位,比如 MOV r6, r0, LSL #31。要注意 S 后缀决定是否更新 CPSR 里的条件标志位,移位量既可以是立即数也可以是寄存器,范围 0 到 31。RRX 是带扩展的右移,不需要给出移位量。

ADD 和 SUB 的第二操作数有三种形式:立即数、寄存器加移位、移位量也是寄存器。为什么没有 ADD r0, #3, #2 这种写法?因为 C 语言里的 1 加 2 在编译阶段就算完了,不需要留到机器指令里算。

这里要重点理解 12 位立即数。ARM 指令是 32 位定长,不可能把任意 32 位数都塞进去,所以第二操作数的立即数只给 12 位:4 位循环右移量加 8 位有效数。判断标准是把数写成二进制,看能不能通过偶数位的循环右移让高 24 位全为 0、低 8 位有效。所以给栈指针赋值 MOV sp, #0x40001000 会报错,得改成 LDR sp, =0x40001000,让汇编器把它放到常量池里。

四、位操作指令

这三个是改寄存器某一位的常用工具:

  1. BIC 是指定位清零,比如 BIC r2, r0, r1, LSL #31 把第 31 位清掉。
  2. ORR 是指定位置一。
  3. MVN 是按位取反后传送。

用 BIC 和 ORR 配合,就能在不影响其他位的前提下修改某一位,这是后面操作外设寄存器的基础。

五、条件标志位和比较跳转

CPSR 里有四个条件标志位。N 是符号标志,结果最高位为 1 时置位,按有符号解释就是负数。Z 是零值标志,结果为 0 时置位。C 是进位标志,无符号运算时用,加法有进位或者减法没借位就是 1。V 是溢出标志,有符号运算时用,超出范围就置位。

常用条件码有 EQ 相等、NE 不等、GE 有符号大于等于、GT 有符号大于、LE 和 LT 反之,AL 是无条件执行。

CMP 本质就是 SUBS,只算标志位不保存结果,所以 CMP 后面紧跟的 MOVGE、MOVLT 就能实现取最大值的逻辑。

跳转有三条指令,区别必须清楚:

  1. B 直接跳转,不保存返回地址。
  2. BL 跳转前把下一条指令的地址存进 LR,用于函数调用。
  3. BX 跳转到寄存器指向的地址,BX lr 就是函数返回。

六、循环和函数调用

循环的三要素是结束条件、推动循环趋向终结的语句、循环体。C 的 do...while 和 while 翻译成汇编只差一个判断位置,do...while 先执行后判断,while 先判断后执行。

函数调用的本质是 PC 和 LR 的互换。但嵌套调用时 LR 会被内层函数覆盖,返回就乱了,所以调用前要把寄存器压栈、返回后再弹栈恢复,这就是保护现场和恢复现场。

栈有四种形态:空或满,增或减。ARM 常用满减栈,也就是先让 SP 减再存数据。对应的指令是 STMFD 压栈、LDMFD 弹栈,感叹号表示栈指针随之更新。弹栈时把 LR 直接弹给 PC,就省掉了单独一次 BX lr。

七、汇编和 C 互相调用

汇编调 C,要声明 extern,Keil 下还要写 IMPORT。最常见的问题是报 L6238E 错误,提示栈没保持 8 字节对齐,加一条 PRESERVE8 伪指令就能解决。

C 调汇编则反过来,汇编里用 EXPORT 导出函数名,C 里用 extern 声明就能调。

参数传递用 R0 到 R3 传前四个,超出的参数压栈。

八、修改工作模式

CPSR 的 M 域决定当前工作模式,用 MRS 读、MSR 写。流程是先读出来,用 BIC 清掉 M 域,再用 ORR 写进目标模式值,最后写回去。

重点总结

  1. 学汇编是为了写启动代码,启动代码的目标只有一个:让 C 程序能跑起来。
  2. 伪操作不是指令,是给汇编器看的,AREA 和 CODE32 都属于这一类。
  3. 12 位立即数是 4 位循环右移量加 8 位有效数,判断时看能否经偶数位右移后高 24 位为 0。
  4. CMP 就是 SUBS,只更新标志位;B 不保存返回地址,BL 保存到 LR,BX 跳寄存器。
  5. 嵌套调用必须保护现场,ARM 用满减栈,STMFD 和 LDMFD 成对出现。
相关推荐
soso196816 小时前
Modbus协议开发实战指南
汇编
女神下凡1 天前
芯参谋(77):裸机(无 OS)存储驱动开发指南
arm开发·单片机·嵌入式硬件·设计规范·1024程序员节
女神下凡1 天前
芯参谋(76):电源监控复位芯片电路设计指南
arm开发·单片机·嵌入式硬件·fpga开发·设计规范
陌上花开缓缓归以1 天前
liteos 内存分布
arm开发
水饺编程2 天前
第2章:Linux 基础,解压缩【.tar.gz】压缩包
linux·c语言·汇编
u0109053592 天前
(永久免费)树莓派 / 香橙派等开发板,安装神卓互联巴比达内网穿透安装教程(Linux ARM 版 V9.4.1)
linux·运维·arm开发
打工仔折腾 AI2 天前
后端部署避坑:吃透IPv4地址、网络接口与CIDR前缀的实际应用
arm开发·人工智能·后端·python·性能优化
水饺编程3 天前
第2章:Linux 基础,exit 命令
linux·c语言·汇编
水饺编程3 天前
第2章:Linux 基础,删除用户
linux·c语言·汇编