Реверсирование и изменение регистра заданной сборки строки x86

Цель этой программы состоит в том, чтобы перевернуть заданную строку, меняя регистр каждой буквы. Строка не может быть длиннее 20 символов, если ввод длиннее, программа требует от пользователя повторного ввода строки. Программа завершается, когда пользователь вводит «ввод», и программа завершается после печати конечного предложения.

При реализации этого у меня есть 3 проблемы:

  1. Я попытался получить входную строку с помощью вызова ReadString, и, поскольку эта процедура останавливается, когда дается клавиша ввода, консоль замораживается, когда я нажимаю клавишу ввода, чтобы нормально завершить программу. Как я могу исправить свой код, чтобы он печатал завершающее сообщение, а затем нормально завершал программу с возвращаемым значением 0?

  2. Если входная строка длиннее 20 символов, пользователь должен снова ввести строку. Поэтому я написал ja L1. Но по какой-то причине mov bytecount, eax; количество байт cmp, 20; похоже, не может правильно отфильтровать случай. Когда выполняется строка mov bytecount, eax, значение bytecount правильное, но когда программа выполняет следующую строку, cmp bytecount, 20, значение bytecount изменяется. Я не знаю, что я делаю неправильно.

  3. Процедура CaseChange зависает при выполнении, поэтому я предполагаю, что она зацикливается бесконечно, но я не могу найти, какое условие неверно.

.data
MaxLength = 20
prompt3 BYTE "End of program",0
buffer BYTE MaxLength DUP(0)
bytecount DWORD ?

.code 
main PROC
    call Clrscr
L1: mov edx, OFFSET buffer
    mov ecx, SIZEOF buffer
    call PromptForInput     ; printing input prompt
    call ReadString      
    mov bytecount, eax
    cmp bytecount, 20       ;*** get input again if number of characters in the string is greater than 20
    ja L1                   ;*** 
    call ReverseString     
    call CaseChange         ;***
    mov edx, OFFSET buffer
    call WriteString        ;printing the result
    loop L1
    mov edx, OFFSET prompt3 ;*** after input <ent> how do I print prompt3?
    call WriteString
    exit
main ENDP

CaseChange PROC
    pushad 
    mov eax, OFFSET buffer
L1:
    mov dl, BYTE PTR[eax]
    test dl, dl
    jz L3
    cmp dl, 'A'
    jl L3
    xor dl,32
    cmp dl,'z'
L2:
    inc eax
    jmp L1
L3:
    popad
    ret
CaseChange ENDP

(подсказка ввода): Кошки и собаки.

(подсказка вывода): .SGOd DNA STAc

(подсказка ввода): слишком долго для данного лимита

(подсказка ввода):

Конец программы


person hj_    schedule 05.05.2019    source источник


Ответы (1)


Руководство говорит нам для ReadString:

Считывает строку, содержащую ненулевые символы ECX, из стандартного ввода, останавливаясь, когда пользователь нажимает клавишу Enter.
Нулевой байт сохраняется после введенных символов, но завершающий возврат каретки и символы перевода строки не помещаются в буфер.
ECX всегда должен быть меньше размера буфера (никогда не равен размеру буфера), поскольку нулевой байт может быть (ECX+1)-м сохраненный персонаж.

После этого становится ясно, что вам нужно увеличить буфер:

buffer BYTE MaxLength + 1 DUP (0)

Если вы указали ECX=MaxLength, вы не сможете получить ввод, длина которого превышает MaxLength символов, и поэтому нет необходимости проверять такое условие. (*)

Если пользователь нажимает клавишу enter без предшествующих символов, ReadString возвращает EAX=0. Проверьте это и перейдите к последнему сообщению.

Большая ошибка там, где вы написали loop L1. Инструкция loop работает с регистром ECX для выполнения известного количества итераций. Ваша программа должна просто вернуться наверх без каких-либо условий. Используйте jmp L1.

Лучше всего, если вы держите вещи логически вместе. Не смешивайте call PromptForInput с call ReadString и его параметрами. Можете ли вы быть уверены, что PromptForInput не изменяет EDX или ECX?

L1: call    PromptForInput

    mov     edx, OFFSET buffer
    mov     ecx, MaxLength
    call    ReadString         ; -> EAX
    test    eax, eax
    jz      L2                 ; Exit

    call    ReverseString       ; Is this working fine?
    call    CaseChange

    mov     edx, OFFSET buffer
    call    WriteString         ; Printing the result

    jmp     L1

L2:
    mov     edx, OFFSET prompt3
    call    WriteString         ; Final message

    exit

  • cmp dl, 'A' jl L3 Процедура ChangeCase должна проходить через всю строку, но вы прекращаете работу, как только натыкаетесь на байт меньше 65. Пожалуйста, используйте условие unsigned при работе с ASCII коды [0,255].

  • xor dl,32 На самом деле вы не записываете никаких изменений в память строк.

  • cmp dl,'z' Вы не действуете на это сравнение.

Если вам нужно сохранить только 1 регистр, не используйте pushad и popad.

CaseChange PROC
    push    esi
    mov     esi, OFFSET buffer
L1:
    lodsb
    test    al, al        ; End of string ?
    jz      L2
    or      al, 32        ; If "A".."Z" Then "a".."z"
    cmp     al, 'a'
    jb      L1
    cmp     al, 'z'
    ja      L1
    xor     byte ptr [esi-1], 32  ; Change case IN string memory
    jmp     L1
L2:
    pop     esi
    ret
CaseChange ENDP

(*) Если вы хотите наложить эту ошибку «Строка не может быть длиннее 20 символов», определите гораздо больший буфер и разрешите ReadString возвращать более 20 символов, чтобы вы могли вернуться в программу:

buffer BYTE 99 + 1 DUP (0)

...

L1: call    PromptForInput

    mov     edx, OFFSET buffer
    mov     ecx, 99
    call    ReadString         ; -> EAX
    cmp     eax, 20
    ja      L1
    test    eax, eax
    jz      L2                 ; Exit

И последний совет: убедитесь, что ReverseString и CaseChange в порядке, проверив их независимо друг от друга:

    call    ReverseString
    ;;;call    CaseChange
    mov     edx, OFFSET buffer
    call    WriteString         ; Printing the result

и позже

    ;;;call    ReverseString
    call    CaseChange
    mov     edx, OFFSET buffer
    call    WriteString         ; Printing the result

и только потом

    call    ReverseString
    call    CaseChange
    mov     edx, OFFSET buffer
    call    WriteString         ; Printing the result
person Sep Roland    schedule 05.05.2019
comment
Ваш ответ мне очень помог. Большое спасибо за подробное и полезное объяснение - person hj_; 06.05.2019