Cheat Engine Forum Index Cheat Engine
The Official Site of Cheat Engine
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 


Floating Point x Integer Multiplication

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine
View previous topic :: View next topic  
Author Message
gibberishh
Cheater
Reputation: 1

Joined: 30 Aug 2021
Posts: 45

PostPosted: Thu Aug 03, 2023 10:41 pm    Post subject: Floating Point x Integer Multiplication Reply with quote

Need help with multiplying an integer by a floating point value to return an integer. The integer is in ecx (say #100). The floating point value is entered by the user in the table [label/symbol Mult, say (float)0.5].

Having read as much as I could absorb about FP values, I understand that both numbers need to be loaded into the stack [st(0),st(1)]. However I'm not 100% on how to do that. And how to be sure that after fmul the number I get back is an integer. Do I need fabs? I don't care about precise rounding. Also, I don't wish to do a multiply-divide cycle instead of using FP because I'm trying to learn FP instructions. Finally, it doesn't matter to me whether the code is for signed or unsigned values -- I'll take whatever you can give me but all my values will be positive (memrec will be unsigned unless you instruct me otherwise).

Online sources are confusing as hell and none of them seem to give a working example of code that I can reference Sad They just give slices that must be put together (in this case) by someone who has never written assembly for floating points.

I am currently not working with game code. I am doing all this in a newmem region that does not change game code (yes, I'm making sure it gets executed) to understand the instructions and concepts involved. I need 32-bit code. ecx will eventually be populated (and read) by game code.

Here's what I would like to do:
Code:
// setup constants
label(Mult)
registersymbol(Mult)
Mult:
  dd 0

mov ecx,#100
mov [Mult],float(0.5) // Mult is exposed to the table and can be changed by the user

// multiply the values // NEED HELP HERE
fild ecx
fld [Mult] // do I need to move [Mult] into a register before loading?
fmul st(0),st(1)
fist ecx // will this have the integer value from the resulting multiplication? or do I have to somehow convert it into integer first?
// how do I clear/pop st(0) and st(1) here?


Thanks!

_________________
It's not cheating. It's playing by my rules.
Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 152

Joined: 06 Jul 2014
Posts: 4724

PostPosted: Thu Aug 03, 2023 11:19 pm    Post subject: Reply with quote

Don't use x87... unless you're working on a really old game.

Basically, convert the integer to a floating point number, do the multiplication using floating point arithmetic, then convert the result back to an integer.
Code:
mult:
  dq (double)1

code:
  cvtsi2sd xmm0,ecx
  mulsd xmm0,[mult]
  cvttsd2si ecx,xmm0

_________________
I don't know where I'm going, but I'll figure it out when I get there.
Back to top
View user's profile Send private message
gibberishh
Cheater
Reputation: 1

Joined: 30 Aug 2021
Posts: 45

PostPosted: Fri Aug 04, 2023 12:09 am    Post subject: Reply with quote

Oh okay. I'll try this out. Online resources (apparently) gave me the wrong impression that xmm registers were for 64-bit.

Also, with this method, do I need to clear xmm0 in some particular way? Or will a simple push-pop work with xmm0 as it does with ecx?

Finally, I'll experiment with this but I'm assuming this will work with floats as well as doubles?

Yes, actually it is a new port of a very old game: The Hell 2, based on the original Hellfire code. That's why I want to work with floats and not doubles because of the 4-byte length of float values. Not sure if that will be pertinent: this whole part of the code will work independent of the game (only the part that reads and writes back to ecx will interact with game code) but I like to maintain consistent "data types" within a game's code.

Thanks again Smile

Edit: It works perfectly with double values. Obviously, the instructions are working with dqwords so they don't work with qword floats. I got this to work with floats instead, but couldn't have done it without your nudge in the right direction:
Code:
  mov [Result],ecx
  fild [Result] // apparently I cannot fild a register, only an address
  fld [Mult]
  fmulp st(1),st(0)
  fistp [Result]
  mov ecx,[Result]

Yes, it's x87 but I don't know of a more 'modern' way to do this with floats. Sorry.

_________________
It's not cheating. It's playing by my rules.
Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 152

Joined: 06 Jul 2014
Posts: 4724

PostPosted: Fri Aug 04, 2023 11:14 am    Post subject: This post has 1 review(s) Reply with quote

gibberishh wrote:
Online resources (apparently) gave me the wrong impression that xmm registers were for 64-bit.
64-bit applications use SSE (xmm registers) by default. They can also be used in 32-bit code. I'd suggest using whatever the game is using.

gibberishh wrote:
Also, with this method, do I need to clear xmm0 in some particular way? Or will a simple push-pop work with xmm0 as it does with ecx?
If the game is using xmm0, then use some other xmm register. e.g. use xmm5 or something.
If you really want to make sure you're safe, add some space on the stack (`sub esp,10`), write the entire xmm register there (`movups [esp],xmm0`), restore it later (`movups xmm0,[esp]`), and clean up the stack (`add esp,10`). This is usually unnecessary.

gibberishh wrote:
Finally, I'll experiment with this but I'm assuming this will work with floats as well as doubles?
Sure. `cvtsi2ss`, `mulss`, and `cvttss2si` are the corresponding mnemonics. There's no good reason to use floats, however.

gibberishh wrote:
Obviously, the instructions are working with dqwords so they don't work with qword floats.
Word is 2 bytes, DWord is 4 bytes, QWord is 8 bytes, DQWord is 16 bytes.
Floats are DWords, doubles are QWords, and XMM registers are DQWords.

gibberishh wrote:
That's why I want to work with floats and not doubles because of the 4-byte length of float values.
x87 floating point arithmetic is done using neither floats nor doubles. It uses an 80-bit double-extended floating point format.

These 80-bit values get stored in memory as either floats or doubles. I'd guess doubles are used more often. Just because the code is 32-bit doesn't mean everything is 4 bytes.
From the other aspect, 64-bit code still uses 4-byte integers as the default. Pretty much all the 8-byte integer values you see being used are pointers.

gibberishh wrote:
I like to maintain consistent "data types" within a game's code.
Do that with the floating point arithmetic unit: either SSE or x87. Don't bother with floats or doubles- there's practically no point.
Floats are mostly used in graphics where throughput is more vital than anything else. For CPU-related calculations, doubles are the default.

gibberishh wrote:
Code:
mov [Result],ecx
fild [Result] // apparently I cannot fild a register, only an address
fld [Mult]
fmulp st(1),st(0)
fistp [Result]
mov ecx,[Result]
`fild` can load an integer from a 16, 32, or 64-bit memory location. You should explicitly specify it in this case.
The stack is more appropriate than some arbitrary "Result" memory location, but that's just a superficial formality.
There's no need for the `fld`. `fmul` can take a memory location. Again, this memory location should specify its size, as it could be a float (dword) or a double (qword).
Code:
sub esp,4
mov [esp],ecx
fild dword ptr [esp]
fmul dword ptr [Mult]
fistp dword ptr [esp]
mov ecx,[esp]
add esp,4

Look at an instruction set reference when you need a more definitive answer as to what instructions do. There are plenty of accessible mirrors online:
https://www.felixcloutier.com/x86/

_________________
I don't know where I'm going, but I'll figure it out when I get there.
Back to top
View user's profile Send private message
gibberishh
Cheater
Reputation: 1

Joined: 30 Aug 2021
Posts: 45

PostPosted: Fri Aug 04, 2023 3:47 pm    Post subject: Reply with quote

Oh this is super enlightening. Thanks for such a detailed answer!

I'll use doubles for the calculations now that I understand what is happening internally. Especially since I am not sharing the double/float value itself with the game, only using it for calculations.

Unfortunately I still can't add to your rep Sad You're the only user I've previously repped and the forum keeps telling me to rep someone else before I can add to yours. Maybe I'll just find some post from Dark Byte to rep and come back here.

Grrr. Now I have to wait xxx seconds before I can rep again. This forum is prohibitively strict with reps. I'll come back to give the thumbs up when the timer resets Smile

_________________
It's not cheating. It's playing by my rules.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Powered by phpBB © 2001, 2005 phpBB Group

CE Wiki   IRC (#CEF)   Twitter
Third party websites