/[gxemul]/trunk/src/cpus/cpu_mips_coproc.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Diff of /trunk/src/cpus/cpu_mips_coproc.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 19 by dpavlin, Mon Oct 8 16:19:11 2007 UTC revision 20 by dpavlin, Mon Oct 8 16:19:23 2007 UTC
# Line 25  Line 25 
25   *  SUCH DAMAGE.   *  SUCH DAMAGE.
26   *   *
27   *   *
28   *  $Id: cpu_mips_coproc.c,v 1.3 2005/10/26 14:37:03 debug Exp $   *  $Id: cpu_mips_coproc.c,v 1.8 2005/11/23 02:17:41 debug Exp $
29   *   *
30   *  Emulation of MIPS coprocessors.   *  Emulation of MIPS coprocessors.
31   */   */
# Line 40  Line 40 
40  #include "cpu.h"  #include "cpu.h"
41  #include "cpu_mips.h"  #include "cpu_mips.h"
42  #include "emul.h"  #include "emul.h"
43    #include "float_emul.h"
44  #include "machine.h"  #include "machine.h"
45  #include "memory.h"  #include "memory.h"
46  #include "mips_cpu_types.h"  #include "mips_cpu_types.h"
# Line 414  struct mips_coproc *mips_coproc_new(stru Line 415  struct mips_coproc *mips_coproc_new(stru
415    
416          if (coproc_nr == 0) {          if (coproc_nr == 0) {
417                  c->nr_of_tlbs = cpu->cd.mips.cpu_type.nr_of_tlb_entries;                  c->nr_of_tlbs = cpu->cd.mips.cpu_type.nr_of_tlb_entries;
418                  c->tlbs = malloc(c->nr_of_tlbs * sizeof(struct mips_tlb));                  c->tlbs = zeroed_alloc(c->nr_of_tlbs * sizeof(struct mips_tlb));
                 if (c->tlbs == NULL) {  
                         fprintf(stderr, "mips_coproc_new(): out of memory\n");  
                         exit(1);  
                 }  
419    
420                  /*                  /*
421                   *  Start with nothing in the status register. This makes sure                   *  Start with nothing in the status register. This makes sure
# Line 798  void mips_invalidate_translation_caches_ Line 795  void mips_invalidate_translation_caches_
795                                  int psize = 12;                                  int psize = 12;
796                                  int or_pmask = 0x1fff;                                  int or_pmask = 0x1fff;
797                                  int phys_shift = 12;                                  int phys_shift = 12;
798                                    int tmp_pmask = cpu->cd.mips.coproc[0]->
799                                        tlbs[i].mask | or_pmask;
800    
801                                  if (cpu->cd.mips.cpu_type.rev == MIPS_R4100) {                                  if (cpu->cd.mips.cpu_type.rev == MIPS_R4100) {
802                                          or_pmask = 0x7ff;                                          or_pmask = 0x7ff;
803                                          phys_shift = 10;                                          phys_shift = 10;
804                                  }                                  }
805                                  switch (cpu->cd.mips.coproc[0]->                                  switch (tmp_pmask) {
                                     tlbs[i].mask | or_pmask) {  
806                                  case 0x000007ff:        psize = 10; break;                                  case 0x000007ff:        psize = 10; break;
807                                  case 0x00001fff:        psize = 12; break;                                  case 0x00001fff:        psize = 12; break;
808                                  case 0x00007fff:        psize = 14; break;                                  case 0x00007fff:        psize = 14; break;
# Line 816  void mips_invalidate_translation_caches_ Line 814  void mips_invalidate_translation_caches_
814                                  case 0x07ffffff:        psize = 26; break;                                  case 0x07ffffff:        psize = 26; break;
815                                  default:                                  default:
816                                          printf("invalidate_translation_caches"                                          printf("invalidate_translation_caches"
817                                              "_paddr(): bad pagemask?\n");                                              "_paddr(): bad pagemask: 0x%x\n",
818                                                (int)tmp_pmask);
819                                  }                                  }
820                                  tlb_paddr0 = (cpu->cd.mips.coproc[0]->tlbs[i].                                  tlb_paddr0 = (cpu->cd.mips.coproc[0]->tlbs[i].
821                                      lo0 & ENTRYLO_PFN_MASK)>>ENTRYLO_PFN_SHIFT;                                      lo0 & ENTRYLO_PFN_MASK)>>ENTRYLO_PFN_SHIFT;
# Line 1017  void coproc_register_read(struct cpu *cp Line 1016  void coproc_register_read(struct cpu *cp
1016          if (cp->coproc_nr==0 && reg_nr==COP0_WIRED)     unimpl = 0;          if (cp->coproc_nr==0 && reg_nr==COP0_WIRED)     unimpl = 0;
1017          if (cp->coproc_nr==0 && reg_nr==COP0_BADVADDR)  unimpl = 0;          if (cp->coproc_nr==0 && reg_nr==COP0_BADVADDR)  unimpl = 0;
1018          if (cp->coproc_nr==0 && reg_nr==COP0_COUNT) {          if (cp->coproc_nr==0 && reg_nr==COP0_COUNT) {
1019    #if 0
1020                  /*                  /*
1021                   *  This speeds up delay-loops that just read the count                   *  This speeds up delay-loops that just read the count
1022                   *  register until it has reached a certain value. (Only for                   *  register until it has reached a certain value. (Only for
# Line 1037  void coproc_register_read(struct cpu *cp Line 1037  void coproc_register_read(struct cpu *cp
1037                          cp->reg[COP0_COUNT] = (int64_t)                          cp->reg[COP0_COUNT] = (int64_t)
1038                              (int32_t)(cp->reg[COP0_COUNT] + increase);                              (int32_t)(cp->reg[COP0_COUNT] + increase);
1039                  }                  }
1040    #endif
1041                  unimpl = 0;                  unimpl = 0;
1042          }          }
1043          if (cp->coproc_nr==0 && reg_nr==COP0_ENTRYHI)   unimpl = 0;          if (cp->coproc_nr==0 && reg_nr==COP0_ENTRYHI)   unimpl = 0;
# Line 1426  void coproc_register_write(struct cpu *c Line 1426  void coproc_register_write(struct cpu *c
1426   *   *
1427   *  TODO:  Move this to some other file?   *  TODO:  Move this to some other file?
1428   */   */
1429    static int mips_fmt_to_ieee_fmt[32] = {
1430            0, 0, 0, 0,  0, 0, 0, 0,
1431            0, 0, 0, 0,  0, 0, 0, 0,
1432            IEEE_FMT_S, IEEE_FMT_D, 0, 0,
1433            IEEE_FMT_W, IEEE_FMT_L, /* PS (Paired Single) */ 0, 0,
1434            0, 0, 0, 0,  0, 0, 0, 0  };
1435    
1436    /*  MIPS floating point types:  */
1437  #define FMT_S           16  #define FMT_S           16
1438  #define FMT_D           17  #define FMT_D           17
1439  #define FMT_W           20  #define FMT_W           20
# Line 1442  void coproc_register_write(struct cpu *c Line 1450  void coproc_register_write(struct cpu *c
1450  #define FPU_OP_C        8  #define FPU_OP_C        8
1451  #define FPU_OP_ABS      9  #define FPU_OP_ABS      9
1452  #define FPU_OP_NEG      10  #define FPU_OP_NEG      10
1453  /*  TODO: CEIL.L, CEIL.W, FLOOR.L, FLOOR.W, RECIP, ROUND.L, ROUND.W,  /*  TODO: CEIL.L, CEIL.W, FLOOR.L, FLOOR.W, RECIP, ROUND.L, ROUND.W, RSQRT  */
  RSQRT  */  
   
   
 struct internal_float_value {  
         double  f;  
         int     nan;  
 };  
   
   
 /*  
  *  fpu_interpret_float_value():  
  *  
  *  Interprets a float value from binary IEEE format into an  
  *  internal_float_value struct.  
  */  
 static void fpu_interpret_float_value(uint64_t reg,  
         struct internal_float_value *fvp, int fmt)  
 {  
         int n_frac = 0, n_exp = 0;  
         int i, nan, sign = 0, exponent;  
         double fraction;  
   
         memset(fvp, 0, sizeof(struct internal_float_value));  
   
         /*  n_frac and n_exp:  */  
         switch (fmt) {  
         case FMT_S:     n_frac = 23; n_exp = 8; break;  
         case FMT_W:     n_frac = 31; n_exp = 0; break;  
         case FMT_D:     n_frac = 52; n_exp = 11; break;  
         case FMT_L:     n_frac = 63; n_exp = 0; break;  
         default:  
                 fatal("fpu_interpret_float_value(): "  
                     "unimplemented format %i\n", fmt);  
         }  
   
         /*  exponent:  */  
         exponent = 0;  
         switch (fmt) {  
         case FMT_W:  
                 reg &= 0xffffffffULL;  
         case FMT_L:  
                 break;  
         case FMT_S:  
                 reg &= 0xffffffffULL;  
         case FMT_D:  
                 exponent = (reg >> n_frac) & ((1 << n_exp) - 1);  
                 exponent -= (1 << (n_exp-1)) - 1;  
                 break;  
         default:  
                 fatal("fpu_interpret_float_value(): unimplemented "  
                     "format %i\n", fmt);  
         }  
   
         /*  nan:  */  
         nan = 0;  
         switch (fmt) {  
         case FMT_S:  
                 if (reg == 0x7fffffffULL || reg == 0x7fbfffffULL)  
                         nan = 1;  
                 break;  
         case FMT_D:  
                 if (reg == 0x7fffffffffffffffULL ||  
                     reg == 0x7ff7ffffffffffffULL)  
                         nan = 1;  
                 break;  
         }  
   
         if (nan) {  
                 fvp->f = 1.0;  
                 goto no_reasonable_result;  
         }  
   
         /*  fraction:  */  
         fraction = 0.0;  
         switch (fmt) {  
         case FMT_W:  
                 {  
                         int32_t r_int = reg;  
                         fraction = r_int;  
                 }  
                 break;  
         case FMT_L:  
                 {  
                         int64_t r_int = reg;  
                         fraction = r_int;  
                 }  
                 break;  
         case FMT_S:  
         case FMT_D:  
                 /*  sign:  */  
                 sign = (reg >> 31) & 1;  
                 if (fmt == FMT_D)  
                         sign = (reg >> 63) & 1;  
   
                 fraction = 0.0;  
                 for (i=0; i<n_frac; i++) {  
                         int bit = (reg >> i) & 1;  
                         fraction /= 2.0;  
                         if (bit)  
                                 fraction += 1.0;  
                 }  
                 /*  Add implicit bit 0:  */  
                 fraction = (fraction / 2.0) + 1.0;  
                 break;  
         default:  
                 fatal("fpu_interpret_float_value(): "  
                     "unimplemented format %i\n", fmt);  
         }  
   
         /*  form the value:  */  
         fvp->f = fraction;  
   
         /*  fatal("load  reg=%016llx sign=%i exponent=%i fraction=%f ",  
             (long long)reg, sign, exponent, fraction);  */  
   
         /*  TODO: this is awful for exponents of large magnitude.  */  
         if (exponent > 0) {  
                 /*  
                  *  NOTE / TODO:  
                  *  
                  *  This is an ulgy workaround on Alpha, where it seems that  
                  *  multiplying by 2, 1024 times causes a floating point  
                  *  exception. (Triggered by running for example NetBSD/pmax  
                  *  2.0 on an Alpha.)  
                  */  
                 if (exponent == 1024)  
                         exponent = 1023;  
   
                 while (exponent-- > 0)  
                         fvp->f *= 2.0;  
         } else if (exponent < 0) {  
                 while (exponent++ < 0)  
                         fvp->f /= 2.0;  
         }  
   
         if (sign)  
                 fvp->f = -fvp->f;  
   
 no_reasonable_result:  
         fvp->nan = nan;  
   
         /*  fatal("f = %f\n", fvp->f);  */  
 }  
1454    
1455    
1456  /*  /*
# Line 1596  no_reasonable_result: Line 1461  no_reasonable_result:
1461  static void fpu_store_float_value(struct mips_coproc *cp, int fd,  static void fpu_store_float_value(struct mips_coproc *cp, int fd,
1462          double nf, int fmt, int nan)          double nf, int fmt, int nan)
1463  {  {
1464          int n_frac = 0, n_exp = 0, signofs=0;          int ieee_fmt = mips_fmt_to_ieee_fmt[fmt];
1465          int i, exponent;          uint64_t r = ieee_store_float_value(nf, ieee_fmt, nan);
         uint64_t r = 0, r2;  
         int64_t r3;  
   
         /*  n_frac and n_exp:  */  
         switch (fmt) {  
         case FMT_S:     n_frac = 23; n_exp = 8; signofs = 31; break;  
         case FMT_W:     n_frac = 31; n_exp = 0; signofs = 31; break;  
         case FMT_D:     n_frac = 52; n_exp = 11; signofs = 63; break;  
         case FMT_L:     n_frac = 63; n_exp = 0; signofs = 63; break;  
         default:  
                 fatal("fpu_store_float_value(): unimplemented format"  
                     " %i\n", fmt);  
         }  
   
         if ((fmt == FMT_S || fmt == FMT_D) && nan)  
                 goto store_nan;  
   
         /*  fraction:  */  
         switch (fmt) {  
         case FMT_W:  
         case FMT_L:  
                 /*  
                  *  This causes an implicit conversion of double to integer.  
                  *  If nf < 0.0, then r2 will begin with a sequence of binary  
                  *  1's, which is ok.  
                  */  
                 r3 = nf;  
                 r2 = r3;  
                 r |= r2;  
   
                 if (fmt == FMT_W)  
                         r &= 0xffffffffULL;  
                 break;  
         case FMT_S:  
         case FMT_D:  
                 /*  fatal("store f=%f ", nf);  */  
   
                 /*  sign bit:  */  
                 if (nf < 0.0) {  
                         r |= ((uint64_t)1 << signofs);  
                         nf = -nf;  
                 }  
   
                 /*  
                  *  How to convert back from double to exponent + fraction:  
                  *  We want fraction to be 1.xxx, that is  
                  *  1.0 <= fraction < 2.0  
                  *  
                  *  This method is very slow but should work:  
                  */  
                 exponent = 0;  
                 while (nf < 1.0 && exponent > -1023) {  
                         nf *= 2.0;  
                         exponent --;  
                 }  
                 while (nf >= 2.0 && exponent < 1023) {  
                         nf /= 2.0;  
                         exponent ++;  
                 }  
   
                 /*  Here:   1.0 <= nf < 2.0  */  
                 /*  fatal(" nf=%f", nf);  */  
                 nf -= 1.0;      /*  remove implicit first bit  */  
                 for (i=n_frac-1; i>=0; i--) {  
                         nf *= 2.0;  
                         if (nf >= 1.0) {  
                                 r |= ((uint64_t)1 << i);  
                                 nf -= 1.0;  
                         }  
                         /*  printf("\n i=%2i r=%016llx\n", i, (long long)r);  */  
                 }  
   
                 /*  Insert the exponent into the resulting word:  */  
                 /*  (First bias, then make sure it's within range)  */  
                 exponent += (((uint64_t)1 << (n_exp-1)) - 1);  
                 if (exponent < 0)  
                         exponent = 0;  
                 if (exponent >= ((int64_t)1 << n_exp))  
                         exponent = ((int64_t)1 << n_exp) - 1;  
                 r |= (uint64_t)exponent << n_frac;  
   
                 /*  Special case for 0.0:  */  
                 if (exponent == 0)  
                         r = 0;  
   
                 /*  fatal(" exp=%i, r = %016llx\n", exponent, (long long)r);  */  
   
                 break;  
         default:  
                 /*  TODO  */  
                 fatal("fpu_store_float_value(): unimplemented format "  
                     "%i\n", fmt);  
         }  
   
 store_nan:  
         if (nan) {  
                 if (fmt == FMT_S)  
                         r = 0x7fffffffULL;  
                 else if (fmt == FMT_D)  
                         r = 0x7fffffffffffffffULL;  
                 else  
                         r = 0x7fffffffULL;  
         }  
1466    
1467          /*          /*
1468           *  TODO:  this is for 32-bit mode. It has to be updated later           *  TODO: This is for 32-bit mode. It has to be updated later
1469           *              for 64-bit coprocessor stuff.           *        for 64-bit coprocessor functionality!
1470           */           */
1471          if (fmt == FMT_D || fmt == FMT_L) {          if (fmt == FMT_D || fmt == FMT_L) {
1472                  cp->reg[fd] = r & 0xffffffffULL;                  cp->reg[fd] = r & 0xffffffffULL;
# Line 1726  store_nan: Line 1488  store_nan:
1488  /*  /*
1489   *  fpu_op():   *  fpu_op():
1490   *   *
1491   *  Perform a floating-point operation.  For those of fs and ft   *  Perform a floating-point operation. For those of fs and ft that are >= 0,
1492   *  that are >= 0, those numbers are interpreted into local   *  those numbers are interpreted into local variables.
  *  variables.  
1493   *   *
1494   *  Only FPU_OP_C (compare) returns anything of interest, 1 for   *  Only FPU_OP_C (compare) returns anything of interest, 1 for true, 0 for
1495   *  true, 0 for false.   *  false.
1496   */   */
1497  static int fpu_op(struct cpu *cpu, struct mips_coproc *cp, int op, int fmt,  static int fpu_op(struct cpu *cpu, struct mips_coproc *cp, int op, int fmt,
1498          int ft, int fs, int fd, int cond, int output_fmt)          int ft, int fs, int fd, int cond, int output_fmt)
1499  {  {
1500          /*  Potentially two input registers, fs and ft  */          /*  Potentially two input registers, fs and ft  */
1501          struct internal_float_value float_value[2];          struct ieee_float_value float_value[2];
1502          int unordered, nan;          int unordered, nan, ieee_fmt = mips_fmt_to_ieee_fmt[fmt];
1503          uint64_t fs_v = 0;          uint64_t fs_v = 0;
1504          double nf;          double nf;
1505    
# Line 1749  static int fpu_op(struct cpu *cpu, struc Line 1510  static int fpu_op(struct cpu *cpu, struc
1510                  if (fmt == FMT_D || fmt == FMT_L)                  if (fmt == FMT_D || fmt == FMT_L)
1511                          fs_v = (fs_v & 0xffffffffULL) +                          fs_v = (fs_v & 0xffffffffULL) +
1512                              (cp->reg[(fs + 1) & 31] << 32);                              (cp->reg[(fs + 1) & 31] << 32);
1513                  fpu_interpret_float_value(fs_v, &float_value[0], fmt);                  ieee_interpret_float_value(fs_v, &float_value[0], ieee_fmt);
1514          }          }
1515          if (ft >= 0) {          if (ft >= 0) {
1516                  uint64_t v = cp->reg[ft];                  uint64_t v = cp->reg[ft];
# Line 1758  static int fpu_op(struct cpu *cpu, struc Line 1519  static int fpu_op(struct cpu *cpu, struc
1519                  if (fmt == FMT_D || fmt == FMT_L)                  if (fmt == FMT_D || fmt == FMT_L)
1520                          v = (v & 0xffffffffULL) +                          v = (v & 0xffffffffULL) +
1521                              (cp->reg[(ft + 1) & 31] << 32);                              (cp->reg[(ft + 1) & 31] << 32);
1522                  fpu_interpret_float_value(v, &float_value[1], fmt);                  ieee_interpret_float_value(v, &float_value[1], ieee_fmt);
1523          }          }
1524    
1525          switch (op) {          switch (op) {

Legend:
Removed from v.19  
changed lines
  Added in v.20

  ViewVC Help
Powered by ViewVC 1.1.26