汇编学习笔记--CALL和RET指令

一、call和ret指令

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

 

ret和retf

ret指令用栈中的数据修改IP的内容,实现段内近转移;相当于pop ip

retf指令用栈中的数据修改CS和IP的内容,实现段间转移;相当于pop ip,pop cs

 

call指令

格式:call 标号      相当于push ip,jump near ptr 标号

格式:call far ptr 标号    相当于push cs,pushi ip,jump far ptr 标号

注意:这里先push cs,在push ip,原因很简单,先push的cs是存放在内存的高地址的,正好与retf指令对应,它先取出ip,再取出cs,与call入栈的顺序正好相反。

 

可以利用call和ret指令来实现子程序的控制。子程序的框架如下:

标号A:

         指令

          ret

使用时call 标号A即可。

 

二、mul指令

mul是乘法指令。

两个相乘的数:要么都是8位,要么都是16位;

如果是8位,一个默认存放在AL中,另一个放在8为reg或内存字节单元中;

如果是16位,则一个默认在AX中,另一个放在16位reg或内存字单元中。

对于8位乘法,结果默认存放在AX中,对于16位乘法,结果高位在DX中,低位在AX中。

 

三、模块化程序设计

实例1:计算data段第一组数据的三次方,结果存放在后面一组dword单元中。

 

assume cs:code,ds:data

data segment
    dw 1,2,3,4,5,6,7,8
    dd 8 dup(0)
data ends

code segment
    start:  mov ax,data
            mov ds,ax                                
            mov di,0   ;ds:di连续的两个字节存放输入数
            mov si,10h ;ds:si连续的四个字节存放输出结果 
            
            mov cx,8        ;循环次数8次 
        s:  mov bx,[di]     ;将输入数存放在bx中
            call cube       ;条用cube子程序
            mov [si],ax     ;将计算结果保留,ax存放低位
            mov [si+2],dx   ;将计算结果保留,dx存放高位
            add di,2        ;计算数内存地址加2
            add si,4        ;计算结果内存地址加4    
            loop s               
            
            ;cube字程序实现计算N的三次方的功能
            ;输入N存放在bx中
            ;输出存放在DX,AX中
     cube:  mov dx,0   ;存放输出结果的高位DX需置零      
            mov ax,bx  ;将一个乘数存放在AX中
            mul bx     ;乘以bx中的N
            mul bx     ;在乘以N(注:这里N*N的结果有可能已经超过16位,本程序忽略了这种情况)
            ret        ;返回程序
         
            mov ax,4c00h
            int 21h
code ends
end start


 实例2:通过子程序实现在指定位置,指定颜色,显示一个以0结束的字符串

代码如下

assume cs:code,ds:data,ss:stack

data segment
    db 'welcome to masm!',0
data ends

stack segment
    db 10 dup(0)
stack ends

code segment
    start:  mov ax,data
            mov ds,ax          ;数据段初始化
            
            mov ax,stack
            mov ss,ax
            mov sp,10          ;栈段初始化
            
            mov si,0
            mov dh,13
            mov dl,35
            mov cl,2           
            call show_str      ;在13行,39列,以红色字体显示字符串
            mov ax,4c00h
            int 21h


;在屏幕上显示字符串
;dh存储行号,范围0~24,dl存储列号,范围0~79
;cl存储颜色
;ds:si指向字符串的首地址,字符串以0结束
show_str:   push ax
            push cx
            push es
            push di   ;该子程序将要使用的寄存器入栈
            
            mov al,0a0h
            mul dh      ;caculate dh*a0h,result in ax      ax = dh * a0h
            mov dh,0
            add ax,dx
            add ax,dx   ;ax = ax + 2 * dl, now we get the first position to display the string
            
            mov dx,0b800h
            mov es,dx      ;初始化es为显存的段地址
            mov di,ax      ;es:di表示显存中首个需要显示的字符串的地址,di=di+2依次显示其余的字符   
            
            mov al,cl      ;将颜色值存储到al中,因为下面需要使用cx寄存器 
            
 display:   mov cl,[si]
            mov ch,0       ;将需要显示的字符读到cx中
            jcxz ok        ;如果该字符为0,则转移到ok标号处,结束程序
            mov es:[di],cl ;既然该字符不为0,那么就将该字符写到es:di中
            mov es:[di+1],al  ;将颜色值写到es:[di+1]中
            add di,2
            inc si
            jmp short display
            
      ok:   pop di
            pop es
            pop cx
            pop ax    ;依次将寄存器出栈 
            ret      

