ARM汇编学习(一)

title: ARM汇编学习(一)
date: 2020-03-01 17:22:07
tags:

  • ARM汇编
  • 寄存器
  • 指令

ARM汇编学习(一)

ARM处理器基础之寻址方式

1.寄存器寻址
寄存器寻址,简单来说就是把ARM寄存器内的值直接赋给另外一个ARM寄存器,是一种非常常见的寻址方式,有点类似于我们编程中,把变量A的值直接赋给B的操作。
var B=A;

例子如下
MOV  R1,R2  ;把寄存器R2里面的值赋值个R1
SUB  R0,R1,R2 ;使用寄存器R1减去R2 结果赋值给R0寄存器
2.立即寻址
立即寻址就是直接把值赋给对应的变量(寄存器),这和我们C语言里面给变量赋一个具体的数值很像
var A = 10;

例子如下
SUBS R0,R0,#1 ;R0-1 -> R0 寄存器R0-1的值保存到R0中
3.寄存器偏移寻址
寄存器偏移寻址是ARM指令集特有的寻址方式,当第二个操作数是寄存器偏移方式时,第二个寄存器操作数在与第一个操作数结合之前,选择进行位移操作
这样的操作其实类似于我们在C语言里面,先把变量进行位移操作,然后再赋值给其他
var A= B>>1;

例子如下
MOV R0,R2,LSL #3  ;R2的值左移3位,结果放入R0,即R0=R2*8
ANDS R1,R1,R2,LSL R3  ;R2的值左移3位,然后和R1相与操作,结果放入R1
寄存器寻址可以使用的位移操作符如下
LSL:逻辑左移,寄存器中低端空出的位置补0
LSR:逻辑右移,寄存器中高端空出的位置补0
ASR:算数右移,位移过程中,符号位保持不变,如果源操作数为正数,则高位空出补0,否则补1
ROR:循环右移,由字的低端移出的位填入字的高端空位
RRX:带扩展的循环右移,操作数右移一位,高端空出的位用原C标志值填充

位移操作图

4,寄存器间接寻址
这种寻址方式,就是取第二个参数寄存器编号里面的值,然后赋值给第一个寄存器
类似于C语言中的
var a=*b;

例子程序
LDR R1,[R2]  ;将R2中的数值作为地址,去除此地址中的数据保存在R1中
SWP R1,R1,[R2] ;将R2中的数值作为地址,取出此地址中的数值与R1中的值交换
5.基址寻址
将基址寄存器的内容与指令中给出的偏移量相加,获取到有效的地址,一般情况,基址寻址用于访问基址附近的存贮单元,经常用于查表,数组操作等。
此操作类似于寄存器寻址,只是额外会再加上一个偏移地址

