/* NOTE: This file is a modified version by Sebastian Reichelt!
         Version number 1.20 used in agreement with Lionel Debroux. */

/******************************************************************************
*
* project name:    TIGCC Tools Suite
* file name:       ttstart.c
* initial date:    14/08/2000
* author:          thomas.nussbaumer@gmx.net
* description:     start utility for exepacked and/or normal executables
*
* =============================================================================
* IMPORTANT NOTE
* =============================================================================
*
* The TIGCC v0.95 version is still experimental !
* When compiling this in TIGCC v0.95 and above, do NOT link it against tigcc.a !
* In the command-line front end, use the -standalone option. Also supply -mno-bss
*
* This program uses many really dirty tricks to overcome several restrictions
* of AMS. Don't see it as an example how to code, dirty tricks are NOT smart !
*
* The special RETURN_VALUE treatment is, of course, the most dirty part.
*
* And: don't modify the source if you haven't completely understood it !
*
*
* =============================================================================
* Usage info - normal build (tigcc -Os ttstart.c)
* =============================================================================
*
* When ttstart is built "normally" it can be used to start either exepacked or
* normal executables on TI-89/TI-92plus running AMS versions up to 2.09.
* To start a program (for example: myprog.ppg or myprog.ASM) just type:
* ttstart("myprog")
* (NOTE: the quotes around the program name!)
* If you want to hand-over some commandline parameters to the started program
* just specify them after the programname like this: ttstart("myprog",10,20)
* The program name itself is removed from the estack by ttstart. The started
* program will only see the additionally parameters of ttstart.
*
* Additionally:
*
* ttstart is the first available launcher which handles return values from the
* started program to the AMS correctly.
*
*
* =============================================================================
* customizing ttstart (generating own, special purpose launchers)
* =============================================================================
*
* -----------------------------------------------------------------------------
* (1) using a predefined filename
* -----------------------------------------------------------------------------
*
* if you want to build a launcher which automatically starts a predefined
* program you can use the TIGCC commandline option -DCUSTOM_NAME=name_of_file
* For example: if you want to build a launcher with name mylaunch which should
* automatically start myprog.ppg compile ttstart like this:
*
* tigcc -Os -DCUSTOM_NAME=myprog -o mylaunch
*
* After uploading mylaunch and myprog to your calc you can simply start myprog
* by just typing: mylaunch()
* You can also hand-over any parameter to myprog. For example: mylaunch(10,20)
* starts myprog with parameter 10 and 20 on the estack.
*
* -----------------------------------------------------------------------------
* (2) customizing the type of program (normal or exepacked)
* -----------------------------------------------------------------------------
*
* It doesn't make much sense to build a launcher with a predefined filename,
* but without customizing the type of the program, too.
* If you build a launcher for your program and the program is NOT exepacked,
* the launcher will still contain the unpacking code (around 1200 bytes) which
* is not necessary. Therefore you can use commandline option -DONLY_PPG
* for exepacked programs or -DONLY_ASM for normal program.
*
* -------- Example 1 - launcher for myprog.ppg --------
*
* tigcc -Os -DCUSTOM_NAME=myprog -DONLY_PPG -o mylaunch
*
* -------- Example 2 - launcher for myprog.asm --------
*
* tigcc -Os -DCUSTOM_NAME=myprog -DONLY_ASM -o mylaunch
*
* -----------------------------------------------------------------------------
* (3) memory leak detection support (-DLEAKWATCH)
* -----------------------------------------------------------------------------
*
* When you compile ttstart with option -DLEAKWATCH it will check if the
* available memory gets changed by the started program. If the memory is changed
* it will output the difference in the status line (negative number == less
* memory available after program run / positive number == more memory available
* after program run).
*
* Not each report is a real memory leak of the started program. If a program
* adds or deletes variables, or if it manipulates the home screen by adding
* or removing items from it, a change of available memory will be, of course,
* reported by ttstart, too. There are several other possibilities for "legal"
* changes of the available memory, but if your program ONLY uses malloc()'s
* and free()'s it's very likely that you have a real memory leak.
*
*
* =============================================================================
* TIGCC -pack option (how to make a new pstarter.o file)
* =============================================================================
*
* Since version 0.90 TIGCC features option -pack which generates automatically
* exepacked programs including a custom launcher program.
* The object code of the custom launcher (pstarter.o) used by TIGCC can be
* found in directory lib of your TIGCC installation.
* pstarter.o can be easily generated from ttstart.c:
*
* [since TIGCC 0.93 no longer an assembly file but an object file is used]
*
* (1) compile ttstart.c this way:
*     tigcc -c -Os --optimize-code --cut-ranges -fomit-frame-pointer
*     -fno-defer-pop -mno-bss -fno-merge-constants -mpcrel -DTIGCC_PSTARTER
*     -Wa,-l ttstart.c -o pstarter.o
*     (all in one line, or use mkpstart.bat)
* (2) backup old pstarter.o from directory lib of your TIGCC
*     installation
* (3) copy the newly generated file pstarter.o over the old one
*     in directory lib of your TIGCC installation
*
*
* =============================================================================
* How to generate exepacked programs
* =============================================================================
*
* The simplest way to generate an exepack program is, of course, the -pack
* option of TIGCC (examine the TIGCC docs).
* The second way to generate an exepack program from ANY executable (even from
* kernel-dependent programs) is to use tool ttppggen from the TIGCC Tools
* Suite.
*
*
* =============================================================================
* CREDITS
* =============================================================================
*
* (1) Zeljko Juric, for the "normal launcher" code from the TIGCC-FAQ and the
*     code which overcomes the RETURN_VALUE problem
* (2) Kevin Kofler, for the idea how to handle RETURN_VALUE in the context of a
*     launcher correctly, for the new version of own_enter_ghost_space,
*     other modifications related to TIGCC 0.93, suggestions
* (3) Greg Dietsche, for various suggestions (normal program handling etc.),
*     modifications in the unpacking routine.
* (4) Sebastian Reichelt, for fixing the comments
* (5) Olivier Armand (ExtendeD) for the necessary modifications to support
*     kernel-dependent exepacked prgs and for the EX_patch replacement
* (6) Samuel Stearley for optimizations in the unpacking routine.
* (7) Lionel Debroux for bug reports and modifications in this file.
* (8) Thomas Nussbaumer for anything else
*
* $Id$
*
******************************************************************************/

