News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

x86 Assembly Beginner: Need help debugging a mess

Started by ProtoflareX, May 08, 2016, 07:40:32 AM

Previous topic - Next topic

ProtoflareX

The goal of the program below is to accept up to 10 signed 8-byte floating-point numbers within the range -100 ≤ X ≤ 100 as input from a user and store them into an array. The user input is received using the ReadFloat Irvine method. If a number outside that range is entered, the subroutine is supposed to stop executing and return through eax the number of values currently in the array. That was just a bit of context intended to describe what the program is supposed to do. The problem I am having with this code is that it does not loop properly after accepting the first value. I have it set up so that it checks to see if the input number is above or equal to -100 in L1, and then below or equal to 100 in L2. If the number is outside of that range, the subroutine should stop executing, but if it is within that range, it should progress to L3 & R1. In L3 and R1, the number is placed into an index in theSFPArray and if the array has less than 10 values in it, the program should unconditionally jump back to L1 for further iterations. The JMP command in R1 is where the problem is. The subroutine in its current state will stop executing after a single number is input and I can't figure out why. Can anyone provide assistance?


INCLUDE    c:\irvine\irvine32.inc
INCLUDELIB c:\irvine\irvine32.lib
INCLUDELIB c:\masm32\lib\user32.lib
INCLUDELIB c:\masm32\lib\kernel32.lib


.data
theSFPArray REAL8 10 dup(?)             ;an array that can store up to 10 signed floating point numbers
tempStoreFP REAL8 ?                     ;this variable will temporarily store the FP number acquired from user input, and then push it onto the stack
lengthOfSFPArray DWORD ?                ;this variable will store the length of theSFPArray. This value will be used to determine if requestSignedFloats should stop looping.
inputLoopCounter DWORD -1               ;used to determine when the requestSignedFloats subroutine should stop accepting input.                 
prompt BYTE "Please enter a value: ",0

   
.CODE

main PROC

    call    requestSignedFloats
    exit

main ENDP

requestSignedFloats PROC

    finit                                 ;initializes floating point unit
    push    edx                           ;pushes the original value of edx onto the stack. This will be popped when the subroutine ends.
    mov     edx, OFFSET theSFPArray       ;moves the offset of theSFPArray into edx so that values can be placed into it.
    push    edx                           ;pushes edx onto the stack while it contains the offset of the SFPArray for later usage.   

    mov eax,100
    push eax
    fild dword ptr [esp]                  ;get the 100 from memory and throw it onto the FPU, into ST(0)
    fchs                                  ;changes the 100 in ST(0) into -100
    pop eax                 

L1:
    mov     edx,OFFSET prompt                         
    call    WriteString                   ;displays the String within the prompt variable on the screen.         
    call    ReadFloat                     ;requests a float as input from the user and stores it at the top of the floating point stack, aka ST(0).
    fcom                               ;compares the value in ST(1) to the value in ST(0).
    jae     L2
    pop     edx                           ;this line and the two lines below it will execute if the comparison dictates that ST(1) is below the value in ST(0). This should cause the subroutine to end.
    pop     edx                           ;a second pop of edx is necessary to restore edx to its original value since two alterations of edx were placed onto the stack at the beginning of the subroutine.
    mov     lengthOfSFPArray,LENGTHOF theSFPArray ;Moves the current number of values stored in theSFPArray into the lengthOfSFPArray variable.
    mov     eax,lengthOfSFPArray          ;Returns in eax,the number of values in the array, as specified by the requirements
    ret
L2:
    fstp    tempStoreFP                   ;pops the user input value off of the stack temporarily so that fchs can be used to change the sign of the value in ST(0)
    fchs                                  ;changes the -100 in ST(0) into a positive 100.
    fld     tempStoreFP                   ;pushes tempStoreFP back onto the stack so that its value is now in ST(1)
    fcom
    jbe     L3
    pop     edx                           ;this line and the two lines below it will execute if the comparison dictates that ST(1) is below the value in ST(0). This should cause the subroutine to end.
    pop     edx                           ;a second pop of edx is necessary to restore edx to its original value since two alterations of edx were placed onto the stack at the beginning of the subroutine.
    mov     lengthOfSFPArray,LENGTHOF theSFPArray ;Moves the current number of values stored in theSFPArray into the lengthOfSFPArray variable.
    mov     eax,lengthOfSFPArray          ;Returns in eax,the number of values in the array, as specified by the requirements
    ret
L3:
    pop     edx                           ;this is done to pop the offset of theSFPArray off of the stack and back into edx since at this point edx still stores the "prompt".
    inc     inputLoopCounter              ;increments inputLoopCounter so that its value is equal to the index that the number input by the user will be stored in.
    mov     ecx,inputLoopCounter          ;uses inputLoopCounter to determine how many times the loop will execute.
