First Commit of my working state
[simh.git] / Interdata / id_fp.c
1 /* id_fp.c: Interdata floating point instructions
2
3 Copyright (c) 2000-2005, Robert M. Supnik
4
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22 Except as contained in this notice, the name of Robert M Supnik shall not be
23 used in advertising or otherwise to promote the sale, use or other dealings
24 in this Software without prior written authorization from Robert M Supnik.
25
26 The Interdata uses IBM 360 floating point format:
27
28 0 7 8 15 23 31
29 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
30 |S| exponent | fraction | :single
31 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
32 | fraction low | :double
33 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
34
35 where S = 0 for plus, 1 for minus
36 exponent = 16**n, in excess 64 code
37 fraction = .hhhhhh, seen as 6-14 hexadecimal digits
38
39 Numbers can be normalized or unnormalized but are always normalized
40 when loaded.
41
42 Interdata has 8 floating point registers, F0, F2, ... , FE. In floating
43 point instructions, the low order bit of the register number is ignored.
44
45 On floating point overflow, the exponent and fraction are set to 1's.
46 On floating point underflow, the exponent and fraction are set to 0's.
47
48 Interdata has both 32b only and 32b/64b floating point implementations.
49 In 32b only implementations, add and subtract are truncated, but multiply
50 and divide are rounded, and the floating point registers are kept in
51 memory. In 64b implementations, all single precision precision operations
52 are rounded, double precision operations are truncated, and the floating
53 point registers are kept in separate hardware arrays.
54 */
55
56 #include "id_defs.h"
57
58 struct ufp { /* unpacked fp */
59 int32 sign; /* sign */
60 int32 exp; /* unbiased exp */
61 uint32 h; /* fr high */
62 uint32 l; /* fr low */
63 };
64
65 #define FP_V_SIGN 31 /* sign */
66 #define FP_M_SIGN 0x1
67 #define FP_GETSIGN(x) (((x) >> FP_V_SIGN) & FP_M_SIGN)
68 #define FP_V_EXP 24 /* exponent */
69 #define FP_M_EXP 0x7F
70 #define FP_GETEXP(x) (((x) >> FP_V_EXP) & FP_M_EXP)
71 #define FP_V_FRH 0 /* fraction */
72 #define FP_M_FRH 0xFFFFFF
73 #define FP_GETFRH(x) (((x) >> FP_V_FRH) & FP_M_FRH)
74 #define FP_GETFRL(x) (x)
75
76 #define FP_BIAS 0x40 /* exp bias */
77 #define FP_CARRY (1 << FP_V_EXP ) /* carry out */
78 #define FP_NORM (0xF << (FP_V_EXP - 4)) /* normalized */
79 #define FP_ROUND 0x80000000
80
81 /* Double precision fraction add/subtract/compare */
82
83 #define FR_ADD(d,s) d.l = (d.l + s.l) & DMASK32; \
84 d.h = (d.h + s.h + (d.l < s.l)) & DMASK32
85
86 #define FR_SUB(d,s) d.h = (d.h - s.h - (d.l < s.l)) & DMASK32; \
87 d.l = (d.l - s.l) & DMASK32
88
89 #define FR_GE(s1,s2) ((s1.h > s2.h) || \
90 ((s1.h == s2.h) && (s1.l >= s2.l)))
91
92 /* Variable and constant shifts; for constants, 0 < k < 32 */
93
94 #define FR_RSH_V(v,s) if ((s) < 32) { \
95 v.l = ((v.l >> (s)) | \
96 (v.h << (32 - (s)))) & DMASK32; \
97 v.h = (v.h >> (s)) & DMASK32; \
98 } \
99 else { \
100 v.l = v.h >> ((s) - 32); \
101 v.h = 0; \
102 }
103
104 #define FR_RSH_K(v,s) v.l = ((v.l >> (s)) | \
105 (v.h << (32 - (s)))) & DMASK32; \
106 v.h = (v.h >> (s)) & DMASK32
107
108 #define FR_LSH_K(v,s) v.h = ((v.h << (s)) | \
109 (v.l >> (32 - (s)))) & DMASK32; \
110 v.l = (v.l << (s)) & DMASK32
111
112 #define Q_RND(op) (OP_DPFP (op) == 0)
113 #define Q_RND_AS(op) ((OP_DPFP (op) == 0) && fp_in_hwre)
114
115 extern uint32 *R;
116 extern uint32 F[8];
117 extern dpr_t D[8];
118 extern uint16 decrom[];
119 extern uint32 fp_in_hwre;
120 extern uint32 ReadF (uint32 loc, uint32 rel);
121 extern void WriteF (uint32 loc, uint32 dat, uint32 rel);
122 void ReadFP2 (struct ufp *fop, uint32 op, uint32 r2, uint32 ea);
123 void UnpackFPR (struct ufp *fop, uint32 op, uint32 r1);
124 void NormUFP (struct ufp *fop);
125 uint32 StoreFPR (struct ufp *fop, uint32 op, uint32 r1, uint32 rnd);
126 uint32 StoreFPX (struct ufp *fop, uint32 op, uint32 r1);
127
128 /* Floating point load */
129
130 uint32 f_l (uint32 op, uint32 r1, uint32 r2, uint32 ea)
131 {
132 struct ufp fop2;
133
134 ReadFP2 (&fop2, op, r2, ea); /* get op, normalize */
135 return StoreFPR (&fop2, op, r1, 0); /* store, chk unflo */
136 }
137
138 /* Floating point compare */
139
140 uint32 f_c (uint32 op, uint32 r1, uint32 r2, uint32 ea)
141 {
142 struct ufp fop1, fop2;
143
144 ReadFP2 (&fop2, op, r2, ea); /* get op2, norm */
145 UnpackFPR (&fop1, op, r1); /* get op1, norm */
146 if (fop1.sign ^ fop2.sign) /* signs differ? */
147 return (fop2.sign? CC_G: (CC_C | CC_L));
148 if (fop1.exp != fop2.exp) /* exps differ? */
149 return (((fop1.exp > fop2.exp) ^ fop1.sign)? CC_G: (CC_C | CC_L));
150 if (fop1.h != fop2.h) /* hi fracs differ? */
151 return (((fop1.h > fop2.h) ^ fop1.sign)? CC_G: (CC_C | CC_L));
152 if (OP_DPFP (op) && (fop1.l != fop2.l)) /* dp: low fracs diff? */
153 return (((fop1.l > fop2.l) ^ fop1.sign)? CC_G: (CC_C | CC_L));
154 return 0;
155 }
156
157 /* Floating to integer conversion */
158
159 uint32 f_fix (uint32 op, uint32 r1, uint32 r2) /* 16b */
160 {
161 struct ufp res;
162 uint32 cc;
163
164 UnpackFPR (&res, op, r2); /* get op2, norm */
165 if ((res.h == 0) || (res.exp < 0x41)) { /* result zero? */
166 R[r1] = 0;
167 return 0;
168 }
169 if ((res.exp > 0x44) || /* result too big? */
170 ((res.exp == 0x44) && (res.h >= 0x00800000))) {
171 res.h = MMASK16;
172 cc = CC_V;
173 }
174 else {
175 res.h = res.h >> ((0x46 - res.exp) * 4); /* right align frac */
176 cc = 0;
177 }
178 if (res.sign) {
179 R[r1] = ((res.h ^ DMASK16) + 1) & DMASK16; /* negate result */
180 return cc | CC_L;
181 }
182 R[r1] = res.h & DMASK16;
183 return cc | CC_G;
184 }
185
186 uint32 f_fix32 (uint32 op, uint32 r1, uint32 r2) /* 32b */
187 {
188 struct ufp res;
189 uint32 cc;
190
191 UnpackFPR (&res, op, r2); /* get op2, norm */
192 if ((res.h == 0) || (res.exp < 0x41)) { /* result zero? */
193 R[r1] = 0;
194 return 0;
195 }
196 if ((res.exp > 0x48) || /* result too big? */
197 ((res.exp == 0x48) && (res.h >= 0x00800000))) {
198 res.h = MMASK32;
199 cc = CC_V;
200 }
201 else {
202 FR_LSH_K (res, 8); /* get all in 32b */
203 res.h = res.h >> ((0x48 - res.exp) * 4); /* right align frac */
204 cc = 0;
205 }
206 if (res.sign) {
207 R[r1] = (res.h ^ DMASK32) + 1; /* negate result */
208 return cc | CC_L;
209 }
210 R[r1] = res.h;
211 return cc | CC_G;
212 }
213
214 /* Integer to floating conversion */
215
216 uint32 f_flt (uint32 op, uint32 r1, uint32 r2) /* 16b */
217 {
218 struct ufp res = { 0, 0x44, 0, 0 }; /* +, 16**4 */
219 uint32 cc;
220
221 if (R[r2] == 0) cc = 0; /* zero arg? */
222 else if (R[r2] & SIGN16) { /* neg arg? */
223 res.sign = FP_M_SIGN; /* set sign */
224 res.h = ((~R[r2] + 1) & DMASK16) << 8; /* get magnitude */
225 cc = CC_L;
226 }
227 else {
228 res.h = R[r2] << 8; /* pos nz arg */
229 cc = CC_G;
230 }
231 NormUFP (&res); /* normalize */
232 StoreFPR (&res, op, r1, 0); /* store result */
233 return cc;
234 }
235
236 uint32 f_flt32 (uint32 op, uint32 r1, uint32 r2) /* 32b */
237 {
238 struct ufp res = { 0, 0x48, 0, 0 }; /* +, 16**8 */
239 uint32 cc, t;
240
241 t = R[r2]; /* int op */
242 if (t) { /* nonzero arg? */
243 if (t & SIGN32) { /* neg arg? */
244 res.sign = FP_M_SIGN; /* set sign */
245 t = (~t + 1) & DMASK32; /* get magnitude */
246 cc = CC_L;
247 }
248 else cc = CC_G; /* pos nz arg */
249 res.h = t >> 8; /* hi frac */
250 res.l = t << 24; /* lo frac */
251 }
252 else cc = 0; /* zero arg */
253 NormUFP (&res); /* normalize */
254 StoreFPR (&res, op, r1, 0); /* store result */
255 return cc;
256 }
257
258 /* Floating point add/subtract */
259
260 uint32 f_as (uint32 op, uint32 r1, uint32 r2, uint32 ea)
261 {
262 struct ufp fop1, fop2, t;
263 int32 ediff;
264
265 ReadFP2 (&fop2, op, r2, ea); /* get op2, norm */
266 UnpackFPR (&fop1, op, r1); /* get op1, norm */
267 if (op & 1) fop2.sign = fop2.sign ^ 1; /* if sub, inv sign2 */
268 if (fop1.h == 0) fop1 = fop2; /* if op1 = 0, res = op2 */
269 else if (fop2.h != 0) { /* if op2 = 0, no add */
270 if ((fop1.exp < fop2.exp) || /* |op1| < |op2|? */
271 ((fop1.exp == fop2.exp) &&
272 ((fop1.h < fop2.h) ||
273 ((fop1.h == fop2.h) && (fop1.l < fop2.l))))) {
274 t = fop2; /* swap operands */
275 fop2 = fop1;
276 fop1 = t;
277 }
278 ediff = fop1.exp - fop2.exp; /* exp difference */
279 if (OP_DPFP (op) || fp_in_hwre) { /* dbl prec or hwre? */
280 if (ediff >= 14) fop2.h = fop2.l = 0; /* diff too big? */
281 else if (ediff) { /* any difference? */
282 FR_RSH_V (fop2, ediff * 4); /* shift frac */
283 }
284 }
285 else { /* sgl prec ucode */
286 if (ediff >= 6) fop2.h = 0; /* diff too big? */
287 else if (ediff) /* any difference? */
288 fop2.h = fop2.h >> (ediff * 4); /* shift frac */
289 }
290 if (fop1.sign ^ fop2.sign) { /* eff subtract */
291 FR_SUB (fop1, fop2); /* sub fractions */
292 NormUFP (&fop1); /* normalize result */
293 }
294 else {
295 FR_ADD (fop1, fop2); /* add fractions */
296 if (fop1.h & FP_CARRY) { /* carry out? */
297 FR_RSH_K (fop1, 4); /* renormalize */
298 fop1.exp = fop1.exp + 1; /* incr exp */
299 }
300 }
301 } /* end if fop2 */
302 return StoreFPR (&fop1, op, r1, Q_RND_AS (op)); /* store result */
303 }
304
305 /* Floating point multiply
306
307 Notes:
308 - Exponent overflow/underflow is tested right after the exponent
309 add, without regard to potential changes due to normalization
310 - Exponent underflow is tested right after normalization, without
311 regard to changes due to rounding
312 - Single precision hardware multiply may generate up to 48b
313 - Double precision multiply generates 56b with no guard bits
314 */
315
316 int32 f_m (uint32 op, uint32 r1, uint32 r2, uint32 ea)
317 {
318 struct ufp fop1, fop2;
319 struct ufp res = { 0, 0, 0, 0 };
320 uint32 i;
321
322 ReadFP2 (&fop2, op, r2, ea); /* get op2, norm */
323 UnpackFPR (&fop1, op, r1); /* get op1, norm */
324 if (fop1.h && fop2.h) { /* if both != 0 */
325 res.sign = fop1.sign ^ fop2.sign; /* sign = diff */
326 res.exp = fop1.exp + fop2.exp - FP_BIAS; /* exp = sum */
327 if ((res.exp < 0) || (res.exp > FP_M_EXP)) /* ovf/undf? */
328 return StoreFPX (&res, op, r1); /* early out */
329 if ((fop1.l | fop2.l) == 0) { /* 24b x 24b? */
330 for (i = 0; i < 24; i++) { /* 24 iterations */
331 if (fop2.h & 1) res.h = res.h + fop1.h; /* add hi only */
332 FR_RSH_K (res, 1); /* shift dp res */
333 fop2.h = fop2.h >> 1;
334 }
335 }
336 else { /* some low 0's */
337 if (fop2.l != 0) { /* 56b x 56b? */
338 for (i = 0; i < 32; i++) { /* do low 32b */
339 if (fop2.l & 1) { FR_ADD (res, fop1); }
340 FR_RSH_K (res, 1);
341 fop2.l = fop2.l >> 1;
342 }
343 }
344 for (i = 0; i < 24; i++) { /* do hi 24b */
345 if (fop2.h & 1) { FR_ADD (res, fop1); }
346 FR_RSH_K (res, 1);
347 fop2.h = fop2.h >> 1;
348 }
349 }
350 NormUFP (&res); /* normalize */
351 if (res.exp < 0) /* underflow? */
352 return StoreFPX (&res, op, r1); /* early out */
353 }
354 return StoreFPR (&res, op, r1, Q_RND (op)); /* store */
355 }
356
357 /* Floating point divide - see overflow/underflow notes for multiply */
358
359 int32 f_d (uint32 op, uint32 r1, uint32 r2, uint32 ea)
360 {
361 struct ufp fop1, fop2;
362 struct ufp quo = { 0, 0, 0, 0 };
363 int32 i;
364
365 ReadFP2 (&fop2, op, r2, ea); /* get op2, norm */
366 UnpackFPR (&fop1, op, r1); /* get op1, norm */
367 if (fop2.h == 0) return CC_C | CC_V; /* div by zero? */
368 if (fop1.h) { /* dvd != 0? */
369 quo.sign = fop1.sign ^ fop2.sign; /* sign = diff */
370 quo.exp = fop1.exp - fop2.exp + FP_BIAS; /* exp = diff */
371 if ((quo.exp < 0) || (quo.exp > FP_M_EXP)) /* ovf/undf? */
372 return StoreFPX (&quo, op, r1); /* early out */
373 if (!FR_GE (fop1, fop2)) {
374 FR_LSH_K (fop1, 4); /* ensure success */
375 }
376 else { /* exp off by 1 */
377 quo.exp = quo.exp + 1; /* incr exponent */
378 if (quo.exp > FP_M_EXP) /* overflow? */
379 return StoreFPX (&quo, op, r1); /* early out */
380 }
381 for (i = 0; i < (OP_DPFP (op)? 14: 6); i++) { /* 6/14 hex digits */
382 FR_LSH_K (quo, 4); /* shift quotient */
383 while (FR_GE (fop1, fop2)) { /* while sub works */
384 FR_SUB (fop1, fop2); /* decrement */
385 quo.l = quo.l + 1; /* add quo bit */
386 }
387 FR_LSH_K (fop1, 4); /* shift divd */
388 }
389 if (!OP_DPFP (op)) { /* single? */
390 quo.h = quo.l; /* move quotient */
391 if (fop1.h >= (fop2.h << 3)) quo.l = FP_ROUND;
392 else quo.l = 0;
393 } /* don't need to normalize */
394 } /* end if fop1.h */
395 return StoreFPR (&quo, op, r1, Q_RND (op)); /* store result */
396 }
397
398 /* Utility routines */
399
400 /* Unpack floating point number */
401
402 void UnpackFPR (struct ufp *fop, uint32 op, uint32 r1)
403 {
404 uint32 hi;
405
406 if (OP_DPFP (op)) { /* double prec? */
407 hi = D[r1 >> 1].h; /* get hi */
408 fop->l = FP_GETFRL (D[r1 >> 1].l); /* get lo */
409 }
410 else {
411 hi = ReadFReg (r1); /* single prec */
412 fop->l = 0; /* lo is zero */
413 }
414 fop->h = FP_GETFRH (hi); /* get hi frac */
415 if (fop->h || fop->l) { /* non-zero? */
416 fop->sign = FP_GETSIGN (hi); /* get sign */
417 fop->exp = FP_GETEXP (hi); /* get exp */
418 NormUFP (fop); /* normalize */
419 }
420 else fop->sign = fop->exp = 0; /* clean zero */
421 return;
422 }
423
424 /* Read memory operand */
425
426 void ReadFP2 (struct ufp *fop, uint32 op, uint32 r2, uint32 ea)
427 {
428 uint32 hi;
429
430 if (OP_TYPE (op) > OP_RR) { /* mem ref? */
431 hi = ReadF (ea, VR); /* get hi */
432 if (OP_DPFP (op)) fop->l = ReadF (ea + 4, VR); /* dp? get lo */
433 else fop->l = 0; /* sp, lo = 0 */
434 }
435 else {
436 if (OP_DPFP (op)) { /* RR */
437 hi = D[r2 >> 1].h; /* dp? get dp reg */
438 fop->l = D[r2 >> 1].l;
439 }
440 else {
441 hi = ReadFReg (r2); /* get sp reg */
442 fop->l = 0;
443 }
444 }
445 fop->h = FP_GETFRH (hi); /* get hi frac */
446 if (fop->h || fop->l) { /* non-zero? */
447 fop->sign = FP_GETSIGN (hi); /* get sign */
448 fop->exp = FP_GETEXP (hi); /* get exp */
449 NormUFP (fop); /* normalize */
450 }
451 else fop->sign = fop->exp = 0; /* clean zero */
452 return;
453 }
454
455 /* Normalize unpacked floating point number */
456
457 void NormUFP (struct ufp *fop)
458 {
459 if ((fop->h & FP_M_FRH) || fop->l) { /* any fraction? */
460 while ((fop->h & FP_NORM) == 0) { /* until norm */
461 fop->h = (fop->h << 4) | ((fop->l >> 28) & 0xF);
462 fop->l = fop->l << 4;
463 fop->exp = fop->exp - 1;
464 }
465 }
466 else fop->sign = fop->exp = 0; /* clean 0 */
467 return;
468 }
469
470 /* Round fp number, store, generate condition codes */
471
472 uint32 StoreFPR (struct ufp *fop, uint32 op, uint32 r1, uint32 rnd)
473 {
474 uint32 hi, cc;
475
476 if (rnd && (fop->l & FP_ROUND)) { /* round? */
477 fop->h = fop->h + 1; /* add 1 to frac */
478 if (fop->h & FP_CARRY) { /* carry out? */
479 fop->h = fop->h >> 4; /* renormalize */
480 fop->exp = fop->exp + 1; /* incr exp */
481 }
482 }
483 if (fop->h == 0) { /* result 0? */
484 hi = fop->l = 0; /* store clean 0 */
485 cc = 0;
486 }
487 else if (fop->exp < 0) { /* underflow? */
488 hi = fop->l = 0; /* store clean 0 */
489 cc = CC_V;
490 }
491 else if (fop->exp > FP_M_EXP) { /* overflow? */
492 hi = (fop->sign)? 0xFFFFFFFF: 0x7FFFFFFF;
493 fop->l = 0xFFFFFFFF;
494 cc = (CC_V | ((fop->sign)? CC_L: CC_G));
495 }
496 else {
497 hi = ((fop->sign & FP_M_SIGN) << FP_V_SIGN) | /* pack result */
498 ((fop->exp & FP_M_EXP) << FP_V_EXP) |
499 ((fop->h & FP_M_FRH) << FP_V_FRH);
500 cc = (fop->sign)? CC_L: CC_G; /* set cc's */
501 }
502 if (OP_DPFP (op)) { /* double precision? */
503 D[r1 >> 1].h = hi;
504 D[r1 >> 1].l = fop->l;
505 }
506 else {
507 WriteFReg (r1, hi);
508 }
509 return cc;
510 }
511
512 /* Generate exception result */
513
514 uint32 StoreFPX (struct ufp *fop, uint32 op, uint32 r1)
515 {
516 uint32 cc = CC_V;
517
518 if (fop->exp < 0) fop->h = fop->l = 0; /* undf? clean 0 */
519 else {
520 fop->h = (fop->sign)? 0xFFFFFFFF: 0x7FFFFFFF; /* overflow */
521 fop->l = 0xFFFFFFFF;
522 cc = cc | ((fop->sign)? CC_L: CC_G);
523 }
524 if (OP_DPFP (op)) { /* double precision? */
525 D[r1 >> 1].h = fop->h;
526 D[r1 >> 1].l = fop->l;
527 }
528 else {
529 WriteFReg (r1, fop->h);
530 }
531 return cc;
532 }