//-----------------------------------------------------------------------------
// uncomment the following line to use an own relocation routine instead
// of EX_patch call. The own relocation routine is added just for the case
// EX_patch would break again in followup AMS versions.
//-----------------------------------------------------------------------------
//#define USE_OWN_EXPATCH

#define USE_TI89       // compile for TI89
#define USE_TI92PLUS   // compile for TI92+
#define USE_V200       // compile for V200

//-----------------------------------------------------------------------------
// Undocumented macro in TIGCC v0.95 and above: Do not create global imports
// for startup code. This macro is defined exclusively for ttstart; do not use
// it in your own programs. In ttstart, it only suppresses a lot of warnings.
//-----------------------------------------------------------------------------
#define _NO_INCLUDE_PATCH

#define MIN_AMS 100                 // works on all AMS versions
#define NO_EXIT_SUPPORT             // the launcher doesn't need exit support
#define NO_CALC_DETECT              // it doesn't need calc detection, either
#define OPTIMIZE_ROM_CALLS          // generates smaller code by reserving a5
#define ENABLE_ERROR_RETURN         // to return errors to AMS.
#ifndef TIGCC_PSTARTER
#define SET_FILE_IN_USE_BIT         // prevent strange things under some circumstances (see TIGCC help)
#endif

#include <version.h>
#include <statline.h>
#include <alloc.h>
#include <vat.h>
#include <string.h>
#include <system.h>
#include <error.h>
#include <estack.h>
#include <compat.h>

// Starting from TIGCC v0.95, we use our own startup code.
#if __TIGCC_VERSION__ >= 95

// tts121: from void* (would generate warnings with -Wpointer-arith).
unsigned char *real_saved_sp;
#define saved_sp (real_saved_sp-4)
#define SAVED_SP_OFFSET 2 // longwords

