## ARM基础编程实验
ARM基础编程实验
作者:毛茏玮 / Saint
掘金:https://juejin.im/user/5aa1f89b6fb9a028bb18966a
微博:https://weibo.com/5458277467/profile?topnav=1&wvr=6&is_all=1
GitHub:github.com/saint-000
****: https://me.****.net/qq_40531974
一、实验目的
1.熟悉并掌握常用ARM汇编指令
2.熟悉并掌握“C+汇编”混合编程技术
3.熟练使用ARM软件开发调试工具Keil
二、实验内容
1.学习使用Keil开发工具。
2.使用ARM汇编语言,编程实现1+2+……+N累加运算功能。
3.使用C调用汇编函数,实现字符串拷贝功能。
4.使用汇编调用C函数,实现求和运算功能。
5.使用ARM汇编语言,实现冒泡排序算法。
三、实验步骤
实验1.1:运行Keil,建立工程文件,单步运行调试演示示例程序,深刻理解每一条指令,观察寄存器,内存空间的变化。ARM数据处理指令寻址方式实验和ARM内存访问指令寻址方式实验,ARM堆栈指针寄存器实验、ARM程序计数器实验、ARM程序状态寄存器实验、ARM工作模式切换实验。
实验1.2:用ARM汇编实现1+2+…+N的累加运算
1.将实验1.2文件夹中的sum.s文件添加至工程中。
2.在文件中,用汇编实现算法核心部分,需添加的代码参考流程图如下:
3. 运行Debug进行调试。
实验1.3:理解C和汇编,并用汇编程序实现字符串拷贝,并在C程序中调用该汇编程序。
1.将实验1.3文件夹中的testfile.s和main.c文件添加至工程中。
2.在汇编文件testfile.s中添加两行汇编代码,分别实现:
(1) 拷贝源字符串的一个字节到R2中;
(2) 将拷贝的字节复制到目标空间。
(3)运行Debug进行调试。
实验1.4:在汇编中调用C函数。
1.将实验1.4文件夹中的sum.c和testfile.s文件添加至工程中。
2.在汇编文件testfile.s中相应位置添加汇编代码,通过调用c函数g()实现1+2+3+glovb1,结果存在R8中。
3.运行Debug进行调试,观察实验现象。
实验1.5:用ARM汇编实现冒泡算法。
1.将实验1.5文件夹中的maopao.s文件添加至工程中。
2.在汇编文件maopao.s中相应位置添加汇编代码,实现冒泡排序。
3.运行Debug进行调试。
4.在debug界面,点击Debug →Memory Map,修改地址分段属性。
5.观察实验现象
四、实验结果
实验1.1ARM数据处理指令寻址方式实验和ARM内存访问指令寻址方式实验中代码如下:
通过调试可以发现知道代码中的数据传送指令MOV;算术运算指令-加法指令ADD和减法指令SUB;以及比较指令CMP等数据处理指令在寄存器的数值和地址的变化,初步了解ARM的寻址方式以及数据传送方式。
实验1.2编程实现1+2+…+N 代码书写:
;功能:计算1+2+3+4+...+N
;说明;N>=0,当N=0时结果为0,当N=1时结果为1
N EQU 100 ;定义N的值100
AREA Examples,CODE,READONLY ;声明代码断Examples3
ENTRY ;标识程序入口
CODE32
ARM_CODE
LDR SP,=0X30003F00 ;设置堆栈指针
ADR R0,THUMB_CODE+1
BX R0 ;跳转并切换处理器状态
LTORG ;声明文字池
CODE16
THUMB_CODE
LDR R0,=N ;设置子程序SUM_N的入口参数
BL SUM_N ;调用子程序SUM_N
B THUMB_CODE
;SUM_N
;功能:计算1+2+3+......+N
;入口参数:R0 N的值
;出口参数:R0 运行结果
;占用资源:R0
;说明:当N=0时结果为0,当N=1时结果为1
;若运算溢出,结果为0
SUM_N
PUSH {R1-R7,LR} ;寄存器入栈保护
MOVS R2,R0 ;将n的值复制到R2,并影响相应条件标志
BEQ SUM_END ;若N=0,则返回,成立即返回
CMP R2,#1 ;比较R2是否为1
BEQ SUM_END ;若N=1,则返回,成立即返回
MOV R1,#1 ;初始化计数器R1=1 MOV R0,#0 ;初始化计数器R0=0
SUN_L1
ADD R0,R1 ;R0=R1+R0
BVS SUM_END ;溢出
CMP R1,R2 ;比较R1和n是否相等
BVS SUM_END ;相等,跳出循环,即加到n结束
ADD R1,#1 ;R1=R1+1;
B SUN_L1 ;跳转循环
SUM_ERR
MOV R0,#0 ;R0=0
SUM_END
MOV R8,R0 ;将结果保存在R8中
POP {R1-R7,PC} ;寄存器出栈,返回
END
实验结果:
最终寄存器R8的读数为为0X000013BA,数值为十进制的5050,正好是从1加至100之和,所以成功实现该功能。
实验1.3理解C和汇编,并用汇编程序实现字符串拷贝,并在C程序中调用该汇编程序。
在C程序中调用该汇编程序
;文件名:main.c
;功能:完成字符串的拷贝
#include <stdio.h>
extern void strcopy(char *d ,char *s);
int main(void)
{
char *srcstr = "aaaa";
char dststr[] = "bbbb";
//printf("Before copying:\n"); //仿真调试时禁用printf语句
//printf(" %s %s\n",srcstr,dststr);
strcopy(dststr,srcstr);
//printf("After copying: \n");
//printf(" %s\n %s\n",srcstr,dststr);
return (0);
}
;文件名:TEST.S
;功能:从C语言中调用汇编语言
AREA Example1,CODE,READONLY ;声明代码段Example1
CODE32 ;声明32位ARM指令
IMPORT __main
EXPORT strcopy
strcopy
LDRB R2,[R1],#1 ;拷贝源字符串的一个字节
STRB R2,[R0],#1 ;将拷贝的字节复制到目标空间,
CMP R2,#0 ;比较R2=0
BNE strcopy
MOV PC,LR
END
实验结果:
将原来的aaaa bbbb的值变成了aaaa aaaa,所以成功实现字符串复制。
实验1.4在汇编中调用C函数
;文件名:main.c
;功能:完成求和
int g(int a, int b ,int c,int d)
{
return a+b+c+d;
}
;文件名:TESTFILE.S
;功能:从汇编语言中调用C语言
IMPORT g
AREA Example1,CODE,READONLY
CODE32
ENTRY
START
MOV R0,#1
MOV R1,#2
MOV R2,#3
MOV R3,#10
BL g
MOV R8,R0
B START
END
实验结果:
最终寄存器R8读数为0X000010,正好为1+2+3+10=16,所以正确实现该功能。
实验1.5用ARM汇编实现冒泡算法。
AREA Sort,CODE,READONLY
ENTRY
start
MOV r4,#0
LDR r6,=src
ADD r6,r6,#len
outer
LDR r1,=src
inner
LDR r2,[r1]
LDR r3,[r1,#4]
CMP r2,r3
STRGT r3,[r1]
STRGT r2,[r1,#4]
ADD r1,r1,#4
CMP r1,r6
BLT inner
ADD r4,r4,#4
CMP r4,#len
SUBLE r6,r6,#2
BLE outer
src DCD 2,4,10,8,14,1,20
AREA Array,DATA,READWRITE
len EQU 7*4
END
实验结果:
从起始寄存器位置开始,每个四个输出一个数据,数据依次为1,2,4,8,10,14,20,由此完成了冒泡排序法。
五、实验总结
1、学习使用Keil开发工具。
2、完成了1+2+…+N求和,N=100时答案为5050。
3、C语言中调用了汇编语言,实现字符串复制。
4、汇编中调用了C语言函数,实现了实现求和运算功能。
5、用汇编语言实现了冒泡排序法,结果为1,2,4,8,10,14,20。
实验心得:
(1)在写冒泡排序过程中发现,如果使用杂项伪命令AREA定义数据段时, AREA Sort,CODE,READONLY这段代码要缩进,以及跟下面结束时的相同代码对齐,否则会出现编译错误。
(2)我们在使用memory map的时候,需要查看同一个寄存器的首地址和尾地址的数据变化,我们需要将程序刚开始的寄存器首地址和结束时的尾地址填入memory map中,注意格式如下:
六、实验思考题
1.ADD替换成ADDS,SUB替换成SUBS有什么影响?
运算结果不影响CPSR中相应标志位的值,跳转指令因为上一步的CPSR的值没有改变而无法正确执行。S用于决定指令的操作是否影响CPSR中的条件标志位。
2.MOV替换成MOVNE有什么影响?
只有在上一步计算结果为不相等时才执行。MOVNE先判断条件NE (不相等)则转移,可以看作是两条指令NE=NOT EQUAL不相等。
3.STMIA换成STMIB,STMIA换成STMDA有什么区别?
STMIA换成STMIB是将每次写入前地址+4改为地址-4,STMIA换成STMDA是将每次写入前地址+4改为+1 ,其中STMIB(地址先增而后完成操作)、STMFA(满递增堆栈);
STMIA(完成操作而后地址递增)、STMEA(空递增堆栈);STMDB(地址先减而后完成操作)、STMFD(满递减堆栈);STMDA(完成操作而后地址递减)、STMED(空递减堆栈)。
4.思考用ARM汇编实现1+3+5+…+(2n+1)或者2+4+6+…+2n。
实现1+3+5+„.+(2n+1):
将 add r1,r1,#1 改为 add r1,r1,lsl #1 sub r1,r1,#1
实现2+4+6+…..+2n:
将 add r1,r1,#1 改为 add r1,r1,lsl #1
5.实验3中如果去除汇编代码中的“EXPORT strcopy”会有什么现象,为什么?
C语言无法调用用strcopy函数。因为EXPORT伪指令用于在程序中声明一个全局的标号,该标号可在其他的文件中引用。
6.实验4中如果去除汇编代码中的“IMPORT ……”会有什么现象,为什么?
把“IMPORT ….注释后发现,编译调试时程序单步运行直接从汇编程序开始,说明无法调用C语言的main函数。IMPORT伪操作告诉编译器当前的符号不是在本源文件中定义的,而是在其他源文件中定义的,在本源文件中可能引用该符号,而且不论本源文件是否实际引用该符号,该符号都将被加入到本源文件的符号表中。