/*
 * \file       trc_pkt_decode_ptm.cpp
 * \brief      OpenCSD : PTM packet decoder.
 * 
 * \copyright  Copyright (c) 2016, ARM Limited. All Rights Reserved.
 */

/* 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice, 
 * this list of conditions and the following disclaimer.
 * 
 * 2. 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. 
 * 
 * 3. Neither the name of the copyright holder 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 HOLDER 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. 
 */ 

#include <sstream>
#include "opencsd/ptm/trc_pkt_decode_ptm.h"

#define DCD_NAME "DCD_PTM"

TrcPktDecodePtm::TrcPktDecodePtm()
    : TrcPktDecodeBase(DCD_NAME)
{
    initDecoder();
}

TrcPktDecodePtm::TrcPktDecodePtm(int instIDNum)
    : TrcPktDecodeBase(DCD_NAME,instIDNum)
{
    initDecoder();
}

TrcPktDecodePtm::~TrcPktDecodePtm()
{
}

/*********************** implementation packet decoding interface */

ocsd_datapath_resp_t TrcPktDecodePtm::processPacket()
{
    ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
    bool bPktDone = false;

    while(!bPktDone)
    {
        switch(m_curr_state)
        {
        case NO_SYNC:
            // no sync - output a no sync packet then transition to wait sync.
            m_output_elem.elem_type = OCSD_GEN_TRC_ELEM_NO_SYNC;
            m_output_elem.unsync_eot_info = m_unsync_info;
            resp = outputTraceElement(m_output_elem);
            m_curr_state = (m_curr_packet_in->getType() == PTM_PKT_A_SYNC) ? WAIT_ISYNC : WAIT_SYNC;
            bPktDone = true;
            break;

        case WAIT_SYNC:
            if(m_curr_packet_in->getType() == PTM_PKT_A_SYNC)
                m_curr_state = WAIT_ISYNC;
            bPktDone = true;
            break;

        case WAIT_ISYNC:
            if(m_curr_packet_in->getType() == PTM_PKT_I_SYNC)
                m_curr_state = DECODE_PKTS;
            else 
                bPktDone = true;
            break;

        case DECODE_PKTS:
            resp = decodePacket();
            bPktDone = true;
            break;

        default:
             // should only see these after a _WAIT resp - in flush handler 
        case CONT_ISYNC: 
        case CONT_ATOM:
            bPktDone = true;
            // throw a decoder error
            break;
        }
    }
    return resp;
}

ocsd_datapath_resp_t TrcPktDecodePtm::onEOT()
{
    ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
    // shouldn't be any packets left to be processed - flush shoudl have done this.
    // just output the end of trace marker
    m_output_elem.setType(OCSD_GEN_TRC_ELEM_EO_TRACE);
    m_output_elem.setUnSyncEOTReason(UNSYNC_EOT);
    resp = outputTraceElement(m_output_elem);
    return resp;
}

ocsd_datapath_resp_t TrcPktDecodePtm::onReset()
{
    ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
    m_unsync_info = UNSYNC_RESET_DECODER;
    resetDecoder();
    return resp;
}

ocsd_datapath_resp_t TrcPktDecodePtm::onFlush()
{
    ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
    resp = contProcess();
    return resp;
}

// atom and isync packets can have multiple ouput packets that can be _WAITed mid stream.
ocsd_datapath_resp_t TrcPktDecodePtm::contProcess()
{
    ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
    switch(m_curr_state)
    { 
    case CONT_ISYNC:
        resp = processIsync();
        break;

    case CONT_ATOM:
        resp = processAtom();
        break;

    case CONT_WPUP:
        resp = processWPUpdate();
        break;        

    case CONT_BRANCH:
        resp = processBranch();
        break;

    default: break; // not a state that requires further processing
    }

    if(OCSD_DATA_RESP_IS_CONT(resp) && processStateIsCont())
        m_curr_state = DECODE_PKTS; // continue packet processing - assuming we have not degraded into an unsynced state.

    return resp;
}

