DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/fpu/fpu.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 
00020 #include "dosbox.h"
00021 #if C_FPU
00022 
00023 #include <math.h>
00024 #include <float.h>
00025 #include "paging.h"
00026 #include "cross.h"
00027 #include "mem.h"
00028 #include "fpu.h"
00029 #include "cpu.h"
00030 #include "../cpu/lazyflags.h"
00031 
00032 FPU_rec fpu;
00033 
00034 void FPU_FLDCW(PhysPt addr){
00035         Bit16u temp = mem_readw(addr);
00036         FPU_SetCW(temp);
00037 }
00038 
00039 Bit16u FPU_GetTag(void){
00040         Bit16u tag=0;
00041 
00042         for (Bitu i=0;i<8;i++)
00043                 tag |= (fpu.tags[i]&3) << (2*i);
00044 
00045         return tag;
00046 }
00047 
00048 #if C_FPU_X86
00049 #include "fpu_instructions_x86.h"
00050 #elif defined(HAS_LONG_DOUBLE)
00051 #include "fpu_instructions_longdouble.h"
00052 #else
00053 #include "fpu_instructions.h"
00054 #endif
00055 
00056 /* WATCHIT : ALWAYS UPDATE REGISTERS BEFORE AND AFTER USING THEM 
00057                         STATUS WORD =>  FPU_SET_TOP(TOP) BEFORE a read
00058                         TOP=FPU_GET_TOP() after a write;
00059                         */
00060 
00061 static void EATREE(Bitu _rm){
00062         Bitu group=(_rm >> 3) & 7;
00063         switch(group){
00064                 case 0x00:      /* FADD */
00065                         FPU_FADD_EA(TOP);
00066                         break;
00067                 case 0x01:      /* FMUL  */
00068                         FPU_FMUL_EA(TOP);
00069                         break;
00070                 case 0x02:      /* FCOM */
00071                         FPU_FCOM_EA(TOP);
00072                         break;
00073                 case 0x03:      /* FCOMP */
00074                         FPU_FCOM_EA(TOP);
00075                         FPU_FPOP();
00076                         break;
00077                 case 0x04:      /* FSUB */
00078                         FPU_FSUB_EA(TOP);
00079                         break;
00080                 case 0x05:      /* FSUBR */
00081                         FPU_FSUBR_EA(TOP);
00082                         break;
00083                 case 0x06:      /* FDIV */
00084                         FPU_FDIV_EA(TOP);
00085                         break;
00086                 case 0x07:      /* FDIVR */
00087                         FPU_FDIVR_EA(TOP);
00088                         break;
00089                 default:
00090                         break;
00091         }
00092 }
00093 
00094 void FPU_ESC0_EA(Bitu rm,PhysPt addr) {
00095         /* REGULAR TREE WITH 32 BITS REALS */
00096         FPU_FLD_F32_EA(addr);
00097         EATREE(rm);
00098 }
00099 
00100 void FPU_ESC0_Normal(Bitu rm) {
00101         Bitu group=(rm >> 3) & 7;
00102         Bitu sub=(rm & 7);
00103         switch (group){
00104         case 0x00:              /* FADD ST,STi */
00105                 FPU_FADD(TOP,STV(sub));
00106                 break;
00107         case 0x01:              /* FMUL  ST,STi */
00108                 FPU_FMUL(TOP,STV(sub));
00109                 break;
00110         case 0x02:              /* FCOM  STi */
00111                 FPU_FCOM(TOP,STV(sub));
00112                 break;
00113         case 0x03:              /* FCOMP STi */
00114                 FPU_FCOM(TOP,STV(sub));
00115                 FPU_FPOP();
00116                 break;
00117         case 0x04:              /* FSUB  ST,STi */
00118                 FPU_FSUB(TOP,STV(sub));
00119                 break;  
00120         case 0x05:              /* FSUBR ST,STi */
00121                 FPU_FSUBR(TOP,STV(sub));
00122                 break;
00123         case 0x06:              /* FDIV  ST,STi */
00124                 FPU_FDIV(TOP,STV(sub));
00125                 break;
00126         case 0x07:              /* FDIVR ST,STi */
00127                 FPU_FDIVR(TOP,STV(sub));
00128                 break;
00129         default:
00130                 break;
00131         }
00132 }
00133 
00134 void FPU_ESC1_EA(Bitu rm,PhysPt addr) {
00135 // floats
00136         Bitu group=(rm >> 3) & 7;
00137         Bitu sub=(rm & 7);
00138         switch(group){
00139         case 0x00: /* FLD float*/
00140                 {
00141                         unsigned char old_TOP = TOP;
00142 
00143                         try {
00144                                 FPU_PREP_PUSH();
00145                                 FPU_FLD_F32(addr,TOP);
00146                         }
00147             catch (const GuestPageFaultException& pf) {
00148                                 (void)pf;
00149                                 TOP = old_TOP;
00150                                 throw;
00151                         }
00152                 }
00153                 break;
00154         case 0x01: /* UNKNOWN */
00155                 LOG(LOG_FPU,LOG_WARN)("ESC EA 1:Unhandled group %d subfunction %d",(int)group,(int)sub);
00156                 break;
00157         case 0x02: /* FST float*/
00158                 FPU_FST_F32(addr);
00159                 break;
00160         case 0x03: /* FSTP float*/
00161                 FPU_FST_F32(addr);
00162                 FPU_FPOP();
00163                 break;
00164         case 0x04: /* FLDENV */
00165                 FPU_FLDENV(addr);
00166                 break;
00167         case 0x05: /* FLDCW */
00168                 FPU_FLDCW(addr);
00169                 break;
00170         case 0x06: /* FSTENV */
00171                 FPU_FSTENV(addr);
00172                 break;
00173         case 0x07:  /* FNSTCW*/
00174                 mem_writew(addr,fpu.cw);
00175                 break;
00176         default:
00177                 LOG(LOG_FPU,LOG_WARN)("ESC EA 1:Unhandled group %d subfunction %d",(int)group,(int)sub);
00178                 break;
00179         }
00180 }
00181 
00182 void FPU_ESC1_Normal(Bitu rm) {
00183         Bitu group=(rm >> 3) & 7;
00184         Bitu sub=(rm & 7);
00185         switch (group){
00186         case 0x00: /* FLD STi */
00187                 {
00188                         Bitu reg_from=STV(sub);
00189                         FPU_PREP_PUSH();
00190                         FPU_FST(reg_from, TOP);
00191                         break;
00192                 }
00193         case 0x01: /* FXCH STi */
00194                 FPU_FXCH(TOP,STV(sub));
00195                 break;
00196         case 0x02: /* FNOP */
00197                 FPU_FNOP();
00198                 break;
00199         case 0x03: /* FSTP STi */
00200                 FPU_FST(TOP,STV(sub));
00201                 FPU_FPOP();
00202                 break;   
00203         case 0x04:
00204                 switch(sub){
00205                 case 0x00:       /* FCHS */
00206                         FPU_FCHS();
00207                         break;
00208                 case 0x01:       /* FABS */
00209                         FPU_FABS();
00210                         break;
00211                 case 0x02:       /* UNKNOWN */
00212                 case 0x03:       /* ILLEGAL */
00213                         LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",(int)group,(int)sub);
00214                         break;
00215                 case 0x04:       /* FTST */
00216                         FPU_FTST();
00217                         break;
00218                 case 0x05:       /* FXAM */
00219                         FPU_FXAM();
00220                         break;
00221                 case 0x06:       /* FTSTP (cyrix)*/
00222                 case 0x07:       /* UNKNOWN */
00223                         LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",(int)group,(int)sub);
00224                         break;
00225                 }
00226                 break;
00227         case 0x05:
00228                 switch(sub){    
00229                 case 0x00:       /* FLD1 */
00230                         FPU_FLD1();
00231                         break;
00232                 case 0x01:       /* FLDL2T */
00233                         FPU_FLDL2T();
00234                         break;
00235                 case 0x02:       /* FLDL2E */
00236                         FPU_FLDL2E();
00237                         break;
00238                 case 0x03:       /* FLDPI */
00239                         FPU_FLDPI();
00240                         break;
00241                 case 0x04:       /* FLDLG2 */
00242                         FPU_FLDLG2();
00243                         break;
00244                 case 0x05:       /* FLDLN2 */
00245                         FPU_FLDLN2();
00246                         break;
00247                 case 0x06:       /* FLDZ*/
00248                         FPU_FLDZ();
00249                         break;
00250                 case 0x07:       /* ILLEGAL */
00251                         LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",(int)group,(int)sub);
00252                         break;
00253                 }
00254                 break;
00255         case 0x06:
00256                 switch(sub){
00257                 case 0x00:      /* F2XM1 */
00258                         FPU_F2XM1();
00259                         break;
00260                 case 0x01:      /* FYL2X */
00261                         FPU_FYL2X();
00262                         break;
00263                 case 0x02:      /* FPTAN  */
00264                         FPU_FPTAN();
00265                         break;
00266                 case 0x03:      /* FPATAN */
00267                         FPU_FPATAN();
00268                         break;
00269                 case 0x04:      /* FXTRACT */
00270                         FPU_FXTRACT();
00271                         break;
00272                 case 0x05:      /* FPREM1 */
00273                         FPU_FPREM1();
00274                         break;
00275                 case 0x06:      /* FDECSTP */
00276                         TOP = (TOP - 1) & 7;
00277                         break;
00278                 case 0x07:      /* FINCSTP */
00279                         TOP = (TOP + 1) & 7;
00280                         break;
00281                 default:
00282                         LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",(int)group,(int)sub);
00283                         break;
00284                 }
00285                 break;
00286         case 0x07:
00287                 switch(sub){
00288                 case 0x00:              /* FPREM */
00289                         FPU_FPREM();
00290                         break;
00291                 case 0x01:              /* FYL2XP1 */
00292                         FPU_FYL2XP1();
00293                         break;
00294                 case 0x02:              /* FSQRT */
00295                         FPU_FSQRT();
00296                         break;
00297                 case 0x03:              /* FSINCOS */
00298                         FPU_FSINCOS();
00299                         break;
00300                 case 0x04:              /* FRNDINT */
00301                         FPU_FRNDINT();
00302                         break;
00303                 case 0x05:              /* FSCALE */
00304                         FPU_FSCALE();
00305                         break;
00306                 case 0x06:              /* FSIN */
00307                         FPU_FSIN();
00308                         break;
00309                 case 0x07:              /* FCOS */
00310                         FPU_FCOS();
00311                         break;
00312                 default:
00313                         LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",(int)group,(int)sub);
00314                         break;
00315                 }
00316                 break;
00317                 default:
00318                         LOG(LOG_FPU,LOG_WARN)("ESC 1:Unhandled group %X subfunction %X",(int)group,(int)sub);
00319         }
00320 }
00321 
00322 
00323 void FPU_ESC2_EA(Bitu rm,PhysPt addr) {
00324         /* 32 bits integer operants */
00325         FPU_FLD_I32_EA(addr);
00326         EATREE(rm);
00327 }
00328 
00329 void FPU_ESC2_Normal(Bitu rm) {
00330         Bitu group=(rm >> 3) & 7;
00331         Bitu sub=(rm & 7);
00332         switch(group){
00333         case 0x00: /* FCMOVB STi */
00334                 if (TFLG_B) FPU_FCMOV(TOP,STV(sub));
00335                 break;
00336         case 0x01: /* FCMOVE STi */
00337                 if (TFLG_Z) FPU_FCMOV(TOP,STV(sub));
00338                 break;
00339         case 0x02: /* FCMOVBE STi */
00340                 if (TFLG_BE) FPU_FCMOV(TOP,STV(sub));
00341                 break;
00342         case 0x03: /* FCMOVU STi */
00343                 if (TFLG_P) FPU_FCMOV(TOP,STV(sub));
00344                 break;
00345         case 0x05:
00346                 switch(sub){
00347                 case 0x01:              /* FUCOMPP */
00348                         FPU_FUCOM(TOP,STV(1));
00349                         FPU_FPOP();
00350                         FPU_FPOP();
00351                         break;
00352                 default:
00353                         LOG(LOG_FPU,LOG_WARN)("ESC 2:Unhandled group %d subfunction %d",(int)group,(int)sub); 
00354                         break;
00355                 }
00356                 break;
00357         default:
00358                 LOG(LOG_FPU,LOG_WARN)("ESC 2:Unhandled group %d subfunction %d",(int)group,(int)sub);
00359                 break;
00360         }
00361 }
00362 
00363 
00364 void FPU_ESC3_EA(Bitu rm,PhysPt addr) {
00365         Bitu group=(rm >> 3) & 7;
00366         Bitu sub=(rm & 7);
00367 
00368         switch(group){
00369         case 0x00:      /* FILD */
00370                 {
00371                         unsigned char old_TOP = TOP;
00372 
00373                         try {
00374                                 FPU_PREP_PUSH();
00375                                 FPU_FLD_I32(addr,TOP);
00376                         }
00377             catch (const GuestPageFaultException& pf) {
00378                                 (void)pf;
00379                                 TOP = old_TOP;
00380                                 throw;
00381                         }
00382                 }
00383                 break;
00384         case 0x01:      /* FISTTP */
00385                 LOG(LOG_FPU,LOG_WARN)("ESC 3 EA:Unhandled group %d subfunction %d",(int)group,(int)sub);
00386                 break;
00387         case 0x02:      /* FIST */
00388                 FPU_FST_I32(addr);
00389                 break;
00390         case 0x03:      /* FISTP */
00391                 FPU_FST_I32(addr);
00392                 FPU_FPOP();
00393                 break;
00394         case 0x05:      /* FLD 80 Bits Real */
00395                 {
00396                         unsigned char old_TOP = TOP;
00397 
00398                         try {
00399                                 FPU_PREP_PUSH();
00400                                 FPU_FLD_F80(addr);
00401                         }
00402             catch (const GuestPageFaultException& pf) {
00403                                 (void)pf;
00404                                 TOP = old_TOP;
00405                                 throw;
00406                         }
00407                 }
00408                 break;
00409         case 0x07:      /* FSTP 80 Bits Real */
00410                 FPU_FST_F80(addr);
00411                 FPU_FPOP();
00412                 break;
00413         default:
00414                 LOG(LOG_FPU,LOG_WARN)("ESC 3 EA:Unhandled group %d subfunction %d",(int)group,(int)sub);
00415         }
00416 }
00417 
00418 void FPU_ESC3_Normal(Bitu rm) {
00419         Bitu group=(rm >> 3) & 7;
00420         Bitu sub=(rm & 7);
00421         switch (group) {
00422         case 0x00: /* FCMOVNB STi */
00423                 if (TFLG_NB) FPU_FCMOV(TOP,STV(sub));
00424                 break;
00425         case 0x01: /* FCMOVNE STi */
00426                 if (TFLG_NZ) FPU_FCMOV(TOP,STV(sub));
00427                 break;
00428         case 0x02: /* FCMOVNBE STi */
00429                 if (TFLG_NBE) FPU_FCMOV(TOP,STV(sub));
00430                 break;
00431         case 0x03: /* FCMOVNU STi */
00432                 if (TFLG_NP) FPU_FCMOV(TOP,STV(sub));
00433                 break;
00434         case 0x04:
00435                 switch (sub) {
00436                 case 0x00:                              //FNENI
00437                 case 0x01:                              //FNDIS
00438                         LOG(LOG_FPU,LOG_ERROR)("8087 only fpu code used esc 3: group 4: subfuntion :%d",(int)sub);
00439                         break;
00440                 case 0x02:                              //FNCLEX FCLEX
00441                         FPU_FCLEX();
00442                         break;
00443                 case 0x03:                              //FNINIT FINIT
00444                         FPU_FINIT();
00445                         break;
00446                 case 0x04:                              //FNSETPM
00447                 case 0x05:                              //FRSTPM
00448 //                      LOG(LOG_FPU,LOG_ERROR)("80267 protected mode (un)set. Nothing done");
00449                         FPU_FNOP();
00450                         break;
00451                 default:
00452                         E_Exit("ESC 3:ILLEGAL OPCODE group %d subfunction %d",(int)group,(int)sub);
00453                 }
00454                 break;
00455         case 0x05:              /* FUCOMI STi */
00456                 FPU_FUCOMI(TOP,STV(sub));
00457                 break;
00458         case 0x06:              /* FCOMI STi */
00459                 FPU_FCOMI(TOP,STV(sub));
00460                 break;
00461         default:
00462                 LOG(LOG_FPU,LOG_WARN)("ESC 3:Unhandled group %d subfunction %d",(int)group,(int)sub);
00463                 break;
00464         }
00465         return;
00466 }
00467 
00468 
00469 void FPU_ESC4_EA(Bitu rm,PhysPt addr) {
00470         /* REGULAR TREE WITH 64 BITS REALS */
00471         FPU_FLD_F64_EA(addr);
00472         EATREE(rm);
00473 }
00474 
00475 void FPU_ESC4_Normal(Bitu rm) {
00476         /* LOOKS LIKE number 6 without popping */
00477         Bitu group=(rm >> 3) & 7;
00478         Bitu sub=(rm & 7);
00479         switch(group){
00480         case 0x00:      /* FADD STi,ST*/
00481                 FPU_FADD(STV(sub),TOP);
00482                 break;
00483         case 0x01:      /* FMUL STi,ST*/
00484                 FPU_FMUL(STV(sub),TOP);
00485                 break;
00486         case 0x02:  /* FCOM*/
00487                 FPU_FCOM(TOP,STV(sub));
00488                 break;
00489         case 0x03:  /* FCOMP*/
00490                 FPU_FCOM(TOP,STV(sub));
00491                 FPU_FPOP();
00492                 break;
00493         case 0x04:  /* FSUBR STi,ST*/
00494                 FPU_FSUBR(STV(sub),TOP);
00495                 break;
00496         case 0x05:  /* FSUB  STi,ST*/
00497                 FPU_FSUB(STV(sub),TOP);
00498                 break;
00499         case 0x06:  /* FDIVR STi,ST*/
00500                 FPU_FDIVR(STV(sub),TOP);
00501                 break;
00502         case 0x07:  /* FDIV STi,ST*/
00503                 FPU_FDIV(STV(sub),TOP);
00504                 break;
00505         default:
00506                 break;
00507         }
00508 }
00509 
00510 void FPU_ESC5_EA(Bitu rm,PhysPt addr) {
00511         Bitu group=(rm >> 3) & 7;
00512         Bitu sub=(rm & 7);
00513         switch(group){
00514         case 0x00:  /* FLD double real*/
00515                 {
00516                         unsigned char old_TOP = TOP;
00517 
00518                         try {
00519                                 FPU_PREP_PUSH();
00520                                 FPU_FLD_F64(addr,TOP);
00521                         }
00522             catch (const GuestPageFaultException& pf) {
00523                                 (void)pf;
00524                                 TOP = old_TOP;
00525                                 throw;
00526                         }
00527                 }
00528                 break;
00529         case 0x01:  /* FISTTP longint*/
00530                 LOG(LOG_FPU,LOG_WARN)("ESC 5 EA:Unhandled group %d subfunction %d",(int)group,(int)sub);
00531                 break;
00532         case 0x02:   /* FST double real*/
00533                 FPU_FST_F64(addr);
00534                 break;
00535         case 0x03:      /* FSTP double real*/
00536                 FPU_FST_F64(addr);
00537                 FPU_FPOP();
00538                 break;
00539         case 0x04:      /* FRSTOR */
00540                 FPU_FRSTOR(addr);
00541                 break;
00542         case 0x06:      /* FSAVE */
00543                 FPU_FSAVE(addr);
00544                 break;
00545         case 0x07:   /*FNSTSW    NG DISAGREES ON THIS*/
00546                 FPU_SET_TOP(TOP);
00547                 mem_writew(addr,fpu.sw);
00548                 //seems to break all dos4gw games :)
00549                 break;
00550         default:
00551                 LOG(LOG_FPU,LOG_WARN)("ESC 5 EA:Unhandled group %d subfunction %d",(int)group,(int)sub);
00552         }
00553 }
00554 
00555 void FPU_ESC5_Normal(Bitu rm) {
00556         Bitu group=(rm >> 3) & 7;
00557         Bitu sub=(rm & 7);
00558         switch(group){
00559         case 0x00: /* FFREE STi */
00560                 fpu.tags[STV(sub)]=TAG_Empty;
00561                 break;
00562         case 0x01: /* FXCH STi*/
00563                 FPU_FXCH(TOP,STV(sub));
00564                 break;
00565         case 0x02: /* FST STi */
00566                 FPU_FST(TOP,STV(sub));
00567                 break;
00568         case 0x03:  /* FSTP STi*/
00569                 FPU_FST(TOP,STV(sub));
00570                 FPU_FPOP();
00571                 break;
00572         case 0x04:      /* FUCOM STi */
00573                 FPU_FUCOM(TOP,STV(sub));
00574                 break;
00575         case 0x05:      /*FUCOMP STi */
00576                 FPU_FUCOM(TOP,STV(sub));
00577                 FPU_FPOP();
00578                 break;
00579         default:
00580         LOG(LOG_FPU,LOG_WARN)("ESC 5:Unhandled group %d subfunction %d",(int)group,(int)sub);
00581         break;
00582         }
00583 }
00584 
00585 void FPU_ESC6_EA(Bitu rm,PhysPt addr) {
00586         /* 16 bit (word integer) operants */
00587         FPU_FLD_I16_EA(addr);
00588         EATREE(rm);
00589 }
00590 
00591 void FPU_ESC6_Normal(Bitu rm) {
00592         /* all P variants working only on registers */
00593         /* get top before switch and pop afterwards */
00594         Bitu group=(rm >> 3) & 7;
00595         Bitu sub=(rm & 7);
00596         switch(group){
00597         case 0x00:      /*FADDP STi,ST*/
00598                 FPU_FADD(STV(sub),TOP);
00599                 break;
00600         case 0x01:      /* FMULP STi,ST*/
00601                 FPU_FMUL(STV(sub),TOP);
00602                 break;
00603         case 0x02:  /* FCOMP5*/
00604                 FPU_FCOM(TOP,STV(sub));
00605                 break;  /* TODO IS THIS ALLRIGHT ????????? */
00606         case 0x03:  /*FCOMPP*/
00607                 if(sub != 1) {
00608                         LOG(LOG_FPU,LOG_WARN)("ESC 6:Unhandled group %d subfunction %d",(int)group,(int)sub);
00609                         return;
00610                 }
00611                 FPU_FCOM(TOP,STV(1));
00612                 FPU_FPOP(); /* extra pop at the bottom*/
00613                 break;
00614         case 0x04:  /* FSUBRP STi,ST*/
00615                 FPU_FSUBR(STV(sub),TOP);
00616                 break;
00617         case 0x05:  /* FSUBP  STi,ST*/
00618                 FPU_FSUB(STV(sub),TOP);
00619                 break;
00620         case 0x06:      /* FDIVRP STi,ST*/
00621                 FPU_FDIVR(STV(sub),TOP);
00622                 break;
00623         case 0x07:  /* FDIVP STi,ST*/
00624                 FPU_FDIV(STV(sub),TOP);
00625                 break;
00626         default:
00627                 break;
00628         }
00629         FPU_FPOP();             
00630 }
00631 
00632 
00633 void FPU_ESC7_EA(Bitu rm,PhysPt addr) {
00634         Bitu group=(rm >> 3) & 7;
00635         Bitu sub=(rm & 7);
00636         switch(group){
00637         case 0x00:  /* FILD Bit16s */
00638                 {
00639                         unsigned char old_TOP = TOP;
00640 
00641                         try {
00642                                 FPU_PREP_PUSH();
00643                                 FPU_FLD_I16(addr,TOP);
00644                         }
00645             catch (const GuestPageFaultException& pf) {
00646                                 (void)pf;
00647                                 TOP = old_TOP;
00648                                 throw;
00649                         }
00650                 }
00651                 break;
00652         case 0x01:
00653                 LOG(LOG_FPU,LOG_WARN)("ESC 7 EA:Unhandled group %d subfunction %d",(int)group,(int)sub);
00654                 break;
00655         case 0x02:   /* FIST Bit16s */
00656                 FPU_FST_I16(addr);
00657                 break;
00658         case 0x03:      /* FISTP Bit16s */
00659                 FPU_FST_I16(addr);
00660                 FPU_FPOP();
00661                 break;
00662         case 0x04:   /* FBLD packed BCD */
00663                 {
00664                         unsigned char old_TOP = TOP;
00665 
00666                         try {
00667                                 FPU_PREP_PUSH();
00668                                 FPU_FBLD(addr,TOP);
00669                         }
00670             catch (const GuestPageFaultException& pf) {
00671                                 (void)pf;
00672                                 TOP = old_TOP;
00673                                 throw;
00674                         }
00675                 }
00676                 break;
00677         case 0x05:  /* FILD Bit64s */
00678                 {
00679                         unsigned char old_TOP = TOP;
00680 
00681                         try {
00682                                 FPU_PREP_PUSH();
00683                                 FPU_FLD_I64(addr,TOP);
00684                         }
00685             catch (const GuestPageFaultException& pf) {
00686                                 (void)pf;
00687                                 TOP = old_TOP;
00688                                 throw;
00689                         }
00690                 }
00691                 break;
00692         case 0x06:      /* FBSTP packed BCD */
00693                 FPU_FBST(addr);
00694                 FPU_FPOP();
00695                 break;
00696         case 0x07:  /* FISTP Bit64s */
00697                 FPU_FST_I64(addr);
00698                 FPU_FPOP();
00699                 break;
00700         default:
00701                 LOG(LOG_FPU,LOG_WARN)("ESC 7 EA:Unhandled group %d subfunction %d",(int)group,(int)sub);
00702                 break;
00703         }
00704 }
00705 
00706 void FPU_ESC7_Normal(Bitu rm) {
00707         Bitu group=(rm >> 3) & 7;
00708         Bitu sub=(rm & 7);
00709         switch (group){
00710         case 0x00: /* FFREEP STi*/
00711                 fpu.tags[STV(sub)]=TAG_Empty;
00712                 FPU_FPOP();
00713                 break;
00714         case 0x01: /* FXCH STi*/
00715                 FPU_FXCH(TOP,STV(sub));
00716                 break;
00717         case 0x02:  /* FSTP STi*/
00718         case 0x03:  /* FSTP STi*/
00719                 FPU_FST(TOP,STV(sub));
00720                 FPU_FPOP();
00721                 break;
00722         case 0x04:
00723                 switch(sub){
00724                         case 0x00:     /* FNSTSW AX*/
00725                                 FPU_SET_TOP(TOP);
00726                                 reg_ax = fpu.sw;
00727                                 break;
00728                         default:
00729                                 LOG(LOG_FPU,LOG_WARN)("ESC 7:Unhandled group %d subfunction %d",(int)group,(int)sub);
00730                                 break;
00731                 }
00732                 break;
00733         case 0x05:              /* FUCOMIP STi */
00734                 FPU_FUCOMI(TOP,STV(sub));
00735                 FPU_FPOP();
00736                 break;
00737         case 0x06:              /* FCOMIP STi */
00738                 FPU_FCOMI(TOP,STV(sub));
00739                 FPU_FPOP();
00740                 break;
00741         default:
00742                 LOG(LOG_FPU,LOG_WARN)("ESC 7:Unhandled group %d subfunction %d",(int)group,(int)sub);
00743                 break;
00744         }
00745 }
00746 
00747 // test routine at startup to make sure our typedef struct bitfields
00748 // line up with the host's definition of a 32-bit single-precision
00749 // floating point value.
00750 void FPU_Selftest_32() {
00751         struct ftest {
00752                 const char*     name;
00753                 float           val;
00754                 int             exponent:15;
00755                 unsigned int    sign:1;
00756                 uint32_t        mantissa;
00757         };
00758         static const struct ftest test[] = {
00759                 // name                 // val          // exponent (no bias)           // sign         // 23-bit mantissa without 23rd implied bit (max 2^23-1 = 0x7FFFFF)
00760                 {"0.0f",                0.0f,           -FPU_Reg_32_exponent_bias,      0,              0x000000},      // IEEE standard way to encode zero
00761                 {"1.0f",                1.0f,           0,                              0,              0x000000},      // 1.0 x 2^0 = 1.0 x 1 = 1.0
00762                 {"2.0f",                2.0f,           1,                              0,              0x000000},      // 1.0 x 2^1 = 1.0 x 2 = 2.0
00763                 {"3.0f",                3.0f,           1,                              0,              0x400000},      // 1.5 x 2^1 = 1.5 x 2 = 3.0
00764                 {"4.0f",                4.0f,           2,                              0,              0x000000},      // 1.0 x 2^2 = 1.0 x 4 = 4.0
00765                 {"-1.0f",               -1.0f,          0,                              1,              0x000000},      // 1.0 x 2^0 = 1.0 x 1 = 1.0
00766                 {"-2.0f",               -2.0f,          1,                              1,              0x000000},      // 1.0 x 2^1 = 1.0 x 2 = 2.0
00767                 {"-3.0f",               -3.0f,          1,                              1,              0x400000},      // 1.5 x 2^1 = 1.5 x 2 = 3.0
00768                 {"-4.0f",               -4.0f,          2,                              1,              0x000000}       // 1.0 x 2^2 = 1.0 x 4 = 4.0
00769         };
00770         static const size_t tests = sizeof(test) / sizeof(test[0]);
00771         FPU_Reg_32 ft;
00772 
00773         if (sizeof(ft) < 4) {
00774                 LOG(LOG_FPU,LOG_WARN)("FPU32 sizeof(reg32) < 4 bytes");
00775                 return;
00776         }
00777         if (sizeof(float) != 4) {
00778                 LOG(LOG_FPU,LOG_WARN)("FPU32 sizeof(float) != 4 bytes your host is weird");
00779                 return;
00780         }
00781 
00782         // make sure bitfields line up
00783         ft.raw = 1UL << 31UL;
00784         if (ft.f.sign != 1 || ft.f.exponent != 0 || ft.f.mantissa != 0) {
00785                 LOG(LOG_FPU,LOG_WARN)("FPU32 bitfield test #1 failed");
00786                 return;
00787         }
00788         ft.raw = 1UL << 23UL;
00789         if (ft.f.sign != 0 || ft.f.exponent != 1 || ft.f.mantissa != 0) {
00790                 LOG(LOG_FPU,LOG_WARN)("FPU32 bitfield test #2 failed");
00791                 return;
00792         }
00793         ft.raw = 1UL << 0UL;
00794         if (ft.f.sign != 0 || ft.f.exponent != 0 || ft.f.mantissa != 1) {
00795                 LOG(LOG_FPU,LOG_WARN)("FPU32 bitfield test #3 failed");
00796                 return;
00797         }
00798 
00799         // carry out tests
00800         for (size_t t=0;t < tests;t++) {
00801                 ft.v = test[t].val; FPU_Reg_m_barrier();
00802                 if (((int)ft.f.exponent - FPU_Reg_32_exponent_bias) != test[t].exponent ||
00803                         ft.f.sign != test[t].sign || ft.f.mantissa != test[t].mantissa) {
00804                         LOG(LOG_FPU,LOG_WARN)("FPU32 selftest fail stage %s",test[t].name);
00805                         LOG(LOG_FPU,LOG_WARN)("  expected t.v = %.10f t.s=%u t.exp=%d t.mantissa=%u",
00806                                 test[t].val,
00807                                 test[t].sign,
00808                                 (int)test[t].exponent,
00809                                 (unsigned int)test[t].mantissa);
00810                         goto dump;
00811                 }
00812         }
00813 
00814         LOG(LOG_FPU,LOG_DEBUG)("FPU32 selftest passed");
00815         return;
00816 dump:
00817         LOG(LOG_FPU,LOG_WARN)("Result: t.v = %.10f t.s=%u t.exp=%d t.mantissa=%u",
00818                 ft.v,
00819                 ft.f.sign,
00820                 (int)ft.f.exponent - FPU_Reg_32_exponent_bias,
00821                 (unsigned int)ft.f.mantissa);
00822 }
00823 
00824 // test routine at startup to make sure our typedef struct bitfields
00825 // line up with the host's definition of a 64-bit double-precision
00826 // floating point value.
00827 void FPU_Selftest_64() {
00828         struct ftest {
00829                 const char*     name;
00830                 double          val;
00831                 int             exponent:15;
00832                 unsigned int    sign:1;
00833                 uint64_t        mantissa;
00834         };
00835         static const struct ftest test[] = {
00836                 // name                 // val          // exponent (no bias)           // sign         // 52-bit mantissa without 52rd implied bit (max 2^52-1 = 0x1FFFFFFFFFFFFF)
00837                 {"0.0d",                0.0,            -FPU_Reg_64_exponent_bias,      0,              0x0000000000000ULL},    // IEEE standard way to encode zero
00838                 {"1.0d",                1.0,            0,                              0,              0x0000000000000ULL},    // 1.0 x 2^0 = 1.0 x 1 = 1.0
00839                 {"2.0d",                2.0,            1,                              0,              0x0000000000000ULL},    // 1.0 x 2^1 = 1.0 x 2 = 2.0
00840                 {"3.0d",                3.0,            1,                              0,              0x8000000000000ULL},    // 1.5 x 2^1 = 1.5 x 2 = 3.0
00841                 {"4.0d",                4.0,            2,                              0,              0x0000000000000ULL},    // 1.0 x 2^2 = 1.0 x 4 = 4.0
00842                 {"-1.0d",               -1.0,           0,                              1,              0x0000000000000ULL},    // 1.0 x 2^0 = 1.0 x 1 = 1.0
00843                 {"-2.0d",               -2.0,           1,                              1,              0x0000000000000ULL},    // 1.0 x 2^1 = 1.0 x 2 = 2.0
00844                 {"-3.0d",               -3.0,           1,                              1,              0x8000000000000ULL},    // 1.5 x 2^1 = 1.5 x 2 = 3.0
00845                 {"-4.0d",               -4.0,           2,                              1,              0x0000000000000ULL}     // 1.0 x 2^2 = 1.0 x 4 = 4.0
00846         };
00847         static const size_t tests = sizeof(test) / sizeof(test[0]);
00848         FPU_Reg_64 ft;
00849 
00850         if (sizeof(ft) < 8) {
00851                 LOG(LOG_FPU,LOG_WARN)("FPU64 sizeof(reg64) < 8 bytes");
00852                 return;
00853         }
00854         if (sizeof(double) != 8) {
00855                 LOG(LOG_FPU,LOG_WARN)("FPU64 sizeof(float) != 8 bytes your host is weird");
00856                 return;
00857         }
00858 
00859         // make sure bitfields line up
00860         ft.raw = 1ULL << 63ULL;
00861         if (ft.f.sign != 1 || ft.f.exponent != 0 || ft.f.mantissa != 0) {
00862                 LOG(LOG_FPU,LOG_WARN)("FPU64 bitfield test #1 failed");
00863                 return;
00864         }
00865         ft.raw = 1ULL << 52ULL;
00866         if (ft.f.sign != 0 || ft.f.exponent != 1 || ft.f.mantissa != 0) {
00867                 LOG(LOG_FPU,LOG_WARN)("FPU64 bitfield test #2 failed");
00868                 return;
00869         }
00870         ft.raw = 1ULL << 0ULL;
00871         if (ft.f.sign != 0 || ft.f.exponent != 0 || ft.f.mantissa != 1) {
00872                 LOG(LOG_FPU,LOG_WARN)("FPU64 bitfield test #3 failed");
00873                 return;
00874         }
00875 
00876         for (size_t t=0;t < tests;t++) {
00877                 ft.v = test[t].val; FPU_Reg_m_barrier();
00878                 if (((int)ft.f.exponent - FPU_Reg_64_exponent_bias) != test[t].exponent ||
00879                         ft.f.sign != test[t].sign || ft.f.mantissa != test[t].mantissa) {
00880                         LOG(LOG_FPU,LOG_WARN)("FPU64 selftest fail stage %s",test[t].name);
00881                         LOG(LOG_FPU,LOG_WARN)("  expected t.v = %.10f t.s=%u t.exp=%d t.mantissa=%llu (0x%llx)",
00882                                 test[t].val,
00883                                 test[t].sign,
00884                                 (int)test[t].exponent,
00885                                 (unsigned long long)test[t].mantissa,
00886                                 (unsigned long long)test[t].mantissa);
00887                         goto dump;
00888                 }
00889         }
00890 
00891         LOG(LOG_FPU,LOG_DEBUG)("FPU64 selftest passed");
00892         return;
00893 dump:
00894         LOG(LOG_FPU,LOG_WARN)("Result: t.v = %.10f t.s=%u t.exp=%d t.mantissa=%llu (0x%llx)",
00895                 ft.v,
00896                 (int)ft.f.sign,
00897                 (int)ft.f.exponent - FPU_Reg_64_exponent_bias,
00898                 (unsigned long long)ft.f.mantissa,
00899                 (unsigned long long)ft.f.mantissa);
00900 }
00901 
00902 // test routine at startup to make sure our typedef struct bitfields
00903 // line up with the host's definition of a 80-bit extended-precision
00904 // floating point value (if the host is i686, x86_64, or any other
00905 // host with the same definition of long double).
00906 void FPU_Selftest_80() {
00907 #if defined(HAS_LONG_DOUBLE)
00908         // we're assuming "long double" means the Intel 80x87 extended precision format, which is true when using
00909         // GCC on Linux i686 and x86_64 hosts.
00910         //
00911         // I understand that other platforms (PowerPC, Sparc, etc) might have other ideas on what makes "long double"
00912         // and I also understand Microsoft Visual C++ treats long double the same as double. We will disable this
00913         // test with #ifdefs when compiling for platforms where long double doesn't mean 80-bit extended precision.
00914         struct ftest {
00915                 const char*     name;
00916                 long double     val;
00917                 int             exponent:15;
00918                 unsigned int    sign:1;
00919                 uint64_t        mantissa;
00920         };
00921         static const struct ftest test[] = {
00922                 // name                 // val          // exponent (no bias)           // sign         // 64-bit mantissa WITH whole integer bit #63
00923                 {"0.0L",                0.0,            -FPU_Reg_80_exponent_bias,      0,              0x0000000000000000ULL}, // IEEE standard way to encode zero
00924                 {"1.0L",                1.0,            0,                              0,              0x8000000000000000ULL}, // 1.0 x 2^0 = 1.0 x 1 = 1.0
00925                 {"2.0L",                2.0,            1,                              0,              0x8000000000000000ULL}, // 1.0 x 2^1 = 1.0 x 2 = 2.0
00926                 {"3.0L",                3.0,            1,                              0,              0xC000000000000000ULL}, // 1.5 x 2^1 = 1.5 x 2 = 3.0
00927                 {"4.0L",                4.0,            2,                              0,              0x8000000000000000ULL}, // 1.0 x 2^2 = 1.0 x 4 = 4.0
00928                 {"-1.0L",               -1.0,           0,                              1,              0x8000000000000000ULL}, // 1.0 x 2^0 = 1.0 x 1 = 1.0
00929                 {"-2.0L",               -2.0,           1,                              1,              0x8000000000000000ULL}, // 1.0 x 2^1 = 1.0 x 2 = 2.0
00930                 {"-3.0L",               -3.0,           1,                              1,              0xC000000000000000ULL}, // 1.5 x 2^1 = 1.5 x 2 = 3.0
00931                 {"-4.0L",               -4.0,           2,                              1,              0x8000000000000000ULL}  // 1.0 x 2^2 = 1.0 x 4 = 4.0
00932         };
00933         static const size_t tests = sizeof(test) / sizeof(test[0]);
00934 #endif
00935         FPU_Reg_80 ft;
00936 
00937         if (sizeof(ft) < 10) {
00938                 LOG(LOG_FPU,LOG_WARN)("FPU80 sizeof(reg80) < 10 bytes");
00939                 return;
00940         }
00941 #if defined(HAS_LONG_DOUBLE)
00942         if (sizeof(long double) == sizeof(double)) {
00943                 LOG(LOG_FPU,LOG_WARN)("FPU80 sizeof(long double) == sizeof(double) so your compiler just makes it an alias. skipping tests. please recompile with proper config.");
00944                 return;
00945         }
00946         else if (sizeof(long double) < 10 || sizeof(long double) > 16) {
00947                 // NTS: We can't assume 10 bytes. GCC on i686 makes long double 12 or 16 bytes long for alignment
00948                 //      even though only 80 bits (10 bytes) are used.
00949                 LOG(LOG_FPU,LOG_WARN)("FPU80 sizeof(float) < 10 bytes your host is weird");
00950                 return;
00951         }
00952 #endif
00953 
00954         // make sure bitfields line up
00955         ft.raw.l = 0;
00956         ft.raw.h = 1U << 15U;
00957         if (ft.f.sign != 1 || ft.f.exponent != 0 || ft.f.mantissa != 0) {
00958                 LOG(LOG_FPU,LOG_WARN)("FPU80 bitfield test #1 failed. h=%04x l=%016llx",(unsigned int)ft.raw.h,(unsigned long long)ft.raw.l);
00959                 return;
00960         }
00961         ft.raw.l = 0;
00962         ft.raw.h = 1U << 0U;
00963         if (ft.f.sign != 0 || ft.f.exponent != 1 || ft.f.mantissa != 0) {
00964                 LOG(LOG_FPU,LOG_WARN)("FPU80 bitfield test #2 failed. h=%04x l=%016llx",(unsigned int)ft.raw.h,(unsigned long long)ft.raw.l);
00965                 return;
00966         }
00967         ft.raw.l = 1ULL << 0ULL;
00968         ft.raw.h = 0;
00969         if (ft.f.sign != 0 || ft.f.exponent != 0 || ft.f.mantissa != 1) {
00970                 LOG(LOG_FPU,LOG_WARN)("FPU80 bitfield test #3 failed. h=%04x l=%016llx",(unsigned int)ft.raw.h,(unsigned long long)ft.raw.l);
00971                 return;
00972         }
00973 
00974 #if defined(HAS_LONG_DOUBLE)
00975         for (size_t t=0;t < tests;t++) {
00976                 ft.v = test[t].val; FPU_Reg_m_barrier();
00977                 if (((int)ft.f.exponent - FPU_Reg_80_exponent_bias) != test[t].exponent ||
00978                         ft.f.sign != test[t].sign || ft.f.mantissa != test[t].mantissa) {
00979                         LOG(LOG_FPU,LOG_WARN)("FPU80 selftest fail stage %s",test[t].name);
00980                         LOG(LOG_FPU,LOG_WARN)("  expected t.v = %.10Lf t.s=%u t.exp=%d t.mantissa=%llu (0x%llx)",
00981                                 test[t].val,
00982                                 test[t].sign,
00983                                 (int)test[t].exponent,
00984                                 (unsigned long long)test[t].mantissa,
00985                                 (unsigned long long)test[t].mantissa);
00986                         goto dump;
00987                 }
00988         }
00989 
00990         LOG(LOG_FPU,LOG_DEBUG)("FPU80 selftest passed");
00991         return;
00992 dump:
00993         LOG(LOG_FPU,LOG_WARN)("Result: t.v = %.10Lf t.s=%u t.exp=%d t.mantissa=%llu (0x%llx)",
00994                 ft.v,
00995                 (int)ft.f.sign,
00996                 (int)ft.f.exponent - FPU_Reg_64_exponent_bias,
00997                 (unsigned long long)ft.f.mantissa,
00998                 (unsigned long long)ft.f.mantissa);
00999 #else
01000         LOG(LOG_FPU,LOG_DEBUG)("FPU80 selftest skipped, compiler does not have long double as 80-bit IEEE");
01001 #endif
01002 }
01003 
01004 void FPU_Selftest() {
01005         FPU_Reg freg;
01006 
01007         /* byte order test */
01008         freg.ll = 0x0123456789ABCDEFULL;
01009 #ifndef WORDS_BIGENDIAN
01010         if (freg.l.lower != 0x89ABCDEFUL || freg.l.upper != 0x01234567UL) {
01011                 LOG(LOG_FPU,LOG_WARN)("FPU_Reg field order is wrong. ll=0x%16llx l=0x%08lx h=0x%08lx",
01012                         (unsigned long long)freg.ll,    (unsigned long)freg.l.lower,    (unsigned long)freg.l.upper);
01013         }
01014 #else
01015         if (freg.l.upper != 0x89ABCDEFUL || freg.l.lower != 0x01234567UL) {
01016                 LOG(LOG_FPU,LOG_WARN)("FPU_Reg field order is wrong. ll=0x%16llx l=0x%08lx h=0x%08lx",
01017                         (unsigned long long)freg.ll,    (unsigned long)freg.l.lower,    (unsigned long)freg.l.upper);
01018         }
01019 #endif
01020 
01021 #if C_FPU_X86
01022     LOG(LOG_FPU,LOG_NORMAL)("FPU core: x86 FPU");
01023 #elif defined(HAS_LONG_DOUBLE)
01024     LOG(LOG_FPU,LOG_NORMAL)("FPU core: long double FPU");
01025 #else
01026     LOG(LOG_FPU,LOG_NORMAL)("FPU core: double FPU (caution: possible precision errors)");
01027 #endif
01028 
01029         FPU_Selftest_32();
01030         FPU_Selftest_64();
01031         FPU_Selftest_80();
01032 }
01033 
01034 void FPU_Init() {
01035         LOG(LOG_MISC,LOG_DEBUG)("Initializing FPU");
01036 
01037         FPU_Selftest();
01038         FPU_FINIT();
01039 }
01040 
01041 #endif
01042 
01043 //save state support
01044 namespace
01045 {
01046 class SerializeFpu : public SerializeGlobalPOD
01047 {
01048 public:
01049     SerializeFpu() : SerializeGlobalPOD("FPU")
01050     {
01051         registerPOD(fpu);
01052     }
01053 } dummy;
01054 }