OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [arch/] [i386/] [math-emu/] [reg_ld_str.c] - Blame information for rev 1777

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1623 jcastillo
/*---------------------------------------------------------------------------+
2
 |  reg_ld_str.c                                                             |
3
 |                                                                           |
4
 | All of the functions which transfer data between user memory and FPU_REGs.|
5
 |                                                                           |
6
 | Copyright (C) 1992,1993,1994,1996                                         |
7
 |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
8
 |                  E-mail   billm@jacobi.maths.monash.edu.au                |
9
 |                                                                           |
10
 |                                                                           |
11
 +---------------------------------------------------------------------------*/
12
 
13
/*---------------------------------------------------------------------------+
14
 | Note:                                                                     |
15
 |    The file contains code which accesses user memory.                     |
16
 |    Emulator static data may change when user memory is accessed, due to   |
17
 |    other processes using the emulator while swapping is in progress.      |
18
 +---------------------------------------------------------------------------*/
19
 
20
#include <asm/segment.h>
21
 
22
#include "fpu_system.h"
23
#include "exception.h"
24
#include "reg_constant.h"
25
#include "fpu_emu.h"
26
#include "control_w.h"
27
#include "status_w.h"
28
 
29
 
30
#define EXTENDED_Ebias 0x3fff
31
#define EXTENDED_Emin (-0x3ffe)  /* smallest valid exponent */
32
 
33
#define DOUBLE_Emax 1023         /* largest valid exponent */
34
#define DOUBLE_Ebias 1023
35
#define DOUBLE_Emin (-1022)      /* smallest valid exponent */
36
 
37
#define SINGLE_Emax 127          /* largest valid exponent */
38
#define SINGLE_Ebias 127
39
#define SINGLE_Emin (-126)       /* smallest valid exponent */
40
 
41
static void write_to_extended(FPU_REG *rp, char *d);
42
 
43
 
44
/* Get a long double from user memory */
45
int reg_load_extended(long double *s, FPU_REG *loaded_data)
46
{
47
  unsigned long sigl, sigh, exp;
48
 
49
  RE_ENTRANT_CHECK_OFF;
50
  FPU_verify_area(VERIFY_READ, s, 10);
51
  sigl = get_fs_long((unsigned long *) s);
52
  sigh = get_fs_long(1 + (unsigned long *) s);
53
  exp = get_fs_word(4 + (unsigned short *) s);
54
  RE_ENTRANT_CHECK_ON;
55
 
56
  loaded_data->tag = TW_Valid;   /* Default */
57
  loaded_data->sigl = sigl;
58
  loaded_data->sigh = sigh;
59
  if (exp & 0x8000)
60
    loaded_data->sign = SIGN_NEG;
61
  else
62
    loaded_data->sign = SIGN_POS;
63
  exp &= 0x7fff;
64
  loaded_data->exp = exp - EXTENDED_Ebias + EXP_BIAS;
65
 
66
  if ( exp == 0 )
67
    {
68
      if ( !(sigh | sigl) )
69
        {
70
          loaded_data->tag = TW_Zero;
71
          return 0;
72
        }
73
      /* The number is a de-normal or pseudodenormal. */
74
      if (sigh & 0x80000000)
75
        {
76
          /* Is a pseudodenormal. */
77
          /* Convert it for internal use. */
78
          /* This is non-80486 behaviour because the number
79
             loses its 'denormal' identity. */
80
          loaded_data->exp++;
81
          return 1;
82
        }
83
      else
84
        {
85
          /* Is a denormal. */
86
          /* Convert it for internal use. */
87
          loaded_data->exp++;
88
          normalize_nuo(loaded_data);
89
          return 0;
90
        }
91
    }
92
  else if ( exp == 0x7fff )
93
    {
94
      if ( !((sigh ^ 0x80000000) | sigl) )
95
        {
96
          /* Matches the bit pattern for Infinity. */
97
          loaded_data->exp = EXP_Infinity;
98
          loaded_data->tag = TW_Infinity;
99
          return 0;
100
        }
101
 
102
      loaded_data->exp = EXP_NaN;
103
      loaded_data->tag = TW_NaN;
104
      if ( !(sigh & 0x80000000) )
105
        {
106
          /* NaNs have the ms bit set to 1. */
107
          /* This is therefore an Unsupported NaN data type. */
108
          /* This is non 80486 behaviour */
109
          /* This should generate an Invalid Operand exception
110
             later, so we convert it to a SNaN */
111
          loaded_data->sigh = 0x80000000;
112
          loaded_data->sigl = 0x00000001;
113
          loaded_data->sign = SIGN_NEG;
114
          return 1;
115
        }
116
      return 0;
117
    }
118
 
119
  if ( !(sigh & 0x80000000) )
120
    {
121
      /* Unsupported data type. */
122
      /* Valid numbers have the ms bit set to 1. */
123
      /* Unnormal. */
124
      /* Convert it for internal use. */
125
      /* This is non-80486 behaviour */
126
      /* This should generate an Invalid Operand exception
127
         later, so we convert it to a SNaN */
128
      loaded_data->sigh = 0x80000000;
129
      loaded_data->sigl = 0x00000001;
130
      loaded_data->sign = SIGN_NEG;
131
      loaded_data->exp = EXP_NaN;
132
      loaded_data->tag = TW_NaN;
133
      return 1;
134
    }
135
  return 0;
136
}
137
 
138
 
139
/* Get a double from user memory */
140
int reg_load_double(double *dfloat, FPU_REG *loaded_data)
141
{
142
  int exp;
143
  unsigned m64, l64;
144
 
145
  RE_ENTRANT_CHECK_OFF;
146
  FPU_verify_area(VERIFY_READ, dfloat, 8);
147
  m64 = get_fs_long(1 + (unsigned long *) dfloat);
148
  l64 = get_fs_long((unsigned long *) dfloat);
149
  RE_ENTRANT_CHECK_ON;
150
 
151
  if (m64 & 0x80000000)
152
    loaded_data->sign = SIGN_NEG;
153
  else
154
    loaded_data->sign = SIGN_POS;
155
  exp = ((m64 & 0x7ff00000) >> 20) - DOUBLE_Ebias;
156
  m64 &= 0xfffff;
157
  if (exp > DOUBLE_Emax)
158
    {
159
      /* Infinity or NaN */
160
      if ((m64 == 0) && (l64 == 0))
161
        {
162
          /* +- infinity */
163
          loaded_data->sigh = 0x80000000;
164
          loaded_data->sigl = 0x00000000;
165
          loaded_data->exp = EXP_Infinity;
166
          loaded_data->tag = TW_Infinity;
167
          return 0;
168
        }
169
      else
170
        {
171
          /* Must be a signaling or quiet NaN */
172
          loaded_data->exp = EXP_NaN;
173
          loaded_data->tag = TW_NaN;
174
          loaded_data->sigh = (m64 << 11) | 0x80000000;
175
          loaded_data->sigh |= l64 >> 21;
176
          loaded_data->sigl = l64 << 11;
177
          return 0; /* The calling function must look for NaNs */
178
        }
179
    }
180
  else if ( exp < DOUBLE_Emin )
181
    {
182
      /* Zero or de-normal */
183
      if ((m64 == 0) && (l64 == 0))
184
        {
185
          /* Zero */
186
          int c = loaded_data->sign;
187
          reg_move(&CONST_Z, loaded_data);
188
          loaded_data->sign = c;
189
          return 0;
190
        }
191
      else
192
        {
193
          /* De-normal */
194
          loaded_data->exp = DOUBLE_Emin + EXP_BIAS;
195
          loaded_data->tag = TW_Valid;
196
          loaded_data->sigh = m64 << 11;
197
          loaded_data->sigh |= l64 >> 21;
198
          loaded_data->sigl = l64 << 11;
199
          normalize_nuo(loaded_data);
200
          return denormal_operand();
201
        }
202
    }
203
  else
204
    {
205
      loaded_data->exp = exp + EXP_BIAS;
206
      loaded_data->tag = TW_Valid;
207
      loaded_data->sigh = (m64 << 11) | 0x80000000;
208
      loaded_data->sigh |= l64 >> 21;
209
      loaded_data->sigl = l64 << 11;
210
 
211
      return 0;
212
    }
213
}
214
 
