/*
 ****************************************************************************
 *
 * simulavr - A simulator for the Atmel AVR family of microcontrollers.
 * Copyright (C) 2001, 2002, 2003   Klaus Rudolph       
 * 
 * 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.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 ****************************************************************************
 *
 *  $Id$
 */

#ifndef AVRDEVICE
#define AVRDEVICE

#include <string>
#include <map>
#include <vector>
#include <algorithm>
#include <types.h> // for dword

#include "systemclocktypes.h"
#include "simulationmember.h"
#include "pin.h"
#include "net.h"
#include "traceval.h"
#include "flashprog.h"

// transfered from global.h
#define BREAK_POINT    -2
#define INVALID_OPCODE -1

// transfered from breakpoint.h
class Breakpoints: public std::vector<dword> { };
class Exitpoints: public std::vector<dword> { };

// from hwsreg.h, but not included, because of circular include with this header
class HWSreg;
class RWSreg;

class AvrFlash;
class HWEeprom;
class HWStack;
class HWWado;
class Data;
class HWIrqSystem;
class RWMemoryMember;
class Hardware;
class DumpManager;
class AddressExtensionRegister;

//! Basic AVR device, contains the core functionality
class AvrDevice: public SimulationMember, public TraceValueRegister {
    
    private:
        RWMemoryMember **invalidRW; //!< hold invalid RW memory cells created by device
        const unsigned int ioSpaceSize;
        static const unsigned int totalIoSpace;
        static const unsigned int registerSpaceSize;
        const unsigned int iRamSize;
        const unsigned int eRamSize;
        unsigned int devSignature; //!< hold the device signature for this core
        std::string devName; //!< hold the device name, which this core simulate

        friend class DumpManager;
        void detachDumpManager() { dumpManager = NULL; }

    protected:
        SystemClockOffset clockFreq;  ///< Period of a tick (1/F_OSC) in [ns]
        std::map < std::string, Pin *> allPins;
        std::string actualFilename;
        
        /// Count of cycles before next instruction is executed (i.e. countdown)
        int cpuCycles;

    public:
        int trace_on;
        Breakpoints BP;
        Exitpoints EP;
        unsigned int PC;  ///< Next/current instruction index. Multiply by 2 to get an address.
        /// When mupti-cycle instruction is "processed" this holds its address, PC holds the next instruction.
        unsigned int cPC;
        const unsigned int PC_size;
        unsigned int dataAddressMask; //!< which bits in address are significant
        AvrFlash *Flash;
        FlashProgramming *spmRegister;
        AvrFuses *fuses;
        AvrLockBits *lockbits;
        HWEeprom *eeprom;
        Data *data;  ///< a hack for symbol look-up
        HWIrqSystem *irqSystem;
        AddressExtensionRegister *rampz; //!< RAMPZ address extension register
        AddressExtensionRegister *eind; //!< EIND address extension register
        bool abortOnInvalidAccess; //!< Flag, that simulation abort if an invalid access occured, default is false
        TraceValueCoreRegister coreTraceGroup;
        bool deferIrq;  ///< Almost always false.
        unsigned int newIrqPc;
        unsigned int actualIrqVector; 
        Pin v_supply; //!< represents supply voltage level, needed for analog peripherals
        Pin v_bandgap; //!< represents bandgap (ref) voltage level, needed for analog peripherals

        bool flagIWInstructions; //!< ADIW and SBIW instructions are available (not on most tiny's!)
        bool flagJMPInstructions; //!< CALL and JMP instructions are available (only on devices with bigger flash)
        bool flagIJMPInstructions; //!< ICALL and IJMP instructions are available (not on attiny1x devices)
        bool flagEIJMPInstructions; //!< EICALL and EIJMP instructions are available (only on some devices with bigger flash)
        bool flagLPMInstructions; //!< LPM and SPM instructions are available (not on some tiny devices)
        bool flagELPMInstructions; //!< ELPM instructions are available (only on devices with bigger flash)
        bool flagMULInstructions; //!< (F)MULxx instructions are available
        bool flagMOVWInstruction; //!< MOVW instruction is available
        bool flagTiny10; //!< core is a tiny4/5/9/10, change used clocks on some instructions and disables instructions
        bool flagTiny1x; //!< core is a tiny1x (but not tiny10!), change used clocks on some instructions and disables instructions
        bool flagXMega; //!< core is a XMEGA device, change used clocks on some instructions
        int DebugRecentJumps[20];  ///< Addresses of last few 'call' and 'jump' executed. For debugging.
        int DebugRecentJumpsIndex;  ///< Index to address of the most recent jump

