Linux嵌入式学习之Ubuntu入门(五)汇编语法学习

系列文章目录

一、Linux嵌入式学习之Ubuntu入门(一)基本命令、软件安装及文件结构

二、Linux嵌入式学习之Ubuntu入门(二)磁盘文件介绍及分区、格式化等

三、Linux嵌入式学习之Ubuntu入门(三)用户、用户组及文件权限

四、Linux嵌入式学习之Ubuntu入门(四)Makefile


文章目录

  • 系列文章目录
  • 前言
  • GNU汇编语法
    • 基本格式
    • 段定义
    • 伪操作
    • 函数
    • 常用的汇编指令
      • 内部数据传输
      • 存储器访问
        • LDR
        • STR
        • 压栈和出栈指令
        • 跳转指令
            • B指令
            • BL指令
        • 算术运算指令
        • 逻辑运算指令


前言

嵌入式的汇编涉及到堆栈,SP指针,以及DDR初始化。

GNU汇编语法

学stm32的时候有一个startup_stm32f10x_hd.s的启动文件,在keil MDK和IAR两个软件下,语法有不同。

ARM汇编编译使用的是GCC编译器,要符合GNU语法。

基本格式

格式:

label: instruction @ comment
  • label 即标号,表示地址位置,可以通过这个标号得到指令的地址。任何以“:”结尾的标识符都会被识别为一个标号。
  • instruction 即指令,也就是汇编指令或伪指令。
  • @符号,表示后面的是注释,也可以使用“/ * ”和 “ * / ”来注释。
  • comment 是注释内容。

指令、伪指令、伪操作、寄存器名可以大写或小写,但不能混用

段定义

系统预定义了一些段名:

  • .text 表示代码段。
  • .data 初始化的数据段。
  • .bss 未初始化的数据段。
  • .rodata 只读数据段。

自己使用.section伪操作定义:

.section .testsection @定义一个 testsetcion 段

伪操作

  • .byte 定义单字节数据,比如.byte 0x12。
  • .short 定义双字节数据,比如.short 0x1234。
  • .long 定义一个 4 字节数据,比如.long 0x12345678。
  • .equ 赋值语句,比如.equ num, 0x12,表示 num=0x12。
  • .align 数据字节对齐,比如: align 4 表示 4 字节对齐。
  • .end 表示源文件结束。
  • .global 定义一个全局符号,比如: .global _start。

例如:

.global _start

_start:
	ldr r0, =0x12 @r0=0x12

.global 是伪操作,表示_start 是一个全局标号,类似 C 语言里面的全局变量

函数

GNU 汇编同样也支持函数,其中返回语句不是必须的。

函数名:
	函数体
	返回语句

例如:

/* 未定义中断 */
Undefined_Handler:
	ldr r0, =Undefined_Handler
	bx r0   @返回指令
/* SVC 中断 */
SVC_Handler:
	ldr r0, =SVC_Handler
	bx r0
/* 预取终止中断 */
PrefAbort_Handler:
	ldr r0, =PrefAbort_Handler
	bx r0

常用的汇编指令

内部数据传输

指令 含义
MOV R0, R1 将寄存器 R1 中的数据传递给 R0
MOV R0, #0X12 将立即数 0X12 传递给 R0 寄存器
MRS R0, CPSR 将特殊寄存器 CPSR 里面的数据传递给 R0(MRS 指令用于将特殊寄存器(如 CPSR 和 SPSR)中的数据传递)(读特殊寄存器)
MSR CPSR, R0 MSR 指令用来将普通寄存器的数据传递给特殊寄存器(写特殊寄存器)

存储器访问

I.MX6UL 中的寄存器就是 RAM 类型的,用汇编来配置 I.MX6UL 寄存器的时需先将要配置的值写入到 Rx(x=0~12)寄存器中,然后将 Rx 中的数据写入到 I.MX6UL 寄存器中。读取寄存器过程相反。

LDR

从存储加载数据到寄存器 Rx ,或者将一个立即数加载,加载立即数时用“=”

如 I.MX6UL 有个寄存器 GPIO1_GDIR,其地址为 0X0209C004,

