首页 百科知识 汇编程序设计

汇编程序设计

时间:2022-10-22 百科知识 版权反馈
【摘要】:为便于人们使用而采用汇编语言来编写程序。本章按照标准汇编指令来分析指令系统的功能和使用方法。在汇编语言中,为了方便且和机器指令相对应,采用助记符来表示操作码,用符号或者符号地址来表示操作数或操作数地址。注释是程序员对该条指令或程序段的说明,给阅读程序带来方便,汇编程序不对它做任何处理。汇编语言程序不能被计算机直接识别并执行,必须经过一个中间环节把它翻译成机器语言程序,这个中间过程叫做汇编。

第3章 寻址方式和指令系统

【教学目的】

本章教学目的是了解指令格式,熟悉单片机执行指令的过程,熟练掌握单片机的各种寻址方式,编写简单的汇编程序。

【教学要求】

本章要求熟悉指令构成,理解和掌握指令的格式,掌握单片机的7种寻址方式,熟记各种类型的指令格式及功能,熟悉各类指令对标志位的影响,并能编写简单程序。

【重点难点】

本章的学习重点是指令的寻址方式、指令的操作功能。难点是指令的间接寻址方式、栈操作、算术运算、跳转指令。

【知识要点】

本章的重要知识点有指令的格式及标志、寻址方式、数据操作和指令类型,数据传送指令,算术操作指令,逻辑操作类指令,控制转移指令,位操作类指令等。

3.1 单片机指令系统

通过前面的学习,我们已经了解了单片机内部的结构,并且也已经知道,要控制单片机,需要通过指令来完成。计算机通过执行指令序列来解决问题,因而每种计算机都有一组指令集提供给用户使用,而计算机所能执行的全部指令集合就称为计算机的指令系统。

指令是根据计算机硬件特点研制出来的,指令系统与计算机硬件有着相对应的关系,用指令对计算机进行控制能够充分利用计算机的硬件资源。目前,一般小型和微型计算机的指令系统可以包括几十至百余种指令。

机器语言指令用二进制码表示,是CPU唯一能直接识别和执行的指令,但是不便于人们理解。为便于人们使用而采用汇编语言来编写程序。汇编语言的指令是一种符号指令,主要由助记符、符号和数字等来表示,它和机器语言指令是一一对应的,它通过汇编程序将其翻译成机器指令代码(目标代码)来控制CPU完成相应的功能。本章按照标准汇编指令来分析指令系统的功能和使用方法。

3.1.1 单片机指令系统

我们已经知道,计算机只能识别二进制代码,所以机器指令是由二进制代码组成的。为便于人们使用,通常采用汇编语言来编写程序。在汇编语言中,为了方便且和机器指令相对应,采用助记符来表示操作码,用符号或者符号地址来表示操作数或操作数地址。汇编语言指令语句格式如下:

标号:操作码【操作数1】,【操作数2】;注释

例如,把立即数F0H送累加器的指令为:

  MOV A,#0F0H        ;立即数F0H(A)

标号是用户定义的符号,实际意义代表当前语句在程序存储器中的存放地址。标号可以缺省,以字母开始,后跟1~8个英文字母或数字,并以冒号结尾。程序中调用或跳转时直接利用标号即可,标号名称应尽量使用与该段程序内容相关且有意义的英文单词或汉语拼音等。

操作码也称为指令助记符,是指令名称的代表符号,汇编语言中由英文单词缩写而成,反映指令的功能。它是指令语句中的关键字,不可缺省,表示指令的操作类型,必要时可以在前面加上一个或多过“前缀”,从而实现某些附加操作。

操作数是参加本指令运算的数据或数据存放的地址。一条指令可以没有操作数,也可以有多个操作数。操作数与操作码之间至少有一个空格隔开,而多个操作数之间必须用逗号(,)将其分开。有些操作数可以用表达式来表示。

注释是程序员对该条指令或程序段的说明,给阅读程序带来方便,汇编程序不对它做任何处理。注释可以缺省,注释必须用分号(;)开头。

汇编语言程序不能被计算机直接识别并执行,必须经过一个中间环节把它翻译成机器语言程序,这个中间过程叫做汇编。汇编有两种方式:机器汇编和手工汇编。机器汇编是用专门的汇编程序,在计算机上进行翻译;手工汇编是编程员把汇编语言指令通过查指令表逐条翻译成机器语言指令。现在主要使用机器汇编,但有时也用到手工汇编。

3.1.2 单片机指令格式

在介绍单片机的指令格式之前,我们先了解一些特殊符号的意义(见表3-1),在后面的指令介绍中将直接使用这些特殊符号。

表3-1 特殊符号意义表

续表

操作数字段可以只有一个、两个或多个,分别对应于一地址、二地址或三地址指令。目前大部分运算型指令都是双操作数指令,前后两个操作数分别称为目的操作数和源操作数。对于有些指令,不仅给出参加运算的两个操作数,还给出了运算结果的存放位置,这种就称作三地址指令。

1.单字节指令

单字节指令只有一个字节,由8位二进制编码表示。操作码和操作数在一个字节中。

例如:MOV A,Ri      ;Ri→(A)把寄存器Ri中的内容送到累加器A中去。

2.双字节指令

双字节的编码由两个字节组成,其中一个字节为操作码,另一个字节为操作数。该指令存放在存储器时需占用两个存储器单元

例如:MOV A,#data     ;data→(A)把立即数data送到累加器A中

在MCS-51汇编语言指令中,立即数前面必须有符号“#”。

3.三字节指令

三字节指令格式中第一个字节为操作码,其后两个字节为操作数。操作数可以是数据,也可以是地址。

例如:MOV direct,#data  ;data →(direct)把立即数data送到内存单元或者特殊功能寄存器中

3.2 寻址方式

根据指令格式,指令的执行就是通过对操作数执行操作码的相应操作来实现。要正确执行指令,就必须能得到正确的操作数和操作码。

指令的操作码字段在机器里的表示比较简单,只需对每一种操作指定确定的二进制代码就可以了。指令的操作数字段的情况就比较复杂,操作数可以存放在寄存器里,也可以放在存储器里,同时由于数据的存放、传送和运算都需要通过指令来完成,程序员必须由始至终都十分清楚操作数的位置。因此,指令用什么方式来获取存放操作数的空间位置和提取操作数就显得非常重要,它会影响到机器运行的速度和效率。

寻址方式就是指令中用来找到存放操作数的地址并把数据提取出来的方法。在带有操作数的指令中,数据可能就在指令中,也有可能在寄存器或存储器中。对这些设备内的数据要正确进行操作就要在指令中指出其地址,寻找操作数地址的方法称为寻址方式。51系列单片机指令系统的寻址方式有以下7种。

3.2.1 立即数寻址

在这种寻址方式中,指令中跟在操作码后面的一个字节就是实际操作数。该操作数直接参与操作,所以又称为立即数,用符号“#”表示,以区别直接地址。在MCS-51汇编语言指令中,立即数前面必须有符号“#”。

例:MOV A,#0FFH     ;FFH→(A)将立即数FFH送入累加器A中

这条指令为双字节指令,操作数FFH以指令形式存放在程序存储器内。

3.2.2 直接寻址

直接寻址就是在指令中包含了操作数的地址,该地址直接给出了参加运算或传送的数据所在的字节单元或位,它可以访问内部RAM的128字节单元、221个位地址空间以及特殊功能寄存器SFR,且SFR和位地址空间只能用直接寻址方式来访问。在指令中含有操作数的直接地址,该地址指出了参与操作的数据所在的字节地址或位地址。直接寻址方式中操作数存储的空间有三种。

(1)访问内部低128个字节单元(00H~7FH),指令中直接给出地址。

例:MOV A,70H   ;70H→(A)把RAM 70H单元中的内容送累加器

(2)访问特殊功能寄存器,只能用直接寻址方式进行访问。

例:MOV IE,#85H   ;85H→(IE)。IE为特殊功能寄存器,其字节地址为A8H

(3)位地址空间的访问,指令中以位名称或者位地址的形式给出。

例:MOV C,00H    ;将00H单元的内容→进位位C

3.2.3 寄存器寻址

