A.5.1 文件格式
ARM
源程序文件(即源文件)为文件格式,可以使用任一文本编辑器编写程序代码。
连接器根据一定的规则将各个段安排到内存中的相应位置。源程序中段之间的相邻关系与执行的映象文件中段之间的相邻关系并不一定相同。
代码段的例子如下:
AREA
Hello,CODE,READONLY ;声明代码段Hello
ENTRY
;程序入口(调试用)
START
MOV R7,#10
MOV
R6,#5
ADD
R6,R6,R7 ;R6=R6+R7
B
;死循环
END
每一个汇编文件都要以END
结束,包括*INC 文件,否则编译会有警告。
数据段的例子如下:
AREA
DataArea,DATA,NOINIT,ALLGN=2
DISPBUF
SPACE 100
RCVBUF
SPACE 100
…
(7)宏定义及其作用
使用宏定义可以提高程序的可读性,简化程序代码和同步修改。ARM
宏定义与标准C的#define 相似,只在源程序中进行字符代换。宏定义从MACRO 伪指令开始,到MEND
结束,并可以使用参数。
宏要先定义,然后再使用。使用时直接书写宏名,并根据对应的宏定义格式设置输入参数或书写标号等。当源程序被汇编时,汇编编译器将展开每一个宏调用,用宏定义体代替程序中的宏调用,并使用实际的参数值代替宏定义时的形式参数。
程序程序清单见后,程序中定义了一个宏CALL,用于调用子程序,调用时设置所要调用的子程序名$Function
及两个入口参数$dat1 和$dat2。由于宏定义体中使用的是MOV 指令,所以$dat1 参数只能为8
位图的立即数或通用寄存器。
宏应用的例子:
…
MACRO
;宏定义
CALL
$Function,$dat1,$dat2 ;宏名称为CALL,带3 个参数
IMPORT
$Function ;声明外部子程序
MOV
R0,$dat1 ;设置子程序参数,R0=$dat1
MOV
R1,$dat2
BL
Function ;调用子程序
MEND
;宏定义结束
…
CALL
FADD1,#3,#2 ;宏调用
…
汇编预处理后,宏调用将被展开,程序清单如下:
…
IMPORT
FADD1
MOV
R0,#3
MOV
R1,#3
BL
FADD1
…
A.5.3 子程序的调用
使用BL
指令进行调用,该指令会把返回的PC 值保存在LR,示例如下:
…
BL
DLEAY
…
DELAY
…
MOV
PC,LR
当子程序执行完毕后,使用MOV、B/BX、STMFD
等指令返回,当然STMFD 要与LDMFD 配套使用,子程序返回的方法:
MOV
PC,LR
或 B
LR
或 BX
LR
或 STMFD
SP!{R0-R7,PC }
ARM7TDMI(-S)是没有BLX
指令的,但是可以通过几条程序实现其功能,模拟BLX 指令如下:
ADR
R1,DELAY+1
MOV
LR,PC ;保存返回地址到LR
BX R1
;跳转并切换指令集
…
A.5.4 数据比较跳转
汇编程序可以使用CMP
指令进行两个数据比较,然后调高相应的ARM 条件码,实现跳转。代码例子如下:
CMP
R5,#10
BEQ
DOEQUAL ;若R5 为10,则跳转到DOEQUAL
…
CMP
R1,R2
ADDHI
R1,R1,#10 ;若R1>R2,则R1=R1+10
ADDLS
R1,R1,#5 ;若R1<=R2,则R1=R1+5
…
ANDS
R1,R1,#0x80 ;R1=R1&0x80,并设置相应标志位
BNE
WAIT ;若R1 的d7 位为则跳转到WAIT
A.5.5 循环
下面的代码为循环程序的例子。例子指定循环次数,每循环一次进行减1
操作,并判断结果是否为0,若为0 则退出循环。
MOV
R1,#10
LOOP …
;循环体
SUBS
R1,R1,#1
BNE
LOOP
…
A.5.6 数据块复制
程序可以使用存储器访问指令LDM/STM
指令进行读取和存储,数据块复制示例如下:
LDR
R0,=DATA_DST ;指向数据目标地址
LDR
R1,=DATA_SRC ;指向数据源地址
MOV
R10,#10 ;复制数据个数为10*N 个字
LOOP
LDMIA R1!,{R2-R9} ;N 为LDM/STM 指令操作数据个数
STMIA
R0!,{R2-R9}
SUBS
R10,R10,#1
BNE
LOOP
…
A.5.7 栈操作
ARM
使用存储器访问指令LDM/STM
实现栈操作,用于子程序寄存器保存。注意,使用堆栈时,要先分配好堆栈空间,设置好寄存器R13(即堆栈指针SP),否则操作失败。
OUTDAT
STMFD
SP!{R0-R7,LR}
…
BL
DELAY
…
LDMFD
SP!{R0-R7,PC}
A.5.8 特殊寄存器定义及应用
基于ARM
核的芯片一般有片内外设,它们通过其特殊寄存器访问。片内外设的使用示例如下:
WDTC
EQU 0xE000000 ;寄存器定义
…
LDR
R0,=WDTC
MOV
R1,#0x12
STR
R1,[R0] ;WDTC=0x12
散转功能
散转是汇编程序常用的一种算法,其示例如下:
CMP
R0,#MAXINDEX ;判断索引号是否超出最大索引值
ADDLO
PC,PC,R0,LSL #2 ;若没有超出,则跳转到相应位置
B ERROR
;若已经超出,则进行出错处理
;散转表,对应索引号为0~N
B
FUN1
B
FUN2
B
FUN3
…
A.5.9 查表操作
查表操作是汇编程序常用的一种操作,其示例如下:
…
LDR
R3,=DISP_TAB ;取得表头
LDR
R2,[R3,R5,LSL #2] ;根据R5 的值查表,取出相应的值
…
;下表为0--F
的字模
DISR_TAB
DCD 0xC0,0xF9,0xA4,0x99,0x92
DCD
0x82,0xF8,0x80,0x90,0x88,0x83
DCD
0xC6,0xa1,0x86,0x8E,0xFF
A.5.10 长跳转
ARM 的B
和BL 指令不能全空间跳转,但通过对PC 进行赋值,实现32 位地址的跳转/调用,示例如下:
ADD
LR,PC,#4 ;保存返回地址,即RET_FUN
LDR
PC,[PC,#-4] ;跳转到LADR_FUN
DCD
LADR_FUN
RET_FUN
…
也可使用伪指令LDR
PC,=LADR_FUN 实现长跳转。
A.5.11 对信号量的支持
ARM
提供一条内存与寄存器交换的指令SWP 用于支持信号量的操作,实现系统任务之间的同步或互斥,其使用的例子如下:
DISP_SEM
EQU 0x40002A00
…
DISP_WAIT
MOV R1,#0
LDR
R0,=DISP_SEM
SWP
R1,R1[R0] ;取出信号量,并设置其为0
CMP
R1,#0 ;判断是否有信号
BEQ
DISP_WAIT ;若没有信号,则等待
…
A.5.12 伪指令使用
LDR
伪指令和NOP 伪指令应用例子代码如下:
LDR
R1,=0x12345678 ;加载32 位立即数
LDR
R0,=LDE_TAB ;加载标号地址
NOP
;空指令
B
;死循环
A.5.13 一个完整的例子
下面是汇编程序完整的例子:
ABC EQU
0x12
;声明一个代码段Example
AREA
Example,CODE,READONLY
ENIRY
CODE32
ADR
R0,Thumb_START+1 ;装载地址,并设置d0 位为1
BX R0
;切换到Thumb 状态
CODE16
;声明16 位代码(Thumb)
Thumb_START
MOV
R1,#ABC
ADD
R1,R1,#0x10
B
Thumb_START
END
A.5.14 外设控制
在32
位的ARM
核芯片中,其外设的控制寄存器中,一般会设置“置位/复位”寄存器,这样可以方便的实现位操作,而不会影响其它位,如 IOSET=0x01
只会将P0.1 的置位,而其它I/O 状态不变,另外,ARM
存储/保存指令具有前偏移功能,所以对外设的控制寄存器进行操作时可使用此功能,避免了每次都加载寄存器地址的操作示例如下:
LDR
R0,=GPIO_BASE
MOV
R1,#0x00
STR
R1,[R0,#0x04] ;IOSET=0x00
MOV
R1,#0x10
STR
R1,[R0,#0x0C] ;IOCLR=0x101
A.5.15 三级流水线介绍
ARM7TDM(-S)使用三级流水线执行指令,第一阶段从内存中取回的指令,第二阶段开始解码,而第三阶段实际执行指令。故此,程序计数器总是超出当前执行的指令两条指令。(在为跳转指令计算偏移量时必须计算在内)。因为有这个流水线,在跳转时丢失2
个指令周期(因为要重新添满流水线)。所以最好利用条件执行指令来避免浪费周期。
条件跳转示例:
…
CMP
R0,#0
BEQ
LOOP1
MOV
R1,#0x10
MOV
R2,#0x88
LOOP1
…
可以写为更有效的:
…
CMP
R0,#0
MOVNE
R1,#0x10
MOVNE
R2,#0x88
…
原文:http://www.cnblogs.com/getyoulove/p/3677424.html