        RWMemoryMember **rw;  ///< The whole memory: R0-R31, IO, Internal RAM.

        HWStack *stack;
        HWSreg *status;           //!< the status register itself
        RWSreg *statusRegister;   //!< the memory interface for status
        HWWado *wado;  ///< WDT timer

        std::vector<Hardware *> hwResetList; 
        std::vector<Hardware *> hwCycleList; 

        DumpManager *dumpManager;
    
        AvrDevice(unsigned int ioSpaceSize, unsigned int IRamSize, unsigned int ERamSize, unsigned int flashSize, unsigned int pcSize = 2);
        virtual ~AvrDevice();

        /*! Adds to the list of parts to reset. If already in that list, does
          nothing. */
        void AddToResetList(Hardware *hw);

        /*! Adds to the list of parts to cycle per clock tick. If already in that list, does
          nothing. */
        void AddToCycleList(Hardware *hw);

        //! Removes from the cycle list, if possible.
        /*! Does nothing if the part is not in the cycle list. */
        void RemoveFromCycleList(Hardware *hw);
    
        void Load(const char* n); //!< Load flash, eeprom, signature, fuses from elf file, wrapper for LoadBFD or LoadSimpleELF
        void ReplaceIoRegister(unsigned int offset, RWMemoryMember *);
        bool ReplaceMemRegister(unsigned int offset, RWMemoryMember *);
        RWMemoryMember *GetMemRegisterInstance(unsigned int offset);
        void RegisterTerminationSymbol(const char *symbol);

        Pin *GetPin(const char *name);
        /*! Steps the AVR core.
          \param untilCoreStepFinished iff true, steps a core step and not a
          single clock cycle. */
        int Step(bool &untilCoreStepFinished, SystemClockOffset *nextStepIn_ns =0);
        void Reset();
        void SetClockFreq(SystemClockOffset f);
        SystemClockOffset GetClockFreq();

        void RegisterPin(const std::string &name, Pin *p) {
            allPins.insert(std::pair<std::string, Pin*>(name, p));
        }

        //! Clear all breakpoints in device
        void DeleteAllBreakpoints(void);

        //! Return filename from loaded program
        const std::string &GetFname(void) { return actualFilename; }
        //! Return device name
        const std::string &GetDeviceName(void) { return devName; }
        //! Return device signature
        unsigned int GetDeviceSignature(void) { return devSignature; }
        //! Set device signature and name
        void SetDeviceNameAndSignature(const std::string &name, unsigned int signature);

        //! Get configured total memory space size
        unsigned int GetMemTotalSize(void) { return totalIoSpace; }
        //! Get configured IO memory space size
        unsigned int GetMemIOSize(void) { return ioSpaceSize; }
        //! Get configured register space size
        unsigned int GetMemRegisterSize(void) { return registerSpaceSize; }
        //! Get configured internal RAM size
        unsigned int GetMemIRamSize(void) { return iRamSize; }
        //! Get configured external RAM size
        unsigned int GetMemERamSize(void) { return eRamSize; }
        
        //! Get a value of RW memory cell
        unsigned char GetRWMem(unsigned addr);
        //! Set a value to RW memory cell
        bool SetRWMem(unsigned addr, unsigned char val);
        //! Get a value from core register
        unsigned char GetCoreReg(unsigned addr);
        //! Set a value to core register
        bool SetCoreReg(unsigned addr, unsigned char val);
        //! Get a value from IO register (without offset of 0x20!)
        unsigned char GetIOReg(unsigned addr);
        //! Set a value to IO register (without offset of 0x20!)
        bool SetIOReg(unsigned addr, unsigned char val);
        //! Set a bit value to lower IO register (without offset of 0x20!)
        bool SetIORegBit(unsigned addr, unsigned bitaddr);
        //! Clear a bit value to lower IO register (without offset of 0x20!)
        bool ClearIORegBit(unsigned addr, unsigned bitaddr);

        //! Get value of X register (16bit)
        unsigned GetRegX(void);
        //! Get value of Y register (16bit)
        unsigned GetRegY(void);
        //! Get value of Z register (16bit)
        unsigned GetRegZ(void);

        //! When a call/jump/cond-jump instruction was executed. For debugging.
        void DebugOnJump();

        friend void ELFLoad(const AvrDevice * core);

};

#endif
