第 4 章 汇编语言程序设计

56
第4第 第第第第第第第第 4.1 第第第 4.1.1 第第第第第第第第第 4.1.2 MCS 51 第第第第第第 4.2 第第第第第第第第 4.2.1 第第第第 4.2.2 第第第第 4.2.3 第第第第 4.2.4 第第第第第第第第第第第

description

第 4 章 汇编语言程序设计. 4.1 伪指令 4.1.1 为什么要使用伪指令 4.1.2 MCS - 51 单片机的伪指令 4.2 汇编语言程序设计 4.2.1 顺序结构 4.2.2 分支程序 4.2.3 循环结构 4.2.4 子程序设计和子程序调用. 4.1.1 为什么要使用伪指令. - PowerPoint PPT Presentation

Transcript of 第 4 章 汇编语言程序设计

Page 1: 第 4 章  汇编语言程序设计

第 4 章 汇编语言程序设计4.1 伪指令 4.1.1 为什么要使用伪指令 4.1.2 MCS - 51 单片机的伪指令4.2 汇编语言程序设计 4.2.1 顺序结构 4.2.2 分支程序 4.2.3 循环结构 4.2.4 子程序设计和子程序调用

Page 2: 第 4 章  汇编语言程序设计

4.1.1 为什么要使用伪指令

◆ 存储空间分配问题:在利用高级语言(如C 语言)编程时,用户无需考虑存储空间的分配,因为操作系统和软件编译器帮助用户解决了这些问题,使得存储空间分配对用户来说是透明的,但是对 MCS - 51 单片机而言,存储空间分配是用户必须解决的一个问题,即存储空间分配对用户不再是透明的。但是在指令系统一章却没有介绍任何指令能够实现对存储空间的分配。

Page 3: 第 4 章  汇编语言程序设计

◆ 变量和常量的定义:在高级语言( C 语言)编程中,首先介绍了常量和变量的定义( Define 、 int , char 等),但是在指令系统一章却没有介绍任何指令能够实现对常量和变量的定义。◆ 数组的定义:在高级语言( C 语言)编程中,可以定义数组,数组在编程中被大量使用,但是在指令系统一章却没有介绍任何指令能够实现对数组的定义。

为什么要使用伪指令(续)

Page 4: 第 4 章  汇编语言程序设计

4.1.2 MCS- 51 单片机的伪指令伪指令不是真正的指令,无对应的机

器码,在汇编时不产生可供 CPU 直接执行的机器码(即目标程序),只是用来对汇编过程进行某种控制。例如规定汇编生成的目标程序在程序存储器中的存放区域,给源程序中的符号或标号赋值以及指示汇编的结束等。 MCS - 51 单片机常用伪指令有下面几种。

Page 5: 第 4 章  汇编语言程序设计

1 ORG (汇编起始命令) ORG 伪指令称为汇编起始命令,常用于

汇编语言源程序或数据块开头。

【标号:】 ORG 16 位地址或标号 ORG 伪指令的功能是将该伪指令后的程

序机器码按照 ORG 伪指令提供的 16 位地址或标号存入相应的程序存储器中。即规定目标程序在程序存储器中存放的起始地址

【注】在用户程序中, ORG 伪指令仅需要用于复位和中断服务入口地址、主程序入口地址设置,其他子程序不必使用 ORG 伪指令。

Page 6: 第 4 章  汇编语言程序设计

2 END (汇编结束命令)END 伪指令的格式: 【标号:】 END在 END 伪指令格式中,标号段为任选项,通常省

略。END 伪指令的功能是通知汇编程序结束汇编。在 E

ND 之后所有的汇编指令均不予以汇编成机器码。【注】 END 伪指令必须放在所有用户程序的后面,

不是放在主程序的后面。

Page 7: 第 4 章  汇编语言程序设计

3 EQU (赋值命令)

EQU 伪指令的格式: 字符名称 EQU 数据或汇编符号

在 EQU 伪指令的格式中,注意字符名称和标号的区别(标号后面有冒号,字符名称后没有冒号),经汇编编译后,字符名称和数据或汇编符号完全等价,在用户程序中,字符名称可以代替数据或汇编符号,在程序中字符名称可以作为数据(常量)、地址(变量)、位地址(一般情况下不使用 EQU 定义位变量,而是采用 BIT 定义位变量)。其中常量和变量可以是 8 位的,也可以是 16 位的。

Page 8: 第 4 章  汇编语言程序设计

