资源描述
第6章 子程序设计
为什么用子程序? UML中的设计模式?
6.1. 子程序结构
主程序与子程序间的关系
6.1.1 子程序的定义
过程名 PROC 属性
.
.
.
RET/RETF
[过程名] ENDP
属性: NEAR, FAR
1.主程序与子程序在同一代码段内
例 6.1.1
CODE SEGMENT
MAIN PROC FAR
…
CALL SUB1
…
RET
MAIN ENDP
SUB1 PROC NEAR
…
CALL SUB2
…
RET
SUB1 ENDP
SUB2 PROC NEAR
…
RET
SUB2 ENDP
…
CODE ENDS
END MAIN
2.主程序与子程序不在同一个代码段
例6.1.2
CODE1 SEGMENT
MAIN1 PROC FAR
…
…
CALL SUB3
…
MAIN1 ENDP
SUB3 PROC FAR
…
RET
SUB3 ENDP
CODE1 ENDS
CODE2 SEGMENT
MAIN2 PROC FAR
…
CALL SUB3
…
MAIN2 ENDP
CODE2 ENDS
END MAIN1
6.1.2 子程序的调用和返回
CALL RET 对堆栈的使用。
CALL RETF
6.1.3 现场的保存与恢复
为什么要保存现场与恢复现场?
1. 利用栈指令进行现场的保存与恢复
例6.1.3
SUB4 PROC NEAR
PUSH AX
PUSH BX
PUSH CX
…子程序体
POP CX
POP BX
POP AX
RET
SUB4 ENDP
**注意保存与恢复的顺序.
2. 利用数据传送指令进行现场的保存与恢复
DATA SEGMENT
BUFFER DW 4 DUP(?)
DATA ENDS
SUB5 PROC NEAR
LEA DI, BUFFER
MOV [DI], AX
MOV [DI+2], BX
MOV [DI+4], CX
Mov [DI+6], DX
…子程序体
LEA DI, BUFFER
MOV AX, [DI]
MOV BX, [DI+2]
MOV CX, [DI+4]
MOV DX, [DI+6]
RET
SUB5 ENDP
**现场保存在子程序的数据空间.
**有什么问题? DI?
3. 利用PUSHA和POPA进行现场的保存与恢复(286以上)
SUB6 PROC NEAR
PUSHA
…子程序体
POPA
RET
SUB6 ENDP
4. 利用PUSHAD和POPAD指令进行现场的保存与恢复(386以上)
SUB7 PROC NEAR
PUSHAD
…子程序体
POPAD
RET
SUB7 ENDP
**保存的原则:子程序使用的寄存器,这些寄存器要保存与恢复.
6.1.4 子程序的参数传送
传送参数方法: 通过通用寄存器(RC); 通过存储单元; 通过堆栈
1.通过通用寄存器传送参数
例6.1.7 用寄存器传送参数方式, 编制5位10进制加法程序。
要求(1)SUB8子程序把键入5位以内的10进制数转(<65536)换成2进制数年存入CX,键入非数字键返主程序。
(2)SUB9子程序完成把CX中的2进制数转换成10进制数显示在CRT上.
(3)在主程序MAIN中求和,两数之和小于等于65535.
(4)MAIN,SUB8,SUB9在同一代码段中.
CODE SEGMENT
ASSUME CS:CODE
//主程序
MAIN PROC FAR
CALL SUB8 ;取被加数
MOV BX,CX
CALL SUB8 ;取加数
ADD CX, BX ;求和
CALL SUB9 ;显示
MOV AH, 4CH
INT 21H
//子程序把键入5位以内的10进制数转(<65536)换成2进制数年存入CX,键入非数字键返主程序。
SUB8 PROC NEAR
PUSH AX
PUSH BX
PUSH DX
XOR CX,CX ; mov cx, 0 CX中保存结果
AA2: MOV AH, 1
INT 21H
//界限0~9
CMP AL, 30H
JC AA1
CMP AL, 3AH
JNC AA1
//x10
ADD CX, CX
MOV BX, CX
ADD CX, CX
ADD CX, CX
ADD CX, BX
AND AX, 0FH
ADD CX, AX ;键入的值加到CX中。
JMP AA2
AA1: POP DX
POP BX
POP AX
RET
SUB8 ENDP
//把CX中的2进制数转换成10进制数显示在CRT上
SUB9 PROC NEAR
PUSH AX
PUSH BX
PUSH DX
//最高位不显示0
CMP CX,10000
JNC AA12 ;JAE AA12 大于等于1000时显示万位
CMP CX, 1000
JNC AA4
CMP CX, 100
JNC AA6
CMP CX, 10
JNC AA8
JMP AA10
//万位计数,并显示
MOV DL, 0
AA3: CMP CX, 10000
JC DISP1
INC DL
SUB CX, 1000
JMP AA3
DISP1: OR DL, 30H
MOV AH, 2
INT 21H
AA12: MOV DL, -1
AA3: SUB CX, 10000
INC DL
JNC AA3
ADD CX, 10000
OR DL, 30H
MOV AH, 2
INT 21H
//千位计数,并显示
AA4: MOV DL, -1
AA5: SUB CX, 1000
INC DL
JNC AA5
ADD CX, 1000
OR DL, 30H
MOV AH, 2
INT 21H
//百位计数,并显示
AA6: MOV DL, -1
AA7: SUB CX, 100
INC DL
JNC AA7
ADD CX, 100
OR DL, 30H
MOV AH, 2
INT 21H
//十位计数,并显示
AA8: MOV DL, -1
AA9: SUB CX, 10
INC DL
JNC AA9
ADD CX, 10
OR DL, 30H
MOV AH, 2
INT 21H
//个位计数, 并显示
AA10: MOV DL, CL
OR DL, 30H
MOV AH, 2
INT 21H
POP DX
POP BX
POP AX
RET
SUB9 ENDP
MAIN ENDP
CODE ENDS
END MAIN
**以上的程序子程序中用到AX,BX,CX,DX. 其中CX用于传送参数及结果,而AX,BX,DX用于中间运算, 内部使用,为了不修改主程序中的AX,BX,DX,要保存现场.
2.通过存储单元传送参数
利用存储单元存放调用子程序的输入参数, 子程序把运算结果(输出参数)也用存储单元保存.
例6.1.8 用存储单元传送参数方式,编写求无符号多字节2进制数的和,然后以16进制数形式显示在CRT上.
要求:
NUMBER1放8个字节的数.
NUMBER2放8个字节的数.
NUMBER3放求和结果,8个字节单元
SUB10 求和子子程序
SUB11 显示子程序.
DATA SEGMENT
NUMBER1 DB 99, 22, 33, 44, 99, 77, 88, 11
NUMBER2 DB 99, 88, 77, 66, 55, 44, 33, 22
NUMBER3 DB 8 DUB(?)
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE, DS: DATA
//主程序
MAIN PROC FAR
MOV AX, DATA
MOV DS, AX
CALL SUB10
LEA BX, NUMBER3+7
CALL SUB 11
MOV AH, 4CH
INT 21H
//8字节求和子程序
SUB10 PROC NEAR
PUSH AX
PUSH CX
PUSH BX
PUSH SI
PUSH DI
LEA SI, NUMBER1
LEA DI, NUMBER2
LEA BX, NUMBER3
MOV CX, 8
AND AL, AL ; CLC Cß0
AA1: MOV AL,[SI]
ADC AL,[DI]
MOV [BX], AL
INC SI
INC DI
INC BX
LOOP AA1
POP DI
POP SI
POP BX
POP CX
POP AX
RET
SUB10 ENDP
//显示子程序
SUB11 PROC NEAR
PUSH DX
PUSH CX
PUSH AX
MOV DH, 8 ; 循环次数
//一个字节的高4位显示
AA4: MOV DL,[BX] ; 从高位NUMBER3+7开始计数
MOV CL, 4
SHR DL, CL ;高4位
OR DL, 30H ; 30H~3FH
CMP DL, 3AH
JC AA2 ; jb AA2
ADD DL, 7 ; 3AH+7 = 41H
AA2: MOV AH, 2
INT 21H
//一个字节的低4位显示
MOV DL, [BX]
AND DL, 0FH
OR DL, 30H
CMP DL, 3AH
JC AA3
ADD DL, 7
AA3: MOV AH, 2
INT 21H
DEC BX
DEC DH
JNZ AA4 ;循环8次
MOV DL, ‘H’
MOV AH, 2
INT 21H
POP AX
POP CX
POP DX
SUB11 ENDP
MAIN ENDP
CODE ENDS
END MAIN
3.通过堆栈传送参数或参数表地址
例 6.1.9用堆栈传送参数和参数表地址方式,编制键入8位的非压缩BCD码加法并显示的源程序。
要求: NUMBER1~NUMBER1+7存放键入的被加数(非压缩BCD码);
NUMBER2~NUMBER2+7存放键入的加数(非压缩BCD码);
NUMBER3~NUMBER3+8存放和(非压缩BCD码);
SUB12接收8位非压缩BCD码,接收被加数、加数(输出:接收结果存入堆栈中)
SUB13显示9位非压缩BCD码,显示和(输入:参数地址表在堆栈中)
DATA SEGMENT
NUMBER1 DB 8 DUB(0) ;被加数
NUMBER2 DB 8 DUB(0) ;加数
NUMBER3 DB 9 DUB(0) ;和
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE, DS:DATA
MAIN PROC FAR
MOV AX, DATA
MOV DS, AX
//1.取被加数
CALL SUB12
POP CX
LEA BX, NUMBER1
AA3: POP AX
MOV [BX], AL
INC BX
LOOP AA3
//2.取加数
CALL SUB12
POP CX
LEA BX, NUMBER2
AA4: POP AX
MOV [BX], AL
INC BX
LOOP AA3
//3.计算和
LEA SI, NUMBER1
LEA DI, NUMBER2
LEA BX, NUMBER3
MOV CX, 8 ; 计数次数
OR CX, CX ; CLC
AA5: MOV AL, [SI]
ADC AL, [DI]
AAA
MOV [BX], AL
INC SI
INC DI
INC BX
LOOP AA5
ADC CL, AH
MOV [BX], CL
//4.显示和
LEA AX, NUMBER3+8
PUSH AX
CALL SUB13
//5.清被加数与加数单元为0
MOV CX, 16
LEA BX, NUMBER1
XOR AL, AL ;MOV AL, 0
QQQ2: MOV [BX], AL
INC BX
LOOP QQQ2
//6.退出
MOV AH, 4CH
INT 21H
//7. 接收8位非压缩BCD码子程序, 接收被加数、加数(输出:接收结果存入堆栈中)
SUB12 PROC NEAR
POP BX ;保存子程序返回地址
SUB CX, CX ; MOV CX, 0
AA1: MOV AH, 1
个数(N)
数据N
数据N-1
…
数据4
数据3
数据2
数据1
INT 21H
CMP AL, 30H
JC AA2
CMP AL, 3AH
JNC AA2
INC CX ;个数加1
PUSH AX ;键入的数压入栈
JMP AA1
AA2: PUSH CX ; 键入数字的个数
PUSH BX; 子程序返回地址入栈
RET
SUB12 ENDP
//8. 显示9位非压缩BCD码子程序, 显示和(输入:参数地址表在堆栈中)
SUB13 PROC NEAR
POP AX ;子程序返回地址出栈
POP BX ;参数表地址出栈
PUSH AX ;子程序返回地址入栈
MOV CX, 9 ;和数是9位,循环计数次数
AA6: MOV DL, [BX]
ADD DL, 30H ;OR DL, 30H
MOV AH, 2
INT 21H
DEC BX
LOOP AA6
RET
SUB13 ENDP
MAIN ENDP
CODE ENDS
END MAIN
6.2. 嵌套与递归子程序
6.2.1 子程序的嵌套
例6.2.1 试编写在ARRAY1无符号数组中,选出最大值及其所在的位置,然后以16进制数显示在CRT上显示的程序.
要求: SUB14找出最大值及其位置子程序;
输入:个数(CX),首地址(SI)
输出:最大值(CX),位置(SI)
SUB15 显示子程序
输入:BX ; 例20FF
SUB16 十六进制显示子程序
输入:DL ;0~F之间的数
DATA SEGMENT
ARRAY1 DW 100 DUB(?)
COUNT EQU ($-ARRAY1)/2
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE, DS:DATA
MAIN PROC FAR
MOV AX, DATA
MOV DS, AX
//1.在数组中找最大值及位置
LEA SI, ARRAY1
MOV CX, COUNT
CALL SUB14 ; 找到最大数在CX中,位置在SI中
MOV DI, CX ; 保存找到的最大值在DI中
//2.显示段地址
MOV BX, DATA
CALL SUB15
//3.显示’:’字符
MOV DL, ‘:’
MOV AH, 2
INT 21H
//4.显示偏移地址
MOV BX, SI
CALL SUB15
//5.显示空格
MOV DL, 20H
MOV AH, 2
INT 21H
//6.显示最大值
MOV BX, DI
CALL SUB15
//7.退出
MOV AH, 4CH
INT 21H
//找出最大值及其位置子程序;
输入:个数(CX),首地址(SI)
输出:最大值(CX),位置(SI)
SUB14 PROC NEAR
PUSH AX
PUSH DI
MOV DI, SI ;DI中保存最大值的地址
MOV AX, [SI] ;从ARRAY1数组中取第一个数
AA1: CMP AX, [SI]
JNC AA2 ; JAE AA2
MOV AX, [SI] ;AX中保存最大值
MOV DI, SI ;DI中保存最大值地址
AA2: ADD SI, 2
LOOP AA1 ;子程序输入参数CX中有个数
MOV SI, DI ;最大值地址值
MOV CX, AX ;最大值
POP DI
POP AX
ret
SUB14 ENDP
//显示子程序. 输入:BX ; 例20FF
SUB15 PROC NEAR
//显示BH中高4位
MOV DL, BH
MOV CL, 4
SHR DL, CL
CALL SUB16
//显示BH中低4位
MOV DL, BH
AND DL, 0FH
CALL SUB16
//显示BL中高4位
MOV DL, BL
MOV CL, 4
SHR DL, CL
CALL SUB16
//显示BL中低4位
MOV DL, BL
AND DL, 0FH
CALL SUB16
RET
SUB15 ENDP
//十六进制显示子程序. 输入:DL ;0~F之间的数.
SUB16 PROC NEAR
OR DL, 30H
CMP DL, 3AH
JC AA3 ;JB AA3
ADD DL, 7 ;41H~46H, 3AH+7=41H
AA3: MOV AH, 2
INT 21H
RET
SUB16 ENDP
MAIN ENDP
CODE ENDS
END MAIN
6.2.2 递归子程序
子程序递归调用: 子程序调用自身子程序.
例6.2.2 试编写计算N!的递归子程序
1. 用缓冲区存放N、N-1、…、2、1,然后再依次取出作1x2x3x…xN运算的方式。
FACT1递归子程序
输入:个数(AL), 缓冲区首地址(SI)
输出: 计数结果(AX)
DATA SEGMENT
BUFF1 DB 3 DUP(?)
NN1 DB 3
RESULT1 DW 0
DATA ENDS
SET1 SEGMENT ‘STACK’
DW 100 DUP(?)
SET1 ENDS
CODE SEGMENT
ASSUME CS:CODE, DS:DATA, SS:SEG1
START:MOV AX, DATA
MOV DS, AX
MOV AL, NN1
LEA SI, BUFF1
CALL FACT1 ;调用递归子程序
AA2: MOV RESULT1, AX ;保存结果
MOV AH, 4CH
INT 21H
;递归子程序.
;输入:个数(AL), 缓冲区首地址(SI)
;输出: 计数结果(AX)
FACT1 PROC NEAR
CMP AL, 0
JNZ AA1 ;递归中止条件
MOV AL, 1
AA4: RET ;转到AA3
AA1: MOV [SI],AL ; 存放N、N-1、…、2、1
DEC AL
INC SI
CALL FACT1 ;调用自身,每调用一次会把AA3压入栈
AA3: DEC SI
MOV BL, [SI]
MUL BL ;AXß al*bl
AA5 : RET ;转到AA3、转到AA3、。。。、转到AA2
FACT1 ENDP
CODE ENDS
END START
2. 2.用堆栈依次压入N、N-1、…、2、1,然后再依次取出作1x2x3x…xN运算的方式。
FACT1递归子程序
输入:个数(AX)
输出: 计数结果(AX)
DATA SEGMENT
N DW 3
RESULT1 DW 0
DATA ENDS
SET1 SEGMENT ‘STACK’
DW 100 DUP(?)
SET1 ENDS
CODE SEGMENT
ASSUME CS:CODE, DS:DATA, SS:SEG1
START:MOV AX, DATA
MOV DS, AX
MOV AX, N
CALL FACT1 ;调用递归子程序,求N!
AA2: MOV RESULT1, AX ;保存结果
MOV AH, 4CH
INT 21H
;递归子程序.
;输入:个数(AX)
;输出: 计数结果(AX)
FACT1 PROC NEAR
CMP AX, 0
JNZ AA1 ;递归中止条件
MOV AX, 1
AA4: RET ;转到AA3
AA1: PUSH AX ; N、N-1、…、2、1依次入栈
DEC AX
CALL FACT1 ;调用自身,每调用一次会把AA3压入栈
AA3: POP BX
MUL BL ; axßal*bl
AA5 : RET ;转到AA3、转到AA3、。。。、转到AA2
FACT1 ENDP
CODE ENDS
END START
作业:P223 6.1, 6.2, 6.3
展开阅读全文