Hi everyone. I was just wondering how to load a constant onto the FPU? For example, I would like to do something equivelent to this....

fld 0.1

or how can I multiply by constants...

fmul 31.45

Is there a better way than creating a temporary variable and loading it? Thanks for any help.
Posted on 2001-11-08 05:32:30 by AlexEiffel
The only better way is for a couple of pre defined constants

fldpi Load Pi
fld1 Load 1.0
fldz Load 0.0

Check out the FP help file with Masm32 for the rest of them.

I assume however that this isn't much help as you want to load your own constants.
Posted on 2001-11-08 09:28:05 by Eóin
Load the constant from a memory location and then do the operation with the value loaded on the stack. Guess you could even use a macro to store the constant and load it onto the stack. I'll work on it when I'm at home later...
Posted on 2001-11-08 10:23:38 by bitRAKE
Heres one possible macro:

fldr4 MACRO Val:VARARG
LOCAL ddVal
.data
ddVal dd Val
.code
fld ddVal
EndM

Usage is simply fldr4 123.456 and then st(0) = 123.456. Of course you may prefeer to used QWORDs or even TBYTEs for better accuracy.
Posted on 2001-11-08 13:00:58 by Eóin
I was thinking more of an inline macro that so you could just have one line of code that produces two instructions. :) Something like:

fmul fpconst(2.5)
fadd st(3),fpconst(2.35813)
Posted on 2001-11-08 13:25:42 by bitRAKE
Surely it would have to generate one entry in the data section and one line of code.

And if you could do that it would be incredibly useful.
Posted on 2001-11-08 15:06:28 by Eóin
Hello Alex !

Also this is a way to store / use real4-values immediately:



db 068h ; PUSH imm32
dd aFlt


But you have to correct the stack-pointer after using this value by



add esp, 4


Greetings, CALEB
Posted on 2001-11-08 17:37:20 by Caleb
Thanks everyone. Hey Caleb, could you explain what your code does please? I don't quite see how it works and what the db 068h means.
Posted on 2001-11-09 00:25:26 by AlexEiffel
Pretty easy :) only took a couple seconds actually.
fpc MACRO val:REQ

LOCAL y

CONST SEGMENT DWORD PUBLIC 'DATA'
y val
CONST ENDS

fld y
EXITM <st>
ENDM

fadd st(1),fpc(REAL4 5.3521)
fadd st(2),fpc(REAL8 7.3389)
fadd st(3),fpc(REAL10 1.11255297)
It'd be harder, but fun - to make the macro keep track of used constants. So, as to not duplicate the storage in the data section. Who cares about program size these days, though. :) You could create separate macros for REAL4, REAL8, REAL10, of course. (Thanks, E?in for the code. :))
Posted on 2001-11-09 00:57:51 by bitRAKE
bitRAKE, what do you mean by

So, as to not duplicate the storage in the data section. Who cares about program size these days, though.


does your macro set aside memory every time it is called? I'm using this in a timer, so it will be called a few hundred times a second with a simple program.
Posted on 2001-11-09 01:17:55 by AlexEiffel
Hello Alex !

The data-byte db 068h defines the opcode for push dword value, this means that the float-const will be pushed onto the stack. After that you can load it with fld dword ptr onto the fpu-stack.

Also it is quite usefull if real4-values are demanded by subroutines as parameters.

Bye, CALEB
Posted on 2001-11-09 16:33:21 by Caleb
The macro only sets aside space in the DATA segment each time it is used. For example, if you used the macro twice with the same value, there would be two occurances of that constant in the DATA segment - instead of referencing the same constant. This is only a personal gripe of mine because HLL's will consolidate duplicate constants within a program, but it is very difficult to do the same in assembler.
Posted on 2001-11-09 17:47:08 by bitRAKE
I create the macro with the feature I mentioned above: not duplicating data in the program. I've also add a feature where the data defaults to REAL4 type, and intergers can be specified.
fpc MACRO val:REQ

LOCAL w,x,y,z

;; split type and value, defaulting to REAL4
z INSTR 1,<&val>,<! >
IF z EQ 0
y TEXTEQU <REAL4>
x TEXTEQU <&val>
ELSE
y TEXTEQU @SubStr(<&val>,1,z-1) ;; Type
x TEXTEQU @SubStr(<&val>,z+1,) ;; Value
ENDIF

