home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast2.iso / c / pcpilot.zip / TSR.C < prev   
Text File  |  1990-01-13  |  11KB  |  402 lines

  1. /*
  2.     TSR.C
  3.     Original code from Al Stevens' book "Extending Turbo C Professional"
  4.     slightly modified by Tom Grubbe
  5.  
  6.     NOTE: In the listing in his book, Mr. Stevens #includes "interrupt.h"
  7.           which is nowhere to be found in the entire book!  However with
  8.           some added #defines and the IREGS structure this module compiles
  9.           and performs without errors.
  10. */
  11.  
  12. #include <stdio.h>
  13. #include <dos.h>
  14. #include <stdlib.h>
  15. #include <conio.h>
  16.  
  17. #define TRUE    1
  18. #define FALSE    0
  19.  
  20. /* --- vectors ---- */
  21. #define DISK    0x13
  22. #define INT28    0x28
  23. #define KYBRD    0x9
  24. #define CRIT    0x24
  25. #define DOS        0x21
  26. #define CTRLC    0x23
  27. #define CTRLBRK    0x1b
  28. #define TIMER    0x1c
  29.  
  30. typedef struct {
  31.     int bp,di,si,ds,es,dx,cx,bx,ax,ip,cs,fl;
  32. } IREGS;
  33. unsigned scancode;
  34. unsigned keymask;
  35.  
  36. extern char signature[];
  37. int unloading;            /* TSR unload flag */
  38.  
  39. static void (*UserRtn)(void);    /* Pointer to user's start routine */
  40. static void (*InitRtn)(void);    /* Pointer to user's initialization routine */
  41.  
  42. /* ---- interrupt vector chains ----- */
  43. static void interrupt (*oldbreak)(void);
  44. static void interrupt (*oldctrlc)(void);
  45. static void interrupt (*oldtimer)(void);
  46. static void interrupt (*old28)(void);
  47. static void interrupt (*oldkb)(void);
  48. static void interrupt (*olddisk)(void);
  49. static void interrupt (*oldcrit)(void);
  50. /* ------ ISRs fot the TSR --------- */
  51. static void interrupt newtimer(void);
  52. static void interrupt new28(void);
  53. static void interrupt newkb(void);
  54. static void interrupt newdisk(IREGS);
  55. static void interrupt newcrit(IREGS);
  56. static void interrupt newbreak(void);
  57.  
  58. static unsigned sizeprogram;/* TSR's program size                           */
  59. static unsigned dosseg;        /* DOS segment address                          */
  60. static unsigned dosbusy;    /* offset to InDos flag                         */
  61. static unsigned psps[2];    /* table of DOS PSP addresses                   */
  62. static int pspctr;            /* # of DOS PSP addresses                       */
  63. static int diskflag;        /* disk BIOS busy flag                          */
  64. static unsigned mcbseg;        /* address of 1st DOS mcb                       */
  65.  
  66. static char far *mydta;        /* TSR's DTA                                    */
  67. static unsigned myss;        /* TSR's stack segment                          */
  68. static unsigned mysp;        /* TSR's stack pointer                          */
  69. static unsigned intpsp;        /* Interrupted PSP address                      */
  70. static int running;            /* TSR running indicator                        */
  71. static int hotkey_flag;        /* Hotkey pressed flag                          */
  72.  
  73. /* ------ local prototypes ------- */
  74. void tsr(void (*FPtr)(void), void (*InitFPtr)(void));
  75.  
  76. static void tsr_init(void);
  77. static void resinit(void);
  78. static void unload(void);
  79. static void resterm(void);
  80. static void pspaddr(void);
  81. static void dores(void);
  82. static void resident_psp(void);
  83. static void interrupted_psp(void);
  84. static int resident(char *signature);
  85. static int test_hotkeys(int ky);
  86.  
  87. #define signon(s) printf("\n%s %s", signature, s);
  88.  
  89. void tsr(void (*FPtr)(void), void (*InitFPtr)(void))
  90. {
  91.     UserRtn = FPtr;
  92.     InitRtn = InitFPtr;
  93.     tsr_init();
  94.     if (resident(signature) == FALSE)    {
  95.         /* ------- initial load of TSR program -------- */
  96. #ifdef DEBUG
  97.         (*UserRtn)();
  98.         return;
  99. #else
  100.         /* ------- Terminate and Stay Resident -------- */
  101.         (*InitRtn)();    /* user's init function */
  102.         resinit();
  103. #endif
  104.     }
  105.     signon("is already installed.\n");
  106. }
  107.  
  108.  
  109.  
  110. /* --------- initialize TSR control values ---------- */
  111. static void tsr_init()
  112. {
  113.     unsigned es, bx;
  114.  
  115.     /* --------- get address of DOS busy flag --------- */
  116.     _AH = 0x34;
  117.     geninterrupt(DOS);
  118.     dosseg  = _ES;
  119.     dosbusy = _BX;
  120.     /* --------- get the seg addr of 1st DOS MCB --------- */
  121.     _AH = 0x52;
  122.     geninterrupt(DOS);
  123.     es = _ES;
  124.     bx = _BX;
  125.     mcbseg = peek(es, bx-2);
  126.     /* --------- get address of resident program's dta -------- */
  127.     mydta = getdta();
  128.     /* --------- get address of PSP in DOS 2.x --------- */
  129.     if (_osmajor < 3)
  130.         pspaddr();
  131. }
  132.  
  133. /* --------- establish & declare residency ---------- */
  134. static void resinit()
  135. {
  136.     myss = _SS;
  137.     mysp = _SP;
  138.     oldtimer = getvect(TIMER);
  139.     old28 = getvect(INT28);
  140.     oldkb = getvect(KYBRD);
  141.     olddisk = getvect(DISK);
  142.     /* ------- attach vectors to resident program ------- */
  143.     setvect(TIMER, newtimer);
  144.     setvect(KYBRD, newkb);
  145.     setvect(INT28, new28);
  146.     setvect(DISK, newdisk);
  147.     /* -------- compute program's size -------- */
  148.     sizeprogram = myss + ((mysp+50) / 16) - _psp;
  149.     /* -------- terminate and stay resident -------- */
  150.     keep(0, sizeprogram);
  151. }
  152.  
  153. /* --------- break handler ----------- */
  154. static void interrupt newbreak()
  155. {
  156.     return;
  157. }
  158.  
  159. /* ---------- critical error ISR --------- */
  160. static void interrupt newcrit(IREGS ir)
  161. {
  162.     ir.ax = 0;        /* ignore critical errors */
  163. }
  164.  
  165. /* -------- BIOS disk functions ISR --------- */
  166. static void interrupt newdisk(IREGS ir)
  167. {
  168.     diskflag++;
  169.     (*olddisk)();
  170.     ir.ax = _AX;        /* for the register returns */
  171.     ir.cx = _CX;
  172.     ir.dx = _DX;
  173.     ir.fl = _FLAGS;
  174.     --diskflag;
  175. }
  176.  
  177. /* -------- test for the hotkey --------- */
  178. static int test_hotkeys(int ky)
  179. {
  180.     static unsigned biosshift;
  181.  
  182.     biosshift = peekb(0, 0x417);
  183.     if (ky == scancode && (biosshift & keymask) == keymask)
  184.         hotkey_flag = !running;
  185.     return hotkey_flag;
  186. }
  187.  
  188. /* --------- keyboard ISR ---------- */
  189. static void interrupt newkb()
  190. {
  191.     static int kbval;
  192.  
  193.     if (test_hotkeys(inportb(0x60)))    {
  194.         /* reset the keyboard */
  195.         kbval = inportb(0x61);
  196.         outportb(0x61, kbval | 0x80);
  197.         outportb(0x61, kbval);
  198.         outportb(0x20, 0x20);
  199.     }
  200.     else
  201.         (*oldkb)();
  202. }
  203.  
  204. /* --------- timer ISR ---------- */
  205. static void interrupt newtimer()
  206. {
  207.     (*oldtimer)();
  208.     test_hotkeys(0);
  209.     if (hotkey_flag && peekb(dosseg, dosbusy) == 0)    {
  210.         if (diskflag == 0)    {
  211.             outportb(0x20, 0x20);
  212.             hotkey_flag = FALSE;
  213.             dores();
  214.         }
  215.     }
  216. }
  217.  
  218. /* ---------- 0x28 ISR ---------- */
  219. static void interrupt new28()
  220. {
  221.     (*old28)();
  222.     if (hotkey_flag && peekb(dosseg, dosbusy) != 0)    {
  223.         hotkey_flag = FALSE;
  224.         dores();
  225.     }
  226. }
  227.  
  228. /* ------ switch psp context from interrupted to TSR ------ */
  229. static void resident_psp()
  230. {
  231.     int pp;
  232.  
  233.     if (_osmajor < 3)    {
  234.         /* --- save interrupted program's psp (DOS 2.x) ---- */
  235.         intpsp = peek(dosseg, *psps);
  236.         /* ------- set resident program's psp ------- */
  237.         for (pp = 0; pp < pspctr; pp++)
  238.             poke(dosseg, psps[pp], _psp);
  239.     }
  240.     else    {
  241.         /* ----- save interrupted program's psp ------ */
  242.         intpsp = getpsp();
  243.         /* ------ set resident program's psp ------- */
  244.         _AH = 0x50;
  245.         _BX = _psp;
  246.         geninterrupt(DOS);
  247.     }
  248. }
  249.  
  250. /* -------- switch psp context from TSR to interrupted --------- */
  251. static void interrupted_psp()
  252. {
  253.     int  pp;
  254.  
  255.     if (_osmajor < 3)    {
  256.         /* --- reset interrupted psp (DOS 2.x) ---- */
  257.         for (pp = 0; pp < pspctr; pp++)
  258.             poke(dosseg, psps[pp], intpsp);
  259.     }
  260.     else    {
  261.         /* ------ reset interrupted psp ------- */
  262.         _AH = 0x50;
  263.         _BX = intpsp;
  264.         geninterrupt(DOS);
  265.     }
  266. }
  267.  
  268. /* -------- execute the resident program ---------- */
  269. static void dores()
  270. {
  271.     static char far *intdta;        /* interrupted DTA                      */
  272.     static unsigned intsp;            /*      "      stack pointer            */
  273.     static unsigned intss;            /*      "      stack segment            */
  274.     static unsigned ctrl_break;        /* Ctrl-Break setting                   */
  275.  
  276.     running = TRUE;                /* set TSR running metaphore                */
  277.     disable();
  278.     intsp = _SP;
  279.     intss = _SS;
  280.     _SP = mysp;
  281.     _SS = myss;
  282.     oldcrit = getvect(CRIT);
  283.     oldbreak = getvect(CTRLBRK);
  284.     oldctrlc = getvect(CTRLC);
  285.     setvect(CRIT, newcrit);
  286.     setvect(CTRLBRK, newbreak);
  287.     setvect(CTRLC, newbreak);
  288.     ctrl_break = getcbrk();            /* get ctrl break setting               */
  289.     setcbrk(0);                        /* turn off ctrl break logic            */
  290.     intdta = getdta();                /* get interrupted dta                  */
  291.     setdta(mydta);                    /* set resident dta                     */
  292.     resident_psp();                    /* swap psps                            */
  293.     enable();
  294.  
  295.     (*UserRtn)();                    /* call the TSR program here            */
  296.  
  297.     disable();
  298.     interrupted_psp();                /* reset interrupted psp                */
  299.     setdta(intdta);                    /* reset interrupted dta                */
  300.     setvect(CRIT, oldcrit);            /* reset critical error                 */
  301.     setvect(CTRLBRK, oldbreak);
  302.     setvect(CTRLC, oldctrlc);
  303.     setcbrk(ctrl_break);            /* reset ctrl break                     */
  304.     _SP = intsp;                    /* reset interrupted stack              */
  305.     _SS = intss;
  306.     enable();
  307.     if (unloading)
  308.         unload();
  309.     running = FALSE;
  310. }
  311.  
  312. /* ------ test to see if the program is already resident -------- */
  313. static int resident(char *signature)
  314. {
  315.     char *sg;
  316.     unsigned df;
  317.     unsigned blkseg, mcbs = mcbseg;
  318.  
  319.     df = _DS - _psp;
  320.     /* --- walk through mcb chain & search for TSR --- */
  321.     while (peekb(mcbs, 0) == 0x4d)    {
  322.         blkseg = peek(mcbs, 1);
  323.         if (peek(blkseg, 0) == 0x20cd)    {
  324.             /* ---- this is a psp ---- */
  325.             if (blkseg == _psp)
  326.                 break;            /* if the transient copy */
  327.             for (sg = signature; *sg; sg++)
  328.                 if (*sg != peekb(blkseg+df, (unsigned)sg))
  329.                     break;
  330.                 if (*sg == '\0')
  331.                     /* ---------- TSR is already resident ----------- */
  332.                     return TRUE;
  333.         }
  334.         mcbs += peek(mcbs, 3) + 1;
  335.     }
  336.     return FALSE;
  337. }
  338.  
  339. /* --------- find address of PSP (DOS 2.x) ----------- */
  340. static void pspaddr()
  341. {
  342.     unsigned adr = 0;
  343.  
  344.     disable();
  345.     /* ------- search for matches on the psp in dos -------- */
  346.     while (pspctr < 2 &&
  347.             (unsigned)((dosseg<<4) + adr) < (mcbseg<<4))    {
  348.         if (peek(dosseg, adr) == _psp)    {
  349.             /* ------ matches psp, set phoney psp ------- */
  350.             _AH = 0x50;
  351.             _BX = _psp + 1;
  352.             geninterrupt(DOS);
  353.             /* ------ did matched psp change to the phoney? ----- */
  354.             if (peek(dosseg, adr) == _psp + 1)
  355.                 /* ------ this is a DOS 2.x psp placeholder ----- */
  356.                 psps[pspctr++] = adr;
  357.             /* ----- reset the original psp ------ */
  358.             _AH = 0x50;
  359.             _BX = _psp;
  360.             geninterrupt(DOS);
  361.         }
  362.         adr++;
  363.     }
  364.     enable();
  365. }
  366.  
  367. /* -------- unload the rsident program --------- */
  368. static void unload()
  369. {
  370.     if (getvect(DISK) == (void interrupt (*)()) newdisk)
  371.         if (getvect(KYBRD) == newkb)
  372.             if (getvect(INT28) == new28)
  373.                 if (getvect(TIMER) == newtimer)    {
  374.                     resterm();
  375.                     return;
  376.                 }
  377.     /* --- another TSR is above us, cannot unload --- */
  378.     putch(7);
  379. }
  380.  
  381. /* --------- TSR unload function ----------- */
  382. static void resterm()
  383. {
  384.     unsigned mcbs = mcbseg;
  385.     /* restore the interrupted vectors */
  386.     setvect(TIMER, oldtimer);
  387.     setvect(KYBRD, oldkb);
  388.     setvect(INT28, old28);
  389.     setvect(DISK, olddisk);
  390.     /* obliterate the signature */
  391.     *signature = '\0';
  392.     /* walk through mcb chain &
  393.         release memory owned by the TSR */
  394.     while (peekb(mcbs, 0) == 0x4d)    {
  395.         if (peek(mcbs, 1) == _psp)
  396.             freemem(mcbs+1);
  397.         mcbs += peek(mcbs, 3) + 1;
  398.     }
  399. }
  400.  
  401.  
  402.