第3章 80x86汇编语言程序设计(下).

Slides:



Advertisements
Similar presentations
输入输出程序设计 输入输出的基本概念 无条件方式输入输出 查询方式输入输出 中断方式输入输出.
Advertisements

汇编语言程序设计 吴 向 军 中山大学计算机科学系
x86/Pentium指令系统 x86寻址方式 1.比例变址寻址方式 (Scaled Indexed Addressing)
微型计算机技术 教 学 指 导(七) 太原广播电视大学 郭建勇.
第10章 DOS功能调用与BIOS中断调用.
第7章 8086/8088汇编语言程序设计 7.1 引言 7.2 顺序程序设计 7.3 分支结构程序设计 7.4 循环结构程序设计
第四章 汇编语言 程序设计 任课教师:王晓甜
大连理工大学软件学院 软件工程系 赖晓晨 计算机组成与结构 大连理工大学软件学院 软件工程系 赖晓晨
本周实验安排 实验内容:(P231)人名排序的例子。
微机原理与接口技术 微机原理与接口技术 朱华贵 2015年10月30日.
第3章 80x86汇编语言程序设计(中) 时间不够的情况下只讲16位汇编.
4.1 汇编语言 4.2 顺序结构程序 4.3 分支程序设计 4.4 循环程序设计 4.5 子程序设计
第3章 80x86汇编语言程序设计(上) 16位汇编版本 时间不够的情况下只讲16位汇编.
9.1 可编程并行输入/输出接口芯片8255A 9.2 可编程计数器/定时器 可编程串行输入/输出接口芯片8251A
3.3.5 程序控制指令 控制转移指令分为: 转移指令 循环控制指令 调用和返回指令 中断指令.
微机原理与接口技术 微机原理与接口技术 朱华贵 2015年11月26日.
微机原理与接口技术 第3章 8086/8088指令系统 黄强 深圳大学 信息工程学院.
第7章 中断与异常.
第5章 循环与分支程序设计  循环程序设计  分支程序设计.
汇编语言程序设计 Assembly Language Programming
第三章 寻址方式与指令系统 3.1 寻址方式 一条指令通常由两大部分构成: 操作码 操作数
微机原理与接口技术 第3章 8086指令系统 朱华贵 2015年09月25日.
第7章 并行接口 7.1 简单并行接口 7.2 可编程并行接口8255A 7.3 键盘接口 7.4 LED显示器接口.
第2章 MCS-51单片机指令系统与汇编语言程序设计
微机原理与接口技术 第2章 8086系统结构 朱华贵 2015年09月17日.
第九章 计数器和定时器电路 第一节 概述 第二节 Intel 8253的控制字 第三节 Intel 8253的工作方式 第九章 计数器和定时器电路 第一节 概述 第二节 Intel 8253的控制字 第三节 Intel 8253的工作方式 第四节 Intel 8253在IBM PC机上的应用.
Assembly Language Programming
复 习 一. 计算机中的数和编码 1. 2,10,16进制数及其之间的转换(整数) 按权展开,除x取余 2
微机原理与接口技术 微机原理与接口技术 朱华贵 2015年12月10日.
微机原理与接口技术 微机原理与接口技术 朱华贵 2015年11月05日.
输入输出与中断 主要内容 CPU与外设之间数据传送方式 中断技术 8086中断系统和中断处理.
第八章 输入输出程序设计 总线 CPU MEM I/O接口 I/O设备.
第3章 IA-32指令系统 3.1 基本数据类型 3.2 IA-32的指令格式 3.3 IA-32指令的操作数寻址方式
汇编语言程序设计 吴 向 军 中山大学计算机科学系
第8章 寻址方式与指令系统.
第4章 汇编语言程序设计 4.1 程序设计语言概述 4.2 汇编语言的程序结构与语句格式 4.3 汇编语言的伪指令
微机原理及应用 主讲:谢维成 西华大学 电气信息学院 1.
基本的”防”黑客技术 Basic” ” Hacker Technique
第一章 8086程序设计 第二章 MCS-51程序设计 第三章 微机基本系统的设计 第四章 存贮器与接口 第五章 并行接口
汇编语言程序设计课程设计 第二次实验 DEBUG基本命令与算术运算指令
微型计算机原理及应用.
第3章 微型计算机输入输出接口 3.1 输入/输出接口 3.2 输入输出数据传输的控制方式 3.3 开关量输入输出接口 欢迎辞.
第六章 子程序结构 §6.1 子程序的设计方法 §6.2 嵌套与递归子程序 §6.3 子程序举例 §6.4 DOS系统功能调用.
第 13 章 中断系统.
條件處理.
第九章 高级宏汇编语言 9.1 结构 结构就是将逻辑上有一定关系的一组数据,以某种方式组合在一起所形成的数据形式。
第5章 循环与分支程序设计 学习目标: 了解并掌握循环程序的构造方法,尤其是对循环控制条件的设置以及可能出现的边界情况的考虑。掌握起泡排序算法这种多重循环程序设计中的常用方法。交换标志位的设置在此算法中更能提高效率。学会在数组排序算法中采用折半查找法来提高查找效率。学会使用跳跃表法实现CASE结构。
4.1 汇编语言程序格式 4.2 MASM中的表达式 4.3 伪指令语句 4.4 DOS系统功能调用和BIOS中断调用
3.4.5 控制转移指令(Control transfer instructions) (一)、控制转移指令概述
微机原理与接口技术 微机原理与接口技术 朱华贵 2015年11月19日.
第二章 8086微处理器. 第二章 8086微处理器 微处理器的基本结构 8086微处理器的主要特性和内部结构 8086CPU的工作模式和引脚信号 8086的存储器管理 8086的总线操作和时序.
第4章 汇编语言程序格式  汇编程序功能  伪操作  汇编语言程序格式  汇编语言程序的上机过程.
第10章 可编程外围接口芯片8255A及其应用 10.1 概述 A的工作原理 A的应用举例.
习题3 1、 分别说明下列指令的原操作数和目的操作数各采用什么寻址方式。 设定如下: ①立即寻址 ② ① ②寄存器寻址
3.4.2 算术运算指令(Arithmetic) 算术运算指令内容: 8086/8088提供加、减、乘、除等六种基本算术操作
第九章 BIOS和DOS中断 在存储器系统中,从地址0FE000H开始的8K ROM(只读存储器)中装有BIOS(Basic Iuput /output System)例行程序。驻留在ROM中的BIOS给PC系列的不同微处理器提供了兼容的系统加电自检,引导装入,主要I/O设备的处理程序以及接口控制等功能模块来处理所有的系统中断。使用BIOS功能调用,给程序员编程带来很大方便,程序员不必了解硬件操作的具体细节,直接用指令设置参数,然后中断调用BIOS中的子功能,所以利用BIOS功能编写的程序简洁,可读性好,
第2章 80x86计算机组织  计算机系统  存储器  中央处理机  外部设备.
《微型计算机原理与接口技术》 第4版 王良 宁德师范学院 吴宁 乔亚男 编著 清华大学出版社 出版
第5章 循环与分支程序设计  循环程序设计  分支程序设计.
微机原理与接口技术 微机原理与接口技术 朱华贵 2015年12月17日.
第八章 中断系统.
微机原理与接口技术 西安邮电大学计算机学院 宁晓菊.
微机原理与接口技术 微机原理与接口技术 朱华贵 2015年11月06日.
第3章 80x86的指令系统和寻址方式 § x86的寻址方式 § x86的指令系统
第6章 子程序结构 在程序设计中,我们会发现一些多次无规律重复的程序段或语句序列。解决此类问题一个行之有效的方法就是将它们设计成可供反复调用的独立的子程序结构,以便在需要时调用。在汇编语言中,子程序又称过程。 调用子程序的程序称为主调程序或主程序。 2019/7/20 ch6.
微机原理与接口技术 第5章 汇编语言程序设计 西安邮电大学计算机学院 王 钰.
大数据搜索挖掘实验室 第五章 子程序设计 张华平 副教授 博士 Website: 大数据搜索挖掘实验室
第4章 MCS-51汇编语言程序设计 教学基本要求: (1)、了解MCS-51汇编语言程序设计的特点;
第4章 汇编语言程序格式  汇编程序功能  伪操作  汇编语言程序格式  汇编语言程序的上机过程
第三章 8086的指令系统 8086指令特点 8086的寻址方式 8086的指令格式及数据类型 8086的指令集.
Presentation transcript:

