我正在使用 MASM 和 Irvine32 编写基本的 ASM 代码。代码按字符输入,用户输入他们想要输入的字符数,然后循环运行多次。
该程序仅接受字母。如果按下 num 或其他内容,则显示“拒绝”消息。
如果 ECX == 0(用户输入的数字),则返回 Main(调用函数)。
现在我的问题是
ret
指令将控制权交还给同一过程(称为过程)的开始,而不是返回到主程序(调用者)。
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
关于堆栈溢出RET函数返回到代码开头而不是CALL点有一个类似的问题,但是有
push
和pop
,而我还没有触及堆栈。Assembly 的控制流很像 BASIC,从某种意义上说,一旦程序开始,除非重定向,否则它将继续前进到下一个“行”。这是
eip
寄存器的硬编码行为;它被设置为起始地址并读取该地址处的字节以确定要运行的指令,然后将该指令的大小添加到其自己的内部值并重复。如果您的代码没有像 jmp
、call
和 ret
这样的控制流指令,CPU 将按照编写的顺序运行程序中的所有代码。 CPU 看不到标签和 proc
;它们的存在只是为了您的方便。汇编器将它们转换为相对于起始位置(即 .code
标签)的内存地址。
.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
...
一旦您的代码到达
call String_Input
,eip
寄存器将被重定向到指令mov ebx, 0
(因为它是直接在String_Input PROC
下面的指令,并从那里继续向前。在您的String_Input PROC
内部是一个ret
,当ret
执行时,eip
寄存器被设置为紧随call String_Input
之后的指令。这就是问题所在,因为main endp
不是指令,它只是为了方便而存在。下一条指令是 mov ebx,0
,这是 String_Input
例程的第一条指令。这就是为什么 ret
似乎只是将控制流再次返回到同一函数 - 因为它直接在调用之后出现!
主要要点是标签和
proc
声明不会改变控制流,这与C和其他高级语言中的花括号不同。
解决此问题的最简单方法是在
ret
末尾添加 proc
。 main
是此规则的一个显着例外,因为正确退出 main
的方式是特定于平台的。 ret
,但我不知道确切的方法。ret
,也许不是。您必须阅读适用于您的操作系统和环境的文档。