// Copyright (c) 2006-2007 The Regents of The University of Michigan
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met: redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer;
// redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution;
// neither the name of the copyright holders nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Authors: Ali Saidi
//          Gabe Black
//          Steve Reinhardt

////////////////////////////////////////////////////////////////////
//
// Base class for sparc instructions, and some support functions
//

output header {{

        union CondCodes
        {
            struct
            {
                uint8_t c:1;
                uint8_t v:1;
                uint8_t z:1;
                uint8_t n:1;
            };
            uint32_t bits;
        };

        enum CondTest
        {
            Always=0x8,
            Never=0x0,
            NotEqual=0x9,
            Equal=0x1,
            Greater=0xA,
            LessOrEqual=0x2,
            GreaterOrEqual=0xB,
            Less=0x3,
            GreaterUnsigned=0xC,
            LessOrEqualUnsigned=0x4,
            CarryClear=0xD,
            CarrySet=0x5,
            Positive=0xE,
            Negative=0x6,
            OverflowClear=0xF,
            OverflowSet=0x7
        };

        enum FpCondTest
        {
            FAlways=0x8,
            FNever=0x0,
            FUnordered=0x7,
            FGreater=0x6,
            FUnorderedOrGreater=0x5,
            FLess=0x4,
            FUnorderedOrLess=0x3,
            FLessOrGreater=0x2,
            FNotEqual=0x1,
            FEqual=0x9,
            FUnorderedOrEqual=0xA,
            FGreaterOrEqual=0xB,
            FUnorderedOrGreaterOrEqual=0xC,
            FLessOrEqual=0xD,
            FUnorderedOrLessOrEqual=0xE,
            FOrdered=0xF
        };

        extern const char *CondTestAbbrev[];

        /**
         * Base class for all SPARC static instructions.
         */
        class SparcStaticInst : public StaticInst
        {
          protected:
            // Constructor.
            SparcStaticInst(const char *mnem,
                 ExtMachInst _machInst, OpClass __opClass)
                    : StaticInst(mnem, _machInst, __opClass)
                {
                }

            std::string generateDisassembly(Addr pc,
                const SymbolTable *symtab) const;

            void printReg(std::ostream &os, int reg) const;
            void printSrcReg(std::ostream &os, int reg) const;
            void printDestReg(std::ostream &os, int reg) const;

            void printRegArray(std::ostream &os,
                const RegIndex indexArray[], int num) const;

            void advancePC(SparcISA::PCState &pcState) const;
        };

        bool passesFpCondition(uint32_t fcc, uint32_t condition);

        bool passesCondition(uint32_t codes, uint32_t condition);

        inline int64_t
        sign_ext(uint64_t data, int origWidth)
        {
            int shiftAmount = 64 - origWidth;
            return (((int64_t)data) << shiftAmount) >> shiftAmount;
        }
}};

output decoder {{

        const char *CondTestAbbrev[] =
        {
            "nev", // Never
            "e", // Equal
            "le", // Less or Equal
            "l", // Less
            "leu", // Less or Equal Unsigned
            "c", // Carry set
            "n", // Negative
            "o", // Overflow set
            "a", // Always
            "ne", // Not Equal
            "g", // Greater
            "ge", // Greater or Equal
            "gu", // Greater Unsigned
            "cc", // Carry clear
            "p", // Positive
            "oc" // Overflow Clear
        };
}};

def template ROrImmDecode {{
    {
        return (I ? (SparcStaticInst *)(new %(class_name)sImm(machInst))
                  : (SparcStaticInst *)(new %(class_name)s(machInst)));
    }
}};

output header {{
    union DoubleSingle
    {
        double d;
        uint64_t ui;
        uint32_t s[2];
        DoubleSingle(double _d) : d(_d)
        {}
        DoubleSingle(uint64_t _ui) : ui(_ui)
        {}
        DoubleSingle(uint32_t _s0, uint32_t _s1)
        {
            s[0] = _s0;
            s[1] = _s1;
        }
    };
}};