215
 
216
/* Get a float from user memory */
217
int reg_load_single(float *single, FPU_REG *loaded_data)
218
{
219
  unsigned m32;
220
  int exp;
221
 
222
  RE_ENTRANT_CHECK_OFF;
223
  FPU_verify_area(VERIFY_READ, single, 4);
224
  m32 = get_fs_long((unsigned long *) single);
225
  RE_ENTRANT_CHECK_ON;
226
 
227
  if (m32 & 0x80000000)
228
    loaded_data->sign = SIGN_NEG;
229
  else
230
    loaded_data->sign = SIGN_POS;
231
  if (!(m32 & 0x7fffffff))
232
    {
233
      /* Zero */
234
      int c = loaded_data->sign;
235
      reg_move(&CONST_Z, loaded_data);
236
      loaded_data->sign = c;
237
      return 0;
238
    }
239
  exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias;
240
  m32 = (m32 & 0x7fffff) << 8;
241
  if ( exp < SINGLE_Emin )
242
    {
243
      /* De-normals */
244
      loaded_data->exp = SINGLE_Emin + EXP_BIAS;
245
      loaded_data->tag = TW_Valid;
246
      loaded_data->sigh = m32;
247
      loaded_data->sigl = 0;
248
      normalize_nuo(loaded_data);
249
      return denormal_operand();
250
    }
251
  else if ( exp > SINGLE_Emax )
252
    {
253
    /* Infinity or NaN */
254
      if ( m32 == 0 )
255
        {
256
          /* +- infinity */
257
          loaded_data->sigh = 0x80000000;
258
          loaded_data->sigl = 0x00000000;
259
          loaded_data->exp = EXP_Infinity;
260
          loaded_data->tag = TW_Infinity;
261
          return 0;
262
        }
263
      else
264
        {
265
          /* Must be a signaling or quiet NaN */
266
          loaded_data->exp = EXP_NaN;
267
          loaded_data->tag = TW_NaN;
268
          loaded_data->sigh = m32 | 0x80000000;
269
          loaded_data->sigl = 0;
270
          return 0; /* The calling function must look for NaNs */
271
        }
272
    }
273
  else
274
    {
275
      loaded_data->exp = exp + EXP_BIAS;
276
      loaded_data->sigh = m32 | 0x80000000;
277
      loaded_data->sigl = 0;
278
      loaded_data->tag = TW_Valid;
279
      return 0;
280
    }
281
}
282
 
283
 
284
/* Get a long long from user memory */
285
void reg_load_int64(long long *_s, FPU_REG *loaded_data)
286
{
287
  int e;
288
  long long s;
289
 
290
  RE_ENTRANT_CHECK_OFF;
291
  FPU_verify_area(VERIFY_READ, _s, 8);
292
  ((unsigned long *)&s)[0] = get_fs_long((unsigned long *) _s);
293
  ((unsigned long *)&s)[1] = get_fs_long(1 + (unsigned long *) _s);
294
  RE_ENTRANT_CHECK_ON;
295
 
296
  if (s == 0)
297
    { reg_move(&CONST_Z, loaded_data); return; }
298
 
299
  if (s > 0)
300
    loaded_data->sign = SIGN_POS;
301
  else
302
  {
303
    s = -s;
304
    loaded_data->sign = SIGN_NEG;
305
  }
306
 
307
  e = EXP_BIAS + 63;
308
  significand(loaded_data) = s;
309
  loaded_data->exp = e;
310
  loaded_data->tag = TW_Valid;
311
  normalize_nuo(loaded_data);
312
}
313
 
314
 
315
/* Get a long from user memory */
316
void reg_load_int32(long *_s, FPU_REG *loaded_data)
317
{
318
  long s;
319
  int e;
320
 
321
  RE_ENTRANT_CHECK_OFF;
322
  FPU_verify_area(VERIFY_READ, _s, 4);
323
  s = (long)get_fs_long((unsigned long *) _s);
324
  RE_ENTRANT_CHECK_ON;
325
 
326
  if (s == 0)
327
    { reg_move(&CONST_Z, loaded_data); return; }
328
 
329
  if (s > 0)
330
    loaded_data->sign = SIGN_POS;
331
  else
332
  {
333
    s = -s;
334
    loaded_data->sign = SIGN_NEG;
335
  }
336
 
337
  e = EXP_BIAS + 31;
338
  loaded_data->sigh = s;
339
  loaded_data->sigl = 0;
340
  loaded_data->exp = e;
341
  loaded_data->tag = TW_Valid;
342
  normalize_nuo(loaded_data);
343
}
344
 
345
 
346
/* Get a short from user memory */
347
void reg_load_int16(short *_s, FPU_REG *loaded_data)
348
{
349
  int s, e;
350
 
351
  RE_ENTRANT_CHECK_OFF;
352
  FPU_verify_area(VERIFY_READ, _s, 2);
353
  /* Cast as short to get the sign extended. */
354
  s = (short)get_fs_word((unsigned short *) _s);
355
  RE_ENTRANT_CHECK_ON;
356
 
357
  if (s == 0)
358
    { reg_move(&CONST_Z, loaded_data); return; }
359
 
360
  if (s > 0)
361
    loaded_data->sign = SIGN_POS;
362
  else
363
  {
364
    s = -s;
365
    loaded_data->sign = SIGN_NEG;
366
  }
367
 
368
  e = EXP_BIAS + 15;
369
  loaded_data->sigh = s << 16;
370
 
371
  loaded_data->sigl = 0;
372
  loaded_data->exp = e;
373
  loaded_data->tag = TW_Valid;
374
  normalize_nuo(loaded_data);
375
}
376
 
