C float in NASM x86 assembly - c

In my university project i have to use binary representation of float number in x86 assembly for arithmetic operations. Using FPU is forbidden so i try to read float number and return it as DWORD but whatever i try to do i get "-nan". Any advices?
Edit:
I use gcc and it's 32 bit code
Declaration in C (i can't change that)
extern "C" float func(float num);
*.asm file
section .text
global func
func:
;prolog
push ebp
mov ebp, esp
; zapamiętanie rejestrów zachowywanych
push ebx
push esi
push edi
mov eax, DWORD [ebp+8]
;mov eax, 0xffffffff i checked that but i still get the same result
; odtworzenie rejestrów, które były zapamiętane
pop edi
pop esi
pop ebx
;epilog
pop ebp
ret
Example result (for 256)
01000011100000000000000000000000
11111111110000000000000000000000
num1: 256.000000
num2: -nan
Edit:
C code without checking bits part
#include <stdio.h>
extern "C" float func(float num);
int main()
{
float num1;
float num2;
scanf("%f", &num1);
num2=func(num1);
printf("num1: %f\nnum2: %f\n", num1, num2);
return 0;
}

If you declare the return type func as float the result will be returned in the FPU (ST0). For returning a value in EAX you have to declare it as an integer type. For printf you have to fake a float. Example:
caller.c:
#include <stdio.h>
#include <stdint.h>
extern float asmfunc1(float);
extern uint32_t asmfunc2(float);
int main (void)
{
printf ("asmfunc1: %f\n", asmfunc1(456.78));
uint32_t ifl = asmfunc2(123.45);
float* pfl = (float*) &ifl; // Faking a float
printf ("asmfunc2: %f\n", *pfl);
return 0;
}
callee.asm:
section .text
global asmfunc1, asmfunc2
asmfunc1:
fld dword [esp+4]
ret
asmfunc2:
push ebp
mov ebp, esp
mov eax, [ebp+8]
leave
ret
Build & run:
nasm -felf callee.asm
gcc -m32 callee.o caller.c
./a.out

In the 32 bit Linux ABI, float values are actually returned as long double at the top of the 8087 FP stack. You cannot return a float without using the FPU.
What you are probably restricted from doing is FP operations for addition, subtraction... But you still need to load the result in the FP stack to return it. In 64 bits mode, you would return float values as double in the xmm0 register.
Try changing the code to this:
section .text
global func
func:
push ebp
mov ebp, esp
flds 8(%ebp)
pop ebp
ret

Related

How to return an assembler value to a C Int Pointer?

I am writing a small ASM/C-Program for calculating the number of dividers of a number. I got the following C function:
#include <stdio.h>
extern void getDivisorCounter(int value, int* result);
int main(int argc, char** argv) {
int number;
printf("Please insert number:\n");
scanf("%d", &number);
int* result;
getDivisorCounter(number, result);
printf("amount of div: %d\n", *result);
return 0;
}
where I receive a result from the following assembler programm:
section .text
global getDivisorCounter
getDivisorCounter:
push ebp
mov ebp, esp
mov ecx, [ebp+8]
mov eax, 0
push ebx
for_loop:
mov ebx, ecx
jmp checking
adding:
add ebx, ecx
checking:
cmp ebx, [ebp+8]
jg looping
jl adding
inc eax
looping:
loop for_loop
mov [ebp+12], eax
pop ebx
pop ebp
ret
From Debugging, I know, that I end up with the right value in eax. But somehow I cannot get it to be printed by my C programm.
Could you give me a hint on how to solve this?
If neccessary, I am using NASM and GCC.
You do not need a pointer for this. Anyway, if you (or the assignment) insist, you must 1) initialize said pointer on the C side and 2) write through that pointer on the asm side.
E.g.
int value;
int* result = &value;
and
mov ecx, [ebp+12]
mov [ecx], eax
If you must use a pointer, this does not mean you need to create an extra pointer variable. You can just pass the address of a variable of proper type. This would eliminate the risk of missing memory allocation.
Missing memory allocation is the reason for your problem. result does not point to valid memory.
Instead of
int val;
int *result = &val; // <<== note the mandatory initialization of your pointer.
getDivisorCounter(number, result);
printf("amount of div: %d\n", val);
you could use this:
int result;
getDivisorCounter(number, &result);
printf("amount of div: %d\n", result);

Calling NASM 32bit SSE function in C

I'm trying to interface C and NASM to pass an array of float, make a few steps (subtract 2.0 to each element of the array) and then return the sum of the array result to C. The incorrect result printed is x = -34975412102996426752.000000
I don't understand why..
Thanks for help
This is the code of file (test.c and myfunc.nasm):
test.c:
extern float myfunc(float* a);
int main(int argc, char** argv) {
float a[] = { 7.0, 24.0, 4.0, 6.1 };
float x = myfunc(a);
printf("x: %f\n",x);
return 0;
}
myfunc.nasm:
%include "sseutils.nasm"
section .data
align 16
y: dd 2.0 , 2.0, 2.0, 2.0
section .bss
alignb 16
A: resd 4
section .text
global myfunc
a equ 8
myfunc:
push ebp
mov ebp, esp
push ebx
movaps xmm0 , [ebp+a]
movaps xmm1,[y]
subps xmm0, xmm1
haddpd xmm0,xmm0
haddpd xmm0,xmm0
movaps [A], xmm0
fld dword[A]
pop ebx
mov esp, ebp
pop ebp
ret

