|
1 /* |
|
2 NetWinder Floating Point Emulator |
|
3 (c) Rebel.COM, 1998,1999 |
|
4 |
|
5 Direct questions, comments to Scott Bambrough <scottb@netwinder.org> |
|
6 |
|
7 This program is free software; you can redistribute it and/or modify |
|
8 it under the terms of the GNU General Public License as published by |
|
9 the Free Software Foundation; either version 2 of the License, or |
|
10 (at your option) any later version. |
|
11 |
|
12 This program is distributed in the hope that it will be useful, |
|
13 but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
15 GNU General Public License for more details. |
|
16 |
|
17 You should have received a copy of the GNU General Public License |
|
18 along with this program; if not, write to the Free Software |
|
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
|
20 */ |
|
21 |
|
22 #include "fpa11.h" |
|
23 #include "fpopcode.h" |
|
24 |
|
25 unsigned int SingleCPDO(const unsigned int opcode); |
|
26 unsigned int DoubleCPDO(const unsigned int opcode); |
|
27 unsigned int ExtendedCPDO(const unsigned int opcode); |
|
28 |
|
29 unsigned int EmulateCPDO(const unsigned int opcode) |
|
30 { |
|
31 FPA11 *fpa11 = GET_FPA11(); |
|
32 unsigned int Fd, nType, nDest, nRc = 1; |
|
33 |
|
34 //printk("EmulateCPDO(0x%08x)\n",opcode); |
|
35 |
|
36 /* Get the destination size. If not valid let Linux perform |
|
37 an invalid instruction trap. */ |
|
38 nDest = getDestinationSize(opcode); |
|
39 if (typeNone == nDest) return 0; |
|
40 |
|
41 SetRoundingMode(opcode); |
|
42 |
|
43 /* Compare the size of the operands in Fn and Fm. |
|
44 Choose the largest size and perform operations in that size, |
|
45 in order to make use of all the precision of the operands. |
|
46 If Fm is a constant, we just grab a constant of a size |
|
47 matching the size of the operand in Fn. */ |
|
48 if (MONADIC_INSTRUCTION(opcode)) |
|
49 nType = nDest; |
|
50 else |
|
51 nType = fpa11->fType[getFn(opcode)]; |
|
52 |
|
53 if (!CONSTANT_FM(opcode)) |
|
54 { |
|
55 register unsigned int Fm = getFm(opcode); |
|
56 if (nType < fpa11->fType[Fm]) |
|
57 { |
|
58 nType = fpa11->fType[Fm]; |
|
59 } |
|
60 } |
|
61 |
|
62 switch (nType) |
|
63 { |
|
64 case typeSingle : nRc = SingleCPDO(opcode); break; |
|
65 case typeDouble : nRc = DoubleCPDO(opcode); break; |
|
66 case typeExtended : nRc = ExtendedCPDO(opcode); break; |
|
67 default : nRc = 0; |
|
68 } |
|
69 |
|
70 /* If the operation succeeded, check to see if the result in the |
|
71 destination register is the correct size. If not force it |
|
72 to be. */ |
|
73 Fd = getFd(opcode); |
|
74 nType = fpa11->fType[Fd]; |
|
75 if ((0 != nRc) && (nDest != nType)) |
|
76 { |
|
77 switch (nDest) |
|
78 { |
|
79 case typeSingle: |
|
80 { |
|
81 if (typeDouble == nType) |
|
82 fpa11->fpreg[Fd].fSingle = |
|
83 float64_to_float32(fpa11->fpreg[Fd].fDouble, &fpa11->fp_status); |
|
84 else |
|
85 fpa11->fpreg[Fd].fSingle = |
|
86 floatx80_to_float32(fpa11->fpreg[Fd].fExtended, &fpa11->fp_status); |
|
87 } |
|
88 break; |
|
89 |
|
90 case typeDouble: |
|
91 { |
|
92 if (typeSingle == nType) |
|
93 fpa11->fpreg[Fd].fDouble = |
|
94 float32_to_float64(fpa11->fpreg[Fd].fSingle, &fpa11->fp_status); |
|
95 else |
|
96 fpa11->fpreg[Fd].fDouble = |
|
97 floatx80_to_float64(fpa11->fpreg[Fd].fExtended, &fpa11->fp_status); |
|
98 } |
|
99 break; |
|
100 |
|
101 case typeExtended: |
|
102 { |
|
103 if (typeSingle == nType) |
|
104 fpa11->fpreg[Fd].fExtended = |
|
105 float32_to_floatx80(fpa11->fpreg[Fd].fSingle, &fpa11->fp_status); |
|
106 else |
|
107 fpa11->fpreg[Fd].fExtended = |
|
108 float64_to_floatx80(fpa11->fpreg[Fd].fDouble, &fpa11->fp_status); |
|
109 } |
|
110 break; |
|
111 } |
|
112 |
|
113 fpa11->fType[Fd] = nDest; |
|
114 } |
|
115 |
|
116 return nRc; |
|
117 } |