R1:
    inc     edx                           ;increments edx an amount of times equivalent to the value stored in inputLoopCounter.
    loop    R1
    fstp    qword ptr [edx]               ;takes the value at the top of the stack and stores it as a REAL8 at the address specified by edx (aka its array index)
    mov     lengthOfSFPArray,LENGTHOF theSFPArray ;Moves the current number of values stored in theSFPArray into the lengthOfSFPArray variable.
    fchs                                  ;changes the 100 in ST(0) to a -100 in preparation for the next iteration of the subroutine.
    cmp     inputLoopCounter,10
    je      L4
    jmp     L1                            ;An unconditional jump to L1 that causes this subroutine to execute repeatedly. The line above this one prevents it from being an infinite loop.
L4:
    mov     eax,lengthOfSFPArray          ;Returns in eax,the number of values in the array, as specified by the requirements
    pop     edx                           ;if the program makes it to this point, the offset of the array would have been popped off of the stack, meaning the original value of edx is the only thing
                                          ;remaining on the stack, so only one pop is necessary
    ret

requestSignedFloats ENDP


FORTRANS

Hi,

   The FPU has its own flags, and does not set the CPU conditional
flags after an operation.  See the FSTSW and FSTSW AX instructions.

HTH,

Steve

qWord


  • use fcomi instead of fcom (the first one sets the CPUflags)
  • Make sure that WriteString and ReadFloat preserves the FPU stack - if this is not documented, you cannot keep the value 100 on the FPU stack. Remarks that you can define constants in memory:
    Quote.const
    myConst REAL8 -100.0
    ...
    .code
    ...
    fld myConst
    ...
    fcomi or fcomip
    jxx
  • LENGTHOF theSFPArray is resolved while assembling and does return the number of elements that were allocated with the definition of array. You must count the actual number at runtime.
MREAL macros - when you need floating point arithmetic while assembling!

ProtoflareX

Quote from: qWord on May 08, 2016, 08:20:47 AM

  • use fcomi instead of fcom (the first one sets the CPUflags)
  • Make sure that WriteString and ReadFloat preserves the FPU stack - if this is not documented, you cannot keep the value 100 on the FPU stack. Remarks that you can define constants in memory:
    Quote.const
    myConst REAL8 -100.0
    ...
    .code
    ...
    fld myConst
    ...
    fcomi or fcomip
    jxx
  • LENGTHOF theSFPArray is resolved while assembling and does return the number of elements that were allocated with the definition of array. You must count the actual number at runtime.

WriteString doesn't affect the FPU stack in any way, but I am unsure if ReadFloat does. I linked a page below that describes the function of ReadFloat. Would it be possible for you to tell me if ReadFloat preserves the FPU stack after reading it? Also I am not sure what you mean by your third comment regarding lengthof theSFParray being resolved. I must be too inexperienced with assembly to understand it.

jj2007

#4
Quote from: ProtoflareX on May 09, 2016, 12:29:00 PMWriteString doesn't affect the FPU stack in any way, but I am unsure if ReadFloat does.

If you are unsure, use a debugger, e.g. Olly*). This is the fpu after reading in 123:
ST0 valid 123.00000000000000000
ST1 valid -100.0000000000000000
ST2 empty 0.0


And these two values are still there after you return from requestSignedFloats.

Re lengthof: With the 10 dup(?), you are determining its length. No need to store it in a dword variable, this is enough:
mov eax, LENGTHOF theSFPArray          ;Returns in eax,the number of values in the array


*) For an alternative, see Irvine32 library

qWord

I did take a look into the source of that functions: they do not save the FPU registers before calling WinAPI functions thus stored values might be altered. So you MUST save the values from the FPU stack to memory before calling these functions and empty the stack.
MREAL macros - when you need floating point arithmetic while assembling!

jj2007

Quote from: qWord on May 10, 2016, 01:27:02 AMfunctions: they do not save the FPU registers before calling WinAPI functions thus stored values might be altered.

As discussed also here: http://masm32.com/board/index.php?topic=5275.msg56463#msg56463 and here: http://www.masmforum.com/board/index.php?topic=10436.msg76648#msg76648

qWord

A bit shocking, but the library code does definitively ignores the calling convention - someone should inform the author.
MREAL macros - when you need floating point arithmetic while assembling!

jj2007

What is so shocking, can you give an example?

qWord

e.g. in ReadFloat FP values are kept in the FPU across WinAPI calls.
MREAL macros - when you need floating point arithmetic while assembling!

jj2007

Right :t

However, M$ has stopped using mmx regs a long time ago, it seems. In Win7-64, the only way I know to trash the FPU are Gdi+ calls.