首页 > 解决方案 > 为什么我将堆栈指针值设置为 bp 时会有 2 个字节的偏移量?

问题描述

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

mov ax, [bp + n]

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

我复制其中包含的地址spbp在我的情况下是 00f8h)。好吧,这让我想到,从现在开始,我所指的一切,使用[bp + n],都是从这个地址开始的。

因此,参考我评论为 DEBUG 的区域,如果bp= 00f8h 并且我想查看内容,我应该找到我在指令中保持不变的字节sub sp, 2,而不是我发现自己的起始地址bp(2 个字节高于00f8h)。

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

mov ax, [bp + 2]

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

程序指令:

mov ax, [bp + 4]

我应该找到过程调用的返回地址。相反,我找到了我从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 

在此处输入图像描述

标签: assemblystackx86-16masm

解决方案


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

当然。

因此,参考我评论为 DEBUG 的区域,如果 bp = 00f8h 并且我想查看内容,我应该找到我用指令 sub sp, 2 保持不变的字节,而不是我找到自己的起始地址bp(比 00f8h 高 2 个字节)。

那是不对的。

我想也许你对如何工作pushpop工作的想法是一成不变的。 push将减sp2 ,然后sp在指向 的地址存储一个字。pop是相反的:加载然后递增。因此,如果您正在执行pushand pop,那么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获取 value 00f8h,那么这就是在sp之后的值push,这意味着您存储的先前值bp是在 address 00f8h。当你,你从地址mov ax, [bp]加载,所以你得到旧值。 包含返回地址(位于 address ),具有第一个参数(at ),具有第二个参数(at )。ax00f8hbp[bp+2]00fah[bp+4]00fch[bp+6]00feh

您的“未更改字节”位于减去 2sp指向的地址,即. 如果你想访问它,你需要使用 address 。00f6h[bp-2]


推荐阅读