/*--------------------------------------------------------------------------*/
/*	Program name: 4_2_2.C                                               */
/*	Compile mode: Tiny                                                  */
/*	Image   file: 4_2_2.COM                                             */
/*	Remove  tool: REMOVE.COM                                            */
/*	Note        : When you press the right-'Shift' and right-'Ctrl' keys*/
/*			it will display a message 'This is a test..'.       */
/*		      It has removed the bugs of 4_2_1.C.                   */
/*--------------------------------------------------------------------------*/


#include	<stdio.h>
#include	<stdarg.h>
#include	<dos.h>

unsigned	int	TSR_SS;
unsigned	int	TSR_SP;
unsigned	int	TSR_DTA_SEG;
unsigned	int	TSR_DTA_OFF;
unsigned	int	TSR_PSP;
unsigned	int	OLD_SS;
unsigned	int	OLD_SP;
unsigned	int	OLD_DTA_SEG;
unsigned	int	OLD_DTA_OFF;
unsigned	int	OLD_PSP;
unsigned	int	Busy;
char		far	*InDos;
extern	void	far	*_heapbase;



void	interrupt	(*OldInt09)(void);
void	interrupt	(*OldInt28)(void);
void	interrupt	(*OldInt60)(void);
void	interrupt	NewInt09(void);
void	interrupt	NewInt28(void);
void	interrupt	NewInt60(void);

int	Printf(char *fmt, ... );
void	Activate(void);
void	Tsr_subroutine(void);

#define	KeyState	*((int far *)0x00400017)
#define	KeySelect	0x0005

/*----------------- Utilities ----------------------------------------------*/

int	Printf(char *fmt, ... )
{	va_list  argptr;
	char str[140];
	int cnt,i;
	union	REGS	reg;

	va_start(argptr,format);	/* Get the parameter		*/
	cnt=vsprintf(str,fmt,argptr);	/* Put output string in str[]	*/
	for(i=0;i<cnt;i++)		/* Use INT 10H to display string*/
	{	reg.h.ah=0x0E;
		reg.h.al=str[i];
		reg.h.bh=0;
		reg.h.bl=5;
		int86(0x10,&reg,&reg);
	}
	va_end(argptr);			/* va function end		*/
	return(cnt);
}

/*----------------- NewInt09 -----------------------------------------------*/

void	interrupt	NewInt09(void)
{
	(*OldInt09)();			/* Execute old ISR		*/

	disable();			/* Disable interrupt !!		*/
	if (Busy)	return;		/* Test Busy semaphore		*/
	Busy=1;				/* Set  Busy semaphore		*/
	enable();			/* Enable  interrupt !!		*/

	if ( ( (KeyState & KeySelect) != KeySelect) || (*InDos) )
	{	Busy=0;			/* If hot key is not pressed	*/
		return;			/* or DOS is busy then return	*/
	}

	Activate();			/* Do the TSR routine		*/

	Busy=0;				/* Reset Busy semaphore		*/
	return;				/* Return to interrupted program*/
}

void	Activate(void)
{	register	unsigned	Temp_DS;

	OLD_SS=_SS;			/* Save old stack segment	*/
	OLD_SP=_SP;			/* Save old stack pointer	*/

	disable();			/* Disable interrupt to switch	*/
	_SS=TSR_SS;			/* stack to TSR's own		*/
	_SP=TSR_SP;			/*				*/
	enable();			/* Switch done			*/


	_AH=0x2F;			/* Get old DTA address		*/
	geninterrupt(0x21);		/*				*/
	OLD_DTA_SEG=_ES;		/* Save it in OLD_DTA_SEG and	*/
	OLD_DTA_OFF=_BX;		/* OLD_DTA_OFF			*/

	_DX=TSR_DTA_OFF;		/* Use DOS function call to	*/
	Temp_DS=_DS;			/* switch DTA to TSR's own	*/
	_DS=TSR_DTA_SEG;		/* Note: _DS will be destoried	*/
	_AH=0x1A;			/*       so it must be save in	*/
	geninterrupt(0x21);		/*       Temp_DS, and restore	*/
	_DS=Temp_DS;			/*       it after calling	*/


	if (!*InDos)			/* If DOS is idle then switch	*/
	{	_AH=0x51;		/* PSP to TSR its own		*/
		geninterrupt(0x21);	/*				*/
		OLD_PSP=_BX;		/*				*/
		_BX=TSR_PSP;		/*				*/
		_AH=0x50;		/*				*/
		geninterrupt(0x21);	/*				*/
	}


	Tsr_subroutine();		/* Your subroutine call can be	*/
					/* inserted here		*/


	if (!*InDos)			/* If DOS is idle then restore	*/
	{	_BX=OLD_PSP;		/* calling program's psp	*/
		_AH=0x50;		/*				*/
		geninterrupt(0x21);	/*				*/
	}

	_DX=OLD_DTA_OFF;		/* Restore old DTA		*/
	Temp_DS=_DS;
	_DS=OLD_DTA_SEG;
	_AH=0x1A;
	geninterrupt(0x21);
	_DS=Temp_DS;


	disable();			/* Restore old stack		*/
	_SS=OLD_SS;
	_SP=OLD_SP;
	enable();

	return;				/* Activate routine end		*/
}

void	Tsr_subroutine(void)
{
	Printf("This is a test..\n\r");	/* a testing subroutine		*/
}

/*----------------- NewInt28 -----------------------------------------------*/

void	interrupt	NewInt28(void)
{
	(*OldInt28)();			/* Execute old ISR		*/

	disable();			/* Disable interrupt !!		*/
	if (Busy) return;		/* Test Busy semaphore		*/
	Busy=1;				/* Set  Busy semaphore		*/
	enable();			/* Enable  interrupt !!		*/

	if ( (KeyState & KeySelect) != KeySelect )
	{	Busy=0;			/* If hot key is not pressed	*/
		return;			/* then return			*/
	}

	Activate();			/* Do the TSR routine		*/

	Busy=0;				/* Reset Busy semaphore		*/
	return;				/* Return to interrupted program*/
}

/*----------------- NewInt60 -----------------------------------------------*/

void	interrupt	NewInt60(void)
{
	setvect(0x09,OldInt09);		/* Restore old interrupt vector	*/
	setvect(0x28,OldInt28);
	setvect(0x60,OldInt60);
}

/*----------------- Initialize ---------------------------------------------*/

main()
{
	TSR_SS=_SS;			/* Save Turbo C stack segment	*/
	TSR_SP=_BP;			/* Save Turbo C stack pointer!!	*/
	TSR_PSP=_psp;			/* Save Turbo C PSP segment	*/

	_AH=0x2F;			/* Use DOS function call to get	*/
	geninterrupt(0x21);		/* the DTA address		*/
	TSR_DTA_SEG=_ES;		/* Save DTA address in		*/
	TSR_DTA_OFF=_BX;		/* TSR_DTA_SEG & TSR_DTA_OFF	*/

	_AH=0x34;			/* Use DOS function call to get	*/
	geninterrupt(0x21);		/* the 'indos' flag and save it	*/
	InDos=MK_FP(_ES,_BX);		/* in InDos			*/

	OldInt09=getvect(0x09);		/* Save old interrupt vector	*/
	OldInt28=getvect(0x28);
	OldInt60=getvect(0x60);
	setvect(0x09,NewInt09);		/* Set  new interrupt vector	*/
	setvect(0x28,NewInt28);
	setvect(0x60,NewInt60);

	keep(0,FP_SEG(_heapbase)-_psp);	/* Terminate and stay resident	*/

	Printf("Something error in TSR.\n\r");
	exit(1);			/* Return error code		*/
}
