]>
Commit | Line | Data |
---|---|---|
ff1555f8 DT |
1 | /* DEC Alpha division and remainder support. |
2 | Copyright (C) 1994, 1999 Free Software Foundation, Inc. | |
3 | ||
4 | This file is free software; you can redistribute it and/or modify it | |
5 | under the terms of the GNU General Public License as published by the | |
6 | Free Software Foundation; either version 2, or (at your option) any | |
7 | later version. | |
8 | ||
9 | In addition to the permissions in the GNU General Public License, the | |
10 | Free Software Foundation gives you unlimited permission to link the | |
f7af368f JL |
11 | compiled version of this file into combinations with other programs, |
12 | and to distribute those combinations without any restriction coming | |
13 | from the use of this file. (The General Public License restrictions | |
14 | do apply in other respects; for example, they cover modification of | |
15 | the file, and distribution when not linked into a combine | |
16 | executable.) | |
ff1555f8 DT |
17 | |
18 | This file is distributed in the hope that it will be useful, but | |
19 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
21 | General Public License for more details. | |
22 | ||
23 | You should have received a copy of the GNU General Public License | |
24 | along with this program; see the file COPYING. If not, write to | |
9f2f9eb6 JM |
25 | the Free Software Foundation, 59 Temple Place - Suite 330, |
26 | Boston, MA 02111-1307, USA. */ | |
ff1555f8 | 27 | |
ff1555f8 DT |
28 | /* This had to be written in assembler because the division functions |
29 | use a non-standard calling convention. | |
30 | ||
31 | This file provides an implementation of __divqu, __divq, __divlu, | |
32 | __divl, __remqu, __remq, __remlu and __reml. CPP macros control | |
33 | the exact operation. | |
34 | ||
35 | Operation performed: $27 := $24 o $25, clobber $28, return address to | |
36 | caller in $23, where o one of the operations. | |
37 | ||
38 | The following macros need to be defined: | |
39 | ||
40 | SIZE, the number of bits, 32 or 64. | |
41 | ||
42 | TYPE, either UNSIGNED or SIGNED | |
43 | ||
44 | OPERATION, either DIVISION or REMAINDER | |
45 | ||
46 | SPECIAL_CALLING_CONVENTION, 0 or 1. It is useful for debugging to | |
47 | define this to 0. That removes the `__' prefix to make the function | |
48 | name not collide with the existing libc.a names, and uses the | |
49 | standard Alpha procedure calling convention. | |
50 | */ | |
51 | ||
52 | #ifndef SPECIAL_CALLING_CONVENTION | |
53 | #define SPECIAL_CALLING_CONVENTION 1 | |
54 | #endif | |
55 | ||
56 | #ifdef L_divl | |
57 | #if SPECIAL_CALLING_CONVENTION | |
58 | #define FUNCTION_NAME __divl | |
59 | #else | |
60 | #define FUNCTION_NAME divl | |
61 | #endif | |
62 | #define SIZE 32 | |
63 | #define TYPE SIGNED | |
64 | #define OPERATION DIVISION | |
65 | #endif | |
66 | ||
67 | #ifdef L_divlu | |
68 | #if SPECIAL_CALLING_CONVENTION | |
69 | #define FUNCTION_NAME __divlu | |
70 | #else | |
71 | #define FUNCTION_NAME divlu | |
72 | #endif | |
73 | #define SIZE 32 | |
74 | #define TYPE UNSIGNED | |
75 | #define OPERATION DIVISION | |
76 | #endif | |
77 | ||
78 | #ifdef L_divq | |
79 | #if SPECIAL_CALLING_CONVENTION | |
80 | #define FUNCTION_NAME __divq | |
81 | #else | |
82 | #define FUNCTION_NAME divq | |
83 | #endif | |
84 | #define SIZE 64 | |
85 | #define TYPE SIGNED | |
86 | #define OPERATION DIVISION | |
87 | #endif | |
88 | ||
89 | #ifdef L_divqu | |
90 | #if SPECIAL_CALLING_CONVENTION | |
91 | #define FUNCTION_NAME __divqu | |
92 | #else | |
93 | #define FUNCTION_NAME divqu | |
94 | #endif | |
95 | #define SIZE 64 | |
96 | #define TYPE UNSIGNED | |
97 | #define OPERATION DIVISION | |
98 | #endif | |
99 | ||
100 | #ifdef L_reml | |
101 | #if SPECIAL_CALLING_CONVENTION | |
102 | #define FUNCTION_NAME __reml | |
103 | #else | |
104 | #define FUNCTION_NAME reml | |
105 | #endif | |
106 | #define SIZE 32 | |
107 | #define TYPE SIGNED | |
108 | #define OPERATION REMAINDER | |
109 | #endif | |
110 | ||
111 | #ifdef L_remlu | |
112 | #if SPECIAL_CALLING_CONVENTION | |
113 | #define FUNCTION_NAME __remlu | |
114 | #else | |
115 | #define FUNCTION_NAME remlu | |
116 | #endif | |
117 | #define SIZE 32 | |
118 | #define TYPE UNSIGNED | |
119 | #define OPERATION REMAINDER | |
120 | #endif | |
121 | ||
122 | #ifdef L_remq | |
123 | #if SPECIAL_CALLING_CONVENTION | |
124 | #define FUNCTION_NAME __remq | |
125 | #else | |
126 | #define FUNCTION_NAME remq | |
127 | #endif | |
128 | #define SIZE 64 | |
129 | #define TYPE SIGNED | |
130 | #define OPERATION REMAINDER | |
131 | #endif | |
132 | ||
133 | #ifdef L_remqu | |
134 | #if SPECIAL_CALLING_CONVENTION | |
135 | #define FUNCTION_NAME __remqu | |
136 | #else | |
137 | #define FUNCTION_NAME remqu | |
138 | #endif | |
139 | #define SIZE 64 | |
140 | #define TYPE UNSIGNED | |
141 | #define OPERATION REMAINDER | |
142 | #endif | |
143 | ||
144 | #define tmp0 $3 | |
145 | #define tmp1 $28 | |
146 | #define cnt $1 | |
147 | #define result_sign $2 | |
148 | ||
149 | #if SPECIAL_CALLING_CONVENTION | |
150 | #define N $24 | |
151 | #define D $25 | |
152 | #define Q RETREG | |
153 | #define RETREG $27 | |
154 | #else | |
155 | #define N $16 | |
156 | #define D $17 | |
157 | #define Q RETREG | |
158 | #define RETREG $0 | |
159 | #endif | |
160 | ||
161 | /* Misc symbols to make alpha assembler easier to read. */ | |
162 | #define zero $31 | |
163 | #define sp $30 | |
164 | ||
165 | /* Symbols to make interface nicer. */ | |
166 | #define UNSIGNED 0 | |
167 | #define SIGNED 1 | |
168 | #define DIVISION 0 | |
169 | #define REMAINDER 1 | |
170 | ||
171 | .set noreorder | |
172 | .set noat | |
173 | .text | |
174 | .align 3 | |
175 | .globl FUNCTION_NAME | |
176 | .ent FUNCTION_NAME | |
177 | FUNCTION_NAME: | |
178 | ||
179 | .frame $30,0,$26,0 | |
180 | .prologue 0 | |
181 | ||
182 | /* Under the special calling convention, we have to preserve all register | |
183 | values but $23 and $28. */ | |
184 | #if SPECIAL_CALLING_CONVENTION | |
185 | lda sp,-64(sp) | |
186 | #if OPERATION == DIVISION | |
187 | stq N,0(sp) | |
188 | #endif | |
189 | stq D,8(sp) | |
190 | stq cnt,16(sp) | |
191 | stq result_sign,24(sp) | |
192 | stq tmp0,32(sp) | |
193 | #endif | |
194 | ||
195 | /* If we are computing the remainder, move N to the register that is used | |
196 | for the return value, and redefine what register is used for N. */ | |
197 | #if OPERATION == REMAINDER | |
198 | bis N,N,RETREG | |
199 | #undef N | |
200 | #define N RETREG | |
201 | #endif | |
202 | ||
203 | /* Perform conversion from 32 bit types to 64 bit types. */ | |
204 | #if SIZE == 32 | |
205 | #if TYPE == SIGNED | |
206 | /* If there are problems with the signed case, add these instructions. | |
207 | The caller should already have done this. | |
208 | addl N,0,N # sign extend N | |
209 | addl D,0,D # sign extend D | |
210 | */ | |
211 | #else /* UNSIGNED */ | |
212 | zap N,0xf0,N # zero extend N (caller required to sign extend) | |
213 | zap D,0xf0,D # zero extend D | |
214 | #endif | |
215 | #endif | |
216 | ||
217 | /* Check for divide by zero. */ | |
218 | bne D,$34 | |
219 | lda $16,-2(zero) | |
220 | call_pal 0xaa | |
221 | $34: | |
222 | ||
223 | #if TYPE == SIGNED | |
224 | #if OPERATION == DIVISION | |
225 | xor N,D,result_sign | |
226 | #else | |
227 | bis N,N,result_sign | |
228 | #endif | |
229 | /* Get the absolute values of N and D. */ | |
230 | subq zero,N,tmp0 | |
231 | cmovlt N,tmp0,N | |
232 | subq zero,D,tmp0 | |
233 | cmovlt D,tmp0,D | |
234 | #endif | |
235 | ||
236 | /* Compute CNT = ceil(log2(N)) - ceil(log2(D)). This is the number of | |
237 | divide iterations we will have to perform. Should you wish to optimize | |
238 | this, check a few bits at a time, preferably using zap/zapnot. Be | |
239 | careful though, this code runs fast fro the most common cases, when the | |
240 | quotient is small. */ | |
241 | bge N,$35 | |
242 | bis zero,1,cnt | |
243 | blt D,$40 | |
244 | .align 3 | |
245 | $39: addq D,D,D | |
246 | addl cnt,1,cnt | |
247 | bge D,$39 | |
248 | br zero,$40 | |
249 | $35: cmpult N,D,tmp0 | |
250 | bis zero,zero,cnt | |
251 | bne tmp0,$42 | |
252 | .align 3 | |
253 | $44: addq D,D,D | |
254 | cmpult N,D,tmp0 | |
255 | addl cnt,1,cnt | |
256 | beq tmp0,$44 | |
257 | $42: srl D,1,D | |
258 | $40: | |
259 | subl cnt,1,cnt | |
260 | ||
261 | ||
262 | /* Actual divide. Could be optimized with unrolling. */ | |
263 | #if OPERATION == DIVISION | |
264 | bis zero,zero,Q | |
265 | #endif | |
266 | blt cnt,$46 | |
267 | .align 3 | |
268 | $49: cmpule D,N,tmp1 | |
269 | subq N,D,tmp0 | |
270 | srl D,1,D | |
271 | subl cnt,1,cnt | |
272 | cmovne tmp1,tmp0,N | |
273 | #if OPERATION == DIVISION | |
274 | addq Q,Q,Q | |
275 | bis Q,tmp1,Q | |
276 | #endif | |
277 | bge cnt,$49 | |
278 | $46: | |
279 | ||
280 | ||
281 | /* The result is now in RETREG. NOTE! It was written to RETREG using | |
282 | either N or Q as a synonym! */ | |
283 | ||
284 | ||
285 | /* Change the sign of the result as needed. */ | |
286 | #if TYPE == SIGNED | |
287 | subq zero,RETREG,tmp0 | |
288 | cmovlt result_sign,tmp0,RETREG | |
289 | #endif | |
290 | ||
291 | ||
292 | /* Restore clobbered registers. */ | |
293 | #if SPECIAL_CALLING_CONVENTION | |
294 | #if OPERATION == DIVISION | |
295 | ldq N,0(sp) | |
296 | #endif | |
297 | ldq D,8(sp) | |
298 | ldq cnt,16(sp) | |
299 | ldq result_sign,24(sp) | |
300 | ldq tmp0,32(sp) | |
301 | ||
302 | lda sp,64(sp) | |
303 | #endif | |
304 | ||
305 | ||
306 | /* Sign extend an *unsigned* 32 bit result, as required by the Alpha | |
307 | conventions. */ | |
308 | #if TYPE == UNSIGNED && SIZE == 32 | |
309 | /* This could be avoided by adding some CPP hair to the divide loop. | |
310 | It is probably not worth the added complexity. */ | |
311 | addl RETREG,0,RETREG | |
312 | #endif | |
313 | ||
314 | ||
315 | #if SPECIAL_CALLING_CONVENTION | |
316 | ret zero,($23),1 | |
317 | #else | |
318 | ret zero,($26),1 | |
319 | #endif | |
320 | .end FUNCTION_NAME |