/*
   Copyright (C) 1998-2003 Scott Dattalo

This file is part of the libgpsim library of gpsim

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see
<http://www.gnu.org/licenses/lgpl-2.1.html>.
*/

#ifndef __REGISTERS_H__
#define __REGISTERS_H__

#include <glib.h>

class symbol;
class XrefObject;
class Processor;
class Module;

#include "gpsim_classes.h"
#include "value.h"
#include "ValueCollections.h"
#include "clock_phase.h"

#define AN_INVALID_ADDRESS 0xffffffff

//---------------------------------------------------------
// RegisterValue class
//
// This class is used to represent the value of registers.
// It also defines which bits have been initialized and which
// are valid.
//

class RegisterValue
{
public:

  unsigned int data;  // The actual numeric value of the register.
  unsigned int init;  // bit mask of initialized bits.

  RegisterValue()
  {
    data = 0;
    init = 0xff;  // assume 8-bit wide, uninitialized registers
  }

  RegisterValue(unsigned int d, unsigned int i) :
    data(d), init(i)
  {
  }

  RegisterValue(const RegisterValue &value) :
  data(value.data), init(value.init)
  {
  }

  inline bool initialized()
  {
    return init == 0;
  }

  inline unsigned int get()
  {
    return data;
  }

  inline void put(unsigned int d)
  {
    data = d;
  }

  inline void put(unsigned int d, unsigned int i)
  {
    data = d;
    init = i;
  }

  inline unsigned int geti()
  {
    return init;
  }

  inline void puti(unsigned int i)
  {
    init = i;
  }

  inline void operator = (RegisterValue rv)
  {
    data = rv.data;
    init = rv.init;
  }

  inline operator unsigned int ()
  {
    return data;
  }

  inline operator int ()
  {
    return (int)data;
  }

  bool operator == (const RegisterValue &rv) const {
    return data == rv.data && init == rv.init;
  }

  bool operator != (const RegisterValue &rv) const {
    return data != rv.data || init != rv.init;
  }

  void operator >>= (unsigned int val) {
      data >>= val;
      init >>= val;
  }
  char * toString(char *str, int len, int regsize=2) const;
  char * toBitStr(char *s, int len, unsigned int BitPos,
                  const char *ByteSeparator="_",
                  const char *HiBitNames=0,
                  const char *LoBitNames=0,
                  const char *UndefBitNames=0) const;

};


//---------------------------------------------------------
/// Register - base class for gpsim registers.
/// The Register class is used by processors and modules to
/// to create memory maps and special function registers.
///

class Register : public Value
{
public:

  enum REGISTER_TYPES
    {
      INVALID_REGISTER,
      GENERIC_REGISTER,
      FILE_REGISTER,
      SFR_REGISTER,
      BP_REGISTER
    };

  RegisterValue value;

  unsigned int address;

  // If non-zero, the alias_mask describes all address at which
  // this file register appears. The assumption (that is true so
  // far for all pic architectures) is that the aliased register
  // locations differ by one bit. For example, the status register
  // appears at addresses 0x03 and 0x83 in the 14-bit core.
  // Consequently, alias_mask = 0x80 and address (above) is equal
  // to 0x03.

  unsigned int alias_mask;

  RegisterValue por_value;  // power on reset value

  unsigned int mValidBits;  // = 255 for 8-bit registers, = 65535 for 16-bit registers.

  // The read_trace and write_trace variables are used while
  // tracing register reads and writes. Essentially, these are
  // the trace commands.

  RegisterValue write_trace;
  RegisterValue read_trace;

  // The trace_state is used to reconstruct the state of the
  // register while traversing a trace buffer.

  RegisterValue trace_state;


  guint64 read_access_count;
  guint64 write_access_count;


public:
  Register(Module *, const char *pName, const char *pDesc=0);
  virtual ~Register();


  virtual int set_break(ObjectBreakTypes bt=eBreakAny, ObjectActionTypes at=eActionHalt, Expression *expr=0);
  virtual int clear_break();

  /// get - method for accessing the register's contents.

  virtual unsigned int get();

  /// put - method for writing a new value to the register.

  virtual void put(unsigned int new_value);


  /// put_value - is the same as put(), but some extra stuff like
  /// interfacing to the gui is done. (It's more efficient than
  /// burdening the run time performance with (unnecessary) gui
  ///  calls.)


  virtual void put_value(unsigned int new_value);

  /// get_value - same as get(), but no trace is performed

  virtual unsigned int get_value() { return(value.get()); }

  /// getRV - get the whole register value - including the info
  /// of the three-state bits.

