assemblyx86-16tasmdosbox

My procedure from another module does not return correctly to main procedure from main module, after some extra pushes


I have 2 module files, one main file and one aux file. In the main file I call a procedure from the second module (which reads a number from the keyboard and stores it in AX). When I debug the code, I notice that when the code returns from the aux procedure to the main procedure, it goes in an infinite loop.

main.asm Module with main procedure:

EXTRN READNR:FAR

DATE SEGMENT PARA 'DATA'
     EMPTY DB ?
DATE ENDS

CODEMAIN SEGMENT PARA 'CODE'
ASSUME CS: CODEMAIN, DS: DATE
MAINPROC PROC FAR
    PUSH DS
    XOR AX, AX  
    PUSH AX
    MOV AX, DATE
    MOV DS, AX


    CALL READNR
    
    RET
MAINPROC ENDP
CODEMAIN ENDS
END MAINPROC

aux.asm Module with aux procedure:

DATA SEGMENT PARA 'DATA'
    NUMBER DB 6, ?, 6 DUP(?)
DATA ENDS

AUX SEGMENT PARA 'CODE'
PUBLIC READNR
ASSUME CS: AUX, DS: DATA
READNR PROC FAR
    PUSH AX
    XOR AX, AX
    PUSH DS
    MOV AX, DATA
    MOV DS, AX

    MOV DX, OFFSET NUMBER 
    MOV AH, 0Ah
    INT 21h
    
    MOV BX, 10
    MOV AX, 0
    MOV CX, 0
    MOV SI, 2
    MOV CL, NUMBER[1]
LOOP1:
    MUL BX
    ADD AL, NUMBER[SI]
    SUB AL, 30h
    INC SI
    LOOP LOOP1
    
    RET
READNR ENDP
AUX ENDS
END

I used these commands to link the 2 modules:

tasm main
tasm aux
tlink main aux
td main

Can you find out why it doesn't return correctly?


Solution

  • READNR is pushing twice and not ever popping. I don't see how it could possibly return to the caller.

    So if I push on the stack in some module and the stack remains non-empty, the program will not return properly to the main module?

    Yes: the call instruction pushes a return address onto the stack, and that's where the ret instruction looks for it — as the top item on the stack.  If you modify the stack by pushing, then whatever you've pushed is now the top thing on the stack.  In order to use return using ret, a function must remove all the items placed onto the stack, restoring the stack pointer to the same value it had upon entry.

    If you leave extra things on the stack, first the ret will not work as expected, but even if somehow could get back to the caller, the caller would likely also have issues because callers expect the stack to be in the same state they left it.