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

ARM的BX指令

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

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

    [LV.1]初来乍到

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

    EDA365欢迎您登录!

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

    x
    bx的语法格式5 X# z* Y7 \  {9 x4 W. f
    BX{<cond>} <Rm>
    ' _  Y4 O7 L* M<cond>为指令执行的条件码。当<cond>忽略时指令为无条件执行。: v( `& g" L6 P6 P  i0 D
    <Rm>该寄存器中为跳转的目标地址。当<Rm>寄存器的bit[0]为0时,目标地址处的指令为ARM指令;
    - Y' [) F! X2 |, z9 Z0 b( C当<Rm>寄存器的bit[0]为1时,目标地址处的指令为Thumb指令。
    - p# q9 f' P1 n) O
    5 P6 j- r" Y+ B2 {8 a看一段简单程序的片段,不用考虑省略号的内容,看框架就可以了!
    " t2 f( B  i8 Q3 \; ~& l6 O8 \+ A
        EXPORT LEDTEST
    9 C  I" p; f- H: |, K    AREA testasm,CODE,READONLY
    & ]* y! x9 }: w( z! \    CODE32
    ! ]7 G# h' g  `, j' pLEDTEST
    4 b( F1 Y7 h3 z    。。。。。。。。。。
    # l( Y9 W( l, c, b9 T) D    。。。。。。。。。
    + F6 ?! h' g4 t/ h/ @# D* ~    。。。。。。。。, Y9 `6 Y2 G# n+ u
        。。。。。。。。。* _4 _& k0 E, |. A
        。。。。。。。。。。。" i5 m' N1 Q2 c3 ?
    ledon
    + y+ V, K# \' L* f; z0 C' w    ...............2 Z5 q: k) g6 n$ j3 o' d7 D1 w
        ...............+ P' q6 q- Q9 S( _
        。。。。。。。。。。。。
    5 {- N: A% Y2 E- _0 M8 X8 D    。。。。。。。。。。。$ F3 f5 e) B% e. Y; {& M8 B
        adr r4,delay1+1* x( e% d9 s( j
        bx r48 i2 t/ r/ E) O) Y, ?

    / L5 P. k" F4 ~3 v6 `ledoff
    ' u+ o* C: D2 h    ..............9 F2 O9 B: Y* P6 {, ?' L2 M% c
        ...............
    6 t8 F( z: I5 F. g4 o7 F    .............
    ; a1 |. T( S, L$ }    .............
    2 E- h3 P2 w* b1 f    .............
    & M! P9 d. l6 d, h# `8 j    ..............
    4 v* y1 f6 C, M0 P; B
    - U2 Y# b+ u4 D2 n    AREA testasm,CODE,READONLY6 b# K8 @) _" n' R& }* A* M+ r- m5 f
        CODE16. A0 J. Q$ f# x7 C7 ^/ m
    delay10 c, t  `/ ^# n, M6 X, V
        ............
    # Z1 Z: z/ D9 E6 e    .........../ |7 A$ K5 n1 E7 w8 L/ T* i3 m
        .............) q6 }3 \) w- v! B; A9 {9 d
        ldr r1,=ledoff
    8 K; |1 A/ y2 P$ A+ e9 g    bx   r1
    , Q2 f7 H" M% A5 f/ _    ........# Z+ i) s3 z( m# X9 Y! u0 b6 c
        ............." b7 W5 @% a! G
        .............
    # w7 s6 A$ }: M- Q6 o/ T: NEND
    8 _) y2 C% U0 s2 S& o( n9 D3 \. B# e" |+ T3 ^3 }1 w0 F
    关于delay1+1:5 R' N5 c5 k' d; r& G# z
    ARM指令是字对齐(指令的地址后两位为[1:0]=0b00),Thumb是半字对齐(指令的地址后两位为[1:0]=0bx0,x为0或1)。指令的地址的最后一位必为0。9 s  j* w7 ?- H: m4 Q" [
    因此bx(不管往ARM还是往Thumb跳转)的跳转指令必须保证指令地址的最后一位为0,上例中bx指令能自2 P1 s2 j+ Q0 q  b2 ~
    动地将目标地址值置为r4的值和0xFFFFFFFE的与后的结果,就会使指令地址的最后一位必为0了。( ^! i! L3 x1 x. A) V- r' T3 M
    那么delay+1的这个1不就被0与掉了么,就没有什么作用了?其实,在执行bx指令时,它是首先判6 E/ ?0 S" U; K
    断指令地址的后一位为0或1(这样就知道要跳转的地方是ARM还是Thumb指令。0跳转arm,1跳转thumb。),然后再PC=r4 AND 0xFFFFFFFE。这样,当我们需要要跳转到Thumb指令处执行时,必须将指令地址的最后以为置1。
    : A  d- c7 N" v, r. T1 h. D而bx再跳转到ARM指令时就无需考虑这些了,就不用像上面的+1处理了。

    5 W( P+ a( w" o- k( [
    关于字对齐和半字对齐
    什么叫对齐。如果一个数据是从偶地址开始的连续存储,那么它就是半字对齐,否则就是非半字对齐;半字对齐的特征是bit0=0,其他位为任意值。字对齐的特征是bit1=0,bit0=0 其他位为任意值。如果一个数据是以能被4 整除的地址开始的连续存储,那么它就是字对齐,否则就是非字对齐。举例说明四字节对齐: 对内存进行操作时,被访问的地址必须为4的倍数。如果分配到的地址不是4的倍数时,CPU实际访问的地址还是按照字对齐的方式来操作。也就是自动屏蔽bit1和bit0.   
    : k( J5 U' k% I& Y8 e
    什么是对齐,以及为什么要对齐:
    & ?: F  c! ^3 V8 r( ?" b       现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就需要各类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
    对齐的作用和原因:
            各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出,而如果存放在奇地址开始的地方,就可能会需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该int数据。显然在读取效率上下降很多。这也是空间和时间的博弈。
    5 c3 J/ O1 M8 ^  X) E  ]/ ]0 v对齐的实现
    - M1 ?  u& L. K3 u( v/ Z8 n& r3 G$ {       通常,写程序的时候,不需要考虑对齐问题。编译器会替我们选择适合目标平台的对齐策略。可以给编译器传递预编译指令而改变对指定数据的对齐方法。
           char   a                       char   a
           short b                        char    c
           char   c                        short b
         int      d                         int     d
    ARM体系中对齐的使用:
    8 u* O: c, {7 [# B- O8 L1.__align(num)  b, ?/ m8 x8 n' Z- h
               这个用于修改最高级别对象的字节边界。在汇编中使用LDRD或者STRD时1 j$ }3 C7 u1 {' g7 G$ U
       就要用到此命令__align(8)进行修饰限制。来保证数据对象是相应对齐。7 f6 M7 V' t2 Y( O
       这个修饰对象的命令最大是8个字节限制,可以让2字节的对象进行4字节6 \. E$ G! P* z/ D, K) a
       对齐,但是不能让4字节的对象2字节对齐。
    : l( i& _$ L4 F6 k8 c! u7 ~- O   __align是存储类修改,他只修饰最高级类型对象不能用于结构或者函数对象。) F8 E# d) _" A- F1 x* c
       
    + F' O7 a4 m7 Q, u3 n2.__packed 1 T! b$ \! R  o# X8 }' |5 w( [
    __packed是进行一字节对齐& s! a8 \2 S. y; E! a4 l
    (1).不能对packed的对象进行对齐: D& b: q9 ]5 H
    (2).所有对象的读写访问都进行非对齐访问' U8 R1 X9 |, o) d' C) ~
    (3).float及包含float的结构联合及未用__packed的对象将不能字节对齐
    & O7 d) `3 k. q- H6 }/ |(4).__packed对局部整形变量无影响# f0 k/ v8 [6 z
    (5).强制由unpacked对象向packed对象转化是未定义,整形指针可以合法定义为packed。
    , x. i8 C" \4 X% C     __packed int* p; //__packed int 则没有意义& `. U/ Q, y  w# S; a. Q* J
    (6).对齐或非对齐读写访问带来问题! ?& S9 a8 o9 _) ?+ i/ g! M
    _packed struct STRUCT_TEST
    " ?' n8 r/ f" U! Z' y2 f  ?{
    ! v+ Q- }7 ~4 f5 ]   char a;+ g( |& {$ P& M% a; \/ _  r, J% _, J1 R
       int b;
    $ W- S- I8 }1 l9 [! E- O3 f   char c;% @5 L2 x8 P. N6 F, s% i( p
    } ;      //定义如下结构此时b的起始地址一定是不对齐的; K6 P% }% t  a" ]4 e+ p( o
             //在栈中访问b可能有问题,因为栈上数据肯定是对齐访问[from CL]
    + x# P  p# O6 B//将下面变量定义成全局静态不在栈上 7 r/ t- K3 r9 v, |: _9 ?
    static char* p;7 p1 q% e' G* v$ k
    static struct STRUCT_TEST a;! H# m( \! P; W7 l* W
    void Main()
    * J( _- l' _3 F7 q; z6 \{
    & P4 z2 _0 v$ g# l$ z+ D" s         __packed int* q;   //此时定义成__packed来修饰当前q指向为非对齐的数据地址下面的访问则可以
              p = (char*)&a;         
    ) f& h! J1 W3 q: g* n5 K          q = (int*)(p+1);      
    , a* V: [* M' S9 ^( j% _9 U1 w   /*
    9 x/ z, O8 U- y( L9 ?         *q = 0x87654321;3 h# p5 X) r7 D8 i5 L
              ldr      r5,0x20001590 ; = #0x12345678
    3 o  P, L1 r* ]6 ]! I         [0xe1a00005]   mov      r0,r5) \/ C4 L! |% _7 j0 {
              [0xeb0000b0]   bl       __rt_uwrite4   //在此处调用一个写4byte的操作函数
    ' Z8 p8 `; v5 m, q9 k      5 k3 ^" H7 \7 p+ S. {1 I
             [0xe5c10000]   strb     r0,[r1,#0]    //函数进行4次strb操作然后返回保证了数据正确的访问
    - H/ q, |$ r# h, o         [0xe1a02420]   mov      r2,r0,lsr #8
    $ O% V( N% u$ s$ M' E( E3 {         [0xe5c12001]   strb     r2,[r1,#1]# h- \7 T; g( r" b0 v- l3 u
             [0xe1a02820]   mov      r2,r0,lsr #16
    2 ^  ^. G( C8 x2 Z! J* C         [0xe5c12002]   strb     r2,[r1,#2]8 {0 ]( e- C0 ?; M, F( D7 }
             [0xe1a02c20]   mov      r2,r0,lsr #24
    7 D) m$ |# u( Q9 m" O% g         [0xe5c12003]   strb     r2,[r1,#3]
      T: |# b5 u0 {* l9 Z' `, Z         [0xe1a0f00e]   mov      pc,r14( a% T6 ]) v  h2 X; T
    */
    /*& ~* V3 t$ A  c
             //如果q没有加__packed修饰则汇编出来指令是这样直接会导致奇地址处访问失败
    ' s) l* Y4 \- o: M2 e/ @/ x          [0xe59f2018]   ldr      r2,0x20001594 ; = #0x87654321
    * ]$ D; |; D# @         [0xe5812000]   str      r2,[r1,#0]- Y7 J- \) z2 H' u1 b
    */
             //这样可以很清楚的看到非对齐访问是如何产生错误的! e9 w8 R* N: q7 a: ?+ p8 x3 D
              //以及如何消除非对齐访问带来问题
    & T5 x/ I- X- h8 Y5 T  w- @/ K         //也可以看到非对齐访问和对齐访问的指令差异导致效率问题
    ) w2 r7 B0 ^; @7 S

    6 Z9 }+ H- Y# q* G7 h. C2 t/ _' i
    先看一个小程序9 G, f# H: f" I0 D* J
    #include<stdio.h>
    6 Q/ Y: e+ W) ]% C& \struct t19 K6 c4 d! Y0 i/ a5 d
    {
    4 C- Q# \9 I4 c/ }' t    char a;  [7 \8 [" l" O* t5 S8 S8 P3 G/ C
        int b;) l3 V( F! X8 K8 k) V3 B$ ~% E
        char c;
    & m9 Z" C( [7 Q% ?3 Q( F  ~; g/ f};
    struct t2
    8 c. B! u; d5 g) L1 \! Z{
    * g2 Y% p4 W: M+ T7 m; I9 a/ n* E    char a;, ^; {. K; J+ G, i
        char b;! e- [' k2 `1 [9 x" ^# b7 w$ c6 o
        int c;
    ! Z' e5 m$ u+ i};
    void main()
    - p9 S# `  g( S  p{
    % u6 W, c4 G+ @    printf("%d/n",sizeof(struct t1));  H3 I0 }# B3 m/ c6 t: C
        printf("%d",sizeof(struct t2));
    ; w. h4 g' h9 y' y8 `& N}
    按一般来说,结构体t1和t2的大小应该是一样的,但在很多编译器中,显示出的结果是不一样的,如我用的VS 2010,t1的大小为12字节,t2 为8字节,为什么会出现这样的结果呢?这其中有个东西就叫字对齐。" `1 r2 d7 v# C& f
    据说很多公司在面试中都考过这个问题,有一年威盛也考过。。。' ^: X% v! }! o1 M
    字对齐,是昨天在看ARM的时候看到的,一般是4个字节为一个单位进行对齐,怎样对齐的呢?有点像平时我们写文档用的右对齐之类的,不一样的就是,句子可以换行写,但一个变量的存储,不是那种本身很长的,一般是不换行的,例如上面的例子,将空的内存看作6 @- m( b# \* i9 ^! q+ K
    oooo
    % c3 x, N$ Y& f* j5 Q2 @oooo; o. g" M0 _- Y/ C/ c
    oooo( f: j" @" m: \" e- @5 b
    t1中为a分配内存,用一个字节,内存变为
    0 P( [; C% B& {" b$ Y9 G, {$ Kxooo* N+ s+ ~# D" k4 a& \1 x: ^
    oooo
    " |8 \& U$ O% e) k7 uoooo& w, z3 L+ n1 H
    然后进行b的内存分配,在VS中,int占4个字节,但第一行只有3个字节空的了,所以就换行对齐,内存变为* w5 {3 L5 C( i# A% Y" ]4 V" m
    xooo
    * k! P) L, _' m9 {% Pxxxx
    6 ^! P% c# n; boooo  l- I: t, F2 L
    最后为c分配,变成! y8 P8 E- ]& V' V
    xooo$ L/ X0 h# q* L! v# Q( C
    xxxx/ o. B1 E% N& Y  C
    xooo
    0 K0 n$ G& N  f" w总共占用内存就是每行4个字节x3行=12字节。! t$ @/ V9 K* j# N- I0 }0 S& T  K4 n) W- r
    在t2中
    9 L7 B/ t$ J5 K8 s+ x内存同样为
    3 F5 O+ }" T* p. A5 }8 G( Zoooo
    - [  _# H9 t6 {4 Joooo
    0 c4 M9 j% {* Ooooo
    . N4 C0 H1 s+ j! F* C8 S为a分配时,同样是8 q0 c9 o% _% R* J" u7 Y& C
    xooo1 a) z2 S5 i8 `
    为b分配时,由于第一行的长度能满足b所以b还是在第一行,内存为- ~9 a9 s* m$ r  o) k4 `
    xxoo
    ( l9 B# p2 e* R. w( yoooo
    3 ]- @9 g4 x. g+ v: J& D9 Yoooo2 p/ v3 S% g2 S, P8 h( ^7 A
    为c分配时,第一行已经不够,就换到第二行,
    / K. g8 b+ S* ?' c5 I8 z. \xxoo
    + o3 [" \, M# w9 Xxxxx( o3 o5 c, V' P" C
    oooo
    % j, |  Q2 u" u4 Q这样第三行就没用到,所占的内存就是每行4个字节x2行=8个字节。
    这就是我所理解的字对齐,至于为什么,我想,应该是方便32位机操作吧,32位刚好就是4个字节,这样可以尽量使数据的读取用一次总线操作就完成,int是4个字节,32位,如果不用字对齐,读取一个int,可能第一次就只能读出高位那部分,然后通过移位将数据的高位移到寄存器的高位,再读出低位,再对第二次读取的高位屏蔽,再进行或操作,效率不高。同样,类似于char型和short型数据,读取了后还是会进行寄存器移位操作,这一点可以通过编译生成的汇编语句得到证实,这也就是为什么在某些系统,如嵌入式系统中,char的效率不如int高的原因了。; a; I1 J% [- N$ I* y; i' N, ~
    另外,半字对齐和字对齐差不多,只是它的单位是2个字节吧,因为有的嵌入式系统是16位的。

    # E, f& t$ {* [8 t

    该用户从未签到

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

    本版积分规则

    关闭

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

    EDA365公众号

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

    GMT+8, 2025-11-25 01:06 , Processed in 0.171875 second(s), 23 queries , Gzip On.

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

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

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