    /*
     * TESSDEMO.C -- TesSeRact Demonstration Program
     */

/******************************< TESSDEMO.C >********************************
*                                                                           *
*                     TesSeRact Demonstration Program                       *
*                     --------------------------------                      *
*                                                                           *
*   Copyright (c) 1986, 1987, 1988, TesSeRact Development Team              *
*                                                                           *
*************************************************************************CR*/
    /*
     * Compiled with Turbo-C 1.5 for demonstration purposes
     *   Used with small model
     *   
     * To compile with MSC, use /DMSC5 command-line switch
     *   Note that if this is compiled with MSC 5.0, a lot of warning
     *   messages are generated -- this is because of the bug in MSC5.0
     *   having to do with functions prototyped with a 'void' parameter.
     *   It's not worth it to fix bugs in the compiler.
     * 
     */

#ifndef MSC5                            /* if MSC5 not defined             */
#define TC                              /*    assume Turbo C               */
#endif

#include <stdio.h>
#include <dos.h>
#include <bios.h>
#include <stdlib.h>
#include <string.h>
#include <io.h>

#ifdef TC
#include <conio.h>
#include <dir.h>
#endif

#include <ctype.h>
#include <errno.h>

#include "tess.h"                       /* Include file for TesSeRact     */
                                        /*    structures and prototypes    */

    /*
     * #define NOTSR to test routines
     */
/* #define NOTSR 1 */

    /*
     * Function prototypes for this file
     */
void c_str(int row, char *str);
void SaveCursor(void);
void RestoreCursor(void);
void NoCursor(void);
void BigCursor(void);
void DisplayTime(void);
void AdjustTime(void);
void fixrows(void);
unsigned SizeOfCode(unsigned type);
void InitTsrDemo(void);
void do_cpyrt(void);


    /*
     * Defines to be used for 'type' parameter
     *   of SizeOfCode
     */
#define NOHEAP  1
#define ALLHEAP 2
#define ALLSTACK 3

#ifdef TC
    /*
     * Variables specific to Turbo-C
     */
extern unsigned _heaplen = 128;         /* Use 128-byte heap               */
extern char _video[];                   /* undocumented video structure    */
extern void _VideoInt(void);            /* undocumented INT 10h call that  */
                                        /*    also saves BP register to    */
                                        /*    compensate for buggy BIOSes  */
#endif

    /*
     * Variables for Turbo-C Video Services
     */

#define MAXVIDROWS  60                  /* VGA allows 50 lines             */
                                        /*   But I have 60 on my screen    */
#define MAXVIDCOLS  80                  /* Only dealing with 80-cols       */
#define MAXVIDSIZE  (MAXVIDROWS * MAXVIDCOLS * 2)

#ifdef MSC5

#define COLORNORM   0x1e
#define COLORREV    0x4f

#else

#define COLORNORM   (YELLOW + (BLUE << 4))
#define COLORREV    (WHITE + (RED << 4))

#endif

#define MONONORM    0x07
#define MONOREV     0x70

#ifdef TC

#define cputc putch                     /* easier to understand            */

#else

#define cprintf printf                  /* CONSOLE I/O doesn't work with   */
#define cputs puts                      /*   MSC 5.x!  Some kind of problem*/
#define cputc putchar                   /*   with reentrancy, I assume     */

#endif

char savescreen[MAXVIDSIZE + 10];       /* buffer to save screen image     */
                                        /*   includes 10-byte fudge factor */

unsigned NormAtt,                       /* Default Normal Attribute        */
         RevAtt,                        /* Default Reverse Attribute       */
         curmode,                       /* Current video mode              */
         pagenum,                       /* Current video page              */
         curtype,                       /* Default cursor type             */
         oldcur,                        /* Old Cursor shape                */
         oldpos;                        /* Old Cursor position             */

unsigned maxrows = 25;                  /* Maximum rows on screen          */

unsigned char far *biosvid;             /* Pointer to video buffer         */

    /*
     * Other global variables
     */
char buffer[10];                        /* work buffer for date/time format*/

unsigned idnum,                         /* TSR Identification Number       */
         hours,                         /* Current hour of day             */
         mins,                          /* Current minute of hour          */
         secs,                          /* Current seconds of minute       */
         ticks;                         /* Timer-tick counter              */

