将旧编译器ftol(float to long)函数移植到C

时间:2013-12-06 19:05:53

标签: c++ c assembly x86 x87

我依赖于执行某些计算的旧实现,并将float转换为int

但是,在复制计算后,由于不同的舍入结果,某些值会关闭。

归结为二进制文件使用以下代码将float转换为intlong)。

                lea     ecx, [esp+var_8] ; Load Effective Address
                sub     esp, 10h        ; Integer Subtraction
                and     ecx, 0FFFFFFF8h ; Logical AND
                fld     st              ; Load Real
                fistp   qword ptr [ecx] ; Store Integer and Pop
                fild    qword ptr [ecx] ; Load Integer
                mov     edx, [ecx+4]
                mov     eax, [ecx]
                test    eax, eax        ; Logical Compare
                jz      short loc_3     ; Jump if Zero (ZF=1)

loc_1:                             
                fsubp   st(1), st       ; Subtract Real and Pop
                test    edx, edx        ; Logical Compare
                jz      short loc_2 ; Jump if Zero (ZF=1)
                fstp    dword ptr [ecx] ; Store Real and Pop
                mov     ecx, [ecx]
                add     esp, 10h        ; Add
                xor     ecx, 80000000h  ; Logical Exclusive OR
                add     ecx, 7FFFFFFFh  ; Add
                adc     eax, 0          ; Add with Carry
                retn                    ; Return Near from Procedure
; ---------------------------------------------------------------------------

loc_2:                            
                fstp    dword ptr [ecx] ; Store Real and Pop
                mov     ecx, [ecx]
                add     esp, 10h        ; Add
                add     ecx, 7FFFFFFFh  ; Add
                sbb     eax, 0          ; Integer Subtraction with Borrow
                retn                    ; Return Near from Procedure
; ---------------------------------------------------------------------------

loc_3:                             
                test    edx, 7FFFFFFFh  ; Logical Compare
                jnz     short loc_1     ; Jump if Not Zero (ZF=0)
                fstp    dword ptr [ecx] ; Store Real and Pop
                fstp    dword ptr [ecx] ; Store Real and Pop
                add     esp, 10h        ; Add
                retn                    ; Return Near from Procedure

这与仅仅执行无符号int var = (unsigned int)floatVal;的行为不同。

我认为这是一个旧的ftol实现,因为从float转换为int非常慢,编译器需要更改FPU舍入模式。

它看起来非常类似于http://www.libsdl.org/release/SDL-1.2.15/src/stdlib/SDL_stdlib.c

有人可以协助我将功能转换为C吗?或者告诉我如何使用float参数创建内联ASM函数并使用Visual Studio返回intSDL_sdtlib.c中的那个没有标题,我不知道如何在没有函数args的情况下调用它。

1 个答案:

答案 0 :(得分:2)

这并没有按照要求完全回答问题,但我想从尝试更彻底的英语翻译开始。它并不完美,而且还有几行我仍然试图追踪意图。每个人都请提出问题和更正。

            lea     ecx, [esp+var_8]; Load Effective Address    // make ecx point to somewhere on the stack (I don't know where var_8 is being generated in this case, but I'm guessing it's set such that it makes ecx point to the local stack space allocated on the next line)
            sub     esp, 10h        ; Integer Subtraction       // make room on stack for 16 bytes of local variable -- doesn't all get used but adds padding to allow aligned loads and stores
            and     ecx, 0FFFFFFF8h ; Logical AND               // align pointer in ecx to 8-byte boundary
            fld     st              ; Load Real                 // duplicates whatever was last left (passed by calling convention) on the top of the FPU stack -- st(1) = st(0)
            fistp   qword ptr [ecx] ; Store Integer and Pop     // convert st(0) to *64bit* int (truncate), store in aligned 8 bytes (of local variable space?) pointed to by ecx, and pop off the top value from the FPU stack
            fild    qword ptr [ecx] ; Load Integer              // convert truncated value back to float and leave it sitting on the top of the FPU stack

;// at this point:
;// - st(0) is the truncated float
;// - st(1) is still the original float.
;// - There is a 64bit integer representation pointed to by [ecx]

            mov     edx, [ecx+4]    ;                           // move [bytes 4 thru 7 of integer output] to edx (most significant bytes)
            mov     eax, [ecx]      ;                           // move [bytes 0 thru 3 of integer output] to eax (least significant bytes) -- makes sense, as EAX should hold integer return value in x86 calling conventions
            test    eax, eax        ; Logical Compare           // (http://stackoverflow.com/questions/13064809/the-point-of-test-eax-eax)
            jz      short loc_3     ; Jump if Zero (ZF=1)       // if the least significant 4 bytes are zero, goto loc_3
                                    ;                           // else fall through to loc_1
loc_1:                              ;
            fsubp   st(1), st       ; Subtract Real and Pop     // subtract the truncated float from the original, store in st(1), then pop. (i.e. for 1.25 st(0) ends up 0.25, and the original float is no longer on the FPU stack)
            test    edx, edx        ; Logical Compare           // same trick as earlier, but for the most significant bytes now
            jz      short loc_2     ; Jump if Zero (ZF=1)       // if the most significant 4 bytes from before were all zero, goto loc_2 -- (i.e. input float does not overflow a 32 bit int)
            fstp    dword ptr [ecx] ; Store Real and Pop        // else, dump the fractional portion of the original float over the least significant bytes of the 64bit integer

;// at this point:
;// - the FPU stack should be empty
;// - eax holds a copy of the least significant 4 bytes of the 64bit integer (return value)
;// - edx holds a copy of the most significant 4 bytes of the 64bit integer
;// - [ecx] points to a float representing the part of the input that would be lost in integer truncation
;// - [ecx+4] points at the most significant 4 bytes of our 64bit integer output (probably considered garbage now and not used again)

            mov     ecx, [ecx]      ;                           // make ecx store what it's pointing at directly instead of the pointer to it
            add     esp, 10h        ; Add                       // clean up stack space from the beginning
            xor     ecx, 80000000h  ; Logical Exclusive OR      // mask off the sign bit of the fractional float
            add     ecx, 7FFFFFFFh  ; Add                       // add signed int max (still need to figure out why this)
            adc     eax, 0          ; Add with Carry            // clear carry bit
            retn                    ; Return Near from Procedure
; ---------------------------------------------------------------------------

loc_2:
;// at this point: the FPU stack still holds the fractional (non-integer) portion of the original float that woud have been lost to truncation
            fstp    dword ptr [ecx] ; Store Real and Pop        // store non-integer part as float in local stack space, and remove it from the FPU stack
            mov     ecx, [ecx]      ;                           // make ecx store what it's pointing at directly instead of the pointer to it
            add     esp, 10h        ; Add                       // clean up stack space from the beginning
            add     ecx, 7FFFFFFFh  ; Add                       // add signed int max to the float we just stored (still need to figure out why this)
            sbb     eax, 0          ; Integer Subtraction with Borrow // clear carry bit
            retn                    ; Return Near from Procedure
; ---------------------------------------------------------------------------

loc_3:                             
            test    edx, 7FFFFFFFh  ; Logical Compare           // test the most significant bytes for signed int max
            jnz     short loc_1     ; Jump if Not Zero (ZF=0)   // if the high bytes equal signed int max go back to loc_1
            fstp    dword ptr [ecx] ; Store Real and Pop        // else, empty the FPU stack
            fstp    dword ptr [ecx] ; Store Real and Pop        // empty the FPU stack
            add     esp, 10h        ; Add                       // clean up stack space from the beginning
            retn                    ; Return Near from Procedure