Сохранение регистров ECX перед вызовами процедур и манипуляция с регистром ecx - PullRequest
0 голосов
/ 08 июня 2019

У меня возникли проблемы с попыткой сохранить регистр ecx в стеке, а затем использовать регистр ecx для цикла внутри вызова процедуры. Вывод, который я получаю из чисел, связан с ecx, который находится в main, а не с процедурой. Суть проблемы заключается в том, чтобы взять строки в 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 Ответ

1 голос
/ 08 июня 2019

Irvine32 ReadString имеет 2 args согласно документации :

  • EDX указывает на входной буфер
  • ECX максимальное число ненулевых символов для чтения

В том месте, где вы используете макрос getstring для его вызова, ECX хранит счетчик цикла main, поэтому ограничение на максимальную длину символа уменьшается с каждой итерацией внешнего цикла.

Выберите другой регистр для вашего счетчика цикла, например, EDI или EBX, чтобы вы могли оставить ECX равным длине буфера или позволить readval уничтожить ECX.

Инструкция loop медленная на всех современных процессорах, кроме AMD Bulldozer / Ryzen, поэтому ее не следует использовать в первую очередь, кроме случаев, когда вы оптимизируете размер кода. Но если вы, и , на самом деле удобно использовать понижающий счетчик в 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 или 3-операнд imul для умножения целых чисел , если вы на самом деле не хотите результат высокого значения. Вам не нужно страдать, используя неудобный и менее эффективный 1-операнд mul.

...