Related
; Input x and y, output min of the two numbers
.586
.MODEL FLAT
INCLUDE io.h
.STACK 4096
.DATA
number DWORD ?
array DWORD 20, 15, 62, 40, 18
nbrElts DWORD 5
prompt BYTE "Enter value:", 0
string BYTE 80 DUP (?)
resultLbl BYTE "Position", 0
result BYTE 11 DUP (?), 0
.CODE
_MainProc PROC
input prompt, string, 20 ; read ASCII characters
atod string ; convert to integer
mov number, eax ; store in memory
push nbrElts ; 3rd parameter (# of elements in array)
lea eax, array ; 2nd parameter (address of array)
push eax
push number ; 1st parameter (value from user)
call searchArray ; searchArray(number, array, 5)
add esp, 12
dtoa result, eax ; convert to ASCII characters
output resultLbl, result ; output label and result
mov eax, 0 ; exit with return code 0
ret
_MainProc ENDP
; searchArray(int x, array, int y)
;
searchArray PROC
push ebp ; save base pointer
mov ebp, esp ; establish stack frame
push eax ; save registers
push ebx
push esi
push ecx
push edx
mov ebx, [ebp+8] ; x, value from user
mov esi, [ebp+12] ; address of array
mov ecx, [ebp+16] ; y, number of elements
mov edx, 1
mov ecx, 5
forLoop:
mov eax, [esi] ; a[i]
cmp eax, ebx ; eax = ebx ?
je isEqual
;cmp eax, ebx
add esi, 4
inc edx
loop forLoop
;mov eax, 0
cmp edx, 6
je notEqual
isEqual:
mov eax, edx
jmp exitCode
notEqual:
mov eax, 0
jmp exitCode
exitCode:
mov eax, edx
pop edx ; restore EBP
pop ecx ; restore EAX
pop esi
pop ebx
pop ebp
ret ; return
searchArray ENDP
END ; end of source code
The pops at the end of the function need to match the pushes at the beginning of the function. If they don't match, the stack pointer ends up in the wrong place and the ret returns to the wrong place.
In your case, you have an extra push without a corresponding pop.
The reason to push registers at the beginning and pop them at the end is to preserve their values. But you don't want to preserve the value of eax. You want to return a different value, the result of the function. So there is absolutely no reason to push eax.
The goal of the program is to calculate a GCD of two numbers using a recursive function defined by this pseudo code
unsigned int gcd ( unsigned int a, unsigned int b ) {
if ( a > b )
return gcd ( a - b, b ) ;
if ( a < b )
return gcd ( a, b - a ) ;
return a ;
}
Here is the (undoubtedly poor) assembly code I'm having issues with.
.586
.MODEL FLAT
INCLUDE io.h
.STACK 4096
.DATA
a DWORD ?
b DWORD ?
prompt1 BYTE "a: ", 0
prompt2 BYTE "b:", 0
string BYTE 20 DUP (?)
resultLbl BYTE "gcd:", 0
result BYTE 11 DUP (?), 0
.CODE
_MainProc PROC
input prompt1, string, 20
atod string
mov a, eax
input prompt2, string, 20
atod string
mov b, eax
push b
push a
call _gcd
add esp, 8
dtoa result, eax
output resultLbl, result
mov eax, 0
ret
_MainProc ENDP
_gcd PROC
push ebp
mov ebp, esp
push ebx
push eax
mov eax, [ebp+8]
mov ebx, [ebp+12]
cmp eax, ebx
jg loop1
cmp eax, ebx
jl loop2
pop ebx
pop ebp
ret
loop1:
push ebx
sub eax, ebx
push eax
call _gcd
loop2:
sub ebx, eax
push ebx
push eax
output did2, result
call _gcd
_gcd ENDP
END
By creating some outputs in the loops to display a message when they occur I can tell that the program is calculating the GCD correctly however as soon as both values are equal and "ret" is hit inside of _gcd the program terminates. What do I need to change so the GCD value is returned and displayed correctly?
I am looking for a way to print an integer in assembler (the compiler I am using is NASM on Linux), however, after doing some research, I have not been able to find a truly viable solution. I was able to find a description for a basic algorithm to serve this purpose, and based on that I developed this code:
global _start
section .bss
digit: resb 16
count: resb 16
i: resb 16
section .data
section .text
_start:
mov dword[i], 108eh ; i = 4238
mov dword[count], 1
L01:
mov eax, dword[i]
cdq
mov ecx, 0Ah
div ecx
mov dword[digit], edx
add dword[digit], 30h ; add 48 to digit to make it an ASCII char
call write_digit
inc dword[count]
mov eax, dword[i]
cdq
mov ecx, 0Ah
div ecx
mov dword[i], eax
cmp dword[i], 0Ah
jg L01
add dword[i], 48 ; add 48 to i to make it an ASCII char
mov eax, 4 ; system call #4 = sys_write
mov ebx, 1 ; file descriptor 1 = stdout
mov ecx, i ; store *address* of i into ecx
mov edx, 16 ; byte size of 16
int 80h
jmp exit
exit:
mov eax, 01h ; exit()
xor ebx, ebx ; errno
int 80h
write_digit:
mov eax, 4 ; system call #4 = sys_write
mov ebx, 1 ; file descriptor 1 = stdout
mov ecx, digit ; store *address* of digit into ecx
mov edx, 16 ; byte size of 16
int 80h
ret
C# version of what I want to achieve (for clarity):
static string int2string(int i)
{
Stack<char> stack = new Stack<char>();
string s = "";
do
{
stack.Push((char)((i % 10) + 48));
i = i / 10;
} while (i > 10);
stack.Push((char)(i + 48));
foreach (char c in stack)
{
s += c;
}
return s;
}
The issue is that it outputs the characters in reverse, so for 4238, the output is 8324. At first, I thought that I could use the x86 stack to solve this problem, push the digits in, and pop them out and print them at the end, however when I tried implementing that feature, it flopped and I could no longer get an output.
As a result, I am a little bit perplexed about how I can implement a stack in to this algorithm in order to accomplish my goal, aka printing an integer. I would also be interested in a simpler/better solution if one is available (as it's one of my first assembler programs).
One approach is to use recursion. In this case you divide the number by 10 (getting a quotient and a remainder) and then call yourself with the quotient as the number to display; and then display the digit corresponding to the remainder.
An example of this would be:
;Input
; eax = number to display
section .data
const10: dd 10
section .text
printNumber:
push eax
push edx
xor edx,edx ;edx:eax = number
div dword [const10] ;eax = quotient, edx = remainder
test eax,eax ;Is quotient zero?
je .l1 ; yes, don't display it
call printNumber ;Display the quotient
.l1:
lea eax,[edx+'0']
call printCharacter ;Display the remainder
pop edx
pop eax
ret
Another approach is to avoid recursion by changing the divisor. An example of this would be:
;Input
; eax = number to display
section .data
divisorTable:
dd 1000000000
dd 100000000
dd 10000000
dd 1000000
dd 100000
dd 10000
dd 1000
dd 100
dd 10
dd 1
dd 0
section .text
printNumber:
push eax
push ebx
push edx
mov ebx,divisorTable
.nextDigit:
xor edx,edx ;edx:eax = number
div dword [ebx] ;eax = quotient, edx = remainder
add eax,'0'
call printCharacter ;Display the quotient
mov eax,edx ;eax = remainder
add ebx,4 ;ebx = address of next divisor
cmp dword [ebx],0 ;Have all divisors been done?
jne .nextDigit
pop edx
pop ebx
pop eax
ret
This example doesn't suppress leading zeros, but that would be easy to add.
I think that maybe implementing a stack is not the best way to do this (and I really think you could figure out how to do that, saying as how pop is just a mov and a decrement of sp, so you can really set up a stack anywhere you like by just allocating memory for it and setting one of your registers as your new 'stack pointer').
I think this code could be made clearer and more modular if you actually allocated memory for a c-style null delimited string, then create a function to convert the int to string, by the same algorithm you use, then pass the result to another function capable of printing those strings. It will avoid some of the spaghetti code syndrome you are suffering from, and fix your problem to boot. If you want me to demonstrate, just ask, but if you wrote the thing above, I think you can figure out how with the more split up process.
; Input
; EAX = pointer to the int to convert
; EDI = address of the result
; Output:
; None
int_to_string:
xor ebx, ebx ; clear the ebx, I will use as counter for stack pushes
.push_chars:
xor edx, edx ; clear edx
mov ecx, 10 ; ecx is divisor, devide by 10
div ecx ; devide edx by ecx, result in eax remainder in edx
add edx, 0x30 ; add 0x30 to edx convert int => ascii
push edx ; push result to stack
inc ebx ; increment my stack push counter
test eax, eax ; is eax 0?
jnz .push_chars ; if eax not 0 repeat
.pop_chars:
pop eax ; pop result from stack into eax
stosb ; store contents of eax in at the address of num which is in EDI
dec ebx ; decrement my stack push counter
cmp ebx, 0 ; check if stack push counter is 0
jg .pop_chars ; not 0 repeat
mov eax, 0x0a
stosb ; add line feed
ret ; return to main
; eax = number to stringify/output
; edi = location of buffer
intToString:
push edx
push ecx
push edi
push ebp
mov ebp, esp
mov ecx, 10
.pushDigits:
xor edx, edx ; zero-extend eax
div ecx ; divide by 10; now edx = next digit
add edx, 30h ; decimal value + 30h => ascii digit
push edx ; push the whole dword, cause that's how x86 rolls
test eax, eax ; leading zeros suck
jnz .pushDigits
.popDigits:
pop eax
stosb ; don't write the whole dword, just the low byte
cmp esp, ebp ; if esp==ebp, we've popped all the digits
jne .popDigits
xor eax, eax ; add trailing nul
stosb
mov eax, edi
pop ebp
pop edi
pop ecx
pop edx
sub eax, edi ; return number of bytes written
ret
I'm trying to sort a array of numbers by Insertion sort but it not sort the numbers correctly. I've tried everything but it does not sort them As it should.
I would be happy if you could help me figure out where the problem and on how I can fix it thanks !!
section .rodata
MSG: DB "welcome to sortMe, please sort me",10,0
S1: DB "%d",10,0 ; 10 = '\n' , 0 = '\0'
section .data
array DB 5,1,7,3,4,9,12,8,10,2,6,11 ; unsorted array
len DB 12
section .text
align 16
global main
extern printf
main:
push MSG ; print welcome message
call printf
add esp,4 ; clean the stack
call printArray ;print the unsorted array
push ebp ;save old frame pointer
mov ebp,esp ;create new frame on stack
pusha
mov esi,array
mov ecx,8
OuterLoop:
mov ebx,ecx
InnerLoop:
add esi,ebx ;basically makes array[0] to array[ebx]
mov eax,[esi] ;eax=array[ebx]
sub esi,8
mov edx,[esi] ; edx=array[ebx-1]
add esi,8
cmp eax,edx ; if(eax<edx)
jle skip2 ; skip the loop
;else:
mov [esi],edx ;array[ebx]=array[ebx-1]
sub esi,8
mov [esi],eax ; array[ebx-1]=array[ebx]
add esi,8
sub esi,ebx ; return the array to its original state (array[0])
sub ebx,8
cmp ebx,0
jne InnerLoop
skip1:
add ecx,8
cmp ecx,96
jle OuterLoop
popa ;restore registers
mov esp,ebp ;clean the stack frame
pop ebp
push MSG ; print welcome message (to divide between the unsorted and sorted)
call printf
add esp,4 ; clean the stack
call printArray
mov eax, 1 ;exit system call
int 0x80
printArray:
push ebp ;save old frame pointer
mov ebp,esp ;create new frame on stack
pusha ;save registers
mov eax,0
mov ebx,0
mov edi,0
mov esi,0 ;array index
mov bl,byte[len]
add edi,ebx ; edi = array size
print_loop:
cmp esi,edi
je print_end
mov al ,byte[array+esi] ;set num to print in eax
push eax
push S1
call printf
add esp,8 ;clean the stack
inc esi
jmp print_loop
print_end:
popa ;restore registers
mov esp,ebp ;clean the stack frame
pop ebp ;return to old stack frame
ret
skip2:
sub esi,ebx ; return the array to the original state
jmp skip1
You're horribly mixing 3 sizes!
1. Array of bytes
2. Values of dwords
3. Steps of qwords
Once you've decided what size to use beware of this code. In its current qword form it does an extra iteration! (use jl OuterLoop)
cmp ecx,96
jle OuterLoop
Why don't you use MOVZX to EAX in this line? It's much cleaner.
mov al ,byte[array+esi] ;set num to print in eax
The same applies to
mov bl,byte[len]
By putting mov esi,array right after OuterLoop: you can avoid that ugly detour via SKIP2.
I think I'm having trouble with my swap, and how I'm accessing the elements in my array. Right now, all of the code runs, but the list does not change after the sort. He's the high level sort I'm trying to implement
for(k=0; k<request-1; k++) {
i = k;
for(j=k+1; j<request; j++) {
if(array[j] > array[i])
i = j;
}
exchange(array[k], array[i]);
}
Here's the assembly code. Note: the assignment is about pushing and popping elements on/off the stack, so I can't change the parameters.
;this is a library with macros for things like printing numbers and strings
INCLUDE Irvine32.inc
MIN = 10 ;lower range limit
MAX = 200 ;upper range limit
LRANGE = 100
HRANGE = 999
.data
;title, intro, and prompts
title_1 BYTE "PROGRAMMING ASSIGNMENT 5: RANDOM GEN/SORT", 0dh, 0ah, 0
intro_1 BYTE "This program generates random numbers in the range (100 - 999),", 0dh, 0ah
BYTE "displays the original list, sorts the list, and calculates the median value.", 0dh, 0ah
BYTE "Finally, it displays the sorted list in descending order.", 0dh, 0ah, 0
prompt_1 BYTE "How many numbers should be generated? (10 - 200): ", 0
error_1 BYTE "Out of range.", 0dh, 0ah, 0
display_1 BYTE "List of random numbers: ", 0dh, 0ah, 0
display_2 BYTE "The median is: ", 0
display_3 BYTE "The sorted list: ", 0dh, 0ah, 0
;placeholders for user entries and calculated data
randArray DWORD MAX DUP(?)
userNum DWORD ? ;integer to be entered by user
;strings for posting results
goodBye_1 BYTE "Thank you for using the Gen/sort-ulator! Good-bye!", 0
.code
main PROC
call Randomize
;Title Screen
push OFFSET title_1
push OFFSET intro_1
call Intro
;Get and validate user numbers
push OFFSET error_1
push OFFSET prompt_1
push OFFSET userNum
call GetData
;Fill Array with random numbers
push OFFSET randArray
push userNum
call FillArray
;display unsorted results
push OFFSET randArray
push userNum
push OFFSET display_1
call DisplayList
;sort the results
push OFFSET randArray
push userNum
call SortList
;display the median
push OFFSET randArray
push userNum
push OFFSET display_2
call median
;display sorted results
push OFFSET randArray
push userNum
push OFFSET display_3
call DisplayList
;Say "goodbye"
push OFFSET goodBye_1
call Goodbye
exit ; exit to operating system
main ENDP
;-------------------------------------------------------
;Gives an Intro to the program
; Receives parameters on the system stack (in the order pushed):
; Address of the title
; Address of the intro
;post: intro displayed
;registers: none
;-------------------------------------------------------
Intro PROC
pushad
mov ebp, esp
mov edx, [ebp+40]
call writeString
call CrLf
mov edx, [ebp+36]
call writeString
call CrLf
popad
ret 8
Intro ENDP
;-------------------------------------------------------
;Prompts user for an integer, int stores in userNum
; Receives parameters on the system stack (in the order pushed):
; Address of the error message
; Address of the prompt
; Address of return value
;Post: userNum
;registers: none
;-------------------------------------------------------
GetData PROC
pushad
;setup stack and prompt for entry
mov ebp, esp
reenter:
mov edx, [ebp+40]
mov ebx, [ebp+36]
call WriteString
call ReadInt
;validate entry
cmp eax, MIN ;if eax < LOWER
jl badEntry ;jump to summary
cmp eax, MAX ;if eax > UPPER
jg badEntry ;reprompt
jmp goodEntry ;else jump to end, we have a good value
;bad entry reprompt
badEntry:
mov edx, [ebp+44]
call WriteString
jmp reenter
goodEntry:
call CrLf
mov [ebx], eax
popad
ret 12
GetData ENDP
;-------------------------------------------------------
;Fills array with a number of random integers within RANGE
;Recieves parameters on the system stack (in order pushed)
; array
; userNum
;Post: array is filled with userNum number of randoms
;Registers used: none
;-------------------------------------------------------
FillArray PROC
pushad
mov ebp, esp
mov ecx, [ebp+36] ;initialize loop counter with user entry
mov edi, [ebp+40] ;setup array offset
fillLoop:
call nextRand
add edi, 4
loop fillLoop
popad
ret 8
FillArray ENDP
;-------------------------------------------------------
; Procedure nextRand
; adapted from check lecture 20 solutions
; Procedure to get the next random number in the range specified by the user.
; Preconditions: LRANGE < HRANGE
; Registers used: eax, edi
;-------------------------------------------------------
nextRand PROC
mov eax, HRANGE
sub eax, LRANGE
inc eax ;add 1 to get the number of integers in range
call RandomRange
add eax, LRANGE ;eax has value in [LOW - HIGH]
mov [edi],eax
ret
nextRand ENDP
;-------------------------------------------------------
;Sorts the contents of an integer array
; Receives parameters on the system stack (in order pushed)
; Array
; Array Size
;registers: none
;-------------------------------------------------------
sortList PROC
pushad
mov ebp, esp
mov ecx, [ebp+36]
mov edi, [ebp+40]
dec ecx ;ecx < request-1
mov ebx, 0 ;ebx=k
;for(k=0; k<request-1; k++)
outerLoop:
mov eax, ebx ;eax=i=k
mov edx, eax
inc edx ;edx=j=k+1
push ecx
mov ecx, [ebp+36] ;ecx < request
;for(j=k+1; j<request; j++)
innerLoop:
mov esi, [edi+edx*4]
cmp esi, [edi+eax*4]
jle skip
mov eax, edx
skip:
inc edx
loop innerLoop
;swap elements
lea esi, [edi+ebx*4]
push esi
lea esi, [edi+eax*4]
push esi
call exchange
pop ecx
inc ebx
loop outerLoop
popad
ret 8
sortList ENDP
;-------------------------------------------------------
; Exchange k and i
; Receives parameters on the system stack (in order pushed)
; array[k]
; array[i]
;registers: none
;-------------------------------------------------------
Exchange PROC
pushad
mov ebp,esp
mov eax, [ebp+40] ;array[k] low number
mov ecx, [eax]
mov ebx, [ebp+36] ;array[i] high number
mov edx, [ebx]
mov [eax], edx
mov [ebx], ecx
popad
ret 8
Exchange ENDP
;-------------------------------------------------------
;Displays the median of an integer array
; Receives parameters on the system stack (in order pushed)
; Array
; Array Size
; display string
;registers: none
;-------------------------------------------------------
Median PROC
pushad
mov ebp, esp
mov edi, [ebp+44]
;display string
mov edx, [ebp+36]
call writeString
;calculate median element
mov eax, [ebp+40]
cdq
mov ebx, 2
div ebx
shl eax, 2
add edi, eax
cmp edx, 0
je isEven
;Array size is odd, so display the middle value
mov eax, [edi]
call writeDec
call CrLf
call CrLf
jmp endMedian
isEven:
;Array size is even so average the two middle values
mov eax, [edi]
add eax, [edi-4]
cdq
mov ebx, 2
div ebx
call WriteDec
call CrLf
call CrLf
endMedian:
popad
ret 12
Median ENDP
;-------------------------------------------------------
;Displays the contents of an integer array, 10 per row
; Receives parameters on the system stack (in order pushed)
; Array
; Array Size
; display string
;registers: none
;-------------------------------------------------------
DisplayList PROC
pushad
mov ebp, esp
;display string
mov edx, [ebp+36]
call writeString
call CrLf
mov ecx, [ebp+40]
mov edi, [ebp+44]
mov ebx, 0
;display array contents
listloop:
inc ebx ;counter for 10 items per row
mov eax, [edi]
call writeDec
add edi, 4
cmp ebx, 10
jne noReturn ;jump if 10 items are not yet printed
call CrLf
mov ebx, 0
jmp noTab ;this skips adding a tab on a new row
noReturn:
mov al, TAB
call writeChar
noTab:
loop listloop
call CrLf
popad
ret 12
DisplayList ENDP
;-------------------------------------------------------
;Says good-bye to the user
; Receives parameters on the system stack:
; Address of string
;registers: none
;-------------------------------------------------------
Goodbye PROC
pushad
mov ebp, esp
mov edx, [ebp+36]
call writeString
call CrLf
popad
ret 4
Goodbye ENDP
END main
In order to exchange elements you should be passing their address (in other words, a pointer to each). What you did was just swapping the values passed as parameters, which were also immediately freed. Your code is the equivalent of this C function:
void Exchange(int x, int y)
{
int eax = x;
int ebx = y;
x = ebx;
y = eax;
}
You need something like:
void Exchange(int* x, int* y)
{
int eax = *x;
int ebx = *y;
*x = ebx;
*y = eax;
}
In asm, that may look like:
Exchange PROC
pushad
mov eax, [esp+40]
mov ecx, [eax]
mov ebx, [esp+36]
mov edx, [ebx]
mov [eax], edx
mov [ebx], ecx
popad
ret 8
Exchange ENDP
To call this function you would use the following:
lea esi, [edi+ebx*4]
push esi
lea esi, [edi+eax*4]
push esi
call Exchange
Note your Exchange function had an instruction mov eax, [edi] which I couldn't make sense of.
Update: Yes, you can emulate the functionality of LEA by doing the calculations manually. For example, lea esi, [edi+ebx*4] becomes:
mov esi, ebx
shl esi, 2 ; esi=ebx*4
add esi, edi ; esi=edi+ebx*4