1
votes

I'm learning how to pass parameters to a function via the stack. I find myself in a situation where I don't understand why (once I've done the various pop's on the stack to save the data), going backwards with the instruction (see DEBUG comments):

mov ax, [bp + n]

I end up with a 2-byte offset. I'll explain. I have sketched everything that happens exclusively on the stack. When I get to education: mov bp, sp

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).

Going on with the next DEBUG statement, I have:

mov ax, [bp + 2]

I should find the value I had memorized of bp; instead I find myself the return address of the call (4 bytes higher than 00f8h).

The program instruction:

mov ax, [bp + 4]

I should find the return address of the procedure call. Instead I find the value I had saved from the cx register.

In short, it seems that bp does not refer to 00f8h but rather to 00fah (2 bytes higher), so that the accounts add up (unfortunately, however, in the registers I see that this is not the case).

Please do not consider the form in which I try to handle the situation of passing parameters; my problem is:

why is there this 2-byte offset?

; 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 

enter image description here

1

1 Answers

4
votes

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.

Sure.

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).

That's not right.

I think maybe you have an off-by-one in your thinking about how push and pop work. push will decrement sp by 2 and then store a word at the address where sp points. pop is the reverse: load and then increment. So if you're doing push and pop, then sp will always point to the word which is at the top of the stack, i.e. the data which was most recently pushed and hasn't been popped.

Now your function starts with

    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

So if bp gets the value 00f8h, then that's the value that was in sp right after the push, and this means your stored previous value of bp is at address 00f8h. When you mov ax, [bp], you load ax from address 00f8h and so you get the old bp value. [bp+2] contains the return address (located at address 00fah), [bp+4] has the first parameter (at 00fch), [bp+6] has the second parameter (at 00feh).

Your "unchanged byte" is at the address where sp pointed after you subtracted 2, namely 00f6h. If you want to access it, you need to use address [bp-2].