在学习STM32前,需要了解的汇编思想
初始化
AREA STACK, NOINIT, READWRITE
__initial_sp
AREA RESET, DATA, READONLY
__Vectors DCD __initial_sp
DCD main
AREA |.text|, CODE, READONLY
ENTRY
main
loop
B loop
END
解释:
1. AREA STACK, NOINIT,READWRITE: 这一行指定了一个名为STACK的内存区域,它用于存储堆栈。NOINIT表示这个内存区域在程序启动时不需要初始化,READWRITE表示这个内存区域可以被读取和写入。
2. __initial_sp: 这一行定义了一个名为__initial_sp
的符号,它表示堆栈指针的初始值。在这里,它被定义为堆栈的起始地址。通常,这个符号会在链接脚本中进一步定义为实际的堆栈内存区域的起始地址。
3. AREA RESET, DATA,READONLY: 这一行指定了一个名为RESET的内存区域,用于存储重置向量表。DATA表示这个区域包含数据,READONLY表示这个区域只能被读取。
4. __Vectors DCD __initial_sp: 这一行定义了一个重置向量表,用于指示程序启动时应该执行的操作。DCD
表示存储一个双字(32位)的数据。在这里,第一个双字存储的是堆栈指针的初始值,即__initial_sp。
5. DCD main: 这一行将程序的入口地址(main函数)添加到重置向量表中。这意味着程序在启动时将跳转到main函数开始执行。
6. AREA |.text|, CODE,READONLY: 这一行指定了一个名为.text的内存区域,用于存储代码。CODE表示这个区域包含代码,READONLY表示这个区域只能被读取。
7. ENTRY main: 这一行指定了程序的入口点为main函数。这意味着程序将从main
函数开始执行。
8. loop B loop: 这一行是一个无限循环,它会不断地跳转到loop
标签所在的位置,导致程序永远循环执行这条指令。
9. END: 这一行表示程序的结束。
数据搬运指令MOVS
<font style="color:rgb(44, 44, 54);">MOVS</font>
指令用于在寄存器之间移动数据或从立即数(一个常数值)到寄存器。它的基本格式如下:
<font style="color:#ee9900;">MOVS R0</font><font style="color:#999999;">,</font><font style="color:#000000;">#</font><font style="color:#990055;">2</font>
<font style="color:#ee9900;">MOVS R1</font><font style="color:#999999;">,</font><font style="color:#ee9900;">R0</font>
内存操作
读写
LDR
:指令主要用于从内存中加载数据到寄存器,或者可以用来处理地址。
LDR R2,=0X12345678
将数据写入寄存器
LDR R2,[R1]
将R2地址中的内容放入R1,相当于读
MOVS与LDR关键区别
- 用途:
<font style="color:rgb(44, 44, 54);">MOV</font>
主要用于寄存器之间的数据移动或加载立即数,而<font style="color:rgb(44, 44, 54);">LDR</font>
用于从内存中加载数据或处理地址。 - 操作数:
<font style="color:rgb(44, 44, 54);">MOV</font>
可以使用寄存器或立即数作为源操作数,而<font style="color:rgb(44, 44, 54);">LDR</font>
需要一个内存地址或地址表达式作为源。 - 性能影响:由于
<font style="color:rgb(44, 44, 54);">LDR</font>
涉及到内存访问,它通常比<font style="color:rgb(44, 44, 54);">MOV</font>
更慢,因为后者只涉及CPU内部的寄存器操作。
STR R0,[R1]
将R0的值写入到R1指向的地址
CPU栈机制
概念
栈是一种具有特殊的访问方式的存储空间,他的特殊性就在于最后一个进入这个空间的数据,是最先出去的
分类
- 空栈:指向的地址为空,可以直接存储,存储完成之后需要将栈指针再次指向空的位置。
- 满栈: 栈指针指向的地址有数据,在栈中存储数据时,需要先将栈指针,指向一个空的位置,然后在存储数据。
- 增栈:栈针向高地址移动
- 减栈:栈针向高地址移动
ARM默认采用的是满减栈
栈的基本操作
栈有两种基本的操作:入栈和出栈
- 入栈:将一个新的元素放到栈顶
- 出栈:从栈顶取出一个元素
- 先进后出,后进先出
PUSH和POP
汇编中的两个指令
PUSH
:入栈(压栈)
<font style="color:#000000;">PUSH {R0-R3,LR}</font>
POP
:出栈(弹栈)
<font style="color:#000000;">POP{R0-R4,PC}</font>
例子
- 主程序给R1,R2,R3分别写入数据
然后跳入函数JUMP
2.函数JUMP,函数内修改R1 R3的数值
3.修改结束后返回主函数
4.主函数再使R1和R3原来的数据相加后存入R1
- 跳出主程序进入死循环
AREA STACK, NOINIT, READWRITE
__initial_sp
AREA RESET, DATA, READONLY
__Vectors DCD __initial_sp
DCD main
AREA |.text|, CODE, READONLY
ENTRY
main
ADD SP,SP,#0X100;向上移动栈针,因为是满减栈
LDR R1,=0X12345678
LDR R2,=0X22222222
LDR R3,=0X33333333;赋值
BL JUMP;跳转到JUMP函数并且保存返回地址
ADD R1,R1,R3
B loop ;跳转到loop函数
JUMP
PUSH {{R0-R3,LR};保护现场,将原来的数据压栈,并在最后保存了函数的返回地址
;进行操作
LDR R1,=0X00000001;
LDR R3,=0X10000000;
POP{R0-R3,PC} ;恢复现场,将数据出栈,恢复了R0~R3的值,并将保存的地址赋给PC指针,程序将跳转值函数调用位置运行
loop
B loop
END