unsigned BackFlag = 0;                  /* Background flag to signal       */
                                        /*   additional processing         */

FILE *fp = NULL;                        /* file structure for file to keep */
                                        /*   open between popups           */

#define FLUSHIT(fp) fclose(fp); \
                        fp = fopen("Tessdemo.dat","a+b")
                        

#ifdef MSC5

        /************************************************************
        * MSC 5.x compatibility routines                            *
        *********************************************************CR*/

void gotoxy(unsigned x, unsigned y)     /* position cursor using BIOS      */
{
    union REGS regs;

    regs.h.dh = y - 1;
    regs.h.dl = x - 1;

    regs.h.bh = 0;
    regs.h.ah = 2;

    int86(0x10, &regs, &regs);
}

void textmode(unsigned mode)            /* set video mode using BIOS       */
{
    union REGS regs;

    regs.h.ah = 0;
    regs.h.al = mode;

    int86(0x10, &regs, &regs);
}

unsigned currentattr = 0x07,            /* only needed for MSC5            */
         ulc = 0, 
         lrc = 0x184f;

                                        /* set local variables             */
void window(int left, int top, int right, int bottom)
{
    ulc = ((top - 1) << 8) + (left - 1);
    lrc = ((bottom - 1) << 8) + (right - 1);
}

void textattr(unsigned attr)
{
    currentattr = attr;                 /* we don't change atts with MSC   */
}

void clrscr(void)                       /* clear active window area        */
{
    union REGS regs;

    regs.x.ax = 0x0600;
    regs.h.bh = currentattr;

    regs.x.cx = ulc;
    regs.x.dx = lrc;

    int86(0x10, &regs, &regs);
}

                                        /* get text from video to buffer   */
int gettext(int left, int top, int right, int bottom, char *buffer)
{
    int i,j;
    char far *video;

    for(i = top - 1; i < bottom; i++)
    {
        FP_SEG(video) = FP_SEG(biosvid);
        FP_OFF(video) = ((i * 80) + (left - 1) ) * 2;

        for(j = left - 1; j < right; j++)
        {
            *buffer++ = *video++;
            *buffer++ = *video++;
        }
    }
    return(0);
}

                                        /* put text from buffer into video */
int puttext(int left, int top, int right, int bottom, char *buffer)
{
    int i,j;
    char far *video;

    for(i = top - 1; i < bottom; i++)
    {
        FP_SEG(video) = FP_SEG(biosvid);
        FP_OFF(video) = ((i * 80) + (left - 1) ) * 2;

        for(j = left - 1; j < right; j++)
        {
            *video++ = *buffer++;
            *video++ = *buffer++;
        }
    }
    return(0);
}

int wherex(void)                        /* get current x coordinate        */
{
    union REGS regs;

    regs.h.ah = 0x03;
    regs.h.bh = 0;

    int86(0x10, &regs, &regs);

    return(regs.h.dl + 1);
}

int wherey(void)                        /* get current y coordinate        */
{
   union REGS regs;

    regs.h.ah = 0x03;
    regs.h.bh = 0;

    int86(0x10, &regs, &regs);

    return(regs.h.dh + 1);
}
#define bioskey(c) _bios_keybrd((c))

#endif
        /************************************************************
        *  Video Support Routines                                   *
        *********************************************************CR*/

void c_str(int row, char *str)          /* Print a string, centered        */
{
    unsigned wid;                       /* temporary width variable        */

    wid = (80 - strlen(str))/2;         /* calculate cursor position       */

    gotoxy(wid,row);                    /* go there                        */

    cputs(str);                         /* display the string              */
}

void SaveCursor(void)                   /* save current cursor size and    */
{                                       /*   position                      */
#ifdef MSC5

#define MONO 7

    union REGS regs;

    regs.h.ah = 3;
    regs.h.bh = 0;

    int86(0x10, &regs, &regs);

    oldpos = regs.x.dx;
    oldcur = regs.x.cx;

#else

    _AH = 3;                            /* Get Cursor Position             */
    _BH = 0;
    _VideoInt();

    oldpos = _DX;                       /* Save return values              */
    oldcur = _CX;

#endif
                                        /* known bug on some monochrome    */
                                        /*   adapters reports the wrong    */
                                        /*   cursor shape when both color  */
                                        /*   and monochrome systems are    */
                                        /*   installed.                    */
    if( (curmode == MONO) && (oldcur == 0x0607) )
        oldcur = 0x0c0d;

    NoCursor();                         /* Make cursor hidden              */
}

