/*****************************************************/
/* texttsr.c                                         */
/* -- This TSR is used to enter and leave 80x25 text */
/*    mode.                                          */
/* -- Windows video state is saved on entry to text  */
/*    mode and restored on exit.                     */
/*****************************************************/

/*****************************************************/
/* Header files.                                     */
/*****************************************************/
#include <dos.h>
#include <stdlib.h>
#include <malloc.h>
#include "texttsr.h"

/*****************************************************/
/* Constants.                                        */
/*****************************************************/
#define cbBlock 0x0040
#define cblkMax 0x0010
#define cbState (cbBlock * cblkMax)
#define pbNull  ((unsigned char *)0)

/*****************************************************/
/* Globals.                                          */
/*****************************************************/
/* Imports. */
extern unsigned       end;

/* Internal. */
void (interrupt far * pfnOld)(); /* Next in chain. */

/* Video state buffer. */
unsigned char         rgbState[cbState];

/* Previous video mode. */
unsigned char         bModeSav;

/* Number of 64 byte blocks required to save state. */
unsigned int          cblk;

/*****************************************************/
/* Routines.                                         */
/*****************************************************/

void interrupt far
IntHandler(int es, int ds, int di, int si, int bp,
  int sp, int bx, int dx, int cx, int ax, int ip,
  int cs, int flags)
/*****************************************************/
/* -- Multiplex interrupt handler.                   */
/*****************************************************/
    {
    switch (ax)
        {
    default:
        _chain_intr(pfnOld);
        break;

    case cmdIsLoaded:
        ax = 0x00ff;
        break;

    case cmdEnterText:
        {
        unsigned char * pb;

        pb = rgbState;
        _asm
            {
            /* Fill hardware state buffer. */
            push    ds
            pop     es
            mov     bx, pb
            mov     cx, 0x0007
            mov     ax, 0x1c01
            int     0x10

            /* Remember current video mode. */
            mov     ah, 0x0f
            int     0x10
            mov     bModeSav, al

            /* Set 80x25 text mode. */
            mov     ax, 0x0007
            int     0x10
            }
        }
        break;

    case cmdExitText:
        {
        unsigned char * pb  = rgbState;

        _asm
            {
            /* Restore previous video mode. */
            mov     ah, 0x00
            mov     al, bModeSav
            int     0x10

            /* Restore previous VGA state. */
            mov     cx, 0x0007
            push    ds
            pop     es
            mov     bx, pb
            mov     ax, 0x1c02
            int     0x10
            }
        }
        break;
        }
    }

int
main(void)
/*****************************************************/
/* -- Entry point.                                   */
/*****************************************************/
    {
    unsigned far * lpuwUpper;
    unsigned int   cpgf, cpgfMax, cbStack;
    unsigned char  bVal;

    /* Check to see if we have been loaded already. */
    _asm
        {
        mov     ax, cmdIsLoaded;
        int     0x2f
        mov     bVal, al
        }
    if (bVal == 0xff)
        return 0;

    /* Make sure adaptor supports save/restore */
    /* state.  Use the get buffer size, returns 0x1c */
    /* in AL for success. */
    _asm
        {
        mov     ax, 0x1c00
        mov     cx, 0x0007
        int     0x10
        mov     cblk, bx
        mov     bVal, al
        }

    if (bVal != 0x1c || cblk > cblkMax)
        return -1;

    /* Install TSR. */
    lpuwUpper = &end;
    cbStack = ((stackavail() + 2048) / 2048) * 2048;
    cpgf = (FP_SEG(lpuwUpper) - _psp) +
      (FP_OFF(lpuwUpper) + cbStack) / 16 + 1;
    pfnOld = _dos_getvect(0x2f);
    _dos_setvect(0x2f, IntHandler);
    _dos_setblock(cpgf, _psp, &cpgfMax);
    _dos_keep(0, cpgf);
    return -1;
    }

