CPU 的功能是从外部设备获得数据,经过加工、处理后再把处理结果发送到和 CPU 连接的外部设备。设计一个CPU,首先需要设计一套可以执行特定功能的操作命令,这种操作命令称为指令。CPU 所能执行的各种指令的集合,称为该 CPU 的指令系统。表2-3给出了ARM Cortex-M指令集概况。在ARM系统中,使用架构(Architecture)一词,即体系结构,主要是指使用的指令集。由同一架构,可以衍生出许多不同的处理器型号。对 ARM 而言,其他芯片厂商,可由 ARM 提供的一种处理器架构具体生产出许多不同的 MCU 或处理器型号。ARMv7-M 是一种架构,其中 v7是指版本号,而基于该架构处理器有 Cortex-M3、Cortex-M4、Cortex-M4F等。读者了解其基本脉络即可。
表2-3 ARM Cortex-M指令集概况
本节在给出指令简表与寻址方式的基础上,简要阐述ARM Cortex-M系列共用的57条基本指令功能。
1.指令简表
ARM Cortex-M4F支持所有的Thumb和Thumb2指令,还支持浮点运算指令、DSP扩展指令等。常用的指令大体可分为数据操作类指令、跳转控制类指令、数据传送类指令,以及其他指令。Cortex-M4F有53条16位指令、92条32位指令和35条浮点指令,这其中包含了一些Cortex-M3中不支持的协议处理器指令和用于Cache的指令。表2-4为基本指令简表,供读者简要了解,需要记忆几个保留字以便理解基本的汇编程序,其他指令在需要时可查阅《ARMv7-M参考手册》。
表2-4 基本指令简表
2.寻址方式
指令是对数据的操作,通常把指令中所要操作的数据称为操作数,ARM Cortex-M4F微控制器所需的操作数可能来自寄存器、指令代码、存储单元。确定指令中所需操作数的各种方法称为寻址方式(Addressing Mode)。下面指令格式中的“{}”表示其中是可选项,如“LDRH Rt, [Rn {, #imm}]”,表示有“LDRH Rt, [Rn ]”“LDRH Rt, [Rn , #imm]”两种指令格式。指令中的“[ ]”表示其中的内容为地址,“//”表示注释。
(1)立即数寻址。在立即数寻址方式中,操作数直接通过指令给出,数据包含在指令编码中,随指令一起被编译成机器码后存储在程序空间中。“#”为立即数的前导标识符,ARM Cortex-M4立即数范围是0x00~0xff。例如:
SUB R1,R0,#1 //R1←R0-1
MOV R0,#0xff //即数0xff装入R0寄存器
(2)寄存器寻址。在寄存器寻址中,操作数来自寄存器。例如:
MOV R1,R2 //←R2
SUB R0,R1,R2 //0←R1-R2
(3)直接寻址。在直接寻址方式中,操作数来自存储单元,在指令中直接给出的是存储单元地址。在指令码中,给出了数据的位数,有字(4字节)、半字(2字节)、单字节三种情况。例如:
LDR Rt,label //在label处连续取4字节至寄存器中
LDRH Rt,label //在label处读取半字到Rt
LDRB Rt,label //在label处读取字节到Rt
(4)偏移寻址及寄存器间接寻址。在偏移寻址中,操作数来自存储单元,指令通过寄存器及偏移量给出存储单元的地址,偏移量不能超过4 KB(指令编码中偏移量为12位)。偏移量为0的偏移寻址也称为寄存器间接寻址。例如:
LDR R3,[PC,#100] //将(PC+100)的存储器单元的内容加载到寄存器R3中
LDR R3,[R4] //将R4的存储单元的内容加载到寄存器R3中
数据传送类指令的功能有两种:一是将存储器地址空间中的数据传送到寄存器中;二是将寄存器中的数据传送到另一寄存器或存储器地址空间中。基本的数据传送类指令有16条。
1.取数指令
取数指令是将存储器中内容加载(Load)到寄存器中的指令,如表2-5所示,其中LDR、LDRH、LDRB 指令分别表示加载来自存储器单元的一个字、半字和单字节(不足部分以0填充)。LDRSH和LDRSB指令用于将存储单元的半字、字节有符号数扩展成32位后加载到指定的寄存器Rt中。
表2-5 取数指令
在“LDM Rn{!},reglist”指令中,Rn表示存储器单元起始地址的寄存器;reglist可包含一个或多个寄存器,若包含多个寄存器则必须以“,”分隔,外面用“{}”标识;“!”是一个可选的回写后缀,reglist列表中包含Rn寄存器时不要回写后缀,否则须带回写后缀“!”。带后缀时,在数据传送完成之后,将最后的地址将写回到Rn=Rn+4×(n-1),n为reglist中寄存器的个数。Rn不能为R15,reglist可以为R0~R15任意组合;Rn寄存器中的值必须字对齐。这些指令不影响N、Z、C、V状态标志。
2.存数指令
存数指令是将寄存器中内容存储(Store)到存储器单元中的指令,如表2-6所示,其中, STR、STRH和STRB指令存储Rt寄存器中的字、低半字或低字节至存储器单元,存储器单元地址由Rn与Rm之和决定,Rt、Rn和Rm必须为R0~R7之一。
其中,“STM Rn!, reglist”指令将reglist列表寄存器内容以字存储到Rn寄存器中的存储单元地址,以4字节访问存储器地址单元,访问地址从Rn寄存器指定的地址值到Rn+4×(n-1), n为reglist中寄存器的个数,按寄存器编号递增顺序访问,最低编号使用最低地址空间,最高编号使用最高地址空间。对于STM指令,若reglist列表中包含Rn寄存器,则Rn寄存器必须位于列表首位;若reglist列表中不包含Rn,则将位于Rn+4×n地址回写到Rn寄存器中。这些指令不影响N、Z、C、V状态标志。
表2-6 存数指令
3.寄存器间数据传送指令
寄存器间数据传送指令如表2-7所示,Rd表示目标寄存器;imm为立即数,范围为0x00~0xff。当MOV指令中Rd为PC寄存器时,则丢弃第0位;当出现跳转时,将传送值的第0位清0后的值作为跳转地址。虽然MOV指令可以作为分支跳转指令,但强烈推荐在跳转时使用BX或BLX指令。MOV指令影响N、Z状态标志,但不影响C、V状态标志。
表2-7 寄存器间数据传送指令
4.堆栈操作指令
堆栈(Stack)操作指令如表2-8所示,PUSH指令将寄存器值保存在堆栈中,最低编号寄存器使用最低存储地址空间,最高编号寄存器使用最高存储地址空间;POP 指令将值从堆栈中弹回寄存器,最低编号寄存器使用最低存储地址空间,最高编号寄存器使用最高存储地址空间。执行PUSH指令后,更新SP寄存器值SP=SP-4;执行POP指令后更新SP寄存器值SP=SP+4。若POP指令的reglist列表中包含指针PC,在POP指令执行完成后将跳转到PC所指地址处。SP寄存器的值最低位通常用于更新xPSR的T位,此位必须置1确保程序正常运行。
表2-8 堆栈操作指令
例如:
PUSH{R0,R4-R7} @将R0,R4~R7寄存器值入栈
PUSH{R2,LR} @将R2,LR寄存器值入栈
POP{R0,R6,PC} @出栈,将堆栈中的值保存R0、R6、PC中,同时跳转至PC所指向的地址
5.生成与指针PC相关地址指令
ADR指令(见表2-9)用于将指针PC值加上一个偏移量得到的地址写入目标寄存器中。若利用ADR指令生成的目标地址用于跳转指令BX、BLX,则必须确保该地址最后一位为1。Rd为目标寄存器,label为与指针PC相关的表达式。在该指令下,Rd必须为R0~R7,数值必须字对齐且在当前PC值的1020字节以内。此指令不影响N、Z、C、V状态标志。这条指令主要在编译阶段使用,一般可看成一条伪指令。
表2-9 ADR指令
数据操作主要包括算术运算、逻辑运算、移位等。
1.算术运算类指令
算术类指令有加、减、乘、比较等,如表2-10所示。
表2-10 算术类指令
加、减指令对操作数的限制条件如表2-11所示
表2-11 加、减法指令对操作数的限制条件
2.逻辑运算类指令
逻辑运算类指令如表2-12所示,其中 AND、EOR 和 ORR 指令将寄存器 Rn、Rm值进行逐位逻辑与、异或以及或操作,BIC 指令将寄存器 Rn 的值与 Rm 的值的反码按位进行逻辑与操作,结果将保存到Rd。这些指令会更新N、Z状态标志,但不影响C、Z状态标志。
Rd、Rn和Rm必须为R0~R7,其中Rd为目标寄存器,Rn为存放第一个操作数寄存器且必须和目标寄存器Rd一致(即Rd就是Rn),Rm为存放第二个操作数寄存器。
表2-12 逻辑运算类指令
3.移位类指令
移位类指令如表2-13所示,其中,ASR、LSL、LSR和ROR指令根据寄存器Rs或立即数imm决定移动位数寄存器Rm的值进行算术右移、逻辑左移、逻辑右移和循环右移操作。在这些指令中,Rd、Rm、Rs必须为R0~R7;对于非立即数指令,Rd和Rm必须一致。Rd为目标寄存器,若省去Rd,表示其值与Rm寄存器一致;Rm为存放被移位数据寄存器;Rs为存放移位长度寄存器;imm为移位长度,ASR指令的移位长度范围为1~32,LSL指令的移位长度范围为0~31,LSR指令的移位长度范围为1~32。
表2-13 移位指令
(1)单向移位指令。算术右移指令ASR指令比较特别,它把要操作的字节当成有符号数,而符号位(b31)保持不变,其他位右移一位,即首先将b0位移入C中,其他位(b1~b31)右移一位,相当于操作数除以2。为了保证符号不变,ASR指令使符号位b31返回本身。逻辑右移指令LSR把32位操作数右移一位,首先将b0位移入C中,其他右移一位,0移入b31。根据结果,ASR、LSL、LSR指令对标志位N、Z有影响;最后移出位更新标志位C。
(2)循环移位指令。在循环右移指令ROR中,将b0位移入b31中的同时也移入C中,其他位右移一位,从b31~b0内部看来循环右移了一位。根据结果,ROR指令对标志位N、Z有影响;最后移出位更新标志位C。
4.位测试指令
位测试指令见表2-14。
表2-14 位测试指令
5.数据序转指令
数据序转指令如表2-15所示。该指令用于改变数据的字节顺序,其中,Rn 为源寄存器,Rd 为目标寄存器,且必须为R0~R7。REV指令将32位大端数据转小端数据存放或将32位小端数据转大端数据存放;REV16指令将一个32位数据分成两个16位大端数据,将这两个16位大端数据转小端数据存放,或者将一个32位数据划分成两个16位小端数据,将这两个16位小端数据转大端数据存放;REVSH指令将16位带符号大端数据转成32位带符号小端数据,或者将16位带符号小端数据转成32位带符号大端数据,如图2-4所示。这些指令不会影响标志位N、Z、C、V。
图2-4 反序操作
表2-15 数据序转指令
6.扩展类指令
扩展类指令如表2-16所示,其中,寄存器Rm存放待扩展操作数,寄存器Rd为目标寄存器,Rm、Rd必须为R0~R7。这些指令不会影响标志位N、Z、C、V。
表2-16 扩展类指令
跳转控制类指令如表2-17所示,这些指令不会影响标志位N、Z、C、V。
表2-17 跳转控制类指令
跳转控制类指令举例如下,特别要注意BL指令常用于调用子程序。
BEQ label @条件跳转,标志位Z=1时跳转到label对应的地址
BL funC @调用子程序funC,把跳转前的下条指令地址保存到LR
BX LR @返回到函数调用处
B指令所带条件众多,可以形成不同条件下的跳转,但只能在前256字节到后254字节地址范围内跳转。B指令所带的条件见表2-18。
表2-18 B指令所带的条件
不属于数据传送类、数据操作类、跳转控制类的指令称为其他指令,如表2-19所示,其中,spec_reg 表示特殊寄存器,如 APSR、IPSR、EPSR、IEPSR、IAPSR、EAPSR、PSR、MSP、PSP、PRIMASK或CONTROL。
表2-19中的中断指令(禁止总中断指令“CPSIE i”,使能总中断指令“CPSID i”)为编程必用指令,在实际编程时,由宏函数给出。
下面对两条休眠指令WFE与WFI进行简要说明。这两条指令只用于低功耗模式,并不产生其他操作(这一点类似于 NOP 指令)。休眠指令 WFE 执行情况由事件寄存器决定,若事件寄存器为零,只有在发生如下事件才执行:
(1)发生异常,且该异常未被异常屏蔽寄存器或当前优先级屏蔽。
(2)在进入异常期间,系统控制寄存器的SEVONPEND置1。
(3)若使能调试模式时,触发调试请求。
(4)外围设备发出一个事件或在多重处理器系统中另一个处理器使用SVC指令。
若事件寄存器为1,WFE指令清该寄存器后立刻执行。休眠指令WFI执行条件为:发生异常或PRIMASK.PM被清0,产生的中断将会抢先,或发生触发调试请求(不论调试是否被使能)。
表2-19 其他指令
续表