EBU5476 Microprocessor System Design 知识点总结_3 Assembly

Assembly

汇编语法。

顺序结构

label							; 可省略,用于跳转到此位置
	助记符 operand1, operand2, … ; Comments
	
MOV r1, #0x01					; 数据0x01放入r1
MOV r1, #'A'					; 数据A的ascii码放入r1
MOV R0, R1 						; move R1 into R0
MOVS R0, R1 					; move R1 into R0, 并且更新APSR的状态

LDR R1, [R0]					; R0存的是一个地址值如0x2000 0000, 这个指令是取出R0代表的地址中的数据存入R1
STR R1, [R0]					; 写回去
LDR R0, =0x12345678 			; Set R0 to 0x12345678
; 等效于:
; LDR R0, [PC, #offset] 
; ...
; DCD 0x12345678
; 也就是先在文档末尾的一条指令里写入数据0x12345678,然后编译器自动计算PC+多少offset到达DCD的位置,把其值返给R0
; DCD是声明一个字 32bit,DCB是声明一个Byte
; 如果多个数值的声明可以用标签声明
LDR R3, =MY_NUMBER

ALIGN 4 ; 字要先用这个声明,代表停止长度
MY_NUMBER DCD 0x2000ABCC
HELLO_TEXT DCB “Hello\n”, 0 ; Null terminated string


LDRB R1, [R0]					; B: 只写8位,就是说R0地址处的数据写入R1后,R1高24位清零
SDRH R1, [R0]					; H: 只写16位

LDRSH R1, [R0]					; 视作signed有符号数,写16位

LDRB R0, [R1, #0x3]				; 从R1+3读取一个字节给R0
LDR R3, [R0, R2, LSL #2]		; 从R0+(R2<<2)读取一个字节给R3
LDR R0, [R1], #4				; 赋完值后,令R1=R1+4

ADD R0, R0, R1
ADDS R0, R0, R1					; 加完更新APSR状态,比如有溢出或者进位则更新
ADC R0, R1, R2					; R1+R2还要+APSR的carry位

; SUB SBC类似

MUL R0, R1, R2
UDIV R0, R1, R2
SDIV R0, R1, R2					; signed

例题:应该是因为有可能减成负的所以signed

1686672985123

指令有1字长,半字长的。hw1是指明功能用的,hw2是一些拓展比如立即数。

1686713591015

地址从低到高分别是:4F F0 0A 00 0A 68 10 44……

PC每次取到半个字 hw,就+2B跳转到下一个hw。

选择结构

	CMP R0, R1						; 相当于if,比较后更新APSR。EQ= LT< GT> LE<= GE >=
	BEQ BRANCH_1					; B是跳转,BL是跳转到函数执行完后返回,BX是根据地址最低位判断目标地址是arm还是thumb在决定跳转到整字还是半字。bx操作数不能是立即数,必须是寄存器
	B BRANCH_2
	
BRANCH_1
	...
	B IFEND							; 不写这个就继续执行BRANCH_2了,像switch的break
BRANCH_2
	...
B IFEND

循环结构

WHILE_BEGIN 
	UDIV R2, R0, R1 ; R2 = n / x
	MUL R3, R2, R1 ; R3 = R2 * x
	CMP R0, R3 ; n == (n / x) * x
	BEQ WHILE_END
	SUBS R1, R1, #1 ; x--
	B WHILE_BEGIN ; loop back
WHILE_END

Stack

内存中有一片内存空间类似栈的数据结构。SP指针指向栈顶。

这个栈地址是从高到低的,也就是存入数据 SP–,取出数据 SP++,类似一个翻转过来的,倒着的书堆。

满堆栈:sp指针指向最后一个栈顶数据。

空堆栈:指向最后一个数据的下一个要放入数据的空位置。

我们的课程中使用空堆栈,指向下一个空位置,存数据就先存入再SP-4,取数据就先SP+4再出栈。不过这两条指令都不需要我们手动执行,有专门的指令:

PUSH {R0, R4-R7} 	; Push r0, r4, r5, r6, r7
POP {R2-R3, R5} 	; Pop to r2, r3, r5。入栈出栈顺序不是按照书写顺序而是自动根据寄存器地址,高地址值给高地址寄存器

存入5个数据和取出3个数据。

Functions

BL先保存当前PC值到LR,然后PC跳转到函数地址,

BX LR跳转到LR中的地址用于函数返回。

Architecture Procedure Call Standard (AAPCS) :规范定义哪些寄存器主函数和函数通用,哪些是独有的。

arm AAPCS规定:r0-r3是通用寄存器(类似全局变量),但main和函数的R4 – R8, R10-R11不通用(类似临时变量,到了函数里这些值就变了,不是原函数的),要压入栈保存。函数调用和返回的时候要保存和恢复通用寄存器值。这些由调用原函数的子函数 callee-procedure 执行。

简单的参数的函数调用:传参给R0-R3作为函数参数,R4-R11压入栈,然后跳转到函数处。

1686733381292

Program Memory Use

ROM里都是只读数据,比如常量常数。

1686733480351

const, static, volatile

貌似是不会过多涉及具体代码实现的部分,就先简单介绍一下了。

const 就是定义常量变量,定义后无法再次修改。

static 通常定义静态函数,静态函数里的值是通用的,也就是每次调用该函数其值都是接着上次调用该函数的值继续。

volatile:一个在嵌入式里挺重要的东西,软考题里出现过几次。大概就是禁止编译器优化该变量来防止不必要的错误。

比如编译器优化num变量,这样每次修改num变量的值的时候都不会立刻写入内存中,可能会先把修改时的值写入寄存器,函数返回时写回内存。

现在比如我们在main中num+=5, 修改值后的num暂时存在寄存器里。然后我们调用中断,从内存中读取当前num的值并+1.但是内存中值还没改,还是原值。返回后,main再把自己手中的num值写回内存,最后内存中num值只+5,而不是我们期望的+6.

volatile 声明后的变量不会做这样的优化,值改变了就立刻写回内存,虽然可能效率低但是安全。

猜你喜欢

转载自blog.csdn.net/jtwqwq/article/details/131213195