/******************************************************************************
*
* project name:    TIGCC Tools Suite
* file name:       ttmem.c
* initial date:    14/08/2000
* author:          thomas.nussbaumer@gmx.net
* description:     oncalc tool for simulating low mem conditions
*
* $Id: ttmem.c,v 1.7 2002/02/28 12:27:42 tnussb Exp $
*
******************************************************************************/

//-----------------------------------------------------------------------------
//
// Usage: ttmem()       .... outputs number of available bytes
//        ttmem(<size>) .... try to generate variables which takes up
//                           given size in bytes
//        ttmem(-1)     .... give memory free again (any size <= 0 will do it)
//
//        NOTE: variables are named ttmXXX where XXX is a number running from
//              000 to 999. if there are already ttmXXX variables they will
//              not be touched
//
//        NOTE2: odd number of bytes are corrected to the next larger even
//               number !!
//
//-----------------------------------------------------------------------------

/*

!! STATS OF A MAXIMUM OTH FILE ON THE PC !!

NOTE: the maximal available block of memory is 65520 bytes !!

filename:  ttm000.9xz
filesize:  65608 bytes
filetype:  OTH (0xf8)
extension: mem
signature: **TI92P*
folder:    main
varname:   ttm000
desc:
size:      65608 (0x10048)
datasize:  65518 (0xffee)      <-- including OTH info (6 bytes) [0][ext1][ext2][ext3][0][F8]
checksum:  51340 (0x8cc8)

*/

#define USE_TI89
#define USE_TI92P

#define OPTIMIZE_ROM_CALLS
#define NO_EXIT_SUPPORT

#include <tigcclib.h>

#define TTMEM_NAME_FORMAT "ttm%03d"
#define TTMEM_MIN_SIZE    10
#define TTMEM_MAX_SIZE    50000
#define TTMEM_MAX_FILES   1000

//=============================================================================
// tries to find a ttmem variable which corresponds to the given number
//=============================================================================
HSym FindVariable(short number) {
    char buffer[100];
    buffer[0] = 0;
    sprintf(buffer+1,TTMEM_NAME_FORMAT,number);
    return SymFind(buffer+7);
}


//=============================================================================
// add a new variable with name "ttm<3-digit-number>.mem" containing dummy
// data of given size
//
// returns: 0 for failure   1 for okay
//=============================================================================
static inline short AddNewVariable(unsigned short size,short number) {
    SYM_ENTRY*     symentry;
    char           name[100];
    unsigned char* varptr;
    HANDLE         hmem;

    //------------------------------------------------------
    // This is a kind of trick !!
    //
    // The user says: allocate, for example, 100 bytes
    // BUT we will not allocate 100 bytes! We just allocate
    // 100-2 = 98 bytes.
    // This way we will get a "correct" HeapAvail() result
    // back.
    // Indeed there will be 100 bytes used!
    // Internally there will be additionally 2 bytes
    // allocated for memory management. I guess there will
    // be the handle which references this memory block
    // stored. Or the length of this memory block is stored
    // there.
    //------------------------------------------------------
    size -= 2;

    //------------------------------------------------------
    // generate TIOS like variable name
    //------------------------------------------------------
    *name = 0;
    sprintf(name+1,TTMEM_NAME_FORMAT,number);

    //------------------------------------------------------
    // try to add and dereferencing it
    //------------------------------------------------------
    if (!(symentry = DerefSym(SymAdd(name+7)))) return 0;

    //------------------------------------------------------
    // try to allocate memory for variable contents
    //------------------------------------------------------
    if (!(hmem = HeapAlloc(size))) {
        SymDel(name+7);
        return 0;
    }

    //------------------------------------------------------
    // assign handle of memory to symbol and lock the memory
    //------------------------------------------------------
    if (!(varptr = (unsigned char*)HLock(symentry->handle = hmem))) {
        SymDel(name+7);
        return 0;
    }

    //-------------------------------------------------------
    // first 2 bytes of each variable is always the size of
    // it excluding the two bytes which holds the size itself
    //-------------------------------------------------------
    size -= 2;
    *varptr++ = (unsigned char)(size>>8);  // 2 bytes: length of the variable - 2
    *varptr++ = (unsigned char)(size&0xFF);

    //------------------------------------------------------
    // "jump" to OTH tag position
    //------------------------------------------------------
    varptr += size - 6;

    *varptr++ = 0;     // must always be zero of OTH type
    *varptr++ = 'm';   // first  character of extension
    *varptr++ = 'e';   // second character of extension
    *varptr++ = 'm';   // third  character of extension
    *varptr++ = 0;     // must always be zero of OTH type
    *varptr = OTH_TAG; // mark this variable to be of OTH type

    HeapUnlock(symentry->handle);

    return 1;
}



