Returning pointer to the letter in string - arrays

This is my code to search ih some string consists of one letter:
selectedWords BYTE "BICYCLE"
inputLetter BYTE 'Y'
cld
mov ecx, LENGTHOF selectedWords
mov edi, offset selectedWords
mov al, inputLetter ;Load character to find
repne scasb ;Search
jne notfound
But how to return the pointer to the letter in string?
If I want after to change one leter with some other. Its easy to do if you have pointer to the letter in string.

If you read the documentation for REP and SCASB you'll see that SCAS updates edi. Thus the location of the matching char is stored in EDI.
All you have to do is return EDI if ZF=1 and return 0 if ZF<>1.
cld
mov ecx, LENGTHOF selectedWords
mov edi, offset selectedWords
mov al, inputLetter ;Load character to find
repne scasb ;Search
jne notfound
found:
mov eax,edi ;return the address of the match.
ret
notfound:
xor eax,eax ;return 0 aka not found as address.
ret

If repne scasb finds the element, EDI points to the element after the first match. You have to decrement it to get the pointer to the desired element.
You don't need to clear the direction flag (cld). It's very very unlikelikely that the direction flag is set without any involvement of your part. And if so, you should seit it back to the former status.
INCLUDE Irvine32.inc
.DATA
selectedWords BYTE "BICYCLE"
inputLetter BYTE 'Y'
err_msg BYTE "Not found.", 0
.CODE
main PROC
mov ecx, LENGTHOF selectedWords
mov edi, offset selectedWords
mov al, inputLetter ; Load character to find
repne scasb ; Search
jne notfound
dec edi
mov al, [edi]
call WriteChar ; Irvine32: Write a character in AL
exit ; Irvine32: ExitProcess
notfound:
lea edx, err_msg
call WriteString ; Irvine32: Write a null-terminated string pointed to by EDX
exit ; Irvine32: ExitProcess
main ENDP
END main
If you don't like repne scasb you can scan the word with a "normal" comparison loop
INCLUDE Irvine32.inc
.DATA
selectedWords BYTE "BICYCLE"
inputLetter BYTE 'Y'
err_msg BYTE "Not found.", 0
.CODE
main PROC
mov edi, offset selectedWords
mov ecx, LENGTHOF selectedWords
mov al, inputLetter
##:
cmp [edi], al ; Compare memory/immediate value
je found ; JE = jump if equal
inc edi ; Increment pointer
dec ecx ; Decrement counter
jne #B ; Jump back to the last ##, if ECX == 0
jmp notfound
found:
mov al, [edi]
call WriteChar ; Irvine32: Write a character in AL
exit ; Irvine32: ExitProcess
notfound:
lea edx, err_msg
call WriteString ; Irvine32: Write a null-terminated string pointed to by EDX
exit ; Irvine32: ExitProcess
main ENDP
END main

Related

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

Change only one letter in string

I have 2 string and one letter.
selectedWords BYTE "BICYCLE"
guessWords BYTE "-------"
inputLetter BYTE 'C'
Base on this answers, I write code who compere if selectedWords have letter C and If this is the case he need to change string guessWords:
guessWords "--C-C--"
But from some strange reason I get all other possibilities, just not correct one. Some suggestions on how to solve this problem.
First, forget the so called string instructions (scas, comps, movs). Second, you need a fixed pointer (dispkacement) with an index, e.g [esi+ebx]. Have you considered that WriteString needs a null-terminated string?
INCLUDE Irvine32.inc
.DATA
selectedWords BYTE "BICYCLE"
guessWords BYTE SIZEOF selectedWords DUP ('-'), 0 ; With null-termination for WriteString
inputLetter BYTE 'C'
.CODE
main PROC
mov esi, offset selectedWords ; Source
mov edi, offset guessWords ; Destination
mov ecx, LENGTHOF selectedWords ; Number of bytes to check
mov al, inputLetter ; Search for that character
xor ebx, ebx ; Index EBX = 0
ride_hard_loop:
cmp [esi+ebx], al ; Compare memory/register
jne #F ; Skip next line if no match
mov [edi+ebx], al ; Hang 'em lower
##:
inc ebx ; Increment pointer
dec ecx ; Decrement counter
jne ride_hard_loop ; Jump if ECX != 0
mov edx, edi
call WriteString ; Irvine32: Write a null-terminated string pointed to by EDX
exit ; Irvine32: ExitProcess
main ENDP
END main

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

Cannot get loop to run

