zoukankan      html  css  js  c++  java
  • 汇编10:CALL和RET指令

    CALL和RET指令

    call和ret指令都是转移指令,它们经常被共同用来实现子程序的设计。

    ret和retf

    ret指令用栈中的数据实现修改IP的内容,从而完成近转移,执行ret指令时相当于执行:

    pop IP
    

    retf指令用栈中的数据实现修改CS和IP的内容,从而完成远转移,执行retf指令相当于执行:

    pop IP
    POP CS
    

    在程序中就可以使用这两个指令来转移:

    code segment
    	mov ax,4c00h
    	int 21h
    start:	...
    	...
    	ret
    code ends
    end start
    

    这样由于ret指令的执行,就会让CS:IP指向代码段的第一条指令,完成返回。

    call指令

    call指令执行后进行两步操作:

    1、将当前的IP或CS和IP压入栈中

    2、转移

    转到标号处

    call指令可以转到标号处,同时将当前的IP压入栈中:

    call 标号
    

    相当于执行:

    push IP
    jmp near ptr 标号
    

    这个指令对应的机器码中有相对于当前IP的转移位移。

    对应的段间转移指令:

    call far ptr 标号
    

    相当于执行:

    push CS
    push IP
    jmp far ptr 标号
    

    这个指令对应的机器码中有转移的目的地址。

    使用内存地址和寄存器

    call指令后也可以跟寄存器:

    call 16位reg
    

    相当于执行:

    push IP
    jmp 16位reg
    

    call指令后也可以加内存地址,一般有两种形式:

    1、段内转移:

    call word ptr 内存单元地址
    

    相当于执行:

    push IP
    jmp word ptr 内存单元地址
    

    2、段间转移:

    call dword ptr 内存单元地址
    

    相当于执行:

    push CS
    push IP
    jmp dword ptr 内存单元地址
    

    实现子程序机制

    结合ret和call指令我们可以总结一个实现子程序的框架:

    assume cs:code
    code segment
    	main:	...
    		...
    		call sub1					调用了子程序sub1
    		...
    		...
    		mov ax,4c00h
    		int 21h
    	sub1:	...
    		...
    		call sub2					调用了子程序sub2
    		...
    		ret						sub1返回
    	sub2:	...
    		...
    		ret						sub2返回
    			
    code ends
    end main
    

    乘法指令mul

    使用格式:

    mul reg
    mul 内存单元
    

    涉及内存单元时,可以中间加word ptr或byte ptr来指定处理的数据长度。

    乘法指令中两个相乘的数要么都是8位,要么都是16位;如果都是8位,则一个默认放在AL寄存器中;如果都是16位,则一个默认放在AX寄存器中。

    如果是8位乘法那么结果默认放在AX中,如果是16位乘法,那么高位默认放在DX中,低位放在AX中。

    模块化程序设计

    有了子程序我们就可以实现汇编语言编程的模块化设计。

    参数和结果传递的问题

    在使用子程序时有两个问题:参数存在哪里?结果存在哪里?

    最常见的方法是使用寄存器,调用子程序者将参数送入参数寄存器,从结果寄存器中取到返回值;子程序从参数寄存器中取到参数,将返回值送入结果寄存器。

    多个参数和多个结果的传递

    如果我们要传递更多的参数怎么办?我们不可能都用寄存器来完成传递,因为寄存器的个数终究是有限的。

    这个时候常用的做法是将批量数据放到内存中,然后将他们所在内存空间的首地址放在寄存器中,传递给需要的子程序。对于多个返回结果,我们也可以用同样的方法。

    栈也可以用来完成这一类任务。

    寄存器冲突问题

    有时程序中会出现寄存器冲突问题,如在一个循环中调用某个子程序,子程序中也有循环,这些循环共用一个cx寄存器来计数,就会导致循环次数的混乱。此时正确的做法应该是在子程序的开始将子程序中所有用到的寄存器中的内容都保存起来,在子程序返回前都恢复。

    案例需求:把数据段中的所有字符串都转换为大写:

    assume cs:code
    data segment
    	db 'word',0
    	db 'unix',0
    	db 'wind',0
    	db 'good',0
    data ends
    

    此时我们需要写一个子程序来处理单个字符串转换为大写:(ds:si指向字符串的首地址)

    capital:mov cl,[si]
    	mov ch,0
    	jcxz ok							    如果cx为0,就结束
    	and byte ptr [si],11011111b				    否则就将该字母转换为大写,然后再将si自增
    	inc si
    	jmp short capital
    ok:	ret
    

    但是这个子程序没有完成环境保存,所以需要再改动一下,加上数据恢复和取用:

    capital:push cx
    	push si
    change:	mov cl,[si]
    	mov ch,0
    	jcxz ok								如果cx为0,就结束
    	and byte ptr [si],11011111b				        否则就将该字母转换为大写,然后再将si自增
    	inc si
    	jmp short change
    ok:	pop si
    	pop cx
    	ret
    

    然后再调用该子程序即可:

    code segment
    	start:	mov ax,data
    		mov ds,ax
    		mov bx,0						指定总的循环次数
    			
    		mov cx,4
    	s:	mov si,bx						处理字符串,然后将指针加5,处理下一个字符串
    		call capital
    		add bx,5
    		loop s
    			
    		mov ax,4c00h
    		int 21h
    			
    	capital:push cx
    		push si
    	change:	mov cl,[si]
    		mov ch,0
    		jcxz ok							如果cx为0,就结束
    		and byte ptr [si],11011111b				否则就将该字母转换为大写,然后再将si自增
    		inc si
    		jmp short change
    	ok:	pop si
    		pop cx
    		ret
    			
    code ends
    end start
    
  • 相关阅读:
    APP内跳转链接用INTENT,但是用系统浏览器,在内部还是要webview
    MNIST练习
    Trigger_word_detection_v1a
    Neural_machine_translation_with_attention_v4a
    Operations_on_word_vectors_v2a
    Improvise_a_Jazz_Solo_with_an_LSTM_Network_v3a-2
    Dinosaurus_Island_Character_level_language_model_final_v3b
    Building_a_Recurrent_Neural_Network_Step_by_Step_v3b
    Sequence model
    Face_Recognition_v3a
  • 原文地址:https://www.cnblogs.com/yinyunmoyi/p/12811519.html
Copyright © 2011-2022 走看看