赵工的个人空间


专业技术部分转网页计算转业余爱好部分


 单片机与嵌入式

首页 > 专业技术 > 单片机与嵌入式 > 51汇编语言程序结构与程序示例
51汇编语言程序结构与程序示例

一、程序结构:

1. 顺序结构程序:

顺序结构程序,是按程序顺序一条指令紧接一条指令执行,是所有程序设计中最基本的程序结构,是应用最普遍的程序结构,是实际编写程序的基础。

2. 选择结构程序(分支程序):

选择结构程序,是指在程序执行过程中,依据条件选择执行不同的分支程序。为实现程序分支,编写选择结构程序时要合理选用具有判断功能的指令,如条件转移指令、比较转移指令和位转移指令等。

选择结构程序允许嵌套,从而形成多级选择程序结构。汇编语言不限制嵌套的层数,但过多的嵌套将使程序的结构变得复杂和臃肿,容易造成混乱。

3. 循环结构程序:

循环是指MCU反复地执行某种相同的操作。从本质上讲,循环只是选择结构程序中的一个特殊形式而已,但因为其重要性,因而独立作为一种程序结构。

在进入循环体之前,需给用于循环过程的工作单元设置初值,如循环控制计数初值、地址指针起始地址的设置、变量初值等,初始化部分是保证循环正确执行所必须的。循环体是循环结构的核心,完成实际的处理工作,在循环体中也可包括改变循环变量、改变地址指针等有关修改循环参数的部分。循环控制部分通过循环变量和结束条件控制循环结束,有时修改循环参数和判断结束条件由一条指令完成,如DJNZ。循环处理程序的结束条件不同,相应控制部分的实现方法也不一样,分循环计数控制和条件控制。

4. 子程序结构:

子程序是在主程序中通过LCALL、ACALL等指令调用的程序段,该程序段的第一条指令地址称子程序入口地址。子程序的最后一条指令必须是RET返回指令,即返回到主程序中调用子程序指令的下一条指令。

实际应用中,大多数子程序具有复杂程度不等的结构,主程序调用的子程序运行时有可能改变主程序中某些寄存器的内容,如PSW、A、B、工作寄存器等,这样就必须先用PUSH指令将相应寄存器的内容压入堆栈保护起来,返回时再用POP指令将压入堆栈的内容弹回到相应的寄存器中。有调用前在主程序保护和子程序开头保护两种,相应的在主程序恢复和子程序末尾恢复。

二、常用汇编语言程序举例:

1. 延时程序:

延时程序是经常使用的程序,一般设计成具有通用性的循环结构子程序。在设计延时子程序时,延时的最小单位为机器周期,所以要注意晶振的频率。

ORG 00H
LCALL YASH20
SJMP $
YASH20: MOV R7,#100
AA0: MOV R6,#49
AA1: NOP
NOP
DJNZ R6,AA1
NOP
DJNZ R7,AA0
NOP
RET
END

2. 查表程序:

查表程序是一种常用程序,可以完成数据计算、转换、补偿等各种功能,具有程序简单、执行速度快等优点。在AT89C51中,数据表格存放在程序存储器ROM中。编写程序时,可以通过DB或DW伪指令,以表格的形式将数据列于ROM。用于查表的指令有MOVC A,@A+DPTR和MOVC A,@A+PC。

用DPTR做基址寄存器时,寻址范围为64kB空间,查表分3个步骤:基址值(表格首地址)→DPTR中;‚变址值(项与表格首地址的间隔)→A;ƒ执行MOVC A,@A+DPTR。示例:将一位16进制数转换为ASCII码。

ORG 00H
MOV R0,#0BH ;设(R0)=BH
MOV A,R0 ;读数据
ANL A,#0FH ;屏蔽高4位
MOV DPTR,#TAB ;置表格首地址
MOVC A,@A+DPTR ;查表
MOV R0,A ;回存
SJMP $

ORG 50H
TAB: DB 30H,31H,32H,33H,34H,35H,36H,37H,38H,39H ;0~9的ascii码
DB 41H,42H,43H,44H,45H,46H ;A~F的ascii码
END

用PC做基址寄存器时,基址PC是当前程序计数器的内容,即查表指令的下一条指令的首址,查表范围是查表指令后256B空间。查表分3个步骤:变址值(项与表格首地址的间隔)→(A);‚偏移量(查表下一条指令的首地址到表格首地址的间隔)+(A)→(A);ƒ执行MOVC A,@A+PC。示例:将一位16进制数转换为ASCII码。