;; replace . with _
z INSTR 1,x,<!.>
IF z EQ 0
w TEXTEQU x
x CATSTR x,<.0> ;; prevent error message
ELSE
w CATSTR @SubStr(%x,1,z-1),<_>,@SubStr(%x,z+1,)
ENDIF

;; figure out global name for constant
z SIZESTR y ;; use last char for size distiction
w CATSTR <__>,w,<r>,@SubStr(%y,z,1)

IF (OPATTR(w)) EQ 0 ;; not defined
CONST SEGMENT
w y x
CONST ENDS
ENDIF
EXITM w
ENDM
Please, ask questions if anyone has a hard time following what's going on. This macro is used differently than the ones above to make it more usable. :) If you have problems using this macro with other macros or certain coding situations, I'd like to fix it - please let me know. Here are some examples of it's use:
fld fpc(20) ; defaults to REAL4 20.0

fld fpc(REAL8 2.5)
fld fpc(2.5) ; REAL4

mcall [g_device], IDirect3DDevice8_Clear, 0, 0, D3DCLEAR_ZBUFFER, 0, fpc(1), 0
fld fpc(1) ; this uses the same constant used above
Posted on 2001-11-17 02:58:59 by bitRAKE
I am an oldschooler, I admit it, but I noticed not a soul has mentioned Fixed-Point Math, that old heresy that went before we had FPU, and its very simple and very fast in asm.

To put it in moronically simple terms, we are trying to deal with numbers smaller than one, ie fractions.
To store them in an integer-based environment, I will give the modern example here of 16:16 FPM, we simply multiply every number in our equation by a fixed factor, in this case, we will be multiplying by 65536, so that our 32 bit integer representation of a fractional number will be maximum 65535.65535, where the lower 16 bits represent a fraction of 65536, ie one half would be 0.32768, or 00008000 as a dword.
We convert any number we want to use in a math formula into this 16:16 FPM format before we do the math, then we simply divide the answer by our factor to convert it back into regular old integer form. Note the multiplying and dividing is done using binary shift opcodes, SHL and SHR, making it very fast.
The resolution on fractions is 1/65536, or 0.0000152587890625 being the smallest number recordable using this system, and being the incremental resolution of the math. If you expect the answer to fall between that number and 65536, then this may be for you.
In the olde dayes of 8-bit machinecode (16-bit addressing), we'd merely use a factor of 256, ie an 8-bit shift, but that didn't give the greatest resolution, fractions of 256... I still use this today on Pentium-based 32-bit machines in preference to FPU for the overall saving in terms of cpu time I can achieve due to remaining loyal to binary math and low-level logic. :)
Posted on 2001-11-17 06:01:10 by Homer
EvilHomer2k, the FPU used to be a lot slower - that isn't the case any more. Both techniques can be used together even. Which is especially useful when your trying to interface an API that uses REAL4 numbers.
Posted on 2001-11-17 13:13:14 by bitRAKE
bitRAKE, wonderful MACRO. For work with the FPU its a God send, thank you.

However, for use with invoke its not the best solution. A guy called serge coban wrote a couple of MACROs for gl which allowed you to push Real4 and 8 values as immediates:

GLfloat		TYPEDEF		REAL4

GLclampf TYPEDEF REAL4 ; float clamped to [0,1]
GLdouble TYPEDEF REAL8
GLclampd TYPEDEF REAL8 ; double clamped to [0,1]

gl_fpush MACRO numb
LOCAL prmstr,prmlen,x,n
prmstr EQU <numb>
prmlen SIZESTR prmstr
IF (prmlen LE 7)
;; constant or varname
x SUBSTR prmstr,prmlen,1
IFIDNI x,<f>
x SUBSTR prmstr,1,prmlen-1
push 12345678h
ORG $-4
real4 &x
ELSE
push GLfloat ptr prmstr
ENDIF
ELSE
x SUBSTR prmstr,1,7
IFIDNI x,<(float)>
x SUBSTR prmstr,8
n=&x
x TEXTEQU %n
x CATSTR x,<.0>
push 12345678h
ORG $-4
real4 &x
ELSE
x SUBSTR prmstr,prmlen,1
IFIDNI x,<f>
x SUBSTR prmstr,1,prmlen-1
push 12345678h
ORG $-4
real4 &x
ELSE
push GLfloat ptr prmstr
ENDIF
ENDIF
ENDIF
ENDM


