嵌入式系统复习--基于ARM的嵌入式程序设计
上一篇
编译环境
ADS/SDT IDE开发环境:它由ARM公司开发,使用了CodeWarrior公司的编译器
一般的:集成了GNU开发工具的IDE开发环境:它由GNU的汇编器as、交叉编译器gcc、和链接器ld等组成
- 伪操作:ARM汇编语言程序里的特殊指令助记符,主要作用是完成汇编程序各种准备工作,在源程序进行编译时由汇编程序处理,而不是在计算机运行期间执行
- 宏指令:是一段独立的代码、可插在源程序中,它通过伪操作来定义。通过实际指令替代宏体实现相关的一段代码
- 伪指令:ARM汇编语言里的特殊指令助记符,不在处理器运行期间由机器执行。它们在编译时将被合适的机器指令替代
ADS编译环境下的伪操作
常用伪操作可分为:
- 符号定义伪操作
-
定义全局变量
伪操作 语法格式 作用 GBLA GBLA Variable 声明一个全局的算术变量,初始化为0 GBLL GBLL Variable 声明一个全局的逻辑变量,初始化为false GBLS GBLS Variable 声明一个全局字符串变量,初始化为空串 -
定义局部变量
伪操作 语法格式 作用 LCLA LCLA Variable 声明一个局部的算术变量,初始化为0 LCLL LCLL Variable 声明一个局部的逻辑变量,初始化为false LCLS LCLS Variable 声明一个局部字符串变量,初始化为空串 -
为变量赋值
伪操作 语法格式 作用 SETA Variable SETA expr 给一个算术变量赋值 SETL Variable SETL expr 给一个逻辑变量赋值 SETS Variable SETS expr 给一个字符串赋值 -
给一串寄存器命名
伪操作 语法格式 作用 RLIST name RLIST {list of registers} 为一个通用寄存器列表定义名称
-
- 数据定义伪操作
- 为一个变量分配内存地址
伪操作 语法格式 作用 DCB {label} DCB expr 分配一段 字节
的内存单元,用字节的方式放exprDCD {label} DCD expr 分配一段 字
的内存单元,用字的方式放expr - 定义数据块的起始地址
伪操作 语法格式 作用 LTORG LTORG 声明一个数据缓冲池的开始(说明此指令后面不是代码,而是数据了)
- 为一个变量分配内存地址
- 汇编控制伪操作
- 条件
IF 逻辑表达式 { ELSE } ENDIF
- 循环
WHILE 逻辑表达式 ... WEND
- 宏定义
类似子程序调用,但是他不是跳转到子程序,而是直接将宏定义的代码,直接复制到宏名称那里MACRO {$label} macroname {$parameter} MEND
- 条件
- 信息报告伪操作
在程序运行不同阶段可以设置让他弹出信息伪操作 语法格式 作用 OPT OPT n 通过OPT可以在源程序中设置列表选项 - 其他伪操作
- 告诉编译器要选择的指令
伪操作 语法格式 作用 CODE 16 CODE 16 告诉编译器后面是Thumb指令 CODE 32 CODE 32 告诉编译器后面是ARM指令 - 定义常量(给变量或标号赋值)
伪操作 语法格式 作用 EQU name EQU expr 将name赋值为expr - 程序结构指令
伪操作 语法格式 作用 AREA AREA sectioname {, attr} 定义一个代码段或数据段 ENTRY ENTRY 指定程序的入口点 END END 告诉编译器已经到了源程序结尾 - 混合调用用到的指令
伪操作 语法格式 作用 EXPORT/GLOBAL EXPORT symbol {[WEAK]} 声明一个文件可以被其他文件引用,相当于声明以个全局变量 IMPORT/EXTERN IMPORT/EXTERN symbol {[WEAK]} 告诉编译器当前符号不是在本源文件中定义的,而是在其他文件中,而在本文件中可能使用
- 告诉编译器要选择的指令
GNU编译环境下的伪操作
常用伪操作为:
-
常量编译控制伪操作
伪操作 语法格式 作用 .byte .byte expr 分配一段字节空间,并用字节的方式放入expr .hword/ .short .hword expr 分配一段半字节空间,并用半字的方式放入expr .ascii .ascii expr 定义字符串expr(非零结束符) .asciz/.string .asciz 定义字符串(以 /0
为结束符).word .word expr 分配一段字内存空间 -
汇编程序代码控制伪操作
伪操作 语法格式 作用 .section .section expr 定义域中包含的段 .text .text{subsection} 代码 .data .data {subsection} 数据 .code 16 .code 16 表明后面是Thumb指令集 .code 32 .code 32 表明后面是ARM指令集 .end .end 结束标识符 .include .include “filename” 将一个源文件包含到当前源文件中 -
宏及条件编译控制伪操作
.macro以及.endm
.macro condition .endm
.ifdef, .else及.endif
.ifdef condition .else .endif
-
其他伪操作
伪操作 语法格式 作用 .print .print string 打印信息到标准输出
ARM汇编语言的伪指令
伪操作 | 语法格式 | 作用 |
---|---|---|
ADR | ADR {cond} register, expr | 将基于PC或基址寄存器的地址读到寄存器中,小范围的地址读取 |
ADRL | ADRL {cond} register,expr | 将基于PC或基址寄存器的地址读到寄存器中,中等范围的地址读取 |
LDR | LDR {cond} register | 将一个32位的立即数或一个地址值读取到寄存器中,大范围地址读取 |
NOP | NOP | 汇编是替换成ARM的空指令(占用一个操作时间但什么到不做) |
汇编语言程序设计
-
文件格式
ARM源程序文件可以有任意一种纯文本
文件编写程序代码
文件格式对应与源程序文件 文件名 说明 汇编程序文件 *.S 用ARM汇编语言编写的ARM程序或Thumb程序 C程序文件 *.C 用C语言编写的程序代码 头文件 *.H 通常将常量命名、宏定义、数据结构定义等放在头文件中 -
ARM汇编语言语句格式如下:
{符号} {指令 | 伪操作 | 伪指令} {; 注释}
;为注释符
-
符号可以代表地址、变量和数字常量
命名规则:符号由大小写字母、数字以及下划线组成(区分大小写)
局部标号以数字开头,其他符号都不能以数字开头 -
变量: 三种变量:数字变量、逻辑变量、串变量
-
数字常量:表示方式:十进制,十六进制,n进制
-
标号:表示程序中的指令或地址的标号,分为基于PC的标号、基于寄存器的标号、绝对地址
-
局部标号:相对当前位置
% {F|B} {A|T} N{routname}
其中:%表示引用操作、F表示只先向前搜索、B表示向后搜索、A表示编译器搜索宏的搜索嵌套层次、T表示宏的当前层次
-
相关运算操作符
- 字符串表达式相关操作符
-
LEN:返回字符串的长度
例:GBLS STR STR SETS "AAA" :LEN: STR ;LEN = 3
-
CHR: 返回一个字符的ASCII值
:CHR:A
-
STR: 将对应变量(数字量或逻辑量)的表示形式用字符串的形式表示
例:GBLA A1 A1 SETA 15 :STR: A1
-
LEFT :返回一个字符从最左端的一定长度的字串
格式:A :LEFT: B
A为源字符串;B为数字量,表示返回字符个数
例:GBLS STR1 GBLS STR2 SETS STR1 "AAAABBBB" SETS STR2 STR1 :LEFT:3 ;结果为STR2为 “AAA”
RIGHT同理,返回最右端一定长度的字串
-
CC:连接2个字符串
A :CC: B
A为第一个源字符串;B为第二个源字符串
例:GBLS STR1 GBLS STR2 STR1 SETS "AAACCC" STR2 SETS "BBB" :CC: (STR1 :LEFT: 3) ; 结果为STR2 为"BBBAAA"
-
- 数字表达式相关操作符
- NOT:按位取反
:NOT: A
- +、-、x、/及MOD: 算术操作符
A + B ; A - B; A x B; A / B; A :MOD: B;
- ROL、ROR、SHL、SHR:循环移位操作
A :ROL: B ;将整数A循环左移B位 A :ROR: B;将整数A循环右移B位 A :SHL: B ;将整数A左移B位空位补0 A :SHR: B;将整数A右移B位空位补0
- AND、OR、EOR:按位逻辑操作符
A :AND: B ;按位与操作 A :OR: B;按位或操作 A :EOR: B;按位异或操作
- NOT:按位取反
- 逻辑表达式
- 关系操作符
=、>、<、
不等于 /= 或则 <> - 逻辑操作符
:LNOT: ;A 取反 A :LAND: B;逻辑与 A :LOR: B;逻辑或 A :LEOR: B;逻辑异或
- 关系操作符
汇编语言格式
ARM汇编语言是以段(section)为单位来组织源文件的。
段是相对独立的、具有特定名称的、不可分割的指令或者数据序列。
段又可以分为代码段和数据段。一个源程序至少需要一个代码段。
例:
AREA EXAMPLE(段的名字), CODE(代码段), READONLY(只读)
ENTRY(程序入口点)
start
...
END
汇编语言程序重点
-
子程序的调用
子程序的调用通过BL指令来完成
语法格式为BL subname(被调用子程序的名称)
-
子程序的返回
在返回调用子程序时,将LR寄存器(R14)中的值拷贝回程序寄存器(R15)
当没有嵌套时SUB2 ... MOV PC, R14
当发生嵌套时, 对于在子程序中出现嵌套调用时,链接寄存器LR中的返回地址可能会在第二次调用时被覆盖,所以需要将返回地址压入堆栈来进行保存
SUB1 STMFD SP!, {R0 - R7, R14}; 保存工作寄存器和链接 BL SUB2 ... LDMFD SP!, {R0 - R7, PC}; 恢复工作寄存器并返回
-
跳转表
类似于c++中的switch
调用一系列子程序中的一个,而决定调用哪一个由程序的计算值决定。
例:BL JUMPTAB; R0存放对应的跳转信息 0表示0号程序... JUMPTAB ADR R1, SUBTAB; R1 <- SUBTAB CMP R0, #SUBMAX; 检查是否超限 LDRLS PC, [R1, R0, LSL #2]; 如果OK则跳转到表中 B ERROR; 否则发生错误 SUBTAB DCD SUB0 DCD SUB1 DCD SUB2 ... SUB MAX;散转表结束地址
-
ARM与Thumb间的状态转换
状态切换是通过一条专用的转移交换指令BX来实现的。
当Rn寄存器中的目的地址的最后一位为0时转换到ARM状态
当Rn寄存器中最后一位为1时,转换到Thumb状态
Thumb地址是半字对齐的末尾一定是0
CODE32 ADR R0, Into_Thumb + 1 BX R0 ... CODE16 Into_Thumb: ... ADR R5, BACK_TO_ARM BX R5 ... CODE32 Back_to_ARM ...
应用实例:
- 简单的ARM指令程序
- 数据块复制
AREA STRINGCOPY, CODE, READ ONLY
NUM EQU 20
CODE 32
ENTRY
start
LDR R0, =src
LDR R1, =dst
LDR R2, #NUM; 移动字符串个数
LDR SP, #0x400 ; 初始化堆栈指针
BACK; 备份
STMFD SP!, {R4-R11}; 将R4与R11中的内容提前保护
MOVS R3, R2, LSR #3; 移动次数NUM整除8的商
BEQ YUSHU; 如果不满8个字符,看是否有余数
BLOCKCOPY ; 按块移动每次移动8个字符
LDMIA R0!, {R4-R11} ;将src中8个字符块移动到R4到R11中
STMIA R1!, {R4-R11} ;将移入的字符移到dst中
SUBS R3, R3, #1
BNE BLOCKCOPY; 未把商移动完就循环
LDMFD SP!, {R4-R11}; 将保护的寄存器出栈
YUSHU
ANDS R2, R2, #7; 将R2只保留8以内的余数了,方便后边一个字符的搬运
BEQ stop
WCOPY ; 一个字符一个字符的移动
LDR R3, [R0], #4; 字对齐每次移动4
STR R3, [R1], #4
SUBS R2, R2, #1
BNE WCOPY
stop
MOV R0, #0x18
LDR R1, #0x20026
SWI 0x123456
AREA DATA1, DATE, READWRITE
src DCD 1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,1,2,3,4
dst DCD 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
END
- 利用跳转表实现程序跳转
C语言的一些技巧
-
变量定义
- 把所有相同类型的变量放在一起定义,这样可以优化存储器空间
- 局部变量使用32位int或unsinged int有时会更有效率。因为每个寄存器是32位的
- 变量定义为了精简是要避免使用冗余变量。但有时使用冗余变量可以减少存储器的访问次数,提高系统性能,冗余变量一般在寄存器,一般变量在存储器
-
参数传递
为了单独编译C语言程序和汇编程序能够相互调用,定义了同一的函数过程调用标准ATPCS
ATPCS规则:- 寄存器的使用规则
子程序通过寄存器R0 ~ R3来传递参数, 使用R4 ~ R11来保存局部变量,因此在进入子程序前要保存这些寄存器的值,返回后恢复,在Thumb中,只使用R4 ~ R7 - 数据栈的使用规则
ATPCS规定数据栈为FD(满递减)类型,并且对数据栈的操作时8字节对齐的 - 参数的传递规则
当参数固定的情况下,第一个整数参数通过寄存器R0 ~ R3来传递。其他参数通过数据栈传递
当参数个数可变的子程序:当不超过4个时用R0 ~ R3 来传递,超过4个时,使用数据栈来传递参数
结果返回是以32位为界,每多一个32就从R0 ~ R3中多增加一个来传递
- 寄存器的使用规则
-
循环时使用
i --
可以省一些汇编指令,更有效率
下一篇
未完待续
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!