本章主要介绍MCS-51单片机的汇编语言和一些常用的汇编语言程序设计方法,并列举了一些具有代表性的汇编语言程序实例。通过对程序的设计、调试和完成,读者可以加深对指令系统的了解和掌握,这是学好单片机的基础。
上一章介绍了MCS-51单片机的指令系统,这些指令只有按工作要求有序地编排为一段完整的程序,才能起到一定的作用,完成某一特定的任务,这些程序称为汇编语言源程序。MCS-51单片机的汇编语言与其他微处理器的一样,是以助记符的形式书写的程序语言。一条完整的汇编语言指令通常由标号、操作码、操作数和注释组成。伪指令为程序提供了必要的信息与参数。用户编制的源程序必须通过汇编后才能生成机器可以执行的目标程序。
计算机程序设计语言是指计算机能够理解和执行的语言,它通常分为机器语言、汇编语言、高级语言3大类。机器语言是一种能为计算机直接识别和执行的机器级语言。汇编语言是一种人们用来替代机器语言进行程序设计的语言,由助记符、保留字和伪指令等组成,很容易为人们识别、记忆和读写。采用汇编语言编写的程序叫做汇编语言源程序。该程序虽然不能为计算机直接识别,但它可由“汇编程序”翻译成机器语言程序(即目标代码),该过程称为“汇编”。高级语言是面向过程和问题的并能独立于机器的通用程序设计语言,是一种接近人们自然语言和常用数学表达式的计算机语言。常用的高级语言有BASIC、FORTRAN、COBOL、PASCAL、ALGOL等。
计算机程序设计语言是指计算机能够理解和执行的语言,它随着计算机的诞生而诞生,随着计算机的发展而发展。迄今为止,计算机程序设计语言很多,但通常分为机器语言、汇编语言和高级语言3类,现对它们的性能特点分析如下。
(1)机器语言(Machine Language)是一种能为计算机直接识别和执行的机器级语言。通常,机器语言有两种表示形式:一种是二进制形式;一种是十六进制形式。机器语言的二进制形式由二进制代码“0”和“1”构成,可以直接存放在计算机的存储器内;十六进制形式由0~9、A~F共16个数字符号组成,是人们通常采用的一种形式,它输入计算机后由监控程序翻译成二进制形式,以供机器直接执行。机器语言不易为人们识别和读写,用机器语言编写程序具有难编写、难读懂、难查错和难交流等缺点。因此,人们通常不用它来进行程序设计。
(2)汇编语言由助记符、保留字和伪指令等组成,很容易为人们识别、记忆和读写,故有时也称为符号语言。采用汇编语言编写的程序叫做汇编语言源程序,该程序虽然不能为计算机直接执行,但它可由“汇编程序”翻译成机器语言程序(即目标代码)。汇编程序(assembler)由计算机软件公司编写,可以驻留在微型计算机开发系统的程序存储器内,也可以存放在软磁盘或硬磁盘上,使用时调入系统机内存。
汇编语言并不独立于具体机器,是一种非常通用的低级程序设计语言。采用汇编语言编程,用户可以直接操作到单片机内部的工作寄存器和片内RAM单元,能把数据的处理过程表述得非常具体和翔实。因此,汇编语言程序设计可以在空间和时间上充分发掘微型计算机的潜力,是一种经久不衰的广泛用于编写实时控制程序的计算机语言。
(3)高级语言(High-level Language)是面向过程和问题的并能独立于机器的通用程序设计语言,是一种接近人们自然语言和常用数学表达式的计算机语言。因此,人们在利用高级语言编程时可以不去了解机器内部结构而把主要精力集中于掌握语言的语法规则和程序的结构设计方面。采用高级语言编写的程序是不能为机器直接执行的,但可以为常驻内存或磁盘上的解释程序和编译程序等编译,编译成目标代码后才能为CPU执行。随着计算技术的飞速发展,高级语言不仅在类型和版本上有所扩大,而且在功能上也越来越接近于人类的自然语言。常用的高级语言有BASIC、FORTRAN、PASCAL和ALGOL等,它们都在按自身的规律发展。
根据题目要求,人们采用汇编语言编写的程序称为汇编语言源程序。这种程序是不能为CPU直接识别和执行的,必须由人工或机器把它翻译成机器语言才能为计算机执行。为了使机器能够识别和正确汇编,人们必须对汇编语言的格式和语法规则作出种种规定。因此,用户在进行程序设计时必须严格遵循汇编语言的格式和语法规则,才能编出符合要求的汇编语言源程序。
汇编语言源程序由一条一条的汇编语言语句构成。这就好像写文章,文章由语句构成,每个语句要正确,不能有病句和漏句,标点符号也要正确。因此,汇编语言源程序中的汇编语言语句也要正确,必须符合相应的语法规则。
汇编语言直接面向机器,因机器不同而异。对MCS-51来说,汇编语言中的每条语句应当符合典型的四分段格式,如图4-1所示。
标号段 (LABLE) |
操作码段 (OPCODE) |
操作数段 (OPRAND) |
注释段 (COMMENT) |
图4-1 四分段格式
式中,标号段和操作码段之间要有冒号“:”相隔;操作码和操作数段间的分界符是空格;双操作数之间用逗号“,”相隔;操作数段和注释段之间的分界符采用分号“;”相隔。操作码段是必选项,其余各段为任选项,这就是说任何语句都必须有操作码字段。
现结合如下程序进行分析。
0RG 0060H
START: MOV A,#00H ;A←0
MOV R2,#0AH ;R2←10
MOV R1,#03H ;R1←3
LOOP: ADD A,R1 ;A←A+R1
DJNZ R2,LOOP ;若R2-1≠0,则LOOP
NOP
SJMP $
END
这个程序共由9条语句组成。其中,第1、9两条是指示性语句(伪指令),其余为指令性语句。第2、5两条是四分段齐全的语句,第3、4、6这3条是默认标号段的语句,第7、9两条只有操作码字段。在第6条语句中,LOOP是一个符号,不是标号地址,实际上是一个相对地址偏移量,可以理解为$-LOOP。其中,$是指DJNZ R2,LOOP这条指令操作码所在内存单元的地址。为了进一步弄清汇编语句中各字段的语法规则,现结合本程序对各字段加以说明。
标号段位于一条语句的开头,用于存放语句的标号,以指明标号所在行指令操作码字节在内存的地址。标号又称为标号地址或符号地址,是一个可有可无的任选项。例如,上述程序中的START和LOOP皆为标号,分别指明了第2、5两条指令操作码字节的内存地址。标号由以大写英文字母开头的字母和数字串组成,长度为1~8个字符。标号长度超过8个字符时,汇编程序自动舍去超过部分的字符。为了避免机器错误地把标号中字符当作指令来汇编,用户在编写自己的程序时绝对不应采用指令保留符、寄存器号以及伪指令符等来作为语句的标号,而且同一标号绝不能在同一程序中的不同语句中使用。
操作码字段可以是指令的保留字(如上述程序中的MOV、ADD和NOP等),也可以是伪指令和宏指令的助记符(如ORG和END),用于指示计算机进行何种操作。操作码字段是任一语句不可缺少的,是必选项,汇编程序就是根据这一字段生成目标代码的。
操作数字段用于存放指令的操作数或操作数地址,可以采用字母和数字等多种表示形式。在操作数字段中,操作数个数因指令不同而不同,通常有双操作数、单操作数和无操作数3种情况。
在MCS-51单片机的汇编中,操作数通常有以下5种合法表示形式。
(1)操作数的二进制、十进制和十六进制形式:在大多数情况下,操作数或操作数地址总是采用十六进制形式表示的,只有在某些特殊场合才采用二进制或十进制的形式表示。若操作数采用二进制形式,则需加后缀B;若操作数采用十进制形式,则需加后缀D;若操作数或操作数地址采用十六进制形式,则需加后缀H。若十六进制的操作数以字符A~F中的某个开头,则还需在它前面加一个“0”,以便机器可以把它和字母A~F区别开来。例如,如下程序中的语句都是合法的。
ORG0500H
MOVA,#00110101B ;A←53
ADDA,#20D ;A←53+20
MOVR0,#20H ;R0←20H
MOVR1,#0BFH ;R1←BFH
SJMP $
END
(2)工作寄存器和特殊功能寄存器:当操作数在某个工作寄存器或特殊功能寄存器中时,操作数字段允许采用工作寄存器或特殊功能寄存器的代号表示。例如上例中的累加器A和工作寄存器R0~R7。
(3)标号地址:为了便于记忆和编程序时方便,操作数字段里的操作数地址常常可以采用经过定义的标号地址表示。例如,若地址M中有一个操作数X,且M已在某处作过定义,则如下指令是合法的。
MOV A, M
(4)带加减算符的表达式:在上例中,若M已在某处作过定义,则M+1和M-1都是可以作为直接地址来使用的。这就是说,如下语句也是合法的。
MOV A, M+1
MOV A, M+3
(5)采用$符:美元符$常在转移类指令的操作数字段中使用,用于表示该转移指令操作码所在内存地址。例如如下指令是合法的。
JNB TF0,$
该指令含义是:若TF0=0,则机器总执行该指令;只有TF0≠0时才继续往下执行程序(TF0是TCON中的一位)。
注释字段用于注解指令或程序的含义,对编写和阅读程序十分有利。注释字段是任选项,但选用时必须以分号“;”开头,一行不够写而需另起一行时也必须以分号“;”开头。在机器汇编时,注释段可以输入系统机,也可以不输入系统机。即使输入系统机,汇编时也不会产生机器码,但可以将原文输出到CRT显示器或打印纸上,供用户阅读和长久保存。当程序较长、较复杂时,在汇编语言源程序的适当位置上标上简练的英文注释,这对程序的交流和以后重读颇有方便之处。
分界符也称分隔符,是汇编语言语句的组成部分。汇编程序对汇编语言源程序汇编时,遇到不合法分界符就会出错停机,要求用户改正。因此,读者在编程时对每条语句中的分界符也不能掉以轻心,必须正确使用。标号字段中的冒号“:”用于指示标号字段的结束;操作数字段中的逗号“,”用于分隔两个操作数;注释段的开头用分号“;”,操作码字段和操作数字段之间应加空格。
汇编语言是汇编语言语句的集合,是构成汇编语言源程序的基本元素,也是汇编语言程序设计的基础。汇编语言因机器而异,常可分为指令性语句和指示性语句两类。
指令性语句是指采用指令助记符构成的汇编语言语句,它当然必须符合汇编语言的语法规则。对MCS-51单片机而言,指令性语句是指111条指令的助记符语句。指令性语句是大量的,是汇编语言语句的主体,也是人们进行汇编语言程序设计的基本语句。每条指令性语句都有与之对应的指令码(即机器码),并由机器在汇编时翻译成目标代码(机器码),以供CPU执行。
指示性语句又称伪指令语句简称伪指令。伪指令不产生相对应的操作码,但在汇编程序将汇编语言源程序汇编成目标程序时,伪指令起协助汇编的作用。伪指令用于规定程序地址、建立数据表格等操作。常用的伪指令有以下6种。
(1)ORG(origin)——汇编起始伪指令。
此伪指令常用于汇编语言源程序或数据块的开始。指令格式为:
<标号> ORG <16位地址或标号>
说明:ORG是伪指令助记符,前面的标号是可选项,通常省略。ORG后面常用16位绝对地址,也可以用标号或表达式。当汇编程序检测到该指令后,就把该指令后面的程序段从该伪指令指定的地址单元开始存放。
例如,在下面的程序中就规定了START开始的程序段从2000H单元开始存放。
ORG 2000H
START:MOV A,20H
【例4-1】 将目标程序从2000H单元开始存放。
解:程序如下。
ORG 2000H
MOV A,20H
……
(2)END——汇编结束伪指令。
该指令常用于源程序的末尾,用来指示源程序的结束位置。其语句格式为:
<标号> END <表达式>
说明:在该指令中,标号通常省略。END后面的表达式通常就是该程序模块的起始地址。
在交叉汇编过程中,当汇编程序检测到该指令,即认为源程序已经结束,END后面的指令将不予汇编。因此,在一段程序中只能出现一条END伪指令,且位于整个程序的末尾。
【例4-2】 下列伪指令汇编后,存储器相应地址里的内容是什么?
ORG 8000H
SUJU DB 09,12H,'A'
ZIFU DB'ABC'
解:存储器相应地址里的内容如下。
(8000H)=09H
(8001H)=12H
(8002H)=41H
(8003H)=41H
(8004H)=42H
(8005H)=43H
(3)EQU(equate)——赋值伪指令。
EQU伪指令用于给它左面的标号赋值。其语句格式为:
<标号> EQU <赋值项>
说明:“赋值项”可以是标号、地址、常数或表达式,EQU相当于“等价于”,一旦标号被赋值,则在程序中就可以作为一个数据或地址使用,即被赋值的标号可以作为地址,也可以作为立即数使用。
例如,
BLEE EQU 2300H
即表明在后面的程序中BLEE等价于2300H。
(4)DATA(data)——数据地址赋值伪指令。
与EQU伪指令类似,DATA伪指令也用于给它左面的标号赋值,其格式如下:
<标号> DATA <表达式>
说明:“表达式”可以是标号、地址、常数等。用DATA伪指令定义的标号在程序中也可以作为一个数据或地址使用。
例如,
BLEE DATA 2300H
即表明在后面的程序中BLEE等价于2300H。
DATA伪指令与EQU伪指令的主要区别在于:EQU伪指令定义的标号必须在使用前先定义(即先定义后使用),而DATA伪指令则没有这种限制。有的MCS-51型单片机汇编程序只允许用DATA伪指令定义8位的数据或地址,而用XDATA伪指令来定义16位地址的有效数据。读者可根据所选用的MCS-51型单片机汇编程序的具体情况合理使用。
(5)DB(Define Byte)——定义字节伪指令。
其格式如下:
<标号> DB <项或项表>
说明:“项或项表”是指一字节的数或字符,也可以是ASCII码字符串(但必须用引号引起来),该伪指令要求为“项或项表”分配一个或多个存储单元。
例如,
WAl DB 64H,4AH
其作用是:(WA1)=64H,(WA1+1)=4AH。
又如:
ORG 2000H
SEM DB1,2,C
其作用是:(2000H)=31H,(2001H)=32H,(2002H)=43H。
(6)DW(Define Word)——定义字伪指令。
其格式如下:
<标号> DW <项或项表>
说明:DW与DB的用法基本相同,区别在于DB定义的每一个数据占据一个字节,而DW定义的数据则是一个字,即两个字节。
例如,
ORG 2000H
MHC DW 1430H,5678H
上述程序汇编结束后,(2000H)=30H,(2001H)=14H,(2002H)=78H,(2003H)=56H。
【例4-3】 下面DW伪指令汇编后,9000H~9003H地址里的内容是多少?
9000H: DW 5566H,88H
解:9000H~9003H地址里的内容如下。
(9000H)=55H
(9001H)=66H
(9002H)=00H
(9003H)=88H
(7)DS(Define Storage)——定义存储区伪指令。
其格式如下:
<标号> DS 表达式
说明:表达式通常是一个常数。该指令用于从标号指定的存储单元开始定义一个存储区,存储区的长度由DS后面的表达式的内容来决定。
例如,
ABC DS 10
表示从标号ABC开始预留10个连续单元。
【例4-4】 用伪指令实现从8000H单元开始,预留连续16个存储单元,然后从8010H单元开始,按DB命令给内存单元赋值,即(8010H)=11H,(8011H)=22H。
解:程序如下。
ORG 8000H
SEG:DS 16
DB 11H,22H
(8)BIT——位定义伪指令。
该指令用于把位地址赋给符号地址。格式如下:
<字符名称> BIT <位地址>
说明:“位地址”可用位符号地址或绝对地址来表示。
例如,
ECW BIT P2.0
表示将P2.0的值赋给变量ECW,也就是说,在程序中对ECW的操作即相当于对P2.0引脚操作。
汇编的方法一般有两种:一种是机器汇编,另一种是人工汇编。
(1)人工汇编。
人工汇编是指利用人脑直接把汇编语言源程序翻译成机器码的过程,有时也称为程序的人工“代真”。人工汇编是历史沿革下来的,常常作为机器汇编的补充,因为人工汇编只需一张指令码表以及一支笔和一张纸就可开展工作。
通常,源程序的人工汇编需要进行两次才能完成,只有无分支程序才可以一次完成。对于包含有转移指令和标号在内的汇编语言源程序,第一次汇编完成指令码的人工“代真”,第二次汇编完成地址偏移量的“代真”。现对它们分述如下。
①第一次汇编。
第一次汇编时,用户应先确定源程序在内存的起始地址,然后在指令码表中依次找出每条指令的指令码,再从程序的起始地址开始逐一把它们写出来,对于一时无法确定其实际值的地址偏移量应照原样写在指令码的相应位置上,但对那些已定义过的“字符名称”应立即代真。
② 第二次汇编。
第二次汇编是第一次的继续,其任务是确定第一次汇编过程中未确定的标号或地址偏移量的值。由于每条指令码的起始地址已在第一次汇编过程中确定,因此确定标号或地址偏移量的实际值仅需进行一些简单计算便可完成。
(2)机器汇编。
人工汇编具有简单易行的优点,但它的效率低,出错率高,尤其在被汇编程序较长和较复杂时更是如此。因此,工程上的实用程序都是采用机器汇编来实现的。
机器汇编是一种用机器来代替人脑的汇编,是机器自动把汇编语言源程序(助记符形式)翻译成目标代码的过程。完成这一翻译工作的机器是系统机(如:IBM-PC/386),给系统机输入源程序的(助记符形式)是人,完成这一翻译工作的软件称为“汇编程序”。因此,机器汇编实际上是通过执行“汇编程序”来对源程序进行汇编的。机器汇编的原理和人工汇编类似,实际上是人工汇编的模拟,如图4-2所示。
图4-2 汇编程序的功能
为了实现对源程序的汇编,汇编程序中编入了两张表:一张是指令操作码表,另一张是伪指令表。“汇编程序”通过两次扫描完成对源程序的汇编:第一次扫描时对源程序中每条指令查一次表,并把查到的指令码存入某一内存区;第二次扫描完成对地址偏移量的计算,并用计算得到的值置换地址偏移量。因此,用户在机器汇编前应预先在系统机上输入被汇编的汇编语言源程序,并把它编辑好后存放到磁盘上,然后再启用“汇编程序”对它进行汇编,以生成源程序列表清单。源程序列表清单可以在CRT显示器或打印机上输出,也可以直接输入单片机内存或作为磁盘文件保存在磁盘上。
“汇编程序”是一种系统软件,有时也称为工具软件,因机器而异,常由计算机厂家提供。
用汇编语言进行程序设计与使用高级语言进行程序设计过程是类似的,同样需要按分析问题、确定算法、设计流程图和编写程序的步骤来进行。但是,汇编语言程序设计也有自己的特点。
(1)设计汇编程序时,设计者要对数据的存放、寄存器和工作单元的使用做出计划安排。
(2)汇编语言程序设计要求设计人员必须对所使用的计算机的硬件结构有较为详细的了解,尤其对寄存器、I/O端口、中断系统等硬件部件有深入的了解,这样才能够在程序设计中正确使用。
(3)汇编语言程序设计的技巧性较高,也比较烦琐,且有软硬件结合的特点。
MCS-51汇编语言程序设计是将该系列单片机应用于工业测控装置和智能仪表所必须进行的一项工作。一般来说,编写程序的过程可分为下述4个步骤。
(1)根据工作要求确定计算方法,定出运算步骤和顺序,把运算步骤画成流程图。
(2)确定数据和工作单元,分配存放单元。
(3)按所使用的计算机的指令系统,把确定的运算程序写成汇编语言程序。
(4)对所编写的汇编语言源程序进行汇编,转化成目标机器码以便在计算机上调试和运行,找出错误并改正。
在进行程序设计时,必须根据实际问题和所使用的计算机的特点来确定算法,然后按尽可能节省数据存放单元、缩短程序长度和程序运行时间3个原则编写程序。
程序设计的基本方法一般分为以下几种。
(1)自顶向下的设计方法,也称做分层设计,就是先考虑整体任务,然后把整个任务划分成若干子任务,每个子任务再划分成若干子任务,这样一层层地分下去直到最底层。
(2)模块化的设计方法,就是把整个程序分成若干较小的独立性很强的程序段(模块)后,分别进行设计和调试,最后再把它们连接起来。
(3)结构化的程序设计方法。任何复杂的程序都可由3种规范化的基本程序结构来组成。这3种基本结构分别是顺序结构、分支结构、循环结构。这3种基本结构的特点是都只有一个入口和一个出口,如图4-3所示。整个程序都用这3种基本结构设计的方法被称为结构化设计方法。
流程图是程序设计的重要工具,它用一些标准图形和符号来描述一个程序的执行过程,可以清楚地表达程序的结构和它们之间的联系,流程图的常用符号如图4-4所示。
(a)顺序结构 (b)分支结构 (c)循环结构
图4-3 基本程序结构
图4-4 程序流程图常用符号
本章将给出MCS-51单片机在工业应用中一些常用而典型的程序例子,读者通过对这些编程实例的学习,从中了解和掌握使用MCS-51汇编语言进行程序设计的基本方法和技巧。