/[pcsx2_0.9.7]/trunk/pcsx2/FPU.cpp
ViewVC logotype

Contents of /trunk/pcsx2/FPU.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 31 - (show annotations) (download)
Tue Sep 7 03:24:11 2010 UTC (10 years, 2 months ago) by william
File size: 11585 byte(s)
committing r3113 initial commit again...
1 /* PCSX2 - PS2 Emulator for PCs
2 * Copyright (C) 2002-2010 PCSX2 Dev Team
3 *
4 * PCSX2 is free software: you can redistribute it and/or modify it under the terms
5 * of the GNU Lesser General Public License as published by the Free Software Found-
6 * ation, either version 3 of the License, or (at your option) any later version.
7 *
8 * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
10 * PURPOSE. See the GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License along with PCSX2.
13 * If not, see <http://www.gnu.org/licenses/>.
14 */
15
16 #include "PrecompiledHeader.h"
17 #include "Common.h"
18
19 #include <cmath>
20
21 // Helper Macros
22 //****************************************************************
23
24 // IEEE 754 Values
25 #define PosInfinity 0x7f800000
26 #define NegInfinity 0xff800000
27 #define posFmax 0x7F7FFFFF
28 #define negFmax 0xFF7FFFFF
29
30
31 /* Used in compare function to compensate for differences between IEEE 754 and the FPU.
32 Setting it to ~0x00000000 = Compares Exact Value. (comment out this macro for faster Exact Compare method)
33 Setting it to ~0x00000001 = Discards the least significant bit when comparing.
34 Setting it to ~0x00000003 = Discards the least 2 significant bits when comparing... etc.. */
35 //#define comparePrecision ~0x00000001
36
37 // Operands
38 #define _Ft_ ( ( cpuRegs.code >> 16 ) & 0x1F )
39 #define _Fs_ ( ( cpuRegs.code >> 11 ) & 0x1F )
40 #define _Fd_ ( ( cpuRegs.code >> 6 ) & 0x1F )
41
42 // Floats
43 #define _FtValf_ fpuRegs.fpr[ _Ft_ ].f
44 #define _FsValf_ fpuRegs.fpr[ _Fs_ ].f
45 #define _FdValf_ fpuRegs.fpr[ _Fd_ ].f
46 #define _FAValf_ fpuRegs.ACC.f
47
48 // U32's
49 #define _FtValUl_ fpuRegs.fpr[ _Ft_ ].UL
50 #define _FsValUl_ fpuRegs.fpr[ _Fs_ ].UL
51 #define _FdValUl_ fpuRegs.fpr[ _Fd_ ].UL
52 #define _FAValUl_ fpuRegs.ACC.UL
53
54 // S32's - useful for ensuring sign extension when needed.
55 #define _FtValSl_ fpuRegs.fpr[ _Ft_ ].SL
56 #define _FsValSl_ fpuRegs.fpr[ _Fs_ ].SL
57 #define _FdValSl_ fpuRegs.fpr[ _Fd_ ].SL
58 #define _FAValSl_ fpuRegs.ACC.SL
59
60 // FPU Control Reg (FCR31)
61 #define _ContVal_ fpuRegs.fprc[ 31 ]
62
63 // FCR31 Flags
64 #define FPUflagC 0X00800000
65 #define FPUflagI 0X00020000
66 #define FPUflagD 0X00010000
67 #define FPUflagO 0X00008000
68 #define FPUflagU 0X00004000
69 #define FPUflagSI 0X00000040
70 #define FPUflagSD 0X00000020
71 #define FPUflagSO 0X00000010
72 #define FPUflagSU 0X00000008
73
74 //****************************************************************
75
76 // If we have an infinity value, then Overflow has occured.
77 #define checkOverflow(xReg, cFlagsToSet, shouldReturn) { \
78 if ( ( xReg & ~0x80000000 ) == PosInfinity ) { \
79 /*Console.Warning( "FPU OVERFLOW!: Changing to +/-Fmax!!!!!!!!!!!!\n" );*/ \
80 xReg = ( xReg & 0x80000000 ) | posFmax; \
81 _ContVal_ |= cFlagsToSet; \
82 if ( shouldReturn ) { return; } \
83 } \
84 }
85
86 // If we have a denormal value, then Underflow has occured.
87 #define checkUnderflow(xReg, cFlagsToSet, shouldReturn) { \
88 if ( ( ( xReg & 0x7F800000 ) == 0 ) && ( ( xReg & 0x007FFFFF ) != 0 ) ) { \
89 /*Console.Warning( "FPU UNDERFLOW!: Changing to +/-0!!!!!!!!!!!!\n" );*/ \
90 xReg &= 0x80000000; \
91 _ContVal_ |= cFlagsToSet; \
92 if ( shouldReturn ) { return; } \
93 } \
94 }
95
96 /* Checks if Divide by Zero will occur. (z/y = x)
97 cFlagsToSet1 = Flags to set if (z != 0)
98 cFlagsToSet2 = Flags to set if (z == 0)
99 ( Denormals are counted as "0" )
100 */
101 #define checkDivideByZero(xReg, yDivisorReg, zDividendReg, cFlagsToSet1, cFlagsToSet2, shouldReturn) { \
102 if ( ( yDivisorReg & 0x7F800000 ) == 0 ) { \
103 _ContVal_ |= ( ( zDividendReg & 0x7F800000 ) == 0 ) ? cFlagsToSet2 : cFlagsToSet1; \
104 xReg = ( ( yDivisorReg ^ zDividendReg ) & 0x80000000 ) | posFmax; \
105 if ( shouldReturn ) { return; } \
106 } \
107 }
108
109 /* Clears the "Cause Flags" of the Control/Status Reg
110 The "EE Core Users Manual" implies that all the Cause flags are cleared every instruction...
111 But, the "EE Core Instruction Set Manual" says that only certain Cause Flags are cleared
112 for specific instructions... I'm just setting them to clear when the Instruction Set Manual
113 says to... (cottonvibes)
114 */
115 #define clearFPUFlags(cFlags) { \
116 _ContVal_ &= ~( cFlags ) ; \
117 }
118
119 #ifdef comparePrecision
120 // This compare discards the least-significant bit(s) in order to solve some rounding issues.
121 #define C_cond_S(cond) { \
122 FPRreg tempA, tempB; \
123 tempA.UL = _FsValUl_ & comparePrecision; \
124 tempB.UL = _FtValUl_ & comparePrecision; \
125 _ContVal_ = ( ( tempA.f ) cond ( tempB.f ) ) ? \
126 ( _ContVal_ | FPUflagC ) : \
127 ( _ContVal_ & ~FPUflagC ); \
128 }
129 #else
130 // Used for Comparing; This compares if the floats are exactly the same.
131 #define C_cond_S(cond) { \
132 _ContVal_ = ( _FsValf_ cond _FtValf_ ) ? \
133 ( _ContVal_ | FPUflagC ) : \
134 ( _ContVal_ & ~FPUflagC ); \
135 }
136 #endif
137
138 // Conditional Branch
139 #define BC1(cond) \
140 if ( ( _ContVal_ & FPUflagC ) cond 0 ) { \
141 intDoBranch( _BranchTarget_ ); \
142 }
143
144 // Conditional Branch
145 #define BC1L(cond) \
146 if ( ( _ContVal_ & FPUflagC ) cond 0 ) { \
147 intDoBranch( _BranchTarget_ ); \
148 } else cpuRegs.pc += 4;
149
150 namespace R5900 {
151 namespace Interpreter {
152 namespace OpcodeImpl {
153 namespace COP1 {
154
155 //****************************************************************
156 // FPU Opcodes
157 //****************************************************************
158
159 float fpuDouble(u32 f)
160 {
161 switch(f & 0x7f800000){
162 case 0x0:
163 f &= 0x80000000;
164 return *(float*)&f;
165 break;
166 case 0x7f800000:
167 f = (f & 0x80000000)|0x7f7fffff;
168 return *(float*)&f;
169 break;
170 default:
171 return *(float*)&f;
172 break;
173 }
174 }
175
176 void ABS_S() {
177 _FdValUl_ = _FsValUl_ & 0x7fffffff;
178 clearFPUFlags( FPUflagO | FPUflagU );
179 }
180
181 void ADD_S() {
182 _FdValf_ = fpuDouble( _FsValUl_ ) + fpuDouble( _FtValUl_ );
183 checkOverflow( _FdValUl_, FPUflagO | FPUflagSO, 1 );
184 checkUnderflow( _FdValUl_, FPUflagU | FPUflagSU, 1 );
185 }
186
187 void ADDA_S() {
188 _FAValf_ = fpuDouble( _FsValUl_ ) + fpuDouble( _FtValUl_ );
189 checkOverflow( _FAValUl_, FPUflagO | FPUflagSO, 1 );
190 checkUnderflow( _FAValUl_, FPUflagU | FPUflagSU, 1 );
191 }
192
193 void BC1F() {
194 BC1(==);
195 }
196
197 void BC1FL() {
198 BC1L(==); // Equal to 0
199 }
200
201 void BC1T() {
202 BC1(!=);
203 }
204
205 void BC1TL() {
206 BC1L(!=); // different from 0
207 }
208
209 void C_EQ() {
210 C_cond_S(==);
211 }
212
213 void C_F() {
214 clearFPUFlags( FPUflagC ); //clears C regardless
215 }
216
217 void C_LE() {
218 C_cond_S(<=);
219 }
220
221 void C_LT() {
222 C_cond_S(<);
223 }
224
225 void CFC1() {
226 if ( !_Rt_ || ( (_Fs_ != 0) && (_Fs_ != 31) ) ) return;
227 cpuRegs.GPR.r[_Rt_].SD[0] = (s32)fpuRegs.fprc[_Fs_]; // force sign extension to 64 bit
228 }
229
230 void CTC1() {
231 if ( _Fs_ != 31 ) return;
232 fpuRegs.fprc[_Fs_] = cpuRegs.GPR.r[_Rt_].UL[0];
233 }
234
235 void CVT_S() {
236 _FdValf_ = (float)_FsValSl_;
237 _FdValf_ = fpuDouble( _FdValUl_ );
238 }
239
240 void CVT_W() {
241 if ( ( _FsValUl_ & 0x7F800000 ) <= 0x4E800000 ) { _FdValSl_ = (s32)_FsValf_; }
242 else if ( ( _FsValUl_ & 0x80000000 ) == 0 ) { _FdValUl_ = 0x7fffffff; }
243 else { _FdValUl_ = 0x80000000; }
244 }
245
246 void DIV_S() {
247 checkDivideByZero( _FdValUl_, _FtValUl_, _FsValUl_, FPUflagD | FPUflagSD, FPUflagI | FPUflagSI, 1 );
248 _FdValf_ = fpuDouble( _FsValUl_ ) / fpuDouble( _FtValUl_ );
249 checkOverflow( _FdValUl_, 0, 1);
250 checkUnderflow( _FdValUl_, 0, 1 );
251 }
252
253 /* The Instruction Set manual has an overly complicated way of
254 determining the flags that are set. Hopefully this shorter
255 method provides a similar outcome and is faster. (cottonvibes)
256 */
257 void MADD_S() {
258 FPRreg temp;
259 temp.f = fpuDouble( _FsValUl_ ) * fpuDouble( _FtValUl_ );
260 _FdValf_ = fpuDouble( _FAValUl_ ) + fpuDouble( temp.UL );
261 checkOverflow( _FdValUl_, FPUflagO | FPUflagSO, 1 );
262 checkUnderflow( _FdValUl_, FPUflagU | FPUflagSU, 1 );
263 }
264
265 void MADDA_S() {
266 _FAValf_ += fpuDouble( _FsValUl_ ) * fpuDouble( _FtValUl_ );
267 checkOverflow( _FAValUl_, FPUflagO | FPUflagSO, 1 );
268 checkUnderflow( _FAValUl_, FPUflagU | FPUflagSU, 1 );
269 }
270
271 void MAX_S() {
272 _FdValf_ = max( _FsValf_, _FtValf_ );
273 clearFPUFlags( FPUflagO | FPUflagU );
274 }
275
276 void MFC1() {
277 if ( !_Rt_ ) return;
278 cpuRegs.GPR.r[_Rt_].SD[0] = _FsValSl_; // sign extension into 64bit
279 }
280
281 void MIN_S() {
282 _FdValf_ = min( _FsValf_, _FtValf_ );
283 clearFPUFlags( FPUflagO | FPUflagU );
284 }
285
286 void MOV_S() {
287 _FdValUl_ = _FsValUl_;
288 }
289
290 void MSUB_S() {
291 FPRreg temp;
292 temp.f = fpuDouble( _FsValUl_ ) * fpuDouble( _FtValUl_ );
293 _FdValf_ = fpuDouble( _FAValUl_ ) - fpuDouble( temp.UL );
294 checkOverflow( _FdValUl_, FPUflagO | FPUflagSO, 1 );
295 checkUnderflow( _FdValUl_, FPUflagU | FPUflagSU, 1 );
296 }
297
298 void MSUBA_S() {
299 _FAValf_ -= fpuDouble( _FsValUl_ ) * fpuDouble( _FtValUl_ );
300 checkOverflow( _FAValUl_, FPUflagO | FPUflagSO, 1 );
301 checkUnderflow( _FAValUl_, FPUflagU | FPUflagSU, 1 );
302 }
303
304 void MTC1() {
305 _FsValUl_ = cpuRegs.GPR.r[_Rt_].UL[0];
306 }
307
308 void MUL_S() {
309 _FdValf_ = fpuDouble( _FsValUl_ ) * fpuDouble( _FtValUl_ );
310 checkOverflow( _FdValUl_, FPUflagO | FPUflagSO, 1 );
311 checkUnderflow( _FdValUl_, FPUflagU | FPUflagSU, 1 );
312 }
313
314 void MULA_S() {
315 _FAValf_ = fpuDouble( _FsValUl_ ) * fpuDouble( _FtValUl_ );
316 checkOverflow( _FAValUl_, FPUflagO | FPUflagSO, 1 );
317 checkUnderflow( _FAValUl_, FPUflagU | FPUflagSU, 1 );
318 }
319
320 void NEG_S() {
321 _FdValUl_ = (_FsValUl_ ^ 0x80000000);
322 clearFPUFlags( FPUflagO | FPUflagU );
323 }
324
325 void RSQRT_S() {
326 FPRreg temp;
327 if ( ( _FtValUl_ & 0x7F800000 ) == 0 ) { // Ft is zero (Denormals are Zero)
328 _ContVal_ |= FPUflagD | FPUflagSD;
329 _FdValUl_ = ( ( _FsValUl_ ^ _FtValUl_ ) & 0x80000000 ) | posFmax;
330 return;
331 }
332 else if ( _FtValUl_ & 0x80000000 ) { // Ft is negative
333 _ContVal_ |= FPUflagI | FPUflagSI;
334 temp.f = sqrt( fabs( fpuDouble( _FtValUl_ ) ) );
335 _FdValf_ = fpuDouble( _FsValUl_ ) / fpuDouble( temp.UL );
336 }
337 else { _FdValf_ = fpuDouble( _FsValUl_ ) / sqrt( fpuDouble( _FtValUl_ ) ); } // Ft is positive and not zero
338
339 checkOverflow( _FdValUl_, 0, 1 );
340 checkUnderflow( _FdValUl_, 0, 1 );
341 }
342
343 void SQRT_S() {
344 if ( ( _FtValUl_ & 0xFF800000 ) == 0x80000000 ) { _FdValUl_ = 0x80000000; } // If Ft = -0
345 else if ( _FtValUl_ & 0x80000000 ) { // If Ft is Negative
346 _ContVal_ |= FPUflagI | FPUflagSI;
347 _FdValf_ = sqrt( fabs( fpuDouble( _FtValUl_ ) ) );
348 }
349 else { _FdValf_ = sqrt( fpuDouble( _FtValUl_ ) ); } // If Ft is Positive
350 clearFPUFlags( FPUflagD );
351 }
352
353 void SUB_S() {
354 _FdValf_ = fpuDouble( _FsValUl_ ) - fpuDouble( _FtValUl_ );
355 checkOverflow( _FdValUl_, FPUflagO | FPUflagSO, 1 );
356 checkUnderflow( _FdValUl_, FPUflagU | FPUflagSU, 1 );
357 }
358
359 void SUBA_S() {
360 _FAValf_ = fpuDouble( _FsValUl_ ) - fpuDouble( _FtValUl_ );
361 checkOverflow( _FAValUl_, FPUflagO | FPUflagSO, 1 );
362 checkUnderflow( _FAValUl_, FPUflagU | FPUflagSU, 1 );
363 }
364
365 } // End Namespace COP1
366
367 /////////////////////////////////////////////////////////////////////
368 // COP1 (FPU) Load/Store Instructions
369
370 // These are actually EE opcodes but since they're related to FPU registers and such they
371 // seem more appropriately located here.
372
373 void LWC1() {
374 u32 addr;
375 addr = cpuRegs.GPR.r[_Rs_].UL[0] + (s16)(cpuRegs.code & 0xffff); // force sign extension to 32bit
376 if (addr & 0x00000003) { Console.Error( "FPU (LWC1 Opcode): Invalid Unaligned Memory Address" ); return; } // Should signal an exception?
377 fpuRegs.fpr[_Rt_].UL = memRead32(addr);
378 }
379
380 void SWC1() {
381 u32 addr;
382 addr = cpuRegs.GPR.r[_Rs_].UL[0] + (s16)(cpuRegs.code & 0xffff); // force sign extension to 32bit
383 if (addr & 0x00000003) { Console.Error( "FPU (SWC1 Opcode): Invalid Unaligned Memory Address" ); return; } // Should signal an exception?
384 memWrite32(addr, fpuRegs.fpr[_Rt_].UL);
385 }
386
387 } } }

  ViewVC Help
Powered by ViewVC 1.1.22