/* Copyright (c) 2007-2008 The Florida State University
 * 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: Stephen Hines
 */
#ifndef __ARCH_ARM_MACROMEM_HH__
#define __ARCH_ARM_MACROMEM_HH__

#include "arch/arm/insts/pred_inst.hh"

namespace ArmISA
{

static inline unsigned int
number_of_ones(int32_t val)
{
    uint32_t ones = 0;
    for (int i = 0; i < 32; i++ )
    {
        if ( val & (1<<i) )
            ones++;
    }
    return ones;
}

/**
 * Arm Macro Memory operations like LDM/STM
 */
class ArmMacroMemoryOp : public PredMacroOp
{
    protected:
    /// Memory request flags.  See mem_req_base.hh.
    unsigned memAccessFlags;
    /// Pointer to EAComp object.
    const StaticInstPtr eaCompPtr;
    /// Pointer to MemAcc object.
    const StaticInstPtr memAccPtr;

    uint32_t reglist;
    uint32_t ones;
    uint32_t puswl,
             prepost,
             up,
             psruser,
             writeback,
             loadop;

    ArmMacroMemoryOp(const char *mnem, ExtMachInst _machInst,
                     OpClass __opClass,
                     StaticInstPtr _eaCompPtr = nullStaticInstPtr,
                     StaticInstPtr _memAccPtr = nullStaticInstPtr)
            : PredMacroOp(mnem, _machInst, __opClass),
                          memAccessFlags(0),
                          eaCompPtr(_eaCompPtr), memAccPtr(_memAccPtr),
                          reglist(machInst.regList), ones(0),
                          puswl(machInst.puswl),
                          prepost(machInst.puswl.prepost),
                          up(machInst.puswl.up),
                          psruser(machInst.puswl.psruser),
                          writeback(machInst.puswl.writeback),
                          loadop(machInst.puswl.loadOp)
    {
        ones = number_of_ones(reglist);
        numMicroops = ones + writeback + 1;
        // Remember that writeback adds a uop
        microOps = new StaticInstPtr[numMicroops];
    }
};

/**
 * Arm Macro FPA operations to fix ldfd and stfd instructions
 */
class ArmMacroFPAOp : public PredMacroOp
{
    protected:
    uint32_t puswl,
             prepost,
             up,
             psruser,
             writeback,
             loadop;
    int32_t disp8;

    ArmMacroFPAOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass)
        : PredMacroOp(mnem, _machInst, __opClass),
                      puswl(machInst.puswl),
                      prepost(machInst.puswl.prepost),
                      up(machInst.puswl.up),
                      psruser(machInst.puswl.psruser),
                      writeback(machInst.puswl.writeback),
                      loadop(machInst.puswl.loadOp),
                      disp8(machInst.immed7_0 << 2)
    {
        numMicroops = 3 + writeback;
        microOps = new StaticInstPtr[numMicroops];
    }
};

/**
 * Arm Macro FM operations to fix lfm and sfm
 */
class ArmMacroFMOp : public PredMacroOp
{
    protected:
    uint32_t punwl,
             prepost,
             up,
             n1bit,
             writeback,
             loadop,
             n0bit,
             count;
    int32_t disp8;

    ArmMacroFMOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass)
        : PredMacroOp(mnem, _machInst, __opClass),
                      punwl(machInst.punwl),
                      prepost(machInst.puswl.prepost),
                      up(machInst.puswl.up),
                      n1bit(machInst.opcode22),
                      writeback(machInst.puswl.writeback),
                      loadop(machInst.puswl.loadOp),
                      n0bit(machInst.opcode15),
                      disp8(machInst.immed7_0 << 2)
    {
        // Transfer 1-4 registers based on n1 and n0 bits (with 00 repr. 4)
        count = (n1bit << 1) | n0bit;
        if (count == 0)
            count = 4;
        numMicroops = (3*count) + writeback;
        microOps = new StaticInstPtr[numMicroops];
    }
};
}

#endif //__ARCH_ARM_INSTS_MACROMEM_HH__