EQU 伪指令的功能是给常量赋值或给变量赋地址。

那么如何区分此时字符名称是常量还是变量呢?现举例说明。例: XX EQU 30 ……….Main: …………MOV A, #XX ;将 XX 作为常量使用MOV A, XX ;将 XX 作为变量使用, XX 表示片内 30H 单元地址MOV A, 30H ; 此指令和上面一条指令等效

Page 9: 第 4 章  汇编语言程序设计

4 DATA (数据地址赋值命令) DATA 伪指令的格式为:

字符名称 DATA 表达式DATA 伪指令的功能与 EQU 类似,这两条伪

指令的主要区别在于 EQU 定义的字符名称必须先定义后使用,而 DATA 定义的字符名可以先使用后定义。

【注】根据 C 语言和其他编程语言的思维定势,对变量都是采用“先定义、后使用”原则,所以 DATA 伪指令很少被使用,都是采用 EQU 伪指令来定义变量和常量。

Page 10: 第 4 章  汇编语言程序设计

5 DB (定义字节命令)DB 伪指令的格式为: 【标号:】 DB 项或项表在 DB 伪指令的格式中,标号为任选项,项或项表

可以是一个 8 位二进制数据、用逗号分开的一串 8 位二进制数据、括在单引号中的 ASCII 字符串。

DB 伪指令的功能是将汇编程序从标号对应的程序存储器地址(若没有标号,则从该 DB 伪指令的汇编地址)开始,存入 DB 伪指令后的项或项表数据,类似于 C 语言中数组的定义。

Page 11: 第 4 章  汇编语言程序设计

例: TABLE : DB 0A3H ; 项或项表为一个 8 位二进制数据 DB 26H, 03H ; 项或项表为用逗号分开的一串 8 位二进制数据 DB ‘ABC’ ; 项或项表为括在单引号中的 ASCII字符串 ┇经汇编后从 TABLE 对应的程序存储器地址开始依次存放 A3H,26H,03H,41H,42H,43H ,其中 41H, 42H,43H 分别为 A, B, C的 ASCII 码。【注】实际应用中当一串 8 位二进制数据超过一行后,下一行必须以 DB 开始,对 ASCII 字符串可以采用‘ abc’ 或‘ a’, ‘b’ ,‘c’

Page 12: 第 4 章  汇编语言程序设计

6 DW (定义字命令)DW 伪指令的格式为: 【标号:】 DW 16 位数据项或项表DW 伪指令的功能是把 DW 后的 16 位数据项或项表

从当前地址连续存放。 16 位数据项或项表采用高 8 位在前,低 8 位后在后原则存放。

Page 13: 第 4 章  汇编语言程序设计

例: TABLE : DW 1234H , 23H , 10H假设经汇编后, TABLE 对应的程序存储器地址单元为 1000H ,那么从 1000H 单元开始依次存放如下:( 1000H) =12H( 1001H) =34H( 1002H) =00H( 1003H) =23H( 1004H) =00H ( 1005H) =10H采用 DW 伪指令,两个逗号之间的数据表示 16 位二进制数,即 23H 是表示 0023H ,所以( 1002H ) =00H ,而不是( 1002H ) =23H 【注】因为 MCS - 51 单片机为 8 位单片机,所以 DB 指令经常被使用,而 DW 指令并不常用,常用于带字符的液晶显示程序。与 DB 伪指令一样,当 16 位数据项或项表超过一行后,下一行同样以 DW 开始。

Page 14: 第 4 章  汇编语言程序设计

7 DS 定义存储空间命令DS 伪指令的格式为: 【标号:】 DS 表达式DS 伪指令的功能是指在汇编时,从指定地址开始保留 DS 之后表达式的值所规定的存储单元以备后用。

例: TABLE: DS 08H DB 30H , 8AH假设经汇编后, TABLE 对应的程序存储器地址单

元为 1000H ,从 1000H保留 8 个单元,然后从 1008H 按 DB 命令给内存赋值,即( 1008H ) =30H,( 1009H ) =8AH

【注】在单片机编程中,极少需要预先开辟存储空间,所以 DS 伪指令很少被使用

Page 15: 第 4 章  汇编语言程序设计

8 BIT ( 位地址符号命令 )BIT 伪指令的格式为: 字符名 BIT 位地址BIT 伪指令的功能是把 BIT 后的位地址值赋给字符

名。其中字符名不是标号,其后没有冒号。 例如: LED1 BIT P1.0 FLAG BIT 02H汇编后, P1 口第 0 位的位地址 90H就赋给了 LED1 ,