ORG 00H
MOV R0,#07H ;设(R0)=7H
MOV A,R0 ;读数据
ANL A,#0FH ;屏蔽高4位
ADD A,#03H ;加偏移量
MOVC A,@A+PC ;查表
MOV R0,A ;回存,1字节
SJMP $ ;2字节

TAB: DB 30H,31H,32H,33H,34H ;0~4的ascii码
DB 35H,36H,37H,38H,39H ;5~9的ascii码
DB 41H,42H,43H,44H,45H,46H ;A~f的ascii码
END

3. 码制转换程序:

MCU内部进行数据计算和存储时,多采用二进制码;在数据的输入/输出中,多采用BCD码。编程中经常会遇到各种码制的转换问题。

十进制数常用BCD码表示,BCD码有两种形式:一种是一个字节放一位BCD码,适用于显示或输出;另一种是压缩的BCD码,一个字节放两个BCD码,节省存储单元。

单字节二进制(或16进制)数转换为BCD码的一般方法是把二进制数除以100,得到百位数,余数除以10的商和余数分别是十位数、个位数。

ORG 00H
MOV A,#89H ;16进制数89H送入A
MOV B,#100 ;100作为除数送入B中
DIV B ;16进制数除以100
MOV R3,A ;百位数送R3,余数在B中
MOV A,#10 ;分离十位数和个位数
XCH A,B ;余数送入A中,除数10放在B中
DIV AB ;分离出十位放在A中,各位放在B中
SWAP A ;十位交换到A的高4位
ADD A,B ;将个位送入A的低4位
SJMP $
END

将压缩BCD码按其高、低4位分别转换为二进制数。示例:

STAR: MOV R2,#89H ;表示BCD码为89
MOV A,R2 ;(A)←(R2)
ANL A,#0F0H ;屏蔽低4位
SWAP A ;高4位与低4位交换
MOV B,10 ;乘数
MUL AB ;相乘
MOV R3,A ;(R3)←(A)
MOV A,R2 ;(A)←(R2)
ANL A,#0FH ;屏蔽高4位
ADD A,R3 ;(A)←(A)+(R3)
MOV R3,A ;(R3)←(A)
SJMP $
END

4,数据排序程序:

经常要对数据进行排序,排序的方法有按从小到大的次序和按从大到小的次序排。示例为将放于片内RAM的50H~5AH单元中的单字节无符号正整数按从小到大的次序重新排列(冒泡法)。

ORG 00H
SORT: MOV R0,#50H ;指针送R0
MOV R7,#0AH ;每次冒泡比较的次数
CLR F0 ;交换标志清0
LOOP: MOV A,@R0 ;取前一个数
MOV R2,A ;暂存前一个数于R2
INC R0 ;取后一个数
MOV 30H,@R0 ;后一个数暂存于30H
CLR C ;清进位为0
CJNE A,30H,LP1 ;前后两数比较
SJMP LP2
LP1: JC LP2 ;前数≦后数,不交换
MOV A,@R0
DEC R0 ;前数>后数,交换
XCH A,@R0
INC R0
MOV @R0,A
SETB F0 ;置交换标志
LP2: DJNZ R7,LOOP ;进行下一次比较
JB F0,SORT ;一趟循环中有交换进行下一趟冒泡
SJMP $ ;无交换退出
END

5. 算术计算程序:

示例1:求解Y=(3×X+4)×5÷8+1。X的取值范围为0~15,存放在30H,设X=4.

ORG 00H
LJMP STAR
ORG 100H
STAR: MOV 30H,#4 ;X=4, (30H)=4
MOV A,30H
CLR C
RLC A ;2X
ADD A,30H ;(A)=3X
MOV 31H,A ;(31H)=(A)=3X
MOV A,#4
ADD A,31H ;(A)=3X+4
MOV B,#5
MUL AB ;(A)=5(3X+4)
MOV B,#8
DIV AB ;(A)=5(3X+4)/8
DEC A ;(A)=[5(3X+4)/8]-1
MOV 31H,A ;结果在31H中,余数在B中
SJMP $
END

示例2:实现。设a、b、c存于片内RAM的3个单元R2、R3、R4中。用查平方表子程序来得到平方值,在主程序中完成相加。(设a、b为0~9之间的数,a=6,b=4)

ORG 00H
MOV R2,#6 ;赋值(R2)=6
MOV R3,#4 ;赋值(R3)=4
MOV A,R2 ;取第一个被加的数据a
ACALL SQR ;第一次调用,得到a的平方值
MOV R1,A ;暂存于R1中
MOV A,R3 ;取第二个被加的数据b
ACALL SQR ;第二次调用,得到b的平方值
ADD A,R1 ;完成相加
MOV R4,A ;存结果到R4
SJMP $
SQR: INC A ;查表位置调整
MOVC A,@A+PC ;查平方表
RET ;子程序返回

TAB: DB 0,1,4,9,16,25,36,49,64,81
END

示例3:n个正整数求和。正整数皆为单字节数,按顺序存放在片内RAM以50H为首地址的连续存储单元中,数据个数n存于R2中。双字节和数存放在R3、R4中。

ORG 00H
MOV 50H,#23H ;为寄存器50H~54H预置数据
MOV 51H,#05H
MOV 52H,#0FFH
MOV 53H,#44H
MOV 54H,#60H

;以下4条指令为置循环初值
MOV R2,#5 ;数据个数计数器R2置数
MOV R3,#00H ;结果高位存储器R3清零
MOV R4,#00H ;结果低位存储器R4清零
MOV R0,#50H ;寄存器(R0)=50H

;以下6条指令为循环体
LOOP: MOV A,R4
ADD A,@R0
MOV R4,A
CLR A
ADDC A,R3
MOV R3,A

;以下3条分别为循环修改、循环控制、退出循环

INC R0 ;循环修改
DJNZ R2,LOOP ;循环控制
SJMP $ ;退出循环
END

示例4:双字节数取补子程序,数存R4R5,结果送回

CMPT: MOV  A,R5
CPL  A
ADD  A,#1
MOV  R5,A
MOV A,R4
CPL A
ADDC A,#0
MOV R4,A
RET

正数的补码与原码相同,负数的补码是按位取反再最低位加1。采用补码后,加减运算简单。

示例5:双字节原码左移一位子程序,数值在R2R3,结果送回,不改变符号位

DRL1: MOV A,R3
CLR C
RLC A
MOV R3,A
MOV A,R2
RLC A
MOV ACC.7,C ;恢复符号
MOV R2,A
RET

对于二进制数,左移一位相当于乘以2,右移一位相当于除以2,对于带符号数要保持符号位不变。

示例6:双字节原码右移一位子程序。数值在R2R3,结果送回,不改变符号位

DRR1: MOV A,R2
MOV C,ACC.7 ;保护符号位
CLR ACC.7 ;移入0
RRC A
MOV R2,A
MOV A,R3
RRC A
MOV R3,A
RET

示例7:双字节补码右移一位子程序。数值在R2R3,结果送回,不改变符号位

CRR1: MOV A,R2
MOV C,ACC.7 ;保护符号位
RRC A ;移入符号位
MOV R2,A
MOV A,R3
RRC A
MOV R3,A
RET

示例8:双字节无符号数相加子程序。数存R2R3和R6R7,结果送R4R5

NADD: MOV A,R3
ADD A,R7
MOV R5,A
MOV A,R2
ADDC A,R6
MOV R4,A
RET

示例9:双字节无符号数相减子程序。数存R2R3和R6R7,结果送R4R5

NSUB1: MOV A,R3
CLR C
SUBB A,R7
MOV R5,A
MOV A,R2
SUBB A,R6
MOV R4,A
RET

示例10:双字节原码加减运算子程序。数存R2R3和R6R7,结果送R4R5

DSUB: MOV A,R6 ;减法入口
CPL ACC.7 ;取反符号位
MOV R6,A
DADD: MOV A,R2 ;加法入口
MOV C,ACC.7
MOV F0,C ;保存被加数符号位
XRL A,R6
MOV C,ACC.7 ;C=1,两数异号;C=1,两数同号
MOV A,R2
CLR ACC.7 ;清0被加数符号
MOV R2,A
MOV A,R6
CLR ACC.7 ;清1加数符号
MOV R6,A
JC DAB2
ACALL NADD ;同号执行加法
MOV A,R4
JB ACC.7,DABE
DAB1: MOV C,F0 ;恢复运算结果
MOV ACC.7,C
MOV R4,A
RET
DABE: SETB C
RET ;溢出

DAB2: ACALL NSUB1 ;异号执行减法
MOV A,R4
JNB ACC.7,DAB1
ACALL CMPT ;不够减,取补
CPL F0 ;符号位取反
SJMP DAB1

示例11:无符号二进制数乘法。数存R2R3和R6R7,结果送R4R5R6R7

