ARM常用指令
一、数据传送指令
1、 mov指令
  格式: mov{<cond>}{s} Rd,op
  操作: Rd = op;
2、 mvn指令
  格式: mvn{<cond>}{s} Rd,op
  操作: Rd = ~op;
二、算术指令
1、 add加法指令
  格式: add{<cond>}{s} Rd,Rn,op
  操作:  Rd = Rn + op;
2、 adc带进位加法指令
  格式: adc{<cond>}{s} Rd,Rn,op
  操作: Rd = Rn + op + C;
3、 sub减法指令
  格式: sub{<cond>}{s} Rd,Rn,op
  操作:Rd = Rn - op;
4、 sbc带借位减法指令
offset指令是什么意思  格式: sbc{<cond>}{s} Rd,Rn,op
  操作: Rd = Rn - op - (~C);
5、 rsb逆向减法指令
  格式: rsb{<cond>}{s} Rd,Rn,op
  操作: Rd = op - Rn;
6、 rsc带借位的逆向减法指令
  格式: rsc{<cond>}{s} Rd,Rn,op
  操作: Rd = op - Rn - (~C);
三、移位
1、 op
立即数:  #number    //#label
寄存器:  Rm
寄存器移位: Rm,<;移位符> #number 或  Rm,<;移位符> Rs
举例:  mov r0,#100    //r0 = 100
  mov r0,r1    //r0 = r1
  mov r0,r1,lsr #1        //r0 = r1 >> 1
  mov r0,r1,lsr r2      //r0 = r1 >> r2
  add r0,r1,r2,lsr #1    //r0 = r1 + r2 >> 1
2、移位符
  lsl 逻辑左移  (unsigned)x << y
  说明:  无符号左移补0
  lsr 逻辑右移  (unsigned)x >> y
  说明:  无符号右移补0
  asl 算术左移  (signed)x << y
  说明:  有符号左移补0
  注意:  逻辑左移和算术左移结果一样
  asr 算术右移  (signed)x >> y
  说明:  有符号右移正数补0,负数补1
  ror 循环右移  ((unsigned)x >> y) | (x << (32 - y))
  说明: x逻辑右移y | x逻辑左移 (32  - y)
  rrx 扩展循环右移 (C << 31)  |  ((unsigned)x >> 1)
  说明:  相当于33位循环右移
  注意:  没有y,只移一位
四、逻辑指令
1、 and逻辑与指令
  格式: and{<cond>}{s} Rd,Rn,op
  操作: Rd = Rn & op;
2、 orr逻辑或指令
  格式: orr{<cond>}{s} Rd,Rn,op
  操作: Rd = Rn | op;
3、 eor逻辑异或指令
  格式: eor{<cond>}{s} Rd,Rn,op
  操作: Rd = Rn ^ op;
4、 bic位清除指令
  格式: bic{<cond>}{s} Rd,Rn,op
  操作: Rd = Rn & (~op);
五、比较指令(默认带S位)
1、 cmp比较指令
  格式: cmp{<cond>} Rn,op
  操作: Rn - op
2、 cmn反比较指令
  格式: cmn{<cond>} Rn,op
  操作: Rn + op
3、 tst位测试指令
  格式: tst{<cond>} Rn,op
  操作: Rn & op
4、 teq相等测试指令
  格式: teq{<cond>} Rn,op
  操作: Rn ^ op
六、数据处理指令位图
1、 op为寄存器
  xxxx 000a aaas nnnn dddd cccc cttt mmmm
2、 op为立即数
  xxxx 001a aaas nnnn dddd rrrr bbbb bbbb
3、 条件xxxx
  见ARMv4v5v6.pdf P112 
  只有条件为真时才会执行指令,否则忽略该指令,继续下一条
4、 数据处理指令aaaa
  见ARMv4v5v6.pdf P115
5、 条件标志s
  指令含s则该位为 1 ,否则为 0
6、 寄存器Rd,Rn,Rm分别对应dddd,nnnn,mmmm
  R0 ---> R15
  0000 ---> 1111
没有寄存器的则对应位置为 0,如:mov没有Rn寄存器nnnn位为 0000 ,cmp没有Rd寄存器dddd位为 0000
7、 移位数ccccc
  立即数方式: 最大为 0x1f
  寄存器方式: 第 1  - 4 个c为寄存器编号,第 5 个c为 0
8、 移位符ttt
  移位符:      lsl  lsr  asr  ror
  立即数编码:    000    010    100    110
  寄存器编码:    001    011    101    111
  asl 与 lsl 的编码相同
  ttt == 110 && ccccc == 00000 表示 rrx
