ARM汇编
ISA
ISA即指指令集架构(Instruction Set Architecture)是与程序设计有关的计算机架构的一部分,包括本地数据类型、指令、寄存器、地址模式、内存架构、中断和意外处理和外部 I/O
一个 ISA 包括一系列 opcodes(机器语言)的一个规格,本地命令由一个特定的 CPU 设计来实现。
寄存器
在ARM微系统中,所有的运算指令都借助寄存器来完成。某些需要对存储器或者I/O值进行操作的情况下,会把存储器或者I/O值先读到寄存器中,然后再对寄存器中的值进行操作。
除了完成普通的数据处理功能外,ARM微处理器中的某些寄存器还有一些特殊的功能,例如:
PC(程序计数器)
LR(链接寄存器)
SP(堆栈寄存器)
CPSR(ARM微处理器状态寄存器)
以下是ARM微处理器的寄存器组织和各种寄存器功能的详细介绍:
通用寄存器
ARM微处理器在不同的工作模式下时,有不同的寄存器组,某些寄存器在不同工作模式下是公用的,而另外一些寄存器并不是公用的。
寄存器的公用与不公用:
在不同工作模式下公用:(例:寄存器R0)如果在一个工作模式下将R0的值设为某个值X,那么当切换到其他模式下,寄存器R0的值也为X
在不同工作模式下不公用:(例:寄存器R13)在用户模式下的R13和管理模式下的R13实际上是两个寄存器。这种不同工作模式下不公用的寄存器保证了不同工作模式下某些操作不会互相干扰。
以下是ARM微处理器的寄存器组织
寄存器R13:堆栈指针,表示为SP,用来指示堆栈顶部数据的地址。在异常入口处,程序需要将一些寄存器的值压到堆栈中,异常返回时再将堆栈中的值重新加载到寄存器中。这种方式保证了异常处理程序的可靠性。
寄存器R14:链接寄存器,表示为LR。当执行带链接的跳转程序时,R14用来保存跳转前程序计数器的PC值,方便跳转之后的程序返回。因此R14可以用来进行函数调用。
异常链接寄存器:(如:R14_svc、R14_abt、R14_und、R14_irq、R14_fiq)能够保证在异常和函数调用同时发生的情况下,程序的正常执行。
寄存器R15:程序计数器PC
状态寄存器
ARM微处理器状态寄存器CPSR主要包含条件标识码,中断屏蔽位和其他一些状态和控制信息。
ARM微处理器每个异常模式都会有一个独立的SPSR寄存器,用来在异常处理时保存CPSR寄存器的值,当异常返回时,ARM微处理器会用SPSR的值恢复CPSR寄存器
·条件标志域CPSR[31:28]
CPSR[31:28]是条件标志区域,这个区域一共有4个比特,分别用N、Z、C、V表示
N:负数标志。当该位为1时表示操作结果为负数;当该位为0时表示操作结果为正数或者0
Z:零标志。当该位为1时表示操作结果为0;当该位为0时表示操作结果非0
C:进位标志。在加法运算时,若该位为1,表示加法结果产生了进位;在减法运算时,若该位为0表示减法结果产生了借位
V:溢出标志。在加法或者减法运算时,如果为1表示运算结果产生溢出
·Q标志位CPSR[27]
Q标志位用于表示DSP指令是否出现一处或饱和
·中断禁止域CPSR[8:6]
CPSR[8:6]是中断禁止位区域,分别用A、I、R表示
·A:置1时表示禁止非精确的数据中止
·I:置1时禁止IRQ中断
·R:置1时禁止FIQ中断
·T位CPSR[5]
T=0时表示ARM微处理器为ARM状态,T-1时表示ARM微处理器为Thumb状态
·ARM微处理器模式位CPSR[4:0]
CPSR[4:0]是ARM微处理器的工作模式位,表示ARM微处理器所处的当前工作模式,具体对应关系如图所示:
ARM的寻址方式
ARM指令集中确定操作数的方式叫做寻址方式。
ARM的寻址方式共五种,分别是:立即寻址方式、寄存器寻址方式、寄存器移位寻址方式、寄存器间接寻址方式、基址变址寻址,下面对各寻址方式做简要介绍:
立即寻址方式
立即寻址方式就是操作数本身直接在指令中给出,取出指令也就获得了操作数
由于指令代码位数的限制,并不是任何32位立即数都可以作为寻址方式的合法立即数,具体的立即数位数要参考相关指令的指令编码
在ARM指令集中,必须用#前缀来表示一个立即数
十六进制立即数:在立即数前面加上“0x”
十进制立即数:在立即数前面加上“0d”
二进制立即数:在立即数前加“0b”
立即寻址方式举例:
ADD R0,R1,#0x15 ;R0<-(R1)+0x15
MOV R0,#0x75 ;R0<-0x75
寄存器寻址方式
寄存器寻址方式就是将寄存器中的数值作为操作数
寄存器寻址方式举例:
ADD R0,R1,R2 ;R0<-(R1)+(R2)
这是一个加法指令的例子,如果寄存器R1,R2中的值分别为0x1和0x2,那么运行后R0中的值为0x3
寄存器移位寻址方式
寄存器移位寻址方式就是将寄存器中的数值作移位变换,移位后的数值作为指令的操作数
移位的位数可以由立即数给出,也可以由寄存器中的数值给出
寄存器移位寻址方式举例:
ADD R0,R1,R2,ROR #2 ;R0<-(R1)+(R2循环右移2位)
MOV R0,R1,LSL R2 ;R0<-(R1逻辑左移R2位)
寄存器间接寻址方式
寄存器间接寻址方式就是将寄存器中的数值作为数据的物理地址,将该物理地址上的数据作为操作数的寻址方式
寄存器简介寻址方式举例:
MOV R0,#0x100
MOV R1,#0x200
STR R0,[R1] ;[R1]<-(R0)
LDR R0,[R1] ;R0<-[R1]
假设内存地址0x200处的值为0x20,那么第3条指令执行后,内存地址0x200处的值变为0x100,第四条指令执行后寄存器R0中的值变为0x200
基址变址寻址
基址变址寻址就是将基址寄存器的值与指令中给出的偏移地址量相加,所得结果作为操作数的物理地址
基址变址寻址举例:
MOV R0,#0x100
MOV R1,#0x200
MOV R2,#0x300
LDR R0,[R1,#0x4] ;R0<-[R1+4]
LDR R0,[R1,E2] ;R0<-[R1+R2]
假设内存地址0x204处的值为0x101,地址0x500处的值为0x105
第四条指令将地址0x204处的数据存储到寄存器R0中,这条指令执行后,寄存器R0中的值为0x101
第五条指令将寄存器R1和R2相加的值作为内存地址,将这个内存地址上的数据存储到寄存器R0中,这条指令执行后,寄存器R0中的值为0x105
运算
ARM对数据的运算包括算数运算以及逻辑运算,下面对他们的指令进行详细介绍
算术运算指令
算术运算指令包括了加、减、乘、累加等操作,下图总结了ARM指令主要的算术运算指令:
不带进位加法指令ADD
·ADD指令格式:将[operand2]数据与[Rn]值相加,结果保存到[Rd]寄存器
ADD{[COND]}{S} [Rd],[Rn],[operand2]
其中:
[cond]为指令执行的条件,当[cond]省略时,指令无条件执行
{S}决定指令是否影响CPSR中标志位的值,当{S}省略时,指令的执行对CPSR寄存器中标志位不产生任何影响
带进位加法指令ADC
·ADC指令格式:将[operand2]数据与[Rn]值相加,再加上CPSR寄存器中的C标志位,结果存到[Rd]寄存器
ADC{[cond]}{S} [Rd],[Rn],[operand2]
其中:
[cond]为指令执行的条件,当[cond]省略时,指令无条件执行
{S}决定指令是否影响CPSR中标志位的值,当{S}省略时,指令的执行对CPSR寄存器中标志位不产生任何影响
不带进位减法指令SUB
·SUB指令格式:将[operand2]数据与[Rn]值相减,结果保存到[Rd]寄存器
SUB{[COND]}{S} [Rd],[Rn],[operand2]
其中:
[cond]为指令执行的条件,当[cond]省略时,指令无条件执行
{S}决定指令是否影响CPSR中标志位的值,当{S}省略时,指令的执行对CPSR寄存器中标志位不产生任何影响
带进位减法指令SBC
·SBC指令格式:将[Rn]减去[operand2],再减去CPSR寄存器中的C标志位的反码,结果存到[Rd]寄存器
SBC{[cond]}{S} [Rd],[Rn],[operand2]
该指令主要用于字长大于32位的数据的减法运算
其中:
[cond]为指令执行的条件,当[cond]省略时,指令无条件执行
{S}决定指令是否影响CPSR中标志位的值,当{S}省略时,指令的执行对CPSR寄存器中标志位不产生任何影响
不带进位逆向减法指令RSB
·RSB指令格式:把[operand2]作为被减数,把[Rn]作为减数进行减法运算,运算结果保存到[Rd]中
RSB{[cond]}{S} [Rd],[Rn],[operand2]
其中:
[cond]为指令执行的条件,当[cond]省略时,指令无条件执行
{S}决定指令是否影响CPSR中标志位的值,当{S}省略时,指令的执行对CPSR寄存器中标志位不产生任何影响
带进位逆向减法指令RSC
·RSC指令格式:把[operand2]作为被减数,把[Rn]作为减数,然后再减去CPSR中的C标志位,最后的结果存储在[Rd]中
RSC{[cond]}{S} [Rd],[Rn],[operand2]
其中:
[cond]为指令执行的条件,当[cond]省略时,指令无条件执行
{S}决定指令是否影响CPSR中标志位的值,当{S}省略时,指令的执行对CPSR寄存器中标志位不产生任何影响
32位乘积运算指令MUL
·MUL指令格式:将[Rm]作为乘数,[Rs]作为被乘数进行乘法运算,并把结果保存到[Rd]中
MUL{[cond]}{S} [Rd],[Rm].[Rs]
其中:
[cond]为指令执行的条件,当[cond]省略时,指令无条件执行
{S}决定指令是否影响CPSR中标志位的值,当{S}省略时,指令的执行对CPSR寄存器中标志位不产生任何影响
乘-累加指令MLA
·MLA指令格式:将[Rm]和[Rs]相乘,再将乘积加上[Rn],所得的结果保存在[Rd]中
MLA{[cond]}{S} [Rd],[Rm],[Rs],[Rn]
其中:
[cond]为指令执行的条件,当[cond]省略时,指令无条件执行
{S}决定指令是否影响CPSR中标志位的值,当{S}省略时,指令的执行对CPSR寄存器中标志位不产生任何影响
无符号数长乘指令UMULL
·UMULL指令格式:UMULL指令可以实现64位无符号数乘法,将寄存器Rm和Rs的值作无符号相乘,结果的低32位保存到寄存器[Rd_L]中,高32位保存到寄存器[Rd_H]中
UNULL{[cond]}{S} [Rd_L],[Rd_H],[Rm],[Rs]
其中:
[cond]为指令执行的条件,当[cond]省略时,指令无条件执行
{S}决定指令是否影响CPSR中标志位的值,当{S}省略时,指令的执行对CPSR寄存器中标志位不产生任何影响
无符号长乘-累加指令UMLAL
·UMLAL指令格式:该指令将Rm和Rs中的值作为无符号相乘,乘积再与[Rd_H,Rd_L]相加,结果的低32位保存到寄存器[Rd_L]中,高32位保存到寄存器[Rd_H]中
UMLAL{[cond]}{S} [Rd_L],[Rd_H],[Rm],[Rs]
其中:[Rd_L],[Rd_H],[Rm],[Rs]为通用寄存器
[cond]为指令执行的条件,当[cond]省略时,指令无条件执行
{S}决定指令是否影响CPSR中标志位的值,当{S}省略时,指令的执行对CPSR寄存器中标志位不产生任何影响
有符号长乘指令SMULL
·SMULL指令格式:该指令将寄存器Rm和Rs中的值作有符号相乘,结果的低32位保存到寄存器[Rd_L]中,高32位保存到寄存器[Rd_H]中
SMULL{[cond]}{S} [Rd_L],[Rd_H],[Rm],[Rs]
其中:
[cond]为指令执行的条件,当[cond]省略时,指令无条件执行
{S}决定指令是否影响CPSR中标志位的值,当{S}省略时,指令的执行对CPSR寄存器中标志位不产生任何影响
有符号长乘-累加指令SMLAL
·SMLAL指令格式:该指令将寄存器Rm和Rs中的值作有符号相乘,乘积再与[Rd_H,Rd_L]相加,结果的低32位保存到寄存器[Rd_L]中,高32位保存到寄存器[Rd_H]中
SMLAL{[cond]}{S} [Rd_L],[Rd_H],[Rm],[Rs]
其中:[Rd_L],[Rd_H],[Rm],[Rs]为通用寄存器
[cond]为指令执行的条件,当[cond]省略时,指令无条件执行
{S}决定指令是否影响CPSR中标志位的值,当{S}省略时,指令的执行对CPSR寄存器中标志位不产生任何影响
比较指令CMP
·CMP指令格式:将寄存器Rn中的值与操作数[operand2]相减,根据相减的结果更新CPSR寄存器中的标志位,但是并不保存相减的结果
CMP{[cond]}{S} [Rn].[operand2]
其中:[cond]为指令的条件域,当其省略时,指令无条件执行
负数比较指令CMN
·CMN指令格式:将寄存器Rn中的值与操作数[operand2]的负值相减,根据相减的结果更新CPSR寄存器中的标志位,但是并不保存相减的结果
CMN{[cond]}{S} [Rn].[operand2]
其中:[cond]为指令的条件域,当其省略时,指令无条件执行
将算数运算指令格式及用法总结成表:
逻辑运算指令
ARM指令集中使用频率比较高的逻辑运算指令有6个:
逻辑运算指令“与”AND
·AND指令格式:该指令用于对两个操作数[Rn]和[operand2]进行逻辑与运算,并把运算的结果储存到目标寄存器Rd中
AND{[cond]}{S} [Rd].[Rn],[operand2]
其中:
进行与运算的第一个操作数是一个寄存器,第二个操作数可以是一个寄存器、被移位的寄存器或一个立即数
AND操作可以用来屏蔽操作数的某些位
[cond]为指令执行的条件,当[cond]省略时,指令无条件执行
{S}决定指令是否影响CPSR中标志位的值,当{S}省略时,指令的执行对CPSR寄存器中标志位不产生任何影响
逻辑运算指令“或”ORR
·ORR指令格式:该指令用于对两个操作数[Rn]和[operand2]进行逻辑或运算,并把运算的结果储存到目标寄存器Rd中
ORR{[cond]}{S} [Rd].[Rn],[operand2]
其中:
进行或运算的第一个操作数是一个寄存器,第二个操作数可以是一个寄存器、被移位的寄存器或一个立即数
[cond]为指令执行的条件,当[cond]省略时,指令无条件执行
{S}决定指令是否影响CPSR中标志位的值,当{S}省略时,指令的执行对CPSR寄存器中标志位不产生任何影响
逻辑指令“异或”EOR
·EOR指令格式:该指令用于对两个操作数[Rn]和[operand2]进行逻辑异或运算,并把运算的结果储存到目标寄存器Rd中
EOR{[cond]}{S} [Rd].[Rn],[operand2]
其中:
进行或运算的第一个操作数是一个寄存器,第二个操作数可以是一个寄存器、被移位的寄存器或一个立即数
[cond]为指令执行的条件,当[cond]省略时,指令无条件执行
{S}决定指令是否影响CPSR中标志位的值,当{S}省略时,指令的执行对CPSR寄存器中标志位不产生任何影响
位清除指令BIC
·BIC指令格式:该指令用于清除操作数的某些位,并把结果存储到目标寄存器中
BIC{[cond]}{S} [Rd].[Rn],[operand2]
其中:
[operand2]为32位的掩码,当[operand2]中的某一位为一时,[Rn]中的对应位会被清零,结果储存到寄存器Rd中
[cond]为指令执行的条件,当[cond]省略时,指令无条件执行
{S}决定指令是否影响CPSR中标志位的值,当{S}省略时,指令的执行对CPSR寄存器中标志位不产生任何影响
测试比较指令TST
·TST指令格式:用于将一个寄存器的值和一个算数值做比较
TST{[cond]}{S} [Rd].[operand2]
其中:
[Rd]为要进行测试的寄存器数据,[operand2]是一个32位掩码
条件标志位根据两个操作数逻辑“与”的结果设置
[cond]为指令的条件标志位,决定指令在什么情况下执行,当[cond]省略时,指令无条件执行
异或测试指令TEQ
·TEQ指令格式:将一个寄存器中的值与另一个寄存器中的数值或立即数进行按位“异或”,并根据运算的结果更新CPSR中的状态位
TEQ{[cond]}{S} [Rd].[operand2]
其中:
[Rd]为通用寄存器,[operand2]可以是一个寄存器,也可以是一个立即数
TEQ指令的功能与EORS指令类似,不同的是,TEQ指令只更新CPRS状态寄存器,并不保存运算结果
逻辑运算指令格式及用法总结如下:
数据传输
执行数据传送指令时,微处理器首先计算存储器地址,并把计算所得的地址送达do地址寄存器,在接下来的周期再进行数据传送
下图总结了寄存器之间主要数据通路行为:
下图总结了寄存器与立即数之间主要数据通路行为:
数据传送指令
数据传送指令是指在存储器和寄存器之间进行数据传送的指令,下图是对数据传送指令的总结:
条件码
当ARM微处理器工作在ARM状态时,指令可以根据CPSR中的状态位和指令中的条件域有条件的执行
每个ARM指令代码中包含四位的条件码,位于指令代码的[31:28]位,当条件代码指示的条件为真时,代码执行,若条件不真实,代码不执行
下图列出了ARM指令条件码和相应的助记符,以及他们的含义:
中断
中断的概念
·中断是一个过程,是CPU在执行当前程序的过程中因硬件或软件的原因插入了另一段程序运行的过程。
·因硬件原因引起的中断过程的出现是不可预测的,即随机的,而软中断是事先安排的。
中断源的概念
·我们把可以引起中断的信号源称之为中断源。
·arm920T中有60个中断源:
·外部子中断源20个:
·内部子中断源15个:
中断优先级的概念
ARM处理器中有7种类型的异常,按优先级从高到低的排列如下:
复位异常(Reset)
数据异常(Data Abort)
快速中断异常(FIQ)
外部中断异常(IRQ)
预取异常(Prefetch Abort)
软件中断(SWI)
未定义指令异常(Undefined instruction)
ARM汇编编程及调试
c语言代码:
#include <stdio.h>
int nGlobalVar = 0;
int tempFunction(int a, int b)
{
printf("tempFunction is called, a = %d, b = %d /n", a, b);
return (a + b);
}
int main()
{
int n;
n = 1;
n++;
n--;
nGlobalVar += 100;
nGlobalVar -= 12;
printf("n = %d, nGlobalVar = %d /n", n, nGlobalVar);
n = tempFunction(1, 2);
printf("n = %d", n);
return 0;
}
用gcc编译一下,使用 -o 参数指定了编译生成的可执行文件名为 gdb-sample,使用参数 -g 表示将源代码信息编译到可执行文件中。如果不使用参数 -g,会给后面的GDB调试造成不便
输入gdb命令启动GDB,首先显示gdb说明
使用“file”命令载入被调试程序 gdb-sample(这里的 gdb-sample 即前面 gcc 编译输出的可执行文件):
最后一行提示已经加载成功
下面使用“r”命令执行(Run)被调试文件,因为尚未设置任何断点,将直接执行到程序结束:
使用“b”命令在 main 函数开头设置一个断点(Breakpoint):
提示已经成功设置断点,并给出了该断点信息:在源文件 gdb-sample.c 第14行处设置断点;这是本程序的第一个断点(序号为1);断点处的代码地址为 0x8048442。源代码中第14行中的代码为“n = 1”,恰好是 main 函数中的第一个可执行语句
再次使用“r”命令执行(Run)被调试程序:
程序中断在gdb-sample.c第14行处,即main函数是第一个可执行语句处。
上面最后一行信息为:下一条将要执行的源代码为“n = 1;”,它是源代码文件gdb-sample.c中的第14行
下面使用“s”命令(Step)执行下一行代码(即第14行“n = 1;”):
上面的信息表示已经执行完“n = 1;”,并显示下一条要执行的代码为第15行的“n++;”。
已经执行了“n = 1;”,即给变量 n 赋值为 1,用“p”命令(Print)看一下变量 n 的值是不是 1 :
下面我们分别在第21行、tempFunction 函数开头各设置一个断点(分别使用命令“b 21”“b tempFunction”):
使用“c”(Continue)命令继续执行被调试程序,程序将中断在第二个断点(21行),再一次执行“c”命令,程序将中断于第三个断点(第7行,tempFunction 函数开头处),此时全局变量 nGlobalVar 的值是 88,tempFunction 函数的两个参数 a、b 的值分别是 1 和 2:
再一次执行“c”命令(Continue),因为后面再也没有其它断点,程序将一直执行到结束:
使用display命令“display /i $pc”查看编译器生成的汇编代码,并进行汇编级的调试或跟踪:
看到了汇编代码,“n = 1;”对应的汇编代码是“movl $0x1,0xc(%ebp)”
并且以后程序每次中断都将显示下一条汇编指令:
接下来试一下命令“b *<函数名称>”
为了更简明,先删除目前所有断点(使用“d”命令——Delete breakpoint):
使用命令“b *main”在 main 函数的 prolog 代码处设置断点(prolog、epilog,分别表示编译器在每个函数的开头和结尾自行插入的代码):
可以使用“i r”命令显示寄存器中的当前值:
也可以查看指定寄存器中的值:
命令“q”(Quit)退出GDB调试环境:
操作实践之后下对gdb命令及功能做了以下总结: