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?
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 proc
s 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 Maybe it's ret
but I don't know the exact method.ret
, maybe not. You'll have to read the documentation for your OS and environment.