let {{
    def filterDoubles(code):
        assignRE = re.compile(r'\s*=(?!=)', re.MULTILINE)
        for opName in ("Frd", "Frs1", "Frs2", "Frd_N"):
            next_pos = 0
            operandsREString = (r'''
            (?<!\w)             # neg. lookbehind assertion: prevent partial matches
            ((%s)(?:_([^\W_]+))?)   # match: operand with optional '.' then suffix
            (?!\w)             # neg. lookahead assertion: prevent partial matches
            ''' % opName)
            operandsRE = re.compile(operandsREString, re.MULTILINE|re.VERBOSE)
            is_src = False
            is_dest = False
            extension = None
            foundOne = False
            while 1:
                match = operandsRE.search(code, next_pos)
                if not match:
                    break
                foundOne = True
                op = match.groups()
                (op_full, op_base, op_ext) = op
                is_dest_local = (assignRE.match(code, match.end()) != None)
                is_dest = is_dest or is_dest_local
                is_src = is_src or not is_dest_local
                if extension and extension != op_ext:
                    raise Exception, "Inconsistent extensions in double filter."
                extension = op_ext
                next_pos = match.end()
            if foundOne:
                # Get rid of any unwanted extension
                code = operandsRE.sub(op_base, code)
                is_int = False
                member = "d"
                if extension in ("sb", "ub", "shw", "uhw", "sw", "uw", "sdw", "udw"):
                    is_int = True
                    member = "ui"
                if is_src:
                    code = ("%s = DoubleSingle(%s_high, %s_low).%s;" % \
                        (opName, opName, opName, member)) + code
                if is_dest:
                    code += '''
                        %s_low = DoubleSingle(%s).s[1];
                        %s_high = DoubleSingle(%s).s[0];''' % \
                             (opName, opName, opName, opName)
                if is_int:
                    code = ("uint64_t %s;" % opName) + code
                else:
                    code = ("double %s;" % opName) + code
        return code
}};

let {{
    def splitOutImm(code):
        matcher = re.compile(r'Rs(?P<rNum>\d)_or_imm(?P<iNum>\d+)(?P<typeQual>_[^\W_]+)?')
        rOrImmMatch = matcher.search(code)
        if (rOrImmMatch == None):
            return (False, code, '', '', '')
        rString = rOrImmMatch.group("rNum")
        if (rOrImmMatch.group("typeQual") != None):
            rString += rOrImmMatch.group("typeQual")
        iString = rOrImmMatch.group("iNum")
        orig_code = code
        code = matcher.sub('Rs' + rString, orig_code)
        imm_code = matcher.sub('imm', orig_code)
        return (True, code, imm_code, rString, iString)
}};