377
 
378
/* Get a packed bcd array from user memory */
379
void reg_load_bcd(char *s, FPU_REG *loaded_data)
380
{
381
  int pos;
382
  unsigned char bcd;
383
  long long l=0;
384
 
385
  RE_ENTRANT_CHECK_OFF;
386
  FPU_verify_area(VERIFY_READ, s, 10);
387
  RE_ENTRANT_CHECK_ON;
388
  for ( pos = 8; pos >= 0; pos--)
389
    {
390
      l *= 10;
391
      RE_ENTRANT_CHECK_OFF;
392
      bcd = (unsigned char)get_fs_byte((unsigned char *) s+pos);
393
      RE_ENTRANT_CHECK_ON;
394
      l += bcd >> 4;
395
      l *= 10;
396
      l += bcd & 0x0f;
397
    }
398
 
399
  RE_ENTRANT_CHECK_OFF;
400
  loaded_data->sign =
401
    ((unsigned char)get_fs_byte((unsigned char *) s+9)) & 0x80 ?
402
      SIGN_NEG : SIGN_POS;
403
  RE_ENTRANT_CHECK_ON;
404
 
405
  if (l == 0)
406
    {
407
      char sign = loaded_data->sign;
408
      reg_move(&CONST_Z, loaded_data);
409
      loaded_data->sign = sign;
410
    }
411
  else
412
    {
413
      significand(loaded_data) = l;
414
      loaded_data->exp = EXP_BIAS + 63;
415
      loaded_data->tag = TW_Valid;
416
      normalize_nuo(loaded_data);
417
    }
418
}
419
 
420
/*===========================================================================*/
421
 
422
/* Put a long double into user memory */
423
int reg_store_extended(long double *d, FPU_REG *st0_ptr)
424
{
425
  /*
426
    The only exception raised by an attempt to store to an
427
    extended format is the Invalid Stack exception, i.e.
428
    attempting to store from an empty register.
429
   */
430
 
431
  if ( st0_ptr->tag != TW_Empty )
432
    {
433
      RE_ENTRANT_CHECK_OFF;
434
      FPU_verify_area(VERIFY_WRITE, d, 10);
435
      RE_ENTRANT_CHECK_ON;
436
      write_to_extended(st0_ptr, (char *) d);
437
      return 1;
438
    }
439
 
440
  /* Empty register (stack underflow) */
441
  EXCEPTION(EX_StackUnder);
442
  if ( control_word & CW_Invalid )
443
    {
444
      /* The masked response */
445
      /* Put out the QNaN indefinite */
446
      RE_ENTRANT_CHECK_OFF;
447
      FPU_verify_area(VERIFY_WRITE,d,10);
448
      put_fs_long(0, (unsigned long *) d);
449
      put_fs_long(0xc0000000, 1 + (unsigned long *) d);
450
      put_fs_word(0xffff, 4 + (short *) d);
451
      RE_ENTRANT_CHECK_ON;
452
      return 1;
453
    }
454
  else
455
    return 0;
456
 
457
}
458
 
459
 
460
/* Put a double into user memory */
461
int reg_store_double(double *dfloat, FPU_REG *st0_ptr)
462
{
463
  unsigned long l[2];
464
  unsigned long increment = 0;   /* avoid gcc warnings */
465
  char st0_tag = st0_ptr->tag;
466
 
467
  if (st0_tag == TW_Valid)
468
    {
469
      int precision_loss;
470
      int exp;
471
      FPU_REG tmp;
472
 
473
      reg_move(st0_ptr, &tmp);
474
      exp = tmp.exp - EXP_BIAS;
475
 
476
      if ( exp < DOUBLE_Emin )     /* It may be a denormal */
477
        {
478
          /* A denormal will always underflow. */
479
#ifndef PECULIAR_486
480
          /* An 80486 is supposed to be able to generate
481
             a denormal exception here, but... */
482
          if ( st0_ptr->exp <= EXP_UNDER )
483
            {
484
              /* Underflow has priority. */
485
              if ( control_word & CW_Underflow )
486
                denormal_operand();
487
            }
488
#endif PECULIAR_486
489
 
490
          tmp.exp += -DOUBLE_Emin + 52;  /* largest exp to be 51 */
491
 
492
          if ( (precision_loss = round_to_int(&tmp)) )
493
            {
494
#ifdef PECULIAR_486
495
              /* Did it round to a non-denormal ? */
496
              /* This behaviour might be regarded as peculiar, it appears
497
                 that the 80486 rounds to the dest precision, then
498
                 converts to decide underflow. */
499
              if ( !((tmp.sigh == 0x00100000) && (tmp.sigl == 0) &&
500
                  (st0_ptr->sigl & 0x000007ff)) )
501
#endif PECULIAR_486
502
                {
503
                  EXCEPTION(EX_Underflow);
504
                  /* This is a special case: see sec 16.2.5.1 of
505
                     the 80486 book */
506
                  if ( !(control_word & CW_Underflow) )
507
                    return 0;
508
                }
509
              EXCEPTION(precision_loss);
510
              if ( !(control_word & CW_Precision) )
511
                return 0;
512
            }
513
          l[0] = tmp.sigl;
514
          l[1] = tmp.sigh;
515
        }
516
      else
517
        {
518
          if ( tmp.sigl & 0x000007ff )
519
            {
520
              precision_loss = 1;
521
              switch (control_word & CW_RC)
522
                {
523
                case RC_RND:
524
                  /* Rounding can get a little messy.. */
525
                  increment = ((tmp.sigl & 0x7ff) > 0x400) |  /* nearest */
526
                    ((tmp.sigl & 0xc00) == 0xc00);            /* odd -> even */
527
                  break;
528
                case RC_DOWN:   /* towards -infinity */
529
                  increment = (tmp.sign == SIGN_POS) ? 0 : tmp.sigl & 0x7ff;
530
                  break;
531
                case RC_UP:     /* towards +infinity */
532
                  increment = (tmp.sign == SIGN_POS) ? tmp.sigl & 0x7ff : 0;
533
                  break;
534
                case RC_CHOP:
535
                  increment = 0;
536
                  break;
537
                }
538
 
539
              /* Truncate the mantissa */
540
              tmp.sigl &= 0xfffff800;
541
 
542
              if ( increment )
543
                {
544
                  if ( tmp.sigl >= 0xfffff800 )
545
                    {
546
                      /* the sigl part overflows */
547
                      if ( tmp.sigh == 0xffffffff )
548
                        {
549
                          /* The sigh part overflows */
550
                          tmp.sigh = 0x80000000;
551
                          exp++;
552
                          if (exp >= EXP_OVER)
553
                            goto overflow;
554
                        }
555
                      else
556
                        {
557
                          tmp.sigh ++;
558
                        }
559
                      tmp.sigl = 0x00000000;
560
                    }
561
                  else
562
                    {
563
                      /* We only need to increment sigl */
564
                      tmp.sigl += 0x00000800;
565
                    }
566
                }
567
            }
568
          else
569
            precision_loss = 0;
570
 
571
          l[0] = (tmp.sigl >> 11) | (tmp.sigh << 21);
572
          l[1] = ((tmp.sigh >> 11) & 0xfffff);
573
 
574
          if ( exp > DOUBLE_Emax )
575
            {
576
            overflow:
577
              EXCEPTION(EX_Overflow);
578
              if ( !(control_word & CW_Overflow) )
579
                return 0;
580
              set_precision_flag_up();
581
              if ( !(control_word & CW_Precision) )
582
                return 0;
583
 
584
              /* This is a special case: see sec 16.2.5.1 of the 80486 book */
585
              /* Overflow to infinity */
586
              l[0] = 0x00000000; /* Set to */
587
              l[1] = 0x7ff00000;        /* + INF */
588
            }
589
          else
590
            {
591
              if ( precision_loss )
592
                {
593
                  if ( increment )
594
                    set_precision_flag_up();
595
                  else
596
                    set_precision_flag_down();
597
                }
598
              /* Add the exponent */
599
              l[1] |= (((exp+DOUBLE_Ebias) & 0x7ff) << 20);
600
            }
601
        }
602
    }
603
  else if (st0_tag == TW_Zero)
604
    {
605
      /* Number is zero */
606
      l[0] = 0;
607
      l[1] = 0;
608
    }
609
  else if (st0_tag == TW_Infinity)
610
    {
611
      l[0] = 0;
612
      l[1] = 0x7ff00000;
613
    }
614
  else if (st0_tag == TW_NaN)
615
    {
616
      /* See if we can get a valid NaN from the FPU_REG */
617
      l[0] = (st0_ptr->sigl >> 11) | (st0_ptr->sigh << 21);
618
      l[1] = ((st0_ptr->sigh >> 11) & 0xfffff);
619
      if ( !(st0_ptr->sigh & 0x40000000) )
620
        {
621
          /* It is a signalling NaN */
622
          EXCEPTION(EX_Invalid);
623
          if ( !(control_word & CW_Invalid) )
624
            return 0;
625
          l[1] |= (0x40000000 >> 11);
626
        }
627
      l[1] |= 0x7ff00000;
628
    }
629
  else if ( st0_tag == TW_Empty )
630
    {
631
      /* Empty register (stack underflow) */
632
      EXCEPTION(EX_StackUnder);
633
      if ( control_word & CW_Invalid )
634
        {
635
          /* The masked response */
636
          /* Put out the QNaN indefinite */
637
          RE_ENTRANT_CHECK_OFF;
638
          FPU_verify_area(VERIFY_WRITE,(void *)dfloat,8);
639
          put_fs_long(0, (unsigned long *) dfloat);
640
          put_fs_long(0xfff80000, 1 + (unsigned long *) dfloat);
641
          RE_ENTRANT_CHECK_ON;
642
          return 1;
643
        }
644
      else
645
        return 0;
646
    }
647
  if ( st0_ptr->sign )
648
    l[1] |= 0x80000000;
649
 
650
  RE_ENTRANT_CHECK_OFF;
651
  FPU_verify_area(VERIFY_WRITE,(void *)dfloat,8);
652
  put_fs_long(l[0], (unsigned long *)dfloat);
653
  put_fs_long(l[1], 1 + (unsigned long *)dfloat);
654
  RE_ENTRANT_CHECK_ON;
655
 
656
  return 1;
657
}
658
 
