我是靠谱客的博主 老迟到镜子,最近开发中收集的这篇文章主要介绍arm第九天(汇编之指令下),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

ARM七种工作模式

  • 管理
  • 快速中断
  • 中断
  • 中止
  • 未定义
  • 系统
  • 用户

ARM运行状态

  • ARM状态 arm指令 32bit 地址字对齐(整除4),最后两bit为0
  • Thumb状态 thumb指令 16bit 半字对齐,最后1bit为0

ARM寄存器
不同的状态,访问不同的寄存器
R0-R12 共用
R13(SP) 保存栈顶指针
R14(LR) 用来保存返回地址 如,BL,在跳转前要保存下一条指令的地址
R15(PC) 用来保存需要取指指令的地址
取指、解码、执行
CPSR(1个)
模式位 bit[4:0]
运行状态bit[5]
FIQ禁止位 bit[6]
IRQ禁止位 bit[7]
条件标志位bit[31:28] NZCV
SPSR(5个)
ARM支持数据类型:字节、字、半字
ARM内存对齐方式:字、半字、字节

跳转指令

  • B 可以用来构建循环执行
    相对跳转 PC(new) = PC(当前) +/- 偏移(32MB) 无返回
  • BL 常用来调用子函数
    相对跳转 PC(new) = PC(当前) +/- 偏移(32MB) 带返回
    CPU会自动将下一条指令地址保存到LR
    MOV PC,LR
  • BX
    绝对跳转
    CPSR[5] = Rm[0]
    PC = Rm & 0xFFFFFFFE
  • BLX

数据处理指令

案例:

ADD R0,R1,#1 //R0 = R1+1
MOVEEQ R0R2 //条件执行,在EQ(相等)条件下,招行R0 = R2
SUBEQS R0,R1,R2,LSR #1  // 在EQ条件下,R0 = R1-(R2>>1)
CMP R1,R3 //R1-R3

数据处理指令可细分为4类:
数据传送指令(MOV,MVN)
算术运算指令(ADD,ADC,SUB,SBC,RSB)
位运算指令(AND,ORR,EOR,BIC)
比较指令(CMP,CMN,TST,TEQ)
数据处理指令的一般格式:
{cond} {S} ,,
:指令操作码,如ADD,MOV,SUB
{cond}:条件,如EQ
{S} :分两种情况
1,当目标寄存器不是PC时,根据目标寄存器的结果影响CPSR的NACV条件标志位

 MOVS R0,R2  //因为MOV加了S,根据R0的结果影响NZCV

2,当目标寄存器是PC时,CPSR = SPSR,如:

 MOVS PC,LR  //PC=LR,CPSR = SPSR 异常的返回; 这种指令只能在异常模式下面使用

:目标寄存器
:第一源操作数,只能是寄存器
:第二源操作数
1,立即数:相当于常量,直接出现在指令中,不用存储在寄存器或存储器,如:

  ADD R0,R1,#3

2,寄存器,如

ADD R0,R1R2 //R0 = R1 + R2

3,寄存器移位,把寄存器里面的值进行不到的移位操作得到的结果。
3.1 ,LSL #,如

ADD R0,R1,R2 LSL #1  //R0 = R1 + (R2 << 1) ,逻辑左移,空出低位用0填充

3.2 ,LSL ,如

ADD R0,R1,R2 R2 LSL R3  //逻辑左移,空出低位用1填充,左移的位数放在了R3寄存器中

3.3 ,LSR #,如

ADD R0,R1,R2 LSR #1  //R0 = R1 + (R2 >> 1) ,逻辑右移,空出的高位用0填充

3.4 ,LSR ,右移的位数放在了寄存器中
3.5 ,ASR #
这里写图片描述
3.6 ,ASR
3.7 ,ROR #
这里写图片描述
ROR循环右移:移出的低位用于填充空出的高位
3.8 ,ROR
3.9 RRX
这里写图片描述
RRX:扩展右移一位,将Rm寄存器的内容右移一位,空出的高位用原来的CPSR的位填充,
并Rm移出的1位填充CPSR的C位,
只有RRX,不需要指定移位位数

