480 likes | 651 Views
一 . 分析问题。 二 . 确定算法。 三 . 制定程序流程图:表示程序结构和程序功能. 5.1.2 汇编语言程序设计步骤. 四 . 编制源程序 1. 合理分配存储器单元和了解 I/O 接口地址。 2. 按功能设计程序,明确各程序之间的相互关系。 3. 用注释行说明程序,便于阅读和修改调试和修改。. 五 . 程序调试 。. 开始. Y. ?. N. 1. 程序的执行时间。 程序所占用的内存字节数。 程序的逻辑性、可读性。 程序的兼容性、可扩展性。 程序的可靠性。. 5.1.3 评价程序质量的标准. 5.2 顺序程序
E N D
一.分析问题。 • 二.确定算法。 • 三.制定程序流程图:表示程序结构和程序功能 5.1.2 汇编语言程序设计步骤 四.编制源程序 1.合理分配存储器单元和了解I/O接口地址。 2.按功能设计程序,明确各程序之间的相互关系。 3.用注释行说明程序,便于阅读和修改调试和修改。 五.程序调试。
开始 Y ? N 1
程序的执行时间。 • 程序所占用的内存字节数。 • 程序的逻辑性、可读性。 • 程序的兼容性、可扩展性。 • 程序的可靠性。 5.1.3 评价程序质量的标准
5.2 顺序程序 • 直线程序又称简单程序,程序走向只有一条路径。 例1 将存放在R4R5的双字节负数求补,仍然存放在R4R5中双字节变补程序(设数据在R4R5中): MOV A,R5 ;取低字节 CPL A ADD A,#1 ;低字节变补 MOV R5,A MOV A,R4 ;取高字节 CPL A ADDC A,#0 ;高字节变补 MOV R4,A
MOV R0,#40H ;设指针 • MOV A,@R0 ;取一个字节 • MOV R2,A ;暂存 • ANL A,#0FH ;清0高半字节 • INC R0 • MOV @R0,A ;保存数据个位 • MOV A,R2 • SWAP A ;十位换到低半字节 • ANL A,#0FH • INC R0 • MOV @R0,A ;保存数据十位 片内RAM40H存放压缩BCD数,将其转换成非压缩BCD数存放在41H(个位)和42H(十位)。 十 个
例5-1 假设两个双字节无符号数,分别存放在R1R0和R3R2中,高字节在前,低字节在后。编程使两数相加,和数存放回R2R1R0中。 • ORG 1000h • CLR C • MOV A,R0 ;取被加数低字节至A • ADD A,R2 ;与加数低字节相加 • MOV R0,A ;存和数低字节 • MOV A,Rl ;取被加数高字节至A • MOV A,#0
ADDC A,#0 ;加进位位 • MOV R2,A ;存和数进位位 • SJMP $ ;原地踏步 • END
5.3.1 单重分支程序 • 一个判断决策框,程序有两条出路。两种分支结构: 5.3 分支程序由条件转移指令构成程序判断框部分,形成程序分支结构。 例1 求R2中补码绝对值,正数不变,负数变补。 MOV A,R2 JNB ACC.7,NEXT;为正数? CPL A ;负数变补
影响条件 Y 条件满足? N 处理段 • INC A • MOV R2,A • NEXT: SJMP NEXT ;结束
例5-7 设30H单元存放的是一元二次方程ax2+bx+c=0根的判别式△=b2-4ac的值。 • 解:△值为有符号数,有三种情况,即大于零、等于零、小于零。 5.3.2 多重分支程序多次使用条件转移指令,形成两个以上判断框。
编程: • ORG 1000H • START: MOV A,30H ;△值送A • JNB ACC.7,YES ;△=>0转YES • MOV 31H,#0;△<0,无实根 • SJMP FINISH • YES: JNZ TOW ;△>0转TOW • MOV 31H,#1 ;△=0有相同实根 • SJMP FINISH • TOW: MOV 31H,#2 ;有两个不同实根 • FINISH:SJMP $
N路分支程序是根据前面程序运行的结果,可以有N种选择,并能转向其中任一处理程序。N路分支程序是根据前面程序运行的结果,可以有N种选择,并能转向其中任一处理程序。 5.3.3 N路分支程序
例5-10 128路分支程序。 • 功能:根据R3的值(00H~7FH)转到128个目的地址。 • 参考程序: • JMPl28: MOV A,R3 • RL A ;(A)×2 • MOV DPTR,#PRGTBL ;散转表首址送DPTR • JMP @A+DPTR ;散转 • PRGTBL: AJMP ROUT00 • AJMP ROUT01 • … • AJMP ROUT7F ;128个AJMP指令占256个字节
例5-11 256路分支程序 功能:根据R3的值转移到256个目的地址; 入口条件:(R3)=转移目的地址代号(00H~FFH); 出口条件:转移到相应分支处理程序入口。 解: JMP256: MOV A,R3 ;取N值 MOV DPTR,#PRGTBL ;指向分支地址表首址 CLR C RLC A ;(A)×2 JNC LOWl28;是前128个分支程序,则转移 INC DPH ;否则基址加25
LOWl28:MOV TEMP,A ;暂存A • INC A ;指向地址低8位 • MOVC A,@A+DPTR;查表,读分支 • 地址低8位 • PUSH ACC ;地址低8位入栈 • MOV A,TEMP ;恢复A,指向地址高8位 • MOVC A,@A+DPTR ;查表,读分支 • 地址高8位 • PUSH ACC ;地址高8位入栈 • RET ;分支地址弹入PC实现转移
LOWl28: MOV TEMP,A ;暂存A • INC A ;指向地址低8位 • MOVC A,@A+DPT;查表,读分支地址低8位 • PUSH ACC ;地址低8位入栈 • MOV A,TEMP ;恢复A,指向地址高8位 • MOVC A,@A+DPT;查表,读分支地址高8位 • PUSH ACC ;地址高8位入栈 • RET ;分支地址弹入PC实现转移 • PRGTBL: DW ROUT00 ;占用512个字节 • DW ROUT01 • …
例5-12 大于256路分支转移程序 • 功能:根据入口条件转向n个分支处理程序 • 入口条件:(R7R6)=转移目的地址代号; • 出口条件:转移到相应分支处理程序入口。 • 程序散转表中有N条LJMP指令,每条LJMP指令占3个字节,因此要按入口条件将址代号乘以3,用乘积的高字节加DPH,乘积的低字节送A(变址寄存器)。
参考程序: • JMPN:MOV DPTR,#PRGTBL;DPTR指向表首址 • MOV A,R7 ;取地址代号高8位 • MOV B,#3 ; • MUL AB ;×3 • ADD A,DPH ; • MOV DPH,A ;修改指针高8位 • MOV A,R6 ;取地址代号低8位 • MOV B,#3 ;×3 • MUL AB ;
XCH A,B ;交换乘积的高低字节 • ADD A,DPH ;乘积的高字节加DPH • MOV DPH,A • XCH A,B ;乘积的低字节送A • JMP @A+DPTR ;散转
循环初始化 循环工作 Y 循环控制 ? N 循环结束 5.4 循环程序 包含多次重复执行的程序段,循环结构使程序紧凑。 • 5.4.1 循环程序的导出 各个环节任务: 一.初始化部分 循环准备工作。如:清结果单元、设指针、设循环控制变量初值等。 二.循环体 循环工作部分: 需多次重复处理的工作。 循环控制部分: 1.修改指针和循环控制变量。
2.检测循环条件:满足循环条件,继续循环,否则退出循环。2.检测循环条件:满足循环条件,继续循环,否则退出循环。 • 三.结束部分 • 处理和保存循环结果。1)已知循环次数 2)循环次数未知 • 允许0次循环的循环结构:在循环工作之前检测循环条件。
单重循环简单循环结构:循环体中不套循环。 • 例:求n个单字节数据的累加,设数据串已在43H起始单元,数据串长度在42H单元,累加和不超过2个字节。 SUM:MOV R0,#42H;设指针 MOV A,@R0 MOV R2,A;循环计数器R2←n CLR A ;结果单元清0 MOV R3,A ADD1:INC R0 ;修改指针 ADD A,@R0 ;累加 JNC NEXT ;处理进位 INC R3 ;有进位,高字节加1
NEXT:DJNZ R2,ADD1 ;循环控制:数据是否加完? • MOV 40H,A ;循环结束,保存结果 • MOV 41H,R3 • RET 循环控制方法:计数控制、特征标志控制。 一.计数控制:设循环计数器,控制循环次数。正计数和倒计数两种方式。 例:为一串7位ASCII码数据的D7位加上奇校验,设数据存放在片外RAM的2101H起始单元,数据长度在2100H单元。
MOV DPTR,#2100H MOVX A,@DPTR MOV R2,A NEXT:INC DPTR MOVX A,@DPTR ORL A,#80H JNB P,PASS ;判断是否加校验 MOVX @DPTR,A;是,则加奇校验 PASS:DJNZ R2,NEXT DONE:SJMP DONE
例:找正数表最小值。正数表存在片外RAM中以LIST为起始单元,用-1作为结束标志。例:找正数表最小值。正数表存在片外RAM中以LIST为起始单元,用-1作为结束标志。 二.特征控制:设定循环结束标志实现循环控制。 START:MOV DPTR,#LIST ;数表首地址 MOV B,#127 ;预置最小值 NEXT: MOVX A,@DPTR ;取数 INC DPTR ;修改指针 CJNE A,#0FFH,NEXT1;是否为数表结尾? SJMP DONE ;循环结束 NEXT1:CJNE A,B,NEXT2 ;比较 NEXT2:JNC NEXT;Cy=1,则A<B;Cy=0,则A>B MOV B,A ;保存较小值 SJMP NEXT DONE: SJMP DONE
5.4.2 多重循环:循环体中套循环结构。以双重循环使用较多。 • 例: 将内存一串单字节无符号数升序排序。 • 步骤: • 每次取相邻单元的两个数比较,决定是否需要交换数据位置。 • 第一次循环,比较N-1次,取到数据表中最大值。 • 第二次循环,比较N-2次,取到次大值。 • … • 第N-1次循环:比较一次,排序结束。
开始 外循环初始化 内循环初始化 循环工作 内循环控制 ? 外循环控制 ? 循环结束
开始 外循环次数→R4 内循环次数→R3 表首TAB→R0 B ←(R0) A ← ( R0 + 1 ) Y A ≥ B? N (R0)←→(R0+1) R0 ← R0+1 R3←R3—1=0 ? R4←R4—1=0 ? 循环结束 • SORT: MOV A,#N-1;N个数据排序 • MOV R4,A;外循环次数 • LOOP1: MOV A,R4 • MOV R3,A;内循环次数 • MOV R0,#TAB;设数据指针 • LOOP2: MOV A,@R0;取二数 • MOV B,A • INC R0 • MOV A,@R0 CJNE A,B,L1 ;比较
L1: JNC UNEX ;A≥B,不交换 • DEC R0 ;否则交换数据 • XCH A ,@R0 • INC R0 • MOV @R0,A • UNEX: DJNZ R3,LOOP2;内循环结束? • DJNZ R4,LOOP1;外循环结束? • RET
软件延时程序用循环程序将指令重复多次执行,实现软件延时。软件延时程序用循环程序将指令重复多次执行,实现软件延时。 • 试计算延时程序的执行时间。 • 源程序 指令周期(M) 指令执行次数 DELAY:MOV R6,#64H 1 I1: MOV R7,#0FFH 1 I2: DJNZ R7,I2 2 DJNZ R6,I1 2 RET2 1 100 100×255 100 1 延时时间计算:(设时钟f=12MHz) t=(1×1+1×100+2×100×255+2×100+2×1)×M=51.3 ms
5.5.2 编写循环程序应注意的问题 • 循环体前应注意安排循环初始化; • 内外循环间不应相互交叉。
例5-19 一个十六进制数存放在HEX单元的低四位,将其转换成ASCII码并送回HEX单元。 • 编程: ORG 0100H • HEX EQU 30H • HEXASC:MOV A, HEX • ANL A ,#00001111B • ADD A, #3 ;变址调整 • MOVC A, @A+PC • MOV HEX,A ;2字节 • RET ;1字节 5.5 查表程序
ASCTAB:DB 30H,3lH,32H,33H • DB 34H,35H,36H,37H • DB 38H,39H,41H,42H • DB 43H,44H,45H,46H • END
5.6 子程序设计子程序:能完成某项特定功能的独立程序段,可被反复调用。 • 优点: • (1)不必重复书写同样的程序,提高编程效率; • (2)程序的逻辑结构简单,便于阅读; • (3)缩短了源程序和目标程序的长度,节省了程序存储器空间; • (4)使程序模块化、通用化,便于交流,共享资源; • (5)便于按某种功能调试。
1. 子程序结构 • 子程序入口用标号作为子程序名。 • 调用子程序之前设置好堆栈。 • 用返回指令RET结束子程序,并保证堆栈栈顶为调用程序的返回地址。 • 子程序嵌套须考虑堆栈容量。 • 提供足够的调用信息: • 如:子程序名、子程序功能、入口参数和出口参数、子程序占用的硬件资源、子程序中调用的其他子程序名。
2. 参数传递 • 入口参数:调用子程序之前,需要传给子程序的参数。 • 出口参数:子程序送回调用程序的结果参数。 • 子程序无须传递参数 • 用累加器和工作寄存器传递参数 • 例5-24 双字节求补子程序CPLD。 • 解 入口参数:(R7R6)=16位数 • 出口参数:(R7R6)=求补后的16位数
CPLD: MOV A,R6 • CPL A • ADD A,#1 • MOV R6,A • MOV A,R7 • CPL A • ADDC A,#0 • MOV R7,A • RET
通过操作数地址传递参数 • 例5-25 n字节求补子程序。 • 解 入口参数:(R0)=求补数低字节指针,(R7)=n-1 • 出口参数:(R0)=求补后的高字节指针 • CPLN: MOV A,@R0 • CPL A • ADD A,#1 • MOV @R0,A • NEXT: INC R0
MOV A,@R0 • CPL A • ADDC A,#0 • MOV @R0,A • DJNZ R7,NEXT • RET • 通过堆栈传递参数 例5-26 在HEX单元存放两个十六进制数,将它们分别转换成ASCⅡ码并存入ASC和ASC+1单元。
解: 由于要进行两次转换,故可调用查表子程序完成。 • 主程序: • MAIN:… • PUSH HEX ;取被转换数 • LCALL HASC ;转子 • *PC→ POP ASC ;ASCL—ASC • MOV A,HEX ;取被转换数 • SWAP A ;处理高四位 • PUSH ACC • LCALL HASC ;转子程序 • POP ASC+1 ;ASCH→ ASC+1 • …
子程序: • HASC:DEC SP • DEC SP ;修改SP指向HEX • POP ACC ;弹出HEX • ANL A,#0FH ;屏蔽高四位。 • ADD A,#5 ;变址调整 • MOVC A,@A+PC ;查表 • PUSH ACC ;结果入栈 (2字节)
INC SP ; (1字节) • INC SP ;修改SP指向断点位置(1字节) • RET ; (1字节) • ASCTAB:DB ‘0 1 2.. .7’ • DB ‘8 9 A.. .F ’ • 3. 现场保护推入与弹出的顺序应按“先进后出”,或“后进先出”的顺序,才能保证现场的恢复。
例:十翻二子程序的现场保护。 • BCDCB:PUSH ACC • PUSH PSW • PUSH DPL ;保护现场 • PUSH DPH • ┇ • POP DPH • POP DPL • POP PSW ;恢复现场 • POP ACC • RET
4. 设置堆栈 • 调用子程序时,主程序的断点将自动入栈;转子后,现场的保护都要占用堆栈工作单元,尤其多重转子或子程序嵌套,需要使栈区有一定的深度。由于MCS-51的堆栈是由Sp指针组织的内部RAM区,仅有128个地址空间,堆栈并非越深越好,深度要恰当。
5.6.3 子程序的调用及嵌套 • 1.子程序调用 • 例5-27:将内RAM41H~43H的内容左移4位,移出部分送40H单元。 • 编制左移1位子程序,再编制左移4位子程序,调用4次左移1位子程序。 • 左移1位子程序RLC1: • RLC1: CLR C • LOOP0: MOV A,@R0 • RLC A • MOV @R0,A
DEC R0 DJNZ R4,LOOP0 MOV A,@R0 RLC A MOV @R0,A RET 左移4位子程序RLC4: RLC4:MOV R7,#4;R7位计数器 NEXT:MOV R0,#43;低字节地址 MOV R4,#3
ACALL RLC1;转子程序1 • *PC DJNZ R7,NEXT;未完继续 • MOV A,@R0 • ANL A,#0FH;屏蔽高4位 • MOV @R0,A;保存高4位 • RET *PC是子程序返回地址
2.子程序嵌套 断点入栈 断点出栈