# 实验六:宏调用 ## 一、基础性实验 ### 实验6_1 宏定义与调用一 #### 实验目的 掌握宏的基本定义和调用方法,理解宏与子程序的区别。 #### 实验内容 定义宏实现字符输出功能。 #### 程序代码 ```asm DATAS SEGMENT CHAR DB 'A' DATAS ENDS STACKS SEGMENT DB 64 DUP(?) STACKS ENDS CODES SEGMENT ASSUME CS:CODES, DS:DATAS, SS:STACKS ; 宏定义 ; 功能:输出一个字符 ; 参数:DL=字符 OUTPUT MACRO MOV AH, 02H INT 21H ENDM START: MOV AX, DATAS MOV DS, AX MOV DL, CHAR OUTPUT MOV AH, 4CH INT 21H CODES ENDS END START ``` #### 程序分析 1. 宏OUTPUT使用MACRO伪指令定义,以ENDM结束 2. 宏调用时直接展开,参数通过DL传递 3. 宏与子程序的区别:宏在调用处展开,子程序需要CALL调用 4. 每次宏调用都会产生一段代码,适合代码量较少的情况 #### 程序结果 运行后,屏幕显示字符'A' --- ### 实验6_2 宏定义与调用二 #### 实验目的 掌握带参数的宏定义方法。 #### 实验内容 定义宏实现数值显示功能。 #### 程序代码 ```asm DATAS SEGMENT NUM DB 5 DATAS ENDS STACKS SEGMENT DB 64 DUP(?) STACKS ENDS CODES SEGMENT ASSUME CS:CODES, DS:DATAS, SS:STACKS ; 宏定义 ; 功能:显示数字0-9 ; 参数:DL=数字 DISP_NUM MACRO ADD DL, 30H ; 转换为ASCII码 MOV AH, 02H INT 21H ENDM START: MOV AX, DATAS MOV DS, AX MOV DL, NUM DISP_NUM MOV AH, 4CH INT 21H CODES ENDS END START ``` #### 程序分析 1. 宏DISP_NUM带有参数DL 2. 宏展开时,参数被替换到相应位置 3. ADD DL,30H将数字转换为ASCII码 4. 适合需要多次调用相同功能的情况 #### 程序结果 运行后,屏幕显示数字'5' --- ## 二、加强性实验 ### 实验6_3 宏嵌套调用 #### 实验目的 掌握宏的嵌套调用,理解宏展开的机制。 #### 实验内容 定义多个宏,实现复杂的输出功能。 #### 程序代码 ```asm DATAS SEGMENT MSG DB 'Test$' DATAS ENDS STACKS SEGMENT DB 64 DUP(?) STACKS ENDS CODES SEGMENT ASSUME CS:CODES, DS:DATAS, SS:STACKS ; 宏定义:输出字符 PRINT_CHAR MACRO CHAR MOV DL, CHAR MOV AH, 02H INT 21H ENDM ; 宏定义:换行 NEWLINE MACRO MOV DL, 0AH MOV AH, 02H INT 21H MOV DL, 0DH MOV AH, 02H INT 21H ENDM ; 宏定义:显示字符串 PRINT_STR MACRO STR LEA DX, STR MOV AH, 09H INT 21H ENDM START: MOV AX, DATAS MOV DS, AX PRINT_CHAR 'H' PRINT_CHAR 'e' PRINT_CHAR 'l' PRINT_CHAR 'l' PRINT_CHAR 'o' NEWLINE PRINT_STR MSG MOV AH, 4CH INT 21H CODES ENDS END START ``` #### 程序分析 1. 定义了三个宏:PRINT_CHAR、NEWLINE、PRINT_STR 2. PRINT_CHAR接受参数CHAR,用于输出单个字符 3. NEWLINE宏内部调用了两次INT 21H实现换行功能 4. PRINT_STR宏使用LEA指令获取字符串地址 5. 每次宏调用都会产生展开代码 #### 程序结果 运行后,屏幕显示: ``` Hello Test ``` --- ### 实验6_4 宏与子程序比较 #### 实验目的 理解宏与子程序的区别和各自适用场景。 #### 实验内容 通过同一功能分别用宏和子程序实现,对比两者区别。 #### 程序代码 ```asm DATAS SEGMENT MSG1 DB 'Macro Test$' MSG2 DB 'Subroutine Test$' DATAS ENDS STACKS SEGMENT DB 64 DUP(?) STACKS ENDS CODES SEGMENT ASSUME CS:CODES, DS:DATAS, SS:STACKS ; 宏定义 PRINT_STRING MACRO STR LEA DX, STR MOV AH, 09H INT 21H ENDM ; 子程序定义 PRINT_PROC PROC LEA DX, MSG2 MOV AH, 09H INT 21H RET PRINT_PROC ENDP START: MOV AX, DATAS MOV DS, AX ; 使用宏 PRINT_STRING MSG1 MOV DL, 0AH MOV AH, 02H INT 21H ; 使用子程序 CALL PRINT_PROC MOV AH, 4CH INT 21H CODES ENDS END START ``` #### 程序分析 1. 宏在调用处展开,节省调用开销,但代码膨胀 2. 子程序代码独立存在,节省存储空间,但需要CALL/RET调用开销 3. 宏适合代码量小、调用频繁的情况 4. 子程序适合代码量大、调用次数少的情况 5. 宏的参数传递通过文本替换实现,子程序通过寄存器或堆栈传递 #### 程序结果 运行后,屏幕显示: ``` Macro Test Subroutine Test ``` --- ## 思考题 1. 宏与子程序的主要区别是什么? - 宏在调用处展开,代码膨胀;子程序独立存在,节省空间 - 宏不需要CALL/RET,节省调用开销;子程序需要调用开销 - 宏参数传递通过文本替换;子程序通过寄存器或堆栈传递 - 宏适合简短代码多次调用;子程序适合较长代码少次调用 2. 宏定义的优点有哪些? - 代码可读性好,程序结构清晰 - 参数传递灵活方便 - 无调用开销,执行效率高 - 便于程序的修改和维护 3. 如何选择使用宏还是子程序? - 代码量小、调用频繁:选择宏 - 代码量大、调用次数少:选择子程序 - 需要递归调用:必须使用子程序 - 需要节省存储空间:选择子程序 --- ## 实验总结 1. 宏使用MACRO伪指令定义,以ENDM结束 2. 宏调用时直接展开,不占用运行时间 3. 宏参数传递方便,适合少量代码的重复使用 4. 宏会增加代码长度,但不占用存储空间(运行时) 5. 子程序节省存储空间,但需要CALL/RET开销 6. 选择宏还是子程序应根据代码量、调用次数和存储空间需求决定