ocsd_err_t TrcPktDecodePtm::onProtocolConfig()
{
    ocsd_err_t err = OCSD_OK;
    if(m_config == 0)
        return OCSD_ERR_NOT_INIT;

    // static config - copy of CSID for easy reference
    m_CSID = m_config->getTraceID();

    // handle return stack implementation
    if (m_config->hasRetStack())
    {
        m_return_stack.set_active(m_config->enaRetStack());
#ifdef TRC_RET_STACK_DEBUG
        m_return_stack.set_dbg_logger(this);
#endif
    }
    
    // config options affecting decode
    m_instr_info.pe_type.profile = m_config->coreProfile();
    m_instr_info.pe_type.arch = m_config->archVersion();
    m_instr_info.dsb_dmb_waypoints = m_config->dmsbWayPt() ? 1 : 0;
    m_instr_info.wfi_wfe_branch = 0;
    return err;
}

/****************** local decoder routines */

void TrcPktDecodePtm::initDecoder()
{
    m_CSID = 0;
    m_instr_info.pe_type.profile = profile_Unknown;
    m_instr_info.pe_type.arch = ARCH_UNKNOWN;
    m_instr_info.dsb_dmb_waypoints = 0;
    m_unsync_info = UNSYNC_INIT_DECODER;
    resetDecoder();
}

void TrcPktDecodePtm::resetDecoder()
{
    m_curr_state = NO_SYNC;
    m_need_isync = true;    // need context to start.

    m_instr_info.isa = ocsd_isa_unknown;
    m_mem_nacc_pending = false;

    m_pe_context.ctxt_id_valid = 0;
    m_pe_context.bits64 = 0;
    m_pe_context.vmid_valid = 0;
    m_pe_context.exception_level = ocsd_EL_unknown;
    m_pe_context.security_level = ocsd_sec_secure;
    m_pe_context.el_valid = 0;
    
    m_curr_pe_state.instr_addr = 0x0;
    m_curr_pe_state.isa = ocsd_isa_unknown;
    m_curr_pe_state.valid = false;

    m_atoms.clearAll();
    m_output_elem.init();
}

ocsd_datapath_resp_t TrcPktDecodePtm::decodePacket()
{
    ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
    switch(m_curr_packet_in->getType())
    {
        // ignore these from trace o/p point of veiw
    case PTM_PKT_NOTSYNC:   
    case PTM_PKT_INCOMPLETE_EOT:
    case PTM_PKT_NOERROR:
        break;

        // bad / reserved packet - need to wait for next sync point
    case PTM_PKT_BAD_SEQUENCE:
    case PTM_PKT_RESERVED:
        m_curr_state = WAIT_SYNC;
        m_need_isync = true;    // need context to re-start.
        m_output_elem.setType(OCSD_GEN_TRC_ELEM_NO_SYNC);
        resp = outputTraceElement(m_output_elem);
        break;

        // packets we can ignore if in sync
    case PTM_PKT_A_SYNC:
    case PTM_PKT_IGNORE:
        break;

        // 
    case PTM_PKT_I_SYNC:
        resp = processIsync();
        break;

    case PTM_PKT_BRANCH_ADDRESS:
        resp = processBranch();
        break;

    case PTM_PKT_TRIGGER:
        m_output_elem.setType(OCSD_GEN_TRC_ELEM_EVENT);
        m_output_elem.setEvent(EVENT_TRIGGER, 0);
        resp = outputTraceElement(m_output_elem);
        break;

    case PTM_PKT_WPOINT_UPDATE:
        resp = processWPUpdate();
        break;

    case PTM_PKT_CONTEXT_ID:
        {
            bool bUpdate = true;  
            // see if this is a change
            if((m_pe_context.ctxt_id_valid) && (m_pe_context.context_id == m_curr_packet_in->context.ctxtID))
                bUpdate = false;
            if(bUpdate)
            {
                m_pe_context.context_id = m_curr_packet_in->context.ctxtID;
                m_pe_context.ctxt_id_valid = 1;
                m_output_elem.setType(OCSD_GEN_TRC_ELEM_PE_CONTEXT);
                m_output_elem.setContext(m_pe_context);
                resp = outputTraceElement(m_output_elem);
            }
        }        
        break;

    case PTM_PKT_VMID:
        {
            bool bUpdate = true;  
            // see if this is a change
            if((m_pe_context.vmid_valid) && (m_pe_context.vmid == m_curr_packet_in->context.VMID))
                bUpdate = false;
            if(bUpdate)
            {
                m_pe_context.vmid = m_curr_packet_in->context.VMID;
                m_pe_context.vmid_valid = 1;
                m_output_elem.setType(OCSD_GEN_TRC_ELEM_PE_CONTEXT);
                m_output_elem.setContext(m_pe_context);
                resp = outputTraceElement(m_output_elem);
            }
        }   
        break;

    case PTM_PKT_ATOM:
        if(m_curr_pe_state.valid)
        {
            m_atoms.initAtomPkt(m_curr_packet_in->getAtom(),m_index_curr_pkt);
            resp = processAtom();
        }
        break;

    case PTM_PKT_TIMESTAMP:
        m_output_elem.setType(OCSD_GEN_TRC_ELEM_TIMESTAMP);
        m_output_elem.timestamp = m_curr_packet_in->timestamp;
        if(m_curr_packet_in->cc_valid)
            m_output_elem.setCycleCount(m_curr_packet_in->cycle_count);
        resp = outputTraceElement(m_output_elem);
        break;

    case PTM_PKT_EXCEPTION_RET:
        m_output_elem.setType(OCSD_GEN_TRC_ELEM_EXCEPTION_RET);
        resp = outputTraceElement(m_output_elem);
        break;

    }
    return resp;
}

