3.8 ARM程序设计举例3.8.1 分支程序程序设计中的三种基本结构是:顺序结构、分支结构和循环结构。在C语言中可以使用if-else语句实现单分支和双分支结构,也可以通过switch-case语句实现多分支结构。但是在汇编语言中,分支结构一般是通过跳转指令结合标号来实现的。
在ARM汇编语言程序中,由于ARM指令支持条件执行,从而大大减少了分支程序的复杂程度。
例如:用两个整数辗转相减的方法求它们的最大公约数。注意体会B指令加条件码的执行方式。程序中使用的main标号是因为IAR汇编器一般默认从main标号处开始执行。
代码清单3.2
NAME GCD
PUBLIC main ;声明外部引用标号main
B main ;从main标号处开始执行
RSEG CODE:CODE
CODE32
main: MOV R0,#120
MOV R1,#96
Gcd: CMP R0,R1 ;比较两数的大小
BEQ Stop ;如果两数相等则跳到结束处
BLT Less ;如果R0<R1则跳到Less标号处
SUB R0,R0,R1 ;否则R0=R0-R1
B Gcd
Less: SUB R1,R1,R0 ;R1=R1-R0
B Gcd
Stop: B Stop ;跳转到指令本身,程序停止运行
ENDMOD ;本程序模块结束
END ;本程序结束
3.8.2 循环程序通过跳转指令还可以实现程序的循环结构。
例如:求n=1+2+…+10累加的和。
代码清单3.3
NAME SUM
PUBLIC main
B main
RSEG CODE
CODE32
main: MOV R0,#10
MOV R1,R0 ;利用R1寄存器做循环计数器
Loop: SUBS R1,R1,1 ;循环次数减1
ADD R0,R0,R1
BNE Loop ;循环次数为0则结束循环
Stop: B Stop
ENDMOD
END
3.8.3 子程序调用通过BL指令可以实现子程序调用,语法:BL子程序名。
在子程序的结束处,可以通过MOV PC,LR返回到主程序中。通常可以使用寄存器R0~R3完成传递参数到子程序和从子程序返回运算的结果。
以下是使用BL指令调用子程序的汇编语言源程序的例子,该程序编写了一个在内存里拷贝字符串的子程序,然后在主程序里调用它。
代码清单3.4
NAME STRCPY
PUBLIC main
B main
RSEG CODE
CODE32
main: LDR R1,=srcstr ;R1指向源字符串
LDR R0,=dststr ;R0指向目标字符串
BL strcopy ;调用strcopy子程序
stop: B stop ;程序停止
strcopy: ;子程序定义
LDRB R2,[R1],#1 ;读一个字符到R2,并更新源字符地址
STRB R2,[R0],#1 ;写一个字符,并更新目的字符地址
CMP R2,#0 ;是否结束。以数字0为标志
BNE strcopy ;循环执行
DATA ;数据区
srcstr DCB "First string - source ",0
dststr DCB "Second string - destination ",0
ENDMOD
END
3.8.4 查表法查表法是编程中常用的一种技巧。当程序涉及到较多的数据、数据串或数据表格时,可以通过地址来对它们进行访问。通常有两种方法装载地址:(1)通过ADR和ADRL伪指令直接装载地址;(2)通过伪指令LDR Rd,=Label从数据表格中装载地址。
下面的程序设置了3个参数,arithfunc根据3个参数返回一个R0值。当R0=0时,R0=R1+R2;当R0=1时,R0=R1-R2;当R0>1时,R0=R1+R2。:
代码清单3.5
NAME JUMP
PUBLIC main
B main
Num EQU 2 ;跳转表格的入口数
RSEG CODE
CODE32
main: MOV R0,#0 ;以下设置3个参数
MOV R1,#3
MOV R2,#2
BL arithfunc ;调用子程序
stop: B stop ;程序停止
arithfunc:
CMP R0,#Num ;比较参数
BHS Doadd ;若R0>=2,则执行加法
ADR R3,jumptable ;装载跳转表格标号地址
LDR PC,[R3,R0,LSL #2] ;跳到相应子程序入口地址处
Jumptable:
DCD Doadd ;Doadd子程序的入口地址
DCD Dosub ;Dosub子程序的入口地址
Doadd: ADD R0,R1,R2 ;=0或>1时执行的操作
MOV PC,LR
Dosub: SUB R0,R1,R2 ;=1时执行的操作
MOV PC,LR
ENDMOD
END