asm(".section _st1\n"
"entry_point:"
// OPTIMIZE_ROM_CALLS support.
"	pea.l (%a5)\n"
"	move.l 0xC8,%a5\n"
// Save the stack pointer.
"	lea.l real_saved_sp(%pc),%a0\n"
"	move.l %a7,(%a0)\n"
// Call main_function.
"	bsr.s main_function\n"
// OPTIMIZE_ROM_CALLS support.
"	move.l (%sp)+,%a5\n"
// Return.
"	rts\n"
".text");

extern unsigned char entry_point;

#else

//-----------------------------------------------------------------------------
// DIRTY: This is needed to get everything to work with "complex main"s
//        (Kevin Kofler)
//-----------------------------------------------------------------------------
#if __TIGCCLIB_VERSION__ != 250
#warning Wrong TIGCCLIB version! Do not expect ttstart to work!
#endif
asm("/* Include Patch: save_the_sp */");
extern void *saved_sp asm("__save__sp__");
#ifdef TIGCC_PSTARTER
#define SAVED_SP_OFFSET 18 // longwords
#else
#define SAVED_SP_OFFSET 22 // longwords
#endif

#endif

//-----------------------------------------------------------------------------
// Support -DTIGCC_PSTARTER to create a launcher (pstarter.o) for TIGCC.
//-----------------------------------------------------------------------------
#ifdef TIGCC_PSTARTER
#define ONLY_PPG
#define CUSTOM_NAME tempprog
#endif

//-----------------------------------------------------------------------------
// The PPG-only version does not need to keep the handle locked and is
// therefore reentrant.
//-----------------------------------------------------------------------------
#ifdef ONLY_PPG
#define REENTRANT
#endif

// Samuel's int2str, takes a parameter and returns a value. A good test for
// the parameter passing and return value handling.
//#define CUSTOM_NAME int2str

//-----------------------------------------------------------------------------
// the following variable is used to protect a launcher from launching itself
//-----------------------------------------------------------------------------
#ifndef REENTRANT
short its_me_checking = 0;
#define fatal(s)         {*its_me_checking_ptr=0; ST_helpMsg(s); return;}
#define fatal_without(s) {ST_helpMsg(s); return;}
#else
#define fatal(s) {ST_helpMsg(s); return;}
#define fatal_without fatal
#endif

//-----------------------------------------------------------------------------
// if -DONLY_ASM and -DONLY_PPG is given undefine both (trivial)
//-----------------------------------------------------------------------------
#ifdef ONLY_ASM
#ifdef ONLY_PPG
#undef ONLY_ASM
#undef ONLY_PPG
#endif
#endif

#ifndef ONLY_ASM
#include "ttunpack.h"    // errorcodes definition
#endif

//-----------------------------------------------------------------------------
// we use an own implementation of enter_ghost_space, because due to the
// arrangements within TIGCCLIB (comes as real compile-time library since 2.4)
// otherwise we cannot generate a compatible pstarter.o anymore
// (the launcher generated with TIGCC option -pack will not be linked against
// tigcc.a)
//-----------------------------------------------------------------------------
void own_enter_ghost_space(void);
asm("own_enter_ghost_space:\n"
"	moveq.l #4,%d1\n"
" swap %d1\n"
"	bsr.w 0f\n"
"0:\n"
"	move.l (%sp)+,%d0\n"
"	cmp.l %d1,%d0\n"
"	bcc.s 2f\n"
"	movea.l 0xC8,%a0\n"
"	cmpi.l #1000,(%a0,-4)\n"
"	bcs.s 2f\n"
"	movem.l %a2-%a6/%d3-%d7,-(%sp)\n"
"	lea.l 0x3E000,%a3\n"
"	lea.l 1f(%pc,%d1.l),%a4\n"
"	bra.s unprotect_a3_jumpto_a4\n"
"1:\n"
"	lea (%sp,20),%sp\n"
"	movem.l (%sp)+,%a2-%a6/%d3-%d7\n"
"2:\n"
"	move.l (%sp)+,%d0\n"
"	bset.l #18,%d0\n"
"	movea.l %d0,%a0\n"
"	jmp (%a0)");


//-----------------------------------------------------------------------------
// The following macro calls function fptr and treats a possible RETURN_VALUE
// correctly. If a return value is available mode is set to 0, otherwise 0xFF.
//
// NOTE: the NOPs are very important due to a dirty hack in the TIGCCLIB with
//       #define RETURN_VALUE (they must be exactly after the call!!)
//
// One nop seems to be enough, that's why I (Lionel Debroux) removed one nop.
//-----------------------------------------------------------------------------

#define ASM_CALL_WITH_RETURN_VALUE_HANDLING(fptr,len,mode) ({\
        if (isTitanium) { \
          ercatcherpc=__errFrame->PC;\
          /* Stupid GCC insists on computing the address in a lame way. */ \
          asm ("lea (ercatcher-middle)(%1,%2.l),%0":"=a"(__errFrame->PC)\
               :"a"(fptr),"da"((long)len):"cc");\
          memcpy((unsigned char*)(fptr)+(len),middle,102);\
        } \
        asm volatile ("movem.l %%d1-%%d7/%%a0-%%a6,-(%%sp)\n"\
            "tst.b %3\n"\
            "beq.s 2f\n"\
            "pea target(%%PC)\n"\
            "movea.l %1,%%a3\n"\
            "movea.l %%a3,%%a4\n"\
            "pea 0(%1,%2.l)\n"\
            "jbra unprotect_a3_jumpto_a4\n"\
            "target:\n"\
            "lea (%%sp,36),%%sp\n"\
            "move.l (%%sp)+,%%d0\n"\
            "bra.s 1f\n"\
            "2: jsr (%1)\n"\
            "bra.s 0f\n"\
            "nop\n"\
            "clr.b %%d0\n"\
            "bra.s 1f\n"\
            "0: st.b %%d0\n"\
            "1: movem.l (%%sp)+,%%d1-%%d7/%%a0-%%a6\n"\
            "move.b %%d0,%0"\
            : "=g" (mode) : "a" ((void *)(fptr)), "da" ((long)len), "dm" (isTitanium) : "d0","cc","memory");})

extern char middle[],ercatcher[];
extern unsigned long ercatcherpc;
asm(
/* Begin code executed at end of program */
"middle: bra.s 0f\n"
"nop\n"
"clr.b %d0\n"
"bra.s 1f\n"
"0: st.b %d0\n"
"1: movea.l (%sp)+,%a4\n"
"lea entry_point-target(%a4),%a3\n"
"move.l %d0,16(%sp)\n"
"unprotect_a3_jumpto_a4:\n"
"	lea (%sp,-20),%sp\n"
"	move.l 0xc8.w,%d0\n"
"	andi.l #0xE00000,%d0\n"
"	addi.l #0x20000,%d0\n"
"	move.l %d0,(%sp,12)\n"
"	move.l %d0,(%sp,16)\n"
"	move.l 20(%sp),(%sp)\n"
"	move.l 24(%sp),4(%sp)\n"
"	trap #0xC\n"
"	move.w #0x2700,%sr\n"
"	moveq.l #0xF,%d3\n"
"	pea (%a4)\n"
"	clr.w  -(%sp)\n"
"	move.l 0xAC,%a0\n"
"	jmp (%a0)\n"
"ercatcherentrypoint: .long entry_point\n"
"ercatcherpc: .long 0\n"
".even\n"
"ercatcher:\n"
"movem.l %d0-%d7/%a0-%a6,-(%sp)\n"
"move.l ercatcherentrypoint(%PC),%a3\n"
"lea.l ercatchercleanup-entry_point(%a3),%a4\n"
"jbra unprotect_a3_jumpto_a4\n"
/* End code executed at end of program */

"ercatchercleanup:\n"
"lea (%sp,20),%sp\n"
"movem.l (%sp)+,%d0-%d7/%a0-%a6\n"
"move.l ercatcherpc(%PC),-(%a7)\n"
"rts\n"
);

//-----------------------------------------------------------------------------
// Replacement of EX_patch call implemented as smart ASM macro

#ifdef USE_OWN_EXPATCH

