ARM:从裸机到操作系统05 —— ARM指令集
简要介绍ARM指令集,移位操作以及主要的寻址方式
ARM编程基础
本章将简要介绍ARM指令集,移位操作以及主要的寻址方式
更详细的 ARM 指令相关内容,可参考
《ARM Compiler Toolchain Assembler Reference》
《ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition》
一、ARM 指令集
ARMv7 架构是一种 32 位处理器架构。它是一种加载/存储架构,这意味着数据处理指令只能对通用寄存器中的值进行操作。只有加载和存储指令才能访问内存。通用寄存器也是 32 位。
ARMv7架构包含两大主要指令集:ARM指令集和Thumb指令集。
- ARM 指令集:ARM 处理器的原生32位指令集。指令长度固定为32位,以字(4字节)对齐方式存储。所有32位指令均可访问全部ARM核心寄存器(R0-R15)。
- Thumb 指令集:Thumb指令集最初在ARM7TDMI处理器中添加,仅包含16位指令。包括Cortex-A系列在内的现代处理器支持Thumb-2技术,该技术扩展了Thumb指令集,提供了16位和32位指令的混合使用。
由于Thumb指令大小仅为ARM指令的一半,其程序体积通常比等效的ARM代码小约三分之一。因此,Thumb指令被用于提高代码密度并减少系统内存需求。当处理器直接连接至窄位宽(16位)内存且无缓存时,Thumb代码的性能也可能优于ARM代码——每个时钟周期可获取一条Thumb指令,而每条32位ARM指令需要两个时钟周期进行获取。
执行Thumb指令时,程序计数器(PC)的值为当前指令地址加4。唯一能直接修改PC的16位Thumb指令是某些编码形式的MOV和ADD。写入PC的值会被强制半字对齐,即忽略最低有效位(LSB),将其视为0。
在ARMCC编译器中,选项–thumb或默认的-arm允许选择编译时使用的指令集。程序在运行时可以在这两种指令集之间切换。
二、ARM指令集中的移位操作
移位与循环移位操作类型:
1. 逻辑左移 (LSL - Logical Shift Left)
- 定义:将位串的每一位向左移动指定位数,右侧空位补零,左侧移出的位丢弃。最后一次移出的位可能作为进位输出。
- 特点:
- 适用于无符号数乘以2n的运算。
- 影响标志位:CF(进位标志)、OF(溢出标志)、ZF(零标志)等。
2. 逻辑右移 (LSR - Logical Shift Right)
- 定义:将位串的每一位向右移动指定位数,左侧空位补零,右侧移出的位丢弃。最后一次移出的位可能作为进位输出。
- 特点:
- 适用于无符号数除以2n的运算。
- 与算术右移的主要区别是填充方式(补0 vs 补符号位)。
3. 算术右移 (ASR - Arithmetic Shift Right)
- 定义:将位串的每一位向右移动指定位数,左侧空位用原符号位(最高位)填充,右侧移出的位丢弃。最后一次移出的位可能作为进位输出。
- 特点:
- 保留符号位,适用于带符号数除以2n的运算。
- 示例:-6(补码:11111010)算术右移1位后为-3(补码:11111101)。
4. 循环右移 (ROR - Rotate Right)
- 定义:将位串的每一位向右移动指定位数,右侧移出的位从左侧重新填入,形成循环。最后一次移出的位可能作为进位输出。
- 特点:
- 循环操作不丢失数据,常用于加密或位重组。
- 示例:10111110循环右移4位后变为11101011。
5. 带扩展的循环右移 (RRX - Rotate Right with Extend)
- 定义:将位串向右移动1位,左侧空位用进位标志(C)的值填充,右侧移出的位作为新的进位输出。
- 特点:
- 结合进位标志实现扩展循环,适用于多精度运算。
- 示例:ARM指令中的RRX操作会将C标志位插入高位,低位移出到C标志
总结
操作类型 | 填充方式 | 应用场景 | 典型指令(汇编) |
---|---|---|---|
逻辑左移 (LSL) | 右侧补0 | 无符号数乘法 | SHL, SAL |
逻辑右移 (LSR) | 左侧补0 | 无符号数除法 | SHR |
算术右移 (ASR) | 左侧补符号位 | 带符号数除法 | SAR |
循环右移 (ROR) | 右侧移出位补到左侧 | 数据加密/循环处理 | ROR |
带扩展循环右移 (RRX) | 左侧补进位标志C | 多精度运算 | RRX |
三、ARM 指令分类及寻址方式
2.1 ARM 指令分类
ARM指令可以分为6类:
- 跳转指令
- 数据处理指令
- 程序状态寄存器处理指令
- Load/Store指令
- 协处理器指令
- 异常中断产生指令
2.2 ARM指令的寻址方式
-
立即寻址(立即数寻址)
指令操作码字段的地址部分是立即数。立即数前面加上“#”。
指令中的立即数是由一个8bit的常数X循环右移4bit值Y的两倍的数得到:
immediate = X ROR (2×Y)
例如:
MOV R0, #100 或 MOV R0, #0x64 ; 令R0的数值为100 -
寄存器寻址(寄存器直接寻址)
寄存器寻址是指将寄存器中的数值作为操作数。
例如:
MOV R0, R1 ; 将R1的数值放到R0中 -
寄存器间接寻址
操作数存放在存储器中,并将所存放的存储单元地址放入某一通用寄存器中。
例如:
LDR R0, [R1] ; 将地址为R1的值的内存单元数据读取到R0中 -
寄存器移位寻址
寄存器的值先进行移位操作再进行指令操作。
例如:
MOV R0,R1,LSL #3 ; 将R1中的值先左移 3 位,得到的结果赋值给R0 -
基址变址寻址
将寄存器(该寄存器一般称作基址寄存器)中的值与指令中给出的地址偏移量相加,结果得到的新地址,通过这个地址取得操作数。
支持寄存器偏移:LDR R0, [R1, R2, LSL #2] ;R1+(R2 LSL 2)得到新的内存地址,将新地址的值赋给R0
前变址:LDR R0, [R1, #4]!(先计算地址后访问,同时更新基址)
后变址:LDR R0, [R1], #4(先访问后更新基址) -
多寄存器寻址
使用多寄存器传送指令LDM/STM,在一条指令中传送多个寄存器的值,可以传送1~16个通用寄存器的值。
寄存器之间的分隔符使用 “-” 或者 “,”。连续的寄存器之间用 “-” 连接;不连续的寄存器中间用 “,” 分隔。4种数据块传输模式:
- IB(Increment Before):地址增加后再完成数据传送,如STMIB、LDMIB;
- IA(Increment After):完成数据传送后再地址增加,如STMIA 、LDMIA;
- DB(Decrement Before):地址先减小再完成数据传送,如STMDB、LDMDB;
- DA(Decrement After):完成数据传送后再地址减小,如STMDA、LDMDA。
指令语法:
LDM{cond}<mode> Rn{!}, {reglist}{^} //从Rn加载到寄存器列表
STM{cond}<mode> Rn{!}, {reglist}{^} //从寄存器列表存储到Rn- Rn:基址寄存器(不能为R15),存储操作起始地址。
- !:自动更新基址寄存器值(如LDMIA R0!, {R1-R3}操作后R0指向下一个数据块)。
- reglist:寄存器列表(如{R0,R2-R5,LR}),低编号寄存器对应低内存地址。
- ^:特权模式操作后缀,用于恢复用户模式寄存器或操作程序状态寄存器(如中断返回)。
-
堆栈寻址
堆栈是一种数据结构,按先进后出(First In Last Out,FILO)的方式工作,使用一个称作堆栈指针的专用寄存器指示当前的操作位置,堆栈指针总是指向栈顶。
根据SP指针指向的位置,栈可以分为:
- 满栈(Full Stack):当堆栈指针SP总是指向最后压入堆栈的数据,称为满栈;
- 空栈(Empty Stack):当堆栈指针SP总是指向下一个将要放入数据的空位置,称为空栈;
根据SP指针移动的方向,栈可以分为: - 递增堆栈(ascending stack):堆栈由低地址向高地址生长。
- 递减堆栈(secending stack):堆栈由高地址向低地址生长。
4种类型的堆栈工作方式: - 满递增(Full Ascending,FA)堆栈
- 满递减(Full Descending,FD)堆栈
- 空递增(Empty Ascending,EA)堆栈
- 空递减(Empty Descending,ED)堆栈。
例如:
STMFD SP!, {R0-R3} ; 将R0-R3的值入栈,满递减堆栈。
LDMED SP!, {R0-R3} ;将堆栈中的数据取到R0-R3。空递减堆栈。
- PC相对寻址
以PC寄存器为基址寄存器,以指令中的地址标号为偏移量,两者相加形成操作数的有效地址。
偏移量指出的是当前指令和地址标号之间的相对位置。
跳转链接指令(子程序调用指令)的寻址方式即是相对寻址方式。
更多推荐
所有评论(0)