1 /*
2 * reg_add_sub.c
3 *
4 * Functions to add or subtract two registers and put the result in a third.
5 *
6 * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond,
7 * Vic 3163, Australia.
8 * E-mail apm233m@vaxc.cc.monash.edu.au
9 * All rights reserved.
10 *
11 * This copyright notice covers the redistribution and use of the
12 * FPU emulator developed by W. Metzenthen. It covers only its use
13 * in the 386BSD operating system. Any other use is not permitted
14 * under this copyright.
15 *
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
18 * are met:
19 * 1. Redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must include information specifying
22 * that source code for the emulator is freely available and include
23 * either:
24 * a) an offer to provide the source code for a nominal distribution
25 * fee, or
26 * b) list at least two alternative methods whereby the source
27 * can be obtained, e.g. a publically accessible bulletin board
28 * and an anonymous ftp site from which the software can be
29 * downloaded.
30 * 3. All advertising materials specifically mentioning features or use of
31 * this emulator must acknowledge that it was developed by W. Metzenthen.
32 * 4. The name of W. Metzenthen may not be used to endorse or promote
33 * products derived from this software without specific prior written
34 * permission.
35 *
36 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
37 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
38 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
39 * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
40 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
41 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
42 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
43 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
44 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
45 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
46 *
47 */
48
49 /*---------------------------------------------------------------------------+
50 | For each function, the destination may be any FPU_REG, including one of |
51 | the source FPU_REGs. |
52 +---------------------------------------------------------------------------*/
53
54 #include "exception.h"
55 #include "reg_constant.h"
56 #include "fpu_emu.h"
57 #include "control_w.h"
58 #include "fpu_system.h"
59
60
reg_add(FPU_REG * a,FPU_REG * b,FPU_REG * dest,int control_w)61 void reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
62 {
63 int diff;
64
65 if ( !(a->tag | b->tag) )
66 {
67 /* Both registers are valid */
68 if (!(a->sign ^ b->sign))
69 {
70 /* signs are the same */
71 reg_u_add(a, b, dest, control_w);
72 dest->sign = a->sign;
73 return;
74 }
75
76 /* The signs are different, so do a subtraction */
77 diff = a->exp - b->exp;
78 if (!diff)
79 {
80 diff = a->sigh - b->sigh; /* Works only if ms bits are identical */
81 if (!diff)
82 {
83 diff = a->sigl > b->sigl;
84 if (!diff)
85 diff = -(a->sigl < b->sigl);
86 }
87 }
88
89 if (diff > 0)
90 {
91 reg_u_sub(a, b, dest, control_w);
92 dest->sign = a->sign;
93 }
94 else if ( diff == 0 )
95 {
96 reg_move(&CONST_Z, dest);
97 /* sign depends upon rounding mode */
98 dest->sign = ((control_w & CW_RC) != RC_DOWN)
99 ? SIGN_POS : SIGN_NEG;
100 }
101 else
102 {
103 reg_u_sub(b, a, dest, control_w);
104 dest->sign = b->sign;
105 }
106 return;
107 }
108 else
109 {
110 if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
111 { real_2op_NaN(a, b, dest); return; }
112 else if (a->tag == TW_Zero)
113 {
114 if (b->tag == TW_Zero)
115 {
116 char different_signs = a->sign ^ b->sign;
117 /* Both are zero, result will be zero. */
118 reg_move(a, dest);
119 if (different_signs)
120 {
121 /* Signs are different. */
122 /* Sign of answer depends upon rounding mode. */
123 dest->sign = ((control_w & CW_RC) != RC_DOWN)
124 ? SIGN_POS : SIGN_NEG;
125 }
126 }
127 else
128 {
129 #ifdef DENORM_OPERAND
130 if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
131 denormal_operand() )
132 return;
133 #endif DENORM_OPERAND
134 reg_move(b, dest);
135 }
136 return;
137 }
138 else if (b->tag == TW_Zero)
139 {
140 #ifdef DENORM_OPERAND
141 if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
142 denormal_operand() )
143 return;
144 #endif DENORM_OPERAND
145 reg_move(a, dest); return;
146 }
147 else if (a->tag == TW_Infinity)
148 {
149 if (b->tag != TW_Infinity)
150 {
151 #ifdef DENORM_OPERAND
152 if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
153 denormal_operand() )
154 return;
155 #endif DENORM_OPERAND
156 reg_move(a, dest); return;
157 }
158 if (a->sign == b->sign)
159 {
160 /* They are both + or - infinity */
161 reg_move(a, dest); return;
162 }
163 arith_invalid(dest); /* Infinity-Infinity is undefined. */
164 return;
165 }
166 else if (b->tag == TW_Infinity)
167 {
168 #ifdef DENORM_OPERAND
169 if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
170 denormal_operand() )
171 return;
172 #endif DENORM_OPERAND
173 reg_move(b, dest); return;
174 }
175 }
176 #ifdef PARANOID
177 EXCEPTION(EX_INTERNAL|0x101);
178 #endif
179 }
180
181
182 /* Subtract b from a. (a-b) -> dest */
reg_sub(FPU_REG * a,FPU_REG * b,FPU_REG * dest,int control_w)183 void reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
184 {
185 int diff;
186
187 if ( !(a->tag | b->tag) )
188 {
189 /* Both registers are valid */
190 diff = a->exp - b->exp;
191 if (!diff)
192 {
193 diff = a->sigh - b->sigh; /* Works only if ms bits are identical */
194 if (!diff)
195 {
196 diff = a->sigl > b->sigl;
197 if (!diff)
198 diff = -(a->sigl < b->sigl);
199 }
200 }
201
202 switch (a->sign*2 + b->sign)
203 {
204 case 0: /* P - P */
205 case 3: /* N - N */
206 if (diff > 0)
207 {
208 reg_u_sub(a, b, dest, control_w);
209 dest->sign = a->sign;
210 }
211 else if ( diff == 0 )
212 {
213 #ifdef DENORM_OPERAND
214 if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
215 denormal_operand() )
216 return;
217 #endif DENORM_OPERAND
218 reg_move(&CONST_Z, dest);
219 /* sign depends upon rounding mode */
220 dest->sign = ((control_w & CW_RC) != RC_DOWN)
221 ? SIGN_POS : SIGN_NEG;
222 }
223 else
224 {
225 reg_u_sub(b, a, dest, control_w);
226 dest->sign = a->sign ^ SIGN_POS^SIGN_NEG;
227 }
228 return;
229 case 1: /* P - N */
230 reg_u_add(a, b, dest, control_w);
231 dest->sign = SIGN_POS;
232 return;
233 case 2: /* N - P */
234 reg_u_add(a, b, dest, control_w);
235 dest->sign = SIGN_NEG;
236 return;
237 }
238 }
239 else
240 {
241 if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
242 { real_2op_NaN(a, b, dest); return; }
243 else if (b->tag == TW_Zero)
244 {
245 if (a->tag == TW_Zero)
246 {
247 char same_signs = !(a->sign ^ b->sign);
248 /* Both are zero, result will be zero. */
249 reg_move(a, dest); /* Answer for different signs. */
250 if (same_signs)
251 {
252 /* Sign depends upon rounding mode */
253 dest->sign = ((control_w & CW_RC) != RC_DOWN)
254 ? SIGN_POS : SIGN_NEG;
255 }
256 }
257 else
258 {
259 #ifdef DENORM_OPERAND
260 if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
261 denormal_operand() )
262 return;
263 #endif DENORM_OPERAND
264 reg_move(a, dest);
265 }
266 return;
267 }
268 else if (a->tag == TW_Zero)
269 {
270 #ifdef DENORM_OPERAND
271 if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
272 denormal_operand() )
273 return;
274 #endif DENORM_OPERAND
275 reg_move(b, dest);
276 dest->sign ^= SIGN_POS^SIGN_NEG;
277 return;
278 }
279 else if (a->tag == TW_Infinity)
280 {
281 if (b->tag != TW_Infinity)
282 {
283 #ifdef DENORM_OPERAND
284 if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
285 denormal_operand() )
286 return;
287 #endif DENORM_OPERAND
288 reg_move(a, dest); return;
289 }
290 /* Both args are Infinity */
291 if (a->sign == b->sign)
292 {
293 arith_invalid(dest); /* Infinity-Infinity is undefined. */
294 return;
295 }
296 reg_move(a, dest);
297 return;
298 }
299 else if (b->tag == TW_Infinity)
300 {
301 #ifdef DENORM_OPERAND
302 if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
303 denormal_operand() )
304 return;
305 #endif DENORM_OPERAND
306 reg_move(b, dest);
307 dest->sign ^= SIGN_POS^SIGN_NEG;
308 return;
309 }
310 }
311 #ifdef PARANOID
312 EXCEPTION(EX_INTERNAL|0x110);
313 #endif
314 }
315
316