code ends
end start

实例3:除法的溢出问题

mov ax,1000
mov bh,1
div bh
上面这个简单的程序,在编译过程中不会报错,但是在执行过程中会报错:divide overflow,也就是除法溢出。

原因很好理解,我们都知道计算结果是1000,商应该保存在al寄存器中,可以al是8位寄存器,不能保存这么大的数,这就造成了溢出的问题。

编写子程序,能够实现不会溢出的div运算(所谓不会溢出,其实就是用更多位的寄存器来保存计算结果而已)

assume cs:code,ds:data

code segment
    
;功能:进行不会产生溢出的除法运算,被除数为dword型,除数为word型,结果为dword型
;输入:ax,被除数低位;dx,被除数高位;cx,除数
;输出:ax,商的低位;dx,商的高位;cx,余数
divdw:  mov bx,ax   ;保存ax的值到bx中
        mov ax,dx
        mov dx,0
        div cx      ;计算dx/cx,商在ax中,余数在dx中,这个除法运算一定不会产生溢出
        push ax     ;将计算的商保存到栈中,它将作为最终结果的高位保存在dx中
        mov ax,bx   ;此时的被除数:dx/cx的余数放在dx中,传入此子程序的被除数的低位在ax中
        div cx
        mov cx,dx   ;保存最终结果的余数
        pop dx      ;最终结果的商的高位 
        ret
        
start:  mov ax,4240h
        mov dx,0fh
        mov cx,0ah    ;计算1000000/10(f4240h/a0h)
        call divdw
        
        mov ax,4c00h
        int 21h
code ends
end start


实例4:

将12666以十进制的形式显示在8行3列上,绿色显示

assume cs:code,ds:data,ss:stack

stack segment
    db 10h dup(0)
stack ends    

code segment
        mov ax,stack
        mov ss,ax
        mov sp,10h
        
start:  mov ax,12666
        call dtoc 
        
        mov dh,13
        mov dl,35
        mov cl,2  
        call show_str
        
        mov ax,4c00h
        int 21h    
    
;功能:将word型数据转换为十进制字符串,并以0结束
;word型数据存放在ax中
;输出字符串保存在ds:si为首的内存中
dtoc:   mov si,0
  s0:   inc si
        mov dx,0
        mov bx,10
        div bx
        ;div byte ptr 10
        ;mov dh,ah
        ;mov dl,0
        add dx,30h
        push dx
        ;mov ah,0
        mov cx,ax
        jcxz ok1
        jmp short s0
        
  ok1:  mov cx,si
        mov si,0
  s1:   pop ax
        mov [si],al
        inc si
        loop s1
        mov byte ptr [si],0
        mov si,0
        ret 
        
        
;在屏幕上显示字符串
;dh存储行号,范围0~24,dl存储列号,范围0~79
;cl存储颜色
;ds:si指向字符串的首地址,字符串以0结束
show_str:   push ax
            push cx
            push es
            push di   ;该子程序将要使用的寄存器入栈
            
            mov al,0a0h
            mul dh      ;caculate dh*a0h,result in ax      ax = dh * a0h
            mov dh,0
            add ax,dx
            add ax,dx   ;ax = ax + 2 * dl, now we get the first position to display the string
            
            mov dx,0b800h
            mov es,dx      ;初始化es为显存的段地址
            mov di,ax      ;es:di表示显存中首个需要显示的字符串的地址,di=di+2依次显示其余的字符   
            
            mov al,cl      ;将颜色值存储到al中,因为下面需要使用cx寄存器 
            
 display:   mov cl,[si]
            mov ch,0       ;将需要显示的字符读到cx中
            jcxz ok        ;如果该字符为0,则转移到ok标号处,结束程序
            mov es:[di],cl ;既然该字符不为0,那么就将该字符写到es:di中
            mov es:[di+1],al  ;将颜色值写到es:[di+1]中
            add di,2
            inc si
            jmp short display
            
      ok:   pop di
            pop es
            pop cx
            pop ax    ;依次将寄存器出栈 
            ret                   
                
code ends
end start



 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值