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

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

[复制链接]
gaosmile 发布时间:2020-11-23 20:55

3 X  \( b5 q, ^2 }8 ]% X& A
* R5 y0 P# G+ `# a- T
不同的平台的汇编代码是不一样的,最早的汇编在50年代就发明了,比很多人的父母的年龄都大,老掉牙,不用学习怎么写汇编。一个公司有一个人知道怎么写汇编就够了。但要学习读汇编,为什么学习汇编?/ \+ Q- X% N: |
1、性能 ' U! @4 U; z6 U# [
直接翻译为机器语言,性能最高。优秀的C语言效率只能达到汇编的80%左右。其他高级语言跟汇编一比差得更远。语言越高级性能越差。很多bootloader和BIOS用汇编写,汇编操作的是电脑,手机刚刚上电时,硬件和初始化的那些命令,它们的性能的要求比较高,效率高开机速度更快。
4 M% r: A5 g* X) T/ }
分析问题
* j. A8 Z3 C- a5 o. W
个人认为,编程人与机器对话,我们写C,写JAVA,但是电脑并不认识这些语言,电脑只认识0和1;所以需要一个人来翻译这些语言,这个翻译官就是编译器,但是编译器不能百分之百准确的表达程序员的意思,也就是所谓的翻译有反义。例如,编译器为了性能好一点,可能会优化变量和语句,这个过程可能好心办坏事,把有用的操作优化了。因此只有看懂一些汇编语句,才能分析程序真正执行的流程。在问题难以定位的情况下,汇编可能是分析问题的最后一根稻草。 帮助理解硬件* p9 f( i1 g* R# x, h
有些学校的单片机课程是以汇编进行教学的,主要原因就是汇编更贴近硬件。不过我不赞成这种做法,C语言能快速做出一点东西,有利于学生在放弃之前,增加成就感,好坚持下去。但是汇编确实更贴近硬件。
6 l2 R1 d/ g6 Y8 _8 \% K# c; W
LDR指令
/ J. y; i* d/ w1 R5 `9 Q
为了便于理解下文,先介绍下LDR指令,其格式如下:
+ J  N3 D+ i/ s3 w8 A6 F
  • LDR{条件} 目的寄存器 <存储器地址>
    & ^/ C8 r# C! G
作用:将 存储器地址 所指地址处连续的4个字节(1个字)的数据传送到目的寄存器中。LDR指令的寻址方式比较灵活,实例如下:
; j9 z  `3 |; D! M- ]) y

  1. : m( x. Y6 w9 X( M2 g$ P
  2. LDR R0,[R1]   ;将存储器地址为R1的字数据读入寄存器R0。( ?, ~8 j0 H" y! G% w, G0 D' c& Z
  3. LDR R0,[R1,R2]   ;将存储器地址为R1+R2的字数据读入寄存器R0。
    % q9 y9 H( I: \9 |# X, a
  4. LDR R0,[R1,#8]    ;将存储器地址为R1+8的字数据读入寄存器R0。, X9 |( K& T9 e3 C/ j# o: X
  5. LDR R0,[R1],R2      ;将存储器地址为R1的字数据读入寄存器R0,并将R1+R2的值存入R1。$ q/ D8 [7 C* X( Y: d
  6. LDR R0,[R1],#8      ;将存储器地址为R1的字数据读入寄存器R0,并将R1+8的值存入R1。
    ( D' D# V0 X* K8 k
  7. LDR R0,[R1,R2]!    ;将存储器地址为R1+R2的字数据读入寄存器R0,并将R1+R2的值存入R1。
    # n4 l3 R2 |0 _3 R
  8. LDR R0,[R1,LSL #3]     ;将存储器地址为R1*8的字数据读入寄存器R0。2 \- Z! x2 b% i
  9. LDR R0,[R1,R2,LSL #2]   ;将存储器地址为R1+R2*4的字数据读入寄存器R0。
    6 d' F0 C7 w! C  s' K
  10. LDR R0,[R1,,R2,LSL #2]!;将存储器地址为R1+R2*4的字数据读入寄存器R0,并将R1+R2*4的值存入R1。
    % ^5 M9 Z. |* X$ G3 s- b6 r
  11. LDR R0,[R1],R2,LSL #2     ;将存储器地址为R1的字数据读入寄存器R0,并将R1+R2*4的值存入R1。
    7 @% H% n3 w7 n- w7 ^: f8 Z
  12. LDR R0,Label        ;Label为程序标号,Label必须是当前指令的-4~4KB范围内。
复制代码
  q/ r) D/ F; k: x' s7 Y
要注意的是:- o1 I+ [$ K: _7 d: Y2 l, T
  • LDR Rd,[Rn],#0x04 ;这里Rd不允许是R15。7 a! I, s+ k+ Z5 o7 j4 ?
另外LDRB 的指令格式与LDR相似,只不过它是将存储器地址中的8位(1个字节)读到目的寄存器中。LDRH的指令格式也与LDR相似,它是将内存中的16位(半字)读到目的寄存器中。% y" q3 ]6 i! L* [* H% I, E
  • LDR R0,=0xff
    $ G0 _$ m9 W6 }
这里的LDR不是arm指令,而是伪指令。这个时候与MOVE很相似,只不过MOV指令后的立即数是有限制的。这个立即数必须是0X00-OXFF范围内的数经过偶数次右移得到的数,所以MOV用起来比较麻烦,因为有些数不那么容易看出来是否合法。
" n" S( W+ q5 A, l8 b
2、如何在KEIL下阅读汇编$ A  W% e6 A3 ~/ w. _
按d进入debug模式,在view下选择disassembly window 。
2 W7 L4 O5 w# m. L7 L: \
微信图片_20201123205435.png
8 T- I+ ?2 S: U! c  _1 e
看光标,c文件下指向了main函数的第一行。 1 w' j8 K4 u- w& a( @$ c
汇编窗口也指向了对应的语句。但是,在执行C语言的第一行之前,仍然有许多操作要做,比如变量放在哪?在哪里调用了main函数等,这些操作都被集成开发环境IDE给封装起来了。我们必须知道,在执行main函数之前,有许多事情要做,只不过,初学的时候不必理会。以下是C语言源码,功能是点亮LED。
$ H  j1 `, Z* x6 L

  1. 1 C* J3 e* D! M4 p6 ^' [5 p: U
  2. //main.c3 U% O9 S1 P" t0 {
  3. #include   $ i9 K: `' C7 c9 {0 @* ^& b4 O& k' Q

  4. ' q  W4 U# C, r! ?3 s$ H% O3 R$ I
  5. int main(void)
    7 |7 o% g6 o; X# `2 c% L0 t, I: d$ A3 \
  6. {
    3 @2 E, J" v6 q% `7 h  {$ B: n& Y# u
  7.     RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
    - l, J% Z! @, A' P

  8. 2 Q4 L9 q( t/ ^. h7 w8 D" o
  9.     GPIOB->CRL &= ~(0xf<<(1*4));2 V, W8 T9 Q* K- m9 t; b/ m

  10. $ ]9 P, M. `9 j* A& {
  11.     GPIOB->CRL |= 0x2<<(1*4);     
    " h! K2 h* O1 f, u
  12. ! m  H' C0 J+ k
  13.     GPIOB->ODR &= ~(1<<1);9 s( i0 `7 b+ s0 z/ g' B

  14. ' t) [3 z( `5 g: _  v8 c9 O" F: c& y
  15.     return 0;            
    , t: S4 B' z+ W/ _" S3 u( s
  16. }
    2 Z: [( `! ?8 ]" G$ R

  17. 0 ^& f: e4 }" q1 I" \: J  P( B
  18. //main.h
    + Q" [( c' u7 n2 u
  19. #define RCC_APB2ENR (*(unsigned int *)0x40021018)8 Q4 a! E7 c7 o' Q% x. }
  20. #define GPIOB_CRL (*(unsigned int *)0x40010c00)1 [. @8 K! `& H, ^& `# M/ R) o
  21. #define GPIOB_ODR (*(unsigned int *)0x40010c0c)
复制代码
7 f# f+ E, H2 W7 ~
汇编窗口往上翻,确实很多语句,先看这几行代码的汇编:2 O0 L3 _+ z  X: ^. S
微信图片_20201123205438.jpg
/ G4 o  ~- }+ @
先说最常用的两句汇编:
: g  |& v! T9 r+ Q7 ]1 S# @# _) Y2 q
  1. 7 H; J2 q2 f- p; t) d
  2. LDR r0,[r1]    r0 = *r1- }) B: Q5 ]) C7 N
  3. , i7 K( U3 ?+ m! h& S7 P& M4 c/ @
  4. STR  r0,[r1]    *r1 = r0
    3 ~( B- U& F, u2 y& p# Z$ q
  5. # w! R) [* b- @. u
  6. MOV r0,r1    r1->r0拷贝
复制代码

3 E& E; q9 [0 @) L
) Y. b& K+ e" B9 g3 n
微信图片_20201123205442.png
  p' v4 W0 v; a" H& \* M
从内存0x0800 017c的32位数据拷贝到r0:
6 H2 Z9 e# D# z9 K$ R% N' O3 D
  • r0 = * 0x0800 017c
    . y& ?( ?9 N; U) Y9 X
我们看到的 1000 4002其实 就是0x4002 1000。这里边有个知识点叫做大小端模式,以下简单讲解,不能理解就记住。
4 _$ p5 A* `, \1 g  W& b" n
微信图片_20201123205445.jpg $ ], E4 ]. U% _5 {8 A
这个数据是在地址是这么存放的: 0 |0 P" Q8 [! L5 H
  7C 7D 7E 7F   00 10 02 40 0 E3 Q% v8 y! X0 d& C; H6 d" |
实际数据是0x4002 1000; D0 D' h3 k6 F$ e9 I
  •  * 0x0800 017c=0x4002 1000
    ' U) d1 e6 y2 \3 K9 a2 e) f
然后r0的值+0x18也就是24 因为这个是第6号(第6号就是第7个的意思)元素 . O, M/ F9 V' }2 |8 p
得到r0 = *0x4002 1018,r0的值由一个地址,变成了地址所存放的数据。 : _+ U1 E3 }& ^8 G8 h
然后是或0x08操作,结果再复制给r0,*0x4002 1018 |=0x08
( [4 n( g9 e+ {- ~" V+ n6 D
给r1分配地址,这个地址也是0x4002 1000, r1 = *0x4002 1000   [1 w" X$ |/ \0 @1 U
把r0存放的值,(不是r0的地址,)存到r1+18的空间上 2 V& i. x! M+ d* b/ \% z( D* H5 Y
  *(r1+0x18) = r0   *0x4002 1018 = (*0x4002 1018 |=0x08)   *0x4002 1018|=0x08 # r% w2 c( F# l: B- z/ F
最终结果:地址4002 1018的数,执行了或0x08的操作。再分析下一句 :" T( T# }# l/ S& C
微信图片_20201123205448.jpg
& M5 U* O/ h7 T2 j
前两句给r0分配空间,r0 = *0x4001 0c00 & _, ~. f# e3 M; e2 u
然后用BIC清除数据位,把4-7位清零,结果再赋值给r0。7 P8 `, U8 q: ^/ D

  1. ' k' r4 f7 B9 Z6 B1 n$ w
  2. *0x4001 0c00 &= ~(0xf0)
    ; \) E' q6 a6 M( ]
  3. r1 = *0x4001 0c00
    ; e1 M0 _5 t6 W" _0 p' P
  4. *0x4001 0c00 &= ~(0xf0)
复制代码

% R% P! T+ J4 f3 F  N
剩下的不再详细分析,直接给答案 :
微信图片_20201123205450.jpg
% b/ s* n2 C# X

  1. ' P$ c! b5 F$ [1 O8 y
  2. ***0x4001 0c00 |= 0x20 ! T* v0 g- m: [7 g, T1 r/ t/ [
  3. 0x4001 0c0c &= ~(0x02)*
复制代码
* I. v& H. [8 J& l0 c: b
最终,可以看到C语句被翻译成了意料之中的汇编语句,自己的意图被机器准确的理解了。
0 f' X: M/ s7 [0 z( |9 {' ?/ y9 M! _- l( L; V; N
收藏 评论0 发布时间:2020-11-23 20:55

举报

0个回答

所属标签

相似分享

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