/* MULTI.C */

#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <dos.h>
#include <io.h>
#include "tsr.h"

#define SEARCH_DIR   "C:\\SPOOL\\"
#define	STACK_SIZE	4096	/* must be 16 byte boundaray */
#define SET_DTA		0x1a  /* SET Disk Transfer Address */
#define GET_DTA		0x2f  /* GET Disk  Transfer Address */

#define STAY_RES        0x31    /* DOS Term & Stay Reg */
#define ALTER_BLOCK	0x4a	/* DOS Alter Mem BLock */
#define	BACKGROUND_TICS 2
#define FOREGROUND_TICS 16
#define BACKGROUND_YIELD 0
#define FOREGROUND_YIELD 0

struct  prReq
{
	char level;
    char far *fname;
};

char	far *stack_ptr;         /* stack for our background TSR */
char 	far *ptr;
unsigned ss_save;             /* slot for stack segment register */
unsigned sp_save;             /* slot for stack pointer register */
unsigned unsafe_flag = 0;     /* set true by variouse interrupts */
int	first_time = 1;         /* flag for first time in running background */

int	my_psp;                 /* our TSR's psp */
int	foreground_psp;         /* PSP of foreground process we've interrupted */
int	foreground_dta_seg;     /* DTA of foreground process we've interrupted */
int	foreground_dta_off;
int	ctr=0; 
int	tic_count = 0;          /* counts timer tices */
int	in_progress = 0;        /* true if we're in background process */
/* various work variables */
int	ax,bx,cx,dx,bp,si,di,ss,sp,cs,es;
int	cs_ip;
union REGS regs;
struct SREGS sregs;
char  search_work[65];		
struct   ExtErr   my_ErrInfo;
struct   ExtErr   foreground_ErrInfo;

int   foreground_limit = FOREGROUND_TICS; /* limit of foreground cycle */
int   background_limit = BACKGROUND_TICS; /* limit of background cycle */

char  search_dir[65] = {SEARCH_DIR}; /* directory to search for spool files */
volatile int   int_28_active = 0;      /* true if activated by INT 28 */

/* old interrupt pointers are stored here */	
INTVECT old_int8, old_int9, old_int10, old_int13;
INTVECT old_int1B, old_int23, old_int24, old_int25;
INTVECT old_int26, old_int28;

/* prototypes for this module */
void main_loop();
void interrupt far new_int8(INTERRUPT_REGS);
void interrupt far new_int9(INTERRUPT_REGS);
void interrupt far new_int10(void);
void interrupt far new_int13(void);
void interrupt far new_int1B(INTERRUPT_REGS);
void interrupt far new_int23(INTERRUPT_REGS);
void interrupt far new_int24(INTERRUPT_REGS);
void interrupt far new_int25(void);
void interrupt far new_int26(void);
void interrupt far new_int28(INTERRUPT_REGS);
int  spooler_active(void);
int  search_spl_que(char * fname);
void suspend_foreground(void);
void suspend_background(void);

/* returns nonzero if spooler active */
int   spooler_active()
{
	union REGS  regs;

   regs.x.ax = 0x0100;  /* spooler active request */
   int86(0x2f,&regs,&regs);   /* call multiplex interrupt */
   return(regs.h.al == 0xff); /* FF if spooler active */
}

/* returns nonzero if string pointed to by fname is in the spooler queue */
int   search_spl_que(char * fname)
{
union REGS  regs;
struct SREGS   sregs;
char far *  que_ptr;
char que_name[65];
int ii;
int found;
   found = 0;
   if (spooler_active())
   {
      regs.x.ax = 0x0104;  /* get spooler status */
      int86x(0x2f,&regs,&regs,&sregs);
      /* on return from call DS:SI points to print queue */
      (long) que_ptr = (sregs.ds * 0x10000L) | regs.x.si;
      regs.x.ax = 0x0105;  /* release hold on spooler, side effect of status*/
      int86x(0x2f,&regs,&regs,&sregs);
      while (*que_ptr && !found)  /* while items in queue */
      {
         for (ii = 0; ii < 65; ii++)
            que_name[ii] = *(que_ptr + ii);
         found = !strcmpi(que_name,fname);
         que_ptr += 65;
      }
   }

   return(found);
}