寄存器寻址是指以某一个可寻址的寄存器的内容为操作数。对于累加器A、通用寄存器B、数据指针寄存器DPTR和进位位C,其寻址时具体的寄存器已隐含在其操作码中,而对于选定的8个工作寄存器R0~R7,则用操作码的低3位指明所用寄存器。寄存器寻址指令中,操作数域中给出的是操作数所在的寄存器,寄存器的内容才是本条指令的操作数。

四个寄存器组共有32个通用寄存器,但指令中使用的是当前工作寄存器组,因此在使用寄存器寻址指令前,必须将RS0,S1位置位,确定当前工作寄存器组(见表2-2)。

例:MOV A,Ri    ;(Ri)→(A)

3.2.4 寄存器间接寻址

在这种寻址方式中,操作数所指定的寄存器中存放的不是操作数本身,而是操作数的地址。寄存器间接寻址方式把指令中寄存器的内容作为地址,再到该地址单元取得操作数。变址寻址寄存器间接寻址用符号“@”表示。

寄存器间接寻址是一种二次寻址方式,程序执行分两步完成:首先根据指令查出寄存器的内容,即操作数的地址;然后根据地址找到所需要的操作数,并完成相应的操作。

寄存器间接寻址只能使用寄存器R0或R1作为地址指针,来寻址内部RAM(00H~FFH)中的数据。寄存器间接寻址也适用于访问外部RAM,此时可使用R0、R1或DPTR作为地址指针。

例:MOV A,@Rj   ;((Rj))→(A)

若R0内容为11H,而内部RAM 11H单元中的内容是33H,则指令MOV A,@R0的功能是将33H这个数送到累加器A中。

3.2.5 基址寄存器加变址寄存器间接寻址

基址寄存器加变址寄存器间接寻址以数据指针DPTR或程序计数器PC的内容为基地址,然后,在这个基地址的基础上加上累加器A中的地址偏移量形成真正的操作数地址。这种寻址方式常用于查表操作。

例:MOVC A,@A+DPTR    ;(DPTR)+(A))→(A)

  MOVC A,@A+PC     ;((PC)+(A))→(A)

A中为无符号数,指令功能是A的内容和DPTR或当前PC的内容相加得到程序存储器的有效地址,把该存储器单元中的内容送到A。

3.2.6 相对寻址

相对寻址是将程序计数器PC中的当前值(该当前值是指执行完这条相对转移指令后的PC的字节地址)为基准,加上指令中给定的偏移量所得结果而形成实际的转移地址。这种寻址方式主要用于转移指令指定转移的目标地址。

一般将相对转移指令操作码所在地址称为源地址,转移后的地址称为目的地址,目的地址的计算方法如下:

目的地址=源地址+相对转移指令字节+相对偏移值

假设PC=1000H是当前该指令的地址,执行SJMP 02H,则单片机要执行的下一条指令的地址=(1000H+2H)+2H=1004H,加02H是因为当前跳转指令代码为两个字节。执行完指令后,PC=1004H,单片机将转移到1004H程序单元执行程序。

3.2.7 位寻址

位寻址是指对片内RAM的位寻址区和某些可位寻址的特殊功能寄存器进行位操作时的寻址方式。位地址表示一个可作位寻址的单元,它或者在内部RAM中或者是一个硬件的位。

例:MOV C,20H      ;将20H单元的内容→进位位C

3.3 指令系统

51系列单片机的指令系统可以分为以下5组:数据传送指令、算术运算指令、逻辑运算指令、位操作类指令和控制转移指令。51指令系统有42种助记符代表了33种操作功能,共构成111种指令。按字节分类,单字节指令49条,双字节指令45条,三字节指令17条。从指令执行的周期看,单机器周期(12个振荡器周期)指令64条,双机器周期指令45条,两条(乘、除)四个机器周期指令。

3.3.1 数据传送指令

数据传送指令主要负责把数据、地址或立即数传送到寄存器或存储单元中。所谓传送就是把源地址单元的内容传送到目的地址单元中去,指令执行后一般源地址单元内容不变,或者源地址单元与目的地址单元内容互换。在通常的应用程序中,传送指令占有极大的比例,数据传送是否灵活、迅速,对整个程序的编写和执行起着很大的作用。

51系列单片机提供了极其丰富的数据传送指令,其数据传送指令可以在累加器A、工作寄存器、内部数据存储器、外部数据存储器和程序存储器之间进行。这类指令共有29条,可分为3大类:基本数据传送指令,数据交换指令,栈操作指令。

执行数据传送指令时,除以累加器A为目的操作数的指令会对奇偶标志位P有影响外,其余指令执行时均不会影响任何标志位。

1. 基本数据传送指令

MOV指令是形式最简单、用得最多的指令。根据数据取自何方和传到何方,还可以有着许多不同的形式。但是有几种组合是没有的:没有寄存器/寄存器的传送,但是指定寄存器组的每个寄存器都有它的直接地址,可以使用直接地址来传送;也没有间接/间接传送。

(1)以累加器A为目的操作数类指令

这组指令的作用是把源操作数指向的内容送到累加器A。有立即数、直接、寄存器和寄存器间接寻址方式:

MOV A,#data   ;data→(A)立即数送到累加器A中

MOV A,direct  ;(direct)→(A)直接单元地址中的内容送到累加器A中

MOV A,Ri    ;(Ri)→(A)Ri中的内容送到累加器A中

MOV A,@Rj    ;((Rj))→(A)Rj内容指向的地址单元中的内容送到累加器A

例如:下面语句可以实现将片内RAM24H单元中的内容送到累加器A中:

MOV R0,#24H

MOV A,@R0

(2)以寄存器Ri为目的操作数的指令

这组指令的功能是把源操作数指定的内容送到所选定的工作寄存器Ri中。有立即、直接和寄存器寻址方式:

MOV Ri,#data  ;data→(Ri)立即数直接送到寄存器Ri中

MOV Ri,direct  ;(direct)→(Ri)直接寻址单元中的内容送到寄存器Ri中

MOV Ri,A    ;(A)→(Ri)累加器A中的内容送到寄存器Ri中

例如:下面语句可以实现将累加器A中的内容送到R2寄存器去:

MOV R2,A

(3)以直接地址为目的操作数的指令

这组指令的功能是把源操作数指定的内容送到由直接地址direct所选定的片内RAM中。有立即、直接、寄存器和寄存器间4种寻址方式:

MOV direct,#data ;data→(direct)立即数送到直接地址单元

MOV direct,direct ;(direct)→(direct)直接地址单元中内容送到直接地址单元

MOV direct,A   ;(A)→(direct)累加器A中的内容送到直接地址单元

MOV direct,Ri   ;(Ri)→(direct)寄存器Ri中的内容送到直接地址单元

MOV direct,@Rj  ;((Rj))→(direct)寄存器Rj中的内容指定的地址单元中的数据送到直接地址单元

例如:下面语句可以实现将累加器A中的内容送到片内RAM24H单元去:

MOV 24H,A

(4)以间接地址为目的操作数的指令

这组指令的功能是把源操作数指定的内容送到以Rj中的内容为地址的片内RAM中。有立即、直接和寄存器3种寻址方式:

MOV @Rj,#data   ;data→((Rj))立即数送以Rj中的内容为地址的RAM单元

MOV @Rj,direct ;(data)→((Rj))直接地址单元中的内容送到以Rj中的内容为地址的RAM单元

MOV @Rj,A      ;(A)→((Rj))累加器A中的内容送到以Rj中的内容为地址的RAM单元

例如:下面语句也可以实现将累加器A中的内容送到片内RAM24H单元去:

MOV R0,#24H

MOV @R0,A

(5)查表指令

这组指令的功能是对存放于程序存储器中的数据表格进行查找传送,使用变址寻址方式:

MOVC A,@A+DPTR    ;((A)+(DPTR))→(A)表格地址单元中的内容送到累加器A中

MOVC A,@A+PC     ;((PC))+1→(PC),((A)+(PC))→(A)表格地址单元中的内容送到累加器A中

(6)累加器A与片外数据存储器RAM传送指令

这组指令的作用是累加器A与片外RAM间的数据传送。使用寄存器寻址方式:

MOVX @DPTR,A  ;(A)→((DPTR))累加器中的内容送到数据指针指向片外RAM地址中

MOVX A,@DPTR ;((DPTR))→(A)数据指针指向片外RAM地址中的内容送到累加器A中

MOVX A,@Rj   ;((Rj))→(A)寄存器Rj指向片外RAM地址中的内容送到累加器A中

