x86 NASM Assembly - Problems with Input - loops

I am working to take input from a user twice, and compare the input. If they are the same, the program exits. If not, it reprints the input from the first time, and waits for the user to type something. If it is the same, the same thing as before occurs. If not, the same thing as before occurs.
Input and looping is not the problem. The main problem is the result I am getting from the program. My following is what I am doing codewise:
%include "system.inc"
section .data
greet: db 'Hello!', 0Ah, 'Please enter a word or character:', 0Ah
greetL: equ $-greet ;length of string
inform: db 'I will now repeat this until you type it back to me.', 0Ah
informL: equ $-inform
finish: db 'Good bye!', 0Ah
finishL: equ $-finish
newline: db 0Ah
newlineL: equ $-newline
section .bss
input: resb 40 ;first input buffer
check: resb 40 ;second input buffer
section .text
global _start
_start:
greeting:
mov eax, 4
mov ebx, 1
mov ecx, greet
mov edx, greetL %include "system.inc"
section .data
greet: db 'Hello!', 0Ah, 'Please enter a word or character:', 0Ah
greetL: equ $-greet ;length of string
inform: db 'I will now repeat this until you type it back to me.', 0Ah
informL: equ $-inform
finish: db 'Good bye!', 0Ah
finishL: equ $-finish
newline: db 0Ah
newlineL: db $-newline
section .bss
input: resb 40 ;first input buffer
check: resb 40 ;second input buffer
section .text
global _start
_start:
greeting:
mov eax, 4
mov ebx, 1
mov ecx, greet
mov edx, greetL
sys.write
getword:
mov eax, 3
mov ebx, 0
mov ecx, input
mov edx, 40
sys.read
sub eax, 1 ;remove the newline
push eax ;store length for later
instruct:
mov eax, 4
mov ebx, 1
mov ecx, inform
mov edx, informL
sys.write
pop edx ;pop length into edx
mov ecx, edx ;copy into ecx
push ecx ;store ecx again (needed multiple times)
mov eax, 4
mov ebx, 1
mov ecx, input
sys.write
mov eax, 4 ;print newline
mov ebx, 1
mov ecx, newline
mov edx, newlineL
sys.write
mov eax, 3 ;get the user's word
mov ebx, 0
mov ecx, check
mov edx, 40
sys.read
xor eax, eax
checker:
mov ebx, check
mov ecx, input
cmp ebx, ecx ;see if input was the same as before
jne loop ;if not the same go to input again
je done ;else go to the end
pop edx
mov ecx, edx
push ecx
mov eax, 4
mov ebx, 1
mov ecx, check
sys.write ;repeat the word
mov eax, 4
mov ebx, 1
mov ecx, newline
mov edx, newlineL
sys.write
loop:
mov eax, 3 ;replace new input with old
mov ebx, 0
mov ecx, check
mov edx, 40
sys.read
jmp checker
done:
mov eax, 1
mov ebx, 0
sys.exit
sys.write
getword:
mov eax, 3
mov ebx, 0
mov ecx, input
mov edx, 40
sys.read
My result is now: EDITED
Hello!
Please enter a word or character:
Nick
I will now repeat this until you type it back to me.
Nick
(I input) Magerko
(I get) M
(I input)Nick
(I get)
(I input)Nick
(I get)
EDITED
And this continues. My checks do not work as intended in the code above, and I eventually don't even get the program to print anything but a newline. Is there a reason for this?
Thanks.