而位寻址空间 02H 定义为 FLAG 。【注】与 EQU 指令功能类似,两种区别在于 EQU

伪指令是对字节常量或变量的定义, BIT 伪指令是对位变量的定义。注意 BIT 伪指令不能定义位常量,因为 MCS - 51 单片机没有提供位常量操作指令,只提供位变量操作指令。

Page 16: 第 4 章  汇编语言程序设计

4.2 汇编语言程序设计

4.2.1 顺序结构 4.2.2 分支程序 4.2.3 循环结构 4.2.4 子程序设计和子程序调用

Page 17: 第 4 章  汇编语言程序设计

4.2.1 顺序程序

第一条指令

第二条指令

第N条指令

顺序结构是最简单的一种程序结构,这种程序中即无分支,也无循环,也不调用子程序,从第一条指令开始按顺序一条一条执行指令,直到结束。典型的顺序结构程序流程如图 4.1 所示。

图 4.1 顺序结构程序流程图

Page 18: 第 4 章  汇编语言程序设计

例 4-1 :试编制单字节的乘法程序,假设被乘数放在 R0 ,乘数放在 R1 中,结果高 8 位放在 30H ,低 8 位放在 31H 。【编程思路】单片机提供了乘法指令( MUL AB ),该指令要求乘数和被乘数必须存放在累加器 A 和寄存器 B 中,同时两数相乘的结果也存放在累加器 A 和寄存器 B 中。根据题目要求和单片机乘法指令的定义,程序应包括以下部分:

◆ 首先必须将乘数( R0 )和被乘数( R1 )分别取出存放到累加器 A 和寄存器 B 中,◆ 利用乘法指令将存放在 A 和 B 中的数相乘◆ 相乘后的结果,按题意要求进行存储。 ◆ 利用 SJMP $ 指令实现程序的死等待,即单片机执行该语句后, PC 指针将不再改变,一直执行该语句,主要为用户在仿真环境下调试程序方便。

◆ 死等待指令常用下面两种方式: SJMP $ 和 HERE: LJMP HERE

Page 19: 第 4 章  汇编语言程序设计

【例 4-1 源程序】ORG 0000H

LJMP MAINORG 0030H

MAIN: MOV A,R0 MOV B,R1 MUL AB MOV 31H,A MOV 30H,B SJMP $

Page 20: 第 4 章  汇编语言程序设计

例 4-2 :将单字节十六进制无符号数转换成非压缩的 BCD 码,设被转换数放在 R0 , BCD 码百位放在 30H ,十位放在 31H ,个位放在 32H 中。【编程思路】◆ 非压缩的 BCD 码是指一个字节中低四位存放一个 BCD 码,高四位为零,压缩 BCD 码是指一个字节存放两个 BCD 码,即高四位和低四位分别存放一个 BCD 码。◆ 单字节十六进制无符号数最大为 255 ,即其 BCD 码需要三位来表示,即百位、十位、个位。◆ 本题编程的关键在于需要将单字节十六进制无符号数的百位、十位、个位依次求出,这里采用将单字节十六进制除以 100 得到百位、余数再除以 10 得到十位,个位即是除以 10 后的余数。◆ 按照题意将百位、十位、个位依次存储在题目要求的存储单元。

Page 21: 第 4 章  汇编语言程序设计

【例 4-2 源程序】 ORG 0000H LJMP MAIN ORG 0030HMAIN: MOV B,#64H MOV A,R0 DIV AB ; 将单字节十六进制除以 100 MOV 30H,A XCH A,B MOV B,#0AH ; 将单字节十六进制除以 10 DIV AB MOV 31H,A MOV 32H,B SJMP $

Page 22: 第 4 章  汇编语言程序设计

例 4-3 :试编制双字加法程序,设被加数的高 8 位放在 30H中,低 8 位放在 31H 中,加数的高 8 位放在 32H ,低 8 位放在 33H 中。加法结果的高 8 位放在 34H 中,低 8 位放在 35H中。【编程思路】◆ 多字节加法程序其算法都是将最低 8 位相加,然后依次将次低 8 位相加,但是必须考虑进位,一直到最高 8 位相加完。◆ 对于双字加法程序同样如此,首先将两个被加数的低 8位相加,还是在将高 8 位进行相加,必须考虑加法操作的进位。◆ 加法操作其目的操作数只能为累加器 ACC ,故首先必须将两个加数中的一个预先存放到累加器 ACC 中。注意在指令中利用 A 来表示累加器 ACC