第3章 80x86汇编语言程序设计(下)

3.5 分支结构程序设计 分支结构是指计算机根据实际情况或条件,作出判断和选择,转而执行不同的程序段的一种程序结构。 Y N N 条件 条件 程序段A 程序段B

根据某个控制字的各“位”状态实行多路转移 多路分支结构 根据某个控制字的各“位”状态实行多路转移 多路条件测试 程序段1 程序段2 …… 程序段n

3.5.1无条件转移指令 JMP 1、段内转移 格式1:JMP SHORT OPR ;段内相对短转移 操作1:IP<--(IP)+disp8 说明:转移范围-128字节至+127字节,操作数OPR为段内某个标号。 段内相对短转移示例 指令JMP SHORT ADDT 存放在CS:0200H 中, 标号ADDT对于IP指针的偏移量为1DH, 则转移地址为0202H+001DH=021FH JMP SHORT ADDT 0200 E8 … 1 1D ADDT: MOV AL, 40H 2 ADD AL, BL +1DH 021F B0 0220 1D

格式2:JMP OPR JMP NEAR PTR OPR ;段内相对近转移 操作2:IP<--(IP)+disp16 说明:转移范围-32KB至+32KB, 操作数OPR为段内某个标号。 格式3:JMP WORD PTR OPR ;段内间接转移 操作3:IP<--(OPR) 说明:OPR是基址/变址寄存器或存储器操作数。 段内间接转移示例 ADDRESS DW 2000H ;定义转移地址 ... LEA SI, ADDRESS ;偏移量-->SI JMP WORD PTR[SI] ;转移到CS:2000

格式4:JMP FAR PTR OPR ;段间直接转移 操作4:IP<--OAopr CS<--(CS)opr 2、段间转移 格式4:JMP FAR PTR OPR ;段间直接转移 操作4:IP<--OAopr CS<--(CS)opr C1段 EA OP码 段间直接转移示例 50 新IP=0250H ;代码段C1 02 …… 00 新CS=2000H JMP FAR PTR NEXT 20 …… ;代码段C2 C2段 20000H NEXT: MOV AL,10H … NEXT 20250H

格式5:JMP DWORD PTR OPR ;段间间接转移 操作5:IP<--((DS)*16+OPR) CS<--((DS)*16+OPR+2) 段间间接转移示例 JMP DWORD PTR [4000H] 设 (DS)=1000H (14000H)=0010H (14002H)=5000H 执行后 (CS)=5000H (IP)=0010H

3.5.2 条件转移指令 格式:J条件 标号 操作:测试条件,若满足,则跳转到标号处执行, 即 IP<--(IP)+disp8 ; 否则,执行后续指令 说明:根据上一条指令所设置的条件码判别测试条件 转移范围在-128到+127字节

条件转移指令(1) 操作符 功能 测试条件 JC 进位标志为1转移 CF=1 JNC 进位标志为0转移 CF=0 操作符 功能 测试条件 JC 进位标志为1转移 CF=1 JNC 进位标志为0转移 CF=0 JZ/JE 等于0/相等转移 ZF=1 JNZ/JNE 不等于0/不相等转移 ZF=0 JS 符号标志为1转移 SF=1 JNS 符号标志为0转移 SF=0 JO 溢出转移 OF=1 JNO 无溢出转移 OF=0 JP/JPE 偶状态转移 PF=1 JNP/JPO 奇状态转移 PF=0 JCXZ CX=0转移 CX=0 JECXZ ECX=0转移 ECX=0 JPE E=EVEN JPO O=ODD

条件转移指令(2) A-B 比较情况 无符号数 有符号数 指令 判断条件 指令 判断条件 A>B JA ZF=0,CF=0 JG SF=OF JNBE JNLE 且 ZF=0 A>=B JAE ZF=1或 JGE SF=OF JNB CF=0 JNL 或ZF=1 A<B JB ZF=0,CF=1 JL SF<>OF JNAE JNGE 且ZF=0 A<=B JBE ZF=1或 JLE SF<>OF JNA CF=1 JNG 或ZF=1

条件转移指令示例1 将X中十六进制的ASCII码转换成其所对应的数值,存放到HEX中。如‘A’应转换为10。 注意ASCII中‘0’~‘9’是30H~39H,’A’~’F’是41H~46H MOV AH, X CMP AH, 39H JBE NEXT ;≤39H则转 SUB AH, 7 ;是‘A’--’F’,减7 NEXT: SUB AH, 30H ;减30H MOV HEX, AH

如果条件转移目标地址超出-128~+127的范围怎么办? 条件转移指令示例2 CMP AX, 0FFFFH JLE P3 CMP AX,1 JL P2 MOV AX, 1 JMP DONE P2: MOV AX, 0 P3: MOV AX,-1 DONE: AX≤(-1) Y N N Y AX<1 AX<--1 AX<--0 AX<--(-1) 如果条件转移目标地址超出-128~+127的范围怎么办?

