Today I'm working on arrays and addressing modes so I made this snippet to train myself. Everything is working fine (apparently) but I chose the easiest way using fixed size arrays. My question is about dynamic memory allocation. In my code, my arrays arrayand array2 have a fixed size (8 * 4(int)). If I wanted to extend them, or to speak with C concepts, make a realloc or something like depending on real time values for the size, may I have to implement system calls like sys_brk and mmapby myself in assembly
or is there an other option ? The other question deals with the part of my code used to reverse an array (label .reversing) I know they are faster solutions than mine so if you could give me some advices, it would be nice.
%define ARRAY_LEN 8
%macro print 2
mov eax, 4
mov ebx, 1
mov ecx, %1
mov edx, %2
int 0x80
%endmacro
%macro exit 0
xor ebx, ebx
mov eax, 1
int 0x80
%endmacro
section .bss
sumarrays resd 1
dynarray resd 256
section .rodata
highest equ 0x78
evensum equ 0x102
sumarray1 equ 0x1e6
sumarray2 equ 0x256
section .data
revarray times ARRAY_LEN dd 0
strok db "Good result !", 0xa, 0
lenstrok equ $ - strok
strerror db "Bad result !", 0xa, 0
lenerror equ $ - strerror
array dd 0x33, 0x21, 0x32, 0x78, 0x48, 0x77, 0x19, 0x10
array2 dd 0x12, 0x98, 0x65, 0x62, 0x4e, 0x3e, 0x1f, 0x3a
section .text
global sort
global sum
global _start
_start:
; sort array from highest value to lowest value
sub esp, 8
push ARRAY_LEN
push array
call sort
add esp, 8
and esp, 0xfffffff0
; copying array into some another ones : array -> revarray
mov esi, eax
mov edi, revarray
mov ecx, ARRAY_LEN
rep movsd
; now let's reverse the array
mov esi, revarray ;esi = address of array
mov ecx, ARRAY_LEN ;ecx = number of elements
lea edi, [esi + ecx * 4 - 4]
.reversing:
mov eax, [esi] ;eax = value at start
mov ebx, [edi] ;ebx = value at end
mov [edi], eax
mov [esi], ebx
add esi, 4 ;incrementing address of next item at start
sub edi, 4 ;incrementing address of next item at end
cmp esi, edi ;middle reached ?
jb .reversing ;no we continue
.reversed:
;after, we compute the sum of the elements in the array
mov esi, revarray ; address of array
mov edx, ARRAY_LEN ; num elem of array
sub esp, 8
call arraysum ; sum computation
add esp, 8
cmp eax, sumarray1 ; we check the result to be sure
jne .failure
; merging array2 with array
; esi = source address of first array
; edi = address of second array, the destination one
mov esi, array2
mov edi, array
sub esp, 8
call mergearrays
add esp, 8
; we compute the sum of the merged arrays into the new (old one in fact)
mov esi, array
mov ecx, ARRAY_LEN
add esp, 8
call arraysum
add esp, 8
cmp eax, 0x43c ; we check the result to be sure
jne .failure ; if not egal, exit
; compute the sum of even numbers only in the array
; set up edx to 0 at beginning of loop
; cmova edx, $value_even_number
mov esi, revarray
xor ebx, ebx
xor ecx, ecx
.iterate:
xor edx, edx ; setting up edx to 0
mov eax, [esi + ecx * 4]
test al, 1 ; CF == 0 ? so it's an even number
cmovz edx, eax ; we store the value of current element in edx
add ebx, edx ; so in case of odd number => add 0
inc ecx
cmp ecx, ARRAY_LEN ; end of array reached ?
jne .iterate ; no, we continue
cmp ebx, 0x102 ; check if result is good
je .success
jmp .failure
exit
.success:
print strok, lenstrok
exit
.failure:
print strerror, lenerror
exit
; ********************************************
; computes the sum of all elements of an array
; esi = address of the array
; edx = number of elements in array
; output : eax
; ********************************************
arraysum:
push ebx
xor ebx, ebx
xor ecx, ecx
.arraysum:
mov eax, [esi + ecx *4]
add ebx, eax
inc ecx
cmp ecx, edx
je .sumdone
jmp .arraysum
.sumdone:
mov eax, ebx
pop ebx
ret
; *********************************************
; procedure that sort array from highest to lowest value
; first arg = address of the array
; second arg = number of elements in a array
; return : eax, sorted array
; ********************************************
sort:
push ebp
mov ebp, esp
mov edx, [ebp + 12]
.loop1:
mov esi, [ebp + 8]
mov ecx, [ebp + 12]
.loop2:
mov eax, [esi]
mov ebx, [esi + 4]
cmp eax, ebx
jg .skip
mov [esi], ebx
mov [esi + 4], eax
.skip:
add esi, 4 ;perform loop checks
dec ecx
cmp ecx, 1
ja .loop2
dec edx
ja .loop1
mov eax,[ebp + 8] ; save result in eax
mov esp,ebp
pop ebp
ret
; ****************************************************
; function adding two arrays merging them
; esi = address of first array
; edi = address of second array (destination array)
; ****************************************************
mergearrays:
xor ecx, ecx
.merging:
mov eax, [esi + ecx * 4]
xadd [edi + ecx * 4], eax
inc ecx
cmp ecx, ARRAY_LEN
je .mergedone
jmp .merging
.mergedone:
ret
Related
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 asked for help earlier and thought I was home free but I'm not. My logic was wrong and I've greatly altered it. This program is supposed to return the Max int in the array (which also happens to be the last element). After tracing it with GDB a few times, I see that I get to the 5th (2nd to last) element in the array, "20", when I hit a segmentation fault and the program halts. I set ECX to the array length and subtracted 1 from ECX to avoid this, but my logic is obviously wrong. Am I wrong in depending on the ECX counter to terminate the loop. Any ideas please?
***EDIT MAJOR EDITS TO CODE
SECTION .data ;data section
msg1: db "Here are the array elements:", 10, 0
msg1Len: equ $-msg1
msg2: db "Here is the sorted array:", 10, 0
msg2Len: equ $-msg2
arr: dd 12, 16, 6, 18, 10, 40, 30
arrLen: equ ($-arr)/4 ;number of elements = array length / 4
SECTION .bss
max resd 1 ;declare and reserve space for max
SECTION .text
global bsort
bsort:
push ebp ; save old ebp register
mov ebp, esp ; build a new stack
restart:
mov ebx, arr ; the base address argument is saved in ebx register
mov ecx, arrLen ; the size argument is saved in exc register
sub ecx, 1 ; Last member has no following element to compare with.
; So we need to reduce the counter by 1
top:
mov eax, [ebx] ;; access first array element. Move its value to eax
cmp eax, [ebx+4] ; compare the value of eax ([ebx]) with [ebx+4]
jle noswap ; if value at eax is less or equal to value of [ebx+4]
; no need to exchang values. Jump to noswap
xchg eax, [ebx+4] ; if value at eax > value [ebx+4], exchange
mov [ebx], eax ; store the new exchanged value at [ebx]
jmp restart ; reset the base address and counter. Start over
noswap:
add ebx, 4 ; move to the next array element
loop top ; loop back to the top if the register ecx > 0
leave
ret
global main
main:
push ebp
mov ebp, esp
mov ecx, msg1 ;print msg1
mov edx, msg1Len
call PString
;save array base address in ebx and save sizein in ecx
mov ebx, arr
mov ecx, arrLen; store num elements in ecx
;loop to print array
PrintArray:
mov eax, [ebx] ;move value [ebx] to eax
call PrintDec
call Println
add ebx, 4
loop PrintArray
;call bubblesort
call bsort
mov ecx, msg2
mov edx, msg2Len
call PString
;save arr base add in sbx and size in ecx
mov ebx, arr
mov ecx, arrLen
PrintSortedArray:
mov eax, [ebx]
call PrintDec
call Println
add ebx, 4
loop PrintSortedArray
;exit program and clean stack
mov esp, ebp
pop ebp
ret
PString:; save register values of the called function
pusha
mov eax,4 ; use 'write' system call = 4
mov ebx,1 ; file descriptor 1 = STDOUT
int 80h ; call the kernel
; restore the old register values of the called function
popa
ret
Println:
;will call PString func
;will change content of ecx and edx
;need to save registers used by the main program
section .data
nl db 10
section .text
pusha
mov ecx, nl
mov edx, 1
call PString
;return original register values
popa
ret
PrintDec:
;saves all registers so they return unmodified
;build the function to handle dword size
section .bss
decstr resb 10 ; 10 32-bit digits
ct1 resd 1 ;keep track of dec-string size
section .text
pusha; save registers
mov dword[ct1],0 ;initially assume 0
mov edi, decstr ; edi points to dec-string
add edi, 9 ; moved to the last element of string
xor edx, edx ; clear edx for 64-bit div
whileNotZero:
mov ebx, 10 ; get ready to divide by 10
div ebx ; divide by 10
add edx, '0' ; convert to ascii
mov byte[edi], dl ; put it in string
dec edi ; move to next char in str
inc dword[ct1] ; inc char counter
xor edx, edx ; clear edx
cmp eax, 0 ;is remainder 0?
jne whileNotZero ;if no, keep on looping
inc edi ; conversion finished, bring edi
mov ecx, edi ; back to start of string. make ecx
mov edx, [ct1] ; point to counterm edx gets # chars
mov eax, 4 ; print to stdout
mov ebx, 1
int 0x80 ; call kernel
popa ; restore registers
ret
My program works, but there is something wrong with my printMax function. The program terminates with a
Segmentation fault (core dumped).
I have tried building a stack for the function and just doing a pusha popa and both ways, I get the seg fault core dumped.
I've tried calling the function, but it just runs twice.
Any idea what I am doing wrong?
SECTION .data ;data section
msg1 : db "Here are the array elements:", 10, 0
msg1Len: equ $-msg1
msg2 : db "Here is the max value in the array:", 10, 0
msg2Len: equ $-msg2
arr : dd 2,4,6,8,10,20,40
arrLen : equ ($-arr)/4 ;number of elements = array length / 4
SECTION .bss
max resd 1 ;declare and reserve space for max
SECTION .text
global main
main:
push ebp
mov ebp, esp
mov ecx, msg1 ;print msg1
mov edx, msg1Len
call PString
;save array base address in ebx and save sizein in ecx
mov ebx, arr
mov ecx, arrLen; store num elements in ecx
;loop to print array
PrintArray:
mov eax, [ebx] ;move value [ebx] to eax
call PrintDec
call Println
add ebx, 4
loop PrintArray
printMax:
section .text
pusha
;reset array to find max
mov ebx, arr
mov ecx, arrLen
loopForMax:
mov eax, [ebx]
cmp eax, [ebx +4]
jle sameMax
mov [max], eax
sameMax:
add ebx, 4 ;move to next element
loop loopForMax
mov ecx, msg2
mov edx, msg2Len
call PString
mov eax, [max]
call PrintDec
call Println
popa
ret
;exit program and clean stack
mov esp, ebp
pop ebp
ret
PString:; save register values of the called function
pusha
mov eax,4 ; use 'write' system call = 4
mov ebx,1 ; file descriptor 1 = STDOUT
int 80h ; call the kernel
; restore the old register values of the called function
popa
ret
Println:
;will call PString func
;will change content of ecx and edx
;need to save registers used by the main program
section .data
nl db 10
section .text
pusha
mov ecx, nl
mov edx, 1
call PString
;return original register values
popa
ret
PrintDec:
;saves all registers so they return unmodified
;build the function to handle dword size
section .bss
decstr resb 10 ; 10 32-bit digits
ct1 resd 1 ;keep track of dec-string size
section .text
pusha; save registers
mov dword[ct1],0 ;initially assume 0
mov edi, decstr ; edi points to dec-string
add edi, 9 ; moved to the last element of string
xor edx, edx ; clear edx for 64-bit div
whileNotZero:
mov ebx, 10 ; get ready to divide by 10
div ebx ; divide by 10
add edx, '0' ; convert to ascii
mov byte[edi], dl ; put it in string
dec edi ; move to next char in str
inc dword[ct1] ; inc char counter
xor edx, edx ; clear edx
cmp eax, 0 ;is remainder 0?
jne whileNotZero ;if no, keep on looping
inc edi ; conversion finished, bring edi
mov ecx, edi ; back to start of string. make ecx
mov edx, [ct1] ; point to counterm edx gets # chars
mov eax, 4 ; print to stdout
mov ebx, 1
int 0x80 ; call kernel
popa ; restore registers
ret
I'm having trouble understanding how to traverse a 2-d array in x86 assembly language. I am missing a bit of understanding. This is what I have so far.
The issue is the lines with //offset and //moving through array
For the //offset line the error I am getting is "non constant expression in second operand"
and also
"ebx: illegal register in second operand"
For the next line I get the error
"edx: illegal register in second operand"
mov esi, dim
mul esi
mov eax, 0 // row index
mov ebx, 0 // column index
mov ecx, dim
StartFor:
cmp ecx, esi
jge EndFor
lea edi, image;
mov edx, [eax *dim + ebx] // offset
mov dl, byte ptr [edi + esi*edx] // moving through array
mov edx, edi
and edx, 0x80
cmp edx, 0x00
jne OverThreshold
mov edx, 0xFF
OverThreshold:
mov edx, 0x0
See x86 tag wiki, including the list of addressing modes.
You can scale an index register by a constant, but you can't multiply two registers in an addressing mode. You'll have to do that yourself (e.g. with imul edx, esi, if the number of columns isn't a compile time constant. If it's a power-of-2 constant, you can shift, or even use a scaled addressing mode like [reg + reg*8]).
re: edit: *dim should work if dim is defined with something like dim equ 8. If it's a memory location holding a value, then of course it's not going to work. The scale factor can be 1, 2, 4, or 8. (The machine code format has room for a 2-bit shift count, which is why the options are limited.)
I'd also recommend loading with movzx to zero-extend a byte into edx, instead of only writing dl (the low byte). Actually nvm, your code doesn't need that. In fact, you overwrite the value you loaded with edi. I assume that's a bug.
You can replace
imul edx, esi
mov dl, byte ptr [edi + edx] ; note the different addressing mode
mov edx, edi ; bug? throw away the value you just loaded
and edx, 0x80 ; AND already sets flags according to the result
cmp edx, 0x00 ; so this is redundant
jne OverThreshold
with
imul edx, esi
test 0x80, byte ptr [edi + edx] ; AND, discard the result and set flags.
jnz
Of course, instead of multiplying inside the inner loop, you can just add the columns in the outer loop. This is called Strength Reduction. So you do p+=1 along each row, and p+=cols to go from row to row. Or if you don't need to care about rows and columns, you can just iterate over the flat memory of the 2D array.
A 2-dimensional array is just an interpretation of a sequence of bytes. You'll have to choose in which order to store the items. For example, you might choose "row-major order".
I've written a demo where a buffer is filled with a sequence of numbers. Then the sequence is interpreted as single- and two-dimensional array.
tx86.s
%define ITEM_SIZE 4
extern printf
section .bss
cols: equ 3
rows: equ 4
buf: resd cols * rows
c: resd 1
r: resd 1
section .data
fmt: db "%-4d", 0 ; fmt string, '0'
fmt_nl: db 10, 0 ; "\n", '0'
section .text ; Code section.
global main
main:
push ebp
mov ebp, esp
; fill buf
mov ecx, cols * rows - 1
mov [buf + ITEM_SIZE], ecx
.fill_buf:
mov [buf + ecx * ITEM_SIZE], ecx
dec ecx
jnz .fill_buf
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; buf as 1-dimensional array
; buf[c] = [buf + c * ITEM_SIZE]
xor ecx, ecx
mov [c], ecx
.lp1d:
mov ecx, [c]
push dword [buf + ecx * ITEM_SIZE]
push dword fmt
call printf
mov ecx, [c]
inc ecx
mov [c], ecx
cmp ecx, cols * rows
jl .lp1d
; print new line
push dword fmt_nl
call printf
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; buf as 2-dimensional array
; buf[r][c] = [buf + (r * cols + c) * ITEM_SIZE]
xor ecx, ecx
mov [r], ecx
.lp1:
xor ecx, ecx
mov [c], ecx
.lp2:
; calculate address
mov eax, [r]
mov edx, cols
mul edx ; eax = r * cols
add eax, [c] ; eax = r * cols + c
; print buf[r][c]
push dword [buf + eax * ITEM_SIZE]
push dword fmt
call printf
; next column
mov ecx, [c]
inc ecx
mov [c], ecx
cmp ecx, cols
jl .lp2
; print new line
push dword fmt_nl
call printf
; next row
mov ecx, [r]
inc ecx
mov [r], ecx
cmp ecx, rows
jl .lp1
mov esp, ebp
pop ebp ; restore stack
xor eax, eax ; normal exit code
ret
Buidling(on Linux)
nasm -f elf32 -l tx86.lst tx86.s
gcc -Wall -g -O0 -m32 -o tx86 tx86.o
Running
./tx86
0 1 2 3 4 5 6 7 8 9 10 11
0 1 2
3 4 5
6 7 8
9 10 11
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 :)