MOVX @Rj,A   ;(A)→((Rj))累加器中的内容送到寄存器Rj指向片外RAM地址中

例如:将累加器A中的内容送到片外RAM240H单元去,则下面语句可以完成此功能:

MOV DPTR,#240H

MOVX @DPTR,A

(7)16位数据传送指令

这条指令的功能是把16位常数送入数据指针寄存器。

MOV DPTR,#data16  ;dataH→(DPH),dataL→(DPL)16位常数的高8位送到DPH,低8位送到DPL

例如:MOV DPTR,#5678H执行后,转用寄存器DPH中的内容为56H,DPL中的内容为78H。

2.交换指令

MOV指令主要完成从一处到另一处的拷贝,XCH指令则可实现数据的双向传送。所有的操作都涉及累加器A,可以把累加器A中的内容与源操作数所指的数据相互交换。

XCH A,direct  ;(A)←→(direct)累加器与直接地址单元中的内容互换

XCH A,Ri    ;(A)←→(Ri)累加器与工作寄存器Ri中的内容互换

XCH A,@Rj ;(A)←→((Rj))累加器与工作寄存器Rj所指的存储单元中的内容互换

XCHD A,@Rj   ;(A3~0)←→((Rj)3~0)累加器与工作寄存器Rj所指的存储单元中的内容低半字节互换

SWAP A      ;(A3~0)←→(A7~4)累加器中的内容高低半字节互换

例如:若R0的内容是12H,片内RAM12H单元的内容是34H,累加器的内容是56H,则:

(1)执行XCH A,@R0后,累加器A的内容变为34H,片内RAM12H单元的内容是56H,R0的内容保持不变。

(2)执行XCHD A,@R0后,累加器A的内容变为54H,片内RAM12H单元的内容是36H,R0的内容保持不变。

(3)执行SWAP A后,累加器A的内容变为65H。

3.入栈/出栈指令

这类指令的作用是把直接寻址单元的内容传送到堆栈指针SP所指的单元中,以及把SP所指单元的内容送到直接寻址单元中。需要指出的是,单片机开机复位后,(SP)默认为07H,但一般都需要重新赋值,设置新的SP首址。入栈的第一个数据必须存放于SP+1所指存储单元,故实际的堆栈底为SP+1所指的存储单元。

(1)PUSH指令

堆栈的入栈指令,该指令可以把某片内RAM单元(低128字节)或某专用寄存器的内容入栈。

PUSH direct    ;(SP)+1→(SP),(direct)→(SP)首先将栈指针SP的内容加1,然后把直接地址指出的单元内容传送到栈指针SP所指的内部RAM单元中

例如:若(SP)=30H,(ACC)=20H,(B)=70H,执行下列指令:

PUSH ACC

PUSH B

则:(SP)=32H,(31H)=20H,(32H)=70H

(2)POP指令

堆栈的出栈指令,该指令用于恢复某片内RAM单元(低128字节)或某专用寄存器的内容。

POP direct ;(SP)→(direct),(SP)—1→(SP)将堆栈指针SP所指的内部RAM单元内容送入直接地址指出的字节单元中,栈指针SP的内容减1

例如:若(SP)=32H,(32H)=20H,(31H)=10H,执行下列命令:

POP DPH

POP DPL

则:(SP)=30H,(DPTR)=2010H。

3.3.2 算术运算指令

在51系列单片机的指令系统中,提供了完备的加、减、乘、除算术运算指令及增量(加1)、减量(减1)运算,可处理不带符号或带符号的8/16二进制数。除加1和减1指令外,算术运算指令会影响进位、半进位和溢出位三个标志位。

1. 不带进位的加法指令

这组指令的作用是把立即数、直接地址、工作寄存器及间接地址内容与累加器A的内容相加,运算结果存在A中。

ADD A,#data    ;(A)+ data→(A)累加器A中的内容与立即数data相加,结果存在A中

ADD A,direct   ;(A)+(direct)→(A)累加器A中的内容与直接地址单元中的内容相加,结果存在A中

ADD A,Ri     ;(A)+(Ri)→(A)累加器A中的内容与工作寄存器Ri中的内容相加,结果存在A中

ADD A,@Rj     ;(A)+((Rj))→(A)累加器A中的内容与工作寄存器Rj所指向地址单元中的内容相加,结果存在A中

本组指令的执行将影响标志位AC、CY、OV、P。当运算结果的第3、7位有进位时,分别将AC、CY标志位置位;否则复位。对于无符号数,进位标志位CY=1,表示溢出;CY=0表示无溢出。带符号数运算的溢出取决于第6、7位,若这2位中有一位产生进位,而另一位不产生进位,则溢出标志位OV置位,否则被复位。

例如:若累加器A的内容为43H,R0的内容为6AH,执行下列指令:

ADD A,R0

则:累加器A的内容变为ADH,C复位,AC复位,OV置位。

2. 带进位加法指令

这组指令的作用是把立即数、直接地址、工作寄存器及间接地址内容与累加器A的内容以及进位位C相加,运算结果存在A中。

ADDC A,#data ;(A)+data+(C)→(A)累加器A中的内容与立即数连同进位位相加,结果存在A中

ADDC A,direct ;(A)+(direct)+(C)→(A)累加器A中的内容与直接地址单元的内容连同进位位相加,结果存在A中

ADDC A,Ri   ;(A)+(Ri)+(C)→(A)累加器A中的内容与工作寄存器Ri中的内容、连同进位位相加,结果存在A中

ADDC A,@Rj   ;(A)+((Rj))+(C)→(A)累加器A中的内容与工作寄存器Rj指向地址单元中的内容、连同进位位相加,结果存在A中

本组指令执行对标志位AC、CY、OV、P的影响与ADD指令相同。

3. 增量指令

这组指令的功能均为原寄存器的内容加1,结果送回原寄存器。这组指令共有直接、寄存器、寄存器间接寻址等寻址方式:

INC A    ;(A)+1→(A)累加器A中的内容加1,结果存在A中

INC direct  ;(direct)+1→(direct)直接地址单元中的内容加1,结果送回原地址单元中

INC Ri    ;(Rn)+1→(Ri)寄存器Ri的内容加1,结果送回原地址单元中

INC @Rj   ;((Rj))+1→((Rj))工作寄存器Rj所指向地址单元中的内容加1,结果送回原地址单元中

INC DPTR   ;(DPTR)+1→(DPTR)数据指针内容加1,结果送回数据指针中。

如果原寄存器的内容为FFH,执行加1后,结果就会是00H

增量指令不会对任何标志有影响。

4. 带借位减法指令

这组指令是把立即数、直接地址、间接地址及工作寄存器与累加器A连同借位位C内容相减,结果送回累加器A中。

SUBB A,#data   ;(A)-data-(C)→(A)累加器A中的内容与立即数、借位位相减,结果存在A中

SUBB A,direct  ;(A)-(direct)-(C)→(A)累加器A中的内容与直接地址单元中的内容、连同借位位相减,结果存在A中

SUBB A,Ri    ;(A)-(Ri)-(C)→(A)累加器A中的内容与工作寄存器中的内容、连同借位位相减,结果存在A中

SUBB A,@Rj    ;(A)-((Rj))-(C)→(A)累加器A中的内容与工作寄存器Rj指向的地址单元中的内容、连同借位位相减,结果存在A中

本指令执行将影响标志位AC、CY、OV、P。若第七位有借位,则将CY置位,否则CY复位。若第3位有错位,则置位辅助进位标志AC,否则 AC复位。若第7和第6位中有一位需借位,而另一位不借位,则置位溢出标志OV。

当在进行单字节或多字节减法前,不知道进位标志位CY的值,则应在减法指令前先将CY复位清“0”。

例如:若累加器A的内容为6AH,R0的内容为43H,进位标志位CY的值为0,执行下列指令:

SUBB A,R0

则:累加器A的内容变为27H,C复位,AC复位,OV复位。

5. 减量指令

这组指令的作用是把所指的寄存器内容减1,结果送回原寄存器,这组指令共有直接、寄存器、寄存器间接寻址等寻址方式。

DEC A    ;(A)-1→(A)累加器A中的内容减1,结果送回累加器A中

DEC direct ;(direct)-1→(direct)直接地址单元中的内容减1,结果送回直接地址单元中

DEC Ri   ;(Ri)-1→(Ri)寄存器Ri中内容减1,结果送回寄存器Ri中

