一、调用子程序时信息的保护与恢复方法
(1) 在子程序中进行
SUBROUTE PROC
PUSH AX
PUSH BX
PUSH CX
...
POP CX
POP BX
POP AX
RET
SUBROUTE ENDP
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
...
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
; 主程序
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
; 调用子程序
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
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
; 子程序中的入栈操作
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)
BCD-16B ENDP
CODES ENDS
END START
↑_sp的当前指向
BCD-16B ENDP
CODES ENDS
END START
三、利用堆栈传递参数的例子
ignore_int:
pushl %eax ;信息保存
pushl %ecx
pushl %edx
pushl %eax ;信息保存
pushl %ecx
pushl %edx
push
%ds
push %es
push %fs
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 "
{
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 ;
}
" 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)
{
...
}
{
...
}