; version without auto-convertion
_gl_fpush MACRO numb
LOCAL prmstr,prmlen,x
prmstr EQU <numb>
prmlen SIZESTR prmstr
x SUBSTR prmstr,prmlen,1
IFIDNI x,<f>
x SUBSTR prmstr,1,prmlen-1
push 12345678h
ORG $-4
real4 &x
ELSE
push GLfloat ptr prmstr
ENDIF
ENDM



; ugly, ugly, ugly... but yet works (from time to time)
IF 0 ; this version is more stable, but generated code much more close to insane
gl_dpush MACRO numb
LOCAL loc1,dat,prmstr,prmlen,x
prmstr EQU <numb>
prmlen SIZESTR prmstr
x SUBSTR prmstr,prmlen,1
IFIDNI x,<f>
x SUBSTR prmstr,1,prmlen-1
jmp loc1
ALIGN DWORD
dat real8 &x
loc1:
push dword ptr dat[4]
push dword ptr dat
ELSE
x CATSTR prmstr,<[4]>
push dword ptr x
push dword ptr prmstr
ENDIF
ENDM

ELSE

gl_dpush MACRO numb
LOCAL prmstr,prmlen,x
prmstr EQU <numb>
prmlen SIZESTR prmstr
x SUBSTR prmstr,prmlen,1
IFIDNI x,<f>
x SUBSTR prmstr,1,prmlen-1
real8 &x
db 68h
real8 &x
ORG $-8-1-8
db 8Dh,40h,0 ; lea eax,[eax+0]
db 68h
ORG $+4+1+4
ELSE
x CATSTR prmstr,<[4]>
mov eax,eax
mov ebx,ebx
push dword ptr x
push dword ptr prmstr
ENDIF
ENDM

ENDIF

When used as follows you'll see the advantage:
gl_fpush 67.7f

push fpc(67.7)
Produces
004010FA 6866668742             push    42876666h

004010FF FF3540204000 push dword ptr [402040h]

Unfortunatly all is not rosey with these, Following is an instance where normal code produces bizarre results:
OpenEye MACRO 

gl_dpush 0.0f
gl_dpush 1.0f
gl_dpush 0.0f

[COLOR=darkred] fld Eye.z
fadd Eye.d.z
fstp tdq[/COLOR]
gl_dpush tdq
fld Eye.y
fadd Eye.d.y
fstp tdq
gl_dpush tdq
fld Eye.x
fadd Eye.d.x
fstp tdq
gl_dpush tdq

gl_dpush Eye.z
gl_dpush Eye.y
gl_dpush Eye.x

mov eax, eax
mov ecx, ecx
call gluLookAt
ENDM

compiles to

00403363 8D4000 lea eax,[eax]
00403366 6800000000 push 0
0040336B 6800000000 push 0
00403370 8D4000 lea eax,[eax]
00403373 680000F03F push 3FF00000h
00403378 6800000000 push 0
0040337D 8D4000 lea eax,[eax]
00403380 6800000000 push 0
00403385 6800000000 push 0[COLOR=darkred]
0040338A DD0510000000 fld qword ptr [10h]
00403390 775A ja loc_004033EC
00403392 035540 add edx,[ebp+40h]
00403395 00D9 add cl,bl
00403397 1D78554000 sbb eax,405578h[/COLOR]
0040339C 8BC0 mov eax,eax
0040339E 8BDB mov ebx,ebx
004033A0 FF357C554000 push dword ptr [40557Ch]
004033A6 FF3578554000 push dword ptr [405578h]
004033AC DD05A3544000 fld qword ptr [4054A3h]
004033B2 DC05BB544000 fadd qword ptr [4054BBh]
004033B8 D91D78554000 fstp dword ptr [405578h]
004033BE 8BC0 mov eax,eax
004033C0 8BDB mov ebx,ebx
004033C2 FF357C554000 push dword ptr [40557Ch]
004033C8 FF3578554000 push dword ptr [405578h]
004033CE DD059B544000 fld qword ptr [40549Bh]
004033D4 DC05B3544000 fadd qword ptr [4054B3h]
004033DA D91D78554000 fstp dword ptr [405578h]
004033E0 8BC0 mov eax,eax
004033E2 8BDB mov ebx,ebx
004033E4 FF357C554000 push dword ptr [40557Ch]
004033EA FF3578554000 push dword ptr [405578h]
004033F0 8BC0 mov eax,eax
004033F2 8BDB mov ebx,ebx
004033F4 FF35AF544000 push dword ptr [4054AFh]
004033FA FF35AB544000 push dword ptr [4054ABh]
00403400 8BC0 mov eax,eax
00403402 8BDB mov ebx,ebx
00403404 FF35A7544000 push dword ptr [4054A7h]
0040340A FF35A3544000 push dword ptr [4054A3h]
00403410 8BC0 mov eax,eax
00403412 8BDB mov ebx,ebx
00403414 FF359F544000 push dword ptr [40549Fh]
0040341A FF359B544000 push dword ptr [40549Bh]
00403420 8BC0 mov eax,eax
00403422 8BC9 mov ecx,ecx
00403424 E877070000 call fn_00403BA0


