I am trying to convert a C function to NASM in 32-bit.
Here is the C function
void shellsort (int *A, int n)
{
int gap, i, j, temp;
for (gap = n/2; gap > 0; gap /=2)
{
for (i= gap; i<n; i++)
{
for (j=i-gap; j>=0 && A[j] > A[j+gap]; j-=gap)
{
temp = A[j];
A[j] = A[j+gap]
A[j+gap] = temp;
}
}
}
}
Here is what I have done so far
%include "asm_io.inc"
SECTION .data
SECTION .bss
SECTION .text
extern printf
global sort
sort:
push ebp ;set up stack frame
mov ebp, esp
sub esp, 0x10 ;assign 16byte space for local variables
firstloop:
mov eax, [ebp+8] ;moves value of n into eax
mov edx, 0 ;prepares for division, remainder init
mov ecx, 2 ;divisor
div ecx ;division, store gap in eax
cmp eax, 0 ;compare if gap with zero
jle firstloopDone ;firstloopDone
firstloopDone:
div ecx
jmp done
secondloop:
mov ecx, eax ;copy value of gap into ecx, i=gap
cmp ecx, [ebp+8] ;compare i with 1st parameter n
jge secondloopDone ;jump to secondloop if greater-equal
inc ecx ;increment i
jmp thirdloop ;jump to thirdloop
secondloopDone:
inc ecx ;increment i
jmp firstloop ;jump to firstloop
thirdloop:
mov edx, ecx ;save i value
sub ecx, eax ;subtract gap from i and store in ecx
cmp ecx, 0 ;compare j with zero
jl thirdloopDone ;if not j>=0, then skip to end of loop
cmp [ebp+12+ecx], [ebp+12+ecx+eax] ;compare A[j] and A[j+gap]
jle thirdloopDone
sub ecx, eax ;subtract gap from j and store in ecx
jmp swap
thirdloopDone:
sub ecx, eax ;j-gap
jmp secondloop ;jump to second loop
swap:
mov edx, [ebp+12+ecx] ;copy A[j] to temp
mov [ebp+12+ecx], [ebp+12+ecx+eax] ;A[j]=A[j+gap]
mov [edp+12+ecx+eax], edx ;A[j+gap]= temp
jmp thirdloop
done:
leave ;destroy stack frame
ret
Needless to say, it doesn't work.
The error message says:
"error: beroset-p-603-invalid effective address"
at line 58 (thirdloop)
cmp [ebp+12+ecx], [ebp+12+ecx+eax]
and at line 71 (swap)
mov edx, [ebp+12+ecx]
I understand that this may be the wrong method. But from the nested loop in C code, I have too many variables to keep so I cant spare any of the registers.
Also, I suspect I may not have a proper understanding of the stack frame, which may show in my code.
Any help, or bug-discovery, would be greatly appreciated. Thanks.
You say you don't have enough registers to play with but you don't even use EBX, ESI, nor EDI. You made provisions for local variables yet you don't use any.
firstloop:
mov eax, [ebp+8] ;moves value of n into eax
mov edx, 0 ;prepares for division, remainder init
mov ecx, 2 ;divisor
div ecx ;division, store gap in eax
The previous code does not need the division. Simplify it with a shift operation. shr eax,1
The next code will always terminate. Use jle done
cmp eax, 0 ;compare if gap with zero
jle firstloopDone ;firstloopDone
firstloopDone:
div ecx
jmp done
Aren't parameters pushed from right to left?
mov ebx, [ebp+8] ;pointer to array A
firstloop:
mov eax, [ebp+12] ;number of elemnets n
You would typically have to code
swap:
mov edx, [ebx+ecx*4] ;TEMP=A[j]
add ecx,eax
xchg edx, [ebx+ecx*4] ;Swap A[j+gap] with TEMP
sub ecx, eax
mov [ebx+ecx*4], edx ;A[j]=TEMP
jmp thirdloop
Related
I'm having an issue with a binary search implementation. Note, I do not want to modify this c code, I'm trying to translate it to assembly. Here's my c code:
int binary_search_c(int n, int list[], int low, int high) {
int middle;
if (low > high)
return -1;
middle = (low + high)/2;
if (n == list[middle])
return middle;
if (n < list[middle]) {
high = middle - 1;
} else {
low = middle + 1;
}
return binary_search_c(n, list, low, high);
}
And here is my binary search in assembly. I've commented NEW with things I've added for the first time, so possible problem areas, though I'm pretty sure my problem lies in bin.
;* int binary_search(int n, int list[], int low, int high); *
;*****************************************************************************
%define n [ebp+8]
%define list [ebp+12]
%define low [ebp+16]
%define high [ebp+20]
binary_search:
push ebp
mov ebp, esp
sub esp, 4 ;one local var |
%define middle [ebp-4] ; define local var as middle | NEW
mov eax, low ; pull low into register
cmp eax, high ; compare low to high low > high
jle less; ; if less than or greater, go to less
mov eax, -1 ; move -1 into eax for return
jmp return; ; call return/end function
less: ;if low is not > high
mov eax, low ;move middle into register
add eax, high ;high+low
mov ebx, 2 ;move 2 into ebx for div
xor edx, edx ;clear edx |
xor eax, eax
div ebx ; div eax, by 2 =>(low+high)/2 | NEW
mov middle, eax ;move (low+high)/2 into middle
mov eax, n ; move middle into eax
mov edi, list ; move list into edi for list call
mov edx, middle ; move middle into edx as list index |
lea esi, [edi+edx*4] ; grab list[middle] |
mov ebx, [esi] ; move list[middle] into register| NEW
cmp eax, ebx ; compare n and list[middle]
jne notE; ; if not equal (n ==list[middle]
mov eax, middle ;move middle into eax for return
jmp return; ;call return/end function
notE: ;if n !=list[middle]
mov eax, n ;move eax into n
mov edi, list ; move list into edi to access list
mov ebx, middle ; move middle into ebx as index |
lea esi, [edi+ebx*4] ; grab list[middle] |
mov ebx, [esi] ;move list[middle] into ebx |NEW
cmp eax, ebx ; compare n and list[middle]
jge .else; ; if greater than or equal n < list[middle]
mov eax, middle ;move middle into eax for return
sub eax, 1 ;middle-1
mov ebx, high ; put high into ebx for replacement
mov ebx, eax ; high = middle-1
jmp bin; ;jump to next part[not return]
.else: ;if n >= list[middle]
mov eax, middle ;move middle into eax to add 1
add eax, 1 ; middle+1
mov ebx, low ;move low into ebx for change in low
mov ebx, eax ;low = middle+1
bin: ; final return
mov eax, high ; move high into eax for push
push eax ; push high
mov ebx, low ;low into ebx
push ebx ; push low
mov ecx, list ; list for push
push ecx ;push list
mov edx, n ;n for push
push edx ;push n |
call binary_search ; call search |
add esp, 16 ; add 16=>(4*4) to stack for reset | NEW
return: ;end function
leave
ret
So after chopping my code up while trying to find the error, I'm getting a seg fault when trying to run anything using bin
I think I may be doing something wrong with the addition to the stack with add esp, 16 though I'm not sure as it's the first time I've called a function with so many arguments and with a local variable at hand.
I'll gladly take any optimization to my code, I'm just weary about the things labeled NEW, and I need to figure out what I'm doing wrong in bin that throws a fault, any help would be greatly appreciated.
mov eax, low ;move middle into register
add eax, high ;high+low
mov ebx, 2 ;move 2 into ebx for div
xor edx, edx ;clear edx |
xor eax, eax ;clear eax |
div ebx ; div eax, by 2 =>(low+high)/2 | NEW
In this code you're trying to calculate the middle but you don't really do that because you zero the EAX register right before the division!
Please remove the xor eax, eax instruction.
mov ebx, high ; put high into ebx for replacement
mov ebx, eax ; high = middle-1
jmp bin
This is a further problem:
You want to setup for the lower partition, but you don't actually store the new high index!
This will do it:
mov high, eax ; high = middle-1
jmp bin
The same applies to the low index.
Just an update on the working code, with a div by 2 instead of a shift, inefficient, by functional. In case anyone else needs it, here:
;* int binary_search(int n, int list[], int low, int high); *
;*****************************************************************************
%define n [ebp+8]
%define list [ebp+12]
%define low [ebp+16]
%define high [ebp+20]
binary_search:
push ebp
mov ebp, esp
sub esp, 4 ;one local var
%define middle [ebp-4] ; define local var as middle
mov eax, low ; pull low into register
cmp eax, high ; compare low to high low > high
jle less; ; if less than or greater, go to less
mov eax, -1 ; move -1 into eax for return
jmp return; ; call return/end function
less: ;if low is not > high
mov eax, low ;move middle into register
add eax, high ;high+low
mov ebx, 2 ;move 2 into ebx for div
xor edx, edx ;clear edx
div ebx ; div eax, by 2 =>(low+high)/2
mov middle, eax ;move (low+high)/2 into middle
mov eax, n ; move middle into eax
mov edi, list ; move list into edi for list call
mov edx, middle ; move middle into edx as list index
lea esi, [edi+edx*4] ; grab list[middle]
mov ebx, [esi] ; move list[middle] into register
cmp eax, ebx ; compare n and list[middle]
jne notE; ; if not equal (n ==list[middle]
mov eax, middle ;move middle into eax for return
jmp return; ;call return/end function
notE: ;if n !=list[middle]
mov eax, n ;move eax into n
mov edi, list ; move list into edi to access list
mov ebx, middle ; move middle into ebx as index
lea esi, [edi+ebx*4] ; grab list[middle]
mov ebx, [esi] ;move list[middle] into ebx
cmp eax, ebx ; compare n and list[middle]
jge .else; ; if greater than or equal n < list[middle]
mov eax, middle ;move middle into eax for return
sub eax, 1 ;middle-1
mov high, eax
jmp bin; ;jump to next part[not return]
.else: ;if n >= list[middle]
mov eax, middle ;move middle into eax to add 1
add eax, 1 ; middle+1
;move low into ebx for change in low
mov low, eax ;low = middle+1
bin: ; final return
mov eax, high ; move high into eax for push
push eax ; push high
mov ebx, low ;low into ebx
push ebx ; push low
mov ecx, list ; list for push
push ecx ;push list
mov edx, n ;n for push
push edx ;push n
call binary_search ; call search
add esp, 16 ; add 16=>(4*4) to stack for reset
return: ;end function
leave
ret
Thank you to #Fifoernik for his answer
This builds successfully and there is nothing wrong with typing integers. However, I can't see the outcome of gcd and the debugger runs without anything. Is it because of the infinite loop? I am totally lost in here. Could anyone figure out what I am missing here? Please help me what is wrong with this?
INCLUDE Irvine32.inc
.data
strA BYTE "Enter an integer A: ",0
strB BYTE "Enter an integer B: ",0
temp DWORD ?
finalStr BYTE "GCD of the two integers is: ",0
.code
main PROC
call Clrscr
mainLoop:
mov edx,OFFSET strA
call WriteString
call ReadInt
mov temp, eax
call Crlf
mov edx, OFFSET strB
call WriteString
call ReadInt
mov ebx, eax
mov eax, temp
call Crlf
call GCD
mov edx, OFFSET finalStr
call WriteString
call WriteInt
call WaitMsg
jmp mainLoop
main ENDP
abs PROC
cmp eax, 0 ; see if we have a negative number
jge done
neg eax
done:
ret
abs ENDP
gcd PROC
call abs ;takes absolute value of both registers
mov temp, eax
mov eax, ebx
call abs
mov ebx, eax
mov eax, temp
cmp eax, ebx ; making sure we divide the bigger number by the smaller
jz DONE ; if numbers are equal, GCD is eax either way
jc SWITCH ;swaps if ebx is larger then eax
mov edx, 0
SWITCH: ;swaps values so eax is larger then ebx
mov temp, eax
mov eax, ebx
mov ebx, temp
mov edx, 0
jmp L1
L1: ;divides until remainder is 0, then eax is GCD
div ebx
cmp edx, 0
jz DONE
mov eax, edx
jmp L1
DONE:
gcd ENDP
END main
Your GCD function is missing a return instruction:
DONE: <-- There should be a RET after the DONE label
gcd ENDP
I'll tell you one thing wrong with that code straight up (there may be other issues, that's just the one that immediately popped out):
jc SWITCH ;swaps if ebx is larger then eax
mov edx, 0
SWITCH: ;swaps values so eax is larger then ebx
mov temp, eax
mov eax, ebx
mov ebx, temp
mov edx, 0
jmp L1
L1: ;divides until remainder is 0, then eax is GCD
This will switch the registers no matter what since regardless of the state of the carry flag, you run the code at SWITCH. Either you explicitly jump to there, or you fall through to there.
I suspect the jmp L1 (which is superfluous at its current position, for reasons identical to the jump if carry) should be immediately after the jc SWITCH so that the whole thing either swaps or doesn't swap.
In other words, something like:
jnc L1 ; skip swap unless ebx > eax.
SWITCH: push eax ; don't need temp at all, use stack.
push ebx
pop eax
pop ebx
L1:
mov edx, 0 ; carry on.
So we're currently studying Intel 8086 Insertion Sort Code that our professor showed us. He wanted us to figure out why the code skips the 0th element within the array and the 3rd element in the array from a code that he had taken from the web.
; void isort(int *a, int n)
; sorts the first n elements of a
;
; Parameters
; a - pointer to the array
; n - number of elements to sorts
%define a [ebp + 8]
%define n [ebp + 12]
isort:
enter 0, 0
pusha
mov ecx, 1
for:
mov ebx, ecx
imul ebx, 4
add ebx, a
mov ebx, [ebx]
mov edx, ecx
dec edx
while:
cmp edx, 0
jl while_quit
mov eax, edx
imul eax, 4
add eax, a
cmp ebx, [eax]
jge while_quit
mov esi, [eax]
mov dword [eax + 4], esi
dec edx
jmp while
while_quit:
mov [eax], ebx
inc ecx
cmp ecx, n
jl for
popa
leave
ret
And the sample array was {5, 8, 12, 2, 1, 7}. This is more for understanding the 8086 language since we just started a couple days ago, and I was wondering if anyone could explain how and what might be going wrong.
Consider what the code will do when ECX is 1:
The while loop will be entered with EBX=8 and EDX=0.
The jl while_quit will not be taken, since EDX is 0.
EBX is compared to [EAX]. That is; 8 is compared to a[0], which is 5, so the jge while_quit is taken.
mov [eax], ebx stores 8 at a[0], so your array now contains {8,8,12,2,1,7}. Clearly not what you want, since you've lost one of the original elements.
Apart from the code's logical flaw, those imul instructions are completely unnecessary since you can use scaled indices on the x86. So the sorting code can be simplified to this (I've verified that it sorts the array correctly):
mov ecx, 1
for_:
; esi = &a[holePos]
lea esi,[a + ecx*4]
; valueToInsert = a[holePos]
mov ebx, [esi]
while_:
; Did we reach the beginning of the array?
cmp esi, OFFSET a
jle while_quit
; valueToInsert < a[holePos-1] ?
cmp ebx, [esi-4]
jge while_quit
; a[holePos] = a[holePos-1]
mov eax, [esi-4]
mov [esi], eax
; esi = &a[holePos-1]
sub esi,4
jmp while_
while_quit:
; a[holePos] = valueToInsert
mov [esi], ebx
inc ecx
cmp ecx, 6
jl for_
I'm using MASM syntax, but you get the idea.
I would like some help with operation on matrix in assembly language.
My code does Cholesky Decomposition in C and ASM and compare their speed.
I created nested loops already and its working fine, yest i have no idea how to adress properly matrix to access its elements. Matrix is DOUBLE in C and i managed to transer its adress (first element) to assembly.
Cholesky_double proc \
tab_addr:DWORD, \ ; begin adres of matrix
num_elem:DWORD ; element count in row/column (n of Matrix[n][n])
LOCAL i:DWORD, k:DWORD, j:DWORD, skoczek:DWORD
;skoczek is for operation count check (ex.should be 13 for 3x3 matrix)
; push register on stack
push edx
push ecx
push ebx
push esi
push edi
mov k, 0
mov skoczek, 0
for0start:
inc skoczek
mov eax, k
mov i, eax
inc i
;there should be MATRIX[k][k] = sqrt(MATRIX[k][k])
mov eax, num_elem
sub eax, i
cmp eax, 0
je for1end
for1start:
inc skoczek
;MATRIX[i][k]=MATRIX[i][k]/MATRIX[k][k]
for1koniec:
inc i
mov eax, num_elem
sub eax, i
cmp eax, 0
jne for1start
for1END:
mov eax, k
mov j, eax
inc j
mov eax, num_elem
sub eax, j
cmp eax, 0
je for2end
for2start:
inc skoczek
mov eax, j
mov i, eax
for3start:
inc skoczek
;MATRIX[i][j] = MATRIX[i][j]-MATRIX[i][k]*MATRIX[j][k]
for3koniec:
inc i
mov eax, num_elem
sub eax, i
cmp eax, 0
jne for3start
for2koniec:
inc j
mov eax, num_elem
sub eax, j
cmp eax, 0
jne for2start
for2end:
for0koniec:
inc k
mov eax, num_elem
sub eax, k
cmp eax, 0
jne for0start
koniec:
pop edi
pop esi
pop ebx
pop ecx
pop edx
mov eax, skoczek
ret
; return with operation count in eax
Cholesky_double endp
The matrix passed in C with
extern "C" int __stdcall Cholesky_double(double* tab_adr, int num_el);
I use Visual Studio 2010 and solution with project that create ASSEMBLY library and project with code in C++ that can use assembler functions.
I am not asking for filling code for me, just for a little bit help with correct adressing of matrix to properly access its elemets. If you foresee more problems coming here (like Sqrt in asm i would be pleased with some guidance.
You first have to linearize the address:
&matrix[k][i] = matrix + i*sizeof(double) + k*N*sizeof(double);
where N is the row width. (Assuming NxN matrix)
That can be loaded with
fld [%eax] // load to top of stack in FPU (assuming ia-32 system)
mov %rbx,[%rax]; // vs. load 64-bit register
movsd %xmm0, [%rax] // vs. load a double to lower 64-bits of xmm register
I am writing a number manipulation program, and one step is to reverse the array. So far I have
reverseArray proc
mov eax, arraySize
mov temp, eax
Imul eax, 4
mov esi, eax
mov eax, 0
mov number, 0
jne L1
L1:
cmp temp, 0
je L3
mov eax, numbers[esi]
mov tempnumbers[ecx], eax
mov eax, tempnumbers[ecx]
call writeDec
call crlf
sub esi,4
add ecx, 4
sub temp, 1
loop L1
L2:
ret
L3:
.If(number >= 0)
mov esi, 0
.ENDIF
mov eax, number
cmp eax, arraySize
je L2
mov eax, tempnumbers[esi]
mov numbers[esi], eax
add esi, 4
add number, 1
loop L3
However this only reverses about half of my array. Did I do something wrong with the esi or ecx counters? Any help would be appreciated.
Use edi instead of ecx, and set it to 0 at the start.