1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 1988,1995-1996,2003 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29/* Pack procedures for Sparc FPU simulator. */
30
31#include <sys/fpu/fpu_simulator.h>
32#include <sys/fpu/globals.h>
33
34/*
35 * Returns 1 if overflow should go to infinity, 0 if to max finite.
36 */
37static int
38overflow_to_infinity(
39	fp_simd_type	*pfpsd,		/* Pointer to simulator data */
40	int		sign)		/* negative or positive */
41{
42	int		inf;
43
44	switch (pfpsd->fp_direction) {
45	case fp_nearest:
46		inf = 1;
47		break;
48	case fp_tozero:
49		inf = 0;
50		break;
51	case fp_positive:
52		inf = !sign;
53		break;
54	case fp_negative:
55		inf = sign;
56		break;
57	}
58	return (inf);
59}
60
61/*
62 * Round according to current rounding mode.
63 */
64static void
65round(
66	fp_simd_type	*pfpsd,		/* Pointer to simulator data */
67	unpacked	*pu)		/* unpacked result */
68{
69	int		increment;	/* boolean to indicate round up */
70	int		sr;
71
72	sr = pu->sticky|pu->rounded;
73
74	if (sr == 0)
75		return;
76	fpu_set_exception(pfpsd, fp_inexact);
77	switch (pfpsd->fp_direction) {
78	case fp_nearest:
79		increment = pu->rounded;
80		break;
81	case fp_tozero:
82		increment = 0;
83		break;
84	case fp_positive:
85		increment = (pu->sign == 0) & (sr != 0);
86		break;
87	case fp_negative:
88		increment = (pu->sign != 0) & (sr != 0);
89		break;
90	}
91	if (increment) {
92	    pu->significand[3]++;
93	    if (pu->significand[3] == 0) {
94		pu->significand[2]++;
95		if (pu->significand[2] == 0) {
96		    pu->significand[1]++;
97		    if (pu->significand[1] == 0) {
98			pu->significand[0]++;	/* rounding carried out */
99			if (pu->significand[0] == 0x20000) {
100			    pu->exponent++;
101			    pu->significand[0] = 0x10000;
102			}
103		    }
104		}
105	    }
106	}
107	if ((pfpsd->fp_direction == fp_nearest) &&
108	    (pu->sticky == 0) && increment != 0) {	/* ambiguous case */
109		pu->significand[3] &= 0xfffffffe; /* force round to even */
110	}
111}
112
113static void
114packint32(
115	fp_simd_type	*pfpsd,	/* Pointer to simulator data */
116	unpacked	*pu,	/* unpacked result */
117	int32_t		*px)	/* packed int32_t */
118{
119	switch (pu->fpclass) {
120	case fp_zero:
121		*px = 0;
122		break;
123	case fp_normal:
124		if (pu->exponent >= 32)
125			goto overflow;
126		fpu_rightshift(pu, 112 - pu->exponent);
127		round(pfpsd, pu);
128		if (pu->significand[3] >= 0x80000000)
129			if ((pu->sign == 0)||(pu->significand[3] > 0x80000000))
130				goto overflow;
131		*px = pu->significand[3];
132		if (pu->sign)
133			*px = -*px;
134		break;
135	case fp_infinity:
136	case fp_quiet:
137	case fp_signaling:
138overflow:
139		if (pu->sign)
140			*px = 0x80000000;
141		else
142			*px = 0x7fffffff;
143		pfpsd->fp_current_exceptions &= ~(1 << (int)fp_inexact);
144		fpu_set_exception(pfpsd, fp_invalid);
145		break;
146	}
147}
148
149static void
150packint64(
151	fp_simd_type	*pfpsd,	/* Pointer to simulator data */
152	unpacked	*pu,	/* unpacked result */
153	int64_t		*px)	/* packed int64_t */
154{
155	union {
156		uint64_t ll;
157		uint32_t i[2];
158	} x;
159
160	switch (pu->fpclass) {
161	case fp_zero:
162		*px = 0;
163		break;
164	case fp_normal:
165		if (pu->exponent >= 64)
166			goto overflow;
167		fpu_rightshift(pu, 112 - pu->exponent);
168		round(pfpsd, pu);
169		if (pu->significand[2] >= 0x80000000)
170			if ((pu->sign == 0) ||
171			    (pu->significand[2] > 0x80000000) ||
172			    (((pu->significand[2] == 0x80000000) &&
173				(pu->significand[3] > 0))))
174				goto overflow;
175		x.i[0] = pu->significand[2];
176		x.i[1] = pu->significand[3];
177		*px = x.ll;
178		if (pu->sign)
179			*px = -*px;
180		break;
181	case fp_infinity:
182	case fp_quiet:
183	case fp_signaling:
184overflow:
185		if (pu->sign)
186			*px = (int64_t)0x8000000000000000;
187		else
188			*px = (int64_t)0x7fffffffffffffff;
189		pfpsd->fp_current_exceptions &= ~(1 << (int)fp_inexact);
190		fpu_set_exception(pfpsd, fp_invalid);
191		break;
192	}
193}
194
195static void
196packsingle(
197	fp_simd_type	*pfpsd,	/* Pointer to simulator data */
198	unpacked	*pu,	/* unpacked result */
199	single_type	*px)	/* packed single */
200{
201	px->sign = pu->sign;
202	switch (pu->fpclass) {
203	case fp_zero:
204		px->exponent = 0;
205		px->significand = 0;
206		break;
207	case fp_infinity:
208infinity:
209		px->exponent = 0xff;
210		px->significand = 0;
211		break;
212	case fp_quiet:
213	case fp_signaling:
214		fpu_rightshift(pu, 113-24);
215		px->exponent = 0xff;
216		px->significand = 0x400000|(0x3fffff&pu->significand[3]);
217		break;
218	case fp_normal:
219		fpu_rightshift(pu, 113-24);
220		pu->exponent += SINGLE_BIAS;
221		if (pu->exponent <= 0) {
222			px->exponent = 0;
223			fpu_rightshift(pu, 1 - pu->exponent);
224			round(pfpsd, pu);
225			if (pu->significand[3] == 0x800000) {
226								/*
227								 * rounded
228								 * back up to
229								 * normal
230								 */
231				px->exponent = 1;
232				px->significand = 0;
233				fpu_set_exception(pfpsd, fp_inexact);
234			} else
235				px->significand = 0x7fffff & pu->significand[3];
236
237			if (pfpsd->fp_current_exceptions & (1 << fp_inexact))
238				fpu_set_exception(pfpsd, fp_underflow);
239			if (pfpsd->fp_fsrtem & (1<<fp_underflow)) {
240				fpu_set_exception(pfpsd, fp_underflow);
241				pfpsd->fp_current_exceptions &=
242						~(1 << (int)fp_inexact);
243			}
244			return;
245		}
246		round(pfpsd, pu);
247		if (pu->significand[3] == 0x1000000) {	/* rounding overflow */
248			pu->significand[3] = 0x800000;
249			pu->exponent += 1;
250		}
251		if (pu->exponent >= 0xff) {
252			fpu_set_exception(pfpsd, fp_overflow);
253			fpu_set_exception(pfpsd, fp_inexact);
254			if (pfpsd->fp_fsrtem & (1<<fp_overflow)) {
255				pfpsd->fp_current_exceptions &=
256						~(1 << (int)fp_inexact);
257			}
258			if (overflow_to_infinity(pfpsd, pu->sign))
259				goto infinity;
260			px->exponent = 0xfe;
261			px->significand = 0x7fffff;
262			return;
263		}
264		px->exponent = pu->exponent;
265		px->significand = 0x7fffff & pu->significand[3];
266	}
267}
268
269static void
270packdouble(
271	fp_simd_type	*pfpsd,	/* Pointer to simulator data */
272	unpacked	*pu,	/* unpacked result */
273	double_type	*px,	/* packed double, sign/exponent/upper 20 bits */
274	uint_t		*py)	/* and the lower 32 bits of the significand */
275{
276	px->sign = pu->sign;
277	switch (pu->fpclass) {
278	case fp_zero:
279		px->exponent = 0;
280		px->significand = 0;
281		*py = 0;
282		break;
283	case fp_infinity:
284infinity:
285		px->exponent = 0x7ff;
286		px->significand = 0;
287		*py = 0;
288		break;
289	case fp_quiet:
290	case fp_signaling:
291		fpu_rightshift(pu, 113-53);
292		px->exponent = 0x7ff;
293		px->significand = 0x80000 | (0x7ffff & pu->significand[2]);
294		*py = pu->significand[3];
295		break;
296	case fp_normal:
297		fpu_rightshift(pu, 113-53);
298		pu->exponent += DOUBLE_BIAS;
299		if (pu->exponent <= 0) {	/* underflow */
300			px->exponent = 0;
301			fpu_rightshift(pu, 1 - pu->exponent);
302			round(pfpsd, pu);
303			if (pu->significand[2] == 0x100000) {
304								/*
305								 * rounded
306								 * back up to
307								 * normal
308								 */
309				px->exponent = 1;
310				px->significand = 0;
311				*py = 0;
312				fpu_set_exception(pfpsd, fp_inexact);
313			} else {
314				px->exponent = 0;
315				px->significand = 0xfffff & pu->significand[2];
316				*py = pu->significand[3];
317			}
318			if (pfpsd->fp_current_exceptions & (1 << fp_inexact))
319				fpu_set_exception(pfpsd, fp_underflow);
320			if (pfpsd->fp_fsrtem & (1<<fp_underflow)) {
321				fpu_set_exception(pfpsd, fp_underflow);
322				pfpsd->fp_current_exceptions &=
323						~(1 << (int)fp_inexact);
324			}
325			return;
326		}
327		round(pfpsd, pu);
328		if (pu->significand[2] == 0x200000) {	/* rounding overflow */
329			pu->significand[2] = 0x100000;
330			pu->exponent += 1;
331		}
332		if (pu->exponent >= 0x7ff) {	/* overflow */
333			fpu_set_exception(pfpsd, fp_overflow);
334			fpu_set_exception(pfpsd, fp_inexact);
335			if (pfpsd->fp_fsrtem & (1<<fp_overflow)) {
336				pfpsd->fp_current_exceptions &=
337						~(1 << (int)fp_inexact);
338			}
339			if (overflow_to_infinity(pfpsd, pu->sign))
340				goto infinity;
341			px->exponent = 0x7fe;
342			px->significand = 0xfffff;
343			*py = 0xffffffffU;
344			return;
345		}
346		px->exponent = pu->exponent;
347		px->significand = 0xfffff & pu->significand[2];
348		*py = pu->significand[3];
349		break;
350	}
351}
352
353static void
354packextended(
355	fp_simd_type	*pfpsd,	/* Pointer to simulator data */
356	unpacked	*pu,	/* unpacked result */
357	extended_type	*px,	/* packed extended, sign/exponent/16 bits */
358	uint_t		*py,	/* 2nd word of extended significand */
359	uint_t		*pz,	/* 3rd word of extended significand */
360	uint_t		*pw)	/* 4th word of extended significand */
361{
362	px->sign = pu->sign;
363	switch (pu->fpclass) {
364	case fp_zero:
365		px->exponent = 0;
366		px->significand = 0;
367		*pz = 0;
368		*py = 0;
369		*pw = 0;
370		break;
371	case fp_infinity:
372infinity:
373		px->exponent = 0x7fff;
374		px->significand = 0;
375		*pz = 0;
376		*py = 0;
377		*pw = 0;
378		break;
379	case fp_quiet:
380	case fp_signaling:
381		px->exponent = 0x7fff;
382		px->significand = 0x8000 | pu->significand[0];
383								/*
384								 * Insure quiet
385								 * nan.
386								 */
387		*py = pu->significand[1];
388		*pz = pu->significand[2];
389		*pw = pu->significand[3];
390		break;
391	case fp_normal:
392		pu->exponent += EXTENDED_BIAS;
393		if (pu->exponent <= 0) {	/* underflow */
394			fpu_rightshift(pu, 1-pu->exponent);
395			round(pfpsd, pu);
396			if (pu->significand[0] < 0x00010000) {
397								/*
398								 * not rounded
399								 * back up
400								 * to normal
401								 */
402				px->exponent = 0;
403			} else {
404				px->exponent = 1;
405				fpu_set_exception(pfpsd, fp_inexact);
406			}
407			if (pfpsd->fp_current_exceptions & (1 << fp_inexact))
408				fpu_set_exception(pfpsd, fp_underflow);
409			if (pfpsd->fp_fsrtem & (1<<fp_underflow)) {
410				fpu_set_exception(pfpsd, fp_underflow);
411				pfpsd->fp_current_exceptions &=
412						~(1 << (int)fp_inexact);
413			}
414			px->significand = pu->significand[0];
415			*py = pu->significand[1];
416			*pz = pu->significand[2];
417			*pw = pu->significand[3];
418			return;
419		}
420		round(pfpsd, pu); /* rounding overflow handled in round() */
421		if (pu->exponent >= 0x7fff) {	/* overflow */
422			fpu_set_exception(pfpsd, fp_overflow);
423			fpu_set_exception(pfpsd, fp_inexact);
424			if (pfpsd->fp_fsrtem & (1<<fp_overflow)) {
425				pfpsd->fp_current_exceptions &=
426						~(1 << (int)fp_inexact);
427			}
428			if (overflow_to_infinity(pfpsd, pu->sign))
429				goto infinity;
430			px->exponent = 0x7ffe;	/* overflow to max norm */
431			px->significand = 0xffff;
432			*py = 0xffffffffU;
433			*pz = 0xffffffffU;
434			*pw = 0xffffffffU;
435			return;
436		}
437		px->exponent = pu->exponent;
438		px->significand = pu->significand[0];
439		*py = pu->significand[1];
440		*pz = pu->significand[2];
441		*pw = pu->significand[3];
442		break;
443	}
444}
445
446void
447_fp_pack(
448	fp_simd_type	*pfpsd,	/* Pointer to simulator data */
449	unpacked	*pu,	/* unpacked operand */
450	uint_t		n,	/* register where datum starts */
451	enum fp_op_type type)	/* type of datum */
452
453{
454	switch (type) {
455	case fp_op_int32:
456		{
457			int32_t		x;
458
459			packint32(pfpsd, pu, &x);
460			if (!(pfpsd->fp_current_exceptions & pfpsd->fp_fsrtem))
461				pfpsd->fp_current_write_freg(&x, n, pfpsd);
462			break;
463		}
464	case fp_op_int64:
465		{
466			int64_t		x;
467
468			packint64(pfpsd, pu, &x);
469			if ((n & 0x1) == 1)	/* fix register encoding */
470				n = (n & 0x1e) | 0x20;
471			if (!(pfpsd->fp_current_exceptions & pfpsd->fp_fsrtem))
472			    pfpsd->fp_current_write_dreg(&x, DOUBLE(n), pfpsd);
473			break;
474		}
475	case fp_op_single:
476		{
477			single_type	x;
478
479			packsingle(pfpsd, pu, &x);
480			if (!(pfpsd->fp_current_exceptions & pfpsd->fp_fsrtem))
481				pfpsd->fp_current_write_freg(&x, n, pfpsd);
482			break;
483		}
484	case fp_op_double:
485		{
486			union {
487				double_type	x[2];
488				uint32_t	y[2];
489				uint64_t	ll;
490			} db;
491
492			packdouble(pfpsd, pu, &db.x[0], &db.y[1]);
493			if (!(pfpsd->fp_current_exceptions &
494			    pfpsd->fp_fsrtem)) {
495				if ((n & 0x1) == 1) /* fix register encoding */
496					n = (n & 0x1e) | 0x20;
497				pfpsd->fp_current_write_dreg(&db.ll, DOUBLE(n),
498					pfpsd);
499			}
500			break;
501		}
502	case fp_op_extended:
503		{
504			union {
505				extended_type	x;
506				uint32_t	y[4];
507				uint64_t	ll[2];
508			} ex;
509			unpacked	U;
510			int		k;
511			switch (pfpsd->fp_precision) {
512							/*
513							 * Implement extended
514							 * rounding precision
515							 * mode.
516							 */
517			case fp_single:
518				{
519					single_type	tx;
520
521					packsingle(pfpsd, pu, &tx);
522					pu = &U;
523					unpacksingle(pfpsd, pu, tx);
524					break;
525				}
526			case fp_double:
527				{
528					double_type	tx;
529					uint_t		ty;
530
531					packdouble(pfpsd, pu, &tx, &ty);
532					pu = &U;
533					unpackdouble(pfpsd, pu, tx, ty);
534					break;
535				}
536			case fp_precision_3:	/* rounded to 64 bits */
537				{
538					k = pu->exponent + EXTENDED_BIAS;
539					if (k >= 0) k = 113-64;
540					else	k = 113-64-k;
541					fpu_rightshift(pu, 113-64);
542					round(pfpsd, pu);
543					pu->sticky = pu->rounded = 0;
544					pu->exponent += k;
545					fpu_normalize(pu);
546					break;
547				}
548			}
549			packextended(pfpsd, pu, &ex.x, &ex.y[1],
550						&ex.y[2], &ex.y[3]);
551			if (!(pfpsd->fp_current_exceptions &
552			    pfpsd->fp_fsrtem)) {
553				if ((n & 0x1) == 1) /* fix register encoding */
554					n = (n & 0x1e) | 0x20;
555				pfpsd->fp_current_write_dreg(&ex.ll[0],
556							QUAD_E(n), pfpsd);
557				pfpsd->fp_current_write_dreg(&ex.ll[1],
558							QUAD_F(n), pfpsd);
559			}
560
561			break;
562		}
563	}
564}
565
566void
567_fp_pack_word(
568	fp_simd_type	*pfpsd,	/* Pointer to simulator data */
569	uint32_t	*pu,	/* unpacked operand */
570	uint_t		n)	/* register where datum starts */
571{
572	pfpsd->fp_current_write_freg(pu, n, pfpsd);
573}
574
575void
576_fp_pack_extword(
577	fp_simd_type	*pfpsd,	/* Pointer to simulator data */
578	uint64_t	*pu,	/* unpacked operand */
579	uint_t		n)	/* register where datum starts */
580{
581	if ((n & 1) == 1)	/* fix register encoding */
582		n = (n & 0x1e) | 0x20;
583	pfpsd->fp_current_write_dreg(pu, DOUBLE(n), pfpsd);
584}
585