//-----------------------------------------------------------------------------
// Replacement of EX_patch call implemented as smart ASM macro
// (original code by Extended, modified to become a smart ASM macro by tnussb)
//-----------------------------------------------------------------------------
#undef EX_patch
#define EX_patch(base_addr,tag_ptr) ({\
    asm volatile (\
        "0: move.b -(%1),-1(%%a7)\n"\
        "   move.b -(%1),-(%%a7)\n"\
        "   moveq  #0,%%d0\n"\
        "   move.w (%%a7)+,%%d0\n"\
        "   beq.s  1f\n"\
        "   move.b -(%1),-1(%%a7)\n"\
        "   move.b -(%1),-(%%a7)\n"\
        "   moveq  #0,%%d1\n"\
        "   move.w (%%a7)+,%%d1\n"\
        "   add.l  %0,%%d1\n"\
        "   move.l %%d1,0(%0,%%d0)\n"\
        "   bra.s  0b\n1:" \
        :: "a" ((void*)(base_addr)),"a" ((void*)(tag_ptr)) : "d0","d1");})

#endif

//-----------------------------------------------------------------------------
// embedded version string -- EDITED BY HAND!
//
// NOTE: this version string is embedded in ALL variants and its should !!NOT!!
//       be removed by anyone
//
// to force the compiler to embed the string in all case this variable is
// volatile by intention!
//-----------------------------------------------------------------------------
volatile const char embedded_ttstart_version[]="[ttsu121]";


//=============================================================================
// nothing special, just a huge main routine ...
//=============================================================================
#if __TIGCC_VERSION__ >= 95
__attribute__((section("_st2"))) void main_function(void) {
#else
void _main(void) {
#endif
    unsigned char*         src;
    unsigned char*         dest;
    unsigned short         length;
// [Comment by Sebastian: tmpstr is used by -DLEAK_WATCH as well.]
#ifndef CUSTOM_NAME
    unsigned char*         fname;
    unsigned char          tmpstr[50];
#endif
    SYM_ENTRY*             symptr;
    HANDLE                 h,uncomp_h=0;
    volatile unsigned char mode;// = 0xFF;
#ifndef ONLY_PPG
    unsigned short         asm_prg  = 0;
    unsigned short         do_alloc = 1;
#endif
#if __TIGCC_VERSION__ >= 95
    void*                  program_ptr    = &entry_point - 2;
    HANDLE                 program_handle = HeapPtrToHandle(program_ptr);
#endif

#ifndef REENTRANT
    // let's help the compiler with optimization ...
    short* volatile        its_me_checking_ptr = &its_me_checking;
    // check if "we" are already active (the same copy of this executable) and
    // if yes exit immediately !!
    if (*its_me_checking_ptr) {fatal_without("can't start myself");}
    else                      *its_me_checking_ptr = 1;  // now we are active ...
#endif



#ifdef LEAKWATCH
    long freemem = (long)HeapAvail();
#endif



#ifndef CUSTOM_NAME
    //-------------------------------------------------------------------------
    // Here comes the first "DIRTY, DIRTY, DONT-DO-IT-NORMALLY" hack:
    //
    // We are not using a copy of the top_estack pointer, but we operate
    // directly on the top_estack pointer itself. This way the called program
    // will not "see" its own program name on the estack !!!!
    //
    // NOTE: due to the "nature" of the estack it is not necessary to restore
    //       the top_estack pointer to its original value
    //-------------------------------------------------------------------------
    fname = top_estack;

    if (*fname != STR_TAG) {
        fatal("use: ttstart(\"name\")");
    }
    top_estack = next_expression_index(fname);
    fname--;
    
    // suggestion from Greg Dietsche: if given filename contains a '(' truncate
    // the string there. This becomes useful if someone uses a auto-completion
    // TSR. Reimplemented by Samuel Stearley.
    if (*(fname-1) == '(' ){
        *(--fname) = 0;
    }

    if (!(symptr = DerefSym(SymFind(fname)))) {
        strncpy(tmpstr,top_estack+2,17);
        tmpstr[17] = 0;
        strcat(tmpstr," not found");
        
        fatal(tmpstr);
    }

#else

    //-------------------------------------------------------------------------
    // if you can figure out why the next lines are that strange, then you are
    // really good in C programming ...
    //-------------------------------------------------------------------------
    #define TOSTR(x) #x
    #define XTOSTR(x) TOSTR(x)

#ifndef TIGCC_PSTARTER
    // The strings (they are constants) are computed at compile time.
    if (!(symptr = DerefSym(SymFind(SYMSTR(XTOSTR(CUSTOM_NAME)))))) {
        fatal(XTOSTR(CUSTOM_NAME)" not found");
    }
#else
    {
        // This is a temporary workaround: The length of the string is not
        // really known, since it is changed by TIGCC.
        const char *fname = XTOSTR(CUSTOM_NAME);
        unsigned char tmpstr[50];
        if (!(symptr = DerefSym(SymFind(SYMSTR(fname))))) {
            sprintf(tmpstr,"%s not found",fname);
            fatal(tmpstr);
        }
    }
#endif

#endif
/*
    //-------------------------------------------------------------------------
    // setup TIOS name for variable and try to find it in the current folder ..
    //-------------------------------------------------------------------------

    tmpstr[0] = 0;
    strcpy(tmpstr+1,fname);
    if (!(symptr = DerefSym(SymFind(tmpstr+1+strlen(fname))))) {
        sprintf(tmpstr,"%s not found",fname);
        fatal(tmpstr);
    }
*/

    //-------------------------------------------------------------------------
    // IMPORTANT: only use handle (symptr may become invalid !!!)
    //-------------------------------------------------------------------------
    h = symptr->handle;

    //-------------------------------------------------------------------------
    // if file is already locked, don't unlock it at the end ...
    //-------------------------------------------------------------------------
    {
        short locked = HeapGetLock(h);
        src = HLock(h);
        if (locked) h = 0;
    }

    if (!src) fatal("locking failed"); // will return!

    length = *(unsigned short*)src+3;
    dest   = src+length-2;

    /* Is this a TI-89 Titanium? */
    char isTitanium = -!!((unsigned long) ROM_base & 0x800000ul);

#ifndef ONLY_PPG
    //-------------------------------------------------------------------------
    // check if we want to start a normal ASM program
    //-------------------------------------------------------------------------
    if (*dest == 0xF3) {
        asm_prg = 1;
        dest    = src;
        length--;
        if (!symptr->flags.bits.archived && !symptr->flags.bits.locked && !isTitanium) do_alloc = 0;
    }
#endif

#ifdef ONLY_ASM
    else {
        if (h) HeapUnlock(h);
        fatal("no ASM prg"); // will return!
    }

#else

    //-------------------------------------------------------------------------
    // check if given program is not even a packed file
    //-------------------------------------------------------------------------
#ifndef ONLY_PPG
    else
#endif
    {
        //---------------------------------------------------------------------
        // skip "length of variable" bytes
        //---------------------------------------------------------------------
        src += 2;
        if (*dest     != 0xF8 ||
            *(dest-4) != 'p'  ||
            *(dest-3) != 'p'  ||
            *(dest-2) != 'g'  ||
            !ttunpack_valid(src))
        {
            if (h) HeapUnlock(h);
#ifndef ONLY_PPG
            fatal("neither ASM nor PPG prg");
#else
            fatal("no PPG prg");
#endif
        }
        //---------------------------------------------------------------------
        // get the original size and allocate memory for it
        //---------------------------------------------------------------------
        length = ttunpack_size(src);
    }

#endif


#ifndef ONLY_PPG
    if (do_alloc)
#endif
    {
        // NOTE: we don't use malloc, but the low level routines
        //       otherwise compressed kernel-dependent progs will crash
        if (!(uncomp_h=HeapAllocHigh(length+(isTitanium?102/*will be rounded up if odd*/:0)))) {
            if (h) HeapUnlock(h);
            fatal("out of memory");
        }

        dest = HeapDeref(uncomp_h);

#ifndef ONLY_PPG
        if (asm_prg) {
            memcpy(dest,src,length);
        }
#endif

#ifndef ONLY_ASM

#ifndef ONLY_PPG
        else
#endif
        {
            ST_helpMsg("decompressing ...");

            if (ttunpack_decompress(src,dest)) {
                //-------------------------------------------------------------
                // "houston we got problems ..."
                //-------------------------------------------------------------
                HeapFree(uncomp_h);
                if (h) HeapUnlock(h);
                fatal("failed");
            }
#ifdef ONLY_PPG
            if (h) HeapUnlock(h);
#endif
            ST_eraseHelp();
        }
    }

#else
    }
#endif

#ifndef TIGCC_PSTARTER
    //-------------------------------------------------------------------------
    // set our own in-use bit, for safety reasons
    //-------------------------------------------------------------------------
#if __TIGCC_VERSION__ >= 95
    if (program_handle) {
        SYM_ENTRY *tmpsym;
        for (tmpsym = SymFindFirst(NULL, FO_RECURSE | FO_SKIP_TEMPS); tmpsym; tmpsym = SymFindNext()) {
            if (tmpsym->handle == program_handle) {
                tmpsym->flags.bits.hidden = 1;
                break;
            }
        }
    }
#endif
#endif

    //-------------------------------------------------------------------------
    // relocate the executable, enter the ghost space and start it ...
    //-------------------------------------------------------------------------
    // Fixed EX_patch call for AMS 2.06+ (thanks Kevin for the fix).
    // Fixed again for HW3/AMS 3.00+ (Kevin).

    unsigned long offset = isTitanium?0:0x40000;
    EX_patch(dest+offset+2,dest+length+offset-1);
    if (!isTitanium) own_enter_ghost_space();
    (*(unsigned long *)saved_sp)|=offset;

    /* If we are on AMS 2, we have to set the "last executed program" to somewhere
       in the last 4 KB of RAM, or else APD may crash under certain circumstances.
       The code below looks for the "last executed program" variable. That variable
       is cleared during initialization, immediately after the stack fence is set up.
       So we look for the value of the stack fence (0xDEADDEAD) in the initialization
       code and add 8 to get the wanted short pointer, which must then be
       sign-extended to an actual pointer. (The sign extension is implicit in the
       generated code, as it should be.) */
    if (!(AMS_1xx||*(short*)0x32==('R'<<8)+'O')) {
      /* rb and q are factored out in order to get more efficient code. */
      char *rb=ROM_base;
      char *p=rb+0x12000;
      char *q=rb+0x18000;

      while (p<q && *(unsigned long*)p!=0xDEADDEAD) p+=2;
      p+=2[(short *)p]?8:12;
      *(void **)(long)*(short *)p=isTitanium?((unsigned char *)dest+2):(void*)0x3f000;
    }

    unsigned long oldercatcherpc=ercatcherpc;
    TRY
        // The second argument MUST be even. It is length-2 rounded up to even.
        ASM_CALL_WITH_RETURN_VALUE_HANDLING(dest+offset+2,(length-1)&0xFFFE,mode);
    FINALLY
        ercatcherpc=oldercatcherpc; /* restore this before rethrowing the error,
                                       in case this is a recursive call */

        // mode must not be changed here !

#ifndef TIGCC_PSTARTER
    //-------------------------------------------------------------------------
    // clear our own in-use bit again; otherwise we are in big trouble
    //-------------------------------------------------------------------------
#if __TIGCC_VERSION__ >= 95
    if (program_handle) {
        SYM_ENTRY *tmpsym;
        for (tmpsym = SymFindFirst(NULL, FO_RECURSE | FO_SKIP_TEMPS); tmpsym; tmpsym = SymFindNext()) {
            if (tmpsym->handle == program_handle) {
                tmpsym->flags.bits.hidden = 0;
                break;
            }
        }
    }
#endif
#endif

#ifndef ONLY_PPG
    if (src!=dest)
#endif
        HeapFree(uncomp_h);

#ifndef ONLY_PPG
    if (h) HeapUnlock(h);
#endif

#ifdef LEAKWATCH
    if ((freemem=((long)HeapAvail())-freemem) != 0) {
        sprintf(tmpstr, "mem changed by %ld", freemem);
        ST_helpMsg(tmpstr);
    }
#endif

#ifndef REENTRANT
    *its_me_checking_ptr = 0; // we are not active anymore ...
#endif

#if __TIGCC_VERSION__ >= 95
    //---------------------------------------------------------------------
    // On AMS 1.xx, we need to unlock our own handle, otherwise we cannot
    // be started again due to an AMS bug.
    //---------------------------------------------------------------------
    if (AMS_1xx) {
        if (program_handle)
            HeapUnlock(program_handle);
    }
#endif

    ENDFINAL

    if (!mode) {
        //---------------------------------------------------------------------
        // launched program uses RETURN_VALUE - handle it correctly
        // (VERY dirty trick implemented by Kevin Kofler)
        // Modified by Lionel Debroux to support ENABLE_ERROR_RETURN and
        // SET_FILE_IN_USE_BIT (the code was broken when these #defines
        // were added).
        // And modified again by Kevin Kofler to use a #define so we don't have
        // to change this everytime the offset changes.
        //---------------------------------------------------------------------
        {
            if (*(((unsigned short **)saved_sp)[SAVED_SP_OFFSET])==0x21ee) ((unsigned short **)saved_sp)[SAVED_SP_OFFSET]+=3;
            else                                                           ((unsigned short **)saved_sp)[SAVED_SP_OFFSET]+=2;
        }
    }
}