DEC @Rj   ;((Rj))-1→((Rj))寄存器Rj指向的地址单元中的内容减1,结果送回原地址单元中

若原来寄存器内容为00H,减1后将为FFH。

运算结果不影响任何标志位。

6. 乘法指令

这条指令的作用是把累加器A和寄存器B中的8位无符号数相乘,所得到的是16位乘积,这个结果低8位存在累加器A中,而高8位存在寄存器B中。

MUL AB   ;(A)×(B)→(B)和(A)累加器A中的内容与寄存器B中的内容相乘,结果存在B、A中

乘法指令需要4个机器周期。

如果乘积大于255(0FFH),即B的内容不为0时,则置位溢出标志位OV,否则OV复位。进位标志位CY总是复位为0。

7. 除法指令

这条指令的作用是把累加器A的8位无符号整数除以寄存器B中的8位无符号整数,所得到的商存在累加器A中,而余数存在寄存器B中。

DIV AB   ;(A)÷(B)→(A)和(B)累加器A中的内容除以寄存器B中的内容,所得到的商存在累加器A中,而余数存在寄存器B中

除法指令需要4个机器周期。

本指令总是将CY和OV标志位复位。当除数(B中内容)为00H时,那么执行结果将为不定值,则置位溢出标志位OV。

8. 十进制调整指令

在进行BCD码运算时,这条指令总是跟在ADD或ADDC指令之后,其功能是将执行加法运算后存于累加器A中的结果进行调整和修正。

DA A

3.3.3 逻辑运算指令

在51系列单片机的指令系统中提供的逻辑运算指令主要包括ANL(与),ORL(或),XRL(异或)等指令。

1. 逻辑与指令ANL

这组指令的功能是在指出的变量之间以位为基础的逻辑与操作。操作数有寄存器寻址、直接寻址、寄存器间接寻址和立即寻址等寻址方式。

ANL A,#data    ;(A)∧data→(A)累加器A的内容和立即数执行逻辑与操作,结果存在累加器A中

ANL A,direct   ;(A)∧(direct)→(A)累加器A中的内容和直接地址单元中的内容执行逻辑与操作,结果存在寄存器A中

ANL A,Ri     ;(A)∧(Ri)→(A)累加器A的内容和寄存器Ri中的内容执行逻辑与操作,结果存在累加器A中

ANL A,@Rj     ;(A)∧((Rj))→(A)累加器A的内容和工作寄存器Rj指向的地址单元中的内容执行逻辑与操作,结果存在累加器A中

ANL direct,#data ;(direct)∧data→(direct)直接地址单元中的内容和立即数执行逻辑与操作,结果存在直接地址单元中

ANL direct,A   ;(direct)∧(A)→(A)直接地址单元中的内容和累加器A的内容执行逻辑与操作,结果存在直接地址单元中

例如:若(A)=07H,(R0)=0FDH,执行指令:ANL A,R0

则:(A)=05H

2. 逻辑或指令ORL

这组指令的功能是在所指出的变量之间执行以位为基础的逻辑或操作,结果存到目的变量中去。操作数有立即寻址、直接寻址、寄存器寻址和寄存器间接寻址方式:

ORL A,#data     ;(A)∨data →(A)累加器A的内容和立即数执行逻辑或操作。结果存在累加器A中

ORL A,direct    ;(A)∨(direct)→(A)累加器A中的内容和直接地址单元中的内容执行逻辑或操作。结果存在寄存器A中

ORL A,Ri      ;(A)∨(Ri)→(A)累加器A的内容和寄存器Ri中的内容执行逻辑或操作。结果存在累加器A中

ORL A,@Rj      ;(A)∨((Rj))→(A)累加器A的内容和工作寄存器Rj指向的地址单元中的内容执行逻辑或操作。结果存在累加器A中

ORL direct,#data  ;(direct)∨data→(direct)直接地址单元中的内容和立即数执行逻辑或操作。结果存在直接地址单元中

ORL direct,A    ;(direct)∨(A)→(direct)直接地址单元中的内容和累加器A的内容执行逻辑或操作。结果存在直接地址单元中

例如:若(P1)=05H,(A)=33H,执行指令:

ORL P1,A

则:(P1)=37H。

3. 逻辑异或指令XRL

这组指令的功能是在所指出的变量之间执行以位为基础的逻辑异或操作,结果存放到目的变量中去。操作数有立即寻址、直接寻址、寄存器寻址和寄存器间接寻址方式。

XRL A,#data    ;(A)⊕data→(A)累加器A的内容和立即数执行逻辑异或操作。结果存在累加器A中

XRL A,direct   ;(A)⊕(direct)→(A)累加器A中的内容和直接地址单元中的内容执行逻辑异或操作。结果存在寄存器A中

XRL A,Ri     ;(A)⊕(Ri)→(A)累加器A的内容和寄存器Ri中的内容执行逻辑异或操作。结果存在累加器A中

XRL A,@Rj     ;(A)⊕(Rj)→(A)累加器A的内容和工作寄存器Rj指向的地址单元中的内容执行逻辑异或操作。结果存在累加器A中

XRL direct,#data ;(direct)⊕data→(direct)直接地址单元中的内容和立即数执行逻辑异或操作。结果存在直接地址单元中

XRL direct,A   ;(direct)⊕(A)→(direct)直接地址单元中的内容和累加器A的内容执行逻辑异或操作。结果存在直接地址单元中

例如:若(A)=90H,(R3)=73H,执行指令:

XRL A,R3

则:(A)=0E3H。

4. 循环移位指令

这4条指令的作用是将累加器中的内容循环左或右移一位,后两条指令是连同进位位CY一起移位。

RL  A    ;累加器A中的内容左移一位

RR  A    ;累加器A中的内容右移一位

RLC A    ;累加器A中的内容连同进位位CY左移一位

RRC A    ;累加器A中的内容连同进位位CY右移一位

5. 求反指令

这条指令将累加器中的内容按位取反。

CPL A    ;累加器中的内容按位取反

6. 清零指令

这条指令将累加器中的内容清0。

CLR A    ;0→(A),累加器中的内容清0

3.3.4 位操作类指令

MCS-51单片机内部有一个布尔处理机,对位地址空间具有丰富的位操作指令。

1. 位传送指令

这2条指令的功能是把由源操作数指出的布尔变量送到目的操作数指定的位中去。其中一个操作数必须为进位标志,另一个可以是任何直接寻址位。

MOV C,bit  ;bit→CY,某位数据送CY

MOV bit,C  ;CY→bit,CY数据送某位

本组指令不影响其他寄存器和标志位。

例如:若(20H)=35H,执行指令:

MOV C,06H。

进位标志位CY的值为0。由于本条指令是条位操作类指令,实际上是将位地址为06H(即20H.6)的值送进位标志位,所以执行完本条指令后进位标志位为0。

2. 位变量修改指令

这些指令对CY及可寻址位进行置位或复位操作。

CLR  C    ;0→CY,复位CY

CLR  bit   ;0→bit,复位某一位

SETB C    ;1→CY,置位CY

SETB bit   ;1→bit,置位某一位。

本组指令不影响其他标志。

例如:若(24H)=75H,执行指令:

CLR 26H

则:(24H)=35H。

3. 位变量逻辑指令

位运算都是逻辑运算,有与、或、非三种指令。

ANL C,bit    ;(CY)∧(bit)→CY如果源操作数的布尔值是逻辑0,则复位进位标志,否则进位标志保持不变

ANL C,/bit    ;(CY)∧(bit)→CY如果源操作数的布尔值是逻辑1,则复位进位标志,否则进位标志保持不变

ORL C,bit    ;(CY)∨(bit)→CY如果源操作数的布尔值为1,则置位进位标志,否则进位标志CY保持原来状态

ORL C,/bit    ;(CY)∧(bit)→CY如果源操作数的布尔值为0,则置位进位标志,否则进位标志CY保持原来状态

CPL C       ;→CY进位标志位CY取反作为新的CY

CPL bit      ;→bit某一位的值取反作为新的值

例如:若P1口为输出口,则执行下列指令:

MOV C,00H  ;CY←(20H.0)

ORL C,01H  ;CY←(CY)∨(20H.1)

ORL C,02H  ;CY←(CY)∨(20H.2)

ORL C,03H  ;CY←(CY)∨(20H.3)

