版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
会计学1D幻灯片定稿MCS汇编语言程序设计2.汇编语言
是一种符号语言,指令由助记符表示。与机器语言相比,汇编语言具有指令容易记忆、理解、识别和可读性好的优点,但实质上由于汇编语言也是面向机器的语言,是机器语言程序的符号表示,所以用户必须熟悉机器的硬件结构和指令系统,掌握计算机的工作过程才能熟练编程。汇编语言程序通常用于实时控制领域,其所能完成的操作不是一般高级语言所能实现的,而且源程序经汇编生成的可执行文件一般运行效率较高。另外,汇编语言所编写的源程序与经过汇编所产生的机器代码程序之间有明显的一一对应关系,所以汇编语言编写的程序同样也存在通用性差,程序不能移植的缺点。第1页/共86页3.高级语言
高级语言是接近于人的自然语言形式的计算机编程语言的总称,例如C语言、BASIC语言等都是高级语言。和汇编语言相比,高级语言指令简单易学,用户容易掌握,且高级语言程序不依赖于具体的硬件结构和指令系统,程序可移植性好,但是高级语言编写的源程序必须经过编译或解释程序翻译成目标程序,机器才能执行,而生成的目标程序需占用较多的存储单元,执行时间较长,运行效率较低。由于目前有些高级语言不具有直接处理接口和中断技术的功能,因此高级语言一般很少应用于实时控制,但随着语言的发展,这种情况将会有所改变。第2页/共86页4.1.2单片机汇编语言源程序的编辑和汇编
1.汇编语言的指令类型用汇编语言编写的、具有特定功能的指令序列,称为汇编语言源程序。源程序由两种类型的汇编语言语句(即指令)构成。语句是汇编语言的基本组成单位,按性质不同分为两类:指令性语句(机器指令)和指示性语句(伪指令)。(1)机器指令即指令系统中的全部指令,每条指令都有对应的机器代码,是机器真正能够执行的指令。(2)伪指令为汇编程序在汇编过程中提供控制或指示信息,并不直接产生机器代码,属于机器不能执行的指令类型。2.汇编计算机不能直接识别和执行源程序。因此源程序必须经过汇编程序汇编产生机器码目标程序文件,程序才能执行。这种将汇编语言源程序转换成机器语言程序的过程称为汇编。对于初学者来说,应注意汇编语言源程序和汇编程序两个术语的区别,它们的功能示例如图4-1所示。第3页/共86页图4-1汇编过程汇编语言源程序汇编程序目标文件和源程序列表经过汇编后第4页/共86页汇编语言源程序的汇编过程分为手工汇编和机器汇编。所谓手工汇编,即采用人工查指令表的方法将汇编指令翻译成相应机器代码。通常源程序的人工汇编需要两次才能完成,第一次汇编查找每条指令的机器代码,第二次汇编完成地址偏移量的计算。由于手工汇编需要计算和查找,繁琐而且容易出错,而且程序修改时可能会引起指令的地址变化,转移指令的偏移量也随之改变,需要重新计算,所以手工汇编主要应用于设计短小程序或调试程序的场合。所谓机器汇编,即采用汇编程序对源程序进行自动汇编,由于单片机软硬件资源的限制,该过程通常借助于PC系统机实现,象这种借助于一种计算机而为另一种计算机产生目标代码的汇编方式又称为交叉汇编。交叉汇编的原理类似于手工汇编,在汇编程序中通常存入了两张表,即MCS-51单片机的指令代码表和伪指令表。汇编程序通常通过两次扫描完成汇编,第一次扫描查找源程序中每条指令的机器代码,第二次扫描完成地址偏移量的计算,汇编后生成目标文件和列表文件。这是目前应用较广的汇编方法。第5页/共86页纵上,汇编的主要功能为:(1)确定程序中每条汇编语言指令的指令机器码。(2)确定每条指令在程序存储器中的存放地址。(3)提供语法、编辑等方面的错误信息,但不能提供程序的逻辑错误。(4)产生目标执行文件(*.OBJ/*.HEX)和列表文件(*.LST)。第6页/共86页4.1.3MCS-51汇编语言的伪指令所谓“伪”指令,即不是真正的可执行指令。如前所述,伪指令只能在对源程序进行汇编时起控制作用,例如设置程序的起始地址,定义符号,给程序分配一定的存储空间等。常用的伪指令共有8条,下面分别介绍。1.ORG(Origin)——设置起始地址指令用来设定程序或数据存储区的起始地址。指令格式为:ORG16位地址例如,有程序段如下,则表示程序存放的起始地址为1000H。指令地址机器码 源程序
ORG 1000H 1000H782MAIN:MOVR0,#20H1002HE6 MOVA,@R0…若省略ORG指令后的16位地址,则汇编后目标程序的起始地址默认为0000H。ORG指令在一个源程序中可以多次使用以指定不同程序段的起始地址,但是指定的多个地址应从小到大,不能使程序段之间产生重叠。第7页/共86页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第8页/共86页4.EQU(Equate)——等值指令指令功能是把操作数段中的地址或数据赋值给标号。赋值后的标号可在整个源程序中使用。指令格式如下:标号EQU数据或汇编符号注意,与程序中一般意义的标号不同的是,这里的标号后不能加“:”。例如, ADDR EQU1000H;给ADDR赋值1000H DAT1 EQU0AH;给DAT1赋值0AH … MAIN: MOVDPTR,#ADDR;DPTR←1000H MOVA,DAT1;A←(0AH)上例中,ADDR被赋值以后,在程序中作为立即数使用,而DAT1赋值后被当作直接地址使用。需要说明的是,使用EQU指令时必须先赋值后使用。第9页/共86页5.DS(DefineStore)——定义存储区指令指令功能为从指定的单元地址开始,保留一定数量的存储单元,以备使用。指令格式如下:[标号:]DS表达式其中,表达式指定保留的内存单元个数。例如,ORG1000HDS10TAB:DB10H…汇编结果:从1000H地址处开始,保留10个字节单元。(100AH)=10H。6.BIT——位地址符号指令指令功能为将位地址赋值给指定的符号名称,通常用于位符号地址的定义。指令功能如下:字符名称 BIT 位地址 例如,X1BITP1.0汇编结果为将位地址P1.0赋给变量X1,即在程序中便可使用X1代替位地址P1.0。第10页/共86页7.DATA——数据地址赋值指令指令功能为将数据地址或指令代码地址赋值给所指定的标号。通常在程序中用于定义数据地址。指令格式如下:标号:DATA表达式例如,AA:DATA2000H汇编结果为:AA=2000H。注意,DATA与EQU的区别在于:EQU定义的标识符在汇编时不在符号表中登记,因此必须先定义后使用,而DATA定义的标识符汇编时在符号表中登记,所以可以先使用后定义。8.END——汇编语言源程序结束指令指令功能,表示源程序到此结束,END指令以后的指令汇编程序将不予处理。一个程序中只能在末尾有一个END指令。例如,START:……END START第11页/共86页4.2汇编语言程序设计对于简单程序的编写,程序设计者往往能够立即完成软件的构思与编写,而对于比较复杂的程序设计问题,则需要科学合理的程序设计步骤。从软件工程角度来说,开发一个应用程序,一般需要经过以下几个步骤:1.分析问题,建立数学模型如果拿到问题,立即着手编写程序代码往往是很难成功的,通常需要首先分析题目的已知条件,了解系统的硬件配制,明确题目的要求和要实现的功能,然后建立数学模型。2.了解系统的硬件配置和性能指标,确定方案算法所谓算法就是为了解决问题而采取的方法和步骤。在分析问题后,应根据系统的具体硬件配置和性能指标等实际情况确定具体有效且计算机能够执行的方法和步骤。3.用流程图表示出程序算法确定算法后,应用简单直观的方法描述算法,以便为进一步编程做好准备。算法的描述方法很多,其中比较常用的是流程图法。一般流程图的符号如图4-2所示。4.根据流程图编写源程序使用汇编语言编写源程序时,应首先合理规划和分配存储器单元,确定程序和数据的存放区域,了解系统的I/O接口地址,最后按照流程图写出源程序。5.调试运行程序通常情况下,源程序编制好后必须经过上机调试才能运行。调试程序的一个重要功能是修正语法和逻辑错误,直到达到题目的要求为止。第12页/共86页4.2.1顺序程序设计
顺序程序是程序设计中最基本的结构,又称为简单程序。特点为顺序执行每一条指令,直到最后。其执行过程如图4-3所示。S1S2图4-3顺序结构第13页/共86页【例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第14页/共86页【例4-2】已知如图4-5所示,双字节4位压缩BCD码数X存于片内RAM30H、31H单元,Y存于32H、33H单元,编程求Z=X+Y,并将结果Z存入片内RAM单元34H、35H、36H中ADDSUM:MOVA,30H ADDA,32H;低位字节相加
DAA;BCD码修正
MOV34H,A;低位字节存入34H中
MOVA,31H ADDCA,33H;高位字节与低位进位相加
DAA MOV35H,A;高位字节之和存入35H中
MOVA,#0 ADDCA,#0DAA MOV36H,A;高位字节的进位存入36H中
END。从该例可以看出,多字节BCD码相加时,应从低位字节开始相加,每进行一次加法运算进行一次BCD码调整才能得到正确结果。第15页/共86页4.2.2分支程序设计1.分支程序结构的基本形式分支程序是程序设计中应用非常广泛的一种基本结构。比如我们经常遇到需要计算机进行逻辑判断的情况,然后根据判断的结果进行不同的处理。例如,比较两个数的大小,并输出判断结果;根据输入压力的不同,用不同的方法计算发动机功率等这些问题都是顺序结构程序所无法实现的,而是属于分支结构程序设计的范畴。具体来说,如图4-6所示,分支程序结构具有以下三种形式。第16页/共86页条件S1S2YN条件S1条件NS2S1Sn…(a)单分支(b)双分支(c)多分支第17页/共86页2.分支程序的设计方法依据分支结构程序的执行过程,结合MCS-51的指令系统可以得出,分支程序的实现需要通过条件转移指令完成。因此如何设定分支条件便成为分支程序设计的关键。具体来说,其设计要点可归纳如下:(1)设定可供条件转移指令测试的条件。通常,可以作为转移指令判断条件的有标志位状态(如进位位C的状态),累加器A或片内RAM中某位的结果状态等。因此为了提供条件转移指令的测试条件,应在程序的转移指令前设定影响判断条件的标志位状态,或通过逻辑运算、算术运算等指令影响标志位。例如,若要使用JZ指令实现分支,则应在该指令前执行影响累加器A的指令。(2)根据标志位状态选择正确的条件转移指令。例如,要判断进位位,可使用JNC或JC等指令。(3)应在转移的目的地址处设定标号。第18页/共86页【例4-4】求8位有符号数的补码。设8位二进制数存放在片内RAM30H单元内。分析:对于二进制数的补码负数可用取反加1的方法求得,而正数不变。ORG2000HMOVA,30HJNBACC.7,NEXT;为正数,不进行处理CPLA ;负数取反INCA;加1MOV30H,ANEXT:SJMPNEXT;结束可以看出,该程序通过符号位是否为0来判断该数为正数还是负数,若为正数则程序结束,若为负数则变补。显然,这是一个单分支的例子。第19页/共86页【例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,41H SJMPRESULTNEXT:MOVA,41H;取M的值
ADDA,R1;W=P×5+MRESULT:MOV42H,A;存结果
END该例是一个双分支程序,通过比较转移指令CJNEA,#10,L1和条件判断指令JCNEXT实现程序的分支,这种编程方法在分支程序设计中应用非常广泛,对于初学者应仔细分析体会。第20页/共86页【例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 ;取X JZ RESULT ;X=0,Y=0 JB ACC.7,NEG;判符号位,若为1,则X<0 MOVA,#1;X>0,Y=1SJMPRESULTNEG:MOVA,#0FFH;X<0,Y=-1,将-1的补码送ARESULT:MOV21H,A ;保存YSJMP$第21页/共86页Y=0Y=1Y=-1保存Y结束X<0?NY开始取XYNX=0?第22页/共86页3.分支表法实现多向分支程序的设计上例中符号函数程序是一个多向分支程序的例子,直接采用了条件转移指令来实现,但在实际应用时,通常遇到根据某变量取值不同从而转向不同分支的题目,这时经常采用分支表法。常用的分支表主要有三种形式:转移指令表、分支地址表和地址偏移量表。虽然分支表的构成各异,具体的编程方法也略有不同,但实质上都是利用散转指令JMP@A+DPTR来实现的。实现散转的方法主要有两种:(1)累加器A清零,根据DPTR的内容决定转移的目标地址。(2)DPTR的值作为基址不变,根据累加器A的内容决定转移的目标地址。下面通过一个具体的例题,说明使用分支表实现多向分支程序设计的方法。第23页/共86页【例4-7】根据R2的内容,转向相应的分支程序。即:R2=0,转向PROG0R2=1,转向PROG1…R2=n,转向PROGn(1)使用转移指令表。所谓转移指令表即由转移指令组成的分支表,如图4-8所示。JMPTAB:AJMPPROG0JMPTAB:LJMPPROG0AJMPPROG1 LJMPPROG1…
…AJMPPROGn LJMPPROGn
(a)(b)图4-8转移指令表第24页/共86页设转移指令表的标号为JMPTAB,分支数为5,解题思路分析如图4-9所示。DPTR←JMPTABA+DPTR←JMPTAB+R2×3JMP@A+DPTRLJMPPROG0LJMPPROGnLJMPPROG1第25页/共86页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指令实现分支。具体程序如下:第26页/共86页说明,因为转移指令表由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:…第27页/共86页(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所示。第28页/共86页DPTR←BRANCHTABA←R2×2JMP@A+DPTR
分支程序PROG0
分支程序PROGn分支程序PROG1DPTR←应用MOVCA,@A+DPTR取分支地址A←0图4-11分支地址表的使用第29页/共86页根据以上解题思路,例4-7程序编写如下:ORG1000HMAIN:MOVDPTR,#BRANCHTAB;取分支表入口地址
MOVA,R2CLRC ;A←R2×2RLCAJNCNOADDINCDPH;进位加到DPH中NOADD:MOVR3,A;R3←R2×2 MOVCA,@A+DPTR;A←分支地址的高位字节
XCHA,R3;R3←分支地址的高位字节,A←R2×2 INCA;指向下一个存储单元
MOVCA,@A+DPTR;取分支地址的低位字节
MOVDPL,A ;
MOVDPH,R3;
CLRAJMP@A+DPTR;分支地址→PC,转移BRANCHTAB:DWPROG0 ;分支地址表
DWPROG1
… PROG0:…
;分支程序0…PROG5:…
;分支程序5第30页/共86页(3)使用地址偏移量表。所谓地址偏移量表,是指由各分支程序段的入口地址与地址偏移量表的标号差(即地址偏移量)形成的线性表。其中地址偏移量表中每项占一个字节,如图4-12所示。TAB:DBPROG0-TAB;PROG0…PROGn为分支程序入口地址
DBPROG1-TAB
…DBPROGn-TAB图4-12地址偏移量表第31页/共86页DPTR←TABA←R2JMP@A+DPTR
分支程序PROG0
分支程序PROGn分支程序PROG1应用MOVCA,@A+DPTR取分支地址偏移量图4-13地址偏移量表的使用第32页/共86页根据以上解题思路,例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字节)范围内,因此该方法适用于分支较少的情况。第33页/共86页4.2.3循环程序设计
在程序设计中,常常会涉及到重复执行的程序段,这可以通过循环结构来实现,循环结构可使程序更加紧凑。1.循环结构程序的构成与高级语言中循环程序的构成相似,汇编语言的循环程序结构主要包括以下四个部分:(1)循环初始化部分。所谓初始化即设置循环开始时的状态,如清结果单元、设置地址指针、设定寄存器初值、循环次数等。(2)循环体部分。循环体是循环结构的主体,为需要重复执行的程序段。(3)循环控制部分。这一部分主要完成循环条件的设定,循环控制变量的修改以及检测循环条件是否仍然满足,若条件成立则继续循环,否则结束循环。(4)结束部分。该部分主要完成循环结束后的结果处理工作,例如结果的保存、计算等。其执行过程如图4-12所示。第34页/共86页循环初始化执行循环体循环条件是否成立?N开始循环结束,结果处理Y结束修改循环变量循环初始化执行循环体循环条件是否成立?N开始循环结束,结果处理Y结束修改循环变量体第35页/共86页2.简单循环程序
所谓简单循环即一重循环,也就是循环程序中只包含一个循环,不嵌套其它循环的循环程序。另外,设定循环执行的条件非常重要,否则可能形成死循环。编写汇编语言程序时,通常采用两种控制循环的方法。一种是使用计数的方法实现循环,即将循环次数作为循环计数器的初值,当计数器的值加满(称为正计数)或减为0(称为倒计数)时结束循环,否则继续循环,该方法适用于循环次数已知的情况。另一种为通过设定特定条件控制循环,若设定条件满足则执行循环,否则结束循环,例如设定特定的循环结束标志等,该方法适用于循环次数未知的情况。第36页/共86页【例4-8】数据块求和。设内部RAM中有一连续单字节数据块,首地址为BLOCK单元,数据块长度存于LEN单元,若数据累加和也为单字节数据,并存于RESULT单元,编程求数据块之和。分析:累加和的求解应使用循环,且数据长度已知,所以循环次数已知,因此可考虑使用指令DJNZ来控制循环条件,其算法流程图如图4-15所示。A←0,R0←BLOCK,R1←LEN累加求和R1≠0?N开始RESULT←和Y结束R0+1,R1-1第37页/共86页程序如下:ORG1000HLEN:DATA20HRESULT:DATA21HBLOCK:DATA22HMOVA,#0MOVR0,#BLOCKMOVR1,#LENLOOP:ADDA,@R0INCR0DJNZR1,LOOPMOVRESULT,AHERE:SJMPHERE可以看出,这是一个循环次数已知的例题。第38页/共86页【例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第39页/共86页【例4-10】求最小值。设内部RAM中有一无符号数数据块,其首地址为BLOCK,长度存于LEN单元,试求出数据块中的最小值,存入MIN单元。分析:求最小值时,通常采用比较交换的方法。即首先取第一个数作为基准,然后将基准数与第二个数进行比较,若基准数大于第二个数,则两数进行交换;若基准数小于第二个数则不进行交换,总之保证基准数单元中的值为最小值,再取下一个数与基准数进行比较,一直到所有数据比较完为止,则基准数单元中的数则为最小值。依此类推,求最大值也可采用类似方法。其算法流程图如图4-16所示。第40页/共86页A←0,R1←BLOCK,R2←LEN清CYA←A-(R1)R2≠0?N开始MIN←AY结束R1+1,R2-1CY=1?
A←(R1)NA←A+(R1)Y第41页/共86页具体程序编写如下: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第42页/共86页【例4-11】设片内RAM中有一无符号数数据块,其首地址为BLOCK,长度未知,但数据结束标志存于LEN单元,求数据块的最小值并存于MIN单元中。
ORG1000HMIN:DATA20HLEN:DATA21HBLOCK:DATA22HMAIN:MOV R1,#BLOCK ;数表首地址
MOVB,@R1 ;取第一个数作为基准NEXT:INC R1 ;修改指针
MOVA,@R1CJNEA,LEN,NEXT1;是否为数表结尾?
SJMPDONE ;循环结束NEXT1:CJNEA,B,NEXT2;比较NEXT2:JNC NEXT;A>B,转NEXT继续取数
MOV B,A ;保存较小值
SJMPNEXTDONE:SJMPDONE可以看出,这是一个循环次数未知的例题,通过设定特征值控制循环结束条件。第43页/共86页3.多重循环多重循环又称之为循环的嵌套,即在一个循环结构的循环体内,又包含另一个完整的循环结构。在实际应用中,有时一重循环并不能解决问题,所以循环的嵌套应用还是比较广泛的。在嵌套循环的使用中,被嵌套的循环可以不止一个,并且可以嵌套多层,但不论是哪种情况,内层循环和外层循环都必须是一个完整的结构,不允许有相互交叉的情况出现。即如出现图4-17(a)所示的情况则是非法的嵌套,而图4-17(b)所示为正确的嵌套。内层循环外层循环内层循环外层循环图4-17(a)非法嵌套示意图图4-17(b)正确嵌套示意图第44页/共86页【例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所示。第45页/共86页开始外循环次数→R4内循环次数→R3(R0)<(R0+1)(R0)←→(R0+1)R0←R0+1R3←R3—1=0?R4←R4—1=0?循环结束YNNNYY第46页/共86页ORG1000HBLOCK:DATA20HSORT:MOV A,#N-1 ;N个数据排序
MOV R4,A ;外循环次数LOOP1:MOV A,R4 MOV R3,A ;内循环次数
MOV R0,#BLOCK ;设数据指针LOOP2:MOV A,@R0 MOV B,A;B←(R0)
INC R0 MOV A,@R0;A←(R0+1)
CJNEA,B,L1;两数比较L1:JNC NEXT ;若B≤A,即(R0)<(R0+1)不交换
DEC R0 ;否则交换数据
XCH A,@R0 INC R0;修改数据指针
MOV @R0,ANEXT:DJNZR3,LOOP2 ;内循环
DJNZR4,LOOP1 ;外循环HERE:SJMPHERE在数据采集系统中,若被采集的信号变化较慢,经常采用中值滤波的方法去掉由于偶然因素造成的干扰误差。中值滤波时,首先将采样数据排序,再取中间值作为本次采样的终值。第47页/共86页4.2.4子程序设计第三章指令系统中已经介绍过子程序的概念与相关指令。子程序是一种重要的程序结构,主要由需要反复执行的操作或程序段构成,以供主程序调用。另外,子程序也可嵌套,即子程序调用子程序,只要堆栈深度足够,子程序可嵌套多层。主程序与子程序以及子程序嵌套调用的关系如图4-19所示。第48页/共86页与一般程序的编写方法相比,子程序具有以下几个特点。(1)子程序应命名。子程序入口即子程序的第一条指令应加标号作为子程序名,以便主程序调用。(2)子程序中应注意堆栈的使用,以保护和恢复现场。在有些情况下,若子程序需要改变主程序中某些寄存器或存储单元的结果,而这些结果又不能被修改或在子程序调用之后仍然需要则应在子程序中首先将这些内容使用进栈指令保护,在子程序返回之前使用出栈指令恢复现场。(3)子程序的结尾必须为子程序返回指令RET,并保证堆栈栈顶为调用程序的返回地址。(4)子程序嵌套时应考虑堆栈的深度。(5)能够正确传送参数。参数分为入口与出口参数。所谓入口参数,即调用子程序之前,需要传给子程序的参数。所谓出口参数,即子程序返回时应送回调用程序的结果参数。应根据具体情况选择不同的参数传递方式,通常使用寄存器、存储器或堆栈的方式传送参数。(6)子程序应具有一定的功能和通用性。对于操作数应尽量使用以地址或寄存器形式给出,一般不针对具体的数据编写子程序。(7)为了便于调用,子程序应提供足够的信息。如:子程序名、子程序功能、入口参数和出口参数、子程序占用的硬件资源、子程序中调用的其他子程序名。第49页/共86页【例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$第50页/共86页【例4-14】设计子程序将单字节数据对半拆分,变成2个字节存放。分析:子程序入口参数:累加器A,即将要拆分的数据送入A累加器中。出口参数:R1,即将拆分后的数据存入(R1)指向的两个相邻地址单元。FEN1:MOV@R1,#0;将(R1)指向的地址单元清零
XCHDA,@R1 ;低半字节存入指定单元
INCR1 ;修改指针,指向下一个地址单元
MOV@R1,#0;清零
SWAPA;高低半字节交换
XCHDA,@R1 ;保存高半字节
RET第51页/共86页【例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。第52页/共86页另外,根据ASCII码和十六进制数的对应关系,将1位十六进制数转换为ASCII码的程序可编写如下:HEXASC:CJNEA,#0AH,L1 L1:JNC L2 ADD A,#30H SJMP HERE L2:ADD A,#37H HERE: RET
请读者自行分析该子程序每条语句的功能及入口和出口参数。第53页/共86页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。其具体程序如下:第54页/共86页BAI: DATA30HSHIGE: DATA31HBINBCD:MOVB,#100 DIVAB;除以100,A←商,B←余数
MOVBAI,A;BAI←商
MOVA,#10;
XCHA,B;B中的余数与A中除数10互换
DIVAB;余数除以10,A←商,B←余数
SWAPA;十位数交换到A的高半字节
ORLA,B;与B中的个位组合成一个字节数据
MOVSHIGE,A;十位、个位送入单元SHIGE RET该子程序入口参数:累加器A出口参数:地址单元BAI和地址单元GESHI第55页/共86页【例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所示。第56页/共86页开始R3←二进制位数二进制数左移1位BCD×2+进位,调整R3-1=0?结束YN存储单元清零字节数R2-1=0?YN第57页/共86页具体程序如下: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;二进制数低位字节地址→R0MOVA,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第58页/共86页【例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所示。第59页/共86页具体程序如下: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开始R3←0R4←(R0)R3R4←R3R4×10R0←R0+1R3R4←R3R4+(R0)R2-1=0?结束YNR2←3第60页/共86页【例4-19】多字节二进制数变补码子程序。分析:多字节二进制数求补,可先将低位字节取反加1变补,然后将高位字节依次取反再加上低位字节求补后的进位即可。设入口参数为:R0,存放将要取补的二进制数的低位字节指针,R2:字节数出口参数为:R0,取补后数据的高位字节指针具体程序如下:
CPLD:SETBCLOOP:MOVA,@R0;取数
CPLA;取反
ADDCA,#0;加进位求补
MOV@R0,A;保存转换结果
INCR0 DJNZR2,LOOP DECR0 RET第61页/共86页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指令即可,请读者自行分析完成。第62页/共86页【例4-21】双字节无符号数乘法。R2R3R7R6R3×R7LHR3×R7R2×R7LR2×R7HR3×R6LR3×R6HR2×R6LR2×R6HR2R3R4R5+×分析:双字节二进制无符号数乘法可利用单字节乘法指令来实现,操作时按照以字节为单位的竖式乘法运算表来完成。例如设R2R3为被乘数,R6R7为乘数,乘积为R2R3R4R5,由高到低排列,则竖式运算的算法如图4-22所示R2R3R7R6R3×R7LHR3×R7R2×R7LR2×R7HR3×R6LR3×R6HR2×R6LR2×R6HR2R3R4R5+×第63页/共86页具体程序如下:入口参数:被乘数在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,R7MULABADDA,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第64页/共86页【例4-22】双字节无符号数除法。分析:对于多字节除法,不能像多字节乘法一样使用单字节除法指令,通常用的算法是参照手算除法的方法进行“移位相减”:首先判断被除数(后为余数)是否大于除数,若成立,则该位商上1,从被除数(余数)中减去除数;否则,商上0,不减除数。然后把被除数的下一位左移到余数后面,再通过比较其大小决定是否与除数相减。重复这个过程知道余数为0或商的位数满足要求为止。通常情况下,若除数和商均为双字节,则被除数为4个字节;若被除数的2个高位字节大于或等于除数则商不能用两个字节表示,发生溢出。因此做除法时应首先判断除数是否为0或发生溢出,若溢出则子程序返回,否则进行除法运算。设有算式:R2R3R4R5÷R6R7,商=R4R5,余数=R2R3,则其算法流程图如图4-23所示。第65页/共86页开始B←循环次数R2R3R4R5左移1位商上1结束YNB-1=0?YN除数=0?R2R3>R6R7?被除数-除数够减?F0←0NYYNFO←1第66页/共86页其具体程序如下:入口参数:被除数为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】程序流程图RLCAMOVR3,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第67页/共86页【例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,#0 CLRCLOOP:MOVA,R2;RLCA;R2×2→AINCA;A加1,求奇数MOVR4,AMOVA,R1SUBBA,R4;R1-奇数MOVR1,AJCDONE;不够减,结束INCR2
;R2+1→R2SJMPLOOPDONE:RET第68页/共86页4.3.3延时程序在单片机应用系统中,常常需要用到准确的延时,一般来说,延时的设计主要通过两种途径实现:使用硬件定时器延时或使用软件延时。这里介绍软件延时。所谓软件延时,即通过程序达到延时的目的。具体来说,即设计一循环子程序,子程序的功能为用循环程序将指令重复多次执行一些无用的操作以达到延时的目的,通过修改循环次数,便可获得不同的延时时间。下面将通过具体的例题介绍延时程序的编写方法。注意:编写软件延时程序时,应特别要禁止中断,否则会影响软件延时的精度。【例4-24】已知单片机系统的晶振频率为12MHZ,试设计一软件延时程序,延时时间为50ms。程序如下:源程序 机器周期(M)指令执行次数DELAY:MOVR1,#50 11 D1:MOVR2,#M 1R1D2:NOP1R1×R2NOP1R1×R2DJNZR2,D2 2
R1×R2DJNZR1,D1 2R1
RET 21外循环内循环第69页/共86页该例题采用双重循环结构来实现延时。程序中内循环次数以M代替,M的实际数值可通过延时时间计算得出,现分析如下:机器周期数是执行一条指令所需的时间,所以延时时间可通过下式计算得出:T总=延时子程序总机器周期数×机器周期。由于该单片机晶振频率fosc为12MHZ,因此其机器周期tj为:其中,内循环机器周期数=1+1+2,假定内循环实现延时1ms,则应有下式成立:1ms=内循环机器周期数×机器周期×内循环次数,即1×103=(1+1+2)×1×M,由此得:M=
将M代入子程序中,可计算得出总的机器周期数为:
((1+1+2)×250+2+1)×50+1+2=50153①②③④其中,①为内循环机器周期数②为MOVR2,#M和DJNZR1,D1两条指令的机器周期数③为外循环次数④为MOVR1,#50和RET两条指令的机器周期数所以延时时间T总=50153×1μs=50.153ms第70页/共86页可以看出,决定延时时间的因素主要有两个:即循环次数与晶振频率。在晶振频率一定的情况下,可以通过增大循环次数和使用多重循环的方法增长延时时间。此外,还应注意将循环以外指令所花费的时间计算在内,否则将影响精度。思考:设有延时子程序如下,若单片机系统的晶振频率为6MHZ,请读者分析它的延时时间。DELAY:MOVR6,#100 D1:MOVR7,#100 D2:NOP
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2021年主管护师(社区护理)资格考试统考题库及答案解析
- 2021年主管护师(社区护理)资格考试统考题库(全真题库)
- 2021年中西医结合执业医师考试题库及答案解析
- 2021年中考数学模拟试题分类汇编之二次函数综合压轴(解析版)
- 2024-2025学年新教材高中政治第一课社会主义从空想到科学从理论到实践的发展2科学社会主义的理论与实践教案部编版必修1
- 川省规划设计院聘用合同草案
- 2024年山西客运从业资格证的考试题目及答案解析
- 《女性生理课件》课件
- 六年级下期家长会
- 重庆人文科技学院《证券投资与管理》2022-2023学年第一学期期末试卷
- 6人小品《没有学习的人不伤心》台词完整版
- 幼儿园小班读书计划三篇
- NUDD新独难异失效模式预防检查表
- 口袋妖怪(黑白)1-649全PM资料Excel版
- 钢琴曲summer 菊次郎的夏天 五线谱
- 精品资料(2021-2022年收藏的)水泥厂煤粉制备系统发生火灾爆炸的条件
- 常用焊条合格证
- 《个人防护用品PPE》ppt课件
- 苏州商业市场市调简析报告
- 论现代企业人力资源管理中激励机制的应用以腾讯公司为例
- CRRT治疗剂量的计算
评论
0/150
提交评论