Ret(来自过程)将控制权返回到该过程的开头,而不是 Main

问题描述 投票:0回答:1

我正在使用 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 x86 masm irvine32
1个回答
5
投票

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
,也许不是。您必须阅读适用于您的操作系统和环境的文档。

© www.soinside.com 2019 - 2024. All rights reserved.