ORL C,04H  ;CY←(CY)∨(20H.4)

ORL C,05H  ;CY←(CY)∨(20H.5)

ORL C,06H  ;CY←(CY)∨(20H.6)

ORL C,07H  ;CY←(CY)∨(20H.7)

MOV P1.0,C  ;P1.0←CY

内部RAM的20单元中只要有一位为1,则P1.0输出就为1。

4. 位变量条件转移指令

位变量条件转移指令是以位的状态作为实现程序转移的判断条件。

JC rel     ;(CY)=1转移,(PC)+2+rel→PC,否则程序往下执行,(PC)+2→PC

JNC rel     ;(CY)=0转移,(PC)+2+rel→PC,否则程序往下执行,(PC)+2→PC

JB bit,rel   ;位状态为1转移

JNB bit,rel  ;位状态为0转移

JBC bit,rel  ;位状态为1转移,并使该位清“0”

3.3.5 控制转移指令

一般情况下指令是顺序执行的逐条执行的,但实际上程序不可能全部顺序执行而经常需要改变程序的执行流程,下面介绍的控制转移指令就是用来控制程序的执行流程的。

1. 无条件转移指令

这组指令执行完后,程序就会无条件转移到指令所指向的地址上去。长转移指令访问的程序存储器空间为16地址64KB,绝对转移指令访问的程序存储器空间为11位地址2KB空间。

LJMP addr16    ;addr16→(PC),将16位地址作为程序计数器的新值

AJMP addr11    ;(PC)+2→(PC),addr11→(PC10~0)将11位地址作为程序计数器的新值的低11位,高5位(PC15-11)不改变

SJMP rel      ;(PC)+ 2 + rel→(PC)将当前程序计数器先加上2再加上偏移量作为程序计数器的新值

JMP @A+DPTR    ;(A)+(DPTR)→(PC),累加器所指向地址单元的值加上数据指针的值作为程序计数器的新值

例:如果累加器A中存放待处理命令编号(0~7),程序存储器中存放着标号为PMTB的转移表首址,则执行下面的程序,将根据A中命令编号转向相应的命令处理程序。

PM:  MOV R1,A    ;(A)*3→(A)

    RL  A

    ADD A,R1

    MOV DPTR,#PMTB ;转移表首址→DPTR

    JMP @A+DPTR   ;据A值跳转到不同入口

PMTB:LJMP  PM     ;转向命令0处理入口

    LJMP PM1     ;转向命令1处理入口

    LJMP PM2     ;转向命令2处理入口

    LJMP PM3     ;转向命令3处理入口

    LJMP PM4     ;转向命令4处理入口

    LJMP PM5     ;转向命令5处理入口

    LJMP PM6     ;转向命令6处理入口

    LJMP PM7     ;转向命令7处理入口

2. 条件转移指令

条件转移指令是依某种特定条件转移的指令。条件满足时转移(相当于一条相对转移指令),条件不满足时则顺序执行下面的指令。目的地址在下一条指令的起始地址为中心的256个字节范围中(-128~+127)。当条件满足时,先把PC加到指向下一条指令的第一个字节地址,再把有符号的相对偏移量加到PC上,计算出转向地址。

JZ rel   ;A=0,(PC)+ 2 + rel→(PC),累加器中的内容为0,则转移到偏移量所指向的地址,否则程序往下执行

JNZ rel   ;A≠0,(PC)+ 2 + rel→(PC),累加器中的内容不为0,则转移到偏移量所指向的地址,否则程序往下执行

3. 比较不相等转移指令

这组指令的功能是比较前面两个操作数的大小。如果它们的值不相等则转移。在PC加到下一条指令的起始地址后,通过把指令最后一个字节的有符号的相对偏移量加到PC上,并计算出转向地址。操作数有寄存器寻址、直接寻址、寄存器间接寻址和立即寻址等方式。

CJNE A,direct,rel  ;A≠(direct),(PC)+ 3 + rel→(PC),累加器中的内容不等于直接地址单元的内容,则转移到偏移量所指向的地址,否则程序往下执行

CJNE A,#data,rel   ;A≠data,(PC)+ 3 + rel→(PC),累加器中的内容不等于立即数,则转移到偏移量所指向的地址,否则程序往下执行

CJNE Ri,#data,rel  ;A≠data,(PC)+ 3 + rel→(PC),工作寄存器Ri中的内容不等于立即数,则转移到偏移量所指向的地址,否则程序往下执行

CJNE @Rj,#data,rel  ;A≠data,(PC)+ 3 + rel→(PC),工作寄存器Rj指向地址单元中的内容不等于立即数,则转移到偏移量所指向的地址,否则程序往下执行。

如果第一个操作数(无符号整数)小于第二个操作数,则进位标志CY置位,否则CY复位。不影响任何一个操作数的内容。

4. 减1不为0转移指令

这组指令把源操作数减1,结果回送到源操作数中去,如果结果不为0则转移,跳到标号rel处执行,等于0就执行下一条指令。源操作数有寄存器寻址和直接寻址方式。该指令通常用于实现循环计数。

DJNZ Ri,rel    ;(Ri)-1→(Ri),(Ri)≠0,(PC)+ 2 + rel→(PC)工作寄存器Ri减1不等于0,则转移到偏移量所指向的地址,否则程序往下执行

DJNZ direct,rel  ;(direct)-1→(direct),(direct)≠0,(PC)+ 2 + rel→(PC)直接地址单元中的内容减1不等于0,则转移到偏移量所指向的地址,否则程序往下执行。

例如: 有如下程序段:

这是个延时程序段。通过延时,在P1.1输出一个方波,可以用改变30H和31H的初值,来改变延时时间实现改变方波的频率。

5. 子程序返回指令

编程时一般都把需要反复执行的一些程序编写成子程序,当需要用它们时,就用一个调用命令使程序按调用的地址去执行,这就需要子程序的调用指令和返回指令。

LCALL addr16 ;长调用指令,可在64KB空间调用子程序。此时(PC)+3→(PC),(SP)+1→(SP),(PC7-0)→(SP),(SP)+1→(SP),(PC15-8)→(SP),addr16→(PC),即分别从堆栈中弹出调用子程序时压入的返回地址

ACALL addr11 ;绝对调用指令,可在2kB空间调用子程序,此时(PC)+2→(PC),(SP)+1→(SP),(PC7-0)→(SP),(SP)+1→(SP),(PC15-8)→(SP),addr11→(PC10-0)

RET    ;子程序返回指令。此时(SP)→(PC15-8),(SP)-1→(SP),(SP)→(PC7-0),(SP)-1→(SP)RET指令通常安排在子程序的末尾,使程序能从子程序返回到主程序

RETI    ;中断返回指令,除具有RET功能外,还具有恢复中断逻辑的功能,需注意的是,RETI指令不能用RET代替

例如:若(SP)=62H,(62H)=07H,(61H)=30H,执行指令RET后,(SP)=60H,(PC)=0730H,CPU从0730H开始执行程序。

6. 空操作指令

空操作也是CPU控制指令,它没有使程序转移的功能,一般用于软件延时。指令为:NOP。

3.4 汇编程序设计

学习了一种计算机及指令系统以后,便可开始编写简单的汇编语言程序。编程技巧应通过实践积累经验,并不断提高。本节中讨论汇编程序的设计方法,本节后面的各节都是汇编语言程序的示例,我们挑选了一些比较简单又十分典型的程序段,结合实践教学的环境,进行剖析。对各例子进行仔细的分析将有助于读者由单条指令过渡到联用,再到试编或剖析程序。学习后面各节的目的,首先是让读者深入理解指令,提高运用能力,更透彻地掌握单片机的使用方法,特别是MCS-51的性能和结构。然后在这基础上,初步熟悉汇编语言程序设计。学有余力的读者,可以广泛地参阅各种有关书籍、各种数据手册、编译器的使用手册或者在互联网上查阅相关资料,从而提高自身的编程能力。

3.4.1 汇编程序功能

汇编程序源程序是汇编指令的有序集合。汇编语言程序设计的基础是与其对应的汇编语言指令集、硬件组成、系统功能要求密切相关的。因此,要求设计者全面了解和掌握应用系统的硬件结构、指令结构、功能要求以及有关算法等,并尽可能以节省存储单元、缩短程序长度、选取合适指令三原则进行程序设计和编程。