output decoder {{

        inline void printMnemonic(std::ostream &os, const char * mnemonic)
        {
            ccprintf(os, "\t%s   ", mnemonic);
        }

        void SparcStaticInst::printRegArray(std::ostream &os,
            const RegIndex indexArray[], int num) const
        {
            if (num <= 0)
                return;
            printReg(os, indexArray[0]);
            for (int x = 1; x < num; x++) {
                os << ", ";
                printReg(os, indexArray[x]);
            }
        }

        void
        SparcStaticInst::advancePC(SparcISA::PCState &pcState) const
        {
            pcState.advance();
        }

        void
        SparcStaticInst::printSrcReg(std::ostream &os, int reg) const
        {
            if (_numSrcRegs > reg)
                printReg(os, _srcRegIdx[reg]);
        }

        void
        SparcStaticInst::printDestReg(std::ostream &os, int reg) const
        {
            if (_numDestRegs > reg)
                printReg(os, _destRegIdx[reg]);
        }

        void
        SparcStaticInst::printReg(std::ostream &os, int reg) const
        {
            const int MaxGlobal = 8;
            const int MaxOutput = 16;
            const int MaxLocal = 24;
            const int MaxInput = 32;
            const int MaxMicroReg = 40;
            if (reg < FP_Base_DepTag) {
                // If we used a register from the next or previous window,
                // take out the offset.
                while (reg >= MaxMicroReg)
                    reg -= MaxMicroReg;
                if (reg == FramePointerReg)
                    ccprintf(os, "%%fp");
                else if (reg == StackPointerReg)
                    ccprintf(os, "%%sp");
                else if (reg < MaxGlobal)
                    ccprintf(os, "%%g%d", reg);
                else if (reg < MaxOutput)
                    ccprintf(os, "%%o%d", reg - MaxGlobal);
                else if (reg < MaxLocal)
                    ccprintf(os, "%%l%d", reg - MaxOutput);
                else if (reg < MaxInput)
                    ccprintf(os, "%%i%d", reg - MaxLocal);
                else if (reg < MaxMicroReg)
                    ccprintf(os, "%%u%d", reg - MaxInput);
                // The fake int regs that are really control regs
                else {
                    switch (reg - MaxMicroReg) {
                      case 1:
                        ccprintf(os, "%%y");
                        break;
                      case 2:
                        ccprintf(os, "%%ccr");
                        break;
                      case 3:
                        ccprintf(os, "%%cansave");
                        break;
                      case 4:
                        ccprintf(os, "%%canrestore");
                        break;
                      case 5:
                        ccprintf(os, "%%cleanwin");
                        break;
                      case 6:
                        ccprintf(os, "%%otherwin");
                        break;
                      case 7:
                        ccprintf(os, "%%wstate");
                        break;
                    }
                }
            } else if (reg < Ctrl_Base_DepTag) {
                ccprintf(os, "%%f%d", reg - FP_Base_DepTag);
            } else {
                switch (reg - Ctrl_Base_DepTag) {
                  case MISCREG_ASI:
                    ccprintf(os, "%%asi");
                    break;
                  case MISCREG_FPRS:
                    ccprintf(os, "%%fprs");
                    break;
                  case MISCREG_PCR:
                    ccprintf(os, "%%pcr");
                    break;
                  case MISCREG_PIC:
                    ccprintf(os, "%%pic");
                    break;
                  case MISCREG_GSR:
                    ccprintf(os, "%%gsr");
                    break;
                  case MISCREG_SOFTINT:
                    ccprintf(os, "%%softint");
                    break;
                  case MISCREG_SOFTINT_SET:
                    ccprintf(os, "%%softint_set");
                    break;
                  case MISCREG_SOFTINT_CLR:
                    ccprintf(os, "%%softint_clr");
                    break;
                  case MISCREG_TICK_CMPR:
                    ccprintf(os, "%%tick_cmpr");
                    break;
                  case MISCREG_STICK:
                    ccprintf(os, "%%stick");
                    break;
                  case MISCREG_STICK_CMPR:
                    ccprintf(os, "%%stick_cmpr");
                    break;
                  case MISCREG_TPC:
                    ccprintf(os, "%%tpc");
                    break;
                  case MISCREG_TNPC:
                    ccprintf(os, "%%tnpc");
                    break;
                  case MISCREG_TSTATE:
                    ccprintf(os, "%%tstate");
                    break;
                  case MISCREG_TT:
                    ccprintf(os, "%%tt");
                    break;
                  case MISCREG_TICK:
                    ccprintf(os, "%%tick");
                    break;
                  case MISCREG_TBA:
                    ccprintf(os, "%%tba");
                    break;
                  case MISCREG_PSTATE:
                    ccprintf(os, "%%pstate");
                    break;
                  case MISCREG_TL:
                    ccprintf(os, "%%tl");
                    break;
                  case MISCREG_PIL:
                    ccprintf(os, "%%pil");
                    break;
                  case MISCREG_CWP:
                    ccprintf(os, "%%cwp");
                    break;
                  case MISCREG_GL:
                    ccprintf(os, "%%gl");
                    break;
                  case MISCREG_HPSTATE:
                    ccprintf(os, "%%hpstate");
                    break;
                  case MISCREG_HTSTATE:
                    ccprintf(os, "%%htstate");
                    break;
                  case MISCREG_HINTP:
                    ccprintf(os, "%%hintp");
                    break;
                  case MISCREG_HTBA:
                    ccprintf(os, "%%htba");
                    break;
                  case MISCREG_HSTICK_CMPR:
                    ccprintf(os, "%%hstick_cmpr");
                    break;
                  case MISCREG_HVER:
                    ccprintf(os, "%%hver");
                    break;
                  case MISCREG_STRAND_STS_REG:
                    ccprintf(os, "%%strand_sts_reg");
                    break;
                  case MISCREG_FSR:
                    ccprintf(os, "%%fsr");
                    break;
                  default:
                    ccprintf(os, "%%ctrl%d", reg - Ctrl_Base_DepTag);
                }
            }
        }

        std::string
        SparcStaticInst::generateDisassembly(Addr pc,
                const SymbolTable *symtab) const
        {
            std::stringstream ss;

            printMnemonic(ss, mnemonic);

            // just print the first two source regs... if there's
            // a third one, it's a read-modify-write dest (Rc),
            // e.g. for CMOVxx
            if (_numSrcRegs > 0)
                printReg(ss, _srcRegIdx[0]);
            if (_numSrcRegs > 1) {
                ss << ",";
                printReg(ss, _srcRegIdx[1]);
            }

            // just print the first dest... if there's a second one,
            // it's generally implicit
            if (_numDestRegs > 0) {
                if (_numSrcRegs > 0)
                    ss << ",";
                    printReg(ss, _destRegIdx[0]);
            }

            return ss.str();
        }

        bool
        passesFpCondition(uint32_t fcc, uint32_t condition)
        {
            bool u = (fcc == 3);
            bool g = (fcc == 2);
            bool l = (fcc == 1);
            bool e = (fcc == 0);
            switch (condition) {
              case FAlways:
                return 1;
              case FNever:
                return 0;
              case FUnordered:
                return u;
              case FGreater:
                return g;
              case FUnorderedOrGreater:
                return u || g;
              case FLess:
                return l;
              case FUnorderedOrLess:
                return u || l;
              case FLessOrGreater:
                return l || g;
              case FNotEqual:
                return l || g || u;
              case FEqual:
                return e;
              case FUnorderedOrEqual:
                return u || e;
              case FGreaterOrEqual:
                return g || e;
              case FUnorderedOrGreaterOrEqual:
                return u || g || e;
              case FLessOrEqual:
                return l || e;
              case FUnorderedOrLessOrEqual:
                return u || l || e;
              case FOrdered:
                return e || l || g;
            }
            panic("Tried testing condition nonexistant "
                    "condition code %d", condition);
        }

        bool
        passesCondition(uint32_t codes, uint32_t condition)
        {
            CondCodes condCodes;
            condCodes.bits =  0;
            condCodes.c = codes & 0x1 ? 1 : 0;
            condCodes.v = codes & 0x2 ? 1 : 0;
            condCodes.z = codes & 0x4 ? 1 : 0;
            condCodes.n = codes & 0x8 ? 1 : 0;

            switch (condition) {
              case Always:
                return true;
              case Never:
                return false;
              case NotEqual:
                return !condCodes.z;
              case Equal:
                return condCodes.z;
              case Greater:
                return !(condCodes.z | (condCodes.n ^ condCodes.v));
              case LessOrEqual:
                return condCodes.z | (condCodes.n ^ condCodes.v);
              case GreaterOrEqual:
                return !(condCodes.n ^ condCodes.v);
              case Less:
                return (condCodes.n ^ condCodes.v);
              case GreaterUnsigned:
                return !(condCodes.c | condCodes.z);
              case LessOrEqualUnsigned:
                return (condCodes.c | condCodes.z);
              case CarryClear:
                return !condCodes.c;
              case CarrySet:
                return condCodes.c;
              case Positive:
                return !condCodes.n;
              case Negative:
                return condCodes.n;
              case OverflowClear:
                return !condCodes.v;
              case OverflowSet:
                return condCodes.v;
            }
            panic("Tried testing condition nonexistant "
                    "condition code %d", condition);
        }
}};

output exec {{
    /// Check "FP enabled" machine status bit.  Called when executing any FP
    /// instruction.
    /// @retval Full-system mode: NoFault if FP is enabled, FpDisabled
    /// if not.  Non-full-system mode: always returns NoFault.
    static inline Fault
    checkFpEnableFault(%(CPU_exec_context)s *xc)
    {
        if (FullSystem) {
            PSTATE pstate = xc->readMiscReg(MISCREG_PSTATE);
            if (pstate.pef && xc->readMiscReg(MISCREG_FPRS) & 0x4) {
                return NoFault;
            } else {
                return new FpDisabled;
            }
        } else {
            return NoFault;
        }
    }
}};