void RestoreCursor(void)                /* restore saved cursor position   */
{                                       /*   and size                      */
#ifdef MSC5

    union REGS regs;

    regs.h.ah = 2;
    regs.h.bh = 0;
    regs.x.dx = oldpos;

    int86(0x10, &regs, &regs);

    regs.h.ah = 1;
    regs.h.bh = 0;
    regs.x.cx = oldcur;

    int86(0x10, &regs, &regs);

#else

    _AH = 2;                            /* restore saved position          */
    _BH = 0;
    _DX = oldpos;
    _VideoInt();

    _AH = 1;                            /* restore saved cursor type       */
    _BH = 0;
    _CX = oldcur;
    _VideoInt();

#endif
}

void NoCursor(void)                     /* turn off cursor                 */
{
#ifdef MSC5

    union REGS regs;

    regs.h.ah = 1;
    regs.x.cx = 0xf0f0;

    int86(0x10, &regs, &regs);

#else

    _AH = 1;
    _CX = 0xf0f0;
    _VideoInt();

#endif
}

void BigCursor(void)                    /* use block cursor                */
{
#ifdef MSC5

    union REGS regs;

    regs.h.ah = 1;
    regs.x.cx = curtype;

    int86(0x10, &regs, &regs);
#else

    _AH = 1;
    _CX = curtype;
    _VideoInt();

#endif
}

void GetVideoMode(void)
{
#ifdef MSC5

    union REGS regs;

    regs.h.ah = 0x0f;

    int86(0x10, &regs, &regs);

    curmode = regs.h.al;
    pagenum = regs.h.bh;

#else

    _AH = 0x0f;
    _VideoInt();

    curmode = _AL;
    pagenum = _BH;

#endif
}

void fixrows(void)                      /* Re-initialize current video     */
{                                       /*   information for new instance  */
                                        /*   of video usage                */
#ifdef TC
    extern char _video[];               /* Undocumented Video Data region  */
    extern void _crtinit(int newmode);  /* Internal Initialization Routine */
#endif

#ifdef MSC5
#define BW40 0
#define C40  1
#define BW80 2
#define C80  3

    GetVideoMode();

    maxrows = (*((unsigned char far *)0x484) + 1);
    if(maxrows < 25)
        maxrows = 25;

#else
    _crtinit(curmode);                  /* re-initialize video subsystem   */

    maxrows = _video[7];
#endif

    switch(curmode)                     /* deal with text mode             */
    {
        case BW40:
            textmode(BW80);             /* we need 80 columns              */
        case BW80:
        case MONO:
            NormAtt = MONONORM;         /* use Monochrome Attributes       */
            RevAtt = MONOREV;
            break;
        case C40:
            textmode(C80);              /* we need 80 columns              */
        case C80:
            NormAtt = COLORNORM;        /* use Color attributes            */
            RevAtt = COLORREV;
            break;
    }


    if(curmode == MONO)                 /* If monochrome ....              */
    {
#ifdef MSC5
        FP_SEG(biosvid) = 0xb000;
        FP_OFF(biosvid) = 0;
#else
        biosvid = MK_FP(0xb000,0);      /* ... set pointer and cursor      */
#endif
        curtype = 0x000d;
    }
    else                                /* That means color ....           */
    {
#ifdef MSC5
        FP_SEG(biosvid) = 0xb800;
        FP_OFF(biosvid) = 0;
#else
        biosvid = MK_FP(0xb800,0);      /* ... so set pointer and cursor   */
        curtype = 0x0007;
#endif
    }

}