//#############################################################################
//###################### NO MORE FAKES BEYOND THIS LINE #######################
//#############################################################################
//
//=============================================================================
// Revision History
//=============================================================================
//
// $Log: ttstart.c,v $
//
// Lost track of versioning, listing changes:
// * Using new (faster and smaller) unpack routine by Greg Dietsche and
//   Samuel Stearley.
// * Corrected a bug where a too long line could make the program crash (in
//   fact, completely reimplemented the argument reading code, code given by
//   Samuel Stearley).
// * Added missing directive ENABLE_ERROR_RETURN (asked by Kevin Kofler).
// * Added missing directive SET_FILE_IN_USE_BIT (asked by various people).
// * As a matter of fact, corrected the special RETURN_VALUE treatment
//   (broken when ENABLE_ERROR_RETURN and SET_FILE_IN_USE_BIT were added).
// 
//
//
// Revision 1.17  2002/05/27 07:54:09  tnussb
// exception handling added (thanx to Sebastian Reichelt)
//
// Revision 1.16  2002/05/22 09:01:38  tnussb
// embedded version string set to tts116 to reflect the "stepping back to old
// unpacking function" modification
//
// Revision 1.15  2002/04/17 10:35:10  tnussb
// documentation extended
//
// Revision 1.14  2002/03/13 15:01:42  tnussb
// (1) some messages shortened
// (2) using now new and minor size optimized unpacking function
// (3) some comments added
//
// Revision 1.13  2002/03/04 08:51:03  tnussb
// Extended's relocation routine as smart ASM macro added (just for the case
// that EX_patch() will "break" again)
//
// Revision 1.12  2002/02/28 08:53:18  tnussb
// EX_patch called fixed to be prepared for new EX_patch version coming with
// AMS 2.06
//
// Revision 1.11  2002/02/07 09:57:58  tnussb
// many changes like new bootstrapping, new enter_ghost_space routine and,
// of course, kernel-dependend PPG support
//
// Revision 1.9  2001/07/31 22:56:59  Thomas Nussbaumer
// generic commit for Tools Suite version 0.99.10
//
// Revision 1.8  2001/02/04 16:45:40  Thomas Nussbaumer
// using new ttunpack.h version
//
// Revision 1.7  2000/11/18 16:00:18  Thomas Nussbaumer
// enter_ghost_space() added
//
// Revision 1.6  2000/10/01 14:56:52  Thomas Nussbaumer
// support for custom start utility build added
//
// Revision 1.5  2000/08/30 20:01:55  Thomas Nussbaumer
// using now assembler optimized version of unpack routine
//
// Revision 1.4  2000/08/30 19:44:24  Thomas Nussbaumer
// TABs replaced by spaces and trailing spaces removed
//
// Revision 1.3  2000/08/20 15:28:22  Thomas Nussbaumer
// including of unpack.c done now before including ttunpack.h
//
// Revision 1.2  2000/08/16 22:29:05  Thomas Nussbaumer
// (1) based now on shared file unpack.c
// (2) argument can be specified on commandline
// (3) define and code for time measurement integrated
//
// Revision 1.1  2000/08/14 22:48:38  Thomas Nussbaumer
// initial version
//
//