659
 
660
/* Put a float into user memory */
661
int reg_store_single(float *single, FPU_REG *st0_ptr)
662
{
663
  long templ;
664
  unsigned long increment = 0;           /* avoid gcc warnings */
665
  char st0_tag = st0_ptr->tag;
666
 
667
  if (st0_tag == TW_Valid)
668
    {
669
      int precision_loss;
670
      int exp;
671
      FPU_REG tmp;
672
 
673
      reg_move(st0_ptr, &tmp);
674
      exp = tmp.exp - EXP_BIAS;
675
 
676
      if ( exp < SINGLE_Emin )
677
        {
678
          /* A denormal will always underflow. */
679
#ifndef PECULIAR_486
680
          /* An 80486 is supposed to be able to generate
681
             a denormal exception here, but... */
682
          if ( st0_ptr->exp <= EXP_UNDER )
683
            {
684
              /* Underflow has priority. */
685
              if ( control_word & CW_Underflow )
686
                denormal_operand();
687
            }
688
#endif PECULIAR_486
689
 
690
          tmp.exp += -SINGLE_Emin + 23;  /* largest exp to be 22 */
691
 
692
          if ( (precision_loss = round_to_int(&tmp)) )
693
            {
694
#ifdef PECULIAR_486
695
              /* Did it round to a non-denormal ? */
696
              /* This behaviour might be regarded as peculiar, it appears
697
                 that the 80486 rounds to the dest precision, then
698
                 converts to decide underflow. */
699
              if ( !((tmp.sigl == 0x00800000) &&
700
                  ((st0_ptr->sigh & 0x000000ff) || st0_ptr->sigl)) )
701
#endif PECULIAR_486
702
                {
703
                  EXCEPTION(EX_Underflow);
704
                  /* This is a special case: see sec 16.2.5.1 of
705
                     the 80486 book */
706
                  if ( !(control_word & EX_Underflow) )
707
                    return 0;
708
                }
709
              EXCEPTION(precision_loss);
710
              if ( !(control_word & EX_Precision) )
711
                return 0;
712
            }
713
          templ = tmp.sigl;
714
        }
715
      else
716
        {
717
          if ( tmp.sigl | (tmp.sigh & 0x000000ff) )
718
            {
719
              unsigned long sigh = tmp.sigh;
720
              unsigned long sigl = tmp.sigl;
721
 
722
              precision_loss = 1;
723
              switch (control_word & CW_RC)
724
                {
725
                case RC_RND:
726
                  increment = ((sigh & 0xff) > 0x80)       /* more than half */
727
                    || (((sigh & 0xff) == 0x80) && sigl)   /* more than half */
728
                      || ((sigh & 0x180) == 0x180);        /* round to even */
729
                  break;
730
                case RC_DOWN:   /* towards -infinity */
731
                  increment = (tmp.sign == SIGN_POS)
732
                              ? 0 : (sigl | (sigh & 0xff));
733
                  break;
734
                case RC_UP:     /* towards +infinity */
735
                  increment = (tmp.sign == SIGN_POS)
736
                              ? (sigl | (sigh & 0xff)) : 0;
737
                  break;
738
                case RC_CHOP:
739
                  increment = 0;
740
                  break;
741
                }
742
 
743
              /* Truncate part of the mantissa */
744
              tmp.sigl = 0;
745
 
746
              if (increment)
747
                {
748
                  if ( sigh >= 0xffffff00 )
749
                    {
750
                      /* The sigh part overflows */
751
                      tmp.sigh = 0x80000000;
752
                      exp++;
753
                      if ( exp >= EXP_OVER )
754
                        goto overflow;
755
                    }
756
                  else
757
                    {
758
                      tmp.sigh &= 0xffffff00;
759
                      tmp.sigh += 0x100;
760
                    }
761
                }
762
              else
763
                {
764
                  tmp.sigh &= 0xffffff00;  /* Finish the truncation */
765
                }
766
            }
767
          else
768
            precision_loss = 0;
769
 
770
          templ = (tmp.sigh >> 8) & 0x007fffff;
771
 
772
          if ( exp > SINGLE_Emax )
773
            {
774
            overflow:
775
              EXCEPTION(EX_Overflow);
776
              if ( !(control_word & CW_Overflow) )
777
                return 0;
778
              set_precision_flag_up();
779
              if ( !(control_word & CW_Precision) )
780
                return 0;
781
 
782
              /* This is a special case: see sec 16.2.5.1 of the 80486 book. */
783
              /* Masked response is overflow to infinity. */
784
              templ = 0x7f800000;
785
            }
786
          else
787
            {
788
              if ( precision_loss )
789
                {
790
                  if ( increment )
791
                    set_precision_flag_up();
792
                  else
793
                    set_precision_flag_down();
794
                }
795
              /* Add the exponent */
796
              templ |= ((exp+SINGLE_Ebias) & 0xff) << 23;
797
            }
798
        }
799
    }
800
  else if (st0_tag == TW_Zero)
801
    {
802
      templ = 0;
803
    }
804
  else if (st0_tag == TW_Infinity)
805
    {
806
      templ = 0x7f800000;
807
    }
808
  else if (st0_tag == TW_NaN)
809
    {
810
      /* See if we can get a valid NaN from the FPU_REG */
811
      templ = st0_ptr->sigh >> 8;
812
      if ( !(st0_ptr->sigh & 0x40000000) )
813
        {
814
          /* It is a signalling NaN */
815
          EXCEPTION(EX_Invalid);
816
          if ( !(control_word & CW_Invalid) )
817
            return 0;
818
          templ |= (0x40000000 >> 8);
819
        }
820
      templ |= 0x7f800000;
821
    }
822
  else if ( st0_tag == TW_Empty )
823
    {
824
      /* Empty register (stack underflow) */
825
      EXCEPTION(EX_StackUnder);
826
      if ( control_word & EX_Invalid )
827
        {
828
          /* The masked response */
829
          /* Put out the QNaN indefinite */
830
          RE_ENTRANT_CHECK_OFF;
831
          FPU_verify_area(VERIFY_WRITE,(void *)single,4);
832
          put_fs_long(0xffc00000, (unsigned long *) single);
833
          RE_ENTRANT_CHECK_ON;
834
          return 1;
835
        }
836
      else
837
        return 0;
838
    }
839
#ifdef PARANOID
840
  else
841
    {
842
      EXCEPTION(EX_INTERNAL|0x163);
843
      return 0;
844
    }
845
#endif
846
  if (st0_ptr->sign)
847
    templ |= 0x80000000;
848
 
849
  RE_ENTRANT_CHECK_OFF;
850
  FPU_verify_area(VERIFY_WRITE,(void *)single,4);
851
  put_fs_long(templ,(unsigned long *) single);
852
  RE_ENTRANT_CHECK_ON;
853
 
854
  return 1;
855
}
856
 
