汇编语言第六章

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

MAIN CODE1 CODE2 SUB1
SUB1 SUB2 SUB2 CODE2
二、设计子程序时应注意的问题
1.子程序说明 为便于引用,子程序应在开 头对其功能、调用参数和返回参 数等予以说明,例如参数的类型 、格式及存放位置等。
2.寄存器的保存与恢复 为了保证调用程序的寄存器内容不被 破坏,应在子程序开头保存它要用到的寄存 器内容,返回前再恢复它们。 通常用PUSH指令保存,用POP恢复。
MAIN CODE1 SEGMENT PROC FAR CALL FAR PTR SUB2 RET SUB2既被段间调用又被段 ENDP 内调用,必须是FAR属性。 ENDS CALL要显式说明是FAR属 SEGMENT 性. PROC FAR CALL FAR PTR SUB2 RET ENDP PROC FAR RET ENDP ENDS ;END后必须跟主程序名 END MAIN
;数组求和子程序 ;保存寄存器
;取数组起始地址 ;取元素个数 ;清0累加器
NEXT:
ADD AX,[SI] ADD SI,2 LOOP NEXT MOV SUM,AX POP SI POP CX POP AX RET ARY_SUM ENDP CODE2 ENDS END MAIN
;累加和 ;修改地址指针 ;存和 ;恢复寄存器
本例通过BP访问堆栈中的参数。 程序的堆栈变化情况参见图6-1,指示了 程序中所有入栈操作对堆栈的影响随入栈数 据的增加,SP的值不断减小,堆栈可用空间 也随之减少。图6-2为已从子程序返回、而主 程序RET指令执行前的堆栈状态,其中的灰 色部分表示执行语句⑿~⒄时已弹出的数据 。
从以上分析可以看出,通过堆栈传递 参数时子程序的返回指令必须是RET N形 式,当堆栈操作是16位时N值应该是压入 堆栈的参数个数的2倍,只有这样保证程序 的正常运行。 用结构形式访问堆栈中的参数,这种 方法更简便及规范化。
MAIN CODE1
;构造数组2的地址表 MOV TABLE,OFFቤተ መጻሕፍቲ ባይዱET NUM MOV TABLE+2,OFFSET CT MOV TABLE+4,OFFSET TOTAL ;传递地址表的首地址 LEA BX,TABLE ;段间调用调用数组求和子程序 CALL FAR PTR ARY_SUM MOVAX,4C00H INT 21H ENDP ENDS
例6.7.完成数组求和功能,其中求和 由子程序实现,要求使用结构访问堆栈中的 参数。 图6-3给出了堆栈及结构数据定义。注 意这些结构字段的顺序为其值压入的逆序。 实际上,它只是给图6-1中由主程序压入的数 据、返回地址及子程序压入的BP值起了个 名字而已,而字段值的预置是通过PUSH和 CALL指令实现的。当子程序用到堆栈中的 参数时,只需使用BP作为基地址、通过结 构字段名访问就可以了。编码见程序6.7。
第六章 子程序设计
为了程序共享或模块化设计的需 要,可以把一段公共语句序列设计成 子程序或宏指令的形式。本章介绍子 程序的设计方法。
6.1 子程序结构及设计方法 一、子程序结构 在汇编语言中用过程定义伪指令定义子程 序。过程定义伪指令格式: 过程名 PROC 属型 … 过程名 ENDP 其中过程名就是子程序名,它也表示子程序入口 的符号地址。属型可以是NEAR型(缺省值)或 FAR型。NEAR型子程序只可以被段内调用,而 FAR型子程序可以被段间或段内调用。
6.2 子程序参数传递
可以通过给子程序传递参数使其更通 用。常用的参数传递方法如下: 通过寄存器传递; 通过地址表传递参数地址; 通过堆栈传递参数或参数地址。 另外: 若调用程序和子程序在同模块(源程序) 中,子程序可以直接访问模块中的变量;
1.通过寄存器传递
这种传递方式使用方便,适用于参数较 少的情况。 例1.把BX中的16位二进制数转换成十 进制并显示在屏幕上。 分析:二进制到十进制数的转换方法有 多种,本例采用从高到低逐个除以十进制位 权的方法。
2.若调用程序和子程序在同模块中, 子程序可以直接访问模块中的变量 例2.实现数组求和功能。要求数 组求和(不考虑溢出情况)由子程序实 现,其数组元素及结果均为字型数据。 见程序6.4。
程序6.4
DATA ARY COUNT SUM DATA SEGMENT DW 1,2,3,4,5,6,7,8,9,10 DW ($-ARY)/2 ;数组元素个数 DW ? ;数组和的地址 ENDS
NEXT:
ADD AX,[SI] ADD SI,2 LOOP NEXT MOV [DI],AX POP DI POP SI POP CX POP AX RET ARY_SUM ENDP CODE2 ENDS END MAIN
;累加和 ;修改地址指针 ;存和 ;恢复寄存器
4.通过堆栈传递参数或参数地址 这种方式适用于参数较多,或子程序 有多层嵌套、递归调用的情况。 步骤: 主程序把参数或参数地址压入堆栈; 子程序使用堆栈中的参数或通过栈中参数 地址取到参数; 子程序返回时使用RET n指令调整SP指 针,以便删除堆栈中已用过的参数,保持 堆栈平衡,保证程序的正确返回。
程序6.3
STSG STSG CODE MAIN
MAIN
SEGMENT DW 32 DUP(?) ENDS SEGMENT ASSUME CS:CODE SS:STSG PROC FAR … MOV BX,162EH CALL TERN ;转换并显示 MOV AX,4C00H INT 21H ENDP
CODE1 SEGMENT MAIN PROC FAR ASSUME CS:CODE1,DS:DATA MOV AX,DATA MOV DS,AX MOV TABLE,OFFSET ARY ;构造数组1的地址表 MOV TABLE+2,OFFSET COUNT MOV TABLE+4,OFFSET SUM LEA BX,TABLE ;传递地址表首地址 CALL FAR PTR ARY_SUM
CODE1 SEGMENT MAIN PROC FAR ASSUME CS:CODE1,DS:DATA MOV AX,DATA MOV DS,AX CALL FAR PTR ARY_SUM MOV AX,4C00H INT 21H MAIN ENDP CODE1 ENDS
CODE2
SEGMENT ASSUME CS:CODE2 ARY_SUM PROC FAR PUSH AX PUSH CX PUSH SI LEA SI,ARY MOV CX,COUNT XOR AX,AX
例6.6.完成数组求和功能,求和由子程序实现,要求通 过堆栈传递参数地址。 STACK SEGMENT STACK ‘STK’ DW 16 DUP(?) STACK ENDS DATA SEGMENT ARY DW 1,2,3,4,5,6,7,8,9,10 COUNT DW ($-ARY)/2 SUM DW ? DATA ENDS CODE1 SEGMENT MAIN PROC FAR ASSUME CS:CODE1,DS:DATA,SS:STACK
程 序 6.7
STAK
SEGMENT STACK 'STK' DW 16 DUP('S') TOP EQU $ STAK ENDS DATA SEGMENT ARY DW 1,2,3,4,5,6,7,8,9,10 COUNT DW ($-ARY)/2 SUM DW ? DATA ENDS CODE1 SEGMENT MAIN PROC FAR ASSUME CS:CODE1,DS:DATA,SS:STAK MOV AX,DATA MOV DS,AX
1.调用程序和子程序在同一个代码段的程序结构(子 程序类型可缺省,注意END后必须跟主程序名)
CODE MAIN SEGMENT PROC FAR … CALL SUB1 RET ENDP PROC … RET ENDP ENDS END MAIN
MAIN SUB1
SUB1 CODE
2.调用程序和子程序在不同段的程序结构
3.通过地址表传递参数地址 适用于参数较多的情况。具体方法 是先建立一个地址表,该表由参数地址 构成。然后把表的首地址通过寄存器或 堆栈传递给子程序。 例3.编写一个数组求和子程序, 其数组元素及结果均为字型数据。另定 义两个数组,并编写一个主程序,通过 调用数组求和子程序分别求出两个数组 的和。
MOV AX,DATA MOV DS,AX MOV AX,STACK MOV SS,AX LEA BX,ARY PUSH BX ;⑶压入数组起始地址 LEA BX,COUNT PUSH BX ;⑷压入元素个数地址 LEA BX,SUM PUSH BX ;⑸压入和地址 CALL FAR PTR ARY_SUM ;⑹调用求和子程序 MOV AX,4C00H INT 21H ;⒅ MAIN ENDP CODE1 ENDS
TERN PROC MOV CX,10000 CALL DEC_DIV MOV CX,1000 CALL DEC_DIV MOV CX,100 CALL DEC_DIV MOV CX,10 CALL DEC_DIV MOV CX,1 CALL DEC_DIV RET TERN ENDP
;二→十并显示。 ;转换万位数
注意由于堆栈操作采用后进先出的规则,一 般说来,弹出寄存器的顺序应该与压入时的 相反。
3.密切注意堆栈状态 在设计含有子程序的源程序时,要密切注 意堆栈的变化。这包括要注意一切与堆栈有关 的操作。 例如 CALL调用类型和子程序定义类型的一致性, PUSH和POP指令的匹配, 通过堆栈传递参数时子程序返回使用RET n指 令等,以确保堆栈平衡,否则后果不可预料。
CODE2
SEGMENT ASSUME CS:CODE2 ARY_SUM PROC FAR ;数组求和子程序 PUSH BP ;⑺保存BP值 MOV BP,SP ;BP是堆栈数据的地址指针 PUSH AX ;⑻保存寄存器内容 PUSH CX ;⑼ PUSH SI ;⑽ PUSH DI ;⑾ MOV SI,[BP+10] ;得到数组起始地址 MOV DI,[BP+8] ;得到元素个数地址 MOV CX,[DI] ;得到元素个数 MOV DI,[BP+6] ;得到和地址 XOR AX,AX
NEXT:
ADD AX,[SI] ADD SI,2 LOOP NEXT MOV [DI],AX POP DI POP SI POP CX POP AX POP BP RET 6 ARY_SUM ENDP CODE2 ENDS END MAIN
;累加 ;修改地址指针 ;存和 ;⑿恢复寄存器内容 ;⒀ ;⒁ ;⒂ ;⒃ ;⒄返回并调整SP指针
CODE2 ARY_SUM
SEGMENT ASSUME CS:CODE2 PROC FAR ;数组求和子程序 PUSH AX ;保存寄存器 PUSH CX PUSH SI PUSH DI MOV SI,[BX] ;取数组起始地址 MOV DI,[BX+2] ;取元素个数地址 MOV CX,[DI] ;取元素个数 MOV DI,[BX+4] ;取结果地址 XOR AX,AX ;清0累加器
分析:虽然主、子程序在同模块中 ,但由于在一个程序中要分别求出两个 数组的和,因此子程序不能直接引用数 组变量名。本例用数组首地址、元素个 数的地址、和地址构成地址表,通过地 址表传送这些参数的地址,以便子程序 能够访问到所需参数。见程序6.5。
程序6.5
DATA ARY COUNT SUM NUM CT TOTAL TABLE DATA SEGMENT DW 1,2,3,4,5,6,7,8,9,10 ;数组1 DW ($-ARY)/2 ;数组1的元素个数 DW ? ;数组1的和地址 DW 10,20,30,40,50 ;数组2 DW ($-NUM)/2 ;数组2的元素个数 DW ? ;数组2的和地址 DW 3 DUP(?) ;地址表 ENDS
;转换千位数
;转换百位数 ;转换十位数
;转换个位数
DEC_DIV PROC ;CX中为十进制的位权 MOV AX,BX MOV DX,0 DIV CX ;商为转换后的一位十进制数 MOV BX,DX MOV DL,AL ADD DL,30H ;转换成ASCII码 MOV AH,2 ;显示 INT 21H RET DEC_DIV ENDP CODE ENDS END MAIN
相关文档
最新文档