第3章单片机汇编语言程序设计 最早应用于51单片机开发与应用的语言是汇编语言。汇编语言的执行速度快,代码短小精炼,且指令的执行周期确定,因此是一种高效的单片机语言。汇编语言也有不足之处: 指令复杂、缺乏通用性、不便于程序移植。随着电子技术的发展,汇编语言的使用范围越来越窄,逐渐被C51语言所代替。汇编语言的程序是汇编指令的集合,可以用来控制单片机实现特定的任务。51单片机的汇编语言程序设计与汇编指令集和硬件结构等有很大关系。汇编语言因为操作硬件的能力强而获得了广泛应用。本章对51单片机汇编语言程序设计的基本情况进行简单介绍。 3.151系列单片机的汇编指令格式和功能描述符 指令是CPU根据人的意图来执行某种操作的命令。一台计算机所能执行的全部指令的集合称为这个CPU的指令系统。指令系统的功能强弱在很大程度上决定了这类计算机性能的高低。 MCS51系列单片机指令系统具有功能强、指令短、执行快等特点,用42个助记符代表33种操作功能,共111条指令,其中,有49条单字节指令、45条双字节指令和17条三字节指令; 有64条单周期指令、45条双周期指令,只有乘法、除法两条指令为四周期指令。占字节数多的指令不一定执行时间就长,反之亦然。按指令的功能属性,MCS51系列单片机指令可分为数据传送指令、算术运算指令、逻辑操作指令、控制转移指令和位操作指令。本节将对指令格式和指令功能描述符进行介绍和说明。 3.1.1指令格式 不同的单片机指令可以实现不同的功能,具体格式也不同。但是从总体上分析,每一条指令通常由操作码和操作数两部分组成。操作码表示计算机执行该指令将进行何种操作,操作数表示参加操作的数或者操作数所在的地址。MCS51单片机汇编语言指令的基本格式为 [标号: ]操作码助记符 [操作数1][,操作数2][,操作数3]{; 注释} 方括号内的部分可以根据实际情况取舍,每个字段之间可以用分隔符分隔,可以用作分隔符的符号有空格(用于操作码和操作数之间)、冒号(用于标号之后)、逗号(用于操作数之间)、分号(用于注释之前)等。例如,“LOOP: MOV A,#7FH; A←7FH”的功能是循环将立即数#7FH传送到目的操作数累加器A中。 具体说明如下: (1) 标号是指令的符号地址,通常作为转移指令的操作数。对标号有如下规定: ① 由1~31个字符组成,要以非数字字符开头,后跟字母、数字、“-”和“?”等字符。 ② 不能用已定义的保留字(如指令助记符、伪指令、寄存器名称和运算符等)。 ③ 必须后跟英文冒号“:”。 (2) 操作码助记符表明指令的不同功能,是汇编语句中唯一不能空缺的部分,汇编器在汇编时会将其翻译成对应的二进制代码。不同的指令有不同的助记符,一般用说明其功能的英文缩写表示。例如,“MOV”表示传送,“ADD”表示加法。三操作数指令只有一条,就是比较转移指令“CJNE”,后面的章节会介绍。 (3) 操作数是指令要操作的数据或数据的地址。在一条汇编语句中,操作数可能是空缺的,也可能是多项的。MCS51单片机的指令按操作数的多少,可分为无操作数、单操作数、双操作数和三操作数四种指令。例如,“RET”指令是返回调用子程序的下一条指令位置,该指令无操作数; “INC A”的功能是对累加器A中的内容加1,只有一个操作数; 而上文提到的“LOOP: MOV A#7FH ; A←7FH”指令,有两个操作数。操作数的内容可能包含以下几种情况: ① 数据。 二进制数,末尾以B标识。如,1111 1000B。 十进制数,末尾以字母D标识或将字母D省略。如,88D,88。 十六进制数,末尾以字母H标识。如,88H,0F8H。此处应该注意,十六进制数以字母A~F开头时,应在其前面加上数字0引导,以便于汇编器将其与标号或符号名相区分。 ASCII码以单引号进行标识。如‘A’,‘1234’。 ② 符号。可以是符号名、标号或特定的符号“$”(该指令的存储地址)等。 ③ 表达式。由运算符和数据构成的算式。可用的运算符如表31所示。 (4) 注释是对语句的解释说明,是编程人员根据需要加上去的,用于对指令进行解释和说明,可以增加程序的可读性,也有助于程序的阅读和维护。对于指令本身的功能而言,是可以不添加的。该字段必须以英文“;”开头,当一行书写不下时,可以换行接着书写,但换行时应注意使用“;”开头。 表3151汇编器的运算符及其优先级 优先级运算符功能表达式及其结果 ( )括号4*(4+4)即64 NOT、HIGH、LOW取反、取高字节、取低字节NOT 55H即AAH; HIGH 1234H即12H +、-正号、负号+3、-4 *、/、MOD乘、 除(取商)、取余数17/6即2; 17/6即5 +、-加、 减5+4即9; 5-4即1 SHL、SHR左移、右移2 SHL 2即8; 8 SHR 2即2 AND、OR、XOR与、 或、异或45H AND 0FH即05H <、>、=、<>、<=、>=比较运算符MOV A,X>8 若X>8为真,则为MOV A,01H; 若X>8为假,则为MOV A,00H 注: 表达式中含有多个相同优先级运算符时,优先级按“从左到右”顺序确定。 3.1.2指令功能描述符 MCS51单片机汇编指令功能描述符常用以下符号表示: (1) Rn(n=0~7): 表示当前工作寄存器组中的寄存器R0~R7之一; (2) Ri(i=0,1): 表示当前工作寄存器组中的寄存器R0或R1; (3) A: 累加器,又可写成ACC; (4) B: 专用寄存器,多用于乘法MUL、除法DIV指令中; (5) C: 表示当前工作寄存器组中的寄存器R0或R1; (6) @: 间接寻址或变址寻址前缀; (7) #data: 8位立即数; (8) #data16: 16位立即数; (9) rel: 以补码形式表示的8位地址偏移量,其值在-128~+127内; (10) addr11: 11位直接地址; (11) addr16: 16位直接地址; (12) direct: 直接寻址的地址(片内RAM单元地址及SFR地址,可用符号名称表示); (13) bit: 按位寻址的直接位地址(片内RAM位地址及SFR中的位地址,可用符号名称表示); (14) DPTR: 数据指针,作16位地址寄存器; (15) /: 表示对该位操作数取反,但不影响该位的原值; (16) →或←: 表示数据传送方向; (17) (×): 表示×地址单元或寄存器的内容; (18) ((×)): 表示×地址单元或寄存器内容为地址所指定单元的内容; (19) : 表示数据交换; (20) $: 指令自身的首地址,用作跳转指令的自循环地址标号。 3.251系列单片机指令的寻址方式 指令由操作码和操作数组成,操作数又分为源操作数和目地操作数。操作数总是存放在某一存储单元中,寻找操作数实际上是寻找操作数所在的单元地址,称为寻址方式,寻址方式取决于单片机自身的硬件结构。总体来说,寻址方式越丰富,单片机的指令功能越强,编程灵活性越大,指令系统也越复杂。寻址方式所要解决的主要问题就是如何在整个存储器和寄存器的寻址空间内,灵活方便、快速地找到指令的操作数。 MCS51单片机中,操作数的存放范围很宽,可以放在片外ROM/RAM中,也可以放在片内ROM/RAM以及SFR中。为了适应在操作数范围内的寻址,MCS51单片机指令系统使用了7种寻址方式: 立即寻址、直接寻址、寄存器寻址、寄存器间接寻址、变址寻址、相对寻址和位寻址。 3.2.1立即寻址 操作数是1字节或2字节的常数,直接在指令中给出,用“#”符号作为前缀,以区别直接寻址方式。在指令编码中,该操作数紧跟在操作码后面,与操作码一起存放在指令代码段(ROM)中,可以立即得到并执行,不需要经过别的途径去寻找,被称为立即数,该寻址方式被称为立即寻址。例如, MOVA,#18H 该指令的功能是把立即数18H送到累加器A,其中操作数18H为源操作数,是立即数。指令执行后累加器A中的内容为18H。该条指令的操作码为74H,存储和执行过程如图31所示。 图31立即寻址方式示意图 在MCS51单片机指令系统中,仅有一条包含16位立即数的指令,形式为“MOVDPTR,#data16”,其中“#data16”表示16位立即数。例如: 指令“MOVDPTR,#1234H”,其功能是把16位立即数“1234H”传送到寄存器DPTR中,其中高8位“12H”送到DPH,低8位“34H”送到DPL。 因为立即数直接存放在ROM中,所以立即寻址对应的寻址空间为ROM空间。 3.2.2直接寻址 指令中直接给出操作数所在存储单元的地址。在指令编码中,操作数所在存储单元的地址紧跟在操作码之后,与操作码一起存放在指令代码段中,而操作数本身则存放在该地址所指示的存储单元中。例如, MOVA,18H 该指令的功能是把内部RAM中18H地址的内容传送到累加器A中。如果指令执行前片内数据存储器18H单元的内容为56H,则执行后累加器A的内容为56H。该条指令的操作码为E5H,存储和执行过程如图32所示。 图32直接寻址方式示意图 直接寻址可访问内部RAM低128个单元及SFR区域。需要注意的是,片内RAM高128个单元(增强型)必须采用寄存器间接寻址方式访问。对于SFR,在指令中通常采用寄存器符号来表示操作数所在单元的地址。例如,“MOV A,80H”可以直接写成“MOV A,P0”。这里的“P0”和“80H”是等效的。 3.2.3寄存器寻址 操作数存放在寄存器中,指令中直接给出该寄存器的名称。存放操作数的寄存器在指令代码中不占据单独的字节,而是包含在操作码字节中,因此该寻址方式的指令为一字节指令。由于寄存器在单片机CPU内部,因此采用寄存器寻址的速度相比于其他几种寻址方式要快,可以使程序具有较好的运算处理速度。 在MCS51系列单片机中,寄存器寻址方式针对的寄存器只能是R0~R7这8个通用工作寄存器和部分特殊功能寄存器(如累加器A、寄存器B、数据指针寄存器DPTR和位累加器CY)。在汇编指令中,寄存器寻址在指令中直接提供寄存器的名称,如R0、R1、A等。例如, MOVA,R0 该指令的功能是把R0中的内容传送到累加器A中。如果指令执行前R0中的内容为56H,则指令执行后累加器A中的内容为56H。该条指令的操作码为E8H,存储和执行过程如图33所示。 图33寄存器寻址方式示意图 另外,对于第0组工作寄存器,R0的物理地址为00H,因此指令“MOV A,00H”和“MOV A,R0”实现的功能是一样的,都是将00H单元的内容传送到累加器A中。但是两者也是有区别的,前者属于直接寻址,机器码为E5H、00H,这条指令占用2字节; 后者属于寄存器寻址,机器码为E8H,这条指令占用1字节。由此可见,同一个功能可以采用不同的指令来实现,而且对于同一个存储单元,若采用不同的形式表示,所对应的寻址方式是不同的。 3.2.4寄存器间接寻址 指令中给出的寄存器中存放的不是操作数,而是操作数所在单元的地址,类似于C语言的指针,即操作数是通过指令中给出的寄存器间接得到的。在MCS51系列单片机中,只能使用寄存器R0、R1、SP和DPTR作为间接寻址的寄存器。为了区别于寄存器寻址方式,在寄存器的名称前加前缀标志“@”来表示寄存器间接寻址。例如, MOVA,@R0 该指令的功能是把以R0中的内容作为地址的片内RAM单元的数据传送到累加器A中。如果指令执行前R0中的内容为80H,片内RAM80H地址单元的内容为56H,则指令执行后累加器A中的内容为56H。该条指令的操作码为E6H,存储和执行过程如图34所示。 图34寄存器间接寻址方式示意图 寄存器间接寻址方式的寻址空间为RAM,并且, (1) 内部RAM低128单元,应使用R0或者R1作为间接寻址寄存器,其通用形式为“@R0”或“@R1”。 (2) 对外部RAM单元的间接寻址,一般采用两种方式: 采用Ri(i=0或1)作为间接寻址寄存器,可以寻址256个单元; 或者采用16位数据指针DPTR作为间接寻址寄存器,可以寻址外部RAM完整64KB地址空间。 (3) 堆栈操作指令PUSH和POP使用堆栈指针SP作为间接寻址寄存器对堆栈区进行间接寻址。 3.2.5变址寻址 以DPTR或PC作为基址寄存器,以累加器A作为变址寄存器,并以两者内容相加形成操作数所在单元的地址。在MCS51系列单片机中,用变址寻址方式只能访问ROM,寻址范围为0000H~FFFFH,最大存储容量为64KB。该寻址方式通常用于访问ROM中的表格型数据,表首单元的地址为基址,放于基址寄存器,访问的单元相对于表首的位移量为变址,放于变址寄存器,通过变址寻址可得到ROM相应单元的数据。例如, MOVCA,@A+DPTR 该指令的功能是把DPTR和累加器A的内容相加得到的ROM中某单元的地址对应单元中的内容传送到累加器A中。如果指令执行前,数据指针寄存器DPTR的值为2000H,累加器A中的值为05H,ROM 2005H单元的内容为56H,则指令执行后累加器A中的内容为56H。该条指令的操作码为93H,存储和执行过程如图35所示。 图35变址寻址方式示意图 需要说明的是,变址寻址的指令只有3条: (1) MOVCA,@A+DPTR (2) MOVCA,@A+PC (3) JMP@A+PC 其中,前两条指令是ROM读指令,最后一条是无条件跳转指令。 3.2.6相对寻址 程序计数器PC的当前值加上指令中给出的偏移量rel,所得结果作为转移地址送入PC中,即跳向一个新的地址来执行程序。采用该寻址方式进行操作时修改的是PC值,因此这种寻址方式用于实现程序的分支跳转。 使用相对寻址时需要注意以下两点, (1) PC的当前值为读出2字节或者3字节的跳转指令后,PC指向的下一条指令的地址,即PC当前地址=相对转移指令所在存储单元的地址+指令字节数。 例如,“JZ rel”是一条累加器A为0就转移的双字节指令。若该指令的存储地址为2020H,则执行该指令时的PC当前值为2022H。 (2) 偏移量rel是一个有符号的8位二进制补码数,以补码的形式置于操作码之后存放,范围是-128~+127。负数表示向地址减小的方向转移,正数表示向地址增加的方向转移,目标地址=当前PC值+rel=指令存储地址+指令字节数+rel。 例如,rel为75H,PSW.7为1,指令“JCrel”存放在ROM地址1000H处开始的单元,即PC中的值为1000H,则执行“JCrel”(指令的机器码为4075H)指令后,程序跳转到1077H单元取指令并执行,执行过程如图36所示。目标地址=当前PC值(1000H+02H)+偏移量(75H)=1077H。 图36相对寻址方式示意图 实际编写程序时,程序中只需要在转移指令中给出地址标号,汇编过程中编译器会自动计算偏移量rel,如果rel值超越规定的有效范围,会自动给出错误提示。此情况下需要利用跳转范围较大的绝对转移指令(如AJMP)转移到所需的地址标号。例如, SJPMLOOP; LOOP为要转向的目标地址标号 3.2.7位寻址 MCS51系列单片机中,有一个独立的位处理器,能够进行各种位运算,位运算的操作对象为片内RAM的位寻址区和某些可位寻址的SFR内的位数据。指令中给出位数据的位地址的寻址方式称为位寻址。位寻址方式属于位的直接寻址,与直接寻址方式不同的是,位寻址只给出位地址,而不是字节地址。需要注意的是,位地址和字节地址的表示形式完全一样,位地址和字节地址是根据指令功能来区分的。如果是位操作指令,则所操作的是位地址,属于字节地址中的某一位; 如果是字节操作指令,则所操作的是字节地址,属于整个字节。例如, MOVC,00H; 对位累加器C操作,00H是位地址(属于字节地址20H的最低位) MOVA,00H; 对累加器A操作,00H是字节地址(属于0区工作寄存器的R0) MCS51系列单片机内部RAM中有两个区域可以位寻址: (1) 片内RAM的20H~2FH单元是可以进行位寻址的区域,共16×8=128位,它们的位地址为00H~7FH。例如,20H单元的0~7位的位地址为00H~07H。该区域的可寻址位有两种表示方法: ① 直接使用位地址表示; ②使用单元地址加位序号表示。 例如,位地址00H和20H.0指的都是片内RAM中20H单元的第0位。编程中常用“位地址”表示方法。 (2) SFR的可寻址位,可提供位寻址的特殊功能寄存器有11个,字节地址能被8整除的特殊功能寄存器可以位寻址。该区域的可寻址位有4种表示方法。 ① 使用位名称表示; ②直接使用位地址表示; ③使用单元地址加位序号表示; ④使用SFR符号加位序号表示。 例如,终端允许寄存器IE可寻址位如下: D7D6D5D4D3D2D1D0 (A8H) IE AFHAEHADHACHABHAAHA9HA8H EAESET1EX1ET0EX0 其中,最高位是中断允许位,位名称是EA,直接位地址是0AFH,单元地址加位序号是0A8H.7,SFR符号加位序号是IE.7。编程中常用“位名称”表示方法。 本节介绍的七种寻址方式及它们所对应的寄存器和寻址空间如表32所示。 表32七种寻址方式所对应的寄存器和寻址储空间 寻 址 方 式使用的变量寄存器或寻址空间 立即寻址直接给出数字,无变量ROM 直接寻址直接给出变量,无地址片内RAM低128字节; SFR 寄存器寻址R0~R7、A、B、DPTR、位累加器C工作寄存器R0~R7; A、AB、DPTR和C; 部分SFR 寄存器间接寻址@R0、@R1、SP或@DPTR片内RAM或片外RAM 变址寻址@A+DPTR、@A+PCROM 相对寻址PC+偏移变量ROM 位寻址直接给出位地址或位符号片内RAM的位寻址区,SFR的可寻址位 3.351系列单片机的指令系统 51系列单片机的指令系统共有111条指令,按功能不同可以分为5大类: (1) 数据传送指令(29条): 实现存储器赋值、数据转移等功能; (2) 算术运算指令(24条): 实现数值的加、减、乘、除等运算功能; (3) 逻辑运算指令(24条): 实现逻辑与、或、异或、移位等功能; (4) 控制转移指令(17条): 实现程序条件转移、无条件转移等功能; (5) 0位操作指令(17条): 实现位清0、置1、判断等功能,由51单片机内部特有的布尔处理器完成。 3.3.1数据传送指令 51系列单片机中,数据传送是最基本也是最主要的操作。该操作可以在片内RAM单元和SFR中进行,也可以在累加器A和片外RAM之间进行,还可以到ROM中进行查表,并且除了以累加器A为目的操作数的指令会对PSW中的奇偶标志位P有影响外,其余指令执行时均不会影响任何标志位。因此,数据传送指令是指令系统中数量最多、使用也最频繁的一类指令。数据传送指令共29条,采用7个助记符MOV、MOVX、XCH、XCHD、SWAP、PUSH和POP表示。 数据传送指令可分为三组: 普通数据传送指令、数据交换指令和堆栈操作指令。 1. 普通数据传送指令 普通数据传送指令的功能是将源操作数的内容复制到目的操作数所在单元,而源操作数的内容不变。该指令以助记符MOV为基础,根据访问对象的不同,分为片内数据存储器传送指令、片外数据存储器传送指令和程序存储器传送指令。 图37操作数组合 1) 片内数据存储器传送指令MOV 指令格式如下: MOV目的操作数,源操作数 其中,源操作数可以是A、Rn、@Ri、direct、#data[16],目的操作数可以为A、Rn、@Ri、direct、DPTR。将目的操作数和源操作数按照不同的寻址方式进行组合,组合起来总共16条,如图37所示。 基于此,片内数据存储器传送指令按目的操作数的寻址方式,可划分为5组。 (1) 以A为目的操作数(4条): MOVA,Rn; A←Rn MOVA,direct; A←(direct) MOVA,@Ri; A←(Ri) MOVA,#data; A←#data 上述指令的功能是将源操作数所指定的工作寄存器Rn(即R0~R7)的内容、直接寻址或寄存器Ri(即R0或R1)寻址所得的片内RAM单元或特殊功能寄存器中的内容以及立即数传送到累加器A中。 上述操作不影响源字节和任何别的寄存器内容,只影响PSW的P标志位。 例如, MOVA,#55H; A←55H MOVA,55H; A←(55H) MOVA,R0; A←(R0) MOVA,@R0; A←((R0)) (2) 以Rn为目的操作数(3条): MOVRn,A; Rn←A MOVRn,direct; Rn←(direct) MOVRn,#data; Rn←#data 上述指令的功能是将源操作数所指定的内容传送到当前工作寄存器组R0~R7中的某个寄存器。源操作数有寄存器寻址、直接寻址和立即数寻址3种方式。 注意,51指令系统中没有“MOVRn,Rn”传送指令。 例如, MOVR7,A; R7←A MOVR7,55H; Rn←(55H) MOVR7,#55H; Rn←#55H (3) 以直接地址direct为目的操作数(5条): MOVdirect,A; (direct)←A MOVdirect,Rn; (direct)←Rn MOVdirect,direct; (direct)←(direct) MOVdirect,@Ri; (direct)←(Ri) MOVdirect,#data; (direct)←#data 上述指令的功能是将源操作数指定的内容送到由直接地址direct所指定的片内存储单元中。源操作数的寻址方式分别为寄存器寻址、直接寻址、寄存器间接寻址和立即寻址。 注意,“MOVdirect,direct”指令中,源地址在前,目的地址在后。 例如, MOV30H,A; (30H)←A MOV30H,R0; (30H)←R0 MOV30H,55H; (30H)←(55H) MOV30H,@R0; (30H)←(R0) MOV30H,#55H; (30H)←#55H (4) 以@Ri为目的操作数(3条): MOV@Ri,A; (Ri)←A MOV@Ri,direct; (Ri)←(direct) MOV@Ri,#data; (Ri)←#data 上述指令的功能是将源操作数所指定的内容传送到Ri(R0或R1)内容所指向的地址单元中,源操作数的寻址分别为寄存器寻址、直接寻址和立即寻址。 例如, MOV@R0,A; (Ri)←A MOV@R0,55H; (Ri)←(55H) MOV@R0,#55H; (Ri)←#55H (5) 以DPTR为目的操作数(1条): MOVDPTR,#data16; DPTR←#data16 该条指令是51单片机指令系统中唯一的一条16位数据传送指令,该指令的功能是将16位立即数传送到数据指针DPTR中。由于DPTR是由DPH和DPL组成的,因此这条指令执行后要把data16的高8位数据传送给DPH,而把低8位数据传送给DPL。例如, MOVDPTR,#2345H 该指令执行后,DPH中的值为23H,DPL中的值为45H。 为了实现此功能,我们也可以分别向DPH和DPL传送数据,即用下面的两条指令实现上述功能。 MOVDPH,#23H MOVDPL,#45H 此时,DPH和DPL是特殊功能寄存器,属于direct类型。 综合以上,可以得出51单片机MOV传送指令中各操作数之间的关系,如图38所示。 图38MOV指令中各操作数之间的关系 需要说明的是,片内数据存储器传送指令MOV在使用时应注意,源操作数和目的操作数中的Rn和@Ri不能相互配对,不允许“MOVRn,Rn”“MOV@Ri,Rn”这样的指令。在MOV指令中,不允许在一条指令中同时出现工作寄存器,无论它是寄存器寻址还是寄存器间接寻址。 【例31】 设(70H)=60H,(60H)=20H,P1为输入口,状态为0B7H,执行如下程序: MOVR0,#70H MOVA,@R0 MOVR1,A MOVB,@R1 MOV@R0,P1 【解】 程序执行后,(R0)=70H,(A)=60H,(R1)=60H,(B)=20H,(70H)=0B7H。 【例32】 把存放在片内RAM 30H单元中的数据00H送到60H单元中(要求最终指令以@Ri为目的操作数)。 【解】 程序1: MOVR1,#60H; (R1)=60H MOV@R1,30H; ((R1))=(60H)=(30H)=00H 程序2: MOVR1,#60H; (R1)=60H MOVA,30H; (A)=(30H)=00H MOV@R1,A; ((R1))=(60H)=(A)=(30H)=00H 2) 片外数据存储器传送指令MOVX 51指令系统中,只能通过累加器A与片外数据存储器进行数据传送,且采用@Ri和@DPTH寄存器间接寻址的方式完成。该类指令的助记符为MOVX,共有4条,分别为 MOVXA,@DPTR; A←(DPTR) MOVX@DPTR,A; (DPTR)←A MOVXA,@Ri; A←(Ri) MOVX@Ri,A; (Ri)←A 其中,前两条指令通过@DPTR间接寻址,可以对整个64KB片外数据存储器访问。高8位地址放在DPH中,由P2口输出,低8位地址放在DPL中,由P0口输出。后两条指令通过@Ri间接寻址,只能对片外数据存储器的低256字节访问,高8位地址放在Ri(R1或R0)中,由P0口输出,此时如果访问超过256字节的外部RAM空间,需利用P2口确定高8位地址(也称页地址)。 值得说明的是,片外扩展的I/O接口也要利用这4条指令进行数据输入和输出。 【例33】 试写出完成以下功能的程序: ① 将片内RAM 60H单元中的内容送入片外RAM 40H单元中。 ② 将片外RAM 2000H单元中的内容送到片内RAM 20H单元中。 ③ 将片外RAM 2010H单元中的内容送到片外RAM 2020H单元中。 【解】 程序如下: ①MOVA,60H MOVR0,#40H MOVX@R0,A ②MOVDPTR,#2000H MOVXA,@DPTR MOV20H,A ③MOVP2,#20H MOVR0,#10H MOVXA,@R0 MOVR1,#20H MOVX@R1,A 3) 程序存储器传送指令MOVC 通常ROM中可以存放两类内容: 一是单片机执行的程序代码; 二是一些固定不变的常数(如表格数据、字符代码等)。访问ROM实际就是读取ROM常数表中的数据,简称查表,而访问ROM的数据传送指令被称为查表指令。该指令必须通过累加器A采用变址寻址的方法来完成,共有两条指令: MOVCA,@A+DPTR; A←(A+DPTR) MOVCA,@A+PC; A←(A+PC) 此两条指令主要用于对存放在ROM中常数表的查找。由于偏移量在A中,故常数表长度不超过256B。利用上面第1条指令查表时,表头地址由DPTR指定,所以常数表易于放置在64KB的任意位置,称为远程查表指令; 利用第2条指令查表时,常数表放在该指令后的256B空间查表,称为近程查表指令。 对于近程查表指令应该注意,PC的内容是指取出该条指令字节后的PC值,与远程查表指令相比,该指令无须DPTR参与(节省资源),也没有影响PC的内容。 2. 数据交换指令 普通数据传送指令实现将源操作数的数据传送到目的操作数,指令执行后,源操作数不变,数据传送是单向的。数据交换指令是把数据双向传送,传送后,前一个操作数原来的内容传送到后一个操作数中,后一个操作数原来的内容传送到前一个操作数中。数据交换指令又分为字节交换指令和半字节交换指令两种。 (1) 字节交换指令包含以下3条: XCHA,Rn;A<=>Rn XCHA,direct;A<=>(direct) XCHA,@Ri;A<=>(Ri) 这3条指令的功能是将累加器A中的数据与源操作数中的数据进行交换。 (2) 半字节交换指令包含以下2条: XCHDA,@Ri;A0~3<=>(Ri)0~3 SWAPA;A0~3<=>A4~7 第1条指令的功能是将累加器A的低4位(低半字节)与间接寄存器所指向的地址单元中的低4位内容互换,而各自的高4位(高半字节)内容保持不变。 第2条指令的功能是将累加器A内部的高4位与低4位的内容互换。 值得说明的是,数据交换指令要求第一个操作数必须为累加器A。 【例34】 已知R0=30H,(30H)=4AH,A=28H,试分析下列指令执行的结果: ① XCHA,@R0 ② XCHDA,@R0 ③ SWAPA 【解】 ①执行后A=4AH,(30H)=28H; ②执行后A=2AH,(30H)=48H; ③执行后A=82H。 3. 堆栈操作指令 堆栈是片内RAM中按“先进后出,后进先出”原则设置的专用存储区。此区域一端固定,称为栈底; 另一端是活动的,称为栈顶,栈顶的位置(地址)由指针SP指示(即SP的内容是栈顶的地址)。在MCS51单片机中,堆栈设置在片内RAM的低128字节单元,且生长方向是向上(地址增大)的。系统复位时,SP的内容为07H。通常,用户应在系统初始化时对SP重新设置。SP的值越小,堆栈的深度越深。 堆栈主要用于子程序调用时保护返回地址,或者用于保护子程序调用之前的某些重要数据(即保护现场)。 堆栈操作指令有2条: PUSHdirect;SP←SP+1,SP←(direct) POPdirect;(direct)←SP,SP←SP-1 其中,PUSH为入栈指令,POP为出栈指令。操作时以字节为单位。入栈时,SP指针先加1,再入栈; 出栈时,内容先出栈,SP指针再减1。通常情况下,入栈指令和出栈指令是成对出现的。用堆栈保护数据时,先入栈的内容后出栈; 后入栈的内容先出栈。例如, 若入栈保存时,入栈的顺序为 PUSHA PUSHB 则出栈的顺序为 POPB POPA 【例35】 用堆栈指令实现RAM 10H和20H中的内容交换,设(10H)=12H,(20H)=34H。 【解】 程序如下: MOVSP,#6FH;设置堆栈指针指向6FH PUSH10H;SP←SP+1,SP=70H,(71H)=12H PUSH20H;SP←SP+1,SP=71H,(71H)=34H POP10H;(10H)=34H,SP←SP-1,SP=70H POP20H;(20H)=12H,SP←SP-1,SP=6FH 3.3.2算术运算指令 算术运算指令可以完成加、减、乘、除、加1、减1和十进制调整,共24条。这些指令的操作数都是8位无符号数,不能直接对有符号数和16位数据进行运算。算术运算指令的执行一般会影响程序状态字PSW中的一些标志位。 1. 加法指令 加法指令有不带进位的加法指令、带进位的加法指令和加1指令。 (1) 不带进位的加法指令ADD ADDA,Rn;A←A+Rn ADDA,direct;A←A+(direct) ADDA,@Ri;A←A+(Ri) ADDA,#data;A←A+#data 这4条8位二进制数加法指令的一个加数总是来自累加器A,而另一个加数可由寄存器寻址、直接寻址、寄存器间接寻址和立即数寻址等不同的寻址方式得到,相加结果总是放在累加器A中。 使用指令时要注意累加器A中的运算结果对各个标志位的影响。 ① 如果位7有进位,则进位标志CY置1,否则CY清0; ② 如果位3有进位,则辅助进位标志AC置1,否则AC清0; ③ 如果位6有进位,而位7没有进位,或者位7有进位,而位6没有进位,则溢出标志位OV置1,否则OV清0; ④ 累加器A中有奇数个“1”时,P=1,反之P=0。 值得说明的是,溢出标志位OV的状态,只有在进行带符号数加法运算时才有意义。当两个带符号数相加时,OV=1,表示加法运算超出了累加器A所能表示的带符号数的有效范围(-128~+127),即产生了溢出,运算结果是错误的,否则运算是正确的,即无溢出产生。 在实际应用中,编程者应该确保单字节无符号数运算结果不超过255。如果结果可能大于255,就应将数据用多字节形式表示; 单字节的有符号数运算结果不要超过-128~127,如果结果可能超过这个范围,就应该将数据用多字节表示或在程序运算中对PSW寄存器中的状态标志进行判断,并根据判断情况进行相应处理。 【例36】 分析如下程序段执行后,累加器A及PSW相关标志的结果。 MOVA,#10010010B ADDA,#11001001B 【解】 指令执行后,(A)=01011011B=5BH,CY=1,AC=0,OV=1,P=1。 【例37】 (A)=53H,(R0)=FCH,执行指令 ADDA,R0 【解】 运算结果为(A)=4FH,CY=1,AC=0,OV=0,P=1(位6和位7同时进位,所以OV=0)。 (2) 带进位的加法指令ADDC ADDCA,Rn;A←A+Rn+CY ADDCA,direct;A←A+(direct)+CY ADDCA,@Ri;A←A+(Ri)+CY ADDCA,#data;A← A+#data+CY 这组加法指令的特点是进位标志位CY参加运算,指令中不同寻址方式所指定的加数、进位标志与累加器A中内容相加,结果存在累加器A中,并根据指令执行情况,重新对CY、AC、OV、P等标志位置1或清0。 带进位的加法指令常用于多字节数加法运算中。 【例38】 已知当前的CY=1,分析下列指令的执行结果。 MOVA,#85H ADDCA,#97H 【解】 指令执行结果为(A)=1DH,CY=1,AC=0,OV=1,P=0。 【例39】 试把存放在R1R2和R3R4中的两个16位数相加,结果存于R5R6中。 【解】 处理时,R2和R4用一般的加法指令ADD,结果存放于R6中; R1和R3用带进位的加法指令ADDC,结果存放于R5中,程序如下: MOVA, R2 ADDA, R4 MOVR6,A MOVA, R1 ADDCA, R3 MOVR5,A (3) 加1指令INC INCA;A←A+1,影响P标志 INCRn;Rn←Rn+1 INCdirect;(direct)←(direct)+1 INC@Ri;((Ri))←((Ri))+1 INCDPTR;(DPTR)←(DPTR)+1 这组指令是单字节指令,其功能是把指令中所指出的变量加1,且不影响除P以外其余PSW中的任何标志位。若变量原来为FFH,加1后将溢出为00H(仅指前4条指令),标志也不会受到影响。最后一条指令是16位数加1指令,首先对低8位指针DPL的内容加1操作,当产生溢出时,就对DPH的内容进行加1操作,并不影响CY的状态。 Ri和DPTR常用作指针并指向一片存储区域的起始地址,将它们的内容加1可以指向下一单元的地址,编程时常利用这两个指针对一片存储区域进行操作。 2. 减法指令 减法指令有带借位减法指令和减1指令。 (1) 带借位减法指令SUBB SUBBA,Rn;A←A-Rn-CY SUBBA,direct;A←A-(direct)-CY SUBBA,@Ri;A←A-(Ri)-CY SUBBA,#data;A←#data-CY 这组指令的功能是从累加器A中的内容减去指定的变量和进位标志CY的值,结果存放在累加器A中,常用于多字节减法运算中。MCS51单片机中,只提供了一种带借位的减法指令。不带借位的减法操作可以通过先对CY标志清零,然后再执行带借位的减法来实现。 使用指令时要注意累加器A中的运算结果对各个标志位的影响。 ① 如果最高位有借位,则CY置1,否则CY清0; ② 如果低4位向高4位有借位时,AC=1,否则AC=0; ③ 如果位6和位7不同时,产生借位时,OV=1,否则OV=0; ④ 累加器A中有奇数个“1”时,P=1,反之P=0。 (2) 减1指令DEC DECA;A←A-1,影响P标志 DECRn;Rn←Rn-1 DECdirect;(direct)←(direct)-1 DEC@Ri;((Ri))←((Ri))-1 这组指令是单字节指令,其功能是把指令中所指出的变量减1,且不影响除P以外其余PSW中的任何标志位。若变量原来为00H,减1后将溢出为FFH,标志也不会受到影响。 【例310】 已知(A)=95H,(R0)=63H,(CY)=1,执行指令“SUBBA,R0”的结果。 【解】 结果为: (A)=31H,(CY)=0,AC=0,OV=1,P=1。 3. 乘法指令 在MCS51单片机中,乘法指令只有1条: MULAB 该指令的功能是将累加器A和寄存器B中的两个8位无符号数相乘,乘积为16位,高8位放在B中,低8位放在A中。对于PSW中标志位的影响情况如下: ① CY总是被清零; ② 若乘积大于FFH(255),则OV=1,否则OV=0; ③ 累加器A中有奇数个“1”时,P=1,反之P=0。 值得注意的是,在51单片机的指令系统中,乘法和除法指令的目的操作数和源操作数必须是A和B,且目的操作数和源操作数之间没有“,”分割符。 4. 除法指令 在MCS51单片机中,除法指令也只有1条: DIVAB 该指令的功能是将累加器A中的8位无符号二进制数除以寄存器B中的8位无符号二进制数,商的整数部分存放在累加器A中,余数部分存放在寄存器B中。对于PSW中标志位的影响情况如下: ① CY总是被清0; ② 当除数为0时,OV=1; ③ 累加器A中有奇数个“1”时,P=1,反之P=0。 5. 十进制调整指令 在MCS51单片机中,十进制调整指令只有1条: DAA 该指令的功能是在进行BCD码加法运算时,跟在ADD或ADDC后,对两个BCD码相加后存放在累加器A中的运算结果进行十进制调整。两个压缩的BCD码按二进制相加后,必须经过调整方能得到正确的压缩BCD码的和。十进制调整指令执行后,PSW中的CY表示结果的百位值。调整的具体过程如下: (1) 若累加器A的低4位为十六进制的A~F或辅助进位AC为1,则累加器A中的内容加06H调整。 (2) 若累加器A的高4位为十六进制的A~F或辅助进位标志CY为1,则累加器A中的内容加60H调整。 因为指令是对BCD码进行修正,所以该指令也称为BCD码修正指令。两个压缩BCD码按二进制数相加后,必须经本指令的调整才能得到正确的结果。 【例311】 在R3中有十进制数67,在R2中有十进制数85,用十进制运算,运算的结果放于R5中。 【解】 程序为 MOVA,R3 ADDA,R2 DAA MOVR5,A 程序中ADD指令运算出来的结果放于累加器A中,DA指令对该结果进行调整。调整后,累加器A中的内容为52H,CY为1,结果为152,最后放于R5中的内容为52H(十进制数52)。 3.3.3逻辑操作指令 逻辑运算指令完成字节的逻辑与、或、异或、清零、取反和左右移位等操作。当以A为目的操作数时,对PSW寄存器中的P标志有影响,循环指令是对A的循环。 1. 逻辑“与”指令ANL ANLA,Rn;A←A&Rn ANLA,direct;A←A&(direct) ANLA,@Ri;A←A&(Ri) ANLA,#data;A←A&#data ANLdirect,A;(direct)←(direct)&A ANLdirect,#data;(direct)←(direct)#data 这组指令的功能是将源操作数单元的内容与目的操作数单元的内容按位相与,结果存放到目的操作数单元中,而源操作数单元中的内容不变。指令运行时仅影响P标志位。 逻辑与运算指令常用于将某些位屏蔽,只要将需要屏蔽的位和“0”相与,要保留的位和“1”相与即可。另外,如果使用该指令修改一个输出口时,作为原始数据的值将从单片机的输出数据锁存器(P0~P3)读入,而不是读引脚的状态。例如, ANLP2,#0F;屏蔽P2口的高4位,低4位保持不变 2. 逻辑“或”指令ORL ORLA,Rn;A←A|Rn ORLA,direct;A←A|(direct) ORLA,@Ri;A←A|(Ri) ORLA,#data;A←A|#data ORLdirect,A;(direct)←(direct)|A ORLdirect,#data;(direct)←(direct)#data 这组指令的功能是将源操作数单元的内容与目的操作数单元的内容按位相或,结果存放到目的操作数单元中,而源操作数单元的内容不变。指令运行时仅影响P标志位。 逻辑或指令常用于将某些位置位,即使之为“1”,只要将需要置位的位与“1”相或,要保留的位与“0”相或即可。另外,如果使用该指令修改一个输出口时,作为原始数据的值将从单片机的输出数据锁存器(P0~P3)读入,而不是读引脚的状态。 3. 逻辑“异或”指令XRL XRLA,Rn;A←A⊕Rn XRLA,direct;A←A⊕(direct) XRLA,@Ri;A←A⊕(Ri) XRLA,#data;A←A⊕#data XRLdirect,A;(direct)←(direct)⊕A XRLdirect,#data;(direct)←(direct)⊕#data 这组指令的功能是将源操作数单元的内容与目的操作数单元的内容相异或,结果存放到目的操作数单元中,而源操作数单元中的内容不变。指令执行时仅影响P标志位。 逻辑异或指令常用于将某些位取反,即使“0”位变为“1”,使“1”位变为“0”。只要将需要取反的位与“1”相异或,要保留的位与“0”相异或即可。另外,如果使用该指令修改一个输出口时,作为原始数据的值将从单片机的输出数据锁存器(P0~P3)读入,而不是读引脚的状态。 4. 清零指令和求反指令 (1) 清零指令 CLRA;A←0 (2) 求反指令 CPLA;A← 在MCS51系统中,只能对累加器A中的内容进行清零和求反,如果要对其他的寄存器或存储器单元进行清零和求反,则需要放在累加器A中进行,运算后再放回原位置。 【例312】 写出对R1寄存器内容求反的程序段。 【解】 程序为 MOVA,R1 CPLA MOVR1,A 5. 循环移位指令 MCS51系统有4条对累加器A的循环移位指令: (1) 循环左移 RLA 图39循环指令移位示意图 指令的功能是累加器A的8位向左循环移位,位7循环移入位0,不影响标志位,如图39(a)所示。 (2) 循环右移 RRA 指令的功能是累加器A的8位向右循环移位,位0循环移入位7,不影响标志位,如图39(b)所示。 (3) 带进位的循环左移 RLCA 指令功能是将累加器A中的内容和进位标志位CY一起向左循环移位1位,位7移入进位为CY,CY移入位0,不影响标志位,如图39(c)所示。 值得注意的是,“累加器A内容乘2”的任务可以利用该指令方便地完成。 (4) 带进位的循环右移 RRCA 指令功能是将累加器A中的内容和进位标志位CY一起向左循环移位1位,位0移入进位为CY,CY移入位0,不影响标志位,如图39(d)所示。 3.3.4控制转移指令 通常情况下,程序的执行是顺序进行的,但也可以根据需要改变程序的执行顺序,称为程序转移。控制程序的转移要利用转移指令。MCS51单片机的控制转移指令包括无条件转移指令、条件转移指令及子程序调用与返回指令。 1. 无条件转移指令 无条件转移指令是指当执行该指令后,程序将无条件地转移到指令指定的地方。无条件转移指令包括长转移指令、绝对转移指令、相对转移指令和间接转移指令。 (1) 长转移指令(LJMP) LJMPaddr16;PC←addr16 该指令为3字节指令,提供了16位的转移目标地址,指令执行时将该16位地址送给程序指针PC,程序无条件地转移到addr16指出的目标地址。该指令可以使程序在64KB范围内跳转,且不影响标志位,使用方便,但执行时间长,字节数多。 【例313】 执行下段程序后,求PC的值。 1000HTable:MOVA,#21H … LJMPTable 【解】 结果: PC=1000H 指令中addr16及后面LJMP指令中的rel常用目的地址的地址标号来代替,由汇编程序自动换成16位或8位相对地址值。例如例313中的“Table”代表的就是1000H。 (2) 绝对转移指令(AJMP) AJMPaddr11;PC←PC+2,PC10~0←addr11,PC15~11不变 该指令为双字节指令,指令提供11位地址,指令执行时,先将PC的内容+2(这时PC指向的是AJMP的下一条指令),然后把指令中11位地址传送到PC10~0,而PC15~11保持不变。因为该指令只提供低11位地址,高5位地址为原PC15~11的值,所以程序转移位置以当前指令位置为基准,向前或向后转移2KB以内的范围,该指令不影响标志位。 【例314】 1030H: AJMP100H 【解】 目的地址: PC=1032H的高5位+100H的低11位=1100H (3) 相对转移指令(SJMP) SJMPrel;PC←PC+2+rel 该指令为双字节指令,指令执行时,先将程序指针PC的值+2(该指令的长度),然后再将PC的值与指令中的位移偏移量rel相加得到目的地址。rel是一个有符号的8位二进制补码,取值范围是-128~+127(00H~7FH表示0~+127,80H~FFH表示-128~-1),负数表示反向转移,正数表示正向转移。与LJMP指令相同,SJMP指令中的相对地址rel常常用目的地址的标号(符号地址)表示。 在单片机程序设计中,为等待中断或程序结束,常有使用程序“原地踏步”的需要,可使用SJMP指令实现,即 LOOP:SJMPLOOP 或者 LOOP:SJMP $;指令机器码为80H,"$"表示PC的当前值 (4) 间接转移指令(JMP) JMP@A+DPTR;PC←A+DPTR 该指令是一条单字节指令,其转移地址由数据指针DPTR的16位数和A的8位数进行无符号数相加形成,并直接装入PC,可以实现64KB范围内的条件转移。指令执行后不改变DPTR及A中原来的内容,也不影响标志位。 该指令以DPTR的内容为基地址,A的内容作变址,因此只要把DPTR的值固定,然后赋予A不同的值,即可实现程序的多分支转移。间接转移指令因为可以代替众多的判别跳转指令,又称为散转指令。 【例315】 已知累加器A中存放着控制程序转向的编号0~3,ROM中存有起始地址为TABLE的双字节绝对转移指令表,试编写程序使单片机能按照累加器A中的编号转去执行相应的命令程序,即当A=00H时,执行TAB0分支程序; 当A=01H时,执行TAB1分支程序; A=02H时,执行TAB2分支程序; A=03H时,执行TAB3分支程序。 【解】 参考代码: CLRC RLCA MOVDPTR,#TABLE;将TABLE地址送入DPTR中 JMP@A+DPTR;程序转到地址为A+DPTR的地址中执行 TABLE:AJMPTAB0;当A=0时,执行TAB0分支程序 AJMPTAB1;当A=1时,执行TAB1分支程序 AJMPTAB2;当A=2时,执行TAB2分支程序 AJMPTAB3;当A=3时,执行TAB3分支程序 … 因为表格中的AJMP是双字节指令,所以执行查表指令之前应将累加器A的内容乘以2,以形成正确的偏移量。 2. 条件转移指令 条件转移指令是指当条件满足时,程序转移到指定位置,条件不满足时,程序将继续顺次执行。在MCS51系统中,条件转移指令有三种: 累加器A判零条件转移指令、比较转移指令和减1不为零转移指令。 (1) 累加器A判零转移指令(JZ/JNZ) JZrel;若A=0,则PC←PC+2+rel,否则PC←PC+2 JNZrel;若A≠0,则PC←PC+2+rel,否则PC←PC+2 这两条指令都是双字节指令,rel为相对地址偏移量,当各自条件满足时,程序转移的目标地址为PC+2+rel,指令中的相对地址rel常常用目的地址的标号(符号地址)表示。该指令的转移范围在以PC当前值为起始地址的-128~+127B范围内。 【例316】 把片外RAM 30H单元开始的数据块传送到片内RAM的40H开始的位置,直到出现零为止。 【解】 参考程序如下: MOVR0,#30H MOVR1,#40H LOOP:MOVXA,@R0 MOV@R1,A INCR1 INCR0 JNZLOOP SJMP$ (2) 比较转移指令(CJNE) CJNEA,#data,rel;当A≠data,则PC←PC+3+rel,否则PC←PC+3 CJNERn,#data,rel;当Rn≠data,则PC←PC+3+rel,否则PC←PC+3 CJNE@Ri,#data,rel;当(Ri)≠data,则PC←PC+3+rel,否则PC←PC+3 CJNEA,direct,rel;当(direct)≠data,则PC←PC+3+rel,否则PC←PC+3 这组指令是三字节指令,功能是对两个规定的操作数进行比较,并且根据比较的结果决定是否转移。若两个操作数相等,则程序顺序执行; 若两个操作数不相等,则程序转移到目标地址PC+3+rel。指令中的地址rel通常也用目的地址的标号(符号地址)表示。 因为指令执行过程中通过减法操作(不保存两数的差)实现操作数比较,所以可以通过标志位CY反映操作数的大小: 如果目的操作数的内容大于源操作数的内容,则CY=0; 如果目的操作数的内容小于源操作数的内容,则CY=1。所以,在程序转移后可以利用标志位CY做进一步判断,可实现三分支转移。 【例317】 已知工作寄存器R0中存放着一个无符号数X,试编写程序求出下式的函数值Y,并存入工作寄存器R1中。 Y=AAHX>20H 00HX=20H FFHX<20H 【解】 参考程序如下: MOVA,R0 CJNEA,#20H,L1 MOVR1,#0 LJMPL3 L1:JCL2;若CY=1,则跳转值L2,否则顺序执行 MOVR1,#0AAH LJMPL3 L2:MOVR1,#0FFH L3:LJMPL3 (3) 减1不为零转移指令(DJNZ) DJNZRn,rel;Rn←Rn-1,若Rn≠0,则PC←PC+2+rel DJNZdirect,re;(direct)←(direct)-1,若(direct)≠0,则PC←PC+2+rel 该组指令将减1和条件转移结合到一起,执行指令时,首先将操作数的内容减1,并将结果存在第一操作数中,然后判断结果是否为0。若不为0,转移到目标地址,否则顺序执行。 该组指令对于构成循环程序是十分有用的,可以指定任何一个工作寄存器作为程序循环计数器,每循环一次,这种指令被执行一次,计数器就减1,预定的循环次数不到,计数器不会为0,继续执行循环操作; 当到达预定的循环次数,计数器就被减至0,顺序执行下一条指令,结束循环。 【例318】 统计片内RAM中32H单元开始的10个数据中0的个数,放于R7中。 【解】 参考程序: MOVR0,#32H MOVR2,#10H MOVR7,#0 LOOP:MOVA,@R0 CJNEA,#0,NEXT INCR7 NEXT:INCR0 DJNZR2,LOOP SJMP$ 图310主程序和子程序之 间的调用关系 3. 子程序调用与返回指令 在一个程序中经常遇到反复多次执行某段程序的情况,如果重复执行编写这个程序段,会使程序变得冗长而杂乱。而子程序结构,可以将重复的程序片段编写为一个子程序,通过主程序调用而使用,既能减少编程的工作量,也能缩短程序的长度。主程序和子程序之间的调用关系如图310所示。 子程序调用及返回指令有4条: 2条程序调用指令和2条程序返回指令。调用和返回指令必须成对使用。调用指令具有把断点地址保护到堆栈以及把子程序入口地址自动送入PC的功能,返回指令则具有把堆栈中的断点地址自动恢复到PC的功能。 (1) 长调用指令LCALL LCALLaddr16;PC←PC+3,SP←SP+1,(SP)←PC7~0, SP←SP+1,(SP)←PC15~8,PC10~0←addr16 该指令为三字节指令,指令提供16位目标地址,可调用64KB范围内的子程序。执行指令时,首先修改PC←PC+3,以获得下一条指令地址; 然后将这16位地址(断点值,即返回到LCALL指定的下一条指令地址)压入堆栈(先压入PC7~0低位字节,后压入PC15~8高位字节),堆栈指针SP加2指向栈顶; 接着将16位目标地址addr16送入程序计数器PC10~0,从而使程序转向目标地址去执行被调用的子程序。 (2) 绝对调用指令ACALL ACALLaddr11;PC←PC+2,SP←SP+1,(SP)←PC7~0, SP←SP+1,(SP)←PC15~8,PC10~0←addr11 该指令为双字节指令,指令提供11位目标地址,只能调用与PC在同一个2KB绝对地址范围内的子程序。执行指令时,首先修改PC←PC+2,作为下一条指令地址,并把修改后的PC内容(先低字节,后高字节)压入堆栈保护,堆栈指针SP加2指向栈顶; 接着将11位目标地址addr11送入程序计数器PC10~0,从而使程序转向目标地址去执行被调用的子程序。 值得说明的是,两条调用指令执行时要将返回地址入栈,初始化时必须设置合适的SP值(SP默认值为07H)。在汇编程序中,调用指令后面通常带转移地址的标号。 (3) 子程序返回指令RET RET;PC15~8←(SP),SP←SP-1,PC7~0←(SP),SP←SP-1 该指令通常放在子程序的最后一条指令位置,用于实现返回到主程序。指令执行时,将子程序调用指令压入堆栈的地址出栈,第一次出栈的内容送到PC的高8位,第二次出栈的内容送到PC的低8位。指令执行完后,程序将返回到调用指令的下一条指令执行。 (4) 中断返回指令 RETI;PC15~8←(SP),SP←SP-1,PC7~0←(SP),SP←SP-1 该指令作为中断服务子程序的最后一条指令,用于返回主程序中断的断点位置,继续执行断点位置后面的指令。该指令的执行过程与RET基本相同,只是RETI在执行后,在转移前将先清除中断的优先级触发器。 MCS51系统中,中断都是硬件中断,没有软件中断调用指令。硬件中断时,由一条长转移指令使程序转移到中断服务程序的入口位置,在转移之前,由硬件将当前的断点地址压入堆栈保存,以便以后通过中断返回指令返回到断点位置后继续执行。 值得注意的是,虽然这两条指令均是把堆栈中的断点地址恢复到PC中,从而使单片机回到断点处执行程序,但二者具有如下区别: ① RET指令为子程序的最后一条指令,而RETI为中断服务程序的最后一条指令,二者不能互换使用。 ② RETI指令除了恢复断点地址外,还恢复CPU响应中断时硬件自动保护的现场信息,如将清除中断响应时所置1的优先级状态触发器,使得已申请的同级或低级中断申请可以响应,而RET指令只能恢复返回地址。 ③ 对开发者而言,RET指令返回的地址是确定的,而RETI指令返回的地址是未知的。 3.3.5位操作指令 位操作又称为布尔变量操作,以位(bit)为单位进行运算和操作,由单片机中一个布尔处理机实现,借用进位标志CY作为累加器。位操作指令共17条,可以实现位传送、位逻辑运算、位控制转移等操作。 1. 位传送指令 MOVC,bit;CY←(bit) MOVbit,C;(bit)←CY 该组指令的功能是实现CY和其他位地址之间的相互数据传送。在程序中,CY记做C。 两个位地址间不能直接进行数据传送,可以通过CY间接实现。 【例319】 把片内RAM中位寻址区的30H位的内容传送到20H位。 【解】 参考程序: MOVC,30H MOV20H,C 2. 位逻辑操作指令 位逻辑操作指令包括清0、置1、取反、与和或,共10条指令。 (1) 位清0 CLRC;CY←0 CLRbit;(bit)←0 (2) 位置1 SETBC;CY←1 SETBbit;(bit)←1 (3) 位取反 CPLC;CY←(/CY) CPLbit;(bit)←(/bit) (4) 位与 ANLC,bit;CY←CY∧(bit) ANLC,/bit;CY←CY∧(/bit) (5) 位或 ORLC,bit;CY←CY∨(bit) ORLC,/bit;CY←CY∨(/bit) 指令中位地址bit若为00H~7FH,则位地址在片内RAM(20H~2FH)中,共128位,bit若为80H~FFH,则位地址在11个特殊功能寄存器中。其中,有4个8位的并行I/O口,每位均可单独进行操作。因此,布尔I/O口共有32个,分别为P0.0~P0.7、P1.0~P1.7、P2.0~P2.7和P3.0~P3.7。 【例320】 MOVP1.0,C;P1.0←CY,把标志位CY的内容送入P1口最低位P1.0中 【例321】 SETBP3.0;把P3口的最低位置1 CLR07H;把20H字节的最高位清0 区分: CLRA ; 只有唯一的一条清累加器A的指令(整个字节清0),因此,上述07H必定属于位地址。 【例322】 利用位逻辑运算指令编程实现如图311所示的硬件逻辑电路的功能。 图311硬件逻辑电路图 【解】 参考程序: MOVC,P1.0 ANLC,P1.1 MOVF0,C MOVC,P1.1 ORLC,P1.2 ANLC,F0 CPLC 3. 位转移指令 位转移指令有以C为条件的转移指令和以bit为条件的转移指令,共5条。 (1) 以C为条件的转移指令 JCrel;若CY=1,则转移,PC←PC+2+rel;否则程序继续执行 JNCrel;若CY=0,则转移,PC←PC+2+rel;否则程序继续执行 该组指令是相对转移指令,以CY的值来判决程序是否需要转移,常常和比较条件转移指令CJNE连用,以便根据CJNE指令执行过程中形成的CY进一步判决程序的流向或形成三分支模式。 (2) 以bit为条件的转移指令 JBbit,rel;若(bit)=1,则转移,PC←PC+3+rel;否则程序继续执行 JNBbit,rel;若(bit)=0,则转移,PC←PC+3+rel;否则程序继续执行 JBCbit,rel;若(bit)=1,则转移,PC←PC+3+rel,且(bit)←0;否则程序继续执行 【例323】 如图312所示,P3.2和P3.3上各接有一个按键,要求它们分别按下时(P3.2=0或P3.3=0),分别使P1口输出0或FFH。 图312例323电路图 【解】 因为P3口是准双向口,所以在读取按键状态之前,要先通过程序使P3口输出高电平,然后再一次读取P3.2和P3.3的状态并进行判断。 参考程序: MOVP3,#0FFH L1:JNBP3.2,L2 JNBP3.3,L3 LJMPL1 L2:MOVP1,#00H LJMPL1 L3:MOVP1,#0FFH LJMPL1 【例324】 从片外RAM 20H单元开始有50个数据,统计其中正数、0和负数的个数,分别放于R5、R6和R7中。 【解】 设R2作计数器,用DJNZ指令对R2减1转移进行循环控制,在循环体外设置R0指针,指向片外RAM 20H,对R5、R6和R7清零,在循环体中用指针R0依次取出片外RAM中的50个数据,然后判断,若大于0,则R5中的内容加1; 若等于0,则R6中的内容加1; 若小于0,则R7中的内容加1。 参考程序: MOVR2,#50 MOVR0,#20H MOVR5,#0 MOVR6,#0 MOVR7,#0 LOOP:MOVXA,@R0 CJNEA,#0,NEXT1 INCR6 SJMPNEXT3 NEXT1:CLRC SUBBA,#80H JCNEXT2 INCR7 SJMPNEXT3 NEXT2:INCR5 NEXT3:INCR0 DJNZR2,LOOP SJMP$ 4. 空操作指令 空操作指令只有1条单字节指令: NOP;PC←PC+1 指令执行时,不做任何操作(即空操作),仅将程序计数器PC的内容加1,使CPU指向下一条指令继续执行程序。该指令要占用一个机器周期,常用来产生延迟,构造延时程序。 3.451系列单片机汇编程序常用伪指令 3.3节介绍51系列单片机的汇编语言指令系统。汇编语言源程序是用汇编语言编写的程序,必须翻译成机器代码才能运行,而汇编就是翻译的过程。伪指令是放在汇编语言源程序中,对汇编过程进行相应的控制和说明的指示性命令。该类指令可以被汇编器识别,但是没有对应的可执行机器码,汇编产生的目标程序中不会再出现伪指令。伪指令通常用于定义数据、分配存储空间、控制程序的输入输出等。MCS51汇编语言源程序常用的伪指令有以下几条: 1. 状态控制伪指令 (1) 起始地址设定伪指令ORG 格式: ORG表达式 该伪指令放在一段源程序或数据的前面,其功能是说明下面紧接的代码或数据存放的起始地址。表达式通常为十六进制地址,也可以是已定义的地址标号。 【例325】 ORG伪指令的使用。 ORG2000H START:MOVA,#40H 【解】 该段程序的机器码从地址2000H单元开始存放。 通常情况下,每个汇编源程序的开始,都需要利用ORG来指明该程序存放的起始位置。若省略,则默认该程序存放的起始地址为0000H。 (2) 汇编器结束汇编伪指令END 格式: END 该伪指令放于程序的最后,用于控制汇编器结束汇编。一个源程序只能有一个END命令,否则就有一部分指令不能被汇编。 2. 符号伪指令 (1) 定义常值为符号名伪指令EQU 格式: 符号名EQU常值表达式 该伪指令的功能是定义一个指定的符号名。在汇编过程中,汇编器会将源程序中出现的该符号用定义的常值来取代。值得注意的是,标号只代表地址,而符号名可以代表地址、常数、段名或字符串、寄存器或位名等,符号名不后接冒号。 【例326】 EQU伪指令的使用。 LEQU12 SUMEQU41H BLOCKEQU42H CLRA MOVR1,#L MOVR0,#BLOCK LOOP:ADDA,@R0 INCR0 DJNZR1,LOOP MOVSUM,A 【解】 该程序段功能是把22H单元开始存放的10个无符号数求和,并将结果存放入41H单元。程序中的符号L、SUM和BLOCK在汇编后将分别由常值12、41H和42H取代。 (2) 定义地址符号名伪指令BIT 格式: 符号名BIT位地址表达式 该伪指令的功能是将位地址赋予指定的符号名。位地址表达式可以是绝对地址,也可以是符号地址。 值得注意的是,用EQA或BIT定义的“符号名”一经定义便不能重新定义和改变。 【例327】 BIT伪指令的使用。 STBITP1.0;将P1.0的位地址赋予符号名ST CFBIT0D7H;将位地址D7H的位定义为符号名CF 3. 存储器空间初始化伪指令 (1) 定义字节数据表伪指令DB 格式: [标号:]DB字节数据表 该伪指令的功能是从标号指定的地址单元开始,在程序存储器中定义字节数据表,可以定义一个字节,也可以定义多个字节。定义多个字节时,两量之间用逗号间隔,定义的多个字节在存储器中是连续存放的。定义的字节可以是一般常数、字符或者字符串。字符和字符串以引号括起来,字符数据在存储器中以ASCII码形式存放。 【例328】 DB伪指令的使用。 ORG0100H TAB1: DB56H,78H DB '4','a','ABC' 【解】 汇编后,各个数据在存储单元中的存放情况如图313所示。 图313DB数据分配情况 (2) 定义字数据表伪指令DW 格式: [标号:]DW字数据表 该伪指令与DB类似,用于定义字数据。项或项表所定义的一个字节在存储器中占2字节。汇编时,机器自动按高字节在前、低字节在后存放,即高字节存放在低地址单元,低字节存放在高地址单元。该伪指令常用于在ROM中定义双字节的跳转地址。 【例329】 DW伪指令的使用。 ORG0100H TAB2:DW1234H,5678H 【解】 汇编后,各个数据在存储单元中的存放情况如图314所示。 4. 保留空间伪指令 (1) DS伪指令 格式: [标号:]DS数值表达式 该伪指令用于在存储器中保留一定数量的字节单元,保留字节单元的数目由表达式的值决定。 【例330】 DS伪指令的使用。 ORG0100H DB34H,56H DS4H DB'7' 【解】 汇编后,各个数据在存储单元中的存放情况如图315所示。 图314DW数据分配情况 图315DS数据分配情况 (2) DBIT伪指令 格式: [标号:]DBIT数值表达式 该伪指令与DS类似,以位为单位保留存储空间。 5. 其他伪指令 为了支持模块化设计,51汇编器还提供了一些其他伪指令,如表33所示。 表33其他伪指令 分类伪指令功能示例 段及类型定义 SEGMENT段名定义bedbinSEGMENTODE RSEG可重新定位段说明RSEGedbin CODE程序代码空间KEYCODE0H DATA直接寻址数据空间oneDATA30H IDATA间接寻址数据空间twoIDATA80H XDATA扩展的外部数据空间SUMXDATA1000H 地址指定 BSEG AT定位位寻址段BSEGAT20H CSEG AT定位代码段CSEGAT0000H DSEG AT定位内部RAM直接寻址数据段DSEGAT40H ISEG AT定位内部RAM间接寻址数据段ISEGAT80H XSEG AT定位外部RAM数据段XSEGAT1000H 模块连接 PUBLISH符号在本模块定义,其他模块引用PUBLISH_BIN2BCDB EXTRN符号在本模块引用,其他模块定义EXTRN CODE(_BIN2BCDB) 其他USING指定当前工作寄存器组USING1 3.551单片机汇编语言程序设计举例 3.5.1概述 用汇编语言设计程序与用高级语言设计程序有相似之处,设计过程大致可以分为以下几个步骤: (1) 明确课题的具体内容。明确对程序的功能、运算精度、执行速度等方面的要求及硬件条件。 (2) 编制框图,绘出流程图。把复杂问题分解为若干个模块,确定各模块的处理方法,画出程序流程图。对复杂问题可分别画出分模块流程图和总的流程图。 (3) 存储器资源分配。根据系统的工作需要,合理选择并分配内存单元及工作寄存器。 (4) 编写源程序。根据程序流程图精心选择合适的指令和寻址方式来编制源程序。 (5) 对程序进行汇编、调试和修改。对编制好的源程序进行汇编,检查修改程序中的错误,执行目标程序,对程序运行结果进行分析,直至正确为止。 图316几何图形符号图 另外,用汇编语言进行程序设计时,对于程序、数据在存储器的存放位置,对工作寄存器、片内数据存储单元、堆栈空间等的安排都要由编程者亲自做,编写过程中要特别注意。 画流程图是指用各种图形、符号、指向线等来说明程序设计的过程,常用几何图形符号如图316所示。 国际通用的图形和符号说明如下: (1) 椭圆框: 起止框,在程序的开始和结束时使用。 (2) 矩形框: 处理框,表示要进行的各种操作。 (3) 菱形框: 判断框,表示条件判断以决定程序的流向。 (4) 圆圈: 连接符,表示不同页之间的流程连接。 (5) 指向线: 流程线,表示程序执行的流向。 3.5.2顺序程序设计 顺序结构程序是一种最简单、最基本的程序,所以有时也称为简单程序设计,是一种无分支的直线型结构,即按照程序的编写顺序依次执行每条指令。 【例331】 设片内RAM的21H单元存放一个十进制数据十位的ASCII码,22H单元存放该数据个位的ASCII码。编写程序将该数据转换成压缩BCD码存放在20H单元。 【解】 由于ASCII码30H~39H对应BCD码的0~9,所以只要保留ASCII码的低4位,而将高4位清0即可。流程图如图317所示。 图317例331程序流程图 参考程序: ORG0000H LJMPSTART ORG0040H START:MOVA,21H;取十位ASCII码 ANLA,#0FH;保留低半字节 SWAPA;移至高半字节 MOV20H,A;存于20H单元 MOVA,22H;取个位ASCII码 ANLA,#0FH;保留低半字节 ORL20H,A;合并到结果单元 SJMP$ END 【例332】 已知两个压缩BCD码分别放在内部RAM的31H、30H和33H、32H这4个单元中,试编程求和,结果存入R4、R3、R2中。 【解】 参考程序: ORG0000H;程序开始 LJMPMAIN;跳转到MAIN标号 ORG0040H;MAIN程序段的起始命令 MAIN:MOVA,30H;将30H单元的内容送给A ADDA,32H;将32H单元的内容与A的内容相加 DAA;进行BCD码调整 MOVR2,A;将A的内容送R2保存,保存低位的和 MOVA,31H;将高位31H的内容送给A ADDCA,33H;33H的内容与A的内容相加,同时加上低位的进位 DAA;进行BCD码调整 MOVR3,A;将A的内容送R3,保存高位和 CLRA;清A的内容 MOVACC.0,C;保存高位和的进位 MOVR4,A;在R4中保存高位的进位 HERE:SJMPHERE;程序在此运行 END;程序结束 图318例333程序流程图 3.5.3分支程序设计 在实际应用过程中,往往需要单片机对某些条件进行判断,从而实现不同的程序流向,这样,就会产生一个或多个分支程序。分支程序设计可以分为单分支程序设计和多分支程序设计。 【例333】 设变量x以补码的形式存放在片内RAM的30H单元,变量y与x的关系是: 当x大于0时,y =x; 当x=0时,y=20H; 当x小于0时,y=x+5。编制程序,根据x的大小求y并送回原单元。流程图如图318所示。 【解】 参考代码: ORG0000H;程序开始 LJMPSTART;跳转到START标号 ORG0040H;START程序段的起始命令 START:MOVA,30H;取x至累加器A JZNEXT;x=0,转NEXT ANLA,#80H;否,保留符号位 JZDONE;x>0,转结束 MOVA,#05H;x<0,处理 ADDA,30H MOV30H,A;x+05H送y SJMPDONE NEXT:MOV30H,#20H;x=0,20H送y DONE:SJMPDONE END 【例334】 假定在外部RAM中,有ST1、ST2和ST3共3个连续单元,其中ST1、ST2单元中分别存放两个8位无符号数,要求找出其中的大数并存入ST3单元。 【解】 比较两个无符号数的大小,可利用两数相减是否有借位来判断。 参考代码: START:CLRC;清C标志 MOVDPTR,#ST1;将立即数ST1送DPTR MOVXA,@DPTR;取外部数据送A MOVR7,A;将A中数据送R7 INCDPTR;DPTR自动加1 MOVXA,@DPTR;再取下一个外部数据 SUBBA,R7;对外部数据进行减法运算 JCB1;若C为l,即(A)<(R7),程序转向B1 MOVXA,@DPTR;再次将外部数据送A SJMPB2;程序转向B2 B1:XCHA,R7;交换A和R7的值 B2:INCDPTR;DPTR加1 MOVX@DPTR,A;(A)送外部数据寄存器 SJMP$;程序在此跳转 【例335】 设变量X存放于30H单元,函数值Y存放于31H单元。若X>0,则Y=1;若X=0,则Y=0; 若X<0,则Y=-1。 【解】 X是有符号数,判断符号位是0还是1,可利用JB或JNB指令。判断X是否等于0,则直接可以使用累加器A的判0指令。流程图如图319所示。 图319例335程序流程图 参考程序: START:MOVA,30H JZOVER JNBACC.7,LAB1 MOVA,#0FFH SJMPOVER LAB1:MOVA,#1 OVER:MOV31H,A SJMP$ 注意,对于分支不多的应用程序,常采用的指令有JZ、JNZ、JC、JNC、JB、JNB、CJNE等。对于多分支的程序,需要采用JMP @A+DPTR指令。 3.5.4循环程序设计 所谓循环程序,是指按照某种规律重复执行的程序,分为“先执行后判断”和“先判断后执行”两种结构。前者的特点是,先执行循环处理部分,然后根据循环控制条件判断是否结束循环,如图320(a)所示; 后者的特点是,先根据循环控制条件判断是否结束循环,若不结束,则执行循环处理部分,否则结束处理,退出循环,如图320(b)所示。 图320循环程序结构图 图321例336的循环程序结构图 【例336】 计算50个8位二进制数(单字节)之和。要求: 50个数存放在30H开头的内部RAM中以及R6R7中。 【解】 采用DJNZ循环体的程序流程图,如图321所示,在参考程序中,R0为数据地址指针,R2为减法循环计数器。在使用DJNZ控制时,循环计数器初值不能为0,当为0时,第一次进入循环执行到DJNZ时,减1使R2变为FFH,循环次数成了256。 参考程序: START:MOVR6,#0 MOVR7,#0 MOVR2,#50 MOVR0,#30H LOOP:MOVA,R7 ADDA,@R0 MOVR7,A CLRA ADDCA,R6 MOVR6,A INCR0 DJNZR2,LOOP SJMP$ 【例337】 编写程序,将内部RAM的30H~3FH单元初始化为00H。 【解】 参考程序: ORG0000H LJMPMAIN ORG0040H MAIN:MOV0,#30H;置初值 MOVA,#00H MOVR7,#16 LOOP:MOV@R0,A;循环处理 INCR0 SJMP$;结束处理 END 3.5.5查表程序设计 查表程序设计中用于查表的指令有两条: MOVCA,@A+PC; MOVCA,@A+DPTR; 当使用DPTR作为基址寄存器时,查表的步骤分为三步: (1) 基址(表格首地址)送DPTR数据指针; (2) 变址值(在表中的位置是第几项)送累加器A; (3) 执行查表指令MOVC A,@A+DPTR,进行读数,查表结果送回到累加器A中。 当使用PC作为基址寄存器时,由于PC本身是一个程序计数器,与指令的存放地址有关,查表时的操作有所不同。查表的步骤也分三步: (1) 变址值(在表中的位置是第几项)送累加器A; (2) 偏移量(查表指令的下一条指令的首地址到表格首地址之间的字节数)+A→A(修正); (3) 执行查表指令MOVC A,@A+PC。 【例338】 编写2位十六进制数与ASCII码的转换程序。设数值在R2中,转换结果的低位存在R2中,高位存在R3中。 【解】 (1) 利用DPTR作基址的参考程序。 HEXASC:MOVDPTR,#TABIE MOVA,R2 ANLA,#0FH MOVCA,@A+DPTR;查表 XCHR2,A ANLA,#0F0H SWAPA MOVCA,@A+DPTR;查表 MOVR3,A RET TABLE:DB30H,31H,32H,33H,34H;ASCII表 DB35H,36H,37H,38H,39H DB41H,42H,43H,44H,45H,46H (2) 利用PC作基址的参考程序。 HEXASC:MOVA,R2 ANLA,#0FH ADDA,#9 MOVCA,@A+PC;查表 XCHR2,A ANLA,#0F0H SWAPA ADDA,#9 MOVCA,@A+PC;查表 MOVR3,A RET TABLE:DB30H,31H,32H,33H,34H;ASCII表 DB35H,36H,37H,38H,39H DB41H,42H,43H,44H,45H,46H 3.5.6子程序设计 将需要多次使用、完成相同的某种基本运算或操作的程序段从整个程序中独立出来,单独编成一个程序段,需要时通过子程序调用指令进行调用。这样的程序段称为子程序。 采用子程序能使整个程序结构简单,缩短程序的设计时间,减少占用的程序存储空间。调用子程序的程序称为主程序或调用程序。 在编写子程序时应注意以下问题: ①子程序的第一条指令地址称为子程序的入口地址,该指令前必须有标号; ②主程序调用子程序,是通过主程序或调用程序中的调用指令来实现的; ③子程序结构中使用堆栈保护断点和现场保护,且堆栈只能存于片内RAM中,不能存在于片外RAM中; ④子程序的最后一条指令必须是RET指令,它的功能是把堆栈中的断点地址弹出。 典型的子程序的基本结构如下: MAIN:…; MAIN为主程序入口标号 … LCALLSUB;调用子程序SUB … SUB:PUSHPSW;现场保护 ACALLAcc 子程序处理程序段: POPAcc;现场恢复,注意要先进后出 POPPSW RET;最后一条指令必须为RET子程序 值得说明的是,上述子程序结构中,现场保护与现场恢复不是必需的,要根据实际情况而定。 【例339】 利用子程序实现c=a2+b2。设a、b、c分别存于内部RAM的20H、21H和22H三个单元中。 【解】 子程序入口: (A)=预平方数; 子程序出口: (A)=平方值。 子程序如下: SQR:MOVDPTR,#TAB MOVCA,@A+DPTR RET TAB:DB0,1,4,9,16,25,36,49,64,81 验证程序段如下: MAIN:MOV20H,#4 MOV21H,#5 MOVA,20H;利用累加器A向子程序传递参数a ACALLSQR MOVR1,A; a2暂存于R1中 MOVA,21H;利用累加器A向子程序传递参数b ACALLSQR ADDA,R1; a2+b2存于A中 MOV22H,A;A中的结果存入22H单元 SJMP$ 结果: 42+52=29H 在程序存储器的一片存储单元中建立起该变量的平方表。用数据指针DPTR指向平方表的首址,则变量与数据指针之和的地址单元中的内容就是变量的平方值。 采用MOVCA,@A+PC指令实现查表功能时,无须DPTR参与(系统资源占用少),但表格只能存放在MOVC指令后的256 B内,即表格存放地点和空间有一定的限制。 3.6本章小结 本章主要介绍51单片机的汇编指令和汇编语言程序设计,重点了解并熟悉汇编语言的基本知识; 熟悉各指令的功能、寻址方式、寻址范围、对标志位的影响以及指令执行的机器周期; 掌握伪指令和单片机指令的使用,并理解二者的区别; 掌握基本程序结构和典型程序设计。 习题 一、 简答题 1. 在MCS51单片机中,寻址方式有几种?其中对片内RAM可以用哪几种寻址方式?对片外RAM可以用哪几种寻址方式?访问特殊功能寄存器区可以用哪几种寻址方式?访问片外部程序存储器可以采用哪些寻址方式? 2. 在对片外RAM单元寻址中,用Ri间接寻址与用DPTR间接寻址有什么区别。 3. 在位处理中,位地址的表示方式有哪几种? 4. 什么是伪指令?常用的伪指令功能如何? 5. 子程序调用时,参数的传递方法有哪几种? 6. 区分下列指令有什么不同? (1)MOVA,30H和MOVA,#30H (2)MOVA,@R0和MOVXA,@R0 (3)MOVA,R0和MOVA,@R0 (4)MOVXA,@R0和MOVXA,@DPTR (5)MOVXA,@DPTR和MOVC@A+DPTR 二、 阅读程序写结果 1. 若R1=30H,A=40H,(30H)=60H,(40H)=08H。试分析执行下列程序段后上述各单元内容的变化。 MOVA,@R1 MOV@R1,40H MOV40H,A MOVR1,#7FH 2. 若(50H)=40H,试写出执行以下程序段后累加器A、寄存器R0及内部RAM的50H、41H、42H单元中的内容各为多少? MOVA,50H MOVR0,A MOVA,#00H MOV@R0,A MOVA,#3BH MOV41H,A MOV42H,41H 3. 设(70H)=60H,(60H)=20H。P1口为输入口,当输入状态为B7H时,执行下列程序,试分析(70H)、B、(R1)、(R0)的内容是什么? MOVR0,#70H MOVA,@R0 MOVR1,A MOVB,@R1 MOVP1,#0FFH MOV@R0,P1 4. 若A=E8H,R0=40H,R1=20H,R4=3AH,(40H)=2CH,(20H)=0FH,试写出下列各指令独立执行后有关寄存器和存储单元的内容。若该指令影响标志位,试指出CY、AC和OV的值。 MOVA,@R0 ANL40H,#0FH ADDA,R4 SWAPA DEC@R1 XCHDA,@R1 5. 设A=83H,R0=17H,(17H)=34H,分析当执行完下面指令段后累加器A、R0、17H单元中的内容。 ANLA,#17H ORL17H,A XRLA,@R0 CPLA 6. 下列程序段汇编后,从1000H单元开始的单元内容是什么? ORG1000H TAB:DB12H,34H DS3 DW5567H,87H 三、 程序设计 1.完成某种操作可以采用几条指令构成的指令序列实现,试写出完成以下每种操作的指令序列。 (1) R0的内容送到R1中。 (2) 内部RAM 60H单元的内容传送到寄存器R2。 (3) 外部 RAM 1000H单元的内容传送到寄存器R2。 (4) 片内RAM 20H单元的内容送到片内RAM的40H单元中。 (5) 片内RAM 20H单元的内容送到片外RAM的60H单元中。 (6) 片内RAM 50H单元的内容送到片外RAM的1000H单元中。 (7) 片外RAM 1000H单元的内容送到片外RAM的20H单元中。 (8) 片外RAM 1000H单元的内容送到片外RAM的4000H单元中。 (9) ROM 1000H单元的内容送到片内RAM的50H单元中。 (10) ROM 1000H单元的内容送到片外RAM的1000H单元中。 2. 写出完成下列要求的指令,要求不得改变未涉及的位的内容。 (1) 使累加器A第0位置位。 (2) 累加器A的高2位置1,其余位不变。 (3) 清除累加器A的高4位。 (4) 清除ACC.3,ACC.4,ACC.5,ACC.6。 (5) 累加器A第0位、2位、4位、6位取反。 3. 试编写一段程序,将片内RAM的1000~101FH单元的内容依次存入片外RAM的20H~3FH单元中。 4. 设被加数存放在内部RAM的20H、21H单元,加数存放在22H、23H单元,若要求和存放在24H、25H中,试编写出16位无符号数相加的程序(采用大端模式存储)。 5. 试编写程序,将内部RAM的40H、41H 单元的两个无符号数相乘,结果存放在R2、R3中,R2中存放高8位,R3中存放低8位。 6. 试编写程序,将R1中的低4位数与R2中的高4位数合并成一个8位数,并将其存放在R1中。 7. 用查表的方法实现一位十六进制数转换成ASCII码。 8. 编写程序,求内部 RAM中40H~49H 10个单元内容的平均值,并存放在6AH单元中。