汇编语言-子程序
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
STRLEN PROC PUSH BP MOV BP , SP PUSH DS PUSH SI MOV DS , [BP + 6] MOV SI , [BP + 4] MOV AL , 0 STRLEN1 : CMP AL , [SI] JZ STRLEN2 INC SI JMP STRLEN1 STRLEN2: MOV AX , SI SUB AX , [BP + 4] POP SI
本章学习要点: (1)子程序的编写格式 (2)子程序调用时的参数传递方法 (3)嵌套及递归子程序 一、过程定义语句(process) 利用过程定义伪指令语句,可把程序片段说明为具有 近类型或远类型的过程,并且能给过程取一个名字。 过程定于语句的格式如下: 过程名 PROC [NEAR | FAR] … 过程名 ENDP 过程的类型在过程定义开始语句PROC中指定; 过程可以被指定位近(NEAR)类型,也可以被指定为 远类型。如果不指定,则通常默认为近类型; 定义一个过程的开始语句PROC和结束语句ENDP前 使用的过程名称必须一致,从而保持配对。
Байду номын сангаас
COUNT PROC NEAR ;主程序和子程序位于同一 MOV SI , 0 ;模块,所以变量可共享。 NEXT: MOV AX , REC[SI] MOV BX , 10 DIV BL ;商位于AL中 CBW MOV BX , AX SUB BX , 6 SAL BX , 1 INC S6[BX] ADD SI , 2 LOOP NEXT ;循环次数由CX控制(主程序设置) RET COUNT ENDP CODE ENDS END START
随着指令的丰富、子程序的引入,汇编语言的表达也越来越灵 活。为了方便地组织数据,引入了结构伪操作STRUC。 STRUC可以把不同类型的数据放在同一个结构里,方便处理。 a). 结构类型说明格式为: structure_name STRUC … ;DB、DW、DD等伪操作 structure_name ENDS 注意:ENDS之前为结构名,注意与段结束相区别。 例如:下列语句说明了一个名STUDENT的结构类型: STUDENT STRUC ID DW ? SCORE DB 0 NAME DB „ABCDEFGH‟ STUDENT ENDS 但是,定义一个结构类型的时候不进行任何存储器分配,只有 在定义结构变量时才进行存储分配。
2.利用约定存储单元传递参数 在传递参数较多的情况下,可利用约定的内存变量来传递参数。 例:写一个实现32位数相加的子程序 ;子程序名:MADD ;功能:32位数相加 ;入口参数:DATA1和DATA2缓冲区中分别存放要相加的数 ;出口参数:DATA3缓冲区存放结果 ;说明: ;(1)32位数据的存放次序采用“高高低低”原则 ;(2)可能产生的进位放在DATA3开始的第5字节中
递归子程序的设计要点: (1)递推性:逐级调用; (2)回归性:逐层回归; (3)有穷性:终止条件; 这3点为所有语言递归程序设计具有的共性。 汇编语言设计递归程序时的个性在于: (1)参数和中间结果一般存于堆栈中,但有时也可以存于寄 存器中; (2)递归的深度受堆栈空间的限制。
例:子程序FACT采用递归算法实现阶乘。 ;子程序名:FACT ;功能:计算n! ;入口参数: (AX)= n ;出口参数: (AX)= n! ;说明: (1)采用递归算法实现阶乘; ; (2)n 不能超过8 FACT(n) = n * FACT(n - 1) = n * [ (n - 1) * FACT(n- 2)]… 当n=0时,FACT(0) = 1. 要点: (1)递推:只要n不为0,即推进到FACT(n-1); (2)有穷:n=0时有确切解,即FACT(0)=1 (3)回归:逐层返回——FACT(n-1)的解和n(保存的中间参数)
MADD PROC PUSH AX ;为什么会把AX,CX,SI压入栈? PUSH CX PUSH SI MOV CX , 2 XOR SI , SI ;CF也会被清0 MADD1:MOV AX , WORD PTR DATA1[SI] ADC AX , WORD PTR DATA2[SI] MOV WORD PTR DATA3[SI] , AX INC SI INC SI POP SI LOOP MADD1 POP CX MOV AL , 0 POP AX ADC AL , 0 RET MOV BYTE PTR [DATA3 + 4] , AL MADD ENDP
像普通标号一样,过程名具有段值、偏移和类型这三个属性。 过程名的段值和偏移是对应过程入口(过程定义开始伪指令语 句后的指令语句)的段值和偏移。 例:下面程序片段运行后,AL=?,BL=?。 XOR AL , AL CALL SUBS MOV BL , AL CALL SUBS RCR AL , 1 HLT ;停机,halt SUBS PROC NEAR NOT AL JS NEXT STC ;CF=1, SeT Cf = 1 NEXT : RET SUBS ENDP
CODE SEGMENT MAIN PROC FAR ASSUME CS:CODE , DS:DSEG, SS:STACK START: PUSH DS SUB AX , AX PUSH AX MOV AX , DSEG MOV DS , AX MOV CX , 10 CALL COUNT ;调用COUNT子程序进行统计 ;可在此处添加显示输出 RET MAIN ENDP 注意:红色部分在这里构成一种固定搭配,把主程序看成DOS 调用的远过程,RET与前2个PUSH配对,相当于: MOV AH , 4CH INT 21H
例:写一个大写字母转换为小写字母的子程序 ;子程序名:UPTOLW ;功能:大写字母转换为小写字母 ;入口参数:AL=字符的ASCII码 ;出口参数:AL=字符的ASCII码 ;说明:如字符为大写字母,则转换为小写,其它字符不变。 UPTOLW PROC PUSHF ;保护各标志 CMP AL , „A‟ JB UPTOLW1 CMP AL , „Z‟ JA UPTOLW1 ADD AL , 20H UPTOLW1:POPF ;恢复各标志 RET UPTOLW ENDP
例:用程序调用的方法,完成一个把16位二进制数转换为4位 十六进制ASCII码的转换程序。 子程序说明:入口参数:DX=欲转换的二进制数; DS:BX=存放转换所得ASCII码串的缓冲区首地址,转换后的 ASCII码串按照高位到低位的次序存放在指定的缓冲区中。 HTASCS PROC RET MOV CX , 4 HTASCS ENDP HTASCS1: ROL DX , 1 HTOASC PROC NEAR ROL DX , 1 AND AL , 0FH ROL DX , 1 ADD AL , 30H ROL DX , 1 CMP AL , 39H MOV AL , DL JBE HTOASC1 CALL HTOASC ADD AL , 7 MOV [BX] , AL HTOASC1: RET INC BX HTOASC ENDP LOOP HTASCS1
四、综合示例 有10个学生的成绩分别为76、69……80。编制一个子程序分 别统计60~69分,70~79分,80~89分,90~99分及100分的人 数,分别存放到S6,S7,S8,S9,S10单元中。 DSEG SEGMENT REC DW 76, 69,63,83,92,73,65,100,99,80 S6 DW 0 S7 DW 0 S8 DW 0 S9 DW 0 S10 DW 0 DSEG ENDS STACK SEGMENT DW 64 DUP (?) STACK ENDS
b). 结构变量的定义 格式是: [变量名] 结构名 < [字段值表]> 例:Lisi STUDENT <103 , 88 , „LI‟> ;三个字段重新赋值 Wangwu STUDENT <104 ,, „WANG‟> ;字段SCORE仍用缺省 值 Zhangsan STUDENT <> ;三个字段均用缺省初值 Team STUDENT 50 DUP (<>) ;定义50个结构变量,初值不变 在定义结构变量时,如果某个字段有多值,就不能给该字段重 新赋初值(定义时存在“DUP”, “ , , , ”等)。 c). 访问方式 访问方式:结构变量名.结构字段名 该变量的地址实质少年宫是结构变量地址的偏移与相应字段偏 移值之和。 例:Zhangsan.ID ;访问张三的学号,实际上是直接寻址 还可以把结构变量地址的偏移先存入某个基址或变址寄存器, 然后利用“[寄存器名]”来代替结构变量名。 例如:MOV BX , OFFSET Zhangsan MOV AL , [BX].SCORE
二、主程序与子程序间的参数传递 主程序在调用子程序时,往往要向子程序传递一些参数;同样 地,子程序运行后夜经常要把一些结果传会给主程序。主程序 和子程序之间的这种信息传递称为参数传递。 有多种参数传递的方法: (1)寄存器传递法 (2)约定内存单元传递法 (3)堆栈传递法 (4)其它方法 1.利用寄存器传递参数 利用寄存器传递参数就是把参数放在约定的寄存器中。这种方 法适用于传递参数较少的情况。
FACT PROC PUSH DX ;保存中间参数(最外层为原有参数) MOV DX , AX CMP AX , 0 ;判断n是否为0? JZ DONE ;如是,则终止推进。(有穷) DEC AX ;否则,继续推进 CALL FACT ;推进 MUL DX ;中间结果后逐层返回: n * FACT(n-1) POP DX ;得到中间参数(最外层为原有参数) RET DONE: MOV AX , 1 ;给出确定结果 0! = 1 POP DX ;得到中间参数 RET FACT ENDP
3.利用堆栈传递参数 (1)如果利用堆栈传递入口参数,那么主程序在调用子程序 之前,把需要传递的参数依次压入堆栈,子程序从堆栈中取入 口参数; (2)如果使用堆栈传递出口参数,那么子程序返回前,把需 要返回的参数存入堆栈,主程序在堆栈中取出口参数。 例:写一个测量字符串长度的子程序,设字符串以0为结束标 志。 ;子程序名:STRLEN ;功能:测量字符串长度 ;入口参数:字符串起始地址的段值和偏移放在堆栈中 ;出口参数:AX=字符串长度。
例:通过结构类型,在主程序和子程序中传输信息。
三、嵌套与递归子程序 一个子程序可以作为调用程序去调用另外一个子程序,这种情 况称为子程序的嵌套。 嵌套的层数称为嵌套深度。
深度为2的嵌套
如果一个子程序直接调用它自身,这种调用称为(直接)递归 调用。 具有递归调用的子程序就称为递归子程序。 递归是嵌套的特殊情况。
POP DS POP BP RET STRLEN ENDP
主程序调用这个子程序的代码片段如下: MOV AX , SEG STR PUSH AX MOV AX , OFFSET STR PUSH AX CALL STRLEN MOV LEN , AX
当然,除了上面提及的3种方式外, 如果过程和调用程序在同一文件(同 一程序块中,则过程可直接访问模块 中的变量。