/*****************************< main          >******************************
*                                                                           *
*                         main routine of C program                         *
*                         -------------------------                         *
*                                                                           *
*   Simple-minded main.  Calculates top of background stack region,         *
*       sets the stack points for the TSR; tests to see if we are already   *
*       resident; if so, displays ID number and exits.  If it is OK         *
*       to install it goes resident with DoTsrInit(). Note that InitTsrDemo *
*       is called by TsrCleanUp().                                          *
*                                                                           *
*   Parameters:                                                             *
*       none                                                                *
*                                                                           *
*   Returns:                                                                *
*       none                                                                *
*                                                                           *
*************************************************************************CR*/


struct ExtraHot MyKeys[2] = {
    { TSRHOT_X, TSRPOPALT, 1 },
    { TSRHOT_Y, TSRPOPCTRL, 2 }
    };

void main(void)
{
    char far *stackptr1,                /* Pointer to top of Popup Stack   */
         far *stackptr2;                /* Pointer to top of Background    */
                                        /*   stack area                    */
#ifdef MSC5
    extern unsigned _atopsp;            /* undocumented offset of top of   */
                                        /*   MSC5 stack area               */
    extern unsigned pascal STKHQQ;      /* undocumented offset of base of  */
                                        /*   MSC5 stack area (plus fudge)  */
    struct SREGS sregs;
#else
    extern unsigned __heapbase,         /* undocumented offset of base of  */
                                        /*   TC 1.5 heap area              */
                    _heaplen,           /* size of heap                    */
                    _stklen;            /* size of stack                   */
#endif

#ifdef MSC5

    segread(&sregs);

    FP_SEG(stackptr2) = sregs.ds;
    FP_OFF(stackptr2) = _atopsp - ((_atopsp - STKHQQ) / 2);

    FP_SEG(stackptr1) = sregs.ds;
    FP_OFF(stackptr1) = _atopsp;

#else
    stackptr1 = MK_FP(_DS, __heapbase + _heaplen + (_stklen / 2) - 16);
    stackptr2 = MK_FP(_DS, __heapbase + _heaplen + _stklen - 16);
#endif

    TsSetStack(stackptr1, stackptr2);   /* Set Popup Stack to stackptr1    */
                                        /*   background stack to stackptr2 */


                                        /* Are we already here?            */
#ifdef MSC5
    if(TsCheckResident("TESSMSC ",&idnum) == 0xffff)
#else
    if(TsCheckResident("TESSDEMO",&idnum) == 0xffff)
#endif
    {
                                        /* Yep!                            */
        puts("The TesSeRact Demonstration TSR has already been loaded");

        if(idnum & 0xff00)              /* if released                     */
        {
            puts("  But it is currently waiting to be released from memory");
            puts("  Restarting the TesSeRact Demonstration Program Now");
            TsRestart(idnum & 0x00ff);
        }
        else
        {
            puts("  Use ALT-LeftShift-R to PopUp the TsrMain() routine");
            printf("  Use ID Number %d to communicate through TesSeRact " \
                "Multiplex functions\n",idnum);
        }
        exit(1);
    }

    if(TsCheckHotkey(TSRHOT_R) == 0xffff) /* is hotkey safe?                 */
    {                                   /*  ... nope!                      */
        puts("The TesSeRact Demonstration TSR cannot be loaded because");
        puts("  another TSR currently resident on this system is using");
        puts("  the same hotkey!");
        exit(1);
    }
#ifdef NOTSR
    TsrMain();                          /* Test to call TsrMain            */
    bioskey(0);
#else

    if( TsDoInit(
        TSRHOT_R,
        TSRPOPALT + TSRPOPLSHIFT,
        TSRUSEPOPUP + TSRUSEBACK + TSRUSETIMER + TSRUSEUSER + NOPOPGRAPH,
        SizeOfCode(ALLSTACK)) )
        puts("Bad DoInit\n");
#endif

}

