ARM 编程

分支

X - Y

int x = 0x1234;
int y = 0x5678;
if (x > y) {
    z = x - y;
    z = 7 * z;
} else {
    z = y - x;
    z = 9 * z;
}
.text
.globl _start
_start:
	LDR R0, =0x1234
	LDR R1, =0x5678
	
	CMP R0,R1
	BHI XY
	BLS YX

XY:
	SUB R2,R0,R1
	RSB R2,R2,R2,LSL #3  @ 逆向减
	
YX:	
	SUB R2,R1,R1
	ADD R2,R2,R2,LSL #3
	
	b .			@ B .表示一直在当前指令处原地跳转,也就是死循环
.end

大于小于

<	:	CC			>=	:	CS
>	:	HI			<=	:	LS
=	:	EQ

完成如下动作

.text
.globl _start
_start:
	# 给R0赋值为3 	0x00000003
	MOV R0,#3   
	# 给R1赋值为4	0x00000004 
	MOV R1,#4	
	# 将R0逻辑左移R1当前值位			0x00000006
	MOV R0,R0,LSL #1   
	# 将R0减5						  0x00000001
	SUB R0,R0,#5
	# 将R0第2位取反				   0x00000041
	EOR R0,R0,#0x40
	# 将R0第3位置0   				0x00000041
	AND R0,R0,#0xDF   
	# 将R0第4为置1					0x00000051
	ORR R0,R0,#0x10
	B .						
.end

函数的应用

如何在汇编中实现函数调用

通过 BL func进行函数调用

函数中如何返回

两种方法:

  1. BX LR 更通用一些,可以在Thumb和ARM间互相调用
  2. MOV PC,LR

PC R15 里面存放将要执行的指令的地址
LR R14 里面存放要返回到的指令的地址

一般在实际编程中:R13写成SP,R14写成LR,R15写成PC

函数调用过程中如何传参

参数依次用r0,r1,r2,r3来进行传递
函数的返回值首选r0来进行传递,如果有两个结果则首先R0,R1,。。。。。。

函数定义和调用的模板

/*函数调用*/

@通过给R0、R1、R2、R3设置值,来向func函数传递参数,用几个寄存器,根据函数的参数个数决定,绝大多数函数的参数个数不会超过4个
BL func  @调用名为 func 的函数

/*函数定义*/
func:
	@ 功能实现
	@ 通过给R0设置值,来向函数调用处返回实现结果,如果结果有多个则继续使用R1、R2....,绝大多数函数的实现结果是一个
	BX LR @函数返回

add 函数

.text
.globl _start
_start:
	MOV R0,#3
	MOV R1,#5
	BL foo
	B .
foo:
	ADD R0,R0,R1
	MOV PC,LR @BX LR
		
.end

求三角形周长

.text
.global _start
_start:
	MOV R0,#3   @a  
	MOV R1,#4   @b
	MOV R2,#5	@c
	BL TRI_C
	B .
	
TRI_C:
	ADD R0,R0,R1
    ADD R0,R0,R2
	BX LR
	
.end

大小端

内存序:整数在内存的存放次序

12345678

小端 78 56 23 12 X86

大端 12 34 56 78 Power PC、ARM(默认,但可改)

LDR/STR

LDR:取地址里的数,如LDR R1, [R0] @ 将 R0 地址的 内容放入 R1 中 (先看后面的)

STR:将数存入地址,如STR R1, [R0] @ 将 R1 的内容放入 R0 所对应的地址里 (先看前面的)

.text		
.globl _start
_start:
	LDR R0,=0x11223344  	
	MOV R1,#0x40000000
	STR R0,[R1]		@ 将 R0 的内容放入 R1 的内容的地址
	LDRB R2,[R1]	@ 读取 R1 地址的前两个 2 进制
	B .
.end

字数据和8位无符号字节数据Load和Store的寻址方式

基址变址寻址:在基础地址上做一些改变形成新地址

