I come, I see, I conquer

                    —Gaius Julius Caesar

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 :: 管理 ::

 

一、调用子程序时信息的保护与恢复方法

(1) 在子程序中进行

SUBROUTE PROC
         
PUSH AX
         
PUSH BX
         
PUSH CX
         ...
         
POP CX
         
POP BX
         
POP AX
         
RET
SUBROUTE ENDP

 

(2) 在主程序中进行

         ...
         
PUSH AX
         
PUSH BX
         
PUSH CX
         
CALL SUBROUTE
         
POP CX
         
POP BX
         
POP AX
         ...

 

 

二、利用堆栈传递参数及堆栈的修正

;将一组BCD数转换成16位二进制数
;
主程序
STACK   SEGMENT STACK
        DB 
256 DUP(?)
STACK   ENDS

DATA    SEGMENT
BCD1    DB 07H,06H,07H,02H,03H
LENG1   DW 
5
RESULT  DW ?
DATA    ENDS

CODEM   SEGMENT
        ASSUME 
CS:CODEM, DS:DATA, SS:STACK
START:  MOV   AX,DATA
        
MOV   DS,AX
        
;将被转换的BCD数所在单元段地址、偏移地址、BCD数的位数依次压栈,传递给子程序
        PUSH  DS
        
LEA   SI,BCD1
        
PUSH  SI
        
MOV   CX,LENG1
        
PUSH  CX
        ; 堆栈内容(栈底:DS, SI, CX)
        
;调用子程序
        CALL  FAR PTR BCD-16B
        
POP   DX
        
MOV   RESULT,DX
        
MOV   AH,4CH
        
INT   21H
CODEM  ENDS

CODES   SEGMENT
        ASSUME  
CS:CODES
BCD-16B PROC FAR
        ; 堆栈内容(栈底:DS, SI, CX, CS, IP)// CS和IP是由于call far ptr标号指令的调用而自动压入的
        PUSH  BP    ;保存BP
        ; 堆栈内容(栈底:DS, SI, CX, CS, IP, BP)
                                             ↑sp当前指向
        MOV   BP,SP ;保存SP
        ; 堆栈内容(栈底:DS, SI, CX, CS, IP, BP)
                                             ↑_bp和sp的当前指向
        ;子程序中的入栈操作
        PUSH  DS
        
PUSH  SI
        
PUSH  CX
        
PUSH  BX
        
PUSH  AX
        ; 堆栈内容(栈底:DS, SI, CX, CS, IP, BP, DS, SI, CX, BX, AX)
                                             ↑_bp指向           ↑_sp指向

        ;根据BP取主程序传递过来的参数
        
MOV   CX,[BP]+6
        MOV
   SI,[BP]+8
        MOV
   DS,[BP]+10
        
;子程序的实际代码
        ADD   SI,CX
        
MOV   DX,0
BCDL:   DEC   SI
        
PUSH  CX  
        
MOV   AL,[SI]
        
AND   AL,0FH
        
CBW
        
MOV   BX,AX
        
MOV   AX,DX
        
MOV   CX,10
        
MUL   CX
        
MOV   DX,AX
        
ADD   DX,BX
        
POP   CX 
        LOOP  BCDL
        
;计算结果入栈
        MOV   [BP]+10,DX
        
;子程序中的出栈操作
        POP   AX
        
POP   BX
        
POP   CX
        
POP   SI
        
POP   DS
        ; 堆栈内容(栈底:DS, SI, CX, CS, IP, BP)
                                             ↑_bp和sp的当前指向

        ;恢复BP
        POP   BP
        ; 堆栈内容(栈底:DS, SI, CX, CS, IP)
                                         ↑_sp的当前指向

        ;该指令将返回地址弹出堆栈, 同时执行SP+4, 有效地将参数从堆栈中移除并返回到调用代码片
        RET   4
        ; 堆栈内容(栈底:DS, SI, CX)
                                 ↑_sp的当前指向

BCD-16B ENDP
CODES   ENDS
        END  START

 

 

三、利用堆栈传递参数的例子

ignore_int:
    pushl %eax
;信息保存
    pushl %ecx
    pushl %edx
    push %ds
    
push %es
    
push %fs

    movl $0x10,%eax
    mov %ax,%ds
    
mov %ax,%es
    
mov %ax,%fs
    pushl $int_msg
;prink函数的参数
    
call _printk ; 
    popl %eax ;
在printk函数中,pop给eax的是堆栈中的什么值呢?怎么放到堆栈中去的?程序自动处理的?

    
pop %fs ;信息恢复
    
pop %es
    
pop %ds
    popl %edx
    popl %ecx
    popl %eax
    
iret

 

int printk(const char *fmt, ...)
{
    va_list args
;
    int i;

    va_start(args, fmt)
;
    i=vsprintf(buf,fmt,args);
    va_end(args);
    __asm__("push %%fs\n\t"   ;保存fs
        "push %%ds\n\t"
        
"pop %%fs\n\t"
        ;在C语言中, 参数入栈顺序按被调用函数从右往左
        
"pushl %0\n\t"        ;字符长度入栈
        "pushl $_buf\n\t"     ;buf入栈
        "pushl $0\n\t"        ;channel 0入栈
        "call _tty_write\n\t"
        
"addl $8,%%esp\n\t"   ;调用者修正堆栈(适用C)
        "popl %0\n\t"         ;字符长度出栈
        "pop %%fs"            ;恢复fs
        ::"r" (i):"ax","cx","dx");
    return i;
}

 

int tty_write(unsigned channel, char * buf, int nr)
{
    ...
}

 

 

参考:Procedures and Functions

 

posted on 2011-08-14 23:37  jcsu  阅读(4796)  评论(0)    收藏  举报