STM32单⽚机-汇编指令1
⽬录:
1、ldr/str/mov指令
1)LDR指令  2)STR和LDRB指令  3)MOV指令  4)使⽤⽰例
2、MOVS指令
3、LDM表⽰装载,STM表⽰存储
4、teq指令
5、使⽤tst来检查是否设置了特定的位
6、'^'的理解
7、spsr_cxsf,cpsr_cxsf的理解
8、cpsr的理解
9、指令后缀和条件判断
10、B、BL、BX、BLX 和 BXJ指令的区别
1)B 指令  2)BL 指令  3)BLX 指令  4)BX 指令和BXJ指令  5)总结
--------------------------------------------
说明:
ARM单⽚机汇编指令的查见“之⼗五、查汇编指令”。
--------------------------------------------
.macro restore_user_regs //宏
ldr r1,[sp, #S_PSR]
ldr lr,[sp, #S_PC]!  @ !⽤来控制基址变址寻址的最终新地址是否进⾏回写操作,
@ 执⾏ldr之后sp被回写成sp+#S_PC基址变址寻址的新地址
msrspsr,r1              @ 把cpsr的值保存到spsr中
ldmdb sp,{r0 - lr}^@lr=[sp-1*4],r13=[sp-2*4],r12=[sp-3*4],......,r0=[sp-15*4]
@ 因为没对pc赋值,所以^的表⽰将数据恢复到User模式的[r0-lr]寄存器组中[gliethttp]  mov r0,r0
add sp,sp,#S_FRAME_SIZE - S_PC
movs pc,lr
.endm
-------------------------------------------------------------------------------------------------------
1、ldr/str/mov指令
1)LDR指令
LDR格式:LDR{条件}  ⽬的寄存器  <;存储器地址>
LDR作⽤:将存储器地址所指地址处连续的4个字节(1个字)的数据传送到⽬的寄存器
⽐如想把数据从内存中某处读取到寄存器中,只能使⽤ldr
⽐如:ldr r0, 0x12345678
就是把0x12345678这个地址中的值存放到r0。
⽽mov不能⼲这个活,mov只能在寄存器之间移动数据,或者把⽴即数移动到寄存器中。
-------------------
2)STR和LDRB指令
STR格式:STR{条件}  源寄存器,<;存储器地址>
STR作⽤:STR指令⽤于从源寄存器中将⼀个32位的字数据传送到存储器中。该指令在程序设计中⽐较常⽤,寻址⽅式灵活多样,使⽤⽅式可参考指令LDR。
LDRB:字节数据加载指令
-------------------
3)MOV指令
MOV格式:mov source, destination
MOV作⽤:source 和 destination 的值可以是内存地址,存储在内存中的数据值,指令语句中定义的数据值,或者寄存器。
-------------------
4)使⽤⽰例
ldr ip,[sp],#4 //将sp中内容存⼊ip,之后sp=sp+4;
ldr ip,[sp,#4] //将sp+4这个新地址下内容存⼊ip,之后sp值保持不变
ldr ip,[sp,#4]! //将sp+4这个新地址下内容存⼊ip,之后sp=sp+4将新地址值赋给sp
str ip,[sp],#4 //将ip存⼊sp地址处,之后sp=sp+4;
str ip,[sp,#4] //将ip存⼊sp+4这个新地址,之后sp值保持不变
str ip,[sp,#4]! //将ip存⼊sp+4这个新地址,之后sp=sp+4将新地址值赋给sp
-------------------------------------------------------------------------------------------------------
2、MOVS指令
movs r1,#3 ; //movs将导致ALU被更改,因为r1赋值⾮0,即操作结果r0⾮0,所以ALU的Z标志清0
bne 1f  ; //因为Z=0,说明不等,所以向前跳到标号1:所在处继续执⾏其他语句
MOVS pc,r14_und; //cpsr(状态寄存器)←spsr_und(备份状态寄存器),pc←r14_und
相关指令详见“嵌⼊式⽹络那些事LwIP协议深度剖析与实战演练P42下⽅”。
MOVS总是会影响cpsr, 包括N,Z,C标志位,执⾏MOVS pc,lr(也就是r14寄存器)时,cpsr会被spsr覆盖,如上。内核态,USER和SYSTEM模式下没有spsr。
-------------------------------------------------------------------------------------------------------
3、LDM表⽰装载,STM表⽰存储
LDMED LDMIB 预先增加装载
LDMFD LDMIA 过后增加装载
LDMEA LDMDB 预先减少装载
LDMFA LDMDA 过后减少装载
STMFA STMIB 预先增加存储
STMEA STMIA 过后增加存储
STMFD STMDB 预先减少存储
STMED STMDA 过后减少存储
注意ED不同于IB;只对于预先减少装是相同的.在存储的时候,ED是过后减少的.
FD、ED、FA、和 EA 指定是满栈还是空栈,是升序栈还是降序栈.
对于存储STM⽽⾔
先加后存 FA 姑且这么来记,先加(first add),存数据
后加先存 EA 姑且这么来记,存数据,后加end add
先减后存 FD 姑且这么来记,先减first dec,存数据
后减先存 ED 姑且这么来记,存数据,后减end dec
然后记忆LDM,LDM是STM的反相弹出动作,所以
因为是先加后存,所以后减先取 FA 就成了与STM对应的取数据,后减
因为是后加先存,所以先减后取 EA 就成了与STM对应的先减,取数据
因为是先减后存,所以后加先取 FD 就成了与STM对应的取数据,后加
因为是后减先存,所以先加后取 ED 就成了与STM对应的先加,取数据
我想通过上⾯的变态⽅式可以⽐较容易的记住这套指令[gliethttp]
⼀个满栈的栈指针指向上次写的最后⼀个数据单元,⽽空栈的栈指针指向第⼀个空闲单元.
⼀个降序栈是在内存中反向增长(就是说,从应⽤程序空间结束处开始反向增长)⽽升序栈在内存中正向增长.
其他形式简单的描述指令的⾏为,意思分别是
IA过后增加(Increment After)、
IB预先增加(Increment Before)、
DA过后减少(Decrement After)、
DB预先减少(Decrement Before).
RISC OS使⽤传统的满降序栈.在使⽤符合APCS规定的编译器的时候,它通常把你的栈指针设置在应⽤程序空间的结束处并接着使⽤⼀个FD(满降序-Full Descending)栈.如果你与⼀个⾼级语⾔(BASIC或C)⼀起⼯作,你将别⽆选择.栈指针(传统上是R13)指向⼀个满降序栈.你必须继续这个格式,或则建⽴并管理你⾃⼰的栈.
-------------------------------------------------------------------------------------------------------
4、teq指令
teq r1,#0      //r1-0,将结果送⼊状态标志,如果r1和0相减的结果为0,那么ALU的Z置位,否则Z清0
bne reschedule//ne表⽰Z⾮0,即:不等,那么执⾏reschedule函数
-------------------------------------------------------------------------------------------------------
5、使⽤tst来检查是否设置了特定的位
tst r1,#0x80 //按位and操作,检测r1的0x1<<7,即第7位是否置1,按位与之后结果为0,那么ALU的Z置位
beq reset    //如果Z置位,即:以上按位与操作结果是0,那么跳转到reset标号执⾏
-------------------------------------------------------------------------------------------------------
6、'^'的理解
'^'是⼀个后缀标志,不能在User模式和Sys系统模式下使⽤该标志.该标志有两个存在⽬的:
1)对于LDM操作,同时恢复的寄存器中含有pc(r15)寄存器,那么指令执⾏的同时cpu⾃动将spsr拷贝到cpsr中如:在IRQ中断返回代码中[如下为ads环境下的代码gliethttp]
ldmfd {r4}          //读取sp中保存的的spsr值到r4中
msr spsr_cxsf,r4    //对spsr的所有控制为进⾏写操作,将r4的值全部注⼊spsr
ldmfd {r0-r12,lr,pc}^//当指令执⾏完毕,pc跳转之前,将spsr的值⾃动拷贝到cpsr中[gliethttp]汇编判断指令
-------------------
2)数据的送⼊、送出发⽣在User⽤户模式下的寄存器,⽽⾮当前模式寄存器
如:ldmdb sp,{r0 - lr}^;表⽰sp栈中的数据回复到User分组寄存器r0-lr中,⽽不是恢复到当前模式寄存器r0-lr
当然对于User,System,IRQ,SVC,Abort,Undefined这6种模式来说[gliethttp]r0-r12是共⽤的,只是r13和r14  为分别独有,对于FIQ模式,仅仅r0-r7是和前6中模式的r0-r7共⽤,r8-r14都是FIQ模式下专有.
-------------------------------------------------------------------------------------------------------
7、spsr_cxsf,cpsr_cxsf的理解
c - control fiel
d maskbyte(PSR[7:0])
x - extension field maskbyte(PSR[15:8])
s - status field maskbyte(PSR[23:16)
f - flags field maskbyte(PSR[31:24]).
⽼式声明⽅式:cpsr_flg,cpsr_all在ADS中已经不在⽀持
cpsr_flg对应cpsr_f
cpsr_all对应cpsr_cxsf
需要使⽤专⽤指令对cpsr和spsr操作:mrs,msr
mrs tmp,cpsr      //读取CPSR的值
bic tmp,tmp,#0x80 //如果第7位为1,将其清0
msr cpsr_c,tmp    //对控制位区psr[7:0]进⾏写操作
-------------------------------------------------------------------------------------------------------
8、cpsr的理解
CPSR = Current Program Status Register
SPSR = Saved Program Status Registers
CPSR寄存器(和保存它的SPSR寄存器)
(上图)
N,Z,C,V称为ALU状态标志
N:如果结果是负数则置位
Z:如果结果是零则置位
C:如果发⽣进位则置位
V:如果发⽣溢出则置位
I:置位表⽰禁⽤IRQ中断,清0表⽰使能IRQ
F:置位表⽰禁⽤FIQ中断,清0表⽰使能FIQ
T:置位表⽰系统运⾏在Thumb态,清0表⽰运⾏在ARM态
M[4:0]:
10000 User模式,和System系统模式⼀样
10001 FIQ模式
10010 IRQ模式
10011 SVC超级管理模式
10111 Abort数据异常模式
11011 Undefined未定义指令模式
11111 System系统模式,和User模式⼀样
举例:
ands r2,r2,#7 使⽤运算结果改变标志位,如果运算结果r2=0,那么Z置位,EQ相等判断成⽴
subs r2,r2,#1 使⽤运算结果改变标志位,如果运算结果r2=0,那么Z置位,EQ相等判断成⽴
beq wordcopy
-------------------------------------------------------------------------------------------------------
9、指令后缀和条件判断
(上图)
EQ : 等于
NE : 不等
CS : ⽆符号>=
CC : ⽆符号<</span>
MI : 负数
PL : ⾮负[>=0]
VS : 溢出
VC : ⽆溢出
HI : ⽆符号>
LS : ⽆符号<=
GE : 有符号>=
LT : 有符号<</span>
GT : 有符号>
LE : 有符号<=
AL : 总是[默认]
对于arm汇编指令,可以参考linux内核的arch/arm⽬录,那⾥的汇编指令很丰富[gliethttp_20080603]
__CopyFromStart
;    ldr    r3, [r9],#4
;
    str    r3, [r7], #4
;    sub  r8, r8, #4
ldrb  r3, [r9], #1
strb  r3, [r7], #1
sub  r8, r8, #1
cmp  r8, #0
bgt    __CopyFromStart
b      __JumpToBootImage
__JumpToBootImage
MOV    pc, r0
-------------------------------------------------------------------------------------------------------
10、B、BL、BX、BLX 和 BXJ指令的区别
跳转指令⽤于实现程序流程的跳转,在 ARM 程序中有两种⽅法可以实现程序流程的跳转:
使⽤专门的跳转指令。
直接向程序计数器 PC 写⼊跳转地址值。
通过向程序计数器 PC 写⼊跳转地址值,可以实现在 4GB 的地址空间中的任意跳转,在跳转之前结合使⽤
MOV LR , PC
等类似指令,可以保存下⼀条指令地址作为将来的返回地址值,从⽽实现在 4GB 连续的线性地址空间的⼦程序调⽤。专门的跳转指令

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。