The objective of this program is to reverse a given string while switching each letter's case. String cannot be longer than 20 characters, if input is longer than that, program requires user to enter string again. Program ends when user inputs 'enter' and the program ends after printing an ending sentence.
Implementing this I have 3 problems:
I tried to get input string by using call ReadString and since this procedure stops when enter key is given, the console freeze-ends when I press the enter key to normally finish the program. How could I correct my code to make it print an ending message and then end the program normally with return value 0?
If input string is longer than 20 characters, it should require user to input string again. So I wrote ja L1. Bur for some reason, mov bytecount, eax; cmp bytecount, 20; cannot seem to filter the case properly. When the line mov bytecount, eax is executed, the value of bytecount is correct, but when the program executes the next line, cmp bytecount, 20, the value of bytecount changes. I don't know what I'm doing wrong.
CaseChange procedure freezes when it is executed, so I guess it's looping infinitely, but I can't find what condition is wrong.
.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
(input prompt): Cats and Dogs.
(output prompt): .SGOd DNA STAc
(input prompt): too long for the given limit
(input prompt):
End of program
The manual tells us for ReadString:
Reads a string of up to ECX non-null characters from standard input, stopping when the user presses the Enter key.
A null byte is stored following the characters input, but the trailing carriage return and line feed characters are not placed into the buffer.
ECX should always be smaller than the buffer size (never equal to the buffer size) because the null byte could be the (ECX+1)th character stored.
Following this it's clear to see that you need to enlarge the buffer:
buffer BYTE MaxLength + 1 DUP (0)
If you specified ECX=MaxLength
then you can't get an input that is longer than MaxLength characters and thus there's no real need to test for such condition. (*)
If the user presses the enter key with no preceeding characters, then ReadString will return with EAX=0
. Test this and jump to your final message.
A big error is where you wrote loop L1
. The loop
instruction works with the ECX
register to perform a known number of iterations. Your program needs to just jump back to the top with no conditions attached. Use jmp L1
.
It's best if you keep things logically together. Don't mix call PromptForInput
with call ReadString
and its parameters. Can you be sure that PromptForInput doesn't change EDX
or 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
The ChangeCase procedure needs to traverse the whole string, yet you leave as soon as you stumble upon a byte less than 65. Please use the unsigned condition when working with ASCII codes [0,255].
xor dl,32
You don't actually write any changes in the string memory.
cmp dl,'z'
You don't act on this compare.
If all you need to preserve is 1 register then don't use pushad
and 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
(*) If you want to impose this "String cannot be longer than 20 characters" error then define a much bigger buffer and allow ReadString to return more than 20 characters so you can jump back in the program:
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
One final advice is to make sure that ReverseString and CaseChange are OK by testing them independently:
call ReverseString
;;;call CaseChange
mov edx, OFFSET buffer
call WriteString ; Printing the result
and later
;;;call ReverseString
call CaseChange
mov edx, OFFSET buffer
call WriteString ; Printing the result
and only then
call ReverseString
call CaseChange
mov edx, OFFSET buffer
call WriteString ; Printing the result