




已阅读5页,还剩82页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第4章MCS 51汇编语言程序设计 本章要点 程序设计语言分类及特点汇编语言程序编写方法顺序结构程序的编写方法分支结构程序的编写方法循环结构程序的编写方法子程序的编写方法 4 1汇编语言概述 4 1 1程序设计语言的分类及特点能够完成一定功能的指令序列称为程序 用来设计计算机程序的语言称为程序设计语言 按照语言的结构及功能程序设计语言可分为 机器语言 汇编语言和高级语言 1 机器语言是计算机所能识别的唯一语言 由二进制代码0和1构成指令和数据 其具有效率高 响应速度快的特点 但由于机器语言编写的程序依赖于计算机的结构 可移植性差 繁琐且难于记忆 识别和调试 通常编程时 不采用机器语言 2 汇编语言 是一种符号语言 指令由助记符表示 与机器语言相比 汇编语言具有指令容易记忆 理解 识别和可读性好的优点 但实质上由于汇编语言也是面向机器的语言 是机器语言程序的符号表示 所以用户必须熟悉机器的硬件结构和指令系统 掌握计算机的工作过程才能熟练编程 汇编语言程序通常用于实时控制领域 其所能完成的操作不是一般高级语言所能实现的 而且源程序经汇编生成的可执行文件一般运行效率较高 另外 汇编语言所编写的源程序与经过汇编所产生的机器代码程序之间有明显的一一对应关系 所以汇编语言编写的程序同样也存在通用性差 程序不能移植的缺点 3 高级语言 高级语言是接近于人的自然语言形式的计算机编程语言的总称 例如C语言 BASIC语言等都是高级语言 和汇编语言相比 高级语言指令简单易学 用户容易掌握 且高级语言程序不依赖于具体的硬件结构和指令系统 程序可移植性好 但是高级语言编写的源程序必须经过编译或解释程序翻译成目标程序 机器才能执行 而生成的目标程序需占用较多的存储单元 执行时间较长 运行效率较低 由于目前有些高级语言不具有直接处理接口和中断技术的功能 因此高级语言一般很少应用于实时控制 但随着语言的发展 这种情况将会有所改变 4 1 2单片机汇编语言源程序的编辑和汇编 汇编语言的指令类型用汇编语言编写的 具有特定功能的指令序列 称为汇编语言源程序 源程序由两种类型的汇编语言语句 即指令 构成 语句是汇编语言的基本组成单位 按性质不同分为两类 指令性语句 机器指令 和指示性语句 伪指令 1 机器指令即指令系统中的全部指令 每条指令都有对应的机器代码 是机器真正能够执行的指令 2 伪指令为汇编程序在汇编过程中提供控制或指示信息 并不直接产生机器代码 属于机器不能执行的指令类型 汇编计算机不能直接识别和执行源程序 因此源程序必须经过汇编程序汇编产生机器码目标程序文件 程序才能执行 这种将汇编语言源程序转换成机器语言程序的过程称为汇编 对于初学者来说 应注意汇编语言源程序和汇编程序两个术语的区别 它们的功能示例如图4 1所示 图4 1汇编过程 汇编语言源程序 汇编程序 目标文件和源程序列表 经过 汇编后 汇编语言源程序的汇编过程分为手工汇编和机器汇编 所谓手工汇编 即采用人工查指令表的方法将汇编指令翻译成相应机器代码 通常源程序的人工汇编需要两次才能完成 第一次汇编查找每条指令的机器代码 第二次汇编完成地址偏移量的计算 由于手工汇编需要计算和查找 繁琐而且容易出错 而且程序修改时可能会引起指令的地址变化 转移指令的偏移量也随之改变 需要重新计算 所以手工汇编主要应用于设计短小程序或调试程序的场合 所谓机器汇编 即采用汇编程序对源程序进行自动汇编 由于单片机软硬件资源的限制 该过程通常借助于PC系统机实现 象这种借助于一种计算机而为另一种计算机产生目标代码的汇编方式又称为交叉汇编 交叉汇编的原理类似于手工汇编 在汇编程序中通常存入了两张表 即MCS 51单片机的指令代码表和伪指令表 汇编程序通常通过两次扫描完成汇编 第一次扫描查找源程序中每条指令的机器代码 第二次扫描完成地址偏移量的计算 汇编后生成目标文件和列表文件 这是目前应用较广的汇编方法 纵上 汇编的主要功能为 1 确定程序中每条汇编语言指令的指令机器码 2 确定每条指令在程序存储器中的存放地址 3 提供语法 编辑等方面的错误信息 但不能提供程序的逻辑错误 4 产生目标执行文件 OBJ HEX 和列表文件 LST 4 1 3MCS 51汇编语言的伪指令 所谓 伪 指令 即不是真正的可执行指令 如前所述 伪指令只能在对源程序进行汇编时起控制作用 例如设置程序的起始地址 定义符号 给程序分配一定的存储空间等 常用的伪指令共有8条 下面分别介绍 1 ORG Origin 设置起始地址指令用来设定程序或数据存储区的起始地址 指令格式为 ORG16位地址例如 有程序段如下 则表示程序存放的起始地址为1000H 指令地址机器码源程序ORG1000H1000H782MAIN MOVR0 20H1002HE6MOVA R0 若省略ORG指令后的16位地址 则汇编后目标程序的起始地址默认为0000H ORG指令在一个源程序中可以多次使用以指定不同程序段的起始地址 但是指定的多个地址应从小到大 不能使程序段之间产生重叠 2 DB DefineByte 定义字节型常数指令指令的功能为从指定的存储单元开始 定义或存储一个或多个字节数据 指令格式为 标号 DB字节常数表例如 ORG1000HTAB DB0A3H 18 AB 汇编结果为 1000H A3H 1001H 12H 1003H 41H 1004H 42H3 DW DefineWord 定义字型常数指令指令的功能为从指定的存储单元开始 定义或存储一个或多个字数据 通常用于在程序中定义地址表 指令格式为 标号 DW字常数表一个字数据占用两个存储单元 存放时高字节存入低位地址 低字节存入高位地址 例如 ORG1000HTAB DW1234H 18 00A3H汇编结果 1000H 12H 1001H 34H 1002H 00H 1003H 12H 1004H 00H 1005H A3H 4 EQU Equate 等值指令指令功能是把操作数段中的地址或数据赋值给标号 赋值后的标号可在整个源程序中使用 指令格式如下 标号EQU数据或汇编符号注意 与程序中一般意义的标号不同的是 这里的标号后不能加 例如 ADDREQU1000H 给ADDR赋值1000HDAT1EQU0AH 给DAT1赋值0AH MAIN MOVDPTR ADDR DPTR 1000HMOVA DAT1 A 0AH 上例中 ADDR被赋值以后 在程序中作为立即数使用 而DAT1赋值后被当作直接地址使用 需要说明的是 使用EQU指令时必须先赋值后使用 5 DS DefineStore 定义存储区指令指令功能为从指定的单元地址开始 保留一定数量的存储单元 以备使用 指令格式如下 标号 DS表达式其中 表达式指定保留的内存单元个数 例如 ORG1000HDS10TAB DB10H 汇编结果 从1000H地址处开始 保留10个字节单元 100AH 10H 6 BIT 位地址符号指令指令功能为将位地址赋值给指定的符号名称 通常用于位符号地址的定义 指令功能如下 字符名称BIT位地址例如 X1BITP1 0汇编结果为将位地址P1 0赋给变量X1 即在程序中便可使用X1代替位地址P1 0 7 DATA 数据地址赋值指令指令功能为将数据地址或指令代码地址赋值给所指定的标号 通常在程序中用于定义数据地址 指令格式如下 标号 DATA表达式例如 AA DATA2000H汇编结果为 AA 2000H 注意 DATA与EQU的区别在于 EQU定义的标识符在汇编时不在符号表中登记 因此必须先定义后使用 而DATA定义的标识符汇编时在符号表中登记 所以可以先使用后定义 8 END 汇编语言源程序结束指令指令功能 表示源程序到此结束 END指令以后的指令汇编程序将不予处理 一个程序中只能在末尾有一个END指令 例如 START ENDSTART 4 2汇编语言程序设计对于简单程序的编写 程序设计者往往能够立即完成软件的构思与编写 而对于比较复杂的程序设计问题 则需要科学合理的程序设计步骤 从软件工程角度来说 开发一个应用程序 一般需要经过以下几个步骤 1 分析问题 建立数学模型如果拿到问题 立即着手编写程序代码往往是很难成功的 通常需要首先分析题目的已知条件 了解系统的硬件配制 明确题目的要求和要实现的功能 然后建立数学模型 2 了解系统的硬件配置和性能指标 确定方案算法所谓算法就是为了解决问题而采取的方法和步骤 在分析问题后 应根据系统的具体硬件配置和性能指标等实际情况确定具体有效且计算机能够执行的方法和步骤 3 用流程图表示出程序算法确定算法后 应用简单直观的方法描述算法 以便为进一步编程做好准备 算法的描述方法很多 其中比较常用的是流程图法 一般流程图的符号如图4 2所示 4 根据流程图编写源程序使用汇编语言编写源程序时 应首先合理规划和分配存储器单元 确定程序和数据的存放区域 了解系统的I O接口地址 最后按照流程图写出源程序 5 调试运行程序通常情况下 源程序编制好后必须经过上机调试才能运行 调试程序的一个重要功能是修正语法和逻辑错误 直到达到题目的要求为止 4 2 1顺序程序设计顺序程序是程序设计中最基本的结构 又称为简单程序 特点为顺序执行每一条指令 直到最后 其执行过程如图4 3所示 例4 1 编写一程序 将累加器A中的两位压缩BCD转换成ASCII码存入1000H 1001H片外存储单元中 开始R0 AA 取A的低4位A A 30H存入1000H单元A 取A的高4位A R0A A 30H存入1001H单元结束分析 由ASCII码表可知 0 9的的ASCII码为30H 39H 即BCD码与ASCII码值的差值为30H 因此只要将两位BCD码分别取出与30H相加即可获得相应的ASCII码值 其算法流程图如图4 4所示 程序如下 ORG2000HMOVDPTR 1000HMOVR0 AANLA 00001111BORLA 30HMOV DPTR AMOVA R0SWAPAORLA 30HINCDPTRMOVX DPTR AEND 例4 2 已知如图4 5所示 双字节4位压缩BCD码数X存于片内RAM30H 31H单元 Y存于32H 33H单元 编程求Z X Y 并将结果Z存入片内RAM单元34H 35H 36H中ADDSUM MOVA 30HADDA 32H 低位字节相加DAA BCD码修正MOV34H A 低位字节存入34H中MOVA 31HADDCA 33H 高位字节与低位进位相加DAAMOV35H A 高位字节之和存入35H中MOVA 0ADDCA 0DAAMOV36H A 高位字节的进位存入36H中END 从该例可以看出 多字节BCD码相加时 应从低位字节开始相加 每进行一次加法运算进行一次BCD码调整才能得到正确结果 4 2 2分支程序设计 1 分支程序结构的基本形式分支程序是程序设计中应用非常广泛的一种基本结构 比如我们经常遇到需要计算机进行逻辑判断的情况 然后根据判断的结果进行不同的处理 例如 比较两个数的大小 并输出判断结果 根据输入压力的不同 用不同的方法计算发动机功率等这些问题都是顺序结构程序所无法实现的 而是属于分支结构程序设计的范畴 具体来说 如图4 6所示 分支程序结构具有以下三种形式 a 单分支 b 双分支 c 多分支 2 分支程序的设计方法依据分支结构程序的执行过程 结合MCS 51的指令系统可以得出 分支程序的实现需要通过条件转移指令完成 因此如何设定分支条件便成为分支程序设计的关键 具体来说 其设计要点可归纳如下 1 设定可供条件转移指令测试的条件 通常 可以作为转移指令判断条件的有标志位状态 如进位位C的状态 累加器A或片内RAM中某位的结果状态等 因此为了提供条件转移指令的测试条件 应在程序的转移指令前设定影响判断条件的标志位状态 或通过逻辑运算 算术运算等指令影响标志位 例如 若要使用JZ指令实现分支 则应在该指令前执行影响累加器A的指令 2 根据标志位状态选择正确的条件转移指令 例如 要判断进位位 可使用JNC或JC等指令 3 应在转移的目的地址处设定标号 例4 4 求8位有符号数的补码 设8位二进制数存放在片内RAM30H单元内 分析 对于二进制数的补码负数可用取反加1的方法求得 而正数不变 ORG2000HMOVA 30HJNBACC 7 NEXT 为正数 不进行处理CPLA 负数取反INCA 加1MOV30H ANEXT SJMPNEXT 结束可以看出 该程序通过符号位是否为0来判断该数为正数还是负数 若为正数则程序结束 若为负数则变补 显然 这是一个单分支的例子 例4 5 已知压力P和功率W之间存在如下关系 其中M为功率修正系数 当P 10 W P 5 M当P 10 W P 5 M分析 首先判断压力P是否大于或等于10 然后根据判定结果计算功率W 设P存在40H单元中 M存放于41H中 结果W存放于42H中 程序如下 ORG2000HMOVA 40H 取压力值MOVR3 AMOVB 05H P 5MULABMOVR1 A 将P 5的结果暂存入RMOVA R3 取回PCJNEA 10 L1 P 10 转L1SJMPNEXT P 10 转NEXTL1 JCNEXT CY 1 P 10 转至NEXTMOVA R1 CY 0 P 10 W P 5 MSUBBA 41HSJMPRESULTNEXT MOVA 41H 取M的值ADDA R1 W P 5 MRESULT MOV42H A 存结果END 该例是一个双分支程序 通过比较转移指令CJNEA 10 L1和条件判断指令JCNEXT实现程序的分支 这种编程方法在分支程序设计中应用非常广泛 对于初学者应仔细分析体会 例4 6 求符号函数Y F X 1 当X 0时 F X 0 当X 0时 1 当X 0时 Y 0Y 1Y 1保存Y结束X 0 NY开始取XYNX 0 分析 这是一个多分支程序示例 设计时可考虑将判断变量X转化为判断累加器A 从而使用条件判断指令JZ来判断是否为零 另外 由于X是有符号数 因此可使用JB或JNB指令判断符号位来实现分支 其流程图如图4 7所示 设变量X存于片内RAM20H单元 结果Y存于21H单元 程序如下 ORG2000HMOVA 20H 取XJZRESULT X 0 Y 0JBACC 7 NEG 判符号位 若为1 则X 0MOVA 1 X 0 Y 1SJMPRESULTNEG MOVA 0FFH X 0 Y 1 将 1的补码送ARESULT MOV21H A 保存YSJMP 3 分支表法实现多向分支程序的设计上例中符号函数程序是一个多向分支程序的例子 直接采用了条件转移指令来实现 但在实际应用时 通常遇到根据某变量取值不同从而转向不同分支的题目 这时经常采用分支表法 常用的分支表主要有三种形式 转移指令表 分支地址表和地址偏移量表 虽然分支表的构成各异 具体的编程方法也略有不同 但实质上都是利用散转指令JMP A DPTR来实现的 实现散转的方法主要有两种 1 累加器A清零 根据DPTR的内容决定转移的目标地址 2 DPTR的值作为基址不变 根据累加器A的内容决定转移的目标地址 下面通过一个具体的例题 说明使用分支表实现多向分支程序设计的方法 例4 7 根据R2的内容 转向相应的分支程序 即 R2 0 转向PROG0R2 1 转向PROG1 R2 n 转向PROGn 1 使用转移指令表 所谓转移指令表即由转移指令组成的分支表 如图4 8所示 JMPTAB AJMPPROG0JMPTAB LJMPPROG0AJMPPROG1LJMPPROG1 AJMPPROGnLJMPPROGn a b 图4 8转移指令表 设转移指令表的标号为JMPTAB 分支数为5 解题思路分析如图4 9所示 MOVDPTR JMPTAB 将JMPTAB DPTRMOVA R2MOVB 03MULAB R2 3 BAPUSHA 暂存乘积的低位字节AMOVA BADDA DPH 将乘积的高位字节B DPH DPHMOVDPH A POPA 将暂存的A内容恢复JMP A DPTR 散转JMPTAB AJMPPROG0 转移指令表AJMPPROG1 AJMPPROG5 PROG0 PROG1 PROG5 采用第二种散转程序设计方法 即DPTR内容固定 根据A的值转向不同的分支程序 则应首先将JMPTAB DPTR 使DPTR的内容固定 将R2 M A 当使用短转移指令表时M的值为2 当使用长转移指令表时M的值为3 然后使用JMP A DPTR指令实现分支 具体程序如下 说明 因为转移指令表由LJMP长跳转指令构成 因此M取值为3 其乘积的高位字节应加在DPH上 若转移指令表由2字节指令AJMP构成 M取值应为2 且各分支程序的入口地址PROG0 PROG1 必须与转移指令表处于同一个2KB的存储地址空间之内 而LJMP指令表则没有这个限制 使用AJMP转移指令表的程序如下 MOVDPTR JMPTAB 将JMPTAB DPTRCLRCMOVA R2RLCA R2 2 AJNCNOADD 判断是否有进位INCDPH 若有进位 将进位加到高字节DPHNOADD JMP A DPTR 散转JMPTAB AJMPPROG0 转移指令表AJMPPROG1 AJMPPROG5 PROG0 PROG1 PROG5 2 使用分支地址表 所谓分支地址表是指由各个分支程序的入口地址组成的线性表 每个入口地址占两个连续字节单元 设PROG0 PROGn为分支程序入口地址 分支地址表如图4 10所示 BRANCHTAB DWPROG0DWPROG1 DWPROGn图4 10分支地址表DPTR BRANCHTABA R2 2JMP A DPTR分支程序PROG0分支程序PROGn分支程序PROG1DPTR 应用MOVCA A DPTR取分支地址A 0图4 11分支地址表的使用设分支地址表的标号为BRANCHTAB 分支数为n 使用分支地址表实现多向分支的解题思路分析如图4 11所示 根据以上解题思路 例4 7程序编写如下 ORG1000HMAIN MOVDPTR BRANCHTAB 取分支表入口地址MOVA R2CLRC A R2 2RLCAJNCNOADDINCDPH 进位加到DPH中NOADD MOVR3 A R3 R2 2MOVCA A DPTR A 分支地址的高位字节XCHA R3 R3 分支地址的高位字节 A R2 2INCA 指向下一个存储单元MOVCA A DPTR 取分支地址的低位字节MOVDPL A MOVDPH R3 CLRAJMP A DPTR 分支地址 PC 转移BRANCHTAB DWPROG0 分支地址表DWPROG1 PROG0 分支程序0 PROG5 分支程序5 3 使用地址偏移量表 所谓地址偏移量表 是指由各分支程序段的入口地址与地址偏移量表的标号差 即地址偏移量 形成的线性表 其中地址偏移量表中每项占一个字节 如图4 12所示 TAB DBPROG0 TAB PROG0 PROGn为分支程序入口地址DBPROG1 TAB DBPROGn TAB图4 12地址偏移量表 根据以上解题思路 例4 7程序编写如下 ORG1000HMOVDPTR TAB 取偏移量表首地址MOVA R2MOVCA A DPTR 将查表所得PROGi TABAJMP A DPTR 由A DPTR PROGi TAB TAB PROGi获得分支程序地址实现跳TAB DBPROG0 TAB 地址偏移量表DBPROG1 TAB DBPROGn TABPROG0 PROGn 使用地址偏移量表实现多向分支时 应注意要使地址偏移量表与各分支程序的长度和在同一页 256字节 范围内 因此该方法适用于分支较少的情况 4 2 3循环程序设计 在程序设计中 常常会涉及到重复执行的程序段 这可以通过循环结构来实现 循环结构可使程序更加紧凑 1 循环结构程序的构成与高级语言中循环程序的构成相似 汇编语言的循环程序结构主要包括以下四个部分 1 循环初始化部分 所谓初始化即设置循环开始时的状态 如清结果单元 设置地址指针 设定寄存器初值 循环次数等 2 循环体部分 循环体是循环结构的主体 为需要重复执行的程序段 3 循环控制部分 这一部分主要完成循环条件的设定 循环控制变量的修改以及检测循环条件是否仍然满足 若条件成立则继续循环 否则结束循环 4 结束部分 该部分主要完成循环结束后的结果处理工作 例如结果的保存 计算等 其执行过程如图4 12所示 2 简单循环程序 所谓简单循环即一重循环 也就是循环程序中只包含一个循环 不嵌套其它循环的循环程序 另外 设定循环执行的条件非常重要 否则可能形成死循环 编写汇编语言程序时 通常采用两种控制循环的方法 一种是使用计数的方法实现循环 即将循环次数作为循环计数器的初值 当计数器的值加满 称为正计数 或减为0 称为倒计数 时结束循环 否则继续循环 该方法适用于循环次数已知的情况 另一种为通过设定特定条件控制循环 若设定条件满足则执行循环 否则结束循环 例如设定特定的循环结束标志等 该方法适用于循环次数未知的情况 例4 8 数据块求和 设内部RAM中有一连续单字节数据块 首地址为BLOCK单元 数据块长度存于LEN单元 若数据累加和也为单字节数据 并存于RESULT单元 编程求数据块之和 分析 累加和的求解应使用循环 且数据长度已知 所以循环次数已知 因此可考虑使用指令DJNZ来控制循环条件 其算法流程图如图4 15所示 程序如下 ORG1000HLEN DATA20HRESULT DATA21HBLOCK DATA22HMOVA 0MOVR0 BLOCKMOVR1 LENLOOP ADDA R0INCR0DJNZR1 LOOPMOVRESULT AHERE SJMPHERE可以看出 这是一个循环次数已知的例题 例4 9 求均值 已知一控制系统 从P1口读入采样值 每周期采样16次 试编程求其每周期的采样均值 分析 要求平均值 应先求得16次采样的数据和 然后除以16即可 由于16 24 因此可使用指令右移指令RRC完成除法运算 设累加和存于寄存器R0R1中 其具体程序如下 ORG1000HMOVR0 0 清零 保存和的高位字节MOVR1 0 清零 保存和的低位字节MOVR3 16 R3用作计数器L1 MOVP1 0FFH 置P1为输入口MOVA P1 读入采样值ADDA R1 累加JNCL2 若无进位 转L2INCR0 进位加到高位字节L2 MOVR1 ADJNZR3 L1 16次采样值是否累加完 MOVR4 4 R4 右移次数4L3 MOVA R0RRCA 高位字节右移1位MOVR0 AMOVA R1RRCA 低位字节右移1位MOVR1 ADJNZR4 L3 循环4次完成除以16HERE SJMPHERE 例4 10 求最小值 设内部RAM中有一无符号数数据块 其首地址为BLOCK 长度存于LEN单元 试求出数据块中的最小值 存入MIN单元 分析 求最小值时 通常采用比较交换的方法 即首先取第一个数作为基准 然后将基准数与第二个数进行比较 若基准数大于第二个数 则两数进行交换 若基准数小于第二个数则不进行交换 总之保证基准数单元中的值为最小值 再取下一个数与基准数进行比较 一直到所有数据比较完为止 则基准数单元中的数则为最小值 依此类推 求最大值也可采用类似方法 其算法流程图如图4 16所示 具体程序编写如下 ORG1000HMIN DATA20HLEN DATA21HBLOCK DATA22HCLRAMOVR2 LENMOVR1 BLOCK 取数据块首地址LOOP CLRC 清CY准备做减法SUBBA R1JCNEXT A R1 转NEXTMOVA R1 A R1 则A R1 SJMPNEXT1NEXT ADDA R1 A R1 恢复ANEXT1 INCR1DJNZR2 LOOPMOVMIN A 存最小值HERE SJMPHERE 例4 11 设片内RAM中有一无符号数数据块 其首地址为BLOCK 长度未知 但数据结束标志存于LEN单元 求数据块的最小值并存于MIN单元中 ORG1000HMIN DATA20HLEN DATA21HBLOCK DATA22HMAIN MOVR1 BLOCK 数表首地址MOVB R1 取第一个数作为基准NEXT INCR1 修改指针MOVA R1CJNEA LEN NEXT1 是否为数表结尾 SJMPDONE 循环结束NEXT1 CJNEA B NEXT2 比较NEXT2 JNCNEXT A B 转NEXT继续取数MOVB A 保存较小值SJMPNEXTDONE SJMPDONE可以看出 这是一个循环次数未知的例题 通过设定特征值控制循环结束条件 3 多重循环多重循环又称之为循环的嵌套 即在一个循环结构的循环体内 又包含另一个完整的循环结构 在实际应用中 有时一重循环并不能解决问题 所以循环的嵌套应用还是比较广泛的 在嵌套循环的使用中 被嵌套的循环可以不止一个 并且可以嵌套多层 但不论是哪种情况 内层循环和外层循环都必须是一个完整的结构 不允许有相互交叉的情况出现 即如出现图4 17 a 所示的情况则是非法的嵌套 而图4 17 b 所示为正确的嵌套 图4 17 a 非法嵌套示意图图4 17 b 正确嵌套示意图 例4 12 数据排序程序 设有N个数据 存于首地址为BLOCK的内存单元中 试设计程序将数据从小到大按升序排列 分析 数据排序的方法很多 这里采用沉底法 沉底法的基本思想 通过相邻两个数之间的比较和交换 使排序码 数值 较小的数逐渐从底部移向顶部 排序码较大的数逐渐从顶部移向底部 就像较大的数往下沉底一样 故而得名 设由R0为数据存放区的首地址 则 R0 N 中存放第N个数据 进行沉底排序的过程可以描述为 1 首先将相邻的 R0 与 R0 1 进行比较 如果 R0 的值小于 R0 1 的值 则不交换两者的位置 否则不交换 即使较小的上浮 较大的下沉 接着比较 R0 1 与 R0 2 同样的方法使小的上浮 大的下沉 依此类推 直到比较完 R0 N 1 和 R0 N 后 R0 N 为具有最大排序码 数值 的元素 称第一趟排序结束 2 然后在 R0 N 1 R0区间内 重新进行第二趟排序 使剩余元素中排序码最大的元素沉底到 R0 N 1 重复进行n 1趟后 整个排序过程结束 其算法流程图如图4 18所示 ORG1000HBLOCK DATA20HSORT MOVA N 1 N个数据排序MOVR4 A 外循环次数LOOP1 MOVA R4MOVR3 A 内循环次数MOVR0 BLOCK 设数据指针LOOP2 MOVA R0MOVB A B R0 INCR0MOVA R0 A R0 1 CJNEA B L1 两数比较L1 JNCNEXT 若B A 即 R0 R0 1 不交换DECR0 否则交换数据XCHA R0INCR0 修改数据指针MOV R0 ANEXT DJNZR3 LOOP2 内循环DJNZR4 LOOP1 外循环HERE SJMPHERE 在数据采集系统中 若被采集的信号变化较慢 经常采用中值滤波的方法去掉由于偶然因素造成的干扰误差 中值滤波时 首先将采样数据排序 再取中间值作为本次采样的终值 4 2 4子程序设计第三章指令系统中已经介绍过子程序的概念与相关指令 子程序是一种重要的程序结构 主要由需要反复执行的操作或程序段构成 以供主程序调用 另外 子程序也可嵌套 即子程序调用子程序 只要堆栈深度足够 子程序可嵌套多层 主程序与子程序以及子程序嵌套调用的关系如图4 19所示 与一般程序的编写方法相比 子程序具有以下几个特点 1 子程序应命名 子程序入口即子程序的第一条指令应加标号作为子程序名 以便主程序调用 2 子程序中应注意堆栈的使用 以保护和恢复现场 在有些情况下 若子程序需要改变主程序中某些寄存器或存储单元的结果 而这些结果又不能被修改或在子程序调用之后仍然需要则应在子程序中首先将这些内容使用进栈指令保护 在子程序返回之前使用出栈指令恢复现场 3 子程序的结尾必须为子程序返回指令RET 并保证堆栈栈顶为调用程序的返回地址 4 子程序嵌套时应考虑堆栈的深度 5 能够正确传送参数 参数分为入口与出口参数 所谓入口参数 即调用子程序之前 需要传给子程序的参数 所谓出口参数 即子程序返回时应送回调用程序的结果参数 应根据具体情况选择不同的参数传递方式 通常使用寄存器 存储器或堆栈的方式传送参数 6 子程序应具有一定的功能和通用性 对于操作数应尽量使用以地址或寄存器形式给出 一般不针对具体的数据编写子程序 7 为了便于调用 子程序应提供足够的信息 如 子程序名 子程序功能 入口参数和出口参数 子程序占用的硬件资源 子程序中调用的其他子程序名 例4 13 编程实现C a2 b2 设a b均小于10且分别存于外部RAM的100H 101H单元 要求运算结果C存于外部RAM102H单元 分析 本题可利用子程序完成求单字节数据的平方 然后通过调用子程序求出a2和b2 其具体程序如下 SQR INCAMOVCA A PC 使用查表指令求平方RETTAB DB0 1 4 9 16 25 36 49 64 81可以看出SQR子程序的入口参数为A 即将要求平方值的数送入A 其出口参数也为A 即求出的平方值也送入A 该例的主程序如下 ORG1000HSTART MOVDPTR 0100HMOVXA DPTR 取a的值ACALLSQR 调用子程序求a的平方MOVR1 A R1 a2MOVDPTR 0101HMOVXA DPTR 取b的值ACALLSQR 调用子程序求b的平方ADDA R1 A a2 b2MOVDPTR 0102HMOVX DPTR A 存结果SJMP 例4 14 设计子程序将单字节数据对半拆分 变成2个字节存放 分析 子程序入口参数 累加器A 即将要拆分的数据送入A累加器中 出口参数 R1 即将拆分后的数据存入 R1 指向的两个相邻地址单元 FEN1 MOV R1 0 将 R1 指向的地址单元清零XCHDA R1 低半字节存入指定单元INCR1 修改指针 指向下一个地址单元MOV R1 0 清零SWAPA 高低半字节交换XCHDA R1 保存高半字节RET 例4 15 编写将累加器A中的ASCII码转换为1位十六进制数的子程序 分析 十六进制数0 9的ASCII码为30H 39H 即十六进制数 0 9 ASCII码 30H 十六进制数A F的ASCII码为41H 46H 即十六进制数 A F ASCII码 37H 根据此对应关系 子程序可编写如下 ASCHEX CLRC 准备相减SUBBA 30H A A 30HCJNEA 0AH L2 转移判断L2 JCL1 若A中的值 0AH则求得十六进制数SUBBA 07H 若A中的值 0AH 则A A 07H 求得十六进制数L1 RET可以看出该子程序的入口参数为累加器A 出口参数也为累加器A 另外 根据ASCII码和十六进制数的对应关系 将1位十六进制数转换为ASCII码的程序可编写如下 HEXASC CJNEA 0AH L1L1 JNCL2ADDA 30HSJMPHEREL2 ADDA 37HHERE RET请读者自行分析该子程序每条语句的功能及入口和出口参数 4 3MCS 51汇编语言实用程序举例在程序设计时 通常把需要多次使用的程序段设计成具有特定功能的子程序 以供调用 这样可使程序设计避免了重复性的工作 从而使效率提高 而程序也更加灵活 下面将分类介绍一些实用的子程序 4 3 1代码转换程序 例4 16 将累加器A中的8位二进制数转换成3位BCD码 3位BCD码占用两个字节 结果的百位数存于BAI单元 十位和个位数占用一个字节单元SHIGE 分析 可应用除法指令实现数制的转换 将被转换的数除以100得百位数 再将余数除以10得十位数 余数为个位数 例如 设要转换得二进制数据为0EFH 则0EFH除以100 商 02H 即百位数为2 余数 27H 27H除以10 商 03H 即十位数为3 余数 09H 即个位数为9 其具体程序如下 BAI DATA30HSHIGE DATA31HBINBCD MOVB 100DIVAB 除以100 A 商 B 余数MOVBAI A BAI 商MOVA 10 XCHA B B中的余数与A中除数10互换DIVAB 余数除以10 A 商 B 余数SWAPA 十位数交换到A的高半字节ORLA B 与B中的个位组合成一个字节数据MOVSHIGE A 十位 个位送入单元SHIGERET该子程序入口参数 累加器A出口参数 地址单元BAI和地址单元GESHI 例4 17 编写将多字节二进制数转换成BCD码的子程序 分析 上例中介绍了一种使用除法实现将二进制数转换成BCD码的算法 但上述方法一般针对转换数较小的情况 若被转换数较大 则该算法需要进行多字节运算 运算速度较慢 程序效率较低 若用b代表各二进制位数 则有下式成立 BCD bn 1 2n 1 bn 2 2n 2 b1 21 b0 20 bn 1 2 bn 2 2 bn 3 2 b1 b0因此可采用从最高位开始 按十进制运算法则循环 乘2加次低位 的算法 D D 2 di 设子程序入口参数 R0 R7 R0为二进制数低位字节地址指针 R7为要转换的二进制字节数 出口参数为 R1 R1为压缩BCD码高位字节地址指针 其流程图如图4 20所示 具体程序如下 ORG1000HBINBCD MOVA R0MOVR5 A R0 R5MOVA R1MOVR6 A R1 R6MOVA R7 取二进制字节数INCA 二进制字节数加1 AMOVR3 ACLRAL0 MOV R1 A 对BCD码存储单元清零INCR1DJNZR3 L0MOVA R7 取二进制字节数MOVB 08MULAB 二进制字节数 8 二进制位数MOVR3 A 二进制位数 R3图4 20 例4 17 程序流程图L3 MOVA R5MOVR0 A 二进制数低位字节地址 R0 MOVA R7MOVR2 A 二进制数字节数 R2CLRCL1 MOVA R0 二进制数左移1位RLCAMOV R0 AINCR0DJNZR2 L1MOVA R6 BCD码地址指针 R1MOVR1 AMOVA R7MOVR2 AINCR2 BCD码字节数 R2L2 MOVA R1 BCD码 2 CYADDCA R1DAAMOV R1 AINCR1DJNZR2 L2DJNZR3 L3RET 例4 18 编程将4位单字节BCD码转换成二进制数 设4位BCD码为D3D2D1D0 子程序入口参数 R0 R0为BCD码的高位字节D3地址指针 设BCD码的高位在前 低位在后 出口参数为 R3R4 即转换后的二进制数存放到寄存器R3R4中 开始R3 0R4 R0 R3R4 R3R4 10R0 R0 1R3R4 R3R4 R0 R2 1 0 结束YNR2 3分析 由于D3D2D1D0的二进制数可表示成 D3 103 D2 102 D1 101 D0 100 D3 10 D2 10 D1 10 D0 所以算法上可采用循环 高位 10 次低位 的方法 其流程图如图4 21所示 具体程序如下 ORG1000HBCDBIN MOVR2 03 R2为计数器MOVR3 00HMOVA R0 取D3MOVR4 ALOOP MOVA R4 R4 R4 10MOVB 0AHMULABMOVR4 A R4 10的低位字节 R4MOVR1 B R4 10的高位字节 R1暂存MOVA R3 R3 R3 10MOVB 0AHMULABADDA R1 R3 10的低位字节 R1 AMOVR3 A R3 高位字节INCR0 修改指针MOVA R0 取下一个数ADDA R4 R3R4 R0 R3R4MOVR4 AMOVA R3ADDCA 00HMOVR3 ADJNZR2 LOOPRET 例4 19 多字节二进制数变补码子程序 分析 多字节二进制数求补 可先将低位字节取反加1变补 然后将高位字节依次取反再加上低位字节求补后的进位即可 设入口参数为 R0 存放将要取补的二进制数的低位字节指针 R2 字节数出口参数为 R0 取补后数据的高位字节指针具体程序如下 CPLD SETBCLOOP MOVA R0 取数CPLA 取反ADDCA 0 加进位求补MOV R0 A 保存转换结果INCR0DJNZR2 LOOPDECR0RET 4 3 2算术运算程序 例4 20 多字节无符号BCD码加 减法运算程序 分析 实现多字节的加 减法运算 可利用指令ADDC SUBB 从低位字节到高位字节依次相加或相减 由于是BCD码运算 所以应在加法运算后使用十进制调整指令DAA 设子程序入口参数为 R0 R1和R2 即R0为被加数或被减数的低位地址指针 R1为加数或减数的低位地址指针 R2为字节数 出口参数为 R0 即R0为和或差的低位地址指针 具体程序如下 ORG1000HADDBCD CLRCLOOP MOVA R0 取被加数ADDCA R1 与加数相加DAA 十进制调整MOV R0 A 存放结果INCR0 指向被加数的下一个字节INCR1 指向加数的下一个字节DJNZR2 LOOPJNCHERE 判断高字节是否有进位 若没有 则结束MOV R0 1 否则将进位加到结果中HERE RET由于减法运算与加法类似 只要使用SUBB指令代替ADDC指令即可 请读者自行分析完成 例4 21 双字节无符号数乘法 R2R3R7R6R3 R7LHR3 R7R2 R7LR2 R7HR3 R6LR3 R6HR2 R6LR2 R6HR2R3R4R5 分析 双字节二进制无符号数乘法可利用单字节乘法指令来实现 操作时按照以字节为单位的竖式乘法运算表来完成 例如设R2R3为被乘数 R6R7为乘数 乘积为R2R3R4R5 由高到低排列 则竖式运算的算法如图4 22所示 具体程序如下 入口参数 被乘数在R2 R3中 乘数在R6 R7中 出口参数 乘积在R2 R3 R4 R5中 MULD MOVA R3 计算R3 R7MOVB R7MULABMOVR4 B R4 R3 R7HMOVR5 A R5 R3 R7LMOVA R3 计算R3 R6MOVB R6MULABADDA R4 R3 R7H R3 R6L R4MOVR4 ACLRAADDCA BMOVR3 A R3 R6H CY R3MOVA R2 计算R2 R7MOVB R7 MULABADDA R4 R2 R7L R3 R7H R3 R6L R4MOVR4 AMOVA R3ADDCA B R2 R7H R3 R6H CY R3MOVR3 ACLRARLCAXCHA R2 计算R2 R6MOVB R6MULABADDA R3 R2 R7H R3 R6H R2 R6L R3MOVR3 AMOVA R2ADDCA B R2 R6H CY R2MOVR2 ARET 例4 22 双字节无符号数除法 分析 对于多字节除法 不能像多字节乘法一样使用单字节除法指令 通常用的算法是参照手算除法的方法进行 移位相减 首先判断被除数 后为余数 是否大于除数 若成立 则该位商上1 从被除数 余数 中减去除数 否则 商上0 不减除数 然后把被除数的下一位左移到余数后面 再通过比较其大小决定是否与除数相减 重复这个过程知道余数为0或商的位数满足要求为止 通常情况下 若除数和商均为双字节 则被除数为4个字节 若被除数的2个高位字节大于或等于除数则商不能用两个字节表示 发生溢出 因此做除法时应首先判断除数是否为0或发生溢出 若溢出则子程序返回 否则进行除法运算 设有算式 R2R3R4R5 R6R7 商 R4R5 余数 R2R3 则其算法流程图如图4 23所示 其具体程序如下 入口参数 被除数为R2R3R4R5 除数在R6 R7中 出口参数 F0 0时 双字节商在R2 R3中 F0 1时溢出或除数为0 DIVD MOVA R6JNZOVER 除数不为0 转移MOVA R7JZRE1 除数为0 置结束标志返回OVER CLRC 被除数高2个字节 除数 MOVA R3SUBBA R7MOVA R2SUBBA R6JCDIV1 若高2个字节 除数 则转移RE1 SETBF0 否则溢出标志置1返回RETDIV1 MOVB 16 无溢出 作除法DIV2 CLRC 部分商和余数同时左移一位MOVA R5RLCAMOVR5 AMOVA R4RLCAMOVR4 AMOVA R3图4 23 例4 22 程序流程图RLCA MOVR3 AXCHA R2RLCAXCHA R2MOVF0 C 保存溢出位CLRCSUBBA R7 计算 R2R3 R6R7 MOVR1 AMOVA R2SUBBA R6JBF0 DIV3 结果判断JCDIV4DIV3 MOVR2 A 够减 存放新的余数MOVA R1MOVR3 AINCR5 DIV4 DJNZB DIV2 商在R4R5中MOVA R4 将商送到出口参数R2R3中MOVR2 AMOVA R5MOVR3 ACLRF0 F0 0 设置除法完成标志RET 例4 23 单字节整数平方根运算 分析 求平方根的算法主要有牛顿迭代法 直接法等 这里介绍利用等差数列求和公式求平方根的算法 等差数列求和公式 n2 1 3 5 2n 1 1 对于任一正整数N 总可以找到这样的n 使得公式 N n2 2 成立 其中n为N的平方根 为误差 将 1 式代入 2 式 则有 N 1 3 5 2n 1 因此只要从N中减去1 3 5 2n 1 直到不够减为止 则减去的奇数的个数则为N的平方根的整数部分 设子程序入口参数为 R1 出口参数为R2 其具体程序如下 SQRT MOVR2 0CLRCLOOP MOVA R2 RLCA R2 2 AINCA A加1 求奇数MOVR4 AMOVA R1SUBBA R4 R1 奇数MOVR1 AJCDONE 不够减 结束 INCR2 R2 1 R2SJMPLOOPDONE RET 4 3 3延时程序在单片机应用系统中 常常需要用到准确的延时 一般来说 延时的设计主要通过两种途径实现 使用硬件定时器延时或使用软件延时 这里介绍软件延时 所谓软件延时 即通过程序达到延时的目的 具体来说 即设计一循环子程序 子程序的功能为用循环程序将指令重复多次执行一些无用的操作以达到延时的目的 通过修改循环次数 便可获得不同的延时时间 下面将通过具体的例题介绍延时程序的编写方法 注意 编写软件延时程序时 应特别要禁止中断 否则会影响软件延时的精度 例4 24 已知单片机系统的晶振频率为12MHZ 试设计一软件延时程序 延时时间为50ms 程序如下 源程序机器周期 M 指令执行次数 DELAY MOVR1 5011 D1 MOVR2 M1R1 D2 NOP1R1 R2 NOP1R1 R2 DJNZR2 D22R1 R2 DJNZR1 D12R1 RET21 该例题采用双重循环结构来实现延时 程序中内循环次数以M代替 M的实际数值可通过延时时间计算得出 现分析如下 机器周期数是执行一条指令所需的时间 所以延时时间可通过下式计算得出 T总 延时子程序总
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- Unit 4 Eat well Section B 1a~1d 英文版教学设计 -人教版七年级英语下册
- 2025年中级注册安全工程师考试金属非金属矿山安全真题及答案
- 康复知识竞赛题库及答案
- 11.3 多边形及其内角和教学设计初中数学人教版2012八年级上册-人教版2012
- 第7课《中华民族一家亲》 第1课时(教学设计)-部编版道德与法治五年级上册
- 春江花月夜教学设计中职基础课-基础模块 上册-高教版(2023)-(语文)-50
- 江苏省徐州市体育中心消防安全测试题八(含答案)
- 《组合图形的面积》(教学设计)-数学五年级上册北师大版
- 2025-2030中国自动驾驶汽车市场格局与技术突破方向研究报告
- 2025-2030中国网络安全服务市场格局演变与龙头企业竞争力分析报告
- 学校物业委托管理服务合同7篇
- 2025少先队基础知识题库(含答案)
- 人教版九年级物理上-各单元综合测试卷含答案共五套
- 《守望成长法治护航》法制教育主题班会
- 三折页设计课件
- 防诈骗消防安全知识培训课件
- 数据标注课件
- 2025公安辅警招聘知识考试题(含答案)
- 劳动课包包子课件
- 教师教育心理学考试试题及答案
- 山河已无恙+吾辈当自强+课件-2025-2026学年高二上学期用《南京照相馆》和731上一节思政课
评论
0/150
提交评论