Page 23: 第 4 章  汇编语言程序设计

【例 4-3 源程序】 ORG 0000H LJMP MAIN ORG 0030HMAIN: CLR C ; 利用 ADDC 指令前一定要考虑 CY 位 MOV A,31H ; 进行加法运算一定要将其中一个加数 存放到 A 中 ADD A,33H MOV 35H,A ;低 8 位相加后存放在 35H 单元 MOV A,30H ADDC A,32H ; 高位相加必须考虑低位相加后的进位 MOV 34H,A ;高 8 位相加后存放在 34H 单元 SJMP $

Page 24: 第 4 章  汇编语言程序设计

例 4-4 :用查表法编一子程序,将 R3 中的 BCD 码转换成ASCII 码

【编程思路】◆ 本例目的要求掌握两个知识点:如何造表、如何访问表

◆ MCS - 51 单片机造表都是使用伪指令 DB 或 DW 实现,8 位数据采用 DB , 16 位数据采用 DW ,造表的时候一定要给表起个名字,本例直接用 TABLE 作为表名。本例中, TABLE 表的内容为 0~ 9 的 ASCII 码◆ 由于表数据存储在程序存储器中,访问表实际上就是对程序存储器的读操作,对程序存储器的读操作一般都使用 MOVC A,@A+DPTR ,即通过改变 A+ DPTR 的值来实现对程序存储器不同地址空间的访问。

Page 25: 第 4 章  汇编语言程序设计

【例 4-4 源程序】MAIN: MOV A,R3 ;待转换的数送 A MOV DPTR,# TABLE ;表首地址送 DPTR MOVC A,@A+DPTR ;查 ASCII 码表 MOV R3,A ;查表结果送 R3 RETTABLE : DB 30H,31H,32H,33H,34H DB 35H,36H,37H,38H,39H

【例 4-4 源程序】MAIN: MOV A, R3 ;待转换的数送 A

ADD A, #2 MOVC A,@A+PC ;查 ASCII 码表 MOV R3,A ;查表结果送 R3 RETTABLE : DB 30H,31H,32H,33H,34H DB 35H,36H,37H,38H,39H

Page 26: 第 4 章  汇编语言程序设计

4.2.2 分支程序从编程中,经常会遇到根据不同的条件,程序作不同的处理,分支程序用于 CPU 执行指令过程中,对条件进行分析和判断,决定程序的走向,分支程序的结构如图 4.2 所示。

条件是否成立

程序1 程序2

图 4.2 分支程序结构图

通常用条件转移指令来实现分支结构,在 MCS - 51 单片机指令系统中,提供了条件转移类指令主要有: JZ 、 JNZ (判断条件为操作数 A 是否为 0 ), CJNE (判断两个操作数是否相等),DJNZ (判断操作数减 1 是否为零), JC 、 JNC 、 JBC (判断CY 是否为 0 ), JB 、 JNB (判断操作位是否为 0 )

Page 27: 第 4 章  汇编语言程序设计

例 4-5 :试编写比较三个数的大小程序,设待比较的三个数放在30H 、 31H 、 32H ,比较后将三个数中最大的数放入 R0

【编程思路】1 依据题意需要找到三个数中的最大值,其程序算法将两个数比较,并将两个数中的最大数赋值给变量 MAX ,然后将 MAX 和第三个数比较,将最大值再赋值给 MAX 。

2 在本例编程中,将累加器 ACC 作为存储最大值 MAX3 比较两个数的大小, MCS - 51 单片机指令系统提供以下两

种方式。◆ 利用 SUBB 指令,将两个数相减后,根据 CY 标志判断两个

数的大小◆ 利用 CJNE 指令,首先判断两个数是否相等,然后根据 CY 标志判断两个数的大小

4 根据 SUBB 指令和 CJNE 指令的不同,分别给出两种方式下的源程序

Page 28: 第 4 章  汇编语言程序设计

例 4-5方式 1 源程序 ORG 0000H LJMP MAIN ORG 0030HMAIN: MOV A,30H CLR C SUBB A,31H JC NEXT; 31H 中的数据大 MOV A,30H SUBB A,32H;取大的和 32H 的数据比 JC NEXT1 ; 32H 中的数据大 MOV R0,30H; 30H 中的数据大 LJMP END1NEXT1: MOV R0,32H LJMP END1NEXT: MOV A,31H SUBB A,32H;取大的和 32H 的数据比 JC NEXT1; 32H 中的数据大 MOV R0,31HEND1: SJMP $