例子程序
LDR R2,[R3,#0x0F] ;将R3中的数值加0x0F作为地址,去除地址的数值保存到R2中
6.多寄存器寻址
多寄存器寻址就是一次可以传送几个几寄存器的值,允许一条指令传送16个寄存器的任何子集或者所有寄存器

例子程序
LDMIA R1!,{R2-R7,R12} ;将R1短语中的数据读出到R2-R7,R12,R1自动加1
STMIA R0!,{R3-R6,R10} ;将R3-R6,R10中的数据保存到R0指向的地址,R0自动加1
7.堆栈寻址
堆栈寻址使用专门的寄存器,堆栈指针指向一块存储区域,指针指向的存储单元就是堆栈顶端,存储器堆栈分为两种,

向上生长:向高地址方向生长,称之为递增堆栈

向下生长:向低地址方向生长,称之为递减堆栈

堆栈指针指向最后压入的堆栈的有效数据项,成为满堆栈,堆栈指针指向下一个要放入的空位置,成为空堆栈,这样就有4种类型的堆栈表示递增和递减的满堆栈和空堆栈
满递增:堆栈通过增大存储器的地址向上增长,堆栈指针指向内含有效数据项的最高地址。例如指令 LDMFA,STMFA等.
空递增:堆栈通过增大存储器的地址向上增长,堆栈指针指向堆栈上的第一个空位置,例如指令 LDMEA,STMFA等.
满递减:堆栈通过减小存储器的地址向下增长,堆栈指针指向内含有效数据项的最低地址。例如指令 LDMFD,STMFD等.
空递减:堆栈通过减小存储器的地址向下增长,堆栈指针指向堆栈下的第一个空位置。例如指令 LDMFD,STMFD等.

STMFD SP!,{R1-R7,LR} ;将R1-R7,LR入栈,满递减堆栈

8.块拷贝寻址
多寄存器传送指令用于一块数据从存储器的某一位置拷贝到另一位置。
有点类似多寄存器寻址
例子代码
STMIA  R0!,{R1,R7} ;将R1-R7的数据保存到存储器,存储器地址保存在R0中,R0递增
9.相对寻址
相对寻址是基址寻址的另外一个变种,有程序计数器PC提供基准地址,指令中的地址码字段作为偏移量,两者相加后得到有效地址
例子代码
BL ROUTE1  ;调用到ROUTE1子程序
BEQ LOOP  ;条件跳转指令

ARM指令集

1.条件码
条件码助记符 影响标志位 含义
EQ Z==1 相等
NE Z==0 不相等
CS/HS C=1 无符号数大于或等于
CC/L0 C=0 无符号数小于
MI N=1 负数
PL N=0 正数或零
VS V=1 溢出
VC V=0 没有溢出
HI C=1,Z=0 无符号数大于
LS C=0,Z=1 无符号书小于或等于
GE N=V 有符号数大于或等于
LT N!=V 有符号数小于
GT Z=0,N=V 有符号数大于
LE Z=1,N!=V 有符号数小于或等于
AL Any 无条件执行
条件码应用举例
C语言代码
if (a>b)
{
  a++;
}
else
{
  b++;
}
对应的ARM指令,其中R0为a,R1为b
CMP      R0, R1        ;R0与R1比较
ADDHI    R0, R0, #1    ;如果R0>R1,R0=R0+1
ADDLS    R1, R1, #1    ;否则R0<R1,R1=R1+1

if ((a!=10)&&(b!=20))
{
      a=a+b;
}
对应ARM指令,其中R0为a,R1为b
CMP    R0,#10    ;比较R0是否为10
CMPNE  R1,#20    ;如果R0不为10,则比较R1是否为20
ADDNE  R0,R0,R1  ;如果R0不为10,并且R1不为20,指令执行R0=R0+R1
2.存储器访问指令
助记符 说明 操作 条件码位置
LDR Rd,addressing 加载字数据 Rd<-[addressing],addressing 索引 LDR {cond}
LDRB Rd,addressing 加载无符号字节数据 Rd<-[addressing],addressing 索引 LDR {cond} B
LDRT Rd,addressing 以用户模式加载字数据 Rd<-[addressing],addressing 索引 LDR {cond} T
LDRBT Rd,addressing 以用户模式加载无符号字数据 Rd<-[addressing],addressing 索引 LDR {cond} BT
LDRH Rd,addressing 加载无符号半字数据 Rd<-[addressing],addressing 索引 LDR {cond} H
LDRSB Rd,addressing 加载有符字节数据 Rd<-[addressing],addressing 索引 LDR {cond} SB
LDRSH Rd,addressing STRH Rd,addressing Rd<-[addressing],addressing 索引 LDR {cond} SH
STR Rd,addressing 存储字数据 [addressing]<-Rd,addressing 索引 STR {cond}
STRB Rd,addressing 存储字节数据 [addressing]<-Rd,addressing 索引 STR {cond} B
STRT Rd,addressing 以用户模式存储字数据 [addressing]<-Rd,addressing 索引 STR {cond} T
STRBT Rd,addressing 以用户模式存储字节数据 [addressing]<-Rd,addressing 索引 STR {cond} BT
STRH Rd,addressing 存储半字数据 [addressing]<-Rd,addressing 索引 STR {cond} H
LDM {mode} Rn{!},reglist 批量(寄存器)加载 reglist <- [Rn … ], Rn 回存等 LDM {cond} {more}
STM {mode} Rn{!},reglist 批量(寄存器)存储 [Rn … ] <- reglist, Rn 回存等 STM {cond} {more}
SWP Rd,Rm,Rn 寄存器和存储器字数据交换 Rd<-[Rd],[Rn]<-[Rm] (Rn <> Rd或Rm) SWP {cond}
SWPB Rd,Rm,Rn 寄存器和存储器字节数据交换 Rd<-[Rd],[Rn]<-[Rm] (Rn <> Rd或Rm) SWP {cond} B
LDR和STR
加载/存储无符号字节指令,使用单一数据传送指令STR和LDR来存储和加载数据
指令格式如下
LDR {cond} {T}  Rd,<address>;加载指定地址上的数据(字),放入Rd中
STR {cond} {T}  Rd,<address>;存储数据(字)到指定地址的存储单元,要存储的数据在Rd中
LDR {cond}B {T}  Rd,<address>;加载字节数据,放入Rd中,既Rd最低字节有效,高24位清零
STR {cond}B {T}  Rd,<address>;存储字节数据,要存储的数据在Rd中,最低字节有效
其中T为可选后缀,若指令中带有T,那么即使处理器是在特权模式下,存储系统也将访问看成是处理器在用户模式下
立即数
立即数可以是个无符号数值,这个数据可以加到基址寄存器,也可以从基址寄存器中减去这个数值,指令举例如下:
LDR R1,[R0,#0x12]; 将R0+0x12地址处的数据独处,保存到R1中,R0的值不变
LDR R1,[R0] ;将R0地址处的数据读出,保存到R1中,0地址偏移
寄存器
寄存器内的数值可以加到基址寄存器,也可以从基址寄存器中减去这个数值,指令举例如下:
LDR R1,[R0,R2] ;将R0+R2地址的数据读出,保存到R1中(R0值不变)
LDR R1,[R0,-R2];将R0-R2的地址处的数据读出,保存到R1中,(R0值不变)
寄存器及位移常数
寄存器移位后的值可以加到基址寄存器,也可以从基址寄存器中减去这个数值,指令举例如下:
LDR R1,[R0,R2,LSL #2]  ;将R0+R2*4(R2左移2位)的地址保存到R1中
LDR R1,[R0,-R2,LSL #2] ;将R0-R2*4(R2左移2位)的地址保存到R1中
寻址方式的地址计算方法分为加载/存储指令等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必须是当前指令的正负4K范围内
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地址上的1字节数据并保存到R3中,R3=R2+1
STRB R6,[R7]       ;读取R6的数据保存到R7指定的地址中,值存储一个字节

加载/存储半字和带符号字节,这类LDR/STR指令可能加载带符号数据,偏移量格式,寻址方式与上面相同,具体指令如下:
LDR{cond}SB Rd,<address>  ;加载指定地址上的数据(带符号字节),放入Rd中
LDR{cond}SH Rd,<address>  ;加载指定地址上的数据(带符号字节),放入Rd中
LDR{cond}H Rd,<address>   ;加载半字数据,放入Rd中,既Rd最低的16位有效,高16位清零
STR{cond}H Rd,<address>   ;存储半字数据,要存储的数据在Rd,最低16位有效
说明:带符号位的半字,字加载是指带符号位加载扩展到32位;无符号位半字加载是指令扩展到32位.
地址对准:对半字传输的地址必须为偶数,非半字对准的半字加载将使Rd内容不可靠,非半字对准的半字存储将使指定的2字节存储内容不可靠
加载/存储半字和带符号字节指令举例如下:
LDRSB R1 [R0,R3]   ;将R0+R3地址傻姑娘的字节数据读取到R1,高24位用符号位扩展
LDRSH R1,[R9]      ;将R9地址上的半字数据读出到R1,高16位用符号位扩展
LDRH  R6,[R2],#2   ;将R2地址的半字数据读出到R6,高16位用零扩展
SHRH  R1,[R0,#2]!  ;将R1的数据存储到R0+2的地址中,只存储低位的2个字节数据R0=R0+2(R0的值增加2)
LDR/STR指令用于对内存变量的访问,内存缓冲区数据访问,查表,外围部件的控制操作等.
LDM和STM
批量加载/存储指令可以实现一组寄存器和一块连续的内存单元之间的传输数据,LDM为加载多个寄存器,STM为存储多个寄存器,允许一条指令传送16个寄存器的任何子集或者所有寄存器,指令格式如下:
LDM{cond}<模式> Rn{!},reglist{^}
STM{cond}<模式> Rn{!},reglist{^}
其中可使用的模式如下:
1.IA:每次传送后地址加4
2.IB:每次传送前地址加4
3.DA:每次传送后地址减4
4.DB:每次传送前地址减4
5.FD:满递减堆栈
6.ED:空递增堆栈
7.FA:满递增堆栈
8.EA:空递增堆栈
其中Rn为基址寄存器,Rn不允许为R15,后缀!表示最后的地址歇会到Rn中,寄存器列表reglist可包含多于一个寄存器或者寄存器范围.使用,分开,如{R1,R2,R6-R9},寄存器排列由小到大排列."^"后缀不允许在用户模式

举例如下:
LDMIA R0!,{R3-R9}    ;加载R0指向的地址上的多字数据,保存到R3-R9中,R0值更新
STMIA R1!,{R3-R9}    ;将R3-R9的数据存储到R1只想的地址上,R1值更新
STMfD SP!,{R0-R7,LR} ;保存现场,将R0-R7,LR入栈
LDMFD SP!,{R0-R7,PC} ;恢复现场,异常处理返回

STMDA

STMDB

STMIA

STMIB


文章作者: 孙老师
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 孙老师 !
 上一篇
使用ndk创建Android系统内的可执行程序 使用ndk创建Android系统内的可执行程序
title: 使用ndk创建Android系统内的可执行程序date: 2020-02-29 21:08:20tags: android linux可执行文件 arm C语言 使用ndk创建Android系统内的可执行程序我们在用
2020-02-29 孙老师
下一篇 
Android面试之Handler相关学习 Android面试之Handler相关学习
###Android面试之Handler相关学习 1.Android消息机制之Looper.java源代码学习1). 准备阶段,在子线程中调用Looper.prepare()方法或者在主线程调用Looper.prepareMainLoope
2020-02-12 孙老师
  目录