3.5.3 分支结构程序设计 教材上的例子请看P99中的例3.18和例3.19,上机实现例3.18程序。 1、比较/转移 利用比较和条件转移指令实现两路分支。 比较结果记录在某些标志位中,条件转移指令 根据约定的条件进行对照,满足条件时转移,不满 足条件时不转移。 2、跳转表转移 利用跳转表实现多路分支。 比较/转移指令可嵌套,但程序结构复杂, 跳转表可使程序结构清晰。 教材上的例子请看P99中的例3.18和例3.19,上机实现例3.18程序。

有一个首地址为ARRAY的N字数组,将其中正数的个数放在DI中,0的个数放在SI中,负数个数放在AX中

MOV CX, N MOV BX, 0 ;初始化 MOV DI, BX ; 正数个数计数器初始化 MOV SI, BX ; 0的个数计数器初始化 AGAIN:CMP WORRD PTR ARRAY[BX], 0 ;数组当前元素与0比较 JLE LEEQ ;小于等于0转移 INC DI ;正数计数 JMP NEXT LEEQ: JL NEXT ;小于0转移 INC SI ;0计数 NEXT: ADD BX, 2 ;数组表指针指向下一元素 DEC CX JNZ AGAIN MOV AX, N ;负数个数=N-DI-SI SUB AX, DI SUB AX, SI

AL<--(N1),AH<--(N2) D1<--(AL),D2<--(AH) 设字节单元N1、N2中存放无符号数 (1)若两个均是偶数,则分别加1后送D1 、D2中 (2)若两个均是奇数,则直接送D1 、D2中 (3)若一个是奇数,一个是偶数,则把奇数送D1,偶数送D2中 AL<--(N1),AH<--(N2) 注意:根据条件,当N1是奇数时,无论N2是奇数还是偶数,都只需直接送D1、D2 偶 (AL)0=0 奇 偶 奇 (AH)0=0 (AL) (AH) AL<--(AL)+1 AH<--(AH)+1 D1<--(AL),D2<--(AH)

程序如下: MOV AL, N1 MOV AH, N2 TEST AL, 01H ;测试 N1的奇偶 JNE ENDO ;N1为奇数 TEST AH, 01H ;测试 N2的奇偶 JNE L1 ;N2是奇数,转移 INC AL ;两个均是偶数 INC AH JMP ENDO L1: XCHG AL, AH ;N1是偶数, N2是奇数 ENDO: MOV D1, AL ;存放结果 MOV D2, AH 转上页

利用跳转表实现多路分支 跳转表是在某一内存区域顺序排列的一组有规律的入口地址。 如是段内分支,每个地址占两个单元(IP的值) 如是段间分支,每个地址占4个单元(CS:IP的值) TABLE SUB1 TABLE SUB1 SUB2 SUB3 SUB2 IP IP IP CS IP IP CS 段内转移 段间转移

根据AL中哪一位为1(从低位到高位)把程序转移到8个不同的程序分支去 .DATA TABLE DW ROUTINE_1 DW ROUTINE_2 DW ROUTINE_3 DW ROUTINE_4 DW ROUTINE_5 DW ROUTINE_6 DW ROUTINE_7 DW ROUTINE_8 .CODE …… ROUTINE_1: MOV AX,0 ROUTINE_2: CMP AL, 3 ROUTINE_8: CLI

用寄存器间接寻址与基址变址寻址该如何做? 用变址寻址方式 CMP AL, 0 JE DONE MOV SI, 0 L: SHR AL, 1 JNB NOT_YET ; CF=0或ZF=1跳转 JMP TABLE[SI] NOT_YET: JZ DONE ADD SI, TYPE TABLE ;Type Table=2 JMP L DONE: …… 方法2 用寄存器间接寻址方式 CMP AL, 0 JE DONE LEA BX, TABLE L: SHR AL, 1 JNB NOT_YET JMP WORD PTR[BX] NOT_YET: JZ DONE ADD BX, TYPE TABLE JMP L DONE: …… 方法3 用基址变址寻址方式 MOV SI, 7*TYPE TABLE MOV CX, 8 L: SHL AL, 1 JMP WORD PTR[BX][SI] SUB SI, TYPE TABLE 用寄存器间接寻址与基址变址寻址该如何做?

在附加段中有一个从小到大排序的无符号数字数组,其首地址在DI中,数组的第一个单元存放数组长度。要求在数组中查找(AX),如找到,CF=0,并在SI中给出该元素在数组中的偏移地址;如未找到,CF=1。 算法:在R数组中查找K,采用折半查找法 LOW1, HIGHN; 若LOW>HIGH,则查找失败,置CF=1,退出程序。否则,计算中点:MID(LOW+HIGH)/2; (3) K与R[MID]比较。若=R[MID],则查找成功,程序结束; 若K<R[MID]则转(4);若K>R[MID],则转(5); (4) HIGHMID-1,转(2); (5) LOWMID+1,转(2)。

CMP AX, ES:[DI+2] ;与第一个数比较 JA CHK_LAST ;(AX)>(ES:[DI+2]) 转 LEA SI, ES:[DI+2] JE EXIT ;相等,找到,就是第一个数 STC ; 小于第一个数,失败 JMP EXIT CHK_LAST:MOV SI, ES:[DI] ;取数组长度 SHL SI, 1 ; 长度*2(DW型) ADD SI, DI CMP AX, ES:[SI] ; 与最后的数比较 JB SEARCH ; 小于则转 JE EXIT ; 相等则结束 STC ; 大于最后一个,失败

SEARCH: MOV LOW_IDX, 1 ; 给LOW赋初值 MOV BX, ES:[DI] ; 取数组长度 MOV HIGH_IDX, BX ; 给HIGH赋初值 MOV BX, DI ; BX中放首地址 MID: MOV CX, LOW_IDX MOV DX, HIGH_IDX CMP CX, DX JA NO_MATCH ; LOW>HIGH,失败 ADD CX, DX SHR CX, 1 ; 折半 MOV SI, CX SHL SI, 1 ; *2 (DW型) COMPARE:CMP AX, ES:[BX+SI] ; 与中间数比较 JE EXIT ; 相等,找到

JA HIGHER ; 大于中间数,转 DEC CX MOV HIGH_IDX, CX ; 调整查找区间到前半部分 JMP MID HIGHER: INC CX MOV LOW_IDX, CX ; 调整查找区间到后半部分 NO_MATCH: STC EXIT: ……

3.6 循环结构程序设计 任务需要重复执行某一程序段,这种情况采用循环结构来实现。 初始化 初始化 N 控制部分 循环体 Y 修改部分

