home *** CD-ROM | disk | FTP | other *** search
/ C!T ROM 2 / ctrom_ii_b.zip / ctrom_ii_b / PROGRAM / C / TSR100JE / TSR.CPP < prev    next >
C/C++ Source or Header  |  1993-03-22  |  34KB  |  983 lines

  1. //--------------------------------------------------------------------------
  2. //
  3. //      TSR.CPP: body of DOS TSR class.
  4. //      Copyright (c) J.English 1993.
  5. //      Author's address: je@unix.brighton.ac.uk
  6. //
  7. //      Permission is granted to use copy and distribute the
  8. //      information contained in this file provided that this
  9. //      copyright notice is retained intact and that any software
  10. //      or other document incorporating this file or parts thereof
  11. //      makes the source code for the TSR class of which this file
  12. //      is a part freely available.
  13. //
  14. //--------------------------------------------------------------------------
  15. //
  16. //      Note: this class is highly DOS specific and hence non-portable.
  17. //      It also involves the use of assembly language and interrupt
  18. //      functions, so it is also very compiler-specific.  Will require
  19. //      modifications for use with compilers other than Borland C++ 3.0
  20. //      or later.  If Borland compilers prior to version to version 3.0
  21. //      are used, note that they do not support nesting of types within
  22. //      classes (so TSR::F1 etc. will need changing to a global name F1)
  23. //      and that the "_chain_intr" function used here was introduced with
  24. //      version 3.0.
  25. //
  26. //      Revision history:
  27. //      1.0     March 1993      Initial coding
  28. //
  29. //--------------------------------------------------------------------------
  30.  
  31. #include "tsr.h"
  32. #include <stdio.h>
  33. #include <string.h>
  34. #include <dos.h>
  35. #include <bios.h>
  36.  
  37. //--------------------------------------------------------------------------
  38. //
  39. //      Constants.
  40. //
  41. const int  MIN_STACK  = 512;            // minimum stack size in bytes
  42. const int  ALT_FKEY   = 0x68 - TSR::F1; // hotkey change for Alt-Fn
  43. const int  CTRL_FKEY  = 0x5E - TSR::F1; // hotkey change for Ctrl-Fn
  44. const int  SHIFT_FKEY = 0x54 - TSR::F1; // hotkey change for Shift-Fn
  45. const long DAY_LENGTH = 1573040L;       // length of day in timer ticks
  46.  
  47. const char far* COPYRIGHT = "C++ TSR class, Copyright (c) J.English 1993";
  48.                                         // copyright notice for self-identify
  49.  
  50. //--------------------------------------------------------------------------
  51. //
  52. //      Global (static) variables.
  53. //
  54. static TSR* instance = 0;               // pointer to the TSR instance
  55. static char TSRname [32];               // TSR identifying string
  56. static unsigned char hotshift;          // shift state for hotkey
  57. static unsigned char hotscan;           // scancode for hotkey
  58. static int interval;                    // length of timeslice in timer ticks
  59. static int TSRfunction;                 // Int 2F function used to find TSR
  60.  
  61. static char far* stack;                 // stack for TSR execution
  62. static unsigned stacklen;               // size of TSR stack in bytes
  63. static unsigned pspseg;                 // saved PSP segment
  64. static unsigned dtaseg;                 // saved DTA segment
  65. static unsigned dtaoff;                 // saved DTA offset
  66. static volatile char far* indos;        // saved INDOS flag address
  67. static volatile char far* critical;     // saved critical error flag address
  68.  
  69. static volatile int diskflag   = 0;     // set if disk interrupt in progress
  70. static volatile int breakflag  = 0;     // set if ctrl-break has been pressed
  71. static volatile int hotkeyflag = 0;     // set if hotkey pressed
  72. static volatile int TSRrequest = 0;     // set if TSR should wake up
  73. static volatile int TSRactive  = 0;     // set if TSR already active
  74. static volatile long nexttick  = 0;     // next timer tick to wake up at
  75.  
  76. static volatile long far* timer    = (volatile long far*) MK_FP(0x40,0x6C);
  77. static volatile char far* midnight = (volatile char far*) MK_FP(0x40,0x70);
  78.                                         // timer values in BIOS data area
  79.  
  80. //--------------------------------------------------------------------------
  81. //
  82. //      Functions and function pointers.
  83. //
  84. typedef void interrupt (*Handler)(...); // interrupt handler address type
  85.  
  86. static Handler oldint8;                 // old int 8 vector  (timer)
  87. static Handler oldint9;                 // old int 9 vector  (keyboard)
  88. static Handler oldint13;                // old int 13 vector (disk)
  89. static Handler oldint21;                // old int 21 vector (DOS services)
  90. static Handler oldint23;                // old int 23 vector (control-break)
  91. static Handler oldint24;                // old int 24 vector (critical error)
  92. static Handler oldint28;                // old int 28 vector (DOS scheduler)
  93. static Handler oldint2F;                // old int 2F vector (multiplex)
  94.  
  95. static int unhook ();                   // unload function used by int 2F
  96.  
  97. //--------------------------------------------------------------------------
  98. //
  99. //      Compare two far strings.
  100. //
  101. //      This is the same as "strcmp" except that we need a function to
  102. //      compare far pointers in all memory models, and "strcmp" only
  103. //      handles near pointers in the small and medium models.
  104. //
  105. int compare (const char far* a, const char far* b)
  106. {
  107.     while (*a == *b)
  108.     {   if (*a == '\0')
  109.             return 0;
  110.         a++, b++;
  111.     }
  112.     return *a - *b;
  113. }
  114.  
  115.  
  116.  
  117. //--------------------------------------------------------------------------
  118. //
  119. //      Check if a key has been pressed.
  120. //
  121. //      This nasty little routine is necessary because bioskey(1) does
  122. //      not recognise extended keyboards and will swallow keystrokes
  123. //      such as "ctrl-del" not supported on 84-key keyboards.  This
  124. //      makes it necessary to use bioskey(0x11) instead, but there
  125. //      is a problem in that this function does not leave AX clear
  126. //      if no key has been pressed (it just sets the zero flag) so
  127. //      some assembler is needed to deal with this.  It also means
  128. //      this library will not work with old (pre-AT) BIOSes that
  129. //      don't support int 16h, function 11h.
  130. //
  131. int check_key ()
  132. {
  133.     asm {
  134.         mov ah,0x11;        // do function 11h,
  135.         int 0x16;           // ... int 16h
  136.         jnz key_found;      // skip if a key has been pressed
  137.         mov ax,0;           // else set AX = 0
  138.     };
  139.   key_found:
  140.     return _AX;
  141. }
  142.  
  143. //--------------------------------------------------------------------------
  144. //
  145. //      Int 08 (timer) handler.
  146. //
  147. //      This handler first calls the original timer handler and then
  148. //      checks if either timeslicing has been selected or the hotkey
  149. //      has been pressed (as shown by "hotkeyflag").  If so, the TSR
  150. //      request flag is set.  If DOS is in a safe state, the TSR is
  151. //      then activated.
  152. //
  153. static void interrupt TSRint8 ()
  154. {
  155.     // chain to old int 8
  156.     oldint8 ();
  157.  
  158.     // exit if TSR is already running
  159.     if (TSRactive)
  160.         return;
  161.  
  162.     // amend "nexttick" if midnight has passed
  163.     if (*midnight != 0 && nexttick >= DAY_LENGTH)
  164.         nexttick -= DAY_LENGTH;
  165.  
  166.     // check if TSR should be activated
  167.     if (hotkeyflag == 0 && (interval == 0 || (*timer - nexttick) < 0))
  168.         return;
  169.  
  170.     // set request flag if so
  171.     TSRrequest = 1;
  172.  
  173.     // reset timer if past last activation period
  174.     if (interval != 0 && (*timer - nexttick) >= 0)
  175.         nexttick += interval;
  176.  
  177.     // activate TSR if DOS is safe
  178.     if (*indos == 0 && *critical == 0 && diskflag == 0)
  179.         activate ();
  180. }
  181.  
  182. //--------------------------------------------------------------------------
  183. //
  184. //      Int 09 (keyboard) handler.
  185. //
  186. //      This handler first calls the original keyboard handler (with
  187. //      interrupts enabled) and then looks to see if the keys pressed
  188. //      match the hotkey.  If so, "hotkeyflag" is set.  If a keycode
  189. //      is included in the hotkey code (i.e. it is not just a set of
  190. //      shift keys only) the key is removed from the keyboard buffer.
  191. //
  192. static void interrupt TSRint9 ()
  193. {
  194.     // chain to old int 9
  195.     asm { sti; }
  196.     oldint9 ();
  197.  
  198.     // check if TSR uses hotkey
  199.     if (hotshift == 0 && hotscan == 0)
  200.         return;
  201.  
  202.     // check if hotkey modifier keys are pressed
  203.     if ((bioskey(2) & 0x0F) != hotshift)
  204.         return;
  205.  
  206.     // check if hotkey (if any) has been pressed
  207.     if (hotscan == 0 || (check_key() >> 8) == hotscan)
  208.     {   hotkeyflag = 1;
  209.         if (hotscan != 0)
  210.             bioskey (0x10);
  211.     }
  212. }
  213.  
  214. //--------------------------------------------------------------------------
  215. //
  216. //      Int 13 (disk) handler.
  217. //
  218. //      This handler sets a flag to show that a (time-critical) disk
  219. //      transfer is in progress and then calls the original disk handler.
  220. //      It is declared as a far non-interrupt function, although it will
  221. //      be called as an interrupt handler; the code here does not affect
  222. //      any registers, and we want the flag settings from the original
  223. //      disk handler to be returned to the caller.
  224. //
  225. //      On entry, BP has been pushed on the stack.  DS must be set up
  226. //      so that "diskflag" and "oldint13" can be accessed, so DS is
  227. //      pushed on the stack and the BP is used to reset it.  On exit,
  228. //      DS and BP are popped from the stack and we return via a RETF 2
  229. //      which will throw away the flag register on the stack below the
  230. //      return address.
  231. //
  232. //      The code here is highly dependent on the compiler-generated entry
  233. //      sequence, so CHECK WITH CARE if any compiler other than Borland
  234. //      is being used!
  235. //      
  236. static void far TSRint13 ()
  237. {
  238.     // set correct data segment
  239.     asm { push ds; mov bp,seg diskflag; mov ds,bp; }
  240.  
  241.     // set flag while disk operation in progress
  242.     diskflag = 1;
  243.  
  244.     // chain to old int 13
  245.     oldint13 ();
  246.  
  247.     // clear disk flag
  248.     diskflag = 0;
  249.  
  250.     // return using "retf 2" to leave flags intact
  251.     asm { pop ds; pop bp; retf 2; }
  252. }
  253.  
  254. //--------------------------------------------------------------------------
  255. //
  256. //      Int 21 (DOS service) handler.
  257. //
  258. //      This handler is installed immediately prior to activating the
  259. //      TSR, and checks that the TSR does not call any unsafe services.
  260. //      The unsafe services are 00-0C (character I/O services), 3E (close
  261. //      file) for standard handles (0-4), 48 (allocate memory) and 4C
  262. //      (terminate process), or functions above 0C if a critical error
  263. //      is being handled.  If any of these are called from the TSR, the
  264. //      virtual function "dos_error" will be called with the service
  265. //      number as a parameter.  All other calls are passed to DOS in
  266. //      the normal way.
  267. //
  268. static void interrupt TSRint21 (unsigned,    unsigned,    unsigned,
  269.                                 unsigned,    unsigned,    unsigned,
  270.                                 unsigned,    unsigned bx, unsigned ax,
  271.                                 unsigned ip, unsigned cs)
  272. {
  273.     // static flag keeps track of whether called from "dos_error"
  274.     static int dosflag = 0;
  275.     
  276.     // ignore DOS calls from within "dos_error"
  277.     if (dosflag != 0)
  278.         return;
  279.  
  280.     // trap and ignore unsafe calls
  281.     const unsigned ah = ax >> 8;
  282.     if ((!*critical && (ah <= 0x0C || ah == 0x48 || ah == 0x4C
  283.                         || (ah == 0x3E && bx <= 4)))
  284.         || (*critical && ah > 0x0C))
  285.     {   dosflag = 1;
  286.         instance->dos_error (ah, *critical, cs, ip);
  287.         dosflag = 0;
  288.         return;
  289.     }
  290.  
  291.     // chain to old handler for safe calls
  292.     _chain_intr (oldint21);
  293. }
  294.  
  295. //--------------------------------------------------------------------------
  296. //
  297. //      Int 23 (control-break) handler.
  298. //
  299. //      This handler is installed immediately prior to activating the
  300. //      TSR.  It just sets a flag to record that control-break has been
  301. //      pressed.  The main TSR function can poll this flag using the
  302. //      member function "userbreak", which returns the flag value and
  303. //      also resets the flag.
  304. //
  305. static void interrupt TSRint23 ()
  306. {
  307.     breakflag = 1;
  308. }
  309.  
  310.  
  311.  
  312. //--------------------------------------------------------------------------
  313. //
  314. //      Int 24 (critical error) handler.
  315. //
  316. //      This handler is installed immediately prior to activating the
  317. //      TSR.  It just calls the virtual function "critical_error" to
  318. //      deal with the error.
  319. //
  320. static void interrupt TSRint24 (unsigned, unsigned di, unsigned,
  321.                                 unsigned, unsigned,    unsigned,
  322.                                 unsigned, unsigned,    unsigned ax)
  323. {
  324.     ax = instance->critical_error (di & 0x00FF);
  325.     if (ax == 2 || ax > 3)
  326.         ax = 3;
  327. }
  328.  
  329.  
  330.  
  331. //--------------------------------------------------------------------------
  332. //
  333. //      Int 28 (DOS scheduler) handler.
  334. //
  335. //      This handler is called by DOS whenever it is idle.  It first
  336. //      checks to see if the TSR is not already running and that there
  337. //      is a pending activation request.  If not, it calls the old
  338. //      handler and exits.  Otherwise it checks if DOS is in a safe
  339. //      state and if it is activates the TSR.  Note that the INDOS
  340. //      flag is safe if it is 1, as this means it is being called
  341. //      from inside DOS but that no nested calls are in progress.
  342. //
  343. static void interrupt TSRint28 ()
  344. {
  345.     // chain to old handler if TSR is already running or activation not
  346.     // requested
  347.     if (TSRactive || (!hotkeyflag && !TSRrequest))
  348.         _chain_intr (oldint28);
  349.  
  350.     // activate TSR if DOS is safe
  351.     if (*indos <= 1 && *critical == 0 && diskflag == 0)
  352.         activate ();
  353. }
  354.  
  355. //--------------------------------------------------------------------------
  356. //
  357. //      Int 2F (multiplex) handler.
  358. //
  359. //      This function provides a means of communicating with the TSR
  360. //      for installation testing and unloading.  AH must be the TSR
  361. //      function code (as located by the constructor), AL must be the
  362. //      subfunction code (00, 01, or 80 to FF), BX:DI must point to a
  363. //      copy of the class copyright notice and ES:SI must point to a
  364. //      copy of the TSR's name (or be a null pointer if AL = 00).  If
  365. //      these conditions are not met, the call is passed to the original
  366. //      handler.  Otherwise ES:SI is set to point to the copyright notice
  367. //      (which is guaranteed not to be the same as any identifying string
  368. //      due to its length) and a result code from the selected subfunction
  369. //      is stored in AX.  The calls are handled as follows:
  370. //
  371. //      Subfunction 00 (installation check): set AX to 0xFF if ES:SI point
  372. //          to a copy of this TSR's name (TSR is installed) or if ES:SI
  373. //          are both zero (check if function is available for use by a
  374. //          TSR built using this class).
  375. //      Subfunction 01 (unload TSR): the handler attempts to unhook the
  376. //          TSR ready to be unloaded from memory.  If this is successful,
  377. //          AX is set to the TSR's PSP address; otherwise AX will be set
  378. //          to zero.
  379. //      Subfunctions 80 - FF (application-specific): CX and DX are the
  380. //          call parameters (which might be a far pointer to a longer
  381. //          parameter block).  They are passed as references to the
  382. //          virtual function "respond".  The function result is returned
  383. //          in AX; the values of CX and DX as set by "respond" will also
  384. //          be returned to the calling program.
  385. //
  386. static void interrupt TSRint2F (unsigned, unsigned di, unsigned si,
  387.                                 unsigned, unsigned es, int dx,
  388.                                 int cx,   unsigned bx, unsigned ax)
  389. {
  390.     if ((ax >> 8) != TSRfunction
  391.         || compare ((char far*) MK_FP(bx,di), COPYRIGHT) != 0)
  392.         _chain_intr (oldint2F);             // this call doesn't return
  393.  
  394.     unsigned al = ax & 0xFF;                // subfunction code
  395.     
  396.     // installation check
  397.     if (al == 0 && ((es == 0 && si == 0)
  398.                     || compare ((char far*) MK_FP(es,si), TSRname) == 0))
  399.         ax = 0xFF;
  400.  
  401.     // unload resident copy request
  402.     else if (al == 1 && compare ((char far*) MK_FP(es,si), TSRname) == 0)
  403.         ax = unhook ();
  404.  
  405.     // application-specific request to resident copy
  406.     else if (al >= 0x80 && compare ((char far*) MK_FP(es,si), TSRname) == 0)
  407.         ax = instance->respond (al & 0x7F, cx, dx);
  408.  
  409.     // interrupt not directed here, so chain to old handler
  410.     else
  411.         _chain_intr (oldint2F);             // this call doesn't return
  412.  
  413.     // set ES:SI to address of resident copy's copyright notice
  414.     es = FP_SEG (COPYRIGHT);
  415.     si = FP_OFF (COPYRIGHT);
  416. }
  417.  
  418. //--------------------------------------------------------------------------
  419. //
  420. //      Activate body of TSR.
  421. //
  422. //      This function performs some essential housekeeping operations
  423. //      before and after calling the main TSR function.  This involves
  424. //      hooking interrupts 21, 23 and 24, saving the current DTA and
  425. //      PSP addresses, switching to the user stack, setting the TSRactive
  426. //      flag to avoid re-entrant activations, and clearing TSRrequest.
  427. //      After TSR::main has returned, the hotkey flag is cleared and
  428. //      the original context (stack, DTA, PSP and interrupt vectors)
  429. //      is restored.  Since this function is declared as a friend in
  430. //      the class declaration, it has to be public; to make sure it is
  431. //      not called directly, it checks that "TSRrequest" is non-zero
  432. //      before doing anything.
  433. //
  434. void activate ()
  435. {
  436.     // declare non-stack variables
  437.     static unsigned oldpsp;             // saved PSP segment
  438.     static unsigned olddtaseg;          // saved DTA segment
  439.     static unsigned olddtaoff;          // saved DTA offset
  440.     static int oldsp;                   // saved stack pointer
  441.     static int oldss;                   // saved stack segment
  442.     static union REGS r;                // registers for DOS calls
  443.     static struct SREGS s;              // segment registers for DOS calls
  444.  
  445.     // test TSR activation has been requested
  446.     if (TSRrequest == 0)
  447.         return;
  448.  
  449.     // save DTA and PSP addresses
  450.     r.h.ah = 0x2F;                          // get DTA
  451.     intdosx (&r, &r, &s);
  452.     olddtaseg = s.es, olddtaoff = r.x.bx;
  453.     r.h.ah = 0x51;                          // get PSP
  454.     intdos (&r, &r);
  455.     oldpsp = r.x.bx;
  456.  
  457.     // set DTA and PSP to saved addresses
  458.     r.h.ah = 0x1A;                          // set DTA
  459.     s.ds = dtaseg, r.x.dx = dtaoff;
  460.     intdosx (&r, &r, &s);
  461.     r.h.ah = 0x50;                          // set PSP
  462.     r.x.bx = pspseg;
  463.     intdos (&r, &r);
  464.  
  465.     // set interrupt vectors for TSR execution
  466.     oldint21 = getvect (0x21);
  467.     oldint23 = getvect (0x23);
  468.     oldint24 = getvect (0x24);
  469.     setvect (0x21, Handler (TSRint21));
  470.     setvect (0x23, Handler (TSRint23));
  471.     setvect (0x24, Handler (TSRint24));
  472.  
  473.     // switch to TSR stack
  474.     asm { cli; }
  475.     oldsp = _SP;
  476.     oldss = _SS;
  477.     _SP = FP_OFF (stack);
  478.     _SS = FP_SEG (stack);
  479.     asm { sti; }
  480.     
  481.     // execute body of TSR
  482.     TSRactive = 1;
  483.     instance->main (hotkeyflag);
  484.     TSRactive = 0;
  485.     
  486.     // restore original stack
  487.     asm { cli; }
  488.     _SP = oldsp;
  489.     _SS = oldss;
  490.     asm { sti; }
  491.  
  492.     // restore original interrupt vectors
  493.     setvect (0x21, oldint21);
  494.     setvect (0x23, oldint23);
  495.     setvect (0x24, oldint24);
  496.  
  497.     // save DTA and PSP addresses
  498.     r.h.ah = 0x2F;                          // get DTA
  499.     intdosx (&r, &r, &s);
  500.     dtaseg = s.es, dtaoff = r.x.bx;
  501.     r.h.ah = 0x51;                          // get PSP
  502.     intdos (&r, &r);
  503.     pspseg = r.x.bx;
  504.  
  505.     // reset original DTA and PSP
  506.     r.h.ah = 0x1A;                          // set DTA
  507.     s.ds = olddtaseg, r.x.dx = olddtaoff;
  508.     intdosx (&r, &r, &s);
  509.     r.h.ah = 0x50;                          // set PSP
  510.     r.x.bx = oldpsp;
  511.     intdos (&r, &r);
  512.  
  513.     // clear hotkey and request flags
  514.     hotkeyflag = 0;
  515.     TSRrequest = 0;
  516. }
  517.  
  518. //--------------------------------------------------------------------------
  519. //
  520. //      TSR::TSR
  521. //
  522. //      This is the class constructor.  It checks for a number of
  523. //      possible errors, and stores the name and stack size of the
  524. //      TSR in global variables.  A pointer to the current instance
  525. //      is stored in the global variable "instance" so that interrupt
  526. //      routines can invoke member functions.  The multiplex (int 2F)
  527. //      functions from 0xC0 to 0xFF are scanned for an unused function
  528. //      or an existing TSR based on this class; this is stored in
  529. //      "TSRfunction" and will be the function code recognised by
  530. //      the int 2F handler.
  531. //
  532. TSR::TSR (const char* name, unsigned stacksize)
  533. {
  534.     stat = SUCCESS;
  535.  
  536.     // copy name
  537.     strncpy (TSRname, name, sizeof(TSRname));
  538.     TSRname [sizeof(TSRname) - 1] = '\0';
  539.  
  540.     // check DOS version
  541.     if (_osmajor < 3)
  542.     {   stat = DOS_VERSION;       // error: DOS version < 3
  543.         return;
  544.     }
  545.  
  546.     // save instance pointer
  547.     if (instance != 0)
  548.     {   stat = MULTI_COPIES;      // error: multiple instances
  549.         return;
  550.     }
  551.     instance = this;
  552.  
  553.     // set stack length
  554.     stacklen = (stacksize < MIN_STACK ? MIN_STACK : stacksize);
  555.  
  556.     // find spare multiplex function
  557.     union REGS r;
  558.     struct SREGS s;
  559.     for (int i = 0xC0; i <= 0xFF; i++)
  560.     {   r.h.ah = i, r.h.al = 0;
  561.         r.x.bx = FP_SEG (COPYRIGHT);
  562.         r.x.di = FP_OFF (COPYRIGHT);
  563.         s.es = 0, r.x.si = 0;
  564.         int86x (0x2F, &r, &r, &s);
  565.         if (r.h.al == 0)
  566.             break;                  // function not in use
  567.         if (r.h.al == 0xFF
  568.             && compare ((char far*) MK_FP(s.es, r.x.si), COPYRIGHT) == 0)
  569.             break;                  // function in use by derivation of TSR
  570.     }
  571.     if (i > 0xFF)
  572.     {   stat = NO_MUX_FUNC;         // error: can't find multiplex function
  573.         return;
  574.     }
  575.     TSRfunction = i;
  576. }
  577.  
  578. //--------------------------------------------------------------------------
  579. //
  580. //      Do TSR setup.
  581. //
  582. //      This function creates the stack, saves the DTA and PSP addresses,
  583. //      locates the INDOS and critical error flags, and hooks the wake-up
  584. //      interrupts.  Note that the stack is never deleted; it will remain
  585. //      in existence while the TSR is loaded, and will be deallocated along
  586. //      with everything else when it is unloaded.  It returns zero if the
  587. //      stack could not be allocated.
  588. //
  589. static int setup ()
  590. {
  591.     // create stack (note: this is never deleted!)
  592.     char* stk = new char [stacklen];
  593.     if (stk == 0)
  594.         return 0;
  595.     stack = (stk + stacklen);
  596.  
  597.     // save DTA and PSP addresses
  598.     union REGS r;
  599.     struct SREGS s;
  600.     r.h.ah = 0x2F;                       // get DTA
  601.     intdosx (&r, &r, &s);
  602.     dtaseg = s.es, dtaoff = r.x.bx;
  603.     r.h.ah = 0x51;                       // get PSP
  604.     intdos (&r, &r);
  605.     pspseg = r.x.bx;
  606.  
  607.     // locate INDOS flag
  608.     r.h.ah = 0x34;
  609.     intdosx (&r, &r, &s);
  610.     indos = (char far*) MK_FP(s.es, r.x.bx);
  611.  
  612.     // locate critical error flag
  613.     r.x.ax = 0x5D06;
  614.     intdosx (&r, &r, &s);
  615.     critical = (char far*) MK_FP(s.ds, r.x.si);
  616.  
  617.     // hook interrupts
  618.     oldint8  = getvect (0x08);
  619.     oldint9  = getvect (0x09);
  620.     oldint13 = getvect (0x13);
  621.     oldint28 = getvect (0x28);
  622.     setvect (0x08, Handler (TSRint8));
  623.     setvect (0x09, Handler (TSRint9));
  624.     setvect (0x13, Handler (TSRint13));
  625.     setvect (0x28, Handler (TSRint28));
  626.  
  627.     return 1;
  628. }
  629.  
  630. //--------------------------------------------------------------------------
  631. //
  632. //      TSR::run
  633. //
  634. //      This function makes the TSR resident, and will only return if
  635. //      a copy of the TSR is already loaded.  The virtual function
  636. //      "startup" is called to perform any class-specific intialisation.
  637. //
  638. void TSR::run (int hotkey, unsigned timeslice)
  639. {
  640.     if (stat != SUCCESS)
  641.         return;
  642.  
  643.     // avoid reloading TSR if already loaded or failed
  644.     if (loaded ())
  645.     {   stat = RELOAD_FAIL;
  646.         return;
  647.     }
  648.  
  649.     // set hotkey shift state and scancode
  650.     hotshift = (hotkey >> 8) & 0xFF;
  651.     hotscan = hotkey & 0xFF;
  652.  
  653.     // check for F1 .. F10, which have different scan codes if ALT, CTRL
  654.     // or SHIFT is pressed (ALT takes precedence over CTRL which takes
  655.     // precedence over SHIFT)
  656.     if (hotscan >= F1 && hotscan <= F10)
  657.     {   if ((hotkey & ALT) != 0)
  658.             hotscan += ALT_FKEY;
  659.         else if ((hotkey & CTRL) != 0)
  660.             hotscan += CTRL_FKEY;
  661.         else if ((hotkey & (LSHIFT | RSHIFT)) != 0)
  662.             hotscan += SHIFT_FKEY;
  663.     }
  664.  
  665.     // set length of timeslice
  666.     interval = timeslice;
  667.     nexttick = *timer + interval;
  668.  
  669.     // perform user startup actions
  670.     if (instance->startup () != 0)
  671.     {   stat = STARTUP_ERR;
  672.         return;
  673.     }
  674.  
  675.     // do TSR setup if hotkey or timeslice specified
  676.     if (hotkey != NONE || timeslice != 0)
  677.     {   if (setup() == 0)
  678.         {   stat = STACK_FAIL;
  679.             return;
  680.         }
  681.     }
  682.   
  683.     // hook multiplex interrupt for self-identification
  684.     oldint2F = getvect (0x2F);
  685.     setvect (0x2F, Handler (TSRint2F));
  686.  
  687.     // terminate and stay resident (exit status = 0), memory size taken
  688.     // from bytes 3 and 4 of arena header in paragraph preceding PSP.
  689.     keep (0, *(unsigned far*) MK_FP(_psp-1, 3));
  690.  
  691.     // this line should never be reached
  692.     stat = LOAD_FAIL;
  693. }
  694.  
  695. //--------------------------------------------------------------------------
  696. //
  697. //      TSR::unload
  698. //
  699. //      Unloads a previously loaded copy of the TSR from memory.  The
  700. //      function returns 0 if the TSR was unloaded successfully, 1 if
  701. //      there is no previously loaded copy, and 2 if the previous copy
  702. //      cannot be unloaded.  The TSR is invoked to unload itself using
  703. //      the multiplex interrupt (2F).
  704. //
  705. int TSR::unload ()
  706. {
  707.     union REGS r;
  708.     struct SREGS s;
  709.  
  710.     // call TSR to unload itself via multiplex interrupt
  711.     r.h.ah = TSRfunction, r.h.al = 1;
  712.     r.x.bx = FP_SEG (COPYRIGHT);
  713.     r.x.di = FP_OFF (COPYRIGHT);
  714.     s.es   = FP_SEG (TSRname);
  715.     r.x.si = FP_OFF (TSRname);
  716.     int86x (0x2F, &r, &r, &s);
  717.  
  718.     // exit if TSR is not loaded
  719.     if (compare ((char far*) MK_FP(s.es, r.x.si), COPYRIGHT) != 0)
  720.         return NOT_LOADED;
  721.  
  722.     // exit if TSR cannot be unloaded
  723.     if (r.x.ax == 0)
  724.         return UNHOOK_FAIL;
  725.  
  726.     // locate the TSR environment segment from the PSP
  727.     const unsigned env = *(unsigned far*) MK_FP(r.x.ax, 0x2C);
  728.  
  729.     // free the TSR's memory
  730.     s.es = r.x.ax;
  731.     r.h.ah = 0x49;
  732.     intdosx (&r, &r, &s);
  733.     if (r.x.cflag != 0)
  734.         return MEM_FROZEN;
  735.  
  736.     // free the TSR's environment
  737.     s.es = env;
  738.     r.h.ah = 0x49;
  739.     intdosx (&r, &r, &s);
  740.     if (r.x.cflag != 0)
  741.         return ENV_FROZEN;
  742.  
  743.     // successfully unloaded
  744.     return SUCCESS;
  745. }
  746.  
  747. //--------------------------------------------------------------------------
  748. //
  749. //      TSR::request
  750. //
  751. //      This function is used by foreground copies of the program to
  752. //      communicate with a resident copy.  "Fn" is the function code
  753. //      and "p1" and "p2" are application-specific parameters to be
  754. //      passed to the resident copy.  Their updated values are returned
  755. //      to the caller.
  756. //
  757. int TSR::request (int& fn, int& p1, int& p2)
  758. {
  759.     union REGS r;
  760.     struct SREGS s;
  761.  
  762.     r.h.ah = TSRfunction, r.h.al = fn | 0x80;
  763.     r.x.bx = FP_SEG (COPYRIGHT);
  764.     r.x.di = FP_OFF (COPYRIGHT);
  765.     s.es   = FP_SEG (TSRname);
  766.     r.x.si = FP_OFF (TSRname);
  767.     r.x.cx = p1, r.x.dx = p2;
  768.     int86x (0x2F, &r, &r, &s);
  769.  
  770.     if (compare ((char far*) MK_FP(s.es, r.x.si), COPYRIGHT) != 0)
  771.         return NOT_LOADED;
  772.  
  773.     fn = r.x.ax, p1 = r.x.cx, p2 = r.x.dx;
  774.     return SUCCESS;
  775. }
  776.  
  777. //--------------------------------------------------------------------------
  778. //
  779. //      Unhook TSR.
  780. //
  781. //      This function prepares the TSR for unloading from memory.  The
  782. //      function returns the PSP address if the TSR can be unloaded
  783. //      successfully, and 0 otherwise (if any of the interrupt vectors
  784. //      have been re-hooked).  The virtual function "shutdown" is also
  785. //      called to perform any class-specific finalisation.  Interrupts
  786. //      08, 09, 13 and 28 only need unhooking if there is a non-null
  787. //      hotkey or timeslice.
  788. //
  789. static int unhook ()
  790. {
  791.     // exit if something has hooked the multiplex interrupt
  792.     if (getvect (0x2F) != Handler (TSRint2F))
  793.         return 0;
  794.  
  795.     // try to unhook wake-up interrupts if necessary
  796.     if (hotshift != 0 || hotscan != 0 || interval != 0)
  797.     {   if (getvect (0x08) != Handler (TSRint8)  ||
  798.             getvect (0x09) != Handler (TSRint9)  ||
  799.             getvect (0x13) != Handler (TSRint13) ||
  800.             getvect (0x28) != Handler (TSRint28))
  801.             return 0;
  802.         setvect (0x08, oldint8);
  803.         setvect (0x09, oldint9);
  804.         setvect (0x13, oldint13);
  805.         setvect (0x28, oldint28);
  806.     }
  807.  
  808.     // unhook multiplex interrupt
  809.     setvect (0x2F, oldint2F);
  810.  
  811.     // perform user shutdown actions
  812.     if (instance->shutdown () != 0)
  813.         return 0;
  814.  
  815.     // return PSP address
  816.     return _psp;
  817. }
  818.  
  819.  
  820.  
  821. //--------------------------------------------------------------------------
  822. //
  823. //      TSR::loaded
  824. //
  825. //      Returns true if a copy of the TSR is already in memory.
  826. //
  827. int TSR::loaded ()
  828. {
  829.     union REGS r;
  830.     struct SREGS s;
  831.     r.h.ah = TSRfunction, r.h.al = 0;
  832.     r.x.bx = FP_SEG (COPYRIGHT);
  833.     r.x.di = FP_OFF (COPYRIGHT);
  834.     s.es   = FP_SEG (TSRname);
  835.     r.x.si = FP_OFF (TSRname);
  836.     int86x (0x2F, &r, &r, &s);
  837.     return r.h.al == 0xFF
  838.            && compare ((char far*) MK_FP(s.es, r.x.si), COPYRIGHT) == 0;
  839. }
  840.  
  841. //--------------------------------------------------------------------------
  842. //
  843. //      Display a string using BIOS calls.
  844. //
  845. //      This function is used by "dos_error" below to display error
  846. //      messages without using DOS calls.
  847. //
  848. static void display (int page, const char* s)
  849. {
  850.     union REGS r;
  851.     while (*s != '\0')
  852.     {   if (*s >= ' ')              // set colours for printing characters
  853.         {   r.x.ax = 0x0920;        // display a space, using...
  854.             r.h.bh = page;
  855.             r.h.bl = 0x0E;          // ...yellow on black
  856.             r.x.cx = 1;
  857.             int86 (0x10, &r, &r);
  858.         }
  859.         r.h.ah = 0x0E;              // now display actual character
  860.         r.h.al = *s++;
  861.         r.h.bh = page;
  862.         int86 (0x10, &r, &r);
  863.     }
  864. }
  865.  
  866.  
  867.  
  868. //--------------------------------------------------------------------------
  869. //
  870. //      TSR::dos_error
  871. //
  872. //      This function is called if an unsafe DOS call is made from the
  873. //      main TSR function.  If it is called, it indicates a program bug.
  874. //      This is a virtual function so that a more appropriate function
  875. //      can be provided in classes derived from TSR.  The parameter "fn"
  876. //      is the function code from register AH; "ce" is non-zero if a
  877. //      critical error is being handled; "cs:ip" is the return address
  878. //      (i.e. the address of the instruction after the illegal call).
  879. //
  880. void TSR::dos_error (int fn, int ce, int cs, int ip)
  881. {
  882.     union REGS r;
  883.     char fmt [10];
  884.  
  885.     // get video mode
  886.     r.h.ah = 0x0F;
  887.     int86 (0x10, &r, &r);
  888.     const int mode = r.h.al;
  889.  
  890.     // select text mode if in graphics mode
  891.     const int graphics = (r.h.al > 3 && r.h.al != 7);
  892.     if (graphics)
  893.     {   r.x.ax = 0x0002;
  894.         int86 (0x10, &r, &r);
  895.     }
  896.  
  897.     // get active page
  898.     r.h.ah = 0x0F;
  899.     int86 (0x10, &r, &r);
  900.     const int page = r.h.bh;
  901.  
  902.     // display error message
  903.     display (page, "\a\r\n*** Illegal DOS call detected in TSR \"");
  904.     display (page, TSRname);
  905.     display (page, "\"\r\nFunction ");
  906.     sprintf (fmt, "%02X", fn); display (page, fmt);
  907.     display (page, " called from ");
  908.     sprintf (fmt, "%04X:%04X", cs, ip - 2); display (page, fmt);
  909.     if (ce != 0)
  910.         display (page, " during critical error handling");
  911.     display (page, ".\r\nThis call should not be used by a TSR and indicates"
  912.                    " a bug in the program.\r\nPress any key to ignore the"
  913.                    " call and continue... ");
  914.  
  915.     // wait for a keypress
  916.     bioskey (0);
  917.     display (page, "\r\n");
  918.  
  919.     // restore screen mode if it was a graphics mode
  920.     if (graphics)
  921.     {   r.x.ax = mode;
  922.         int86 (0x10, &r, &r);
  923.     }
  924. }
  925.  
  926. //--------------------------------------------------------------------------
  927. //
  928. //      TSR::pause
  929. //
  930. //      This function generates an INT 28 (the DOS scheduler interrupt).
  931. //      This interrupt is used to wake up TSRs, so the main TSR function
  932. //      can call this during lengthy processing to ensure that other TSRs
  933. //      get a chance to wake up.
  934. //
  935. void TSR::pause ()
  936. {
  937.     union REGS r;
  938.     int86 (0x28, &r, &r);
  939. }
  940.  
  941.  
  942.  
  943. //--------------------------------------------------------------------------
  944. //
  945. //      TSR::sync
  946. //
  947. //      This function resynchronises the timeslice interval so that the
  948. //      next timed wakeup will happen after "interval" ticks from now.
  949. //
  950. void TSR::sync ()
  951. {
  952.     nexttick = *timer + interval;
  953. }
  954.  
  955.  
  956.  
  957. //--------------------------------------------------------------------------
  958. //
  959. //      TSR::userbreak
  960. //
  961. //      This function returns the value of the flag which indicates if
  962. //      control-break has been pressed.  It also resets the flag.
  963. //
  964. int TSR::userbreak ()
  965. {
  966.     int b = breakflag;
  967.     breakflag = 0;
  968.     return b;
  969. }
  970.  
  971.  
  972.  
  973. //--------------------------------------------------------------------------
  974. //
  975. //      TSR::name
  976. //
  977. //      This function returns the name of the TSR instance.
  978. //
  979. const char* TSR::name ()
  980. {
  981.     return TSRname;
  982. }
  983.