xref: /reactos/sdk/lib/crt/math/arm/__rt_div_worker.h (revision b09b5584)
1 /*
2  * PROJECT:     ReactOS CRT library
3  * LICENSE:     MIT (https://spdx.org/licenses/MIT)
4  * PURPOSE:     Implementation of __rt_div_worker
5  * COPYRIGHT:   Copyright 2015 Timo Kreuzer <timo.kreuzer@reactos.org>
6  *              Copyright 2021 Roman Masanin <36927roma@gmail.com>
7  */
8 
9 /*
10  * See also:
11  * http://research.microsoft.com/pubs/70645/tr-2008-141.pdf
12  * https://web.archive.org/web/20100110044008/http://research.microsoft.com/en-us/um/redmond/projects/invisible/src/crt/md/arm/_div10.s.htm
13  * https://github.com/bawoodruff/BeagleBoard/blob/2731e3174af6daefe4a287a6be82e5ff9c46c99a/OS/BootLoader/Runtime/_udiv.c
14  * https://github.com/bawoodruff/BeagleBoard/blob/2731e3174af6daefe4a287a6be82e5ff9c46c99a/OS/BootLoader/Runtime/arm/_udivsi3.s
15  * https://github.com/bawoodruff/BeagleBoard/blob/2731e3174af6daefe4a287a6be82e5ff9c46c99a/OS/BootLoader/Runtime/arm/divide.s
16  * https://github.com/qemu/edk2/blob/e3c7db50cac9125607df49d5873991df6df11eae/ArmPkg/Library/CompilerIntrinsicsLib/Arm/uldiv.asm
17  * https://github.com/jmonesti/qemu-4.1.1/blob/55fb6a81039a62174c2763759324c43a67d752a1/roms/ipxe/src/arch/arm32/libgcc/lldivmod.S
18  */
19 
20 #ifdef _USE_64_BITS_
21 typedef unsigned long long UINT3264;
22 typedef long long INT3264;
23 typedef struct
24 {
25     unsigned long long quotient; /* to be returned in R0,R1 */
26     unsigned long long modulus;  /* to be returned in R2,R3 */
27 } RETURN_TYPE;
28 #define _CountLeadingZeros _CountLeadingZeros64
29 #else
30 typedef unsigned int UINT3264;
31 typedef int INT3264;
32 typedef unsigned long long RETURN_TYPE; /* to be returned in R0,R1 */
33 #endif
34 
35 __forceinline
36 void
37 __brkdiv0(void)
38 {
39     __emit(0xDEF9);
40 }
41 
42 typedef union _ARM_DIVRESULT
43 {
44     RETURN_TYPE raw_data;
45     struct
46     {
47         UINT3264 quotient;
48         UINT3264 modulus;
49     } data;
50 } ARM_DIVRESULT;
51 
52 RETURN_TYPE
53 __rt_div_worker(
54     UINT3264 divisor,
55     UINT3264 dividend)
56 {
57     ARM_DIVRESULT result;
58     UINT3264 shift;
59     UINT3264 mask;
60     UINT3264 quotient;
61 #ifdef _SIGNED_DIV_
62     int dividend_sign = 0;
63     int divisor_sign = 0;
64 #endif // _SIGNED_DIV_
65 
66     if (divisor == 0)
67     {
68         /* Raise divide by zero error */
69         __brkdiv0();
70     }
71 
72 #ifdef _SIGNED_DIV_
73     if ((INT3264)dividend < 0)
74     {
75         dividend_sign = 1;
76         dividend = -(INT3264)dividend;
77     }
78 
79     if ((INT3264)divisor < 0)
80     {
81         divisor_sign = 1;
82         divisor = -(INT3264)divisor;
83     }
84 #endif // _SIGNED_DIV_
85 
86     if (divisor > dividend)
87     {
88         result.data.quotient = 0;
89 #ifdef _SIGNED_DIV_
90         if (dividend_sign)
91             dividend = -(INT3264)dividend;
92 #endif // _SIGNED_DIV_
93         result.data.modulus = dividend;
94         return result.raw_data;
95     }
96 
97     /* Get the difference in count of leading zeros between dividend and divisor */
98     shift = _CountLeadingZeros(divisor);
99     shift -= _CountLeadingZeros(dividend);
100 
101     /* Shift the divisor to the left, so that it's highest bit is the same
102        as the highest bit of the dividend */
103     divisor <<= shift;
104 
105     mask = (UINT3264)1 << shift;
106 
107     quotient = 0;
108     do
109     {
110         if (dividend >= divisor)
111         {
112             quotient |= mask;
113             dividend -= divisor;
114         }
115         divisor >>= 1;
116         mask >>= 1;
117     }
118     while (mask);
119 
120 #ifdef _SIGNED_DIV_
121     if (dividend_sign ^ divisor_sign)
122     {
123         quotient = -(INT3264)quotient;
124     }
125 
126     if (dividend_sign)
127     {
128         dividend = -(INT3264)dividend;
129     }
130 #endif // _SIGNED_DIV_
131 
132     result.data.quotient = quotient;
133     result.data.modulus = dividend;
134     return result.raw_data;
135 }
136