assembly intel x86 call function with local variables - c

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

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

Shellsort: Write NASM code from C

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

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

Intel 8086 Insertion Sort: Skipping numbers within the Array

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.

How to reverse an array in Irvine Assembly Language?

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.

Resources