点个小赞关注一波,持续更新…… [专栏]嵌入式软件校招笔记(点击跳转) [知识点] 嵌入式软件开发知识点学习 [知识点] ARM指令集详解 [知识点] 通讯协议(very重要) [项目] C++高并发Web服务器+个人改进项目详解 [八股] C/C++基础八股 [八股] C/C++进阶八股 [八股] 计算机网络八股 [八股] 操作系统八股 [八股] 嵌入式系统八股 [八股] Linux系统编程八股 [八股] Linux网络编程八股 秋招嵌入式企业面经 ARM指令集可以分为以下六种 数据处理指令 跳转指令 程序状态寄存器传输指令 Load/Store指令 协处理器指令 异常中断指令 1. ARM 数据处理指令 数据处理指令大致可分为3 类: (1)数据传送指令(如MOV、MVN) (2)算术逻辑运算指令(如ADD,SUM,AND) (3)比较指令(如CMP、TST)。 数据运算指令格式 <操作码><目标寄存器><第一操作寄存器><第二操作数> 操作码:表示执行哪种操作 目标寄存器:用于存储运算的结果 第一操作寄存器:存储第一个参与运算的数据(只能是寄存器) 第二操作数:第二个参与运算的数据(可以是寄存器,也可以是立即数) 数据处理指令只能对寄存器的内容操作。 所有ARM 数据处理指令均可选择使用S 后缀,以影响状态标志。比较指令CMP、CMN、TST和TEQ不需要后缀S,它们会直接影响状态标志。 默认情况下数据运算不会对条件位产生影响,当在指令后加后缀"s"后可以影响 2.1数据传送指令 2.1.1 MOV 数据传送指令 把一个数移动到目标寄存器 格式: MOV 条件 s 条件: 就表示mov指令是否要执行,如果满足条件就执行mov s: 是否影响cpsr的值 注:寄存器存储得数据,可以是一个常数,也可以是一个数经过左移右移得到的数据。 将立即数或寄存器(operant2)传送到目标寄存器Rd,可用于移位运算等操作。指令格式如下: MOV{cond}{S} Rd,operand2MOV 指令举例如下:MOV R1#0x10 @ R1=0x10MOV R0,R1 @ R0=R1MOVS R3,R1,LSL #2 @ R3=R1<<2,并影响标志位MOV PC,LR @ PC=LR ,子程序返回 立即数: 由0-255之间的任意数据通过循环右移偶数位得到的数据 立即数的本质就是包含在指令当中的数,属于指令的一部分 立即数的优点: 取址的时候就可以将其读到CPU,不用单独去内存读取,速度快 立即数的缺点 不能是任意的32位的数字,有局限性 2.1.2 MVN 数据非传送指令 将立即数或寄存器(operand2)按位取反后传送到目标寄存器(Rd)。指令格式如下: MVN{cond}{S} Rd,operand2MVN 指令举例如下:MVN R1,#0xFF ;R1=0xFFFFFF00MVN R1,R2 ;将R2 取反,结果存到R1 2.2算术逻辑运算指令 2.2.1 ADD 加法运算指令 将operand2 数据与Rn 的值相加,结果保存到Rd 寄存器。格式如下: ADD{cond}{S} Rd,Rn,operand2ADD 指令举例如下:ADDS R1,R1,#1 ;R1=R1+1ADD R1,R1,R2 ;R1=R1+R2ADDS R3,R1,R2,LSL #2 ;R3=R1+R2<<2 不可以两个数相加 ADD R1, #2, #2是错误的 2.2.2 SUB 减法运算指令 用寄存器Rn 减去operand2,结果保存到Rd 中,格式如下: SUB{cond}{S} Rd,Rn,operand2SUB 指令举例如下:SUBS R0,R0,#1 ;R0=R0-1SUBS R2,R1,R2 ;R2=R1-R2SUB R6,R7,#0x10 ;R6=R7-0x10 2.2.3 RSB 逆向减法指令 用寄存器operand2 减法Rn,结果保存到Rd 中,格式如下: RSB{cond}{S} Rd,Rn,operand2 SUB 指令举例如下: RSB R3,R1,#0xFF00 ;R3=0xFF00-R1RSBS R1,R2,R2,LSL #2 ;R1=R2<<2-R2=R2×3RSB R0,R1,#0 ;R0=-R1 2.2.4 ADC 带进位加法指令 将operand2 的数据与Rn 的值相加,再加上CPSR中的C 条件标志位。结果保存到Rd 寄存器。指令格式如下: ADC{cond}{S} Rd,Rn,operand2ADC 指令举例如下:ADDS R0,R0,R2ADC R1,R1,R3 ;使用ADC 实现64 位加法,(R1、R0)=(R1、R0)+(R3、R2) 2.2.5 SBC 带进位减法指令 用寄存器Rn 减去operand2,再减去CPSR 中的C条件标志位的非(即若C 标志清零,则结果减去1),结果保存到Rd 中。指令格式如下: SCB{cond}{S}Rd,Rn,operand2SBC 指令举例如下:SUBS R0,R0,R2SBC R1,R1,R3 ;使用SBC 实现64 位减法,(R1,R0)-(R3,R2) 2.2.6 RSC 带进位逆向减法指令 用寄存器operand2 减去Rn,再减去CPSR 中的C条件标志位,结果保存到Rd 中。指令格式如下: RSC{cond}{S} Rd,Rn,operand2RSC 指令举例如下:RSBS R2,R0,#0RSC R3,R1,#0 ;使用RSC 指令实现求64 位数值的负数 2.2.7 AND 逻辑与操作指令 将operand2 值与寄存器Rn 的值按位作逻辑与操作,结果保存到Rd中。指令格式如下: AND{cond}{S} Rd,Rn,operand2AND 指令举例如下:ANDS R0,R0,#x01 ;R0=R0&0x01,取出最低位数据AND R2,R1,R3 ;R2=R1&R3 2.2.8 ORR 逻辑或操作指令 将operand2 的值与寄存器Rn的值按位作逻辑或操作,结果保存到Rd 中。指令格式如下: ORR{cond}{S} Rd,Rn,operand2ORR 指令举例如下:ORR R0,R0,#x0F ;将R0 的低4 位置1MOV R1,R2,LSR #4ORR R3,R1,R3,LSL #8 ;使用ORR 指令将近R2 的高8位数据移入到R3 低8 位中 2.2.9 EOR 逻辑异或操作指令 将operand2 的值与寄存器Rn 的值按位作逻辑异或操作,结果保存到Rd中。指令格式如下: EOR{cond}{S}Rd,Rn,operand2EOR 指令举例如下:EOR R1,R1,#0x0F ;将R1 的低4 位取反EOR R2,R1,R0 ;R2=R1^R0EORS R0,R5,#0x01 ;将R5 和0x01 进行逻辑异或,结果保存到R0,并影响标志位 2.2.10 BIC 位清除指令 将寄存器Rn 的值与operand2 的值的反码按位作逻辑与操作,结果保存到Rd中。指令格式如下: BIC{cond}{S}Rd,Rn,operand2BIC 指令举例如下:BIC R2,R1,#0x0F ;将R1 的低4 位清零,其它位不变(将#0x0F中值为1的位对应R1中的位清零存到R2,R1并没有变) LSL(或ASL)左移操作 LSL(或ASL)可完成对通用寄存器中的内容进行逻辑(或算术)的左移操作,按操作数所指定的数量向左移位,低位用零来填充。其中,操作数可以是通用寄存器,也可以是立即数(0~31)。 MOV R0, R1, LSL #2 @ 将R1中的内容左移两位后传送到R0中。 MOV R1, #1MOV R2, #2MOV R3, #3LSL R1,R2,R3 @ R1=(R2 <<R3) LSR右移操作 LSR可完成对通用寄存器中的内容进行右移的操作,按操作数所指定的数量向右移位,左端用零来填充。其中,操作数可以是通用寄存器,也可以是立即数(0~31)。 MOV R0, R1, LSL #2 @ 将R1中的内容左移两位后传送到R0中。MOV R3, #1MOV R4, #2MOV R5, #3LSR R3,R4,R5 @ R3=(R4 >> R5) 2.3 比较指令 2.3.1 CMP 比较指令 指令使用寄存器Rn 的值减去operand2 的值,根据操作的结果更新CPSR中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。指令格式如下: 本质就是一条减法指令(SUBS),只是没有将运算结果存入寄存器 CMP R1, R2: =》CPSR寄存器如果标志位Z=1 -> 两者相等,如果Z=0 -> 两者不相等 =》标志位C=0 -> R1<R2,如果C=0或Z=1 -> R1<=R2 =》标志位C=1且Z=0 -> R1>R2 =》标志位C=1 -> R1>=R2 CMP{cond} Rn,operand2CMP 指令举例如下:CMP R1,#10 ;R1 与10 比较,设置相关标志位CMP R1,R2 ;R1 与R2 比较,设置相关标志位CMP 指令与SUBS 指令的区别在于CMP 指令不保存运算结果。在进行两个数据大小判断时,常用CMP指令及相应的条件码来操作。 2.3.2 CMN 负数比较指令 指令使用寄存器Rn 与值加上operand2 的值,根据操作的结果更新CPSR中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行,指令格式如下: CMN{cond} Rn,operand2CMN R0,#1 ;R0+1,判断R0 是否为1 的补码,若是Z 置位 CMN 指令与ADDS 指令的区别在于CMN 指令不保存运算结果。CMN指令可用于负数比较,比如CMNR0,#1 指令则表示R0 与-1 比较,若R0 为-(即1 的补码),则Z 置位,否则Z复位。 2.3.3 TST 位测试指令 指令将寄存器Rn 的值与operand2 的值按位作逻辑与操作,根据操作的结果更新CPSR中相应的条件标志位(当结果为0时,EQ位被设置),以便后面指令根据相应的条件标志来判断是否执行。指令格式如下: TST{cond} Rn,operand2TST 指令举例如下:TST R0,#0x01 ;判断R0 的最低位是否为0TST R1,#0x0F ;判断R1 的低4 位是否为0TST 指令与ANDS 指令的区别在于TST4 指令不保存运算结果。TST指令通常于EQ、NE条件码配合使用,当所有测试位均为0 时,EQ 有效,而只要有一个测试为不为0,则NE 有效。 2.3.4 TEQ 相等测试指令 指令寄存器Rn 的值与operand2 的值按位作逻辑异或操作,根据操作的结果更新CPSR中相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。指令格式如下: TEQ{cond} Rn,operand2TEQ 指令举例如下:TEQ R0,R1 ;比较R0 与R1 是否相等(不影响V 位和C 位)TST 指令与EORS 指令的区别在于TST 指令不保存运算结果。使用TEQ进行相等测试,常与EQNE 条件码配合使用,当两个数据相等时,EQ 有效,否则NE 有效。 补充:条件码 操作码 条件码助记符 标志 含义0000 EQ Z=1 相等0001 NE(Not Equal) Z=0 不相等0010 CS/HS(Carry Set/High or Same) C=1 无符号数大于或等于0011 CC/LO(Carry Clear/LOwer) C=0 无符号数小于0100 MI(MInus) N=1 负数0101 PL(PLus) N=0 正数或零0110 VS(oVerflow set) V=1 溢出0111 VC(oVerflow clear) V=0 没有溢出1000 HI(HIgh) C=1,Z=0 无符号数大于1001 LS(Lower or Same) C=0,Z=1 无符号数小于或等于1010 GE(Greater or Equal) N=V 有符号数大于或等于1011 LT(Less Than) N!=V 有符号数小于1100 GT(Greater Than) Z=0,N=V 有符号数大于1101 LE(Less or Equal) Z=1,N!=V 有符号数小于或等于1110 AL 任何 无条件执行(默认)1111 NV 任何 从不执行 MOV R1, #1 MOV R2, #2 BEQ FUNC @ if(EQ){B FUNC} 本质:if(Z==1){B FUNC} MOV R4, #3 MOV R3, #3FUNC: MOV R5, #5 MOV R6, #7 2.4 乘法指令 ARM7TDMI(-S)具有32×32 乘法指令、32×32 乘加指令、32×32结果为64 位的乘法指令。 2.4.1 MUL 32 位乘法指令 指令将Rm 和Rs 中的值相乘,结果的低32 位保存到Rd中。(只能是两个寄存器相乘) 指令格式如下: MUL{cond}{S} Rd,Rm,RsMUL 指令举例如下:MUL R1,R2,R3 ;R1=R2×R3MULS R0,R3,R7 ;R0=R3×R7,同时设置CPSR 中的N位和Z 位 2.4.2 MLA 32 位乘加指令 指令将Rm 和Rs 中的值相乘,再将乘积加上第3 个操作数,结果的低32位保存到Rd 中。指令格式如下: MLA{cond}{S} Rd,Rm,Rs,RnMLA 指令举例如下:MLA R1,R2,R3,R0 ;R1=R2×R3+10 2.4.3 UMULL 64 位无符号乘法指令 指令将Rm 和Rs 中的值作无符号数相乘,结果的低32位保存到RsLo 中,而高32 位保存到RdHi 中。指令格式如下: UMULL{cond}{S} RdLo,RdHi,Rm,RsUMULL 指令举例如下:UMULL R0,R1,R5,R8 ;(R1、R0)=R5×R8 2.4.4 UMLAL 64 位无符号乘加指令 指令将Rm 和Rs 中的值作无符号数相乘,64 位乘积与RdHi、RdLo相加,结果的低32 位保存到RdLo 中,而高32 位保存到RdHi 中。指令格式如下: UMLAL{cond}{S} RdLo,RdHi,Rm,RsUMLAL 指令举例如下:UMLAL R0,R1,R5,R8;(R1,R0)=R5×R8+(R1,R0) 2.4.5 SMULL 64 位有符号乘法指令 指令将Rm 和Rs 中的值作有符号数相乘,结果的低32位保存到RdLo 中,而高32 位保存到RdHi 中。指令格式如下: SMULL{cond}{S} RdLo,RdHi,Rm,RsSMULL 指令举例如下:SMULL R2,R3,R7,R6 ;(R3,R2)=R7×R6 2.4.6 SMLAL 64 位有符号乘加指令 指令将Rm 和Rs 中的值作有符号数相乘,64 位乘积与RdHi、RdLo,相加,结果的低32位保存到RdLo 中,而高32 位保存到RdHi 中。指令格式如下: SMLAL{cond}{S} RdLo,RdHi,Rm,RsSMLAL 指令举例如下:SMLAL R2,R3,R7,R6;(R3,R2)=R7×R6+(R3,R2) 2. ARM 跳转指令 跳转指令用于实现程序流程的跳转,在ARM程序中有两种方法可以实现程序流程的跳转: 使用专门的跳转指令; 直接向程序计数器PC写入跳转地址值。 通过向程序计数器PC写入跳转地址值,可以实现在4GB的地址空间中的任意跳转,在跳转之前结合使用MOV LR,PC等类似指令,可以保存将来的返回地址值,从而实现在4GB连续的线性地址空间的子程序调用。 ARM指令集中的跳转指令可以完成从当前指令向前或向后的32MB的地址空间的跳转,包括以下4条指令。 B:跳转指令。 BL:带返回的跳转指令。 BLX:带返回和状态切换的跳转指令。 BX:带状态切换的跳转指令。 3.1 B 跳转指令 跳转到指定的地址执行程序。 B{cond} label 举例如下: B WAITA ;跳转到WAITA 标号处B 0x1234 ;跳转到绝对地址0x1234 处 B指令是最简单的跳转指令。一旦遇到一个B指令,ARM处理器将立即跳转到给定的目标地址,从那里继续执行。注意存储在跳转指令中的实际值是相对当前PC值的一个偏移量,而不是一个绝对地址,它的值由汇编器来计算(参考寻址方式中的相对寻址)。它是24位有符号数,左移两位后有符号扩展为32位,表示的有效偏移为26位(前后32MB的地址空间)。(跳转到指令B 限制在当前指令的±32Mb 的范围内。) EQ:equal 相等NE:not equal,不相等GT:great than,大于GE greate equal,大于等于LT:less than,小于LE:less equal,.小于等于 3.2 BL 带链接的跳转指令 BL是另一个跳转指令,但在跳转之前,会在寄存器R14中保存PC的当前内容,因此,可以通过将R14的内容重新加载到PC中,来返回到跳转指令之后的那个指令处执行。该指令是实现子程序调用的一个基本但常用的手段。 指令将下一条指令的地址拷贝到R14(即LR)链接寄存器中,然后跳转到指定地址运行程序。 BL{cond} label 举例如下: BL Label ;当程序无条件跳转到标号Label处执行时,同时将当前的PC 值保存到R14中 跳转指令B 限制在当前指令的±32MB 的范围内。BL 指令用于子程序调用。 3.3 BX 带状态切换的跳转指令 跳转到Rm 指定的地址执行程序,若Rm 的位[0]为1,则跳转时自动将CPSR 中的标志T 置位,即把目标地址的代码解释为Thumb代码;若Rm 的位[0]为0,则跳转时自动将CPSR 中的标志T 复位,即把目标地址的代码解释为ARM代码。指令格式如下: BX{cond} Rm举例如下:ADRL R0,ThumbFun+1BX R0 ;跳转到R0 指定的地址,并根据R0 的最低位来切换处理器状态 3.4 BLX BLX目标地址:跳转,改变状态及保存PC值 汇编指令实例 area RESET, code,readonlycode32 entry start mov r0, #9 mov r1, #15loop cmp r0,r1 ;判断两个寄存器值是否相等 beq stop ;如果相等,结束程序 cmp r0,r1 ;判断两个寄存器值 subgt r0,r0,r1 ;如果大于,则执行r0=r0-r1 sublt r1,r1,r0 ;如果小于,则执行r1=r1-r0bloop ;跳转到loop标号继续执行stop end 伪代码:@ if(R1== R2)@ STOP()@ else if(R1 >R2)@ {@ R1 = R1- R2;@ goto START;@ }@ else{@ R2 = R2 - R1;@ goto START;@ }汇编: MOV R1, #1 MOV R2, #4START: CMP R1, R2 BEQ STOP SUBGT R1, R2, R3 SUBLT R2, R2, R1 B START STOP: @死循环防止程序跑飞 B STOP.end @汇编的结束 3. Load/Store指令 ARM 处理器是冯诺依曼存储结构,程序空间、RAM 空间及IO 映射空间统一编址,除对对RAM 操作以外,对外围IO、程序数据的访问均要通过加载/存储指令进行。 ARM 的加载/存储指令是可以实现字、半字、无符/有符字节操作;批量加载/存储指令可实现一条指令加载/存储多个寄存器的内容,大大提高效率。 Load/Store 内存访问指令在 ARM 寄存器和存储器之间传送数据。(访问(读写)内存)ARM 指令中有 3 种基本的数据传送指令。 (1)单寄存器 Load/Store 指令(Single Register),这些指令在 ARM 寄存器和存储器之间提供更灵活的单数据项传送方式。数据项可以是字节、16 位半字或 32 位字。 (2)多寄存器 Load/Store 内存访问指令。这些指令的灵活性比单寄存器传送指令差,但可以使大量的数据更有效地传送。它们用于进程的进入和退出、保存和恢复工作寄存器及复制存储器中的一块数据。 (3)单寄存器交换指令(Single Register Swap)。这些指令允许寄存器和存储器中的数值进行交换,在一条指令中有效地完成 Load/Store 操作。它们在用户级编程中很少用到。它的主要用途是在多处理器系统中实现信号量(Semaphores)的操作,以保证不会同时访问公用的数据结构。 (4)单寄存器的 Load/Store 指令,这种指令用于把单一的数据传入或者传出一个寄存器。支持的数据类型有字节(8 位)、半字(16 位)和字(32 位)。 3.1. 单寄存器的Load/Store指令 如表 3-8 所示列出了所有单寄存器的 Load/Store 指令。 使用单一数据传送指令(STR 和LDR)来装载和存储单一字节或字的数据从/到内存。LDR指令用于从内存读取数据放入寄存器中;STR 指令用于将寄存器中的数据保存到内存。指令格式如下: LDR{cond}{T} Rd,<地址>;加载指定地址上的数据(字),放入Rd中STR{cond}{T} Rd,<地址>;存储数据(字)到指定地址的存储单元,要存储的数据在Rd中LDR{cond}B{T} Rd,<地址>;加载字节数据,放入Rd中,即Rd最低字节有效,高24位清零STR{cond}B{T} Rd,<地址>;存储字节数据,要存储的数据在Rd,最低字节有效 其中,T 为可选后缀,若指令有T,那么即使处理器是在特权模式下,存储系统也将访问看成是处理器是在用户模式下。T在用户模式下无效,不能与前索引偏移一起使用T。 LDR/STR 指令寻址是非常灵活的,由两部分组成,一部分为一个基址寄存器,可以为任一个通用寄存器,另一部分为一个地址偏移量。地址偏移量有以下3种格式: (1) 立即数。立即数可以是一个无符号数值,这个数据可以加到基址寄存器,也可以从基址寄存器中减去这个数值。指令举例如下: LDR R1,[R0,#0x12] ;将R0+0x12 地址处的数据读出,保存到R1中(R0 的值不变)LDR R1,[R0,#-0x12];将R0-0x12 地址处的数据读出,保存到R1中(R0 的值不变)LDR R1,[R0] ;将R0 地址处的数据读出,保存到R1 中(零偏移) (2)寄存器。寄存器中的数值可以加到基址寄存器,也可以从基址寄存器中减去这个数值。指令举例值。指令举例如下: LDR R1,[R0,R2] ;将R0+R2 地址的数据计读出,保存到R1中(R0 的值不变)LDR R1,[R0,-R2] ;将R0-R2 地址处的数据计读出,保存到R1中(R0 的值不变) (3)寄存器及移位常数。寄存器移位后的值可以加到基址寄存器,也可以从基址寄存器中减去这个数值。指令举例如下: LDR R1,[R0,R2,LSL #2] ;将R0+R2*4地址处的数据读出,保存到R1中(R0,R2的值不变)LDR R1,[R0,-R2,LSL #2];将R0-R2*4地址处的数据计读出,保存到R1中(R0,R2的值不变) 从寻址方式的地址计算方法分,加载/存储指令有以下4 种形式: (1)零偏移。Rn 的值作为传送数据的地址,即地址偏移量为0。指令举例如下: LDR Rd,[Rn] (2)前索引偏移。在数据传送之前,将偏移量加到Rn 中,其结果作为传送数据的存储地址。若使用后缀“!”,则结果写回到Rn中,且Rn 值不允许为R15。指令举例如下: LDR Rd,[Rn,#0x04]!LDR Rd,[Rn,#-0x04] (3)程序相对偏移。程序相对偏移是索引形式的另一个版本。汇编器由PC 寄存器计算偏移量,并将PC寄存器作为Rn 生成前索引指令。不能使用后缀“!”。指令举例如下: LDR Rd,label ;label 为程序标号,label 必须是在当前指令的±4KB范围内 (4) 后索引偏移。Rn 的值用做传送数据的存储地址。在数据传送后,将偏移量与Rn相加,结果写回到Rn中。Rn 不允许是R15。指令举例如下: LDR Rd,[Rn],#0x04 地址对准--大多数情况下,必须保证用于32 位传送的地址是32 位对准的。 加载/存储字和无符号字节指令举例如下: LDR R2,[R5] ;加载R5 指定地址上的数据(字),放入R2 中STR R1,[R0,#0x04] ;将R1 的数据存储到R0+0x04存储单元,R0 值不变LDRB R3,[R2],#1 ;读取R2 地址上的一字节数据,并保存到R3中,R2=R3+1STRB R6,[R7] ;读R6 的数据保存到R7 指定的地址中,只存储一字节数据 加载/存储半字和带符号字节。这类LDR/STR 指令可能加载带符字节\加载带符号半字、加载/存储无符号半字。偏移量格式、寻址方式与加载/存储字和无符号字节指令相同。指令格式如下: