/******************************************************************************
*
* project name:    TIGCC Tools Suite
* file name:       unpack.c (improved version of asm_upck.c)
* initial date:    13/03/2002
* authors:         albert@cs.tut.fi
*                  b.denis@libertysurf.fr
*                  thomas.nussbaumer@gmx.net
*
* description:     unpack routine to exepack compression
*
*                  stripped down version - only handles files with
*                  maxgamma==7 ... but other files shouldn't be around, because
*                  this is the standard setting of ttpack
*
* --------------------------------------------------------------------------------------
* to generate the hexcode array (works with TIGCC 0.93 - other versions are not tested):
* --------------------------------------------------------------------------------------
*
* (1) tigcc -Os -DUSE_TI89 -Wall -W -Wwrite-strings -fomit-frame-pointer -DSTANDALONE -mpcrel unpack.c
* (2) ttstrip unpack.89z unpack.bin
* (3) ttextract unpack.bin unpack.bi2 UNPACK_0 UNPACK_1
* (4) ttbin2hex -b2 -c 10 -slash unpack.bi2 unpack.txt
* (5) del unpack.bin
* (6) del unpack.bi2
*
* file unpack.txt contains now the data of the hexcode array
*
*
* -----------------------------------------------------------------------------
*
* based on code from Pasi 'Albert' Ojala, albert@cs.tut.fi
* heavily reduced to fit to the needs by thomas.nussbaumer@gmx.net
* assembler optimizations by b.denis@libertysurf.fr
*
* for details on the used algorithm see:
*
* http://www.cs.tut.fi/~albert/Dev/pucrunch/index.html
*
* $Id: unpack.c,v 1.2 2002/03/13 14:26:01 tnussb Exp $
*
******************************************************************************/
//#define ALL_CHECKS  --- just for testing purposes ...

#if STANDALONE
#define USE_TI89             // only produce the TI89 version
#define NO_EXIT_SUPPORT      // no exit support for this code
#define OPTIMIZE_ROM_CALLS   // don't use a5 in this function! (reserve it)
#include <tigcclib.h>
#endif

//-----------------------------------------------------------------------------
// all structures and defines from ttunpack.h are used here as copies which are
// additionally prefixed with "__"
//-----------------------------------------------------------------------------
typedef struct {
    unsigned char  osize_lo;   // original size lowbyte
    unsigned char  osize_hi;   // original size highbyte
    unsigned char  magic1;     // must be equal to UNPACK_MAGIC1
    unsigned char  magic2;     // must be equal to UNPACK_MAGIC2
    unsigned char  csize_lo;   // compressed size lowbyte
    unsigned char  csize_hi;   // compressed size lowbyte
    unsigned char  esc1;       // escape >> (8-escBits)
    unsigned char  notused3;
    unsigned char  notused4;
    unsigned char  esc2;       // escBits
    unsigned char  gamma1;     // maxGamma + 1
    unsigned char  gamma2;     // (1<<maxGamma)
    unsigned char  extralz;    // extraLZPosBits
    unsigned char  notused1;
    unsigned char  notused2;
    unsigned char  rleentries; // rleUsed
} __PACKHEADER;

#define __MAGIC_CHAR1          0x54
#define __MAGIC_CHAR2          0x50

#define __ERRPCK_OKAY             0
#define __ERRPCK_NOESCFOUND     248
#define __ERRPCK_ESCBITS        249
#define __ERRPCK_MAXGAMMA       250
#define __ERRPCK_EXTRALZP       251
#define __ERRPCK_NOMAGIC        252
#define __ERRPCK_OUTBUFOVERRUN  253
#define __ERRPCK_LZPOSUNDERRUN  254


#define COMBINE_LOWHIGH(lo,hi)  (((unsigned short)lo) | ((unsigned short)(hi << 8)))

//-----------------------------------------------------------------------------
// these following two lines are only forward declarations to make the compiler
// happy
//
// the real variables are in the ASM block at the end of this file
//
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// don't forget to compile with -mpcrel to get it working correctly !
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

extern unsigned char* __inputbuffer__; // input buffer
extern unsigned short __inputmask__;   // input buffer byte mask


//---------------------------------------------------------------------------
// CORRECT_IN_MASK rotates the lower byte of __inputmask__ one to the right.
// If it "wraps around" from 0x01 to 0x80 pointer __inputbuffer__ will be
// increased by one.
//---------------------------------------------------------------------------

// NOTE: IT IS VERY IMPORTANT THAT "memory" is part of the clobbered regs list!!
//       otherwise TIGCC produces NOT what we want (it will produce crash code)
//
// ADDITIONALLY: Note that "volatile" for asm blocks like this is VERY important.
//               Without declaring it volatile, the compiler removes them, because
//               it thinks they are useless!!
#define CORRECT_IN_MASK() ({               \
     unsigned short __tmp;                 \
     asm volatile(                         \
"    lea     __inputbuffer__(%%pc),%%a0\n" \
"    lea     __inputmask__(%%pc),%%a1\n"   \
"    move.w  (%%a1),%0\n"                  \
"    ror.b    #1,%0\n"                     \
"    bcc.s   0f\n"                         \
"    addq.l  #1,(%%a0)\n"                  \
"0:  move.w  %0,(%%a1)\n" : "=d" (__tmp):  \
: "d0","a0","a1","cc","memory");})


//---------------------------------------------------------------------------
// returns >0 if next bit is set, otherwise 0
//---------------------------------------------------------------------------
#define NEXT_BIT_SET   (*__inputbuffer__ & __inputmask__)


//-----------------------------------------------------------------------------
// Prototypes of internal functions. These functions are below the unpack
// function by intention. Hopefully the compiler will not rearrange them so
// we can produce the hexcode array style headerfile of the complete function
// block (all functions in this file) ...
//
// NOTE: These prototypes are macros which calls the function PC-relative.
//       This is necessary to make a HEXCODE array from them!
//
// Additionally: __GETBITS() hands over its argument using a register and NOT
//              by using the stack!
//-----------------------------------------------------------------------------

#define __GETBITS(bits) \
({unsigned short __result,__arg=(bits);         \
asm volatile ("move.w  %1,%%d1\n"               \
	          "lea     (__GetBits,%%pc),%%a0\n" \
              "jbsr    (%%a0)\n"                \
              "move.w %%d0,%0" : "=d" (__result) : "d" (__arg) : "d0","d1","d2","a0","a1","cc","memory"); \
              __result;})


#define __GET8BIT() \
({unsigned short __result;                      \
asm volatile ("lea     (__Get8Bit,%%pc),%%a0\n" \
              "jbsr    (%%a0)\n"                \
              "move.w  %%d0,%0" : "=d" (__result) :: "d0","d1","d2","a0","a1","cc","memory"); \
              __result;})


#define __GETVALUE() \
({unsigned short __result;                       \
asm volatile ("lea     (__GetValue,%%pc),%%a0\n" \
              "jbsr    (%%a0)\n"                 \
              "move.w  %%d0,%0" : "=d" (__result) :: "d0","d1","d2","a0","a1","cc","memory");  \
              __result;})



