第九节汇编语言典型编程方法汇编语言编程只讲一些最基本的,复杂的运算不讲,因为后面还要讲单片机C语言编程,用单片机C语言编程有很大的优越性。学习了单片机C语言编程后,提倡用C语言和汇编语言混合编程,以C语言为主。一、算术逻辑处理例1:将40H开始存放的10个字节的数与50H开始存放的10个字节的数相减(假设被减数大于减数)。设被减数地址(指针)存放在R0,减数地址(指针)存放在R1,差数放回被减数单元,R5存放字节个数,则程序如下:SUB:MOVR0,#40H;被减数首地址MOVR1,#50H;减数地址MOVR5,#10;字节个数CLRC;清进位位SUB1:MOVA,@R0;被减数送累加器ASUBBA,@R1;两数相减结果送AMOV@R0,A;结果送被减数地址INCR0;指向下一个被减数INCR1;指向下一个减数DJNZR5,SUB1;10个数未运算完,转SUB1RET例2:将两个半字节数合并成一个字节数。设内部RAM40H,41H单元中分别存放着8位二进制数,要求取出两个单元中的低半字节,并成一个字节,将40H的低字节为合成字节的高字节,存入50H单元中。程序如下:START:MOVR1,#40H;设置R1为数据指针MOVA,@R1;取40H中的内容ANLA,#0FH;取40H的低半字节SWAPA;40H低半字节移至高半字节INCR1;修改数据指针,指向41H;XCHA,@R1;41H内容送A,40H低半字节送41H原40H低半字节送41H高半字节;ANLA,#0FH;取41H的低半字节ORLA,@R1;拼字MOV50H,A;存放结果在50HRET二、数制转换程序例3:将一个字节二进制数转换成BCD码。转换后存到RAM的20H、21H。源程序:MOVB,#100;MOVA,#99H;被转换数(153)DIVAB;求百位,商在A,余数在BMOV21H,A;将百位数送RAM的21HMOVA,#10;XCHA,B;被除数送A,除数送BDIVAB;十位在A,个位在BSWAPA;将十位移到高4位ADDA,B;将十位与个位合并,放到AMOV20H,A;将十位、个位存20H单元RET(调二-BCD转换)例4:二进制转换为十进制将计数器的TH0读入R2,TL0读入R3,将读入的16位二进制转换为5位十进制(最大65535)。1)转换结果为5位,必须占3个存储单元,将转换结果存到R4、R5、R6中。2)二-十进制转换公式ii102aNi=0,…,MM为二进制的位数,从右向左,从0开始;ai为该位的值“1”或“0”。如1010转换为二进制:1020212021N0123103)转换编程思路如何编程实现转换,以上面1010中的最高位1为例:20=1;21=20+20=1×2=2;(1+1)22=21+21=2×2=4;(2+2)23=22+22=4×2=8;(4+4)规律:当前位置的权值(如权值为3)是将从1开始每步自相加的结果。将所有系数为1的权值累加起来就是十进制的结果。实现方法:把R2、R3中的二进制带进位位左移,就是最高位开始累加,每移一次,将R4、R5、R6带进位位自加(相当于乘2),最高位等于a15×215,如果最高位为1,循环16次就累加了16次,变成32768。依次类推,实现了转换。4)参考源程序Z0Y0:CLRA;A清0;MOVR4,A;R4、R5、R6清0,R4放最高位MOVR5,A;MOVR6,A;MOVR7,#10H;循环16次LO30:CLRC;进位位清0,进位位中存放的系数,最高为开始MOVA,R3;以下将二进制带进位位左移(6句)RLCA;低位的最高位送入CMOVR3,A;MOVA,R2;RLCA;把低位中的最高位送入高位的最低位,高位的最高位MOVR2,A;移到CMOVA,R6;将十进制的低位送AADDCA,R6;带进位位相加,进位位是由上面左移得到DAA;进行十进制调整MOVR6,A;将结果又送R6MOVA,R5;对十进制中位进行处理ADDCA,R5;进位位是由R6相加到进位时得到DAA;MOVR5,A;MOVA,R4;对十进制高位进行处理ADDCA,R4;进位位是由R5相加到进位时得到DAA;MOVR4,A;DJNZR7,LO30;判断是否循环了16次RET(调二-十转换)三、分支程序设计例5:双向分支程序设计设X存在30H单元中,根据下式X+2X0Y=100X=0求出Y值,将Y值存入31H。∣X∣X0解:根据数据的符号位判别该数的正负,若最高位为0,再判别该数是否为0。程序流程如图4-4所示。参考程序如下:ORG1000HMOVA,30H;取数JBACC.7,NEG;最高位为符号位,负数=1,转NEGJZZER0;最高位为零,判读X为0转ZER0ADDA,#02H;为正数,求X+2AJMPSAVE;转到SAVE,保存数据ZER0:MOVA,#64H;数据为零,Y=100AJMPSAVE;转到SAVE,保存数据NEG:DECA;CPLA;求∣X∣,负数用补码表示,要求绝对值,减1取反SAVE:MOV31H,A;保存数据SJMP$;暂停例6:多向分支程序设计举例,根据R0的值转向7个分支程序。R010,转向SUB0;R020,转向SUB1;…R060,转向SUB5;R0=60,转向SUB6;解:利用JMP@A+DPTR指令直接给PC赋值,使程序实现转移。参考程序如下:ORG2000HMOVDPTR,#TAB;转移指令表首地址MOVA,R0;取数MOVB,#10……DIVAB;A/10,商在A中,也进行了取整CLRCRLCA;A←2A,因为AJMP是双字节指令JMP@A+DPTR;PC←A+DPTRTAB:AJMPSUB0;转移指令表AJMPSUB1AJMPSUB2…AJMPSUB5AJMPSUB6四、循环程序的结构例7:有一数据块从片内RAM的30H单元开始存入,设数据块长度为10个单元。根据下式:(原数据为x,计算后的数据为y)X+2X0Y=100X=0求出Y值,并将Y值放回原处。∣X∣X0解:设置一个计数器控制循环次数,每处理完一个数据,计数器减1。参考源程序如下:ORG2000HMOVR0,#10;数据长度MOVR1,#30H;起始地址START:MOVA,@R1;取数JBACC.7,NEG;若为负数,转NEGJZZER0;若为零,转ZER0ADDA,#02H;若为正数,求X+2AJMPSAVE;转到SAVE,保存数据ZER0:MOVA,#64H;数据为零,Y=100AJMPSAVE;转到SAVE,保存数据NEG:DECACPLA;求∣X∣SAVE:MOV@R1,A;保存数据INCR1;地址指针指向下一个地址DJNZR0,START;数据未处理完,继续处理SJMP$;动态停止五、子程序的调用通常把这些基本操作功能编制为程序段作为独立的子程序,以供不同程序或同一程序反复调用。在程序中需要执行这种操作的地方放置一条调用指令,当程序执行到调用指令,就转到子程序中完成规定的操作,并返回到原来的程序继续执行下去。子程序调用中,主程序应先把有关的参数存入约定的位置,子程序在执行时,可以从约定的位置取得参数,当子程序执行完,将得到的结果再存入约定的位置,返回主程序后,主程序可以从这些约定的位置上取得需要的结果,这就是参数的传递。例8、将一个字节拆分成两个字节,用于显示。设待拆字符已经存入累加器A,R0中存放的是转换后的低字节地址。是实验中的一段程序。很重要!本段程序本身是一段子程序;在其中又调用子程序。PTDS:MOVR1,A;将待拆字符送R1,暂存ACALLPTDS1;调子程序MOVA,R1;将待拆字符送ASWAPA;高4位与低4位交换PTDS1:ANLA,#0FH;读取低4位,该段程序运行两次,第二次实际读高字节MOV@R0,A;存拆开后的字节,INCR0;指向下一个存放高字节的地址RET(可以运行“字符拆分”)六、查表程序(*)查表程序是一种常用程序,它广泛使用于LED显示控制、打印机打印控制、数据补偿、数值计算、转换等功能程序中,这类程序具有简单、执行速度快等特点。所谓查表法,就是预先将满足一定精度要求的表示变量与函数值之间关系的一张表求出,然后把这张表存于单片机的程序存储器中,这时自变量值为单元地址,相应的函数值为该地址单元中的内容。查表,就是根据变量X在表格中查找对应的函数值Y,使Y=f(X)。例9:一个十六进制数存放在内部RAM的40H单元的低4位中,将其转换成ASCII码并送回40H单元。十六进制0~9的ASCII码为30H~39H,A~F的ASCII码为41H~46H,ASCII码表格的首地址为ASCTAB。编程如下:ORG1000HHEXASC:MOVA,40HANLA,#0FH;取低4位MOVDPTR,#ASCTAB;对待查询值修改指针,见下面MOVCA,@A+DPTRMOV40H,ARETASCTAB:DB30H,31H,32H,33H,34H,35H,36H,37H,38H,39H,41H,42H,43H,44H,45H,46H第十节单片机基本资源编程一、端口和中断资源编程注意事项:•单片机复位后,特殊功能寄存器IE、IP内容均为00H,所以应对IE、IP进行初始化编程,以开放中断,允许某些中断源中断和设置中断优先级等。•中断系统对五个中断源分别规定了各自的入口地址,但这些入口地址相距很近(8个字节)。•如中断服务程序的指令代码少于8个字节,则可从规定的中断服务程序入口地址开始,直接编写中断服务程序;•如中断服务程序的指令代码大于8个字节,则应采用与主程序相同的方法,在相应的入口处写一条跳转指令,并以跳转指令的目标地址作为中断服务程序的起始地址进行编程。例10:如图所示,将P1口的P1.4~P1.7作为输入位,P1.0~P1.3作为输出位。要求利用89C51将开关所设的数据读入单片机内,按键按下为低电平输入。由P1.0~P1.3输出的发光二极管,显示P1.4~P1.7输入的电平情况(若输入为低电平则相应的LED亮)。现要求采用中断边沿触发方式,每中断一次,完成一次读/写操作。解:如图所示,采用外部中断0,中断申请从INT0输入,并采用了去抖动电路。当P1.0~P1.3的任何一位输出为0时,相应的发光二极管就会发光。当开关K闭合时,发出中断请求。中断服务程序的矢量地址为0003H。源程序如下页。ORG0000HSJMPMAIN;上电,转向主程序ORG0003H;外部中断0入口地址SJMPINSER;转向中断服务程序ORG0030H;主程序MAIN:SETBEX0;允许外部中断0中断SETBIT0;选择边沿触发方式SETBEA;CPU开中断HERE:SJMPHERE;等待中断ORG0200H;中断服务程序INSER:MOVP1,#0F0H;读端口前先置1,高4位MOVA,P1;读开关输入状态SWAPA;A的高、低四位互换MOVP1,A;按键按下对应的LED亮RETI;中断返回END对应关系:P4、P5、P6、P7→P0、P1、P2、P3注意:在中断服务程序的末尾,必须安排一条中断返回指令RETI,使程序自动返回主程序。(本例是单片机复位、振荡电路、端口、中断的应用,注意理解)二、定时器/计数器资源编程一)定时器/计数器的应用:1.计数功能:生产线上产品计数:每个产品通过得到一个脉冲信号,计数器记录脉冲个数,当计数值与设定值相等,启动包装机器。检测转速:电机转动一圈发出一个脉冲,计数器记录一秒时间内脉冲个数,显示转速。2.定时功能:用于实时控制:定时采样、定时启动等。当定时时间与设定值相等,执行规定操作。二)定时器初始化编程:使用定时器工作之前,先写控制寄存器,确定好定时器工作方式。初始化编程格式:MOVTMOD,#方式字;选择方式MOVTHx,#XH;装入Tx时间常数MOVTLx,#XL(SETBEA);开Tx中断(SETBETx)SETBTRx;启动Tx定时器需考虑:1.按实际需要选择定时/计数功能;2.按时间或计数长度选择工作方式;3.计算