440 likes | 590 Views
第四章 汇编语言程序设计. 4.1 汇编语言程序设计概述. 所谓程序设计,就是按照给定的任务要求,编写出完整的计算机程序。要完成同样的任务,使用的方法或程序并不是唯一的。因此,程序设计的质量将直接影响到计算机系统的工作效率、运行可靠性。 前面我们学过了汇编语言形式的指令系统,本章重点介绍汇编语言程序结构以及如何利用汇编语言指令进行程序设计的方法。. 4.1.1 汇编语言程序设计步骤 使用汇编语言设计一个程序大致上可分为以下几个步骤。 (1) 分析题意,明确要求。 (2) 确定算法。 (3) 画程序流程图,用图解来描述和说明解题步骤。
E N D
4.1 汇编语言程序设计概述 所谓程序设计,就是按照给定的任务要求,编写出完整的计算机程序。要完成同样的任务,使用的方法或程序并不是唯一的。因此,程序设计的质量将直接影响到计算机系统的工作效率、运行可靠性。 前面我们学过了汇编语言形式的指令系统,本章重点介绍汇编语言程序结构以及如何利用汇编语言指令进行程序设计的方法。
4.1.1 汇编语言程序设计步骤 使用汇编语言设计一个程序大致上可分为以下几个步骤。 (1) 分析题意,明确要求。 (2) 确定算法。 (3) 画程序流程图,用图解来描述和说明解题步骤。 图4.1 常用的流程图符号 (4) 分配内存工作单元,确定程序与数据区的存放地址。 (5) 编写源程序 (6) 程序优化。 (7)上机调试、修改和最后确定源程序。
4.2.2 伪指令语句 伪指令并不是真正的指令,也不产生相应的机器码,它们只是在计算机将汇编语言转换为机器码时,指导汇编过程,告诉汇编程序如何汇编。下面介绍一些MCS-51汇编程序常用的伪指令。 (1)汇编起始伪指令ORG 格式:[标号:] ORG 16位地址 功能:规定程序块或数据块存放的起始地址。如: ORG 8000H START: MOV A ,#30H …… 该指令规定第一条指令从地址8000H单元开始存放,即标号START的值为8000H。
(2)汇编结束伪指令END 格式:[标号:] END [表达式] 功能:结束汇编。 例如: ORG 2000H START: MOV A ,# 00H …… END (END START) 表示标号START开始的程序段结束。 (3)等值指令EQU 格式:字符名称 EQU 项 例如,TEST EQU R0 MOV A,TEST
(4)定义字节指令DB 格式:[标号:] DB 8位二进制数表 DB命令是从指定的地址单元开始,定义若干个8位内存单元的内容。例如, ORG 1000H TAB; DB 23H,73, “6”, “B” TABl: DB 110B 以上伪指令经汇编以后,将对从1000H开始的若干内存单元赋值: (1000H)=23H (1001H)=49H (1002H)=36H (1003H)=42H (1004H)=06H 其中36H和42H分别是字符6和B的ASCII码,其余的十进制数(73)和二进制数(110B)也都换算为十六进制数了。
(5)定义字命令 DW 格式:[标号:] DW 16位二进制数表 例如, ORG 1000H TAB: DW 1234H , 0ABH , 10 汇编后: (1000H)=12H (1001H ) = 34H (1002H ) = 00H ( 1003H ) = ABH (1004H ) =00H (1005H) =0AH DB、DW伪指令都只对程序存储器起作用,不能用来对数据存储器的内容进行赋值或进行其它初始化的工作。
ORG 0000H CLR C MOV R0 ,# 60H ;设R0为数据指针 MOV A ,@R0 ;取X1 1NC R0 ADDC A ,@R0 ;X1+X2 1NC R0 MOV @R0,A ;保存结果 END 4.2 顺序程序设计 顺序结构程序是一种最简单、最基本的程序(也称为简单程序),它是一种无分支的直线形程序,按照程序编写的顺序依次执行。 【例4-1】 两个8位无符号数相加,和仍为8位。 假设两个无符号数X1, X2分别存放于内部RAM60H、61H单元中,求其和并将和送入62H单元。 程序如下:
ORG 0000H CLR C ;将C清零 MOV R0 ,#31H ;送被加数首址 MOV R1 ,#41H ;送加数首址 MOV A ,@R0 ;取被加数低字节 ADD A ,@R1 ;两个低字节相加 MOV @R0 ,A ;低字节和存人被加数低字节 DEC R0 ;修改指针,指向被加数高字节 DEC R1 ;修改指针,指向加数高字节 MOV A,@R0 ;取被加数高字节 ADDC A,@R1 ;高字节相加 MOV @R0 , A ;存结果 END 【例4-2】两个无符号双字节数相加。设被加数存放在内部存储器30H(高位字节)、31H(低位字节)单元,加数存放在内部存储器40H(高位字节)、41H(低位字节)单元,和存入30H(高位字节)、31H(低位字节)单元。 程序如下:
ORG 0200H MOV A ,R0 ;低8位送A CPL A ;取反 ADD A ,#01H ;加l MOV R2 ,A ;存结果 MOV A ,R1 ;高8位送A CPL A ;取反 ADDC A ,#00H ;加进位 MOV R3 ,A ;存结果(R1R0---R3R2) END 【例4-3】编写16位二进制数求补程序 二进制数的求补可归结为“求反加1”的过程,求反可用CPL指令实现;加1时应注意,加1只能加在低8位的最低位上。因为现在是16位数,有两个字节,因此要考虑进位问题,即低8位取反加1,高8位取反后应加上低8位加1时可能产生的进位,还要注意这里的加1不能用INC指令,因为INC指令不影响CY标志。 程序如下:
ORG 1000H MOV A ,20H ;取数送A MOV B ,# 64H ;除数100送B中 DIV AB ;商(百位数BCD码)在A中,余数在B中 MOV 22H ,A ;百位数送22H MOV A , B ;余数送A做被除数 MOV B , #0AH ;除数10送B中 DIV AB ;十位数BCD码在A中,个位数在B中 SWAP A ;十位数BCD码移至高4位 ORL A , B ;并入个位数的BCD码 MOV 21H , A ;十位、个位BCD码存人21H END 【例4-4】编程将20H单元中的8位无符号二进制数转换成3位BCD码,并存放在22H(百位)和21H(10位,个位)两个单元中。 程序如下:
查表 [例4-5]一变量放在内部RAM 的20H,取值为00H-05H。编写程序,求该变量的平方值,将结果放片内21H ORG 1000H START:MOV DPTR, #2000H; or MOV DPTR, #TABLE MOV A, 20H MOVC A, @A+DPTR MOV 21H, A SJMP $ ORG 2000H TABLE: DB 00, 01, 04, 09, 16, 25 END
4.3 分支程序设计 图4.2 分支程序结构 图4.2(a) 结构使用条件转移指令来实现分支,当给出的条件成立时,执行程序段A,否则执行程序段B。 图4.2 (b) 结构使用散转指令JMP来实现多分支转移,它首先将分支程序按序号的值来实现分支转移。
【例4-6】设补码X放在内部RAM30H单元中,函数Y与X有如下的关系式: 试编写程序,根据X的值求出Y,并放回原单元。 解 取出X后先做取值范围的判断,用累加器A状态转移指令判断X是否为0,用位状态转移指令判断X是大于0还是小于0。程序流程图如图4.3所示。 程序如下:
MOV A,30H JZ ZER0 JNB ACC.7,PLUS ADD A,#5 MOV 30H,A PLUS: SJMP $ ZERO: MOV 30H,#20H SJMP $ END
【例4-7】内部RAM40H和41H单元中各有一无符号数,比较其大小,将大数存放于内部RAM60H单元,小数存放于内部RAM61H单元,如两数相等,则分别送往这2个单元。【例4-7】内部RAM40H和41H单元中各有一无符号数,比较其大小,将大数存放于内部RAM60H单元,小数存放于内部RAM61H单元,如两数相等,则分别送往这2个单元。 解 用比较不等转移指令CJNE比较力两个无符号书,先确定它们是否相等,若不相等时再根据借位标志确定这两个无符号书的大小。程序框图如图4.4所示。 程序如下:
MOV A, 40H MOV 61H, 41H CJNE A, 41H, LOOP AJMP AGEQ LOOP: JNC AGEQ ;A≥(41H)则无借位 XCH A, 61H ;A<(41H)有借位;A与(61H)交换 AGEQ: MOV 60H, A SJMP $ END
CJNE A,55H,LOOP1 ;Ta≠T55,转向LOOPl AJMP FH ;Ta=T55,返回 LOOPl: JNC JW ;若(CY)=0,表明Ta>T55,转降温处理程序 CJNE A,54H,LOOP2 ;Ta≠T54,转向LOOP2 AJMP FH ;Ta=54,返回 LOOP2: JC SW ;若(CY)=1,表明Ta<T54,转升温处理程序 FH: RET ;T55≥Ta≥T54,返回主程序 【例4-8】某温度控制系统,采集的温度值(Ta)放在累加器A中。此外,在内部RAM54H单元存放控制温度下限(T54),在55H单元存放控制温度上限(T55)。若Ta >T55,程序转向JW(降温处理程序);若Ta<T54,则程序转向SW(升温处理程序);T55≥Ta≥T54,则程序转向FH(返回主程序)。 程序如下:
MOV A ,R3 MOV DPTR ,#BRTAB MOVC A ,@A+DPTR JMP @A+DPTR BRTAB AJMP BR0 AJMP BR1 AJMP BR2 AJMP BR3 BR0: SETB P1.0 SJMP BRK BR1: SETB P1.1 SJMP BRK BR2: SETB P1.2 SJMP BRK BR3: SETB P1.3 BRK: SJMP BRK 【例4-9】N路分支程序,N≤8。要求程序根据其运行中所产生的寄存器R3的值,来决定如何进行分支。
4.4 循环程序设计 循环程序一般由4部分组成。 (1)置循环初值。 (2)循环体。 (3)循环修改。 (4)循环控制。 图4.7(a)结构是“先执行后判断”,适用于循环次数已知的情况。 图4.7(b)结构是“先判断后执行”,适用于循环次数未知的情况。
ORG OOOOH 二、程序清单 START: MOV A,#01H ;使L1灯亮,其它不亮 LOOP: MOV P1,A ;从P1口输出到发光二极管 MOV R1,#10H ;延时1秒,根据机器周期算 DEL1: MOV R2,#200 DEL2: MOV R3,#126 DEL3: DJNZ R3,DEL3 DJNZ R2,DEL2 DJNZ R1,DEL1 RL A ;左移一位,下一个发光二极管亮 AJMP LOOP ;循环 END
【例4-10】多个单字节数求知。 已知有10个单字节数,依次存放在内部RAM 40H单元开始的数据存储区中,求和并将结果存人寄存器R2、R3中(高位存R2,低位存R3)。 本题中,要重复进行加法运算,因此采用循环结构程序。循环次数就是数据块字节数,这是已知的。在置初值时,将数据块长度置人寄存器R5;将数据块首地址送人寄存器R0,即以R0作为数据块的地址指针,采用间接寻址方式:每做一次加法之后,修改地址指针,以便取出下一个数来相加,并且使计数器R5减l。到R5减为0时,求和结束。程序流程图如图4.8所示。
ORG 2000H SUM: MOV R0,#40H ;设地址指针 MOV R5,#0AH ;计数器初值送R5 SUM: MOV A,#00H MOV R2,A LP: ADD A,@R0 JNC LP1 INC R2 ;若有进位,和的高八位+1 LP1: INC R0 ;地址指针+1 DJNZ R5,LP ;判循环结束条件 MOV R3,A ;存和的低八位 END
【例4-11】从内存BLOCK单元开始有一个无符号数的数据块,其长度为LEN,试求出其最大值,并存入MAX单元。 这是一个搜索问题。这里采用依次进行比较和取代的方法来寻找最大值。具体做法是:先取出第一个数作为基准,和第二个数比较,若比较结果基准数大,不作变动;若比较结果基准数小,则用大数来代替原基准数,然后再和下一个数作比较。到比较结束时,基准数就是所求的最大值。 为了进行两数的比较,采用两数相减以后判断CY的值来确定哪个数大,这比用CJNE指令更简单。比较时将基准数放在累加器A中。若A中先放零,比较次数等于LEN;若A中先放人第一个数,则比较次数等于LEN-1。采用R2作为计数器,R1作为地址指针。程序流程如图4.9所示。
ORG 2000H COMP: CLR A MOV R2, #LEN MOV R1, #BLOCK MOV R3, A LOOP: CLR C MOV A, R3 SUBB A, @R1 JNC NEXT ; A> ((R1)) MOV A, @R1 : A< ((R1)) MOV R3, A NEXT: INC R1 DJNZ R2 ,LOOP MOV A, R3 MOV MAX, A
【例4-12】假设从内存RAM的50H单元,连续存放一串字符,以回车符(其ASCII码为0DH)作为结束标志,要求测出该字符串的长度。测试方法可采用将该字符串的每一个字符与回车符依次相比,若不相等,则将统计字符串长度的计数器加l,继续比较;若比较相等,则表示该字符串结束,这时计数器中的值就是字节符串的长度。【例4-12】假设从内存RAM的50H单元,连续存放一串字符,以回车符(其ASCII码为0DH)作为结束标志,要求测出该字符串的长度。测试方法可采用将该字符串的每一个字符与回车符依次相比,若不相等,则将统计字符串长度的计数器加l,继续比较;若比较相等,则表示该字符串结束,这时计数器中的值就是字节符串的长度。 程序如下: ORG 8000H COUNT: MOV R2,# 0FFH MOV R0,# 4FH LOOP: INC R0 ;50H begin INC R2 ;0 begin CJNE @ R0, #0DH, LOOP SJMP $
【例4-13】编制用软件方法延时1S的程序 软件延时时间与执行指令的时间有关。如果使用6MHz晶振,一个机器周期为2µs(=12 /6M)。计算出执行每一条指令以及一个循环所需要的时间,根据要求的延时时间确定循环次数,如果单循环时间不够长,可以采用多重循环。
程序如下: MOV R5, #05H ;1 机器周期 DELY0: MOV R6, #0C8H ;1 DELY1: MOV R7, #0F8H ;1 NOP ;1 DELY2: DJNZ R7, DELY2 ;2…..x 248=496 DJNZ R6, DELY1 ; DJNZ R5, DELY0 这是一个三重循环程序。前4条指令的机器周期数为1,后3条指令的机器周期数为2。执行内循环所用的机器周期数为248×2=496,执行中间循环所用的机器周期数(496+4)×200=100000;执行外循环所用的机器周期数为(100000+3)×5=500015,再加上1(执行第一条指令)就是执行整段程序所用的机器周期数。因此这段程序的延时时间位(500015+1)×2µs=1.000032s。
【例4-14】编写无符号数排序程序。 假设在片内RAM中,起始地址为40H的10个单元中存放有10个无符号数。试进行升序排序。 解 数据排序常用方法是冒泡排序法。这种方法的过程类似水中气泡上浮,故称冒泡法。执行时从前向后进行相邻数的比较,如数据的大小次序与要求的的顺序不符就将这两个数互换,否则不互换。对于升序排序通过这种相邻数的互换,使小数向前移动,大数向后移动;从前向后进行一次冒泡(相邻数的互换),就会把最大的数换到最后;再进行一次冒泡就会把次大的数排在倒数第二的位置。依此类推,完成由小到大的排序。 编程中选用R7做比较次数计数器,初始值为09H,位地址00H作为冒泡过程中是否有数据互换的标志位,若(00H) =0,表明无互换发生,已排序完毕。(00H) =1,表明有互换发生。流程图如图4-11所示。
ORG 0400H START: MOV R0,#40H ;数据区首址送R0 MOV R7,#09H ;各次冒泡比较次数送R7 CLR 00H ;互换标志位清零 LOOP: MOV A,@R0 ;取前数送A中 MOV 2BH,A ;暂存到2BH单元中 INC R0 ;修改地址指针 MOV 2AH,@R0 ;取后数暂存到2AH单元中 CLR C ;清CY SUBB A,@R0 ;前数减后数 JC NEXT ;前数小于后数,则转(不互换) MOV @R0,2BH ;前数大于后数,两数交换 DEC R0 MOV @R0,2AH INC R0 ;地址加1,准备下一次比较 SETB 00H ;置互换标志 NEXT: DJNZ R7,LOOP ;未比较完,进行下一次比较 JB 00H,START ;有交换,表示未排完序,进行下一轮冒泡 END ;无交换,表示已排好序,结束
4.5 子程序设计 在汇编语言源程序中使用子程序时,一般要注意两个问题: • 现场保护 • 参数传递
A. 在主程序中保护现场: PUSH ACC PUSH PSW PUSH B PUSH R0 LCALL 。。。。 POP R0 POP B POP PSW POP ACC RET
B、在子程序中保护 SUB1: PUSH PSW PUSH ACC PUSH B MOV PSW, #10H POP B POP ACC POP PSW RET
参数传递1---利用累加器和寄存器 • 编写程序实现c=a^2+b^2, a, b, c 存内部RAM 30H-32H. START: MOV A, 30H ACALL SQR MOV R1, A MOV A, 31H ACALL SQR ADD A, R1 MOV 32H, A SJMP $ SQR: MOV DPTR, #TAB MOVC A, @A+DPTR RET TAB: DB 0, 1, 4, 9, ………81
参数传递1---利用存储器传 递 利用存储器传 递: 在要传递的数据比较多时,可以将数据事先写入到程序存储器或在主程序中写入到数据存储器,进一步在子程序中读出 参数传递1---利用堆栈 利用堆栈传 递: ,可以将数据主程序中压入到堆栈,进一步在子程序中弹出。也可以在子程序中压入,再在返回后弹出。
MAIN: MOV SP,#55H MOV R1,#41H ;R1为存结果指针 MOV A,40H ;取要转换的数据 SWAP A ;先转换高位字节 PUSH ACC ;压栈 ACALL HEASC ;调用低半字节转换成 ASCII码程序 【例4-16】将内部数据存储器某一单元中的一个字节的十六进制数转换成两位ASCII码,结果存放在内部数据存储器的两个连续单元中。 假设一个字节的十六进制数在内部数据存储器40H单元,结果存于41H、42H单元中,用堆栈进行参数传递。
POP ACC ; 要转换的数据出栈 MOV @ R1 , A ;存高半字节转换结果 INC R1 PUSH 40H ACALL HEASC POP ACC MOV @ R1, A ;存低半字节转换结果 END HEASC: MOV R0, SP DEC R0 DEC R0 XCH A, @R0 ;取被转换数据 AND A, # 0FH ;保留低半字节 ADD A, #2 ;修改A ,下条到数据表差2字节 MOVC A, @A+PC ;查表 XCH A, @R0 ;结果送回堆栈 RET TAB: DB 30H,31H,32H,… ;ACALL 时 , SP=SP+2, 分别放入口地址PC 15-8, PC 7-0, 若LCALL, SP=SP+3
【例4-17】求两个无符号数据块中的最大值。数据块的首地址分别为60H和70H,每个数据块的第一个字节都存放数据块的长度,结果存人5FH单元。 解 本例可采用分别求出两个数据块的最大值,然后比较其大小的方法,求最大值的过程可采用子程序。 子程序名称:QMAX。 子程序入口条件:R1中存有数据块首地址。出口 条件:最大值在A中, 下面分别编写主程序和子程序。
ORG 2000H MOV SP,#2FH ;设堆栈指针 MOV R1,#60H ;取第一数据块首地址送R1中 ACALL QMAX ;第一次调用求最大值子程序 MOV 40H,A ;第一个数据块的最大值暂存40H MOV R1,#70H ;取第二数据块首地址送R1中 ACALL QMAX ;第二次调用求最大值子程序 CJNE A,40H,NEXT ;两个最大值进行比较 NEXT: JNC LP ;A大,则转LP MOV A,40H ;A小,则把40H中内容送人A LP: MOV 5FH,A SJMP $ 主程序:
ORG 2200H QMAX: MOV A,@R1 ;取数据块长度 MOV R2,A ;R2做计数器 CLR A ;A清零,准备做比较 LP1: INC R1 ;指向下一个数据地址 CLR C ;0+cY,准备做减法 SUBB A,@R1 ;用减法做比较 JNC LP3 ;若A大,则转LP3 MOV A,@R1 ;A小,则将大数送A中 SJMP LP4 ;五条件转LP4 LP3: ADD A,@R1 ;恢复A中值 LP4: DJNZ R2,LP1 ;计数器减1,不为零,转继续比较 RET ;比较完,子程序返回 子程序:
作业1 • 若振荡频率为12MHz, 分析下列时延程序的时延时间 DEL0: MOV R7, #200 DEL1:MOV R6, #123 NOP DEL2:DJNZ R6, DEL2 DJNZ R7, DEL1 RET
作业2 10个无符号数依次放在BLOCK1开始的10个内存字节单元。 1)编成寻找最小值,放内存MIN单元; 2)编程将该数据块传输到BLOCK2开始的内存区。