fruit-popper/GDLIB/FIXEDP.PAS
2021-07-07 17:10:18 -04:00

194 lines
6.9 KiB
Plaintext

{ Fixed-point math type definition, constants and functions.
Gered King, 2019-2020 }
{$A+,B-,F-,G+,I-,N+,P-,Q-,R-,S-,T-,V-,X+}
unit FixedP;
interface
const
FP_INT_SHIFT = 16;
FP_FLOAT_SHIFT = 65536.0;
FP_DIV_ERROR = $7fffffff;
FP_1 = trunc(1 * FP_FLOAT_SHIFT);
FP_2 = trunc(2 * FP_FLOAT_SHIFT);
FP_3 = trunc(3 * FP_FLOAT_SHIFT);
FP_4 = trunc(4 * FP_FLOAT_SHIFT);
FP_5 = trunc(5 * FP_FLOAT_SHIFT);
FP_6 = trunc(6 * FP_FLOAT_SHIFT);
FP_7 = trunc(7 * FP_FLOAT_SHIFT);
FP_8 = trunc(8 * FP_FLOAT_SHIFT);
FP_9 = trunc(9 * FP_FLOAT_SHIFT);
FP_10 = trunc(10 * FP_FLOAT_SHIFT);
FP_16 = trunc(16 * FP_FLOAT_SHIFT);
FP_32 = trunc(32 * FP_FLOAT_SHIFT);
FP_64 = trunc(64 * FP_FLOAT_SHIFT);
FP_128 = trunc(128 * FP_FLOAT_SHIFT);
FP_256 = trunc(256 * FP_FLOAT_SHIFT);
FP_0_1 = trunc(0.1 * FP_FLOAT_SHIFT);
FP_0_2 = trunc(0.2 * FP_FLOAT_SHIFT);
FP_0_3 = trunc(0.3 * FP_FLOAT_SHIFT);
FP_0_4 = trunc(0.4 * FP_FLOAT_SHIFT);
FP_0_5 = trunc(0.5 * FP_FLOAT_SHIFT);
FP_0_6 = trunc(0.6 * FP_FLOAT_SHIFT);
FP_0_7 = trunc(0.7 * FP_FLOAT_SHIFT);
FP_0_8 = trunc(0.8 * FP_FLOAT_SHIFT);
FP_0_9 = trunc(0.9 * FP_FLOAT_SHIFT);
FP_0_25 = trunc(0.25 * FP_FLOAT_SHIFT);
FP_0_75 = trunc(0.75 * FP_FLOAT_SHIFT);
FP_1_OVER_3 = trunc((1 / 3) * FP_FLOAT_SHIFT);
FP_2_OVER_3 = trunc((2 / 3) * FP_FLOAT_SHIFT);
type
Fixed = LongInt;
{ turbo pascal's "inline" used here solely for functions that we want to be
inlined for best performance. in c/c++, these would've been written
as macros, but turbo pascal unfortunately lacks macro support. so we
are using "inline" as a poor way to get a sort of function inlining... }
function IntToFix(x : integer) : fixed;
inline(
$58/ { pop ax }
$8B/$D0/ { mov dx, ax }
$33/$C0 { xor ax, ax }
);
function FixToInt(x : fixed) : integer;
inline(
$66/$58/ { pop eax }
$66/$C1/$E8/FP_INT_SHIFT { shr eax, FP_INT_SHIFT }
);
function FloatToFix(x : single) : fixed;
function FixToFloat(x : fixed) : single;
function FixMul(a, b : fixed) : fixed;
inline(
$66/$5B/ { pop ebx }
$66/$58/ { pop eax }
$66/$F7/$EB/ { imul ebx }
$66/$0F/$AC/$D0/FP_INT_SHIFT/ { shrd eax, edx, FP_INT_SHIFT }
$66/$C1/$C0/FP_INT_SHIFT/ { rol eax, FP_INT_SHIFT }
$8B/$D0/ { mov dx, ax }
$66/$C1/$C0/FP_INT_SHIFT { rol eax, FP_INT_SHIFT }
);
function FixDiv(a, b : fixed) : fixed;
inline(
$66/$5B/ { pop ebx }
$66/$58/ { pop eax }
$66/$33/$C9/ { xor ecx, ecx }
$66/$0B/$C0/ { or eax, eax }
$79/$05/ { jns checkDivisorSign }
$66/$F7/$D8/ { neg eax }
$66/$41/ { inc ecx }
{ checkDivisorSign: }
$66/$0B/$DB/ { or ebx, ebx }
$79/$05/ { jns divide }
$66/$F7/$DB/ { neg ebx }
$66/$41/ { inc ecx }
{ divide: }
$66/$33/$D2/ { xor edx, edx }
$66/$0F/$A4/$C2/FP_INT_SHIFT/ { shld edx, eax, FP_INT_SHIFT }
$66/$C1/$E0/FP_INT_SHIFT/ { shl eax, FP_INT_SHIFT }
$66/$3B/$D3/ { cmp edx, ebx }
$73/$08/ { jae error }
$66/$F7/$F3/ { div ebx }
$66/$0B/$C0/ { or eax, eax }
$79/$08/ { jns restoreSignBit }
{ error: }
$66/$B8/$FF/$FF/$FF/$7F/ { mov eax, FP_DIV_ERROR }
$EB/$09/ { jmp done }
{ restoreSignBit: }
$66/$83/$F9/$01/ { cmp ecx, 1 }
$75/$03/ { jne done }
$66/$F7/$D8/ { neg eax }
{ done: }
$66/$C1/$C0/FP_INT_SHIFT/ { rol eax, FP_INT_SHIFT }
$8B/$D0/ { mov dx, ax }
$66/$C1/$C0/FP_INT_SHIFT { rol eax, FP_INT_SHIFT }
);
function FixDivFast(a, b : fixed) : fixed;
inline(
$66/$5B/ { pop ebx }
$66/$58/ { pop eax }
$66/$99/ { cdq }
$66/$0F/$A4/$C2/FP_INT_SHIFT/ { shld edx, eax, FP_INT_SHIFT }
$66/$C1/$E0/FP_INT_SHIFT/ { shl eax, FP_INT_SHIFT }
$66/$F7/$FB/ { idiv ebx }
$66/$C1/$C0/FP_INT_SHIFT/ { rol eax, FP_INT_SHIFT }
$8B/$D0/ { mov dx, ax }
$66/$C1/$C0/FP_INT_SHIFT { rol eax, FP_INT_SHIFT }
);
function FixSqr(x : fixed) : fixed;
inline(
$66/$58/ { pop eax }
$66/$F7/$E8/ { imul eax }
$66/$0F/$AC/$D0/FP_INT_SHIFT/ { shrd eax, edx, FP_INT_SHIFT }
$66/$C1/$C0/FP_INT_SHIFT/ { rol eax, FP_INT_SHIFT }
$8B/$D0/ { mov dx, ax }
$66/$C1/$C0/FP_INT_SHIFT { rol eax, FP_INT_SHIFT }
);
function FixSqrt(x : fixed) : fixed;
implementation
function FloatToFix(x : single) : fixed;
begin
FloatToFix := trunc(x * FP_FLOAT_SHIFT);
end;
function FixToFloat(x : fixed) : single;
begin
FixToFloat := x / FP_FLOAT_SHIFT;
end;
function FixSqrt(x : fixed) : fixed;
assembler;
asm
db $66; mov bx, x.word { mov ebx, x }
db $66,$b9,$00,$00,$00,$40 { mov ecx, $40000000 }
db $66; xor ax, ax { xor eax, eax }
@@1:
db $66; mov dx, ax { mov edx, eax }
db $66; add dx, cx { add edx, ecx }
db $66; cmp bx, dx { cmp ebx, edx }
jl @@2
db $66; sub bx, dx { sub ebx, edx }
db $66; mov ax, dx { mov eax, edx }
db $66; add ax, cx { add eax, ecx }
@@2:
db $66; shl bx, 1 { shl ebx, 1 }
db $66; shr cx, 1 { shr ecx, 1 }
db $66; cmp cx, 40h { cmp ecx, $40 }
jg @@1
db $66; shr ax, 8 { shr eax, 8 }
db $66; rol ax, FP_INT_SHIFT { rol eax, FP_INT_SHIFT }
mov dx, ax
db $66; rol ax, FP_INT_SHIFT { rol eax, FP_INT_SHIFT }
end;
begin
if Test8086 < 2 then begin
writeln('The FIXEDP unit requires a 386 cpu or higher!');
halt;
end;
end.