// Copyright (c) 2008 The Hewlett-Packard Development Company
// All rights reserved.
//
// The license below extends only to copyright in the software and shall
// not be construed as granting a license to any other intellectual
// property including but not limited to intellectual property relating
// to a hardware implementation of the functionality of the software
// licensed hereunder.  You may use the software subject to the license
// terms below provided that you ensure that this notice is replicated
// unmodified and in its entirety in all distributions of the software,
// modified or unmodified, in source code or in binary form.
//
// 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: Gabe Black

//////////////////////////////////////////////////////////////////////////
//
// Debug Microops
//
//////////////////////////////////////////////////////////////////////////

output header {{
    class MicroDebug : public X86ISA::X86MicroopBase
    {
      protected:
        std::shared_ptr<GenericISA::M5DebugFault> fault;

      public:
        MicroDebug(ExtMachInst _machInst, const char *mnem,
                const char *instMnem, uint64_t setFlags,
                GenericISA::M5DebugFault *_fault);

        Fault
        execute(ExecContext *xc, Trace::InstRecord *traceData) const
        {
            return fault;
        }

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

            printMnemonic(response, instMnem, mnemonic);
            response << "\"" << fault->message() << "\"";

            return response.str();
        }
    };

    class MicroDebugFlags : public MicroDebug
    {
      protected:
        uint8_t cc;

      public:
        MicroDebugFlags(ExtMachInst _machInst, const char *mnem,
                const char *instMnem, uint64_t setFlags,
                GenericISA::M5DebugFault *_fault, uint8_t _cc);

        Fault execute(ExecContext *, Trace::InstRecord *) const;
    };
}};

output decoder {{
    MicroDebug::MicroDebug(ExtMachInst _machInst, const char *mnem,
            const char *instMnem, uint64_t setFlags,
            GenericISA::M5DebugFault *_fault) :
        X86ISA::X86MicroopBase(_machInst, mnem, instMnem,
                               setFlags, No_OpClass),
        fault(_fault)
    {}
}};

def template MicroDebugFlagsExecute {{
        Fault
        %(class_name)s::execute(ExecContext *xc,
                Trace::InstRecord *traceData) const
        {
            %(op_decl)s
            %(op_rd)s
            if (%(cond_test)s) {
                return %(base_class)s::execute(xc, traceData);
            } else {
                return NoFault;
            }
        }
}};

def template MicroDebugFlagsConstructor {{
    %(class_name)s::%(class_name)s(
            ExtMachInst machInst, const char *mnem,
            const char *instMnem, uint64_t setFlags,
            GenericISA::M5DebugFault *_fault, uint8_t _cc) :
        %(base_class)s(machInst, mnem, instMnem, setFlags, _fault),
        cc(_cc)
    {
        %(constructor)s;
    }
}};

let {{
    iop = InstObjParams("", "MicroDebugFlags", "MicroDebug",
            {"code": "",
             "cond_test": "checkCondition(ccFlagBits | cfofBits | \
                                              dfBit | ecfBit | ezfBit, cc)"})
    exec_output = MicroDebugFlagsExecute.subst(iop)
    decoder_output = MicroDebugFlagsConstructor.subst(iop)
}};

let {{
    class MicroDebug(X86Microop):
        def __init__(self, name, fault, message, once, flags):
            self.name = name
            self.fault = fault
            self.message = message
            self.once = once
            self.flags = flags
            if flags and not isinstance(flags, (list, tuple)):
                raise Exception, "flags must be a list or tuple of flags"

            self.className = "MicroDebugFlags" if flags else "MicroDebug"

        def getAllocator(self, microFlags):
            if self.once:
                fault_allocator_template = \
                    "new %(fault_type)s(%(token)s, %(message)s)"
            else:
                fault_allocator_template = \
                    "new %(fault_type)s(%(message)s)"
            fault_allocator = fault_allocator_template % {
                "fault_type": self.fault,
                "token": "std::string(\"%s\")" % self.message,
                "message": "\"%s\"" % self.message
            }

            args = ["machInst", "\"%s\"" % self.name, "macrocodeBlock",
                self.microFlagsText(microFlags), fault_allocator]

            if self.flags:
                args.append(" | ".join(self.flags))

            return "new " + self.className + "(" + ", ".join(args) + ")"

    def buildDebugMicro(name, with_once=False):
        global microopClasses

        fault_class = "GenericISA::M5" + name.capitalize() + "Fault"

        class MicroDebugChild(MicroDebug):
            def __init__(self, message, flags=None):
                super(MicroDebugChild, self).__init__(
                        name, fault_class, message, False, flags)

        microopClasses[name] = MicroDebugChild

        if with_once:
            fault_once_class = \
                "GenericISA::M5" + name.capitalize() + "OnceFault"
            name_once = name + "_once"

            class MicroDebugOnceChild(MicroDebug):
                def __init__(self, message, flags=None):
                    super(MicroDebugOnceChild, self).__init__(
                            name_once, fault_once_class, message, True, flags)

            microopClasses[name_once] = MicroDebugOnceChild

    buildDebugMicro("panic")
    buildDebugMicro("fatal")
    buildDebugMicro("hack", True)
    buildDebugMicro("inform", True)
    buildDebugMicro("warn", True)
}};