Apart from what #Joshua is pointing out, you're not comparing your strings correctly.
checker:
mov ebx, check ; Moves the *address* of check into ebx
mov ecx, input ; Similarly for input
cmp ebx, ecx ; Checks if the addresses are the same (they never are)
Firstly, when you have e.g. label dd 1234 in your data segment mov eax, label will move the address of label to eax while mov eax, [label] will move the contents stored at label (in this case 1234) into eax.
Note that in the above example I deliberately used a 32-bit variable so that it would fit neatly into eax. If you're using byte sized variables (like ascii characters) e.g. mybyte db 0xfe you'll either have to use byte sized register (al, ah, dh etc.) or use the move with zero/sign extend opcodes: movzx eax, byte [mybyte] will set eax to 254, while movsx eax, byte [mybyte] will set eax to -2 (0xfffffffe).
You also need to do a character by character comparison of the strings. Assuming you save the read string length (you really should be checking for negative return values - meaning errors) in input_len and check_len it could look something like:
mov eax, [input_len]
cmp eax, [check_len]
jne loop ; Strings of different length, do loop again
mov ebx, check
mov ecx, input
.checkloop:
mov dl, [ebx] ; Read a character from check
cmp dl, [ecx] ; Equal to the character from input?
jne loop ; Nope, jump to `loop`
inc ebx ; Move ebx to point at next character in check
inc ecx ; and ecx to next character in input
dec eax ; one less character to check
jnz .checkloop ; done?
; the strings are equal if we reach this point in the code
jmp done
If you're interested in another way of doing this in fewer instructions look up rep cmpsb.
There are a few other problems in the code immediately following your checker code. The pop edx instruction (and the code following, down to the loop label) will not be execute as you're always jumping either to loop or done.
jne loop ;if not the same go to input again
je done ;else go to the end
pop edx ; Will never be reached!
The reason you're getting funny characters is from newlineL: db $-newline This should be equ instead of db or you should replace mov edx, newlineL with movzx edx, byte [newlineL]. Since newlineL unlike the other *L names refers to a variable and not a constant equ mov edx, newlineL will use the address of the newlineL variable as the number of bytes to write, when you wanted it to be 1.

You are assuming sys.read returns the entire line. It is not required to do so. It may return after only one character, or even possibly after part of the second line.
You know, this kind of thing kind of ticks me off. This looks like a homework problem in writing in assembly, but the problem is not with the assembly, but with the assumptions in how the system calls work.
I really wish the instructors would provide an fgets library function for stuff like this.
Anyway, the stupid way to fix it is to read one byte at a time, looking for LF (byte 10) to end the loop.

Related

Can't find the reason for a build error on Assembly language program which converts a string Array into an integer array