I can't understand why the affected code is after the MACRO. Note that if this code is rearranged i.e. the three initial gl_dpushes are moved to just prior to the last three (the Eye ones) it compiles fine and generates the code you'd expect, sadly than the values are out of order on the stack.

Just some bedtime reading :grin:
Posted on 2001-11-17 19:08:35 by Eóin
E?in, that error is because of the way org $-4 is used within the macro to back over code -- this is bad. I have found that this messes with the internal lists within MASM - mainly code relocation. Use:
db 068h

REAL4 val
...instead. Pushing a REAL8 is even worse. There is no way around this that I know of - this is a limitation of MASM - org is bad to use for this. If I figure out a solution I'll surely post it.

As the FPU can't load immediate data, this macro is mainly for use directly with the FPU instructions. I'm working on my own version of mcall - it'll have as many features as I can pack in. :)
Posted on 2001-11-18 00:35:05 by bitRAKE
BitRake
The macro that you have posted (fpc) has a bug, it does not work with negative values.

Thus:

fld fpc(1.0)

will work

but

fld fpc(-2.0)

will not.

The following is your macro with the correction for negative numbers.



fpc MACRO val:REQ
LOCAL w,x,y,z,zz,ww

;; split type and value, defaulting to REAL4
z INSTR 1,<&val>,<! >
IF z EQ 0
y TEXTEQU <REAL4>
x TEXTEQU <&val>
ELSE
y TEXTEQU @SubStr(<&val>,1,z-1) ;; Type
x TEXTEQU @SubStr(<&val>,z+1,) ;; Value
ENDIF

;; replace . with _
z INSTR 1,x,<!.>
IF z EQ 0
w TEXTEQU x
x CATSTR x,<.0> ;; prevent error message
ELSE
w CATSTR @SubStr(%x,1,z-1),<_>,@SubStr(%x,z+1,)
ENDIF

;; replace - with _
zz INSTR 1,w,<!->
IF zz EQ 0
ww TEXTEQU w
ELSE
ww CATSTR @SubStr(%w,1,zz-1),<_>,@SubStr(%w,zz+1,)
ENDIF

;; figure out global name for constant
z SIZESTR y ;; use last char for size distiction
ww CATSTR <__>,ww,<r>,@SubStr(%y,z,1)

IF (OPATTR(ww)) EQ 0 ;; not defined
CONST SEGMENT
ww y x
CONST ENDS
ENDIF
EXITM ww
ENDM



So that a -2.0 becomes ___2_0_

PS: I know that it has been a long time since you posted the macro, but I just found the bug today.
Posted on 2001-12-22 21:30:03 by dxantos
Thanks, nice to know someone actually uses these things. :grin:
Posted on 2001-12-22 21:45:28 by bitRAKE

E?in, that error is because of the way org $-4 is used within the macro to back over code -- this is bad. I have found that this messes with the internal lists within MASM - mainly code relocation. Use:
db 068h

REAL4 val
...instead. Pushing a REAL8 is even worse. There is no way around this that I know of - this is a limitation of MASM - org is bad to use for this. If I figure out a solution I'll surely post it.

As the FPU can't load immediate data, this macro is mainly for use directly with the FPU instructions. I'm working on my own version of mcall - it'll have as many features as I can pack in. :)


I wander if anyone found solution to push real8?
Posted on 2003-04-05 15:10:55 by The Svin