若余相思 发表于 2017-9-21 19:35:03

第十章 CALL和RET指令

本帖最后由 若余相思 于 2017-9-21 19:35 编辑

        call和ret都是转移指令,它们都修改了IP,或同时修改了CS和IP



10.1节ret和retf

        执行ret执行下面两步操作
(1)(IP) = ((SS) * 16 + (SP))
(2)(SP) = (SP) + 2

相当于执pop ip指令

        执行retf执行下面几步操作
(1)(IP) = ((SS) * 16 + (SP))
(2)(SP) = (SP) + 2
(1)(CS) = ((SS) * 16 + (SP))
(2)(SP) = (SP) + 2

相当于执pop ip和pop cs指令

10.3节依据位移进行转移的call指令

        call 标号(将当前的IP压栈后,转到标号处执行指令)
(1)(SP) = (SP) - 2
          ((SS) * 16 + (SP)) = (IP)
(2)(IP) = (IP) + 16位位移
16位位移 = 标号处的地址 - call指令后的第一个字节的地址
16位位移的范围为 -32768 ~32767
16位位移在编译的时候算出

CPU在执行call 标号时相当于进行
push IP
jmp near ptr 标号


1.4节 转移的目的地址在指令中的call指令

        call far ptr 标号,进行如下的操作
(1)(SP) = (SP) - 2
          ((SS) * 16+ (SP)) = (CS)
          (SP) = (SP) - 2
          ((SS) * 16 + (SP)) = (IP)
(2)CS标号所在的段地址
       IP标号所在的偏移地址

CPU执行call far ptr 标号 的时候相当于执行
pushCS
push IP
jmp far ptr 标号

10.5节转移地址在寄存器中的call指令

        call 16位reg
功能:
(SP) = (SP) - 2
((SS) * 16 + (SP)) = (IP)
(IP) = (16reg)

相当于push IP
jmp 16位reg


10.6节转移地址在内存中的call指令

(1)call word ptr 内存单元地址相当于
push IP
call word ptr 内存单元地址

(2)call dword ptr 内存单元地址相当于
push CS
push IP
jmp dword ptr 内存单元地址

比如下面的指令:
mov sp 10H
mov ax, 0123H
mov ds:, ax
mov word ptr ds:, 0
call dword ptr ds:

10.7节call和ret的配合使用
程序:
mov ax, 1
mov cx, 3
call s
mov bx, ax
mov ax, 4c00H
int 21H
s: add ax, ax
loop s
ret
call s 的时候将IP入栈,然后转到标号处执行代码,执行完后,到ret后将栈中的数据放在IP中,所以会回到call后面的代码执行,程序结束

所以我们得到具有子程序的框架:
assume cs: code
code segment
main : ...
           ...
           call sub
           ...
           ...
           mov ax, 4c00H
           int 21H
sub :   ...
           ...
           ret
code ends
end main

10.8节mul指令
使用mul指令应注意以下两点:
(1)两个数相乘,要么都是8位,要么都是16位。如果是8位,一个默认放在AL中,另一个放在8位的reg或内存字节单元中
如果是16位相乘一个默认放在AX中,另一个放在16位reg或内存字单元中。
(2)结果:如果是8位相乘,结果默认放在AX中;如果是16位相乘,结果默认在DX中存放,低位在AX中放

格式如下:mul reg
                  mul内存单元

比如:mov al, 100
          mov bl 10
          mul bl
结果在(ax) = 1000

又比如16位:mov ax, 100
                     mov bx, 10000
                     mul bx
结果:(ax) = 4240H, (dx) = 000FH

10.12节寄存器冲突的问题

        因为在主程序中用到的寄存器,在子程序也会用到,所以便会出现寄存器冲突的问题
那么主要的解决的办法就是在进入子程序的时候将用到的寄存器都放在栈中,然后在ret之前,将之前的pop 出栈
比如下面的例子:
assume cs: code

data segment

        db 'good', 0
        db 'link', 0
        db 'masm', 0
        db 'aini', 0
       
data ends



code segment

start : mov ax, data
                mov ds, ax
                mov bx, 0
               
                mov cx, 4
               
        s : mov si, bx
                call capital
                add bx, 5
               
                loop s
               
                mov ax, 4c00H
                int 21H
               
        capital: push cx
                       push si
               
        change: mov cl,
                       mov ch, 0
                       jcxz ok
                       and byte ptr , 11011111B
                       inc si
                       
                       loop change
                       
                ok :pop si
                        pop cx
                        ret
                       
code ends
end start

这样就不会发生寄存器的冲突问题了

总结
(1)本章主要讲ret和call的配合使用
(2)ret是将栈中的数据放在IP中,让CPU转去IP那里执行,retf是将栈的数据都放在CS和IP中执行
(3)call 有多种格式,有call 标号、call near ptr 标号、call word ptr 内存单元、call dword ptr 内存单元和call far ptr 标号,主要功能是将当前的(IP)(前三个)或者同时将(IP)和(CS)(后两个)放在栈中
(4)介绍了乘法指令mul,分8位和16位,8位默认放在al中,16位默认放在ax中
(5)call和ret的配合使用形成了子程序(即C语言中的函数),注意在子程序中要注意寄存器的冲突问题,记得将冲突的寄存器放在栈中
页: [1]
查看完整版本: 第十章 CALL和RET指令