ocsd_datapath_resp_t TrcPktDecodePtm::processIsync()
{
    ocsd_datapath_resp_t resp = OCSD_RESP_CONT;

    // extract the I-Sync data if not re-entering after a _WAIT
    if(m_curr_state == DECODE_PKTS)
    {        
        m_curr_pe_state.instr_addr = m_curr_packet_in->getAddrVal();
        m_curr_pe_state.isa = m_curr_packet_in->getISA();
        m_curr_pe_state.valid = true;
        
        m_i_sync_pe_ctxt = m_curr_packet_in->ISAChanged();
        if(m_curr_packet_in->CtxtIDUpdated())
        {
            m_pe_context.context_id = m_curr_packet_in->getCtxtID();
            m_pe_context.ctxt_id_valid = 1;
            m_i_sync_pe_ctxt = true;
        }

        if(m_curr_packet_in->VMIDUpdated())
        {
            m_pe_context.vmid = m_curr_packet_in->getVMID();
            m_pe_context.vmid_valid = 1;
            m_i_sync_pe_ctxt = true;
        }
        m_pe_context.security_level = m_curr_packet_in->getNS() ? ocsd_sec_nonsecure : ocsd_sec_secure;
        
        if(m_need_isync || (m_curr_packet_in->iSyncReason() != iSync_Periodic))
        {
            m_output_elem.setType(OCSD_GEN_TRC_ELEM_TRACE_ON);
            m_output_elem.trace_on_reason = TRACE_ON_NORMAL;
            if(m_curr_packet_in->iSyncReason() == iSync_TraceRestartAfterOverflow)
                m_output_elem.trace_on_reason = TRACE_ON_OVERFLOW;
            else if(m_curr_packet_in->iSyncReason() == iSync_DebugExit)
                m_output_elem.trace_on_reason = TRACE_ON_EX_DEBUG;
            if(m_curr_packet_in->hasCC())
                m_output_elem.setCycleCount(m_curr_packet_in->getCCVal());
            resp = outputTraceElement(m_output_elem);           
        }
        else
        {
            // periodic - no output
            m_i_sync_pe_ctxt = false;
        }
        m_need_isync = false;   // got 1st Isync - can continue to process data.
        m_return_stack.flush();
    }
    
    if(m_i_sync_pe_ctxt && OCSD_DATA_RESP_IS_CONT(resp))
    {
        m_output_elem.setType(OCSD_GEN_TRC_ELEM_PE_CONTEXT);
        m_output_elem.setContext(m_pe_context);
        m_output_elem.setISA(m_curr_pe_state.isa);
        resp = outputTraceElement(m_output_elem); 
        m_i_sync_pe_ctxt = false;
    }

    // if wait and still stuff to process....
    if(OCSD_DATA_RESP_IS_WAIT(resp) && ( m_i_sync_pe_ctxt))
        m_curr_state = CONT_ISYNC;

    return resp;
}

