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

ARM的BX指令

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

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

    [LV.1]初来乍到

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

    EDA365欢迎您登录!

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

    x
    bx的语法格式
    % D$ o' {9 K8 t' h' G' a$ xBX{<cond>} <Rm>
    % `% f' e: y* \% ~* v<cond>为指令执行的条件码。当<cond>忽略时指令为无条件执行。$ g$ u" J1 R! B* J3 F: b2 D0 r- a
    <Rm>该寄存器中为跳转的目标地址。当<Rm>寄存器的bit[0]为0时,目标地址处的指令为ARM指令;; b1 Q; s) @% k1 L0 ]+ K" C' L3 K2 V
    当<Rm>寄存器的bit[0]为1时,目标地址处的指令为Thumb指令。: V# H. j3 \* ~+ T8 y9 |  [

    $ u3 }+ l$ |* q1 D- @. p看一段简单程序的片段,不用考虑省略号的内容,看框架就可以了!
    - `% a& E" t- n4 T) T$ ]
    5 u- F& m/ M7 S7 j$ v    EXPORT LEDTEST
    ) ]: f' G; x( z# W    AREA testasm,CODE,READONLY4 S; Z9 ?- N. B; |; Y$ {9 `
        CODE32
    7 [& e3 |3 r. ~LEDTEST
    4 L0 E: Q' r2 q    。。。。。。。。。。
    & I% O8 G5 @3 B+ }9 E2 B; o* R    。。。。。。。。。
    / y. ]& i0 f) ]0 Q$ {    。。。。。。。。2 d, ?7 n5 Z; e! x
        。。。。。。。。。
    6 G  j9 r9 X6 s" {7 J- |    。。。。。。。。。。。
      z4 E( v, i9 j; R6 Oledon
    ( y, S3 O) d( I# U. L3 X+ ^    ...............
    + Z6 ~8 d, y$ T8 ~2 K    ...............
    ) u# v3 b$ T9 F8 l8 O/ @8 I. l    。。。。。。。。。。。。/ E+ ]. Y3 [$ @% E0 Y# i
        。。。。。。。。。。。
    + O( j. ?+ b( u1 k( r7 a    adr r4,delay1+1
    " i( V: o& n: y. d6 B    bx r4( m$ H0 a' ]' {4 `. G0 f6 x! w

    2 I0 v, r* R; O8 X  F  C! s6 Qledoff
    6 Y4 {. O! o: ~    ..............
    & \/ E* n9 @7 L8 ^    ...............
    1 R/ H! e* l5 Z6 a( @" Z    .............3 r: e! G4 t0 s2 X- X- K8 h
        .............
    / [# p) i0 l& I% }    .............% k) j) y* X# y  g+ z1 l+ E
        ............../ i5 l2 ^" S( {+ r4 c

    ; D/ k* Y0 W$ H    AREA testasm,CODE,READONLY  G7 P7 d4 h( }/ ]0 ]
        CODE16
    / d' h/ h2 a/ v4 ]delay17 B# l: n7 r+ @1 V/ ]" R  I
        ............7 Z) P$ o: ?3 A, R2 k2 i) V8 E
        ...........
    / P, q6 n1 E5 T    .............; N/ s  b* ]* c* X! F
        ldr r1,=ledoff
    7 R$ m- a, \6 q+ r    bx   r1) l5 x% G$ v) v: R; c  W
        ........
    ! y9 O7 `0 |; v7 K" _5 |    .............
    $ b/ m' t+ }- K    .............* b) [; v! x2 \+ s% {3 P
    END/ U& R& x5 R2 D8 p2 D1 R

    3 P8 J  z" T* r% `1 s2 k2 g3 j关于delay1+1:
    1 `/ z. o, [1 a' \' d- RARM指令是字对齐(指令的地址后两位为[1:0]=0b00),Thumb是半字对齐(指令的地址后两位为[1:0]=0bx0,x为0或1)。指令的地址的最后一位必为0。
    8 o6 z, A# N. J: Y; n, A( m因此bx(不管往ARM还是往Thumb跳转)的跳转指令必须保证指令地址的最后一位为0,上例中bx指令能自
    ; w$ O4 d/ x( t/ A( u动地将目标地址值置为r4的值和0xFFFFFFFE的与后的结果,就会使指令地址的最后一位必为0了。7 c) V' K; }# l, p# Q5 e
    那么delay+1的这个1不就被0与掉了么,就没有什么作用了?其实,在执行bx指令时,它是首先判
    - c7 U! N. m/ m断指令地址的后一位为0或1(这样就知道要跳转的地方是ARM还是Thumb指令。0跳转arm,1跳转thumb。),然后再PC=r4 AND 0xFFFFFFFE。这样,当我们需要要跳转到Thumb指令处执行时,必须将指令地址的最后以为置1。, C6 ~& ]% J5 O' g
    而bx再跳转到ARM指令时就无需考虑这些了,就不用像上面的+1处理了。
    . n4 @. Z! z' _: w+ ]7 S6 N
    关于字对齐和半字对齐
    什么叫对齐。如果一个数据是从偶地址开始的连续存储,那么它就是半字对齐,否则就是非半字对齐;半字对齐的特征是bit0=0,其他位为任意值。字对齐的特征是bit1=0,bit0=0 其他位为任意值。如果一个数据是以能被4 整除的地址开始的连续存储,那么它就是字对齐,否则就是非字对齐。举例说明四字节对齐: 对内存进行操作时,被访问的地址必须为4的倍数。如果分配到的地址不是4的倍数时,CPU实际访问的地址还是按照字对齐的方式来操作。也就是自动屏蔽bit1和bit0.   

    $ l8 Z0 Z  ]- o0 K
    什么是对齐,以及为什么要对齐:. ^9 T  ]) q3 X: a" v5 D+ g: X
           现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就需要各类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
    对齐的作用和原因:
            各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出,而如果存放在奇地址开始的地方,就可能会需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该int数据。显然在读取效率上下降很多。这也是空间和时间的博弈。
    3 g: a) i* g% {+ l. s对齐的实现
    - h5 w* P% }3 Q3 v6 a* z) X       通常,写程序的时候,不需要考虑对齐问题。编译器会替我们选择适合目标平台的对齐策略。可以给编译器传递预编译指令而改变对指定数据的对齐方法。
           char   a                       char   a
           short b                        char    c
           char   c                        short b
         int      d                         int     d
    ARM体系中对齐的使用:
    ; t9 u( u* K. \2 [/ F1.__align(num)& h# g. O' ]. o0 N& H
               这个用于修改最高级别对象的字节边界。在汇编中使用LDRD或者STRD时
    * a& u3 ~; m# U1 d6 G   就要用到此命令__align(8)进行修饰限制。来保证数据对象是相应对齐。
    1 G  z; V$ M4 e   这个修饰对象的命令最大是8个字节限制,可以让2字节的对象进行4字节
      h; r+ }6 m6 C( [1 j   对齐,但是不能让4字节的对象2字节对齐。( j! K7 p1 V2 H2 `* N8 f; X
       __align是存储类修改,他只修饰最高级类型对象不能用于结构或者函数对象。- Y$ {8 x( m: C" ^8 B. ?& m' n
       
    $ B- y0 H4 ~6 H4 S2.__packed ( a" ~7 l. g5 F4 D) n, [  A
    __packed是进行一字节对齐7 F( Y( Z' \5 c! {5 X% f
    (1).不能对packed的对象进行对齐. V1 V1 w& z' Z% ?6 e
    (2).所有对象的读写访问都进行非对齐访问
    1 v9 ~6 K  Q- h1 s' L0 y/ Q, M(3).float及包含float的结构联合及未用__packed的对象将不能字节对齐
    5 m, q: ^; N4 F9 z7 M( q(4).__packed对局部整形变量无影响
    5 O' t$ @% @9 o& `' r(5).强制由unpacked对象向packed对象转化是未定义,整形指针可以合法定义为packed。! ?1 E6 l) s: b7 v
         __packed int* p; //__packed int 则没有意义
    - q2 e; V; J7 ]- M(6).对齐或非对齐读写访问带来问题; Y! c5 p+ ?1 r3 n
    _packed struct STRUCT_TEST
    7 n4 N  h3 h# m# F( s3 @{
    4 l8 [  o* Q$ w: e3 I  r/ X1 W) ?9 m   char a;; {8 `9 L# v6 u0 W# y6 C/ U9 }4 o+ D
       int b;
    8 q, `# ^6 u. S5 a# Y   char c;
    / @/ i6 [& x  u} ;      //定义如下结构此时b的起始地址一定是不对齐的6 T  i: o5 u& Z' r
             //在栈中访问b可能有问题,因为栈上数据肯定是对齐访问[from CL]
    - H; Q7 G6 E- s//将下面变量定义成全局静态不在栈上 % K0 V$ ]! Y, t% w
    static char* p;
    4 i8 l6 _8 J! I, o" c5 V4 zstatic struct STRUCT_TEST a;
    6 h2 F) ~. J0 f! D, l2 R  l7 B& Z5 `6 lvoid Main(). o) Q6 B; t4 C2 a
    {5 W/ q2 f9 y' _4 }8 L7 v/ j1 _4 y6 T
             __packed int* q;   //此时定义成__packed来修饰当前q指向为非对齐的数据地址下面的访问则可以
              p = (char*)&a;          ; X9 |% r! N- |# h3 ~
              q = (int*)(p+1);      0 \& i6 g: ^. R4 E8 m: t( ?" g
       /*
    ! `: A* G' J, t  V' R/ Z6 B) J8 S) C         *q = 0x87654321;
    ; |8 }% w% V3 m) s          ldr      r5,0x20001590 ; = #0x12345678& E! `* R: r4 P
             [0xe1a00005]   mov      r0,r5) A5 g, E; Y& a  @6 Q
              [0xeb0000b0]   bl       __rt_uwrite4   //在此处调用一个写4byte的操作函数 / G0 `0 L+ T! ^9 o' l4 T
          : J/ }& P. {6 h0 h4 e, ?
             [0xe5c10000]   strb     r0,[r1,#0]    //函数进行4次strb操作然后返回保证了数据正确的访问$ l9 V. X2 D* M  A4 ?: o
             [0xe1a02420]   mov      r2,r0,lsr #8
    6 o& j- i& k1 M. i& z         [0xe5c12001]   strb     r2,[r1,#1]! {2 F5 t$ `+ `8 {$ X; ]* K0 u
             [0xe1a02820]   mov      r2,r0,lsr #16
    5 F7 P; Y8 x: E; A         [0xe5c12002]   strb     r2,[r1,#2]% w4 H* M2 W$ ?  l$ a
             [0xe1a02c20]   mov      r2,r0,lsr #24
    0 O* X2 c/ ?$ L6 ^- g1 x) p         [0xe5c12003]   strb     r2,[r1,#3]
    7 q1 N! [  D: Y2 }6 S( e6 m; K         [0xe1a0f00e]   mov      pc,r14
    ; m3 ~8 i. [6 v+ ^( c8 H*/
    /*+ W; v$ q+ r/ g+ l
             //如果q没有加__packed修饰则汇编出来指令是这样直接会导致奇地址处访问失败
    6 Q) D& f" m& P3 Z' z# h: Z5 ~          [0xe59f2018]   ldr      r2,0x20001594 ; = #0x87654321" k2 p! ?# K8 K, S8 A
             [0xe5812000]   str      r2,[r1,#0]
    2 M$ r/ X- t- Z' Z6 U" W& u*/
             //这样可以很清楚的看到非对齐访问是如何产生错误的# ~! D% i3 e3 ~
              //以及如何消除非对齐访问带来问题. w: ~' [' z0 n  `
             //也可以看到非对齐访问和对齐访问的指令差异导致效率问题
    : Z) b; {$ B' T. V% T

    - e* s0 U" o' E5 P4 |( S) @
    先看一个小程序$ l& ^2 \5 h- H5 W
    #include<stdio.h>8 r- W+ U, m$ ^3 x( |' D6 k' l
    struct t15 J  z' p2 x) B2 r, N
    {
    : E" V' g, _/ G- x, j- I    char a;5 U8 T5 K$ b- f# E3 e5 B) a
        int b;1 f- u; U3 Q& _( K) R8 i
        char c;# I$ Y+ W& Q  u7 [2 ^
    };
    struct t2
      T$ ?& {3 s* @; k# v4 v  Y( F7 A{
    ! X  I/ n$ X0 p; @7 u3 D    char a;
    % v* W* E+ C/ ~4 E. W' N    char b;
    / Y+ V" d/ b/ h2 g    int c;
    ) S9 V6 C" p9 i4 Y};
    void main()2 `# P6 O2 W$ q4 D' i" \
    {7 S0 b& v1 O: |7 P& D
        printf("%d/n",sizeof(struct t1));) F! g! }$ R: h: R* P) G
        printf("%d",sizeof(struct t2));: d/ j  S2 R1 N/ a' G$ u
    }
    按一般来说,结构体t1和t2的大小应该是一样的,但在很多编译器中,显示出的结果是不一样的,如我用的VS 2010,t1的大小为12字节,t2 为8字节,为什么会出现这样的结果呢?这其中有个东西就叫字对齐。/ d! E  t  H# r1 u# z* v, n
    据说很多公司在面试中都考过这个问题,有一年威盛也考过。。。
    & {5 A/ c0 z7 E1 ~( H0 M8 l4 q字对齐,是昨天在看ARM的时候看到的,一般是4个字节为一个单位进行对齐,怎样对齐的呢?有点像平时我们写文档用的右对齐之类的,不一样的就是,句子可以换行写,但一个变量的存储,不是那种本身很长的,一般是不换行的,例如上面的例子,将空的内存看作
    & P$ F0 v4 s8 e- Roooo
    ; i! }: P& x, }8 O, T( ?; moooo8 `! {/ g( @) K
    oooo/ f" V" m& ]# Z2 q: T' @
    t1中为a分配内存,用一个字节,内存变为
    4 u) Q0 {; x% U& v! s. Y) Uxooo- Z6 N% ]8 v& x+ B
    oooo
    * P. J1 y7 D1 J! S3 ^$ }7 ooooo* X5 {9 f4 E0 m
    然后进行b的内存分配,在VS中,int占4个字节,但第一行只有3个字节空的了,所以就换行对齐,内存变为
    8 T! Y" ^$ S  xxooo  u. g( ~7 u( f) }
    xxxx/ o7 Y0 M3 g$ a' k
    oooo
    * F& L) `9 ~, l* l) _最后为c分配,变成% c8 y6 `) p: c. q3 J2 G
    xooo
    / i+ _  ^# ~' R* m* fxxxx
    3 z. L/ v! D- N; b% ^3 gxooo6 N. k  u1 f( J7 F' h
    总共占用内存就是每行4个字节x3行=12字节。
    9 ?5 J) v: G3 s% N3 V, z在t2中9 c' `8 s, n! e: {% f
    内存同样为8 W5 ~* q0 ]0 I6 K2 }- B& g9 V! _
    oooo3 Y9 W. f8 V, }$ K7 A0 A8 _- O
    oooo6 h% X4 O& o( R; b; K* U( e
    oooo$ T- b, Z% ?- {7 h
    为a分配时,同样是1 ~( ~, t: {* d
    xooo
    + [1 A! h8 v, G5 F为b分配时,由于第一行的长度能满足b所以b还是在第一行,内存为
    * _) v% ]. d0 C1 ^' E$ D" gxxoo
    , G- P! e5 ~5 doooo1 k0 o% ~. F& J& X
    oooo2 O, d# K( q! G/ c- }+ }  s
    为c分配时,第一行已经不够,就换到第二行,0 H2 b1 T5 v) E- ]1 E
    xxoo
    : a: b) {9 l% w3 ]3 k, Q* Y) ]7 pxxxx
    - k" Y2 w. d- `" m+ ]4 d+ [1 Zoooo
    6 M1 E' b' }/ N% ?" E! F3 f这样第三行就没用到,所占的内存就是每行4个字节x2行=8个字节。
    这就是我所理解的字对齐,至于为什么,我想,应该是方便32位机操作吧,32位刚好就是4个字节,这样可以尽量使数据的读取用一次总线操作就完成,int是4个字节,32位,如果不用字对齐,读取一个int,可能第一次就只能读出高位那部分,然后通过移位将数据的高位移到寄存器的高位,再读出低位,再对第二次读取的高位屏蔽,再进行或操作,效率不高。同样,类似于char型和short型数据,读取了后还是会进行寄存器移位操作,这一点可以通过编译生成的汇编语句得到证实,这也就是为什么在某些系统,如嵌入式系统中,char的效率不如int高的原因了。4 r& y* s4 X5 m0 M/ T) D/ x0 P$ i
    另外,半字对齐和字对齐差不多,只是它的单位是2个字节吧,因为有的嵌入式系统是16位的。
    # d/ d, p* |8 P

    该用户从未签到

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

    本版积分规则

    关闭

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

    EDA365公众号

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

    GMT+8, 2025-11-24 21:00 , Processed in 0.156250 second(s), 23 queries , Gzip On.

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

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

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