嵌入式Linux ARM汇编(四)——ARM汇编程序设计
汇编程序有顺序、循环、分支、子程序四种结构形式。
一、顺序结构
程序实例:
AREA Buf,DATA,READWRITE;定义数据段Buf
Array DCB 0x11,0x22,0x33,0x44;定义12个字节的数组Array
DCB 0x55,0x66,0x77,0x88
DCB 0x00,0x00,0x00,0x00
AREA hello,CODE32,READONLY
ENTRY
LDR R0,=Array;取得数组Array的首地址
LDR R2,[R0];装载数组第1字节数据给R2
MOV R1,#1
LDR R3,[R0,R1,LSL#2];装载数组第5字节数据给R3
ADD R2,R2,R3
MOV R1,#8
STR R2,[R0,R1,LSL#2];
END
二、分支结构
1、ifelse分支结构的程序实现
C语言实现:
int x=76;
int y=88;
if(x>y)
z=100;
else
z=50
ARM汇编语言实现
MOV R0,#76;初始化R0的值
MOV R1,#88;初始化R1的值
CMP R0,R1;判断R0>R1?
MOVHI R2,100;R0>R1时,R2=100
MOVLS R1,50;R0
2、B指令实现散转功能
CMP R0,#MAX_INDEX;判断索引号是否超出最大索引值
ADDLO PC,PC,R0,LSL#2;索引号若没有超出,则程序跳到相应位置
BHI ERROR;索引号若超出,则进行出错处理
BFUN1;跳到第一个程序
BFUN2;跳到第二个程序
BFUN3;跳到第三个程序
三、循环结构
1、for循环
C语言实现:
for(i = 0; i < 10; i++)
x++;
ARM汇编语言实现:
R0为x,R2为i,均为无符号整数
MOV R0,#0;初始化R0=0
MOV R2,#0;初始化R2=0
FOR CMP R2,#10;判断R2<10?
BCS FOR_E;若条件失败(即R>=10),退出循环
ADD R0,R0,#1;执行循环体,R0=R0+1
ADD R2,R2,#1;R2=R2+1
B FOR
FOR_E
2、wihle循环
C语言实现:
while(x<=y)
X*=2;
ARM汇编语言实现:
x为R0,y为R1,均为无符号整数
MOV R0,#1;初始化R0=1
MOV R1,#20;初始化R1=20
B W_2;首先要判断条件
W_1 MOV R0,R0,LSL#1;循环体,R0*=2
W_2 CMP R0,R1;判断R0<=R1,即x<=y
BLS W_1;若R0<=R1,继续循环体
W_END
3、循环语句实现数据块的复制
ARM汇编语言实现:
LDR R0,=DATA_DST;指向数据目标地址
LDR R1,=DATA_SRC;指向数据源地址
MOV R10,#20;复制数据个数20*N个字
;N为LDM指令操作数据个数
LOOP LDMIA R1!,{R2-R9};从数据源读取8个字节到R2-R9
STMIA R0!,{R2-R9};将R2-R9的数据保存到目标地址
SUBS R10,R10,#1;R0-1,并改变程序状态寄存器
BNE LOOP
4、双层for循环的实现
ARM汇编语言实现:
AREA Block,CODE; 声明代码段
ENTRY
;for(i = 0; i < 10; i++)
;for(j = i+1; j<=10; j++)
;z +=1
START
MOV R1, #0; i = 0
MOV R0, #0; Z
LOOP
CMP R1, #10; i < 10
BEQ STOP
ADD R2, R1, #1; j = i+1
LOOP1
CMP R2, #10+1; j<=10
ADDNE R0, R0, #1; z +=1
ADDNE R2, R2, #1; j++
BNE LOOP1
ADD R1, R1, #1; i++
B LOOP
STOP
MOV R0, #0x18
LDR R1, =0x20026
SWI 0x123456
END
5、数据块拷贝
ARM汇编语言实现:
;数据块拷贝,利用LDR/STR指令
num EQU 10
AREA BlockData,DATA; 声明数据段
src DCD 0,1,2,3,4,5,6,7,8,9; 定义十个数
dst SPACE 10*4
AREA Block,CODE; 声明代码段
ENTRY
START
LDR R1, =src
LDR R2, =dst
MOV R3, #num
LOOP
LDR R0, [R1], #4
STR R0, [R2], #4
SUBS R3, R3, #1
BNE LOOP
STOP
MOV R0, #0x18
LDR R1, =0x20026
SWI 0x123456
END
四、子程序
调用程序在调用子程序时,经常需要传送一些参数给子程序,子程序运行完成后也需要回送结果给调用程序。调用程序和子程序之间的信息传送称为参数传送。参数传送的两种方法:
A、当参数比较少时,可以通过寄存器传送参数
B、当参数比较多时,可以通过内存块或堆栈传送参数
调用程序在调入子程序时必须保存正确的返回地址,即当前PC值,PC值可以保存在专用的寄存器R14中,也可以保存到堆栈中。根据这两种情况,可以在子程序采用如下的返回语句:
MOV PC,LR;恢复PC的值
STMFD SP!,{R0-R7,PC};将PC值从堆栈中返回
使用堆栈来恢复处理器的状态时,STMFD与LDMFD要配合使用。
一般来说,在ARM汇编语言程序中,子程序的调用是通过B来实现的。BL在执行时完成如下操作:
将子程序的返回地址存放在链接寄存器LR中,同时将程序计数器PC指向子程序的入口点,当子程序执行完毕需要返回调用处时,只需要将存放在LR中的返回地址重新拷贝给程序计数器PC即可。
C语言实现:
int MAX(int i, int j)
{
if(x > j)
return i;
else
return j;
}
int main(void)
{
int a, b, c;
a = 19;
b = 20;
c = MAX(a, b);
}
ARM汇编语言实现:
X EQU 19;定义X的值为19
N EQU 20;定义N的值为20
AREA hello,CODE32,READONLY;声明代码段
ENTRY;标识程序入口
START LDR R0,=X;给R0,R1赋初值
LDR R1,=N
BL MAX;调用子程序MAX
HALT B HALT;死循环
MAX;声明子程序MAX
CMP R0,R1
MOVHI R2,R0;比较R0,R1,R2等于最大值
MOVLS R2,R1
MOV PC,LR;返回语句
MAX_END
END
五、常见程序设计示例
以下是几个类似C语言编程题目的例子,用ARM汇编语言给出的程序。
1、存储器从0x400000开始的100个单元中存放着ASCII码,编写程序,将其所有的小写字母转换成大写字母,对其它的ASCII码不做变换。
MOV R0,#0x400000
MOV R1,#0
LP
LDRB R2,[R0,R1]
CMP R2,#0x61
BLO NEXT
CMP R2,#0x7B;0x61---0x7A为小写字母的ASC
SUBLO R2, R2,#0x20
STRBLO R2,[R0,R1]
NEXT
ADD R1, R1,#1
CMP R1,#100
BNE LP
2、编写一简单ARM汇编程序段,实现1+2+…+100的运算
MOV R2,#100
MOV R1,#0
LOOP
ADD R1,R1,R2;R1中为累加和
SUBS R2,R2,#1;R2控制循环
BNE LOOP
3、编写程序,比较存储器中0x400000和0x400004两无符号字数据的大小,并且将比较结果存于0x400008的字中,若两数相等其结果记为0,若前者大于后者其结果记为1,若前者小于后者其结果记为-1
MOV R0,#0x400000
LDR R1,[R0];取第1个数
LDR R2,[R0,#4] ;取第2个数
CMP R1,R2;两个数相比较
MOVHI R1,#1;R1大
MOVLO R1,# -1;R1小
MOVEQ R1,#0;两个数相等
STRR1,[R0,#8]
4、编写一程序,存储器中从0x400200开始有一个64位数。(1)将取反,再存回原处;(2)求其补码,存放到0x400208处
LDR R0,=0x400200
LDR R2,=0xFFFFFFFF
LDR R1,[R0];取低32位数
EOR R1,R1,R2;取反
STR R1,[R0];存低32位反码
ADDS R1,R1,#1;又加1为求补
STR R1,[R0,#8] ;存低32位补码
LDR R1,[R0,#4] ;取高32位数
EOR R1,R1,R2;取反
STR R1,[R0,#4] ;存高32位反码
ADC R1,R1,#0;高32位求补
STR R1,[R0,#12] ;存高32位补码