857
 
858
/* Put a long long into user memory */
859
int reg_store_int64(long long *d, FPU_REG *st0_ptr)
860
{
861
  FPU_REG t;
862
  long long tll;
863
  int precision_loss;
864
  char st0_tag = st0_ptr->tag;
865
 
866
  if ( st0_tag == TW_Empty )
867
    {
868
      /* Empty register (stack underflow) */
869
      EXCEPTION(EX_StackUnder);
870
      goto invalid_operand;
871
    }
872
  else if ( (st0_tag == TW_Infinity) ||
873
           (st0_tag == TW_NaN) )
874
    {
875
      EXCEPTION(EX_Invalid);
876
      goto invalid_operand;
877
    }
878
 
879
  reg_move(st0_ptr, &t);
880
  precision_loss = round_to_int(&t);
881
  ((long *)&tll)[0] = t.sigl;
882
  ((long *)&tll)[1] = t.sigh;
883
  if ( (precision_loss == 1) ||
884
      ((t.sigh & 0x80000000) &&
885
       !((t.sigh == 0x80000000) && (t.sigl == 0) &&
886
         (t.sign == SIGN_NEG))) )
887
    {
888
      EXCEPTION(EX_Invalid);
889
      /* This is a special case: see sec 16.2.5.1 of the 80486 book */
890
    invalid_operand:
891
      if ( control_word & EX_Invalid )
892
        {
893
          /* Produce something like QNaN "indefinite" */
894
          tll = 0x8000000000000000LL;
895
        }
896
      else
897
        return 0;
898
    }
899
  else
900
    {
901
      if ( precision_loss )
902
        set_precision_flag(precision_loss);
903
      if ( t.sign )
904
        tll = - tll;
905
    }
906
 
907
  RE_ENTRANT_CHECK_OFF;
908
  FPU_verify_area(VERIFY_WRITE,(void *)d,8);
909
  put_fs_long(((long *)&tll)[0],(unsigned long *) d);
910
  put_fs_long(((long *)&tll)[1],1 + (unsigned long *) d);
911
  RE_ENTRANT_CHECK_ON;
912
 
913
  return 1;
914
}
915
 
916
 
917
/* Put a long into user memory */
918
int reg_store_int32(long *d, FPU_REG *st0_ptr)
919
{
920
  FPU_REG t;
921
  int precision_loss;
922
  char st0_tag = st0_ptr->tag;
923
 
924
  if ( st0_tag == TW_Empty )
925
    {
926
      /* Empty register (stack underflow) */
927
      EXCEPTION(EX_StackUnder);
928
      goto invalid_operand;
929
    }
930
  else if ( (st0_tag == TW_Infinity) ||
931
           (st0_tag == TW_NaN) )
932
    {
933
      EXCEPTION(EX_Invalid);
934
      goto invalid_operand;
935
    }
936
 
937
  reg_move(st0_ptr, &t);
938
  precision_loss = round_to_int(&t);
939
  if (t.sigh ||
940
      ((t.sigl & 0x80000000) &&
941
       !((t.sigl == 0x80000000) && (t.sign == SIGN_NEG))) )
942
    {
943
      EXCEPTION(EX_Invalid);
944
      /* This is a special case: see sec 16.2.5.1 of the 80486 book */
945
    invalid_operand:
946
      if ( control_word & EX_Invalid )
947
        {
948
          /* Produce something like QNaN "indefinite" */
949
          t.sigl = 0x80000000;
950
        }
951
      else
952
        return 0;
953
    }
954
  else
955
    {
956
      if ( precision_loss )
957
        set_precision_flag(precision_loss);
958
      if ( t.sign )
959
        t.sigl = -(long)t.sigl;
960
    }
961
 
962
  RE_ENTRANT_CHECK_OFF;
963
  FPU_verify_area(VERIFY_WRITE,d,4);
964
  put_fs_long(t.sigl, (unsigned long *) d);
965
  RE_ENTRANT_CHECK_ON;
966
 
967
  return 1;
968
}
969
 
