练习一:逐个点亮8只LED。(本实验箱LED按负逻辑工作:输出0则灯亮。)
算法:预设一个灯全灭的值(比如FFH),然后在每次循环中,对预设值重复进行“左移1位、输出”的操作(每次从低位补一个0)。8次以后(8位全0),再从头开始。
/*************************led1.s*******************************/
AREA SAMPLE,CODE,READONLY ;注意不能顶格写
ENTRY ;入口
LDR R0, =0x20000000 ;标号要顶格,本实验箱8只LED所连接的端口地址是0x20000000
LOOP MOV R1, #0xff
MOV R3, #0
LOOP1 MOV R1, R1, LSL #1 ;R1左移1位低位补0,把R1左移1位的值赋给R1
STRB R1, [R0] ;B是可选后缀,表示字节操作,若有B则输出/存储R1的最低字节,把R1的值存储到0x20000000中,此时流水灯的状态被改变
ADD R3, R3,#1 ;R3起计数器的作用,流水灯改变一次状态加1
CMP R3, #8 ;将寄存器R3的值与立即数8相减,根据结果设置CPSR中的相应标志位,比较结果为0,CPSR中的Z位置为1,比较结果非0时,Z为0
BNE LOOP1 ;如果R3和#8不相等(Z为0)就跳到LOOP1,继续改变流水灯的状态,否则向下执行
B LOOP ;8个流水灯改变了1轮状态了,从新赋值改变状态,跳到LOOP
END
注意:此程序没有延时,所以连续运行会使点灯速度太快,看不出效果。只能单步运行!
练习二:8只LED流水灯。
算法:与上题的区别是:造一个8位2进制数,其中只有一个位为0, 循环中使0的位置不断移动。为了保证这个8位2进制数只有一位0,不能用shift移位指令(shift移位会补0),而应该用rotate循环移位。由于ARM只有循环右移指令,所以亮灯的次序只能是从高位开始。
/*************************led1.s*******************************/
AREA SAMPLE,CODE,READONLY ;注意不能顶格写
ENTRY ;入口
START LDR R0, =0x20000000 ;标号要顶格
LOOP LDR R1, =0xfffffeff ;e的二进制代码:1110,0xfffffeff 右移一位低8位的最高位刚好是0,小灯亮
MOV R3, #0 ;R3同上个程序相同也是起到计数器作用
LOOP1 MOV R1, R1, ROR #1 ;R1循环右移1位的值赋给R1,这里只用普通的右移也可以,因为小灯挨个亮完之后直接跳到LOOP了,又重新给R1赋值
STRB R1, [R0] ;把R1的值存储到0x20000000中,此时流水灯的状态被改变
BL DELAY ;程序无条件跳到DELAY去执行,同时PC的值保存到R14中
ADD R3, R3,#1 ;计数器R3加1
CMP R3, #8 ;将寄存器R3的值与立即数8相减,根据结果设置CPSR中的相应标志位,比较结果为0,CPSR中的Z位置为1,比较结果非0时,Z为0
BNE LOOP1 ;如果R3和#8不相等(Z为0)就跳到LOOP1,继续改变流水灯的状态,否则向下执行
B LOOP ;8个流水灯改变了1轮状态了,从新赋值改变状态,跳到LOOP
GET E:\LED\DELAY.S ;汇编时子程序文件的内容就会展开在GET指令处,子程序的代码会复制到GET伪指令的位置
END`在这里插入代码片`
延时汇编程序 DELAY.S
DELAY LDR R2, =0xfffff
LDR R8, =0x0
LOOP2 SUB R2, R2, #1
CMP R2, R8
BNE LOOP2
MOV PC,LR
END
ARM的子程序调用指令:BL 子程序名 (子程序第一句的标号!)
子程序返回指令:MOV PC, LR (写在子程序的结尾,等效于RET!)
处理器执行子程序调用指令BL时,自动把返回地址保存到R14中(LR)。(如果子程序中又调用子程序,那必须对R14的值进行保存。可用寄存器、存储器、堆栈来保存。如果使用堆栈保存的话,还得设置堆栈指针的初始值。)
ARM程序中的子程序可以有三种调用形式:
(1)直接把子程序写在主程序文件中。可在主程序任何位置调用。
这种形式虽然简单,但是子程序无法被其他程序使用。(代码重用性不好!)
(2)子程序单独写成一个文件(不需要添加到工程中、可在任何文件夹下)。在主程序文件的结尾(END之前),使用【GET 路径\子程序文件全名】伪指令,把子程序文件包含到主程序中(汇编时子程序文件的内容就会展开在GET指令处),然后可在主程序的任何位置写出调用指令。(本次实验就练习这种形式。请注意:这种形式的调用类似8086汇编语言中宏汇编中的宏展开,子程序的代码会复制到GET伪指令的位置,最终得到与上面说的(1)相同的效果。)
(3)子程序单独写成一个文件,添加到工程中。子程序文件的内容要以【AREA】开头,还要在文件中用到【IMPORT】和【EXPORT】伪指令。(实验五中练习)
关于(2)、(3)所述子程序文件的写法要点:任何独立的汇编语言的程序文件,都要有段定义、都要有END,但是不能随便有ENTRY!
注意:
1 .只有标号要顶格写,并且没有冒号
2.GET指令相当于 子程序的代码复制到GET伪指令的位置,使用GET的汇编程序的子程序中的某段代码需要调用它,子程序中不能使用和主程序一样的寄存器,标号,这个时候如果保护现场效果应该会很好,单纯的避开主程序中的寄存器,在其他程序中如果又冲突了还需要改子程序,如果主程序用到的寄存器很多,子程序就没有足够的寄存器使用了。保护现场可把子程序中使用到的寄存器都保护起来,这样就不会冲突了。标号好像没有办法,因为它是直接复制展开的,只能避开使用和主程序相同的标号。
3.如果把GET指令写在调用延时函数,DELAY.S里边删除掉 MOV PC,LR即可,但这种使用很少。