基址:基础地址,用寄存器来存放,因此这样的寄存器被称为基址寄存器

不索引:LDR R1, [R0] 存到R1中

前索引:LDR R3, [R0,#4]! 存到R1中,更新地址 (先使用,再修改地址) 如:找出数组中最小数,先将之前的R0放入R3,再改变地址

后索引:LDR R1, [R0],#4 更新地址,存到R1中 (先修改地址,再使用)

LDR|STR{cond}{B}{T} ,
有九种方式:访问内存用的地址是[]里计算出来的地址
不索引三种:先计算新地址但不更新基址寄存器,然后用新地址访问内存
[,#+/-]

[,+/-]

[,+/-, #]

前索引三种:先计算新地址并更新基址寄存器,然后用新地址访问内存

[,#+/-]!

[,+/-]!

[,+/-, #]!

后索引三种:先用基址寄存器的地址访问内存,然后计算新地址并更新基址寄存器

[],#+/-

[],+/-

[],+/-, #

两数相加

将内存中的两个数相加并保存到内存

.text		
.globl _start
_start:
	LDR R0, =Value1 		@ 将 Value1 的地址放入 R0 中
	LDR R1, [R0] 			@ 将 R0 地址的 内容放入 R1 中
	LDR R0, =Value2 
	LDR R2, [R0] 
	# 两数x
	ADD R1, R1, R2 
	LDR R0, =Result 
	STR R1, [R0] 			@ 将 R1 的内容放入 R0 所对应的地址里
	B .
.data
Value1: .word 0x11111111
Value2: .word 0x22222222
Result: .word 0x0
.end

两数比较

LDR 取后地址的内容 后面有立即数,地址加上立即数

STR 存前到地址的内容

@找内存中两个32位数的最大数,并将最大数保存到新的内存块
.text		
.globl _start
_start:
	LDR R0, =Value1 
	LDR R1, [R0],#4 		@ 将 R0 地址的内容放入 R1 ,并且 R0 地址 + 4
	LDR R2, [R0]			@ 将 R0 地址的内容放入 R2
    
	CMP R1,R2
    MOVHI R3,R1				@ > 
    MOVLS R3,R2				@ <=
	
	LDR R0, =Result 		@ 将 Result 地址的内容放入 R0
	STR R3, [R0] 			@ R3(大的值)放入 R0 (result) 中
	B .
.data
Value1: .word 0x11111111
Value2: .word 0x22222222
Result: .word 0x0
.end

数组

数组相加

ARM汇编编写程序,程序功能为:定义一个数组,数组中包含4个32位无符号整数,找出最大值,并将结果保存到内存

@定义一个数组,数组中含4个32位数,求数组中元素的总和,并将结果保存到内存
.text		
.globl _start
_start:
	LDR R0,=Length
	LDR R2, [R0] 			@ R2  数组的长度

	LDR R0, =Table 
	EOR R1, R1, R1 			@ 清零
Loop:
	LDR R3, [R0],#4
	ADD R1, R1, R3
	SUBS R2, R2, #0x1
	BNE Loop
	
	LDR R0, =Result
	STR R1, [R0]
	B .

.data
Table: .word 0x1000,0x2000,0x3000,0x4000
TablEnd: .word 0
Length: .word (TablEnd-Table)/4 		
Result: .word 0 

.end

数组最小数

.text		
.globl _start
_start:
	LDR R0,=Length
	LDR R2, [R0] 			@ R2  数组的长度

	LDR R0, =Table 			@ Table 起始地址

	LDR R1, [R0]			@ R1 为数组中的第一个元素
	
	SUBS R2, R2, #0x1
Loop:

	LDR R3, [R0,#4]!			
	
	CMP R1,R3
	MOVHI R1,R3
	SUBS R2, R2, #0x1
	
	BNE Loop
	
	LDR R0, =Result
	STR R1, [R0]
	B .

.data
Table: .word 0x2000,0x1234,0x3000,0x4000
TablEnd: .word 0
Length: .word (TablEnd-Table)/4 		@ d
Result: .word 0 

.end

数组最大数

ARM汇编编写程序,程序功能为:定义一个数组,数组中包含4个32位无符号整数,找出最大值,并将结果保存到内存

.text		
.globl _start
_start:
	LDR R0,=Length
	LDR R2, [R0] 			@ R2  数组的长度

	LDR R0, =Table 			@ Table 起始地址

	LDR R1, [R0]			@ R1 为数组中的第一个元素
	
	SUBS R2, R2, #0x1
Loop:

	LDR R3, [R0,#4]!		
	
	CMP R1,R3
	MOVCC R1,R3
	
	SUBS R2, R2, #0x1
	
	BNE Loop
	
	LDR R0, =Result
	STR R1, [R0]
	B .

.data
Table: .word 0x2000,0x1234,0x5555,0x3000
TablEnd: .word 0
Length: .word (TablEnd-Table)/4 		
Result: .word 0 

.end

数组复制

.equ num,20
.text
.global _start
_start:
	LDR R0,=src
	LDR R1,=dst
	MOV R2,#num

blockcopy: @以8个字为单位复制
	MOVS R3,R2,LSR #3   @除以2的3次方,得到以8个字为单位复制的次数
	BEQ copywords
octcopy:
	LDMIA R0!,{R4-R11}
	STMIA R1!,{R4-R11}
	SUBS R3,R3,#1
	BNE octcopy

copywords:@以单个字为单位复制
	ANDS R2,R2,#7 @总字数对8取余,得到剩余要复制的字数
	BEQ stop
wordcopy:
	LDR R3,[R0],#4
	STR R3,[R1],#4
	SUBS R2,R2,#1
	BNE wordcopy

stop:
	B stop
.data
src:
	.word 1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,1,2,3,4
dst:
	.word 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

.end

实验

@ 亮第个灯
.text
.global _start
_start:
	 LDR R0,=0x114001E0 @GPF3CON
	 LDR R1,=0x114001E4 @GPF3DAT

	 /*Set CON 20~23 0001*/
	 LDR R2,[R0]			@ R2 0x00000000
	 BIC R2, #0xF00000		@ R2 0x00000000		清楚之前 R2 残余的数据
	 ORR R2,R2, #0x100000	@ R2 0x00100000
	 STR R2,[R0]			@ R0  0x114001E0	:	0x00100000
	 
	 /*LED5 ON*/
	 LDR R2,[R1]			@ R2 0x00000000
	 ORR R2, #0x20			@ R2 0x00200000
	 STR R2,[R1]			@ R1 0x114001E4		:	0x00200000

	 B .
.end

生成裸机可执行文件

# 编译各源文件得到对应的.o
"C:\Program Files (x86)\CodeSourcery\Sourcery G++ Lite\bin\arm-none-eabi-gcc.exe" -c myprogram.S
# 链接生成elf格式的可执行文件(注意次序,汇编源码的.o文件在前):
"C:\Program Files (x86)\CodeSourcery\Sourcery G++ Lite\bin\arm-none-eabi-ld.exe" -Ttext 0x40008000 myprogram.o -o led.elf
#  由elf格式的可执行文件生成裸机可执行文件(.bin文件)
"C:\Program Files (x86)\CodeSourcery\Sourcery G++ Lite\bin\arm-none-eabi-objcopy.exe" -O binary -S led.elf led.bin

下载并运行裸机程序

如何将文件从电脑发送到开发板的内存中?

loadb 0x40008000	

0x40008000代表开发板上内存的地fs址

点击 File -> Transfer -> Kermit -> Send 选择 led.bin 文件

C:\Users\admin\Documents\Tencent Files\xxxx\FileRecv led.bin

go 0x40008000

执行裸机程序led.bin

你可能感兴趣的:(arm,stm32,c语言)