EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
bx的语法格式
/ |) m/ k6 P: _BX{<cond>} <Rm>
' w |1 b5 Z0 d4 }9 y; X<cond>为指令执行的条件码。当<cond>忽略时指令为无条件执行。 7 _ ` M7 n0 R5 N1 p
<Rm>该寄存器中为跳转的目标地址。当<Rm>寄存器的bit[0]为0时,目标地址处的指令为 ARM指令;
8 U* l! k# `" H当<Rm>寄存器的bit[0]为1时,目标地址处的指令为Thumb指令。 6 m5 [6 [) U. C( \5 f1 t" i% b4 Z2 e2 }
- ^% n# ]8 V3 y3 H1 a5 p看一段简单程序的片段,不用考虑省略号的内容,看框架就可以了! - u! n) }$ |/ |7 N2 A- n' n; b
, l2 v3 f" p: m1 @3 Y. s3 O7 m+ Y2 S. w
EXPORT LEDTEST $ k3 e" M, s, E: J: ^0 m
AREA testasm,CODE,READONLY ; h0 Q2 w; q* n: i3 M& t& e# W
CODE32 a3 j4 Q X; Z& _: g8 G
LEDTEST
+ E4 M1 |: N$ `9 e 。。。。。。。。。。
; G3 X5 J7 l5 y7 {4 a 。。。。。。。。。
5 {- S8 B( Z5 t8 |. ~& c 。。。。。。。。
5 }. I: I: c7 E* t3 a5 } 。。。。。。。。。
; a5 q. L: @/ }6 P 。。。。。。。。。。。 # U1 W F' p. ?+ E
ledon
! @. h& T- a5 h1 \/ O ...............
3 D# B( X* |+ h+ |0 _1 b ............... 5 B/ `1 y6 x! A4 D2 m# B; p
。。。。。。。。。。。。 $ C" A' U- `0 I# l
。。。。。。。。。。。
6 S) p3 `+ {- r; }7 Z& B adr r4,delay1+1 ! ?' b$ D/ X, f: ^0 Y) S9 |1 j
bx r4
: h6 E% j# b* f0 {. F# c! l. a: p7 ^
3 w1 W! x/ f, s; c7 @ledoff
- P" R; U* t* a' `! ^1 N0 U ..............
1 e9 L" g# |' G& I3 p' U ............... ( F. ~, C! O. b( M! C, g
.............
, K5 q5 a7 Y+ y+ i; j: e, Y P ............. - D+ u6 X2 }$ p) i7 @$ E2 ^. J
.............
4 _% F6 O# [) s# I5 l .............. $ [ D3 B T" S! _
2 d; t1 ?. l3 o
AREA testasm,CODE,READONLY
0 K! H/ H- A; m; P( Z5 e CODE16
6 Q+ |5 e; y( r) \/ Tdelay1
! H# Z0 ?7 \) Y2 _! X0 ] ............
( ]& b i! [( [+ ]+ L) v, ]2 j ........... : n) ]' r5 o5 O/ a# D
............. 8 f6 c& o& v0 [" K! q8 Q
ldr r1,=ledoff
4 {7 L2 K6 E1 B; m5 ] bx r1
( n2 l* n! ^! s A( x( W ........
8 ]' M6 s, y# ?, U* }9 q7 {$ o .............
, O5 ^1 {& F' T% [/ D4 f .............
0 @( B9 v% S# t/ V4 [3 y5 gEND 8 ]- Z8 v; z/ x! ^7 p' V g
5 O' P3 k4 K: {& |+ R+ m2 A
关于delay1+1: ) W( d( L$ I9 N. {" U n
ARM指令是字对齐(指令的地址后两位为[1:0]=0b00),Thumb是半字对齐(指令的地址后两位为[1:0]=0bx0,x为0或1)。指令的地址的最后一位必为0。 + f$ L/ ^1 u w7 i
因此bx(不管往ARM还是往Thumb跳转)的跳转指令必须保证指令地址的最后一位为0,上例中bx指令能自 7 H* H. d: j& r/ M' \$ y; W
动地将目标地址值置为r4的值和0xFFFFFFFE的与后的结果,就会使指令地址的最后一位必为0了。
5 r8 Z# J8 R3 X! n3 o4 H* f那么delay+1的这个1不就被0与掉了么,就没有什么作用了?其实,在执行bx指令时,它是首先判 7 s" q7 c. J0 @1 D; a
断指令地址的后一位为0或1(这样就知道要跳转的地方是ARM还是Thumb指令。0跳转arm,1跳转thumb。),然后再PC=r4 AND 0xFFFFFFFE。这样,当我们需要要跳转到Thumb指令处执行时,必须将指令地址的最后以为置1。
7 n7 C, ^1 X2 ?, c( j/ K而bx再跳转到ARM指令时就无需考虑这些了,就不用像上面的+1处理了。 . E9 B- z2 x! y" J \+ h
关于字对齐和半字对齐 什么叫对齐。如果一个数据是从偶地址开始的连续存储,那么它就是半字对齐,否则就是非半字对齐;半字对齐的特征是bit0=0,其他位为任意值。字对齐的特征是bit1=0,bit0=0 其他位为任意值。如果一个数据是以能被4 整除的地址开始的连续存储,那么它就是字对齐,否则就是非字对齐。举例说明四字节对齐: 对内存进行操作时,被访问的地址必须为4的倍数。如果分配到的地址不是4的倍数时,CPU实际访问的地址还是按照字对齐的方式来操作。也就是自动屏蔽bit1和bit0. / _, R3 m# x2 R x
什么是对齐,以及为什么要对齐:
% j: g/ ~. Q8 O% d6 }6 I 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就需要各类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。 对齐的作用和原因: 各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出,而如果存放在奇地址开始的地方,就可能会需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该int数据。显然在读取效率上下降很多。这也是空间和时间的博弈。* W- L% F4 ?# h- U$ o
对齐的实现7 e) u3 Y1 U- r+ s$ k! v0 o
通常,写程序的时候,不需要考虑对齐问题。编译器会替我们选择适合目标平台的对齐策略。可以给编译器传递预编译指令而改变对指定数据的对齐方法。 char a char a short b char c char c short b int d int d ARM体系中对齐的使用:# L+ h* U; O. Q: R# L$ u$ y
1.__align(num)
# N" c) F$ u9 X% h7 _9 D2 C 这个用于修改最高级别对象的字节边界。在汇编中使用LDRD或者STRD时
, M. ` w, ~1 r6 B9 H$ N 就要用到此命令__align(8)进行修饰限制。来保证数据对象是相应对齐。
$ o6 K( U; I+ q 这个修饰对象的命令最大是8个字节限制,可以让2字节的对象进行4字节7 L @' r# d3 A5 Z( m" ?
对齐,但是不能让4字节的对象2字节对齐。" S/ N3 N, n8 L; n
__align是存储类修改,他只修饰最高级类型对象不能用于结构或者函数对象。
% V* w8 y3 ?% ?" y: {6 a! @ * C( M l8 P8 N
2.__packed
. s- @0 Q* x5 Z__packed是进行一字节对齐
* y" ?( z9 o" R7 K$ R8 r(1).不能对packed的对象进行对齐
6 c) V9 O1 w' j: \ a( E0 t(2).所有对象的读写访问都进行非对齐访问
9 `5 A W1 A2 p5 ]/ B* V(3).float及包含float的结构联合及未用__packed的对象将不能字节对齐
' z& a8 ]7 j- s; `(4).__packed对局部整形变量无影响
1 F& s/ q! R% ], O' s(5).强制由unpacked对象向packed对象转化是未定义,整形指针可以合法定义为packed。
/ g. [3 R# x) O/ ~5 b* E' v5 p __packed int* p; //__packed int 则没有意义7 n Z0 P1 m* S" i( i) Z2 l/ J
(6).对齐或非对齐读写访问带来问题
+ J) B Q2 L) s6 B8 A: W! V_packed struct STRUCT_TEST
! M* |( E$ a( f) u{
' S* G) A3 a; S; o: Q char a;
* {1 |. p/ m6 q5 e) W int b;# L1 J0 [9 N0 y; G
char c;
1 A2 O1 |0 k4 O( f' O5 U0 X6 y} ; //定义如下结构此时b的起始地址一定是不对齐的
9 {9 A# e1 p X/ R( F- k8 m //在栈中访问b可能有问题,因为栈上数据肯定是对齐访问[from CL]& i! h4 o+ o2 W' `% j% y$ E2 I1 v
//将下面变量定义成全局静态不在栈上
2 G7 C! t* n' t) xstatic char* p;. x i5 o7 {8 I k) o
static struct STRUCT_TEST a;% S4 J: m; ?& H. P: U
void Main()- g& f2 S( z, R8 T e8 k
{9 Z+ K; i0 m! U- R/ r
__packed int* q; //此时定义成__packed来修饰当前q指向为非对齐的数据地址下面的访问则可以 p = (char*)&a; 3 {! X' k. i j( O& E) Z
q = (int*)(p+1); $ w" s2 \4 E9 u+ {8 [
/*% ~ |) {1 Q- `8 y' c( B7 V
*q = 0x87654321;! m' e& c( T* o& { s
ldr r5,0x20001590 ; = #0x12345678
" g& h" ?2 u; `* y. }" B; [ [0xe1a00005] mov r0,r5
@8 e# u9 j; S/ G1 r I [0xeb0000b0] bl __rt_uwrite4 //在此处调用一个写4byte的操作函数
' K9 A. q/ R! }$ L$ q7 g
! j5 w6 x6 C3 p, ^! K [0xe5c10000] strb r0,[r1,#0] //函数进行4次strb操作然后返回保证了数据正确的访问
. X" `+ N0 @: U% a) Q9 S [0xe1a02420] mov r2,r0,lsr #8) n" g: d1 C, o3 Q- b- W5 x
[0xe5c12001] strb r2,[r1,#1]
( O5 V, C& r/ Z8 Z [0xe1a02820] mov r2,r0,lsr #167 R/ L4 |2 y' U' J& H" _- W
[0xe5c12002] strb r2,[r1,#2]8 u$ ?6 P" Z* m* ^
[0xe1a02c20] mov r2,r0,lsr #24
: Q+ E% R# E- J, k0 Q1 q [0xe5c12003] strb r2,[r1,#3]
1 @! a3 l( r6 G/ y! Q [0xe1a0f00e] mov pc,r14
7 P- I1 ~2 L9 C) U4 b, T*/ /*8 ^6 |4 a9 G; j0 Z* [1 j# V
//如果q没有加__packed修饰则汇编出来指令是这样直接会导致奇地址处访问失败" e `* h; |8 z* N. S/ _( R
[0xe59f2018] ldr r2,0x20001594 ; = #0x876543213 N {( g4 \* N) Z. V9 m, R
[0xe5812000] str r2,[r1,#0]
& X+ v* ]( j9 n* W*/ //这样可以很清楚的看到非对齐访问是如何产生错误的
& e; G9 u8 G/ ` //以及如何消除非对齐访问带来问题
: y8 H% I) y6 X) }7 R: o# ^0 M //也可以看到非对齐访问和对齐访问的指令差异导致效率问题 - W2 c" ^* O' B
& `0 k' R8 B) x9 I" B! \0 F
先看一个小程序" o6 _, p2 T ? k' G
#include<stdio.h>6 \2 k3 p0 n5 k
struct t1
* @$ X* m& S" J{
. E8 P' q1 z4 v" O# Z char a; p( z2 |7 e6 k2 d- ?4 ?. N0 d
int b;/ W3 ~$ E2 z& X2 ^" s0 ^0 a
char c;, c! Y- w, Y2 e5 {( [ f
}; struct t2
5 W! x' Q2 ~4 j" [. S- r{- d2 s$ y$ j' `, Y) O' m4 a, V
char a;
5 G( D, F Z5 S% f. u! _- H char b;
9 i9 F6 k, B, ~5 ~5 A0 n6 C int c;0 v6 d% \- s3 v% }+ t: c
}; void main(). x0 j! t% i' X( k
{
o! B B. w0 F0 Y+ `) {' | printf("%d/n",sizeof(struct t1));
+ Q7 H2 X7 |1 w printf("%d",sizeof(struct t2));7 v' j; _1 h3 U8 d( i" A1 I
} 按一般来说,结构体t1和t2的大小应该是一样的,但在很多编译器中,显示出的结果是不一样的,如我用的VS 2010,t1的大小为12字节,t2 为8字节,为什么会出现这样的结果呢?这其中有个东西就叫字对齐。
P! ?% |6 g; e" j, [) E5 g7 ?7 M! b据说很多公司在面试中都考过这个问题,有一年威盛也考过。。。$ S; V J- `, B6 o. x- M8 q# M5 r
字对齐,是昨天在看ARM的时候看到的,一般是4个字节为一个单位进行对齐,怎样对齐的呢?有点像平时我们写文档用的右对齐之类的,不一样的就是,句子可以换行写,但一个变量的存储,不是那种本身很长的,一般是不换行的,例如上面的例子,将空的内存看作
! y0 t% W9 Y( b: [ u& v; U$ Joooo) B6 B% z1 ?2 a
oooo
) {' c% A* k, P$ z+ c) C6 koooo! }0 S7 D2 ^" x. {& y+ ]) s
t1中为a分配内存,用一个字节,内存变为0 M7 b9 f3 z: z9 B, w9 D: _
xooo9 `9 t" a; J. A9 W. p% \# D
oooo
% @2 d% y" t$ `6 xoooo
6 I& X) x' h0 y& C. G然后进行b的内存分配,在VS中,int占4个字节,但第一行只有3个字节空的了,所以就换行对齐,内存变为
% C/ p& G0 C9 L$ Z8 q2 X; wxooo# @3 ^' y& l% H% ?; H
xxxx
7 }( j; e# @& v1 i" e' eoooo- {$ j; Z; K9 r: j- g" N# ]) }
最后为c分配,变成
4 e3 a `( c1 {1 ^& S2 u @xooo
3 }. p' U* J" P ]xxxx
7 r) A) U+ I: B9 h9 n& oxooo
) ?5 q( H# Z+ M% |' ^) ]总共占用内存就是每行4个字节x3行=12字节。! |9 b; u7 O" D3 m$ u1 k5 S
在t2中/ t1 G* O; N6 u2 W8 p" x
内存同样为/ ~4 M, ] T( P: O3 I& U3 A7 ~
oooo
, _' W" W6 k2 A! u0 a) Zoooo2 a6 P% @1 w5 [8 P4 |# ^. D* M' q& n
oooo* u; m# a8 o7 c2 L4 J- ~' Z) [
为a分配时,同样是
) o1 k8 P: T8 V' ^4 M; C/ x1 exooo5 p8 \$ J4 q: a8 k
为b分配时,由于第一行的长度能满足b所以b还是在第一行,内存为+ s8 ~+ N! _4 M7 _, I
xxoo, @$ V: T; s/ n) Z, ^
oooo
1 s' Y' }/ r( A- U' Loooo
& V! L" M0 b+ D6 F: c- [5 [为c分配时,第一行已经不够,就换到第二行,
4 ~& }! x7 O9 ]xxoo
4 q$ x7 n# w4 I3 e- yxxxx
+ D0 k! y1 n% A& K ~+ M/ noooo
* ^; @( ?2 w; F1 C* B2 T这样第三行就没用到,所占的内存就是每行4个字节x2行=8个字节。 这就是我所理解的字对齐,至于为什么,我想,应该是方便32位机操作吧,32位刚好就是4个字节,这样可以尽量使数据的读取用一次总线操作就完成,int是4个字节,32位,如果不用字对齐,读取一个int,可能第一次就只能读出高位那部分,然后通过移位将数据的高位移到寄存器的高位,再读出低位,再对第二次读取的高位屏蔽,再进行或操作,效率不高。同样,类似于char型和short型数据,读取了后还是会进行寄存器移位操作,这一点可以通过编译生成的汇编语句得到证实,这也就是为什么在某些系统,如 嵌入式系统中,char的效率不如int高的原因了。 0 B: F& a. i, ^9 j% F3 f$ `0 T$ h
另外,半字对齐和字对齐差不多,只是它的单位是2个字节吧,因为有的嵌入式系统是16位的。 * |' h, q/ N4 U8 G" K E# V: A
|