/***************************************************************************
 *   Copyright (C) 2006 Nicolas Hadacek <hadacek@kde.org>                  *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 ***************************************************************************/
#include "pic_register.h"

#include "pic.h"

//-----------------------------------------------------------------------------
Pic::RegistersData::RegistersData(const Data &data)
  : nbBanks(0), accessBankSplit(0), _data(data)
{}

Address Pic::RegistersData::mirroredAddress(Address address) const
{
  Address mirror = address;
  for (uint i=0; i<uint(mirrored.count()); i++) {
    int delta = -1;
    for (uint k=0; k<uint(mirrored[i].count()); k++) {
      if ( address<mirrored[i][k].start
           || address>=mirrored[i][k].start+mirrored[i][k].length ) continue;
      delta = address - mirrored[i][k].start;
      break;
    }
    if ( delta==-1 ) continue;
    for (uint k=0; k<uint(mirrored[i].count()); k++)
      mirror = TQMIN(mirrored[i][k].start + delta, mirror);
    break;
  }
  return mirror;
}

Device::RegisterProperties Pic::RegistersData::properties(Address address) const
{
  switch ( type(address) ) {
    case Mirrored:
    case UnusedRegister: return Device::NotAccessible;
    case Gpr: return (Device::Readable | Device::Writable);
    case Sfr: {
      Device::RegisterProperties properties = Device::NotAccessible;
      RegisterBitProperties rbp = RegisterBitUnused;
      TQMap<TQString, RegisterData>::const_iterator it;
      for (it=sfrs.begin(); it!=sfrs.end(); ++it) {
        if ( it.data().address!=address ) continue;
        for (uint i=0; i<Device::MAX_NB_PORT_BITS; i++) rbp |= it.data().bits[i].properties;
        if ( rbp & RegisterBitRead ) properties |= Device::Readable;
        if ( rbp & RegisterBitWrite ) properties |= Device::Writable;
        break;
      }
      return properties;
    }
  }
  Q_ASSERT(false);
  return Device::NotAccessible;
}

Pic::RegisterType Pic::RegistersData::type(Address address) const
{
  for (uint i=0; i<uint(unused.count()); i++)
    if ( address>=unused[i].start && address<unused[i].start+unused[i].length ) return UnusedRegister;
  if ( !sfrNames[address].isEmpty() ) return Sfr;
  Address mirror = mirroredAddress(address);
  if ( !sfrNames[mirror].isEmpty() ) return Mirrored;
  if ( address==mirror ) return Gpr;
  return Mirrored;
}

TQString Pic::RegistersData::label(Address address) const
{
  switch ( type(address) ) {
    case UnusedRegister: return "---";
    case Mirrored: return i18n("Mirror of %1").arg(toHexLabel(mirroredAddress(address), nbCharsAddress()));
    case Gpr: return "<GPR>";
    case Sfr: return sfrNames[address];
  }
  Q_ASSERT(false);
  return TQString();
}

bool Pic::RegistersData::hasPort(uint index) const
{
  Q_ASSERT( index<Device::MAX_NB_PORTS );
  if ( sfrs.contains("GPIO") ) return ( index==0 );
  if ( !sfrs.contains(portName(index)) ) return false;
  return true;
}

int Pic::RegistersData::portIndex(Address address) const
{
  TQString name = sfrNames[address];
  if ( name.isEmpty() ) return -1;
  for (uint i=0; i<Device::MAX_NB_PORTS; i++) {
    if ( !hasPort(i) ) continue;
    if ( name==portName(i) || name==trisName(i) || name==latchName(i) ) return i;
  }
  return -1;
}

bool Pic::RegistersData::hasPortBit(uint index, uint bit) const
{
  if ( !hasPort(index) ) return false;
  const RegisterData &port = sfrs[portName(index)];
  return ( port.bits[bit].properties!=RegisterBitUnused );
}

TQString Pic::RegistersData::portName(uint index) const
{
  if ( sfrs.contains("GPIO") ) {
    if ( index!=0 ) return TQString();
    return "GPIO";
  }
  return TQString("PORT") + char('A' + index);
}

TQString Pic::RegistersData::trisName(uint index) const
{
  if ( sfrs.contains("GPIO") ) {
    if ( index!=0 ) return TQString();
    return "TRISIO";
  }
  if ( _data.architecture()==Architecture::P17C ) {
    if ( index==0 ) return TQString();
    return TQString("DDR") + char('A' + index);
  }
  return TQString("TRIS") + char('A' + index);
}

bool Pic::RegistersData::hasTris(uint index) const
{
  TQString name = trisName(index);
  if ( name.isEmpty() ) return false;
  return sfrs.contains(name);
}

TQString Pic::RegistersData::latchName(uint index) const
{
  if ( _data.architecture()==Architecture::P10X || _data.architecture()==Architecture::P16X || _data.architecture()==Architecture::P17C )
    return TQString();
  return TQString("LAT") + char('A' + index);
}

bool Pic::RegistersData::hasLatch(uint index) const
{
  TQString name = latchName(index);
  if ( name.isEmpty() ) return false;
  return sfrs.contains(name);
}

