2020/1/271第4章汇编语言程序设计程序编制的方法和技巧4.1基本程序结构4.2子程序设计4.32020/1/272单片机应用系统由硬件系统和应用程序构成汇编语言高级语言应用程序设计方法汇编语言,生成的目标程序占内存空间少、运行速度快,具有效率高、实时性强。高级语言,对系统的功能描述与实现简单,程序阅读、修改和移植方便,适合于编写复杂的程序。2020/1/2734.1程序编制的方法和技巧4.1.1汇编语言程序设计的步骤分析问题,抽象出描述问题的数学模型。确定解决问题的算法或解题思想。分配存储空间和工作单元。绘制流程图编制程序。程序调试和程序优化。2020/1/274绘制流程图流程图符号开始或结束符号工作任务符号判断分支符号程序连接符号程序流向符号程序流向符号2020/1/275强化模块化观念使程序占用空间减少、结构清晰循环初值和结束条件,避免“死机”现象子程序的现场保护(注意栈平衡、寄存器内容)程序模块(主程序模块、各种子程序模块)模块化优点:分块设计、便于阅读、调试方便4.1.2编制程序的方法和技巧采用循环和子程序结构对中断子程序还有注意保护PSW的内容2020/1/2764.1.3汇编语言的语句格式非数字字符开头,后跟字母、数字、“-”、“?”等不能用已定义的保留字(指令助记符、伪指令等)后跟英文冒号“:”标号(即符号地址)[标号:]指令助记符[操作数1,][操作数2,][操作数3,][;注释]指令助记符是指令功能的英文缩写。2020/1/277数据:二进制(B)十进制(D或省略D)十六进制(H),注意A~F开头时要加“0”ASCII码,如‘A’,‘1245’符号:符号名、标号或“$”(PC的当前值)表达式:由运算符和数据构成操作数注释英文分号“;”开头4.1.3汇编语言的语句格式2020/1/278优先级运算符功能表达式及其结果示例高↓↓↓↓↓↓↓低·()括号4*(5+6)即44NOT、HIGH、LOW取反、取高字节、取低字节NOT55H即AAH;HIGH1234H即12H+、-正号、负号+5、-6*、/、MOD乘、除(取商)、取余数17/5即3;17MOD5即2+、-加、减5+4即9;5-4=即1SHL、SHR左移、右移2SHL2即8;8SHR2即2AND、OR、XOR与、或、异或45HAND0FH即05H、、=、、=、=比较运算符MOVA,X8;若X8为真,则为MOVA,01H若X8为假,则为MOVA,00H补充知识2020/1/2794.2.1顺序结构程序设计4.2基本程序结构【例4-1】试编制双字节加法程序。题目要求:设被加数的高字节放在30H中,低字节放在31H中,加数的高字节放在32H,低字节放在33H中。加法结果的高字节放在34H中,低字节放在35H中(大端模式)。程序分析:由于80C51单片机的加法指令只能处理8位二进制数,所以双字节加法程序的算法应首先从低字节开始相加,然后依次将次低字节和来自低字节相加的进位进行加法运算。2020/1/2710CY=0取低字节相加保存低字节开始取高字节相加保存高字节结束ORG0040HSTART:CLRCMOVA,31HADDA,33HMOV35H,AMOVA,30HADDCA,32HMOV34H,ASJMP$END2020/1/2711【例4-3】片内RAM的21H单元存放一个十进制数据十位的ASCII码,22H单元存放该数据个位的ASCII码。编写程序将该数据转换成压缩BCD码存放在20H单元。开始结束取十位的ASCII码保留低半字节移至高半字节,存回取个位的ASCII码保留低半字节合并到结果单元2020/1/2712ORG0040HSTART:MOVA,21H;取十位ASCII码ANLA,#0FH;保留低半字节SWAPA;移至高半字节MOV20H,A;存于20H单元MOVA,22H;取个位ASCII码ANLA,#0FH;保留低半字节ORL20H,A;合并到结果单元SJMP$END2020/1/27134.2.2分支结构程序设计在实际问题的编程处理中,通常会根据不同的条件进行判断,根据不同的判断结果,程序作出不同的相应处理,这种结构被称为分支。分支程序的设计主要依靠条件转移指令、比较转移指令和位转移指令来实现。2020/1/2714条件成立?YN程序段1程序段2单分支结构该图使用条件转移指令来实现分支,当给出的条件成立时,执行程序段1,否则执行程序段2。2020/1/2715该图使用条件转移指令来实现分支,当给出的条件成立时,执行程序段A,否则执行程序段B。2020/1/2716该图使用散转指令JMP来实现多分支转移,它首先将分支程序按序号的值来实现分支转移。2020/1/2717【例4-6】编制计算符号函数y=SGN(x)的程序。y=设片内RAM的30H单元内有自变量x(-128≤x≤127)的值,编制程序求函数Y的值,并将其存入片内RAM的31H单元中。010001xxx当当当2020/1/2718YYNN开始X﹥0?结束图4-8例4-6流程图X=0?Y-1Y0Y12020/1/2719ORG1000HSTART:MOVA,30H;将X送入A中JZZERO;为0转移JNBACC.7,POSITIVE;为正数转移MOVA,#0FFH;将1(补码)送入A中SJMPZEROPOSITIVE:MOVA,#01H;将+1送入A中ZERO:MOV31H,A;结果存入YEND2020/1/2720【补充例题】设变量x以补码的形式存放在片内RAM的30H单元,变量y与x的关系是:当x大于0时,y=x;当x=0时,y=20H;当x小于0时,y=x+5。编制程序,根据x的大小求y并送回原单元。开始结束取x至累加器X=0?X+05H送Y20H送YNX0?NYY2020/1/2721ORG0040HSTART:MOVA,30H;取x至累加器JZNEXT;x=0,转NEXTANLA,#80H;否,保留符号位JZDONE;x0,转结束MOVA,#05H;x0处理ADDA,30HMOV30H,A;X+05H送YSJMPDONENEXT:MOV30H,#20H;x=0,20H送YDONE:SJMPDONEEND2020/1/27224.2.3循环结构程序设计YN开始置循环初值循环处理循环修改结束处理循环结束?结束Y(a)先执行后判断开始结束置循环初值循环结束?循环处理循环修改结束处理N(b)先判断后执行2020/1/2723循环程序的组成大致包括以下内容:1、循环初始化:位于循环程序开头,设置各工作单元的初始值,设定循环次数等。2、循环体:循环体也称为循环处理部分,是循环程序的核心;用于完成实际操作处理,是重复的执行部分。3、循环控制:位于循环体内,一般由循环次数修改、指针修改和条件控制等组成,用于控制循环次数循环参数。4、循环结束:用于存放执行循环程序运行后的结果,以及恢复各工作单元的初值。2020/1/2724【例4-8】将内部RAM的30H至3FH单元初始化为00H。置初值开始结束循环处理循环修改结束处理循环结束?YNMAIN:MOVR0,#30H;置初值MOVA,#00H;MOVR7,#16;LOOP:MOV@R0,A;循环处理INCR0;DJNZR7,LOOP;循环修改,判结束SJMP$;结束处理2020/1/2725【例4-9】将内部RAM起始地址为60H的数据串传送到外部RAM中起始地址为1000H的存储区域,直到发现‘$’字符停止传送。置初值开始结束循环处理循环修改结束处理循环结束?YNMAIN:MOVR0,#60H;置初值MOVDPTR,#1000HLOOP0:MOVA,@R0;取数据CJNEA,#24H,LOOP1;循环结束?SJMPDONE;是LOOP1:MOVX@DPTR,A;循环处理INCR0;循环修改INCDPTRSJMPLOOP0;继续循环DONE:SJMPDONE;结束处理2020/1/2726【例4-12】设80C51单片机的时钟频率为fosc=12MHz,试设计0.1s的延时程序。DELAY:MOVR3,#Data1;1个机器周期(T)DEL2:MOVR4,#Data2;1个机器周期(T)DEL1:NOP;1个机器周期(T)NOP;1个机器周期(T)DJNZR4,DEL1;2个机器周期(T)DJNZR3,DEL2;2个机器周期(T)RET延时时间的计算结果:{1+[1+(1+1+2)*Data2+2]*Data1}*机器周期(T)若Datat1=125,Data2=200,则该程序产生的延时时间为:100376*机器周期(T)=0.100376s=0.1s2020/1/27274.3子程序设计完成通用功能、反复使用的程序设计成子程序。使应用程序结构清晰紧凑,便于阅读和调试执行要由其它程序来调用,执行完后要返回到调用程序结构上仍然采用一般程序的3种结构调用时注意:一是现场的保护和恢复;二是主程序与子程序间的参数传递。2020/1/27284.3.1子程序的调用与返回主程序:MAINLCALLSUB调用子程序子程序:SUBRET2020/1/27294.3.2保存与恢复寄存器内容PUSHPSW;保护现场(含当前工作寄存器组号)PUSHACC;PUSHB;﹕POPB;恢复现场POPACC;POPPSW;含当前工作寄存器组切换2020/1/27304.3.3子程序的参数传递主程序在调用子程序时传送给子程序的参数和子程序结束后送回主程序的参数统称为参数传递。入口参数:子程序运行时所需要的原始参数。在调用子程序前,必须将所需参数送到指定的存储单元(或寄存器)中,然后子程序从约定的存储单元(或寄存器)中获得这些入口参数。出口参数:子程序根据入口参数执行程序后所得的结果。子程序运行结束(返回)前,必须将出口参数送到指定的存储单元(或寄存器)中,以便主程序从指定的存储单元(或寄存器)中获得运行结果。2020/1/27314.3.3子程序的参数传递利用累加器或寄存器(简单、快速,但参数个数不多)【例4-15】实现两个8位的十六进制无符号数求和的子程序。SADD:MOVA,R3;取加数(在R3中)CLRCADDA,R4;被加数(在R4中)加AJCPP1MOVR3,#00H;结果小于255时,高字节R3内容为00HSJMPPP2PP1:MOVR3,#01H;结果大于255时,高字节R3内容为01HPP2:MOVR4,A;结果的低字节在R4中RET入口:(R3)=加数;(R4)=被加数。出口:(R3)=和的高字节;(R4)=和的低字节。2020/1/2732利用存储器(个数多,用R0或R1及DPTR为参数表指针)【例4-16】将内部RAM中两个4字节无符号整数相加,和的高字节由R0指向。数据采用大端模式存储。入口:(R0)=加数低字节地址;(R1)=被加数低字节地址。出口:(R0)=和的高字节起始地址。NADD:MOVR7,#4;字节数4送计数器CLRC;NADD1:MOVA,@R0;利用指针,取加数低字节ADDCA,@R1;利用指针,被加数低字节加AMOV@R0,A;DECR0DECR1DJNZR7,NADD1INCR0;调整指针,指向出口RET2020/1/2733利用堆栈【例4-17】将内部RAM中20H单元中的1个字节十六进制数转换为2位ASCII码,存放在R0指示的两个单元中。入口:预转换数据(低半字节)在栈顶出口:转换结果(ASCII码)在栈顶HEASC:MOVR1,SP;借用R1为堆栈指针DECR1DECR1;R1指向被转换数据XCHA,@R1;取被转换数据ANLA,#0FH;取一位十六进制数ADDA,#2;偏移调整,所加值为MOVC与DB间总字节数MOVCA,@A+PC;查表XCHA,@R1;1字节指令,存结果于堆栈中RET;1字节指令ASCTAB:DB30H,31H,32H,33H,34H,35H,36H,37HDB38H,39H,41H,42H,43H,44H,45H,46H2