第三章 程序设计的基本技术4
合集下载
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
ABCDCB PROC NEAR MOV AX, 0 ABCDC1:PUSH CX MOV CX, 10 MUL CX AND BYTE PTR[ SI ], 0FH ADD AL, [SI] ADC AH, 0 INC SI POP CX LOOP ABCDC1 RET ABCDCB ENDP
方法2
转移指令有去无回 子程序调用需要返回, 其中利用堆栈保存返回地址 演示
1、调用指令CALL
一般格式:CALL sub ;sub为子程序的入口 根据子程序入口的寻址方式,子程序调用有四类。
(1)段内直接调用
子程序的偏移地址直接由CALL指令给出。 格式:CALL near_proc CALL执行时,它首先将IP内容压栈,
• 3.3 • 3.4 • 3.5 • 3.6
顺序程序设计 分支程序设计 循环程序设计 子程序设计
教学提示
在学习和应用汇编语言进行 程序设计时,有一些经常遇 到的问题,例如算术运算、 代码转换等,需要掌握子程 序设计技术。
3.6 子程序设计
把功能相对独立的程序段单独编写和调试,作为 一个相对独立的模块供程序使用,就形成子程序。 子程序可以实现源程序的模块化,可以简化源程 序结构,可以提高编程效率。 子程序设计要利用过程定义伪指令
第3章
2、返回指令RET
RET
•
•
根据段内和段间、有无参数,分成4种类型 RET ;无参数段内返回 i16参数的作用 RET i16 ;有参数段内返回 RET ;无参数段间返回 RET i16 ;有参数段间返回 需要弹出CALL指令压入堆栈的返回地址 –段内返回——出栈偏移地址IP IP←SS:((SP)+1,(SP)), SP←SP+2 –段间返回——出栈偏移地址IP和段地址CS IP←SS: ((SP)+1,(SP)) ,SP←SP+2 CS←SS: ((SP)+1,(SP)) ,SP←SP+2
3.6.3
子程序的参数传递
入口参数(输入参数):主程序 提供给 子程序 出口参数(输出参数):子程序 返回给 主程序
参数的形式:① 数据本身(传值) ② 数据的地址(传址) 传递的方法:① 寄存器 ② 变量 ③ 堆栈
例3.6.2 子程序计算数组元素的“校验和” 入口参数:数组的逻辑地址(传址), 元素个数(传值); 出口参数:求和结果(传值)。
子程序的常见格式
subname proc push ax push bx push cx ... pop cx pop bx pop ax ret subname endp ;具有缺省属性的subname过程 ;保护寄存器:顺序压入堆栈 ;ax/bx/cx仅是示例
;过程体 ;恢复寄存器:逆序弹出堆栈
例3.6.1下
... ;主程序, 例3.6.1应用 loop outlp ;外循环尾 mov bx,offset array ;调用开始,实现输出显示. mov cx,count ;array中的count个字符显示. displp: mov al,[bx] call ALDISP ;调用显示过程 mov dl,',' ;显示一个逗号,以分隔两个数据 mov ah,2 int 21h inc bx loop displp ;调用程序段结束 操作 ... ;过程定义 end
参数传递是子程序设计的重点和难点
子程序可以嵌套; 一定条件下,还可以递归和重入
过程名 proc [near|far] ... 过程名 endp
3.6.1
子程序概念
过程名(子程序名)为符合语法的标识符。 • NEAR(段内近调用)的过程只能被相同代码 段的其他程序调用; • FAR(段间远调用)的过程可以被相同或不 同代码段的程序调用。 在微型、小型和紧凑存储模式下,缺省属性 为near;在中型、大型和巨型存储模式下,缺省 属性为far。一般缺省属性为near。可以在过程 定义时用near或far改变缺省属性。
① SP←SP-2, SS:[SP]←IP
然后把指令中给出的位移量加到IP上。 ② IP ← near_proc 注:汇编后的调用地址是相对于CALL的下一条指令 的位移量。
例:CALL 0120H ;子程序偏移地址由指令给出
位移量由汇编程序在汇编时进行计算,如下例: CS:0102 CALL 0120H ;3字节 CS:0105 ……
;过程返回 பைடு நூலகம்过程结束
例3.6.1
ALDISP PROC ;实现AL中两位十六进制数显示子程序 PUSH AX PUSH CX ;过程中使用了AX、CX和DX PUSH DX 例3.6.1上 PUSH AX ;暂存ax MOV DL,AL ;转换AL的高4位 MOV CL,4 SHR DL,CL OR DL,30h ;AL高4位变成ASCII码 CMP DL,39h JBE ALDISP1 ADD DL,7 ;是0AH~0FH,ASCII码加上7 ALDISP1: MOV AH,2 ;显示 INT 21H
段间间接调用示意图
例:下面的程序执行后,(AX)=? (DX)=? CS:2000H MOV AX, 2012H 2003H MOV CX, 200CH 2006H PUSH CX 2007H CALL 4000H 200AH ADD AX, BX 200CH ADD AX, DX ;(DX)=200CH 200EH HLT ;(AX)=6028H …… …… CS:4000H MOV BX, 200AH POP DX RET
例:CALL
2000H:1000H
(4)段间间接调用
子程序的段和偏移地址为存储器的连续4个单元中 的内容。 格式:CALL mem32 指令的操作为: ① SP←(SP)-2 ((SP)+1,(SP))←(CS) ;CS压栈 ② CS←(mem32+2) ③ SP←(SP)-2 ((SP)+1,(SP))←(IP) ;IP压栈 ④ IP←(mem32)
子程序调用:进入子程序的操作;
子程序返回:子程序返回到主程序的操作; 主程序与子程序相互传递的信息称为参数 ;
3.6.2 子程序指令
• 子程序是完成特定功能的一段程序 • 当主程序(调用程序)需要执行这个功能时, 采用CALL调用指令转移到该子程序的起始处执 行 • 当运行完子程序功能后,采用RET返回指令 回到主程序继续执行
主程序和子程序直接采用同一个变量 名共享同一个变量,实现参数的传递。 不同模块间共享时,需要声明。
用 变 量 传 递 参 数
例3.6.2b
• 入口参数: COUNT=元素个数, ARRAY=数组名(含段地址:偏移地 址) • 出口参数: RESULT=校验和
call checksumb ;调用求和过程 Checksumb proc 主程序b push ax push bx push cx xor al,al ;累加器清0 mov bx,offset array ;BX←数组的偏址 mov cx,count ;CX←数组的元素个数 sumb: add al,[bx] ;求和 inc bx pop cx loop sumb pop bx pop ax mov result,al ;保存校验和 ret checksumb endp
例:CALL DWORD PTR[DI]
调用地址在[DI],[DI]+1,[DI]+2,[DI]+3四个存 储单元中。低字内容为偏移地址送IP,高字内容为 段地址送CS。
CALL DWORD PTR[DI] IPH IPL
CALL
代码段
[DI]
CSH
CSL
[DI]+1 [DI]+2 [DI]+3
数据段
用 堆 栈 传 递 参 数
主程序将子程序的入口参数压入堆 栈,子程序从堆栈中取出参数; 子程序将出口参数压入堆栈,主程 序弹出堆栈取得它们。
例3.6.2c
• • 入口参数: 顺序压入偏移地址和元素个数 出口参数: AL=校验和
Checksumc proc push bp 子程序c mov bp,sp push bx push cx mov bx,[bp+6] mov cx,[bp+4] xor al,al sumc: add al,[bx] inc bx •利用BP间接寻址取出 loop sumc 参数 pop cx pop bx 注意位移量 pop bp •主程序实现平衡堆栈: ret add sp,n Checksumc endp •子程序实现平衡堆栈: ret n
AX ;调用地址由AX给出 WORD PTR[SI] ;调用地址由存储器给出
对于 CALL WORD PTR [SI]这条指令 若:(DS) = 8000H,(SI) = 1200H 则指令操作图示如下。 CALL IPH IPL
81200H 81201H 代码段
数据段
(3)段间直接调用
子程序的段地址和偏移地址直接由CALL指令给出。 格式:CALL far_proc ;far_proc为远过程地址 指令的操作为: ① SP←(SP)-2 SS:(SP)←(CS) ;CS压栈 ② CS←段地址 ③ SP←(SP)-2 SS:(SP)←(IP) ④ IP←偏移地址 ;IP压栈
主程序c mov ax,offset array push ax mov ax,count push ax 参 call checksumc 见 add sp,4 图 mov result,al
3.6.4 子程序及其调用程序举例
例3.29 编制将标准设备输入的ASCII十进制数(如键盘 输入的十进制数)转换为16位二进制数的子程序(键入十 转二ABCDCB )。 算 法: ((X i×10+X i-1 ) ×10)+ X i-2 入口参数:DS:SI←待转换ASCII十进制数的首地址 CX ← ASCII十进制数的位数 出口参数:AX ←转换结果,即16位二进制数 方法1 调用子程序的主程序与子程序在同一模块同一 代码段,子程序过程应定义为NEAR过程,与主程序过程 并列放在同一代码段中。
当前(IP)+位移量=目的地址
则位移量为: 0120-0105H=001BH 于是CALL 0120H的机器码为E8 1B 00 CS:0102 E8 CS:0103 1B CALL 0120H CS:0104 00 CS:0105 ……
(2)段内间接调用
子程序的偏移地址在寄存器或存储器中。 格式:CALL mem16/reg16 CALL执行时,它首先将IP内容压栈, ① SP←SP-2, SS:[SP]←IP 然后把指定的寄存器/存储器的内容送入IP。 ② IP ← mem16/reg16 例: CALL CALL
POP DX ;恢复原AX值到DX AND DL,0FH ;转换AL的低4位 OR DL,30H CMP DL,39H JBE ALDISP2 ADD DL,7 ALDISP2:MOV AH,2 ;显示 INT 21H POP DX POP CX POP AX RET ;过程返回 ALDISP ENDP
用 寄 存 器 传 递 参 数
把参数存于约定的寄存器中,可以传值, 也可以传址。 子程序对带有出口参数的寄存器不能保 护和恢复(主程序视具体情况进行保护)。 子程序对带有入口参数的寄存器可以保 护,也可以不保护;但最好一致。
例3.6.2a
• • 入口参数:CX=元素个数, DS:BX=数组的段地址:偏移地址 出口参数:AL=校验和
子程序与主程序在同一模块但不在同一代码段, 子程序应定义为FAR过程。
SUBCODE SEGMENT ASSUME CS: SUBCODE ABCDCB PROC FAR MOV AX, 0 ABCDC1: PUSH CX MOV CX, 10 MUL CX AND BYTE PTR[ SI], 0FH ADD AL, [SI] ADC AH, 0 RET INC SI ABCDCB ENDP POP CX SUBCODE ENDS LOOP ABCDC1
…… ;置入口参数(DS←数组段地址) MOV BX,OFFSET ARRAY;BX←数组偏址 MOV CX,COUNT ;CX←数组元素个数 CALL CHECKSUMA ;调用求和过程 MOV RESULE,AL ;处理出口参数 主程序a
CHECKSUMA PROC XOR AL,AL ;累加器清0 子程序a SUMA:ADD AL,[BX] ;求和 INC BX ;指向下一个字节 LOOP SUMA RET CHECKSUMA ENDP END