通過下面幾個程序來一步步理解附註4用棧傳遞數據的思想:
mov ax,1
push ax ;(sp)=(sp-2)=10-2=E, (ss:[sp])=(ss:[E])=1
mov ax,2
push ax ;(sp)=(sp-2)=E-2=C, (ss:[sp])=(ss:[C])=2
mov ax,3
push ax ;(sp)=(sp-2)=C-2=A, (ss:[sp])=(ss:[A])=3
call addsub ;(sp)=(sp-2)=A-2=8, (ss:[sp])=(ss:[8])=(下一個指令的ip)
add sp,6 ;(sp)=(A+6)=10 還原爲初始值
addsub:
mov ax,ss:[sp+2] ;(ax)=(ss:[sp+2])=(ss:[8+2])=(ss:[A])=3
add ax,ss:[sp+4] ;(ax)=3+(ss:[sp+2])=3+(ss:[8+4])=3+(ss:[C])=5
add ax,ss:[sp+6] ;(ax)=5+(ss:[sp+2])=5+(ss:[8+6])=5+(ss:[E])=6
ret ;(ip)=(棧頂的值),(sp)=(sp+2)=A
mov ax,1
push ax ;(sp)=(sp-2)=10-2=E, (ss:[sp])=(ss:[E])=1
mov ax,2
push ax ;(sp)=(sp-2)=E-2=C, (ss:[sp])=(ss:[C])=2
mov ax,3
push ax ;(sp)=(sp-2)=C-2=A, (ss:[sp])=(ss:[A])=3
call addsub ;(sp)=(sp-2)=A-2=8, (ss:[sp])=(ss:[8])=(下一個指令的ip)
addsub:
mov ax,ss:[sp+2] ;(ax)=(ss:[sp+2])=(ss:[8+2])=(ss:[A])=3
add ax,ss:[sp+4] ;(ax)=3+(ss:[sp+2])=3+(ss:[8+4])=3+(ss:[C])=5
add ax,ss:[sp+6] ;(ax)=5+(ss:[sp+2])=5+(ss:[8+6])=5+(ss:[E])=6
ret 6 ;(ip)=(棧頂的值),(sp)=(sp+2)=A,(sp)=(sp+6)=10 還原爲初始值
但實際程序中,往往需要暫存寄存器,所以用sp定位方式也得變化,如下: mov ax,1
push ax ;(sp)=(sp-2)=10-2=E, (ss:[sp])=(ss:[E])=1
mov ax,2
push ax ;(sp)=(sp-2)=E-2=C, (ss:[sp])=(ss:[C])=2
mov ax,3
push ax ;(sp)=(sp-2)=C-2=A, (ss:[sp])=(ss:[A])=3
call addsub ;(sp)=(sp-2)=A-2=8, (ss:[sp])=(ss:[8])=(下一個指令的ip)
add sp,6 ;(sp)=(A+6)=10 還原爲初始值
addsub: ;這裏我們假設子程序中還有其他指令,所以要暫存相關寄存器
push bx ;(sp)=(sp-2)=8-2=6, (ss:[sp])=(ss:[6])=(bx)
push cx ;(sp)=(sp-2)=6-2=4, (ss:[sp])=(ss:[4])=(cx)
push dx ;(sp)=(sp-2)=4-2=2, (ss:[sp])=(ss:[2])=(dx)
;....假設省略了其他指令,注意下面用sp定位之前傳遞入棧的參數方式變化
mov ax,ss:[sp+6+2] ;(ax)=(ss:[sp+8])=(ss:[2+8])=(ss:[A])=3
add ax,ss:[sp+6+4] ;(ax)=3+(ss:[sp+2])=3+(ss:[2+A])=3+(ss:[C])=5
add ax,ss:[sp+6+6] ;(ax)=5+(ss:[sp+2])=5+(ss:[2+C])=5+(ss:[E])=6
pop dx ;(sp)=(sp+2)=2+2=4
pop cx ;(sp)=(sp+2)=4+2=6
pop bx ;(sp)=(sp+2)=6+2=8
ret ;(ip)=(棧頂的值),(sp)=(sp+2)=8+2=A
那麼上述用sp定位方法很不方便,因爲每次都有數據需要進棧暫存,解決方法如下:
;這裏爲了方便我們假設初始的 SS=0,SP=20H
mov ax,1
push ax ;(sp)=(sp-2)=20-2=1E, (ss:[sp])=(ss:[1E])=1
mov ax,2
push ax ;(sp)=(sp-2)=1E-2=1C, (ss:[sp])=(ss:[1C])=2
mov ax,3
push ax ;(sp)=(sp-2)=1C-2=1A, (ss:[sp])=(ss:[1A])=3
call addsub ;(sp)=(sp-2)=1A-2=18h, (ss:[sp])=(ss:[18h])=(下一個指令的ip)
add sp,6 ;(sp)=(1A+6)=20h 還原爲初始值
addsub: ;下面是用bp來定位棧中數據的方法
push bp ;(sp)=(sp-2)=18-2=16h, (ss:[sp])=(ss:[16h])=(bp)
mov bp,sp ;(bp)=(sp)=16h
sub sp,10h ;(sp)=16-10=6h, 這裏可以隨便減去一個值
;這裏我們假設子程序中還有其他指令,所以要暫存相關寄存器
push bx ;(sp)=(sp-2)=6-2=4, (ss:[sp])=(ss:[6])=(bx)
push cx ;(sp)=(sp-2)=4-2=2, (ss:[sp])=(ss:[4])=(cx)
push dx ;(sp)=(sp-2)=2-2=0, (ss:[sp])=(ss:[2])=(dx)
;下面用bp定位棧中數據,bp永遠從+4開始,與上面入棧多少數據無關
mov ax,ss:[bp+4] ;(ax)=(ss:[bp+4])=(ss:[16h+4])=(ss:[1A])=3
add ax,ss:[bp+6] ;(ax)=3+(ss:[bp+4])=3+(ss:[16+6])=3+(ss:[1C])=5
add ax,ss:[bp+8] ;(ax)=5+(ss:[bp+8])=5+(ss:[16+8])=5+(ss:[1E])=6
pop dx ;(sp)=(sp+2)=0+2=2
pop cx ;(sp)=(sp+2)=2+2=4
pop bx ;(sp)=(sp+2)=4+2=6
mov sp,bp ;等價於add sp,10h, 目的是還原sp,(sp)=16h
pop bp ;(sp)=(sp+2)=18h
ret ;(ip)=(棧頂的值),(sp)=(sp+2)=18+2=1A
在這個基礎上,再去理解附註4就很簡單了:
附註4最後那個C程序轉換的彙編程序中:
mov sp,bp 爲的就是還原sp,避免萬一子程序中有棧操作改變了sp,所以最後必須還原,相當於清除自身臨時變量。否則沒有這句話保護可能導致整個程序崩潰。
至於 add sp,6 爲的是平衡棧,因爲之前sp減了6,將棧還原到初始狀態
inc word ptr [bp-2] 對應 C++