/*****************************< SizeOfCode    >******************************
*                                                                           *
*                 Determine size of program to keep resident                *
*                 ------------------------------------------                *
*                                                                           *
*   This function is an example of a function that can be used to determine *
*       the size of the TSR that is to remain resident.  There are three    *
*       options to this function -- NOHEAP, ALLHEAP, and ALLSTACK.  ALLHEAP *
*       and ALLSTACK are identical with MSC 5.0 -- the stack is below       *
*       the heap, and the stack will be part of the NOHEAP version as well. *
*       In Turbo C 1.5, with the stack ABOVE the heap in tiny and small     *
*       models, we can keep part of the heap, but drop off the stack.       *
*       Example code is shown for both MSC 5 and TC; other compilers and    *
*       langauges can determine the appropriate info as well.               *
*                                                                           *
*   Parameters:                                                             *
*       type             NOHEAP, ALLHEAP or ALLSTACK parameters define above*
*                                                                           *
*   Returns:                                                                *
*       Number of 16-byte paragraphs of memory to keep when going resident. *
*                                                                           *
*************************************************************************CR*/

unsigned SizeOfCode(unsigned type)
{
#ifdef  MSC5
    unsigned int far *PSP;              /* far pointer to PSP              */
    extern unsigned _psp,               /* segment of PSP                  */
                    _atopsp;            /* undocumented offset of top of   */
                                        /*   MSC 5.0 stack                 */
#endif  /* End of MSC5 */
#ifdef  TC
    extern unsigned _psp,               /* segment address of PSP          */
                    __heapbase,         /* undocumented offset of base of  */
                                        /*   TC 1.5 heap area              */
                    _heaplen,           /* size of heap                    */
                    _stklen;            /* size of stack                   */
#endif  /* End of TC */

    unsigned used;                      /* variable to save paragraphs     */
    struct SREGS sregs;                 /* segment register structure      */

    segread(&sregs);                    /* read the segment regs           */

    switch(type)
    {
        case ALLSTACK:
#ifdef TC
            used = (((__heapbase + 16 + _heaplen + _stklen) >> 4) + sregs.ds) - _psp;
            break;
#endif
        case ALLHEAP:
#ifdef MSC5
            FP_SEG(PSP) = _psp;         /* segment address of psp          */
            FP_OFF(PSP) = 0;            /* offset of the psp is zero       */
            used = *(PSP+1) - _psp;     /* number of paras used by program */
#endif
#ifdef TC
            used = (((__heapbase + 16 + _heaplen) >> 4) + sregs.ds) - _psp;
#endif
            break;
        case NOHEAP:
#ifdef  MSC5
            used = (((_atopsp + 16) >> 4) + sregs.ds) - _psp;
#endif  /* End of MSC5 */
#ifdef  TC
            used = (((__heapbase + 16) >> 4) + sregs.ds) - _psp;
#endif  /* End of TC */
            break;
    }

    return(used);                       /* return number of paragraphs     */
}

/*****************************< do_cpyrt      >******************************
*                                                                           *
*                       Display Copyright Information                       *
*                       -----------------------------                       *
*                                                                           *
*   Function to display formatted copyright information on the screen.      *
*                                                                           *
*   Parameters:                                                             *
*       none                                                                *
*                                                                           *
*   Returns:                                                                *
*       none                                                                *
*                                                                           *
*************************************************************************CR*/

void do_cpyrt(void)
{
    textattr(RevAtt);
    c_str(2,"The TesSeRact Demonstration Program");
    textattr(NormAtt);

    gotoxy(12,4);
    cputs("Copyright 1986, 1987, 1988, TesSeRact Development Team");

    gotoxy(12,5);
    cputs("All Rights Reserved");
}

/*****************************< DisplayTime   >******************************
*                                                                           *
*                     'Poke' current time into video RAM                    *
*                     ----------------------------------                    *
*                                                                           *
*   Adjusts minutes and seconds, and then pokes the holding buffer into     *
*       the first 8 character bytes of the Video RAM segment.  Note that    *
*       the 'hours' will be adjusted by the AdjustTime function.            *
*                                                                           *
*   Parameters:                                                             *
*       none                                                                *
*                                                                           *
*   Returns:                                                                *
*       none                                                                *
*                                                                           *
*************************************************************************CR***/

void DisplayTime(void)
{
    int i;

    buffer[0] = (hours / 10) + 0x30;
    buffer[1] = (hours % 10) + 0x30;

    buffer[3] = (mins / 10) + 0x30;
    buffer[4] = (mins % 10) + 0x30;

    buffer[6] = (secs / 10) + 0x30;
    buffer[7] = (secs % 10) + 0x30;

    for(i=0;i<8;i++)
        biosvid[i*2] = buffer[i];
}

