Printing Out a String Stored in an Array of DWORDS - arrays

I'm writing a program in Assembly that will Bubble Sort an Array of Strings. A zero length string terminates the array. I approached this by declaring a DWORD array, where the string var., that is a byte size, shall be stored. My main problem is not the bubble sort itself, but that strings that were stored in the array wasn't outputting completely.
To hopefully make it clear, here is my code:
.586
.MODEL FLAT
INCLUDE io.h ; header file for input/output
space equ 0
cr equ 0dh
.STACK 4096
.DATA
myStrings byte "Delts",0
byte "Abs",0
byte "Biceps",0
byte 0
labelStrOut byte "Output is: ", 0
stringOut dword 11 dup (?)
stringNum dword 0
stringArray dword 20 dup (?)
.CODE
_MainProc PROC
mov edi, offset myStrings
mov esi, offset stringArray
popltLp:
cmp BYTE PTR [edi], 0
jz popltDone
mov ebx, [edi]
mov DWORD PTR [esi], ebx
add esi, 4
inc stringNum
xor ecx, ecx
not ecx
xor al, al
repne scasb
jmp popltLp
popltDone:
xor edx, edx
lea esi, stringArray
mov ebx, DWORD PTR [esi]
mov stringOut, ebx
output labelStrOut, stringOut
add esi, 4
mov ebx, DWORD PTR [esi]
mov stringOut, ebx
output labelStrOut, stringOut
add esi, 4
mov ebx, DWORD PTR [esi]
mov stringOut, ebx
output labelStrOut, stringOut
outptDone:
mov eax, 0 ; exit with return code 0
ret
_MainProc ENDP
END ; end of source code
As can be seen, no Bubble Sorting is being done yet...
The lines below 'popltDone' is just me messing around to see if the strings carried over to the array just fine. However, when printed out on the screen, only 4 characters were just showing up! The entire string line was just not being printed out, which is currently driving me crazy. Can someone please tell me what I am doing wrong?
Thanks to anybody taking the time reading this.

The problem is, you aren't using string pointers correctly. Specifically, here's the code I'm referring to:
mov ebx, [edi]
mov DWORD PTR [esi], ebx
If you were to translate this into English, it would be something like this:
Move the 4 byte value pointed to by edi into ebx.
Move the value in ebx into the memory address pointed to by esi.
This is perfectly legal and may actually be what you want in some cases, but I'm guess this isn't one of them. The reason you are only seeing the first 4 characters when you output your array of strings is because you copied the literal string into your array. A DWORD is 4 bytes so you get the first 4 characters. Here's what I would write:
mov DWORD PTR [esi], edi
Which translates into:
Move the pointer value edi into the memory address pointed to by esi.
Now you have not an array of strings, but an array of string pointers. If you were to write your code in C, decompile it, this is most likely what you would see. Rewrite your comparison and output functions to work with the pointer to a string instead of the literal characters in the string and you'll fix your problem.

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...

Making string of --- with 0 on the end