3.6.1 循环指令 格式:指令码 标号; (CX中存放循环次数) 操作符 操 作 功 能 LOOP CX<--(CX)-1 循环 操作符 操 作 功 能 LOOP CX<--(CX)-1 循环 若(CX)<>0,则循环 LOOPZ CX<--(CX)-1 当CX不为零且 LOOPE 若(CX)<>0且ZF=1,则循环 相等时循环 LOOPNZ CX<--(CX)-1 当CX不为零且 LOOPNE 若(CX)<>0且ZF=0,则循环 不相等时循环

循环指令示例1 求长度为10的字节数组ARRAY之和,并将和存入TOTAL中 LEA SI, ARRAY ;数组首地址-->SI MOV CX, 10 ;数组长度-->CX MOV AX, 0 AGAIN: ADD AL, [SI] ;求数组和 ADC AH, 0 INC SI ;修改指针 LOOP AGAIN MOV TOTAL, AX ;存和 其中 语句 LOOP AGAIN 相当于: DEC CX JNZ AGAIN

循环指令示例2 在某一字节串中寻找第一个非0字节 设串首地址在DI中,串末地址在BX中 SUB BX, DI ;串长度在BX中 INC BX MOV CX, BX ; 串字节数-->CX DEC DI AGAIN: INC DI ;修改指针 CMP BYTE PTR [DI], 0 ;串元素=0? LOOPZ AGAIN ;循环查找 JNZ FOUND ;找到非0字节跳转 …… FOUND:

3.6.2 串操作指令 串——存储器中一序列字或字节单元,单元中的内 容是字符或数据 串操作——对序列字或字节单元中的内容进行某种 操作 串操作指令有7条: 1、MOVS——串传送指令 2、CMPS——串比较指令 3、SCAS——串扫描指令 4、LODS——装入串指令 5、STOS——存储串指令 6、INS——串输入 7、OUTS——串输出

说明: 每条指令有三种形式,分别对应于字节操作、 字操作和双字操作 如 MOVSB 字节操作 MOVSW 字操作 MOVSD 双字操作 与此配合使用的指令前缀有: REP 重复 REPE/REPZ 相等/为零则重复 REPNE/REPNZ 不相等/不为零则重复

例:将字节串从源区传送到目的区 源区首偏址-->SI 目的区首偏址-->DI,串长-->CX Y CX=0 N 按SI所指取一字节 结束 按DI所指存此字节 (SI)+1-->SI 用一般传送指令实现 的流程图 (DI)+1-->DI (CX)-1-->CX

源区首偏址-->SI 目的区首偏址-->DI 串长-->CX,0-->DF 源区首偏址-->SI CX=0 Y 源区首偏址-->SI 目的区首偏址-->DI 串长-->CX,0-->DF CX=0 N 串传送指令 结束 带前缀REP的 串传送指令 (CX)-1-->CX 用串传送指令实现 的流程图 用带前缀的串传送指令实现的流程图

使用串操作指令时微处理器设计有若干约定: 1、源串地址由DS:SI指定 目的串地址在ES:DI中 2、串长送CX寄存器 3、设置方向标志位DF(在EFLAG寄存器中) 当DF=0(指令CLD)时地址为增量修改 (+1 或 +2 或 +4) 当DF=1(指令STD)时地址为减量修改 (-1 或 –2 或 -4)

方向标志对应的指针移动示意 正向传送 反向传送 DF=0 DF=1 低地址方向 ‘A’ ... 源串 ‘A’ ‘J’ … 1 n 源串 n 高地址方向 1 n 源串 n 1 ‘J’ ... 目的串 … 目的串 正向传送 反向传送 DF=0 DF=1

MOVS 串传送 ES:DI<--(DS:SI) REP SI<--(SI)(+/-)1 DI<--(DI)(+/-)1 符号 功能 操作 相关前缀 MOVS 串传送 ES:DI<--(DS:SI) REP SI<--(SI)(+/-)1 DI<--(DI)(+/-)1 CMPS 串比较 (DS:SI)-(ES:DI) REPZ/REPNZ SCAS 串扫描 (ES:DI)-(AL) REPZ/REPNZ LODS 装入串 AL<--(DS:SI) 一般不联用 STOS 存入串 (ES:DI)<--(AL) REP

INS 串输入 ES:DI((DX)) REP DI(DI)(+/-)1 OUTS 串输出 ((DX))(DS:SI) REP 符号 功能 操作 相关前缀 INS 串输入 ES:DI((DX)) REP DI(DI)(+/-)1 OUTS 串输出 ((DX))(DS:SI) REP SI(SI)(+/-)1 其中DX寄存器中存放的是接口电路的端口号

REPZ CX=0 或 ZF=0 CX<--(CX)-1,继续 REPE SI,DI指向下一元素 串未结束且串相等时继续 重复前缀 终止条件 否则 REP CX=0 CX<--(CX)-1,继续 SI,DI指向下一元素 REPZ CX=0 或 ZF=0 CX<--(CX)-1,继续 REPE SI,DI指向下一元素 串未结束且串相等时继续 REPNZ CX=0 或 ZF=1 CX<--(CX)-1,继续 REPNE SI,DI指向下一元素 串未结束且串不相等 时继续

例:REP MOVSB 传送过程如下: (1)(CX)=0? 若等于0,中止传送, 否则执行下一步 (2)CX(CX)-1 (3)串传送 (4)修改指针 (5)转到(1)

MOVS指令示例 MOV DI, 0100H ; (ES)=3000H MOV CX, 5 CLD ; 地址递增方式 REP MOVSB MOV SI, 0050H ; (DS)=2000H MOV DI, 0100H ; (ES)=3000H MOV CX, 5 CLD ; 地址递增方式 REP MOVSB 执行前 执行后 ‘A’ 20050 00 30100 ‘A’ 20050 30100 ‘B’ 1 00 1 ‘B’ 1 1 ‘C’ 2 00 2 ‘C’ 2 2 ‘D’ 3 00 3 ‘D’ 3 3 ‘E’ 4 00 4 ‘E’ 4 4 ‘F’ 5 00 5 ‘F’ 5 5 ‘A’ ‘B’ ‘C’ ‘D’ ‘E’ 源区 目的区 源区 目的区 SI=0050 DI=0100 SI=0055 DI=0105

串String1和String2分别定义在数据段和附加段中。 比较两串,如相等则转移到标号NEXT处。 CMPS指令示例 串String1和String2分别定义在数据段和附加段中。 比较两串,如相等则转移到标号NEXT处。 String1 DB ‘HELP’ ;定义String1 String2 DB ‘HEPP’ ;定义String2 …… CLD ;DF=0 LEA SI, String1 ;源串地址-->SI LEA DI, String2 ;目的串地址-->DI MOV CX, 4 ;重复次数-->CX REPZ CMPSB ;重复比较 JZ NEXT ;串相等转移 .... NEXT: 之所以用JZ来判断,是由于,如果两个串不相等,则REPZ一定是在ZF=0的时候退出来的,如果串完全相等,则退出的时候ZF=1(因为最后一对也匹配)

