你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

学习读汇编,为什么学习汇编?

[复制链接]
gaosmile 发布时间:2020-11-23 20:55
) A6 ?. J% K' M4 v

0 X$ B! F# S! S; `7 y 不同的平台的汇编代码是不一样的,最早的汇编在50年代就发明了,比很多人的父母的年龄都大,老掉牙,不用学习怎么写汇编。一个公司有一个人知道怎么写汇编就够了。但要学习读汇编,为什么学习汇编?
, y( s/ G# F8 W1 P3 D: c
1、性能
' Q% o( g+ \$ F( i
直接翻译为机器语言,性能最高。优秀的C语言效率只能达到汇编的80%左右。其他高级语言跟汇编一比差得更远。语言越高级性能越差。很多bootloader和BIOS用汇编写,汇编操作的是电脑,手机刚刚上电时,硬件和初始化的那些命令,它们的性能的要求比较高,效率高开机速度更快。
7 {) P& G0 u, ^4 D2 g
分析问题
% w8 j+ z- h3 V8 r* ]
个人认为,编程人与机器对话,我们写C,写JAVA,但是电脑并不认识这些语言,电脑只认识0和1;所以需要一个人来翻译这些语言,这个翻译官就是编译器,但是编译器不能百分之百准确的表达程序员的意思,也就是所谓的翻译有反义。例如,编译器为了性能好一点,可能会优化变量和语句,这个过程可能好心办坏事,把有用的操作优化了。因此只有看懂一些汇编语句,才能分析程序真正执行的流程。在问题难以定位的情况下,汇编可能是分析问题的最后一根稻草。 帮助理解硬件% @; h( D. F6 K1 L1 k4 W( q* \9 w
有些学校的单片机课程是以汇编进行教学的,主要原因就是汇编更贴近硬件。不过我不赞成这种做法,C语言能快速做出一点东西,有利于学生在放弃之前,增加成就感,好坚持下去。但是汇编确实更贴近硬件。
" u' c& _3 v6 P7 A) A  r' c' j
LDR指令
0 ~5 E( o# i5 A- ]5 o. l
为了便于理解下文,先介绍下LDR指令,其格式如下: 8 w4 Q0 M. f7 j7 E# a+ r7 m% V/ a
  • LDR{条件} 目的寄存器 <存储器地址>
    3 x0 p4 n$ {/ S9 U/ d
作用:将 存储器地址 所指地址处连续的4个字节(1个字)的数据传送到目的寄存器中。LDR指令的寻址方式比较灵活,实例如下:9 M- T/ z& x: r$ p, m- K$ z* T& {
  1. 1 Q( W5 @5 Y3 W! j( U
  2. LDR R0,[R1]   ;将存储器地址为R1的字数据读入寄存器R0。
    9 ?+ @; v/ k3 I0 ?( T. a
  3. LDR R0,[R1,R2]   ;将存储器地址为R1+R2的字数据读入寄存器R0。. m7 t& ~# {8 r, B2 z; d
  4. LDR R0,[R1,#8]    ;将存储器地址为R1+8的字数据读入寄存器R0。
    , ]# _1 \6 l2 s: V) U
  5. LDR R0,[R1],R2      ;将存储器地址为R1的字数据读入寄存器R0,并将R1+R2的值存入R1。) g6 j8 p% {) W7 V- r" Y
  6. LDR R0,[R1],#8      ;将存储器地址为R1的字数据读入寄存器R0,并将R1+8的值存入R1。: @& \  M. L' ~7 B6 {0 Y; \
  7. LDR R0,[R1,R2]!    ;将存储器地址为R1+R2的字数据读入寄存器R0,并将R1+R2的值存入R1。( e; A7 [8 v" Y- ~
  8. LDR R0,[R1,LSL #3]     ;将存储器地址为R1*8的字数据读入寄存器R0。
    , n' j  I" B& i; C$ E
  9. LDR R0,[R1,R2,LSL #2]   ;将存储器地址为R1+R2*4的字数据读入寄存器R0。2 o% r9 j- G! Y
  10. LDR R0,[R1,,R2,LSL #2]!;将存储器地址为R1+R2*4的字数据读入寄存器R0,并将R1+R2*4的值存入R1。! r$ k3 j' |5 W( D* `! P
  11. LDR R0,[R1],R2,LSL #2     ;将存储器地址为R1的字数据读入寄存器R0,并将R1+R2*4的值存入R1。
    $ O3 t7 K7 `5 W' E2 J% N" j
  12. LDR R0,Label        ;Label为程序标号,Label必须是当前指令的-4~4KB范围内。
复制代码
  G+ ?+ C/ R' Z: p( F7 m' y  W9 t, o( Y
要注意的是:
8 X- k: M9 {: T1 D  H
  • LDR Rd,[Rn],#0x04 ;这里Rd不允许是R15。
    $ O3 \0 U3 v" L. ?; e
另外LDRB 的指令格式与LDR相似,只不过它是将存储器地址中的8位(1个字节)读到目的寄存器中。LDRH的指令格式也与LDR相似,它是将内存中的16位(半字)读到目的寄存器中。
; i5 U; L- c! C& z1 N7 l! s
  • LDR R0,=0xff
    , k( R' H' f7 M
这里的LDR不是arm指令,而是伪指令。这个时候与MOVE很相似,只不过MOV指令后的立即数是有限制的。这个立即数必须是0X00-OXFF范围内的数经过偶数次右移得到的数,所以MOV用起来比较麻烦,因为有些数不那么容易看出来是否合法。
' \. v! b- R7 w" o  C
2、如何在KEIL下阅读汇编- i; ?3 f2 o# s" K# V( N3 ?
按d进入debug模式,在view下选择disassembly window 。
/ Z& |- M  r/ r2 ^0 K% T" Q8 b# o7 w
微信图片_20201123205435.png
/ g8 Z9 H- y+ m0 h/ s
看光标,c文件下指向了main函数的第一行。
' L2 {0 G7 C# @1 R
汇编窗口也指向了对应的语句。但是,在执行C语言的第一行之前,仍然有许多操作要做,比如变量放在哪?在哪里调用了main函数等,这些操作都被集成开发环境IDE给封装起来了。我们必须知道,在执行main函数之前,有许多事情要做,只不过,初学的时候不必理会。以下是C语言源码,功能是点亮LED。8 X+ q; V% @1 x+ \2 A
  1. * [' L8 I% k& [2 w
  2. //main.c
    % ]6 D0 C! p8 l3 q, Q% t
  3. #include   
    % M  r* t- B. I- ~6 h; s/ e( P& t

  4.   ]7 n  I- u- e8 v" O
  5. int main(void)
    ; b1 m1 J7 I( v! }4 b/ j# K
  6. {# y! ]' s$ Y% x8 v3 M
  7.     RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;' ]! x* e2 v# T( I& U# _4 N

  8. ! a& g2 y" `3 e! }* Q/ u
  9.     GPIOB->CRL &= ~(0xf<<(1*4));' F7 d+ |0 z4 P9 n) T" f9 H: ]
  10. " {2 Y3 c" M. r5 r
  11.     GPIOB->CRL |= 0x2<<(1*4);     
    1 V! g! J6 }6 T  n

  12. $ ?! M. a  F" k
  13.     GPIOB->ODR &= ~(1<<1);
    8 i6 i9 T6 ]6 }. w0 `2 @; H

  14. + z1 \) _2 q$ Y! z* |: T8 j% U( \* T
  15.     return 0;            
      r7 x0 o/ V  ?) r) F) H+ G; w8 X
  16. }: S) _1 X* N* u$ d, Q

  17. * G# O+ M; P# D" q
  18. //main.h
    # x2 ^& Q/ k! c" Y  [
  19. #define RCC_APB2ENR (*(unsigned int *)0x40021018)3 d. }5 T  E/ K6 f+ I% b
  20. #define GPIOB_CRL (*(unsigned int *)0x40010c00)
    ) y3 b- \  e' _* l* a( ~
  21. #define GPIOB_ODR (*(unsigned int *)0x40010c0c)
复制代码
* i, a: e% w1 y+ c7 s& i& ^0 V% {
汇编窗口往上翻,确实很多语句,先看这几行代码的汇编:
; n* m- ~  T% h+ E8 [
微信图片_20201123205438.jpg
$ D, n% L% i  E6 m7 e
先说最常用的两句汇编:
! |  y1 b% _0 k! H! S6 @8 U
  1. # ~& ^2 e1 {% I' N4 F: Z# J
  2. LDR r0,[r1]    r0 = *r1
    1 @1 {) `9 @$ C" o8 v
  3. 8 E2 e% o+ M$ N; P* h: `
  4. STR  r0,[r1]    *r1 = r06 R$ O: H! M* v+ ?' @
  5. 1 i$ m9 l2 x, _
  6. MOV r0,r1    r1->r0拷贝
复制代码

% m$ M& T+ t. i( W
1 x. P4 W- p# k6 L
微信图片_20201123205442.png

/ @9 ^3 \$ I  n. r1 K; q6 D2 ?
从内存0x0800 017c的32位数据拷贝到r0:
, Y+ a' {1 m' h+ Z+ T, P! N$ ]
  • r0 = * 0x0800 017c
    0 z7 r: E# F& N- ]+ Z$ @# ~  ?9 m& K
我们看到的 1000 4002其实 就是0x4002 1000。这里边有个知识点叫做大小端模式,以下简单讲解,不能理解就记住。 4 n( U% p. }9 M) R* G/ v3 l4 ^
微信图片_20201123205445.jpg . \5 V2 T6 j6 q) Q, M! u
这个数据是在地址是这么存放的:
" r0 Y6 n4 Z$ v
  7C 7D 7E 7F   00 10 02 40 & F( z2 s. k& p  U" M0 G6 G
实际数据是0x4002 1000- d. S8 A3 ?3 B0 ^  ?0 U
  •  * 0x0800 017c=0x4002 1000
      m5 e- j+ @8 s* e" l; l
然后r0的值+0x18也就是24 因为这个是第6号(第6号就是第7个的意思)元素
3 L( h. }- V9 z0 [
得到r0 = *0x4002 1018,r0的值由一个地址,变成了地址所存放的数据。 " u7 ^" ~" P5 U
然后是或0x08操作,结果再复制给r0,*0x4002 1018 |=0x08
, b4 @; P8 f8 ~) c# Z5 `7 w
给r1分配地址,这个地址也是0x4002 1000, r1 = *0x4002 1000 ; G! B3 t& s: E) b* L6 Z; V
把r0存放的值,(不是r0的地址,)存到r1+18的空间上
$ a! K  F6 W& \7 \6 @
  *(r1+0x18) = r0   *0x4002 1018 = (*0x4002 1018 |=0x08)   *0x4002 1018|=0x08 + q/ |* d6 ^: u
最终结果:地址4002 1018的数,执行了或0x08的操作。再分析下一句 :
! B: ?/ B7 [; _6 q: i 微信图片_20201123205448.jpg
0 [" }) M" C! Y4 O/ J
前两句给r0分配空间,r0 = *0x4001 0c00 ( F$ h3 ~7 |, ^- e9 M* g! h0 c
然后用BIC清除数据位,把4-7位清零,结果再赋值给r0。& T1 I+ q% K4 ~; i

  1. & W0 ^4 X1 a$ O# R! L' g. ~7 m
  2. *0x4001 0c00 &= ~(0xf0) 7 k4 Z1 Y# J+ P  g0 H: s* u0 ?
  3. r1 = *0x4001 0c00
    - E5 L, V9 ?+ t
  4. *0x4001 0c00 &= ~(0xf0)
复制代码

' i1 Z9 Q4 q8 M( I. ?
剩下的不再详细分析,直接给答案 :
微信图片_20201123205450.jpg
9 y9 e8 E7 w1 H
  1. $ U+ k( `' q: n
  2. ***0x4001 0c00 |= 0x20 0 i/ Z1 ?( [+ m2 U5 x8 x2 s1 U
  3. 0x4001 0c0c &= ~(0x02)*
复制代码

4 T  A: U& W% v' v+ o2 O
最终,可以看到C语句被翻译成了意料之中的汇编语句,自己的意图被机器准确的理解了。) _0 t( t; D# L# S$ `# r
! P+ ~( A$ G$ Y# t9 Y7 b8 \/ M) z: U
收藏 评论0 发布时间:2020-11-23 20:55

举报

0个回答

所属标签

相似分享

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版