常用汇编指令

LDR

作用:LDR{条件} 目的寄存器 <存储器地址>。将 存储器地址 所指地址处连续的32位(4个字节 ==1个字)的数据传送到目的寄存器中。(只能操作一次内存、寄存器)

LDR指令的寻址方式比较灵活,实例如下:

LDR   R0,[R1]                  ;将存储器地址为R1的字数据读入寄存器R0。
LDR   R0,[R1,R2]             ;将存储器地址为R1+R2的字数据读入寄存器R0。
LDR   R0,[R1,#8]             ;将存储器地址为R1+8的字数据读入寄存器R0。
LDR   R0,[R1,R2]!          ;将存储器地址为R1+R2的字数据读入寄存器R0,并将新地址R1+R2写入R1。
LDR   R0,[R1,#8]!          ;将存储器地址为R1+8的字数据读入寄存器R0,并将新地址R1+8写入R1。
LDR   R0,[R1],R2              ;将存储器地址为R1的字数据读入寄存器R0,并将新地址R1+R2写入R1。
LDR   R0,[R1,R2,LSL#2]!   ;将存储器地址为R1+R2×4的字数据读入寄存器R0,并将新地址R1+R2×4写入R1。
LDR   R0,[R1],R2,LSL#2     ;将存储器地址为R1的字数据读入寄存器R0,并将新地址R1+R2×4写入R1。
//  !   地址更新,结果写回到Rn中,Rn不允许是R15
//LSP 表示左移的意思

ldr R0, label 和 ldr R0, =label的区别:

  • ldr是ARM中的指令,也是伪指令。编译器会根据 立即数的大小,来分辨是伪指令还是ARM指令,如:ldr R0,=0x100,这是个立即数,是ARM中的指令,意思就是说把0x100这个数写入到R0;ldr R0,=0x12345678,这个是伪指令,意思就是把地址为0x12345678的数据写入到R0

  • ldr R0, =label 会把label表示的值加载到寄存器中,而LDR r, label会把label当做地址,把label指向的地址中的值加载到寄存器中。譬如 label的值是 0x8000, LDR r, =label会将 0x8000加载到寄存器中,而LDR r, label则会将内存0x8000处的值加载到寄存器中。

STR

作用:str{条件} 源寄存器,<存储器地址>,将源寄存器中数据存到存储器地址中。:将源寄存器 所指地址处连续的32位(4个字节 ==1个字)的数据传送到目的存储器地址中

str   r1,[r2]        ; 将r1中的值存到r2所指定的地址中

str  r1,[r2,#4]   ;将r1中的值存到r2+4所指定的地址中

STR   R0,[R1],#8    ;将R0中的字数据写入以R1为地址的存储器中,并将新地址R1+8写入R1。

ldr跟str最明显的区别就是,寄存器写入数据到存储器,还是存储器写入东西到寄存器。
常用汇编指令_第1张图片

MOV

MOV指令可以在CPU内或CPU和存储器之间传送字或字节,它传送的信息可以从寄存器到寄存器,立即数到寄存器,立即数到存储单元,从存储单元到寄存器,从寄存器到存储单元,从寄存器或存储单元到除CS外的段寄存器 ( 注意立即数不能直接送段寄存器) , 从段寄存器到寄存器或存储单元。

格式

MOV目标地址(除CS、IP以外的寄存器或存储器) 源地址(寄存器、存储器、立即数 )

实例:MOV R0,R1 //把R1的值传送到R0,就是R0 = R1

注意

  • (1) MOV 指令中的源操作数绝对不能是立即数和代码段CS 寄存器;
  • (2) MOV 指令中绝对不允许在两个存储单元之间直接传送数据;
  • (3) MOV 指令中绝对不允许在两个段寄存器之间直接传送数据;
  • (4) MOV 指令不会影响标志位

B

B 指令的格式为:B{条件} 目标地址

  • B 指令是最简单的跳转指令(相对跳转指令)。一旦遇到一个 B 指令,ARM 处理器将立即跳转到给定的目标地址,从那里继续执行。
  • 注意存储在跳转指令中的实际值是相对当前PC 值的一个偏移量,而不是一个绝对地址,它的值由汇编器来计算(参考寻址方式中的相对寻址)。它是 24 位有符号数,左移两位后有符号扩展为 32 位,表示的有效偏移为 26 位(前后32MB 的地址空间)。以下指令:B Label ;程序无条件跳转到标号 Label 处执行

BL

bl作用

  • b指令的作用:实现程序跳转
  • bl指令的作用:实现程序跳转,把返回地址存储到下一条指令那里

b与bl指令的区别

  • b指令:简单的程序跳转,跳转到到目标标号处执行。
  • bl指令:带链接程序跳转,也就是要带返回地址。在发生跳转前,将当前PC-4保存到R14中。 也就是返回地址存在R14中,所以可以在子程序返回时只要MOV PC, LR即可。

add

格式: add A,B (A=A+B)
功能: 两数相加

A和B均为寄存器是允许的,一个为寄存器而另一个为存储器也是允许的, 但不允许两个都是存储器操作数,也就是说A与B不能同时是指针 如: add [eax],[ebx] 这类情况是错的

add 寄存器,数据       比如:add ax,8
 
add 寄存器,寄存器     比如:add ax,bx
 
add 寄存器,内存单元    比如:add ax,[0]

add 内存单元,寄存器    比如:add [0],ax
;加法指令执行时,若没有进位 CPSR 'C' 位置 0
mov r0, #1		  ;r0 = r0 + 1
mov r1, #1		  ;r1 = r0 + 1
add r2, r1, r0  ;r2 = r1 + r0
add r2, r1, #2  ;r2 = r1 + 2

sub

格式: sub A, B (A=A-B)
功能:从目的操作数中减去源操作数

A和B均为寄存器是允许的,一个为寄存器而另一个为存储器也是允许的, 但不允许两个都是存储器操作数,也就是说A与B不能同时是指针 如: sub [eax],[ebx] 这类情况是错的

sub 寄存器,数据       比如:sub ax,8
 
sub 寄存器,寄存器     比如:sub ax,bx
 
sub 寄存器,内存单元    比如:sub ax,[0]

sub 内存单元,寄存器    比如:sub [0],ax

LDMIA、LDMIB、LDMDB、LDMDA、STMIA、LDMFD、LDMFA、LDMED、LDMEA指令详解

搬砖的文章:https://www.cnblogs.com/lifexy/p/7363208.html

ARM指令中多数据传输共有两种:

  • LDM:(load much)多数据加载,将地址上的值加载到寄存器上,出栈操作,从左到右。

  • STM:(store much)多数据存储,将寄存器的值存到地址上,入栈操作从右到左。

主要用途

现场保护、数据复制、参数传送等,共有8种模式(前面4种用于数据块的传输,后面4种是堆栈操作)如下:

  • (1)IA:(Increase After) 每次传送后地址加4,其中的寄存器从左到右执行,例如:STMIA R0,{R1,LR} 先存R1,再存LR

  • (2)IB:(Increase Before)每次传送前地址加4,同上

  • (3)DA:(Decrease After)每次传送后地址减4,其中的寄存器从右到左执行,例如:STMDA R0,{R1,LR} 先存LR,再存R1

  • (4)DB:(Decrease Before)每次传送前地址减4,同上

  • (5)FD: 满递减堆栈 (每次传送前地址减4)

  • (6)FA: 满递增堆栈 (每次传送后地址减4)

  • (7)ED: 空递减堆栈 (每次传送前地址加4)

  • (8)EA: 空递增堆栈 (每次传送后地址加4)

栈地址的增长方向:ARM将向高地址增长的栈称为递增栈(Descendent Stack),将向低地址增长的栈称为递减栈(Acendant
Stack)
栈指针的指向位置:ARM将栈指针指向栈顶元素位置的栈称为满栈(Full Stack),讲栈指针指向即将入栈也就是栈底的元素位置的栈称为空栈(Empty
Stack)

注意:其中在数据块的传输中是STMMDB和LDMIA对应,STMMIA和LDMDB对应。而在堆栈操作是STMFD和LDMFD对应,STMFA和LDMFA对应。更详细的fd fa ed ea堆栈操作可以看看这篇文章

格式:

LDM{cond}  mode  Rn{!}, reglist{^}

STM{cond}  mode  Rn{!}, reglist{^}

Rn: 基址寄存器,装有传送数据的起始地址,Rn不允许为R15;

!: 表示最后的地址写回到Rn中;

reglist: 可包含多于一个寄存器范围,用“,”隔开,如{R1,R2,R6-R9},寄存器由小到大顺序排列;

^; 不允许在用户模式和系统模式下运行

数据块的传输-实例:

Ldr R1,=0x10000000          //传送数据的起始地址0x10000000     

LDMIB R1!,{R0,R4-R6}      //从左到右加载,相当于 LDR R0,10000004  LDR R4,10000008... ...

/*传送前地址加+4,

所以地址加4,R0=0X1000004地址里的内容,

地址加4,R4=0X10000008地址里的内容,

地址加4,R5=0X1000000C地址里的内容,

地址加4,R6=0X10000010 地址里的内容,

由于!, 最后的地址写回到R1中,R1=0X10000010   */
     
Ldr R1,=0x10000000          //传送数据的起始地址0x10000000   

LDMIA R1!,{R0,R4-R6}         //从左到右加载,相当于 LDR R0,10000000  LDR R4,10000004... ...

/*传送后地址加+4,

所以R0=0X10000000地址里的内容,地址加4,

R4=0X10000004地址里的内容,地址加4,

R5=0X10000008地址里的内容,地址加4,

R6=0X1000000C 地址里的内容,地址加4,

由于!,最后的地址写回到R1中,所以R1=0X10000010   */

LDR R1,=0x10000000          //传送数据的起始地址0x10000000  
LDR R4,=0X10
LDR R5,=0X20
LDR R6,=0X30

STMIB R1,{R4-R6}          
/*从右到左加载,相当于STR [R4],0X10000004    STR [R5],0X10000008 ..... */
/*传送前地址加+4,所以0X10000004地址=0X10,0X10000008地址=0X20,0X1000000C地址=0X30 */
   
Ldr R1,=0x10000000        //传送数据的起始地址0x10000000  
LDR R4,=0X10
LDR R5,=0X20
LDR R6,=0X30     

STMIA R1!,{R4-R6 }     

/*传送后地址加+4,所以0X10000000地址=0X10,0X10000004地址=0X20,0X10000008地址=0X30,由于!,最后的地址写回到R1中,所以R1=0X1000000C  */

中断实例(利用STMDB和LDMIA保护现场,然后通过LR寄存器返回)

  • 1.先设置栈sp,用于后面使用stmdb存储寄存器数据

  • 2.当产生异常时,便进入中断:

sub lr, lr, #4                  

 //首先将lr-4,因为arm流水线,lr=当前pc+8,由于pc+4段没有执行,所以lr=(当前pc+8)-4;
stmdb sp!, { r0-r12,lr }  

//每次传送前-4,由于递减,所以从右往左存储寄存器

//所以sp-4=lr,sp-8=r12,... sp-56=r0; 由于!,所以最后的地址写回到sp中,sp=sp-56;
 

ldr lr, =int_return  //设置返回地址

ldr pc, =EINT_Handle //进入中断服务函数,如果中途返回就会调用pc=lr,即可执行int_return;

int_return:
ldmia sp!, { r0-r12,pc }^  

//每次传送后+4,所以从左往右加载数据到寄存器

//所以r0=sp, r1=sp+4,...pc=sp+52;由于!,所以最后地址写回到sp中,sp=sp+56;

//此时,sp=sp+56就等于最初栈顶值,pc=lr,然后返回到异常发生前的相应位置继续执行。

//^  ^表示将spsr的值复制到cpsr,因为异常返回后需要恢复异常发生前的工作状态

你可能感兴趣的:(常用汇编指令)