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
Related
I have elements loaded in stack and I need to move them to array. My code looks like this:
%include "asm_io.inc"
segment .data
array db 100 dup(0)
length db 0
segment .text
global _asm_main
extern getchar
_asm_main:
enter 0, 0
pusha
call getchar
char_loop:
mov ebx,10
sub eax, '0'
mul ebx
mov ebx, eax
call getchar
sub eax, '0'
add eax, ebx
push eax
inc BYTE[length]
call getchar
cmp eax, 10
je fill_array
cmp eax, 13
je fill_array
cmp eax, 32
je skip_spaces
jmp char_loop
skip_spaces:
call getchar
cmp eax, 32
je skip_spaces
jmp char_loop
fill_array:
mov ecx, [length]
mov ebx, array
l1:
pop eax
mov [ebx], eax ; should be al instead of eax
inc ebx
call print_int
call print_nl
loop l1
print_array:
mov ecx, [length]
mov ebx, array
l2:
mov eax, [ebx] ; should be al instead of eax
call print_int
call print_nl
inc ebx
loop l2
_asm_end:
call print_nl
popa
mov eax, 0
leave
ret
print_int in asm_io.asm is
print_int:
enter 0,0
pusha
pushf
push eax
push dword int_format
call _printf
pop ecx
pop ecx
popf
popa
leave
ret
Where int_format is int_format db "%i",0
Length and values in stack are correct, I had them printed but when I try to print array only last value is correct. Other values are random numbers. I tried combinations of registers of different sizes but it did not work. I think that error has to do something with size of registers or size of array.
Answer here:
As #xiver77 said in comments I was writing into array 4 bytes instead 1 byte. One element in array has 1 byte and I tried to write 4 bytes. That creates overflow of bites and change other elements in array. Instead mov [ebx], eax should be mov [ebx], al and mov eax, [ebx] for printing should be mov al [ebx].
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
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
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 :)
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