// change of address and/or exception in program flow.
// implies E atom before the branch if none exception.
ocsd_datapath_resp_t TrcPktDecodePtm::processBranch()
{
    ocsd_datapath_resp_t resp = OCSD_RESP_CONT;

    // initial pass - decoding packet.
    if(m_curr_state == DECODE_PKTS)
    {
        // specific behviour if this is an exception packet.
        if(m_curr_packet_in->isBranchExcepPacket())
        {
            // exception - record address and output exception packet.
            m_output_elem.setType(OCSD_GEN_TRC_ELEM_EXCEPTION);
            m_output_elem.exception_number = m_curr_packet_in->excepNum();
            m_output_elem.excep_ret_addr = 0;
            if(m_curr_pe_state.valid)
            {
                m_output_elem.excep_ret_addr = 1;
                m_output_elem.en_addr = m_curr_pe_state.instr_addr;
            }
            // could be an associated cycle count
            if(m_curr_packet_in->hasCC())
                m_output_elem.setCycleCount(m_curr_packet_in->getCCVal());

            // output the element
            resp = outputTraceElement(m_output_elem);
        }
        else
        {
            // branch address only - implies E atom - need to output a range element based on the atom.
            if(m_curr_pe_state.valid)
                resp = processAtomRange(ATOM_E,"BranchAddr");
        }

        // now set the branch address for the next time.
        m_curr_pe_state.isa = m_curr_packet_in->getISA();
        m_curr_pe_state.instr_addr = m_curr_packet_in->getAddrVal();
        m_curr_pe_state.valid = true;
    }

    // atom range may return with NACC pending 
    checkPendingNacc(resp);

    // if wait and still stuff to process....
    if(OCSD_DATA_RESP_IS_WAIT(resp) && ( m_mem_nacc_pending))
        m_curr_state = CONT_BRANCH;

    return resp;
}

// effectively completes a range prior to exception or after many bytes of trace (>4096)
//
ocsd_datapath_resp_t TrcPktDecodePtm::processWPUpdate()
{
    ocsd_datapath_resp_t resp = OCSD_RESP_CONT;

    // if we need an address to run from then the WPUpdate will not form a range as 
    // we do not have a start point - still waiting for branch or other address packet
    if(m_curr_pe_state.valid)
    {
        // WP update implies atom - use E, we cannot be sure if the instruction passed its condition codes 
        // - though it doesn't really matter as it is not a branch so cannot change flow.
        resp = processAtomRange(ATOM_E,"WP update",TRACE_TO_ADDR_INCL,m_curr_packet_in->getAddrVal());
    }

    // atom range may return with NACC pending 
    checkPendingNacc(resp);

    // if wait and still stuff to process....
    if(OCSD_DATA_RESP_IS_WAIT(resp) && ( m_mem_nacc_pending))
        m_curr_state = CONT_WPUP;

    return resp;
}