/*****************************< AdjustTime    >******************************
*                                                                           *
*                     Call DOS to get the current time                      *
*                     --------------------------------                      *
*                                                                           *
*   Calls DOS to get the current time, save it to clobal values, and then   *
*       calls the C runtime sprintf() function to format it into the buffer *
*                                                                           *
*   Parameters:                                                             *
*       none                                                                *
*                                                                           *
*   Returns:                                                                *
*       none                                                                *
*                                                                           *
*************************************************************************CR***/

void AdjustTime(void)
{
#ifdef MSC5
    struct dostime_t timep;

    _dos_gettime(&timep);

    hours = timep.hour;
    mins = timep.minute;
    secs = timep.second;
#else
    struct time timep;

    gettime(&timep);

    hours = timep.ti_hour;
    mins = timep.ti_min;
    secs = timep.ti_sec;
#endif

    sprintf(buffer,"%02d:%02d:%02d",hours,mins,secs);
}

/*****************************< InitTsrDemo   >******************************
*                                                                           *
*                      Initialize variables and video                       *
*                      ------------------------------                       *
*                                                                           *
*   This function just initializes everything, displays a sign-on message,  *
*       and gets the clock info for the first time.                         *
*                                                                           *
*   Parameters:                                                             *
*       none                                                                *
*                                                                           *
*   Returns:                                                                *
*       none                                                                *
*                                                                           *
*************************************************************************CR***/

void InitTsrDemo(void)
{
    GetVideoMode();                     /* save current mode for later     */

    fixrows();

    clrscr();

    window(1,1,80,8);
    textattr(NormAtt);
    clrscr();

    do_cpyrt();

    c_str(7,"Press Alt-LeftShift-R to activate the TesSeRact " \
        "Demonstration Program\n ");

    AdjustTime();
    DisplayTime();

    if(fp == NULL)
    {
        fp = fopen("Tessdemo.dat","a+b");
        fprintf(fp,"TesSeRact Demonstration Program loaded at %s\n\r",buffer);
        FLUSHIT(fp);
    }
}

        /************************************************************
        *   TSR Procedures                                          *
        *********************************************************CR*/

char *StuffBuf = "\x72\x13\x69\x17\x6e\x31\x67\x22";
unsigned StuffLen = 4;