在串“That is CAI”中查找字符‘a’,找到,则转到标号FOUND处 SCAS指令示例 在串“That is CAI”中查找字符‘a’,找到,则转到标号FOUND处 String DB ‘That is CAI’ ;定义串 …… CLD ;DF=0 LEA DI, String ;串地址-->DI MOV AL, ‘a’ ;查找字符-->AL MOV CX, 11 ;重复次数-->CX REPNZ SCASB ;重复扫描 JZ FOUND ;找到目的串元素转移 FOUND:

比较SOURCE和DESTIN (串长度为100个字节),并将串中的第一个不匹配元素装入AL寄存器中。 LODS指令示例 比较SOURCE和DESTIN (串长度为100个字节),并将串中的第一个不匹配元素装入AL寄存器中。 …… LEA SI, SOURCE ;源串偏移量-->SI LEA DI, DESTIN ;目的串偏移量-->DI CLD ;DF=0 MOV CX,100 ;重复比较次数-->CX REPZ CMPSB ;重复串比较 JCXZ MATCH ;没有不匹配元素跳转 DEC SI ;指向不匹配元素 LODSB ;装入不匹配元素到AL ... MATCH:

给首地址为BUF,长度为1000个字节的存储器区域清零。 STOS指令示例 给首地址为BUF,长度为1000个字节的存储器区域清零。 BUFF DB 1000 DUP(?) ;定义缓冲区 …… CLD ;DF=0 LEA DI, BUFF ;缓冲区首地址-->DI MOV CX, 1000 ;重复次数 MOV AL, 0 ;0-->AL REP STOSB ;重复存储串

将存储区A到A+i中的数据传送到存储区B到B+i中,要求存放的顺序与原先的顺序相反。 综合应用例1 将存储区A到A+i中的数据传送到存储区B到B+i中,要求存放的顺序与原先的顺序相反。 B +1 +2 ‘b’ B+i ‘a’ A ‘a’ +1 ‘b’ +2 ‘c’ A+i

LEA SI, A LEA DI, B ADD DI, I ;DI指向存储区B的末尾 MOV CX, I+1 ;串的长度 LP: CLD ;DF=0 LODSB ;从源区取一数据 STD ;DF=1,改变方向 STOSB ;存入目的区 DEC CX JNZ LP

3.6.3 循环结构程序设计 循环程序的组成: 1、初始化部分 设置初始值 2、循环工作部分 具体的操作和运算 1、初始化部分 设置初始值 2、循环工作部分 具体的操作和运算 3、循环修改部分 为执行下一循环而修改某些参数 4、循环控制部分 判断循环继续还是结束 循环控制方法有: (1)计数控制法 增数法 减数法 (2)条件控制法

单重循环程序设计 将以s1为起始地址的26个字母依次传送到以s2为起始地址的连续单元中。 数据定义如下: .DATA S1 DB ‘ABCD……XYZ’ .DATA ESTRA S2 DB 26 DUP(?)

方法1 采用寄存器间接寻址方式 MOV AX, SEG S1 ;初始化部分 MOV DS, AX MOV AX, SEG S2 MOV ES, AX MOV SI, OFFSET S1 MOV DI, OFFSET S2 MOV CX, 26 LOP1: MOV AL, [SI] ;工作部分 MOV ES:[DI], AL INC SI ;修改部分 INC DI LOOP LOP1 ;控制部分

方法2 采用串处理指令 MOV AX, SEG S1 ;初始化部分 MOV DS, AX MOV AX, SEG S2 MOV ES, AX LEA SI, S1 LEA DI, S2 MOV CX, 26 CLD REP MOVSB ;工作、修改、控制合为一条指令

计数控制法 计数控制法适用于循环次数已知的场合 1、增数法 初始化时循环计数器置0,每执行一次循环体后计数器加1,并与已知的循环次数比较,如相等则退出循环。 增数法一般用比较指令和条件转移指令实现循环转移。 2、减数法 初始化时循环计数器置为循环次数,每执行一 次循环体后计数器减1,并测试循环计数器是否为0, 如为0则终止循环。 减数法一般用循环指令形成循环回路。

如何做N阶乘N! 增数法 计算S=1+2+3+···+50, 结果存入AX中 NUM DW 1 ; ...... MOV CX,0 ;初始化 MOV AX,0 ROTATE: ADD AX, NUM ;累加 INC WORD PTR[NUM] INC CX ;计数器加1 CMP CX,50 ;与已知的循环次数比较 JNZ ROTATE MOV S, AX 如何做N阶乘N!

将内存中6个十进制数的ASCII码转换为非压缩BCD码,并存放在后继相应单元中,如错,存放0FFH。 减数法 将内存中6个十进制数的ASCII码转换为非压缩BCD码,并存放在后继相应单元中,如错,存放0FFH。 ASCBUF DB 35H, 38H, 30H, 4DH, 39H, 32H DB 6 DUP(?) ...... MOV DI OFFSET ASCBUF MOV CX, 6 LAB_1:MOV BL, 0FFH ; 设置错误标志 MOV AL, [DI] CMP AL, 3AH JNB OK ;大于等于3A则错 SUB AL, 30H JC OK ; 小于30H也错 MOV BL, AL OK: MOV AL, BL MOV [DI+06H], AL INC DI LOOP LAB1 ...... 十进制数字‘0’~‘9’的ASCII是30H~39H

条件控制法 在许多情况下,事先无法确定循环次数,这时可选用“条件”来控制循环。在问题的求解过程中,找出一个终止循环的条件。 每循环一次,对条件进行一次检测,如满足终止循环的条件,便退出循环,否则继续循环。 利用条件转移指令控制循环是否结束。 有些情况下为防止死循环,可以附加一个合适的循环次数。

条件控制法 求字符串长度。从STRN地址开始有一个字符串,以‘$’作为结束标志,长度不超过100个字节,要求统计该字符串长度并存于LENG单元。 分析:1)设DX存放统计的串长度,为防止程序死循环,可根据串长不超过100作为循环结束的附加条件。 2)如果程序运行过程中找到‘$’则正常退出循环,如找不到‘$’,由于CX的初值为100,故不会使程序死循环。 定义数据段如下: .DATA STRN DB ‘XCVFATTDEQJHI…’,’$’ LENG DB 0