第一源操作数:寄存器
第二源操作数(11种):
1,立即数
2,寄存器
3,寄存器移位
1, lSL #shift
2,LSL Rs 空出的低位用0填充
3,LSR #shift
4,LSR Rs 空出的高位用0填充
5,ASR #shift
6,ASR Rs 空出的高位用符号位填充
7,ROR #shift
8,ROR Rs 循环右移,将移出的低位填充空出的高位
9, RRX 扩展右移一位

立即数(8位图立即数):
CMP R1,#0x4800000
32 bit 机器码
这里写图片描述

这里写图片描述
找到8bit立即数,然后将8bit立即数扩展为32bit,再将32bit的数采用循环右移的方式进行移位,移2X(rot)次,最终得到0x04800000,将0x12扩展成32bit,高位全用0填充,然后将循环右移2*5 = 10 次
0x04800000
5 0x12 循环右移10次
6 0x48 循环右移12次
如果找到的组合不唯一,取循环次数少的那组。

合法立即数据的判定?
1,0-0xFF 就是立即数
MOV R1,0x55 //R1 = 0x55
2,立即数中1的个数大于8的一定不合法
3,从两个方向看1的间距大于8,非法


数据传送指令
指令格式
MOV{cond}{S} ,
shifter_operand:立即数(8位图)、寄存器、寄存器移位
示例:

MOVEQ R0,#0x80 //在EQ的条件下,R0=0x80
MOVS PC,LR   //由于MOV后有S,PC = LR,CPSR= SPSR,用于异常返回;只能在异常模式下使用
MOVEQS R0,R1 //在EQ条件下,后面加有S,根据R0的结果去影响CPSR的NZCV,
             //后面几行代码是具体影响方法
     N = R0[31] //符号位
     Z = if R0 == 0 then 1 else 0 //为正?为负?
     C = shifter_carry_out,
     不影响V,V代表溢出
MOV R3,R4,LSL #2  //R3 = (R4 << 2)
MOV R0,R0  //R0 = R0
MOV PC,R14  //PC = R14

NZCV:
N:运算结果为负值的标志位
N= 1,负值
N= 0,正值
Z:运算结果为0的标志位
Z=1,结果为0
Z= 0,结果为非0

数据反传送指令:MVN
指令格式:
MVN{cond}{S} ,
功能:Rd = 按位取反
shifter_operand:立即数(8位图),寄存器,寄存器移位
示例:

MOVNEQ R0,#0x80 //EQ,将0x80按位取反  R0 = 0xFFFFFF7F
MVNS PC,R0 //后面加了S,PC = R0按位取反,CPSR = SPSR ;异常返回
MVNEQS R0,R1 //EQ,R0 = R1按位取反,R0影响CPSR的NZCV
    N = Rd[31]
    Z = if Rd ==0 then 1 else 0
    C = shifter_carry_cout
    不影响V
MVN R3,R4,LSL #2 // R3=(R4<<2)

案例:

MOV R0#0x00 //R0 =0x00
MVN R0#0x00 //R0 =0xFFFF,FFFF
MOV R0,#0xffffff00  //非法立即数 
MVN R0,#0x000000FF //R0 = 0xffffff00

小结:
MOV
MVN
这两指令没有第一源操作数
S:
PC,CPSR= SPSR
不是PC,根据结果影响CPSR的NZCV


算术指令
加法指令:ADD
指令格式
ADD{cond}{S} ,,
功能:Rd = Rn +
示例 :

ADD R0,R1#0x80  //
ADDEQ R0,R1,R3
ADDS R0,R1,R2  //R0 = R1+R2根据R0结果影响NZVC
    N = Rd[31]
    Z = if Rd ==0 then 1 else 0
    C = CarryFrom(Rn+shifter_operand)
    V = OVerflowFrom(Rn+shifter_operand)
