home *** CD-ROM | disk | FTP | other *** search
/ For Beginners & Professional Hackers / cd.iso / docum / dos-ref.doc / examples / chap5.arj / TSREXAMP.C < prev    next >
Encoding:
C/C++ Source or Header  |  1990-09-26  |  15.4 KB  |  556 lines

  1. /* 
  2. TSREXAMP.C
  3. by Raymond J. Michels
  4. with revisions by Tim Paterson
  5. and Andrew Schulman
  6. */
  7.  
  8. #include <stddef.h>
  9. #include <stdlib.h>
  10. #include <stdio.h>
  11. #include <conio.h>
  12. #include <dos.h>
  13. #include <bios.h>
  14. #include "tsr.h"
  15.  
  16. #define STACK_SIZE      8192  /* must be 16 byte boundaray */
  17. #define SET_DTA         0x1a  /* SET Disk Transfer Address */
  18. #define GET_DTA         0x2f  /* GET Disk Transfer Address */   
  19.  
  20. #define DOS_EXIT        0x4C  /* DOS terminate (exit) */
  21.  
  22. #define KEYBOARD_PORT   0x60  /* KEYBOARD Data Port */
  23.  
  24. #define PSP_TERMINATE   0x0A  /* Termination addr. in our PSP */
  25. #define PSP_PARENT_PSP  0x16  /* Parent's PSP from our PSP */
  26. #define PSP_ENV_ADDR    0x2c  /* environment address from PSP */
  27.  
  28. #define HOT_KEY         32    /* Hot Key along with ALT (D)*/
  29.  
  30. #define RIGHT_SHIFT     1
  31. #define LEFT_SHIFT      2
  32. #define CTRL_KEY        4
  33. #define ALT_KEY         8
  34.  
  35. #define MULTIPLEX_ID    0xC0
  36. #define INSTALL_CHECK   0x00
  37. #define INSTALLED       0xFF
  38. #define DEINSTALL       0x01
  39.  
  40. #define PARAGRAPHS(x)   ((FP_OFF(x) + 15) >> 4)
  41.  
  42. unsigned char multiplex_id = MULTIPLEX_ID;    
  43. char far *stack_ptr;           /* pointer to TSR stack */
  44. unsigned ss_save;              /* slot for stack segment register */
  45. unsigned sp_save;              /* slot for stack pointer register */
  46. int tsr_already_active = 0;    /* true if TSR active */
  47. int popup_while_dos_busy = 0;  /* true if hot key hit while dos busy */
  48. int int_28_in_progress = 0;    /* true if INT 28 in progress */
  49. int unsafe_flag = 0;           /* true if INT 13 in progress */
  50. unsigned keycode;                
  51. unsigned foreground_psp;       /* PSP of process we've interrupted */
  52. unsigned foreground_dta_seg;   /* DTA of process we've interrupted */
  53. unsigned foreground_dta_off;
  54. char buf[20];                  /* work buffer */
  55. unsigned long TerminateAddr;   /* used during de-install */
  56. union REGS regs;               /* register work structures */
  57. struct SREGS sregs;     
  58. struct ExtErr ErrInfo;         /* save area for extended error info */
  59. int hot_key;                   /* keycode for activation */
  60. int shift_key;                 /* shift status bits (alt,  ctrl..) */
  61. int user_key_set = 0;
  62.     
  63. /* Save areas for old interrupt pointers */
  64. INTVECT old_int8, old_int9, old_int10, old_int13, old_int1b, old_int23;
  65. INTVECT old_int24, old_int28, old_int2f;
  66.  
  67. #ifdef DOS_SWAP
  68. extern int dos_critical;     /* used by DOSSWAP.C */
  69. INTVECT old_int2a;
  70. void interrupt far new_int2a(INTERRUPT_REGS);
  71. #endif
  72.  
  73. /* PROTOTYPES FOR THIS MODULE */
  74. void interrupt far new_int8(INTERRUPT_REGS);
  75. void interrupt far new_int9(INTERRUPT_REGS);
  76. extern void interrupt far new_int13(void);  /* in TSRUTIL.ASM */
  77. void interrupt far new_int1b(INTERRUPT_REGS);
  78. void interrupt far new_int23(INTERRUPT_REGS);
  79. void interrupt far new_int24(INTERRUPT_REGS);
  80. void interrupt far new_int28(INTERRUPT_REGS);
  81. void interrupt far new_int2f(INTERRUPT_REGS);
  82. void tsr_function(void);
  83. void tsr_exit(void);
  84. void usage(char *);
  85. int UnlinkVect(int Vect, INTVECT NewInt, INTVECT OldInt);
  86. void parse_cmd_line(int argc, char *argv[]);
  87. void main(int argc,char *argv[]);
  88.  
  89. /*********
  90. * TIMER INTERRUPT HANDLER
  91. *********/
  92. void interrupt far new_int8(INTERRUPT_REGS r)
  93. {
  94. #ifdef DOS_SWAP
  95.     if (!tsr_already_active && popup_while_dos_busy &&
  96.         !dos_critical && !unsafe_flag)
  97. #else
  98.     if (!tsr_already_active && popup_while_dos_busy &&
  99.         !DosBusy() && !unsafe_flag) 
  100. #endif
  101.     {
  102.         popup_while_dos_busy = 0;
  103.         tsr_already_active = 1;
  104.         (*old_int8)();    /* process timer tick */
  105.         _enable(); /* turn interrupts back on */
  106.         tsr_function();
  107.         tsr_already_active = 0;
  108.     }
  109.     else
  110.         (*old_int8)();    /* process timer tick */
  111.  
  112. /**********
  113. * KEYBOARD INTERRUPT HANDLER
  114. **********/
  115. void interrupt far new_int9(INTERRUPT_REGS r)
  116. {
  117.     if (! tsr_already_active)
  118.     {
  119.          if ((keycode = inp(KEYBOARD_PORT)) != hot_key)
  120.             _chain_intr(old_int9);
  121.  
  122.          if ((_bios_keybrd(_KEYBRD_SHIFTSTATUS) & 
  123.             shift_key) == shift_key)
  124.          {      
  125. #ifdef USES_DISK
  126.             if (!unsafe_flag)
  127.             {
  128. #endif
  129.                 popup_while_dos_busy = 0;   
  130.                 tsr_already_active = 1;
  131.                 (*old_int9)();     /* send key to old int routine */
  132.                 tsr_function();
  133.                 tsr_already_active = 0; 
  134.  
  135. #ifdef USES_DISK
  136.             }
  137.             else
  138.             {
  139.                 popup_while_dos_busy = 1;
  140.                 _chain_intr(old_int9);
  141.             }
  142. #endif
  143.          }
  144.          else
  145.             _chain_intr(old_int9);
  146.      }      
  147.      else 
  148.          _chain_intr(old_int9);     
  149.  
  150. /*********
  151. * CTRL-BREAK INTERRUPT HANDLER
  152. *********/
  153. void interrupt far new_int1b(INTERRUPT_REGS r)
  154. {
  155.     /* do nothing */
  156.  
  157. /**********
  158. * CTRL-C INTERRUPT HANDLER
  159. **********/
  160. void interrupt far new_int23(INTERRUPT_REGS r)
  161. {
  162.     /* do nothing */
  163.  
  164. /**********
  165. * CRTITICAL ERROR INTERRUPT HANDLER
  166. **********/
  167. void interrupt far new_int24(INTERRUPT_REGS r)
  168. {
  169.     if (_osmajor >= 3)
  170.         r.ax = 3;   /* fail dos function */
  171.     else
  172.         r.ax = 0;        
  173.  
  174. /**********
  175. * DOS IDLE INTERRUPT HANDLER
  176. **********/
  177. void interrupt far new_int28(INTERRUPT_REGS r)
  178. {
  179.     int_28_in_progress++;
  180.  
  181. #ifdef DOS_SWAP
  182.     if (popup_while_dos_busy && !dos_critical 
  183.         && !tsr_already_active && !unsafe_flag)
  184. #else
  185.     if (popup_while_dos_busy && (!Int28DosBusy()) 
  186.         && !tsr_already_active && !unsafe_flag) 
  187. #endif
  188.     {
  189.          tsr_already_active = 1;
  190.          tsr_function();
  191.          tsr_already_active = 0;
  192.     }
  193.  
  194.     int_28_in_progress--;
  195.     _chain_intr(old_int28);  
  196.  
  197. #ifdef DOS_SWAP
  198. /*********
  199. * DOS INTERNAL INTERRUPT HANDLER
  200. *********/
  201. void interrupt far new_int2a(INTERRUPT_REGS r)
  202. {
  203.    switch (r.ax & 0xff00)
  204.    {
  205.       case 0x8000:    /* start critical section */
  206.          dos_critical++;
  207.          break;
  208.       case 0x8100:    /* end critical section */
  209.       case 0x8200:    /* end critical section */
  210.          if (dos_critical)    /* don't go negative */
  211.             dos_critical--;
  212.          break;
  213.       default:
  214.          break;
  215.     }
  216.    _chain_intr(old_int2a);
  217. #endif
  218.  
  219. /*********
  220. * DOS MULTIPLEX INTERRUPT HANDLER
  221. *********/
  222. void interrupt far new_int2f(INTERRUPT_REGS r)
  223. {
  224.     unsigned ah = r.ax >> 8;
  225.     unsigned al = r.ax & 0xFF;
  226.     
  227.     if (ah == multiplex_id)
  228.     {
  229.         if (al == INSTALL_CHECK)
  230.             r.ax |= INSTALLED;
  231.         else if (al == DEINSTALL)
  232.         {
  233.             // because of stack swap, pass arg in static variable.
  234.              TerminateAddr = ((long)r.bx << 16) + r.dx;
  235.              if (! tsr_already_active)  /* don't exit if we're active */
  236.              {
  237.                  _enable(); /* STI */
  238.                  tsr_exit();
  239.                  // If we got here, we weren't able to unlink
  240.                  r.ax = 0xFFFF;  //let caller know we're still there
  241.                  // MSC 6.0 /Ox optimizes the above instruction away
  242.                  // get it back by using the value in ax
  243.                  tsr_already_active = -r.ax;  
  244.                  // set to 1 to prevent any more action
  245.              }
  246.          }
  247.     }
  248.     else
  249.          _chain_intr(old_int2f);
  250.  
  251. /**********
  252. * TSR ACTIVE SECTION
  253. **********/
  254. void tsr_function()
  255. {
  256.     set_stack();
  257.  
  258. #ifdef DOS_SWAP
  259.     if (SaveDosSwap() && !int_28_in_progress)
  260. #else
  261.     if (DosBusy() && !int_28_in_progress)
  262. #endif
  263.         popup_while_dos_busy = 1; /* set flag: next INT 8,28 activates us */
  264.     else
  265.     {
  266.         popup_while_dos_busy = 0;
  267.  
  268.         /* save old interrupt-CTRL-BREAK, CTRL-C and CRIT ERROR */ 
  269.         old_int1b = _dos_getvect(0x1b); 
  270.         old_int23 = _dos_getvect(0x23); 
  271.         old_int24 = _dos_getvect(0x24); 
  272.  
  273.         /* set our interrupts functions */
  274.        _dos_setvect(0x1b, new_int1b);
  275.        _dos_setvect(0x23, new_int23);
  276.        _dos_setvect(0x24, new_int24);
  277.  
  278.         /* save current PSP and set to ours */
  279.         /* not needed for DOSSWAP, but can be used by application */
  280.         foreground_psp = GetPSP();
  281.  
  282.         SetPSP(_psp);   // _psp in STDLIB.H
  283.  
  284. #ifndef DOS_SWAP
  285.         /* get foreground DTA */
  286.         regs.h.ah = GET_DTA;
  287.         intdosx(®s, ®s, &sregs); 
  288.         foreground_dta_seg = sregs.es;
  289.         foreground_dta_off = regs.x.bx;
  290. #endif
  291.  
  292.         /* set up our DTA */
  293.         regs.h.ah = SET_DTA;
  294.         regs.x.dx = 0x80;   /* use default in PSP area */
  295.         sregs.ds = _psp;
  296.         intdosx(®s, ®s, &sregs); 
  297.  
  298. #ifndef DOS_SWAP
  299.         /* Get Extended Error Information */
  300.         GetExtErr(&ErrInfo);
  301. #endif
  302.  
  303.         /* suck up key(s) in buffer */
  304.         while (_bios_keybrd(_KEYBRD_READY))
  305.            _bios_keybrd(_KEYBRD_READ);
  306.  
  307.         /* your code goes here */
  308.         application();
  309.  
  310. #ifdef DOS_SWAP
  311.         RestoreDosSwap();
  312. #else
  313.         /* put back extended error information */
  314.         SetExtErr(&ErrInfo);
  315.  
  316.         /* put back original DTA */
  317.         regs.h.ah = SET_DTA;
  318.         regs.x.dx = foreground_dta_off; 
  319.         sregs.ds  = foreground_dta_seg;
  320.         intdosx(®s, ®s, &sregs); 
  321.  
  322.         /* put back original PSP */
  323.         SetPSP(foreground_psp);
  324. #endif
  325.  
  326.         /* put back original INTS */
  327.        _dos_setvect(0x1b, old_int1b);
  328.        _dos_setvect(0x23, old_int23);
  329.        _dos_setvect(0x24, old_int24);
  330.     }
  331.  
  332.     restore_stack();
  333. }
  334.  
  335. // only restores OldInt if someone hasn't grabbed away Vect
  336. int UnlinkVect(int Vect, INTVECT NewInt, INTVECT OldInt)
  337. {
  338.     if (NewInt == _dos_getvect(Vect))
  339.     {
  340.         _dos_setvect(Vect, OldInt);
  341.         return 0;
  342.     }
  343.     return 1;
  344. }
  345.  
  346. void tsr_exit(void)
  347. {
  348.     set_stack();
  349.     /* put interrupts back the way they were, if possible */
  350.  
  351.     if (!(UnlinkVect(8, new_int8, old_int8)      |
  352.           UnlinkVect(9, new_int9, old_int9)      |  // Do not use ||, we
  353.           UnlinkVect(0x28, new_int28, old_int28) |  // don't want early out
  354.           UnlinkVect(0x13, new_int13, old_int13) |
  355. #ifdef DOS_SWAP
  356.           UnlinkVect(0x2a, new_int2a, old_int2a) |
  357. #endif
  358.           UnlinkVect(0x2f, new_int2f, old_int2f) ))
  359.     {
  360.         // Set parent PSP, stored in our own PSP, to the current PSP
  361.         *(int far *)(((long)_psp << 16) + PSP_PARENT_PSP) = GetPSP();
  362.  
  363.         // Set terminate address in our PSP
  364.         *(long far *)(((long)_psp << 16) + PSP_TERMINATE) = TerminateAddr;
  365.  
  366.         /* set psp to be ours */
  367.         SetPSP(_psp);
  368.  
  369.         /* exit program */
  370.         bdos(DOS_EXIT, 0, 0);
  371.     }
  372.     restore_stack();
  373. }
  374.  
  375. void usage(char *progname)
  376. {
  377.     fputs("Usage: ", stdout);
  378.     puts(progname);
  379.     puts(" [-d to deinstall] [-k keycode shift-status] [-f multiplex id]");
  380.     puts(" Valid multiplex id");
  381.     puts("    00 through 15 specifies a unique INT 2F ID");
  382.     puts(" Valid shift-status is any combination of:");
  383.     puts("    1 = Right Shift");
  384.     puts("    2 = Left Shift");
  385.     puts("    4 = CTRL");
  386.     puts("    8 = ALT");
  387.     exit(1);
  388. }
  389.  
  390. void do_deinstall(char *progname)
  391. {
  392.     fputs(progname, stdout);
  393.     switch (deinstall())
  394.     {
  395.         case 1:
  396.             puts(" was not installed");
  397.             break;
  398.         case 2:
  399.             puts(" deinstalled");
  400.             break;
  401.         default:
  402.             puts(" deactivated but not removed");
  403.             break;
  404.     }
  405.     exit(0);
  406. }
  407.  
  408. int set_shift_key(unsigned sh)
  409. {
  410.     /* figure out, report on shift statuses */
  411.     /* make sure shift key < 0x10 and non-zero */
  412.     if (((shift_key = sh) < 0x10) && shift_key)
  413.     {
  414.         printf("Activation: %s%s%s%sSCAN=%d\n",
  415.         shift_key & RIGHT_SHIFT ? "RIGHT " : "",
  416.         shift_key & LEFT_SHIFT ? "LEFT " : "",
  417.         shift_key & CTRL_KEY ? "CTRL " : "",
  418.         shift_key & ALT_KEY ? "ALT " : "",
  419.           hot_key);
  420.         return 1;
  421.     }
  422.     else /* error, bad param */
  423.     {
  424.       puts("Invalid Shift-Status");
  425.       return 0;
  426.     }
  427. }
  428.                     
  429. void parse_cmd_line(int argc, char *argv[])
  430. {
  431.     int i;
  432.     int tmp;
  433.  
  434.     for (i = 1; i < argc; i++)   /* for each cmdline arg */
  435.         if ((argv[i][0] == '-') || (argv[i][0] == '/'))
  436.             switch(toupper(argv[i][1]))
  437.             {
  438.                 case 'D':
  439.                     do_deinstall(argv[0]);
  440.                     break;
  441.                 case 'K':    /* set pop-up key sequence */          
  442.                     user_key_set = 1;
  443.                     i++;     /* bump to next argument */
  444.                     if ((hot_key = atoi(argv[i])) != 0)
  445.                     {
  446.                         i++;   /* bump to next argument */
  447.                         if (! set_shift_key(atoi(argv[i])))
  448.                             usage(argv[0]);
  449.                     }
  450.                     else
  451.                         usage(argv[0]);
  452.                     break;
  453.                 case 'F':   /* set multiplex ID */
  454.                     i++;    /* bump to next argument */
  455.                     if ((tmp = atoi(argv[i])) < 0x10)                    
  456.                         multiplex_id += tmp; /* range of C0-CF */
  457.                     else
  458.                         usage(argv[0]);
  459.                     break;
  460.                 default:    /* invalid argument */
  461.                     usage(argv[0]);
  462.             }   /* end switch */
  463.         else
  464.             usage(argv[0]);
  465. }
  466.  
  467. void main(int argc,char *argv[])
  468. {
  469.     union REGS  regs;
  470.     struct SREGS sregs;     
  471.     unsigned far *fp;
  472.     unsigned memtop;
  473.     unsigned dummy;
  474.  
  475.     InitInDos();
  476.  
  477.     parse_cmd_line(argc,argv);
  478.  
  479.     /* check if TSR already installed! */
  480.     regs.h.ah = multiplex_id;
  481.     regs.h.al = INSTALL_CHECK;
  482.     int86(0x2f, ®s, ®s);
  483.     if (regs.h.al == INSTALLED)
  484.     {
  485.         puts("TSR already installed");
  486.         fputs(argv[0], stdout); puts(" -D de-installs");
  487.         exit(1);
  488.     }
  489.  
  490.     if (! user_key_set)
  491.     {
  492.         puts("Press ALT-D to activate TSR ");
  493.         printf("Multiplex ID = %0x \n",multiplex_id);
  494.         hot_key = HOT_KEY;
  495.         shift_key = ALT_KEY;
  496.     }
  497.    
  498. #ifdef DOS_SWAP
  499.     if (InitDosSwap() != 0)
  500.     {
  501.         puts("Error initializing DOS Swappable Data Area");
  502.         exit(1);
  503.     }
  504. #endif   
  505.  
  506.     /* MALLOC a stack for our TSR section */ 
  507.     stack_ptr = malloc(STACK_SIZE);
  508.     stack_ptr += STACK_SIZE;
  509.  
  510.     /* get interrupt vector */
  511.     old_int8  = _dos_getvect(8);     /* timer interrupt */
  512.     old_int9  = _dos_getvect(9);     /* keyboard interrupt */
  513.     old_int13 = _dos_getvect(0x13);  /* disk intr, in TSRUTIL.ASM */
  514.     old_int28 = _dos_getvect(0x28);  /* dos idle */
  515.     old_int2f = _dos_getvect(0x2f);  /* multiplex int */
  516.  
  517. #ifdef DOS_SWAP
  518.     old_int2a = _dos_getvect(0x2a);  /* dos internal int */
  519. #endif
  520.  
  521.     init_intr();  /* initilize int routines in TSRUTIL.ASM */
  522.  
  523.     /* set interrupts to our routines */
  524.     _dos_setvect(8, new_int8);
  525.     _dos_setvect(9, new_int9);
  526.     _dos_setvect(0x13, new_int13);      /* in TSRUTIL.ASM */
  527.     _dos_setvect(0x28, new_int28);
  528.     _dos_setvect(0x2f, new_int2f);
  529.  
  530. #ifdef DOS_SWAP
  531.     _dos_setvect(0x2a, new_int2a);
  532. #endif
  533.  
  534.     /* release environment back to MS-DOS */
  535.     FP_SEG(fp) = _psp;
  536.     FP_OFF(fp) = PSP_ENV_ADDR;
  537.     _dos_freemem(*fp);
  538.  
  539.     /* release unused heap to MS-DOS */
  540.     /* All MALLOCS for TSR section must be done in TSR_INIT() */
  541.     /* calculate top of memory, shrink block, and go TSR */
  542.     segread(&sregs);    
  543.     memtop = sregs.ds + PARAGRAPHS(stack_ptr) - _psp;
  544.     
  545.     _dos_setblock(memtop, _psp, &dummy);
  546.     _dos_keep(0, memtop);
  547. }
  548.