I can not use this declaration, because selectedWords can be any string.
.DATA
guessWords BYTE SIZEOF selectedWords DUP ('-'), 0
So I try to do this:
;Wordls what we select by rundom code
selectedWords BYTE ?
lengthSelectedWorld DWORD ?
;Letter what we guess, input from keyboard
guessLetter BYTE ?
guessWords BYTE ?
;Letter what are unknows, change with -
letterUnknown BYTE "-", 0
And I have write this function
make_array1 PROC
mov edx,OFFSET selectedWords
call StrLength
mov lengthSelectedWorld,eax
mov lengthSelectedWorld1 ,eax
inc lengthSelectedWorld
loop_add_more:
cmp lengthSelectedWorld, 1
je done
dec lengthSelectedWorld
mov eax, '-'
mov ecx, lengthSelectedWorld1
mov edi, offset guessWords
rep stosw
mov edx, offset guessWords
call WriteString
call Crlf ;stampamo enter novi red
jmp loop_add_more
done:
mov eax, '0'
mov ecx, lengthSelectedWorld1
mov edi, offset guessWords
rep stosw
mov edx, offset guessWords
call WriteString
call Crlf ;stampamo enter novi red
ret
make_array1 ENDP
But after this funcion I get guessWords what is string of ------- and dont have 0 on the and. So how to make string guessWords=-------0?
Its important for me to have 0 on the end of string because of some other comparation in code..
selectedWords BYTE ? reserves just one byte for selectedWords. The same issue with guessWords BYTE ?. Don't play with dynamically allocated memory as newbie. Rather reserve space which is sufficient in any case: guessWords BYTE 50 DUP (?). The question mark means that MASM can decide to treat it as uninitialized memory (not stored in the .exe file, but allocated at program start).
STOSW stores a WORD (= two characters). However Irvine's StrLength returns the number of bytes of the string. Use STOSB instead. After STOSB, EDI points to the character after the last stored AL. You can store a null there. If you want to see it, temporarily change 0 to '0'.
INCLUDE Irvine32.inc
.DATA
;Wordls what we select by rundom code
selectedWords BYTE "WEIGHTLIFTING", 0
lengthSelectedWord DWORD ?
;Letter what we guess, input from keyboard
guessLetter BYTE ?
guessWords BYTE 50 DUP (?)
;Letter what are unknows, change with -
letterUnknown BYTE "-", 0
.CODE
make_array1 PROC
mov edx,OFFSET selectedWords
call StrLength ; Irvine32: Length of a null-terminated string pointed to by EDX
mov lengthSelectedWord,eax
loop_add_more:
mov al, '-' ; Default charcter for guessWords
mov ecx, lengthSelectedWord ; REP counter
mov edi, offset guessWords ; Destination
rep stosb ; Build guessWords
mov BYTE PTR [edi], 0 ; Store the null termination
mov edx, offset guessWords
call WriteString ; Irvine32: write a string pointed to by EDX
call Crlf ; Irvine32: new line
ret
make_array1 ENDP
main PROC
call make_array1
exit ; Irvine32: ExitProcess
main ENDP
END main

Looping backwards in Assembly x86

When you are looping backwards in Assembly x86, what is currently happening in the memory (Can you try to be visual, thanks)? The following code is what I am currently wondering about:
INCLUDE Irvine32.inc
.data
arrayb byte 1,2,3,4,5,6 ;6-7 bytes
len dword lengthof arrayb
space byte " ",0
x dword 3
.code
main PROC
mov edx,offset space
mov eax,0 ; clear ecx of garbage
mov ecx, len
mov esi,offset arrayb ; start of the array's memory
add esi,len ;This causes the array value to start at 6
dec esi ; esi goes from esi+5,esi+4,...,esi
myloop2:
mov al,[esi]
call writedec
call writestring
dec esi
loop myloop2
call crlf
In particular, why did I have to add 1 to esi? When you add 1 to the high speed memory transfer register esi, it seems that it causes the array value to start at 6. Why is that?Thank you.

x86 assembly compare with null terminated array

I'm working on a function in assembly where I need to count the characters in a null terminated array. I'm using visual studio. The array was made in C++ and the memory address is passed to my assembly function. Problem is my loop isn't ending once I reach null (00). I have tried using test and cmp but it seems as though 4 bytes are being compared instead of 1 byte (size of the char).
My code:
_arraySize PROC ;name of function
start: ;ebx holds address of the array
push ebp ;Save caller's frame pointer
mov ebp, esp ;establish this frame pointer
xor eax, eax ;eax = 0, array counter
xor ecx, ecx ;ecx = 0, offset counter
arrCount: ;Start of array counter loop
;test [ebx+eax], [ebx+eax] ;array address + counter(char = 1 byte)
mov ecx, [ebx + eax] ;move element into ecx to be compared
test ecx, ecx ; will be zero when ecx = 0 (null)
jz countDone
inc eax ;Array Counter and offset counter
jmp arrCount
countDone:
pop ebp
ret
_arraySize ENDP
How can I compare just 1 byte? I just thought of shifting the bytes I don't need but that seems like a waste of an instruction.
If you want to compare a single byte, use a single byte instruction:
mov cl, [ebx + eax] ;move element to be compared
test cl, cl ; will be zero when NUL
(Note that a zero character is ASCII NUL, not an ANSI NULL value.)

Resources