970
 
971
/* Put a short into user memory */
972
int reg_store_int16(short *d, FPU_REG *st0_ptr)
973
{
974
  FPU_REG t;
975
  int precision_loss;
976
  char st0_tag = st0_ptr->tag;
977
 
978
  if ( st0_tag == TW_Empty )
979
    {
980
      /* Empty register (stack underflow) */
981
      EXCEPTION(EX_StackUnder);
982
      goto invalid_operand;
983
    }
984
  else if ( (st0_tag == TW_Infinity) ||
985
           (st0_tag == TW_NaN) )
986
    {
987
      EXCEPTION(EX_Invalid);
988
      goto invalid_operand;
989
    }
990
 
991
  reg_move(st0_ptr, &t);
992
  precision_loss = round_to_int(&t);
993
  if (t.sigh ||
994
      ((t.sigl & 0xffff8000) &&
995
       !((t.sigl == 0x8000) && (t.sign == SIGN_NEG))) )
996
    {
997
      EXCEPTION(EX_Invalid);
998
      /* This is a special case: see sec 16.2.5.1 of the 80486 book */
999
    invalid_operand:
1000
      if ( control_word & EX_Invalid )
1001
        {
1002
          /* Produce something like QNaN "indefinite" */
1003
          t.sigl = 0x8000;
1004
        }
1005
      else
1006
        return 0;
1007
    }
1008
  else
1009
    {
1010
      if ( precision_loss )
1011
        set_precision_flag(precision_loss);
1012
      if ( t.sign )
1013
        t.sigl = -t.sigl;
1014
    }
1015
 
1016
  RE_ENTRANT_CHECK_OFF;
1017
  FPU_verify_area(VERIFY_WRITE,d,2);
1018
  put_fs_word((short)t.sigl,(short *) d);
1019
  RE_ENTRANT_CHECK_ON;
1020
 
1021
  return 1;
1022
}
1023
 
1024
 
1025
/* Put a packed bcd array into user memory */
1026
int reg_store_bcd(char *d, FPU_REG *st0_ptr)
1027
{
1028
  FPU_REG t;
1029
  unsigned long long ll;
1030
  unsigned char b;
1031
  int i, precision_loss;
1032
  unsigned char sign = (st0_ptr->sign == SIGN_NEG) ? 0x80 : 0;
1033
  char st0_tag = st0_ptr->tag;
1034
 
1035
  if ( st0_tag == TW_Empty )
1036
    {
1037
      /* Empty register (stack underflow) */
1038
      EXCEPTION(EX_StackUnder);
1039
      goto invalid_operand;
1040
    }
1041
 
1042
  reg_move(st0_ptr, &t);
1043
  precision_loss = round_to_int(&t);
1044
  ll = significand(&t);
1045
 
1046
  /* Check for overflow, by comparing with 999999999999999999 decimal. */
1047
  if ( (t.sigh > 0x0de0b6b3) ||
1048
      ((t.sigh == 0x0de0b6b3) && (t.sigl > 0xa763ffff)) )
1049
    {
1050
      EXCEPTION(EX_Invalid);
1051
      /* This is a special case: see sec 16.2.5.1 of the 80486 book */
1052
    invalid_operand:
1053
      if ( control_word & CW_Invalid )
1054
        {
1055
          /* Produce the QNaN "indefinite" */
1056
          RE_ENTRANT_CHECK_OFF;
1057
          FPU_verify_area(VERIFY_WRITE,d,10);
1058
          for ( i = 0; i < 7; i++)
1059
            put_fs_byte(0, (unsigned char *) d+i); /* These bytes "undefined" */
1060
          put_fs_byte(0xc0, (unsigned char *) d+7); /* This byte "undefined" */
1061
          put_fs_byte(0xff, (unsigned char *) d+8);
1062
          put_fs_byte(0xff, (unsigned char *) d+9);
1063
          RE_ENTRANT_CHECK_ON;
1064
          return 1;
1065
        }
1066
      else
1067
        return 0;
1068
    }
1069
  else if ( precision_loss )
1070
    {
1071
      /* Precision loss doesn't stop the data transfer */
1072
      set_precision_flag(precision_loss);
1073
    }
1074
 
1075
  RE_ENTRANT_CHECK_OFF;
1076
  FPU_verify_area(VERIFY_WRITE,d,10);
1077
  RE_ENTRANT_CHECK_ON;
1078
  for ( i = 0; i < 9; i++)
1079
    {
1080
      b = div_small(&ll, 10);
1081
      b |= (div_small(&ll, 10)) << 4;
1082
      RE_ENTRANT_CHECK_OFF;
1083
      put_fs_byte(b,(unsigned char *) d+i);
1084
      RE_ENTRANT_CHECK_ON;
1085
    }
1086
  RE_ENTRANT_CHECK_OFF;
1087
  put_fs_byte(sign,(unsigned char *) d+9);
1088
  RE_ENTRANT_CHECK_ON;
1089
 
1090
  return 1;
1091
}
1092
 
1093
/*===========================================================================*/
1094
 
1095
/* r gets mangled such that sig is int, sign:
1096
   it is NOT normalized */
1097
/* The return value (in eax) is zero if the result is exact,
1098
   if bits are changed due to rounding, truncation, etc, then
1099
   a non-zero value is returned */
1100
/* Overflow is signalled by a non-zero return value (in eax).
1101
   In the case of overflow, the returned significand always has the
1102
   largest possible value */