ADDS PC,R1,#0 //PC = R1,CPSR = SPSR

NZCV:
N:运算结果为负值的标志位
N= 1,负值
N= 0,正值
Z:运算结果为0的标志位
Z=1,结果为0
Z= 0,结果为非0
C:运算结果产生进位的标志位
进位针对无符号数来说
C= 0 ,有进位
C = 1,无进位
V:运算结果产生溢出的标志位
溢出针对有符号数来说
V= 0 ,有溢出
V = 1,无溢出

8bit
有符号:-128~127
无符号:0~255

3AH + 7CH = B6H //H表示十进制
无符号:58+124 = 182,没有超过255,无进位
有符号:58+124 = 182,超过了127,溢出

AAH + 7CH = B6H //H表示十进制
无符号:170+124 = 294,没有超过255,有进位
有符号:-86+124 = 38,没有超过了127,无溢出

带进位的加法:ADC
指令格式:
ADC{cond}{S} ,,
功能: Rd = Rn + + C位
示例 :

ADC R0,R1,#0x80 //R0 = R1+0x80+C
ADCEQ R0,R1,R3  //EQ,R0 = ..
ADCS R0,R1,R3 //R0 = Ra+R2+C,根据R0的值,影响CPSR的NZCV
    Z = if Rd ==0 then 1 else 0
    C = CarryFrom(Rn+shifter_operand + C Flag)
    V = OverflowFrom(Rn+shifter_operand + C Flag)
ADCS PC,R1,#0 //

案例:

加数(R1,R0)
被加数(R3,R2)
结果(R1,R0)
前者为高32位,后者为低32位

减法指令
指令格式:
SUB{cond}{S} ,,
示例:

SUB R0R1,#0x80 //R0 = R1-0x80
SUBNE R0,R1,R3 //NE,R0 = R1-R3
SUBS R0,R1,R2 //R0 = R1-R2,根据结果R0影响CPSR的NZCV
    Z = if Rd ==0 then 1 else 0
    C = NOT BorrowFrom(Rn-shifter_operand)
    //有借位 C= 0
    //无借位 C= 1
    V = OverflowFrom(Rn-shifter_operand )
SUBS PC,R14,#04 //PC = R14 - 4,CPSR = SPSR     异常返回

案例:

SUB R0,R1,R2
SUB R0,R1,#256
SUB R0,R2,R3,LSL #1 //R0 = R2 - (R3<<1);
SUBS R0,R1,R3  //R1 = R2-R3,影响NZCV
SUBS PC,R14,#4  //同上

带借位的减法指令:SBC
指令格式:
SBC{S} ,,
功能: Rd= Rn- shifter_operand - Not C
示例:

SBC R0,R1,#0x80  //R0 = R1 - 0x80 - Not C
SBCNE  R0,R1,R3 //NE,R0 = R1 - R3 - Not C
SBCS R0,R1,R2//R0 = R1-R2,根据结果R0影响CPSR的NZCV
    Z = if Rd ==0 then 1 else 0
    C = NOT BorrowFrom(Rn-shifter_operand - NOT(C Flag))
    V = OverflowFrom(Rn-shifter_operand -  NOT(C Flag))
SBCS PC,R1#0

反向减法指令:RSB
指令格式:
RSB{S} ,,
功能:
Rd = - Rn
示例:

RSB R0,R1,0x80 // r0= 0x80-r2
RSBNE R0,R1,R3 //NE,R0= R3-R1
RSBS R0,R1,R2 //R0= R2-R1,影响NZCV
....
PSBS PC,R1,#0
RSB R3,R1,#0 //R3 = -R1;
RSBS R1,R2,R2,LSL #2 //R1=(R2<<2)-R2 = R2*3,用反向减法实现乘法
//会影响标志位

带借位的反向减法指令:RSC
指令格式:
RSC{S} ,,
功能:Rd= shifter_operand - Rn - Not C
示例:

