/* ppcbxx.h -- PowerPC Branch trick

   This file is part of the UPX executable compressor.

   Copyright (C) 1996-2020 Markus Franz Xaver Johannes Oberhumer
   Copyright (C) 1996-2020 Laszlo Molnar
   Copyright (C) 2004-2020 John F. Reiser
   All Rights Reserved.

   UPX and the UCL library are free software; you can redistribute them
   and/or modify them under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of
   the License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; see the file COPYING.
   If not, write to the Free Software Foundation, Inc.,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

   Markus F.X.J. Oberhumer              Laszlo Molnar
   <markus@oberhumer.com>               <ezerotven+github@gmail.com>

   John F. Reiser
   <jreiser@users.sourceforge.net>
 */



/*************************************************************************
// filter / scan
**************************************************************************/

#define W_CTO 4  /* width of cto; must match stub/ppc_bxx.S */

static int F(Filter *f)
{
#ifdef U
    // filter
    upx_byte *b = f->buf;
    const unsigned addvalue = f->addvalue;
#else
    // scan
    const upx_byte *b = f->buf;
#endif
    const unsigned size  = umin(f->buf_len, 0u - (~0u<<(32 - (6+ W_CTO))));
    const unsigned size4 = size -4;

    unsigned ic;
    unsigned calls = 0, noncalls = 0;
    unsigned lastnoncall = size, lastcall = 0;

    // find 26-2-W_CTO bits of displacements that contain no targets
    {
        unsigned char buf[256];
        unsigned short wbuf[256];
        const size_t WW = (size_t)0 - ((~(size_t)0) << W_CTO); // ???
        memset(wbuf, 0, sizeof(wbuf));
        memset(buf     , 0,       WW);
        memset(buf + WW, 1, 256 - WW);

        for (ic = 0; ic<=size4; ic+=4) if (COND(b,ic)) {
            unsigned const off = (int)(get_be32(b+ic)<<6) >>6;
            if (size <= (off & (~0u<<2))+ic) {
                buf[(~(~0u<<W_CTO)) & (off>>(26 - W_CTO))] |= 1;
                ++wbuf[0xff&(off>>18)];
            }
        }

        if (getcto(f, buf) < 0) {
#if (W_CTO != 0)
            return -1;
#else
            f->cto = 0;
#endif
        }
    }
    const unsigned char cto8 = f->cto;
#ifdef U
    const unsigned cto = (unsigned)f->cto << (24+2 - W_CTO);
#endif

    for (ic = 0; ic<=size4; ic+=4) if (COND(b,ic)) {
        unsigned const word = get_be32(b+ic);
        unsigned const off = (int)(word<<6) >>6;
        unsigned const jc = (off & (~0u<<2))+ic;
        // try to detect 'real' calls only
        if (jc < size) {
#ifdef U
            set_be32(b+ic,(0xfc000003&word) | (jc+addvalue+cto));
#endif
            calls++;
            lastcall = ic;
        }
        else {
#if (W_CTO != 0)
            assert((~(~0u<<W_CTO) & (word>>(24+2 - W_CTO))) != (unsigned) cto8);  // this should not happen
#endif
            lastnoncall = ic;
            noncalls++;
        }
    }

    f->calls = calls;
    f->noncalls = noncalls;
    f->lastcall = lastcall;
    ACC_UNUSED(lastnoncall);

#if 0 || defined(TESTING)
    printf("\ncalls=%d noncalls=%d text_size=%x calltrickoffset=%x\n",
        calls,noncalls,size,cto8);
#endif
    return 0;
}


/*************************************************************************
// unfilter
**************************************************************************/

#ifdef U
static int U(Filter *f)
{
    upx_byte *b = f->buf;
    const unsigned size4 = umin(f->buf_len - 4, 0u - (~0u<<(32 - (6+ W_CTO))));
    const unsigned addvalue = f->addvalue;

    unsigned ic;

   for (ic = 0; ic<=size4; ic+=4) if (COND(b,ic)) {
        unsigned const word = get_be32(b+ic);
        if ((~(~0u<<W_CTO) & (word>>(24+2 - W_CTO))) == (unsigned) f->cto) {
            unsigned const jc = word & (~(~0u<<(26 - W_CTO)) & (~0u<<2));
            set_be32(b+ic, (0xfc000003&word)|(0x03fffffc&(jc-ic-addvalue)));
            f->calls++;
            f->lastcall = ic;
        }
        else {
            f->noncalls++;
        }
    }
    return 0;
}
#endif


#undef F
#undef U

#undef W_CTO

/* vim:set ts=4 sw=4 et: */
