嵌入式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

 

 

 

二、分支结构

1ifelse分支结构的程序实现

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 R0R1;判断R0>R1

MOVHI R2,100;R0>R1时,R2=100

MOVLS R1,50;R0时,R2=50

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汇编语言实现:

R0xR2i,均为无符号整数

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汇编语言实现:

xR0yR1,均为无符号整数

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个字

;NLDM指令操作数据个数

 

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-R7PC};PC值从堆栈中返回

使用堆栈来恢复处理器的状态时,STMFDLDMFD要配合使用。

    一般来说,在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;R0R1赋初值

LDR R1,=N

BL MAX;调用子程序MAX

HALT B HALT;死循环

MAX;声明子程序MAX

CMP R0,R1

MOVHI R2,R0;比较R0R1R2等于最大值

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 R2R2#0x20

STRBLO R2[R0,R1]

NEXT

ADD R1R1#1

CMP R1#100

BNE LP

2编写一简单ARM汇编程序段,实现1+2+…+100的运算

MOV R2#100

MOV R1#0

LOOP

ADD R1R1R2R1中为累加和

SUBS R2R2#1R2控制循环

BNE LOOP

3编写程序,比较存储器中0x4000000x400004两无符号字数据的大小,并且将比较结果存于0x400008的字中,若两数相等其结果记为0,若前者大于后者其结果记为1,若前者小于后者其结果记为-1

MOV R0#0x400000

LDR R1[R0];取第1个数

LDR R2[R0#4] ;取第2个数

CMP R1R2;两个数相比较

MOVHI R1#1R1

MOVLO R1# -1R1

MOVEQ R1#0;两个数相等

STRR1[R0#8]

4编写一程序,存储器中从0x400200开始有一个64位数。(1)将取反,再存回原处;(2)求其补码,存放到0x400208 

LDR R0=0x400200

LDR R2=0xFFFFFFFF

LDR R1[R0];取低32位数

 EOR R1R1R2;取反

STR R1[R0];存低32位反码

 ADDS R1R1#1;又加1为求补

STR R1[R0#8] ;存低32位补码

LDR R1[R0#4] ;取高32位数

 EOR R1R1R2;取反

STR R1[R0#4] ;存高32位反码

 ADC R1R1#0;高32位求补

STR R1[R0#12] ;存高32位补码