I am creating a program which reads a list of integers seperated by a single space via console and printing the sum of all the integers. The main problem is extracting the integers from the string array into a signed integer array.
Some examples of input are "-20 30 5" (each integer is seperated by a single space) or " [space]-20 30 5 [space]" (there may be spaces between the beginning and the end of the list, but the numbers are still seperated by a single space)
Also, after printing the sum, the program returns to reading another input unless only the enter key is typed.
After writing the code and pressing the Debug button, I am getting these two following build errors:
A2005 symbol redefinition: InBuffer
A2111 conflicting parameter definition
I've checked the error messages and apparently both of them are related to the PROTO and PROC directives. But there seems to be no problems regarding the parameter definition.
Here is my code.
INCLUDE Irvine32.inc
ArrayGet PROTO, ; convert string array into int array
inBuffer: PTR BYTE,
inBufferN: DWORD,
intArray: PTR SDWORD
.data
BUF_SIZE EQU 256
inBuffer BYTE BUF_SIZE DUP(?) ; input buffer
inBufferN DWORD ? ; length of input
intArray SDWORD BUF_SIZE/2 DUP(?) ; integer array for storing converted string
intArrayN DWORD ? ; number of integers
prompt BYTE "Enter numbers(<ent> to exit) : ", 0
bye BYTE "Bye!", 0
.code
main PROC
L1:
mov esi, 0
mov edx, OFFSET prompt
call WriteString
mov edx, OFFSET inBuffer
mov ecx, BUF_SIZE
call ReadString
cmp inBuffer[0], 0ah
je L3 ; only typing <ent> ends the program
mov inBufferN, eax
mov ecx, inBufferN
SpaceCheck: ; calls procedure when it finds a number
cmp inBuffer[esi], 20h
jne L2
inc esi
loop SpaceCheck
jmp L1
L2:
INVOKE ArrayGet, ADDR inBuffer, inBufferN, ADDR intArray ; put inBuffer offset on edx, inBufferN on ecx
mov intArrayN, eax
mov ecx, intArrayN
mov eax, 0
mov esi, OFFSET intArray
Ladd: ; adding the integer array
add eax, [esi]
inc esi
loop Ladd
call WriteInt
call CRLF
jmp L1
L3:
mov edx, OFFSET bye
call WriteString
exit
main ENDP
; procedure definition
ArrayGet PROC USES edx ecx,
inBuffer : PTR BYTE,
inBufferN: DWORD,
intArray: PTR SDWORD
LOCAL ArrayNum: DWORD
mov ArrayNum, 0
mov ecx, inBufferN
sub ecx, esi ; ecx(loop count) from first char to the end
LOOP1:
lea edx, inBuffer
add edx, esi ; edx points the offset of first char
mov edi, esi ; save location of first char
LOOP2: ; check spaces between integers
cmp inBuffer[esi], 20h
je getNum
inc esi
loop LOOP2
jmp getNum ; jump to getNum if array ends with a number
getNum: ; converting char into int
push ecx
inc esi
cmp inBuffer[esi], 20h ; two spaces in a row is considered as no more numbers afterwards
je EndBuffer
dec esi
mov ecx, esi
sub ecx, edi ; length of single number in char
call ParseInteger32
mov edi, ArrayNum
mov intArray[edi], eax
inc ArrayNum
inc esi
pop ecx
loop LOOP1
jmp EndBuffer ; end procedure when loop is over
EndBuffer:
mov eax, ArrayNum
inc eax
ret
ArrayGet ENDP
END main
In case you have questions about my intentions in the code or about the form of the input, feel free to leave it at the comment section

How to write an encryption loop where the plain text and the key are of different lengths