Page 29: 第 4 章  汇编语言程序设计

【例 4-5方式 2 源程序】ORG 0000H LJMP MAIN ORG 0030HMAIN: CLR C MOV A,30H CJNE A,31H,NEXT ;30H 和 31H比较大小NEXT: JC NEXT1 ;30H小于 31H ,跳 NEXT1 MOV A,30H ; 将 30H 作为最大值保存 LJMP COMP2NEXT1: MOV A,31H ; 第一次比较结束,最大值存储在 A 中COMP2: CJNE A,32H,NEXT2 ;30H 和 31H 中最大值和 32H比较大小NEXT2: JC NEXT3 LJMP END1NEXT3: MOV A,32H ; 第二次比较结束,最大值存储在 A 中 END1: MOV R0,A ; 依题意将最大值存在 R0 中 SJMP $

Page 30: 第 4 章  汇编语言程序设计

例 4 - 6: 设变量 X 存放在 30H 单元中 , 函数 Y 存放在 31H 单元。 试编程实现下式要求给 Y 赋值的程序。

【编程思路】1 由于 X 为有符号数 , 因此可以根据它的符号位来决定其正负。即根据 X 的最高位是 0 还是 1 决定 X 的正负 , 可利用 JB或 JNB 指令。 2 而判别 X 是否为 0, 可用 CJNE 指令或累加器判零指令 JZ

Page 31: 第 4 章  汇编语言程序设计

ORG 0000H LJMP MAIN ORG 0030HMAIN : MOV A,30H CJNE A,#00H,NEXT ; 将 X 和 0 进行比较 MOV 31H,#00H ;当 X= 0 时,将 Y 赋值为 0 LJMP END1NEXT: JNB ACC.7,NEXT1 ;当 X 不为 0 时,判断 X 的正负 MOV 31H,#0FFH ; 若 X<0 则 A= - 1 LJMP END1NEXT1: MOV 31H,#01H ; 若 X>0 则 A=1END1: SJMP $

【例 4 - 6 源程序】

Page 32: 第 4 章  汇编语言程序设计

例 4-7 :求双字节有符号数的补码,设字节高 8 位放在 30H ,低 8 位放在 31H ,将补码的高 8 位放在 32H ,低 8 位放在 33H 。

【编程思路】1 补码运算的算法思路为对有符号数“取反加 1”2 对 MCS - 51 单片机指令系统而言,提供取反指令 C

PL ,加 1 指令则采用加法指令即可。3 由于 51 单片机只能进行字节运算,对于双字节运算

则 只能采用低 8 位和高 8 位分别运算的方式,对于双字节有符号数的补码运算则需要在低 8 位取反加 1 ,高 8 位仅取反即可。

Page 33: 第 4 章  汇编语言程序设计

【例 4-7 源程序】 ORG 0000H LJMP MAIN ORG 0030H MAIN: MOV A,30H ;取高 8 位 JNB ACC.7,NEXT; 正数不处理 CPL A SETB C MOV ACC.7,C MOV 32H,A MOV A,31H CPL A ADD A,#01H MOV 33H,A MOV A,32H ADDC A,#00H MOV 32H,A LJMP END1 NEXT: MOV 32H,30H MOV 33H,31H END1: SJMP $

Page 34: 第 4 章  汇编语言程序设计

例 4-8 :试实现 16 路分支转移程序,程序框图如图 4.3 所示

转向0分支(PROG0)

转向1分支(PROG1)

转向15分支(PROG15)

分支转移

R0=0 R0=1 R0=0FH

……

试根据 R0 中的序号 (00H-0FH), 控制程序转移到不同的分支程序去,设有 16 个分支程序分别为 PROG0~ PROG15 。

图 4.3 多分支程序结构

Page 35: 第 4 章  汇编语言程序设计

【例 4-8 源程序】ORG 0000H

LJMP MAIN ORG 0030H MAIN: MOV A,R0 RL A MOV DPTR,#JMPTAB JMP @A+DPTRJMPTAB: LJMP PROG0 LJMP PROG1 ………. LJMP PROG15

Page 36: 第 4 章  汇编语言程序设计

4.2.3 循环结构

