// Copyright Notice
// ================
// BOCHS is Copyright 1994-1999 by Kevin P. Lawton.
//
// BOCHS is commercial software.
//
// For more information, read the file 'LICENSE' included in the bochs
// distribution.  If you don't have access to this file, or have questions
// regarding the licensing policy, the author may be contacted via:
//
//     US Mail:  Kevin Lawton
//               439 Marrett Rd.
//               Lexington, MA 02421-7714
//
//     EMail:    bochs@world.std.com




#include "bochs.h"



//
// i440FX Support
//


#if BX_USE_PCI_SMF
bx_pci_c bx_pci;
#define this (&bx_pci)
#endif


bx_pci_c::bx_pci_c(void)
{
}

bx_pci_c::~bx_pci_c(void)
{
}


  void
bx_pci_c::init(bx_devices_c *d)
{
  // called once when bochs initializes

  BX_PCI_THIS devices = d;

  for (unsigned i=0x0CFC; i<=0x0CFF; i++) {
    d->register_io_read_handler(this, read_handler, i, "i440FX");
    }

  d->register_io_write_handler(this, write_handler, 0x0CF8, "i440FX");
  for (unsigned i=0x0CFC; i<=0x0CFF; i++) {
    d->register_io_write_handler(this, write_handler, i, "i440FX");
    }

  // should this go into ::reset() ???
  if (bx_options.i440FXSupport) {
    for (unsigned i=0; i<256; i++)
      BX_PCI_THIS s.i440fx.array[i] = 0x0;
    }
}

  void
bx_pci_c::reset(void)
{
  // upon RESET
}



  // static IO port read callback handler
  // redirects to non-static class handler to avoid virtual functions

  Bit32u
bx_pci_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
{
#if !BX_USE_PCI_SMF
  bx_pci_c *class_ptr = (bx_pci_c *) this_ptr;

  return( class_ptr->read(address, io_len) );
}


  Bit32u
bx_pci_c::read(Bit32u address, unsigned io_len)
{
#else
  UNUSED(this_ptr);
#endif // !BX_USE_PCI_SMF

  switch (address) {
    case 0x0CFC:
    case 0x0CFD:
    case 0x0CFE:
    case 0x0CFF:
      {
      Bit32u idx440fx, val440fx, retMask;
      idx440fx = BX_PCI_THIS s.i440fx.confAddr & 0x00FC;
      val440fx = (BX_PCI_THIS s.i440fx.array[idx440fx] >> ((address & 0x3)*8) );

      switch (io_len) {
        case 1:
          retMask = 0xFF; break;
        case 2:
          retMask = 0xFFFF; break;
        case 4:
          retMask = 0xFFFFFFFF; break;
        default:
          retMask = 0xFFFFFFFF; break;
        }
      bx_printf("440FX IO read from port: %04x, len: %02x, data: %04x\n",
                address, io_len, (val440fx & retMask));
      return (val440fx & retMask);
      }
    }

  bx_panic("pci: unsupported IO read to port 0x%x\n",
           (unsigned) address);
  return(0xffffffff);
}


  // static IO port write callback handler
  // redirects to non-static class handler to avoid virtual functions

  void
bx_pci_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
{
#if !BX_USE_PCI_SMF
  bx_pci_c *class_ptr = (bx_pci_c *) this_ptr;

  class_ptr->write(address, value, io_len);
}

  void