Help me to check my code, I can't get the expected output.
Input: prompt user to insert plain text, and key
Output: calculate and come out with the encrypted text by using vigenère cipher.
I can input the plain text and the key, but I can't get the cipher text output.
INCLUDE IRVINE32.INC
.DATA
BUFMAX = 128
sPrompt1 BYTE "Enter the plain text:", 0
sPrompt2 BYTE "Key:",0
sEncrypt BYTE "Cipher text:",0
buffer BYTE BUFMAX+1 DUP(?)
key BYTE BUFMAX+1 DUP (?)
bufsize DWORD ?
keysize DWORD ?
.CODE
main PROC
call Clrscr
call inputString ;input plain text, key
call translateBuffer ;encrypt the buffer
call displayMessage ;display encrypted message
exit
main ENDP
inputString
inputString PROC
;--------------------------------------------------------------------------------
;Prompts user to input string and key, saves the string and it's length
;recieves: nothing
;returns: nothing
;--------------------------------------------------------------------------------
pushad
mov edx, OFFSET sPrompt1 ;prompt plain text
call WriteString
mov ecx, BUFMAX
mov edx, OFFSET buffer
call ReadString
mov bufsize, eax
call Crlf
mov edx, OFFSET sPrompt2 ;prompt key
call WriteString
mov ecx, BUFMAX
mov edx, OFFSET key
call ReadString
mov keysize, eax
call Crlf
popad
ret
inputString ENDP
translateBuffer
translateBuffer PROC
;--------------------------------------------------------------------------------
;translate the plain text with key to cipher text
;recieves: nothing
;returns: nothing
;--------------------------------------------------------------------------------
pushad
mov ecx, bufsize
mov esi, 0
mov edi, 0
L1: mov al, key[edi]
xor buffer[esi], al
inc esi
inc edi
cmp edi, keysize+1
jne L1
mov edi, 0
loop L1
popad
ret
translateBuffer ENDP
display encrypted message
displayMessage PROC
;--------------------------------------------------------------------------------
; Displays the encrypted message.
; Receives: EDX points to the message
; Returns: nothing
;--------------------------------------------------------------------------------
pushad
mov edx, OFFSET sEncrypt
call WriteString
mov edx,OFFSET buffer
call WriteString
call Crlf
call Crlf
popad
ret
displayMessage ENDP
END main
Your input and output procedures seem fine. The troubles sit in-between, in your encryption procedure.
The user can input strings of varying lengths for the plain text and for the encryption key. Your program needs to consider these different lengths in order to not overflow any buffers, because that's exactly what is happening in your code!
Problem 1
Because the jne L1 instruction, going the wrong way, bypasses the normal loop counting, your loop runs for much too long and starts overwriting memory that does no longer belong to the buffers that were defined.
The loop must only traverse the plain text string. If the text in the encryption key happens to be shorter than the plain text string, then you will want to repeat the key. Therefore you have to reset the offset register EDI to 0, but you have to continu the loop normally (not just return to the top!).
Problem2
A second problem is that you have this cmp edi, keysize+1 instruction that, because of the +1, will be consuming the zero-terminator from the key string as if it were part of that string. That's never the idea behind a string terminator. It's not part of the string.
push esi ; No need to preserve the scratch registers EAX, ECX, EDX
mov esi, OFFSET buffer
xor edx, edx
mov ecx, bufsize
L1: movzx eax, byte ptr key[edx]
xor [esi], al
inc edx
inc esi
cmp edx, keysize
je L3 ; Branches in the least common case
L2: dec ecx
jnz L1
pop esi
ret
L3: xor edx, edx
jmp L2
In the above snippet I have improved the code a bit.
You can zero a register simply by XORing it to itself.
You should never use the LOOP instruction because it is very slow.
And some more optimizations that you can read about in Peter Cordes' comments below this answer.
And this is a version that even eliminates the conditional branch using the CMOVE (Conditional MOVe If Equal) instruction that will store the (zeroed) EDX register over the EDI register if the Equal condition happens to be true. If not, there's no moving at all.
xor edi, edi
xor esi, esi
xor edx, edx
mov ecx, bufsize
L1: mov al, buffer[esi]
xor al, key[edi]
mov buffer[esi], al
inc esi
inc edi
cmp edi, keysize
cmove edi, edx
dec ecx
jnz L1
The danger of XOR
Because the encryption uses a mere XOR operation and depending on the composition of both strings, it's possible to obtain a zero byte amongst the bytes of the encrypted string. Obviously a print function like WriteString that depends on the zero-terminating of a string will consider the first zero as the end of the string. If it so happens that this occurs in the first position of the encrypted string, well then there's nothing at all to print...

NASM: Convert multi character input to decimal