1103
int round_to_int(FPU_REG *r)
1104
{
1105
  char     very_big;
1106
  unsigned eax;
1107
 
1108
  if (r->tag == TW_Zero)
1109
    {
1110
      /* Make sure that zero is returned */
1111
      significand(r) = 0;
1112
      return 0;        /* o.k. */
1113
    }
1114
 
1115
  if (r->exp > EXP_BIAS + 63)
1116
    {
1117
      r->sigl = r->sigh = ~0;      /* The largest representable number */
1118
      return 1;        /* overflow */
1119
    }
1120
 
1121
  eax = shrxs(&r->sigl, EXP_BIAS + 63 - r->exp);
1122
  very_big = !(~(r->sigh) | ~(r->sigl));  /* test for 0xfff...fff */
1123
#define half_or_more    (eax & 0x80000000)
1124
#define frac_part       (eax)
1125
#define more_than_half  ((eax & 0x80000001) == 0x80000001)
1126
  switch (control_word & CW_RC)
1127
    {
1128
    case RC_RND:
1129
      if ( more_than_half                       /* nearest */
1130
          || (half_or_more && (r->sigl & 1)) )  /* odd -> even */
1131
        {
1132
          if ( very_big ) return 1;        /* overflow */
1133
          significand(r) ++;
1134
          return PRECISION_LOST_UP;
1135
        }
1136
      break;
1137
    case RC_DOWN:
1138
      if (frac_part && r->sign)
1139
        {
1140
          if ( very_big ) return 1;        /* overflow */
1141
          significand(r) ++;
1142
          return PRECISION_LOST_UP;
1143
        }
1144
      break;
1145
    case RC_UP:
1146
      if (frac_part && !r->sign)
1147
        {
1148
          if ( very_big ) return 1;        /* overflow */
1149
          significand(r) ++;
1150
          return PRECISION_LOST_UP;
1151
        }
1152
      break;
1153
    case RC_CHOP:
1154
      break;
1155
    }
1156
 
1157
  return eax ? PRECISION_LOST_DOWN : 0;
1158
 
1159
}
1160
 
1161
/*===========================================================================*/
1162
 
1163
char *fldenv(fpu_addr_modes addr_modes, char *s)
1164
{
1165
  unsigned short tag_word = 0;
1166
  unsigned char tag;
1167
  int i;
1168
 
1169
  if ( (addr_modes.default_mode == VM86) ||
1170
      ((addr_modes.default_mode == PM16)
1171
      ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX)) )
1172
    {
1173
      RE_ENTRANT_CHECK_OFF;
1174
      FPU_verify_area(VERIFY_READ, s, 0x0e);
1175
      control_word = get_fs_word((unsigned short *) s);
1176
      partial_status = get_fs_word((unsigned short *) (s+2));
1177
      tag_word = get_fs_word((unsigned short *) (s+4));
1178
      instruction_address.offset = get_fs_word((unsigned short *) (s+6));
1179
      instruction_address.selector = get_fs_word((unsigned short *) (s+8));
1180
      operand_address.offset = get_fs_word((unsigned short *) (s+0x0a));
1181
      operand_address.selector = get_fs_word((unsigned short *) (s+0x0c));
1182
      RE_ENTRANT_CHECK_ON;
1183
      s += 0x0e;
1184
      if ( addr_modes.default_mode == VM86 )
1185
        {
1186
          instruction_address.offset
1187
            += (instruction_address.selector & 0xf000) << 4;
1188
          operand_address.offset += (operand_address.selector & 0xf000) << 4;
1189
        }
1190
    }
1191
  else
1192
    {
1193
      RE_ENTRANT_CHECK_OFF;
1194
      FPU_verify_area(VERIFY_READ, s, 0x1c);
1195
      control_word = get_fs_word((unsigned short *) s);
1196
      partial_status = get_fs_word((unsigned short *) (s+4));
1197
      tag_word = get_fs_word((unsigned short *) (s+8));
1198
      instruction_address.offset = get_fs_long((unsigned long *) (s+0x0c));
1199
      instruction_address.selector = get_fs_word((unsigned short *) (s+0x10));
1200
      instruction_address.opcode = get_fs_word((unsigned short *) (s+0x12));
1201
      operand_address.offset = get_fs_long((unsigned long *) (s+0x14));
1202
      operand_address.selector = get_fs_long((unsigned long *) (s+0x18));
1203
      RE_ENTRANT_CHECK_ON;
1204
      s += 0x1c;
1205
    }
1206
 
1207
#ifdef PECULIAR_486
1208
  control_word &= ~0xe080;
1209
#endif PECULIAR_486
1210
 
1211
  top = (partial_status >> SW_Top_Shift) & 7;
1212
 
1213
  if ( partial_status & ~control_word & CW_Exceptions )
1214
    partial_status |= (SW_Summary | SW_Backward);
1215
  else
1216
    partial_status &= ~(SW_Summary | SW_Backward);
1217
 
1218
  for ( i = 0; i < 8; i++ )
1219
    {
1220
      tag = tag_word & 3;
1221
      tag_word >>= 2;
1222
 
1223
      if ( tag == 3 )
1224
        /* New tag is empty.  Accept it */
1225
        regs[i].tag = TW_Empty;
1226
      else if ( regs[i].tag == TW_Empty )
1227
        {
1228
          /* Old tag is empty and new tag is not empty.  New tag is determined
1229
             by old reg contents */
1230
          if ( regs[i].exp == EXP_BIAS - EXTENDED_Ebias )
1231
            {
1232
              if ( !(regs[i].sigl | regs[i].sigh) )
1233
                regs[i].tag = TW_Zero;
1234
              else
1235
                regs[i].tag = TW_Valid;
1236
            }
1237
          else if ( regs[i].exp == 0x7fff + EXP_BIAS - EXTENDED_Ebias )
1238
            {
1239
              if ( !((regs[i].sigh & ~0x80000000) | regs[i].sigl) )
1240
                regs[i].tag = TW_Infinity;
1241
              else
1242
                regs[i].tag = TW_NaN;
1243
            }
1244
          else
1245
            regs[i].tag = TW_Valid;
1246
        }
1247
      /* Else old tag is not empty and new tag is not empty.  Old tag
1248
         remains correct */
1249
    }
1250
 
1251
  return s;
1252
}
1253
 
1254
 
1255
void frstor(fpu_addr_modes addr_modes, char *data_address)
1256
{
1257
  int i, stnr;
1258
  unsigned char tag;
1259
  char *s = fldenv(addr_modes, data_address);
1260
 
1261
  for ( i = 0; i < 8; i++ )
1262
    {
1263
      /* Load each register. */
1264
      stnr = (i+top) & 7;
1265
      tag = regs[stnr].tag;   /* Derived from the fldenv() loaded tag word. */
1266
      reg_load_extended((long double *)(s+i*10), &regs[stnr]);
1267
      if ( tag == TW_Empty )  /* The loaded data over-rides all other cases. */
1268
        regs[stnr].tag = tag;
1269
    }
1270
 
1271
}
1272
 
1273
 
1274
unsigned short tag_word(void)
1275
{
1276
  unsigned short word = 0;
1277
  unsigned char tag;
1278
  int i;
1279
 
1280
  for ( i = 7; i >= 0; i-- )
1281
    {
1282
      switch ( tag = regs[i].tag )
1283
        {
1284
        case TW_Valid:
1285
          if ( regs[i].exp <= (EXP_BIAS - EXTENDED_Ebias) )
1286
            tag = 2;
1287
          break;
1288
        case TW_Infinity:
1289
        case TW_NaN:
1290
          tag = 2;
1291
          break;
1292
        case TW_Empty:
1293
          tag = 3;
1294
          break;
1295
          /* TW_Zero already has the correct value */
1296
        }
1297
      word <<= 2;
1298
      word |= tag;
1299
    }
1300
  return word;
1301
}
1302
 