bx_pci_c::write(Bit32u address, Bit32u value, unsigned io_len)
{
#else
  UNUSED(this_ptr);
#endif // !BX_USE_PCI_SMF

  switch (address) {
    case 0xCF8:
      BX_PCI_THIS s.i440fx.confAddr = value;
      bx_printf("440FX IO write to port %04x of %04x, len %02x \n",
                address, value, io_len);
      break;

    case 0xCFC:
      {
      Bit32u dMask, idx;

      switch (io_len) {
          case 1:
            dMask = 0xFF; break;
          case 2:
            dMask = 0xFFFF; break;
          case 4:
            dMask = 0xFFFFFFFF; break;
          default:
            dMask = 0x0; break;
        }
      if (BX_PCI_THIS s.i440fx.confAddr & 0x80000000) {
        idx = (BX_PCI_THIS s.i440fx.confAddr & 0xFC);
        BX_PCI_THIS s.i440fx.array[idx] = (BX_PCI_THIS s.i440fx.array[idx] & ~dMask) | (value & dMask);
        bx_printf("440FX IO write to port %04x of %04x, len %02x \n",
                  address, value, io_len);
        }
     }
     break;

    case 0xCFD:
      {
      Bit32u dMask, idx;

      switch (io_len) {
        case 1:
          dMask = 0xFF00; break;
        case 2:
          dMask = 0xFFFF00; break;
        default:
          dMask = 0x0; break;
        }
      if (BX_PCI_THIS s.i440fx.confAddr & 0x80000000) {
        idx = (BX_PCI_THIS s.i440fx.confAddr & 0xFC);
        BX_PCI_THIS s.i440fx.array[idx] = (BX_PCI_THIS s.i440fx.array[idx] & ~dMask) | ((value << 8) & dMask);
        bx_printf("440FX IO write to port %04x of %04x, len %02x \n",
                  address, value, io_len);
        }
     }
     break;

    case 0xCFE:
      {
      Bit32u dMask, idx;

      switch (io_len) {
        case 1:
          dMask = 0xFF0000; break;
        case 2:
          dMask = 0xFFFF0000; break;
        default:
          dMask = 0x0; break;
        }
      if (BX_PCI_THIS s.i440fx.confAddr & 0x80000000) {
        idx = (BX_PCI_THIS s.i440fx.confAddr & 0xFC);
        BX_PCI_THIS s.i440fx.array[idx] = (BX_PCI_THIS s.i440fx.array[idx] & ~dMask) | ((value << 16) & dMask);
        bx_printf("440FX IO write to port %04x of %04x, len %02x \n",
                  address, value, io_len);
        }
     }
     break;

    case 0xCFF:
      {
      Bit32u dMask, idx;

      switch (io_len) {
        case 1:
          dMask = 0xFF000000; break;
        default:
          dMask = 0x0; break;
        }
      if (BX_PCI_THIS s.i440fx.confAddr & 0x80000000) {
        idx = (BX_PCI_THIS s.i440fx.confAddr & 0xFC);
        BX_PCI_THIS s.i440fx.array[idx] = (BX_PCI_THIS s.i440fx.array[idx] & ~dMask) | ((value << 24) & dMask);
        bx_printf("440FX IO write to port %04x of %04x, len %02x \n",
                  address, value, io_len);
        }
     }
     break;

    default:
      bx_panic("pci: IO write to port 0x%x\n", (unsigned) address);
    }
}


  Bit32u
bx_pci_c::mapRead (Bit32u val)
{
   switch (val) {
     case 0x0:
     case 0x2:
          return (1);    // (0) Goto ROM

     case 0x1:
     case 0x3:
          return (0);    // (1) Goto Shadow
   }
   return (2);
}

  Bit32u
bx_pci_c::mapWrite (Bit32u val)
{
   switch (val) {
     case 0x0:
     case 0x1:
          return (1);    // (0) Goto ROM

     case 0x2:
     case 0x3:
          return (0);    // (1) Goto Shadow
   }
   return (2);
}

  Bit32u
bx_pci_c::rd_memType (Bit32u addr)
{
   switch ((addr & 0xFC000) >> 12) {
      case 0xC0:
           return (mapRead ( (BX_PCI_THIS s.i440fx.array[0x58] >> 16) & 0x3));
      case 0xC4:
           return (mapRead ( (BX_PCI_THIS s.i440fx.array[0x58] >> 20) & 0x3));
      case 0xC8:
           return (mapRead ( (BX_PCI_THIS s.i440fx.array[0x58] >> 24) & 0x3));
      case 0xCC:
           return (mapRead ( (BX_PCI_THIS s.i440fx.array[0x58] >> 28) & 0x3));


      case 0xD0:
           return (mapRead (BX_PCI_THIS s.i440fx.array[0x5C] & 0x3));
      case 0xD4:
           return (mapRead ( (BX_PCI_THIS s.i440fx.array[0x5C] >> 4) & 0x3));
      case 0xD8:
           return (mapRead ( (BX_PCI_THIS s.i440fx.array[0x5C] >> 8) & 0x3));
      case 0xDC:
           return (mapRead ( (BX_PCI_THIS s.i440fx.array[0x5C] >> 12) & 0x3));

      case 0xE0:
           return (mapRead ( (BX_PCI_THIS s.i440fx.array[0x5C] >> 16) & 0x3));
      case 0xE4:
           return (mapRead ( (BX_PCI_THIS s.i440fx.array[0x5C] >> 20) & 0x3));
      case 0xE8:
           return (mapRead ( (BX_PCI_THIS s.i440fx.array[0x5C] >> 24) & 0x3));
      case 0xEC:
           return (mapRead ( (BX_PCI_THIS s.i440fx.array[0x5C] >> 28) & 0x3));

      case 0xF0:
      case 0xF4:
      case 0xF8:
      case 0xFC:
           return (mapRead ( (BX_PCI_THIS s.i440fx.array[0x58] >> 12) & 0x3));

      default:
           bx_panic ("wr_memType () Error: Memory Type not known !\n");
           return(0); // keep compiler happy
           break;
   }

}

  Bit32u