汇编语言编写的程序不能由机器直接执行,而必须翻译成机器代码组成的目标程序,这个过程就称为汇编。在微型机中,在绝大多数情况下,汇编过程就是通过软件自动完成的。首先用编辑程序产生汇编语言的源程序,源程序通过汇编程序翻译之后就成了二进制代码表示的目标文件。前面讲的指令系统中的每条指令都是构成源程序的基本语句。汇编语言指令和机器语言的指令之间有着一一对应的关系。

目标文件虽然已经是二进制文件,但它还不能直接运行,需要通过连接程序把目标文件和其他目标文件连接在一起形成可执行文件。这个文件才能在机器上运行。因此,要在计算机上运行汇编语言程序的步骤是:

(1)用编辑程序建立源文件ASM;

(2)用汇编程序把ASM文件转换成OBJ文件;

(3)用连接程序LINK把OBJ文件转换成EXE文件;

(4)执行该程序。

3.4.2 汇编语言源程序的格式

汇编语言源程序有一定的书写格式。一般由左到右按序至少包括下列四项内容:

[名字] 操作 操作数 [;注释]

其中:名字项是指一个标号或变量。操作项是一个操作码的助记符,它可以是指令、伪指令或宏指令名。操作数项由一个或多个表达式组成,它提供为执行所要求的操作而需要的信息。操作数项可以是常数、寄存器、标号、变量或由表达式组成。注释项用来说明程序或语句的功能。“;”为识别注释项的开始。“;”也可以从一行的第一个字符开始,此时整行都是注释,常用来说明下面一段程序的功能。上面四项中带方括号的两项是可选项。各项之间必须用“空格”(Space)或“水平制表”(Tab)符隔开。

1. 名字项

源程序中用下列字符来表示名字:

字母A~Z

数字0~9

专用字符 ?、·、@ 、-、$

除数字外,所有字符都可以放在源语句的第一个位置。名字中如果用到,则它必须是第一个字符。可以用很多字符来说明名字,但只有前面的31个字符能被汇编程序所识别。

一般说来,名字项可以是标号或变量。它们都用来表示本语句的符号地址,都是可有可无的,只有当需要用符号地址来访问该语句时它才需要出现。

标号:标号在代码段中定义,后面跟着冒号(:),它也可以用LABEL或EQU伪操作来定义。此外,它还可以作为过程名定义,这将在以后的章节中加以说明。

变量:变量在数据段或附加数据段中定义,后面不跟冒号。它也可以用LABEL或EQU伪操作来定义。变量经常在操作数字段出现。

2. 操作项

操作项可以是指令、伪指令或宏指令的助记符。对于指令,汇编程序将其翻译为机器语言指令。对于伪指令,汇编程序将根据其所要求的功能进行处理。对于宏指令,则将根据其定义展开。宏指令在后面将会有专门论述。

3. 操作数项

操作数项由一个或多个表达式组成,多个操作数项之间一般用逗号分开。对于指令,操作数项一般给出操作数地址,它们可能有一个,或两个,或三个,或一个也没有。对于伪操作或宏指令,则给出它们所要求的参数。操作数项可以是常数、寄存器、标号、变量或由表达式组成。

4. 注释项

注释项用来说明一段程序、一条或几条指令的功能。对于汇编语言程序来说,注释项的作用是很明显的,它可以使程序容易被读懂,因此汇编语言程序必须写好注释。注释应该写出本条(或本段)指令在程序中的功能和作用,而不应该只写指令的动作。读者在有机会阅读程序例子时,应注意学习注释的写法,在编制程序时,更应学会写好注释。

3.4.3 汇编程序设计的步骤与方法

1.汇编语言程序设计的基本步骤

对于一个单片机应用系统,在经过系统总体方案论证、硬件组成设计基本定型的基础上,即可着手应用软件的设计。一个完整的程序大致可以分为以下几个步骤:

(1)设计任务的分析、确定有关算法或思路

一个应用系统程序设计的第一步,必须根据总体方案所确定的功能要求、技术指标、硬件系统所提供的资源和工作环境等详细地分析、研究,有些还需要通过某些实验以获得实现某种功能或技术指标的第一手资料,或者相应的程序段。从而明确程序设计应承担的任务,实现需要达到的功能和技术指标等。

(2)程序总体设计

在上述的基础上,第二步应进行程序的总体设计,即从整体出发,确定程序结构、数据结构、数据类型、资源分配、参数计算等等,以及遵循应用要求勾画出程序执行的逻辑顺序。这部分的设计要求严密、细致、完整、正确和可靠。

在上述考虑的基础上,用图形的方法将总体设计的思路及程序流向完整地展现在平面图上,使程序的总体直观、一目了然,有利于审核、查找和修改。设计好的流程图,可以大大节省源程序的编辑、调试时间,保证源程序的质量和正确性。关于程序流程图的设计方法请参阅有关书籍。

(3)编写汇编程序

一个应用程序的设计,经过前述的两个步骤之后进入编程阶段。程序的编制应是根据总体设计的要求,按照流程图所设定的程序结构、算法和流向,选择合适的指令、顺序的编写程序。通常把这部分的工作称为编辑,所编写的程序成为应用系统的源程序。

编程要力求简练、层次清楚,同时有适当的注释。在单片机系统中,系统资源极其有限,所以编写代码时要尽量使程序所占用的存储空间少,同时也要求执行效率较高。不过很多时候这两个要求并不能同时满足,我们根据实际的需求确定一种较好的方案。编写汇编程序的时候还应当保证整个系统的正确可靠的运行,汇编语言不同于其他高级语言(比如说C语言),它要求编程者对系统结构、功能特点、指令集都有全面的理解,才能达到上述要求,顺利地完成程序的设计任务。

编制好的源程序,还必须经汇编成目标代码,在开发环境中经过严格的测试,才能付诸实际应用。

(4)源程序的汇编与调试

用汇编语言程序编写的源程序必须汇编成计算机能识别的机器语言目标代码才能在计算机上执行。汇编有手工汇编与自动汇编两种方式。汇编的过程实际上是将汇编指令逐条转换成对应的机器码。手工汇编即用手工方法逐条将汇编语言指令转换成对应的机器代码;自动汇编即利用PC等计算机通过汇编程序自动将汇编语言源程序转换成对应的机器码。较好的开发器都已充分利用了PC等计算机的丰富资源,大大地提高了汇编和调试手段,如提供了单步跟踪、断点调试等手段,有的编译器甚至可以虚拟出一个完整的单片机出来,这样就为开发人员调试程序提供非常便利的手段,极大地缩短了应用软件的开发周期。

以上所述的程序设计步骤仅仅为程序设计者建立一个完整的概念和过程。在实际的工作中应视应用软件的实际需求、程序量的大小和复杂程度等,选择合适的设计步骤和调试方法。

2.汇编语言程序设计方法

单片机汇编语言应用程序的设计方法可以说不拘一格,灵活多样。不仅与功能要求、规模、复杂程度有关,同时也与开发人员的经验和习惯相关。在实际的开发过程中一定要注意不断地总结经验、提高技巧。下面给出的一般的设计方法,仅供参考。

3.汇编语言源程序的基本结构

一个单片机汇编语言应用程序,无论其简单还是复杂,总是由简单程序、分支程序、循环程序、查表程序、子程序(包括中段服务程序)等结构化的程序段有机组合而成。这是程序设计的基础。

4.划分功能模块

对于一个功能单一的简单程序,一般按其功能要求及操作顺序,合理地选择上述结构化程序块,自始至终地由上而下一气呵成。

一个具有多种功能而较复杂的程序,则通常采用模块化设计方法。即按不同功能划分成若干功能相对独立程序模块,分别进行独立的设计和测试,最终装配成程序的整体,通过联调,完成程序的整体设计。

模块化程序设计方法具有明显优点,它把一个多功能的复杂程序划分成若干个简单的、单功能程序模块,有利于程序设计、调试、优化和分工,提高程序的正确性和可靠性,并使程序结构层次一目了然。但必须确切规定各程序模块之间的关系以及相互联系的方式和有关参数,在一个主程序的统一管理下连成一个完整的程序整体,这是目前采用较多的程序设计方法之一。本书中的例子也都采用了模块化程序的思想,在后面的实例部分读者将会看到。

5.自顶而下逐步求精

