嵌入式系统入门篇2之keil启动文件详解

嵌入式系统入门,下面的启动文件是较简单的版本,结合网上搜索到的资料和老师课堂上的讲解整理出来的,如有错误欢迎指出。

1.汇编指令类别

  • ARM汇编指令(ARM公司定):一条汇编指令唯一对应一条机器指令 RISC
    MOV R0, #5 => 010101…

    • 操作码: 表示是何种操作 “指令助记符”
      MOV 表示 移动
      ADD 表示 加法

    • 操作数:

      • 结果操作数: 用来保存计算的结果(目的操作数)R0
      • 运算操作数:第一操作数 R1、第二操作数 R2

      ADD R0, R1, R2

      指令是用来“运算”:运算符 操作数

  • 伪指令(由编译器厂商定的,keil环境下有keil的伪指令,gnu环境有gnu的伪指令)

      keil环境下常用的伪指令:
      	数据:
      		int a = 5;
      		int b[10];
      		a				//顶格写的 标号,标号表示一个地址
      			DCD 5		//DCD X : 分配4bytes空间并且把X的值,填入此处
      					
      		b
      			SPACE 40   //SPACE X : 在此处开辟 X 个字节的空间,内容不定。
      			int c[10] = {2,3};
      		c
      			DCD 2
      			DCD 3
      			SPACE 8*4				
      		
      	ps: DCW分配2bytes空间
      	
      	"段":分区域
      		代码段 这个区域都是代码
      		数据段 这个区域存放的是全局的数据
      		堆栈段 stack
      		...
      		
      keil下面如何定义“段”呢?
      AREA 段名1, 段的属性1, 段的属性2, ...
      eg:AREA  mstack, DATA, READWRITE
      eg:AREA RESET , DATA, READONLY					
      段名:自己定义,规定的定义
    
      	段的属性1:
      		CODE  代码段
      		CODE32  thumb-2代码
      		DATA  数据段
      			
      		属性2:
      		READWRITE   只读可写				
      		READONLY  只读
      		
      		属性3:
      		ALIGN=3   8字节对齐(2的3次幂对齐)
      		Cortex-M4要求代码必须是 8字节对齐,否则编译不会通过;	
      		PRESERVE8 指令,表示后续如果没有特殊说明,都是采用 8字节对齐当有C语言和汇编并存时,C语言的编译后的指令也需要8字节对齐,这时这条指令可以实现。
    
  • 宏指令(同上,由编译器厂商指定)由编译器厂商写的)keil环境下有keil的宏指令,宏指令在keil中需要顶格。
    eg:stack_size EQU 0x200 ; // #define stack_size 0x200

keil环境下的汇编语句格式

label
mnemonic operand1, operand2, … ; Comments

lable:顶格写,标识这一行的地址(The “label” is used as a reference to an address location. It is optional; some instructions might have a label in front of them so that the address of the address of the instruction can be obtained by using the label. Labels can also be used to reference data addresses.) 嗯…我就不翻译了,这是Cortex M3与M4数据手册的原文。
指令:ARM指令,伪指令
; (分号是注释)到行末,都是注释部分。

2.ARM指令的格式