…… MOV AX, 0 MOV DX, AX ; DX清零 LEA DI, STRN ; 串指针赋初值 MOV CX, 100 ; 初始值为100 MOV AL, ‘$’ LP: CMP AL, [DI] JE DONE ; 条件控制 INC DX ; 串长+1 INC DI ; 串指针后移一个字节 LOOP LP ; 附加条件 DONE: MOV LENG, DL; 存字符串长度

多重循环程序设计 多重循环就其本质而言,就是循环层1包含循环层2,循环层2可能又包含循环层3…,循环层1为最外层,内层循环是外层循环的循环体的一部分。

如果多重循环都使用LOOP语句,则要保护好外层循环的计数器CX,通常采用压栈或退栈的方法或者转存的方法。 多重循环程序设计

有一个首地址为A的N字数组,使该数组中的数从大到小排序 冒泡排序法 MOV CX, N DEC CX LOOP1: MOV DI, CX ; 暂存外循环计数值 MOV BX, 0 LOOP2: MOV AX, A[BX] CMP AX, A[BX+2] ;比较a(i)与a(i+1) JGE COTINUE XCHG AX, A[BX+2] ;交换 MOV A[BX], AX COTINUE: ADD BX, 2 LOOP LOOP2 MOV CX, DI ;恢复外循环计数值 LOOP LOOP1

3.7 子程序设计 子程序调用(返回)指令 过程定义伪指令 子程序的调用和返回 主程序与子程序的连接 子程序调用中的数据保护与恢复 主程序与子程序之间的参数传递 子程序嵌套与递归

3.7.1 子程序调用(返回)指令 1)CALL 子程序调用指令 格式1: CALL 子程序名 ;段内直接调用 操作1: SP<--(SP)-2 ((SP)+1,(SP))<--(IP) IP<--(IP)+disp16 格式2:CALL reg/mem ;段内间接调用 操作2:SP<--(SP)-2 ((SP)+1,(SP))<--(IP) IP<--(reg) 或 IP<--(mem) 说明:1)类似于段内无条件转移指令,不同的是需要将返回地址IP入栈 2)也可以在子程序名前使用near ptr 前缀

格式3:CALL FAR PTR 子程序名 ;段间直接调用 操作3:SP<--(SP)-2 ((SP)+1,(SP))<--(CS) SP<--(SP)-2 ((SP)+1,(SP))<--(IP) IP<--子程序名对应的偏移量 CS<--子程序所在的段地址

格式4:CALL DWORD PTR mem ;段间间接调用 操作4: SP<--(SP)-2 ((SP)+1,(SP))<--(CS) IP<--(SP)-2 ((SP)+1,(SP))<--(IP) IP<--(EA) CS<--(EA+2) 说明:段间调用须保存返回地址IP和CS 386及其后继机型,用EIP代替IP

2) RET 子程序返回指令 格式 功能 操作 RET 段内返回 IP<--((SP)+1,(SP)) SP<--(SP)+2 RET 段间返回 IP<--((SP)+1,(SP)) CS<--((SP)+1,(SP)) RET EXP 带立即数返回 在上述操作之后再做 SP<--(SP)+EXP

3.5.2 过程定义伪指令 格式:过程名 PROC 属性 ...; 过程体 RET 过程名 ENDP 说明: 1、过程名是该子程序名,也是指令CALL的目标操作数 2、过程的属性有两种:NEAR和FAR,分别表示段内调用和段间调用。若省略,则默认为NEAR 3、至少有一条RET指令从过程中返回,可在过程中的任何位置

3.5.3 子程序的调用和返回 主程序 子程序 SUB PROC . . CALL SUB . (断点) . RET

3.5.4 主程序与子程序的连接 (1)主程序和子程序在同一代码段内 .CODE C_SEG MAIN PROC FAR ;主程序 ... CALL SUB_A MAIN ENDP SUB_A PROC NEAR ;子程序 RET SUB_A ENDP END MAIN

(2)调用程序与子程序不在同一代码段内 ;模块1 ;模块2 .CODE C_SEG1 .CODE C_SEG2 …… …… ;模块1 ;模块2 .CODE C_SEG1 .CODE C_SEG2 …… …… MAIN PROC FAR SUB_1 PROC FAR …… …… CALL FAR PTR SUB_1 RET …… …… RET SUB_1 ENDP MAIN END END SUB_1 END MAIN

示例 C1段 MAIN …… CALL FAR PTR PRO_A 0500:1000 C2段 PRO_A CALL NEAR PTR PRO_B 2000:2500 CALL NEAR PTR PRO_C 2000:3700 …... RET PRO_B …… CALL NEAR PTR PRO_C 2000:4000 RET PRO_C 转1 转2 转3 68

(1) MAIN调用PRO_A之前 SP (3) PRO_A调用PRO_B之后 0100 2500 1000 0500 00FA 4000 (4) PRO_B调用PRO_C之后 SP 00F8 00FC 1000 0500 转 69

(5) PRO_C返回PRO_B之后 SP 4000 00FA 2500 (7) PRO_A调用PRO_C之后 1000 0500 SP (6) PRO_B返回PRO_A之后 00FA (7) PRO_A调用PRO_C之后 SP 00FA 4000 3700 1000 0500 00FC 转 70

(8) PRO_C返回PRO_A之后 SP 00FC 4000 3700 (9) PRO_A返回MAIN之后 1000 SP 0500 0100 转 71

使用DOS功能调用的4CH功能: MOV AH, 4CH INT 21H 利用程序段前缀退出程序的方法 使用DOS功能调用的4CH功能: MOV AH, 4CH INT 21H 使用INT 20H指令 利用程序段前缀法 将主程序设计成一个属性为FAR的过程,由DOS调用该过程执行,由RET指令返回DOS(利用程序段前缀的结构) 72