自顶而下逐步求精的程序设计方法是首先设计主干程序,将从属的或者子程序等用程序标志或过渡程序代替,在主干程序完成的前提下再逐个充实从属程序段或子程序,使程序的生成逐步展开,逐步深化、求精,最后完成程序的设计。

这种程序设计方法能使程序结构紧凑,流向层次清晰,调试方便。比较接近设计者的思维,设计效率高。缺点是上层的错误可能对下层产生严重影响,一处修改可能就会牵动全局,因此在设计中要尽量避免。

6.子程序方式

近年来采用子程序的汇编语言程序设计较为普遍。这种设计方法的主导思想是将应用系统的多个主要功能,或者一个大的功能划分为若干个子程序。主程序完成对系统的初始化、各功能模块的子程序的调用等。

这种程序设计方法同样结构紧凑、流向分明、调试方便,特别适合以下应用场合。例如过程控制系统,其每个过程都有相对独立的功能操作,每一个过程与后一个过程又有着密切的联系,过程之间按一定的顺序进行。调试时可按过程顺序逐个延伸,比较方便。但是不足之处就是,调用子程序可能要多占用些存储单元作为堆栈。

程序设计的实践性很强,只有通过实际的程序设计,才能不断地提高设计技巧和调试经验。由于AT89S系列单片机的指令系统完全兼容MCS-51系列单片机,其应用已十分广泛,成功的软件很多、很丰富。因此,可根据设计需要,寻找并参考合适的、现成的程序模块,特别是通用子程序,略加修改即可应用于自己的系统中。

3.4.4 伪指令

上面的指令都是使计算机进行一定操作的指令,而这些指令需要通过汇编程序和连接程序编译连接之后才能被计算机执行。汇编程序对用汇编语言写的源程序进行汇编时,还要提供一些汇编用的控制指令,例如要指定程序或数据存放的起始地址;要给一些连续存放的数据确定单元等等。但是,这些指令和上一章讲的指令不同,在汇编时并不产生目标代码,不影响程序的执行,所以称为伪指令。

伪指令在将汇编语言源程序汇编成目标程序时有用(有的书籍也称之为“汇编指令”),对阅读汇编语言源程序也有用。一些常用的伪指令还带有普遍性,可以通用于各种计算机;使用某种计算机时,可以查阅计算机中的使用手册。下面介绍MCS-51系列单片机常用的几种伪指令:

1.ORG指令

ORG指令的语法为:ORG expression

这条指令用在一段源程序或数据块的前面,说明紧随在后面的程序段或数据块的起始地址。指令中的16位地址便是该起始地址值。该指令的使用示例如下:

ORG   100H

ORG   RESTART

ORG   EXIT1

ORG   ($ + 15)AND 0FFF0H

2.DATA指令

DATA指令的语法为:Symbol DATA address

这条指令用于分配一个地址(范围为00H~0FFH)给某个特定的标识符。这个标识符不能被重定义。与DATA指令相近的还有BIT、CODE、DSEG、IDATA、XDATA,它们的作用都是定义一个标识符,使用的方式也与DATA类似,只是它们分配的地址范围与DATA不一样,具体请参考相应的编译器手册。

DATA指令的使用示例如下:

SERBUF  DATA   SBUF

PORT1   DATA   90h

RESULT  DATA   44h

3.DB指令

DB指令的语法为:[ label: ] DB expression [,expression ... ]

这条指令用于通知汇编程序用expression中的内容来初始化label开始的存储器单元。expression可以是单个字节数字、用逗号分隔开的字节串或用双引号所指示的字符串。方括号表示括号中的内容是可选的。

值得注意的是,该指令以及下面将提到的DW、DD、DS指令都只能用来定义代码段或者常数段内的数据。在其他段中使用该指令将使汇编程序在汇编源程序时产生错误。

例:ORG 9000H

DATA1:DB 73H,01H,90H

DATA2:DB 02H

伪指令ORG 9000H指定了标号DATA1的地址为9000H,伪指令DB指定了数据73H、01H、90H顺序地存放在从9000H开始的单元中,DATA2也是一个标号,它的地址与前一条伪指令DB连续,为9003H,因此数据的最终存放结果是:73H存放在9000H中;01H存放在9001H中;90H存放在9002H中;02H存放在9003H中。

4.DW/DD

DW指令的语法为:[ label:] DW expression [,expression ... ]

用DD替换DW即为DD的语法,可以看出来着两条指令和DB比较相似,但是它们是分别用来定义一个字(两个字节)和一个双字(四个字节)。与DB一样,使用逗号分隔从而可以定义多个字或多个双字。低位字节放在低地址,高字节放在高地址。

该指令的使用示例为:

TABLE:  DW    TABLE,TABLE+10,HERE

HERE:  DW    0

CTAB:  DW    CASE0,CASE1,CASE2,CASE3

DW    $

5.DS指令

DS指令的语法为:[ label:]  DS expression

这条指令的作用是在待存放的一定数量的存储单元前面定义应保留的存储器单元数。说明自标号所在的地址起共有expression所指明的存储单元数保留着可供存入数据。

该指令的使用示例为:

GAP:  DS    (($ + 15)AND 0FFF0h)-$ ;15-byte alignment

DS   10

TIME: DS     8

6.EQU指令

EQU指令的语法为:标号 EQU 操作数

EQU 伪指令的功能是将操作数赋值于标号,使两边的两个量等值。

例:AREA EQU 1000H

即给标号AREA赋值为1000H。

例:STK  EQU AREA

即相当于STK=AREA。若AREA已赋值为1000H,则STK也为1000H。

使用EQU伪指令给一个标号赋值后,这个标号在整个源程序中的值是固定的。也就是说在一个源程序中,任何一个标号只能赋值一次。

7.END指令

END指令的语法为:END

这条指令用在源程序的最后,表明源程序文件的结束,END指令后的指令将不会被汇编程序处理。在汇编源文件中这条指令是必须的并且应当是原文件的最后一条指令。若没有END指令,在汇编程序编译该源文件时将产生一个错误。

3.4.5 汇编程序设计

1.简单程序设计

简单程序又称顺序程序。这种程序的形式最简单,计算机执行程序的方式是“从头到尾”,逐条执行指令语句,直到程序结束,除非用特殊指令让它跳转,不然它会在PC控制下执行。这是程序的最基本形式,任何程序都离不开这种形式。

例1:编写1+2的程序。

解:首先用 ADD A,Rn指令,该指令是将寄存器Rn中的数与累加器A中的数相加,结果存于A中,这就要求先将1和2分别送到A中和寄存器Rn中,而Rn有四组,每组有八个单元R0~R7,首先要知道Rn在哪组,默认值(不设定值)是第0组,在同一个程序中,同组中的Rn不能重复使用,不然会数据出错,唯独A可反复使用,不出问题。明确了这些后,可写出程序如下:

ORG 0000H    ;定下面这段程序在存储器中的首地址,必不可少的

MOV R2,#02   ;2送R2

MOV A,#01    ;1送A

ADD A,R2    ;相加,结果3存A中

END ;程序结束标志,是必不可少的程序到此编写完成,然后在仿真软件中调试、验证,若不对,反复修改程序,直到完全正确为止。

该程序若用ADD A,direct指令编程时,可写出如下程序:

ORG 0000H

MOV 30H,#02

MOV A,#01

ADD A,30H

END

该程序若用ADD A,@Ri指令编程时,可写出如下程序(假设((R0))=02H):

ORG 0000H

MOV A,#01

ADD A,@R0

END

注意间接寻址方式的用法,Ri(i=0,1)即Ri只有R0和R1。

该程序若用 ADD A,#data指令编程时,可写出如下程序:

ORG 0000H

MOV A,#01

ADD A,#02

END

从以上例子可见,同一个程序有多种编写方法,思路不同编出来的程序也不同,但结果都一样,但我们认为最后一个程序较好。以上加法程序是最简单的形式,加法有多种:无进位加法、有进位加法、有符号加法、无符号加法,还有浮点数的加法、单字节加法、双字节加法、多字节加法等等。一般编写程序时,编成通用的程序。在调用通用程序之前,先判断是哪一种类型,再调相应的子程序。如以上1+2的程序,也可以这样写,先将加数和被加数分别送入40H、41H单元,加完后和送入42H单元。它的完整程序是:

此程序也用子程序调用的方法写。将加的这一部分写成通用程序:

使用这个程序之前,先将加数、被加数送入40H、41H单元,完整的程序如下:

