当前位置:   article > 正文

[004] [ARM-Cortex-M3/4] 汇编基础与常用指令集_汇编mls 指令

汇编mls 指令
ARM
Contents
ARM汇编语言基础·
基本语法
指令后缀
ARM编译器与GCC··
译器语法差异
ARM UAL模拟器VisUAL··
常用指令集
存储器访问指令
数据操作指令
跳转指令
ARM杂项指令
伪指令
CMSIS内核接口函数··

Cortex­M0、Cortex-M0+和Cortex-M1处理器只支持多数16位指令和部分32位指令,Cortex-M3支持的32位指令更多。Cortex-M4处理器支持剩下的SIMD等DSP提升指令集可选的浮点指令。

image-20220312223223876

▲ Cortex-M处理器的指令集

1 ARM汇编语言基础

1.1 基本语法

标号
	操作码{cond}{S} 操作数1, 操作数2, … 		;注释
  • 1
  • 2
  • 标号:可选,必须顶格写,作用是让汇编器计算程序转移的地址(可以是C函数名)
  • cond:可选,即指令执行条件,如果不写则使用默认条件AL(无条件执行)。
  • S:可选,表示指令执行后,会修改PSR寄存器
  • 操作码:即指令助记符,前面必须至少有一个空格
  • 操作数:第1个操作数一般为本指令的执行结果存储处。形成操作数的有效地址的方法称为操作数的寻址方式,分为:
    • 隐含寻址:不是明显地给出操作数的地址。而是在指令中隐含着操作数的地址
    • 立即寻址:立即寻址方式的操作数,即操作数本身即为地址,即立即数,必须以#开头,且立即数不能作为指令中的第一操作数(目的操作数),如:MOV R1, #'A'
    • (寄存器)直接寻址:在指令中直接给出有效地址,操作数的地址是直接给出的,如:LDR R0, R1,将源寄存器R1的地址加载到目的寄存器R0
    • (寄存器)间接寻址:操作数中存放的值作为其有效地址,此时必须在操作数两端加方括号 [],如:LDR R0, [R1],将源寄存器R1中保存的值加载到目的寄存器R0中(类似C指针
    • 相对寻址:相对当前指令的地址(程序计数器PC中的内容±常量)
    • 寄存器偏移寻址:当第2操作数是寄存器偏移方式时,第2个寄存器操作数在与第1个操作数结合之前,选择进行移位操作。如:MOV R0, R2, LSL #3,将R2的左移3位,结果放入R0,即R0 = R2 * 8
    • 基址寻址:将基址寄存器的内容与指令中给出的偏移量相加,形成操作数的有效地址,基址寻址用于访问基址附近的存储单元,常用于查表,数组操作,功能部件寄存器访问等。如:LDR R2, [R3,#0x0F]将R3中的值+0x0f作为地址,加载到R2中
    • 多寄存器寻址(块拷贝寻址):一次可以传送几个寄存器值。如:LDMIA R1!,{R2-R7,R12},将R1单元中的数据读出到R2-R7,R12,R1自动+1(!);STMIA R0!,{R3-R6,R10};将R3-R6, R10 中的数据保存到R0指向的地址,R0自动+1。(使用多寄存器寻址指令时,寄存器子集的顺序时由小到大的顺序排列,连续的寄存器可用连接,否则,用,分隔书写。)
    • 堆栈寻址栈操作
  • 注释;开头

注意

  • 立即数不可以是任意数值,必须由一个8位的常数循环移位偶数位得到,如:8位数0xbe左移8位 -> 0x0000 be00。可以使用伪指令操作立即数。
  • 绝大多数16位指令只能访问R0-R732Thumb-2指令则可以随意访问R0-R15

1.2 指令后缀

image-20220307014919667

▲ 指令后缀

具体的条件码为:

image-20220308225519021

▲ 条件码表
  • 对于Thumb指令集,只有转移指令B指令)才可随意使用。而对于其它指令,CM3引入了IF-THEN指令块,在这个块中才可以加后缀,且必须加以后缀。
  • S后缀可以与条件后缀一起使用。

image-20220308160530841

▲ CPSR寄存器条件标志位

ARM内部寄存器

条件码应用示例:

  • C代码
if (a > b)	a++;
else		b++;
  • 1
  • 2
  • 对应的ARM指令(R0-a, R1-b)

image-20220307214221517

▲ UAL模拟器VisUAL运行结果

1.3 ARM编译器与GCC编译器语法差异

参考:ARM编译器和GNU ARM编译器之间的差异

image-20220307022400986

▲ GUN与ARM汇编器语法差异
  • armasm即为ARM汇编编译器语法
  • GUN即为GCC汇编编译器语法

ARM 系列目前支持三大主流的工具链,即ARM RealView (armcc), IAR EWARM (iccarm), and GNU Compiler Collection (gcc).

core_cm3.h中有如下定义:

  /* define compiler specific symbols */

 \#if defined ( __CC_ARM )

   \#define __ASM __asm /*!< asm keyword for armcc */

   \#define __INLINE __inline /*!< inline keyword for armcc */

 \#elif defined ( __ICCARM__ )

   \#define __ASM __asm /*!< asm keyword for iarcc */

   \#define __INLINE inline /*!< inline keyword for iarcc. Only

                 avaiable in High optimization mode! */

   \#define __nop __no_operation /*!< no operation intrinsic in iarcc */

 \#elif defined ( __GNUC__ )

   \#define __ASM asm /*!< asm keyword for gcc */

   \#define __INLINE inline /*!< inline keyword for gcc

 \#endif 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

1.4 ARM UAL模拟器VisUAL

官方手册

1.4.1 内存映射

image-20220307203750724

▲ VisUAL 内存映射
  • 不模拟外设

  • 较低的内存地址是用来保存指令的,因此程序计数器PC从地址0x0处的指令1开始。

  • 执行期间,指令执行内存区域(Instruction Memory Execute Accesss)不可读写,即为ROM。(这是软件的局限,实际ROM区域只读)

  • 指令执行内存区域默认大小0x10000字节,允许模拟16,384行代码

  • VisUAL 支持两种内存访问模式: OpenStrict

    • Open访问模式下,所有数据内存地址都具有读/写访问权限(默认启用)
    • Strict访问模式下,只有使用 DCD、 DCB 或 FILL 指令定义的明确数据内存地址才具有写访问权,其他地址只有读访问权限

1.4.2 支持的指令

image-20220307205857683

▲ List of Supported Instructions

在软件中可以鼠标点击指令,然后按ctrl+space查看用法:

image-20220307210122759

▲ VisUAL软件指令帮助

关于指令的限制参考:instructions note

2 常用指令集

指令语法中带有{}的表示可选!

2.1 存储器访问指令

名字功能
LDR从存储器中加载(Load)字到一个寄存器(Register)中
LDRH从存储器中加载半(Half)字到一个寄存器中
LDRB从存储器中加载字节(Byte)到一个寄存器中
LDRSH从存储器中加载半字,再经过带符号扩展后存储一个寄存器中
LDRSB从存储器中加载字节,再经过带符号扩展后存储一个寄存器中
STR把一个寄存器按字存储(Store)到存储器中
STRH把一个寄存器存器的低半字存储到存储器中
STRB把一个寄存器的低字节存储到存储器中
LDMIA加载多个字,并且在加载后自增基址寄存器
STMIA存储多个字,并且在存储后自增基址寄存器
PUSH压入多个寄存器到栈中
POP从栈中弹出多个值到寄存器中

STR&LDR示例

		MOV		R0, #0x20000
		MOV		R1, #0x08
		MOV		R2, #0x34
		STR		R2, [R0]				; R2的值存到R0所示地址
		STR		R2, [R0, #4]			; R2的值存到R0+4所示地址
		STR		R2, [R0, #8]!			; R2的值存到R0+8所示地址, R0=R0+8
		STR		R2, [R0, R1]			; R2的值存到R0+R1所示地址
		STR		R2, [R0, R1, LSL #4]	; R2的值存到R0+(R1<<4)所示地址
		MOV		R2, #0x12
		STR		R2, [R0], #0x20			; R2的值存到R0所示地址, R0=R0+0x20
		LDR		R3, [R0], +R1, LSL #1	; R0+(R1<<1)所示地址的值存到R3
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在旧的ARM架构中,不能对内存进行非对齐的字访问。LDR和STR指令的字地址必须是4的倍数。

2.1.1 LDRD/STRD

CM3支持64位整数,该指令用于传送64位整数的数据。

LDRD{cond} Rt, Rt2, [{Rn},+/-{Rm}]{!}
LDRD{cond} Rt, Rt2, [{Rn}],+/-{Rm}

STRD{cond} Rt, Rt2, [Rn {,#+/-<imm>}]
STRD{cond} Rt, Rt2, [<Rn>, #+/-<imm>
STRD{cond} Rt, Rt2, [<Rn>, #+/-<imm>]!
STRD{cond} Rt, Rt2, [{Rn},+/-{Rm}]{!}
STRD{cond} Rt, Rt2, [{Rn}],+/-{Rm}

  • Rt是第一个源寄存器,t必须是偶数(但不能为R14)
  • Rt2是第二个源寄存器,Rt2 = R(t+1)
  • Rm为要加载/存储的寄存器

注:Rt和Rt2顺序可互换

【例】

记 (0x1000)= 0x1234_5678_ABCD_EF00,则:

LDR R2, =0x1000 ;
LDRD R0, R1, [R2]
; R0= 0xABCD_EF00(低32位), R1=0x1234_5678(高32位)
  • 1
  • 2
  • 3

同理,用STRD存储64位整数:

STRD.W R1, R0, [R2]
; (0x1000)=0xABCD_EF00_1234_5678
  • 1
  • 2

实现了双字的字序反转操作。

2.1.2 LDM:Load Multiple Register

语法:LDM{addr_mode}{cond} Rn{!},reglist{^}

2.1.3 STM:Store Multiple Register

语法:STM{addr_mode}{cond} Rn{!},reglist{^}

  • addr_mode

    • IA - Increment After, 每次传输后才增加Rn的值(默认,可省)
    • IB - Increment Before, 每次传输前就增加Rn的值(ARM only)
    • DA - Decrement After, 每次传输后才减小Rn的值(ARM only)
    • DB - Decrement Before, 每次传输前就减小Rn的值
  • !: 表示修改后的Rn值会写入Rn寄存器, 如果没有!, 指令执行完后Rn恢复/保持原值

  • ^ : 在特权级模式下会影响CPSR

    • 数据传输到用户模式寄存器而不是当前模式寄存器(Reglist 不包含 PC)
    • 发生正常的多寄存器传输,并将 SPSR 复制到 CPSR 中,这用于从异常处理程序返回。(Reglist 包含 PC)

image-20220308001624936

▲ 不同addr_mode下的STM指令

低标号的寄存器Rn存放在低地址

2.1.4 栈操作

LDM & STM 实现

F-满, E-空,D-减, I-增, B-前, A-后

根据LDM/STM指令4种操作地址的方式,可以得出4种类型的栈:

  • FD 满递减堆栈:堆栈通过减小存储器的地址向下增长,SP指向最后压入堆栈的数据(先调整SP再存数据),指令如LDMFDSTMFD 等;
  • ED 空递减堆栈:堆栈通过减小存储器的地址向下增长,SP指向下一个要放入的空位置(先存数据再调整SP),指令如LDMEDSTMED 等;
  • FA 满递增堆栈:堆栈通过增大存储器的地址向上增长,SP指向最后压入堆栈的数据(先调整SP再存数据),指令如LDMFASTMFA 等;
  • EA 空递增堆栈:堆栈通过增大存储器的地址向上增长,SP指向下一个要放入的空位置(先存数据再调整SP),指令如LDMEASTMEA 等。

对于**满减栈**:

  • 压栈时:先减小SP,再存数据;STMDB <==> STMFD
  • 弹栈时:先读数据,再增大SP;LDMIA <==> LDMFD
STMFD  R13!,{R0,R4-R12,LR}  ;将寄存器列表中的寄存器(R0,R4到R12,LR)存入堆栈。
LDMFD  R13!,{R0,R4-R12,PC}  ;将堆栈内容恢复到寄存器(R0,R4到R12,LR)。
  • 1
  • 2

示例:

		MOV		R1, #1
		MOV		R2, #2
		MOV		R3, #3
		MOV		R0, #0x20000
		STMFD	SP!, {R1-R3}		; 压栈 R3,R2,R1分别存入SP-4, SP-8,SP-12地址处,SP=SP-12
		MOV		R1, #0
		MOV		R2, #0
		MOV		R3, #0
		LDMFD	SP!, {R1-R3}		; 弹栈 R1,R2,R3分别得到SP, SP+4,SP+8地址处的值,SP=SP+12

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 压栈:SP先减后存,R3-0x1FFFC, R2-0x1FFF8, R1-0x1FFF4
  • 弹栈:SP先读后增,R1-0x1FFF8, R2-0x1FFFC, R3-0x20000
PUSH & POP

下面语法解释都是基于向下增长的满栈

PUSH	{R0}	; *(--SP) = R0,	SP为 long*指针
POP		{R0}	; R0 = *SP++

subroutine_1
    PUSH {R0-R7, R12, R14}  ; 保存寄存器列表, r14先入栈, r0最后入栈
    … 						; 执行处理
    POP {R0-R7, R12, R14} 	; 恢复寄存器列表, 先出栈的数据(即原先最后入栈的r0)保存到r0, 最后出栈的数据(即原先最先入栈的r14)保存到r14
    BX R14 					; 返回到主调函数
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

注意:寄存器的PUSHPOP操作永远都是4字节对齐

2.2 数据操作指令

image-20220308195813472

▲ 16位数据操作指令

2.2.1 数据传输指令

指令必须加条件后缀才会更新CPSR!

  • MOV

MOV{S}{cond} Rn, <Operand2>(若Rd 或Rm 是高寄存器(R8~R15),则标志不受影响,若Rd 或Rm 都是低寄存器(R0~R7),则会更新N和Z,且清除标志C和V)

MOV{cond} Rd, #imm16 (指令会更新N 和Z 标志,对标志C 和V 无影响)

imm16 is an immediate value in the range 0-65535.

MOV只能在寄存器之间移动数据,或者把立即数移动到寄存器中,不能读写内存!

MOV R1,#0x10 ;R1=0x10
MOV R0,R8 	 ;R0=R8
MOV PC,LR 	 ;PC=LR,子程序返回
  • 1
  • 2
  • 3
  • MVN

MVN Rd, Op2 ; Rd = ~Op2(指令会更新N 和Z 标志,对标志C 和V 无影响)

数据非传送指令.将寄存器Rm按位取反后传送到目标寄存器(Rd)

MVN		R1, #0x1	; R1 = ~0x01
  • 1
  • NEG

指令会更新N,Z,C,V 标志

NEG R1,R0, 			; R1 = -R0
  • 1

2.2.2 算术逻辑运算指令

四则运算
加法

在加法指令中(包括比较指令CMN),当结果产生了进位,则C = 1 ,表示无符号数运算发生上溢出;其他情况C = 0

  • 加法指令ADD
ADD  R1, R2, R3        ; R1 = R2 + R3
ADD  R1, R2, #0x12     ; R1 = R2 + 0x12
ADD  R3, R8 		   ; R3 = R3 + R8
  • 1
  • 2
  • 3
  • 带进位加法指令ADC

ADC Rd, Rn,Op2 ;Rd = Rn + Op2 + C (CPSR的C位)

一般用于超过32位的整数相加,实现64 位加法

ADDS R0, R1, R2		; R0=R1+R2,   ADDS中S表示把进位结果写入CPSR
ADC  R5, R3, R4 	; R5=R3+R4+C, ADD相加溢出时 C = 1
  • 1
  • 2
  • 带12位立即数的常规加法ADDW

ADDW Rd, #imm12 ; Rd += imm12

减法

在减法指令中(包括比较指令CMP),当结果发生错位,则C = 0 ,表示无符号数运算发生下溢出;其他情况C = 1

  • 减法指令SUB
SUB  R1, R2, R3         ; R1 = R2 - R3
SUB  R1, R2, #0x12   	; R1 = R2 - 0x12
  • 1
  • 2
  • 带进位减法指令SBC

SBC Rd, Rn, Op2 ;Rd = Rn – Op2 - !C

使用SBC实现64 位减法

subs	r2, r0, r1
sbc		r5, r3, r4		; r5 = r3 - r4 - !c
  • 1
  • 2
  • 逆向减法指令RSB

RSB Rd, Rn, Op2 ;Rd = Op2 – Rn

  • 带进位逆向减法指令RSC

RSC Rd, Rn, Op2 ;Rd = Op2 – Rn - !C

乘法
  • 乘法指令MUL(32位)

对于32位乘法指令操作是单周期的

MUL  R1, R2, R3        ; R1 = R2 * R3
  • 1

VisUAL不支持该指令

  • 乘加MLA

MLA Rd, Rm, Rn, Ra ;Rd = Ra+Rm*Rn

  • 乘减MLS

MLS Rd, Rm, Rn, Ra ;Rd = Ra-Rm*Rn

除法(硬件支持-CM3前卫指令)
  • 无符号除法UDIV

UDIV Rd, Rn, Rm ;Rd = Rn/Rm

  • 有符号除法SDIV

SDIV Rd, Rn, Rm ;Rd = Rn/Rm

位操作

VisUAL里不支持(1<<4)这样的写法,写成:0x10

  • 逻辑与AND
AND  R1, R2, #(1<<4)   	; 位与,R1 = R2 & (1<<4)
AND  R1, R2, R3         ; 位与,R1 = R2 & R3
  • 1
  • 2
  • 逻辑或ORR
ORR  R1, R2, R3			; 逻辑或   R1 = R2|R3
  • 1
  • 位清除BIC
BIC  R1, R2, #(1<<4)    ; 清除某位,R1 = R2 & ~(1<<4)
BIC  R1, R2, R3         ; 清除某位,R1 = R2 & ~R3
  • 1
  • 2
  • 逻辑异或EOR
EOR  R3, R2, 			; 逻辑异或	R3=R3^R2	
  • 1
移位运算
  • LSL:逻辑左移(Logical Shift Left),寄存器中字的低端空出的位补0
  • LSR:逻辑右移(Logical Shift Right),寄存器中字的高端空出的位补0
  • ROR:循环右移(Rotate Right),由字的低端移出的位填入字的高端空出的位
  • ASR:算术右移(Arithmetic Shift Right),移位过程中保持符号位不变,即如果源操作数为正数,则字的高端空出的位补0,否则补1
  • RRX :带扩展的循环右移,操作数右移一位,高端空出的位用原C标志值填充。

image-20220308224750327

▲ 移位与循环指令
	MOV		r0, #0x1
    MOV		r1, #0x3
    MOV		r2, r0, LSL r1	; r2 = r0 << r1
    LSL		r1, r0, #3		; r1 = r0 << 3
  • 1
  • 2
  • 3
  • 4

注意:仅将寄存器的移位结果作为操作数,而寄存器保存的值不变。

2.2.3 比较指令

以下指令会更新CPSR寄存器的N、Z、C 和V 标志

  • 比较指令CMP

比较两数相减的结果:

为0:Z = 1

不为0:Z = 0

CMP R0, R1                	; 比较R0-R1的结果
CMP R0, #0x12           	; 比较R0-0x12的结果
  • 1
  • 2
  • 负数比较指令CMN

比较两数相加的结果:

为0(溢出进位):Z = 1

不为0:Z = 0

LDR		R1, =0xFFFFFFFF
MOV		R2, #0x1
CMN		R1, R2		;NZCV 0110
  • 1
  • 2
  • 3
  • 位测试指令TST

第一操作数Rn最低位为0:Z = 1

第一操作数Rn最低位不为0:Z = 0

TST  R0, R1                 ;  测试 R0 & R1的结果
TST  R0, #(1<<4)         	;  测试 R0 & (1<<4)的结果
  • 1
  • 2
  • 相等测试指令TEQ32位指令

比较两数按位异或结果:

为0:Z = 1

不为0:Z = 0

TEQ  R0, R1                 ;  测试R0 ^ R1结果
  • 1

2.3 跳转指令

  • B:Branch,跳转

B{cond}{.W} label

.w表示跳转到Thumb2指令集中的32位指令

  • BL:Branch with Link,跳转前先把返回地址保持在LR寄存器中

BL{cond} label

  • BX:Branch and eXchange,根据跳转地址的BIT0切换为ARM或Thumb状态(0:ARM状态,1:Thumb状态)

BX{cond} Rm

Rm bit[0] = 0 ARM state.
Rm bit[0] = 1 Thumb state.

  • BLX:Branch with Link and eXchange, 根据跳转地址的BIT0切换为ARM或Thumb状态(0:ARM状态,1:Thumb状态)

BLX{cond} label
BLX{cond} Rm

Rm bit[0] = 0 ARM state.
Rm bit[0] = 1 Thumb state.

X:表示带状态;L:表示带返回

  • **LDR**伪指令直接给PC赋值

BL示例

		BL		delay
		MOV		R1, #1	; BL指令的下一条指令的地址赋给LR
delay
		MOV		R0, #5
loop
		SUBS		R0, R0, #1
		;CMP		R0, #0	; CMP指令会更新Z标志
		BNE		loop		; not equal(Z=0)执行
		MOV		PC, LR	
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

LDRLDR示例

		ADR		LR, return
		ADR		PC, delay
		; LDR		LR, =return
		; LDR		PC, =delay
return
		MOV		R1, #1
delay
		MOV		R0, #5
loop
		SUBS		R0, R0, #1
		;CMP		R0, #0	; CMP指令会更新Z标志
		BNE		loop		; not equal(Z=0)执行
		MOV		PC, LR
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

注意BLBLX会将当前跳转指令的下一条指令的地址赋给LR,如果不使用这些指令,那么当前子程序的LR第一条指令的地址

2.4 ARM杂项指令

  • 软中断指令SWI

令用于产生软中断,从而实现在用户模式变换到特权模式,CPSR保存到管理模式的SPSR 中,执行转移到SWI向量,在其它模式下也可使用SWI 指令,处理同样地切换到特权模式。

CM3中该指令名为SVC

  • 读特殊功能寄存器指令MRS

加载()特殊功能寄存器的值到通用寄存器

MRS{cond} Rd, SReg

Rd: 目标寄存器 不允许为R15

  • 写特殊功能寄存器指令MSR

存储()通用寄存器的值到特殊功能寄存器

MSR{cond} Sreg, Rn

image-20220308231840582

▲ MRS/MSR可以使用的特殊功能寄存器
MRS R0, PSR	; 读组合程序状态寄存器值
MSR PSR, R0 	; 写组合程序状态寄存器值
-----------------------------------
MRS R0, APSR	; 将状态标志读入到RO
MRS R0, IPSR	; 读取异常/中断状态
MSR APSR, R0	; 写状态标志
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

:除了APSR可以在用户级访问外,MRS/MSR必须在特权级下使用

2.5 伪指令

2.5.1 Thumb伪指令

  • ADR

基于PC相对偏移的地址值读取到寄存器中。没有=

ADR register,expr

expr:地址表达式。偏移量必须是正数并小于1KB,且必须局部定义,不能被导入

; 标号
LOOP
	ADD R0, R0, #1
	ADR	R0, Loop	; 伪指令
; 编译器转换的真实指令
	ADD R0, PC, #val	; val链接时确定, 相对寻址方式得出LOOP地址
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • LDR

用于加载32位的立即数一个地址值到指定寄存器。LDR作为伪指令时,指令中必须有一个=

LDR register,=expr/label_expr

register: 加载的目标寄存器
expr: 32位立即数
label_exp: 基于PC的地址表达式或外部表达式

立即数

LDR R0, =0x12   	; 0x12是立即数,ARM编译器替换为:MOV R0, #0x12
  • 1

地址值

LDR R0, =0x12345678		
; 0x12345678不是立即数,ARM编译器替换为:
Label DCD 0x12345678	; 编译器在程序某个地方保持该值
LDR R0, [PC,#offset]	; offset链接时确定,从内存中读取该值并加载到R0中
  • 1
  • 2
  • 3
  • 4

LDRADR区别LDR通常是把要加载的数值预先定义,再使用一条PC相对加载指令来取出。而ADR则对PC作算术加法或减法来取得立即数。因此ADR未必总能求出需要的立即数,一般是为了取出附近某条指令或者变量的地址,而LDR则是取出一个通用的32位整数。(ADR效率比LDR

  • NOP

空操作伪指令,一般用于延时

2.5.2 数据定义伪指令

  • MAP

定义一个结构化的内存表的首地址,^与MAP 同义

MAP expr,{base_register}

expr 数字表达式或程序中的标号

base_register 当指令中没有base_register 时,expr 即为结构化内存表的首地址;否则首地址为expr与base_register和

MAP 0x01,R9 	; 内存表的首地址为R9+0x01
MAP 0x40003000 	; 内存表的首地址为0x40003000
  • 1
  • 2
  • FIELD

定义一个结构化内存表中的数据域,#与FIELD 同义

{tabel} FIELD expr

label 当指令中包含其时,label 的值为当前内存表的位置计数器{VAR}的值,汇编器处理这条FIELD伪指令后,内存表计数器的值将加上expr
expr 表示本数据域在内存表中所占用的字节数

count1 FIELD 4 	;定义数据域count1,长度为4 字节
  • 1

2.5.3 数据分配伪指令

  • SPACE

分配一块内存单元,并用0初始化。%与SPACE 同义

{label} SPACE expr

label 内存块起始地址标号

expr 所要分配的内存字节数

DataBuf SPACE 1000 	;分配1000 字节空间
  • 1
  • DCB

分配一段字节内存单元,初始化为expr。=与DCB 同义

{label} DCB expr{,expr}{,expr}…

label 内存块起始地址标号
expr 可以为**-128~255的数值字符串**,内存分配的字节数由expr个数决定

DISPTAB 	DCB 	0x33,0x43,0x76,0x12
			DCB 	-120,20,36,55
ERRSTR  	DCB 	“Send,data is error!”,0
  • 1
  • 2
  • 3
  • DCDDCDU

申请一个字(32bit)的内存空间,初始化为expr。&与DCD 同义。

DCD需要字对齐,DCDU则不用。

{label} DCD expr{,expr}{,expr}…
{label} DCDU expr{,expr}{,expr}…

label 内存块起始地址标号
expr 常数表达式程序中的标号,内存分配字节数由expr个数决定

Stack_Size      EQU     0x00000400  	;1KB
                AREA    STACK, NOINIT, READWRITE, ALIGN=3  ;分配栈空间 不初始化 可读写 2^3字节对齐
Stack_Mem       SPACE   Stack_Size  
__initial_sp  ;栈顶地址
__Vectors       DCD     __initial_sp               ; Top of Stack  栈顶指针
                DCD     Reset_Handler              ; Reset Handler 复位中断
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

2.5.4 汇编控制伪指令

  • IFELSEENDIF
IF :DEF:__MICROLIB ; 即 use MicroLib  
EXPORT  __initial_sp
EXPORT  __heap_base
EXPORT  __heap_limit
ELSE
IMPORT  __use_two_region_memory  ;由用户自己实现
EXPORT  __user_initial_stackheap
ALIGN
ENDIF
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • MACROMEND

MACRO 和MEND 伪指令用于宏定义。MACRO 标识宏定义的开始,MEND 标识宏定义久的结束,用MACRO 及MEND 定义的一段代码,称为宏定义体

  • WHILWEND

WHILE logical_expr
;指令或伪指令代码段
WEND

2.5.5 杂项伪指令

  • ALIGN

使当前位置满足一定的字节对齐方式。

ALIGN {expr{,offset}}

expr 用于指定对齐的方式,取值为2的n次幂(没有指定,默认字对齐
offset 当前位置对齐到下面形式的地址处:offset+n*expr

AREA    STACK, NOINIT, READWRITE, ALIGN=3  ;分配栈空间 不初始化 可读写 2^3字节对齐
ALIGN
  • 1
  • 2
  • AREA

定义一个代码段或数据段。

AREA sectionname{,attr}{,attr}…

sectionname 所定义的代码段或数据段的名称。如果该名称是数据开头的或数据段(.text.bss.data),则两边必须用|括起来

attr 该代码段或数据段的属性 (逗号隔开)

attr属性具体说明:

  • ALIGN = expr:ELF的代码段和数据段默认4字节对齐,使用该属性可以2^expr字节对齐(expr为0~31),但是代码段的expr不能为0和1

  • ASSOC = section:指定与本段相关的ELF段(.text.bss.data),任何时候连接section 段也必须包括sectionname段

  • DODE:定义代码段,默认属性为READONLY

  • COMDEF:定义一个通用的段,可以包含代码或者数据,在其它源文件中,同名的COMDEF 段必须相同;

  • COMMON:定义一个通用的段,不可以包含代码或者数据,连接器将其初始化为此,各源文件中同名的COMMON 段共用同样的内存单元,连接器为其分配合适的尺寸;

  • DATA:定义数据段,默认属性为READWRITE

  • NOINIT:不初始化,或初始化为0

  • READONLY:指定本段为只读

  • READWRITE:指定本段为可读写

AREA    |.text|, CODE, READONLY 	; 定义.text段位代码段,属性设为只读
  • 1

注意:使用AREA将程序分为多个ELF 格式的段,段名称可以相同,这时同名的段被放在同一个ELF 段

  • END

指示汇编编译器源文件已结束。

  • ENDP

指示汇编文件中的程序已结束。

  • PROC

定义子程序,与 ENDP成对使用。

  • EQU

定义数字常量(#define)

name EQU expr{,type}

type:当expr 为32 位常量时,可用type 指示expr 表示的数据类型(CODE16、CODE32、DATA)

Stack_Size      EQU     0x00000400  ;1KB
  • 1
  • EXPORTGLOBAL

声明一个符号可以被其它文件引用,相当于声明了一个全局变量

EXPORT symbol{[WEAK]}
GLOBAL symbol{[WEAK]}

symbol 要声明的符号名称

[WEAK] 弱定义

EXPORT  __Vectors
  • 1
  • IMPORTEXTERN

即C语言extern,指示编译器当前的符号不是在本源文件中定义的,而是在其他源文件中定义的,在本源文件中可能引用该符号

IMPORT symbol{[WEAK]}
EXTERN symbol{[WEAK]}

IMPORT  __main                          ;C main函数
IMPORT  SystemInit  					;初始化系统时钟函数
  • 1
  • 2
  • KEEP

表示编译器保留符号表中的局部符号

KEEP {symbol}

symbol 要保留的局部标号(若没有该项,除了基于寄存器处的所有符号将包含在目标文件的符号表)

  • PEQUIRE8PRESERVE8

PEQUIRE8: 当前文件请求堆栈为8 字节对齐
PRESERVE8:当前文件保持堆栈为8 字节对齐

PRESERVE8  ;当前堆栈以8字节对齐
  • 1

3 CMSIS内核接口函数

InstructionCMSIS intrinsic function
CPSIE Ivoid __enable_irq(void)
CPSID Ivoid __disable_irq(void)
CPSIE Fvoid __enable_fault_irq(void)
CPSID Fvoid __disable_fault_irq(void)
ISBvoid __ISB(void)
DSBvoid __DSB(void)
DMBvoid __DMB(void)
REVuint32_t __REV(uint32_t int value)
REV16uint32_t __REV16(uint32_t int value)
REVSHuint32_t __REVSH(uint32_t int value)
RBITuint32_t __RBIT(uint32_t int value)
SEVvoid __SEV(void)
WFEvoid __WFE(void)
WFIvoid __WFI(void)

CMSIS还提供了一些使用MRSMSR指令访问特殊寄存器的函数:

Special registerAccessCMSIS function
PRIMASKReaduint32_t __get_PRIMASK (void)
Writevoid __set_PRIMASK (uint32_t value)
FAULTMASKReaduint32_t __get_FAULTMASK(void)
Writevoid __set_FAULTMASK (uint32_t value)
BASEPRIReaduint32_t __get_BASEPRI (void)
Writevoid __set_BASEPRI (uint32_t value)
CONTROLReaduint32_t __get_CONTROL (void)
Writevoid __set_CONTROL (uint32_t value)
MSPReaduint32_t __get_MSP (void)
Writevoid __set_MSP (uint32_t TopOfMainStack)
PSPReaduint32_t __get_PSP (void)
Writevoid __set_PSP (uint32_t TopOfProcStack)

END

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/658417
推荐阅读
相关标签
  

闽ICP备14008679号