操作系统加载EXE程序时自动在程序前加上256字节的程序段前缀,DS指向它 INT 20H DS 程序段前缀区 使用程序段前缀 退出程序: 操作系统加载EXE程序时自动在程序前加上256字节的程序段前缀,DS指向它 INT 20H DS 程序段前缀区 程序区 PUSH DS MOV AX,0 PUSH AX 把程序段前缀区第一个字节单元的地址和偏移量压栈保存,这个单元是指令INT 20H,这几句一定要放在主程序开始的位置 RET 指令 把程序段前缀区第一个字节单元的地址和偏移量弹出分别送IP和CS,转去执行INT 20H,从而实现返回DOS 求助!!(旧坛转贴)这是一个实现5+2=7功能的程序,请帮忙看一下,有几个问题想向大家请教!!谢谢了!! 1----------SAMPLE PROGRAM FOR ADD AND DISPLAYING SUM TO THE SCREEN 2----------DATA SEGMENT ;数据段 3----------AUGEND DB 05H 4----------ADDEND DB 02H 5----------SUM DB ? 6----------DATA ENDS 7----------STACK SEGMENT PARA STACK 'STACK' ;堆栈段 8---------- DB 64 DUP(?) 9----------STACK ENDS 10---------CODE SEGMENT ;代码段 11---------START PROC FAR 12--------- ASSUME CS:CODE,DS:DATA,SS:STACK,ES:DATA 13--------- PUSH DS ;保存返回地址 14--------- MOV AX,0 15--------- PUSH AX 16--------- MOV AX,DATA ;初始化DS,ES 17--------- MOV DS,AX 18--------- MOV ES,AX 19--------- MOV AL,AUGEND ;完成05H+02H的程序正文 20--------- ADD AL,ADDEND 21--------- MOV SUM,AL ;存结果 22--------- ADD AL,30H ;将结果变为ASCII码 23--------- MOV DL,AL ;显示结果 24--------- MOV AH,02H 25--------- INT 21H 26--------- RET 27---------START ENDP 28---------CODE ENDS 29--------- END START ;汇编结束 我想问的是14行为什么将AX置零呀,还有23行和24行是什么意思呀,非常感谢您的解答。 发贴时间: 2004-3-25 16:47:04 221.8.*.* ================================================ 首先回答你的第一个问题: 这个问题说起来比较复杂一点。首先先要介绍一下DOS可执行程序的结构,我们知道,DOS执行文件通常有代码段,数据段,堆栈段,有的还有附加段,当DOS可执行程序被装入到内存的时候,DOS会在可执行程序的前面加上一个256字节的数据结构,这个结构称为PSP(Program Segment Prefix, 程序段前缀控制块。该块的结构如下: 偏移量 长度 描述 00H 1 WORD 指令 INT 20H 返回到DOS 02H 1 WORD 程序分配块的底部 04H 1 BYTE 保留 05H 5 BYTE CALL功能调用入口 0AH 2 WORD INT 22H结束地址 0EH 2 WORD INT 23H CTRL-BREAK处理程序地址 12H 2 WORD INT 24H 标准错误处理程序地址 16H 1 WORD 父进程PSP 18H 20 BYTE 句柄表 2CH 1 WORD 环境块地址 2EH 2 WORD 保留 32H 1 WORD 句柄表大小 34H 2 WORD 句柄表地址 38H 24 BYTE 保留 50H 1 WORD INT 21H DOS 调用 52H 1 BYTE FAR RET 53H 9 BYTE 保留 5CH 16 BYTE 为未打开的FCB1使用 6CH 20 BYTE 为未打开的FCB2使用 80H 1 BYTE 命令行参数的长度 81H 127 BYTE 命令行参数 程序真正的代码就紧接在该PSP之后,既从100H开始。程序调入后,CS和SS会自动指到代码段和堆栈段。但DS和ES并不指向数据段和附加段,而是指向PSP段地址(所以我们程序里都会根据实际需要加上16,17,18行来明确地为DS和ES赋值,使他们指向数据段或必要的时候指向附加段)。 现在我们来看看26行,这是一个RET语句,我们知道,RET语句会执行以下操作 IP<-SS:[SP] SP<-SP+2 CS<-SS:[SP] 从而实现过程返回,返回的地址是CS:IP。这是整个程序的结束语句,但是在整个程序的开头以及程序当中并没有CALL这样的语句和这里的RET配对,显然堆栈中就不会有要返回的CS:IP值,这样执行到这句就会出错。而13,14,15三行,正是在人为构造这个返回的CS:IP值,首先讲DS压栈,从前面的分析我们知道,DS目前的值是指向PSP,而AX中是0,所以15行显然是将0压入堆栈。这样一来,26行的RET语句就使得CS:IP=PSP:0000H,我们再来看PSP:0000H的位置放的是INT 20H指令,该指令的功能是返回到DOS,显然就实现了程序的结束,并返回DOS。 下面看第二个问题,这个问题很简单,注意24, 25行是要调用DOS功能调用INT 21H 的02H号功能(DOS功能调用需要将功能号放到AH中所以有24行的语句),02H号功能是写标准输出设备(主要是显示器),该号功能要求将要显示的字符的ASCII码放到DL寄存器中,23行语句就是做这件事情的。 发贴时间: 2004-3-25 19:27:08 218.2.*.* 程序加载结构

存储单元NUM中为一个16位的二进数,统计其中值为1 的位的个数存入RESULT .CODE ...… MAIN PROC FAR JZ DONE PUSH DS SAL AX, 1 MOV AX,0 JNC NEXT PUSH AX INC CL MOV AX, @D_SEG NEXT: JMP LOOP1 MOV DS, AX DONE: MOV RESULT, CL MOV CX,0 RET MOV AX, NUM MAIN ENDP LOOP1: AND AX, AX END MAIN 本程序采用的是用RET返回DOS的方法 循环结束条件是什么?采用这样的条件有什么好处?

3.5.5 子程序调用中的数据保护与恢复 为避免在主程序和子程序中使用相同的寄存器而引起的冲突,需要在子程序中对这些寄存器进行保护。在子程序返回前,恢复这些寄存器原来的值。 保护和恢复现场最佳的办法就是利用堆栈。 以下就是一个保护和恢复现场的例子: SUBT PROC NEAR PUSH AX ;保护现场 PUSH BX PUSH CX …… POP CX ;恢复现场 POP BX POP AX ; 注意弹出的顺序与压栈顺序相反 RET SUBT ENDP

教材上的例子请看P103的例3.21 定义一个过程,它的功能是将AL中的组合BCD码转为ASCII码,再存入BX寻址的连续的两个内存单元中。 TRAN PROC FAR PUSH AX PUSH BX PUSH CX PUSH DX MOV DL, AL MOV CL, 4 SHR AL, CL OR AL, 30H MOV [BX], AL INC BX MOV AL, DL AND AL, 0FH OR AL, 30H MOV [BX], AL POP DX POP CX POP BX POP AX RET TRAN ENDP 教材上的例子请看P103的例3.21

3.5.6 主程序与子程序之间的参数传递 参数传递的方法一般有三种: (1) 寄存器传送 将入口参数和出口参数放在约定的寄存器中 (1) 寄存器传送 将入口参数和出口参数放在约定的寄存器中 适用于参数个数较少的情况 (2) 存储单元传递 有直接存储单元传递和地址表传递两种方法 (3) 利用堆栈传递参数 应注意避免破坏断点

寄存器传递参数示例1 十进制到十六进制数转换。从键盘取得一个十进制数,将其以十六进数形式显示出来。 从键盘取得十进制数(小于65536),保存在BX 调用DECIBIN 调用CRLF 显示回车和换行 调用BINIHEX 用十六进制形式显示BX中的数 调用CRLF

主程序: MAIN PROC FAR REPEAT: CALL DECIBIN CALL CRLF CALL BINIHEX JMP REPEAT MAIN ENDP

DECIBIN PROC NEAR ;出口参数BX PUSH AX ;保护现场 PUSH CX MOV BX, 0 NEWCHAR: MOV AH, 1 INT 21H ;读键盘,输入的ASCII在AL中 SUB AL, 30H JL EXIT ;小于0转 CMP AL, 9 JG EXIT ;大于9转 CBW ; 字节转成字 XCHG AX, BX MOV CX, 10 MUL CX ; 将以前的值乘以10 ADD BX, AX ; 加这一次读的值 JMP NEWCHAR EXIT: POP CX ;恢复现场 POP AX RET DECIBIN ENDP 功能:从键盘取得十进制数,保存在BX

功能:用十六进制形式显示BX中的数 BINIHEX PROC NEAR ;入口参数BX …… MOV CH, 4 ;循环4次(BX中有4位16进制数) ROTATE: MOV CL, 4 ROL BX, CL ; 循环左移四位,从最高位输出 MOV AL, BL AND AL, 0FH ADD AL, 30H CMP AL, 3AH JL PRINTIT ;是0--9 ADD AL, 7 ;是A--F PRINTIT: MOV DL, AL MOV AH, 2 INT 21H ;显示(输出)DL中的一个字符 DEC CH JNZ ROTATE RET BINIHEX ENDP 功能:用十六进制形式显示BX中的数

功能:显示回车和换行 CRLF PROC NEAR PUSH AX PUSH DX MOV DL, 0DH ;回车 MOV AH, 2 INT 21H ; 输出DL中的字符 MOV DL, 0AH ; 换行 POP DX POP AX RET CRLF ENDP

寄存器传递参数示例2 求数组元素之和。 .DATA ARRAY DB 10,20,30,5,60 COUNT EQU $-ARRAY ;数组元素个数 .STACK 100H .CODE START: ... LEA SI, ARRAY ;参数准备 MOV CX, COUNT CALL SUM1 ;求和 ……

;子程序:SUM1 ;入口参数:SI=数组首址,CX=数组长度 ;出口参数:AX=数组和 ;使用寄存器:AX, CX, SI SUM1 PROC NEAR …… CMP CX,0 JZ EXIT XOR AX, AX AGAIN: ADD AL, [SI] ADC AH,0 INC SI LOOP AGAIN EXIT: …… RET SUM1 ENDP

直接存储单元传送示例 求数组元素之和,结果送SUM单元 .DATA ARRAY DW 100 DUP(?) COUNT DW 100 SUM DW ? .CODE ...... CALL PROADD ;主程序

PROADD PROC PUSH AX ;保存现场 PUSH CX PUSH SI XOR AX, AX LEA SI, ARRAY ;直接使用存储单元中数据 MOV CX, COUNT NEXT: ADD AX, [SI] ADD SI, 2 LOOP NEXT MOV SUM, AX ;结果直接送SUM POP SI ;恢复现场 POP CX POP AX RET PROADD ENDP

地址表传递示例 调用子程序前,把所有参数的地址送入地址表,然后把地址表的偏移量通过寄存器带进子程序,子程序从地址表中取得参数地址。 求数组之和 .DATA ARRAY DW 50 DUP(?) COUNT DW 50 SUM DW ? TABLE DW 3 DUP(?) ...... MOV TABLE, OFFSET ARRAY MOV TABLE+2, OFFSET COUNT MOV TABLE+4, OFFSET SUM TABLE ARRAY首址 LEA BX, TABLE +2 COUNT地址 CALL PROADD +4 SUM地址

PROADD PROC ;入口参数 BX为地址表首地址 PUSHA ;保护现场 MOV SI, [BX] ;数组首地址送SI MOV DI, [BX+2] ;数组长度单元地址送DI MOV CX, [DI] ;数组长度送CX MOV DI, [BX+4] ;存储和的单元地址送DI MOV AX, 0 ADDT: ADD AX, [SI] ADD SI, 2 LOOP ADDT MOV [DI], AX POPA ;恢复现场 RET PROADD ENDP

利用堆栈传递参数示例——求数组之和 主程序中: LEA BX, ARRAY ;参数进栈 PUSH BX LEA BX, COUNT LEA BX, SUM CALL FAR PTR PROADD ...... PROADD PROC FAR PUSH BP ;保护现场 MOV BP, SP PUSH AX PUSH CX PUSH SI PUSH DI (DI) (SI) 注意,一共3个参数进栈,每个占2字节 (CX) (AX) 原始(BP) 新BP (IP) (CS) SUM地址 COUNT地址 ARRAY地址 高地址

MOV SI, [BP+10] ;取得参数地址 MOV DI, [BP+8] MOV CX, [DI] MOV DI, [BP+6] MOV AX, 0 ADDT: ADD AX, [SI] ADD SI,2 LOOP ADDT MOV [DI],AX ;保存结果 POP DI ;恢复现场 POP SI POP CX POP AX POP BP RET 6; 弹出压栈的6字节参数 PROADD ENDP SP (DI) (SI) (CX) (AX) BP 原始(BP) BP+2 (IP) BP+4 (CS) BP+6 SUM地址 BP+8 COUNT地址 BP+10 ARRAY地址 高地址

子程序嵌套 程序设计中,子程序A调用子程序B,子程序B 调用子程序C......这一过程称为子程序嵌套。 CALL CALL CALL RET RET RET 主程序 子程序1 子程序2 子程序N 注意: 1、每一层调用都要注意寄存器的保存和恢复 2、PUSH ,POP 的使用要严格平衡,确保自内向外顺序退出

当一个程序直接或间接调用其自身时,称为递归调用. 递归子程序示例——阶乘函数的计算 ...... N DB 4 RESULT DW ? 主程序: MOV AL, N CALL FACT ADD1: MOV RESULT, DX Y N n=0 1-->F n进栈 求出基数返回 n-1-->n 递归调用 弹出n n*F-->F 递归返回

FACT PROC ;入口参数AL(N的值) ,出口参数N!=DX CMP AL, 0 ;N=0? JNZ F1 ;N<>0跳转 MOV DX, 1 ;(DX)<--1 RET F1: PUSH AX ;N进栈 DEC AL ;(AL)<--N-1 CALL FACT ;递归 ADD2: POP CX ;(CX)<--N CALL MULT ;(DX)<--N*FACT(N-1) FACT ENDP MULT PROC ; 入口CL中是N,DX中是FACT(N-1) MOV AL, CL ; 出口参数DX中是N*FACT(N-1) MUL DL MOV DX, AX MULT ENDP