/***************************************************************************
 *   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.                                   *
 ***************************************************************************/
#ifndef COFF_OBJECT_H
#define COFF_OBJECT_H

#include "coff.h"
#include "devices/base/register.h"

namespace Coff
{
//----------------------------------------------------------------------------
extern bool getName(const TQByteArray &data, uint &offset, uint nbChars, uint stringTableOffset, Log::Base &log, TQString &name);
extern int disassemble(long int opcode, long int opcode2, int org, Pic::Architecture architecture, char *buffer, size_t sizeof_buffer);

BEGIN_DECLARE_ENUM(OptHeaderFormat)
  OldMicrochip = 0, NewMicrochip, Picc, Ccsc
END_DECLARE_ENUM_STD(OptHeaderFormat)

struct OptHeaderData {
  uint magic;
  OptHeaderFormat optHeaderFormat;
  bool parsed;
};
extern const OptHeaderData OPT_HEADER_DATA[];

class Object;
class Section;

//----------------------------------------------------------------------------
class Element
{
public:
  Element(const Object &object) : _object(object) {}
  virtual ~Element() {}

protected:
  const Object &_object;
};

//----------------------------------------------------------------------------
class BaseSymbol : public Element
{
public:
  BaseSymbol(const Object &object) : Element(object) {}
  virtual bool isAuxSymbol() const = 0;
};

BEGIN_DECLARE_ENUM(AuxSymbolType)
  Direct = 0, File, Identifier, Section
END_DECLARE_ENUM_STD(AuxSymbolType)

class AuxSymbol : public BaseSymbol
{
public:
  virtual bool isAuxSymbol() const { return true; }
  static AuxSymbol *factory(const Object &object, AuxSymbolType type, const TQByteArray &data,
                            uint offset, uint stringTableOffset, Log::Base &log);

public:
  AuxSymbol(const Object &object) : BaseSymbol(object) {}
  virtual AuxSymbolType type() const = 0;
};

class AuxSymbolDirect : public AuxSymbol
{
public:
  AuxSymbolDirect(const Object &object, const TQByteArray &data, uint offset, uint stringTableOffset, Log::Base &log);
  virtual AuxSymbolType type() const { return AuxSymbolType::Direct; }

private:
  uchar   _command;
  TQString _string;
};

class AuxSymbolFile : public AuxSymbol
{
public:
  AuxSymbolFile(const Object &object, const TQByteArray &data, uint offset, uint stringTableOffset, Log::Base &log);
  virtual AuxSymbolType type() const { return AuxSymbolType::File; }
  TQString filename() const { return _filename; }
  uint line() const { return _line; }

private:
  uint    _line;
  TQString _filename;
};

class AuxSymbolIdentifier : public AuxSymbol
{
public:
  AuxSymbolIdentifier(const Object &object, const TQByteArray &data, uint offset, uint stringTableOffset, Log::Base &log);
  virtual AuxSymbolType type() const { return AuxSymbolType::Identifier; }
  TQString string() const { return _string; }

private:
  TQString _string;
};

class AuxSymbolSection : public AuxSymbol
{
public:
  AuxSymbolSection(const Object &object, const TQByteArray &data, uint offset, uint stringTableOffset, Log::Base &log);
  virtual AuxSymbolType type() const { return AuxSymbolType::Section; }

private:
  uint _length, _nbRelocations, _nbLineNumbers;
};

class AuxSymbolUnknown : public AuxSymbol
{
public:
  AuxSymbolUnknown(const Object &object) : AuxSymbol(object) {}
  virtual AuxSymbolType type() const { return AuxSymbolType::Nb_Types; }
};

//----------------------------------------------------------------------------
BEGIN_DECLARE_ENUM(SymbolSectionType)
  InsideSection = 0, UndefinedSection, AbsoluteValue, DebugSymbol
END_DECLARE_ENUM_STD(SymbolSectionType)

struct SymbolClassData {
  const char *key, *label;
  uint id;
};
BEGIN_DECLARE_ENUM(SymbolClass)
  Automatic = 0, External, Static, Register, ExternalDefinition,
  Label, UndefinedLabel, MemberOfStructure, FunctionArgument, StructureTag,
  MemberOfUnion, UnionTag, TypeDefinition, UndefinedStatic, EnumerationTag,
  MemberOfEnumeration, RegisterParameter, BitField, AutoArgument, EndOfBlock,
  BeginEndOfBlock, BeginEndOfFunction, EndOfStructure, Filename, LineNumber,
  DuplicateTag, Section
END_DECLARE_ENUM(SymbolClass, SymbolClassData)

struct SymbolTypeData {
  const char *key, *label;
  uint id;
};
BEGIN_DECLARE_ENUM(SymbolType)
  Void = 0, Char, Short, Int, Long, Float, Double, Struct, Union,
  Enum, MemberOfEnum, UChar, UShort, UInt, ULong, LongDouble
END_DECLARE_ENUM(SymbolType, SymbolTypeData)

struct SymbolDerivedTypeData {
  const char *key, *label;
  uint id;
};
BEGIN_DECLARE_ENUM(SymbolDerivedType)
  Pointer = 0, Function, Array
END_DECLARE_ENUM(SymbolDerivedType, SymbolDerivedTypeData)

class Symbol : public BaseSymbol
{
public:
  Symbol(const Object &object, const TQByteArray &data, uint offset, uint stringTableOffset,
         const TQString &lastFilename, Log::Base &log);
  virtual bool isAuxSymbol() const { return false; }
  TQString name() const { return _name; }
  TQString filename() const { return _filename; }
  const TQValueVector<AuxSymbol *> &auxSymbols() const { return _aux; }
  SymbolClass symbolClass() const { return _sclass; }
  SymbolSectionType sectionType() const;
  SymbolType type() const { return _type; }
  SymbolDerivedType derivedType() const { return _dtype; }
  uint value() const { return _value; }
  uint section() const { Q_ASSERT( sectionType()==SymbolSectionType::InsideSection ); return _section; }

private:
  TQString _name, _filename;
  uint _value, _section;
  SymbolClass _sclass;
  SymbolType  _type;
  SymbolDerivedType _dtype;
  TQValueVector<AuxSymbol *> _aux;
};

//----------------------------------------------------------------------------
class Relocation : public Element
{
public:
  Relocation(const Object &object, const Section &section, const TQByteArray &data,
             uint offset, Log::Base &log);

private:
  ulong  _address, _type;
  short  _offset;
  const Symbol *_symbol;
};

//----------------------------------------------------------------------------
class CodeLine : public Element
{
public:
  CodeLine(const Object &object, const Section &section, const TQByteArray &data,
           uint offset, const TQString &lastFilename, Log::Base &log);
  const Section &section() const { return _section; }
  TQString filename() const { return _filename; }
  uint line() const { return _line; }
  Address address() const { return _address; }
  const Symbol *symbol() const { return _symbol; }

private:
  const Section &_section;
  uint           _line;
  Address        _address;
  TQString        _filename;
  const Symbol  *_symbol;
};

//----------------------------------------------------------------------------
BEGIN_DECLARE_ENUM(SectionType)
  Config = 0, DeviceId, UserIds, UninitializedData, InitializedData, DataRom, Code
END_DECLARE_ENUM_STD(SectionType)

class Section : public Element
{
public:
  class InstructionData {
  public:
    BitValue value;
    TQString opcode, disasm;
  };

public:
  Section(const Device::Data &device, const Object &object, const TQByteArray &data, uint offset,
          uint stringTableOffset, Log::Base &log);
  ~Section();
  SectionType type() const;
  TQString name() const { return _name; }
  Address address() const { return _address; }
  uint size() const { return _size; }
  uint flags() const { return _flags; }
  const TQMap<Address, InstructionData> &instructions() const { return _instructions; }
  const TQValueVector<Relocation *> &relocations() const { return _relocations; }
  const TQValueVector<CodeLine *> &lines() const { return _lines; }

private:
  TQString _name;
  Address _address;
  uint _size, _flags;
  TQMap<Address, InstructionData> _instructions;
  TQValueVector<Relocation *> _relocations;
  TQValueVector<CodeLine *> _lines;

