找回密码
 注册
关于网站域名变更的通知
查看: 306|回复: 1
打印 上一主题 下一主题

ARM的BX指令

[复制链接]
  • TA的每日心情

    2019-11-20 15:22
  • 签到天数: 2 天

    [LV.1]初来乍到

    跳转到指定楼层
    1#
    发表于 2020-10-14 15:37 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

    EDA365欢迎您登录!

    您需要 登录 才可以下载或查看,没有帐号?注册

    x
    bx的语法格式% _/ d. m. @5 C3 ~
    BX{<cond>} <Rm>
    - n4 u# S1 ^. V5 M7 y<cond>为指令执行的条件码。当<cond>忽略时指令为无条件执行。
    * i% R3 W+ W6 s6 L<Rm>该寄存器中为跳转的目标地址。当<Rm>寄存器的bit[0]为0时,目标地址处的指令为ARM指令;
    8 x; |- G  P: S) n( E" d当<Rm>寄存器的bit[0]为1时,目标地址处的指令为Thumb指令。
    4 O/ I+ n/ F6 O" `$ a
    6 i  r: H. I# v( @看一段简单程序的片段,不用考虑省略号的内容,看框架就可以了!
    . R# B0 `7 O) Z9 k, L; Z1 w3 J8 M5 k
        EXPORT LEDTEST# n" N* a! b, Q8 |
        AREA testasm,CODE,READONLY
    : L# J3 V5 m' W) {% i' S/ O    CODE32( [* J* r, d: T! E/ r
    LEDTEST9 Z9 U6 g2 F, ]9 q% a" ~7 H0 o
        。。。。。。。。。。; y# ]+ x1 _5 I! U+ o7 h; A* |! J
        。。。。。。。。。
    ) |! Y! }  I' J' M    。。。。。。。。
    . B% p0 y  x3 e; x4 C" ]1 n) ?    。。。。。。。。。! [( R  ?1 Z8 U3 C
        。。。。。。。。。。。6 \3 \6 ]  d% e' ]1 s5 I$ `# S
    ledon) M$ S5 A! l/ q, x  d
        ...............  `+ w, q- J6 h: L
        ...............% A4 R' g7 c  x# D* |3 X
        。。。。。。。。。。。。; }* @* T$ r1 @& Q" ]9 ]
        。。。。。。。。。。。: k8 m/ r% P+ M. G; ~) m  g9 k/ A
        adr r4,delay1+1
    4 `) P6 x! k4 k, [$ P% [. }1 j    bx r43 K9 g& x2 `! U' P7 M
    - i- Q. l( D) L2 n# O) P* M
    ledoff
    ; p& U/ V+ p  Q* V/ r7 p, y    ..............
    % O" u4 c: T) G- q1 ~, f3 K1 d/ ?    ...............
    . x$ V# e- g2 ~+ \9 O    .............
    $ N9 g9 d/ n9 \4 I    .............; I0 L+ ?- l- b( b* g% O
        .............7 `  w" n1 I  l
        ..............4 h& U. q: _& A- x1 D* g2 I! |

    8 w# M( K8 [5 f  a5 ~    AREA testasm,CODE,READONLY) W- I* V8 z& G! ^; Y0 U
        CODE16
    ) ^3 `! z( |' k7 X1 W% P. {8 k" gdelay1
    - _6 i# T$ `* W4 W& w0 d    ............
    + q% h5 \, [* L% a4 o    ...........+ g5 G: N8 Z+ @! @. B; v
        ............./ f+ ]4 I+ r! ]
        ldr r1,=ledoff
    , S: B. b6 y- g- X, ]  r  G# I6 }    bx   r15 b3 \0 r9 r1 r! V* i0 w
        ........
    9 y- ^. r2 T+ e' w6 t5 S! @    .............% I) _# L! K; O( _) J
        .............) }5 y+ \( J) o5 ^% K
    END0 t: [* Z$ ^/ i6 x0 U

    ) `0 g" L8 x* w8 M+ s: l" w; L) g关于delay1+1:8 X/ G* _% o3 T* N0 }5 q- Y
    ARM指令是字对齐(指令的地址后两位为[1:0]=0b00),Thumb是半字对齐(指令的地址后两位为[1:0]=0bx0,x为0或1)。指令的地址的最后一位必为0。/ W* Z3 P) D, E( b. V) W* r1 A
    因此bx(不管往ARM还是往Thumb跳转)的跳转指令必须保证指令地址的最后一位为0,上例中bx指令能自' w% R. T3 @" j5 s
    动地将目标地址值置为r4的值和0xFFFFFFFE的与后的结果,就会使指令地址的最后一位必为0了。
    . x# w8 T% m: a0 l3 v. `那么delay+1的这个1不就被0与掉了么,就没有什么作用了?其实,在执行bx指令时,它是首先判6 d* z2 H4 Q9 H% Z( H
    断指令地址的后一位为0或1(这样就知道要跳转的地方是ARM还是Thumb指令。0跳转arm,1跳转thumb。),然后再PC=r4 AND 0xFFFFFFFE。这样,当我们需要要跳转到Thumb指令处执行时,必须将指令地址的最后以为置1。
    5 I& V  A9 t" G而bx再跳转到ARM指令时就无需考虑这些了,就不用像上面的+1处理了。
    " a9 Z1 H+ |  b$ v! c7 K1 w
    关于字对齐和半字对齐
    什么叫对齐。如果一个数据是从偶地址开始的连续存储,那么它就是半字对齐,否则就是非半字对齐;半字对齐的特征是bit0=0,其他位为任意值。字对齐的特征是bit1=0,bit0=0 其他位为任意值。如果一个数据是以能被4 整除的地址开始的连续存储,那么它就是字对齐,否则就是非字对齐。举例说明四字节对齐: 对内存进行操作时,被访问的地址必须为4的倍数。如果分配到的地址不是4的倍数时,CPU实际访问的地址还是按照字对齐的方式来操作。也就是自动屏蔽bit1和bit0.   
    ( l0 y: l" ~% [  F+ t2 c
    什么是对齐,以及为什么要对齐:
      m8 b9 m5 R) `) ?+ i       现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就需要各类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
    对齐的作用和原因:
            各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出,而如果存放在奇地址开始的地方,就可能会需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该int数据。显然在读取效率上下降很多。这也是空间和时间的博弈。, ?6 ]' g5 n5 S
    对齐的实现+ {0 x7 m$ o0 B) |- v: T
           通常,写程序的时候,不需要考虑对齐问题。编译器会替我们选择适合目标平台的对齐策略。可以给编译器传递预编译指令而改变对指定数据的对齐方法。
           char   a                       char   a
           short b                        char    c
           char   c                        short b
         int      d                         int     d
    ARM体系中对齐的使用:+ s1 \# `8 P- ~0 ^! H1 R
    1.__align(num)
    ( R7 ?. T* E7 ~5 ^7 M           这个用于修改最高级别对象的字节边界。在汇编中使用LDRD或者STRD时
    / e4 Y$ O/ g/ h) O$ u! L   就要用到此命令__align(8)进行修饰限制。来保证数据对象是相应对齐。/ Y) ~$ `5 S% n! w: F; i
       这个修饰对象的命令最大是8个字节限制,可以让2字节的对象进行4字节# I+ i0 V8 [' L( S! x
       对齐,但是不能让4字节的对象2字节对齐。
    & n' r/ M) t( h7 r2 J4 z" }   __align是存储类修改,他只修饰最高级类型对象不能用于结构或者函数对象。& Q! k: u: l. ?9 K! f( y$ q0 o5 ^9 y
       
    * B: M1 L0 V4 r* y% M# _2.__packed
    & h) h4 g8 l2 G4 W" ?- Q__packed是进行一字节对齐4 I- M: ^  u, Y# g1 C7 Y
    (1).不能对packed的对象进行对齐
    - m9 H! f2 w! y(2).所有对象的读写访问都进行非对齐访问2 t. }% w; o4 u" s- i5 d, {
    (3).float及包含float的结构联合及未用__packed的对象将不能字节对齐
    5 b! x; l# h' Z, I  i' p; C(4).__packed对局部整形变量无影响
    $ S2 W$ }/ Q/ F# a  _( E- H(5).强制由unpacked对象向packed对象转化是未定义,整形指针可以合法定义为packed。
    5 w8 R, w3 z. f1 |6 I     __packed int* p; //__packed int 则没有意义  _/ H/ j7 U8 K# s& K- v
    (6).对齐或非对齐读写访问带来问题8 s% K' b0 A: U# K) x' z  e! i9 W6 f
    _packed struct STRUCT_TEST, F- B  s# n! g/ U1 |& G6 ~
    {6 a1 Q. B% y7 P% ~
       char a;
    " S  b$ _5 N! a( Q1 W6 ?   int b;
    ( Q" s5 S6 H+ M0 I9 y& s   char c;' A4 q. ~5 Z! a- T' M
    } ;      //定义如下结构此时b的起始地址一定是不对齐的
    & N& a" J2 H- ^( A         //在栈中访问b可能有问题,因为栈上数据肯定是对齐访问[from CL]
      m6 `+ s  O4 A7 y8 D. h& M8 D//将下面变量定义成全局静态不在栈上
    3 G- Z& V$ Z+ a1 L( h: _4 @static char* p;) t4 V' }/ V0 s1 M7 }
    static struct STRUCT_TEST a;
    ; p3 x7 o( j$ Q  }+ {void Main()
    " m9 l0 U! g0 ]! k3 Y( g{
    2 d4 Z* D5 x, ?. s9 M4 Z         __packed int* q;   //此时定义成__packed来修饰当前q指向为非对齐的数据地址下面的访问则可以
              p = (char*)&a;         
    7 ?4 N6 ~0 N3 l) u          q = (int*)(p+1);      9 W4 `. _$ |) ~5 X6 D
       /*1 |" h9 T+ ^$ I, Y& C
             *q = 0x87654321;: @$ }  k- p' ~
              ldr      r5,0x20001590 ; = #0x123456785 ~/ d$ i  A# a- B
             [0xe1a00005]   mov      r0,r5
    " A( v" ~) ^# O0 q. ?8 z) o          [0xeb0000b0]   bl       __rt_uwrite4   //在此处调用一个写4byte的操作函数
    1 G$ v4 |, t! Z# P, G      + V& Q' `7 M. U  ]  Y  n+ @4 o
             [0xe5c10000]   strb     r0,[r1,#0]    //函数进行4次strb操作然后返回保证了数据正确的访问8 Y" z* }# a2 E
             [0xe1a02420]   mov      r2,r0,lsr #8' {3 L1 p# e+ C$ y1 x; M3 A
             [0xe5c12001]   strb     r2,[r1,#1]$ U! e* W& e9 \" F. A$ M1 Q
             [0xe1a02820]   mov      r2,r0,lsr #16& d: F. H) \& ?- n
             [0xe5c12002]   strb     r2,[r1,#2]- j7 J  ~8 q5 A
             [0xe1a02c20]   mov      r2,r0,lsr #24( G" S# C) J" }; @4 _
             [0xe5c12003]   strb     r2,[r1,#3]3 Z" b( Y/ S) d$ m
             [0xe1a0f00e]   mov      pc,r145 P9 }. R" p! Y- w' A: S
    */
    /*8 d4 m* Q! W8 u, c4 g3 m
             //如果q没有加__packed修饰则汇编出来指令是这样直接会导致奇地址处访问失败! A3 ]! @' p3 V3 o
              [0xe59f2018]   ldr      r2,0x20001594 ; = #0x87654321
    - d3 e: B( n; O; b" F         [0xe5812000]   str      r2,[r1,#0]
    0 w& N3 O6 [5 u3 N: e5 S* r*/
             //这样可以很清楚的看到非对齐访问是如何产生错误的" a* z5 `$ d6 e' Q! }& }" B6 _
              //以及如何消除非对齐访问带来问题
    & o7 m& S# U* K6 v: N         //也可以看到非对齐访问和对齐访问的指令差异导致效率问题
    9 m# s+ u$ A6 R: h6 e9 N
    # x- J* p' ]- J
    先看一个小程序# G4 K. R* ~0 F3 ~
    #include<stdio.h>' t3 [$ y$ t  \/ m# ^3 {+ V1 Q
    struct t1
    & y! K1 i; f. g0 a; y" Q{3 B- I( f" y, j3 Y
        char a;
    : i+ L' O0 N4 {. J4 o7 M    int b;: U! D( f! A# Y
        char c;: w6 [" A3 u  U0 e) w
    };
    struct t2% U( d' g1 i$ N+ I# {! x
    {
    # d5 t5 I' y! c& `  C# k* V6 T, G    char a;
    9 e1 J  b7 W  A: R! X9 N0 |    char b;. E' O0 ]$ c4 K+ m& A8 M
        int c;. }  G& r! N- J9 U
    };
    void main()
      n  Q2 n5 c9 s+ f0 F{
    ! H$ E; o" |  D; U$ H3 l8 H: I$ B$ b    printf("%d/n",sizeof(struct t1));
    # c" G# R- T6 ?    printf("%d",sizeof(struct t2));
    : |6 w5 @0 a8 l, W( E}
    按一般来说,结构体t1和t2的大小应该是一样的,但在很多编译器中,显示出的结果是不一样的,如我用的VS 2010,t1的大小为12字节,t2 为8字节,为什么会出现这样的结果呢?这其中有个东西就叫字对齐。
    8 m8 G& S0 ^) r6 q据说很多公司在面试中都考过这个问题,有一年威盛也考过。。。1 C$ F5 I2 p4 R( ~) J
    字对齐,是昨天在看ARM的时候看到的,一般是4个字节为一个单位进行对齐,怎样对齐的呢?有点像平时我们写文档用的右对齐之类的,不一样的就是,句子可以换行写,但一个变量的存储,不是那种本身很长的,一般是不换行的,例如上面的例子,将空的内存看作
    ! L! ]; U" p/ G2 ?: loooo
    2 W9 ~+ f) B4 A, |/ xoooo" Z. b# p5 _/ t# J7 h  I
    oooo9 l) X( n3 g! |' E: o! d8 t
    t1中为a分配内存,用一个字节,内存变为
    7 z: t3 \% q5 L4 i" c- Txooo
    ' s! _" p8 ]5 P  K: q- C8 Y% Moooo& D3 ]# y. ]0 Q& V( P
    oooo
    + m( x: B3 ]9 V" c; ^) K然后进行b的内存分配,在VS中,int占4个字节,但第一行只有3个字节空的了,所以就换行对齐,内存变为
      I) a9 T1 T* W" i2 T; hxooo4 |0 x& C6 v. d7 t
    xxxx
    & H7 P# M' \" {& s% J- coooo
    - Y- i. D9 p5 W1 {1 ?9 s( G* n最后为c分配,变成6 N  `7 D# w/ M
    xooo5 A, U6 O/ c' N% i
    xxxx
    : y& r8 `+ [, J5 _( v# Mxooo
    ! _/ A0 K, J$ z总共占用内存就是每行4个字节x3行=12字节。
    0 b; G' G2 }. A9 K$ ~  i在t2中! ^$ s2 ?! x4 j& `# @+ u* e
    内存同样为
    2 F* W! N1 \- r) A# S; y1 ]& Poooo( g* _4 \$ x. g% C/ k# y8 }4 Q& |
    oooo+ F7 ~) G; U& x7 U
    oooo
    4 S; |# t+ v; D为a分配时,同样是
    8 F* ^/ o4 t4 j4 i! B. vxooo
    6 F5 ~8 B" H/ y: I; }" y为b分配时,由于第一行的长度能满足b所以b还是在第一行,内存为
    ! ?, M) T7 U- w( Rxxoo  X; N- h8 _0 Y; |) a( S. W& x+ J; Q( q
    oooo
    + N( |7 R" r  X) w3 B) D- H0 O  B: y8 ooooo
    - t$ ]3 _% m2 b7 ]/ V9 f3 M为c分配时,第一行已经不够,就换到第二行,
    2 |" [: Z" Y& f" y" }. i! {$ O4 O% wxxoo0 X, B  s/ w1 }5 h" d# R$ N* L0 {/ u
    xxxx7 q( {/ j" f( X) l
    oooo
    0 J1 U* B, s& i9 s这样第三行就没用到,所占的内存就是每行4个字节x2行=8个字节。
    这就是我所理解的字对齐,至于为什么,我想,应该是方便32位机操作吧,32位刚好就是4个字节,这样可以尽量使数据的读取用一次总线操作就完成,int是4个字节,32位,如果不用字对齐,读取一个int,可能第一次就只能读出高位那部分,然后通过移位将数据的高位移到寄存器的高位,再读出低位,再对第二次读取的高位屏蔽,再进行或操作,效率不高。同样,类似于char型和short型数据,读取了后还是会进行寄存器移位操作,这一点可以通过编译生成的汇编语句得到证实,这也就是为什么在某些系统,如嵌入式系统中,char的效率不如int高的原因了。
    $ Z- N6 U6 _0 v/ r% h4 N- b7 O另外,半字对齐和字对齐差不多,只是它的单位是2个字节吧,因为有的嵌入式系统是16位的。
    2 D7 H# f0 M; ~/ A" g

    该用户从未签到

    2#
    发表于 2020-10-14 16:07 | 只看该作者
    ARM的BX指令
    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    关闭

    推荐内容上一条 /1 下一条

    EDA365公众号

    关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

    GMT+8, 2025-11-24 20:40 , Processed in 0.171875 second(s), 23 queries , Gzip On.

    深圳市墨知创新科技有限公司

    地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

    快速回复 返回顶部 返回列表