在过程调用和操作 ecx 寄存器之前保存 ECX 寄存器

Posted

技术标签:

【中文标题】在过程调用和操作 ecx 寄存器之前保存 ECX 寄存器【英文标题】:Saving ECX registers before procedure calls and manipulatation of ecx register 【发布时间】:2019-06-08 06:41:54 【问题描述】:

我在尝试将 ecx 寄存器保存在堆栈上然后在过程调用中将 ecx 寄存器用于循环时遇到了一些麻烦。我从数字中获得的输出受主程序中的 ecx 约束,而不是程序。问题的本质是在 Masm 中获取字符串,然后使用 ascii 图表表将其更改为数字,然后将这些新数字放入数组中。但是当我从用户那里输入数字时,它会受到主循环的约束,例如,如果我的 ecx 为 3,那么它将只取我输入的前三个数字。首先代码是主要的,然后是两个宏的程序。任何帮助将不胜感激。

我尝试将 ecx 寄存器推入程序内部,然后在程序结束后将其弹出,但这只会弄乱我在主程序中的循环。我还尝试在过程调用开始时使用 pushad 保存所有寄存器,然后在代码末尾使用 popad 将它们全部弹出。那也没用。




.data 
prompt1     byte    "Welcome to Low level I/O programming , Assignment6.asm, I am your Programmer Jackson Miller :)",0
prompt2     byte    "Ths will prompt you for 10 unsigned integears, make sure they can fit into a 32 bit register. After you enter 10 raw ints, I will display the list, sum, and average value",0
prompt3     byte    " Please enter a unsigned integear: ",0
prompt4     byte    " Invalid Entry",0
input       byte    200 dup(0)
list        dword   20 dup(0)   
num         dword   ?
temp        dword   ?
test1       byte    "How many times it get here",0
main PROC
    push    offset prompt1
    push    offset prompt2
    call    introduction
    mov     ecx,10
    mov     edi, offset list
    mov     ebx,0
    mov     edx,0
    fillnumbers:
    push    ecx
    push    edx
    displaystring    prompt3
    push    offset  input
    call    readval
    pop     edx
    mov     [edi+edx],eax
    add     edx,4
    pop     ecx
    loop    fillnumbers

    mov     esi, offset list
    mov     ecx,10
    mov     ebx,0
    displayints:
    mov     eax, [esi+ebx]
    call    writedec
    add     ebx,4
    loop    displayints

    exit    ; exit to operating system
main ENDP

readval     PROC
    push    ebp
    mov     ebp,esp
    retry:
    mov     edx, [ebp+8]
    getstring   edx
    mov     esi,edx
    mov     ecx,0
    check: 
    lodsb
    cmp     ax,0
    je      done
    cmp     ax,57
    jle     good
    jmp     notgood
    good:
    cmp     ax,48
    jge     doublegood
    jmp     notgood

    doublegood:
    sub     ax,48
    mov     ebx,10
    xchg    eax,ecx
    mul     ebx
    add     ecx,eax
    mov     eax,0
    jmp     check

    notgood:
    mov     edx,offset prompt4
    call    writestring
    call    crlf
    mov     edx, offset prompt3
    call    writestring
    jmp     retry

    done:
    mov     eax,ecx
    call    writedec
    pop     ebp
    ret     4


readval     ENDP




    displaystring           MACRO input
        push    edx
        mov     edx, offset input
        call    writestring
        pop     edx

    ENDM



    getstring       MACRO buffer
        push    edx
        mov     edx,buffer
        call    readstring
        pop     edx

    ENDM

【问题讨论】:

您说的是writedec 过程,对吧?不幸的是,您没有发布writedec 程序... 首先不要使用loop,如果您希望ECX 用于循环内的其他内容。使用呼叫保留寄存器。 (在 Irvine32 的调用约定中,所有寄存器都是调用保留的,返回值除外。) @PeterCordes 我没有完全理解这个问题:在哪个时刻ecx 没有预期的价值? 【参考方案1】:

Irvine32 ReadString 根据the documentation 有 2 个参数:

EDX 指向输入缓冲区 ECX 要读取的最大非空字符数

在您使用getstring 宏调用它时,ECX 会保存main 的循环计数器,因此您的最大长度字符限制会随着每次外循环迭代而减少。

为您的循环计数器选择一个不同的寄存器,例如 EDI 或 EBX,这样您就可以将 ECX 设置为您的缓冲区长度,或者让 readval 销毁 ECX。

loop 指令在除 AMD Bulldozer/Ryzen 之外的所有现代 CPU 上都很慢,因此您不应该首先使用它,除非您正在优化代码大小。但是,如果您是,并且,在 ECX 中使用递减计数器实际上很方便,那么当然可以。否则以不同的方式循环,例如dec edi / jnz top_of_loop


其他错误:

lodsb / cmp ax,0:你没有将 EAX 归零,lodsb 有点像 mov al, [esi] / inc esi。所以 AX 的高字节在这里可能是非零的。如果您正在寻找零终止符,那么检查它是没有意义的。

ReadString 返回EAX = 输入字符串的大小,因此在实践中(除了非常长的输入)只有 EAX 的低字节非零,lodsb 替换它。因此,您的代码恰好适用于正常输入。

不过,通过检查第一个非数字而不是分别检查 0 和非数字要容易得多。您可以将该 ReadString 返回值用作递减计数器,但如果您正在检查其他非数字输入以结束循环,

   ... get string input
    xor   eax, eax
    jmp  first_iteration_starts_here
top_of_loop:
    imul  eax, 10
    add   eax, edx

first_iteration_starts_here:
    movzx   edx, [esi]
    inc     esi

    sub  edx, '0'
 ;; EDX = an integer from 0 .. 9   else out of bounds
    cmp  edx, 9
    jbe  top_of_loop         ; *unsigned* compare catches low characters that wrapped, too

   ; EDX = some non-digit character minus '0'
   ; total in EAX.
    ret

您正在编写 32 位代码,因此您应该使用 2 or 3-operand imul for integer multiply,除非您确实想要高半结果。您不必为使用不方便且效率较低的 1 操作数 mul 而受苦。

【讨论】:

以上是关于在过程调用和操作 ecx 寄存器之前保存 ECX 寄存器的主要内容,如果未能解决你的问题,请参考以下文章

汇编里的"PUSH"和"POP"起了啥功能,,谁能用通俗点的话告诉我吗??

汇编各寄存器的作用说明

movzbl 参数

有没有一种简单的方法可以在 AT&T 汇编中像这样将两个寄存器相乘:%eax * %ebx = %ecx

汇编之 eax, ebx, ecx, edx, esi, edi, ebp, esp??

汇编语言寄存器都叫啥?