标号AD1到RET的这段程序就为子程序。送入40H、41H单元的数,叫入口参数。送入42H单元的数称为出口参数。

2. 分支程序设计

在处理实际事务中,只用简单程序设计的方法是不够的。因为大部分程序总包含有判断、比较等情况,这就需要分支程序。分支程序是利用条件转移指令,使程序执行到某一指令后,根据条件(即上面运行的情况)是否满足,来改变程序执行的持续。下面举两个分支程序的例子。

例2:求单字节有符号二进制数的补码。

正数补码是其本身,负数的补码是其反码加1。因此,程序首先判断被转换数的符号,负数进行转换,正数即为补码。设二进制数放在累加器A中,其补码放回到A中。

程序为:

例3:比较两个无符号数的大小。设两个连续外部RAM单元ST1和ST2中存放不带符号的二进制数,找出其中的大数存入ST3单元中。

程序如下:

上面程序中,用减法指令SUBB来比较两数的大小。由于这是一条带借位的减法指令,在执行该指令前,先把进位位清零。用减法指令通过借位(CY)的状态判两数的大小,是两个无符号数比较大小时常用的方法。设两数X、Y,当X≥Y时,用X-Y结果无借位(CY)产生,反之借位为1,表示X<Y。用减法指令比较大小,会破坏累加器中的内容,故作减法前先保存累加器中的内容。执行JNC指令后,形成了分支。执行SJMP指令后,实现程序的转移。

分支程序在实际使用中用处很大,除了用于比较数的大小之外,还常用于控制子程序的转移。

3. 循环程序设计

在程序设计中,只有简单程序和分支程序是不够的。因为简单程序,每条指令只执行一次,而分支程序则根据条件的不同,会跳过一些指令,执行另一些指令。它们的特点是,每一条指令至多执行一次。在处理实际事务时,有时会遇到多次重复处理的问题,这就需要下面讲的循环程序来完成。循环程序是强制CPU重复执行某一指令系列(程序段)的一种程序结构形式,凡是要重复执行的程序段都可以按循环结构设计。

循环程序一般由五部分组成:

(1)初始化部分:为循环程序做准备。如:设置循环次数计数器的初值,地址指针置初值,为循环变量赋初值等。

(2)处理部分:为反复执行的程序段,是循环程序的实体。

(3)修改部分:每执行一次循环体后,对指针作一次修改,使指针指向下一数据所在位置,为进入下一轮处理做准备。

(4)控制部分:根据循环次数计数器的状态或循环条件,检查循环是否能继续进行,若循环次数到或循环条件不满足,应控制退出循环,否则继续循环。

通常(2)、(3)、(4)部分又称为循环体。

(5)结束部分:分析及存放执行结果。

循环程序的结构一般有两种形式:

(1)先进入处理部分,再控制循环。即至少执行一次循环体。如图3-1(a)所示。

(2)先控制循环,后进入处理部分。即先根据判断结果,控制循环的执行与否,有时可以不进入循环体就退出循环程序。如图3-1(b)所示。

循环结构的程序,不论是先处理后判断,还是先判断后处理,其关键是控制循环的次数。根据需要解决问题的实际情况,对循环次数的控制有多种,循环次数已知的,用计数器来控制循环,循环次数未知的,可以按条件控制循环,也可以用逻辑尺控制循环。

图3-1 循环程序的结构

例4:工作单元清零程序设计。在程序设计时,有时需要将存储器中的部分地址作为工作单元,存放程序执行的中间值和结果,此时常需要对这些工作单元清零。

如:将40H为起点的8个单元清“0”。

此程序的前2~4句为设定循环初值,4~7句为循环体。

以上是内部RAM单元清零,也可清外部RAM单元。

例如:设有50个外部RAM单元要清“0”,即为循环次数存放在R2寄存器中,其首地址存放在DPTR中,设为2000H。

程序如下:

本例中循环次数是已知,用R2作循环次数计数器。用DJNZ指令修改计数器值,并控制循环的结束与否。

此程序也可写成通用子程序形式:

入口参数是由实际需要而定,若要清4000H为起点的100个单元,只要改动前面两句就行。

例5:多个单字节数据求和程序设计。已知有n个单字节数据,依次存放在内部RAM40H单元开始的连续单元中。要求把计算结果存入R2和R3中(高位存R2,低位存R3)。

程序如下:

上述程序中,用R0作间址寄存器,每作一次加法,R0加1,数据指针指向下一数据地址,R5为循环次数计数器,控制循环的次数。

(2)循环次数未知的循环程序。以上介绍的几个循环程序例子,它们的循环次数都是已知的,适合用计数器置初值的方法。而有些循环程序事先不知道循环次数。不能用以上方法。这时需要根据判断循环条件的成立与否,或用建立标志的方法,控制循环程序的结果。

如果在一个循环体中又包含了其他的循环程序,即循环中还套着循环,这种程序称为多重循环程序。

例6:冒泡程序设计。设有N个数,它们依次存放于LIST地址开始的存储区域中,将N个数比较大小后,使它们按由小到大(或由大到小)的次序排列,存放在原存储区域中。

编制该程序的方法:依次将相邻两个单元的内容作比较,即第一个数和第二个数比较,第二个数和第三个数比较……如果符合从小到大的顺序则不改变它们在内存中的位置,否则交换它们之间的位置。如此反复比较,直至数列排序完成为止。

由于在比较过程中将小数(或大数)向上冒,因此这种算法称为“冒泡法”或称排序法,它是通过一轮一轮的比较,第一轮经过N次两两比较后,得到一个最大数。第二轮经过N-1次两两比较后,得到次大数……

每轮比较后得到本轮最大数(或最小数),该数就不再参加下一轮的两两比较,故进入下一轮时,两两比较次数减1。为了加快数据排序速度,程序中设置一个标志位,只要在比较过程中两数之间没有发生过交换,就表示数列已按大小顺序排列了。可以结束比较。

设数列首地址在R0寄存器中,R2为外循环次数计数器,R3为内循环次数计数器,R1为交换标志。

程序如下:

【本章小结】

本章主要学习单片机的寻址方式和指令系统。寻址方式是本章的一个难点,也是一个重点,应该重点掌握。单片机的指令主要包括五大类指令,还包括一些伪指令。使用单片机汇编指令具有较高的效率,具有较大的应用价值。单片机的指令虽然很多,但是重点还是要掌握常用的指令。

3.5 习题

1. MCS-51系列单片机的寻址方式有哪几种?请分析各种寻址方式的访问对象与寻址范围。

2. 要访问片内RAM,可有哪几种寻址方式?要访问片外RAM,有哪几种寻址方式?要访问ROM,又有哪几种寻址方式?

3. 请判断下列各条指令的书写格式是否有错,如有错说明原因。

(1)MOV 28H,@R2

(2)MOV F0,C

(3)CLR R0

(4)MUL R0R1

(5)MOVC @A+DPTRA

(6)JZ A,LOOP

4. 已知程序执行前有(A)=02H,(SP)=42H,(41H)=FFH,(42H)=3FH。下述程序执行后,(A)=_______,(SP)=_______,(51H)=_______,(52H)=_______。

    POP  DPH

    POP  DPL

    RL  A

    MOV  B,A

    MOVC A,@A+DPTR

    PUSH A

    MOV  A,B

    INC  A

    MOVC A,@A+DPTR

    PUSH A

    RET

    ORG  4000H

    DB  10H,80H,30H,50H,30H,50H

5. 已知(R0)=20H,(20H)=10H,(P0)=30H,(R2)=20H,执行如下程序段后,(40H)=_______。

MOV @R0,#10H

MOV A,R2

SETB C

ADDC A,20H

MOV PSW,#80H

SUBB A,P0

XRL A,#67H

MOV 40H,A

6. 假定(A)=83H,(R0)=27H,(27H)=34H,执行以下指令后,A的内容为_______。

ANL A,#27H

ORL 27H A

XRL A,@R0

CPL A

7. 若SP=40H,标号LABEL所在的地址为3456H。LCALL指令的地址为2000H,执行指令如下:2000H LCALL LABEL

(1)堆栈指针SP和堆栈内容发生了什么变化?

(2)PC的值等于什么?

(3)如果将指令LCALL直接换成ACALL,是否可以?

(4)如果换成ACALL指令,可调用的地址范围是什么?

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