题目
题目和上一篇一样,都是累加数组中的元素。
思路
采用通过堆栈传送参数地址法的程序,是在主程序里把参数地址保存到堆栈中,在子程序里从堆栈中取出这些参数以达到传送参数的目的。必须注意,子程序结束时的RET指令应该使用带常数返回的指令,常数大小和保存到堆栈的地址相关,以便返回主程序后,堆栈能恢复原始状态不变。
堆栈情况示意图,图中所示程序是far型的,Call的时候push进了cs和ip。
为什么是bp? 因为bp默认的段地址是堆栈段,如果用其它如(bx)需要 ss:[bx]
代码
;;;;;;;;通过堆栈传送地址;;;;;;;;;;;
datasg segment
ary dw 1,2,3,4,5,6,7,8,9,10
count dw 10
sum dw ?
datasg ends
;--------------------
codesg segment
assume ds:datasg,es:datasg,cs:codesg
;----------MAIN---------
main proc far
start:
push ds
sub ax,ax
push ax
mov ax,datasg
mov ds,ax
mov es,ax
;------------
mov ax,offset ary
push ax
mov ax,offset count
push ax
mov ax,offset sum
push ax
call proadd
ret
;-----------
main endp
;----------MAIN---------
;---------PROADD--------
proadd proc near
push bp ;为什么使用bp呢?因为bp默认的段地址是堆栈段,如果用其它如(bx)需要 ss:[bx]
mov bp,sp
push ax ;保护寄存器
push cx
push si
push di ;保护寄存器
mov si,[bp+8] ;获取addr的地址
mov di,[bp+6] ;获取count的地址
mov cx,[di] ;获取count的值
mov di,[bp+4] ;获取sum的地址
xor ax,ax
next:
add ax,[si]
add si,2
loop next
mov [di],ax
pop di ;保护寄存器
pop si
pop cx
pop ax ;保护寄存器
pop bp
ret 6 ;为什么ret 6 呢,是因为ret pop出了ip,而需要再把压入堆栈的三个地址pop出来
proadd endp
;---------PROADD--------
codesg ends
end start
调试
反汇编
push地址之前的情况
push地址之后,堆栈段多了3个地址
执行CALL之后堆栈段的情况,因为子程序是near行的,所以只push进了ip,如果是far型,需要push 进 cs 和 ip。
子程序里的内容
用bp保存当前的位置,随后push进ax,bx,cx,…等需要保护的寄存器
取地址正确
g到子程序将要结束的地方
执行ret 6后,pop出了3个地址,回到主程序
查看结果