嵌入式系统复习–Thumb指令集
ADS/SDT IDE开发环境:它由ARM公司开发,使用了CodeWarrior公司的编译器
一般的:集成了GNU开发工具的IDE开发环境:它由GNU的汇编器as、交叉编译器gcc、和链接器ld等组成
常用伪操作可分为:
定义全局变量
伪操作 | 语法格式 | 作用 |
---|---|---|
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 | 分配一段字节 的内存单元,用字节的方式放expr |
DCD | {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]} | 告诉编译器当前符号不是在本源文件中定义的,而是在其他文件中,而在本文件中可能使用 |
常用伪操作为:
常量编译控制伪操作
伪操作 | 语法格式 | 作用 |
---|---|---|
.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 string | 打印信息到标准输出 |
伪操作 | 语法格式 | 作用 |
---|---|---|
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: A
A + B ;
A - B;
A x B;
A / B;
A :MOD: B;
A :ROL: B ;将整数A循环左移B位
A :ROR: B;将整数A循环右移B位
A :SHL: B ;将整数A左移B位空位补0
A :SHR: B;将整数A右移B位空位补0
A :AND: B ;按位与操作
A :OR: B;按位或操作
A :EOR: B;按位异或操作
: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
...
应用实例:
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语言程序和汇编程序能够相互调用,定义了同一的函数过程调用标准ATPCS
ATPCS规则:
循环时使用i --
可以省一些汇编指令,更有效率
未完待续