11、STMFD和LDMFD指令
1)STMFD SP! ,{R0-R7,LR} 2)LDMFD SP! ,{R0-R7,LR}
99、伪指令
1)PROC伪指令 2)EXPORT伪指令 3)IMPORT伪指令 4)DCD与DCDU伪指令
5)ALIGN伪指令 6)AREA伪指令 7)SPACE和DCD伪指令的区别 8)ENTRY伪指令
-----------------------------------------
说明:
STM32单片机-汇编指令使用1
STM32单片机-汇编指令使用2
-------------------------------------------------------------------------------------------------------
11、STMFD和LDMFD指令
根据ATPCS规则,一般使用FD(FullDescending)类型的数据栈!所以经常使用的指令就有STMFD和LDMFD。这两个指令一般用于进行程序搬移等大规模操作前的cpu现场保护和操作结束后的现场恢复,属于非单一连续的压栈和出栈。
1)STMFD SP! ,{R0-R7,LR}
意义是:
SP = SP-9x4
ADDRESS =SP
for i = 0 to 7
memory[address] = Ri
address =address+4
memory[address] = LR
由于ARM堆栈结构是从高向低压栈的,此时SP即是栈顶。
这里的sp = sp-4,是因为处理器是32位的ARM,所以每次压一次栈SP就会移动4个字节(32位)。
假设此时SP地址为: 0x40000460,减去0x24(9x4=36d)得0x4000043C,由前面解释伪代码可得下图(蓝色填充区为地址):
蓝色标注的SP为执行指令前的SP地址,红色标注的SP是执行指令后的SP地址,由此看出STMFD指令是向下压栈的。
------------------
2)LDMFD SP! ,{R0-R7,LR}
过程算是STMFD的逆过程
address = SP;
for i = 0 to 7
Ri = Memory[address ,4]
address = address + 4;
SP = address;
假设此时SP地址为: 0x4000043C,由前面解释伪代码可得下图(蓝色填充区为地址):
蓝色标注的SP为执行指令前的SP地址,红色标注的SP是执行指令后的SP地址。
-------------------------------------------------------------------------------------------------------
99、伪指令
1)PROC伪指令
一般格式:<过程名> PROC [类型]
此指令为过程定义指令。过程即子程序,一个过程可以被其他程序调用(CALL指令)。
<过程名> PROC [类型]
…….
…….
ENDP
注:PROC和ENDP必须成对出现。
------------------
2)EXPORT伪指令
语法格式:
EXTERN 标号 {[WEAK]}
EXTERN 伪指令用于通知编译器要使用的标号在其他的源文件中定义,但要在当前源文件中引用,如果当前源文件实际并未引用该标号,该标号就不会被加入到当前源文件的符号表中。标号在程序中区分大小写,[WEAK] 选项表示当所有的源文件都没有定义这样一个标号时,编译器也不给出错误信息,在多数情况下将该标号置为 0 ,若该标号为 B 或 BL 指令引用,则将 B 或 BL指令置为 NOP 操作。
使用示例:
AREA Init , CODE , READONLY
EXTERN Main ;通知编译器当前文件要引用标号Main,但Main 在其他源文件中定义……
END
------------------
3)IMPORT伪指令
语法格式:
IMPORT 标号 {[WEAK]}
IMPORT 伪指令用于通知编译器要使用的标号在其他的源文件中定义,但要在当前源文件中引用,而且无论当前源文件是否引用该标号,该标号均会被加入到当前源文件的符号表中。类似于C语言中的EXTERN。
标号在程序中区分大小写, [WEAK] 选项表示当所有的源文件都没有定义这样一个标号时,编译器也不给出错误信息,在多数情况下将该标号置为0 ,若该标号为B 或BL指令引用,则将B或BL指令置为 NOP 操作。
使用示例:
AREA Init , CODE , READONLY
IMPORT Main ;通知编译器当前文件要引用标号Main,但Main在其他源文件中定义
……
END
------------------
4)DCD与DCDU伪指令
(1)DCD指令定义与作用
DCD指令是数据定义( Data Definition )伪指令,一般用于为特定的数据分配存储单元,同时可完成已分配存储单元的初始化。DCD用于分配一片连续的字存储单元并用指定的数据初始化。
(2)DCD语法格式
标号 DCD 表达式
DCD伪指令用于分配一片连续的字存储单元并用伪指令中指定的表达式初始化。其中,表达式可以为程序标号或数字表达式。 DCD 也可用“ & ”代替。
用 DCD 分配的字存储单元是字对齐的,而用DCDU分配的字存储单元并不严格字对齐。
(3)使用示例
DataTest DCD 4 , 5 , 6 ;分配一片连续的字存储单元并初始化。
举例1:
SwiFunction
DCD TASK_SW ;0
DCD ENTER_CRITICAL ;1
DCD EXIT_CRITICAL ;2
DCD ISRBegin ;3
DCD ChangeToSYSMode ;4
DCD ChangeToUSRMode ;5
DCD __OSStartHighRdy ;6
DCD TaskIsARM ;7
DCD TaskIsTHUMB ;8
DCD OSISRNeedSwap ;9
DCD GetOSFunctionAddr ;10
DCD GetUsrFunctionAddr ;11
TASK_SW
MRS R3, SPSR ;保存任务的CPSR
MOV R2, LR ;保存任务的PC
举例2:
语句如下:
FiqStackSpace SPACE FIQ_Stack_Legth
FiqStack DCD FiqStackSpace + FIQ_Stack_Legth
最后一条语句,DCD的后面跟FiqStackSpace + FIQ_Stack_Legth,
DCD后面的FiqStackSpace,表示一串空字符,这好理解
但是再上FIQ_Stack_Legth,就不明白了,WHY?
FiqStackSpace是标号,相当与地址,实际上就是在FiqStackSpace SPACE FIQ_Stack_Legth 这个语句给分配的空间的首地址,FiqStackSpace+ FIQ_Stack_Legth 是这个地址空间的结束地址,将FiqStack指向这个地址,当做栈的顶部,该栈向下生长,长度FIQ_Stack_Legth.
------------------
5)ALIGN伪指令
作用:对指令或者数据的存放地址进行对齐
有些CPU架构要求固定的指令长度并且存放地址相对于2的幂指数圆整,否则程序无法正常运行,比如ARM。
------------------
6)AREA伪指令
指示汇编程序汇编新的代码节或数据节。程序是以程序段为单位组织代码的。段是相对独立的指令或代码序列,拥有特定的名称。段的种类有代码段、数据段和通用段,代码段的内容为执行代码,数据段存放代码运行时需要用到的数据,通用段不包含用户代码和数据,所有通用段共用一个空间。
AREA 伪指令用于定义一个代码段或数据段。
语法格式:
AREA 段名 属性 1 ,属性 2 ,……
其中,段名若以数字开头,则该段名需用 “ | ” 括起来,如 |1_test|
属性字段表示该代码段(或数据段)的相关属性,多个属性用逗号分隔。常用的属性如下:
— CODE 属性:用于定义代码段,默认为 READONLY
— DATA 属性:用于定义数据段,默认为 READWRITE
— READONLY 属性:指定本段为只读,代码段默认为 READONLY
— READWRITE 属性:指定本段为可读可写,数据段的默认属性为 READWRITE
— ALIGN 属性:使用方式为ALIGN表达式。在默认时, ELF (可执行连接文件)的代码段和数据段是按字对齐的,表达式的取值范围为 0 ~ 31 ,相应的对齐方式为表达式2次方。
— COMMON 属性:该属性定义一个通用的段,不包含任何的用户代码和数据。各源文件中同名的COMMON段共享同一段存储单元
— NOINIT属性: 指定此数据段仅仅保留了内存单元,而没有将各初始值写入内存单元,或者将各个内存单元值初始化为0
一个汇编语言程序至少要包含一个段,当程序太长时,也可以将程序分为多个代码段和数据段。
使用示例:
AREA Init , CODE , READONLY
该伪指令定义了一个代码段,段名为 Init ,属性为只读
------------------
7)SPACE和DCD伪指令的区别
SPACE和DCD的功能类似,SPACE申请一片内存空间,DCD申请一个字(32bit)的内存空间。
SPACE和DCD的区别在于,SPACE申请空间但不赋初值,DCD申请一个字的空间,并赋初值。
------------------
8)ENTRY伪指令
ENTRY 伪指令用于指定汇编程序的入口点。
语法格式:
ENTRY
在一个完整的汇编程序中至少要有一个ENTRY(当有多个ENTRY时,程序的真正入口点由链接器指定),但在一个源文件里最多只能有一个ENTRY(可以没有)。
使用示例:
AREA Init , CODE , READONLY
ENTRY ;指定应用程序的入口点
……
-------------------------------------------------------------------------------------------------------