100406dffSbellard /*
200406dffSbellard NetWinder Floating Point Emulator
300406dffSbellard (c) Rebel.COM, 1998,1999
400406dffSbellard (c) Philip Blundell, 1999
500406dffSbellard
600406dffSbellard Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
700406dffSbellard
800406dffSbellard This program is free software; you can redistribute it and/or modify
900406dffSbellard it under the terms of the GNU General Public License as published by
1000406dffSbellard the Free Software Foundation; either version 2 of the License, or
1100406dffSbellard (at your option) any later version.
1200406dffSbellard
1300406dffSbellard This program is distributed in the hope that it will be useful,
1400406dffSbellard but WITHOUT ANY WARRANTY; without even the implied warranty of
1500406dffSbellard MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1600406dffSbellard GNU General Public License for more details.
1700406dffSbellard
1800406dffSbellard You should have received a copy of the GNU General Public License
1970539e18SBlue Swirl along with this program; if not, see <http://www.gnu.org/licenses/>.
2000406dffSbellard */
2100406dffSbellard
22*d39594e9SPeter Maydell #include "qemu/osdep.h"
2300406dffSbellard #include "fpa11.h"
246b4c305cSPaolo Bonzini #include "fpu/softfloat.h"
2500406dffSbellard #include "fpopcode.h"
2600406dffSbellard #include "fpa11.inl"
2700406dffSbellard //#include "fpmodule.h"
2800406dffSbellard //#include "fpmodule.inl"
2900406dffSbellard
3000406dffSbellard unsigned int PerformFLT(const unsigned int opcode);
3100406dffSbellard unsigned int PerformFIX(const unsigned int opcode);
3200406dffSbellard
3300406dffSbellard static unsigned int
3400406dffSbellard PerformComparison(const unsigned int opcode);
3500406dffSbellard
EmulateCPRT(const unsigned int opcode)3600406dffSbellard unsigned int EmulateCPRT(const unsigned int opcode)
3700406dffSbellard {
3800406dffSbellard unsigned int nRc = 1;
3900406dffSbellard
4000406dffSbellard //printk("EmulateCPRT(0x%08x)\n",opcode);
4100406dffSbellard
4200406dffSbellard if (opcode & 0x800000)
4300406dffSbellard {
4400406dffSbellard /* This is some variant of a comparison (PerformComparison will
4500406dffSbellard sort out which one). Since most of the other CPRT
4600406dffSbellard instructions are oddball cases of some sort or other it makes
4700406dffSbellard sense to pull this out into a fast path. */
4800406dffSbellard return PerformComparison(opcode);
4900406dffSbellard }
5000406dffSbellard
5100406dffSbellard /* Hint to GCC that we'd like a jump table rather than a load of CMPs */
5200406dffSbellard switch ((opcode & 0x700000) >> 20)
5300406dffSbellard {
5400406dffSbellard case FLT_CODE >> 20: nRc = PerformFLT(opcode); break;
5500406dffSbellard case FIX_CODE >> 20: nRc = PerformFIX(opcode); break;
5600406dffSbellard
5700406dffSbellard case WFS_CODE >> 20: writeFPSR(readRegister(getRd(opcode))); break;
5800406dffSbellard case RFS_CODE >> 20: writeRegister(getRd(opcode),readFPSR()); break;
5900406dffSbellard
6000406dffSbellard #if 0 /* We currently have no use for the FPCR, so there's no point
6100406dffSbellard in emulating it. */
6200406dffSbellard case WFC_CODE >> 20: writeFPCR(readRegister(getRd(opcode)));
6300406dffSbellard case RFC_CODE >> 20: writeRegister(getRd(opcode),readFPCR()); break;
6400406dffSbellard #endif
6500406dffSbellard
6600406dffSbellard default: nRc = 0;
6700406dffSbellard }
6800406dffSbellard
6900406dffSbellard return nRc;
7000406dffSbellard }
7100406dffSbellard
PerformFLT(const unsigned int opcode)7200406dffSbellard unsigned int PerformFLT(const unsigned int opcode)
7300406dffSbellard {
7400406dffSbellard FPA11 *fpa11 = GET_FPA11();
7500406dffSbellard
7600406dffSbellard unsigned int nRc = 1;
7700406dffSbellard SetRoundingMode(opcode);
7800406dffSbellard
7900406dffSbellard switch (opcode & MASK_ROUNDING_PRECISION)
8000406dffSbellard {
8100406dffSbellard case ROUND_SINGLE:
8200406dffSbellard {
8300406dffSbellard fpa11->fType[getFn(opcode)] = typeSingle;
8400406dffSbellard fpa11->fpreg[getFn(opcode)].fSingle =
8520495218Sbellard int32_to_float32(readRegister(getRd(opcode)), &fpa11->fp_status);
8600406dffSbellard }
8700406dffSbellard break;
8800406dffSbellard
8900406dffSbellard case ROUND_DOUBLE:
9000406dffSbellard {
9100406dffSbellard fpa11->fType[getFn(opcode)] = typeDouble;
9200406dffSbellard fpa11->fpreg[getFn(opcode)].fDouble =
9320495218Sbellard int32_to_float64(readRegister(getRd(opcode)), &fpa11->fp_status);
9400406dffSbellard }
9500406dffSbellard break;
9600406dffSbellard
9700406dffSbellard case ROUND_EXTENDED:
9800406dffSbellard {
9900406dffSbellard fpa11->fType[getFn(opcode)] = typeExtended;
10000406dffSbellard fpa11->fpreg[getFn(opcode)].fExtended =
10120495218Sbellard int32_to_floatx80(readRegister(getRd(opcode)), &fpa11->fp_status);
10200406dffSbellard }
10300406dffSbellard break;
10400406dffSbellard
10500406dffSbellard default: nRc = 0;
10600406dffSbellard }
10700406dffSbellard
10800406dffSbellard return nRc;
10900406dffSbellard }
11000406dffSbellard
PerformFIX(const unsigned int opcode)11100406dffSbellard unsigned int PerformFIX(const unsigned int opcode)
11200406dffSbellard {
11300406dffSbellard FPA11 *fpa11 = GET_FPA11();
11400406dffSbellard unsigned int nRc = 1;
11500406dffSbellard unsigned int Fn = getFm(opcode);
11600406dffSbellard
11700406dffSbellard SetRoundingMode(opcode);
11800406dffSbellard
11900406dffSbellard switch (fpa11->fType[Fn])
12000406dffSbellard {
12100406dffSbellard case typeSingle:
12200406dffSbellard {
12300406dffSbellard writeRegister(getRd(opcode),
12420495218Sbellard float32_to_int32(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status));
12500406dffSbellard }
12600406dffSbellard break;
12700406dffSbellard
12800406dffSbellard case typeDouble:
12900406dffSbellard {
13026a76461Sbellard //printf("F%d is 0x%" PRIx64 "\n",Fn,fpa11->fpreg[Fn].fDouble);
13100406dffSbellard writeRegister(getRd(opcode),
13220495218Sbellard float64_to_int32(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status));
13300406dffSbellard }
13400406dffSbellard break;
13500406dffSbellard
13600406dffSbellard case typeExtended:
13700406dffSbellard {
13800406dffSbellard writeRegister(getRd(opcode),
13920495218Sbellard floatx80_to_int32(fpa11->fpreg[Fn].fExtended, &fpa11->fp_status));
14000406dffSbellard }
14100406dffSbellard break;
14200406dffSbellard
14300406dffSbellard default: nRc = 0;
14400406dffSbellard }
14500406dffSbellard
14600406dffSbellard return nRc;
14700406dffSbellard }
14800406dffSbellard
14900406dffSbellard
15086178a57SJuan Quintela static __inline unsigned int
PerformComparisonOperation(floatx80 Fn,floatx80 Fm)15100406dffSbellard PerformComparisonOperation(floatx80 Fn, floatx80 Fm)
15200406dffSbellard {
15320495218Sbellard FPA11 *fpa11 = GET_FPA11();
15400406dffSbellard unsigned int flags = 0;
15500406dffSbellard
15600406dffSbellard /* test for less than condition */
15720495218Sbellard if (floatx80_lt(Fn,Fm, &fpa11->fp_status))
15800406dffSbellard {
15900406dffSbellard flags |= CC_NEGATIVE;
16000406dffSbellard }
16100406dffSbellard
16200406dffSbellard /* test for equal condition */
163211315fbSAurelien Jarno if (floatx80_eq_quiet(Fn,Fm, &fpa11->fp_status))
16400406dffSbellard {
16500406dffSbellard flags |= CC_ZERO;
16600406dffSbellard }
16700406dffSbellard
16800406dffSbellard /* test for greater than or equal condition */
16920495218Sbellard if (floatx80_lt(Fm,Fn, &fpa11->fp_status))
17000406dffSbellard {
17100406dffSbellard flags |= CC_CARRY;
17200406dffSbellard }
17300406dffSbellard
17400406dffSbellard writeConditionCodes(flags);
17500406dffSbellard return 1;
17600406dffSbellard }
17700406dffSbellard
17800406dffSbellard /* This instruction sets the flags N, Z, C, V in the FPSR. */
17900406dffSbellard
PerformComparison(const unsigned int opcode)18000406dffSbellard static unsigned int PerformComparison(const unsigned int opcode)
18100406dffSbellard {
18200406dffSbellard FPA11 *fpa11 = GET_FPA11();
18300406dffSbellard unsigned int Fn, Fm;
18400406dffSbellard floatx80 rFn, rFm;
18500406dffSbellard int e_flag = opcode & 0x400000; /* 1 if CxFE */
18600406dffSbellard int n_flag = opcode & 0x200000; /* 1 if CNxx */
18700406dffSbellard unsigned int flags = 0;
18800406dffSbellard
18900406dffSbellard //printk("PerformComparison(0x%08x)\n",opcode);
19000406dffSbellard
19100406dffSbellard Fn = getFn(opcode);
19200406dffSbellard Fm = getFm(opcode);
19300406dffSbellard
19400406dffSbellard /* Check for unordered condition and convert all operands to 80-bit
19500406dffSbellard format.
19600406dffSbellard ?? Might be some mileage in avoiding this conversion if possible.
19700406dffSbellard Eg, if both operands are 32-bit, detect this and do a 32-bit
19800406dffSbellard comparison (cheaper than an 80-bit one). */
19900406dffSbellard switch (fpa11->fType[Fn])
20000406dffSbellard {
20100406dffSbellard case typeSingle:
20200406dffSbellard //printk("single.\n");
2033ebe80c2SPeter Maydell if (float32_is_any_nan(fpa11->fpreg[Fn].fSingle))
20400406dffSbellard goto unordered;
20520495218Sbellard rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status);
20600406dffSbellard break;
20700406dffSbellard
20800406dffSbellard case typeDouble:
20900406dffSbellard //printk("double.\n");
2103ebe80c2SPeter Maydell if (float64_is_any_nan(fpa11->fpreg[Fn].fDouble))
21100406dffSbellard goto unordered;
21220495218Sbellard rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status);
21300406dffSbellard break;
21400406dffSbellard
21500406dffSbellard case typeExtended:
21600406dffSbellard //printk("extended.\n");
2173ebe80c2SPeter Maydell if (floatx80_is_any_nan(fpa11->fpreg[Fn].fExtended))
21800406dffSbellard goto unordered;
21900406dffSbellard rFn = fpa11->fpreg[Fn].fExtended;
22000406dffSbellard break;
22100406dffSbellard
22200406dffSbellard default: return 0;
22300406dffSbellard }
22400406dffSbellard
22500406dffSbellard if (CONSTANT_FM(opcode))
22600406dffSbellard {
22700406dffSbellard //printk("Fm is a constant: #%d.\n",Fm);
22800406dffSbellard rFm = getExtendedConstant(Fm);
2293ebe80c2SPeter Maydell if (floatx80_is_any_nan(rFm))
23000406dffSbellard goto unordered;
23100406dffSbellard }
23200406dffSbellard else
23300406dffSbellard {
23400406dffSbellard //printk("Fm = r%d which contains a ",Fm);
23500406dffSbellard switch (fpa11->fType[Fm])
23600406dffSbellard {
23700406dffSbellard case typeSingle:
23800406dffSbellard //printk("single.\n");
2393ebe80c2SPeter Maydell if (float32_is_any_nan(fpa11->fpreg[Fm].fSingle))
24000406dffSbellard goto unordered;
24120495218Sbellard rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle, &fpa11->fp_status);
24200406dffSbellard break;
24300406dffSbellard
24400406dffSbellard case typeDouble:
24500406dffSbellard //printk("double.\n");
2463ebe80c2SPeter Maydell if (float64_is_any_nan(fpa11->fpreg[Fm].fDouble))
24700406dffSbellard goto unordered;
24820495218Sbellard rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble, &fpa11->fp_status);
24900406dffSbellard break;
25000406dffSbellard
25100406dffSbellard case typeExtended:
25200406dffSbellard //printk("extended.\n");
2533ebe80c2SPeter Maydell if (floatx80_is_any_nan(fpa11->fpreg[Fm].fExtended))
25400406dffSbellard goto unordered;
25500406dffSbellard rFm = fpa11->fpreg[Fm].fExtended;
25600406dffSbellard break;
25700406dffSbellard
25800406dffSbellard default: return 0;
25900406dffSbellard }
26000406dffSbellard }
26100406dffSbellard
26200406dffSbellard if (n_flag)
26300406dffSbellard {
26400406dffSbellard rFm.high ^= 0x8000;
26500406dffSbellard }
26600406dffSbellard
26700406dffSbellard return PerformComparisonOperation(rFn,rFm);
26800406dffSbellard
26900406dffSbellard unordered:
27000406dffSbellard /* ?? The FPA data sheet is pretty vague about this, in particular
27100406dffSbellard about whether the non-E comparisons can ever raise exceptions.
27200406dffSbellard This implementation is based on a combination of what it says in
27300406dffSbellard the data sheet, observation of how the Acorn emulator actually
27400406dffSbellard behaves (and how programs expect it to) and guesswork. */
27500406dffSbellard flags |= CC_OVERFLOW;
27600406dffSbellard flags &= ~(CC_ZERO | CC_NEGATIVE);
27700406dffSbellard
27800406dffSbellard if (BIT_AC & readFPSR()) flags |= CC_CARRY;
27900406dffSbellard
28020495218Sbellard if (e_flag) float_raise(float_flag_invalid, &fpa11->fp_status);
28100406dffSbellard
28200406dffSbellard writeConditionCodes(flags);
28300406dffSbellard return 1;
28400406dffSbellard }
285