4.2 指令集介绍

来源:互联网 发布:ubuntu进不去系统 编辑:程序博客网 时间:2024/05/17 22:53
      ARM指令集——指令格式
     ARM指令的基本格式如下:
     其中<>号内的项是必须的,{}号内的项是可选的。各项的说明如下:
     ARM指令集——第2个操作数
     ARM指令的基本格式如下:
     灵活的使用第2个操作数“operand2”能够提高代码效率。它有如下的形式:
     (1)#immed_8r——常数表达式;
     (2)Rm——寄存器方式;
     (3)Rm,shift——寄存器移位方式;
     关于“#immed_8r——常数表达式”
     该常数必须对应8位位图,即一个8位的常数通过循环右移偶数位得到。
     关于“Rm——寄存器方式”:
     在寄存器方式下,操作数即为寄存器的数值。
     例如:
     SUB R1,R1,R2
     MOV PC,R0
     关于“Rm,shift——寄存器移位方式”:将寄存器的移位结果作为操作数,但Rm值保持不变,移位方法如下:
     关于“Rm,shift——寄存器移位方式”:
     例如:
     ADD R1,R1,R1,LSL #3 ;R1=R1+R1*8=9R1
     SUB R1,R1,R2,LSR R3 ;R1=R1-(R2/2R3)
     关于“ARM指令集——条件码”;ARM指令的基本格式如下:
     使用条件码“cond”可以实现高效的逻辑操作,提高代码效率。
     所有的ARM指令都可以条件执行,而Thumb指令只有B(跳转)指令具有条件执行 功能。如果指令不标明条件代码,将默认为无条件(AL)执行。
     关于“ARM指令集——条件码”:
     关于“ARM指令集——存储器访问指令”:ARM处理器是典型的RISC处理器,对存储器的访问只能使用加载和存储指令实现。ARM处理器是冯·诺依曼存储结构,程序空间、RAM空间及I/O映射空间统一编址,除对RAM操作以外,对外围IO、程序数据的访问均要通过加载/存储指令进行。
     存储器访问指令分为“单寄存器操作指令”和“多寄存器操作指令”。
     LDR/STR指令用于对内存变量的访问、内存缓冲区数据的访问、查表、外围部件的控制操作等。若使用LDR指令加载数据到PC寄存器,则实现程序跳转功能,这样也就实现了程序散转。
     所有单寄存器加载/存储指令可分为“字和无符号字节加载存储指令”和“半字和有符号字节加载存储指令。
     下面讲讲,LDR和STR——字和无符号字节加载/存储指令:LDR指令用于从内存中读取单一字或字节数据存入寄存器中,STR指令用于将寄存器中的单一字或字节数据保存到内存。指令格式如下:
     LDR{cond}{T} Rd,<地址> ;将指定地址上的字数据读入Rd
     STR{cond}{T} Rd,<地址> ;将Rd中的字数据存入指定地址
     LDR{cond}B{T} Rd,<地址> ;将指定地址上的字节数据读入Rd
     STR{cond}B{T} Rd,<地址> ;将Rd中的字节数据存入指定地址
     其中,T为可选后缀。若指令有T,那么即使处理器是在特权模式下,存储系统也将访问看成  是在用户模式下进行的。T在用户模式下无效,不能与前索引偏移一起使用T。
     关于“LDR和STR——字和无符号字节加载/存储指令编码”:
     关于“LDR和STR——字和无符号字节加载/存储指令”:LDR/STR指令寻址非常灵活,它由两部分组成,其中一部分为一个基址寄存器,可以为任一个通用寄存器;另一部分为一个地址偏移量。地址偏移量有以下3种格式:
     (1)立即数。立即数可以是一个无符号的数值。这个数据可以加到基址寄存器,也可以从基址寄存器中减去这个数值。 如:LDR R1,[R0,#0x12]
     (2)寄存器。寄存器中的数值可以加到基址寄存器,也可以从基址寄存器中减去这个数值。 如:LDR R1,[R0,R2]
     (3)寄存器及移位常数。寄存器移位后的值可以加到基址寄存器,也可以从基址寄存器中减去这个数值。 如:LDR R1,[R0,R2,LSL #2]
     从寻址方式的地址计算方法分,加载/存储指令有以下4种格式:
     (1)零偏移。 如:LDR Rd,[Rn]
     (2)前索引偏移。 如:LDR Rd,[Rn,#0x04]!
     (3)程序相对偏移。 如:LDR Rd,labe1
     (4)后索引偏移。 如:LDR Rd,[Rn],#0x04
     注意:大多数情况下,必须保证字数据操作的地址是32位对齐的。
     关于“LDR和STR——半字和有符号字节加载/存储指令”:这类LDR/STR指令可加载有符号半字或字节,可加载/存储无符号半字。偏移量格式、寻址方式与加载/存储字和无符号字节指令相同。
     LDR{cond}SB Rd,<地址> ;将指定地址上的有符号字节读入Rd
     LDR{cond}SH Rd,<地址> ;将指定地址上的有符号半字读入Rd
     LDR{cond}H Rd,<地址> ;将指定地址上的半字数据读入Rd
     STR{cond}H Rd,<地址> ;将Rd中的半字数据存入指定地址
     注意:1.有符号位半字/字节加载是指用符号位加载扩展到32位,无符号半字加载是指用零扩展到32位;
     2.半字读写的指定地址必须为偶数,否则将产生不可靠的结果;
     下面介绍一下,LDR和STR指令应用示例:
     1.加载/存储字和无符号字节指令LDR R2,[R5] ;将R5指向地址的字数据存入R2STR R1,[R0,#0x04];将R1的数据存储到R0+0x04地址LDRB R3,[R2],#1 ;将R2指向地址的字节数据存入R3,R2=R2+1STRBR6,[R7] ;将R7指向地址的字节数据存入R6
     2.加载/存储半字和有符号字节指令LDRSB R1,[R0,R3];将R0+R3地址上的字节数据存入R1;高24位用符号扩展LDRH R6,[R2],#2;将R2指向地址的半字数据存入R6,高16位用0扩展;读出后,R2=R2+2STRH R1,[R0,#2]!;将R1的半字数据保存到R0+2地址,只修改低2字节数据,R0=R0+2
     多寄存器加载/存储指令可以实现在一组寄存器和一块连续的内存单元之间传输数据。LDM为加载多个寄存器;STM为存储多个寄存器。允许一条指令传送16个寄存器的任何子集或所有寄存器。它们主要用于现场保护、数据复制、常数传递等。
     多寄存器加载/存储指令格式如下:
     (1)LDM{cond}<模式> Rn{!},reglist{^}
     (2)STM{cond}<模式> Rn{!},reglist{^}
     cond:指令执行的条件;
     模式:控制地址的增长方式,一共有8种模式;
     !:表示在操作结束后,将最后的地址写回Rn中;
     reglist :表示寄存器列表,可以包含多个寄存器,它们使用“,”隔开,如{R1,R2,R6-R9},寄存器由小到大排列;
     ^:加入该后缀后,进行数据传送且寄存器列表不包含PC时,加载/存储的寄存器是用户模式下的,而不是当前模式的寄存器。若在LDM指令且寄存器列表中包含有PC时使用,那么除了正常的多寄存器传送外,还将SPSR也拷贝到CPSR中,这可用于异常处理返回。
     注意:该后缀不允许在用户模式或系统模式下使用。
     关于“LDM和STM——多寄存器加载/存储指令编码”:
      多寄存器加载/存储指令的8种模式如下表所示,右边四种为堆栈操作、左边四种为数据传送操作。
      进行数据复制时,先设置好源数据指针和目标指针,然后使用块拷贝寻址指令LDMIA/STMIA、LDMIB/STMIB、LDMDA/STMDA、LDMDB/STMDB进行读取和存储 。
     进行堆栈操作操作时,要先设置堆栈指针(SP),然后使用堆栈寻址指令STMFD/LDMFD 、STMED/LDMED、STMFA/LDMFA和STMEA/LDMEA实现堆栈操作。
     堆栈操作(详见“4.1 寻址方式堆栈寻址”)和数据块传送指令类似,也有4种模式,它们之间的关系如下表所示:
     两段代码的执行结果是一样的,但是使用堆栈指令的压栈和出栈操作编程很简单(只要前后一致即可),而使用数据块指令进行压栈和出栈操作则需要考虑空与满、加与减对应的问题。
     关于“ARM存储器访问指令——寄存器和存储器交换指令”:
     SWP指令用于将一个内存单元(该单元地址放在寄存器Rn中)的内容读取到一个寄存器Rd中,同时将另一个寄存器Rm的内容写入到该内存单元中。使用SWP可实现信号量操作。
     指令格式如下: SWP{cond}{B} Rd,Rm,[Rn]
     其中,B为可选后缀,若有B,则交换字节,否则交换32位字;Rd用于保存从存储器中读入的数据;Rm的数据用于存储到存储器中,若Rm与Rn相同,则为寄存器与存储器内容进行交换;Rn为要进行数据交换的存储器地址,Rn不能与Rd和Rm相同。
     SWP和SWPB——寄存器和存储器交换指令编码
     关于“ARM指令集——ARM数据处理指令”:数据处理指令大致可分为3类:
     (1)数据传送指令;(2)算术逻辑运算指令;(3)比较指令。
     数据处理指令只能对寄存器的内容进行操作,而不能对内存中的数据进行操作。所有ARM数据处理指令均可选择使用S后缀,并影响状态标志。
     下面讲讲“ARM数据处理指令——指令编码”:
     关于“ARM数据处理指令——数据传送”:
     关于“加法运算指令——ADD”,指令将operand2的值与Rn的值相加,结果保存到Rd寄存器。 指令格式如下: ADD{cond}{S} Rd,Rn,operand2
     应用示例:
     ADDS R1,R1,#1 ;R1=R1+1,并影响标志位
     ADD R1,R1,R2 ;R1=R1+R2
     关于“减法运算指令——SUB”,指令用寄存器Rn减去operand2,结果保存到Rd中。指令格式如下:SUB{cond}{S} Rd,Rn,operand2
     应用示例:
     SUBS R0,R0,#1 ;R0=R0-1 ,并影响标志位
     SUBS R2,R1,R2 ;R2=R1-R2 ,并影响标志位
     关于“逆向减法运算指令——RSB”,指令将operand2的值减去Rn,结果保存到Rd中。指令格式如下: RSB{cond}{S} Rd,Rn,operand2
     应用示例:
     RSB R3,R1,#0xFF00 ;R3=0xFF00-R1
     RSBS R1,R2,R2,LSL #2 ;R1=(R2<<2)-R2=R2×3
     关于“带进位加法指令——ADC”,将operand2的值与Rn的值相加,再加上CPSR中的C条件标志位,结果保存到Rd寄存器。指令格式如下:
     ADC{cond}{S} Rd,Rn,operand2
     应用示例(使用ADC实现64位加法,结果存于R1、R0中):
     ADDS R0,R0,R2 ;R0等于低32位相加,并影响标志位
     ADC R1,R1,R3 ;R1等于高32位相加,并加上低位进位
     关于“带进位减法指令——SBC”,用寄存器Rn减去operand2,再减去CPSR中的C条件标志位的非(即若C标志清零,则结果减去1),结果保存到Rd中。指令格式如下:
     SBC{cond}{S} Rd,Rn,operand2
     应用示例(使用SBC实现64位减法,结果存于R1、R0中):
     SUBS R0,R0,R2 ; 低32位相减,并影响标志位
     SBC R1,R1,R3 ;高32位相减,并减去低位借位
     关于“带进位逆向减法指令——RSC”,指令用寄存器operand2减去Rn,再减去CPSR中的C条件标志位,结果保存到Rd中。指令格式如下:RSC{cond}{S} Rd,Rn,operand2
     应用示例(使用RSC指令实现求64位数值的负数 ):
     RSBS R2,R0,#0
     RSC R3,R1,#0
     关于“ARM数据处理指令——逻辑运算指令”
     逻辑与操作指令——AND,指令将operand2的值与寄存器Rn的值按位作逻辑“与”操作,结果保存到Rd中。指令格式如下: AND{cond}{S} Rd,Rn,operand2
     应用示例:
     ANDS R0,R0,#0x01 ;R0=R0&0x01,取出最低位数据
     AND R2,R1,R3 ;R2=R1&R3
     逻辑或操作指令——ORR指令将operand2的值与寄存器Rn的值按位作逻辑“或”操作,结果保存到Rd中。指令格式如下:ORR{cond}{S} Rd,Rn, operand2
     应用示例:
     ORR R0,R0,#0x0F ;将R0的低4位置1
     MOV R1,R2,LSR #24 ;使用ORR指令将R2的高8位
     ORR R3,R1,R3,LSL #8 ;数据移入到R3低8位中
     逻辑异或操作指令——EOR指令将operand2的值与寄存器Rn的值按位作逻辑“异或”操作,结果保存到Rd中。指令格式如下:EOR{cond}{S} Rd,Rn, operand2
     应用示例:
     EOR R1,R1,#0x0F ;将R1的低4位取反
     EOR R2,R1,R0 ;R2=R1^R0
     EORS R0,R5,#0x01 ; 将R5和0x01进行逻辑异或,;结果保存到R0,并影响标志位
     位清除指令——BIC,指令将寄存器Rn的值与operand2的值的反码按位作逻辑“与”操作,结果保存到Rd中。指令格式如下:BIC{cond}{S} Rd,Rn, operand2
     应用示例:
     BIC R1,R1,#0x0F ;将R1的低4位清零,其它位不变
     BIC R1,R2,R3 ;将R3的反码和R2相逻辑“与”,
     ;结果保存到R1中
     ARM数据处理指令——比较指令
     比较指令——CMP指令将寄存器Rn的值减去operand2的值,根据操作的结果更新CPSR中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。指令格式如下:CMP{cond}   Rn, operand2
     应用示例:
     CMP R1,#10 ; R1与10比较,设置相关标志位
     CMP R1,R2 ; R1与R2比较,设置相关标志位
     比较指令——CMP指令将寄存器Rn的值减去operand2的值,根据操作的结果更新CPSR中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。指令格式如下:CMP{cond}Rn, operand2
     注意:CMP指令与SUBS指令的区别在于CMP指令不保存运算结果。在进行两个数据的大小判断时,常用CMP指令及相应的条件码来操作。
     负数比较指令——CMN,指令使用寄存器Rn的值加上operand2的值,根据操作的结果更新CPSR中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。指令格式如下: CMN{cond} Rn, operand2
     应用示例:
     CMN R0,#1 ; R0+1,判断R0是否为1的补码
     如果是,则设置Z标志位
     注意:CMN指令与ADDS指令的区别在于CMN指令不保存运算结果。CMN指令可用于负数比较,比如CMN R0,#1指令则表示R0与-1比较,若R0为-1(即1的补码),则Z置位;否则Z复位。
     位测试指令——TST,指令将寄存器Rn的值与operand2的值按位作逻辑“与”操作,根据操作的结果更新CPSR中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。指令 格式如下:TST{cond} Rn, operand2
     应用示例:
     TST R0,#0x01 ; 判断R0的最低位是否为0
     TST R1,#0x0F ; 判断R1的低4位是否为0
     注意:TST指令与ANDS指令的区别在于TST指令不保存运算结果。TST指令通常与EQ、NE条件码配合使用,当所有测试位均为0时,EQ有效,而只要有一个测试位不为0,则NE有效。
     相等测试指令——TEQ指令将寄存器Rn的值与operand2的值按位作逻辑“异或”操作,根据操作的结果更新CPSR中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。指令格式如下:TEQ{cond} Rn, operand2
     应用示例:
     TEQ R0,R1 ; 比较R0与R1是否相等 (不影响V位和C位)
     注意:TEQ指令与EORS指令的区别在于TEQ指令不保存运算结果。使用TEQ进行相等测试时,常与EQ、NE条件码配合使用。当两个数据相等时,EQ有效;否则NE有效。
     ARM指令集——乘法指令,ARM7TDMI具有三种乘法指令,分别为:(1)32×32位乘法指令;(2)32× 32位乘加指令;(3)32× 32位结果为64位的乘/乘加指令。
     乘法指令编码:
     ARM指令——乘法指令
     32位乘法指令——MUL指令将Rm和Rs中的值相乘,结果的低32位保存到Rd中。指令格式如下:MUL{cond}{S} Rd,Rm,Rs
     应用示例:
     MUL R1,R2,R3 ;R1=R2×R3
     MULS R0,R3,R7 ;R0=R3×R7,同时影响CPSR中的N位和Z位
     32位乘加指令——MLA指令将Rm和Rs中的值相乘,再将乘积加上第3个操作数,结果的低32位保存到Rd中。指令格式如下: MLA{cond}{S} Rd,Rm,Rs,Rn
     应用示例:
     MLA R1,R2,R3,R0 ; R1=R2×R3+R0
     64位无符号乘法指令——UMULL指令将Rm和Rs中的值作无符号数相乘,结果的低32位保存到RdLo中,而高32位保存到RdHi中。指令格式如下:
     UMULL{cond}{S} RdLo,RdHi,Rm,Rs
     应用示例:
     UMULL R0,R1,R5,R8 ; (R1、R0)=R5×R8
     64位无符号乘加指令——UMLAL指令将Rm和Rs中的值作无符号数相乘,64位乘积与RdHi、RdLo相加,结果的低32位保存到RdLo中,而高32位保存到RdHi中。指令格式如下:
    UMLAL{cond}{S} RdLo,RdHi,Rm,Rs
     应用示例:
     UMLAL R0,R1,R5,R8 ;(R1、R0)=R5×R8+(R1、R0)
     64位有符号乘法指令——SMULL指令将Rm和Rs中的值作有符号数相乘,结果的低32位保存到  RdLo中,而高32位保存到RdHi中。指令格式如下:
     SMULL{cond}{S} RdLo,RdHi,Rm,Rs
     应用示例:
     SMULL R2,R3,R7,R6 ; (R3、R2)=R7×R6
     64位有符号乘加指令——SMLAL指令将Rm和Rs中的值作有符号数相乘,64位乘积与RdHi、RdLo相加,结果的低32位保存到RdLo中,而高32位保存到RdHi中。指令格式如下:
    SMLAL{cond}{S} RdLo,RdHi,Rm,Rs
     应用示例:
     SMLAL R2,R3,R7,R6 ; (R3、R2)=R7×R6+(R3、R2)
     ARM指令集——分支指令
     在ARM中有两种方式可以实现程序的跳转,一种是使用分支指令直接跳转,另一种则是直接向PC寄存器赋值实现跳转。 分支指令有以下三种:(1)分支指令B;(2)带链接的分支指令BL;(3)带状态切换的分支指令BX。
     分支指令——B指令,该指令跳转范围限制在当前指令的±32M字节地址内(ARM指令为字对齐,最低2位地址固定为0)。指令格式如下:B{cond} Label
     应用示例:
     B WAITA ; 跳转到WAITA标号处
     B 0x1234 ; 跳转到绝对地址0x1234处
     带链接的分支指令——BL指令适用于子程序调用,使用该指令后,下一条指令的地址被拷贝 到R14(即LR) 连接寄存器中,然后跳转到指定地址运行程序。跳转范围限制在当前指令的±32M字节地址内。指令格式如下:BL{cond} Label
     带链接的分支指令——BL,指令适用于子程序调用,使用该指令后,下一条指令的地址被拷贝到R14(即LR) 连接寄存器中,然后跳转到指定地址运行程序。跳转范围限制在当前指令的±32M字节地址内。指令格式如下:BL{cond} Label
     应用示例:
     BL DELAY ; 调用子程序DELAY
     带状态切换的分支指令——BX指令,该指令可以根据跳转地址(Rm)的最低位来切换处理器状态。其跳转范围限制在当前指令的±32M字节地址内(ARM指令为字对齐,最低2位地址固定为0)。指令格式如下: BX{cond} Rm
     应用示例:
     ADRL R0,ThumbFun+1 ;将Thumb程序的入口地址加1存入R0BX R0 ; 跳转到R0指定的地址,并根据R0的最低位来切换处理器状态
     ARM指令集——协处理器指令
     ARM内核支持协处理器操作,协处理器的控制要通过协处理器命令实现。
     ARM协处理器指令——数据操作指令
     ARM处理器通过CDP指令通知ARM协处理器执行特定的操作。该操作由协处理器完成,即对命令的参数的解释与协处理器有关,指令的使用取决于协处理器。若协处理器不能成功地执行该操作,将产生未定义指令异常中断。指令格式如下:
     ARM协处理器指令——数据存取指令
     协处理器数据存取指令LDC/STC指令可以将某一连续内存单元的数据读取到协处理器的寄存器中,或者将协处理器的寄存器数据写入到某一连续的内存单元中,传送的字数由协处理器来控制。若协处理器不能成功地执行该操作,将产生未定义指令异常中断。
     ARM协处理器指令——寄存器传送指令
     如果需要在ARM处理器中的寄存器与协处理器中的寄存器之间进行数据传送,那么可以使用MCR/MRC指令。MCR指令用于将ARM处理器的寄存器中的数据传送到协处理器的寄存器。MRC指令用于将协处理器的寄存器中的数据传送到ARM处理器的寄存器中。若协处理器不能成功地执行该操作,将产生未定义指令异常中断。
     ARM杂项指令——软中断指令
     SWI指令用于产生软中断,从而实现在从户模式变换到管理模式,并且将CPSR保存到管理模式的SPSR中,然后程序跳转到SWI异常入口。在其它模式下也可使用SWI指令,处理器同样地切换到管理模式。
     该指令主要用于用户程序调用操作系统的系统服务,操作系统在SWI异常处理程序中进行相应的系统服务。

     ARM杂项指令——状态寄存器读指令
    在ARM处理器中,只有MRS指令可以对状态寄存器CPSR和SPSR进行读操作。通过读CPSR可以了解当前处理器的工作状态。读SPSR寄存器可以了解到进入异常前的处理器状态。

     ARM杂项指令——状态寄存器写指令
     在ARM处理器中,只有MSR指令可以对状态寄存器CPSR和SPSR进行写操作。与MRS配合使用,可以实现对CPSR或SPSR寄存器的读-修改-写操作,可以切换处理器模式、或者允许/禁止IRQ/FIQ中断等。
     ARM伪指令
     ARM伪指令不属于ARM指令集中的指令,是为了编程方便而定义的。伪指令可以像其它ARM指令 一样使用,但在编译时这些指令将被等效的ARM指令代替。ARM伪指令有四条,分别为ADR伪指令、ADRL伪指令、LDR伪指令、NOP伪指令。
     ARM伪指令——小范围的地址读取
     ADR伪指令将基于PC相对偏移的地址值或基于寄存器相对偏移的地址值读取到寄存器中。在汇 编编译器编译源程序时,ADR伪指令被编译器替换成一条合适的指令。通常,编译器用一条ADD指令或SUB指令来实现该ADR伪指令的功能,若不能用一条指令实现,则产生错误,编译失败。
     地址表达式expr的取指范围:
     (1)当地址值不是字对齐时,其取指范围为-255~255;
     (2)当地址值是字对齐时,其取指范围为-1020~1020;
     (3)当地址值是16字节对齐时,其取指范围将更大。
     ARM伪指令——中等范围的地址读取
     ADRL伪指令将基于PC相对偏移的地址值或基于寄存器相对偏移的地址值读取到寄存器中,比  ADR伪指令可以读取更大范围的地址 。在汇编编译器编译源程序时,ADRL伪指令被编译器替换成两条合适的指令。若不能用两条指令实现,则产生错误,编译失败。
     地址表达式expr的取指范围:
     1.当地址值不是字对齐时,其取指范围为-64K~64K;
     2.当地址值是字对齐时,其取指范围为-256K~256K;
     3.当地址值是16字节对齐时,其取指范围将更大。
     ARM伪指令——大范围的地址读取
     LDR伪指令用于加载32位的立即数或一个地址值到指定寄存器。在汇编编译源程序时,LDR伪 指令被编译器替换成一条合适的指令。若加载的常数未超出MOV或MVN的范围,则使用MOV或MVN指令代替该LDR伪指令,否则汇编器将常量放入文字池,并使用一条程序相对偏移的LDR指令从文字池读出常量。
     注意:
     1.从指令位置到文字池的偏移量必须小于4KB;
     2.与ARM指令的LDR相比,伪指令的LDR的参数有“=”号。
     ARM伪指令——空操作伪指令
     NOP伪指令在汇编时将会被代替成ARM中的空操作,比如可能是“MOV R0,R0”指令
     Thumb指令
     Thumb指令集可以看作是ARM指令压缩形式的子集,它是为减小代码量而提出的,具有16位的代码密度。Thumb指令体系不完整,只支持通用功能。必要时仍需要使用ARM指令,如进入异常时。
在这里要说明一下:Thumb指令的格式与使用方式与ARM指令集类似,而且使用并不是很频繁,建议这部分内容选修。
     Thumb指令小节目录
     (1).Thumb指令集与ARM指令集的区别
     (2).存储器访问指令
     (3).数据处理指令
     (4).分支指令
     (5).杂项指令
     (6).伪指令
     下面讲一下Thumb指令集与ARM指令集的区别
     首先说一下,Thumb指令集较ARM指令集有如下限制:
     (1)只有B指令可以条件执行,其它指令都不能条件执行;
     (2)分支指令的跳转范围有更多限制;
     (3)数据处理指令的操作结果必须放入其中一个;
     (4)单寄存器访问指令,只能操作R0~R7;
     (5)LDM和STM指令可以对R0~R7的任何子集进行操作;
     下面讲一下,Thumb存储器访问指令
     LDR和STR——加载/存储指令:
     根据指令的寻址方式不同,可以分为以下三类:(1)立即数偏移寻址;(2)寄存器偏移寻 址;(3)PC或SP相对偏移寻址;
     立即数偏移寻址:以这种寻址方式对存储器访问时,存储器的地址以一个寄存器的内容为基址,在偏移一个立即数后指明。指令格式如下:
     LDR Rd,[Rn,#immed_5×4] ;加载内存中的字数据到寄存器Rd中
     STR Rd,[Rn,#immed_5×4] ;将Rd中的字数据存储到指定地址的内存中
     LDRH Rd,[Rn,#immed_5×2] ;加载内存中的半字数据到寄存器Rd的低16位中
     STRH Rd,[Rn,#immed_5×2] ;存储Rd中的低16位半字数据到指定的内存单元
     LDRB Rd,[Rn,#immed_5×1] ;加载内存中的字节数据到寄存器Rd中
     STRB Rd,[Rn,#immed_5×1] ;存储Rd中的低8位字节数据到指定的内存单元
     其中:Rd 表示加载或存储的寄存器。必须为R0~R7。
     Rn 表示基址寄存器。必须为R0~R7。
     immed_5×N 表示立即数偏移量,其取值范围为(0~31)×N。
     立即数偏移指令编码
     立即数偏移寻址
     应用示例:
     LDR R0,[R1,#0x4]
     STR R3,[R4]
     LDRH R5,[R0,#0x02]
     STRH R1,[R0,#0x08]
     LDRB R3,[R6,#20]
     STRB R1,[R0,#31]
     注意:进行字数据访问时,必须保证传送地址为32位对齐。进行半字数据访问时,必须保证传送地址为16位对齐。
     下面讲一下,寄存器偏移寻址
     这种寻址方式是以一个寄存器的内容为基址,以另一个寄存器的内容为偏移量,两者相加作为存储器的地址。指令格式如下:
     LDR Rd,[Rn,Rm] ;加载一个字数据
     STR Rd,[Rn,Rm] ;存储一个字数据
     LDRH Rd,[Rn,Rm] ;加载一个无符号半字数据
     STRH Rd,[Rn,Rm] ;存储一个无符号半字数据
     LDRB Rd,[Rn,Rm] ;加载一个无符号字节数据
     STRB Rd,[Rn,Rm] ;存储一个无符号字节数据
     LDRSH Rd,[Rn,Rm] ;加载一个有符号半字数据
     LDRSB Rd,[Rn,Rm] ;存储一个有符号半字数据
     其中:Rd 表示加载或存储的寄存器。必须为R0~R7。
     Rn 表示基址寄存器。必须为R0~R7。
     Rm 表示内含数偏移量的寄存器,必须为R0~R7 。
     寄存器偏移指令编码
     寄存器偏移寻址
     应用示例:
     LDR R3,[R1,R0]
     STR R1,[R0,R2]
     LDRH R6,[R0,R1]
     STRH R0,[R4,R5]
     LDRB R2,[R5,R1]
     STRB R1,[R3,R2]
     LDRSH R7,[R6,R3]
     LDRSB R5,[R7,R2]
     注意:进行字数据访问时,必须保证传送地址为32位对齐。进行半字数据访问时,必须保证传送地址为16位对齐。
     相对偏移寻址:这种寻址方式是以PC或SP寄存器的内容为基址,以一个立即数为偏移量,两  者相加作为存储器的地址。指令格式如下:
     LDR Rd,[PC,#immed_8×4]
     LDR Rd,label
     LDR Rd,[SP,#immed_8×4]
     STR Rd,[SP,#immed_8×4]
     其中:Rd 表示加载或存储的寄存器。必须为R0~R7。
     immed_8×4 表示偏移量,取值范围是(0~255)×4 。
     label 表示程序相对偏移表达式,Label必须在当前指令之后的1KB范围内。
     相对偏移指令编码
     相对偏移寻址
     应用示例:
     LDR R0,[PC,#0x08] ;读取PC+0x08地址上的字数据,保存到R0中
     LDR R7,LOCALDAT ;读取LOCALDAT地址上的字数据,保存到R7中LDR R3,[SP,#1020] ; 读取SP+1020地址上的字数据,保存到R3中STR R2,[SP] 存储R2寄存器的数据到SP指向的存储单元 (偏移量为0)
     注意:以PC作为基地址的相对偏移寻址指令只有LDR,而没有STR指令。
     Thumb存储器访问指令
     PUSH和POP——寄存器入栈及出栈指令
     实现低寄存器和可选的LR寄存器入栈及低寄存器和可选的PC寄存器出栈操作。堆栈地址由SP寄存器设置,堆栈是满递减堆栈。
     寄存器入栈及出栈指令
     应用示例:
     PUSH {R0-R7,LR} ;将低寄存器R0~R7全部入栈,LR也入栈POP {R0-R7,PC} ;将堆栈中的数据弹出到低寄存器R0~R7及PC中
     Thumb存储器访问指令
     LDMIA和STMIA——多寄存器加载/存储指令
     可以实现在一组寄存器和一块连续的内存单元之间传输数据。LDMIA为加载多个寄存器;STMIA为存储多个寄存器。使用它们允许一条指令传送8个低寄存器R0~R7的任何子集。
     多寄存器加载/存储指令
     LDMIA/STMIA 的主要用于数据复制、参数传送等。进行数据传送时,每次传送后地址加4。
     若Rn在寄存器列表中:
     对于LDMIA指令,Rn的最终值是加载的值,而不是增加后的地址;
     对于STMIA指令,若Rn是寄存器列表中的最低数字的寄存器,则Rn存储的值为Rn在初值,其它 情况不可预知。
     应用示例:
     LDMIA R0!,{R2-R7} ;加载R0指向的地址上的多字数据,保存到R2~R7中, R0的值更新。
STMIA R1!,{R2-R7} ;将R2~R7的数据存储到R1指向的地址上,R1值更新
     Thumb数据处理指令
     Thumb数据处理指令涵盖了编译器需要的大多数操作。大部分的Thumb数据处理指令采用2地址格式,不能在单指令中同时完成一个操作数的移位及一个ALU操作。所以数据处理操作比ARM状态的更少,并且访问寄存器R8~R15受到限制。数据处理指令分为两类:
     (1)数据传送指令;(2)算术逻辑运算指令
     Thumb数据处理指令——数据传送指令
数据传送指令——MOV
      注意:
     “MOV Rd,#expr”指令会更新N和Z标志,对标志C和V无影响。
     “MOV Rd,Rm”指令,若Rd或Rm是高寄存器(R8~R15),则标志不受影响,若Rd或Rm都是低  寄存器(R0~R7),则更新标志N和Z,且清除标志C和V。
     应用示例:
     MOV R1,#0x10 ; R1=0x10
     MOV R0,R8 ; R0=R8
     MOV PC,LR ; PC=LR,子程序返回
     数据传送指令——MVN
     MVN指令将寄存器Rm按位取反后传送到目标寄存器Rd中。指令的执行会更新N和Z标志,对标志C和V无影响。其指令格式如下:
     应用示例:
     MVN R1,R2 ; 将R2取反,结果存于R1
     数据传送指令——NEG
     NEG指令将寄存器Rm乘以-1后传送到目标寄存器Rd中。指令会更新N、Z、C和V标志。其指令格式如下:
     应用示例:
     NEG R1,R0 ; R1=-R0
     Thumb数据处理指令——算术逻辑运算指令
     算术逻辑指令包括以下几类:(1)算术指令(2)逻辑运算指令(3)移位指令(4)比较指令。
     算术运算指令——ADD
     ADD指令将两个数据相加,结果保存到Rd寄存器中。
     应用示例:
     ADD R1,R1,R0 ; R1=R0+R1
     ADD R1,R1,#7 ; R1=R1+7
     ADD R1,#200 ; R1=R1+200
     应用示例:
     ADD R1,R10 ; R1=R1+R10
     应用示例:
     ADD SP,#-500 ;SP=SP-500
     算术运算指令——SUB
     SUB指令将两个数据相减,结果保存到Rd寄存器中。
     应用示例:
     SUB R1,R1,R0 ; R1=R1-R0
     SUB R1,R1,#7 ; R1=R1-7
     SUB R1,#200 ; R1=R1-200
     应用示例:
     SUB SP,#-380 ;SP=SP-380
     算术运算指令——ADC
     ADC指令将Rm的值相加,再加上CPSR中的C条件标志位,结果保存到Rd寄存器。
     应用示例(64位加法):
     ADD R0,R2
     ADC R1,R3 ;(R1、R2)=(R1、R0)+(R3、R2)
     算术运算指令——SBC
     SBC指令用寄存器Rd减去Rm,再减去CPSR中的C条件标志位的非,结果保存到Rd寄存器。
     应用示例(64位减法):
     SUB R0,R2
     SBC R1,R3 ;(R1、R2)=(R1、R0)-(R3、R2)
     算术运算指令——MUL
     MUL乘法指令用寄存器Rd乘以Rm,结果保存到Rd寄存器。
     应用示例(64位减法):
     MUL R0,R1 ;R0=R0×R1
     逻辑运算指令——AND
     AND指令将寄存器Rd的值与寄存器Rm的值按位作逻辑“与”操作,结果保存到Rd寄存器中。
     应用示例:
     MOV R1,#0x0F
     AND R0,R1 ; R0=R0 & R1,清零R0高24位
     逻辑运算指令——ORR
     ORR指令将寄存器Rd的值与寄存器Rn的值按位作逻辑“或”操作,结果保存到Rd寄存器中。
      应用示例:
      MOV R1,#0x0F
      ORR R0,R1 ; R0=R0 | R1,置位R0低4位
     逻辑运算指令——EOREOR指令将寄存器Rd的值与寄存器Rn的值按位作逻辑“异或”操作,结果保存到Rd寄存器中。
     应用示例:
     MOV R1,#0x0F
     EOR R0,R1 ; R0=R0 ^ R1,取反R0低4位
     逻辑运算指令——BIC
     BIC指令将寄存器Rd的值与寄存器Rm的值的反码作逻辑“与”操作,结果保存到Rd寄存器中。
     应用示例:
     MOV R1,#0x02
     BIC R0,R1 ; 清零R0的第2位,其它位不变
     移位指令——ASR
     ASR指令将数据算术右移,将符号位拷贝到左侧空出的位,移位结果保存到Rd寄存器中。
     若移位位数为32,则Rd清零,最后移出的位保留在标志C中;若移位位数大于32,则Rd和标志C均被清零;若移位位数为0,则不影响C标志。
     应用示例:
     ASR R1,R2
     ASR R3,R1,#2
     移位指令——LSL
     LSL指令将数据逻辑左移,空位清零,移位结果保存到Rd寄存器中。
     若移位位数为32,则Rd清零,最后移出的位保留在标志C中;若移位位数大于32,则Rd和标志C均被清零;若移位位数为0,则不影响C标志。
     应用示例:
     LSL R6,R7
     LSL R1,R6,#2
     移位指令——LSR
     LSR指令将数据逻辑右移,空位清零,移位结果保存到Rd寄存器中。
     若移位位数为32,则Rd清零,最后移出的位保留在标志C中;若移位位数大于32,则Rd和标志C均被清零;若移位位数为0,则不影响C标志。
     应用示例:
     LSR R3,R0
     LSR R5,R2,#2
     移位指令——ROR
     ROR指令将数据循环右移,寄存器右侧移出的位放入左侧空出的位上,移位结果保存到Rd寄存器中。
     应用示例:
     ROR R3,R0
     比较指令——CMP
     CMP指令使用寄存器Rn的值减去第二个操作数的值,根据操作的结果更新CPSR中的N、Z、C和V标志位。
     应用示例:
     CMP R1,#10 ;R1与10比较,设置相关标志位
     CMP R1,R2 ;R1与R2比较,设置相关标志位
     比较指令——CMN
     CMN指令使用寄存器Rn的值加上寄存器Rm的值,根据操作的结果更新CPSR中的N、Z、C和V标志位。
     应用示例:
     CMN R0,R2 ;R0与-R2比较,设置相关标志位
     比较指令——TST
     TST指令将寄存器Rn的值与寄存器Rm的值按位作逻辑“与”操作,根据操作的结果更新CPSR中的N、Z、C和V标志位。
     应用示例:
     MOV R0,#0x01
     TST R1,R0 ;判断R1的最低位是否为0
     Thumb分支指令
     分支指令——B
     B指令跳转到指定的地址执行程序,它是Thumb指令集中的唯一有条件执行指令。如果使用了条件执行,那么跳转范围在-252~+256字节内。如果没有使用条件执行,那么跳转范围在±2K内。
     应用示例:
     B WAITB ;WAITB标号在当前指令的±2K范围内
     BEQ LOOP1 ;LOOP1标号在当前指令的-252~+256范围内
     分支指令——BL
     BL指令在跳转到指定地址执行程序前,将下一条指令的地址拷贝到R14链接寄存器中。
     注意:由于BL指令通常需要大的地址范围,很难用16位指令格式实现,为此,Thumb采用两条这样的指令组合成22位半字偏移(符号扩展为32位),使指令转移范围为±4MB。
     分支指令——BX
     BX指令是带状态切换的分支指令,跳转地址由Rm指定,同时根据Rm的最低位的值切换处理器状态,当最低两位均为0时,切换到ARM状态。
     应用示例:
     ADR R0,ArmFun ;将ARM程序段地址存入R0
     BX R0 ;跳至R0指定的地址,并切换到ARM状态
     Thumb杂项指令——SWI
     SWI指令用于产生软中断,从而实现从用户模式变换到管理模式,CPSR保存到管理模式的SPSR中,同时程序跳转到SWI向量。在系统模式下也可以使用SWI指令,处理器同样能切换到管理模式。(参数传递的方法参看ARM指令SWI的使用)
     应用示例:
     SWI 1 ;软中断,中断立即数为0
     SWI 0x55 ;软中断,中断立即数为0x55
     Thumb伪指令——ADR
     ADR伪指令将基于PC相对偏移的地址值读取到寄存器中。
     Thumb伪指令——LDR
     LDR伪指令用于加载32位的立即数或一个地址值到指定寄存器。在汇编编译源程序时,LDR伪指令被编译器替换成一条合适的指令。详细说明参看ARM伪指令部分。
     Thumb伪指令——NOP
     NOP伪指令在汇编时将被替换成一条Thumb空操作的指令。比如可能为“MOV R0,R0”指令。NOP伪指令可用于延时操作。
0 0