为什么我设置栈指针值为bp时会有2个字节的偏移量?

Why is there a 2 byte offset when I set the stack pointer value to bp?

我正在学习如何通过堆栈将参数传递给函数。我发现自己处于一种我不明白为什么的情况(一旦我在堆栈上完成了各种 pop 以保存数据),按照指令倒退(参见 DEBUG 注释):

mov ax, [bp + n]

我得到了一个 2 字节的偏移量。 我会解释。 我已经勾勒出所有发生在堆栈上的事情。当我接受教育时: mov bp, sp

我将sp中包含的地址复制到bp中(在我的例子中是00f8h)。好吧,这让我想到,从现在开始,我使用 [bp + n] 引用的所有内容都从这个地址开始。

所以,参考我注释为DEBUG的区域,如果bp = 00f8h并且我想查看内容,我应该找到我用指令sub sp, 2保持不变的字节,相反,我发现自己的起始地址具有 bp(比 00f8h 高 2 个字节)。

继续下一个 DEBUG 语句,我有:

mov ax, [bp + 2]

我应该找到我记忆中的值bp;相反,我发现自己是调用的 return 地址(比 00f8h 高 4 个字节)。

程序指令:

mov ax, [bp + 4]

我应该找到过程调用的 return 地址。相反,我找到了我从 cx 寄存器中保存的值。

简而言之,bp 似乎不是指 00f8h,而是指 00fah(高 2 个字节),因此帐户相加(不幸的是,然而,在寄存器中我看到这是不是这样的)。

请不要考虑我尝试处理传递参数情况的形式;我的问题是:

为什么会有这个2字节的偏移量?

; Use parameterized procedures.
;
;
; To assemble an .ASM file with TASM:
; # TASM PUNTATOR.ASM /L /ZI
; # TLINK PUNTATOR.OBJ /V


dosseg
.model medium

.stack 100h

.data   
    num1    db  5
    num2    db  3
    ris     db  ?
    
    
.code
    
main proc
    mov     ax, dgroup      ; mette il segmento dati in AX
    mov     ds, ax          ; imposta DS in modo da puntare ai dati
    
    xor     cx, cx          ; I clean up the cx registry
    mov     cl, num1        ; cx = 5
    
    xor     bx, bx          ; I clean up the bx registry
    mov     bl, num2        ; bx = 3
    
    push    bx              ; 2° parameter
    push    cx              ; 1° parameter
    
    xor     ax, ax          ; I prepare ax to hold the return parameter
    
    call    somma
    add     sp, 4           ; it's like I'm doing two pops. That is, I restore the 
                            ; stack pointer to its original value, before calling 
                            ; the two parameters above.
                                
    
    mov     ris, al
    
    mov     ah, 02h
    mov     dl, cl          ; print cl
    add     dl, '0'
    int     21h
    
    mov     ah, 02h
    mov     dl, '+'         ; print '+'
    int     21h
    
    mov     ah, 02h
    mov     dl, bl          ; print bl
    add     dl, '0'
    int     21h
    
    mov     ah, 02h
    mov     dl, '='         ; print '='
    int     21h
    
    mov     ah, 02h
    mov     dl, ris
    add     dl, '0'
    int     21h
    
    mov     ah, 4Ch
    int     21h             ;  Return to DOS


main endp

somma proc near
    push    bp              ; I store the base pointer in the stack
    mov     bp, sp          ; copy in bp the address of the stack pointer in this moment
                            ; momento, cioè quando inizia la procedura.
    sub     sp, 2           ; I save 2 bytes for a local variable
    
    push    cx              ; I save these two registers because I want to use them as 
    push    dx              ; registers for my local parameters, then they will be restored 
            
    
    ; DEBUG
        
    xor     ax, ax
    mov     ax, [bp]        ; Initial stored address of bp
    mov     ax, [bp + 2]    ; Address of the instruction following the call
    mov     ax, [bp + 4]    ; The value I had put from cx
    mov     ax, [bp + 6]    ; The value I had put from bx
        
    ; END DEBUG
                                
    mov     cx, [bp + 4]                
    mov     dx, [bp + 6]
                            
    mov     [bp - 2], cx
    add     [bp - 2], dx
    mov     ax, [bp - 2]
    
    pop     dx
    pop     cx
    mov     sp, bp
    pop     bp
    ret
somma endp                                                                          

    end main 

I copy the address contained in sp in bp (which in my case is 00f8h). Well, it makes me think now that everything I refer to from now on, using [bp + n], starts from this address.

当然可以。

So, referring to the area that I commented as DEBUG, if bp = 00f8h and I want to view the content, I should find the byte that I left unchanged with the instruction sub sp, 2, instead I find myself the starting address that had bp (2 bytes higher than 00f8h).

不对。

我想您可能对 pushpop 的工作方式有不同的看法。 push 会将 sp 减 2 ,然后 sp 指向的地址处存储一个字。 pop 是相反的:加载然后递增。因此,如果您正在执行 pushpop,那么 sp 将始终指向位于堆栈顶部的单词,即最近推送但尚未推送的数据没有被弹出。

现在你的函数开始于

    push    bp              ; I store the base pointer in the stack
    mov     bp, sp          ; copy in bp the address of the stack pointer in this moment

因此,如果 bp 获得值 00f8h,那么这就是 sppush 之后的值,这意味着您存储的先前值bp 位于地址 00f8h。当你 mov ax, [bp] 时,你从地址 00f8h 加载 ax,所以你得到旧的 bp 值。 [bp+2]包含return地址(位于地址00fah),[bp+4]有第一个参数(在00fch),[bp+6]有第二个参数(在 00feh)。

你的“不变字节”在sp指向的地址你减去2后,即00f6h。如果你想访问它,你需要使用地址 [bp-2].