1 /*
2 * linux/kernel/math/math_emulate.c
3 *
4 * (C) 1991 Linus Torvalds
5 *
6 * [expediant "port" of linux 8087 emulator to 386BSD, with apologies -wfj]
7 *
8 * from: 386BSD 0.1
9 * $FreeBSD: src/sys/i386/i386/math_emulate.c,v 1.18.2.1 1999/09/05 08:11:12 peter Exp $
10 */
11
12 /*
13 * Limited emulation 27.12.91 - mostly loads/stores, which gcc wants
14 * even for soft-float, unless you use bruce evans' patches. The patches
15 * are great, but they have to be re-applied for every version, and the
16 * library is different for soft-float and 80387. So emulation is more
17 * practical, even though it's slower.
18 *
19 * 28.12.91 - loads/stores work, even BCD. I'll have to start thinking
20 * about add/sub/mul/div. Urgel. I should find some good source, but I'll
21 * just fake up something.
22 *
23 * 30.12.91 - add/sub/mul/div/com seem to work mostly. I should really
24 * test every possible combination.
25 */
26
27 /*
28 * This file is full of ugly macros etc: one problem was that gcc simply
29 * didn't want to make the structures as they should be: it has to try to
30 * align them. Sickening code, but at least I've hidden the ugly things
31 * in this one file: the other files don't need to know about these things.
32 *
33 * The other files also don't care about ST(x) etc - they just get addresses
34 * to 80-bit temporary reals, and do with them as they please. I wanted to
35 * hide most of the 387-specific things here.
36 */
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40
41 #ifdef LKM
42 #include <sys/types.h>
43 #include <sys/kernel.h>
44 #include <sys/lkm.h>
45 #endif
46
47 #include <machine/cpu.h>
48 #include <machine/psl.h>
49 #include <machine/reg.h>
50
51 #include <sys/proc.h>
52 #include <sys/acct.h>
53 #include <sys/kernel.h>
54 #include <sys/signal.h>
55
56 #include <vm/vm.h>
57 #include <vm/vm_param.h>
58 #include <vm/vm_prot.h>
59 #include <vm/lock.h>
60 #include <vm/pmap.h>
61 #include <vm/vm_map.h>
62 #include <sys/user.h>
63
64 #define __ALIGNED_TEMP_REAL 1
65 #include <i386/i386/math_emu.h>
66
67 #define bswapw(x) __asm__("xchgb %%al,%%ah":"=a" (x):"" ((short)x))
68 #define ST(x) (*__st((x)))
69 #define PST(x) ((const temp_real *) __st((x)))
70 #define math_abort(tfp, signo) tfp->tf_eip = oldeip; return (signo);
71
72 /*
73 * We don't want these inlined - it gets too messy in the machine-code.
74 */
75 static void fpop(void);
76 static void fpush(void);
77 static void fxchg(temp_real_unaligned * a, temp_real_unaligned * b);
78 static temp_real_unaligned * __st(int i);
79
80 static unsigned char
81 get_fs_byte(char *adr)
82 { return(fubyte(adr)); }
83
84 static unsigned short
85 get_fs_word(unsigned short *adr)
86 { return(fuword(adr)); }
87
88 static unsigned long
89 get_fs_long(unsigned long *adr)
90 { return(fuword(adr)); }
91
92 static void
93 put_fs_byte(unsigned char val, char *adr)
94 { (void)subyte(adr,val); }
95
96 static void
97 put_fs_word(unsigned short val, short *adr)
98 { (void)susword(adr,val); }
99
100 static void
101 put_fs_long(u_long val, unsigned long *adr)
102 { (void)suword(adr,val); }
103
104 static int
105 math_emulate(struct trapframe * info)
106 {
107 unsigned short code;
108 temp_real tmp;
109 char * address;
110 u_long oldeip;
111
112 /* ever used fp? */
113 if ((((struct pcb *)curproc->p_addr)->pcb_flags & FP_SOFTFP) == 0) {
114 ((struct pcb *)curproc->p_addr)->pcb_flags |= FP_SOFTFP;
115 I387.cwd = 0x037f;
116 I387.swd = 0x0000;
117 I387.twd = 0x0000;
118 }
119
120 if (I387.cwd & I387.swd & 0x3f)
121 I387.swd |= 0x8000;
122 else
123 I387.swd &= 0x7fff;
124 oldeip = info->tf_eip;
125 /* 0x001f means user code space */
126 if ((u_short)info->tf_cs != 0x001F) {
127 printf("math_emulate: %04x:%08lx\n", (u_short)info->tf_cs,
128 oldeip);
129 panic("?Math emulation needed in kernel?");
130 }
131 code = get_fs_word((unsigned short *) oldeip);
132 bswapw(code);
133 code &= 0x7ff;
134 I387.fip = oldeip;
135 *(unsigned short *) &I387.fcs = (u_short) info->tf_cs;
136 *(1+(unsigned short *) &I387.fcs) = code;
137 info->tf_eip += 2;
138 switch (code) {
139 case 0x1d0: /* fnop */
140 return(0);
141 case 0x1d1: case 0x1d2: case 0x1d3: /* fst to 32-bit mem */
142 case 0x1d4: case 0x1d5: case 0x1d6: case 0x1d7:
143 math_abort(info,SIGILL);
144 case 0x1e0: /* fchs */
145 ST(0).exponent ^= 0x8000;
146 return(0);
147 case 0x1e1: /* fabs */
148 ST(0).exponent &= 0x7fff;
149 return(0);
150 case 0x1e2: case 0x1e3:
151 math_abort(info,SIGILL);
152 case 0x1e4: /* ftst */
153 ftst(PST(0));
154 return(0);
155 case 0x1e5: /* fxam */
156 printf("fxam not implemented\n");
157 math_abort(info,SIGILL);
158 case 0x1e6: case 0x1e7: /* fldenv */
159 math_abort(info,SIGILL);
160 case 0x1e8: /* fld1 */
161 fpush();
162 ST(0) = CONST1;
163 return(0);
164 case 0x1e9: /* fld2t */
165 fpush();
166 ST(0) = CONSTL2T;
167 return(0);
168 case 0x1ea: /* fld2e */
169 fpush();
170 ST(0) = CONSTL2E;
171 return(0);
172 case 0x1eb: /* fldpi */
173 fpush();
174 ST(0) = CONSTPI;
175 return(0);
176 case 0x1ec: /* fldlg2 */
177 fpush();
178 ST(0) = CONSTLG2;
179 return(0);
180 case 0x1ed: /* fldln2 */
181 fpush();
182 ST(0) = CONSTLN2;
183 return(0);
184 case 0x1ee: /* fldz */
185 fpush();
186 ST(0) = CONSTZ;
187 return(0);
188 case 0x1ef:
189 math_abort(info,SIGILL);
190 case 0x1f0: /* f2xm1 */
191 case 0x1f1: /* fyl2x */
192 case 0x1f2: /* fptan */
193 case 0x1f3: /* fpatan */
194 case 0x1f4: /* fxtract */
195 case 0x1f5: /* fprem1 */
196 case 0x1f6: /* fdecstp */
197 case 0x1f7: /* fincstp */
198 case 0x1f8: /* fprem */
199 case 0x1f9: /* fyl2xp1 */
200 case 0x1fa: /* fsqrt */
201 case 0x1fb: /* fsincos */
202 case 0x1fe: /* fsin */
203 case 0x1ff: /* fcos */
204 uprintf(
205 "math_emulate: instruction %04x not implemented\n",
206 code + 0xd800);
207 math_abort(info,SIGILL);
208 case 0x1fc: /* frndint */
209 frndint(PST(0),&tmp);
210 real_to_real(&tmp,&ST(0));
211 return(0);
212 case 0x1fd: /* fscale */
213 /* incomplete and totally inadequate -wfj */
214 Fscale(PST(0), PST(1), &tmp);
215 real_to_real(&tmp,&ST(0));
216 return(0); /* 19 Sep 92*/
217 case 0x2e9: /* ????? */
218 /* if this should be a fucomp ST(0),ST(1) , it must be a 0x3e9 ATS */
219 fucom(PST(1),PST(0));
220 fpop(); fpop();
221 return(0);
222 case 0x3d0: case 0x3d1: /* fist ?? */
223 return(0);
224 case 0x3e2: /* fclex */
225 I387.swd &= 0x7f00;
226 return(0);
227 case 0x3e3: /* fninit */
228 I387.cwd = 0x037f;
229 I387.swd = 0x0000;
230 I387.twd = 0x0000;
231 return(0);
232 case 0x3e4:
233 return(0);
234 case 0x6d9: /* fcompp */
235 fcom(PST(1),PST(0));
236 fpop(); fpop();
237 return(0);
238 case 0x7e0: /* fstsw ax */
239 *(short *) &info->tf_eax = I387.swd;
240 return(0);
241 }
242 switch (code >> 3) {
243 case 0x18: /* fadd */
244 fadd(PST(0),PST(code & 7),&tmp);
245 real_to_real(&tmp,&ST(0));
246 return(0);
247 case 0x19: /* fmul */
248 fmul(PST(0),PST(code & 7),&tmp);
249 real_to_real(&tmp,&ST(0));
250 return(0);
251 case 0x1a: /* fcom */
252 fcom(PST(code & 7),PST(0));
253 return(0);
254 case 0x1b: /* fcomp */
255 fcom(PST(code & 7),PST(0));
256 fpop();
257 return(0);
258 case 0x1c: /* fsubr */
259 real_to_real(&ST(code & 7),&tmp);
260 tmp.exponent ^= 0x8000;
261 fadd(PST(0),&tmp,&tmp);
262 real_to_real(&tmp,&ST(0));
263 return(0);
264 case 0x1d: /* fsub */
265 ST(0).exponent ^= 0x8000;
266 fadd(PST(0),PST(code & 7),&tmp);
267 real_to_real(&tmp,&ST(0));
268 return(0);
269 case 0x1e: /* fdivr */
270 fdiv(PST(0),PST(code & 7),&tmp);
271 real_to_real(&tmp,&ST(0));
272 return(0);
273 case 0x1f: /* fdiv */
274 fdiv(PST(code & 7),PST(0),&tmp);
275 real_to_real(&tmp,&ST(0));
276 return(0);
277 case 0x38: /* fld */
278 fpush();
279 ST(0) = ST((code & 7)+1); /* why plus 1 ????? ATS */
280 return(0);
281 case 0x39: /* fxch */
282 fxchg(&ST(0),&ST(code & 7));
283 return(0);
284 case 0x3b: /* ??? ??? wrong ???? ATS */
285 ST(code & 7) = ST(0);
286 fpop();
287 return(0);
288 case 0x98: /* fadd */
289 fadd(PST(0),PST(code & 7),&tmp);
290 real_to_real(&tmp,&ST(code & 7));
291 return(0);
292 case 0x99: /* fmul */
293 fmul(PST(0),PST(code & 7),&tmp);
294 real_to_real(&tmp,&ST(code & 7));
295 return(0);
296 case 0x9a: /* ???? , my manual don't list a direction bit
297 for fcom , ??? ATS */
298 fcom(PST(code & 7),PST(0));
299 return(0);
300 case 0x9b: /* same as above , ATS */
301 fcom(PST(code & 7),PST(0));
302 fpop();
303 return(0);
304 case 0x9c: /* fsubr */
305 ST(code & 7).exponent ^= 0x8000;
306 fadd(PST(0),PST(code & 7),&tmp);
307 real_to_real(&tmp,&ST(code & 7));
308 return(0);
309 case 0x9d: /* fsub */
310 real_to_real(&ST(0),&tmp);
311 tmp.exponent ^= 0x8000;
312 fadd(PST(code & 7),&tmp,&tmp);
313 real_to_real(&tmp,&ST(code & 7));
314 return(0);
315 case 0x9e: /* fdivr */
316 fdiv(PST(0),PST(code & 7),&tmp);
317 real_to_real(&tmp,&ST(code & 7));
318 return(0);
319 case 0x9f: /* fdiv */
320 fdiv(PST(code & 7),PST(0),&tmp);
321 real_to_real(&tmp,&ST(code & 7));
322 return(0);
323 case 0xb8: /* ffree */
324 printf("ffree not implemented\n");
325 math_abort(info,SIGILL);
326 case 0xb9: /* fstp ???? where is the pop ? ATS */
327 fxchg(&ST(0),&ST(code & 7));
328 return(0);
329 case 0xba: /* fst */
330 ST(code & 7) = ST(0);
331 return(0);
332 case 0xbb: /* ????? encoding of fstp to mem ? ATS */
333 ST(code & 7) = ST(0);
334 fpop();
335 return(0);
336 case 0xbc: /* fucom */
337 fucom(PST(code & 7),PST(0));
338 return(0);
339 case 0xbd: /* fucomp */
340 fucom(PST(code & 7),PST(0));
341 fpop();
342 return(0);
343 case 0xd8: /* faddp */
344 fadd(PST(code & 7),PST(0),&tmp);
345 real_to_real(&tmp,&ST(code & 7));
346 fpop();
347 return(0);
348 case 0xd9: /* fmulp */
349 fmul(PST(code & 7),PST(0),&tmp);
350 real_to_real(&tmp,&ST(code & 7));
351 fpop();
352 return(0);
353 case 0xda: /* ??? encoding of ficom with 16 bit mem ? ATS */
354 fcom(PST(code & 7),PST(0));
355 fpop();
356 return(0);
357 case 0xdc: /* fsubrp */
358 ST(code & 7).exponent ^= 0x8000;
359 fadd(PST(0),PST(code & 7),&tmp);
360 real_to_real(&tmp,&ST(code & 7));
361 fpop();
362 return(0);
363 case 0xdd: /* fsubp */
364 real_to_real(&ST(0),&tmp);
365 tmp.exponent ^= 0x8000;
366 fadd(PST(code & 7),&tmp,&tmp);
367 real_to_real(&tmp,&ST(code & 7));
368 fpop();
369 return(0);
370 case 0xde: /* fdivrp */
371 fdiv(PST(0),PST(code & 7),&tmp);
372 real_to_real(&tmp,&ST(code & 7));
373 fpop();
374 return(0);
375 case 0xdf: /* fdivp */
376 fdiv(PST(code & 7),PST(0),&tmp);
377 real_to_real(&tmp,&ST(code & 7));
378 fpop();
379 return(0);
380 case 0xf8: /* fild 16-bit mem ???? ATS */
381 printf("ffree not implemented\n");
382 math_abort(info,SIGILL);
383 fpop();
384 return(0);
385 case 0xf9: /* ????? ATS */
386 fxchg(&ST(0),&ST(code & 7));
387 return(0);
388 case 0xfa: /* fist 16-bit mem ? ATS */
389 case 0xfb: /* fistp 16-bit mem ? ATS */
390 ST(code & 7) = ST(0);
391 fpop();
392 return(0);
393 }
394 switch ((code>>3) & 0xe7) {
395 case 0x22:
396 put_short_real(PST(0),info,code);
397 return(0);
398 case 0x23:
399 put_short_real(PST(0),info,code);
400 fpop();
401 return(0);
402 case 0x24:
403 address = ea(info,code);
404 for (code = 0 ; code < 7 ; code++) {
405 ((long *) & I387)[code] =
406 get_fs_long((unsigned long *) address);
407 address += 4;
408 }
409 return(0);
410 case 0x25:
411 address = ea(info,code);
412 *(unsigned short *) &I387.cwd =
413 get_fs_word((unsigned short *) address);
414 return(0);
415 case 0x26:
416 address = ea(info,code);
417 /*verify_area(address,28);*/
418 for (code = 0 ; code < 7 ; code++) {
419 put_fs_long( ((long *) & I387)[code],
420 (unsigned long *) address);
421 address += 4;
422 }
423 return(0);
424 case 0x27:
425 address = ea(info,code);
426 /*verify_area(address,2);*/
427 put_fs_word(I387.cwd,(short *) address);
428 return(0);
429 case 0x62:
430 put_long_int(PST(0),info,code);
431 return(0);
432 case 0x63:
433 put_long_int(PST(0),info,code);
434 fpop();
435 return(0);
436 case 0x65:
437 fpush();
438 get_temp_real(&tmp,info,code);
439 real_to_real(&tmp,&ST(0));
440 return(0);
441 case 0x67:
442 put_temp_real(PST(0),info,code);
443 fpop();
444 return(0);
445 case 0xa2:
446 put_long_real(PST(0),info,code);
447 return(0);
448 case 0xa3:
449 put_long_real(PST(0),info,code);
450 fpop();
451 return(0);
452 case 0xa4:
453 address = ea(info,code);
454 for (code = 0 ; code < 27 ; code++) {
455 ((long *) & I387)[code] =
456 get_fs_long((unsigned long *) address);
457 address += 4;
458 }
459 return(0);
460 case 0xa6:
461 address = ea(info,code);
462 /*verify_area(address,108);*/
463 for (code = 0 ; code < 27 ; code++) {
464 put_fs_long( ((long *) & I387)[code],
465 (unsigned long *) address);
466 address += 4;
467 }
468 I387.cwd = 0x037f;
469 I387.swd = 0x0000;
470 I387.twd = 0x0000;
471 return(0);
472 case 0xa7:
473 address = ea(info,code);
474 /*verify_area(address,2);*/
475 put_fs_word(I387.swd,(short *) address);
476 return(0);
477 case 0xe2:
478 put_short_int(PST(0),info,code);
479 return(0);
480 case 0xe3:
481 put_short_int(PST(0),info,code);
482 fpop();
483 return(0);
484 case 0xe4:
485 fpush();
486 get_BCD(&tmp,info,code);
487 real_to_real(&tmp,&ST(0));
488 return(0);
489 case 0xe5:
490 fpush();
491 get_longlong_int(&tmp,info,code);
492 real_to_real(&tmp,&ST(0));
493 return(0);
494 case 0xe6:
495 put_BCD(PST(0),info,code);
496 fpop();
497 return(0);
498 case 0xe7:
499 put_longlong_int(PST(0),info,code);
500 fpop();
501 return(0);
502 }
503 switch (code >> 9) {
504 case 0:
505 get_short_real(&tmp,info,code);
506 break;
507 case 1:
508 get_long_int(&tmp,info,code);
509 break;
510 case 2:
511 get_long_real(&tmp,info,code);
512 break;
513 case 4:
514 get_short_int(&tmp,info,code);
515 }
516 switch ((code>>3) & 0x27) {
517 case 0:
518 fadd(&tmp,PST(0),&tmp);
519 real_to_real(&tmp,&ST(0));
520 return(0);
521 case 1:
522 fmul(&tmp,PST(0),&tmp);
523 real_to_real(&tmp,&ST(0));
524 return(0);
525 case 2:
526 fcom(&tmp,PST(0));
527 return(0);
528 case 3:
529 fcom(&tmp,PST(0));
530 fpop();
531 return(0);
532 case 4:
533 tmp.exponent ^= 0x8000;
534 fadd(&tmp,PST(0),&tmp);
535 real_to_real(&tmp,&ST(0));
536 return(0);
537 case 5:
538 ST(0).exponent ^= 0x8000;
539 fadd(&tmp,PST(0),&tmp);
540 real_to_real(&tmp,&ST(0));
541 return(0);
542 case 6:
543 fdiv(PST(0),&tmp,&tmp);
544 real_to_real(&tmp,&ST(0));
545 return(0);
546 case 7:
547 fdiv(&tmp,PST(0),&tmp);
548 real_to_real(&tmp,&ST(0));
549 return(0);
550 }
551 if ((code & 0x138) == 0x100) {
552 fpush();
553 real_to_real(&tmp,&ST(0));
554 return(0);
555 }
556 printf("Unknown math-insns: %04x:%08x %04x\n",(u_short)info->tf_cs,
557 info->tf_eip,code);
558 math_abort(info,SIGFPE);
559 }
560
561 static void
562 fpop(void)
563 {
564 unsigned long tmp;
565
566 tmp = I387.swd & 0xffffc7ffUL;
567 I387.swd += 0x00000800;
568 I387.swd &= 0x00003800;
569 I387.swd |= tmp;
570 }
571
572 static void
573 fpush(void)
574 {
575 unsigned long tmp;
576
577 tmp = I387.swd & 0xffffc7ffUL;
578 I387.swd += 0x00003800;
579 I387.swd &= 0x00003800;
580 I387.swd |= tmp;
581 }
582
583 static void
584 fxchg(temp_real_unaligned * a, temp_real_unaligned * b)
585 {
586 temp_real_unaligned c;
587
588 c = *a;
589 *a = *b;
590 *b = c;
591 }
592
593 static temp_real_unaligned *
594 __st(int i)
595 {
596 i += I387.swd >> 11;
597 i &= 7;
598 return (temp_real_unaligned *) (i*10 + (char *)(I387.st_space));
599 }
600
601 /*
602 * linux/kernel/math/ea.c
603 *
604 * (C) 1991 Linus Torvalds
605 */
606
607 /*
608 * Calculate the effective address.
609 */
610
611
612 static int __regoffset[] = {
613 tEAX, tECX, tEDX, tEBX, tESP, tEBP, tESI, tEDI
614 };
615
616 #define REG(x) (curproc->p_md.md_regs[__regoffset[(x)]])
617
618 static char *
619 sib(struct trapframe * info, int mod)
620 {
621 unsigned char ss,index,base;
622 long offset = 0;
623
624 base = get_fs_byte((char *) info->tf_eip);
625 info->tf_eip++;
626 ss = base >> 6;
627 index = (base >> 3) & 7;
628 base &= 7;
629 if (index == 4)
630 offset = 0;
631 else
632 offset = REG(index);
633 offset <<= ss;
634 if (mod || base != 5)
635 offset += REG(base);
636 if (mod == 1) {
637 offset += (signed char) get_fs_byte((char *) info->tf_eip);
638 info->tf_eip++;
639 } else if (mod == 2 || base == 5) {
640 offset += (signed) get_fs_long((unsigned long *) info->tf_eip);
641 info->tf_eip += 4;
642 }
643 I387.foo = offset;
644 I387.fos = 0x17;
645 return (char *) offset;
646 }
647
648 static char *
649 ea(struct trapframe * info, unsigned short code)
650 {
651 unsigned char mod,rm;
652 long * tmp;
653 int offset = 0;
654
655 mod = (code >> 6) & 3;
656 rm = code & 7;
657 if (rm == 4 && mod != 3)
658 return sib(info,mod);
659 if (rm == 5 && !mod) {
660 offset = get_fs_long((unsigned long *) info->tf_eip);
661 info->tf_eip += 4;
662 I387.foo = offset;
663 I387.fos = 0x17;
664 return (char *) offset;
665 }
666 tmp = (long *) ®(rm);
667 switch (mod) {
668 case 0: offset = 0; break;
669 case 1:
670 offset = (signed char) get_fs_byte((char *) info->tf_eip);
671 info->tf_eip++;
672 break;
673 case 2:
674 offset = (signed) get_fs_long((unsigned long *) info->tf_eip);
675 info->tf_eip += 4;
676 break;
677 #ifdef notyet
678 case 3:
679 math_abort(info,1<<(SIGILL-1));
680 #endif
681 }
682 I387.foo = offset;
683 I387.fos = 0x17;
684 return offset + (char *) *tmp;
685 }
686 /*
687 * linux/kernel/math/get_put.c
688 *
689 * (C) 1991 Linus Torvalds
690 */
691
692 /*
693 * This file handles all accesses to user memory: getting and putting
694 * ints/reals/BCD etc. This is the only part that concerns itself with
695 * other than temporary real format. All other cals are strictly temp_real.
696 */
697
698 static void
699 get_short_real(temp_real * tmp, struct trapframe * info, unsigned short code)
700 {
701 char * addr;
702 short_real sr;
703
704 addr = ea(info,code);
705 sr = get_fs_long((unsigned long *) addr);
706 short_to_temp(&sr,tmp);
707 }
708
709 static void
710 get_long_real(temp_real * tmp, struct trapframe * info, unsigned short code)
711 {
712 char * addr;
713 long_real lr;
714
715 addr = ea(info,code);
716 lr.a = get_fs_long((unsigned long *) addr);
717 lr.b = get_fs_long(1 + (unsigned long *) addr);
718 long_to_temp(&lr,tmp);
719 }
720
721 static void
722 get_temp_real(temp_real * tmp, struct trapframe * info, unsigned short code)
723 {
724 char * addr;
725
726 addr = ea(info,code);
727 tmp->a = get_fs_long((unsigned long *) addr);
728 tmp->b = get_fs_long(1 + (unsigned long *) addr);
729 tmp->exponent = get_fs_word(4 + (unsigned short *) addr);
730 }
731
732 static void
733 get_short_int(temp_real * tmp, struct trapframe * info, unsigned short code)
734 {
735 char * addr;
736 temp_int ti;
737
738 addr = ea(info,code);
739 ti.a = (signed short) get_fs_word((unsigned short *) addr);
740 ti.b = 0;
741 if (ti.sign = (ti.a < 0))
742 ti.a = - ti.a;
743 int_to_real(&ti,tmp);
744 }
745
746 static void
747 get_long_int(temp_real * tmp, struct trapframe * info, unsigned short code)
748 {
749 char * addr;
750 temp_int ti;
751
752 addr = ea(info,code);
753 ti.a = get_fs_long((unsigned long *) addr);
754 ti.b = 0;
755 if (ti.sign = (ti.a < 0))
756 ti.a = - ti.a;
757 int_to_real(&ti,tmp);
758 }
759
760 static void
761 get_longlong_int(temp_real * tmp, struct trapframe * info, unsigned short code)
762 {
763 char * addr;
764 temp_int ti;
765
766 addr = ea(info,code);
767 ti.a = get_fs_long((unsigned long *) addr);
768 ti.b = get_fs_long(1 + (unsigned long *) addr);
769 if (ti.sign = (ti.b < 0))
770 __asm__("notl %0 ; notl %1\n\t"
771 "addl $1,%0 ; adcl $0,%1"
772 :"=r" (ti.a),"=r" (ti.b)
773 :"" (ti.a),"1" (ti.b));
774 int_to_real(&ti,tmp);
775 }
776
777 #define MUL10(low,high) \
778 __asm__("addl %0,%0 ; adcl %1,%1\n\t" \
779 "movl %0,%%ecx ; movl %1,%%ebx\n\t" \
780 "addl %0,%0 ; adcl %1,%1\n\t" \
781 "addl %0,%0 ; adcl %1,%1\n\t" \
782 "addl %%ecx,%0 ; adcl %%ebx,%1" \
783 :"=a" (low),"=d" (high) \
784 :"" (low),"1" (high):"cx","bx")
785
786 #define ADD64(val,low,high) \
787 __asm__("addl %4,%0 ; adcl $0,%1":"=r" (low),"=r" (high) \
788 :"" (low),"1" (high),"r" ((unsigned long) (val)))
789
790 static void
791 get_BCD(temp_real * tmp, struct trapframe * info, unsigned short code)
792 {
793 int k;
794 char * addr;
795 temp_int i;
796 unsigned char c;
797
798 addr = ea(info,code);
799 addr += 9;
800 i.sign = 0x80 & get_fs_byte(addr--);
801 i.a = i.b = 0;
802 for (k = 0; k < 9; k++) {
803 c = get_fs_byte(addr--);
804 MUL10(i.a, i.b);
805 ADD64((c>>4), i.a, i.b);
806 MUL10(i.a, i.b);
807 ADD64((c&0xf), i.a, i.b);
808 }
809 int_to_real(&i,tmp);
810 }
811
812 static void
813 put_short_real(const temp_real * tmp,
814 struct trapframe * info, unsigned short code)
815 {
816 char * addr;
817 short_real sr;
818
819 addr = ea(info,code);
820 /*verify_area(addr,4);*/
821 temp_to_short(tmp,&sr);
822 put_fs_long(sr,(unsigned long *) addr);
823 }
824
825 static void
826 put_long_real(const temp_real * tmp,
827 struct trapframe * info, unsigned short code)
828 {
829 char * addr;
830 long_real lr;
831
832 addr = ea(info,code);
833 /*verify_area(addr,8);*/
834 temp_to_long(tmp,&lr);
835 put_fs_long(lr.a, (unsigned long *) addr);
836 put_fs_long(lr.b, 1 + (unsigned long *) addr);
837 }
838
839 static void
840 put_temp_real(const temp_real * tmp,
841 struct trapframe * info, unsigned short code)
842 {
843 char * addr;
844
845 addr = ea(info,code);
846 /*verify_area(addr,10);*/
847 put_fs_long(tmp->a, (unsigned long *) addr);
848 put_fs_long(tmp->b, 1 + (unsigned long *) addr);
849 put_fs_word(tmp->exponent, 4 + (short *) addr);
850 }
851
852 static void
853 put_short_int(const temp_real * tmp,
854 struct trapframe * info, unsigned short code)
855 {
856 char * addr;
857 temp_int ti;
858
859 addr = ea(info,code);
860 real_to_int(tmp,&ti);
861 /*verify_area(addr,2);*/
862 if (ti.sign)
863 ti.a = -ti.a;
864 put_fs_word(ti.a,(short *) addr);
865 }
866
867 static void
868 put_long_int(const temp_real * tmp,
869 struct trapframe * info, unsigned short code)
870 {
871 char * addr;
872 temp_int ti;
873
874 addr = ea(info,code);
875 real_to_int(tmp,&ti);
876 /*verify_area(addr,4);*/
877 if (ti.sign)
878 ti.a = -ti.a;
879 put_fs_long(ti.a,(unsigned long *) addr);
880 }
881
882 static void
883 put_longlong_int(const temp_real * tmp,
884 struct trapframe * info, unsigned short code)
885 {
886 char * addr;
887 temp_int ti;
888
889 addr = ea(info,code);
890 real_to_int(tmp,&ti);
891 /*verify_area(addr,8);*/
892 if (ti.sign)
893 __asm__("notl %0 ; notl %1\n\t"
894 "addl $1,%0 ; adcl $0,%1"
895 :"=r" (ti.a),"=r" (ti.b)
896 :"" (ti.a),"1" (ti.b));
897 put_fs_long(ti.a,(unsigned long *) addr);
898 put_fs_long(ti.b,1 + (unsigned long *) addr);
899 }
900
901 #define DIV10(low,high,rem) \
902 __asm__("divl %6 ; xchgl %1,%2 ; divl %6" \
903 :"=d" (rem),"=a" (low),"=r" (high) \
904 :"" (0),"1" (high),"2" (low),"c" (10))
905
906 static void
907 put_BCD(const temp_real * tmp,struct trapframe * info, unsigned short code)
908 {
909 int k,rem;
910 char * addr;
911 temp_int i;
912 unsigned char c;
913
914 addr = ea(info,code);
915 /*verify_area(addr,10);*/
916 real_to_int(tmp,&i);
917 if (i.sign)
918 put_fs_byte(0x80, addr+9);
919 else
920 put_fs_byte(0, addr+9);
921 for (k = 0; k < 9; k++) {
922 DIV10(i.a,i.b,rem);
923 c = rem;
924 DIV10(i.a,i.b,rem);
925 c += rem<<4;
926 put_fs_byte(c,addr++);
927 }
928 }
929
930 /*
931 * linux/kernel/math/mul.c
932 *
933 * (C) 1991 Linus Torvalds
934 */
935
936 /*
937 * temporary real multiplication routine.
938 */
939
940
941 static void
942 shift(int * c)
943 {
944 __asm__("movl (%0),%%eax ; addl %%eax,(%0)\n\t"
945 "movl 4(%0),%%eax ; adcl %%eax,4(%0)\n\t"
946 "movl 8(%0),%%eax ; adcl %%eax,8(%0)\n\t"
947 "movl 12(%0),%%eax ; adcl %%eax,12(%0)"
948 ::"r" ((long) c):"ax");
949 }
950
951 static void
952 mul64(const temp_real * a, const temp_real * b, int * c)
953 {
954 __asm__("movl (%0),%%eax\n\t"
955 "mull (%1)\n\t"
956 "movl %%eax,(%2)\n\t"
957 "movl %%edx,4(%2)\n\t"
958 "movl 4(%0),%%eax\n\t"
959 "mull 4(%1)\n\t"
960 "movl %%eax,8(%2)\n\t"
961 "movl %%edx,12(%2)\n\t"
962 "movl (%0),%%eax\n\t"
963 "mull 4(%1)\n\t"
964 "addl %%eax,4(%2)\n\t"
965 "adcl %%edx,8(%2)\n\t"
966 "adcl $0,12(%2)\n\t"
967 "movl 4(%0),%%eax\n\t"
968 "mull (%1)\n\t"
969 "addl %%eax,4(%2)\n\t"
970 "adcl %%edx,8(%2)\n\t"
971 "adcl $0,12(%2)"
972 ::"S" ((long) a),"c" ((long) b),"D" ((long) c)
973 :"ax","dx");
974 }
975
976 static void
977 fmul(const temp_real * src1, const temp_real * src2, temp_real * result)
978 {
979 int i,sign;
980 int tmp[4] = {0,0,0,0};
981
982 sign = (src1->exponent ^ src2->exponent) & 0x8000;
983 i = (src1->exponent & 0x7fff) + (src2->exponent & 0x7fff) - 16383 + 1;
984 if (i<0) {
985 result->exponent = sign;
986 result->a = result->b = 0;
987 return;
988 }
989 if (i>0x7fff) {
990 set_OE();
991 return;
992 }
993 mul64(src1,src2,tmp);
994 if (tmp[0] || tmp[1] || tmp[2] || tmp[3])
995 while (i && tmp[3] >= 0) {
996 i--;
997 shift(tmp);
998 }
999 else
1000 i = 0;
1001 result->exponent = i | sign;
1002 result->a = tmp[2];
1003 result->b = tmp[3];
1004 }
1005
1006 /*
1007 * linux/kernel/math/div.c
1008 *
1009 * (C) 1991 Linus Torvalds
1010 */
1011
1012 /*
1013 * temporary real division routine.
1014 */
1015
1016 static void
1017 shift_left(int * c)
1018 {
1019 __asm__ __volatile__("movl (%0),%%eax ; addl %%eax,(%0)\n\t"
1020 "movl 4(%0),%%eax ; adcl %%eax,4(%0)\n\t"
1021 "movl 8(%0),%%eax ; adcl %%eax,8(%0)\n\t"
1022 "movl 12(%0),%%eax ; adcl %%eax,12(%0)"
1023 ::"r" ((long) c):"ax");
1024 }
1025
1026 static void
1027 shift_right(int * c)
1028 {
1029 __asm__("shrl $1,12(%0) ; rcrl $1,8(%0) ; rcrl $1,4(%0) ; rcrl $1,(%0)"
1030 ::"r" ((long) c));
1031 }
1032
1033 static int
1034 try_sub(int * a, int * b)
1035 {
1036 char ok;
1037
1038 __asm__ __volatile__("movl (%1),%%eax ; subl %%eax,(%2)\n\t"
1039 "movl 4(%1),%%eax ; sbbl %%eax,4(%2)\n\t"
1040 "movl 8(%1),%%eax ; sbbl %%eax,8(%2)\n\t"
1041 "movl 12(%1),%%eax ; sbbl %%eax,12(%2)\n\t"
1042 "setae %%al":"=a" (ok):"c" ((long) a),"d" ((long) b));
1043 return ok;
1044 }
1045
1046 static void
1047 div64(int * a, int * b, int * c)
1048 {
1049 int tmp[4];
1050 int i;
1051 unsigned int mask = 0;
1052
1053 c += 4;
1054 for (i = 0 ; i<64 ; i++) {
1055 if (!(mask >>= 1)) {
1056 c--;
1057 mask = 0x80000000UL;
1058 }
1059 tmp[0] = a[0]; tmp[1] = a[1];
1060 tmp[2] = a[2]; tmp[3] = a[3];
1061 if (try_sub(b,tmp)) {
1062 *c |= mask;
1063 a[0] = tmp[0]; a[1] = tmp[1];
1064 a[2] = tmp[2]; a[3] = tmp[3];
1065 }
1066 shift_right(b);
1067 }
1068 }
1069
1070 static void
1071 fdiv(const temp_real * src1, const temp_real * src2, temp_real * result)
1072 {
1073 int i,sign;
1074 int a[4],b[4],tmp[4] = {0,0,0,0};
1075
1076 sign = (src1->exponent ^ src2->exponent) & 0x8000;
1077 if (!(src2->a || src2->b)) {
1078 set_ZE();
1079 return;
1080 }
1081 i = (src1->exponent & 0x7fff) - (src2->exponent & 0x7fff) + 16383;
1082 if (i<0) {
1083 set_UE();
1084 result->exponent = sign;
1085 result->a = result->b = 0;
1086 return;
1087 }
1088 a[0] = a[1] = 0;
1089 a[2] = src1->a;
1090 a[3] = src1->b;
1091 b[0] = b[1] = 0;
1092 b[2] = src2->a;
1093 b[3] = src2->b;
1094 while (b[3] >= 0) {
1095 i++;
1096 shift_left(b);
1097 }
1098 div64(a,b,tmp);
1099 if (tmp[0] || tmp[1] || tmp[2] || tmp[3]) {
1100 while (i && tmp[3] >= 0) {
1101 i--;
1102 shift_left(tmp);
1103 }
1104 if (tmp[3] >= 0)
1105 set_DE();
1106 } else
1107 i = 0;
1108 if (i>0x7fff) {
1109 set_OE();
1110 return;
1111 }
1112 if (tmp[0] || tmp[1])
1113 set_PE();
1114 result->exponent = i | sign;
1115 result->a = tmp[2];
1116 result->b = tmp[3];
1117 }
1118
1119 /*
1120 * linux/kernel/math/add.c
1121 *
1122 * (C) 1991 Linus Torvalds
1123 */
1124
1125 /*
1126 * temporary real addition routine.
1127 *
1128 * NOTE! These aren't exact: they are only 62 bits wide, and don't do
1129 * correct rounding. Fast hack. The reason is that we shift right the
1130 * values by two, in order not to have overflow (1 bit), and to be able
1131 * to move the sign into the mantissa (1 bit). Much simpler algorithms,
1132 * and 62 bits (61 really - no rounding) accuracy is usually enough. The
1133 * only time you should notice anything weird is when adding 64-bit
1134 * integers together. When using doubles (52 bits accuracy), the
1135 * 61-bit accuracy never shows at all.
1136 */
1137
1138 #define NEGINT(a) \
1139 __asm__("notl %0 ; notl %1 ; addl $1,%0 ; adcl $0,%1" \
1140 :"=r" (a->a),"=r" (a->b) \
1141 :"" (a->a),"1" (a->b))
1142
1143 static void signify(temp_real * a)
1144 {
1145 a->exponent += 2;
1146 __asm__("shrdl $2,%1,%0 ; shrl $2,%1"
1147 :"=r" (a->a),"=r" (a->b)
1148 :"" (a->a),"1" (a->b));
1149 if (a->exponent < 0)
1150 NEGINT(a);
1151 a->exponent &= 0x7fff;
1152 }
1153
1154 static void unsignify(temp_real * a)
1155 {
1156 if (!(a->a || a->b)) {
1157 a->exponent = 0;
1158 return;
1159 }
1160 a->exponent &= 0x7fff;
1161 if (a->b < 0) {
1162 NEGINT(a);
1163 a->exponent |= 0x8000;
1164 }
1165 while (a->b >= 0) {
1166 a->exponent--;
1167 __asm__("addl %0,%0 ; adcl %1,%1"
1168 :"=r" (a->a),"=r" (a->b)
1169 :"" (a->a),"1" (a->b));
1170 }
1171 }
1172
1173 static void
1174 fadd(const temp_real * src1, const temp_real * src2, temp_real * result)
1175 {
1176 temp_real a,b;
1177 int x1,x2,shift;
1178
1179 x1 = src1->exponent & 0x7fff;
1180 x2 = src2->exponent & 0x7fff;
1181 if (x1 > x2) {
1182 a = *src1;
1183 b = *src2;
1184 shift = x1-x2;
1185 } else {
1186 a = *src2;
1187 b = *src1;
1188 shift = x2-x1;
1189 }
1190 if (shift >= 64) {
1191 *result = a;
1192 return;
1193 }
1194 if (shift >= 32) {
1195 b.a = b.b;
1196 b.b = 0;
1197 shift -= 32;
1198 }
1199 __asm__("shrdl %4,%1,%0 ; shrl %4,%1"
1200 :"=r" (b.a),"=r" (b.b)
1201 :"" (b.a),"1" (b.b),"c" ((char) shift));
1202 signify(&a);
1203 signify(&b);
1204 __asm__("addl %4,%0 ; adcl %5,%1"
1205 :"=r" (a.a),"=r" (a.b)
1206 :"" (a.a),"1" (a.b),"g" (b.a),"g" (b.b));
1207 unsignify(&a);
1208 *result = a;
1209 }
1210
1211 /*
1212 * linux/kernel/math/compare.c
1213 *
1214 * (C) 1991 Linus Torvalds
1215 */
1216
1217 /*
1218 * temporary real comparison routines
1219 */
1220
1221
1222 #define clear_Cx() (I387.swd &= ~0x4500)
1223
1224 static void
1225 normalize(temp_real * a)
1226 {
1227 int i = a->exponent & 0x7fff;
1228 int sign = a->exponent & 0x8000;
1229
1230 if (!(a->a || a->b)) {
1231 a->exponent = 0;
1232 return;
1233 }
1234 while (i && a->b >= 0) {
1235 i--;
1236 __asm__("addl %0,%0 ; adcl %1,%1"
1237 :"=r" (a->a),"=r" (a->b)
1238 :"" (a->a),"1" (a->b));
1239 }
1240 a->exponent = i | sign;
1241 }
1242
1243 static void
1244 ftst(const temp_real * a)
1245 {
1246 temp_real b;
1247
1248 clear_Cx();
1249 b = *a;
1250 normalize(&b);
1251 if (b.a || b.b || b.exponent) {
1252 if (b.exponent < 0)
1253 set_C0();
1254 } else
1255 set_C3();
1256 }
1257
1258 static void
1259 fcom(const temp_real * src1, const temp_real * src2)
1260 {
1261 temp_real a;
1262
1263 a = *src1;
1264 a.exponent ^= 0x8000;
1265 fadd(&a,src2,&a);
1266 ftst(&a);
1267 }
1268
1269 static void
1270 fucom(const temp_real * src1, const temp_real * src2)
1271 {
1272 fcom(src1,src2);
1273 }
1274
1275 /*
1276 * linux/kernel/math/convert.c
1277 *
1278 * (C) 1991 Linus Torvalds
1279 */
1280
1281
1282 /*
1283 * NOTE!!! There is some "non-obvious" optimisations in the temp_to_long
1284 * and temp_to_short conversion routines: don't touch them if you don't
1285 * know what's going on. They are the adding of one in the rounding: the
1286 * overflow bit is also used for adding one into the exponent. Thus it
1287 * looks like the overflow would be incorrectly handled, but due to the
1288 * way the IEEE numbers work, things are correct.
1289 *
1290 * There is no checking for total overflow in the conversions, though (ie
1291 * if the temp-real number simply won't fit in a short- or long-real.)
1292 */
1293
1294 static void
1295 short_to_temp(const short_real * a, temp_real * b)
1296 {
1297 if (!(*a & 0x7fffffff)) {
1298 b->a = b->b = 0;
1299 if (*a)
1300 b->exponent = 0x8000;
1301 else
1302 b->exponent = 0;
1303 return;
1304 }
1305 b->exponent = ((*a>>23) & 0xff)-127+16383;
1306 if (*a<0)
1307 b->exponent |= 0x8000;
1308 b->b = (*a<<8) | 0x80000000UL;
1309 b->a = 0;
1310 }
1311
1312 static void
1313 long_to_temp(const long_real * a, temp_real * b)
1314 {
1315 if (!a->a && !(a->b & 0x7fffffff)) {
1316 b->a = b->b = 0;
1317 if (a->b)
1318 b->exponent = 0x8000;
1319 else
1320 b->exponent = 0;
1321 return;
1322 }
1323 b->exponent = ((a->b >> 20) & 0x7ff)-1023+16383;
1324 if (a->b<0)
1325 b->exponent |= 0x8000;
1326 b->b = 0x80000000UL | (a->b<<11) | (((unsigned long)a->a)>>21);
1327 b->a = a->a<<11;
1328 }
1329
1330 static void
1331 temp_to_short(const temp_real * a, short_real * b)
1332 {
1333 if (!(a->exponent & 0x7fff)) {
1334 *b = (a->exponent)?0x80000000UL:0;
1335 return;
1336 }
1337 *b = ((((long) a->exponent)-16383+127) << 23) & 0x7f800000;
1338 if (a->exponent < 0)
1339 *b |= 0x80000000UL;
1340 *b |= (a->b >> 8) & 0x007fffff;
1341 switch ((int)ROUNDING) {
1342 case ROUND_NEAREST:
1343 if ((a->b & 0xff) > 0x80)
1344 ++*b;
1345 break;
1346 case ROUND_DOWN:
1347 if ((a->exponent & 0x8000) && (a->b & 0xff))
1348 ++*b;
1349 break;
1350 case ROUND_UP:
1351 if (!(a->exponent & 0x8000) && (a->b & 0xff))
1352 ++*b;
1353 break;
1354 }
1355 }
1356
1357 static void
1358 temp_to_long(const temp_real * a, long_real * b)
1359 {
1360 if (!(a->exponent & 0x7fff)) {
1361 b->a = 0;
1362 b->b = (a->exponent)?0x80000000UL:0;
1363 return;
1364 }
1365 b->b = (((0x7fff & (long) a->exponent)-16383+1023) << 20) & 0x7ff00000;
1366 if (a->exponent < 0)
1367 b->b |= 0x80000000UL;
1368 b->b |= (a->b >> 11) & 0x000fffff;
1369 b->a = a->b << 21;
1370 b->a |= (a->a >> 11) & 0x001fffff;
1371 switch ((int)ROUNDING) {
1372 case ROUND_NEAREST:
1373 if ((a->a & 0x7ff) > 0x400)
1374 __asm__("addl $1,%0 ; adcl $0,%1"
1375 :"=r" (b->a),"=r" (b->b)
1376 :"" (b->a),"1" (b->b));
1377 break;
1378 case ROUND_DOWN:
1379 if ((a->exponent & 0x8000) && (a->b & 0xff))
1380 __asm__("addl $1,%0 ; adcl $0,%1"
1381 :"=r" (b->a),"=r" (b->b)
1382 :"" (b->a),"1" (b->b));
1383 break;
1384 case ROUND_UP:
1385 if (!(a->exponent & 0x8000) && (a->b & 0xff))
1386 __asm__("addl $1,%0 ; adcl $0,%1"
1387 :"=r" (b->a),"=r" (b->b)
1388 :"" (b->a),"1" (b->b));
1389 break;
1390 }
1391 }
1392
1393 static void
1394 frndint(const temp_real * a, temp_real * b)
1395 {
1396 int shift = 16383 + 63 - (a->exponent & 0x7fff);
1397 unsigned long underflow;
1398
1399 if ((shift < 0) || (shift == 16383+63)) {
1400 *b = *a;
1401 return;
1402 }
1403 b->a = b->b = underflow = 0;
1404 b->exponent = a->exponent;
1405 if (shift < 32) {
1406 b->b = a->b; b->a = a->a;
1407 } else if (shift < 64) {
1408 b->a = a->b; underflow = a->a;
1409 shift -= 32;
1410 b->exponent += 32;
1411 } else if (shift < 96) {
1412 underflow = a->b;
1413 shift -= 64;
1414 b->exponent += 64;
1415 } else {
1416 underflow = 1;
1417 shift = 0;
1418 }
1419 b->exponent += shift;
1420 __asm__("shrdl %2,%1,%0"
1421 :"=r" (underflow),"=r" (b->a)
1422 :"c" ((char) shift),"" (underflow),"1" (b->a));
1423 __asm__("shrdl %2,%1,%0"
1424 :"=r" (b->a),"=r" (b->b)
1425 :"c" ((char) shift),"" (b->a),"1" (b->b));
1426 __asm__("shrl %1,%0"
1427 :"=r" (b->b)
1428 :"c" ((char) shift),"" (b->b));
1429 switch ((int)ROUNDING) {
1430 case ROUND_NEAREST:
1431 __asm__("addl %4,%5 ; adcl $0,%0 ; adcl $0,%1"
1432 :"=r" (b->a),"=r" (b->b)
1433 :"" (b->a),"1" (b->b)
1434 ,"r" (0x7fffffff + (b->a & 1))
1435 ,"m" (*&underflow));
1436 break;
1437 case ROUND_UP:
1438 if ((b->exponent >= 0) && underflow)
1439 __asm__("addl $1,%0 ; adcl $0,%1"
1440 :"=r" (b->a),"=r" (b->b)
1441 :"" (b->a),"1" (b->b));
1442 break;
1443 case ROUND_DOWN:
1444 if ((b->exponent < 0) && underflow)
1445 __asm__("addl $1,%0 ; adcl $0,%1"
1446 :"=r" (b->a),"=r" (b->b)
1447 :"" (b->a),"1" (b->b));
1448 break;
1449 }
1450 if (b->a || b->b)
1451 while (b->b >= 0) {
1452 b->exponent--;
1453 __asm__("addl %0,%0 ; adcl %1,%1"
1454 :"=r" (b->a),"=r" (b->b)
1455 :"" (b->a),"1" (b->b));
1456 }
1457 else
1458 b->exponent = 0;
1459 }
1460
1461 static void
1462 Fscale(const temp_real *a, const temp_real *b, temp_real *c)
1463 {
1464 temp_int ti;
1465
1466 *c = *a;
1467 if(!c->a && !c->b) { /* 19 Sep 92*/
1468 c->exponent = 0;
1469 return;
1470 }
1471 real_to_int(b, &ti);
1472 if(ti.sign)
1473 c->exponent -= ti.a;
1474 else
1475 c->exponent += ti.a;
1476 }
1477
1478 static void
1479 real_to_int(const temp_real * a, temp_int * b)
1480 {
1481 int shift = 16383 + 63 - (a->exponent & 0x7fff);
1482 unsigned long underflow;
1483
1484 b->a = b->b = underflow = 0;
1485 b->sign = (a->exponent < 0);
1486 if (shift < 0) {
1487 set_OE();
1488 return;
1489 }
1490 if (shift < 32) {
1491 b->b = a->b; b->a = a->a;
1492 } else if (shift < 64) {
1493 b->a = a->b; underflow = a->a;
1494 shift -= 32;
1495 } else if (shift < 96) {
1496 underflow = a->b;
1497 shift -= 64;
1498 } else {
1499 underflow = 1;
1500 shift = 0;
1501 }
1502 __asm__("shrdl %2,%1,%0"
1503 :"=r" (underflow),"=r" (b->a)
1504 :"c" ((char) shift),"" (underflow),"1" (b->a));
1505 __asm__("shrdl %2,%1,%0"
1506 :"=r" (b->a),"=r" (b->b)
1507 :"c" ((char) shift),"" (b->a),"1" (b->b));
1508 __asm__("shrl %1,%0"
1509 :"=r" (b->b)
1510 :"c" ((char) shift),"" (b->b));
1511 switch ((int)ROUNDING) {
1512 case ROUND_NEAREST:
1513 __asm__("addl %4,%5 ; adcl $0,%0 ; adcl $0,%1"
1514 :"=r" (b->a),"=r" (b->b)
1515 :"" (b->a),"1" (b->b)
1516 ,"r" (0x7fffffff + (b->a & 1))
1517 ,"m" (*&underflow));
1518 break;
1519 case ROUND_UP:
1520 if (!b->sign && underflow)
1521 __asm__("addl $1,%0 ; adcl $0,%1"
1522 :"=r" (b->a),"=r" (b->b)
1523 :"" (b->a),"1" (b->b));
1524 break;
1525 case ROUND_DOWN:
1526 if (b->sign && underflow)
1527 __asm__("addl $1,%0 ; adcl $0,%1"
1528 :"=r" (b->a),"=r" (b->b)
1529 :"" (b->a),"1" (b->b));
1530 break;
1531 }
1532 }
1533
1534 static void
1535 int_to_real(const temp_int * a, temp_real * b)
1536 {
1537 b->a = a->a;
1538 b->b = a->b;
1539 if (b->a || b->b)
1540 b->exponent = 16383 + 63 + (a->sign? 0x8000:0);
1541 else {
1542 b->exponent = 0;
1543 return;
1544 }
1545 while (b->b >= 0) {
1546 b->exponent--;
1547 __asm__("addl %0,%0 ; adcl %1,%1"
1548 :"=r" (b->a),"=r" (b->b)
1549 :"" (b->a),"1" (b->b));
1550 }
1551 }
1552
1553 #ifdef LKM
1554 MOD_MISC(fpu);
1555 static int
1556 fpu_load(struct lkm_table *lkmtp, int cmd)
1557 {
1558 if (pmath_emulate) {
1559 printf("Math emulator already present\n");
1560 return EBUSY;
1561 }
1562 pmath_emulate = math_emulate;
1563 return 0;
1564 }
1565
1566 static int
1567 fpu_unload(struct lkm_table *lkmtp, int cmd)
1568 {
1569 if (pmath_emulate != math_emulate) {
1570 printf("Cannot unload another math emulator\n");
1571 return EACCES;
1572 }
1573 pmath_emulate = 0;
1574 return 0;
1575 }
1576
1577 int
1578 fpu(struct lkm_table *lkmtp, int cmd, int ver)
1579 {
1580 DISPATCH(lkmtp, cmd, ver, fpu_load, fpu_unload, lkm_nullcmd);
1581 }
1582 #else /* !LKM */
1583
1584 static void
1585 fpu_init(void *unused)
1586 {
1587 if (pmath_emulate)
1588 printf("Another Math emulator already present\n");
1589 else
1590 pmath_emulate = math_emulate;
1591 }
1592
1593 SYSINIT(fpu, SI_SUB_CPU, SI_ORDER_ANY, fpu_init, NULL);
1594
1595 #endif /* LKM */
Cache object: a3c3250ca2723f4af9f160be7dda59c1
|