9、 立即数构造
  立即数 = bbbb bbbb 循环右移  rrrr * 2 次
  这种方式并不能把所有的32位数都构造出来
  如: #12345,#0xffff
七、常量的装载
1、 ldr伪指令
格式: ldr Rd,=<number|label>  //ldr只有带'='号时才是伪指令
说明: 将number或label的值赋给Rd,该指令会伪造成mov、mvn、ldr等可能的指令
举例: ldr r0,=0x100  //mov r0,#0x100
  ldr r0,=0x0  //mvn r0,#0x0
  ldr r0,=0x12345  //ldr r0,[pc,#offset] //offset是该指令到.word的偏移量减去8
    //...
    //.word 0x12345
2、 adr / adrl伪指令
格式: adr Rd,=label
说明: 此指令不能用立即数,其他基本和ldr一样,小范围时用adr,大范围用时adrl
八、乘法指令
1、 mul乘法指令
  格式:  mul{<cond>}{s} Rd,Rm,Rs
  操作:  Rd = Rm * Rs;
2、 mla乘累加指令
  格式: mla{<cond>}{s} Rd,Rm,Rs,Rn
  操作: Rd = Rm * Rs + Rn;
九、分支指令
1、 分支指令
  格式:  b{l}{<cond>} label
  位图:  xxxx 101L jjjj jjjj jjjj jjjj jjjj jjjj
  汇编: code[23:0]  = (offset - 8) >> 2
    //offset是label的地址减去当前指令的地址,正数向前跳,负数向后跳
  code[24]    = L
  code[27:25] = 101
  code[31:28] = <cond>
  操作: if(L == 1)
    lr = pc - 8 + 4;
    //保存下一条指令,当把lr的值赋给pc时可实现函数返回
  pc = pc + (SignExtend_30(code[23:0]) << 2);
  //SignExtend_30表示有符号扩展30位
  ARM指令是按每 4 个字节对齐的(所有指令的地址最低两位都为0
  所以可以先右移 2 位保存,取出值后再左移 2 位
  offset的有效位数是26,共64M,前后各32M
2、 分支切换指令
  格式: bx{<cond>} Rm
  操作: pc = Rm & 0xfffffffc;
  T = Rm[0];
  格式: blx{<cond>} Rm
  操作: lr = pc + 4 - 8;
  pc = Rm & 0xfffffffc;
  T  = Rm[0];
  格式: blx{<cond>} label //label必须为thumb指令函数,只能前后各跳32M
十、单寄存器load-store指令
1、 ldr 32 位数据读取指令
格式:  ldr{<cond>} Rd,addr //addr用法请见内存寻址方式
操作:  val = *(int *)addr;
2、 ldr 16 位数据读取指令
格式:  ldr{<cond>}h Rd,addr
操作:  Rd = *(short *)addr & 0x0000ffff;
3、 ldr 16 位有符号数据读取指令
格式:  ldr{<cond>}sh Rd,addr
操作:  Rd = *(short *)addr & 0x0000ffff;
  if(Rd[15] == 1)
    Rd |= 0xffff0000;
4、 ldr 8 位数据读取指令
格式
:  ldr{<cond>}b Rd,addr
操作:  Rd  = *(char *)addr & 0x000000ff;
5、 ldr 8 位有符号数据读取指令
格式:  ldr{<cond>}b Rd,addr
操作:  Rd = *(char *)addr & 0x000000ff;
  if(Rd[7] == 1)
    Rd |= 0xffffff00;
6、 str 32 位数据写入指令
格式:  str{<cond>} Rd,addr
操作:  *(int *)addr = Rd;
7、 str 16 位数据写入指令
格式:  str{<cond>}h Rd,addr
操作:  *(short *)addr = Rd[15:0];
8、 str 8 位数据写入指令
格式:  str{<cond>}b Rd,addr
操作:  *(char *)addr = Rd[7:0];
注: 没有str有符号数据写入指令
十一、内存寻址方式
1、 label
举例: ldr r0,label
  lebel:
  .word 0x12345
操作: r0 = *(int *)label;
2、 前变址
格式: [Rn,op]
操作: addr = Rn + op;
注: addr只是地址,还没有取值
举例: ldr R0,[R1,#8]
操作: addr = R1 + 8;
  R0  = *(int *)addr;
3、 回写前变址
格式: [Rn,op]!
操作: addr = Rn + op;
  Rn  = Rn + op;
4、 后变址
格式: [Rn],op
操作: addr = Rn;
  Rn  = Rn + op;
5、 ldr、ldrb、str、strb指令的op
立即数: #+/-offset_12
寄存器: +/-Rm
寄存器移位: +/-Rm,<;移位符> #number
注:  #+/-offset_12(与前面的立即数构造方法一样)
  number最大为 0x1f,没有 +/-Rm,<;移位符> Rs方式的寄存器移位
  +号可以省略
  立即数等于 0 可以省略
6、 ldrh、ldrsh、ldrsb、strh指令的op
立即数: #+/-offset_8
寄存器: +/-Rm
注:  offset_8最大为255,没有寄存器移位
7、Rn == pc
说明: 当Rn == pc时,addr = 当前指令的地址 + 8 + op
举例: 0 ldr r0,[pc,#-4]
  4 .word 0x12345
  8
  这两行指令等于 mov r0,#0x12345,但是 0x12345 不能构造成立即数,所以可以通过这种方式来赋值
  我们不能写成 ldr r0,[pc,#4],因为执行这条指令时pc已经执行地址 8 的指令,应该减去 4 而不是加 4
十二、多寄存器方式
1、 ldm / stm
格式: <ldm|stm>{<cond>}{ia|ib|da|db}  Rn{!},R_list 
  //{r0 - r15}^ / {r0,r5,r9,pc},如果顺序不对,编译器会自动按从R0-R15调整
  // ^表示cpsr --> spsr或spsr -->cpsr
操作: if(cond==1) 
  {
  if(ia == 1) 
    //执行后增加, 如果寻址模式为空,默认是ia
    addr = Rn;
  else if(ib == 1) 
    //执行前增加
    addr = Rn + 4;
  else if(da == 1) 
    //执行后减少
    addr = Rn - sizeof(R_list) + 4; 
    //sizeof(R_list) == 寄存器数 * 4
  else //db == 1 
    //执行前减少
    addr = Rn - sizeof(R_list);
  if(ldm == 1) 
    ldm_func(addr,R_list);
  else //stm == 1
    stm_func(addr,R_list);
  if(! == 1)
  {
    if(ia == 1 || ib == 1)
    Rn += sizeof(R_list); 
    else //da == 1 || db == 1
    Rn -= sizeof(R_list);
  }
  }
  void ldm_func(addr,R_list)
  {
  for(i = 0;i <= 14;i++)
  {
    if(R_list[i] == 1)
    R_list[i] = *(int *)addr;
    addr += 4;
}
  if(R_list[15] == 1)
  {
    pc = *(int *)addr & 0xfffffffc;
  }
  if(^ == 1)
  {
    cpsr = spsr;
  }
  }
  void stm_func(addr,R_list)
  {
  for(i = 0;i <= 15;i++)
  {
    if(R_list[i] == 1)
    *(int *)addr = R_list[i];
    addr += 4;
  }
  if(^ == 1)
  {
    spsr = cpsr;
  }
  }
2 、更新基地址的指令对,可实现栈的push和pop操作
  stmia <=======> ldmdb
  stmib <=======> ldmda
  stmda <=======> ldmib
  stmdb <=======> ldmia
  举例:  stmdb sp!,{r1,r2,r3}
    ldmia sp!,{r1,r2,r3}
    执行后 sp,r1,r2,r3 的值保持不变
3、 栈操作
  栈向高地址扩展的为递增栈(A,ascending),向低地址扩展的为递减栈(D,descending)
  栈指针(sp)指向的是有效数据称为满栈(F,full),指向的是无效数据称为空栈(E,empty)
  在ARM-Thumb过程调用标准(ATPCS)中,栈被定义为递减式满栈
  寻址方式 说明 push =stm pop =ldm
  FA 递增满 stmfa stmib ldmfa ldmda 
  FD 递减满 stmfd stmdb ldmfd ldmia //arm汇编的push和pop操作指令
  EA 递增空 stmea stmia ldmea ldmdb 
  ED 递减空 stmed stmda ldmed ldmib
十三、程序状态寄存器指令
1、 mrs
格式: mrs{<cond>} Rd,<cpsr|spsr>
2、 msr
格式: msr{<cond>} <cpsr|spsr>{_fields},<Rm|#number>
fields可以是f、x、s、f的任意组合,表示只对该域操作,默认全部
f 表示标志域 psr[31:24]
s 表示状态域 psr[23:16]
x 表示扩展域 psr[15:8]
c 表示控制域 psr[7:0]

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。