// a single atom packet can result in multiple range outputs...need to be re-entrant in case we get a wait response.
// also need to handle nacc response from instruction walking routine
// 
ocsd_datapath_resp_t TrcPktDecodePtm::processAtom()
{
    ocsd_datapath_resp_t resp = OCSD_RESP_CONT;

    // loop to process all the atoms in the packet
    while(m_atoms.numAtoms() && m_curr_pe_state.valid && OCSD_DATA_RESP_IS_CONT(resp))
    {
        resp = processAtomRange(m_atoms.getCurrAtomVal(),"atom");
        if(!m_curr_pe_state.valid)
            m_atoms.clearAll();
        else
            m_atoms.clearAtom();
    }

    // bad address may mean a nacc needs sending
    checkPendingNacc(resp);

    // if wait and still stuff to process....
    if(OCSD_DATA_RESP_IS_WAIT(resp) && ( m_mem_nacc_pending || m_atoms.numAtoms()))
        m_curr_state = CONT_ATOM;

    return resp;
}

 void TrcPktDecodePtm::checkPendingNacc(ocsd_datapath_resp_t &resp)
 {
    if(m_mem_nacc_pending && OCSD_DATA_RESP_IS_CONT(resp))
    {
        m_output_elem.setType(OCSD_GEN_TRC_ELEM_ADDR_NACC);
        m_output_elem.st_addr = m_nacc_addr;
        resp = outputTraceElementIdx(m_index_curr_pkt,m_output_elem);
        m_mem_nacc_pending = false;
    }
 }

// given an atom element - walk the code and output a range or mark nacc.
ocsd_datapath_resp_t TrcPktDecodePtm::processAtomRange(const ocsd_atm_val A, const char *pkt_msg, const waypoint_trace_t traceWPOp /*= TRACE_WAYPOINT*/, const ocsd_vaddr_t nextAddrMatch /*= 0*/)
{
    ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
    bool bWPFound = false;
    std::ostringstream oss;
    ocsd_err_t err = OCSD_OK;

    m_instr_info.instr_addr = m_curr_pe_state.instr_addr;
    m_instr_info.isa = m_curr_pe_state.isa;

    // set type (which resets out-elem) before traceInstrToWP modifies out-elem values
    m_output_elem.setType(OCSD_GEN_TRC_ELEM_INSTR_RANGE); 
    
    err = traceInstrToWP(bWPFound,traceWPOp,nextAddrMatch);
    if(err != OCSD_OK)
    {
        if(err == OCSD_ERR_UNSUPPORTED_ISA)
        {
                m_curr_pe_state.valid = false; // need a new address packet
                oss << "Warning: unsupported instruction set processing " << pkt_msg << " packet.";
                LogError(ocsdError(OCSD_ERR_SEV_WARN,err,m_index_curr_pkt,m_CSID,oss.str()));  
                // wait for next address
                return OCSD_RESP_WARN_CONT;
        }
        else
        {
            resp = OCSD_RESP_FATAL_INVALID_DATA;
            oss << "Error processing " << pkt_msg << " packet.";
            LogError(ocsdError(OCSD_ERR_SEV_ERROR,err,m_index_curr_pkt,m_CSID,oss.str()));  
            return resp;
        }
    }
    
    if(bWPFound)
    {
        //  save recorded next instuction address
        ocsd_vaddr_t nextAddr = m_instr_info.instr_addr;

        // action according to waypoint type and atom value
        switch(m_instr_info.type)
        {
        case OCSD_INSTR_BR:
            if (A == ATOM_E)
            {
                m_instr_info.instr_addr = m_instr_info.branch_addr;
                if (m_instr_info.is_link)
                    m_return_stack.push(nextAddr,m_instr_info.isa);
            }
            break;

            // For PTM -> branch addresses imply E atom, N atom does not need address (return stack will require this)
        case OCSD_INSTR_BR_INDIRECT:
            if (A == ATOM_E)
            {
                // atom on indirect branch - either implied E from a branch address packet, or return stack if active.

                // indirect branch taken - need new address -if the current packet is a branch address packet this will be sorted.
                m_curr_pe_state.valid = false; 

                // if return stack and the incoming packet is an atom.
                if (m_return_stack.is_active() && (m_curr_packet_in->getType() == PTM_PKT_ATOM))
                {
                    // we have an E atom packet and return stack value - set address from return stack
                    m_instr_info.instr_addr = m_return_stack.pop(m_instr_info.next_isa);

                    if (m_return_stack.overflow())
                    {
                        resp = OCSD_RESP_FATAL_INVALID_DATA;
                        oss << "Return stack error processing " << pkt_msg << " packet.";
                        LogError(ocsdError(OCSD_ERR_SEV_ERROR, OCSD_ERR_RET_STACK_OVERFLOW, m_index_curr_pkt, m_CSID, oss.str()));
                        return resp;
                    }
                    else
                        m_curr_pe_state.valid = true; 
                }
                if(m_instr_info.is_link)
                    m_return_stack.push(nextAddr, m_instr_info.isa);
            }
            break;
        }
        
        m_output_elem.setLastInstrInfo((A == ATOM_E),m_instr_info.type, m_instr_info.sub_type,m_instr_info.instr_size);
        m_output_elem.setISA(m_curr_pe_state.isa);
        if(m_curr_packet_in->hasCC())
            m_output_elem.setCycleCount(m_curr_packet_in->getCCVal());
        m_output_elem.setLastInstrCond(m_instr_info.is_conditional);
        resp = outputTraceElementIdx(m_index_curr_pkt,m_output_elem);

        m_curr_pe_state.instr_addr = m_instr_info.instr_addr;
        m_curr_pe_state.isa = m_instr_info.next_isa;
    }
    else
    {
        // no waypoint - likely inaccessible memory range.
        m_curr_pe_state.valid = false; // need an address update 

        if(m_output_elem.st_addr != m_output_elem.en_addr)
        {
            // some trace before we were out of memory access range
            m_output_elem.setLastInstrInfo(true,m_instr_info.type, m_instr_info.sub_type,m_instr_info.instr_size);
            m_output_elem.setISA(m_curr_pe_state.isa);
            m_output_elem.setLastInstrCond(m_instr_info.is_conditional);
            resp = outputTraceElementIdx(m_index_curr_pkt,m_output_elem);
        }
    }
    return resp;
}

