2.08k likes | 2.27k Views
嵌入式系统讲义 第 3 章 ARM 指令系统. 周国运 2007.3. 第 3 章 ARM 指令系统. 主要内容 3.1 ARM 指令结构 3.2 ARM 寻址方式 3.3 ARM 指令集 3.4 Thumb 指令集 3.5 ARM 伪指令 3.6 ARM 汇编语言程序设计. 3.1 ARM 指令系统概述. 主要内容 一、指令系统概念 二、 ARM 指令的特点 三、 ARM 指令的格式 四、指令的条件码. 3.1 ARM 指令系统概述. 一、指令系统概念 指令: 是规定计算机进行某种操作的命令。
E N D
嵌入式系统讲义第3章 ARM指令系统 周国运 2007.3
第3章 ARM指令系统 主要内容 3.1 ARM指令结构 3.2 ARM寻址方式 3.3 ARM指令集 3.4 Thumb指令集 3.5 ARM伪指令 3.6 ARM汇编语言程序设计
3.1 ARM指令系统概述 主要内容 一、指令系统概念 二、ARM指令的特点 三、ARM指令的格式 四、指令的条件码
3.1 ARM指令系统概述 一、指令系统概念 指令:是规定计算机进行某种操作的命令。 指令系统:计算机能够执行的各种指令的集合。 二、ARM指令的特点 • 所有指令都是32位的。 • 大多数指令都在单周期内完成。 • 所有指令都可以条件执行。 • ARM指令为load/store类型。 • 基本指令仅36条,分成五类。 • 有7种寻址方式。 • 指令集可以通过协处理器扩展。
ARM指令是加载/存储(Load/Store)型: 也即指令集仅能处理寄存器中的数据,而且处理结果都要放回寄存器中,而对系统存储器的访问则需要通过专门的加载/存储指令来完成。 • ARM指令可以分为五大类: 数据处理指令、存储器访问指令、分支指令、协处理器指令、杂项指令。 • ARM指令有7种寻址方式: 立即寻址、寄存器寻址、寄存器间接寻址、基址寻址、堆栈寻址 、块拷贝寻址、相对寻址。
31 28 27 25 24 21 20 19 16 15 12 11 8 7 0 Cond Opcode S Rn Rd Operand2 三、ARM指令的格式 ARM指令基本的语法格式为: <Opcode> {<cond>}{s} <Rd>, <Rn> {, <Operand2>} • Opcode:指令操作码。 • cond:指令的条件码。 • S:决定指令的操作是否影响cpsr的值。 • Rd:目标寄存器编码。 • Rn:包含第一个操作数的寄存器编码。 • Operand2:第2操作数。 ARM指令典型的编码格式为: (数据处理指令类) 例: ADDS R2,R1,#1 SUBNES R2,R1,#0x20 LDR R0,[R1]
四、指令的条件码 • 条件码的位数和位置:每条ARM指令包含4位条件码域<cond>,它占用指令编码的最高四位[31:28]。 • 条件码的表示:条件编码共 24=16 种,其中,15种用于指令的条件码。每种条件码用2个英文缩写字符表示。(见P47 表2-6) • 带条件指令的执行:ARM处理器根据指令的执行条件是否满足,决定当前指令是否执行。 只有在cpsr中的条件标志位满足指定的条件时,指令才会被执行。不符合条件的代码依然占用一个时钟周期(相当于一个NOP指令)。 • 条件码的书写方法:条件码的位置在指令助记符的后面(因此也称为条件后缀)。 例如:MOVEQ R0, R1
3.2 ARM寻址方式 主要内容 一、立即寻址 二、寄存器寻址 三、寄存器间接寻址 四、基址寻址 五、堆栈寻址 六、块拷贝寻址 七、相对寻址
3.2 ARM寻址方式 寻址方式:处理器根据指令中给出的(地址)信息,寻找操作数(物理地址)的方式。 我们将ARM指令系统的寻址方式分为7种(有分为9种的等)。
3.2.1 立即寻址 立即寻址也叫立即数寻址。 立即寻址概念:操作数本身就在指令中给出,只要取出指令也就取到了操作数。这个操作数被称为立即数,对应的寻址方式也就叫做立即寻址。 ADD R0,R0,#1;R0←R0+1 MOV R0 ,#0x3f ; R0←#0x3f 书写立即数时,要求以“#”为前缀。 • 十六进制数,#后加 0x或&,如 #0x3f,#&3f. • 二进制数, #后加 0b, 如 #0b1011 • 十进制数, #后加 0d或缺省,如#0d678,#789 如何构造32位立即数?
12 11 8 7 0 …… rot Immed_8 Shifter ROR immediate 在指令格式中,第二个操作数有12位: 因此有效立即数immediate可以表示成: <immediate>=immed_8 循环右移(2×rot) 4 bit 移位值 (0-15)乘于2,得到一个范围在0-30,步长为 2的移位值。 因此,将ARM中的立即数称为8位位图。 记住一条准则: “最后8位移动偶数位”得到立即数。
31 7 0 ror #0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ror #8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ror #30 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 例: • 下列命令中,汇编器把立即数转换为移位操作: • MOV R0,#4096 ; uses 0x40 ror 26 • ADD R1,R2,#0xFF0000 ; uses 0xFF ror 16 带有立即数的MOV 指令的二进制编码为: MOV R0,#0xF200 ;E3A00CF2. MOV R1,#0x110000 ;E3A01811. MOV R4,#0x12800 ;E3A04B4A. 0xF200 =0xF2循环右移(2*C) 0x110000 =0x11循环右移(2*8) 0x12800 =0x4A循环右移(2*B)
immed_8=0x3F,rot=0xE,对3F左移4位 immed_8=0xFC,rot=0xF,对FC左移2位 只有能够通过此构造方法得到的才是合法的立即数。 • 合法立即数: • 0xFF;0x104(其8位图为0x41);0xFF0;0xFF00 • 非法立即数: • 0x101;0x102;0xFF1 深入理解:一个合法的立即数可能有多种编码方法,将使某些指令的执行产生不同的结果。 如 0x3F0 ARM汇编编译器生成立即数的规则为: • 当立即数数值在0到0xFF范围时,令immed_8=<immediate>,rot=0。 • 其它情况下,汇编编译器选择使rot数值最小的编码方式。
寄存器寻址: ——利用寄存器中的数值作为操作数。 这种寻址方式是各类微处理器经常采用的一种方式,也是一种执行效率较高的寻址方式。 两种具体形式:寄存器寻址、寄存器移位寻址。 一、寄存器寻址 如指令: ADD R0,R1,R2 ;R0←R1+R2 3.2.2 寄存器寻址
二、寄存器移位寻址 1、寄存器移位寻址 当第二操作数为寄存器型时,在执行寄存器寻址操作时,也可以对第二操作数寄存器进行移位,此时第二操作数形式为: MOV Rd, Rn, Rm,{<shift>} 其中: Rm 称为第二操作数寄存器 <shift>用来指定移位类型和移位位数,有两种形式: • 5位立即数 (其值小于32) • 寄存器(用Rs表示) (其值小于32) 在指令执行时将寄存器移位后的内容作为第二操作数参与运算。例如指令: ADD R3,R2,R1,LSR #2;R3←R2+(R1右移2位) ADD R3,R2,R1,LSR R0;R3←R2+(R1右移R0位)
31 0 0 2、第二操作数移位方式 共有6种移位方式: • — LSL 逻辑左移 — LSR 逻辑右移 • — ASL 算术左移 — ASR 算术右移 • — ROR 循环右移 — RRX 带扩展的循环右移 (1)LSL:逻辑左移,空出的最低有效位用0填充。 (2)LSR:逻辑右移,空出的最高有效位用0填充。 31 0 0 SUB R3,R2,R1,LSL #2;R3←R2-(R1左移2位) SUB R3,R2,R1,LSR R0;R3←R2-(R1右移R0位)
(3)ASL:算术左移,由于左移空出的有效位用0填充,因此它与LSL同义。(3)ASL:算术左移,由于左移空出的有效位用0填充,因此它与LSL同义。 (4)ASR:算术右移(Arithmetic Shift Right) 。算术移位的对象是带符号数,移位过程中必须保持操作数的符号不变。如果源操作数是正数,空出的最高有效位用0 填充,如果是负数用1填充。 30 0 ADD R3,R2,R1,ASL #2 ;R3←R2+(R1左移2位) SUB R3,R2,R1,ASR R3 ;R3←R2-(R1算术右移R3位)
(5)ROR:循环右移(Rotate Right),移出的字的最低有效位依次填入空出的最高有效位。 31 0 (6)RRX:带进位位的循环右移(Rotate Right Extended) 。将寄存器的内容循环右移1位,空位用原来C标志位填充。 31 0 SUB R3,R2,R1,ROR #2 ;R3←R2+(R1循环右移2位) SUB R3,R2,R1,RRX R0 ;R3←R2-(R1带进位位循环右移R0位)
3、第二操作数的移位位数 移位位数可以用立即数方式或者寄存器方式给出,其值均小于32,应为0---31。 如下所示: ADD R3,R2,R1,LSR #2;R3R2+(R1右移2位) ADD R3,R2,R1,LSR R4;R3R2+(R1右移R4位) 寄存器R1的内容分别逻辑右移2位、R4位,再与寄存器R2的内容相加,结果放入R3中。
寄存器间接寻址 ——就是以寄存器中的值作为操作数的地址,而操作数本身存放在存储单元中。例如以下指令: LDR R0,[R1] ;R0←[R1] STR R0,[R1] ;[R1]←R0 第一条指令将以R1的值为地址的存储单元中的内容加载到寄存器R0中。 第二条指令将R0的内容存储到以R1的值为地址的存储单元中。 R1—— 基址寄存器 R1的内容——基地址 3.2.3 寄存器间接寻址
20 19 16 15 12 11 31 28 27 0 Rn Rd Cond Opcode Offset 一、变址寻址 也叫基址加偏(变)址寻址 ——将基址寄存器的内容与指令中给出的地址偏移量相加,得到操作数所在的存储器的有效地址。 变址寻址方式常用于访问某基地址附近的地址单元。(4K) 3.2.4 变址寻址 例如: LDR R0,[R1,#4] ;R0←mem32[R1+4]
Pre-indexed: STR r0,[r1,#12] R0 偏移量 源 寄存器 0x05 12 0x05 0x20C R1 基址寄存器 0x200 0x200 二、偏移地址方式 有三种加偏址的方式 1、前变址模式(不修改基址寄存器): ——先基址+偏址,生成操作数地址,做指令指定的操作。 也叫前索引偏移。
2、自动变址模式(修改基址寄存器): ——①先基址+偏移,生成操作数地址,做指令指定的操作。②然后自动修改基址寄存器。 • 例如: LDR R0,[R1,#4]! ;R0←mem32[R1+4] ;R1←R1+4 !——表示更新基址寄存器。
例Post-indexed: STR r0,[r1],#12 r1 偏移量 更新基址寄存器 0x20c 12 0x20c r0 原基址寄存器 源 寄存器 r1 0x5 0x5 0x200 0x200 3、后变址模式(修改基址寄存器): ——①基址寄存器不加偏移作为操作数地址。 ②完成指令操作后,用(基址+偏移)的值修改基址寄存器。 即先用基地址传数,然后修改基地址(基址+偏移),也叫后索引偏移。
三、偏移地址形式 ——可以是一个立即数,也可以是另一个寄存器,并且还可以是寄存器移位操作。如下所示: LDR r0,[r1,r2] ;r0<—mem32[r1+r2] LDR r0,[r1,r2,LSL #2];r0<—mem32[r1+r2*4] 常用的是立即数偏移的形式。
3.2.5 堆栈寻址 堆栈寻址 ——堆栈寻址是隐含的,它使用一个专门的寄存器(堆栈指针SP)指向一块存储区域(堆栈)。 堆栈可分为两种增长方式: 向上生长:向高地址方向生长,称为递增堆栈。 向下生长:向低地址方向生长,称为递减堆栈。
根据堆栈指针指向的数据位置的不同,可分为:根据堆栈指针指向的数据位置的不同,可分为: • 满堆栈 ——堆栈指针指向最后压入堆栈的有效数据项,称为满堆栈; • 空堆栈 ——堆栈指针指向下一个待压入数据的空位置,称为空堆栈。 这样就有4种类型的堆栈表示递增和递减的满和空堆栈的各种组合。
四种类型的堆栈工作方式 • 满递增堆栈FA( Full Ascending): ——堆栈指针指向最后压入的数据,且由低地址向高地址生长。 • 满递减堆栈FD: ——堆栈指针指向最后压入的数据,且由高地址向低地址生长。 • 空递增堆栈EA: ——堆栈指针指向下一个将要放入数据的空位置,且由低地址向高地址生长。 • 空递减堆栈ED( Empty Descending): ——堆栈指针指向下一个将要放入数据的空位置,且由高地址向低地址生长。
STMFD sp!,{r4-r7,lr} 满递减 LDMFA sp!,{r4-r7,pc} 满递增 高地址 Old SP 9753 9753 8034 8420 8420 A0BE 1234 1234 pc pc 8034 9020 lr 8034 1234 1010 1010 Old SP FF 8034 FFFF 8034 8034 SP 100 r7 A0BE 16 A0BE AOBE r7 12 r6 1234 1234 102E 1234 r6 0 r5 FF FF 8765 FF r5 14544 r4 100 ABCD 100 100 r4 1 SP …… 8034 100 r4 100 A0BE r7 A0BE FF r5 FF 1234 r6 1234 第5次到此
3.2.6 块拷贝寻址 一、块拷贝寻址 ——把存储器中的一个数据块加载到多个寄存器中,或者是把多个寄存器中的内容保存到存储器中。 应用指令:块拷贝寻址是多寄存器传送指令LDM/STM的寻址方式,因此也叫多寄存器寻址。 块拷贝寻址操作中的寄存器,可以是R0-R15这16个寄存器的子集(一部分),或是所有寄存器。
IA IB DA DB STMxx r10, {r0,r1,r4} r4 r4 r1 地址 增加 r1 r0 基址寄存器 (Rn) r10 r0 r4 r1 r4 r0 r1 r0 二、4种寻址操作 LDMIA / STMIA Increment After(先传送,后地址加4) LDMIB / STMIB Increment Before(先地址加4 ,后传送) LDMDA / STMDA Decrement After(先传送,后地址减4) LDMDB / STMDB Decrement Before (先地址减4,后传送) 地址递增,指针最后位置 地址递减,指针最后位置
多寄存器load和stroe指令的堆栈和块拷贝对照 STMFA(Full、Add):栈满递增。F---栈顶满空;A---栈增减 LDMIB*(Inc、Befo):块先增地址、再传数。
3.2.7 相对寻址 相对寻址 ——与基址变址寻址方式相类似,相对寻址以程序计数器PC的当前值为基地址,指令中的地址标号作为偏移量,将两者相加之后得到操作数的有效地址。 相对寻址指令举例如下: BL SUBRl ;调用到SUBRl子程序 . . . ; SUBRl应为24位有符号数 SUBR1 … … MOV PC, LR ;返回
3.3 ARM指令集 主要内容 1、存储器访问指令 2、数据处理指令 3、分支指令 4、协处理器指令 5、杂项指令 6、ARM伪指令
3.3.1 存储器访问指令 ARM微处理器用加载/存储指令访问存储器,实现在寄存器和存储器之间传送数据。加载指令用于将存储器中的数据传送到寄存器,存储指令则完成相反的操作。 由于ARM处理器对外设寄存器、I/O映射空间与存储器统一编址,因此,对外围设备的I/O操作也用此类指令。 基本的加载/存储指令仅有5条,分为3种: — LDR和STR,单寄存器加载/存储指令 — LDM和STM,多寄存器加载/存储指令 — SWP,寄存器和存储器数据交换指令
一、单寄存器的存取指令 单寄存器加载/存储指令是ARM在寄存器和存储器间传送单个字节和字的最灵活方式。 根据传送数据的类型不同,单个寄存器存取指令又可以分为以下两类: • 单字和无符号字节的加载/存储指令 • 半字和有符号字节的加载/存储指令
1、单字和无符号字节的加载/存储指令 • LDR: 指令从内存中取32位字或8位无符号字节数据放入寄存器; • STR: 指令将寄存器中的32位字或8位无符号字节数据保存到存储器中。 • 注意: • 无符号字节加载时,用0将8位的操作数扩展到32位。
(1)指令格式 LDR{cond}{T} Rd,<地址> ;加载指定地址上的字数据,放入Rd中。 STR{cond}{T} Rd,<地址> ;存储Rd中字数据,到指定地址的存储单元。 LDR{cond}B{T} Rd,<地址> ;加载字节数据到Rd中, Rd最低字节有效, 高24位为0。 STR{cond}B{T} Rd,<地址> ;存储Rd中字节数据, Rd中最低字节为传送数据。
T后缀 • T为可选后缀,若指令有T,那么即使处理器是在特权模式下,存储系统也将访问看成是处理器是在用户模式下。 • 用于存储器保护。 • 不能与前变址模式、自动变址模式一起使用(即不能改变基址寄存器值)。 • T在用户模式下无效。
(2)操作数寻址方式 LDR/STR指令为变址寻址,由两部分组成: • 基地址部分:为一个基址寄存器,可以为任一个通用寄存器; • 偏移地址部分:这一部分非常灵活,实际就是第二个操作数,可以有以下3种格式: • 立即数 • 寄存器 • 寄存器及移位常数。
① 立即数 ——12位立即数可以是一个无符号的数值。这个数据可以加到基址寄存器,也可以从基址寄存器中减去这个数值。 指令举例如下: • LDR R1,[R0,#0x12] ;将R0+0x12地址处的数据读出,保存到R1中(R0的值不变) • LDR R1,[R0,# -0x12] ;将R0-0x12地址处的数据读出,保存到R1中(R0的值不变)
②寄存器 ——寄存器中的数值可以加到基址寄存器,也可以从基址寄存器中减去这个数值。 指令举例如下: • LDR R1,[R0,R2] ;将R0+R2地址处的数据读出,保存到R1中 • LDR R1,[R0,-R2] ;将R0-R2地址处的数据读出,保存到R1中
③ 寄存器及移位常数 ——寄存器移位后的值可以加到基址寄存器,也可以从基址寄存器中减去这个数值。 指令举例如下: • LDR R1,[R0,R2,LSL #2] ;将R0+R2×4地址处的数据读出,保存到R1中(R0、R2的值不变) • LDR R1,[R0,-R2,LSL #2] ;将R0-R2×4地址处的数据读出,保存到R1中(R0、R2的值不变) 注意:移位位数只能是5位的立即数,不能使用寄存器指定移位位数。
④PC(即R15)使用的几个问题 • 使用PC作为基址时,使用的数值是指令的地址加8个字节(取指与执行相差8个字节)。 • PC不能用做偏移寄存器,也不能用于任何变址寻址模式。 • 把一个字加载到PC,将使程序转移到所加载的地址,这是一个公认的实现跳转的方法。但是应当避免将一个字节加载到PC。 • 把PC存到存储器的操作在不同体系结构的处理器中产生不同的结果,应尽可能避免。
2、半字和有符号字节的加载/存储指令 这类LDR/STR指令可实现半字(有符号和无符号)、有符号字节数据的传送。 特点: • 偏移量格式、寻址方式与加载/存储字和无符号字节指令基本相同。 • 立即数偏移量限定在8位,寄存器偏移量不可经过移位得到。
(1)指令格式如下 LDR {cond}H Rd,<地址> ;加载无符号半字数据到Rd的低16位,高16位清零。 LDR {cond}SB Rd,<地址> ;加载指定地址上有符号字节到Rd中,高24位用符号位扩展 LDR {cond}SH Rd,<地址> ;加载指定地址上的有符号半字到Rd中,高16位用符号位扩展。 STR{cond}H Rd,<地址> ;存储Rd中的低16位半字数据。 存储有符号数据和无符号数据之间没有差别。
(2)两点说明: • 符 号 位——有符号字节或有符号半字的加载,用“符号位”扩展到32位;无符号半字传送是用0扩展到32位。 • 地址对齐——对半字传送的地址必须为偶数。非半字对齐的半字加载将使Rd内容不可靠;非半字对齐的半字存储将使指定地址的2字节存储内容不可靠。
(3)指令举例 LDRSB R1,[R0,R3] ;将R0+R3地址上的字节数据读到R1,高24位用符号位扩展。 LDRSH R1,[R9] ;将R9地址上的半字数据读出到R1,高16位用符号位扩展。 LDRH R6,[R2],#2 ;将R2地址上的半字数据读出到R6,高16位用零扩展,然后修改R2=R2+2。 STRH R1,[R0,#2]! ;将R1的数据保存到R0+2地址中,只存储低2字节数据,并且修改R0=R0+2。
二、多寄存器的存取指令 LDM和STM指令可以实现在一组寄存器和一块连续的内存单元之间存/取数据。 LDM为加载多个寄存器;STM为存储多个寄存器。 这两条指令,允许传送16个寄存器R0---R15的任何子集或所有寄存器。 1、指令格式 • LDM{cond}<模式> Rn{!},<reglist>{^} • STM{cond} <模式> Rn{!},<reglist>{^}