; 386-specific fixed point routines. ; Tested with TASM 3.0. ROUNDING_ON EQU 1 ; 1 for rounding, 0 for no rounding ; No rounding is faster, rounding is more accurate ALIGNMENT EQU 1 .model tpascal .386 .code ;=========================================================================== ; Multiplies two fixed-point values together. ; C far-callable as: ; Fixedpoint FixedMul(Fixedpoint M1, Fixedpoint M2); ;=========================================================================== FMparms struc DW 3 dup (?) ; return address & pushed BP M1 DD ? M2 DD ? FMparms ends PFMparms struc DW 3 dup (?) ; return address & pushed BP PM1 DD ? PM2 DD ? PFMparms ends align ALIGNMENT public _FixedMul _FixedMul proc far PUSH BP MOV BP,SP MOV EAX,[BP + M1] IMUL DWORD PTR [BP + M2] ; Multiply if ROUNDING_ON ADD EAX,8000h ; Round by adding 2^(-17) ADC EDX,0 ; Whole part of result is in DX endif ; ROUNDING_ON SHR EAX,16 ; Put the fractional part in AX POP BP RETF _FixedMul endp align ALIGNMENT public _PFixedMul _PFixedMul proc far PUSH BP MOV BP,SP MOV EAX,[BP + PM1] IMUL DWORD PTR [BP + PM2] ; Multiply if ROUNDING_ON ADD EAX,8000h ; Round by adding 2^(-17) ADC EDX,0 ; Whole part of result is in DX endif ; ROUNDING_ON SHR EAX,16 ; Put the fractional part in AX POP BP RETF 8 _PFixedMul endp ;=========================================================================== ; Divides one fixed-point value by another. ; C far-callable as: ; Fixedpoint FixedDiv(Fixedpoint Dividend, Fixedpoint Divisor); ;=========================================================================== FDparms struc DW 3 dup (?) ; return address & pushed BP Dividend DD ? Divisor DD ? FDparms ends align ALIGNMENT public _FixedDiv _FixedDiv proc far PUSH BP MOV BP,SP if ROUNDING_ON SUB CX,CX ; Assume positive result MOV EAX,[BP + Dividend] AND EAX,EAX ; Positive dividend? JNS FDP1 ; Yes INC CX ; Mark it's a negative dividend NEG EAX ; Make the dividend positive FDP1: SUB EDX,EDX ; Make it a 64-bit dividend, then shift ; left 16 bits so that result will be ; in EAX SHLD EDX,EAX,16 ; Put whole part of dividend in EDX SHL EAX,16 ; Put fractional part of dividend in ; high word of EAX MOV EBX,DWORD PTR [BP + Divisor] AND EBX,EBX ; Positive divisor? JNS FDP2 ; Yes DEC CX ; Mark it's a negative divisor NEG EBX ; Make divisor positive FDP2: DIV EBX ; Divide SHR EBX,1 ; Divisor/2, minus 1 if the divisor is ADC EBX,0 ; even DEC EBX CMP EBX,EDX ; Set Carry if remainder is at least ADC EAX,0 ; half as large as the divisor, then ; use that to round up if necessary AND CX,CX ; Should the result be made negative? JZ FDP3 ; No NEG EAX ; Yes, negate it FDP3: else ;!ROUNDING_ON MOV EDX,[BP + Dividend] SUB EAX,EAX SHRD EAX,EDX,16 ; Position so that result ends up in EAX SAR EDX,16 IDIV DWORD PTR [BP + Divisor] endif ;ROUNDING_ON SHLD EDX,EAX,16 ; Whole part of result in DX; ; fractional part is already in AX POP BP RETF _FixedDiv endp PFDparms struc DW 3 dup (?) ; return address & pushed BP PDivisor DD ? PDividend DD ? PFDparms ends align ALIGNMENT public _PFixedDiv _PFixedDiv proc far PUSH BP MOV BP,SP if ROUNDING_ON SUB CX,CX ; Assume positive result MOV EAX,[BP + PDividend] AND EAX,EAX ; Positive dividend? JNS PFDP1 ; Yes INC CX ; Mark it's a negative dividend NEG EAX ; Make the dividend positive PFDP1: SUB EDX,EDX ; Make it a 64-bit dividend, then shift ; left 16 bits so that result will be ; in EAX SHLD EDX,EAX,16 ; Put whole part of dividend in EDX SHL EAX,16 ; Put fractional part of dividend in ; high word of EAX MOV EBX,DWORD PTR [BP + PDivisor] AND EBX,EBX ; Positive divisor? JNS PFDP2 ; Yes DEC CX ; Mark it's a negative divisor NEG EBX ; Make divisor positive PFDP2: DIV EBX ; Divide SHR EBX,1 ; Divisor/2, minus 1 if the divisor is ADC EBX,0 ; even DEC EBX CMP EBX,EDX ; Set Carry if remainder is at least ADC EAX,0 ; half as large as the divisor, then ; use that to round up if necessary AND CX,CX ; Should the result be made negative? JZ PFDP3 ; No NEG EAX ; Yes, negate it PFDP3: else ;!ROUNDING_ON MOV EDX,[BP + PDividend] SUB EAX,EAX SHRD EAX,EDX,16 ; Position so that result ends up in EAX SAR EDX,16 IDIV DWORD PTR [BP + PDivisor] endif ;ROUNDING_ON SHLD EDX,EAX,16 ; Whole part of result in DX; ; fractional part is already in AX POP BP RETF 8 _PFixedDiv endp end