十八、使用call和ret指令实现子程序的调用和返回

1. 子程序实现的基础——跳转:

    1) 汇编中的子程序即等价于C语言的函数,即实现程序的模块化;

    2) 在汇编语言中,子程序其实就是以一个标号起始,最后有类似C函数的返回指令的一段代码块,主程序可以在中途调用该代码块(其实就是跳转到子程序执行),调用结束后再从子程序返回到调用处(其实就是从子程序处跳转回调用它的地方);

    3) 也就是说子程序实现的基础就是跳转,即需要转移指令的支持;


2. 利用call和ret指令来调用子程序和从子程序返回:

    1) call的使用方法是:call 子程序入口处地址,该地址可以是标号,也可以存放在寄存器和内存中;

    2) ret的使用方法就是直接在子程序中使用ret指令即可,可以没有参数,执行该指令会直接返回至调用子程序的位置处;

    3) 这两条指令是如何实现的?

        i. 由于调用和返回都是通过跳转实现的,因此两条指令的背后肯定都修改了cs:ip;

        ii. 考虑到最后要返回至调用处,则肯定需要在调转至子程序处之前先保存好返回时的位置(即call后面一条指令的地址),待子程序运行完后再恢复该位置并赋给cs:ip,而保存和恢复的工具必然是栈了;

        iii. 因此call、ret其实是一组复合动作,其执行流程是:

call proc_addr:
	push ++(cs:ip)
	jmp -> proc_addr

ret:
	pop (cs:ip)
    4) 由于call和ret背后也是转移,因此也是要分段内跳转和段间元跳转的:

        i. 段内跳转call:

           *1. call near ptr proc_tag:等价于jmp near ptr,因此也是一种位移转移,其中near ptr可以省略,但不建议这样做,将near ptr写上可以联想到jmp near ptr,这样不会导致记忆混乱,并且使程序清晰易读;

           *2. call 16-bit-register:子程序段内偏移地址存在16位寄存器中,等价于jmp 16-bit-register,因此是直接修改ip但不修改cs;

           *3. call word ptr 内存单元:子程序段内偏移地址存在内存单元中,等价于jmp word ptr 内存单元,也是只修改ip

           *注意:它们都会在跳转之前先push ip进行备份!没有段内你短转移!

        ii. 远跳转call:直接同时修改cs:ip

            *1. call far ptr proc_tag:等价于jmp far ptr

            *2. call dword ptr 内存单元:等价于jmp dword ptr,第16位是偏移地址,高16位是段地址;

            *注意:在跳转之前都会先保存cs:ip的值,顺序是先push cs再push ip;

        iii. 段内跳转ret:直接ret即可,其实就等价于pop ip

        iv. 远跳retf:注意,远跳是retf,即return far的缩写,f不要忘了,等价于pop (ip, cs),注意和远跳的call对应(栈是后进先出的!)

    5) call和ret要对应使用,即段内跳的call就和ret配合使用,远跳的call就和retf配合使用,千万不能交叉配合使用,虽然这样编译不会报错,但是一定会发生运行时错误或者是不可预料的错误,因为段内跳push和pop16位的地址,而远跳push和pop32位的地址,交叉使用就会相差16位,从而导致cs:ip指向异常!

一定要牢记这点!


4. 乘法指令mul:

    1) mul有两种类型,一种是两个8位相乘得到一个16位的结果,另一种是两个16位相乘得到一个32位的结果;

    2) 双8位相乘:一个乘数默认放在al中,另一个乘数可以放在任意一个8位寄存器或者内存中,结果默认放在ax中;

    3) 双16位相乘:一个乘数默认放在ax中,另一个乘数可以放在任意一个16位寄存器或者内存中,结果的高16位默认放在dx中,低16位默认放在ax中;

    4) 使用格式:

; 8-bit mul 8-bit
mov		al, XXX
mov		8-bit-register/memory, XXX
mul		8-bit-register/memory
; -> ax

; 16-bit mul 16-bit
mov		ax, XXX
mov		16-bit-register/memory, XXX
mul		16-bit-register/memory
; -> [dx:ax]
    5) 示例: 100 × 10和100 × 10000:

assume cs:code

code segment
	dd 0

start:
	mov		al, 100
	mov		ah, 10
	mul		ah

	mov		ax, 100
	mov		dx, 10000
	mul		dx

	mov		word ptr cs:[0], ax
	mov		word ptr cs:[2], dx

	mov		ax, 4C00H
	int		21H
code ends

end start
运行结果:

*1. ax -> 03E8H

*2. 将结果保存在了代码段开头定义的数据区中,结果是000F4240H



  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值