1 LDR R0, =0X0209C004 @ R0=0X0209C004
2 LDR R1, [R0] @ 读取地址 0X0209C004 中的数据到 R1 寄存器中

STR

STR 就是将数据写入到存储器中

例如:
配置寄存器 GPIO1_GDIR 的值为 0X20000002

1 LDR R0, =0X0209C004 @ R0=0X0209C004
2 LDR R1, =0X20000002 @ R1=0X20000002
3 STR R1, [R0] @将 R1 的值写入到 R0 所保存的地址中

LDR 和 STR 都是按照字进行读取和写入的,在指令“LDR”后面加上 B 或 H,比如按字节操作的指令就是 LDRB 和STRB,按半字操作的指令就是 LDRH 和 STRH。

指令 含义
LDR Rd, [Rn , #offset] 从存储器 Rn+offset 的位置读取数据存放到 Rd 中
STR Rd, [Rn, #offset] 将 Rd 写入到存储器中的 Rn+offset 位置

压栈和出栈指令

中断和跳转时要保存当前状态:现场保护。与之对应还有恢复现场。(就是保存 R0~R15 这些寄存器值)
压栈的指令为 PUSH,出栈的指令为 POP, 可一次操作多个寄存器数据,利用当前的栈指针 SP 来生成地址。

如:压栈前SP地址指向 0X80000000

PUSH {R0~R3, R12} @将 R0~R3 和 R12 压栈
PUSH {LR} @将 LR 进行压栈

Linux嵌入式学习之Ubuntu入门(五)汇编语法学习_第1张图片
(0x80000000指向R12,0x7FFFFFFC指向R0)

出栈:

POP {LR} @先恢复 LR
POP {R0~R3,R12} @在恢复 R0~R3,R12

PUSH 和 POP 的另外一种写法是“STMFD SP!”和“LDMFD SP!”

STMFD SP!,{R0~R3, R12} @R0~R3,R12 入栈
STMFD SP!,{LR} @LR 入栈

LDMFD SP!, {LR} @先恢复 LR
LDMFD SP!, {R0~R3, R12} @再恢复 R0~R3, R12

STM和LDM是多存储和多加载,前面 LDR 和 STR每次只能读写存储器中的一个数据。
FD 是 Full Descending 的缩写,即满递减的意思:SP 指向最后一个入栈的数值,堆栈是由高地址向下增长的,编号小的对应低地址,编号高的对应高地址。

跳转指令

可以用指令或者直接向PC寄存器中写入数据。

指令 描述
B < label> 跳转到 label,如果超过了+/-2KB,可以指定 B.W< label>使用 32 位的跳转指令
BX < Rm> 间接跳转,跳转到存放于 Rm 中的地址处,并且切换指令集
BL < label> 跳转到标号地址,并将返回地址保存在 LR 中。
BLX < Rm> 结合 BX 和 BL 的特点,跳转到 Rm 指定的地址,并将返回地址保存在 LR 中,切换指令集。
B指令

调用的函数不会再返回到原来的执行处

_start:

ldr sp,=0X80200000 @设置栈指针
b main @跳转到 main 函数
BL指令

跳转之前会在寄存器 LR(R14)中保存当前 PC 寄存器值,所以可以通过将 LR 寄存器中的值重新加载到 PC 中来继续从跳转之前的代码处运行。

push {r0, r1} @保存 r0,r1
cps #0x13 @进入 SVC 模式,允许其他中断再次进去

bl system_irqhandler @加载 C 语言中断处理函数到 r2 寄存器中

cps #0x12 @进入 IRQ 模式
pop {r0, r1}
str r0, [r1, #0X10] @中断执行完成,写 EOIR

第 5 行就是执行 C 语言版的中断处理函数,当处理完成以后返回来继续执行下面的程序,所以使用了 BL 指令。

算术运算指令

Linux嵌入式学习之Ubuntu入门(五)汇编语法学习_第2张图片
(详细见正点原子的驱动开发指南)

逻辑运算指令

Linux嵌入式学习之Ubuntu入门(五)汇编语法学习_第3张图片

你可能感兴趣的:(嵌入式Linux,linux,学习,ubuntu)