1 /* id_fp.c: Interdata floating point instructions
3 Copyright (c) 2000-2005, Robert M. Supnik
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:
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
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.
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.
26 The Interdata uses IBM 360 floating point format:
29 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
30 |S| exponent | fraction | :single
31 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
32 | fraction low | :double
33 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
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
39 Numbers can be normalized or unnormalized but are always normalized
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.
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.
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.
58 struct ufp
{ /* unpacked fp */
59 int32 sign
; /* sign */
60 int32 exp
; /* unbiased exp */
61 uint32 h
; /* fr high */
62 uint32 l
; /* fr low */
65 #define FP_V_SIGN 31 /* sign */
67 #define FP_GETSIGN(x) (((x) >> FP_V_SIGN) & FP_M_SIGN)
68 #define FP_V_EXP 24 /* exponent */
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)
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
81 /* Double precision fraction add/subtract/compare */
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
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
89 #define FR_GE(s1,s2) ((s1.h > s2.h) || \
90 ((s1.h == s2.h) && (s1.l >= s2.l)))
92 /* Variable and constant shifts; for constants, 0 < k < 32 */
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; \
100 v.l = v.h >> ((s) - 32); \
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
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
112 #define Q_RND(op) (OP_DPFP (op) == 0)
113 #define Q_RND_AS(op) ((OP_DPFP (op) == 0) && fp_in_hwre)
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
);
128 /* Floating point load */
130 uint32
f_l (uint32 op
, uint32 r1
, uint32 r2
, uint32 ea
)
134 ReadFP2 (&fop2
, op
, r2
, ea
); /* get op, normalize */
135 return StoreFPR (&fop2
, op
, r1
, 0); /* store, chk unflo */
138 /* Floating point compare */
140 uint32
f_c (uint32 op
, uint32 r1
, uint32 r2
, uint32 ea
)
142 struct ufp fop1
, fop2
;
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
));
157 /* Floating to integer conversion */
159 uint32
f_fix (uint32 op
, uint32 r1
, uint32 r2
) /* 16b */
164 UnpackFPR (&res
, op
, r2
); /* get op2, norm */
165 if ((res
.h
== 0) || (res
.exp
< 0x41)) { /* result zero? */
169 if ((res
.exp
> 0x44) || /* result too big? */
170 ((res
.exp
== 0x44) && (res
.h
>= 0x00800000))) {
175 res
.h
= res
.h
>> ((0x46 - res
.exp
) * 4); /* right align frac */
179 R
[r1
] = ((res
.h
^ DMASK16
) + 1) & DMASK16
; /* negate result */
182 R
[r1
] = res
.h
& DMASK16
;
186 uint32
f_fix32 (uint32 op
, uint32 r1
, uint32 r2
) /* 32b */
191 UnpackFPR (&res
, op
, r2
); /* get op2, norm */
192 if ((res
.h
== 0) || (res
.exp
< 0x41)) { /* result zero? */
196 if ((res
.exp
> 0x48) || /* result too big? */
197 ((res
.exp
== 0x48) && (res
.h
>= 0x00800000))) {
202 FR_LSH_K (res
, 8); /* get all in 32b */
203 res
.h
= res
.h
>> ((0x48 - res
.exp
) * 4); /* right align frac */
207 R
[r1
] = (res
.h
^ DMASK32
) + 1; /* negate result */
214 /* Integer to floating conversion */
216 uint32
f_flt (uint32 op
, uint32 r1
, uint32 r2
) /* 16b */
218 struct ufp res
= { 0, 0x44, 0, 0 }; /* +, 16**4 */
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 */
228 res
.h
= R
[r2
] << 8; /* pos nz arg */
231 NormUFP (&res
); /* normalize */
232 StoreFPR (&res
, op
, r1
, 0); /* store result */
236 uint32
f_flt32 (uint32 op
, uint32 r1
, uint32 r2
) /* 32b */
238 struct ufp res
= { 0, 0x48, 0, 0 }; /* +, 16**8 */
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 */
248 else cc
= CC_G
; /* pos nz arg */
249 res
.h
= t
>> 8; /* hi frac */
250 res
.l
= t
<< 24; /* lo frac */
252 else cc
= 0; /* zero arg */
253 NormUFP (&res
); /* normalize */
254 StoreFPR (&res
, op
, r1
, 0); /* store result */
258 /* Floating point add/subtract */
260 uint32
f_as (uint32 op
, uint32 r1
, uint32 r2
, uint32 ea
)
262 struct ufp fop1
, fop2
, t
;
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 */
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 */
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 */
290 if (fop1
.sign
^ fop2
.sign
) { /* eff subtract */
291 FR_SUB (fop1
, fop2
); /* sub fractions */
292 NormUFP (&fop1
); /* normalize result */
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 */
302 return StoreFPR (&fop1
, op
, r1
, Q_RND_AS (op
)); /* store result */
305 /* Floating point multiply
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
316 int32
f_m (uint32 op
, uint32 r1
, uint32 r2
, uint32 ea
)
318 struct ufp fop1
, fop2
;
319 struct ufp res
= { 0, 0, 0, 0 };
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;
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
); }
341 fop2
.l
= fop2
.l
>> 1;
344 for (i
= 0; i
< 24; i
++) { /* do hi 24b */
345 if (fop2
.h
& 1) { FR_ADD (res
, fop1
); }
347 fop2
.h
= fop2
.h
>> 1;
350 NormUFP (&res
); /* normalize */
351 if (res
.exp
< 0) /* underflow? */
352 return StoreFPX (&res
, op
, r1
); /* early out */
354 return StoreFPR (&res
, op
, r1
, Q_RND (op
)); /* store */
357 /* Floating point divide - see overflow/underflow notes for multiply */
359 int32
f_d (uint32 op
, uint32 r1
, uint32 r2
, uint32 ea
)
361 struct ufp fop1
, fop2
;
362 struct ufp quo
= { 0, 0, 0, 0 };
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 */
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 */
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 */
387 FR_LSH_K (fop1
, 4); /* shift divd */
389 if (!OP_DPFP (op
)) { /* single? */
390 quo
.h
= quo
.l
; /* move quotient */
391 if (fop1
.h
>= (fop2
.h
<< 3)) quo
.l
= FP_ROUND
;
393 } /* don't need to normalize */
394 } /* end if fop1.h */
395 return StoreFPR (&quo
, op
, r1
, Q_RND (op
)); /* store result */
398 /* Utility routines */
400 /* Unpack floating point number */
402 void UnpackFPR (struct ufp
*fop
, uint32 op
, uint32 r1
)
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 */
411 hi
= ReadFReg (r1
); /* single prec */
412 fop
->l
= 0; /* lo is zero */
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 */
420 else fop
->sign
= fop
->exp
= 0; /* clean zero */
424 /* Read memory operand */
426 void ReadFP2 (struct ufp
*fop
, uint32 op
, uint32 r2
, uint32 ea
)
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 */
436 if (OP_DPFP (op
)) { /* RR */
437 hi
= D
[r2
>> 1].h
; /* dp? get dp reg */
438 fop
->l
= D
[r2
>> 1].l
;
441 hi
= ReadFReg (r2
); /* get sp reg */
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 */
451 else fop
->sign
= fop
->exp
= 0; /* clean zero */
455 /* Normalize unpacked floating point number */
457 void NormUFP (struct ufp
*fop
)
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;
466 else fop
->sign
= fop
->exp
= 0; /* clean 0 */
470 /* Round fp number, store, generate condition codes */
472 uint32
StoreFPR (struct ufp
*fop
, uint32 op
, uint32 r1
, uint32 rnd
)
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 */
483 if (fop
->h
== 0) { /* result 0? */
484 hi
= fop
->l
= 0; /* store clean 0 */
487 else if (fop
->exp
< 0) { /* underflow? */
488 hi
= fop
->l
= 0; /* store clean 0 */
491 else if (fop
->exp
> FP_M_EXP
) { /* overflow? */
492 hi
= (fop
->sign
)? 0xFFFFFFFF: 0x7FFFFFFF;
494 cc
= (CC_V
| ((fop
->sign
)? CC_L
: CC_G
));
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 */
502 if (OP_DPFP (op
)) { /* double precision? */
504 D
[r1
>> 1].l
= fop
->l
;
512 /* Generate exception result */
514 uint32
StoreFPX (struct ufp
*fop
, uint32 op
, uint32 r1
)
518 if (fop
->exp
< 0) fop
->h
= fop
->l
= 0; /* undf? clean 0 */
520 fop
->h
= (fop
->sign
)? 0xFFFFFFFF: 0x7FFFFFFF; /* overflow */
522 cc
= cc
| ((fop
->sign
)? CC_L
: CC_G
);
524 if (OP_DPFP (op
)) { /* double precision? */
525 D
[r1
>> 1].h
= fop
->h
;
526 D
[r1
>> 1].l
= fop
->l
;
529 WriteFReg (r1
, fop
->h
);