How safe is this swap w/o pushing registers?

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.

Calling Nasm function from C but return -nan

I'm learning assembly and how to call nasm function from C.
So i wrote a simple code just to try it:
This is my test.c file:
#include <stdio.h>
extern int test(int c);
int main(int argc, char ** argv){
int x = 10;
printf("Original value: %d\nUpdated value: %d\n",x,test(x));
return 0;
}
This is my test.nasm file:
; 32 bit
SECTION .text
global test ;unix
global _test ;windows
test:
_test:
push ebp
mov ebp, esp
push ebx
mov ebx , [ebp+8]
mov eax, ebx
sub eax, 2
pop ebx
mov esp, ebp
pop ebp
ret
As expected i get:
Original value: 10
Updated value: 8
But if i change test.c file as:
#include <stdio.h>
extern float test(float c);
int main(int argc, char ** argv){
float x = 10.0;
printf("Original value: %f\nUpdated value: %f\n",x,test(x));
return 0;
}
The output is:
Original value: 10.000000
Updated value: -nan
Could, please, someone explain why i get this and how should i correct it?
Thanks in advance.
Edit:
Thanks all for answers. I've edited the nasm file for the floating variables:
; 32 bit
SECTION .data
align 16
y: dd 2.0 , 2.0, 2.0, 2.0
SECTION .bss
alignb 16
A: resd 4
SECTION .text
global test ;unix
global _test ;windows
test:
_test:
push ebp
mov ebp,esp
push ebx
movss xmm0 , [ebp+8]
subps xmm0, [y]
movaps [A], xmm0
fld dword[A]
pop ebx
mov esp, ebp
pop ebp
ret
Now it works as expected. Is that code correct?
machine code generated by C compilers generally follows a specific calling protocol, depending on the function signature. For instance, on the X86-64 platform, functions will see their parameters placed in certain registers, depending on their types and numbers: integral types (int, long, char, etc.) will be placed in generic registers (RAX, RDX, RCX, etc), while floating point values will be passed in XMM registers. Likewise, values may be returned differently depending on the function return type.
In your case, you are targeting the IA-32 platform: the convention there is to return integral values through the x86 EAX register, and floating point values through the x87 ST0 register (parameters are still passed through the stack).
So, by changing the function signature, you are telling the compiler that test will no longer return its value the same way, and since your assembly code doesn't touch the floating point registers, you get as a result whatever was stored in there. Furthermore, subtracting the integer encoded value 2 to the 32bit floating point encoded value 10.0 (which turns out to be $41200000) will probably not yield what you would expect.

Using assembly routines with C on DOS

I've been playing with DOS real mode assembly for a while and now I want to utilize some routines in a C program. I'm using Turbo C 2.01 and TASM 3.0. I'm however unable to modify a variable passed by address, see the _setval routine below. I don't need/want inline assembly. A simple example:
foo.c
#include <stdio.h>
extern void setval(int *x, int *y);
extern int sum(int x, int y);
int main()
{
int result, a, b;
result = a = b = 0;
setval(&a, &b);
result = a + b;
printf("a+b=%i, a=%i, b=%i\n", result, a, b);
result = 0;
a = 42;
b = 19;
result = sum(a, b);
printf("a+b=%i, a=%i, b=%i\n", result, a, b);
return 0;
}
foortn.asm
public _setval
public _sum
.model small
.stack
.data
.code
_setval proc near
push bp
mov bp, sp
mov word ptr [bp+4], 42
mov word ptr [bp+6], 19
pop bp
ret
endp
_sum proc near
push bp
mov bp, sp
mov ax, word ptr [bp+4]
add ax, word ptr [bp+6]
pop bp
ret
endp
end
I compile it like this:
tcc -c -ms foo.c
tasm /ml foortn.asm
tcc foo.obj foortn.obj
The result is:
a+b=0, a=0, b=0
a+b=61, a=42, b=19
I'm obviously missing something, but what?
Hans, Mark and Bill, thank you very much for your prompt and helpful responses.
Your current code is overwriting the passed pointer. You need to retrieve the pointer and write through it. Something like this:
mov ax, word ptr [bp+4]
mov word ptr [ax], 42
Write this code in C first and look at the assembly code that it generates to get this right.
Try replacing:
mov word ptr [bp+4], 42
mov word ptr [bp+6], 19
with
mov bx, word ptr [bp+4]
mov [bx], 42
mov bx, word ptr [bp+6]
mov [bx], 19
This:
mov word ptr [bp+4], 42
mov word ptr [bp+6], 19
is writing to the stack, not the addresses on the stack. You'll need to read the addresses on the stack, then write to them instead:
mov bx,[bp+4] ; get the address of (a)
mov [bx],42 ; Write to that address
mov bx,[bp+6] ; (b)
mov [bx],19 ; write
I don't know assembler ... but C passes everything by value.
In sum [bp+4] is 42 (or 19); in addsetval [bp+4] is 0xDEADBEEF 0xDECAFBAD (or whatever)

Resources