第1节 子程序结构

1.1 子程序的定义
过程名 PROC 属性
.
.
.
过程名 ENDP
属性: NEAR, FAR
1.主程序与子程序在同一代码段内
例 6.1.1
CODE SEGMENT ‘code’
;-----------Main过程---------------
MAIN PROC FAR
…
CALL SUB1
…
RET
MAIN ENDP
;------------------------------------------
;-----------SUB1子过程---------------
SUB1 PROC NEAR
…
CALL SUB2
…
RET
SUB1 ENDP
;--------------------------------------------
;-----------SUB2子过程---------------
SUB2 PROC NEAR
…
RET
SUB2 ENDP
;--------------------------------------------
;--------------------------------------------
CODE ENDS
END MAIN

2.主程序与子程序不在同一个代码段
例6.1.2
;---------CODE1段----------------------
CODE1 SEGMENT ‘code’
;-----------Main过程---------------
MAIN1 PROC FAR
…
RET
MAIN1 ENDP
;------------------------------------------
;-----------SUB3子过程---------------
SUB3 PROC FAR
…
RET
SUB3 ENDP
;--------------------------------------------
CODE1 ENDS
;---------CODE2段----------------------
CODE2 SEGMENT ‘code’
MAIN2 PROC FAR
…
CALL SUB3
…
RET
MAIN2 ENDP
CODE2 ENDS
END MANI1

1.2 子程序的调用和返回
CALL RET 对堆栈的使用。
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
**保存的原则:子程序使用的寄存器,这些寄存器要保存与恢复.
1.4 子程序的参数传送

传送参数方法: (1)通过通用寄存器(RC); (2)通过存储单元; (3)通过堆栈
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
MAIN ENDP
//子程序把键入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
//万位计数,并显示
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
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 C0
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
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


