单片机汇编语言学习

一、写在开始之前

        现在我们已经很少能够接触到汇编语言了,汇编使用起来很麻烦,也不太好理解,而且平时大家学习的用的基本上都是过程或者面向对象的高级语言,比如C、C++、Java、Python等,我也是由于项目需要才有机会接触到汇编,而且是单片机的汇编语言,相对比较简单,常用的指令很少,不复杂,当然也不能用来实现太复杂的功能,只能用来做点消费类的电子小产品,通过这次的项目,1K的空间,也能够实现红外接收控制、EEPROM驱动存储、PWM实现对应的RGB彩灯效果,初步看来,用汇编编程相比于C语言真的很省代码空间,每段语句运行的时间效率也能够很清晰的统计出来。

        程序编写大致可以分为以下的几个部分:文件、关键字、变量、算术运算、逻辑运算、流程判断、函数调用,其实我们只需要弄清楚上面的这些内容在汇编里面是怎么实现的,那么我们想用汇编实现想要的功能,就很轻松了。下面我也将按照如上的内容一一说明。

二、开始学习

  1. 汇编文件

       C语言中我们常用的两种文件类型分别是.c和.h,C文件用来做为函数实现,H文件用来作为函数声明,宏定义等,通过#include就能够包含该头文件,进而可以使用该头文件中的宏定义、调用声明的函数。那么汇编中是否也有类似的文件呢?汇编也提供了两种文件类型,一种是.asm源文件、一种是.inc文件,asm就是汇编源文件,主要的逻辑代码都可以在这个文件中实现,而子程序、变量、宏定义等内容就可以放到inc文件中,并且也是通过include包含inc文件。

            示例如下:

             

            通过这种方式,汇编编程也可以模块化,把独立功能的模块实现单独放到一个inc文件中,使代码更清晰便于维护。

            备注:1. 某些平台下,包含文件之间一定要有空行,否则会出现编译错误,比如:调用的函数未定义、符号未定义等编译问题。2. 某些平台的编译器不能把.inc文件加入到IDE中,否则会报编译错误。

  1. 汇编指令集

            汇编指令就像C语言中的关键字一样,不同的指令可以实现不同的功能,汇编语言指令分为:指令和伪指令。

            指令是指CPU执行的依据,是可执行的代码,在执行阶段发挥作用。

            伪指令只是起辅助作用,为编译服务,是不可执行的代码,只在编译阶段发挥作用,编译完成后消失。我这次用的单片机指令集如下:

             

             

  1. 开头的ORG指令

            在汇编文件的开头,我们经常能够看到ORG指令后面再跟了一个数字的表示方式,示例如下:

             

            这个是什么意思呢,ORG是汇编的语言的一条伪指令,意思是把下面的一段代码编译到指定地址开头的代码段。这里就涉及到芯片的存储结构了,这个平台的芯片存储结构如下:

             

                由上图可知,芯片复位后,程序开始运行的地址是0000H,所以我们就需要把程序的起始函数定义的0000H地址,这样芯片复位后,程序才能正常运行。很类似于C语言中的main,只不过相关的处理在startup.s文件中已经处理好了。

                程序的中断向量地址为0008H,那么如果我们需要使用中断,就需要把中断函数定义到该地址,如果不定义,那么芯片产生中断后,就会跑飞。这个就相当于C语言中的中断函数申明,如果要使用就要申明,否则就会死机,如果不用,就不需要申明。

  1. 变量及赋值

            a) 变量声明

                 8位单片机中,声明的变量一般都是八位的,而且不支持浮点型变量,不关心正数还是负数,一切都是通过程序自行去处理。浮点数可以用整数去存储和计算,负数可以用最高位的符号位来表示。汇编中声明变量有两种方式:

                方法一:通过伪指令res实现:

                res大致意思是占用一个字节,这样可以连续定义变量而不需要关系这个变量处于那个地址,比较方便。

                方法二:通过伪指令equ实现:

                这种变量定义方式很常见,但是需要指定变量在RAM中的地址,如果删除一个变量或者要调整变量就需要修改注意地址是否会有冲突,使用起来不是那么方便。

                注意:不同的平台变量声明有所不同,请以平台为准

            b) 变量赋值

                在C语言中给变量赋值直接用=就可以了,非常方便,但是汇编中没有这样的符号,只能通过指令操作,查看指令表有三条指令是可以用来给变量赋值的:

                 

                根据指令说明,如果我们要把一个立即数(直接给出值那种),那么我们需要先把立即数放到W寄存器(存放操作数或计算结果的寄存器)中,然后在把W的内容传送到指定的变量中。

                比如需要把立即数0x12赋值给变量A,那么操作的代码如下:             

                 

                如果要把变量A赋值给变量B,操作的代码如下:

                 

                备注:在这里我们不考虑间接寻址,全是直接寻址操作,间接寻址没有用到,不去深究,有使用到了再说。

            c) 局部变量和全局变量

                现在很多国产单片机是没有局部变量和全局变量一说的,都是全局变量,因为要使用局部变量,那么就需要提供压栈出栈的指令才行,但是很多单片机是没有的。

  1. 算术运算-加减乘除

            编写程序过程中,少不了加减乘除这4个基本运算,其他复杂的运算都是建立在这个基础之上的,下面就以代码的形式实现加减乘除。

            a) 加

                加法运算,汇编里面直接提供了对应的指令,指令如下:

                 

                比如需要实现变量X+0x03,相加结果存到变量X中,在不考虑溢出的情况下,实现代码如下:

            b) 减

                减法运算,汇编里面直接提供了对应的指令,指令如下:

                比如要实现变量X-0x03,相减结果存到变量X中,在不考虑溢出的情况下,实现代码如下:

                 

            c) 乘

                大部分的国产单片机OTP单片机都没有提供乘法指令,只能通过其他方法实现。其实乘法是可以通过加法实现的,比如5*2,相当于两个5相加,根据这个思想我们就能很轻松的实现两个数的乘法了,那么X*Y,其实就是一共有Y这么多个X相加之和。在不考虑变量X*变量Y会产生溢出的情况下,乘法实现Result=X*Y如下:

                 

            备注:请先看6,7, 8章节后再来看乘法实现。

            d) 除

                同样的该款单片机也没有除法指令,所以也只能通过其他方法去实现。X除Y,其实就是看X中包含了多少个Y,比如5除2,那么5中包含了2个1,于了一个1,所以5除2只取整数部分的情况下,结果就是2,这个就和减法的功能就很相似了,如果X/Y,其实就是看X能够减多少次Y。参考这个思路,实现除法Result=X/Y如下:

                 

            备注:请先看6,7, 8章节后再来看除法实现。

  1. 关系运算

             在关系运算中,会用到状态寄存器STATUS中的C位和Z位,说明如下:

                 

            a) 大于

                这个平台没有直接提供比较指令,所以需要通过减法指令去实现,当一个数A大于另外一个数B的时候,那么A减B一定不会产生借位,但是A=B的时候,A减B也不会产生借位,所以这个时候就需要反过来,用B去减A,如果B减A产生了借位,那么B一定小于A。判断A大于B实现如下:

                

            b) 小于

                小于其实可以直接转换为大于,比如A<B,其实就是判断B>A,所以直接参考大于的实现。

            c) 等于

                等于其实就是一个数减去另外一个数结果为0,所以当A-B=0的时候,那么可以判断这两个数是相等的。(不考虑浮点数啥的),在汇编里面有一个运算结果是否为0的指示位Z,我们可以直接判断Z位来判断A是否等于B:

                                 

            d) 不等于

                不等于其实就是A-B不等于0,判断方式和判断等于的方式一致。

    

  1. 流程语句

            a) if else

                一般的条件语句都是结合关系运算结果然后执行对应代码,结合判断跳转指令,则可以实现if else的功能。

                 

            b) switch case

                switch语句要根据条件分支的变量是否连续采用不同的实现方式,如果条件分支的变量不连续,那么就采用最通用的做法,用多个if去判断。

 如果条件分支语句的值连续,那么可以通过操作程序指针PCL来实现switch case操作。

                 

                PCL即程序指针的高址节,通过对PCL的加减,可以让程序代码向前或者向后跳过指定条数的语句开始执行。注意:某些平台上当PCL相加或者相减溢出的时候,程序不会自动加减程序指针的高字节(俗称翻页),导致程序跑飞。如果出现这种情况,就需要把该部分代码挪动一下位置,不让PCL加减的时候产生翻页问题。

  1. 函数

            a) 函数声明

                汇编语言里面的函数不像C语言,可以带输入参数,输出参数,只有一个函数标签,可以通过goto直接跳转,也可以通过call命令调用。函数申明采用函数名称+冒号的形式,当然所有的函数名称不能重复,没有函数作用域一说。

                 

            b) 跳转或者调用

                1)goto 

                    goto 语句既程序直接跳转到指定位置开始执行,不会有程序指针压栈操作,可向前或者向后跳转。

                2)call

                    call即程序的调用,会有程序指针压栈操作,当程序返回的时候,程序指针会恢复。每一次调用都会占用一级堆栈,当多级嵌套调用的时候,需要注意程序指针堆栈级数,防止堆栈溢出,程序跑飞。

  1. 其他

            a) 数组实现

                1) 数组常量

                    数组常量可以通过PCL指令加RETLW指令(返回时将立即数传送给W)实现。就是通过操作PCL程序指针,返回对应的值就可以了。

                     

                2) 变量数组

                    这种只能通过类似DB、DW、DT等伪指令把数据定义到RAM区域,然后通过间接寻址操作。暂时没有用到过,不知道使用方法。

            b) 立即数类型

                在汇编编程中经常会使用到立即数,常用的二进制(B)、10进制(D)、16进制数(H),表示方式如下:

                 

            c) 宏定义

                汇编语言中也支持宏定义和C语言一样,关键字为#define 

                 

三、结尾

        这里只是对汇编语言编程做了一下简单的总结,其实汇编语言也不是那么难,只是思维方式不一样,没有高级语言那么直接。只要把高级语言编程过程中常用的实现方式和汇编一一对照,并实现一下,相信能够很快上手。当然每个平台的汇编指令有很大差异,实现方式有所不同,但是思维方式是一样的,不用过于担心。但是汇编的可移植性差就差在不同平台指令不一样。

  • 15
    点赞
  • 124
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值