assemblyx86masmirvine32

Ret (from a Procedure) takes control back to the start of that Procedure instead of Main


I am writing a basic ASM code using MASM & Irvine32. The code takes input character-wise, the user enters how many characters they want to enter, and the loop runs that many times.
The Procedure only accepts Alphabets. Displays 'rejected' message if num or something else pressed.
Returns to Main (calling function) if ECX == 0 (number entered by user).
Now my problem is that the ret instruction gives back the control to the start of the same Procedure (called procedure) instead of going back to main (caller).

include Irvine32.inc
.data
Input_Prompt BYTE "Enter String:", 0
Max_Length_input BYTE "Enter max length to read:", 0
Rejected_mess BYTE "Rejected !", 0
ret_mess BYTE "returing mess !", 0
USER_STR BYTE ?
.code
main PROC
    mov edx, offset Max_Length_input    ;ask for max length
    call WriteString
    call Readint
    mov ecx, eax
    
    mov esi, offset USER_STR ;passing offset of storage
    call String_Input
main endp

String_Input PROC
    mov ebx, 0
    mov edx, offset Input_Prompt ;ask for input
    l1:
        call WriteString
        call Readchar
        call writechar
        call crlf
        cmp al, 'a'
        JA L2
        cmp al, 'z'
        JB L2
        L2:
        cmp al, 'A'
        JA break
        JB Reject
        cmp al, 'Z'
        JB break
        JA Reject
        break:
        mov [esi+ebx], al   ;filling chars in the storage
        inc ebx
    loop l1
    mov edx, offset USER_STR    ;displaying final data in storage
    call writestring
    
    mov edx, offset ret_mess
    call crlf
    call writestring
    call crlf
    ret 

    reject:
    mov edx, offset Rejected_mess
    call crlf
    call writestring
String_Input endp
exit
end main

There is a similar question on stack overflow RET function returning to beginning of code instead of CALL point, but that has push and pop whereas I haven't touched the stack.
What am I missing?


Solution

  • Assembly's control flow is a lot like BASIC's, in the sense that once the program begins it will keep going to the next "line" unless redirected. This is the hardcoded behavior of the eip register; it's set to a starting address and reads the bytes at that address to determine what instruction to run, then adds the size of that instruction to its own internal value and repeats. If your code has no control flow instructions like jmp,call, and ret, the CPU will just run all the code in your program in the order it was written. Labels and procs aren't seen by the CPU; they exist solely for your convenience. The assembler translates them into a memory address that is relative to the starting location (i.e. the .code label.)

    .code
    main PROC
        mov edx, offset Max_Length_input    ;ask for max length
        call WriteString
        call Readint
        mov ecx, eax
        
        mov esi, offset USER_STR ;passing offset of storage
        call String_Input
    main endp
    
    String_Input PROC
        mov ebx, 0
    
        ...
    

    Once your code gets to call String_Input, the eip register is redirected to the instruction mov ebx, 0 (because it's the instruction directly underneath String_Input PROC and continues forward from there. Inside your String_Input PROC is a ret, and when the ret is executed, the eip register is set to the instruction immediately after call String_Input. And that is where the problem lies, as main endp isn't an instruction, it just exists for your convenience. The next instruction is mov ebx,0, which is the first instruction of your String_Input routine. Which is why the ret appears to just give control flow back to the same function again- because it comes directly after the call!

    The main takeaway is that labels and proc declarations do not alter control flow, unlike curly braces in C and other high level languages.

    Easiest way to fix this problem is to have a ret at the end of your proc. main is a notable exception to this rule, as the way you properly exit main is platform-specific. In your case it's most likely not ret but I don't know the exact method. Maybe it's ret, maybe not. You'll have to read the documentation for your OS and environment.