NMUL: MOV R4,#0
MOV R5,#0
MOV R0,#16 ;16位二进制数
CLR C
NMLP: MOV A,R4 :右移一位
RRC A
MOV R4,A
MOV A,R5
RRC A
MOV R5,A
MOV A,R6
RRC A
MOV R6,A
MOV A,R7
RRC A
MOV R7,A
JNC NMLN ;C为移出的乘数最低位,若为0,不执行加法
MOV A,R5 ;执行加法
ADD A,R3
MOV R5,A
MOV A,R4
ADDC A,R2
MOV R4,A
NMLN: DJNZ R0,NMLP ;执行16次
MOV A,R4 ;最后再右移一位
RRC A
MOV R4,A
MOV A,R5
RRC A
MOV R5,A
MOV A,R6
RRC A
MOV R6,A
MOV A,R7
RRC A
MOV R7,A
RET

示例12:无符号双字节快速乘法。数存R2R3和R6R7,结果送R4R5R6R7

QMUL: MOV A,R3
MOV B,R7
MUL AB ;R3XR7
XCH A,R7 ;R7=(R3XR7)L
MOV R5,B ;R5=(R3XR7)H
MOV B,R2
MUL AB ;R2XR7
ADD A,R5
MOV R4,A
CLR A
ADDC A,B
MOV R5,A ;R5=(R2XR7)H
MOV A,R6
MOV B,R3
MUL AB ;R3XR6
ADD A,R4
XCH A,R6
XCH A,B
ADDC A,R5
MOV R5,A
MOV F0,C ;暂存CY
MOV A,R2 ;R2XR6
MUL AB
ADD A,R5
MOV R5,A
CLR A
MOV ACC.0,C
MOV C,F0 ;加以前加法的进位
ADDC A,B
MOV R4,A
RET

示例13:双字节原码乘法。数存R2R3和R6R7,结果送R4R5R6R7

IMUL: MOV A,R2
XRL A,R6
MOV C,ACC.7
MOV F0,C ;暂存积的符号
MOV A,R2
CLR ACC.7 ;清0被乘数符号位
MOV R2,A
MOV A,R6
CLR ACC.7 ;清0乘数符号位
MOV R6,A
ACALL NMUL ;调用无符号双字节乘法子程序
MOV A,R4
MOV C,F0 ;回送积的符号
MOV ACC.7,C
MOV R4,A
RET

对源码表示的带符号的二进制数乘法,在乘法之前按负负得正、正负得负得出积的符号,然后清符号位,执行无符号乘法,最后送积的符号。

示例14:双字节无符号数比较法相除,数存R2R3和R6R7,商送R4R5,余数送R6R7

NDIV1: MOV A,R3 ;先比较是否发生溢出
CLR C
SUBB A,R7
MOV A,R2
SUBB A,R6
JNC NDVE1
MOV B,#16 ;无溢出,执行除法
NDVL1: CLR C ;执行左移1位,移入为0
MOV A,R5
RLC A
MOV R5,A
MOV A,R4
RCL A
MOV R4,A
MOV A,R3
RLC A
MOV R3,A
XCH A,R2
RLC A
XCH A,R2
MOV F0,C ;保存移出的最高位
CLR C
SUBB A,R7 ;比较部分余数与除数
MOV R1,A
MOV A,R2
SUBB A,R6
JB F0,NDVM1
JC NDVD1
NDVM1: MOV R2,A ;执行减法(回送减法结果)
MOV A,R1
MOV R3,A
INC R5 ;商为1
NDVD1: DJNZ B,NDVL1 ;循环16次
CLR F0 ;正常出口
RET

NDVE1: SETB F0 ;溢出
RET

示例15:双字节原码相除。数存R2R3和R6R7,商送R4R5,余数送R6R7

IDIV: MOV A,R2
XRL A,R6
MOV C,ACC.7
MOV 00H,C ;保存符号位
MOV A,R2
CLR ACC.7 ;清0被除数符号位
MOV R2,A
MOV A,R6
CLR ACC.7 ;清0除数符号位
MOV R6,A
ACALL NDIV1 ;调用无符号双字节除法子程序
MOV A,R4
JB ACC.7,IDIVE
MOV C,00H ;回送商的符号
MOV ACC.7,C
MOV R4,A
RET

IDIVE: SETB F0 ;溢出
RET

有符号原码除法与原码乘法一样,只要在除法之前先计算商的符号,然后清0符号位,执行不带符号的除法,最后送商的符号。

Copyright@dwenzhao.cn All Rights Reserved   备案号:粤ICP备15026949号
联系邮箱:dwenzhao@163.com  QQ:1608288659