  virtual RegisterValue getRV()
  {
    value.data = get();
    return value;
  }

  /// putRV - write a new value to the register.
  /// \deprecated {use SimPutAsRegisterValue()}
  ///

  virtual void putRV(RegisterValue rv)
  {
    value.init = rv.init;
    put(rv.data);
  }

  /// getRV_notrace and putRV_notrace are analogous to getRV and putRV
  /// except that the action (in the derived classes) will not be
  /// traced. The primary reason for this is to allow the gui to
  /// refresh it's windows without having the side effect of filling
  /// up the trace buffer

  virtual RegisterValue getRV_notrace()
  {
    value.data = value.get();
    return value;
  }
  virtual void putRV_notrace(RegisterValue rv)
  {
    value.init = rv.init;
    put_value(rv.data);
  }

  virtual RegisterValue getRVN()
  {
    return getRVN_notrace();
  }
  virtual RegisterValue getRVN_notrace()
  {
    return getRV_notrace();
  }

  /// set --- cast another Value object type into a register type
  /// this is used primarily by expression and stimuli processing
  /// (the put() methods are used by the processors).
  /// FIXME -- consolidate the get, set, and put methods
  virtual void set(Value *);

  /// copy --- This is used during expression parsing.
  virtual Value *copy();

  /// get(gint64 &i) --- ugh.
  virtual void get(gint64 &i);

  virtual void initialize()
  {
  }

  /// get3StateBit - returns the 3-state value of a bit
  /// if a bit is known then a '1' or '0' is returned else,
  /// a '?' is returned. No check is performed to ensure
  /// that only a single bit is checked, thus it's possible
  /// to get the state of a group of bits using this method.

  virtual char get3StateBit(unsigned int bitMask)
  {
    RegisterValue rv = getRV_notrace();
    return (rv.init & bitMask) ? '?' : ((rv.data & bitMask) ? '1' : '0');
  }
  /// In the Register class, the 'Register *get()' returns a
  /// pointer to itself. Derived classes may return something
  /// else (e.g. a break point may be pointing to the register
  /// it replaced and will return that instead).

  virtual Register *getReg()
  {
    return this;
  }

  virtual REGISTER_TYPES isa() {return GENERIC_REGISTER;};
  virtual void reset(RESET_TYPE r) { return; };


  /// The setbit function is not really intended for general purpose
  /// registers. Instead, it is a place holder which is over-ridden
  /// by the IO ports.

  virtual void setbit(unsigned int bit_number, bool new_value);


  ///  like setbit, getbit is used mainly for breakpoints.

  virtual bool get_bit(unsigned int bit_number);
  virtual double get_bit_voltage(unsigned int bit_number);


  ///  Breakpoint objects will overload this function and return true.

  virtual bool hasBreak()
  {
    return false;
  }


  ///  register_size returns the number of bytes required to store the register
  ///  (this is used primarily by the gui to determine how wide to make text fields)

  virtual unsigned int register_size () const;

  /*
    When the register is accessed, this action is recorded in the trace buffer.
    Here we can specify the exact trace command to use.
   */
  virtual void set_write_trace(RegisterValue &rv);
  virtual void set_read_trace(RegisterValue &rv);
  virtual void put_trace_state(RegisterValue rv)
  {
    trace_state = rv;
  }

  virtual RegisterValue get_trace_state()
  {
    return trace_state;
  }

  /*
    convert value to a string:
   */
  virtual char * toString(char *str, int len);
  virtual char * toBitStr(char *s, int len);
  virtual string &baseName()
  {
    return name_str;
  }

  virtual unsigned int getAddress()
  {
    return address;
  }
  virtual void setAddress(unsigned int addr)
  {
    address = addr;
  }
  Register *getReplaced() { return m_replaced; }
  void setReplaced(Register *preg) { m_replaced = preg; }

  virtual void new_name(string &);
  virtual void new_name(const char *);

protected:
  // A pointer to the register that this register replaces.
  // This is used primarily by the breakpoint code.
  Register *m_replaced;

};


//---------------------------------------------------------
// define a special 'invalid' register class. Accessess to
// to this class' value get 0

class InvalidRegister : public Register
{
public:

  InvalidRegister(Processor *, const char *pName, const char *pDesc=0);

  void put(unsigned int new_value);
  unsigned int get();
  virtual REGISTER_TYPES isa() {return INVALID_REGISTER;};
  virtual Register *getReg()   {return 0; }
};


//---------------------------------------------------------
// Base class for a special function register.
class BitSink;

class sfr_register : public Register
{
public:
  sfr_register(Module *, const char *pName, const char *pDesc=0);

