NASM - The Netwide Assembler
NASM Forum => Using NASM => Topic started by: nobody on December 06, 2008, 06:02:00 PM
-
Hi
I'm obviously missing something here.
I have two C functions (int64val & float64val) that, given an address, are supposed to return the 64-bit contents of that address. The int64val function returns the correct value (printed in hex for consistency) but the float64val does not. I know that 0x40eec30000000000 is the correct answer for the float64val because I did the IEEE math on it to determine the value:
0100000011101110110000110000000000000000000000000000000000000000
0 10000001110 1110110000110000000000000000000000000000000000000000
S E F
S=0
E=1038
E-1023=15
(-1)**S = (-1)**0 = 1
2**(E-127)=2**15=32768
1.F=1.1110110000110000000000000000000000000000000000000000 = 1.922607422
32768*1.922607422=63000
So, why doesn't float64val give me what I need? And, is it possible to write a Nasm function that WILL work?
Compilations, output, and source listings below.
Michael
=======================================
[michael@localhost Slip]$ gcc -c callint64val.c -o callint64val.o
[michael@localhost Slip]$ gcc -c int64val.c -o int64val.o
[michael@localhost Slip]$ gcc callint64val.o int64val.o -o callint64val
[michael@localhost Slip]$ ./callint64val
0x0000000000000018
0x0000000000000018
[michael@localhost Slip]$ gcc -c callfloat64val.c -o callfloat64val.o
[michael@localhost Slip]$ gcc -c float64val.c -o float64val.o
[michael@localhost Slip]$ gcc callfloat64val.o float64val.o -o callfloat64val
[michael@localhost Slip]$ ./callfloat64val
0x40eec30000000000
0xc1d00f29e0000000
[michael@localhost Slip]$ cat callint64val.c
#include
int main() {
long long int k,m;
k = 24;
printf("%#018llx\n", k);
m = int64val(&k);
printf("%#018llx\n", m);
}
[michael@localhost Slip]$ cat int64val.c
long long int int64val(void *k) {
long long int *m;
m = (long long int *) k;
return *m;
}
[michael@localhost Slip]$ cat callfloat64val.c
#include
int main() {
double a,b;
a = 63.e3;
printf("%#018llx\n", a);
b = float64val(&a);
printf("%#018llx\n", b);
}
[michael@localhost Slip]$ cat float64val.c
double float64val(void *k) {
double *m;
m = (double *) k;
return *m;
}
[michael@localhost Slip]$
-
Hi Michael,
If, in callfloat64val.c, you prototype float64val as returning double, it magically works!
#include
double float64val (double *);
int main() {
double a,b;
a = 63.e3;
printf("%#018llx\n", a);
printf("%f\n", a);
b = float64val(&a);
printf("%#018llx\n", b);
printf("%f\n", b);
}
I figured out from a disassembly that callfloat64val wasn't treating the return from float64val right (like it was returning a single precision float in eax). On a hunch, I tried this, and it worked. Dunno why.
My "asmfloat64val.asm":
global float64val
section .text
float64val:
mov eax, [esp + 4]
fld qword [eax]
ret
also seems to work. This seems to confirm my suspicion that gcc expects a double to be returned on the FPU stack, not in edx:eax like a "long long int". I thought a single precision float would be returned on the FPU stack also, but this may not be true - more experimentation needed!
Best,
Frank
-
Thanks, Frank.
I was hoping not to have to let my main() know what my functions are doing, but may have to rethink that. Let me know if you come up with something in Nasm that lets me circumvent the prototype. Also, this stuff doesn't have to be portable.
MIchael
-
Well... you could let main() guess, look to see what gcc made of it, and write a called function to suit. Guessing on both ends hasn't been working out well. :)
A compiler needs to know about "types" and stuff, since it has to make up the instructions. An assembler doesn't need to know, since we're telling it the instructions. But *somebody* has to know!
Easiest way is to bit-bucket the C and write the caller in asm, too. That way, there's no guessing. (if there's an error, it's in the assembly part :)
Many people hold a different opinion.
Best,
Frank
; nasm -f elf asmcallfloat64.asm
; link with previously assembled or compiled .o file
; ld -o asmcallfloat64 asmcallfloat64.o asmfloat64.o
; or ld -o asmcallfloat64 asmcallfloat64.o float64.o
global _start
extern float64val
section .data
a dq 63.0e3
hnl db "h", 10
hnl_len equ $ - hnl
word_10 dw 10 ; multiplier for the float-to-ascii routine
section .bss
numbuf resb 80
floatbuf resq 1
bcdbuf rest 1
section .text
_start:
; call our function with "&a"
push a
call float64val
add esp, 4
; result is top of FPU stack - store it
fst qword [floatbuf]
; print it in hex
mov eax, [floatbuf + 4]
call showeaxh
mov eax, [floatbuf]
call showeaxh
; print "h" and a newline
mov ecx, hnl
mov edx, hnl_len
call write_stdout
; result is still on top of FPU stack - convert to ascii
mov edi, numbuf ; buffer for string
mov ecx, 6 ; number of decimal places
call ftoa
; print it
mov ecx, numbuf
; zero-terminated string - we need length
or edx, byte -1
.getlen:
cmp byte [ecx + edx + 1], 1
inc edx
jnc .getlen
call write_stdout
; fudge another newline - skip the "h" :)
mov ecx, hnl + 1
mov edx, 1
call write_stdout
; exit
xor ebx, ebx ; no error (we claim)
mov eax, 1 ; __NR_exit
int 80h
;----------------------
;-----------------------------------------------------
; ftoa - converts floating point to string
;
; Based on some code "sponged" from a post to clax
; From: "Jim Morrison"
;
; Expects: Number to convert is on stack top - st(0)
; edi points to buffer to store string
; ecx = Decimal Places.
;-------------------------------------------------------------
ftoa:
push eax
push ecx
push edx
push edi
push esi
mov edx, ecx ; save a copy of dec places
; if no decimal (integer)
jcxz .f2a2 ; skip multiply by ten loop
.f2a1: ; else loop to "scale" number
fimul word [word_10]
loop .f2a1
.f2a2:
fbstp [bcdbuf] ; convert to bcd and store
mov esi, bcdbuf ; we'll pull digits from there
mov ecx, 9
.f2a3:
lodsb ; get a pair of digits
mov ah, al ; move a copy to ah
shr ah, 4 ; shift out low nibble, keeping high
and eax, 0F0Fh ; mask out the digits we want
add eax, 3030h ; convert 'em both to ascii
push eax
mov al, ah ; swap and store the other digit
push eax
loop .f2a3 ; until done
cmp byte [bcdbuf+9], 0 ; sign flag at bcdbuf + 9 ?
je .f2a6
mov al, '-' ; minus sign if we need it
stosb ; store it at front of our string
.f2a6:
mov ecx, 18
xor dh, dh
inc dl
.poploop:
pop eax
cmp al, '0'
jnz .store
or dh, dh
jnz .store
jmp short .nostore
.store:
stosb
inc dh
.nostore:
cmp cl, dl
jne .nopoint
or dh, dh ; if we haven't encountered a non-zero
jnz .nolz
mov al, '0' ; put a zero before the decimal point
stosb
.nolz:
mov al, '.' ; decimal point
stosb ; and store it
inc dh
.nopoint:
loop .poploop
mov al, 0
stosb
pop esi
pop edi
pop edx
pop ecx
pop eax
ret
;--------------------------------------------------
;---------------------------
write_stdout:
push ebx
mov ebx, 1
mov eax, 4
int 80h
pop ebx
ret
;----------------------------
;------------------------------
showeaxh:
push eax
push ebx
push ecx
push edx
sub esp, 10h
mov ecx, esp
xor edx, edx
mov ebx, eax
.top:
rol ebx, 4
mov al, bl
and al, 0Fh
cmp al, 0Ah
sbb al, 69h
das
mov [ecx + edx], al
inc edx
cmp edx, 8
jnz .top
mov ebx, 1
mov eax, 4
int 80h
add esp, 10h
pop edx
pop ecx
pop ebx
pop eax
ret
;------------------------------