void main_loop()
{
struct find_t  c_file;
union REGS  regs;
struct SREGS   sregs;
struct prReq prRequest;
struct prReq far * ptr;
int   sleep_cntr;

    while (1)
    	{	
      strcpy(search_work,search_dir);
      strcat(search_work,"*.SPL");   /* create dir search string */
   
      sleep_cntr = 20000;
      while (sleep_cntr)   /* wait a while between each dir search */
         {
         sleep_cntr-= 1;
         }
      printf("Background Active!\n");
  	   }

}

void  suspend_foreground()
{
		/* SWAP TO BACKGROUND */
		tic_count = 0;
	/* save old interrupt-CTRL-BREAK, CTRL-C and CRIT ERROR */ 
        old_int1B= _dos_getvect(0x1B); 
        old_int23= _dos_getvect(0x23); 
        old_int24= _dos_getvect(0x24); 

	/* set our interrupts functions */
       _dos_setvect(0x1b,new_int1B);
       _dos_setvect(0x23,new_int23);
       _dos_setvect(0x24,new_int24);

	/* save current PSP and set to ours */
	foreground_psp = GetPSP();
	SetPSP(my_psp);

	/* get foreground DTA */
	regs.h.ah = GET_DTA;
   intdosx(&regs, &regs, &sregs); 
	foreground_dta_seg = sregs.es;
	foreground_dta_off = regs.x.bx;

	/* set up our DTA */
	regs.h.ah = SET_DTA;
	regs.x.dx = 0x80;	/* use default in PSP area */
	sregs.ds = my_psp;
	intdosx(&regs, &regs, &sregs); 

   /* save error info */
   GetExtErr(&foreground_ErrInfo);

   if (! first_time)
      SetExtErr(&my_ErrInfo);

   in_progress = 1;
   background_limit = BACKGROUND_TICS; /* set default limit */
}

void suspend_background()
{
		/* SWAP TO FOREGROUND */

	/* put back original DTA */
	regs.h.ah = SET_DTA;
	regs.x.dx = foreground_dta_off;	
	sregs.ds  = foreground_dta_seg;
	intdosx(&regs, &regs, &sregs); 

	/* put back original PSP */
	SetPSP(foreground_psp);

	/* put back original INTS */
       _dos_setvect(0x1b,old_int1B);
       _dos_setvect(0x23,old_int23);
       _dos_setvect(0x24,old_int24);

   /* get error info */
   GetExtErr(&my_ErrInfo);
   SetExtErr(&foreground_ErrInfo);

	tic_count = 0;	
	in_progress = 0;
   int_28_active = 0;
   foreground_limit = FOREGROUND_TICS; /* set default limit */
}

/**********
* TIMER TICK INTERRUPT HANDLER
**********/
void interrupt far new_int8(INTERRUPT_REGS r)
{
	tic_count++;

	if ((in_progress && (tic_count >= background_limit) &&  
       !DosBusy() && !unsafe_flag) || 
       (in_progress && int_28_active && !Int28DosBusy() && 
		   (tic_count >=background_limit)))
	{
      suspend_background();
		restore_stack();
	}
    else if ((!in_progress && (tic_count >= foreground_limit) &&
		!DosBusy() && !unsafe_flag) ||
       (!in_progress && int_28_active && !Int28DosBusy() && 
		   (tic_count >=foreground_limit)))
	{
		set_stack();
      suspend_foreground();
      if (first_time)
		{
			first_time = 0;
			timer_int_chain();
		}
	}
   old_int8();    /* call old handler */
} 