I am trying to write a program that gets a number with one or two digits and write Hello! as many times as that number.
I used this posts to write my code:
NASM: The loop turns into an infinite loop
Check null character in assembly language
Multi-Digit Input from NASM I did not understand this :)
But my code only works for two digit numbers and the result for single digit numbers are wrong.
My code:
section .data
msg db 'Hello!',0xA
len equ $-msg
section .bss
n resb 2
section .text
global _start
_start:
;get n
mov edx, 2
mov ecx, n
mov ebx, 0
mov eax, 3
int 0x80
lea esi,[n] ;address of our text
call toint
;loop for print 'Hello!\n'
print_loop:
push ecx
mov edx, len
mov ecx, msg
mov ebx, 1
mov eax, 4
int 0x80
pop ecx
loop print_loop
mov eax, 1
int 0x80
toint:
push eax
push ebx
xor ebx, ebx
next_digit:
movzx eax, byte[esi]
sub al , '0'
imul ebx, 10
add ebx, eax
inc esi
cmp byte [esi] , 0x0 ;check next character is null or not
jne next_digit
; end_loop:
mov ecx, ebx
pop eax
pop ebx
ret
The sys_read call returns in EAX the count of characters that were sent to your inputbuffer. Because you allowed for an input of max. 2 characters, this count will be either 0, 1, or 2. You could use this info in your toint routine.
; IN (eax,esi) OUT (ecx)
toint:
mov ecx, eax ; Number of digits
jecxz done
push eax ; (1)
push ebx ; (2)
push esi ; (3)
xor ebx, ebx
next:
movzx eax, byte [esi]
sub al , '0'
imul ebx, 10
add ebx, eax
inc esi
dec ecx
jnz next
mov ecx, ebx
pop esi ; (3)
pop ebx ; (2)
pop eax ; (1)
done:
ret
Please notice that there's a reversed ordering to be observed when preserving/restoring registers on the stack! (Your code missed this...)
4 tips
a. Prefer the MOV variant to load an address. It's always a shorter instruction.
b. Guard yourself against an input of zero.
c. Don't use LOOP. It's a slow instruction.
d. Provide the exit code to terminate the program.
mov esi, n ; (a)
call toint ; -> ECX
jecxz Exit ; (b)
print_loop:
...
dec ecx ; (c)
jnz print_loop
Exit:
xor ebx, ebx ; (d)
mov eax, 1
int 0x80

Assembler stuck in sys_read loop

I've written a piece of code that takes a number in ASCII characters from the prompt, converts it into a decimal number and stores it in 'dnumber'. The conversion has been checked and goes well. It goes wrong at the prompt. It seems to be stuck in an infinite loop while asking the user for the ASCII character number. I want the program stop asking for input when the user presses ENTER, but that termination value never seems to be reached even though I think I've set it that way.
I've asked two related questions on this forum lately and it showed that I don't understand system calls properly. I've read all the documentation on 'http://www.tutorialspoint.com/assembly_programming', 'http://cs.lmu.edu/~ray/notes/nasmtutorial/' and some of 'http://www.x86-64.org/documentation/abi.pdf' and apparently I'm still not getting it. Hopefully you can show me the light.
Here is the compiler info:
nasm -f elf64 convinput.asm
ld -s -o convinput convinput.o
Here is the prompt:
$ ./convinput
Enter a number and press enter:
123
123
123
As you can see I've pressed ENTER twice, but the prompt still asks for input.
Here is the code:
section .text
global _start
_start:
mov eax, 4
mov ebx, 1
mov edx, lenmsg1
mov ecx, msg1
int 80h
xor eax, eax
xor ebx, ebx
xor edx, edx
mov esi, data
call input
mov esi, data
movzx ecx, byte [dignum]
xor ebx,ebx ; clear ebx
call string_to_int
mov dword [dnumber], eax
mov eax, 1
mov ebx, 0
int 80h
input:
mov eax, 3
mov ebx, 0
mov ecx, esi
mov edx, 1
int 80h
inc byte [dignum]
cmp byte [esi], 13
inc esi
jne input
ret
string_to_int:
xor ebx,ebx
movzx eax, byte [esi]
inc esi
sub al,'0' ; convert from ASCII to number4
mov ebx, 10
mul ebx
add ebx,eax ; ebx = ebx*10 + eax
dec byte [dignum]
cmp byte [dignum], 0
jne string_to_int
mov eax,ebx
ret
section .bss
dignum resb 1
data resb 1000
dnumber resd 1
section .data
msg1 db 'Enter a number and press enter: ', 10, 0
lenmsg1 equ $ -msg1
; ESI = pointer to the string to convert
; ECX = number of digits in the string (must be > 0)
; Output:
; EAX = integer valu
Judging by values you put into eax you are using a 32-bit syscall table, which on linux is different than 64-bit one.
You can easily see what syscalls (and with what arguments) are called by running your program under strace.