一般情况下,循环程序应包括以下几个部分:1 循环初值:包括循环次数、循环体内工作单元的初值等2 循环体:需要多次执行的程序段3 循环控制:循环控制用于判断是否结束循环。判断条件有两种方式◆ 用户设置固定的循环次数,利用 DJNZ 指令对循环次数进行计数,循环程序结构如图 4.4 ( a )所示。◆ 用户无法设置固定的循环次数,必须根据循环结束条件判断是否结束循环,循环控制部分每循环一次,检查结束条件,当满足条件时,就停止循环,往下继续执行其他程序,常用 CJNE 指令来实现循环结束条件判断。循环程序结构如图 4.4 ( b )所示。

Page 37: 第 4 章  汇编语言程序设计

置初值

循环体

循环修改

循环控制完?

退出循环

Y

N

a( )

置初值

循环体

循环修改

循环控制完? Y

N

退出循环

b( )

图 4.4 循环方式流程图

Page 38: 第 4 章  汇编语言程序设计

例 4-9 :试编写累加程序,设 30H 到 37H 存储的数进得累加,结果高 8 位放在 40H ,低 8 位放在 41H 。

【编程思路】本题算法思路非常简单,即将 30H到 37H 单元的数相加即可,

但是如何连续将 30H到 37H 值取出呢?一般情况下,对片内 RAM 空间的访问可以采用直接寻址和寄存器间接寻址方式,对于连续访问片内 RAM 单元,则只有采用寄存器间接寻址方式。

采用寄存器间接寻址方式连续访问片内 RAM 单元,一定需要三条指令

MOV R0,#30H ;设置访问 RAM 的首地址ADD A,@R0 ;取出 RAM 单元数据INC R0 ;指向下一个 RAM 单元

Page 39: 第 4 章  汇编语言程序设计

【例 4-9 源程序】 ORG 0000H LJMP MAIN ORG 0030H MAIN: MOV 40H,#00H ; 对存放结果的存储单元初始化 MOV 41H,#00H MOV R0,#30H ; 采用寄存器间接寻址必须在循环体前设置初值 MOV R3,#08H ; 设置固定的循环次数初值 LOOP: MOV A,41H ; 将低 8 位相加结果送到 A 中 ADD A,@R0 ; 依次取出 30H到 37H 单元内容和 A 相加 MOV 41H,A ;保存相加结果 MOV A,40H ADDC A,#00H ;保存相加进位,得到高 8 位的值 MOV 40H,A INC R0 ; 采用寄存器间接寻址,连续访问下一个单元 DJNZ R3,LOOP SJMP $

Page 40: 第 4 章  汇编语言程序设计

例 4-10 :试编程判断 30H 到 37H 中最大的数 , 并将存在 40H 中【编程思路】1 、本例和例 4-4 的不同之处在于本例采用循环结构程序,即在多个数据中找到最大值2 、编程思路与例 4-4 相同,本例程序算法为采用循环结构将30H到 37H 依次取出和 40H 单元内部比较,并将两个数中的最大数赋值给变量 MAX ( 40H )

Page 41: 第 4 章  汇编语言程序设计

【例 4-10 源程序】 ORG 0000H LJMP MAIN ORG 0030HMAIN:MOV R0,#30H MOV R2,#08H MOV 40H,#00HLOOP:MOV A,@R0 SUBB A,40H JC NEXT; 40H 中的数据较大 MOV 40H,@R0; 大数据放入 40hNEXT:INC R0 DJNZ R2,LOOP SJMP $

Page 42: 第 4 章  汇编语言程序设计

例 4-11 :试编写排序程序,设将 30H到 37H 单元中的 8 个数据从大到小的排列,最大值放在 30H ,最小值放在 37H 。

【编程思路】排序程序又称为冒泡程序,其算法思路为将两个相邻的数

据相比较,所有数据比较完后,找到最大的数,并存入 30H单元,再次将剩余的 7 个数相互比较,找到最大的数,并存入 31H 单元,依次比较直到完成从大到小的排列。

Page 43: 第 4 章  汇编语言程序设计

LOOP1: MOV 50H,@R0 DEC R0 MOV A,@R0 INC R0 MOV @R0,A DEC R0 MOV @R0,50H INC R0LOOP2: DJNZ R7,LOOP DJNZ R2,LOOP3 LJMP END1LOOP3: MOV A,R2 MOV R7,A MOV A,R4 MOV R0,A LJMP LOOP END1: SJMP $

【例 4-11 源程序】 ORG 0000H LJMP MAIN ORG 0030HMAIN: MOV R7,#08H MOV R2,#08H MOV R0,#30H MOV R4,#30HLOOP: MOV A,@R0 INC R0 CLR C SUBB A,@R0 JC LOOP1 LJMP LOOP2

