blob: ae5cae4ddea51959b0e196d871de7b6ed56781d2 [file] [log] [blame] [raw]
Bob Supnik4d6dfa42001-11-06 20:50:00 -08001/* pdp10_mdfp.c: PDP-10 multiply/divide and floating point simulator
2
Mark Pizzolato820d77e2016-05-05 03:50:21 -07003 Copyright (c) 1993-2016, Robert M Supnik
Bob Supnik4d6dfa42001-11-06 20:50:00 -08004
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
Bob Supnikb7c1eae2005-09-09 18:09:00 -070022 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
Bob Supnik4d6dfa42001-11-06 20:50:00 -080024 in this Software without prior written authorization from Robert M Supnik.
Bob Supnik26aa6de2004-04-06 05:17:00 -070025
Mark Pizzolato820d77e2016-05-05 03:50:21 -070026 05-May-16 RMS Fixed bug in DMUL carry (Mark Pizzolato)
Bob Supnikb7c1eae2005-09-09 18:09:00 -070027 2-Apr-04 RMS Fixed bug in floating point unpack
28 Fixed bug in FIXR (found by Phil Stone, fixed by
29 Chris Smith)
30 31-Aug-01 RMS Changed int64 to t_int64 for Windoze
31 10-Aug-01 RMS Removed register in declarations
Bob Supnik4d6dfa42001-11-06 20:50:00 -080032
33 Instructions handled in this module:
Bob Supnikb7c1eae2005-09-09 18:09:00 -070034 imul integer multiply
35 idiv integer divide
36 mul multiply
37 div divide
38 dmul double precision multiply
39 ddiv double precision divide
40 fad(r) floating add (and round)
41 fsb(r) floating subtract (and round)
42 fmp(r) floating multiply (and round)
43 fdv(r) floating divide and round
44 fsc floating scale
45 fix(r) floating to fixed (and round)
46 fltr fixed to floating and round
47 dfad double precision floating add/subtract
48 dfmp double precision floating multiply
49 dfdv double precision floating divide
Bob Supnik4d6dfa42001-11-06 20:50:00 -080050
51 The PDP-10 stores double (quad) precision integers in sequential
52 AC's or memory locations. Integers are stored in 2's complement
53 form. Only the sign of the high order word matters; the signs
54 in low order words are ignored on input and set to the sign of
55 the result on output. Quad precision integers exist only in the
56 AC's as the result of a DMUL or the dividend of a DDIV.
57
58 0 00000000011111111112222222222333333
59 0 12345678901234567890123456789012345
60 +-+-----------------------------------+
61 |S| high order integer | AC(n), A
62 +-+-----------------------------------+
63 |S| low order integer | AC(n + 1), A + 1
64 +-+-----------------------------------+
65 |S| low order integer | AC(n + 2)
66 +-+-----------------------------------+
67 |S| low order integer | AC(n + 3)
68 +-+-----------------------------------+
69
70 The PDP-10 supports two floating point formats: single and double
71 precision. In both, the exponent is 8 bits, stored in excess
72 128 notation. The fraction is expected to be normalized. A
73 single precision floating point number has 27 bits of fraction;
74 a double precision number has 62 bits of fraction (the sign
75 bit of the second word is ignored and is set to zero).
76
77 In a negative floating point number, the exponent is stored in
78 one's complement form, the fraction in two's complement form.
79
80 0 00000000 011111111112222222222333333
81 0 12345678 901234567890123456789012345
82 +-+--------+---------------------------+
83 |S|exponent| high order fraction | AC(n), A
84 +-+--------+---------------------------+
85 |0| low order fraction | AC(n + 1), A + 1
86 +-+------------------------------------+
87
88 Note that treatment of the sign is different for double precision
89 integers and double precision floating point. DMOVN (implemented
90 as an inline macro) follows floating point conventions.
91
92 The original PDP-10 CPU (KA10) used a different format for double
93 precision numbers and included certain instructions to make
94 software support easier. These instructions were phased out in
95 the KL10 and KS10 and are treated as MUUO's.
96
97 The KL10 added extended precision (11-bit exponent) floating point
98 format (so-called G floating). These instructions were not
99 implemented in the KS10 and are treated as MUUO's.
100*/
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700101
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800102#include "pdp10_defs.h"
103#include <setjmp.h>
104
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700105typedef struct { /* unpacked fp number */
106 int32 sign; /* sign */
107 int32 exp; /* exponent */
108 t_uint64 fhi; /* fraction high */
109 t_uint64 flo; /* for double prec */
110 } UFP;
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800111
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700112#define MSK32 0xFFFFFFFF
113#define FIT27 (DMASK - 0x07FFFFFF)
114#define FIT32 (DMASK - MSK32)
115#define SFRC TRUE /* frac 2's comp */
116#define AFRC FALSE /* frac abs value */
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800117
118/* In packed floating point number */
119
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700120#define FP_BIAS 0200 /* exponent bias */
121#define FP_N_FHI 27 /* # of hi frac bits */
122#define FP_V_FHI 0 /* must be zero */
Mark Pizzolatoc93658f2013-04-05 12:34:37 -0700123#define FP_M_FHI INT64_C(0000777777777)
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700124#define FP_N_EXP 8 /* # of exp bits */
125#define FP_V_EXP (FP_V_FHI + FP_N_FHI)
126#define FP_M_EXP 0377
127#define FP_V_SIGN (FP_V_EXP + FP_N_EXP) /* sign */
128#define FP_N_FLO 35 /* # of lo frac bits */
129#define FP_V_FLO 0 /* must be zero */
Mark Pizzolatoc93658f2013-04-05 12:34:37 -0700130#define FP_M_FLO INT64_C(0377777777777)
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700131#define GET_FPSIGN(x) ((int32) (((x) >> FP_V_SIGN) & 1))
132#define GET_FPEXP(x) ((int32) (((x) >> FP_V_EXP) & FP_M_EXP))
133#define GET_FPHI(x) ((x) & FP_M_FHI)
134#define GET_FPLO(x) ((x) & FP_M_FLO)
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800135
136/* In unpacked floating point number */
137
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700138#define FP_N_GUARD 1 /* # of guard bits */
139#define FP_V_UFLO FP_N_GUARD /* <35:1> */
140#define FP_V_URNDD (FP_V_UFLO - 1) /* dp round bit */
141#define FP_V_UFHI (FP_V_UFLO + FP_N_FLO) /* <62:36> */
142#define FP_V_URNDS (FP_V_UFHI - 1) /* sp round bit */
143#define FP_V_UCRY (FP_V_UFHI + FP_N_FHI) /* <63> */
144#define FP_V_UNORM (FP_V_UCRY - 1) /* normalized bit */
Mark Pizzolatoc93658f2013-04-05 12:34:37 -0700145#define FP_UFHI INT64_C(0x7FFFFFF000000000)
146#define FP_UFLO INT64_C(0x0000000FFFFFFFFE)
147#define FP_UFRAC INT64_C(0x7FFFFFFFFFFFFFFE)
148#define FP_URNDD INT64_C(0x0000000000000001)
149#define FP_URNDS INT64_C(0x0000000800000000)
150#define FP_UNORM INT64_C(0x4000000000000000)
151#define FP_UCRY INT64_C(0x8000000000000000)
152#define FP_ONES INT64_C(0xFFFFFFFFFFFFFFFF)
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800153
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700154#define UNEG(x) ((~x) + 1)
155#define DUNEG(x) x.flo = UNEG (x.flo); x.fhi = ~x.fhi + (x.flo == 0)
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800156
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800157void mul (d10 a, d10 b, d10 *rs);
158void funpack (d10 h, d10 l, UFP *r, t_bool sgn);
Bob Supnik654937f2001-11-06 21:02:00 -0800159void fnorm (UFP *r, t_int64 rnd);
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800160d10 fpack (UFP *r, d10 *lo, t_bool fdvneg);
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700161
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800162/* Integer multiply - checked against KS-10 ucode */
163
164d10 imul (d10 a, d10 b)
165{
166d10 rs[2];
167
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700168if ((a == SIGN) && (b == SIGN)) { /* KS10 hack */
169 SETF (F_AOV | F_T1); /* -2**35 squared */
170 return SIGN;
171 }
172mul (a, b, rs); /* mpy, dprec result */
173if (rs[0] && (rs[0] != ONES)) { /* high not all sign? */
174 rs[1] = TSTS (a ^ b)? SETS (rs[1]): CLRS (rs[1]); /* set sign */
175 SETF (F_AOV | F_T1); /* overflow */
176 }
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800177return rs[1];
178}
179
180/* Integer divide, return quotient, remainder - checked against KS10 ucode
181 The KS10 does not recognize -2^35/-1 as an error. Instead, it produces
182 2^35 (that is, -2^35) as the incorrect result.
183*/
184
185t_bool idiv (d10 a, d10 b, d10 *rs)
186{
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700187d10 dvd = ABS (a); /* make ops positive */
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800188d10 dvr = ABS (b);
189
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700190if (dvr == 0) { /* divide by 0? */
191 SETF (F_DCK | F_AOV | F_T1); /* set flags, return */
192 return FALSE;
193 }
194rs[0] = dvd / dvr; /* get quotient */
195rs[1] = dvd % dvr; /* get remainder */
Bob Supnik9c4779c2009-02-08 09:06:00 -0800196if (TSTS (a ^ b)) /* sign of result */
197 rs[0] = NEG (rs[0]);
198if (TSTS (a)) /* sign of remainder */
199 rs[1] = NEG (rs[1]);
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800200return TRUE;
201}
202
203/* Multiply, return double precision result - checked against KS10 ucode */
204
205void mul (d10 s1, d10 s2, d10 *rs)
206{
Bob Supnik654937f2001-11-06 21:02:00 -0800207t_uint64 a = ABS (s1);
208t_uint64 b = ABS (s2);
209t_uint64 t, u, r;
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800210
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700211if ((a == 0) || (b == 0)) { /* operand = 0? */
212 rs[0] = rs[1] = 0; /* result 0 */
213 return;
214 }
215if ((a & FIT32) || (b & FIT32)) { /* fit in 64b? */
216 t = a >> 18; /* no, split in half */
217 a = a & RMASK; /* "dp" multiply */
218 u = b >> 18;
219 b = b & RMASK;
220 r = (a * b) + (((a * u) + (b * t)) << 18); /* low is only 35b */
221 rs[0] = ((t * u) << 1) + (r >> 35); /* so lsh hi 1 */
222 rs[1] = r & MMASK;
223 }
224else {
225 r = a * b; /* fits, native mpy */
226 rs[0] = r >> 35; /* split at bit 35 */
227 rs[1] = r & MMASK;
228 }
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800229
Bob Supnik9c4779c2009-02-08 09:06:00 -0800230if (TSTS (s1 ^ s2)) { /* result -? */
231 MKDNEG (rs);
232 }
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700233else if (TSTS (rs[0])) { /* result +, 2**70? */
234 SETF (F_AOV | F_T1); /* overflow */
235 rs[1] = SETS (rs[1]); /* consistent - */
236 }
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800237return;
238}
239
240/* Divide, return quotient and remainder - checked against KS10 ucode
241 Note that the initial divide check catches the case -2^70/-2^35;
242 thus, the quotient can have at most 35 bits.
243*/
244
245t_bool divi (int32 ac, d10 b, d10 *rs)
246{
Bob Supnik89bcd022001-11-06 20:58:00 -0800247int32 p1 = ADDAC (ac, 1);
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700248d10 dvr = ABS (b); /* make divr positive */
Bob Supnik654937f2001-11-06 21:02:00 -0800249t_int64 t;
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800250int32 i;
251d10 dvd[2];
252
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700253dvd[0] = AC(ac); /* divd high */
254dvd[1] = CLRS (AC(p1)); /* divd lo, clr sgn */
Bob Supnik9c4779c2009-02-08 09:06:00 -0800255if (TSTS (AC(ac))) { /* make divd positive */
256 DMOVN (dvd);
257 }
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700258if (dvd[0] >= dvr) { /* divide fail? */
259 SETF (F_AOV | F_DCK | F_T1); /* set flags, return */
260 return FALSE;
261 }
262if (dvd[0] & FIT27) { /* fit in 63b? */
263 for (i = 0, rs[0] = 0; i < 35; i++) { /* 35 quotient bits */
264 dvd[0] = (dvd[0] << 1) | ((dvd[1] >> 34) & 1);
265 dvd[1] = (dvd[1] << 1) & MMASK; /* shift dividend */
266 rs[0] = rs[0] << 1; /* shift quotient */
267 if (dvd[0] >= dvr) { /* subtract work? */
268 dvd[0] = dvd[0] - dvr; /* quo bit is 1 */
269 rs[0] = rs[0] + 1;
270 }
271 }
272 rs[1] = dvd[0]; /* store remainder */
273 }
274else {
275 t = (dvd[0] << 35) | dvd[1]; /* concatenate */
276 rs[0] = t / dvr; /* quotient */
277 rs[1] = t % dvr; /* remainder */
278 }
Bob Supnik9c4779c2009-02-08 09:06:00 -0800279if (TSTS (AC(ac) ^ b)) /* sign of result */
280 rs[0] = NEG (rs[0]);
281if (TSTS (AC(ac))) /* sign of remainder */
282 rs[1] = NEG (rs[1]);
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800283return TRUE;
284}
285
286/* Double precision multiply. This is done the old fashioned way. Cross
287 product multiplies would be a lot faster but would require more code.
288*/
289
290void dmul (int32 ac, d10 *mpy)
291{
292int32 p1 = ADDAC (ac, 1);
293int32 p2 = ADDAC (ac, 2);
294int32 p3 = ADDAC (ac, 3);
295int32 i;
296d10 mpc[2], sign;
297
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700298mpc[0] = AC(ac); /* mplcnd hi */
299mpc[1] = CLRS (AC(p1)); /* mplcnd lo, clr sgn */
300sign = mpc[0] ^ mpy[0]; /* sign of result */
Bob Supnik9c4779c2009-02-08 09:06:00 -0800301if (TSTS (mpc[0])) { /* get abs (mpcnd) */
302 DMOVN (mpc);
303 }
304if (TSTS (mpy[0])) { /* get abs (mpyer) */
305 DMOVN (mpy);
306 }
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700307else mpy[1] = CLRS (mpy[1]); /* clear mpy lo sign */
308AC(ac) = AC(p1) = AC(p2) = AC(p3) = 0; /* clear AC's */
Bob Supnik9c4779c2009-02-08 09:06:00 -0800309if (((mpy[0] | mpy[1]) == 0) || ((mpc[0] | mpc[1]) == 0))
310 return;
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700311for (i = 0; i < 71; i++) { /* 71 mpyer bits */
312 if (i) { /* shift res, mpy */
313 AC(p3) = (AC(p3) >> 1) | ((AC(p2) & 1) << 34);
314 AC(p2) = (AC(p2) >> 1) | ((AC(p1) & 1) << 34);
315 AC(p1) = (AC(p1) >> 1) | ((AC(ac) & 1) << 34);
316 AC(ac) = AC(ac) >> 1;
317 mpy[1] = (mpy[1] >> 1) | ((mpy[0] & 1) << 34);
318 mpy[0] = mpy[0] >> 1;
319 }
320 if (mpy[1] & 1) { /* if mpy lo bit = 1 */
321 AC(p1) = AC(p1) + mpc[1];
Mark Pizzolato820d77e2016-05-05 03:50:21 -0700322 AC(ac) = AC(ac) + mpc[0] + (TSTS (AC(p1)) != 0);
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700323 AC(p1) = CLRS (AC(p1));
324 }
325 }
326if (TSTS (sign)) { /* result minus? */
327 AC(p3) = (-AC(p3)) & MMASK; /* quad negate */
328 AC(p2) = (~AC(p2) + (AC(p3) == 0)) & MMASK;
329 AC(p1) = (~AC(p1) + (AC(p2) == 0)) & MMASK;
330 AC(ac) = (~AC(ac) + (AC(p1) == 0)) & DMASK;
331 }
Bob Supnik9c4779c2009-02-08 09:06:00 -0800332else if (TSTS (AC(ac))) /* wrong sign */
333 SETF (F_AOV | F_T1);
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700334if (TSTS (AC(ac))) { /* if result - */
335 AC(p1) = SETS (AC(p1)); /* make signs consistent */
336 AC(p2) = SETS (AC(p2));
337 AC(p3) = SETS (AC(p3));
338 }
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800339return;
340}
341
342/* Double precision divide - checked against KS10 ucode */
343
344void ddiv (int32 ac, d10 *dvr)
345{
346int32 i, cryin;
347d10 sign, qu[2], dvd[4];
348
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700349dvd[0] = AC(ac); /* save dividend */
Bob Supnik9c4779c2009-02-08 09:06:00 -0800350for (i = 1; i < 4; i++)
351 dvd[i] = CLRS (AC(ADDAC (ac, i)));
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700352sign = AC(ac) ^ dvr[0]; /* sign of result */
353if (TSTS (AC(ac))) { /* get abs (dividend) */
354 for (i = 3, cryin = 1; i > 0; i--) { /* negate quad */
355 dvd[i] = (~dvd[i] + cryin) & MMASK; /* comp + carry in */
Bob Supnik9c4779c2009-02-08 09:06:00 -0800356 if (dvd[i]) /* next carry in */
357 cryin = 0;
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700358 }
359 dvd[0] = (~dvd[0] + cryin) & DMASK;
360 }
Bob Supnik9c4779c2009-02-08 09:06:00 -0800361if (TSTS (dvr[0])) { /* get abs (divisor) */
362 DMOVN (dvr);
363 }
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800364else dvr[1] = CLRS (dvr[1]);
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700365if (DCMPGE (dvd, dvr)) { /* will divide work? */
366 SETF (F_AOV | F_DCK | F_T1); /* no, set flags */
367 return;
368 }
369qu[0] = qu[1] = 0; /* clear quotient */
370for (i = 0; i < 70; i++) { /* 70 quotient bits */
371 dvd[0] = ((dvd[0] << 1) | ((dvd[1] >> 34) & 1)) & DMASK;;
372 dvd[1] = ((dvd[1] << 1) | ((dvd[2] >> 34) & 1)) & MMASK;
373 dvd[2] = ((dvd[2] << 1) | ((dvd[3] >> 34) & 1)) & MMASK;
374 dvd[3] = (dvd[3] << 1) & MMASK; /* shift dividend */
375 qu[0] = (qu[0] << 1) | ((qu[1] >> 34) & 1); /* shift quotient */
376 qu[1] = (qu[1] << 1) & MMASK;
377 if (DCMPGE (dvd, dvr)) { /* subtract work? */
378 dvd[0] = dvd[0] - dvr[0] - (dvd[1] < dvr[1]);
379 dvd[1] = (dvd[1] - dvr[1]) & MMASK; /* do subtract */
380 qu[1] = qu[1] + 1; /* set quotient bit */
381 }
382 }
Bob Supnik9c4779c2009-02-08 09:06:00 -0800383if (TSTS (sign) && (qu[0] | qu[1])) {
384 MKDNEG (qu);
385 }
386if (TSTS (AC(ac)) && (dvd[0] | dvd[1])) {
387 MKDNEG (dvd);
388 }
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700389AC(ac) = qu[0]; /* quotient */
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800390AC(ADDAC(ac, 1)) = qu[1];
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700391AC(ADDAC(ac, 2)) = dvd[0]; /* remainder */
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800392AC(ADDAC(ac, 3)) = dvd[1];
393return;
394}
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700395
Bob Supnik26aa6de2004-04-06 05:17:00 -0700396/* Single precision floating add/subtract - checked against KS10 ucode
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800397 The KS10 shifts the smaller operand regardless of the exponent diff.
398 This code will not shift more than 63 places; shifts beyond that
399 cannot change the value of the smaller operand.
400
401 If the signs of the operands are the same, the result sign is the
402 same as the source sign; the sign of the result fraction is actually
403 part of the data. If the signs of the operands are different, the
404 result sign is determined by the fraction sign.
405*/
406
407d10 fad (d10 op1, d10 op2, t_bool rnd, int32 inv)
408{
Bob Supnik89bcd022001-11-06 20:58:00 -0800409int32 ediff;
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800410UFP a, b, t;
411
Bob Supnik9c4779c2009-02-08 09:06:00 -0800412if (inv) /* subtract? -b */
413 op2 = NEG (op2);
414if (op1 == 0) /* a = 0? result is b */
415 funpack (op2, 0, &a, AFRC);
416else if (op2 == 0) /* b = 0? result is a */
417 funpack (op1, 0, &a, AFRC);
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700418else {
419 funpack (op1, 0, &a, SFRC); /* unpack operands */
420 funpack (op2, 0, &b, SFRC); /* fracs are 2's comp */
421 ediff = a.exp - b.exp; /* get exp diff */
422 if (ediff < 0) { /* a < b? switch */
423 t = a;
424 a = b;
425 b = t;
426 ediff = -ediff;
427 }
Bob Supnik9c4779c2009-02-08 09:06:00 -0800428 if (ediff > 63) /* cap diff at 63 */
429 ediff = 63;
430 if (ediff) /* shift b (signed) */
431 b.fhi = (t_int64) b.fhi >> ediff;
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700432 a.fhi = a.fhi + b.fhi; /* add fractions */
433 if (a.sign ^ b.sign) { /* add or subtract? */
434 if (a.fhi & FP_UCRY) { /* subtract, frac -? */
435 a.fhi = UNEG (a.fhi); /* complement result */
436 a.sign = 1; /* result is - */
437 }
438 else a.sign = 0; /* result is + */
439 }
440 else {
Bob Supnik9c4779c2009-02-08 09:06:00 -0800441 if (a.sign) /* add, src -? comp */
442 a.fhi = UNEG (a.fhi);
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700443 if (a.fhi & FP_UCRY) { /* check for carry */
444 a.fhi = a.fhi >> 1; /* flo won't be used */
445 a.exp = a.exp + 1;
446 }
447 }
448 }
449fnorm (&a, (rnd? FP_URNDS: 0)); /* normalize, round */
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800450return fpack (&a, NULL, FALSE);
451}
452
453/* Single precision floating multiply. Because the fractions are 27b,
454 a 64b multiply can be used for the fraction multiply. The 27b
455 fractions are positioned 0'frac'0000, resulting in 00'hifrac'0..0.
456 The extra 0 is accounted for by biasing the result exponent.
457*/
458
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700459#define FP_V_SPM (FP_V_UFHI - (32 - FP_N_FHI - 1))
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800460d10 fmp (d10 op1, d10 op2, t_bool rnd)
461{
462UFP a, b;
463
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700464funpack (op1, 0, &a, AFRC); /* unpack operands */
465funpack (op2, 0, &b, AFRC); /* fracs are abs val */
Bob Supnik9c4779c2009-02-08 09:06:00 -0800466if ((a.fhi == 0) || (b.fhi == 0)) /* either 0? */
467 return 0;
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700468a.sign = a.sign ^ b.sign; /* result sign */
469a.exp = a.exp + b.exp - FP_BIAS + 1; /* result exponent */
470a.fhi = (a.fhi >> FP_V_SPM) * (b.fhi >> FP_V_SPM); /* high 27b of result */
471fnorm (&a, (rnd? FP_URNDS: 0)); /* normalize, round */
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800472return fpack (&a, NULL, FALSE);
473}
474
475/* Single precision floating divide. Because the fractions are 27b, a
476 64b divide can be used for the fraction divide. Note that 28b-29b
477 of fraction are developed; the code will do one special normalize to
478 make sure that the 28th bit is not lost. Also note the special
479 treatment of negative quotients with non-zero remainders; this
480 implements the note on p2-23 of the Processor Reference Manual.
481*/
482
483t_bool fdv (d10 op1, d10 op2, d10 *rs, t_bool rnd)
484{
485UFP a, b;
Bob Supnik654937f2001-11-06 21:02:00 -0800486t_uint64 savhi;
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800487t_bool rem = FALSE;
488
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700489funpack (op1, 0, &a, AFRC); /* unpack operands */
490funpack (op2, 0, &b, AFRC); /* fracs are abs val */
491if (a.fhi >= 2 * b.fhi) { /* will divide work? */
492 SETF (F_AOV | F_DCK | F_FOV | F_T1);
493 return FALSE;
494 }
Mark Pizzolato0f8e6cf2012-04-29 11:59:44 -0700495if ((savhi = a.fhi)) { /* dvd = 0? quo = 0 */
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700496 a.sign = a.sign ^ b.sign; /* result sign */
497 a.exp = a.exp - b.exp + FP_BIAS + 1; /* result exponent */
498 a.fhi = a.fhi / (b.fhi >> (FP_N_FHI + 1)); /* do divide */
499 if (a.sign && (savhi != (a.fhi * (b.fhi >> (FP_N_FHI + 1)))))
500 rem = TRUE; /* KL/KS hack */
501 a.fhi = a.fhi << (FP_V_UNORM - FP_N_FHI - 1); /* put quo in place */
502 if ((a.fhi & FP_UNORM) == 0) { /* normalize 1b */
503 a.fhi = a.fhi << 1; /* before masking */
504 a.exp = a.exp - 1;
505 }
506 a.fhi = a.fhi & (FP_UFHI | FP_URNDS); /* mask quo to 28b */
507 }
508fnorm (&a, (rnd? FP_URNDS: 0)); /* normalize, round */
509*rs = fpack (&a, NULL, rem); /* pack result */
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800510return TRUE;
511}
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700512
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800513/* Single precision floating scale. */
514
515d10 fsc (d10 val, a10 ea)
516{
517int32 sc = LIT8 (ea);
518UFP a;
519
Bob Supnik9c4779c2009-02-08 09:06:00 -0800520if (val == 0)
521 return 0;
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700522funpack (val, 0, &a, AFRC); /* unpack operand */
Bob Supnik9c4779c2009-02-08 09:06:00 -0800523if (ea & RSIGN) /* adjust exponent */
524 a.exp = a.exp - sc;
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800525else a.exp = a.exp + sc;
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700526fnorm (&a, 0); /* renormalize */
527return fpack (&a, NULL, FALSE); /* pack result */
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800528}
529
530/* Float integer operand and round */
531
532d10 fltr (d10 mb)
533{
534UFP a;
535d10 val = ABS (mb);
536
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700537a.sign = GET_FPSIGN (mb); /* get sign */
538a.exp = FP_BIAS + 36; /* initial exponent */
539a.fhi = val << (FP_V_UNORM - 35); /* left justify op */
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800540a.flo = 0;
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700541fnorm (&a, FP_URNDS); /* normalize, round */
542return fpack (&a, NULL, FALSE); /* pack result */
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800543}
544
545/* Fix and truncate/round floating operand */
546
547void fix (int32 ac, d10 mb, t_bool rnd)
548{
549int32 sc;
Bob Supnik654937f2001-11-06 21:02:00 -0800550t_uint64 so;
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800551UFP a;
552
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700553funpack (mb, 0, &a, AFRC); /* unpack operand */
Bob Supnik9c4779c2009-02-08 09:06:00 -0800554if (a.exp > (FP_BIAS + FP_N_FHI + FP_N_EXP))
555 SETF (F_AOV | F_T1);
556else if (a.exp < FP_BIAS) /* < 1/2? */
557 AC(ac) = 0;
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700558else {
559 sc = FP_V_UNORM - (a.exp - FP_BIAS) + 1;
560 AC(ac) = a.fhi >> sc;
561 if (rnd) {
562 so = a.fhi << (64 - sc);
Bob Supnik9c4779c2009-02-08 09:06:00 -0800563 if (so >= (0x8000000000000000 + a.sign))
564 AC(ac) = AC(ac) + 1;
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700565 }
Bob Supnik9c4779c2009-02-08 09:06:00 -0800566 if (a.sign)
567 AC(ac) = NEG (AC(ac));
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700568 }
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800569return;
570}
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700571
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800572/* Double precision floating add/subtract
573 Since a.flo is 0, adding b.flo is just a copy - this is incorporated into
574 the denormalization step. If there's no denormalization, bflo is zero too.
575*/
576
577void dfad (int32 ac, d10 *rs, int32 inv)
578{
579int32 p1 = ADDAC (ac, 1);
Bob Supnik89bcd022001-11-06 20:58:00 -0800580int32 ediff;
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800581UFP a, b, t;
582
Bob Supnik9c4779c2009-02-08 09:06:00 -0800583if (inv) { /* subtract? -b */
584 DMOVN (rs);
585 }
586if ((AC(ac) | AC(p1)) == 0) /* a == 0? sum = b */
587 funpack (rs[0], rs[1], &a, AFRC);
588else if ((rs[0] | rs[1]) == 0) /* b == 0? sum = a */
589 funpack (AC(ac), AC(p1), &a, AFRC);
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800590else {
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700591 funpack (AC(ac), AC(p1), &a, SFRC); /* unpack operands */
592 funpack (rs[0], rs[1], &b, SFRC);
593 ediff = a.exp - b.exp; /* get exp diff */
594 if (ediff < 0) { /* a < b? switch */
595 t = a;
596 a = b;
597 b = t;
598 ediff = -ediff;
599 }
Bob Supnik9c4779c2009-02-08 09:06:00 -0800600 if (ediff > 127) /* cap diff at 127 */
601 ediff = 127;
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700602 if (ediff > 63) { /* diff > 63? */
603 a.flo = (t_int64) b.fhi >> (ediff - 64); /* b hi to a lo */
604 b.fhi = b.sign? FP_ONES: 0; /* hi = all sign */
605 }
606 else if (ediff) { /* diff <= 63 */
607 a.flo = (b.flo >> ediff) | (b.fhi << (64 - ediff));
608 b.fhi = (t_int64) b.fhi >> ediff; /* shift b (signed) */
609 }
610 a.fhi = a.fhi + b.fhi; /* do add */
611 if (a.sign ^ b.sign) { /* add or subtract? */
612 if (a.fhi & FP_UCRY) { /* subtract, frac -? */
613 DUNEG (a); /* complement result */
614 a.sign = 1; /* result is - */
615 }
616 else a.sign = 0; /* result is + */
617 }
618 else {
Bob Supnik9c4779c2009-02-08 09:06:00 -0800619 if (a.sign) { /* add, src -? comp */
620 DUNEG (a);
621 };
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700622 if (a.fhi & FP_UCRY) { /* check for carry */
623 a.fhi = a.fhi >> 1; /* flo won't be used */
624 a.exp = a.exp + 1;
625 }
626 }
627 }
628fnorm (&a, FP_URNDD); /* normalize, round */
629AC(ac) = fpack (&a, &AC(p1), FALSE); /* pack result */
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800630return;
631}
632
633/* Double precision floating multiply
634 The 62b fractions are multiplied, with cross products, to produce a
635 124b fraction with two leading and two trailing 0's. Because the
636 product has 2 leading 0's, instead of the normal 1, an extra
637 normalization step is needed. Accordingly, the exponent calculation
638 increments the result exponent, to compensate for normalization.
639*/
640
641void dfmp (int32 ac, d10 *rs)
642{
643int32 p1 = ADDAC (ac, 1);
Bob Supnik654937f2001-11-06 21:02:00 -0800644t_uint64 xh, xl, yh, yl, mid;
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800645UFP a, b;
646
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700647funpack (AC(ac), AC(p1), &a, AFRC); /* unpack operands */
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800648funpack (rs[0], rs[1], &b, AFRC);
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700649if ((a.fhi == 0) || (b.fhi == 0)) { /* either 0? result 0 */
650 AC(ac) = AC(p1) = 0;
651 return;
652 }
653a.sign = a.sign ^ b.sign; /* result sign */
654a.exp = a.exp + b.exp - FP_BIAS + 1; /* result exponent */
655xh = a.fhi >> 32; /* split 62b fracs */
656xl = a.fhi & MSK32; /* into 32b halves */
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800657yh = b.fhi >> 32;
658yl = b.fhi & MSK32;
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700659a.fhi = xh * yh; /* hi xproduct */
660a.flo = xl * yl; /* low xproduct */
661mid = (xh * yl) + (yh * xl); /* fits in 64b */
662a.flo = a.flo + (mid << 32); /* add mid lo to lo */
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800663a.fhi = a.fhi + ((mid >> 32) & MSK32) + (a.flo < (mid << 32));
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700664fnorm (&a, FP_URNDD); /* normalize, round */
665AC(ac) = fpack (&a, &AC(p1), FALSE); /* pack result */
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800666return;
667}
668
669/* Double precision floating divide
670 This algorithm develops a full 62 bits of quotient, plus one rounding
671 bit, in the low order 63b of a 64b number. To do this, we must assure
672 that the initial divide step generates a 1. If it would fail, shift
673 the dividend left and decrement the result exponent accordingly.
674*/
675
676void dfdv (int32 ac, d10 *rs)
677{
678int32 p1 = ADDAC (ac, 1);
679int32 i;
Bob Supnik654937f2001-11-06 21:02:00 -0800680t_uint64 qu = 0;
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800681UFP a, b;
682
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700683funpack (AC(ac), AC(p1), &a, AFRC); /* unpack operands */
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800684funpack (rs[0], rs[1], &b, AFRC);
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700685if (a.fhi >= 2 * b.fhi) { /* will divide work? */
686 SETF (F_AOV | F_DCK | F_FOV | F_T1);
687 return;
688 }
689if (a.fhi) { /* dvd = 0? quo = 0 */
690 a.sign = a.sign ^ b.sign; /* result sign */
691 a.exp = a.exp - b.exp + FP_BIAS + 1; /* result exponent */
692 if (a.fhi < b.fhi) { /* make sure initial */
693 a.fhi = a.fhi << 1; /* divide step will work */
694 a.exp = a.exp - 1;
695 }
696 for (i = 0; i < 63; i++) { /* 63b of quotient */
697 qu = qu << 1; /* shift quotient */
698 if (a.fhi >= b.fhi) { /* will div work? */
699 a.fhi = a.fhi - b.fhi; /* sub, quo = 1 */
700 qu = qu + 1;
701 }
702 a.fhi = a.fhi << 1; /* shift dividend */
703 }
704 a.fhi = qu;
705 }
706fnorm (&a, FP_URNDD); /* normalize, round */
707AC(ac) = fpack (&a, &AC(p1), FALSE); /* pack result */
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800708return;
709}
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700710
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800711/* Unpack floating point operand */
712
713void funpack (d10 h, d10 l, UFP *r, t_bool sgn)
714{
715d10 fphi, fplo;
716
Bob Supnik2c2dd5e2002-11-17 15:54:00 -0800717r->sign = GET_FPSIGN (h);
718r->exp = GET_FPEXP (h);
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800719fphi = GET_FPHI (h);
720fplo = GET_FPLO (l);
Bob Supnik2c2dd5e2002-11-17 15:54:00 -0800721r->fhi = (fphi << FP_V_UFHI) | (fplo << FP_V_UFLO);
722r->flo = 0;
723if (r->sign) {
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700724 r->exp = r->exp ^ FP_M_EXP; /* 1s comp exp */
725 if (sgn) { /* signed frac? */
Bob Supnik9c4779c2009-02-08 09:06:00 -0800726 if (r->fhi) /* extend sign */
727 r->fhi = r->fhi | FP_UCRY;
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700728 else {
729 r->exp = r->exp + 1;
Mark Pizzolato02cb5c22014-02-14 17:07:45 -0800730 r->fhi = (t_uint64)(FP_UCRY | FP_UNORM);
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700731 }
732 }
733 else { /* abs frac */
Bob Supnik9c4779c2009-02-08 09:06:00 -0800734 if (r->fhi)
735 r->fhi = UNEG (r->fhi) & FP_UFRAC;
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700736 else {
Bob Supnik9c4779c2009-02-08 09:06:00 -0800737 r->exp = r->exp + 1;
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700738 r->fhi = FP_UNORM;
739 }
740 }
741 }
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800742return;
743}
744
745/* Normalize and optionally round floating point operand */
746
Bob Supnik654937f2001-11-06 21:02:00 -0800747void fnorm (UFP *a, t_int64 rnd)
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800748{
749int32 i;
Bob Supnik654937f2001-11-06 21:02:00 -0800750static t_uint64 normmask[6] = {
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800751 0x6000000000000000, 0x7800000000000000, 0x7F80000000000000,
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700752 0x7FFF800000000000, 0x7FFFFFFF80000000, 0x7FFFFFFFFFFFFFFF
753 };
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800754static int32 normtab[7] = { 1, 2, 4, 8, 16, 32, 63 };
Bob Supnik26aa6de2004-04-06 05:17:00 -0700755extern a10 pager_PC;
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800756
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700757if (a->fhi & FP_UCRY) { /* carry set? */
Mark Pizzolato995ab8f2014-10-24 14:37:37 -0700758 sim_printf ("%%PDP-10 FP: carry bit set at normalization, PC = %o\n", pager_PC);
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700759 a->flo = (a->flo >> 1) | ((a->fhi & 1) << 63); /* try to recover */
760 a->fhi = a->fhi >> 1; /* but root cause */
761 a->exp = a->exp + 1; /* should be fixed! */
762 }
763if ((a->fhi | a->flo) == 0) { /* if fraction = 0 */
764 a->sign = a->exp = 0; /* result is 0 */
765 return;
766 }
767while ((a->fhi & FP_UNORM) == 0) { /* normalized? */
768 for (i = 0; i < 6; i++) {
Bob Supnik9c4779c2009-02-08 09:06:00 -0800769 if (a->fhi & normmask[i])
770 break;
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700771 }
772 a->fhi = (a->fhi << normtab[i]) | (a->flo >> (64 - normtab[i]));
773 a->flo = a->flo << normtab[i];
774 a->exp = a->exp - normtab[i];
775 }
776if (rnd) { /* rounding? */
777 a->fhi = a->fhi + rnd; /* add round const */
778 if (a->fhi & FP_UCRY) { /* if carry out, */
779 a->fhi = a->fhi >> 1; /* renormalize */
780 a->exp = a->exp + 1;
781 }
782 }
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800783return;
784}
785
786/* Pack floating point result */
787
788d10 fpack (UFP *r, d10 *lo, t_bool fdvneg)
789{
790d10 val[2];
791
Bob Supnik9c4779c2009-02-08 09:06:00 -0800792if (r->exp < 0)
793 SETF (F_AOV | F_FOV | F_FXU | F_T1);
794else if (r->exp > FP_M_EXP)
795 SETF (F_AOV | F_FOV | F_T1);
Bob Supnik2c2dd5e2002-11-17 15:54:00 -0800796val[0] = (((((d10) r->exp) & FP_M_EXP) << FP_V_EXP) |
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700797 ((r->fhi & FP_UFHI) >> FP_V_UFHI)) & DMASK;
Bob Supnik9c4779c2009-02-08 09:06:00 -0800798if (lo)
799 val[1] = ((r->fhi & FP_UFLO) >> FP_V_UFLO) & MMASK;
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800800else val[1] = 0;
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700801if (r->sign) { /* negate? */
802 if (fdvneg) { /* fdvr special? */
803 val[1] = ~val[1] & MMASK; /* 1's comp */
804 val[0] = ~val[0] & DMASK;
Mark Pizzolato651780c2013-06-03 06:29:01 -0700805 }
Bob Supnik9c4779c2009-02-08 09:06:00 -0800806 else { /* 2's comp */
807 DMOVN (val);
808 }
Bob Supnikb7c1eae2005-09-09 18:09:00 -0700809 }
Bob Supnik9c4779c2009-02-08 09:06:00 -0800810if (lo)
811 *lo = val[1];
Bob Supnik4d6dfa42001-11-06 20:50:00 -0800812return val[0];
813}