![嵌入式技术基础与实践(第5版)](https://wfqqreader-1252317822.image.myqcloud.com/cover/467/31794467/b_31794467.jpg)
2.2 指令系统
CPU的功能是从外部设备获得数据,通过加工、处理,再把处理结果送到CPU的外部世界。设计一个CPU,首先需要设计一套可以执行特定功能的操作命令,这种操作命令称为指令。CPU所能执行的各种指令的集合,称为该CPU的指令系统。表2-4所示为ARM Cortex-M指令集概况。在ARM系统中,架构(Architecture)即体系结构,主要指使用的指令集,由同一架构可以衍生出许多不同处理器型号。对ARM而言,其他芯片厂商,可由ARM提供的一种处理器型号具体生产出许多不同的MCU或应用处理器型号。ARMv7-M是一种架构型号,其中v7是指版本号,而基于该架构处理器的有Cortex-M3、Cortex-M4、Cortex-M4F等。
表2-4 ARM Cortex-M指令集概况
![](https://epubservercos.yuewen.com/2B70D2/17214368204801306/epubprivate/OEBPS/Images/Figure-T57_28507.jpg?sign=1739529253-nUiyvht40prqRSSag6VLpCTpncoAVH08-0-6857371486f0c3d65fea4c97b32e70da)
本节在给出指令简表与寻址方式的基础上,简要阐述ARM Cortex-M系列共有的57条基本指令功能。
2.2.1 指令简表与寻址方式
1. 指令简表
ARM Cortex-M4F不仅支持所有的Thumb和Thumb-2的全部指令,还支持浮点运算指令、DSP扩展指令等。常用的指令大体分为数据操作指令、转移指令、存储器数据传送指令和其他指令四大类,如表2-5所示。其他指令需要时请查阅《ARMv7-M参考手册》。
表2-5 常用指令简表
![](https://epubservercos.yuewen.com/2B70D2/17214368204801306/epubprivate/OEBPS/Images/Figure-T58_28509.jpg?sign=1739529253-3LvcvPzDW0pvLF3YrvOh2iLroFgrcVJZ-0-3cbefc986e6a6f26ef780a19a0a26cb9)
2. 寻址方式
指令是对数据的操作,通常把指令中所要操作的数据称为操作数,ARM Cortex-M4F处理器所需的操作数可能来自寄存器、指令代码、存储单元。而确定指令中所需操作数的各种方法称为寻址方式(Addressing Mode)。下面指令格式中的“{}”表示其中可选项。例如,LDRH Rt,[Rn{,#imm}],表示有“LDRH Rt,[Rn]”“LDRH Rt,[Rn,#imm]”两种指令格式。指令中的“[]”表示其中内容为地址,“//”表示注释。
1)立即数寻址
在立即数寻址方式中,操作数直接通过指令给出,数据包含在指令编码中,随着指令一起被编译成机器码存储于程序空间中,用“#”作为立即数的前导标识符。ARM Cortex-M4立即数范围为0x00~0xff。例如:
![](https://epubservercos.yuewen.com/2B70D2/17214368204801306/epubprivate/OEBPS/Images/Figure-P59_28510.jpg?sign=1739529253-IZhoiKtwyJxExn5p7RVkgnXjYrfNtcTP-0-1579a471e3669d694dfff9376f211f6c)
2)寄存器寻址
在寄存器寻址中,操作数来自于寄存器。例如:
![](https://epubservercos.yuewen.com/2B70D2/17214368204801306/epubprivate/OEBPS/Images/Figure-P59_28511.jpg?sign=1739529253-EOFgBb39YJBXtSILDFya66IybFsd8L8j-0-71d71f94b5ba4d4bab8387b2e8535241)
3)直接寻址
在直接寻址方式中,操作数来自于存储单元,指令中直接给出存储单元地址。指令码中,显示给出数据的位数,有字(4字节)、半字(2字节)、单字节3种情况。例如:
![](https://epubservercos.yuewen.com/2B70D2/17214368204801306/epubprivate/OEBPS/Images/Figure-P59_28512.jpg?sign=1739529253-k0U3JSJuhP9GZJAJQ0y3ndWhTnNsSia3-0-87b1e3966933df35680c7f54527fb5ba)
4)偏移寻址及寄存器间接寻址
在偏移寻址中,操作数来自于存储单元,指令中通过寄存器及偏移量给出存储单元的地址。偏移量不超过4KB(指令编码中偏移量为12位)。偏移量为0的偏移寻址也称为寄存器间接寻址。例如:
![](https://epubservercos.yuewen.com/2B70D2/17214368204801306/epubprivate/OEBPS/Images/Figure-P59_28513.jpg?sign=1739529253-2qZYHhkLeSXhUqXYwiDbRVokaChQ19hw-0-e79c3be6f6e8c0c83666b367bd675fb4)
2.2.2 数据传送类指令
数据传送类指令的功能有两种情况:一是取存储器地址空间中的数传送到寄存器中;二是将寄存器中的数传送到另一寄存器或存储器地址空间中。数据传送类基本指令有16条。
1. 取数指令
存储器中内容加载(Load)到寄存器中的指令如表2-6所示。其中,LDR、LDRH、LDRB指令分别表示加载来自存储器单元的一个字、半字和单字节(不足部分以0填充);LDRSH和LDRSB指令是指加载存储单元的半字、字节有符号数扩展成32位到指定寄存器Rt。
表2-6 取数指令
![](https://epubservercos.yuewen.com/2B70D2/17214368204801306/epubprivate/OEBPS/Images/Figure-T59_28515.jpg?sign=1739529253-uiGhOpRtudltbpV7spkWRjL7zLZpofMu-0-b6431d4fa74ecdb59c92beeb8e34246e)
在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-7所示。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寄存器必须位于列表首位。如果列表中不包含Rn,则将位于Rn+4×n地址回写到Rn寄存器中。这些指令不影响N、Z、C、V状态标志。
表2-7 存数指令
![](https://epubservercos.yuewen.com/2B70D2/17214368204801306/epubprivate/OEBPS/Images/Figure-T60_28519.jpg?sign=1739529253-NpgHGFdwJRo3Ug7UhVdGq7wjKUYnE3O4-0-916023e66dfab636588585ad27a05000)
3. 寄存器间数据传送指令
如表2-8所示,MOV指令中,Rd表示目标寄存器;imm为立即数,范围为0x00~0xff。当MOV指令中Rd为PC寄存器时,丢弃第0位;当出现跳转时,传送值的第0位清零后的值作为跳转地址。虽然MOV指令可以用作分支跳转指令,但强烈推荐使用BX或BLX指令。这些指令影响N、Z状态标志,但不影响C、V状态标志。
表2-8 寄存器间数据传送指令
![](https://epubservercos.yuewen.com/2B70D2/17214368204801306/epubprivate/OEBPS/Images/Figure-T61_28521.jpg?sign=1739529253-0TKOaup1tpL1izJKsQL7MldJxYAZryMy-0-c47399569283475838233bc1a9ff04ca)
4. 堆栈操作指令
堆栈(Stack)操作指令如表2-9所示。PUSH指令将寄存器值存于堆栈中,最低编号寄存器使用最低存储地址空间,最高编号寄存器使用最高存储地址空间;POP指令将值从堆栈中弹回寄存器,最低编号寄存器使用最低存储地址空间,最高编号寄存器使用最高存储地址空间。执行PUSH指令后,更新SP寄存器值SP=SP-4;执行POP指令后更新SP寄存器值SP=SP+4。若POP指令的reglist列表中包含了PC寄存器,在POP指令执行完成时跳转到该指针PC所指地址处。该值最低位通常用于更新xPSR的T位,此位必须置1,才能确保程序正常运行。
表2-9 堆栈操作指令
![](https://epubservercos.yuewen.com/2B70D2/17214368204801306/epubprivate/OEBPS/Images/Figure-T61_28523.jpg?sign=1739529253-ickQT6O816XSlqJLl2dfy0lGXx89G3cV-0-e3a1fb4d71def7028c01c8241007af34)
例如:
![](https://epubservercos.yuewen.com/2B70D2/17214368204801306/epubprivate/OEBPS/Images/Figure-P61_28719.jpg?sign=1739529253-mCmDZ36kMrn65JP3nZVxnMbsPfWkVYBY-0-db8fe870d7c6287b29fe96bc7ac755ca)
5. 生成与指针PC相关地址指令
如表2-10所示,ADR指令将指针PC值加上一个偏移量得到的地址写进目标寄存器中。若利用ADR指令生成的目标地址用于跳转指令BX、BLX,则必须确保该地址最后一位为1。Rd为目标寄存器,label为与指针PC相关的表达式。在该指令下,Rd必须为R0~R7,数值必须字对齐且在当前PC值的1020字节以内。此指令不影响N、Z、C、V状态标志。这条指令主要提供编译阶段使用,一般可看成一条伪指令。
表2-10 ADR指令
![](https://epubservercos.yuewen.com/2B70D2/17214368204801306/epubprivate/OEBPS/Images/Figure-T61_28720.jpg?sign=1739529253-9V5ina00sQJn2invP3O4CRsTrG0ewD5t-0-abd01c0423ff534ae5ae457fed488081)
2.2.3 数据操作类指令
数据操作主要指算术运算、逻辑运算、移位等。
1. 算术运算类指令
(1)算术运算类指令有加、减、乘、比较等,如表2-11所示。
表2-11 算术运算类指令
![](https://epubservercos.yuewen.com/2B70D2/17214368204801306/epubprivate/OEBPS/Images/Figure-T62_28526.jpg?sign=1739529253-WJ4cqvu4lznHyelO0h1ll4b0fE6nMBar-0-48e888b1d20ebd267da6d14cc7eab381)
(2)加、减指令对操作数的限制条件,如表2-12所示。
表2-12 ADC、ADD、RSB、SBC和SUB操作数限制条件
![](https://epubservercos.yuewen.com/2B70D2/17214368204801306/epubprivate/OEBPS/Images/Figure-T62_28528.jpg?sign=1739529253-0DAj1WwD0Hgc6u8MwiMzHie99qqASvGU-0-4420253c1565fa66a16081ac81cd9e8e)
2. 逻辑运算类指令
逻辑运算类指令如表2-13所示。AND、EOR和ORR指令把寄存器Rn、Rm值逐位与、异或和或操作;BIC指令是将寄存器Rn的值与Rm的值的反码按位做逻辑“与”操作,结果保存到Rd。这些指令更新N、Z状态标志,不影响C、Z状态标志。
表2-13 逻辑运算类指令
![](https://epubservercos.yuewen.com/2B70D2/17214368204801306/epubprivate/OEBPS/Images/Figure-T63_28530.jpg?sign=1739529253-uul4LLTLxriJGcba2pX3anfXaQYwTH1C-0-5dfcbdf157ca287c4b79d86b330883d4)
Rd、Rn和Rm必须为R0~R7,其中Rd为目标寄存器,Rn为存放第一个操作数寄存器,且必须和目标寄存器Rd一致(即Rd就是Rn),Rm为存放第二个操作数寄存器。
3. 移位类指令
移位类指令如表2-14所示。ASR、LSL、LSR和ROR指令,将寄存器Rm的值由寄存器Rs或立即数imm决定移动位数,执行算术右移、逻辑左移、逻辑右移和循环右移。这些指令中,Rd、Rm、Rs必须为R0~R7。对于非立即数指令,Rd和Rm必须一致。Rd为目标寄存器,若省去Rd,表示其值与Rm寄存器一致;Rm为存放被移位数据寄存器;Rs为存放移位长度寄存器;imm为移位长度,ASR指令移位长度范围为1~32,LSL指令移位长度范围为0~31,LSR指令移位长度范围为1~32。
表2-14 移位指令
![](https://epubservercos.yuewen.com/2B70D2/17214368204801306/epubprivate/OEBPS/Images/Figure-T63_28532.jpg?sign=1739529253-v8OcsuMRlzGj94LPFX1wrCyFZz0lpSyX-0-5589221f1546384a1370cd445fde3ce9)
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中,其他位右移一位。根据结果,ROR指令对标志位N、Z有影响,最后移出位更新C标志位。
4. 位测试指令
位测试指令如表2-15所示。
表2-15 位测试指令
![](https://epubservercos.yuewen.com/2B70D2/17214368204801306/epubprivate/OEBPS/Images/Figure-T64_28534.jpg?sign=1739529253-NqO5Mu44XJyF8YJ3FtcPN9W4gfbvOivj-0-260f02e5fedc43fad724507c8d685e6b)
5. 数据序转指令
数据序转指令如表2-16所示,该指令用于改变数据的字节顺序。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状态标志。
![](https://epubservercos.yuewen.com/2B70D2/17214368204801306/epubprivate/OEBPS/Images/Figure-P64_4903.jpg?sign=1739529253-9uZgLceSvRnB3ThpyY9lss8bujgKcvEy-0-0c511eaeec2d802908695896333ae721)
图2-4 反序操作
表2-16 数据序转指令
![](https://epubservercos.yuewen.com/2B70D2/17214368204801306/epubprivate/OEBPS/Images/Figure-T64_28536.jpg?sign=1739529253-qEk93NnY9zG25mnm4lmFsMFvqvlDxUwN-0-39930ae65cbe0c0d2e30d0e6873a1696)
6. 扩展类指令
扩展类指令如表2-17所示。寄存器Rm存放待扩展操作数;寄存器Rd为目标寄存器;Rm、Rd必须为R0~R7。这些指令不影响N、Z、C、V状态标志。
表2-17 扩展类指令
![](https://epubservercos.yuewen.com/2B70D2/17214368204801306/epubprivate/OEBPS/Images/Figure-T65_28538.jpg?sign=1739529253-Rfh8a0BSz2gr5Yc1v9LSr7Kk7mceBSXJ-0-d5851d16d81f3faf1e5c233e89430b8b)
2.2.4 跳转控制类指令
跳转控制类指令如表2-18所示,这些指令不影响N、Z、C、V状态标志。
表2-18 跳转控制类指令
![](https://epubservercos.yuewen.com/2B70D2/17214368204801306/epubprivate/OEBPS/Images/Figure-T65_28540.jpg?sign=1739529253-7J8wQ5Q2x8lHX1a7wK9J9u7ZPzFdOqPD-0-991e50f5420a966aadf50b6e310e37d0)
跳转控制类指令举例如下,特别注意BL用于调用子程序。
![](https://epubservercos.yuewen.com/2B70D2/17214368204801306/epubprivate/OEBPS/Images/Figure-P65_28724.jpg?sign=1739529253-GUhpFPTkMwEEGmMw4mjEiGanvcMZDaz7-0-1607daeed1b9e96b506b2e4d0989332f)
B指令所带条件众多,形成不同条件下的跳转,但只在前256字节至后254字节地址范围内跳转。B指令所带的条件如表2-19所示。
表2-19 B指令所带的条件
![](https://epubservercos.yuewen.com/2B70D2/17214368204801306/epubprivate/OEBPS/Images/Figure-T65_28727.jpg?sign=1739529253-9zTGBScu4v7A6HCTOM2nyGsC8m48ngLB-0-6d1034192ce7afeef8c7e71b9f28d7d7)
2.2.5 其他指令
未列入数据传输类、数据操作类、跳转控制类三大类的指令,归为其他指令,如表2-20所示。其中,spec_reg表示特殊寄存器,如APSR、IPSR、EPSR、IEPSR、IAPSR、EAPSR、PSR、MSP、PSP、PRIMASK或CONTROL。
表2-20 其他指令
![](https://epubservercos.yuewen.com/2B70D2/17214368204801306/epubprivate/OEBPS/Images/Figure-T66_28545.jpg?sign=1739529253-DN120OoG0c38JwLbNmTXjw9goe2UCvIH-0-4e75c322e24fbe8235996af509196208)
表中的中断指令(禁止总中断指令“CPSIE i”,使能总中断指令“CPSID i”)为编程必用指令,实际编程时,由宏函数给出。
下面对两条休眠指令WFE与WFI做简要说明。这两条指令均只用于低功耗模式,并不产生其他操作(这一点类似于NOP指令)。休眠指令WFE执行情况由事件寄存器决定。若事件寄存器为零,只有在发生如下事件时才执行:①发生异常,且该异常未被异常屏蔽寄存器或当前优先级屏蔽;②在进入异常期间,系统控制寄存器的SEVONPEND置位;③若使能调试模式时,触发调试请求;④外围设备发出一个事件或在多重处理器系统中另一个处理器使用SVC指令。若事件寄存器为1,WFE指令清该寄存器后立刻执行。休眠指令WFI执行条件为:发生异常,或PRIMASK.PM被清0,产生的中断将会先占,或者发生触发调试请求(不论调试是否被使能)。