Page 44: 第 4 章  汇编语言程序设计

例 4-12 :试编程将 30H 到 40H 存储单元清零【编程思路】

1 存储单元清零可以直接采用数据传送类指令,直接将00H 赋值给 30H到 40H 即可实现,但是将需要清零的存储单元较多时,采用这种方式就比较繁琐,应采用循环结构。2 采用循环结构将连续的存储单元清零只需要能够将存储单元连续取出,然后对相应的存储单元用 00H 赋值即可。

Page 45: 第 4 章  汇编语言程序设计

【例 4-12 源程序】 ORG 0000H LJMP MAIN ORG 0030HMAIN: MOV A,#00 MOV R0,#30H MOV R3,#10HLOOP: MOV @R0,A INC R0 DJNZ R3,LOOP SJMP $

Page 46: 第 4 章  汇编语言程序设计

例 4-13 :编程将片内 RAM30H 单元开始的 15 个单元的数据传送到片外 RAM 的 3000H 开始的单元中去。

【编程思路】1 本例目的是要求掌握对片外 RAM 的连续读写操作的编程方法

2 对片外 RAM 的访问都是采用 MOVX 指令,本例中主要是对片外 RAM 的写操作, MCS - 51 单片机提供了两条指令对片外 RAM 的写操作 MOVX @DPTR, A MOVX @Ri, A

这两条指令的不同之处在于寻址范围不同,采用 @DPTR 可以访问片外 64K的 RAM ,而采用 @Ri 仅能访问 256 字节的空间,本例中要求访问 3000H 单元开始的片外 RAM 空间,必然只能采用 @DPTR形式。

3、MOVX 指令只能采用 MOVX @DPTR, A ,常用的错误形式为 MOVX 3000H, A ; MOV 3000H, A

4 、既然访问片外 RAM 只能采用 MOVX @DPTR, A ,那么希望连续访问片外 RAM ,唯一的办法就只有不断改变DPTR 的值,因为 DPTR 表示片外 RAM 的地址

Page 47: 第 4 章  汇编语言程序设计

【例 4-13 源程序】ORG 0000H

LJMP MAIN ORG 0030HMAIN:MOV R0,#30H MOV R7,#0FH MOV DPTR,#3000H ; 给 DPTR 赋初值,即访问片

外 RAM 的首地址LOOP : MOV A,@R0 MOVX @DPTR,A ; 对片外 RAM写操作 INC R0 INC DPTR ; 片外 RAM 的地址加 1 ,以访问

下 一单元 DJNZ R7,LOOPHERE: AJMP HERE

Page 48: 第 4 章  汇编语言程序设计

4.2.4 子程序设计和子程序调用在编程时,使用子程序设计应注意以下几点1 子程序必须由用户起名,即子程序的第一条指令地址称为子程序的起始地址,该指令前必须有标号,该标号即为用户根据子程序所完成的功能所定义的程序名。2 调用子程序是通过调用指令来实现的,即 LCALL 子程序名。子程序的最后一条指令必须是返回指令( RET ),在执行调用指令时单片机硬件自动保护 PC 指针,当执行 RET 指令时自动恢复 PC 值。3 参数传递问题。子程序应注意它的入口参数和出口参数,入口参数是由主程序传给子程序的参数,出口参数是子程序运算完传给主程序的运算结果。即在调用子程序时,必须对子程序的入口参数赋值。

Page 49: 第 4 章  汇编语言程序设计

4 参数传递一般采用以下方法◆ 利用寄存器或片内 RAM传递子程序的参数,这种方法常用◆ 利用堆栈来传递子程序的参数,这种方法是将参数压入堆栈,在子程序运行时从堆栈取参数,这种方法很少被使用,因为在子程序调用和中断返回时系统会改变堆栈的值,容易造成用户弹出错误数据。◆ 若参数为位变量,则利用位地址传送子程序参数。5 在调用子程序时应注意现场保护问题,在子程序中可能会使用累加器和寄存器,在主程序调用子程序前,这些寄存器可以存放有主程序的中间结果,它们在子程序返回时还需要使用,这样就需要在进入子程序时,将这些累加器和寄存器中的数据保存起来。当子程序执行完,在恢复这些数据,一般采用堆栈方式保护这些数据。

子程序设计应注意以下几点(续)

Page 50: 第 4 章  汇编语言程序设计