I am trying to get my frequency table to fully display but while trying to run the loop
through the next label it gives me an exception handler error. I am wondering how I can get my loop to run fully
My code:
INCLUDE Irvine32.inc
.data
target BYTE "AAEBDCFBBC", 0
freqTable DWORD 256 DUP(0)
introPrompt BYTE "Here are the indexes in hex and their counts in the array: ", 0ah,
0dh, 0
prompt BYTE "Location ", 0
prompt2 BYTE " : ", 0
numcount DWORD 0
sLength DWORD ?
location DWORD ?
count DWORD 0
temp BYTE ?
.code
main PROC
mov edx, OFFSET target
call StrLength
mov sLength, eax
Get_frequencies PROTO,
pval1: DWORD,
tableVal: DWORD
INVOKE Get_frequencies, ADDR target, ADDR freqTable
mov eax, numCount
call WriteDec
call crlf
mov edx, OFFSET introPrompt
call WriteString
;Writes the frequency table
mov esi, OFFSET freqTable
mov ecx, 256
top:
mov edx, OFFSET prompt
call WriteString
mov eax, count
call WriteHex
mov edx, OFFSET prompt2
call WriteString
mov eax, [esi]
call WriteDec
call crlf
add esi, 4
inc count
loop top
call WaitMsg
exit
main ENDP
Get_frequencies PROC USES edi esi,
pval1: DWORD,
tableVal: DWORD
mov edi, pval1 ;edi points to target array
mov esi, pval1 ;esi points to freqTable
mov edx, 0
mov eax, 0
mov numCount, 0
L1:
mov al, [edi]
mov dl, [esi]
mov temp, dl
cmp al, 0
jne L2
jmp next
L2:
inc edi
cmp al, dl
jne L1
inc numCount
jmp L1
next:
mov ecx, esi
mov ecx, tableVal
imul edx, 4
add ecx, edx
mov ebx, numCount
mov [ecx], ebx
cmp temp, 0
je finish
inc esi
inc edi
jmp L1
finish:
ret
Get_frequencies ENDP
END main
So the numCount global variable contains how many times a character is repeated and I add the number to the frequency table depending on the hex value. I try to increase the esi and edi to go again starting at the following value on the start but it gives me an exception handle error.
Thank you very much

Masm Reading Input File Into Array

I am using Masm with Irvine32 library. I am new to Assembly and I'm having trouble with converting the contents of a file into an array once I read it in. Once I read in the file and convert it, I should be able to sum up the array or whatever else I need to do with it but right now I'm having trouble with converting my array back to ascii after converting it to int, in order to print it out. I believe what I need to do is read in an input file that contains a list of numbers separated by spaces, convert ascii to int, store in an array, and finally, convert back to ascii and output the results. Is this correct?
My input looks something like this, with spaces to separate the numbers:
24 31 4 63 9 11 17 3 56 37
Here is my program so far:
INCLUDE Irvine32.inc
.data
TEN dword 10
BUFFER_SIZE = 5000
buffer dword BUFFER_SIZE dup (?)
bytesRead dword 0
inFilename byte "input.txt", 0
infileH dword 0
cnt dword 0
ary dword 20 dup (?) ; Array for storing converted ascii to int
bry dword 20 dup (?)
size dword 10
.code
main PROC
call zeroOut
; Open input file
mov edx, OFFSET inFilename
call OpenInputFile
mov infileH, eax
; Read file into buffer
mov edx, OFFSET buffer
mov ecx, BUFFER_SIZE
call ReadFromFile
mov bytesRead, eax
; Close input file
mov eax, infileH
call CloseFile
; Convert ascii to int and store in ary
call zeroOut
lea esi, OFFSET buffer
lea edi, OFFSET ary
mov edx, size
L1:
call convertasciitoint
mov [edi], eax
inc edi
inc esi
dec edx
call DumpRegs
cmp edx, 0
jne L1
call zeroOut
; Convert int to ascii for printing
lea esi, OFFSET ary
lea edi, OFFSET bry
mov ebx, size
L2:
call convertinttoascii
mov [edi], eax
inc esi
inc edi
dec ebx
cmp ebx, 0
jne L2
; Print output
lea esi, OFFSET bry
call myLine
exit
main ENDP
convertasciitoint PROC
mov ecx, 0
mov eax, 0
nextDigit:
mov bl, [esi]
cmp bl, '0'
jl outOfHere
cmp bl, '9'
jg outOfHere
add bl, -30h
imul eax, 10
add eax, ebx
;mov [esi], eax
;mov [edi], eax
inc ecx
inc esi
;inc edi
jmp nextDigit
outOfHere:
mov cnt, ecx
ret
convertasciitoint ENDP
convertinttoascii PROC
mov ecx, cnt
nextDigit:
mov al, [esi]
div TEN
mov eax, 0
mov al, dl
add al, 30h
;mov [edi], dl
;mov dl, [esi]
;inc esi
;inc edi
call DumpRegs
dec ecx
cmp ecx, 0
jne nextDigit
ret
convertinttoascii ENDP
myLine PROC
nextChar:
mov al, [esi]
inc esi
call WriteChar
cmp al, NULL
jne nextChar
ret
myLine ENDP
zeroOut PROC
mov eax, 0
mov ebx, 0
mov ecx, 0
mov edx, 0
ret
zeroOut ENDP
END main
Right now my program reads in the entire file correctly, if I print the buffer array everything is output correctly. I can convert my array to int but I can't convert it back to ascii correctly. Am I not looping or incrementing correctly? Using the input above, my output (after converting back to ascii) is 8589793965, which isn't correct. I can't figure out what I'm doing wrong. I'm trying to read in a number, divide by ten and add 30h to the remainder, is this correct? I can't seem to get to the second digit of the number correctly.
Any help would be much appreciated. Thanks

Resources