ARM指令的基本格式:< opcode >{< cond >}{S} < Rd >, < operand1> {, < operand2 >}
<>内的必须要的 {} 可选的

  • opcode: operator code 操作码,指令助记符,表示哪条指令
    如:MOV、ADD、SUB、LDR/STR…

  • cond: condition 条件。该指令执行的条件,如果省略不写,则表示该条件指令无条件执行(必须执行)。

    例如:
    if (r1== r2) { r0 = 250; // MOV R0, #250 }
    => CMP R1, R2;
    => if R1== R2 , R1 - R2结果为0, xPSR.Z== 1
    //MOV(xPSR.Z== 1) R0, #250
    MOVEQ R0, #250
    EQ “equal” => 检查 xPSR.Z== 1
    EQ => 条件

    条件码    含义              测试的标志(xPSR中的标志位)
    EQ     Equal(相等)               Z== 1
    NE    Not Equal(不相等)              Z== 0
    CS/HS   Carry Set (C== 1)
          unsigned Higher or Same          C== 1
          >=
          a - b无须借位。=> a >= b
          “无须借位” 表示做减法时不需要借位
          C== 1表示在做减法时,没有借位。
    CC/LO   Carry Clear(C== 0)             C== 0
          unsigned Lower
          <
          a < b
          a - b就需要借位(C== 0)
    MI     MInous(负数)               N== 1
          a < b
          a - b < 0(N== 1)
    PL     Positive or Zero(非负数)           N== 0
          a >= b
    VS     V Set(溢出)                  V== 1
    VC     V Clear(没溢出)                 V== 0
    HI     unsigned HIgher            (C== 1) && (Z== 0)
          a > b
          a - b > 0
          a - b 没有借位(C== 1)并且结果不为0(Z== 0)
    LS     unsigned Lower or Same        (C== 0) || (Z== 1)
          a <= b
          a - b <= 0
    GE     signed Greater or Equql            N== V
          >=
    LT     Less Than                  N != V
          <
    GT     Greater Than              (N== V) && (Z== 0)
          >
    LE     Less than or Equal            (Z==1) ||(N != V)

  • S: Status 表示该指令执行结果是否影响xPSR(程序状态寄存器)的标志位

    如:
    MOV R0, #0; -> 不会影响任何状态标志
    MOVS R0, #0 ; ->会影响状态标志位
    有一些指令如: CMP, CMN, TEQ … 这些不加S也影响状态标志位,因为这些指令,不保存运算结果,只影响状态标志位。

  • Rd: Register Desatiation 目标寄存器,用来保存运算的结果。

  • operand1: 第一个操作数

  • operand2: 第2个操作数(有些指令没有第二个操作数).

    操作数有如下形式:
    (1) #immer_8r 立即数(常量表达式):立即数 -> 常数(不是所有的常数都可以成为"立即数")
    整个指令,只有32bits,这32bits需要保存指令助记符,目标寄存器的编号,第一个操作数的编号,剩下的几个bits是用来保存第二个操作数,bit位数非常有限,所以立即数的生成是有要求的。
    ADD R0, R1, #250
    ADD R0, R1, #0x10
    ADD R0, R1, #(1 << 3) | (1 <<4)
    为了避免立即数不合规,建议大家这样用:LDR R0, =0x20001000(伪指令,可以放数值可以放地址)
    (2) Rm 寄存器,操作数可以是一个寄存器。
    如:
    ADD R0, R1, R2; (R1 + R2 -> R0)
    (3) Rm,shift 寄存器移位方式,操作数可以是一个寄存器加移位方式
    算术移位符号参与,逻辑移位无符号
    算术右移:右边的低位直接干掉,左边全部补符号位。
    逻辑移位:无论是左移还是右移全部补0.
    LSL #n(Logic Shift Left 逻辑左移n位)
    Logic逻辑移位,无论是左移还是右移,空出的位,都补0
    LSR #n(Logic Shift Right 逻辑右移n位)
    在移出的位为0的情况下,
    LSL #n 就相当于在原寄存器值 乘以 2的n次方
    LSR #n 就相当于在原寄存器值 除以 2的n次方
    ASR #n 算术右移n位
    算术移位,不应该改变它的符号,最高n位补符号位的值。
    ASL #n 没有。 => LSL
    ROR #n:ROtate Right 循环右移,把右边移的n位,补到最左边。
    RRX 带扩展的循环右移1位,带C(xPSR.C) 那个位
    type Rs
    type为上述移位方式的一种,如: LSL, LSR,ASR, ROR, RRX…
    Rs偏移量寄存器,低8位有效,要移的bit位数保存在Rs中。

    代码测试:
      (1) MOV R1,#3 ;r1== 3
       MOV R2,#1 ;r2== 1
       LSL R2,R1 ;r2== 8 1<<3
      (2) ADD R0, R1, R2, LSR #2; (2)和(1)组合测试
       r1== 3
       r2== 8
       r0== 5
    思考:3 x 22在汇编中如何表示?将结果放在R1寄存器中(用最少的寄存器和空间)
    分析:3 x 22 = 3 x 16 + 3 x 4 + 3 x 2 => 3<<4 + 3<<2 + 3 << 1
    至少要用两个寄存器(一个用于存放3,另一个用来存放中间结果和最终结果)和16字节的空间(0x08000018-0x08000008)
    MOV R0,#3
    LSL R1,R0, #1
    ADD R1,R1,R0,LSL #2
    ADD R1,R1,R0,LSL #4
    贴图~~
    在这里插入图片描述

3.启动文件详解

根据启动文件从上至下编写顺序可以分为以下五个典型部分:

  1. 堆栈空间定义;
  2. 存放中断向量表;
  3. 复位中断函数(Reset_Handler);
  4. 其它中断异常服务函数,以及弱[WEAK]声明;
  5. 将堆栈地址传递给库函数,利用库函数初始化堆栈,和库函数自身初始化。

其中启动文件必需的部分为堆栈段、中断向量表及代码段。本文解释的启动文件也仅包含这三部分。

小贴士:

  1. 汇编中分号用于注释
  2. 指令不能顶格写
stack_size EQU  0x200	

这是简单的宏定义,定义栈的大小为0x200即512个字节

; define stack
	AREA mystack, DATA, READWRITE
stack_start
	SPACE stack_size
stack_end
	PRESERVE8    

AREA关键字定义名为mystack的可读可写数据段(栈显然放在数据节中)
SPACE关键字申请stack_size大小的栈空间
PRESERVE8:指定当前文件保持堆栈8字节对齐

;define vectors
	AREA RESET, DATA, READONLY
vectors
	DCD  stack_end
	DCD  test_start
vectors_end

AREA关键字定义名为RESET的只读数据段(只能为RESET,因为某文件里已写死,编译的时候会找这个)
DCD stack_end ;中断向量表的第一个元素,必须为堆栈栈顶的地址 stack_end
DCD test_start ;中断向量表的第二个元素,开机复位后执行的第一条指令的地址

;define code
	AREA mycode, CODE, READONLY,ALIGN=3

AREA关键字定义名为mycode的只读代码段
ALIGN=3 ;在2^3(8字节)边界上对齐

test_start

	;这里写入汇编指令
	
	B .		;while(1)
	END

完整启动代码如下:

stack_size EQU  0x200	
; define stack
	AREA mystack, DATA, READWRITE
stack_start
	SPACE stack_size
stack_end
	PRESERVE8 
	
;define vectors
	AREA RESET, DATA, READONLY
vectors
	DCD  stack_end
	DCD  test_start
vectors_end

;define code
	AREA mycode, CODE, READONLY,ALIGN=3
	
test_start

	;这里写入汇编指令
	
	B .		;while(1)
	END
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值