折腾来折腾去

pikipity的blog

汇编语言初学者遇到的困难和解决方法

这是一篇汇编初学者的总结,欢迎指正与讨论,以下讨论都以8051单片机的汇编语言为基础进行讨论。

使用汇编语言也有一段时间了,由于汇编语言是非常底层的语言,用惯了C或是C++之类的高级编程语言,突然开始使用这种与硬件紧密结合的语言会有很大的难度,无论是从分析、理解还是撰写。总结起来,对于一名汇编语言的初学者会遇到的最大的困难无非是3个:

  1. 汇编语言本身命令有限且难以理解:

    C与C++的大部分语句都是以函数的形式存在,把每个函数当做一个黑箱,只要搞清每个函数的输入输出即可了解程序本身,而且函数众多,几乎涵盖了可以想到的所有基础操作。

    但是汇编却不一样,每一个命令行不是单纯的“输入输出”的黑箱作业,会牵扯到很多隐藏的bit操作,并且命令有限,一个在C中简单的加减法的实现都要好多行,常常让初学者算法心中有数却不知如何用什么命令实现。

  2. C的编写逻辑不再适用:

    虽然C是强调过程的语言,不像Python等脚本语言是强调对象的语言,但是在编写的时候,我们更多思考的是对一个变量如何如何。但是汇编则完全不行,我们很难对一个地址中的内容做直接操作,需要跳转,在这种跳转极易出错

  3. 汇编语言运算基础是二进制:

    困难最大的一点,C中只要数据类型是正确的,那么以十进制的思维考虑就可以了。但是汇编中只有二进制,二进制在生活中很少用到,思考难度很大,常常无法想的全面,并且一些二进制独有的特性往往被忽略掉

那么对应这三个问题,我们应该如何解决呢?这里列出了我的解决方案:

  1. 熟悉汇编命令:

    对于汇编语言,命令少,既是缺点也是优点。缺点上面已经列出,优点就是极其容易做到全局把握。全局把握就是说,不是要把每条命令是什么都背过但是至少要做到以下几点:

    • 至少把每条命令都看过一遍,做到看到这条命令就可以回想起这条命令涉及的内存地址有哪些。
    • 熟悉汇编命令,做到有哪些命令心中有数,比如,一说到跳转命令就马上联想到AT89C51 datasheet中architectural overview部分中的boolean and jump两个table中有相关的指令,这样就可以快速查询了

    做到这些之后就不会面对着自己辛辛苦苦画出的程序设计流程图茫然无措了

  2. 汇编的编写逻辑基本是这样的:

    1. 数据存放入内存
    2. 将数据移入Acc或是B中
    3. 在Acc或是B中操作
    4. 将结果存回内存中

    只要按照这样的思路,就可以很容易的理解和写出汇编中数据操作的部分了,虽然过程复杂了一点,在C中一行就可以的搞定的,往往在汇编中要4、5行才行,但是这就是C语言那一行命令的本质。接下来,就是C语言中if, for, while在汇编中如何实现了,其实基本都对应了固定的程序块,只要记住就可以直接拿来是要了。这里列出常用的程序块写法

    • XXX 循环n次

          MOV 存放循环次数的地址,#n
      L1: XXX
          DJNZ 存放循环次数的地址,L1       
      
    • 执行 XXX 从i=x到i=y,也就是for循环

          MOV 起始数值存放地址,#x
          MOV 终止数值存放地址,#y
          INC 终止数值存放地址
      L1: XXX
          INC 起始地址
          MOV A,起始地址
          CJNE A,终止数值存放地址,L1
      
    • if x=y then XXX else YYY

          XCH A,x的存放地址
          CJNE A,y的存放地址,L1
          XCH A,x的存放地址
      L2: XXX
          JMP L3
      L1: XCH A,x的存放地址
          YYY
      L3:        
      
    • do XXX while x>=y

      L1: XXX
          XCH A,x的存放地址
          CJNE A,y的存放地址,L2
      L2: XCH A,x的存放地址
          JNC L1
      

    还有一种,能够让逻辑清晰的方法,就是写出数学表达式。数学表达式的优点就是每一步的运算都逻辑清晰,并且对于二进制,十进制的基本运算的运算方法还是适用的,比如加法结合律还有乘法分配律,所以只要列出数据的二进制数学表达式,那么逻辑自然明了了。

  3. 为什么汇编语言用2进制就让人难以计算和想象了呢?主要原因是人们会习惯性地按照十进制“位”的概念以bit为计算单位,在进行数据操作的时候,动辄就是8位,16位,就相当于十进制里的“千万”、“千万亿”,如此大的数量总会让人头昏眼花。但要注意的是单片机里已经提供了一些硬件运算,比如AT89C51就提供了8位二进制的硬件加减乘除,我们应该以这些基本运算为前提来搭建自己的程序,也就是说我们思考的单位不应该是bit,而是硬件的硬件计算单位,比如AT89C51就以8位为思考单位就会大大减少思考量。当然还是需要具体问题具体分析的,如果以3位为思考单位会更简单就用3位,5为简单就用5位,这也就是为什么二进制里为什么提出byte概念的原因吧。

    总的来说,就是以“byte”来思考二进制,而不是bit

最后介绍两种学习汇编的方法

  1. 跟编译器学习,比如使用Keil的时候,先用C编写程序,然后编译,就可以查看编译器产生的汇编语言了,虽然非常难懂,但也不失为一种向高手学习的方法。在Keil上查看相关汇编的方法如下(下图为Keil uVision3)

    1. 编写C程序,并编译成功,测试成功

      编译成功

    2. 点击工具栏上的红色D “start/stop debug session”

    3. 然后再点击工具栏上的 “Disassembly window”

      就得到了编译产生的汇编文件,并且C语句和相对应的汇编语句对应排列。

      汇编文件

  2. 如果可以上网,就比较方便了,去Github看别人写的程序,经常自己苦恼上好几天的程序,也许别人已经提供了范例在那里,不要抄,看思路,那不是最好的,最好的程序永远是自己写出来了

接下来,就是如何将方法运用到解决实际问题中去了,这里pikipity自己编写的一些程序了。



Comments