RSC R0,R1,#0x80 //R0 = 0x80 - R1- Not C
RSCNE  R0,R1,R3  //R0 = R3 - R1- Not C
RSCS R0,R1,R3 //R0 = R3 - R1- Not C,由于加了S,会根据结果影响NZCV
N=Rd[31]
  Z = ...
  C = NOT BrowwFrom(shifter_operand - Rn - Not(C Flag))
  ...

总结
算术运算:
ADD 加法 Rd = Rn + C = 1 进位,C= 0无进位
ADC 带进位的加法 Rd = Rn + +C 位
SUB 减法 Rd = Rn - , C = 0 有借位,C= 1无借位
SBC 带借位的减法 Rd = Rn - - Not C
RSB 反向减法 Rd = - Rn
RSC带借位的反向减法 Rd = - Rn - Not C
如果指令代码加了S:
1),目标PC,CPSR = SPSR 异常返回,只能在异常模式下进行
2),不是PC,根据Rd结果,影响CPSR的NZCV

位运算指令(与、或、异或、位清除)
位与指令:AND
指令格式:
AND{S} ,,
指令功能:
Rd = Rn &

AND R0,R1,0x80 //R0=R1&0x80,把R1的bit7保留,其余清0
ANDNE R0,R1,R3//NE ,R0=R1&R3
ANDS R0,R1,R3  //R0=R1&R3,由于后面加了S,会根据R0的结果影响NZCV
    N = Rd[31]
    Z= if Rd == 0 ..
    C = shifter_carry_out
ANDS PC,R1,#0xFFFFFFFF
ANDS R0,R1,R2,LSL #1 //R0 = R1& (R2<<1)

按位操作实现什么目的?
&1 用来判断某位是1还是0
&0 清除某一位

位与指令:ORR
指令格式:
ORR{S} ,,
指令功能:
Rd = Rn |

ORR R0,R1,#0x80//将R1的bit[7]置1
ORRNE R0,R1,R3
ORRS R0,R1,R2
   N = Rd[31]
   Z = if Rd  == 0 then 1 else 0
   C = shifter_carry_out
OPRS PC,R1,#0x0 //异常返回
ORR  R0R1,R2,LSL #2

用或操作实现什么?
|1 ,将某位置1
|0,保留某些位,保持不变

异或指令:EOR
指令格式:
EOR{S} ,,
指令功能:
Rd = Rn^
这里写图片描述
示例

EOR R0,R1,#0x80
...

用异或操作实现什么?
相同 为 0
不同 为1
1,判断两个数是否相等
EORS R3,R1,R2
R1^R2 == 0 表示 R1==R2
2,把一个变量的某位与1做异或可以实现取反
0 ^ 1 = 1
1^ 1 = 0
3,加密
0x80^ 0xFF = 1000,0000^ 1111,1111 = 0111,1111 = 0x7F
0x7F^ 0xFF = 0111,11111 = 1111,1111 = 1000,0000=0x80
这里的0xFF就相当一个密钥

EOR R0,R0#FF
EOR R0,R0,R1
EORS  R0,R0,#1

位清除指令:BIC
指令格式:
BIC{S} ,,
指令功能:
Rd = Rn&(~ )

BIC R0,R1,#0x80 // Ro = R1&(~0x80) 
BICEQ R0,R1,R3
BICS R0,R1,R2
   N = Rd[31]
   Z = if Rd == 0 then 1 else 0
   C = shifter_carry_out
BICS  PC,R1,#0x0

总结
位运算
AND
ORR
EOR
BIC
有无S:

比较测试指令
比较指令:CMP
指令格式:
CMP ,
指令功能:
Rn -

CMP  R0,#0x80  
CMPEQ R0,R3
   N = ALU_OUT[31]
   Z = if ALU_out == 0 then 1 else 0
   C = NOT BorrowFrom(Rn - shifter_operand)
   V = OverflowFrom(Rn - shifter_operand)
cmp R1,#10 //R1-10 == 0 ,r1=10 z = 1
moveq r0,r1
bleq test  //这里的eq,用的cmp R1,#10里面的影响结果
  ...
