https://developer.arm.com arm官网可以在上面下载有用的参考手册和开源软件,例如mail GPU驱动,arm contex, arm-linux-gcc等
https://pan.baidu.com/s/192LRhwKWwX_zkxVY_3In7A arm体系架构和接口 pdf 仅供参考
usr 用户模式 正常程序执行模式,大部分任务执行在这种模式下
fiq 快速中断 当一个高优先级(fast)中断产生时将会进入这种模式,一般用于高速数据传输和通道处理
irq 外部中断 低优先级中段
svc 特权模式
abt 数据访问终止
und 未定义指令
sys 系统运行模式
mon 监控模式 在安全和非安全之间转化
1.CPRS的四种域(32位)
f:标志域
s:状态域
x:扩展域
c:控制域
2.标志域(f)的4个状态位
N = 负数,从ALU来的数据
Z = 零,从ALU来的数据
C = ALU操作需借位或有进位
V = ALU操作出现了溢出
ARM指令集
Thumb指令集
<opcode> {<cond>} {S} <Rd> , <Rn> {,<operand2>}
<指令助记符>{<执行条件>}{是否影响CPSR的值} <地址1>, <地址2>, <地3>
条件码 条件助记符 标志 含义
0000 EQ Z=1 相等
0001 NE Z=0 不相等
0010 CS/HS C=1 无符号数大于或等于
0011 CC/LO C=0 无符号数小于
0100 MI N=1 负数
0101 PL N=0 正数或零
0110 VS V=1 溢出
0111 VC V=0 没有溢出
1000 HI C=1,Z=0 无符号数大于
1001 LS C=0,Z=1 无符号数小于或等于
1010 GE N=V 有符号数大于或等于
1011 LT N!=V 有符号数小于
1100 GT Z=0,N=V 有符号数大于
1101 LE Z=1,N!=V 有符号数小于或等于
1110 AL 任何 无条件执行 (指令默认条件)
1111 NV 任何 从不执行(不要使用)
//立即数寻址
立即数是 8bit数值移动4bit偶数位(0, 2, 4 ... 30)得到,如果不能通过这种运算得到就是非法立即数
MOV R0,#0 R0赋值为0
ADD R2,R3,#1 R3加1赋值给R2
CMP R7,#100 R7和100比较
BIC R9,R8,#0xFF00 位清零
//寄存器寻址
MOV R2,R0 R0给R2
ADD R4,R3,R2
CMP R7,R8
//寄存器移位寻址
算数右移 左边添加数和符号位相同
LSL : 逻辑左移, 高位丢弃, 低位补 0, 如果使用立即数, 则取数范围为 0 - 31
LSR : 逻辑右移, 低位丢弃, 高位补 0, 如果使用立即数, 则取数范围为 1 - 32
ASR : 算术右移, 低位丢弃, 高位补符号, 如果使用立即数, 则取数范围为 1 - 32
ROR : 循环右移, 低位补到高位, 如果使用立即数, 则取数范围为 1 - 31
RRX : 带标志位的循环右移, 每次只移一位, 先将C位移入最高位, 再将最后一位移入C位,
注意, 主指令必须带 s, 否则不会将最后一位移入C位
内存访问指令的寻址方式可以分为以下几种。
(1) 字及无符号字节的 Load/Store 指令的寻址方式。
(2) 杂类 Load/Store 指令的寻址方式。
(3) 批量 Load/Store 指令的寻址方式。
(4) 协处理器 Load/Store 指令的寻址方式。
字及无符号字节的 Load/Store 指令语法格式如下:
LDR|STR{<cond>}{B}{T} <Rd>,<addressing_mode>
B按无符号字节访问
其中,<addressing_mode>共有 9 种寻址方式,如表所示。
表 字及无符合字节的 Load/Store 指令的寻址方式
格 式 模 式
1 [Rn,#±<offset_12>] 立即数偏移寻址 LDR R0,[R1,#-4]
2 [Rn,±Rm] 寄存器偏移寻址 LDR R0,[R1,-R2]
3 [Rn,Rm,<shift>#< offset_12>] 带移位的寄存器偏移寻址 LDR R0,[R1,R2,LSL#2]
4 [Rn,#±< offset_12>]! 立即数前索引寻址 LDR R0,[R1,#4]! R1=R1+4
5 [Rn,±Rm]! 寄存器前索引寻址
6 [Rn,Rm,<shift>#< offset_12>]! 带移位的寄存器前索引寻址
7 [Rn],#±< offset_12> 立即数后索引寻址 LDR R0,[R1],#4 R1=R1+4 R0=*R1
8 [Rn],±<Rm> 寄存器后索引寻址
9 [Rn],±<Rm>,<shift>#< offset_12>带移位的寄存器后索引寻址
上表中,“!”表示完成数据传输后要更新基址寄存器
LDM/STM堆栈寻址
表 批量 Load/Store 指令的寻址方式
格 式 模 式
1 IA(Increment After) 后递增方式
2 IB(Increment Before) 先递增方式
3 DA(Decrement After) 后递减方式
4 DB(Decrement Before) 先递减方式
(1) 满递减 FD(Full Descending)。
(2) 空递减 ED(Empty Descending)。
(3) 满递增 FA(Full Ascending)。
(4) 空递增 EA(Empty Ascending)。
批量数据寻址方式 堆栈寻址方式 L 位 P 位 U 位
LDMDA LDMFA 1 0 0
LDMIA LDMFD 1 0 1
LDMDB LDMEA 1 1 0
LDMIB LDMED 1 1 1
STMDA STMED 0 0 0
STMIA STMEA 0 0 1
STMDB STMFD 0 1 0
STMIB STMFA 0 1 1
MOV 数据传送
MVN 数据取反传送
AND 逻辑与 Rd:=Rn AND op2
EOR 逻辑异或 Rd:=Rn EOR op2
SUB 减 Rd:=Rn − op2
RSB 翻转减 Rd:=op2 − Rn
ADD 加 Rd:=Rn + op2
ADC 带进位的加 Rd:=Rn + op2 + C
SBC 带进位的减 Rd:=Rn− op2 + C − 1
RSC 带进位的翻转减 Rd:=op2 − Rn + C − 1
TST 测试 Rn AND op2 并更新标志位
TEQ 测试相等 Rn EOR op2 并更新标志位
CMP 比较 Rn−op2 并更新标志位
CMN 负数比较 Rn+op2 并更新标志位
ORR 逻辑或 Rd:=Rn OR op2
BIC 位清 0 Rd:=Rn AND NOT(op2)
MOV R0,R0,LSL#3 R0左移3给R0
MOV PC,R14
MOVS PC,R14 退出调用更新标志位
//按位取反
MVN R0,#0 R0=-1
R0=0xFFFFFFFF 因为计算机按补码方式存储 求0xFFFFFFFF 源码等于 (除符号位取反加一) 100.....001 就是-1
AND R0,R0,#3 0和3位保留 其余为0
EOR 安位异或
RSB R0,R1,#0
ADC R0,R1,R2
1、分支指令 ---- B指令
该指令跳转范围限制在当前指令的±32M字节地址内(ARM指令为字对齐,最低2位地址固定为0)。
B{cond} Label
; 条数范围: -2^23 ---- +2^23-1 条
; 地址范围: -2^25 ---- +2^25-4 byte
B L1
B %f1 ;向前跳(即跳至接下来未执行的指令)
B %b2 ;向后跳(默认向后跳,即已经执行过的指令)
2、分支指令 ---- BL指令, 带链接的跳转指令
BL{cond} Label
; 条数范围: -2^23 ---- +2^23-1 条
; 地址范围: -2^25 ---- +2^25-4 byte
将bl指令的下一条指令地址保存到了lr中.该指令跳转范围限制在当前指令的±32M字节地址内(ARM指令为字对齐,最低2位地址固定为0)。
3、分支指令 ---- BX指令, 带状态切换的跳转指令
pc = Rm & 0xfffffffe, T = Rm & 1. ARM和Thumb指令切换
4、分支指令 ---- BLX指令, 带状态切换带链接的跳转指令,
MRS
ARM处理器中,只有MRS指令可以对状态寄存器CPSR和SPSR进行读操作。通过读CPSR可以了解当前处理器的工作状态。读SPSR寄存器可以了解到进入异常前的处理器状态。
指令格式如下:
MRS{cond} Rd, psr
psr: CPSR或SPSR
MSR
ARM处理器中,只有MSR指令可以对状态寄存器CPSR和SPSR进行写操作。
与MRS配合使用,可以实现对CPSR或SPSR寄存器的读-修改-写操作,
可以切换处理器模式、或者允许/禁止IRQ/FIQ中断等。
MSR指令格式1 MSR{cond} psr_fields,#immed_8r
MSR指令格式2 MSR{cond} psr_fields,Rm
psr: CPSR或SPSR
fields: 指定传送的区域,可以为以下字母(必须小写)的一个或者组合:
c 控制域屏蔽字节(psr[7..0])
x 扩展域屏蔽字节(psr[15..8])
s 状态域屏蔽字节(psr[23..16])
f 标志域屏蔽字节(psr[31..24])
immed_8r: 要传送到状态寄存器指定域的 8 位图立即数
Rm: 保存要传送到状态寄存器指定域数据的源寄存器
实例:
mrs r0, cpsr
bic r0, r0, #(1 << 7)
msr cpsr_c, r0
指令名 作用 示例
LDR 加载字数据 ldr r6, data
STR 存储字数据 str r8, [r5]
LDRB 加载字节数据 ldrb r3, [r1, r3]
STRB 存储字节数据 strb r0, [r1,#0x20]
LDRH 加载半字数据 ldrh r10, [r0], r6
STRH 存储半字数据 strh r10, [r0], r6
LDRSB 加载有符号字节数据 ldrsb r1, [lr,#0x40]
LDRSH 加载有符号半字数据 ldrsb r1, [lr,#0x40]
多寄存器加载/存储指令可以实现在一组寄存器和一块连续的内存单元之间传输数据。
LDM为加载多个寄存器;STM为存储多个寄存器。允许一条指令传送16个寄存器的任何 子集或所有寄存器。它们主要用于现场保护、数据复制、常数传递等。
指令格式如下:
LDM{cond}<模式> Rn{!},{reglist}
STM{cond}<模式> Rn{!},{reglist}
cond:指令执行的条件;
模式:控制地址的增长方式,一共有8种模式;
!:表示在操作结束后,将最后的地址写回Rn中;
reglist :表示寄存器列表,可以包含多个寄存器,它们使用","隔开,如{R1,R2,R6-R9},寄存器由小到大排列;
模式:
多寄存器加载/存储指令的8种模式如下表所示,右边四种为堆栈操作、左边四种为数据传送操作。
数据块传送操作 | 堆栈操作
模式 说明 | 模式 说明
IA 每次传送后地址加4 | FD 满递减堆栈
IB 每次传送前地址加4 | ED 空递减堆栈
DA 每次传送后地址减4 | FA 满递增堆栈
DB 每次传送前地址减4 | EA 空递增堆栈
指令格式如下:
SWP{cond}{B} Rd,Rm,[Rn]
其中,B为可选后缀,若有B,则交换字节,否则交换32位字;
Rd用于保存从存储器中读入的数据;
Rm的数据用于存储到存储器中,若Rd与Rm相同,则为寄存器与存储器内容进行交换;
Rn为要进行数据交换的存储器地址,Rn不能与Rd和Rm相同。
1.指令名 作用
CDP 协处理器数据操作指令 示例:CDP p7,0,c0,c2,c3,0
LDC 协处理器数据读取指令 示例:LDC p5,c2,[R1]
STC 协处理器数据写入指令 示例:STC p5,c1,[R0]
MCR ARM寄存器到协处理器的数据传送指令 示例:mcr p15, 0, r1, c1, c0, 0
MRC 协处理器到ARM寄存器的数据传送指令 示例:mrc p15, 0, r5, c0, c0, 0
2,实例:
mov r1, #0x12
MCR p15, 0, r1, c1, c0, 0 ; 写入到协处理器
MRC p15, 0, r0, c1, c0, 0 ; 读出到寄存器
解析:
Mrc p15,0,r1,c1,c0,0
从协处理器p15的c1和c0传输数据到ARM处理器的r1,其中操作码1为0,操作码2为0.
SWI 软件中断
SWI num
参数解析:
num: 是一个 24 位二制数, 称为中断号, 处理器不解析这个号码,
它由程序员来解析
SWI 指令是主动从用户模式进入特权模式(管理模式)的唯一方法
实例:
if 0=1
; 切换到用户模式
msr cpsr_c, #0xd0
swi 0x1234
endif
if 0=0
; 解析软件中断号
ldr r0, [lr, #-4] ; 取出中断指令的机器码
bic r0, r0, #0xff000000 ; 去掉高 8 位, 得到中断号
; movs 的目的寄存器是 pc 时, 会自动将 spsr 的值存入 cpsr
movs pc, lr
; 返回到中断的地方并恢复中断时的程序状态
nop
endif
指令名 作用
DCD 分配一段字内存单元
MACRO 宏定义的开始
MEND 宏定义的结束
ALIGN 添加补丁字节使当前位置满足一定的对齐方式
AREA 定义一个代码段或数据段.
END 汇编源文件已结束
ENTRY 程序的入口点
NOP 空操作
EXPORT 定义一个代码段或数据段.
IMPORT 汇编源文件已结束
INCLUDE 程序的入口点
常用gnu伪指令
.global _start @ 给_start外部链接属性
.section .text @ 指定当前段为代码段
.ascii .byte .short .long .word .quad .float .string @ 定义数据
.align 4 @ 以16字节对齐
.balignl 16 0xabcdefgh @ 16字节对齐填充
偶尔会用到的gnu伪指令
.end @标识文件结束
.include @ 头文件包含
.arm / .code32 @声明以下为arm指令
.thumb / .code16 @声明以下为thubm指令
最重要的几个伪指令
ldr 大范围的地址加载指令
adr 小范围的地址加载指令
adrl 中等范围的地址加载指令
nop 空操作
ARM中有一个ldr指令,还有一个ldr伪指令 一般都使用ldr伪指令而不用ldr指令
adr与ldr
adr编译时会被1条sub或add指令替代,而ldr编译时会被一条mov指令替代或者文字池方式处理;
adr总是以PC为基准来表示地址,因此指令本身和运行地址有关,可以用来检测程序当前的运行地址在哪里
ldr加载的地址和链接时给定的地址有关,由链接脚本决定
格式:
宏定义:
MACRO
宏名 参数1, 参数2, …
宏体
MEND
宏调用:
宏名 参数1, 参数2, ...
;定义无参宏
macro
my_m1
mov r0, #0x12
mend
;定义有参宏
macro
my_swap $a, $b
eor $a, $a, $b
eor $b, $a, $b
eor $a, $a, $b
mend
;带标号的宏
;从a加到b
macro
$lab my_sum $c, $a, $b
mov $c, #0 ;将c初始化为0,
$lab
add $c, $c, $a
add $a, $a, #1
cmp $a, $b
ble $lab ;若比较后不相等,则循环加
mend
.if logical-expression
Instruction
.elseif logical-expression2
Instructions
.endif
符号定义( Symbol Definition )伪指令
符号定义伪指令用于定义 ARM 汇编程序中的变量、对变量赋值以及定义寄存器的别名等操作。
常见的符号定义伪指令有如下几种:
— 用于定义全局变量的 GBLA 、 GBLL 和 GBLS 。
— 用于定义局部变量的 LCLA 、 LCLL 和 LCLS 。
— 用于对变量赋值的 SETA 、 SETL 、 SETS 。
— 为通用寄存器列表定义名称的 RLIST 。
1、 GBLA、GBLL 和GBLS
语法格式:
GBLA ( GBLL 或 GBLS )全局变量名
GBLA 、 GBLL 和 GBLS 伪指令用于定义一个 ARM 程序中的全局变量,并将其初始化。其中:
GBLA 伪指令用于定义一个全局的数字变量,并初始化为 0 ;
GBLL 伪指令用于定义一个全局的逻辑变量,并初始化为 F (假);
GBLS 伪指令用于定义一个全局的字符串变量,并初始化为空;
由于以上三条伪指令用于定义全局变量,因此在整个程序范围内变量名必须唯一。
使用示例:
GBLA Test1 ;定义一个全局的数字变量,变量名为 Test1
Test1 SETA 0xaa ;将该变量赋值为 0xaa
GBLL Test2 ;定义一个全局的逻辑变量,变量名为 Test2
Test2 SETL {TRUE} ;将该变量赋值为真
GBLS Test3 ;定义一个全局的字符串变量,变量名为 Test3
Test3 SETS “ Testing ” ;将该变量赋值为 “ Testing ”
2、 LCLA、LCLL 和LCLS
语法格式:
LCLA ( LCLL 或 LCLS )局部变量名
LCLA 、 LCLL 和 LCLS 伪指令用于定义一个 ARM 程序中的局部变量,并将其初始化。其中:
LCLA 伪指令用于定义一个局部的数字变量,并初始化为 0 ;
LCLL 伪指令用于定义一个局部的逻辑变量,并初始化为 F (假);
LCLS 伪指令用于定义一个局部的字符串变量,并初始化为空;
以上三条伪指令用于声明局部变量,在其作用范围内变量名必须唯一。
使用示例:
LCLA Test4 ;声明一个局部的数字变量,变量名为 Test4
Test3 SETA 0xaa ;将该变量赋值为 0xaa
LCLL Test5 ;声明一个局部的逻辑变量,变量名为 Test5
Test4 SETL {TRUE} ;将该变量赋值为真
LCLS Test6 ;定义一个局部的字符串变量,变量名为 Test6
Test6 SETS “ Testing ” ;将该变量赋值为 “ Testing ”
3、 SETA、SETL 和SETS
语法格式:
变量名 SETA ( SETL 或 SETS )表达式
伪指令 SETA 、 SETL 、 SETS 用于给一个已经定义的全局变量或局部变量赋值。
SETA 伪指令用于给一个数学变量赋值;
SETL 伪指令用于给一个逻辑变量赋值;
SETS 伪指令用于给一个字符串变量赋值;
其中,变量名为已经定义过的全局变量或局部变量,表达式为将要赋给变量的值。
使用示例:
LCLA Test3 ;声明一个局部的数字变量,变量名为 Test3
Test3 SETA 0xaa ;将该变量赋值为 0xaa
LCLL Test4 ;声明一个局部的逻辑变量,变量名为 Test4
Test4 SETL {TRUE} ;将该变量赋值为真
4 、 RLIST
语法格式:
名称 RLIST { 寄存器列表 }
RLIST 伪指令可用于对一个通用寄存器列表定义名称,使用该伪指令定义的名称可在 ARM 指令 LDM/STM 中使用。在 LDM/STM 指令中,列表中的寄存器访问次序为根据寄存器的编号由低到高,而与列表中的寄存器排列次序无关。
使用示例:
RegList RLIST {R0-R5 , R8 , R10} ;将寄存器列表名称定义为 RegList ,可在 ARM 指令
LDM/STM中通过该名称访问寄存器列表。
数据定义( Data Definition )伪指令
数据定义伪指令一般用于为特定的数据分配存储单元,同时可完成已分配存储单元的初始化。
常见的数据定义伪指令有如下几种:
— DCB 用于分配一片连续的字节存储单元并用指定的数据初始化。
— DCW ( DCWU )用于分配一片连续的半字存储单元并用指定的数据初始化。
— DCD ( DCDU )用于分配一片连续的字存储单元并用指定的数据初始化。
— DCFD ( DCFDU )用于为双精度的浮点数分配一片连续的字存储单元并用指定的数据初始
化。
— DCFS ( DCFSU )用于为单精度的浮点数分配一片连续的字存储单元并用指定的数据初
始化。
— DCQ ( DCQU ) 用于分配一片以 8 字节为单位的连续的存储单元并用指定的数据初始
化。
— SPACE 用于分配一片连续的存储单元
— MAP 用于定义一个结构化的内存表首地址
— FIELD 用于定义一个结构化的内存表的数据域
1、 DCB
语法格式:
标号 DCB 表达式
DCB 伪指令用于分配一片连续的字节存储单元并用伪指令中指定的表达式初始化。其中,表达式可以为
0 ~ 255 的数字或字符串。 DCB 也可用 “ = ” 代替。
使用示例:
Str DCB “ This is a test ! ” ;分配一片连续的字节存储单元并初始化。
2、 DCW(或DCWU)
语法格式:
标号 DCW (或 DCWU )表达式
DCW (或 DCWU )伪指令用于分配一片连续的半字存储单元并用伪指令中指定的表达式初始化。
其中,表达式可以为程序标号或数字表达式。。
用 DCW 分配的字存储单元是半字对齐的,而用 DCWU 分配的字存储单元并不严格半字对齐。
使用示例:
DataTest DCW 1 , 2 , 3 ;分配一片连续的半字存储单元并初始化。
3、 DCD(或DCDU)
语法格式:
标号 DCD (或 DCDU )表达式
DCD (或 DCDU )伪指令用于分配一片连续的字存储单元并用伪指令中指定的表达式初始化。其中,表
达式可以为程序标号或数字表达式。 DCD 也可用 “ & ” 代替。
用 DCD 分配的字存储单元是字对齐的,而用 DCDU 分配的字存储单元并不严格字对齐。
使用示例:
DataTest DCD 4 , 5 , 6 ;分配一片连续的字存储单元并初始化。
4、 DCFD(或DCFDU)
语法格式:
标号 DCFD (或 DCFDU )表达式
DCFD (或 DCFDU )伪指令用于为双精度的浮点数分配一片连续的字存储单元并用伪指令中指定的表达式初始化。每个双精度的浮点数占据两个字单元。用 DCFD 分配的字存储单元是字对齐的,而用 DCFDU 分配的字存储单元并不严格字对齐。
使用示例:
FDataTest DCFD 2E115 , -5E7 ;分配一片连续的字存储单元并初始化为指定的双精度数。
5、 DCFS(或DCFSU)
语法格式:
标号 DCFS (或 DCFSU )表达式
DCFS (或 DCFSU )伪指令用于为单精度的浮点数分配一片连续的字存储单元并用伪指令中指定的表达式初始化。每个单精度的浮点数占据一个字单元。 用 DCFS 分配的字存储单元是字对齐的,而用 DCFSU 分配的字存储单元并不严格字对齐。
使用示例:
FDataTest DCFS 2E5 , -5E - 7 ;分配一片连续的字存储单元并初始化为指定的单精度数。
6、 DCQ(或DCQU)
语法格式:
标号 DCQ (或 DCQU )表达式
DCQ (或 DCQU )伪指令用于分配一片以 8 个字节为单位的连续存储区域并用伪指令中指定的表达式
初始化。
用 DCQ 分配的存储单元是字对齐的,而用 DCQU 分配的存储单元并不严格字对齐。
使用示例:
DataTest DCQ 100 ;分配一片连续的存储单元并初始化为指定的值。
7、 SPACE
语法格式:
标号 SPACE 表达式
SPACE 伪指令用于分配一片连续的存储区域并初始化为 0 。其中,表达式为要分配的字节数。
SPACE 也可用 “ % ” 代替。
使用示例:
DataSpace SPACE 100 ;分配连续 100 字节的存储单元并初始化为 0 。
8、 MAP
语法格式:
MAP 表达式 { ,基址寄存器 }
MAP 伪指令用于定义一个结构化的内存表的首地址。 MAP 也可用 “ ^ ” 代替。
表达式可以为程序中的标号或数学表达式,基址寄存器为可选项,当基址寄存器选项不存在时,表达式的值即为内存表的首地址,当该选项存在时,内存表的首地址为表达式的值与基址寄存器的和。
MAP 伪指令通常与 FIELD 伪指令配合使用来定义结构化的内存表。
使用示例:
MAP 0x100 , R0 ;定义结构化内存表首地址的值为 0x100 + R0 。
9、 FILED
语法格式:
标号 FIELD 表达式
FIELD 伪指令用于定义一个结构化内存表中的数据域。 FILED 也可用 “ # ” 代替。
表达式的值为当前数据域在内存表中所占的字节数。
FIELD 伪指令常与 MAP 伪指令配合使用来定义结构化的内存表。 MAP 伪指令定义内存表的首地址, FIELD 伪指令定义内存表中的各个数据域,并可以为每个数据域指定一个标号供其他的指令引用。
注意 MAP 和 FIELD 伪指令仅用于定义数据结构,并不实际分配存储单元。
使用示例:
MAP 0x100 ;定义结构化内存表首地址的值为 0x100 。
A FIELD 16 ;定义 A 的长度为 16 字节,位置为 0x100
B FIELD 32 ;定义 B 的长度为 32 字节,位置为 0x110
S FIELD 256 ;定义 S 的长度为 256 字节,位置为 0x130
汇编控制( Assembly Control )伪指令
汇编控制伪指令用于控制汇编程序的执行流程,常用的汇编控制伪指令包括以下几条:
— IF 、 ELSE 、 ENDIF
— WHILE 、 WEND
— MACRO 、 MEND
— MEXIT
1、 IF、ELSE、ENDIF
语法格式:
IF 逻辑表达式
指令序列 1
ELSE
指令序列 2
ENDIF
IF 、 ELSE 、 ENDIF 伪指令能根据条件的成立与否决定是否执行某个指令序列。当 IF 后面的逻辑表
达式为真,则执行指令序列 1 ,否则执行指令序列 2 。其中, ELSE 及指令序列 2 可以没有,此时,当
IF 后面的逻辑表达式为真,则执行指令序列 1 ,否则继续执行后面的指令。
IF 、 ELSE 、 ENDIF 伪指令可以嵌套使用。
使用示例:
GBLL Test ;声明一个全局的逻辑变量,变量名为 Test……
IF Test = TRUE
指令序列 1
ELSE
指令序列 2
ENDIF
2、 WHILE、WEND
语法格式:
WHILE 逻辑表达式
指令序列
WEND
WHILE 、 WEND 伪指令能根据条件的成立与否决定是否循环执行某个指令序列。当 WHILE 后面的逻辑
表达式为真,则执行指令序列,该指令序列执行完毕后,再判断逻辑表达式的值,若为真则继续执行,一直
到逻辑表达式的值为假。
WHILE 、 WEND 伪指令可以嵌套使用。
使用示例:
GBLA Counter ;声明一个全局的数学变量,变量名为 Counter
Counter SETA 3 ;由变量Counter 控制循环次数
……
WHILE Counter < 10
指令序列
WEND
3、 MACRO、MEND
语法格式:
$ 标号 宏名 $ 参数 1 , $ 参数 2 ,……
指令序列
MEND
MACRO 、 MEND 伪指令可以将一段代码定义为一个整体,称为宏指令,然后就可以在程序中通过宏指令
多次调用该段代码。其中, $ 标号在宏指令被展开时,标号会被替换为用户定义的符号,宏指令可以使用
一个或多个参数,当宏指令被展开时,这些参数被相应的值替换。
宏指令的使用方式和功能与子程序有些相似,子程序可以提供模块化的程序设计、节省存储空间并提高
运行速度。但在使用子程序结构时需要保护现场,从而增加了系统的开销,因此,在代码较短且需要传递的
参数较多时,可以使用宏指令代替子程序。
包含在 MACRO 和 MEND 之间的指令序列称为宏定义体,在宏定义体的第一行应声明宏的原型(包含宏
名、所需的参数),然后就可以在汇编程序中通过宏名来调用该指令序列。在源程序被编译时,汇编器将宏
调用展开,用宏定义中的指令序列代替程序中的宏调用,并将实际参数的值传递给宏定义中的形式参数。
MACRO 、 MEND 伪指令可以嵌套使用。
4、 MEXIT
语法格式:
MEXIT
MEXIT 用于从宏定义中跳转出去。
其他常用的伪指令
还有一些其他的伪指令,在汇编程序中经常会被使用,包括以下几条:
— AREA
— ALIGN
— CODE16 、 CODE32
— ENTRY
— END
— EQU
— EXPORT (或 GLOBAL )
— IMPORT
— EXTERN
— GET (或 INCLUDE )
— INCBIN
— RN
— ROUT
1、 AREA
语法格式:
AREA 段名属性 1 ,属性 2 ,……
AREA 伪指令用于定义一个代码段或数据段。其中,段名若以数字开头,则该段名需用 “ | ” 括起来
,如 |1_test| 。
属性字段表示该代码段(或数据段)的相关属性,多个属性用逗号分隔。常用的属性如下:
— CODE 属性:用于定义代码段,默认为 READONLY 。
— DATA 属性:用于定义数据段,默认为 READWRITE 。
— READONLY 属性:指定本段为只读,代码段默认为 READONLY 。
— READWRITE 属性:指定本段为可读可写,数据段的默认属性为 READWRITE 。
— ALIGN 属性:使用方式为 ALIGN 表达式。在默认时, ELF (可执行连接文件)的代码段和数据段
是按字对齐的,表达式的取值范围为 0 ~ 31 ,相应的对齐方式为 2 表达式次方。
— COMMON 属性:该属性定义一个通用的段,不包含任何的用户代码和数据。各源文件中同名的
COMMON 段共享同一段存储单元。
一个汇编语言程序至少要包含一个段,当程序太长时,也可以将程序分为多个代码段和数据段。
使用示例:
AREA Init , CODE , READONLY
该伪指令定义了一个代码段,段名为 Init ,属性为只读
2、 ALIGN
语法格式:
ALIGN { 表达式 { ,偏移量 }}
ALIGN 伪指令可通过添加填充字节的方式,使当前位置满足一定的对其方式 | 。其中,表达式的值用
于指定对齐方式,可能的取值为 2 的幂,如 1 、 2 、 4 、 8 、 16 等。若未指定表达式,则将当前位
置对齐到下一个字的位置。偏移量也为一个数字表达式,若使用该字段,则当前位置的对齐方式为: 2 的
表达式次幂+偏移量。
使用示例:
AREA Init , CODE , READONLY , ALIEN = 3 ;指定后面的指令为 8 字节对齐。
指令序列
END
3、 CODE16、CODE32
语法格式:
CODE16 (或 CODE32 )
CODE16 伪指令通知编译器,其后的指令序列为 16 位的 Thumb 指令。
CODE32 伪指令通知编译器,其后的指令序列为 32 位的 ARM 指令。
若在汇编源程序中同时包含 ARM 指令和 Thumb 指令时,可用 CODE16 伪指令通知编译器其后的指令序
列为 16 位的 Thumb 指令, CODE32 伪指令通知编译器其后的指令序列为 32 位的 ARM 指令。因此,在使
用 ARM 指令和 Thumb 指令混合编程的代码里,可用这两条伪指令进行切换,但注意他们只通知编译器其后
指令的类型,并不能对处理器进行状态的切换。
使用示例:
AREA Init , CODE , READONLY
……
CODE32 ;通知编译器其后的指令为 32 位的 ARM 指令
LDR R0 ,= NEXT + 1 ;将跳转地址放入寄存器 R0
BX R0 ;程序跳转到新的位置执行,并将处理器切换到 Thumb 工作状态
……
CODE16 ;通知编译器其后的指令为 16 位的 Thumb 指令
NEXT LDR R3,=0x3FF
……
END ;程序结束
4、 ENTRY
语法格式:
ENTRY
ENTRY 伪指令用于指定汇编程序的入口点。在一个完整的汇编程序中至少要有一个 ENTRY (也可以有
多个,当有多个 ENTRY 时,程序的真正入口点由链接器指定),但在一个源文件里最多只能有一个 ENTRY
(可以没有)。
使用示例:
AREA Init , CODE , READONLY
ENTRY ;指定应用程序的入口点
……
5、 END
语法格式:
END
END 伪指令用于通知编译器已经到了源程序的结尾。
使用示例:
AREA Init , CODE , READONLY
……
END ;指定应用程序的结尾
6、 EQU
语法格式:
名称 EQU 表达式 { ,类型 }
EQU 伪指令用于为程序中的常量、标号等定义一个等效的字符名称,类似于 C 语言中的# define 。
其中 EQU 可用 “ * ” 代替。
名称为 EQU 伪指令定义的字符名称,当表达式为 32 位的常量时,可以指定表达式的数据类型,可以
有以下三种类型:
CODE16 、 CODE32 和 DATA
使用示例:
Test EQU 50 ;定义标号 Test 的值为 50
Addr EQU 0x55 , CODE32 ;定义 Addr 的值为 0x55 ,且该处为 32 位的 ARM 指令。
7、 EXPORT(或GLOBAL)
语法格式:
EXPORT 标号 {[WEAK]}
EXPORT 伪指令用于在程序中声明一个全局的标号,该标号可在其他的文件中引用。 EXPORT可用
GLOBAL 代替。标号在程序中区分大小写, [WEAK] 选项声明其他的同名标号优先于该标号被引用。
使用示例:
AREA Init , CODE , READONLY
EXPORT Stest ;声明一个可全局引用的标号Stest……
END
8、 IMPORT
语法格式:
IMPORT 标号 {[WEAK]}
IMPORT 伪指令用于通知编译器要使用的标号在其他的源文件中定义,但要在当前源文件中引用,而且
无论当前源文件是否引用该标号,该标号均会被加入到当前源文件的符号表中。
标号在程序中区分大小写, [WEAK] 选项表示当所有的源文件都没有定义这样一个标号时,编译器也不
给出错误信息,在多数情况下将该标号置为 0 ,若该标号为 B 或 BL 指令引用,则将 B 或 BL指令置为
NOP 操作。
使用示例:
AREA Init , CODE , READONLY
IMPORT Main ;通知编译器当前文件要引用标号Main,但Main 在其他源文件中定义……
END
9、 EXTERN
语法格式:
EXTERN 标号 {[WEAK]}
EXTERN 伪指令用于通知编译器要使用的标号在其他的源文件中定义,但要在当前源文件中引用,如果
当前源文件实际并未引用该标号,该标号就不会被加入到当前源文件的符号表中。标号在程序中区分大小写
, [WEAK] 选项表示当所有的源文件都没有定义这样一个标号时,编译器也不给出错误信息,在多数情况下
将该标号置为 0 ,若该标号为 B 或 BL 指令引用,则将 B 或 BL指令置为 NOP 操作。
使用示例:
AREA Init , CODE , READONLY
EXTERN Main ;通知编译器当前文件要引用标号Main,但Main 在其他源文件中定义……
END
10、 GET(或INCLUDE)
语法格式:
GET 文件名
GET 伪指令用于将一个源文件包含到当前的源文件中,并将被包含的源文件在当前位置进行汇编处理。
可以使用 INCLUDE 代替 GET 。
汇编程序中常用的方法是在某源文件中定义一些宏指令,用 EQU 定义常量的符号名称,用 MAP和
FIELD 定义结构化的数据类型,然后用 GET 伪指令将这个源文件包含到其他的源文件中。使用方法与 C 语
言中的 “ include ” 相似。
GET 伪指令只能用于包含源文件,包含目标文件需要使用 INCBIN 伪指令
使用示例:
AREA Init , CODE , READONLY
GET a1.s ;通知编译器当前源文件包含源文件a1.s
GE T C:\a2.s ;通知编译器当前源文件包含源文件C:\ a2.s ……
END
11、 INCBIN
语法格式:
INCBIN 文件名
INCBIN 伪指令用于将一个目标文件或数据文件包含到当前的源文件中,被包含的文件不作任何变动的
存放在当前文件中,编译器从其后开始继续处理。
使用示例:
AREA Init , CODE , READONLY
INCBIN a1.dat ;通知编译器当前源文件包含文件a1.dat
INCBIN C:\a2.txt ;通知编译器当前源文件包含文件C:\a2.txt……
END
12、 RN
语法格式:
名称 RN 表达式
RN 伪指令用于给一个寄存器定义一个别名。采用这种方式可以方便程序员记忆该寄存器的功能。其中
,名称为给寄存器定义的别名,表达式为寄存器的编码。
使用示例:
Temp RN R0 ;将R0 定义一个别名Temp
13、 ROUT
语法格式:
{ 名称 } ROUT
ROUT 伪指令用于给一个局部变量定义作用范围。在程序中未使用该伪指令时,局部变量的作用范围为
所在的 AREA ,而使用 ROUT 后,局部变量的作为范围为当前 ROUT 和下一个 ROUT 之间。
.arm .arm @ 定义以下代码使用 ARM 指令集编译
.code 32 .code 32 @作用同.arm
.code 16 .code 16 @作用同.thumb
.thumb .thumb @定义以下代码使用 Thumb 指令集编译
.section .section expr @定义域中包含的段。expr 可以使.text,.data.,.bss
嵌入式 ARM 体系结构与接口技术
- 129 -
.text .text {subsection} @ 将 定 义 符 开 始 的 代 码 编 译 到 代 码 段 或 代 码 子 段
(subsection)
.data .data {subsection} @ 将 定 义 符 开 始 的 代 码 编 译 到 数 据 段 或 数 据 子 段
(subsection)
.bss .bss {subsection} @将变量存放到.bss 段或.bss 的子段(subsection)
.align .align{alignment}{,fill}{,max} @通过用零或指定的数据进行填充来使当前位置
与指定边界对齐
.org .org offset{,expr} @指定从当前地址加上 offset 开始存放代码,并且从当前地
址到当前地址加上 offset 之间的内存单元,用零或指定的数据进行填充
助记符 说明 操作
MUL R1,R2,R3 32位乘法 R1=R2×R3
MLA R1,R2,R3,R0 32位乘加 R1=R2×R3+R0
UMULL R0,R1,R5,R8 64位无符号乘法 (R1、R0)=R5×R8
UMLAL R0,R1,R5,R8 64位无符号乘加 (R1、R0)=R5×R8+(R1、R0)
SMULL R2,R3,R7,R6 64位有符号乘法 (R3、R2)=R7×R6
SMLAL R2,R3,R7,R6 64位有符号乘加 (R3、R2)=R7×R6+(R3、R2)
1)将CPSR寄存器内容存入IRQ模式的SPSR寄存器中
2)修改CPSR寄存器:
将I位设置为1(禁止IRQ中)
将T位设置为0(进入ARM状态)
设置MOD位,切换处理器模式至IRQ模式
3)将下一条指令的地址存入IRQ模式的lr寄存器中
4)将跳转地址存入PC,实现跳转
1)将SPSR寄存器的值复制回CPSR寄存器
2)将lr寄存器的值减去一个常量后复制到PC寄存器,跳转到被中断的用户程序
寄存器列表后的^的作用可以让 SPSR 的值恢复到 CPSR 中
ldmfd sp!, {r0, r1, r12, pc}^