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

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, … 		;注释
  • 标号:可选,必须顶格写,作用是让汇编器计算程序转移的地址(可以是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++;
  • 对应的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.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

在旧的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位)

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

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

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

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)。

示例:

		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

  • 压栈: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 					; 返回到主调函数

注意:寄存器的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,子程序返回
  • MVN

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

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

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

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

NEG R1,R0, 			; R1 = -R0

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
  • 带进位加法指令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
  • 带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
  • 带进位减法指令SBC

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

使用SBC实现64 位减法

subs	r2, r0, r1
sbc		r5, r3, r4		; r5 = r3 - r4 - !c
  • 逆向减法指令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

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
  • 逻辑或ORR
ORR  R1, R2, R3			; 逻辑或   R1 = R2|R3
  • 位清除BIC
BIC  R1, R2, #(1<<4)    ; 清除某位,R1 = R2 & ~(1<<4)
BIC  R1, R2, R3         ; 清除某位,R1 = R2 & ~R3
  • 逻辑异或EOR
EOR  R3, R2, 			; 逻辑异或	R3=R3^R2	
移位运算
  • 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

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

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的结果
  • 负数比较指令CMN

比较两数相加的结果:

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

不为0:Z = 0

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

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

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

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

比较两数按位异或结果:

为0:Z = 1

不为0:Z = 0

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

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	

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

注意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	; 写状态标志

:除了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地址
  • LDR

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

LDR register,=expr/label_expr

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

立即数

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

地址值

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

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
  • FIELD

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

{tabel} FIELD expr

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

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

2.5.3 数据分配伪指令

  • SPACE

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

{label} SPACE expr

label 内存块起始地址标号

expr 所要分配的内存字节数

DataBuf SPACE 1000 	;分配1000 字节空间
  • 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
  • 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 复位中断

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
  • 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
  • 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段位代码段,属性设为只读

注意:使用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
  • EXPORTGLOBAL

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

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

symbol 要声明的符号名称

[WEAK] 弱定义

EXPORT  __Vectors
  • IMPORTEXTERN

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

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

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

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

KEEP {symbol}

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

  • PEQUIRE8PRESERVE8

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

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

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

  • 13
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 《ARM Cortex-M3和Cortex-M4处理器权威指南(第三版)》是一本关于ARM Cortex-M3和Cortex-M4处理器的权威指南,该书在CSDN有提供。 本书是针对ARM Cortex-M3和Cortex-M4处理器的深入介绍和解析,旨在帮助读者更好地理解和应用这两款处理器。书中详细介绍了这两款处理器的关键特性、架构、指令集,而且还提供了丰富的编程示例和实践案例。 书中首先介绍了ARM Cortex-M系列处理器的基本概念和特性,包括处理器的体系结构、寄存器组织、中断处理、异常处理等。接着,详细分析了Cortex-M3和Cortex-M4处理器的指令集,包括寻址模式、数据处理指令、逻辑指令等。 本书还涵盖了处理器的重要特性,如睡眠模式、低功耗性能、外设接口等。此外,针对实际应用中的常见问题,还提供了一些解决方案和调试技巧。读者通过学习本书,可以了解到如何最大限度地发挥Cortex-M3和Cortex-M4处理器的性能,以及如何优化代码和调试工具。 需要注意的是,本书是第三版,相较于前两版做了一些更新和完善。所以,即使已经阅读过前两版的读者,也可以通过阅读本书来进一步扩展和深化对ARM Cortex-M3和Cortex-M4处理器的理解。 总之,《ARM Cortex-M3和Cortex-M4处理器权威指南(第三版)》是一本非常有价值的技术书籍,对于从事嵌入式开发、嵌入式系统设计以及对ARM Cortex-M系列感兴趣的读者来说,是一本必备的参考书。在CSDN上能够获取此书,可以帮助读者更加方便地获取相关知识。 ### 回答2: 《ARM Cortex-M3和Cortex-M4处理器权威指南(第三版)》是一本介绍ARM Cortex-M3和Cortex-M4处理器的权威指南,旨在帮助读者深入了解这两种处理器的架构、特性和应用。 该书分为多个章节,首先介绍了ARM公司和Cortex-M系列处理器的背景和发展历程。然后详细介绍了Cortex-M3和Cortex-M4的架构和指令集,并从底层硬件到软件编程的角度对其进行了全面阐述。 在介绍Cortex-M3处理器时,书中详细解释了其内部的存储器结构、中断系统、异常处理、时钟和电源管理等重要组成部分。同时,也讲解了Cortex-M3处理器的特性,例如支持的调试功能、低功耗设计和可靠性等。 针对Cortex-M4处理器,该书还介绍了其在基于ARMv7-M架构上的新特性和性能提升。其中包括浮点运算单元(FPU)、数字信号处理扩展(DSP)和Systick定时器等。同时,还详细介绍了Cortex-M4处理器和Cortex-M3处理器的差异和应用场景。 此外,该书还深入讲解了使用C语言编程和汇编语言编程开发应用程序的方法和技巧。读者可以学习到如何进行中断处理、时钟配置、外设控制以及优化代码等方面的内容。 总之,《ARM Cortex-M3和Cortex-M4处理器权威指南(第三版)》通过深入浅出的方式,全面介绍了ARM Cortex-M3和Cortex-M4处理器的架构、特性和应用。对于从事嵌入式系统开发的工程师和研究人员来说,这本书是一本非常实用的参考资料。 ### 回答3: 《ARM Cortex-M3和Cortex-M4处理器权威指南(第三版)》是一本完整介绍ARM Cortex-M3和Cortex-M4处理器的权威性指南。此书由ARM公司主持编写,旨在帮助读者全面了解和掌握这两款处理器的原理、特性和功能。 Cortex-M3和Cortex-M4处理器是ARM公司推出的针对嵌入式系统设计的32位处理器。它们具有低功耗、高性能和强大的系统集成能力等特点,在嵌入式行业应用广泛。本书对这两种处理器的内部结构及其相互之间的差异进行了详细解析,同时还介绍了它们的指令集、内存管理单元、中断控制器等硬件特性。 本书还详细讨论了Cortex-M3和Cortex-M4处理器的软件开发环境和编程模型。读者可以了解到如何使用ARM开发工具链进行软件开发,并学习到如何编写高效的嵌入式软件程序。此外,还介绍了一些常见的嵌入式系统设计方法和技巧,以帮助读者更好地应用这两款处理器。 《ARM Cortex-M3和Cortex-M4处理器权威指南(第三版)》适合嵌入式系统的开发者、工程师和学生阅读。无论是初学者还是有经验的专业人士,都能从中获得宝贵的知识和实战经验。此书内容全面且详细,对于深入了解和应用Cortex-M3和Cortex-M4处理器非常有帮助。无论是学习嵌入式系统设计还是进行相应的应用开发,本书都是一本不可或缺的参考资料。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柯西的彷徨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值