//=============================================================================
// the decompression routine
//
// using it is very simple: feed in a filled source array and a buffer which is
// large enough to hold the decompression result
//
// returns 0 if okay
//=============================================================================
#if defined(STANDALONE)
asm(".data\n.ascii \"UNPACK_0\"");  // used to mark block for later extraction
#endif
short UnpackBuffer(unsigned char *src, unsigned char *outbuffer)  {
    unsigned short startesc;
    unsigned char* bytecodevec;
    __PACKHEADER*  cth = (__PACKHEADER*)src;
    unsigned short escbits8;
    unsigned short escbits;
    unsigned short extralzposbits;
#ifdef ALL_CHECKS
    unsigned char* dest;
    unsigned char* pend_in;
    unsigned char* pend_out;
#endif

    //---------------------------------------------------------------------
    // check magic markers, gamma settings, escape bits and extra lz-bits
    // settings ... if there are problems return corresponding errorcode
    //---------------------------------------------------------------------
    if (cth->magic1 != __MAGIC_CHAR1 || cth->magic2 != __MAGIC_CHAR2) return __ERRPCK_NOMAGIC;
    if (cth->gamma1 != 8 || cth->gamma2 != 128)                       return __ERRPCK_MAXGAMMA;
    if ((escbits = cth->esc2) > 8)                                    return __ERRPCK_ESCBITS;
    if ((extralzposbits = cth->extralz) > 4)                          return __ERRPCK_EXTRALZP;

    startesc = cth->esc1;
    escbits8 = 8 - escbits;
    bytecodevec = &src[15];

#ifdef ALL_CHECKS
    dest = outbuffer;
#endif
    //--------------------------
    // initialize buffer globals
    //--------------------------
    __inputbuffer__ = src + sizeof(__PACKHEADER) + cth->rleentries;   // points at start of data
    __inputmask__   = 0x80;


#ifdef ALL_CHECKS
    pend_in  = __inputbuffer__ + COMBINE_LOWHIGH(cth->csize_lo,cth->csize_hi);
    pend_out = dest            + COMBINE_LOWHIGH(cth->osize_lo,cth->osize_hi);
#endif

    while (1) {
        unsigned short sel;

#ifdef ALL_CHECKS
        if (outbuffer > pend_out)       return __ERRPCK_OUTBUFOVERRUN;
        if (__inputbuffer__  > pend_in) return __ERRPCK_NOESCFOUND;
#endif

        sel = (escbits) ? __GETBITS(escbits) : startesc;

        if (sel == startesc) {
            unsigned short lzpos, lzlen,i;
            unsigned short add = 0;

            lzlen = __GETVALUE();

            //-----------------------------------------------------------------
            // lzlen != 1 -> ZIP-decoding
            //-----------------------------------------------------------------
            if (lzlen != 1) {
                unsigned short lzposlo;
                unsigned short lzposhi = __GETVALUE() - 1;

                if (lzposhi == 254) {
                    if (lzlen > 3) {
                        add   = __GET8BIT();
                        lzpos = __GET8BIT() ^ 0xff;
                    }
                    else {
                        break;  // finish !!!
                    }
                }
                else {
                    if (extralzposbits) lzposhi = (lzposhi<<extralzposbits) | __GETBITS(extralzposbits);

                    lzposlo = __GET8BIT() ^ 0xff;
                    lzpos   = COMBINE_LOWHIGH(lzposlo,lzposhi);
                }
            }
            //-----------------------------------------------------------------
            // lzlen == 1 -> RLE-decoding
            //-----------------------------------------------------------------
            else {
                if (NEXT_BIT_SET) {
                    unsigned short rlelen, bytecode, byte;

                    CORRECT_IN_MASK();

                    if (!NEXT_BIT_SET) {
                        unsigned short newesc;
                        CORRECT_IN_MASK();

                        newesc = __GETBITS(escbits);

                        *outbuffer++ = (startesc<<escbits8) | __GETBITS(escbits8);
                        startesc = newesc;
#ifdef ALL_CHECKS
                        if (outbuffer > pend_out) return __ERRPCK_OUTBUFOVERRUN;
#endif
                        continue;  // continue the main loop ...
                    }

                    CORRECT_IN_MASK();
                    rlelen = __GETVALUE();
                    if (rlelen >= 128) {
                        rlelen = ((rlelen-128)<<1) | __GETBITS(1);
                        rlelen |= (((__GETVALUE())-1)<<8);
                    }
                    bytecode = __GETVALUE();
                    if (bytecode < 32) byte = bytecodevec[bytecode];
                    else               byte = ((bytecode-32)<<3) | __GETBITS(3);

                    for (i=0; i<=rlelen; i++) *outbuffer++ = byte;
                    continue;   // continue the main loop ...
                }
                CORRECT_IN_MASK();
                lzpos = __GET8BIT() ^ 0xff;
            }


#ifdef ALL_CHECKS
            if (outbuffer + lzlen + 1 > pend_out) return __ERRPCK_OUTBUFOVERRUN;
#endif
            for (i=0; i<=lzlen; i++) {
                *outbuffer = *(outbuffer - lzpos - 1) + add;
                outbuffer++;
            }

        }

        //---------------------------------------------------------------------
        // sel != startesc
        //---------------------------------------------------------------------
        else {
            *outbuffer++ = (sel<<escbits8) | __GETBITS(escbits8);
#ifdef ALL_CHECKS
            if (outbuffer > pend_out) return __ERRPCK_OUTBUFOVERRUN;
#endif
        }
    }

    return __ERRPCK_OKAY;
}

