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

时间:2019-06-08 06:41:54

标签: assembly x86 masm cpu-registers irvine32

尝试将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

1 个答案:

答案 0 :(得分: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-operand mul不必痛苦。