Hi, Im new here. :bg
Im taking an assembly class and the code I need to do is very complex (to me) :eek
TITLE Power in Floating Point Values
; This code is supposed to n1absolute value(n2) and store it in power
; I've managed to declare, initialize float variables and print them successfully
.386
.model flat, stdcall
option casemap :none
include \masm32\include\masm32rt.inc
.data
; all these variables used to be SDWORD
n1 real8 0.0 ; declaring and initializing a floating-point value with 8 bytes
n2 real8 0.0 ; same^
power real8 1.0 ; same^
n2prnt real8 0.0 ; used for printing the value of n2 b/c n2 changes in the code
.code
start:
; change to float values errors start here
mov n1, sval(input("Enter a value for n1: ")) ; how to move?
mov n2, sval(input("Enter a value for n2: "))
mov ebx, n2
mov n2prnt, ebx
cmp n2, 0.0 ; how to compare?
jl movtoabs
jmp wileloop
movtoabs:
xor eax, eax
sub eax, n2 ; how to subtract?
mov n2, eax
xor eax, eax
wileloop:
cmp n2, 0
jg powerade
jmp done
powerade:
mov eax, power
imul n1 ; how to multiply?
mov power, eax
xor eax, eax
dec n2 ; how to decrement?
jmp wileloop
; errors done here
done:
invoke crt_printf,chr$("The value of n1 is: %f%c"),n1,10
invoke crt_printf,chr$("The value of n2 is: %f%c"),n2,10
invoke crt_printf,chr$("The power of the two values is: %f%c"),power,10
inkey
exit
end start
Can someone help to fix this code?
The CPU registers don't work with floating point values, you need to be using the floating point unit (FPU). The opcodes begin with Fxxxx
The FPU might also be referred to as a floating point coprocessor, 8087, x87
-Clive
What is an opcode?
x86
mov
add
FPU
fld
fadd
fstp
Look under Help->FPU Lib Help Menu in Quick Editor
(big ! circle)Navigation to the webpage was canceled
What you can try:
- Retype the address
I tried going to FPU Help but this is what I got
but I can see the available functions like
FpuAbs
FpuAdd
and stuff
help?
You could try Raymonds tutorial if you cant access that:
http://www.ray.masmcode.com/fpu.html
it should work
but, the same thing is available online
Ray is our resident FPU guru...
http://www.ray.masmcode.com/tutorial/index.html
:P :bg
lol - you beat me to it
Gaurav,
Just to get you started in the right direction. I think you can figure out the rest from Simply FPU.
.DATA
n1 SDWORD -5
r1 REAL8 0.0
.CODE
start:
finit
fild n1
fstp r1
This converts an SDWORD to a REAL8.
Now if n2 can be a 32-bit integer, we can use this method. Still you're not going to learn anything if other people do all the thinking.
-Clive
..
fld1 ; x = 1 Load 1 into the 80x87
mov eax,n2 ; Where n2 is an integer
cdq ; ABS(eax)
xor eax,edx
sub eax,edx
jz label_40 ; is zero, n1**0 = 1
fld qword ptr n1 ; y = n1
label_10:
test al,1 ; Is LSB set
jz label_20
fmul st(1),st ; x = x * y
label_20:
shr eax,1
jz label_30 ; Continue if non zero
fmul st,st(0) ; y = y * y
jmp label_10
label_30:
fstp st ; Pop y
label_40:
fstp qword ptr power ; Pop x
..
n1 real8 10.0
n2 dd -3
power real8 ?
Somehow I think the value of n1 should be the absolute value, as it needs to be positive.
Referring to a couple of books and manuals, the following seems to be a semi workable solution where both n1 and n2 are doubles, and n1 is positive.
-Clive
; x**y = 2**(y*log2(x))
; x**y = 1 + (2**(y*log2(x)) - 1)
; x**y = (1 + (2**(frac(y*log2(x)) - 1))) * 2**int(y*log2(x))
fld qword ptr n2 ; y
fabs ; y = abs(y)
fld qword ptr n1 ; x
fyl2x ; b = y*log2(x)
fld st(0) ; dup(b)
frndint ; int = int(b)
fsub st(1),st(0) ; frac = b - int
fld1 ; 1
fscale ; 1 << int (2**int)
fstp st(1) ; discard int
fxch ; swap st(0) and st(1)
f2xm1 ; a = (2**frac - 1) where 0 <= frac <= 0.5
fld1 ; 1
fadd ; += 1
fmul ; * 2**int
fstp qword ptr power
Gaurav,
If you are taking a newbie class in assembly, I doubt very much that the teacher is expecting the students to know all the details of programming floating point math. Using macros and/or libraries should thus be very acceptable to him.
I would suggest you download the latest version of the Fpulib which is more liberal with the float size you can use (older versions only allowed REAL10).
http://www.ray.masmcode.com/fpu.html#fpulib
Unzip the content into your existing MASM32/fpulib folder, overwriting all the files if prompted.
Then run the MAKE.BAT file.
Also transfer the Fpulib.chm file to the MASM32/help folder, overwriting the existing one. You don't need to be connected to the Internet to access that file.
One of your questions which has not been answered was how to convert your input to a float. One possibility is to store the input as a string and use the FpuAtoFL function from the Fpulib to convert that string to a float and store it wherever you want.
As clive mentioned, the n1 value must be positive. If the input is a negative number, it must NOT be processed but a message returned to indicate that such a number is not acceptable as input. Converting it to a positive number and processing it would be wrong since the returned value would thus not reflect reality. (The Fpulib contains a function FpuXexpY to raise any positive number to any power.
If you really want to understand what you are doing, I would strongly suggest that you study the source code of the functions you will be using. And, while at the above link, you can also download the FPU tutorial. Be sure to read the first two chapters which describe (i) the FPU internals and (ii) the floating point format with its limitations.
Wish you the best in your studies.
Hey raymond,
I transferred the fpulib.chm to the MASM32/help folder and i also opened it but it still says the navigation to the webpage was canceled for some odd reason...
I also opened it from the original zip file and it works.. Idk what the problem is
Also, I am taking a newbie college course and the floating point math is just extra credit for a project that I already finished and probably not due in a month or so.
But the assignment was to do ~n1abs(n2) = power~ so... is there such a function to do that?
Btw clive, I'm using q-editor and the command prompt cant assemble st(0)/st(1) code... what is it?
What datatype is a string?
Quote from: Gaurav on March 05, 2010, 04:34:45 PM
But the assignment was to do ~n1abs(n2) = power~ so... is there such a function to do that?
No, the 80x87 is a binary machine, it does not directly support 10**x, e**x, log10(x) or ln(x), it performs operations like 2**x and log2(x). You have to use these more limited functions, along with constants and other optimized instructions to achieve the work. The core operates efficiently with simple operations, you have to perform more individual operations to do more complex operations.
The following book by the designers of the 8087 is a good reference on the part, and explains some of the functions, trade-offs, precision and optimizations used.
http://www.amazon.com/8087-Primer-John-F-Palmer/dp/0471875694/ref=sr_1_1?ie=UTF8&s=books&qid=1267809630&sr=1-1
The code provided is a fairly concise expression of your function, although probably not optimal. In C you could use "power = pow(n1, fabs(n2));", but all you are doing is abstracting the underlying complexity into the library code doing pretty much what is being described here.
QuoteBtw clive, I'm using q-editor and the command prompt cant assemble st(0)/st(1) code... what is it?
They refer to the stack top relative registers within the 80x87 floating point unit, it contains 8 extended precision floating point registers (80-bit) which work on a stacked basis. For example fabs takes the top register off the stack, and pushes back the absolute value. The fadd will by default pull off the top two values, and push back the sum of the two.
I was using the inline assembler within Microsoft C, but it also works in MASM. I also tried a slightly different encoding my disassembler spits out, which also works.
-Clive
Microsoft (R) Macro Assembler Version 6.15.8803 03/05/10 11:09:08
test2.asm Page 1 - 1
.486
.MODEL FLAT
00000000 .CODE
00000000 test2: ; Code fragment, will need additional harness
00000000 DD 05 00000008 R fld qword ptr [n2]
00000006 D9 E1 fabs
00000008 DD 05 00000000 R fld qword ptr [n1]
0000000E D9 F1 fyl2x
00000010 D9 C0 fld st(0)
00000012 D9 FC frndint
00000014 DC E9 fsub st(1),st
00000016 D9 E8 fld1
00000018 D9 FD fscale
0000001A DD D9 fstp st(1)
0000001C D9 C9 fxch st(1)
0000001E D9 F0 f2xm1
00000020 D9 E8 fld1
00000022 DE C1 faddp st(1),st
00000024 DE C9 fmulp st(1),st
00000026 DD 1D 00000010 R fstp qword ptr [power]
00000000 .DATA
00000000 n1 real8 2.5
4004000000000000
00000008 n2 real8 2.5
4004000000000000
00000010 power real8 ?
0000000000000000
END test2
Microsoft (R) Macro Assembler Version 6.15.8803 03/05/10 11:09:08
test2.asm Symbols 2 - 1
Segments and Groups:
N a m e Size Length Align Combine Class
FLAT . . . . . . . . . . . . . . GROUP
_DATA . . . . . . . . . . . . . 32 Bit 00000018 Para Public 'DATA'
_TEXT . . . . . . . . . . . . . 32 Bit 0000002C Para Public 'CODE'
Symbols:
N a m e Type Value Attr
@CodeSize . . . . . . . . . . . Number 00000000h
@DataSize . . . . . . . . . . . Number 00000000h
@Interface . . . . . . . . . . . Number 00000000h
@Model . . . . . . . . . . . . . Number 00000007h
@code . . . . . . . . . . . . . Text _TEXT
@data . . . . . . . . . . . . . Text FLAT
@fardata? . . . . . . . . . . . Text FLAT
@fardata . . . . . . . . . . . . Text FLAT
@stack . . . . . . . . . . . . . Text FLAT
n1 . . . . . . . . . . . . . . . QWord 00000000 _DATA
n2 . . . . . . . . . . . . . . . QWord 00000008 _DATA
power . . . . . . . . . . . . . QWord 00000010 _DATA
test2 . . . . . . . . . . . . . L Near 00000000 _TEXT
0 Warnings
0 Errors
Quote from: GuaravBtw clive, I'm using q-editor and the command prompt cant assemble st(0)/st(1) code... what is it?
Try using upper case ST(0), ST(1) etc., if I remember correctly, it has to do with the placement of OPTION CASEMAP:NONE.
I believe that using upper or lower case letters for mnemonics is immaterial for the MASM assembler, regardless of the casemap option. Please someone correct me if I'm wrong.
MOV eax,Ecx assembles just fine.
QuoteWhat datatype is a string?
Bytes. Ex.:
inputbuffer db 32 DUP(?) ;or whatever size you may need
Raymond,
I was talking specifically about FPU registers ST(0), ST(1) etc.
I know mmx and xmm registers can have problems with case. If you put option casemap:none before .mmx or .xmm you will have to use all uppercase for mmx and xmm registers.
I could swear I saw that same problem with ST(0) etc. Maybe it was with PowerBASIC Inline Assembler or something. :bg
Quote from: Greg Lyon on March 06, 2010, 03:31:44 AM
Raymond,
I was talking specifically about FPU registers ST(0), ST(1) etc.
I know mmx and xmm registers can have problems with case. If you put option casemap:none before .mmx or .xmm you will have to use all uppercase for mmx and xmm registers.
I could swear I saw that same problem with ST(0) etc. Maybe it was with PowerBASIC Inline Assembler or something. :bg
I've always found MASM to be pretty tolerant by default, perhaps it got a bit weird when using MACROs to fake KNI/SSE back in the day but here's a quick mashup using ML -c -Fl test.asm
-Clive
Microsoft (R) Macro Assembler Version 6.15.8803 03/05/10 21:44:57
test.asm Page 1 - 1
.686
.XMM
.MODEL FLAT
00000000 .DATA
00000000 .CODE
00000000 start:
00000000 0F F6 DA psadbw mm3,mm2
00000003 0F F6 37 psadbw mm6,[edi]
00000006 0F F6 DA pSadBw Mm3,mM2
00000009 0F F6 37 PsadbW MM6,[eDi]
0000000C 0F C6 F4 03 shufps xmm6,xmm4,3
00000010 0F C6 13 02 shufps xmm2,[ebx],2
00000014 0F C6 F4 03 ShufPS xMM6,xMm4,3
00000018 0F C6 13 02 sHUFps Xmm2,[EbX],2
END start
Clive,
Did your code contain OPTION CASEMAP:NONE? The masm32 includes do use it, and it does effect case sensitivity.
I don't want to get into a pissing match about this. It was just a suggestion for Gaurav based on what I remembered happening to me in the past.
QuoteDid your code contain OPTION CASEMAP:NONE?
No, pretty much plain and vanilla like the listing, not including anything either.
QuoteI don't want to get into a pissing match about this.
No problem. I've run into case problems on other assemblers, but did'nt recall running into any with MASM, which is usually pretty tolerant of case and syntax. I usually use the inline assembler, and lower case, just wanted to confirm MASM wouldn't totally choke on my code.
-Clive
Clive,
I just tried a few tests, OPTION CASEMAP:NONE does not seem to effect the case sensitivity of the FPU registers but it does effect the mmx and xmm registers as I described in my prior post. I guess I was thinking of that. :red
.386 ; create 32 bit code
.model flat, stdcall ; 32 bit memory model
option casemap :none ; case sensitive
include \masm32\include\masm32rt.inc
.data
n1 REAL8 ? ; the first value
n2 REAL8 ? ; the second value
n2prnt REAL8 ? ; the printable value of n2
power REAL8 ? ; the result of n1^abs(n2)
n2o SDWORD 0 ; the loop counter
mine SDWORD 0 ; getting inputs
.code
start:
finit ; initialize fpu
mov mine, sval(input("Enter 1st number: "))
fild mine ; loading in the number as an integer(i want float)
fst n1 ; storing the number in n1
mov mine, sval(input("Enter 2nd number: "))
fild mine ; loading in the number as an integer(i want float)
fabs ; absolute value of exponent
fst n2 ; storing the number in n2
fld n2 ; load the float n2
fst n2prnt ; store as a printable var
fist n2o ; store n2 as a loop counter also
wileloop: ; while loop
cmp n2o, 0 ; compare loop counter and 0
jg powerade ; jump if less than, goes to label powerade
jmp done ; jump to label done otherwise
powerade: ; do the power of the values
fmul n1 ; multiply n1 to it
fst n2 ; store register value back in n2
dec n2o ; decrement the loop counter
jmp wileloop ; go back to the while loop
done:
fst power ; store the result in power
invoke crt_printf,chr$("The value of n1 is: %f%c"),n1,10
invoke crt_printf,chr$("The value of n2 is: %f%c"),n2prnt,10
invoke crt_printf,chr$("The value of power is: %f%c"),power,10
inkey
exit
end start
This is what I've got so far.. As you can see im not getting the float values through input and i can't loop an exponent of like 4.5, it only does 4. Any suggestions?
x^y can be calculated through logarithm - take a look in (or use) Raymond's fpulib: FpuXexpY.asm
.386 ; create 32 bit code
.model flat, stdcall ; 32 bit memory model
option casemap :none ; case sensitive
include \masm32\include\masm32rt.inc
include \masm32\fpulib\Fpu.inc
.data
n1 REAL8 ? ; the first value
n2 REAL8 ? ; the second value
n2prnt REAL8 ? ; the printable value of n2
power DWORD ? ; the result of n1^abs(n2)
n2o SDWORD 0 ; the loop counter
mine SDWORD 0 ; getting inputs
.code
start:
finit ; initialize fpu
mov mine, sval(input("Enter 1st number: "))
fild mine ; loading in the number as an integer(i want float)
fst n1 ; storing the number in n1
mov mine, sval(input("Enter 2nd number: "))
fild mine ; loading in the number as an integer(i want float)
fabs ; absolute value of exponent
fst n2 ; storing the number in n2
fld n2 ; load the float n2
fst n2prnt ; store as a printable var
fist n2o ; store n2 as a loop counter also
invoke FpuXexpY, n1, n2, power, SRC1_REAL8 or SRC2_REAL8
done:
invoke crt_printf,chr$("The value of n1 is: %f%c"),n1,10
invoke crt_printf,chr$("The value of n2 is: %f%c"),n2prnt,10
invoke crt_printf,chr$("The value of power is: %f%c"),power,10
inkey
exit
end start
OK, i have access to the fpu commands but how to fix this error?
(40) : error A2114: INVOKE argument type mismatch : argument : 2
(40) : error A2114: INVOKE argument type mismatch : argument : 1
Quote
x^y can be calculated through logarithm
I think this has been expressed earlier, and demonstrated using x87 intrinsics, the OP is persisting with his own method.
I fail to understand the "fst n2" in the loop. You need to be using fstp's to stop the x87 getting cluttered up with junk. The integer method I previously posted is far more efficient.
You can't do a fractional exponent using the looping method here, please refer to some docs/books on the 80x87.
I'd suggest using a CRT sscanf() method for inputting the floating point value. Or using atof(). For starters you could hard code values.
The point of the assignment/homework problem is to understand the x87, not calling existing library code.
-Clive
Gaurav,
If you are going to use the Fpulib functions (or functions from any other library), you MUST read and understand the provided help file. For example, the FpuXexpY function specifies the following:
SRC1_REAL8 Src1 is a pointer to a 64-bit REAL number (you tried to pass the value of a REAL8 instead of its address)
SRC2_REAL8 Src2 is a pointer to a 64-bit REAL number (same as above)
DEST_MEM lpDest is a pointer to a TBYTE
(this is the default and does not need to be indicated)
Since you don't specify any specific destination, the function expects that the value provided is the address of a REAL10. And, because you provided the current value of the dword variable "power" as that parameter, it was considered as a correct argument type (addresses are dwords). However, the current value of "power" was probably 0 and your program would thus have crashed (trying to write at address 0) if your first two arguments had been correct.
As a side note, that function generally expects REAL arguments as sources and thus returns a REAL result. Returning an integer is not directly possible. The help file will thus need to be altered to avoid any potential misunderstanding because of the use of the DWORD/QWORD terminology instead of REAL4/REAL8 for destinations.
Well, I have modified the code. The problem seems to be that I cannot invoke the FpuXexpY proc at all.. everything else runs
.386 ; create 32 bit code
;.model flat, stdcall ; 32 bit memory model
option casemap :none ; case sensitive
include \masm32\include\masm32rt.inc
include \masm32\fpulib\Fpu.inc
.data
n1 REAL8 ? ; the first value
n2 SDWORD ? ; the second value
power REAL10 ? ; the result of n1^abs(n2)
.code
start:
finit ; initialize fpu
; getting inputs
invoke StrToFloat, input("Enter number: "), OFFSET n1
invoke StrToFloat, input("Enter exponent: "), OFFSET n2
fld n1 ; load the flat n1
fabs ; make it positive
fstp n1 ; store it back in n1
fld n2 ; load the float n2
fabs ; make it positive
fstp n2 ; store it back in n2
fld n1 ; loads n1 to stack
invoke FpuXexpY, 0, n2, ADDR power, SRC1_FPU or SRC2_DIMM
fstp power ; get the result from invoking...
done:
print chr$("The value of n1 is: ")
print real8$(n1)
print chr$(10, "The value of new n2 is: ")
print str$(n2)
print chr$(10, "The value of power is: ")
print real10$(power)
print chr$(10)
inkey
exit
end start
Any suggestions?
You left out:
includelib \masm32\fpulib\Fpu.lib
So the essential library is not being included in the linking process, and the linker fails when it cannot resolve the reference to FpuXexpY. The linker should have returned:
error LNK2001: unresolved external symbol _FpuXexpY@16
fatal error LNK1120: 1 unresolved externals
Thanks it works now.. but result is weird
Enter number: 5
Enter exponent: 5
...
The value of power is: -1.#IND00
Is the result translated to hex values?
The result should be 3125...
The IND in the result means indefinite. I have a listing somewhere of these error results and what they mean, but I can't seem to find it.
Your code is mixing incompatible data types. StrToFloat returns a REAL8, and FpuXexpY cannot directly handle a REAL8. Since your code is displaying the value of n2 with str$, I assumed that n2 is supposed to be a 4-byte integer. So to input it I used:
invoke atodw, input("Enter exponent: ")
mov n2, eax
And to load it from memory and store it back:
fild n2
...
fistp n2
And since the destination for FpuXexpY apparently defaults to DEST_MEM, I removed the:
fstp power
And with those changes the code appears to work correctly.
Ok, I changed the code to floating-point and it's good! Here's the bit of coding:
.386 ; create 32 bit code
.model flat, stdcall ; 32 bit memory model
option casemap :none ; case sensitive
include \masm32\include\masm32rt.inc
include \masm32\fpulib\Fpu.inc
includelib \masm32\fpulib\Fpu.lib
.data
n1 REAL10 ? ; the first value
n2 REAL8 ? ; the second value
power REAL10 ? ; the result of n1^n2
.code
start:
finit ; initialize fpu
invoke FpuAtoFL, input("Enter Float Value: "), ADDR n1, DEST_MEM
invoke StrToFloat, input("Enter Exponent: "), OFFSET n2
fld n1 ; pushes n1 to stack
fabs ; make it positive if needed
fstp n1 ; store it back and pop
fld n2 ; pushes n2 to stack
fabs ; change to positive if needed
invoke FpuXexpY, ADDR n1, 0, ADDR power, SRC1_REAL or SRC2_FPU
print chr$(10, "The result is: ")
print real10$(power)
print chr$(10)
inkey
exit
end start
Thank you all for helping!
Now if only I can find the same XexpY for integers.. that would be awesome!
Michael,
Quoteand FpuXexpY cannot directly handle a REAL8
The latest version of the Fpulib has been expanded to accept REAL4 and REAL8 as input and output. It is not restricted anymore to using REAL10 throughout.
The latest version is always available from:
http://www.ray.masmcode.com/fpu.html#fpulib
Note that the Fpulib made available on other sites (or with the MASM32 package) is never guaranteed to be the latest version.