bx_pci_c::wr_memType (Bit32u addr)
{
   switch ((addr & 0xFC000) >> 12) {
      case 0xC0:
           return (mapWrite ( (BX_PCI_THIS s.i440fx.array[0x58] >> 16) & 0x3));
      case 0xC4:
           return (mapWrite ( (BX_PCI_THIS s.i440fx.array[0x58] >> 20) & 0x3));
      case 0xC8:
           return (mapWrite ( (BX_PCI_THIS s.i440fx.array[0x58] >> 24) & 0x3));
      case 0xCC:
           return (mapWrite ( (BX_PCI_THIS s.i440fx.array[0x58] >> 28) & 0x3));


      case 0xD0:
           return (mapWrite (BX_PCI_THIS s.i440fx.array[0x5C] & 0x3));
      case 0xD4:
           return (mapWrite ( (BX_PCI_THIS s.i440fx.array[0x5C] >> 4) & 0x3));
      case 0xD8:
           return (mapWrite ( (BX_PCI_THIS s.i440fx.array[0x5C] >> 8) & 0x3));
      case 0xDC:
           return (mapWrite ( (BX_PCI_THIS s.i440fx.array[0x5C] >> 12) & 0x3));

      case 0xE0:
           return (mapWrite ( (BX_PCI_THIS s.i440fx.array[0x5C] >> 16) & 0x3));
      case 0xE4:
           return (mapWrite ( (BX_PCI_THIS s.i440fx.array[0x5C] >> 20) & 0x3));
      case 0xE8:
           return (mapWrite ( (BX_PCI_THIS s.i440fx.array[0x5C] >> 24) & 0x3));
      case 0xEC:
           return (mapWrite ( (BX_PCI_THIS s.i440fx.array[0x5C] >> 28) & 0x3));

      case 0xF0:
      case 0xF4:
      case 0xF8:
      case 0xFC:
           return (mapWrite ( (BX_PCI_THIS s.i440fx.array[0x58] >> 12) & 0x3));

      default:
           bx_panic ("rd_memType () Error: Memory Type not known !\n");
           return(0); // keep compiler happy
           break;
   }
}

  void
bx_pci_c::print_i440fx_state( FILE *fd )
{
#ifdef DUMP_FULL_I440FX
  int  i;
#endif /* DUMP_FULL_I440FX */

  fprintf( fd, "i440fxConfAddr:0x%x\n", BX_PCI_THIS s.i440fx.confAddr );
  fprintf( fd, "i440fxConfData:0x%x\n", BX_PCI_THIS s.i440fx.confData );

#ifdef DUMP_FULL_I440FX
  for (i=0; i<256; i++) {
    fprintf( fd, "i440fxArray%02x:0x%x\n", i, BX_PCI_THIS s.i440fx.array[i] );
    }
#else /* DUMP_FULL_I440FX */
  fprintf( fd, "i440fxArray58:0x%x\n", BX_PCI_THIS s.i440fx.array[0x58] );
  fprintf( fd, "i440fxArray5c:0x%x\n", BX_PCI_THIS s.i440fx.array[0x5c] );
#endif /* DUMP_FULL_I440FX */
}

  Bit8u*
bx_pci_c::i440fx_fetch_ptr(Bit32u addr)
{
  if (bx_options.i440FXSupport) {
    switch (bx_pci.rd_memType (addr)) {
      case 0x0:   // Read from ShadowRAM
        return (&BX_MEM.vector[addr]);

      case 0x1:   // Read from ROM
        return (&bx_pci.s.i440fx.shadow[(addr - 0xc0000)]);
      default:
        bx_panic("i440fx_fetch_ptr(): default case\n");
        return(0);
      }
    }
  else
    return (&BX_MEM.vector[addr]);
}