void far pascal TsrMain(void)
{
    unsigned oldstat,curdisk,ret;
    long bypercl, frees, total;
    unsigned char SaveVideoPageNum = 0;
    struct TsrParms far *ParmsPtr;
#ifdef MSC5
    struct diskfree_t diskfree;
    union REGS regs;
#else
    struct dfree diskfree;
#endif

    GetVideoMode();                     /* save current mode for later     */

    SaveCursor();

    if(pagenum)
    {
        SaveVideoPageNum = pagenum;

#ifdef MSC5
        regs.x.ax = 0x0500;
        int86(0x10, &regs, &regs);
#else
        _AX = 0x0500;
        _VideoInt();
#endif
    }

    fixrows();

    window(1,1,80,maxrows);
    gettext(1,1,80,maxrows,savescreen);

    textattr(NormAtt);
    clrscr();

    do_cpyrt();

    ParmsPtr = TsGetParms(idnum);

    oldstat = TsGetStat(idnum);

    gotoxy(5,7);
    cprintf("This TSR popped up with HotKey #%d, and is using the "
                "following procedures:", ParmsPtr->HotKeyFlag);

    if(oldstat & TSRUSEPOPUP)
    {
        gotoxy(10,wherey()+1);
        cputs("User-Defined PopUp Procedure");
    }
    if(oldstat & TSRUSEBACK)
    {
        gotoxy(10,wherey()+1);
        cputs("User-Defined Background Procedure");
    }
    if(oldstat & TSRUSETIMER)
    {
        gotoxy(10,wherey()+1);
        cputs("User-Defined Timer Procedure");
    }
    if(oldstat & TSRUSEUSER)
    {
        gotoxy(10,wherey()+1);
        cputs("User-Defined User Communication Procedure");
    }

#ifdef MSC5

    _dos_getdrive(&curdisk);

    _dos_getdiskfree(curdisk, &diskfree);

    bypercl = (long)(diskfree.bytes_per_sector * diskfree.sectors_per_cluster);
    frees = bypercl * diskfree.avail_clusters;
    total = bypercl * diskfree.total_clusters;

#else

    curdisk = getdisk() + 1;

    getdfree(curdisk, &diskfree);

    bypercl = (long)(diskfree.df_bsec * diskfree.df_sclus);
    frees = bypercl * diskfree.df_avail;
    total = bypercl * diskfree.df_total;

#endif

    gotoxy(5,19);
    cprintf("Current disk is %c:, with %ld bytes available, %ld total bytes",
        curdisk + 0x40, frees, total);

    fprintf(fp,"TesSeRact Demonstration Program popped up at %s\n\r",buffer);
    FLUSHIT(fp);

    gotoxy(5,21);
    cprintf("This TSR is called %-8.8Fs, and has a PSP at segment %04x",
        ParmsPtr->IdCode, ParmsPtr->TsrPSP);

    gotoxy(25,22);
    cprintf("Supported functions are %lx", ParmsPtr->FuncFlags);
    
    c_str(24,"Press 'R' to remove TSR from RAM; 'K' to stuff keyboard; any other key to exit");

    ret = bioskey(0) & 0xff;
    if(toupper(ret) == 'R')
        TsRelease(idnum);
    else
        if(toupper(ret) == 'K')
            TsStuffKeyboard(idnum, StuffBuf, StuffLen, STUFF_FAST);
    puttext(1,1,80,maxrows,savescreen);

    if(SaveVideoPageNum)
    {
#ifdef MSC5
        regs.h.ah = 0x05;
        regs.h.al = SaveVideoPageNum;
        int86(0x10, &regs, &regs);
#else
        _AH = 0x05;                     /* Routine to restore correct      */
        _AL = SaveVideoPageNum;         /*   video page; courtesy of Bruce */
        _VideoInt();                    /*   Kitchin                       */
#endif
    }

    RestoreCursor();
}

unsigned far pascal TsrBackCheck(void)
{
    return(BackFlag);
}

void far pascal TsrBackProc(void)
{
    AdjustTime();
    fprintf(fp,"TesSeRact Demonstration Program adjusted time at %s\n\r",buffer);
    FLUSHIT(fp);
    BackFlag = 0;
}

void far pascal TsrTimerProc(void)
{
    if(++ticks > 18)
    {
        ticks = 0;                      /* always clear ticks if > 18      */
        secs++;
        switch(secs)
        {
            case 60:
                secs = 0;               /* reset ticks for display & count */
                if(++mins > 59)         /* inc mins                        */
                {
                    mins = 0;           /* flip the minutes                */
                    if(++hours > 23)    /* update the hours                */
                        hours = 0;
                }
                BackFlag = 1;
                break;
            case 20:
            case 40:
                secs++;                 /* fudge for approx ticks          */
                break;
        }
        DisplayTime();                  /* always display time!            */
    }
}

void far pascal TsrUserProc(void far *UserPtr)
{
    printf("This is the user procedure:  Passed ptr = %Fs\n",UserPtr);
}

void far pascal TsrCleanUp(unsigned InitOrShutdown)
{
#ifdef TC
    extern void _restorezero(void);
#else
    extern void _ctermsub(void);
#endif  /* End of  */

    if(InitOrShutdown)      /* if we're shutting down          */
    {
        if(fp != NULL)
        {
            fprintf(fp,"TesSeRact Demonstration Program "
                "released at %s\n\r",buffer);
            fclose(fp);
        }
    /*
     * Please note that it is *absolutely* vital to call _restorezero()
     *   or _ctermsub() at this point -- otherwise, the INT 0 vector
     *   is not restored, and a divide-by-zero exception will cause
     *   a crash, rather than a clean exit.  Note that this routine
     *   is compiler-dependent .... CR
     */

#ifdef TC
        _restorezero();
#else
        _ctermsub();
#endif

    }
    else
    {
        TsSetExtraHot(idnum, 2, MyKeys);
        InitTsrDemo();
    }
}

