1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | NetWinder Floating Point Emulator |
4 | (c) Rebel.COM, 1998,1999 |
5 | (c) Philip Blundell, 2001 |
6 | |
7 | Direct questions, comments to Scott Bambrough <scottb@netwinder.org> |
8 | |
9 | */ |
10 | |
11 | #include "fpa11.h" |
12 | #include "fpopcode.h" |
13 | |
14 | unsigned int SingleCPDO(struct roundingData *roundData, const unsigned int opcode, FPREG * rFd); |
15 | unsigned int DoubleCPDO(struct roundingData *roundData, const unsigned int opcode, FPREG * rFd); |
16 | unsigned int ExtendedCPDO(struct roundingData *roundData, const unsigned int opcode, FPREG * rFd); |
17 | |
18 | unsigned int EmulateCPDO(const unsigned int opcode) |
19 | { |
20 | FPA11 *fpa11 = GET_FPA11(); |
21 | FPREG *rFd; |
22 | unsigned int nType, nDest, nRc; |
23 | struct roundingData roundData; |
24 | |
25 | /* Get the destination size. If not valid let Linux perform |
26 | an invalid instruction trap. */ |
27 | nDest = getDestinationSize(opcode); |
28 | if (typeNone == nDest) |
29 | return 0; |
30 | |
31 | roundData.mode = SetRoundingMode(opcode); |
32 | roundData.precision = SetRoundingPrecision(opcode); |
33 | roundData.exception = 0; |
34 | |
35 | /* Compare the size of the operands in Fn and Fm. |
36 | Choose the largest size and perform operations in that size, |
37 | in order to make use of all the precision of the operands. |
38 | If Fm is a constant, we just grab a constant of a size |
39 | matching the size of the operand in Fn. */ |
40 | if (MONADIC_INSTRUCTION(opcode)) |
41 | nType = nDest; |
42 | else |
43 | nType = fpa11->fType[getFn(opcode)]; |
44 | |
45 | if (!CONSTANT_FM(opcode)) { |
46 | register unsigned int Fm = getFm(opcode); |
47 | if (nType < fpa11->fType[Fm]) { |
48 | nType = fpa11->fType[Fm]; |
49 | } |
50 | } |
51 | |
52 | rFd = &fpa11->fpreg[getFd(opcode)]; |
53 | |
54 | switch (nType) { |
55 | case typeSingle: |
56 | nRc = SingleCPDO(roundData: &roundData, opcode, rFd); |
57 | break; |
58 | case typeDouble: |
59 | nRc = DoubleCPDO(roundData: &roundData, opcode, rFd); |
60 | break; |
61 | #ifdef CONFIG_FPE_NWFPE_XP |
62 | case typeExtended: |
63 | nRc = ExtendedCPDO(&roundData, opcode, rFd); |
64 | break; |
65 | #endif |
66 | default: |
67 | nRc = 0; |
68 | } |
69 | |
70 | /* The CPDO functions used to always set the destination type |
71 | to be the same as their working size. */ |
72 | |
73 | if (nRc != 0) { |
74 | /* If the operation succeeded, check to see if the result in the |
75 | destination register is the correct size. If not force it |
76 | to be. */ |
77 | |
78 | fpa11->fType[getFd(opcode)] = nDest; |
79 | |
80 | #ifdef CONFIG_FPE_NWFPE_XP |
81 | if (nDest != nType) { |
82 | switch (nDest) { |
83 | case typeSingle: |
84 | { |
85 | if (typeDouble == nType) |
86 | rFd->fSingle = float64_to_float32(&roundData, rFd->fDouble); |
87 | else |
88 | rFd->fSingle = floatx80_to_float32(&roundData, rFd->fExtended); |
89 | } |
90 | break; |
91 | |
92 | case typeDouble: |
93 | { |
94 | if (typeSingle == nType) |
95 | rFd->fDouble = float32_to_float64(rFd->fSingle); |
96 | else |
97 | rFd->fDouble = floatx80_to_float64(&roundData, rFd->fExtended); |
98 | } |
99 | break; |
100 | |
101 | case typeExtended: |
102 | { |
103 | if (typeSingle == nType) |
104 | rFd->fExtended = float32_to_floatx80(rFd->fSingle); |
105 | else |
106 | rFd->fExtended = float64_to_floatx80(rFd->fDouble); |
107 | } |
108 | break; |
109 | } |
110 | } |
111 | #else |
112 | if (nDest != nType) { |
113 | if (nDest == typeSingle) |
114 | rFd->fSingle = float64_to_float32(&roundData, rFd->fDouble); |
115 | else |
116 | rFd->fDouble = float32_to_float64(rFd->fSingle); |
117 | } |
118 | #endif |
119 | } |
120 | |
121 | if (roundData.exception) |
122 | float_raise(roundData.exception); |
123 | |
124 | return nRc; |
125 | } |
126 | |