//=============================================================================
// unsigned short __GetBits(unsigned short bits)
// -- get number of bits from inputbuffer ... d1.w is used as input parameter
//
// unsigned short __Get8Bit(void)
// -- get 8 bits from inputbuffer
//
//
// unsigned short __GetValue(void)
// -- get next value from inputbuffer
//=============================================================================
asm(".data\n"
"    .even\n"
"__inputbuffer__:\n"
"	 .long 0\n"
"__inputmask__:\n"
"	 .word 0\n"
"    rts\n"
"__Get8Bit:\n"
"    lea     __inputbuffer__(%pc),%a0\n"
"    movea.l (%a0),%a1\n"
"    clr.w   %d0\n"
"    move.b  (%a1)+,%d0\n"
"    move.l  %a1,(%a0)\n"
"    move.w  __inputmask__(%pc),%d1\n"
"    cmpi.b  #0x80,%d1\n"
"    bne.s   __inl_tries\n"
"    rts\n"
"__inl_tries:\n"
"    lsl.w   #8,%d0\n"
"    move.b  (%a1),%d0\n"
"    cmpi.b  #0x08,%d1\n"
"    bhi.s   __inl_try20\n"
"    bne.s   __inl_try02\n"
"    andi.w  #0x0fff,%d0\n"
"    lsr.w   #4,%d0\n"
"    rts\n"
"__inl_try40:\n"
"    andi.w  #0x7fff,%d0\n"
"    lsr.w   #7,%d0\n"
"    rts\n"
"__inl_try20:\n"
"    cmpi.b  #0x20,%d1\n"
"    bhi.s   __inl_try40\n"
"    bcs.s   __inl_try10\n"
"    andi.w  #0x3fff,%d0\n"
"    lsr.w   #6,%d0\n"
"    rts\n"
"__inl_try10:\n"
"    andi.w  #0x1fff,%d0\n"
"    lsr.w   #5,%d0\n"
"    rts\n"
"__inl_try04:\n"
"    andi.w  #0x07ff,%d0\n"
"    lsr.w   #3,%d0\n"
"    rts\n"
"__inl_try02:\n"
"    cmpi.b  #0x02,%d1\n"
"    bhi.s   __inl_try04\n"
"    bne.s   __inl_try01\n"
"    andi.w  #0x03ff,%d0\n"
"    lsr.w   #2,%d0\n"
"    rts\n"
"__inl_try01:\n"
"    andi.w  #0x01ff,%d0\n"
"    lsr.w   #1,%d0\n"
"    rts\n"
"__GetValue:\n"
"    move.l  %d3,-(%sp)\n"
"    clr.l   %d1\n"
"    move.w  #7,%d0\n"
"    move.l  __inputbuffer__(%pc),%a0\n"
"    move.w  __inputmask__(%pc),%d2\n"
"__inl_loop1:\n"
"    cmp.w   %d1,%d0\n"
"    bls.s   __inl_exitloop\n"
"    move.w  %d2,%d3\n"
"    and.b   (%a0),%d3\n"
"    ror.b   #1,%d2\n"
"    bcc.s   __inl_samebyte1\n"
"    addq.l  #1,%a0\n"
"__inl_samebyte1:\n"
"    tst.b   %d3\n"
"    beq.s   __inl_exitloop\n"
"    addq.w  #1,%d1\n"
"    bra.s   __inl_loop1\n"
"__inl_exitloop:\n"
"    move.l  %a0,%d3\n"
"    lea     __inputbuffer__(%pc),%a0\n"
"    move.l  %d3,(%a0)\n"
"    addq.l  #4,%a0                      | a0 points now to __inputmask__\n"
"    move.w  %d2,(%a0)\n"
"    moveq   #1,%d3\n"
"    lsl.w   %d1,%d3\n"
"    lea     (__GetBits,%pc),%a0\n"
"    jbsr    (%a0)\n"
"    or.w    %d3,%d0\n"
"    move.l  (%sp)+,%d3\n"
"    rts\n"
"__GetBits:\n"
"    clr.w   %d0\n"
"    tst.w   %d1\n"
"    beq.s   __inl_exit\n"
"    movea.l __inputbuffer__(%pc),%a0\n"
"    movea.l %d3,%a1                    | saves d3 in a1\n"
"    move.w  __inputmask__(%pc),%d2\n"
"    subq.w  #1,%d1\n"
"__inl_loop0:\n"
"    lsl.w   #1, %d0\n"
"    move.w  %d2,%d3\n"
"    and.b   (%a0),%d3\n"
"    beq.s   __inl_correct0\n"
"    addq.b  #1,%d0\n"
"__inl_correct0:\n"
"    ror.b   #1,%d2\n"
"    bcc.s   __inl_samebyte0\n"
"    addq.l  #1,%a0\n"
"__inl_samebyte0:\n"
"    dbra    %d1,__inl_loop0\n"
"    move.l  %a1,%d3                     | restores content of d3\n"
"    lea     __inputbuffer__(%pc),%a1\n"
"    move.l  %a0,(%a1)\n"
"    addq.l  #4,%a1                      | a1 points now to __inputmask__\n"
"    move.w  %d2,(%a1)\n"
"__inl_exit:\n"
"    rts");
#if defined(STANDALONE)
asm(".data\n.ascii \"UNPACK_1\"");   // used to mark block for later extraction
#endif




#if defined(STANDALONE)
void _main(void) {
    UnpackBuffer(NULL,NULL);
}
#endif

//=============================================================================
// Revision History
//=============================================================================
//
// $Log: unpack.c,v $
// Revision 1.2  2002/03/13 14:26:01  tnussb
// minor improvements done and many comments added
//
// Revision 1.1  2002/03/13 11:13:32  tnussb
// initial version
//
//