  enum Flag { FText = 0x00020, FData = 0x00040, FBSS = 0x00080, FDataRom = 0x00100,
              FAbs = 0x01000, FShared = 0x02000, FOverlay = 0x04000, FAccess = 0x08000,
              FActivationRecord = 0x10000 };
};

//----------------------------------------------------------------------------
class Object : public Base
{
public:
  Object(const Device::Data *device, const PURL::Url &url);
  virtual ~Object();
  virtual bool parse(Log::Base &log);
  Format format() const { return _format; }
  const Device::Data *device() const { return _device; }
  uint size(SizeType stype) const { return _format.data().sizes[stype]; }
  OptHeaderFormat optHeaderFormat() const { return _optHeaderFormat; }
  uint optHeaderMagic() const { return _optHeaderMagic; }
  uint nbSymbols() const { return _symbols.count(); }
  const BaseSymbol *symbol(uint i) const { return _symbols[i]; }
  const Symbol *symbol(const TQString &name) const { return (_msymbols.contains(name) ? _msymbols[name] : 0); }
  uint nbSections() const { return _sections.count(); }
  const Section *section(uint i) const { return _sections[i]; }
  const TQStringList &filenames() const { return _filenames; }
  const TQMap<TQString, Address> &variables() const { return _variables; }
  TQString variableName(Address address) const;

  enum Flag { RelocationStripped = 0x0001, Executable = 0x0002, LineNumberStripped = 0x0004,
              SymbolStripped = 0x0080, Extended18 = 0x4000, Generic = 0x8000 };
  TQ_DECLARE_FLAGS(Flags, Flag)

protected:
  TQ_UINT32                _optHeaderMagic;
  OptHeaderFormat         _optHeaderFormat;
  const Device::Data     *_device;
  uint                    _nbSections, _nbSymbols, _symbolOffset;
  Flags                   _flags;
  TQValueVector<BaseSymbol *> _symbols;
  TQMap<TQString, Symbol *>  _msymbols; // name -> Symbol *
  TQValueVector<Section *> _sections;
  TQStringList             _filenames;
  TQMap<TQString, Address>  _variables; // name -> address

  virtual bool parseHeader(const TQByteArray &data, uint &offset, Log::Base &log);
  virtual bool parseOptionnalHeader(const TQByteArray &data, uint &offset, Log::Base &log);
};
TQ_DECLARE_OPERATORS_FOR_FLAGS(Object::Flags)

} // namespace

//----------------------------------------------------------------------------
namespace Pic
{

class RegisterNameData
{
public:
  RegisterNameData() {}
  RegisterNameData(const TQString &label, const Register::TypeData &data) : _label(label), _data(data) {}
  TQString label() const { return _label; }
  const Register::TypeData &data() const { return _data; }
  bool operator <(const RegisterNameData &rnd) const { return _label<rnd._label; };

private:
  TQString _label;
  Register::TypeData _data;
};
extern TQValueVector<RegisterNameData> sfrList(const Pic::Data &data);
extern TQValueVector<RegisterNameData> gprList(const Pic::Data &data, const Coff::Object *coff);
extern TQValueVector<RegisterNameData> variableList(const Pic::Data &data, const Coff::Object &coff);

} // namespace

#endif