/**********
* KEYBOARD INTERRUPT HANDLER
**********/
void interrupt far new_int9(INTERRUPT_REGS r)
{
   unsafe_flag++;
   old_int9();     
   if (in_progress)
      background_limit = BACKGROUND_YIELD; /* set to swap to foreground */  
   foreground_limit = 18;     /* since user hit keyboard */
   unsafe_flag--;
} 

/*********
* CTRL-BREAK INTERRUPT HANDLER
*********/
void interrupt far new_int1B(INTERRUPT_REGS r)
{
	/* do nothing */
} 
                  
/**********
* CTRL-C INTERRUPT HANDLER
**********/
void interrupt far new_int23(INTERRUPT_REGS r)
{
	/* do nothing */
} 

/**********
* CRTITICAL ERROR INTERRUPT HANDLER
**********/
void interrupt far new_int24(INTERRUPT_REGS r)
{
    if (_osmajor >= 3)
        r.ax = 3;	/* fail dos function */
    else
        r.ax = 0;		 
} 

/**********
* DOS IDLE INTERRUPT HANDLER
**********/
void interrupt far new_int28(INTERRUPT_REGS r)
{
	if (!in_progress && !Int28DosBusy() && !unsafe_flag && 
		tic_count > foreground_limit)
	{
      foreground_limit = FOREGROUND_YIELD;   /* stop foreground */
      int_28_active = 1;      
	  _enable();		/* STI */
      while (int_28_active);  /*spin waiting for task swap to background*/
	}
   old_int28();    /* call old handler */
} 

main()
{
	union REGS	regs;
	struct SREGS	sregs;		
	unsigned	memtop;
	void far* far*  tmpptr;

   puts("Multi-Tasking PRINT spooler installing");
   
   if (_osmajor < 3)
   {
      puts("Error: MS-DOS version 3.00 or greater required");
      exit(1);
   }
   
   if (! spooler_active())
      puts("Warning: Print Spooler not active");

   InitInDos();
   my_psp = GetPSP();

   /* MALLOC a stack for our TSR section */ 
   stack_ptr = malloc(STACK_SIZE);      
   stack_ptr += STACK_SIZE;

	ptr = stack_ptr;
	*(--stack_ptr) = 0xF2;  /* set up stack to look like an IRET was done*/
	*(--stack_ptr) = 0x02;
	stack_ptr -= 4;
	tmpptr = stack_ptr;
	*(tmpptr) = main_loop;

	/* get interrupt vectors */
   old_int8  = _dos_getvect(0x08); /* timer int */
   old_int9  = _dos_getvect(0x09); /* keyboard int */
   old_int10 = _dos_getvect(0x10); /* video int */
   old_int13 = _dos_getvect(0x13); /* disk int */
   old_int25 = _dos_getvect(0x25); /* sector read int */
   old_int26 = _dos_getvect(0x26); /* sector write int */
   old_int28 = _dos_getvect(0x28); /* dos idle int */

   init_intr();    /* init asm variables */ 

	_dos_setvect(0x08,new_int8); 
	_dos_setvect(0x09,new_int9); 
	_dos_setvect(0x10,new_int10); 
	_dos_setvect(0x13,new_int13); 
	_dos_setvect(0x25,new_int25); 
	_dos_setvect(0x26,new_int26); 
	_dos_setvect(0x28,new_int28); 
	
	/* release unused heap to MS-DOS */
   /* All MALLOCS for TSR section must be done in TSR_INIT() */
	segread(&sregs);	

   /* calculate top of memory and shrink block*/
	regs.h.ah = ALTER_BLOCK;
	memtop    = sregs.ds + ((FP_OFF(ptr)+15) >> 4) - _psp;
	regs.x.bx = memtop;
    sregs.es  = _psp;
    intdosx(&regs, &regs, &sregs); 

	bdos( STAY_RES, memtop, 0);  /* Make TSR */
}