test:
  ...

负值比较指令:CMN
指令格式:
CMN ,
指令功能:
Rn +

   CMN R0,#0x80 // R0 + 0x80
   CMN R1,#5 // R0 + 5 == 0 ? Z = 1
   BEQ SKIP //根据Z判断是否为相等
   ....
SKIP:
   ...

位测试指令:TST
指令格式:
TST ,
指令功能:
Rn &
会自动影响NZCV,后面的指令可以根据NZCV来判断结果

TST R0,#0x80
TESTEQ R0,R3
   N = ALU_out[31]
   Z = if ALU_out == 0 ...
   C shifter_carray_out
TXT R0,#0x1
ADDEQ R1,R2,R3
TST R1,R4

相等测试指令:TEQ
指令格式:
TEQ ,
指令功能:
Rn ^

TEQ R0,#0x80
TEQNE R0,R3
   N = ALU_out[31]
   Z = if ALU_out == 0  then 1 else 0
   C shifter_carray_out

比较
CMP -
CMN +
TST &
TEQ ^

总结
数据处理指令

  • 数据传送(MOV,MVN)
  • 算术运算(ADD ADC SUB RSB RSC)
    进位:C=1 有进位,C = 0,无进位
    借位:C=0 有借位,C =1,无借位
  • 位运算(AND ORR EOR BIC)
  • 比较测试(CMP CMN TST TEQ)

一般格式:
操作码{条件码}S 目标寄存器,第一源操作数,第二源操作数

  1. 第一源操作数只能是寄存器
  2. 第二源操作数
    1,8位图立即数据 (#rot ,8bit立即数,ROR)
    2,寄存器
    3,寄存器移位(LSL,LSR,ASR,ROR,RRX)

  3. .S
    1,目标寄存器为pC, CPSR = SPSR
    2,目标寄存器不是PC,根据运算结果影响CPSR的NZCV

  4. 数据传送指令没有第一源操作数

  5. 比较测试指令没有目标寄存器,不加S,结果默认影响NZCV

案例
用汇编指令实现1-10累加和计算。
思路:用B跳转指令构建循环,用比较指令跳出循环,用加法指令累加。

sum.s

    .text
    .global _start
    .global sum
    .code 32

 _start:
    mov r0,#10 //保存循环次数
    mov r1,#0 //用来保存结果 
 sum:
    add r1,r1,r0 //r1 = r1+r0
    subs r0,r0,#1 // r0--
    cmp r0,#0//比较R0 与 0 ,如果不等于0,Z位就会为0
    bne sum //条件跳转,ne表示不等于,则执行sum,如果相等,执行 b  .
 sum_ok:
    b .
    .end

编译、链接、调试

arm-linux-as -g -o sum.o sum.s
arm-linux-ld -e _start,-o,sum sum.o
qemu-arm -g 1234 sum #调试

用汇编指令求最大公约数?
20 12 8 = 20-12 4 = 12-8
5*4 4*3 4*2 4*1
R0=20,R1=12
while(R0!=R1){
if(R0>=R1){//cmp 默认影响NZCV
//beq
R0 = R0 - R1;//subcs
}else{
R1= R1-R0;//subcc
//b
}
}

gcd.s

    .text
    .code 32
    .global gcd //标号
gcd:
    //把两个数先放到寄存器
    mov r0,#20 
    mov r1,#12 
loop:
    cmp r0,r1 
    //以下几行相当于if .. else if ..
    beq boop_end
    subcs  r0,r0,r1 //如果cmp r0,r1条件满足,执行本条;cs 表示无符号大于或等于
    subcc  r1,r1,r0 //如果cmp r0,r1条件不满足,执行本条
    b loop //无条件跳转
 boop_end:
    b   .
    .end   

最后

以上就是老迟到镜子为你收集整理的arm第九天(汇编之指令下)的全部内容,希望文章能够帮你解决arm第九天(汇编之指令下)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(42)

评论列表共有 0 条评论

立即
投稿
返回
顶部