Which memory points can I actually use?
I have this assembly code to power(a,b) by recursion:
int power(int x, int y); *
;*****************************************************************************
%define x [ebp+8]
%define y [ebp+12]
power:
push ebp
mov ebp, esp
mov eax, y ;move y into eax
cmp eax, 0 ;compare y to 0
jne l10; ;if not equal, jump to l10
mov eax, 1 ;move 1 into eax
jmp l20; ;jump to l20, the leave, ret label
l10:
mov eax, y ; move y into eax
sub eax, 1 ; y-1
push eax ; push y-1 onto stack
mov ebx, x ; move x into ebx
push ebx ; push x onto stack
call power ; call power/recursion
add esp, 8 ; add 8 to stack(4*2) for the two vars
imul eax, x ; multiply the returned value from call by x
l20:
leave ; leave
ret ;ret
It's coded straight from this c code:
int power_c(int x, int y) {
if (y == 0) {
return 1;
} else {
return power_c(x, y - 1)*x;
}
}
The asm code works perfectly, any suggested adjustments would be great, I'm still new to assembly. My question is, which addresses can I actually use to define arguments? Here, I have two, and I use:
%define x [ebp+8]
%define y [ebp+12]
If I have more, do I just increase it? Lets say all are ints, 4bytes, like so?
%define x [ebp+8]
%define y [ebp+12]
%define z [ebp+16]
%define a [ebp+20]
%define b [ebp+24]
I've hit a snag with code where I need to define more arguments, I just can't figure this out, any help would be greatly appreciated.
Arguments are passed on the stack - the caller is responsible for pushing them. The space is already reserved when your function starts. If you changed your prototype to power_c(int x, int y, int z), then z would be at [ebp+16].
Local Variables (or automatics) are your responsibility. The standard way is to subtract the space you need from esp, as #Peter Cordes mentions in the comments. So to create variable a and b, you would do:
sub esp, 8
%define a [ebp+4]
%define b [ebp-8]
Note that at this point ebp == esp+8. We define the variables relative to ebp, not esp, so that you can continue to use push and pop instructions (which change the stack pointer). Remember to set esp back to ebp (mov esp, ebp) before you exit the function, so that the return address is found correctly.
Related
In the first loop I fill the array, then I want to print this array, but I'll have an error. It's Segmentation fault. I just change ecx register, since is my counter for _loop2.
extern printf
SECTION .bss
array resb 10
SECTION .data
fmt: db "array[%d] = %d", 10, 0 ; The printf format, "\n",'0'
SECTION .text
global main
main:
mov ecx, 0
_loop:
inc ecx
mov [array + ecx * 4], ecx
cmp ecx, 10
jnz _loop
mov ecx, 0
_loop2:
jmp print
add ecx, 1
cmp ecx, 10
jnz _loop2
ret
print:
;push ebp ; set up stack frame
;mov ebp, esp
push ecx
push dword [array + ecx * 4] ; value of variable a SECOND
push dword fmt ; address of ctrl string
call printf ; Call C function
add esp, 8 ; stack (4 * 2)
;mov esp, ebp ; takedown stack frame
;pop ebp ; same as "leave" op
mov eax,0 ; normal, no error, return value
;ret ; return
Several issues as mentioned in comments:
array resb 10 will reserve space for 10 bytes, but you want to store 10 dwords there (40 bytes). Change to array resd 10.
(Pointed out by Sep Roland) In _loop you have an off-by-one bug; since the inc is done before the mov you will access the dwords at [array+4], [array+8], ... [array+40], where the last one is out of range. This is like doing int array[10]; for (i=1; i <= 10; i++) array[i]=i; in C, and is incorrect for exactly the same reason. One fix would be to do mov [array + ecx * 4 - 4], ecx instead.
After _loop2 you have jmp print, which will transfer control to print and never come back. Since you apparently want to call print as a subroutine and continue executing with add ecx, 1 ; cmp ecx, 10, etc, you need to call print instead of jmp. And also uncomment the ret at the end of print so that it will actually return. Subroutines in assembly language don't automatically return unless you actually execute ret; otherwise the CPU will just continue executing whatever garbage happens to be next in memory.
You have a push ecx to save the value of ecx before the call to printf, which is good since printf will overwrite that register, but you need to pop ecx afterwards to get that value back and put the stack back to where it was.
Specifically, the pop ecx should follow the add esp, 8; a stack is a last-in-first-out structure, and the push ecx was before the pushing of the printf arguments, so you need to pop ecx after removing those arguments from the stack.
The mov eax, 0 as a return value at the end of print is unnecessary since you never use it anywhere else.
With these changes the code works as it should.
Summary: We have an int variable and 4 double arrays in C, 2 of which hold input data and 2 of which we want to write output data to. We pass the variable and arrays to a function in an external .asm file, where the input data is used to determine output data and the output data is written to the output arrays.
Our problem is, that the output arrays remain seemingly untouched after the assembly routine finishes its work. We don't even know, if the routine reads the correct input data. Where did we go wrong?
We compile with the following commands
nasm -f elf32 -o calculation.o calculation.asm
gcc -m32 -o programm main.c calculation.o
If you need any more information, feel free to ask.
C code:
// before int main()
extern void calculate(int32_t counter, double radius[], double distance[], double paper[], double china[]) asm("calculate");
// in int main()
double radius[counter];
double distance[counter];
// [..] Write Input Data to radius & distance [...]
double paper[counter];
double china[counter];
for(int i = 0; i < counter; i++) {
paper[i] = 0;
china[i] = 0;
}
calculate(counter, radius, distance, paper, china);
// here we expect paper[] and china[] to contain output data
Our Assembly code currently only takes in the values, puts them into the FPU, then places them into the output array.
x86 Assembly (Intel Syntax) (I know, this code looks horrible, but we're beginners, so bear with us, please; Also I can't get syntax highlighting to work correctly for this one):
BITS 32
GLOBAL calculate
calculate:
SECTION .BSS
; declare all variables
pRadius: DD 0
pDistance: DD 0
pPaper: DD 0
pChina: DD 0
numItems: DD 0
counter: DD 0
; populate them
POP DWORD [numItems]
POP DWORD [pRadius]
POP DWORD [pDistance]
POP DWORD [pPaper]
POP DWORD [pChina]
SECTION .TEXT
PUSH EBX ; because of cdecl calling convention
JMP calcLoopCond
calcLoop:
; get input array element
MOV EBX, [counter]
MOV EAX, [pDistance]
; move it into fpu, then move it to output
FLD QWORD [EAX + EBX * 8]
MOV EAX, [pPaper]
FSTP QWORD [EAX + EBX * 8]
; same for the second one
MOV EAX, [pRadius]
FLD QWORD [EAX + EBX * 8]
MOV EAX, [pChina]
FSTP QWORD [EAX + EBX * 8]
INC EBX
MOV [counter], EBX
calcLoopCond:
MOV EAX, [counter]
MOV EBX, [numItems]
CMP EAX, EBX
JNZ calcLoop
POP EBX
RET
There are a couple of problems in the assembler routine. The POP instructions are emitted into the .bss section, so they are never executed. In the sequence of POPs, the return address (pushed by the caller) is not accounted for. Depending on the ABI, you must leave the arguments on the stack anyway. Because the POPs are never executed, the loop exit condition always happens to be true.
And you really should not use global variables this way. Instead, create a stack frame and use that.
Thanks to all your answers and comments and some heavy research, we managed to finally produce functioning code, which now properly uses stack frames and fulfills the cdecl calling convention:
BITS 32
GLOBAL calculate
SECTION .DATA
electricFieldConstant DQ 8.85e-12
permittivityPaper DQ 3.7
permittivityChina DQ 7.0
SECTION .TEXT
calculate:
PUSH EBP
MOV EBP, ESP
PUSH EBX
PUSH ESI
PUSH EDI
MOV ECX, 0 ; counter for loop
JMP calcLoopCond
calcLoop:
MOV EAX, [EBP + 12]
FLD QWORD [EAX + ECX * 8]
MOV EAX, [EBP + 20]
FSTP QWORD [EAX + ECX * 8]
MOV EAX, [EBP + 16]
FLD QWORD [EAX + ECX * 8]
MOV EAX, [EBP + 24]
FSTP QWORD [EAX + ECX * 8]
ADD ECX, 1 ; increase loop counter
calcLoopCond:
MOV EDX, [EBP + 8]
CMP ECX, EDX
JNZ calcLoop
POP EDI
POP ESI
POP EBX
MOV ESP, EBP
POP EBP
RET
I'm trying to make an assembly power(a, b) function, based on this c code:
int power(int x, int y)
{
int z;
z = 1;
while (y > 0) {
if ((y % 2) == 1) {
y = y - 1;
z = z * x;
} else {
y = y / 2;
x = x * x;
}
}
return z;
}
Though, for some reason, it only gets some outputs right, and I can't figure out where the problem is. Here is my assembly code:
;* int power(int x, int y); *
;*****************************************************************************
%define y [ebp+12]
%define x [ebp+8]
%define z [ebp-4]
power:
push ebp
mov ebp,esp
sub esp,16
mov byte[ebp-4], 1
jmp L3;
L1:
mov eax,[ebp+12]
and eax,0x1
test eax,eax
je L2;
sub byte[ebp+12],1
mov eax,[ebp-4]
imul eax, [ebp+8]
mov [ebp-4],eax
jmp L3;
L2:
mov eax,[ebp+12]
mov edx,eax
shr edx,31
add eax,edx
sar eax,1
mov [ebp+12],eax
mov eax,[ebp+8]
imul eax,[ebp+8]
mov [ebp+8],eax
L3:
cmp byte[ebp+12],0
jg L1;
mov eax,[ebp-4]
leave
ret
When I run it, this is my output:
power(2, 0) returned -1217218303 - incorrect, should return 1
power(2, 1) returned 2 - correct
power(2, 2) returned -575029244 - incorrect, should return 4
power(2, 3) returned 8 - correct
Could someone please help me figure out where I've gone wrong, or to correct my code, any help would be appreciated.
You're storing a single byte, then reading back that byte + 3 garbage bytes.
%define z [ebp-4] ; what's the point of this define if you write it out explicitly instead of mov eax, z?
; should just be a comment if you don't want use it
mov byte[ebp-4], 1 ; leaves [ebp-3..ebp-1] unmodified
...
mov eax, [ebp-4] ; loads 4 bytes, including whatever garbage was there
Using a debugger would have shown you that you were getting garbage in EAX at this point. You could fix it by using movzx eax, byte [ebp-4], or by storing 4B in the first place.
Or, better, don't use any extra stack memory at all, since you can use ECX without even saving/restoring it in the usual 32-bit calling conventions. Keep your data in registers, that's what they're for.
Your "solution" of using [ebp+16] is writing to stack space owned by the caller. You're just getting lucky that it happens to have the upper 3 bytes zeroed, I guess, and that clobbering it doesn't lead to a crash.
and eax,1
test eax,eax
is redundant: test eax, 1 is sufficient. (Or if you want to destroy eax, and eax, 1 sets flags based on the result).
There's a huge amount of other inefficiency in your code, too.
I'm very new to Assembly and the code below is supposed to swap two integers via two different functions: first using swap_c and then using swap_asm.
However, I doubt, whether I need to push (I mean save) each value of registers before assembly code and pop them later (just before returning to main). In other words, will the CPU get mad at me if I return different register content (not the crucial ones like ebp or esp; but, just eax, ebx, ecx & edx) after running swap_asm function? Is it better to uncomment the lines in the assembly part?
This code runs OK for me and I managed to reduce the 27 lines of assembled C code down to 7 Assembly lines.
p.s.: System is Windows 10, VS-2013 Express.
main.c part
#include <stdio.h>
extern void swap_asm(int *x, int *y);
void swap_c(int *a, int *b) {
int t = *a;
*a = *b;
*b = t;
}
int main(int argc, char *argv[]) {
int x = 3, y = 5;
printf("before swap => x = %d y = %d\n\n", x, y);
swap_c(&x, &y);
printf("after swap_c => x = %d y = %d\n\n", x, y);
swap_asm(&x, &y);
printf("after swap_asm => x = %d y = %d\n\n", x, y);
getchar();
return (0);
}
assembly.asm part
.686
.model flat, c
.stack 100h
.data
.code
swap_asm proc
; push eax
; push ebx
; push ecx
; push edx
mov eax, [esp] + 4 ; get address of "x" stored in stack into eax
mov ebx, [esp] + 8 ; get address of "y" stored in stack into ebx
mov ecx, [eax] ; get value of "x" from address stored in [eax] into ecx
mov edx, [ebx] ; get value of "y" from address stored in [ebx] into edx
mov [eax], edx ; store value in edx into address stored in [eax]
mov [ebx], ecx ; store value in ecx into address stored in [ebx]
; pop edx
; pop ecx
; pop ebx
; pop eax
ret
swap_asm endp
end
Generally, this depends on the calling convention of the system you are working on. The calling convention specifies how to call functions. Generally, it says where to put the arguments and what registers must be preserved by the called function.
On i386 Windows with the cdecl calling convention (which is the one you probably use), you can freely overwrite the eax, ecx, and edx registers. The ebx register must be preserved. While your code appears to work, it mysteriously fails when a function starts to depend on ebx being preserved, so better save and restore it.
I am trying to make an assembly program for finding GCD, takes in two integers, and prints out the GCD. The code assembles fine, however the program gets stuck in an infinite loop after both integers have been submitted:
;Title - GCD
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
; Computes the absolute value of a number.
; Receives: eax = the number
; Returns: eax = absolute value of the number
;-----------------------------------------------
cmp eax, 0 ; see if we have a negative number
jge done
neg eax
done:
ret
abs ENDP
;-----------------------------------------------
gcd PROC
; Finds Greatest Common Divisor of two integers
; Recieves: eax= int A , ebx = int B
; Returns eax = GCD
;-----------------------------------------------
call abs ;takes absolute value of both registers
mov temp, eax
mov eax, ebx
call abs
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
How can I get out of this loop?
I see one problem straight up:
call abs ;takes absolute value of both registers
mov temp, eax
mov eax, ebx
call abs
mov eax, temp
There's nothing there moving the abs-ed ebx value back into ebx, it should be:
call abs ;takes absolute value of both registers
mov temp, eax
mov eax, ebx
call abs
mov ebx, eax
mov eax, temp
Another issue, though not directly related to your problem, is that the x86 architecture has long had an xchg instruction that would greatly clean up your code, specifically the use of temp.
And think about this sequence:
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
You'll find that the code at SWITCH: is going to be run regardless of which value is larger.
You can fix that by changing:
jc SWITCH ; swaps if ebx is larger then eax
mov edx, 0
into:
jc SWITCH ; swaps if ebx is larger then eax
mov edx, 0
jmp L1
Beyond that, I'm going to suggest actually running that code in your head to get an understanding of how it works but, more importantly, how to think like a machine in such a way that you become a better developer.
Start with the following table:
[temp] eax ebx edx <stack>
-------- -------- -------- -------- --------
? ? ? ? ?,
Then execute each line in turn, filling in the columns as data changes.
You'll eventually figure out where the problem is coming from and you'll also understand why sometimes the older guys that only ever had this means to debug are much better at it :-)
I'll give you a clue. Keep a particular eye on edx and see how that's changing, and how it's likely to affect certain other instructions, like div, that may depend on it being zero.