1303
 
1304
char *fstenv(fpu_addr_modes addr_modes, char *d)
1305
{
1306
  if ( (addr_modes.default_mode == VM86) ||
1307
      ((addr_modes.default_mode == PM16)
1308
      ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX)) )
1309
    {
1310
      RE_ENTRANT_CHECK_OFF;
1311
      FPU_verify_area(VERIFY_WRITE,d,14);
1312
#ifdef PECULIAR_486
1313
      put_fs_long(control_word & ~0xe080, (unsigned short *) d);
1314
#else
1315
      put_fs_word(control_word, (unsigned short *) d);
1316
#endif PECULIAR_486
1317
      put_fs_word(status_word(), (unsigned short *) (d+2));
1318
      put_fs_word(tag_word(), (unsigned short *) (d+4));
1319
      put_fs_word(instruction_address.offset, (unsigned short *) (d+6));
1320
      put_fs_word(operand_address.offset, (unsigned short *) (d+0x0a));
1321
      if ( addr_modes.default_mode == VM86 )
1322
        {
1323
          put_fs_word((instruction_address.offset & 0xf0000) >> 4,
1324
                      (unsigned short *) (d+8));
1325
          put_fs_word((operand_address.offset & 0xf0000) >> 4,
1326
                      (unsigned short *) (d+0x0c));
1327
        }
1328
      else
1329
        {
1330
          put_fs_word(instruction_address.selector, (unsigned short *) (d+8));
1331
          put_fs_word(operand_address.selector, (unsigned short *) (d+0x0c));
1332
        }
1333
      RE_ENTRANT_CHECK_ON;
1334
      d += 0x0e;
1335
    }
1336
  else
1337
    {
1338
      RE_ENTRANT_CHECK_OFF;
1339
      FPU_verify_area(VERIFY_WRITE,d,28);
1340
#ifdef PECULIAR_486
1341
      /* An 80486 sets all the reserved bits to 1. */
1342
      put_fs_long(0xffff0040 | (control_word & ~0xe080), (unsigned long *) d);
1343
      put_fs_long(0xffff0000 | status_word(), (unsigned long *) (d+4));
1344
      put_fs_long(0xffff0000 | tag_word(), (unsigned long *) (d+8));
1345
#else
1346
      put_fs_word(control_word, (unsigned short *) d);
1347
      put_fs_word(status_word(), (unsigned short *) (d+4));
1348
      put_fs_word(tag_word(), (unsigned short *) (d+8));
1349
#endif PECULIAR_486
1350
      put_fs_long(instruction_address.offset, (unsigned long *) (d+0x0c));
1351
      put_fs_word(instruction_address.selector, (unsigned short *) (d+0x10));
1352
      put_fs_word(instruction_address.opcode, (unsigned short *) (d+0x12));
1353
      put_fs_long(operand_address.offset, (unsigned long *) (d+0x14));
1354
#ifdef PECULIAR_486
1355
      /* An 80486 sets all the reserved bits to 1. */
1356
      put_fs_word(operand_address.selector, (unsigned short *) (d+0x18));
1357
      put_fs_word(0xffff, (unsigned short *) (d+0x1a));
1358
#else
1359
      put_fs_long(operand_address.selector, (unsigned long *) (d+0x18));
1360
#endif PECULIAR_486
1361
      RE_ENTRANT_CHECK_ON;
1362
      d += 0x1c;
1363
    }
1364
 
1365
  control_word |= CW_Exceptions;
1366
  partial_status &= ~(SW_Summary | SW_Backward);
1367
 
1368
  return d;
1369
}
1370
 
1371
 
1372
void fsave(fpu_addr_modes addr_modes, char *data_address)
1373
{
1374
  char *d;
1375
  int i;
1376
 
1377
  d = fstenv(addr_modes, data_address);
1378
  RE_ENTRANT_CHECK_OFF;
1379
  FPU_verify_area(VERIFY_WRITE,d,80);
1380
  RE_ENTRANT_CHECK_ON;
1381
  for ( i = 0; i < 8; i++ )
1382
    write_to_extended(&regs[(top + i) & 7], d + 10 * i);
1383
 
1384
  finit();
1385
 
1386
}
1387
 
1388
/*===========================================================================*/
1389
 
1390
/*
1391
  A call to this function must be preceded by a call to
1392
  FPU_verify_area() to verify access to the 10 bytes at d
1393
  */
1394
static void write_to_extended(FPU_REG *rp, char *d)
1395
{
1396
  long e;
1397
  FPU_REG tmp;
1398
 
1399
  e = rp->exp - EXP_BIAS + EXTENDED_Ebias;
1400
 
1401
#ifdef PARANOID
1402
  switch ( rp->tag )
1403
    {
1404
    case TW_Zero:
1405
      if ( rp->sigh | rp->sigl | e )
1406
        EXCEPTION(EX_INTERNAL | 0x160);
1407
      break;
1408
    case TW_Infinity:
1409
    case TW_NaN:
1410
      if ( (e ^ 0x7fff) | !(rp->sigh & 0x80000000) )
1411
        EXCEPTION(EX_INTERNAL | 0x161);
1412
      break;
1413
    default:
1414
      if (e > 0x7fff || e < -63)
1415
        EXCEPTION(EX_INTERNAL | 0x162);
1416
    }
1417
#endif PARANOID
1418
 
1419
  /*
1420
    All numbers except denormals are stored internally in a
1421
    format which is compatible with the extended real number
1422
    format.
1423
   */
1424
  if ( e > 0 )
1425
    {
1426
      /* just copy the reg */
1427
      RE_ENTRANT_CHECK_OFF;
1428
      put_fs_long(rp->sigl, (unsigned long *) d);
1429
      put_fs_long(rp->sigh, (unsigned long *) (d + 4));
1430
      RE_ENTRANT_CHECK_ON;
1431
    }
1432
  else
1433
    {
1434
      /*
1435
        The number is a de-normal stored as a normal using our
1436
        extra exponent range, or is Zero.
1437
        Convert it back to a de-normal, or leave it as Zero.
1438
       */
1439
      reg_move(rp, &tmp);
1440
      tmp.exp += -EXTENDED_Emin + 63;  /* largest exp to be 63 */
1441
      round_to_int(&tmp);
1442
      e = 0;
1443
      RE_ENTRANT_CHECK_OFF;
1444
      put_fs_long(tmp.sigl, (unsigned long *) d);
1445
      put_fs_long(tmp.sigh, (unsigned long *) (d + 4));
1446
      RE_ENTRANT_CHECK_ON;
1447
    }
1448
  e |= rp->sign == SIGN_POS ? 0 : 0x8000;
1449
  RE_ENTRANT_CHECK_OFF;
1450
  put_fs_word(e, (unsigned short *) (d + 8));
1451
  RE_ENTRANT_CHECK_ON;
1452
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.