  RegisterValue wdtr_value; // wdt or mclr reset value

  virtual REGISTER_TYPES isa() {return SFR_REGISTER;};
  virtual void initialize() {};

  virtual void reset(RESET_TYPE r);

  // The assign and release BitSink methods don't do anything
  // unless derived classes redefine them. Their intent is to
  // provide an interface to the BitSink design - a design that
  // allows clients to be notified when bits change states.

  virtual bool assignBitSink(unsigned int bitPosition, BitSink *) {return false;}
  virtual bool releaseBitSink(unsigned int bitPosition, BitSink *) {return false;}
};



//---------------------------------------------------------
// Program Counter
//
class PCTraceType;
class Program_Counter : public Value
{
public:
  unsigned int value;              /* pc's current value */
  unsigned int memory_size;
  unsigned int pclath_mask;        /* pclath confines PC to banks */
  unsigned int instruction_phase;
  unsigned int trace_state;        /* used while reconstructing the trace history */


  // Trace commands
  unsigned int trace_increment;
  unsigned int trace_branch;
  unsigned int trace_skip;
  unsigned int trace_other;

  Program_Counter(const char *name, const char *desc, Module *pM);
  ~Program_Counter();
  virtual void increment();
  virtual void start_skip();
  virtual void skip();
  virtual void jump(unsigned int new_value);
  virtual void interrupt(unsigned int new_value);
  virtual void computed_goto(unsigned int new_value);
  virtual void new_address(unsigned int new_value);
  virtual void put_value(unsigned int new_value);
  virtual void update_pcl();
  virtual void get(char *buffer, int buf_size);
  virtual unsigned int get_value()
  {
    return value;
  }
  virtual unsigned int get_PC() {
    return value;
  }

  virtual void set_PC(unsigned int new_value) {
    value = new_value;
    this->update();
  }

  /// set --- cast another Value object type into a program counter register type
  /// this is used primarily by expression and stimuli processing
  /// (the put() methods are used by the processors).
  /// FIXME -- consolidate the get, set, and put methods
  virtual void set(Value *);

  // initialize the dynamically allocated trace type
  virtual void set_trace_command();

  /// get_raw_value -- on the 16-bit cores, get_value is multiplied by 2
  /// whereas get_raw_value isn't. The raw value of the program counter
  /// is used as an index into the program memory.
  virtual unsigned int get_raw_value()
  {
    return value;
  }

  virtual void set_phase(int phase)
  {
    instruction_phase = phase;
  }
  virtual int get_phase()
  {
    return instruction_phase;
  }

  void set_reset_address(unsigned int _reset_address)
  {
    reset_address = _reset_address;
  }
  unsigned int get_reset_address()
  {
    return reset_address;
  }

  void reset();

  virtual unsigned int get_next();

  virtual void put_trace_state(unsigned int ts)
  {
    trace_state = ts;
  }

protected:
  unsigned int reset_address;      /* Value pc gets at reset */
  PCTraceType *m_pPCTraceType;

};

// Used in the command prompt interface
class RegisterCollection : public IIndexedCollection
{
public:
  RegisterCollection(Processor *pProcessor,
                     const char *collection_name,
                     Register   **ppRegisters,
                     unsigned int uiSize);
  ~RegisterCollection();
  virtual void get(char *return_str, int len);
  virtual unsigned int GetSize();
  virtual Value &GetAt(unsigned int uIndex, Value *pValue=0);
  virtual void SetAt(unsigned int uIndex, Value *pValue);
  virtual void ConsolidateValues(int &iColumnWidth,
                                 vector<string> &aList,
                                 vector<string> &aValue);
//  virtual void SetAt(ExprList_t* pIndexers, Expression *pExpr);
  virtual unsigned int GetLowerBound();
  virtual unsigned int GetUpperBound();
private:
  Processor *   m_pProcessor;
  Register **   m_ppRegisters;
  unsigned int  m_uSize;
  Integer       m_ReturnValue;
};


//------------------------------------------------------------------------
// BitSink
//
// A BitSink is an object that can direct bit changes in an SFR to some
// place where they're needed. The purpose is to abstract the interface
// between special bits and the various peripherals.
//
// A client wishing to be notified whenever an SFR bit changes states
// will create a BitSink object and pass its pointer to the SFR. The
// client will also tell the SFR which bit this applies to. Now, when
// the bit changes states in the SFR, the SFR will call the setSink()
// method.

class BitSink
{
public:
  virtual ~BitSink()
  {
  }

  virtual void setSink(bool) = 0;
};


#endif // __REGISTERS__
