汇编第6章 子程序设计
int i=0; void func(){ i++; } int main(){ func();
;ax相当于i ;调用s子程序
;i++
return 0;
}
程序 DISPLAY2.ASM
汇编语言程序设计
四. 现场保护与恢复
要保护的寄存器:应该是在子程序 中将被使用,返回调用程序后仍然 需要使用其原有内容的那些寄存器。 即保护调用程序和子程序两者在使 用上发生冲突的那些寄存器。但在 编程时,一时很难弄清哪些是有冲 突的寄存器,一种较为简单的方法 是把所有的寄存器均加以保护。 一般在子程序中进行寄存器保护较 好。即在子程序的开始部分,先进 行相关寄存器(主要是在子程序中 使用的各寄存器)的保护。然后再 进行子程序的处理操作。在执行完 子程序后,返回前,先恢复各寄存 器内容后,再返回调用程序。
汇编语言程序设计
code segment main proc far …… call subp …… ret main endp subp proc near ……
code1 segment main proc far …… call far ptr subp …… ret main endp code1 ends
newchar:
add_to:
mov int sub jl cmp jl sub cmp jl cmp jge mov shl mov add pop loop ret endp
exit: hexibin
binidec dec_div proc mov mov div mov mov add mov int ret dec_div endp
; 1610
Crlf proc near push ax push dx mov dl, 0dh ; mov 162 ah,2 int 21h ; 210 mov dl, 0ah mov ah,2 int 21h pop dx pop ax ret Crlf endp
ends end
start
汇编语言程序设计
例3:累加数组中的元素(通过地址表传送参数地址)
data segment ary dw 10,20,30,40,50,60,70,80,90,100 count dw 10 sum dw ? table dw 3 dup (?) ; 地址表 ends segment proc far assume cs:code, ds:data push ds sub ax, ax push ax mov ax, data mov ds, ax mov table, offset ary ;把要传送给子程序 mov table+2, offset count;的参数都存放在地址 mov table+4, offset sum ;表中,然后把地址表 mov bx, offset table;的首地址通过寄存器 call proadd ;BX传送到子程序中去。 ret endp
binidec:输出5位的十进制数值 入口参数为BX。
binidec proc mov call mov call mov call mov call mov call ret near cx, 10000d dec_div cx, 1000d dec_div cx, 100d dec_div cx, 10d dec_div cx, 1d dec_div
指令功能: 把CALL指令的下一条指令地址(称为返回点或断点) 推入堆栈保存,然后转到目标地址(DST)。 CALL指令可以在段内、段间调用,寻址方式分为直接 和间接两种。
汇编语言程序设计
段内直接近调用:CALL DST 执行操作: (SP) ← (SP) - 2 ( (SP)+1,(SP) ) ← (IP)
dw dw dw dw dw dw
1,2,3,4,5,6,7,8,9,10 10 ?
10,20,30,40,50,60,70,80,90,100 10 ?
如果直接访问内存变量,那么累加数组ary
和数组ary1中的元素不能用同一个子程序 proadd。 那如何能实现调用同一个子程序proadd来累加另一个 数组元素?
subt
proc
push push push push …… …… pop pop pop pop
ret
near
ax bx cx dx
dx cx bx ax
subt
endp
汇编语言程序设计
五. 子程序的参数传送
入口参数:子程序需要从主程序获取的参数。使子程序可以对不同 实现的方法是把子程序所需要的入口参 数据进行相同功能的处理。 数,由调用程序预先放入指定的寄存器 出口参数:是子程序返回给主程序的参数。使子程序可以将不同的 中。在进入子程序后,子程序就可直接 结果送至主程序 对这些寄存器内容进行操作了。同样子 (1) 通过寄存器传送参数 程序的运行结果,也可置入寄存器中, 把它们作为子程序的出口参数寄存器使 (2) 通过存储器传送参数 用。由于寄存器数目有限,适用于参数 较少的情况。
执行操作: POP IP
表达式(能 计算出结果)
(IP)
。。。 (SP)→(SP)+EXP
段内带立即数近返回:RET EXP
执行操作: POP IP (SP) (SP)+EXP
(SP)→ (SP)→
段间远返回:RET(F)
执行操言程序设计 code segment main proc far Start: 主程序模块 push ds Sub ax,ax push ax …… push ax 程序返回 push bx push cx call sub …… ret 子程序模块 main endp sub proc near …… ret 6 sub endp code ends end start
code2 segment subp proc far ……
ret
subp endp code ends
ret
subp endp code2 ends
段内调用和返回
段间调用和返回
汇编语言程序设计
segx
subt
segment
proc …… ret endp …… subt …… far
subt call
segx segy
call
ends segment
…… far ptr subt ……
同一个子程序 可以被段内调 用,也可以段 间调用
segy
ends
汇编语言程序设计
三. 子程序的调用与返回指令
(1)CALL 子程序调用指令:隐含使用堆栈保存返回地址 指令格式: CALL DST ;其中DST为过程的目标地址(即过程名)。
endp near ax, bx dx, 0 cx bx, dx dl, al dl, 30h ah, 2 21h
汇编语言程序设计
例2:累加数组中的元素(通过存储器传送参数)
data segment ary count sum ends segment assume dw dw dw 1,2,3,4,5,6,7,8,9,10 10 ? proadd cs:code,ds:data proc
调用程序:编制程序时,按需要转向子程序,称为子程序调用, 或称为过程调用。调用子程序的程序称为调用程序或主程序。主、 子程序是相对而言的。但子程序一定是受调用程序或主程序调用 的。子程序定义的位置可以放在主程序的前面或后面。
汇编语言程序设计
子程序与调用程序
子程序调用
主程序: … CALL ---… CALL ---… CALL ---…
call
ax, data ds, ax
proadd
next:
main
mov int endp
ax,4c00h 21h
code
ends end
start
proadd
endp
汇编语言程序设计
如果数据段定义如下:
data segment
ary count sum ary1 count1 sum1
data ends
汇编语言程序设计 hexibin:接收4位十六进制数的输入 出口参数为BX。
hexibin proc mov mov push near bx, 0 cx,4 cx ah, 1 21h al, 30h exit al, 10 add_to al, 27h al, 0ah exit al, 10h exit cl, 4 bx, cl ah, 0 bx, ax cx newchar
(IP) ← (EA)
DST给出寄存器或存储单元的内 容(转向地址) 比如: CALL word ptr [bx]
汇编语言程序设计
DST给出子程序的入口地址 (子程序为far属性) 比如:CALL far ptr subp
段间直接远调用:CALL FAR PTR DST
执行操作: PUSH CS PUSH IP (IP) ← DST偏移地址 (CS) ← DST段地址
子程序: … … … RET
子程序返回
汇编语言程序设计
二. 过程定义伪操作
子程序入口 的符号地址
过程名 PROC . . . RET 过程名 ENDP
NEAR ( FAR )
子程序返回
(1)NEAR属性:调用程序和子程序在同一代码段中, 可省略。(段内调用) (2)FAR属性:调用程序和子程序不在同一代码段中。 (段间调用)
hexidec:接收键盘输入的十六进制数,在屏 幕上输出相应的十进制数
hexidec main start: segment assume cs: hexidec proc far push ds sub ax, ax push ax call call call call main …… …… …… hexidec ret endp hexibin crlf binidec crlf