// -*- mode:c++ -*-

// Copyright (c) 2003-2005 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: Steve Reinhardt
//          Kevin Lim

////////////////////////////////////////////////////////////////////
//
// Memory-format instructions: LoadAddress, Load, Store
//

output header {{
    /**
     * Base class for general Alpha memory-format instructions.
     */
    class Memory : public AlphaStaticInst
    {
      protected:

        /// Memory request flags.  See mem_req_base.hh.
        Request::Flags memAccessFlags;

        /// Constructor
        Memory(const char *mnem, ExtMachInst _machInst, OpClass __opClass)
            : AlphaStaticInst(mnem, _machInst, __opClass)
        {
        }

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

    /**
     * Base class for memory-format instructions using a 32-bit
     * displacement (i.e. most of them).
     */
    class MemoryDisp32 : public Memory
    {
      protected:
        /// Displacement for EA calculation (signed).
        int32_t disp;

        /// Constructor.
        MemoryDisp32(const char *mnem, ExtMachInst _machInst, OpClass __opClass)
            : Memory(mnem, _machInst, __opClass),
              disp(MEMDISP)
        {
        }
    };


    /**
     * Base class for a few miscellaneous memory-format insts
     * that don't interpret the disp field: wh64, fetch, fetch_m, ecb.
     * None of these instructions has a destination register either.
     */
    class MemoryNoDisp : public Memory
    {
      protected:
        /// Constructor
        MemoryNoDisp(const char *mnem, ExtMachInst _machInst, OpClass __opClass)
            : Memory(mnem, _machInst, __opClass)
        {
        }

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


output decoder {{
    std::string
    Memory::generateDisassembly(Addr pc, const SymbolTable *symtab) const
    {
        return csprintf("%-10s %c%d,%d(r%d)", mnemonic,
                        flags[IsFloating] ? 'f' : 'r', RA, MEMDISP, RB);
    }

    std::string
    MemoryNoDisp::generateDisassembly(Addr pc, const SymbolTable *symtab) const
    {
        return csprintf("%-10s (r%d)", mnemonic, RB);
    }
}};

def format LoadAddress(code) {{
    iop = InstObjParams(name, Name, 'MemoryDisp32', code)
    header_output = BasicDeclare.subst(iop)
    decoder_output = BasicConstructor.subst(iop)
    decode_block = BasicDecode.subst(iop)
    exec_output = BasicExecute.subst(iop)
}};


def template LoadStoreDeclare {{
    /**
     * Static instruction class for "%(mnemonic)s".
     */
    class %(class_name)s : public %(base_class)s
    {
      public:

        /// Constructor.
        %(class_name)s(ExtMachInst machInst);

        %(BasicExecDeclare)s

        %(EACompDeclare)s

        %(InitiateAccDeclare)s

        %(CompleteAccDeclare)s
    };
}};


def template EACompDeclare {{
    Fault eaComp(%(CPU_exec_context)s *, Trace::InstRecord *) const;
}};

def template InitiateAccDeclare {{
    Fault initiateAcc(%(CPU_exec_context)s *, Trace::InstRecord *) const;
}};


def template CompleteAccDeclare {{
    Fault completeAcc(PacketPtr, %(CPU_exec_context)s *,
                      Trace::InstRecord *) const;
}};

def template LoadStoreConstructor {{
    inline %(class_name)s::%(class_name)s(ExtMachInst machInst)
         : %(base_class)s("%(mnemonic)s", machInst, %(op_class)s)
    {
        %(constructor)s;
    }
}};

def template EACompExecute {{
    Fault %(class_name)s::eaComp(%(CPU_exec_context)s *xc,
                                  Trace::InstRecord *traceData) const
    {
        Addr EA;
        Fault fault = NoFault;

        %(fp_enable_check)s;
        %(op_decl)s;
        %(op_rd)s;
        %(ea_code)s;

        if (fault == NoFault) {
            %(op_wb)s;
            xc->setEA(EA);
        }

        return fault;
    }
}};


def template LoadExecute {{
    Fault %(class_name)s::execute(%(CPU_exec_context)s *xc,
                                  Trace::InstRecord *traceData) const
    {
        Addr EA;
        Fault fault = NoFault;

        %(fp_enable_check)s;
        %(op_decl)s;
        %(op_rd)s;
        %(ea_code)s;

        if (fault == NoFault) {
            fault = xc->read(EA, (uint%(mem_acc_size)d_t&)Mem, memAccessFlags);
            %(memacc_code)s;
        }

        if (fault == NoFault) {
            %(op_wb)s;
        }

        return fault;
    }
}};


def template LoadInitiateAcc {{
    Fault %(class_name)s::initiateAcc(%(CPU_exec_context)s *xc,
                                      Trace::InstRecord *traceData) const
    {
        Addr EA;
        Fault fault = NoFault;

        %(fp_enable_check)s;
        %(op_src_decl)s;
        %(op_rd)s;
        %(ea_code)s;

        if (fault == NoFault) {
            fault = xc->read(EA, (uint%(mem_acc_size)d_t &)Mem, memAccessFlags);
        }

        return fault;
    }
}};


def template LoadCompleteAcc {{
    Fault %(class_name)s::completeAcc(PacketPtr pkt,
                                      %(CPU_exec_context)s *xc,
                                      Trace::InstRecord *traceData) const
    {
        Fault fault = NoFault;

        %(fp_enable_check)s;
        %(op_decl)s;

        Mem = pkt->get<typeof(Mem)>();

        if (fault == NoFault) {
            %(memacc_code)s;
        }

        if (fault == NoFault) {
            %(op_wb)s;
        }

        return fault;
    }
}};


def template StoreExecute {{
    Fault %(class_name)s::execute(%(CPU_exec_context)s *xc,
                                  Trace::InstRecord *traceData) const
    {
        Addr EA;
        Fault fault = NoFault;

        %(fp_enable_check)s;
        %(op_decl)s;
        %(op_rd)s;
        %(ea_code)s;

        if (fault == NoFault) {
            %(memacc_code)s;
        }

        if (fault == NoFault) {
            fault = xc->write((uint%(mem_acc_size)d_t&)Mem, EA,
                              memAccessFlags, NULL);
        }

        if (fault == NoFault) {
            %(postacc_code)s;
        }

        if (fault == NoFault) {
            %(op_wb)s;
        }

        return fault;
    }
}};

def template StoreCondExecute {{
    Fault %(class_name)s::execute(%(CPU_exec_context)s *xc,
                                  Trace::InstRecord *traceData) const
    {
        Addr EA;
        Fault fault = NoFault;
        uint64_t write_result = 0;

        %(fp_enable_check)s;
        %(op_decl)s;
        %(op_rd)s;
        %(ea_code)s;

        if (fault == NoFault) {
            %(memacc_code)s;
        }

        if (fault == NoFault) {
            fault = xc->write((uint%(mem_acc_size)d_t&)Mem, EA,
                              memAccessFlags, &write_result);
        }

        if (fault == NoFault) {
            %(postacc_code)s;
        }

        if (fault == NoFault) {
            %(op_wb)s;
        }

        return fault;
    }
}};

def template StoreInitiateAcc {{
    Fault %(class_name)s::initiateAcc(%(CPU_exec_context)s *xc,
                                      Trace::InstRecord *traceData) const
    {
        Addr EA;
        Fault fault = NoFault;

        %(fp_enable_check)s;
        %(op_decl)s;
        %(op_rd)s;
        %(ea_code)s;

        if (fault == NoFault) {
            %(memacc_code)s;
        }

        if (fault == NoFault) {
            fault = xc->write((uint%(mem_acc_size)d_t&)Mem, EA,
                              memAccessFlags, NULL);
        }

        return fault;
    }
}};


def template StoreCompleteAcc {{
    Fault %(class_name)s::completeAcc(PacketPtr pkt,
                                      %(CPU_exec_context)s *xc,
                                      Trace::InstRecord *traceData) const
    {
        return NoFault;
    }
}};


def template StoreCondCompleteAcc {{
    Fault %(class_name)s::completeAcc(PacketPtr pkt,
                                      %(CPU_exec_context)s *xc,
                                      Trace::InstRecord *traceData) const
    {
        Fault fault = NoFault;

        %(fp_enable_check)s;
        %(op_dest_decl)s;

        uint64_t write_result = pkt->req->getExtraData();

        if (fault == NoFault) {
            %(postacc_code)s;
        }

        if (fault == NoFault) {
            %(op_wb)s;
        }

        return fault;
    }
}};


def template MiscExecute {{
    Fault %(class_name)s::execute(%(CPU_exec_context)s *xc,
                                  Trace::InstRecord *traceData) const
    {
        Addr EA;
        Fault fault = NoFault;

        %(fp_enable_check)s;
        %(op_decl)s;
        %(op_rd)s;
        %(ea_code)s;

        warn_once("Prefetch instrutions is Alpha do not do anything\n");
        if (fault == NoFault) {
            %(memacc_code)s;
        }

        return NoFault;
    }
}};

// Prefetches in Alpha don't actually do anything
// They just build an effective address and complete
def template MiscInitiateAcc {{
    Fault %(class_name)s::initiateAcc(%(CPU_exec_context)s *xc,
                                      Trace::InstRecord *traceData) const
    {
        warn("initiateAcc undefined: Misc instruction does not support split "
             "access method!");
        return NoFault;
    }
}};


def template MiscCompleteAcc {{
    Fault %(class_name)s::completeAcc(PacketPtr pkt,
                                      %(CPU_exec_context)s *xc,
                                      Trace::InstRecord *traceData) const
    {
        warn("completeAcc undefined: Misc instruction does not support split "
             "access method!");

        return NoFault;
    }
}};


// load instructions use Ra as dest, so check for
// Ra == 31 to detect nops
def template LoadNopCheckDecode {{
 {
     AlphaStaticInst *i = new %(class_name)s(machInst);
     if (RA == 31) {
         i = makeNop(i);
     }
     return i;
 }
}};


// for some load instructions, Ra == 31 indicates a prefetch (not a nop)
def template LoadPrefetchCheckDecode {{
 {
     if (RA != 31) {
         return new %(class_name)s(machInst);
     }
     else {
         return new %(class_name)sPrefetch(machInst);
     }
 }
}};


let {{
def LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags,
                  postacc_code = '', base_class = 'MemoryDisp32',
                  decode_template = BasicDecode, exec_template_base = ''):
    # Make sure flags are in lists (convert to lists if not).
    mem_flags = makeList(mem_flags)
    inst_flags = makeList(inst_flags)

    # Some CPU models execute the memory operation as an atomic unit,
    # while others want to separate them into an effective address
    # computation and a memory access operation.  As a result, we need
    # to generate three StaticInst objects.  Note that the latter two
    # are nested inside the larger "atomic" one.

    # Generate InstObjParams for each of the three objects.  Note that
    # they differ only in the set of code objects contained (which in
    # turn affects the object's overall operand list).
    iop = InstObjParams(name, Name, base_class,
                        { 'ea_code':ea_code, 'memacc_code':memacc_code, 'postacc_code':postacc_code },
                        inst_flags)
    memacc_iop = InstObjParams(name, Name, base_class,
                        { 'memacc_code':memacc_code, 'postacc_code':postacc_code },
                        inst_flags)

    if mem_flags:
        mem_flags = [ 'Request::%s' % flag for flag in mem_flags ]
        s = '\n\tmemAccessFlags = ' + string.join(mem_flags, '|') + ';'
        iop.constructor += s
        memacc_iop.constructor += s

    # select templates

    # The InitiateAcc template is the same for StoreCond templates as the
    # corresponding Store template..
    StoreCondInitiateAcc = StoreInitiateAcc

    fullExecTemplate = eval(exec_template_base + 'Execute')
    initiateAccTemplate = eval(exec_template_base + 'InitiateAcc')
    completeAccTemplate = eval(exec_template_base + 'CompleteAcc')

    # (header_output, decoder_output, decode_block, exec_output)
    return (LoadStoreDeclare.subst(iop),
            LoadStoreConstructor.subst(iop),
            decode_template.subst(iop),
            fullExecTemplate.subst(iop)
            + EACompExecute.subst(iop)
            + initiateAccTemplate.subst(iop)
            + completeAccTemplate.subst(iop))
}};

def format LoadOrNop(memacc_code, ea_code = {{ EA = Rb + disp; }},
                     mem_flags = [], inst_flags = []) {{
    (header_output, decoder_output, decode_block, exec_output) = \
        LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags,
                      decode_template = LoadNopCheckDecode,
                      exec_template_base = 'Load')
}};


// Note that the flags passed in apply only to the prefetch version
def format LoadOrPrefetch(memacc_code, ea_code = {{ EA = Rb + disp; }},
                          mem_flags = [], pf_flags = [], inst_flags = []) {{
    # declare the load instruction object and generate the decode block
    (header_output, decoder_output, decode_block, exec_output) = \
        LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags,
                      decode_template = LoadPrefetchCheckDecode,
                      exec_template_base = 'Load')

    # Declare the prefetch instruction object.

    # Make sure flag args are lists so we can mess with them.
    mem_flags = makeList(mem_flags)
    pf_flags = makeList(pf_flags)
    inst_flags = makeList(inst_flags)

    pf_mem_flags = mem_flags + pf_flags + ['PREFETCH']
    pf_inst_flags = inst_flags

    (pf_header_output, pf_decoder_output, _, pf_exec_output) = \
        LoadStoreBase(name, Name + 'Prefetch', ea_code, ';',
                      pf_mem_flags, pf_inst_flags, exec_template_base = 'Misc')

    header_output += pf_header_output
    decoder_output += pf_decoder_output
    exec_output += pf_exec_output
}};


def format Store(memacc_code, ea_code = {{ EA = Rb + disp; }},
                 mem_flags = [], inst_flags = []) {{
    (header_output, decoder_output, decode_block, exec_output) = \
        LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags,
                      exec_template_base = 'Store')
}};


def format StoreCond(memacc_code, postacc_code,
                     ea_code = {{ EA = Rb + disp; }},
                     mem_flags = [], inst_flags = []) {{
    (header_output, decoder_output, decode_block, exec_output) = \
        LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags,
                      postacc_code, exec_template_base = 'StoreCond')
}};


// Use 'MemoryNoDisp' as base: for wh64, fetch, ecb
def format MiscPrefetch(ea_code, memacc_code,
                        mem_flags = [], inst_flags = []) {{
    (header_output, decoder_output, decode_block, exec_output) = \
        LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags,
                      base_class = 'MemoryNoDisp', exec_template_base = 'Misc')
}};