TQString Pic::RegistersData::portBitName(uint index, uint bit) const
{
  if ( sfrs.contains("GPIO") ) return TQString("GP") + TQString::number(bit);
  return TQString("R") + char('A' + index) + TQString::number(bit);
}

TQValueList<Register::TypeData> Pic::RegistersData::relatedRegisters(const Register::TypeData &data) const
{
  TQValueList<Register::TypeData> list;
  if ( data.type()==Register::Regular ) {
    int i = portIndex(data.address());
    if ( i==-1 ) list.append(data);
    else {
      list.append(Register::TypeData(sfrs[portName(i)].address, nbChars()));
      if ( hasTris(i) ) list.append(Register::TypeData(sfrs[trisName(i)].address, nbChars()));
      if ( hasLatch(i) ) list.append(Register::TypeData(sfrs[latchName(i)].address, nbChars()));
    }
  } else if ( data.type()==Register::Combined ) {
    uint nb = nbBitsToNbBytes(4*data.nbChars()) / nbBytes();
    for (uint i=0; i<nb; i++) list.append(Register::TypeData(data.address() + i*nbBytes(), nbChars()));
  } else list.append(data);
  return list;
}

bool Pic::RegistersData::isBankUsed(uint i) const
{
  Q_ASSERT( i<nbBanks );
  return !(unusedBankMask & (1 << i));
}

bool Pic::RegistersData::bankHasSfrs(uint i) const
{
  if ( i==0 ) return true;
  if ( (_data.architecture()==Pic::Architecture::P18F || _data.architecture()==Pic::Architecture::P18J) && i==15 ) return true;
  if ( !isBankUsed(i) ) return false;
  TQMap<Address, TQString>::const_iterator it;
  for (it=sfrNames.begin(); it!=sfrNames.end(); ++it)
    if ( bankFromAddress(it.key())==i ) return true;
  return false;
}

bool Pic::RegistersData::hasSharedGprs(uint &firstIndex, bool &all) const
{
  bool ok = false;
  all = true;
  for (uint i=0; i<nbRegistersPerBank(); i++) {
    Address address = addressFromIndex(i);
    if ( type(address)!=Gpr ) continue;
    uint k = 1;
    for (; k<nbBanks; k++) {
      RegisterType t = type(address + k*nbBytesPerBank());
      if ( t!=Mirrored ) break;
    }
    if ( k==nbBanks ) {
      if ( !ok ) firstIndex = i;
      ok = true;
    } else all = false;
  }
  return ok;
}

uint Pic::RegistersData::firstGprIndex() const
{
  for (uint i=0; i<nbRegistersPerBank(); i++)
    if ( type(addressFromIndex(i))==Gpr ) return i;
  Q_ASSERT(false);
  return 0;
}

//----------------------------------------------------------------------------
TQDataStream &Pic::operator <<(TQDataStream &s, const RangeData &rd)
{
  s << rd.start << rd.length;
  return s;
}
TQDataStream &Pic::operator >>(TQDataStream &s, RangeData &rd)
{
  s >> rd.start >> rd.length;
  return s;
}

TQDataStream &Pic::operator <<(TQDataStream &s, const RegisterBitData &rbd)
{
  s << TQ_UINT8(rbd.properties) << TQ_UINT8(rbd.por) << TQ_UINT8(rbd.mclr);
  return s;
}
TQDataStream &Pic::operator >>(TQDataStream &s, RegisterBitData &rbd)
{
  TQ_UINT8 properties, por, mclr;
  s >> properties >> por >> mclr;
  rbd.properties = RegisterBitProperties(properties);
  rbd.por = RegisterBitState(por);
  rbd.mclr = RegisterBitState(mclr);
  return s;
}

TQDataStream &Pic::operator <<(TQDataStream &s, const RegisterData &rd)
{
  s << rd.address;
  for (int i=0; i<Device::MAX_NB_PORT_BITS; i++) s << rd.bits[i];
  return s;
}
TQDataStream &Pic::operator >>(TQDataStream &s, RegisterData &rd)
{
  s >> rd.address;
  for (int i=0; i<Device::MAX_NB_PORT_BITS; i++) s >> rd.bits[i];
  return s;
}

TQDataStream &Pic::operator <<(TQDataStream &s, const CombinedData &rd)
{
  s << rd.address << rd.nbChars;
  return s;
}
TQDataStream &Pic::operator >>(TQDataStream &s, CombinedData &rd)
{
  s >> rd.address >> rd.nbChars;
  return s;
}

TQDataStream &Pic::operator <<(TQDataStream &s, const RegistersData &rd)
{
  s << rd.nbBanks << rd.accessBankSplit << rd.unusedBankMask;
  s << rd.sfrs << rd.mirrored << rd.unused << rd.combined;
  return s;
}
TQDataStream &Pic::operator >>(TQDataStream &s, RegistersData &rd)
{
  s >> rd.nbBanks >> rd.accessBankSplit >> rd.unusedBankMask;
  s >> rd.sfrs >> rd.mirrored >> rd.unused >> rd.combined;
  rd.sfrNames.clear();
  TQMap<TQString, RegisterData>::const_iterator it;
  for(it=rd.sfrs.begin(); it!=rd.sfrs.end(); ++it) rd.sfrNames[it.data().address] = it.key();
  return s;
}