ocsd_err_t TrcPktDecodePtm::traceInstrToWP(bool &bWPFound, const waypoint_trace_t traceWPOp /*= TRACE_WAYPOINT*/, const ocsd_vaddr_t nextAddrMatch /*= 0*/)
{
    uint32_t opcode;
    uint32_t bytesReq;
    ocsd_err_t err = OCSD_OK;
    ocsd_vaddr_t curr_op_address;

    ocsd_mem_space_acc_t mem_space = (m_pe_context.security_level == ocsd_sec_secure) ? OCSD_MEM_SPACE_S : OCSD_MEM_SPACE_N;

    m_output_elem.st_addr = m_output_elem.en_addr = m_instr_info.instr_addr;
    m_output_elem.num_instr_range = 0;

    bWPFound = false;

    while(!bWPFound && !m_mem_nacc_pending)
    {
        // start off by reading next opcode;
        bytesReq = 4;
        curr_op_address = m_instr_info.instr_addr;  // save the start address for the current opcode
        err = accessMemory(m_instr_info.instr_addr,mem_space,&bytesReq,(uint8_t *)&opcode);
        if(err != OCSD_OK) break;

        if(bytesReq == 4) // got data back
        {
            m_instr_info.opcode = opcode;
            err = instrDecode(&m_instr_info);
            if(err != OCSD_OK) break;

            // increment address - may be adjusted by direct branch value later
            m_instr_info.instr_addr += m_instr_info.instr_size;

            // update the range decoded address in the output packet.
            m_output_elem.en_addr = m_instr_info.instr_addr;
            m_output_elem.num_instr_range++;

            m_output_elem.last_i_type = m_instr_info.type;
            // either walking to match the next instruction address or a real waypoint
            if(traceWPOp != TRACE_WAYPOINT)
            {
                if(traceWPOp == TRACE_TO_ADDR_EXCL)
                    bWPFound = (m_output_elem.en_addr == nextAddrMatch);
                else
                    bWPFound = (curr_op_address == nextAddrMatch);
            }
            else
                bWPFound = (m_instr_info.type != OCSD_INSTR_OTHER);
        }
        else
        {
            // not enough memory accessible.
            m_mem_nacc_pending = true;
            m_nacc_addr = m_instr_info.instr_addr;
        }
    }
    return err;
}

/* End of File trc_pkt_decode_ptm.cpp */