//=============================================================================
// where all the fun starts ...
//=============================================================================
void _main(void) {
    ESI           argptr;
    short         argtype;
    char          s[100];
    long          input;
    unsigned long available;
    short         i;

    //------------------------------------------------------
    // try to fetch the commandline argument
    //------------------------------------------------------
    InitArgPtr(argptr);
    argtype = GetArgType(argptr);

    //------------------------------------------------------
    // if it is not a positive or negative integer, just
    // display the available memory
    //------------------------------------------------------
    if (argtype != POSINT_TAG && argtype != NEGINT_TAG) {
        HeapCompress(); // compress it first !!!
        sprintf(s,"avail=%lu bytes (maxblock=%lu)",HeapAvail(),HeapMax());
        ST_showHelp(s);
        return;
    }

    //------------------------------------------------------
    // GetIntArg() will only returns positive integers, so
    // we have to treat the sign by "hand"
    //------------------------------------------------------
    input = GetIntArg(argptr);

    if (argtype == NEGINT_TAG) input = -input;


    //------------------------------------------------------
    // a negative or zero input means that we should delete
    // the previously allocated variables
    //------------------------------------------------------
    if (input <= 0) {
        HSym h;

        //------------------------------------------------
        // NOTE: we step here through all possible values,
        //       because we don't know if the user
        //       deletes some values by hand !!
        //------------------------------------------------
        for (i=0;i<TTMEM_MAX_FILES;i++) {
            h = FindVariable(i);
            // In a failure case we will not getting a NULL pointer
            // put a HSym with zero values !!!
            if (h.folder) HSymDel(h);
        }
        HeapCompress();
        sprintf(s,"avail=%lu bytes",HeapAvail());
        ST_showHelp(s);
        return;
    }

    //------------------------------------------------------
    // if we'll come here we want to "waste" memory by
    // adding variables
    //------------------------------------------------------
    available = HeapAvail();

    if (input < TTMEM_MIN_SIZE) {
        ST_showHelp("param too small");
        return;
    }

    if (input > (long)available) {
        ST_showHelp("not so much available");
        return;
    }

    //------------------------------------------------------
    // skip already existing variables
    //------------------------------------------------------
    for (i=0;i<TTMEM_MAX_FILES;i++) {
        HSym h = FindVariable(i);
        if (!h.folder) break;
    }

    //------------------------------------------------------
    // THAT shouldn't really happen. I think we will be
    // short of handles first !!
    //------------------------------------------------------
    if (i==TTMEM_MAX_FILES) {
        ST_showHelp("all vars used");
        return;
    }

    for (;i<TTMEM_MAX_FILES;i++) {
        unsigned short tempsize;

        if ((input - TTMEM_MAX_SIZE) > 0) tempsize = TTMEM_MAX_SIZE;
        else                              tempsize = input;

        if (!AddNewVariable(tempsize,i)) {
            ST_showHelp("add new var failed");
            return;
        }

        if (tempsize==TTMEM_MAX_SIZE) input -= TTMEM_MAX_SIZE;
        else                          break;
    }

    sprintf(s,"avail=%lu bytes",HeapAvail());
    ST_helpMsg(s);
}



//#############################################################################
//###################### NO MORE FAKES BEYOND THIS LINE #######################
//#############################################################################
//
//=============================================================================
// Revision History
//=============================================================================
//
// $Log: ttmem.c,v $
// Revision 1.7  2002/02/28 12:27:42  tnussb
// optimizations to reduce filesize
//
// Revision 1.6  2002/02/18 08:39:30  tnussb
// misstyping in addNewVariable() fixed [Lionel Debroux]
//
// Revision 1.5  2002/02/11 08:56:02  tnussb
// (1) added OPTIMIZE_ROM_CALLS and NO_EXIT_SUPPORT directives.
// (2) Stats of a maximum oth file on the PC corrected:
//     size was incorrect (the long was in big endian, not in
//     little endian: 0x48001000 instead of 0x10048).
// (3) int's replaced by short's
// [all changes of this revision done by Lionel Debroux]
//
// Revision 1.4  2001/07/31 22:56:59  Thomas Nussbaumer
// generic commit for Tools Suite version 0.99.10.
//
// Revision 1.3  2000/08/30 19:40:06  Thomas Nussbaumer
// result of GetIntArg() will be always assigned to input and
// just in the case of a negative integer inverted.
// This is shorter than doing it in a if/else construct.
//
// Revision 1.2  2000/08/23 01:07:00  Thomas Nussbaumer
// extended to show maxmem block and commented.
//
// Revision 1.1  2000/08/15 12:21:05  Thomas Nussbaumer
// initial version.
//
//
//