例 4-14:试编写将两个半字节数合并成一个单字节数子程序。两个半字节数分别存放在 R0 和 R1 中,合并后的单字节数存放在 R2 中。【编程思路】R0 作为合并后字节的高位, R1 作为合并后字节的低位,即需要将 R0 的高低四位互换,同时与 R1 的低四位合并,并将结果存入 R2 。

Page 51: 第 4 章  汇编语言程序设计

【例 4-14 源程序】; 入口参数: R0、 R1 (两个半字节数);出口参数: R2 (由两个半字节数合并成一个单字节数)BYTE_UNITE: MOV A,R0 ANL A,#0FH SWAP A MOV R2,A MOV A,R1 ANL A,#0FH ORL A,R2 MOV R2,A RET

Page 52: 第 4 章  汇编语言程序设计

例 4-15 :利用调用子程序的方法,进行两个 4 字节无符号数相加。假设两个 4 字节无符号数分别存放在 20H 单元和 30H单元开始的片内 RAM 中,请编制主程序及子程序。 【编程思路】1 若采用子程序调用方式编程,首先必须考虑子程序的入口参数和出口参数。2 在本例中,子程序完成两个多字节无符号数的加法运算,即入口参数应包括:两个多字节无符号数的存储单元、无符号数的字节长度,出口地址应包括加法运算结果所在的存储单元。3 考虑到多字节无符号数加法运算需要连续对从低字节到最高字节的连续读操作,所以需要寄存器间接寻址方式访问这两个多字节无符号数,采用 R0 和 R1 作数据指针, R0 指向第一个加数,并兼作“和”的指针, R1 指向另一个加数,字节数存放到 R2 中。

Page 53: 第 4 章  汇编语言程序设计

【源程序如下】;********* 主程序如下 ***************ORG 0000H LJMP MAINORG 0030HMAIN: MOV R0,#20H ; 入口参数赋值(加数的首地址)    MOV R1,#30H ; 入口参数赋值(另一加数的首地

址)    MOV R2,#04H ; 入口参数赋值(字节数作计数

值)    ACALL ADDPROG ; 调用加法子程序    AJMP $

Page 54: 第 4 章  汇编语言程序设计

;********* 多字节加法子程序如下 ***************; 入口参数: R0 和 R1 (两个多字节无符号数的首地址), R2(无符号数的字节长度);出口参数:加法运算结果所在的存储单元( R0 开始的一段存储空间)ADDPROG: CLR CADD1: MOV A,@R0      ADDC A,@R1 ;加上另一数的一个字节     MOV @R0,A ;保存和数     INC R0 ; 指向加数的高位     INC R1 ; 指向另一加数的高位     DJNZ R2, ADD1 ; 全部加完了吗?     RET ; 必须有返回指令

Page 55: 第 4 章  汇编语言程序设计

例 4 - 16: 设计 100 ms 延时子程序,并通过调用子程序方式实现 1s 、 2s 的延时子程序。【编程思路】1 由于单片机广泛应用在工控领域,所以延时程序经常被使用,单片机的延时程序一般有两种:指令延时和定时器延时。定时器延时在后面的章节讲述,本例主要阐述指令延时方式。2 单片机执行一条指令需要一定的时间 , 由一些指令组成一段程序 , 并反复循环执行 , 利用单片机执行程序所用的时间来实现延时 , 这种程序称为指令延时程序。如当系统使用 12 MHz晶振时 , 一个机器周期为 1μs , 执行一条双字节双周期 DJNZ指令的时间为 2μs, 因此 执行该指令 50 000 次 , 就可以达到延时 100 ms 的目的。3 对于 50 000次循环可采用外循环、 内循环嵌套的多重循环结构。

Page 56: 第 4 章  汇编语言程序设计

【 100 ms延时子程序如下】DELAY: MOV R6, #0C8H ; (1)外循环 200 次 LOOP1: MOV R7, #0F8H ; (1) 内循环 248 次 NOP ; (1) 时间补偿 LOOP2: DJNZ R7, LOOP2 ; (2)延时 2 μs×248=496 μs DJNZ R6, LOOP1 ; (2)延时 500 μs×200=100 ms RET ; (1)

(2*248+1+1+2)*200+1+1=100002 μs

【延时 1s 调用 100ms延时子程序如下】;1s 是 100ms 的 10倍,即连续执行 100ms延时子程

序 10 次即可 MOV R0, #10 LOOP: LCALL DELAY

DJNZ R0, LOOP