1 /////////////////////////////////////////////////////////////////////////
2 // $Id: fpu_compare.cc 13466 2018-02-16 07:57:32Z sshwarts $
3 /////////////////////////////////////////////////////////////////////////
4 //
5 //   Copyright (c) 2003-2018 Stanislav Shwartsman
6 //          Written by Stanislav Shwartsman [sshwarts at sourceforge net]
7 //
8 //  This library is free software; you can redistribute it and/or
9 //  modify it under the terms of the GNU Lesser General Public
10 //  License as published by the Free Software Foundation; either
11 //  version 2 of the License, or (at your option) any later version.
12 //
13 //  This library is distributed in the hope that it will be useful,
14 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 //  Lesser General Public License for more details.
17 //
18 //  You should have received a copy of the GNU Lesser General Public
19 //  License along with this library; if not, write to the Free Software
20 //  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
21 //
22 /////////////////////////////////////////////////////////////////////////
23 
24 #define NEED_CPU_REG_SHORTCUTS 1
25 #include "bochs.h"
26 #include "cpu/cpu.h"
27 #define LOG_THIS BX_CPU_THIS_PTR
28 
29 #if BX_SUPPORT_FPU
30 
31 #include "cpu/decoder/ia_opcodes.h"
32 
33 extern float_status_t i387cw_to_softfloat_status_word(Bit16u control_word);
34 
35 #include "softfloatx80.h"
36 
status_word_flags_fpu_compare(int float_relation)37 static int status_word_flags_fpu_compare(int float_relation)
38 {
39   switch(float_relation) {
40      case float_relation_unordered:
41          return (FPU_SW_C0|FPU_SW_C2|FPU_SW_C3);
42 
43      case float_relation_greater:
44          return (0);
45 
46      case float_relation_less:
47          return (FPU_SW_C0);
48 
49      case float_relation_equal:
50          return (FPU_SW_C3);
51   }
52 
53   return (-1);        // should never get here
54 }
55 
write_eflags_fpu_compare(int float_relation)56 void BX_CPU_C::write_eflags_fpu_compare(int float_relation)
57 {
58   switch(float_relation) {
59    case float_relation_unordered:
60       setEFlagsOSZAPC(EFlagsZFMask | EFlagsPFMask | EFlagsCFMask);
61       break;
62 
63    case float_relation_greater:
64       clearEFlagsOSZAPC();
65       break;
66 
67    case float_relation_less:
68       clearEFlagsOSZAPC();
69       assert_CF();
70       break;
71 
72    case float_relation_equal:
73       clearEFlagsOSZAPC();
74       assert_ZF();
75       break;
76 
77    default:
78       BX_PANIC(("write_eflags: unknown floating point compare relation"));
79   }
80 }
81 
FCOM_STi(bxInstruction_c * i)82 void BX_CPP_AttrRegparmN(1) BX_CPU_C::FCOM_STi(bxInstruction_c *i)
83 {
84   BX_CPU_THIS_PTR prepareFPU(i);
85   BX_CPU_THIS_PTR FPU_update_last_instruction(i);
86 
87   int pop_stack = 0;
88   if (i->getIaOpcode() == BX_IA_FCOMP_STi)
89       pop_stack = 1;
90 
91   clear_C1();
92 
93   if (IS_TAG_EMPTY(0) || IS_TAG_EMPTY(i->src()))
94   {
95       FPU_exception(i, FPU_EX_Stack_Underflow);
96       setcc(FPU_SW_C0|FPU_SW_C2|FPU_SW_C3);
97 
98       if(BX_CPU_THIS_PTR the_i387.is_IA_masked())
99       {
100           if (pop_stack)
101               BX_CPU_THIS_PTR the_i387.FPU_pop();
102       }
103       BX_NEXT_INSTR(i);
104   }
105 
106   float_status_t status =
107      i387cw_to_softfloat_status_word(BX_CPU_THIS_PTR the_i387.get_control_word());
108 
109   int rc = floatx80_compare(BX_READ_FPU_REG(0), BX_READ_FPU_REG(i->src()), status);
110   setcc(status_word_flags_fpu_compare(rc));
111 
112   if (! FPU_exception(i, status.float_exception_flags)) {
113      if (pop_stack)
114         BX_CPU_THIS_PTR the_i387.FPU_pop();
115   }
116 
117   BX_NEXT_INSTR(i);
118 }
119 
FCOMI_ST0_STj(bxInstruction_c * i)120 void BX_CPP_AttrRegparmN(1) BX_CPU_C::FCOMI_ST0_STj(bxInstruction_c *i)
121 {
122   BX_CPU_THIS_PTR prepareFPU(i);
123   BX_CPU_THIS_PTR FPU_update_last_instruction(i);
124 
125   int pop_stack = i->b1() & 4;
126 
127   clear_C1();
128 
129   if (IS_TAG_EMPTY(0) || IS_TAG_EMPTY(i->src()))
130   {
131       FPU_exception(i, FPU_EX_Stack_Underflow);
132       setEFlagsOSZAPC(EFlagsZFMask | EFlagsPFMask | EFlagsCFMask);
133 
134       if(BX_CPU_THIS_PTR the_i387.is_IA_masked())
135       {
136           if (pop_stack)
137               BX_CPU_THIS_PTR the_i387.FPU_pop();
138       }
139       BX_NEXT_INSTR(i);
140   }
141 
142   float_status_t status =
143       i387cw_to_softfloat_status_word(BX_CPU_THIS_PTR the_i387.get_control_word());
144 
145   int rc = floatx80_compare(BX_READ_FPU_REG(0), BX_READ_FPU_REG(i->src()), status);
146   BX_CPU_THIS_PTR write_eflags_fpu_compare(rc);
147 
148   if (! FPU_exception(i, status.float_exception_flags)) {
149      if (pop_stack)
150          BX_CPU_THIS_PTR the_i387.FPU_pop();
151   }
152 
153   BX_NEXT_INSTR(i);
154 }
155 
FUCOMI_ST0_STj(bxInstruction_c * i)156 void BX_CPP_AttrRegparmN(1) BX_CPU_C::FUCOMI_ST0_STj(bxInstruction_c *i)
157 {
158   BX_CPU_THIS_PTR prepareFPU(i);
159   BX_CPU_THIS_PTR FPU_update_last_instruction(i);
160 
161   int pop_stack = i->b1() & 4;
162 
163   clear_C1();
164 
165   if (IS_TAG_EMPTY(0) || IS_TAG_EMPTY(i->src()))
166   {
167       FPU_exception(i, FPU_EX_Stack_Underflow);
168       setEFlagsOSZAPC(EFlagsZFMask | EFlagsPFMask | EFlagsCFMask);
169 
170       if(BX_CPU_THIS_PTR the_i387.is_IA_masked())
171       {
172           if (pop_stack)
173               BX_CPU_THIS_PTR the_i387.FPU_pop();
174       }
175       BX_NEXT_INSTR(i);
176   }
177 
178   float_status_t status =
179       i387cw_to_softfloat_status_word(BX_CPU_THIS_PTR the_i387.get_control_word());
180 
181   int rc = floatx80_compare_quiet(BX_READ_FPU_REG(0), BX_READ_FPU_REG(i->src()), status);
182   BX_CPU_THIS_PTR write_eflags_fpu_compare(rc);
183 
184   if (! FPU_exception(i, status.float_exception_flags)) {
185      if (pop_stack)
186          BX_CPU_THIS_PTR the_i387.FPU_pop();
187   }
188 
189   BX_NEXT_INSTR(i);
190 }
191 
FUCOM_STi(bxInstruction_c * i)192 void BX_CPP_AttrRegparmN(1) BX_CPU_C::FUCOM_STi(bxInstruction_c *i)
193 {
194   BX_CPU_THIS_PTR prepareFPU(i);
195   BX_CPU_THIS_PTR FPU_update_last_instruction(i);
196 
197   int pop_stack = 0;
198   if (i->getIaOpcode() == BX_IA_FUCOMP_STi)
199       pop_stack = 1;
200 
201   if (IS_TAG_EMPTY(0) || IS_TAG_EMPTY(i->src()))
202   {
203       FPU_exception(i, FPU_EX_Stack_Underflow);
204       setcc(FPU_SW_C0|FPU_SW_C2|FPU_SW_C3);
205 
206       if(BX_CPU_THIS_PTR the_i387.is_IA_masked())
207       {
208           if (pop_stack)
209               BX_CPU_THIS_PTR the_i387.FPU_pop();
210       }
211       BX_NEXT_INSTR(i);
212   }
213 
214   float_status_t status =
215       i387cw_to_softfloat_status_word(BX_CPU_THIS_PTR the_i387.get_control_word());
216 
217   int rc = floatx80_compare_quiet(BX_READ_FPU_REG(0), BX_READ_FPU_REG(i->src()), status);
218   setcc(status_word_flags_fpu_compare(rc));
219 
220   if (! FPU_exception(i, status.float_exception_flags)) {
221      if (pop_stack)
222          BX_CPU_THIS_PTR the_i387.FPU_pop();
223   }
224 
225   BX_NEXT_INSTR(i);
226 }
227 
FCOM_SINGLE_REAL(bxInstruction_c * i)228 void BX_CPP_AttrRegparmN(1) BX_CPU_C::FCOM_SINGLE_REAL(bxInstruction_c *i)
229 {
230   BX_CPU_THIS_PTR prepareFPU(i);
231 
232   int rc, pop_stack = 0;
233   if (i->getIaOpcode() == BX_IA_FCOMP_SINGLE_REAL)
234       pop_stack = 1;
235 
236   RMAddr(i) = BX_CPU_RESOLVE_ADDR(i);
237   float32 load_reg = read_virtual_dword(i->seg(), RMAddr(i));
238 
239   BX_CPU_THIS_PTR FPU_update_last_instruction(i);
240 
241   clear_C1();
242 
243   if (IS_TAG_EMPTY(0))
244   {
245       FPU_exception(i, FPU_EX_Stack_Underflow);
246       setcc(FPU_SW_C0|FPU_SW_C2|FPU_SW_C3);
247 
248       if(BX_CPU_THIS_PTR the_i387.is_IA_masked())
249       {
250           if (pop_stack)
251               BX_CPU_THIS_PTR the_i387.FPU_pop();
252       }
253       BX_NEXT_INSTR(i);
254   }
255 
256   float_status_t status =
257       i387cw_to_softfloat_status_word(BX_CPU_THIS_PTR the_i387.get_control_word());
258 
259   floatx80 a = BX_READ_FPU_REG(0);
260 
261   if (floatx80_is_nan(a) || floatx80_is_unsupported(a) || float32_is_nan(load_reg)) {
262     rc = float_relation_unordered;
263     float_raise(status, float_flag_invalid);
264   }
265   else {
266     rc = floatx80_compare(a, float32_to_floatx80(load_reg, status), status);
267   }
268   setcc(status_word_flags_fpu_compare(rc));
269 
270   if (! FPU_exception(i, status.float_exception_flags)) {
271      if (pop_stack)
272          BX_CPU_THIS_PTR the_i387.FPU_pop();
273   }
274 
275   BX_NEXT_INSTR(i);
276 }
277 
FCOM_DOUBLE_REAL(bxInstruction_c * i)278 void BX_CPP_AttrRegparmN(1) BX_CPU_C::FCOM_DOUBLE_REAL(bxInstruction_c *i)
279 {
280   BX_CPU_THIS_PTR prepareFPU(i);
281 
282   int rc, pop_stack = 0;
283   if (i->getIaOpcode() == BX_IA_FCOMP_DOUBLE_REAL)
284       pop_stack = 1;
285 
286   RMAddr(i) = BX_CPU_RESOLVE_ADDR(i);
287   float64 load_reg = read_virtual_qword(i->seg(), RMAddr(i));
288 
289   BX_CPU_THIS_PTR FPU_update_last_instruction(i);
290 
291   clear_C1();
292 
293   if (IS_TAG_EMPTY(0))
294   {
295       FPU_exception(i, FPU_EX_Stack_Underflow);
296       setcc(FPU_SW_C0|FPU_SW_C2|FPU_SW_C3);
297 
298       if(BX_CPU_THIS_PTR the_i387.is_IA_masked())
299       {
300           if (pop_stack)
301               BX_CPU_THIS_PTR the_i387.FPU_pop();
302       }
303       BX_NEXT_INSTR(i);
304   }
305 
306   float_status_t status =
307       i387cw_to_softfloat_status_word(BX_CPU_THIS_PTR the_i387.get_control_word());
308 
309   floatx80 a = BX_READ_FPU_REG(0);
310 
311   if (floatx80_is_nan(a) || floatx80_is_unsupported(a) || float64_is_nan(load_reg)) {
312     rc = float_relation_unordered;
313     float_raise(status, float_flag_invalid);
314   }
315   else {
316     rc = floatx80_compare(a, float64_to_floatx80(load_reg, status), status);
317   }
318   setcc(status_word_flags_fpu_compare(rc));
319 
320   if (! FPU_exception(i, status.float_exception_flags)) {
321      if (pop_stack)
322          BX_CPU_THIS_PTR the_i387.FPU_pop();
323   }
324 
325   BX_NEXT_INSTR(i);
326 }
327 
FICOM_WORD_INTEGER(bxInstruction_c * i)328 void BX_CPP_AttrRegparmN(1) BX_CPU_C::FICOM_WORD_INTEGER(bxInstruction_c *i)
329 {
330   BX_CPU_THIS_PTR prepareFPU(i);
331 
332   int pop_stack = 0;
333   if (i->getIaOpcode() == BX_IA_FICOMP_WORD_INTEGER)
334       pop_stack = 1;
335 
336   RMAddr(i) = BX_CPU_RESOLVE_ADDR(i);
337   Bit16s load_reg = (Bit16s) read_virtual_word(i->seg(), RMAddr(i));
338 
339   BX_CPU_THIS_PTR FPU_update_last_instruction(i);
340 
341   clear_C1();
342 
343   if (IS_TAG_EMPTY(0))
344   {
345       FPU_exception(i, FPU_EX_Stack_Underflow);
346       setcc(FPU_SW_C0|FPU_SW_C2|FPU_SW_C3);
347 
348       if(BX_CPU_THIS_PTR the_i387.is_IA_masked())
349       {
350           if (pop_stack)
351               BX_CPU_THIS_PTR the_i387.FPU_pop();
352       }
353       BX_NEXT_INSTR(i);
354   }
355 
356   float_status_t status =
357       i387cw_to_softfloat_status_word(BX_CPU_THIS_PTR the_i387.get_control_word());
358 
359   int rc = floatx80_compare(BX_READ_FPU_REG(0),
360                       int32_to_floatx80((Bit32s)(load_reg)), status);
361   setcc(status_word_flags_fpu_compare(rc));
362 
363   if (! FPU_exception(i, status.float_exception_flags)) {
364      if (pop_stack)
365          BX_CPU_THIS_PTR the_i387.FPU_pop();
366   }
367 
368   BX_NEXT_INSTR(i);
369 }
370 
FICOM_DWORD_INTEGER(bxInstruction_c * i)371 void BX_CPP_AttrRegparmN(1) BX_CPU_C::FICOM_DWORD_INTEGER(bxInstruction_c *i)
372 {
373   BX_CPU_THIS_PTR prepareFPU(i);
374 
375   int pop_stack = 0;
376   if (i->getIaOpcode() == BX_IA_FICOMP_DWORD_INTEGER)
377       pop_stack = 1;
378 
379   RMAddr(i) = BX_CPU_RESOLVE_ADDR(i);
380   Bit32s load_reg = (Bit32s) read_virtual_dword(i->seg(), RMAddr(i));
381 
382   BX_CPU_THIS_PTR FPU_update_last_instruction(i);
383 
384   clear_C1();
385 
386   if (IS_TAG_EMPTY(0))
387   {
388       FPU_exception(i, FPU_EX_Stack_Underflow);
389       setcc(FPU_SW_C0|FPU_SW_C2|FPU_SW_C3);
390 
391       if(BX_CPU_THIS_PTR the_i387.is_IA_masked())
392       {
393           if (pop_stack)
394               BX_CPU_THIS_PTR the_i387.FPU_pop();
395       }
396       BX_NEXT_INSTR(i);
397   }
398 
399   float_status_t status =
400       i387cw_to_softfloat_status_word(BX_CPU_THIS_PTR the_i387.get_control_word());
401 
402   int rc = floatx80_compare(BX_READ_FPU_REG(0), int32_to_floatx80(load_reg), status);
403   setcc(status_word_flags_fpu_compare(rc));
404 
405   if (! FPU_exception(i, status.float_exception_flags)) {
406      if (pop_stack)
407          BX_CPU_THIS_PTR the_i387.FPU_pop();
408   }
409 
410   BX_NEXT_INSTR(i);
411 }
412 
413 /* DE D9 */
FCOMPP(bxInstruction_c * i)414 void BX_CPP_AttrRegparmN(1) BX_CPU_C::FCOMPP(bxInstruction_c *i)
415 {
416   BX_CPU_THIS_PTR prepareFPU(i);
417   BX_CPU_THIS_PTR FPU_update_last_instruction(i);
418 
419   clear_C1();
420 
421   if (IS_TAG_EMPTY(0) || IS_TAG_EMPTY(1))
422   {
423       FPU_exception(i, FPU_EX_Stack_Underflow);
424       setcc(FPU_SW_C0|FPU_SW_C2|FPU_SW_C3);
425 
426       if(BX_CPU_THIS_PTR the_i387.is_IA_masked())
427       {
428           BX_CPU_THIS_PTR the_i387.FPU_pop();
429           BX_CPU_THIS_PTR the_i387.FPU_pop();
430       }
431       BX_NEXT_INSTR(i);
432   }
433 
434   float_status_t status =
435       i387cw_to_softfloat_status_word(BX_CPU_THIS_PTR the_i387.get_control_word());
436 
437   int rc = floatx80_compare(BX_READ_FPU_REG(0), BX_READ_FPU_REG(1), status);
438   setcc(status_word_flags_fpu_compare(rc));
439 
440   if (! FPU_exception(i, status.float_exception_flags)) {
441      BX_CPU_THIS_PTR the_i387.FPU_pop();
442      BX_CPU_THIS_PTR the_i387.FPU_pop();
443   }
444 
445   BX_NEXT_INSTR(i);
446 }
447 
448 /* DA E9 */
FUCOMPP(bxInstruction_c * i)449 void BX_CPP_AttrRegparmN(1) BX_CPU_C::FUCOMPP(bxInstruction_c *i)
450 {
451   BX_CPU_THIS_PTR prepareFPU(i);
452   BX_CPU_THIS_PTR FPU_update_last_instruction(i);
453 
454   if (IS_TAG_EMPTY(0) || IS_TAG_EMPTY(1))
455   {
456       FPU_exception(i, FPU_EX_Stack_Underflow);
457       setcc(FPU_SW_C0|FPU_SW_C2|FPU_SW_C3);
458 
459       if(BX_CPU_THIS_PTR the_i387.is_IA_masked())
460       {
461           BX_CPU_THIS_PTR the_i387.FPU_pop();
462           BX_CPU_THIS_PTR the_i387.FPU_pop();
463       }
464       BX_NEXT_INSTR(i);
465   }
466 
467   float_status_t status =
468       i387cw_to_softfloat_status_word(BX_CPU_THIS_PTR the_i387.get_control_word());
469 
470   int rc = floatx80_compare_quiet(BX_READ_FPU_REG(0), BX_READ_FPU_REG(1), status);
471   setcc(status_word_flags_fpu_compare(rc));
472 
473   if (! FPU_exception(i, status.float_exception_flags)) {
474      BX_CPU_THIS_PTR the_i387.FPU_pop();
475      BX_CPU_THIS_PTR the_i387.FPU_pop();
476   }
477 
478   BX_NEXT_INSTR(i);
479 }
480 
481 /* D9 E4 */
FTST(bxInstruction_c * i)482 void BX_CPP_AttrRegparmN(1) BX_CPU_C::FTST(bxInstruction_c *i)
483 {
484   BX_CPU_THIS_PTR prepareFPU(i);
485   BX_CPU_THIS_PTR FPU_update_last_instruction(i);
486 
487   clear_C1();
488 
489   if (IS_TAG_EMPTY(0)) {
490      FPU_exception(i, FPU_EX_Stack_Underflow);
491      setcc(FPU_SW_C0|FPU_SW_C2|FPU_SW_C3);
492   }
493   else {
494      extern const floatx80 Const_Z;
495 
496      float_status_t status =
497         i387cw_to_softfloat_status_word(BX_CPU_THIS_PTR the_i387.get_control_word());
498 
499      int rc = floatx80_compare(BX_READ_FPU_REG(0), Const_Z, status);
500      setcc(status_word_flags_fpu_compare(rc));
501      FPU_exception(i, status.float_exception_flags);
502   }
503 
504   BX_NEXT_INSTR(i);
505 }
506 
507 /* D9 E5 */
FXAM(bxInstruction_c * i)508 void BX_CPP_AttrRegparmN(1) BX_CPU_C::FXAM(bxInstruction_c *i)
509 {
510   BX_CPU_THIS_PTR prepareFPU(i);
511   BX_CPU_THIS_PTR FPU_update_last_instruction(i);
512 
513   floatx80 reg = BX_READ_FPU_REG(0);
514   int sign = floatx80_sign(reg);
515 
516   /*
517    * Examine the contents of the ST(0) register and sets the condition
518    * code flags C0, C2 and C3 in the FPU status word to indicate the
519    * class of value or number in the register.
520    */
521 
522   if (IS_TAG_EMPTY(0))
523   {
524       setcc(FPU_SW_C3|FPU_SW_C1|FPU_SW_C0);
525   }
526   else
527   {
528       float_class_t aClass = floatx80_class(reg);
529 
530       switch(aClass)
531       {
532         case float_zero:
533            setcc(FPU_SW_C3|FPU_SW_C1);
534            break;
535 
536         case float_SNaN:
537         case float_QNaN:
538            // unsupported handled as NaNs
539            if (floatx80_is_unsupported(reg)) {
540                setcc(FPU_SW_C1);
541            } else {
542                setcc(FPU_SW_C1|FPU_SW_C0);
543            }
544            break;
545 
546         case float_negative_inf:
547         case float_positive_inf:
548            setcc(FPU_SW_C2|FPU_SW_C1|FPU_SW_C0);
549            break;
550 
551         case float_denormal:
552            setcc(FPU_SW_C3|FPU_SW_C2|FPU_SW_C1);
553            break;
554 
555         case float_normalized:
556            setcc(FPU_SW_C2|FPU_SW_C1);
557            break;
558       }
559   }
560 
561   /*
562    * The C1 flag is set to the sign of the value in ST(0), regardless
563    * of whether the register is empty or full.
564    */
565   if (! sign)
566     clear_C1();
567 
568   BX_NEXT_INSTR(i);
569 }
570 
571 #endif
572