Assigning values to array - Assembly

I am trying to read chars from input file, and place them in an array (except new line chars).
here is my code:
mov dword [counter], 0
mov edi, [size]
loop:
mov esi, state
cmp [counter], edi ; read all chars of the file
je end_loop
pushad
mov eax, 3
mov ebx, dword [file_desc]
mov ecx, read_char
mov edx, 1
int 0x80
popad
cmp byte [read_char], '1'
je put_char
cmp byte [read_char], ' '
je put_char
jmp loop
put_char:
mov edx, [read_char]
mov [esi + counter], edx
;; print number of char read from 0 to size-1
pushad
mov ecx, dword [counter]
push ecx
push printInt
call printf
add esp, 8
popad
;; print char read
pushad
push edx
push printChar
call printf
add esp, 8
popad
;; print value stored in state[counter]
pushad
push dword [esi + counter]
push printChar
call printf
add esp, 8
popad
mov eax, [counter]
inc eax
mov [counter], eax
jmp loop
end_loop:
the printing inside the loop works fine, as i get the char number, the char i have just read and the char in [esi + counter] (supposed to be state[counter]).
however, trying to print it after the reading loop, with this code:
mov dword [counter], 0
mov edi, [size]
printarray:
mov esi, state
cmp [counter], edi
je end
pushad
push dword [esi + counter]
push printChar
call printf
add esp, 8
popad
pushad
mov ecx, [counter]
inc ecx
mov [counter], ecx
popad
jmp printarray
end:
all I get is blanks (new char lines every line, from my printChar).
I don't understand my the values I read are not stored in the array.
There is no code between end loop and mov dword [counter], 0 just before the printarray loop.
mere are my data and bss:
section .data
newLine: DB "", 10, 0
printInt: DB "%d", 10, 0
printString: DB "%s", 10, 0
printChar: DB "%c", 10, 0
hello: DB "hello", 10, 0
section .bss
file_name resb 80
file_desc resd 1
WorldLength resd 1
WorldWidth resd 1
generations resd 1
print_freq resd 1
state resb 60*60
read_char resb 1
counter resd 1
size resd 1
Thank you for your help.
Well...
First of all, don't use 32-bit registers when operating on bytes. I'm sure that even if your code worked, some data would be overwritten.
I believe that your problem resides somewhere in statements similar to these
mov [esi + counter], edx
...
push dword [esi + counter]
They actually mean: "take the address of counter and add it to esi", which I think isn't what you want.
Onto this,
- reading the file character by character is terribly inefficient
- using counter variables instead of ecx is inefficient
- incrementing register rather than a memory location iself is inefficient too
I've tried to rewrite your code as much as I could, and I hope it was worth something.
mov eax, 3
mov ebx, dword [file_desc]
mov ecx, state
mov edx, [size]
int 0x80
; eax now contains the number of bytes read, so why not to use it?
mov ebx, eax
add ebx, state
mov byte [ebx], 0x0 ; this will be end-of-string, although it may not really be necessary
xor ecx, ecx ; this will be our counter now
_loop: ; loop is actually an instruction
cmp ecx, eax
je _end
inc ecx ; ecx++
mov dl, '\n'
cmp byte [state + ecx], dl ; is newline?
jne _loop ; nope? ok, try again
mov dl, ' ' ; yes?
mov byte [state + ecx], dl ; replace newline with space character
jmp _loop
_end:
; print the result
mov edx, eax ; the size - number of bytes read
mov eax, 4
mov ebx, dword [file_desc]
mov ecx, state
int 0x80
Problem solved.
I should have used this to put the char in the array:
put_char:
mov dl, [read_char]
mov [esi], dl
mov eax, [counter]
inc eax
mov [counter], eax
inc esi
jmp loop
I removed the printing, those were for debugging only.
Thanks :)

Resources