home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft-Programers-Library-v1.3.iso / sampcode / c / other / learn / alarm.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-11-01  |  7.5 KB  |  232 lines

  1. /* ALARM.C illustrates inline assembly and functions or keywords
  2.  * related to Terminate-and-Stay-Resident programs. Functions include:
  3.  *      _dos_setvect    _dos_getvect    _dos_keep
  4.  *      _chain_intr     _enable         _disable
  5.  *
  6.  * Keywords:
  7.  *      interrupt       _asm
  8.  * Directive:
  9.  *      #pragma
  10.  * Pragma:
  11.  *      check_stack     check_pointer
  12.  * Global variables:
  13.  *      _psp            _amblksiz
  14.  *
  15.  * WARNING: You must run ALARM from the DOS command line. The QC
  16.  * environment does not permit TSRs to be installed from inside it, since
  17.  * this would cause subsequent memory problems.
  18.  *
  19.  * See HARDERR.C for another example of inline assembler. See MOVEMEM.C
  20.  * for another pragma example.
  21.  */
  22.  
  23. #include <dos.h>
  24. #include <malloc.h>
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <time.h>
  28.  
  29. /* Stack and pointer checking off */
  30. #pragma check_stack( off )
  31. #pragma check_pointer( off )
  32.  
  33. /* Inline assembler macro to sound a bell. Note that comments in macros must
  34.  * be in the C format, not the assembler format.
  35.  */
  36. #define BEEP() _asm { \
  37.                         _asm     push bx        /* Save register   */ \
  38.                         _asm     sub  bx, bx    /* Page 0          */ \
  39.                         _asm     mov  ax, 0E07h /* TTY bell        */ \
  40.                         _asm     int  10h       /* BIOS 10         */ \
  41.                         _asm     pop  bx        /* Restore         */ \
  42.                     }
  43.  
  44. #define TICKPERMIN  1092L
  45. #define MINPERHOUR  60L
  46. enum BOOLEAN { FALSE, TRUE };
  47.  
  48. /* Prototypes for interrupt functions */
  49. void (interrupt far *oldtimer)( void );
  50. void (interrupt far *oldvideo)( void );
  51. void interrupt far newtimer( void );
  52. void interrupt far newvideo( unsigned _es, unsigned _ds, unsigned _di,
  53.                              unsigned _si, unsigned _bp, unsigned _sp,
  54.                              unsigned _bx, unsigned _dx, unsigned _cx,
  55.                              unsigned _ax, unsigned _ip, unsigned _cs,
  56.                              unsigned _flags );
  57.  
  58. /* Variables that will be accessed inside TSR must be global. */
  59. int  ftimesup = FALSE, fintimer = FALSE, finvideo = FALSE;
  60. long goaltick;
  61. long far *pcurtick = (long far *)0x0000046cL;
  62.  
  63. /* Huge pointers force compiler to do segment arithmetic for us. */
  64. char huge *tsrstack;
  65. char huge *appstack;
  66. char huge *tsrbottom;
  67.  
  68. main( int argc, char **argv )
  69. {
  70.     long minute, hour;
  71.     unsigned tsrsize;
  72.  
  73.     /* Initialize stack and bottom of program. */
  74.     _asm mov  WORD PTR tsrstack[0], sp
  75.     _asm mov  WORD PTR tsrstack[2], ss
  76.     FP_SEG( tsrbottom ) = _psp;
  77.     FP_OFF( tsrbottom ) = 0;
  78.  
  79.     /* Use 16 paragraph heap (controlled through global in malloc.h). */
  80.     _amblksiz = 256;
  81.  
  82.     /* Program size is:
  83.      *     top of stack
  84.      *   - bottom of program (converted to paragraphs)
  85.      *   + paragraphs in the heap plus
  86.      *   + one extra paragraph just to be safe
  87.      */
  88.     tsrsize = ((tsrstack - tsrbottom) >> 4) + (_amblksiz >> 4) + 1;
  89.  
  90.     /* If command-line, convert time to ticks past midnight. Time must
  91.      * include 0 in first place (0930, not 930). Time must be later than
  92.      * current time.
  93.      */
  94.     if( argc < 2 )
  95.     {
  96.         puts( "  Syntax: ALARM <hhmm> " );
  97.         puts( "     where <hhmm> is time (in military format) to ring alarm" );
  98.         exit( 1 );
  99.     }
  100.  
  101.     minute = atol( argv[1] + 2 );
  102.     argv[1][2] = 0;
  103.     hour = atol( argv[1] );
  104.     goaltick = (hour * MINPERHOUR * TICKPERMIN) + (minute * TICKPERMIN);
  105.     if( *pcurtick > goaltick )
  106.     {
  107.         puts( "It's past that time now" );
  108.         exit( 1 );
  109.     }
  110.  
  111.     /* Replace existing timer and video routines with ours. */
  112.     oldtimer = _dos_getvect( 0x1c );
  113.     _dos_setvect( 0x1c, newtimer );
  114.     oldvideo = _dos_getvect( 0x10 );
  115.     _dos_setvect( 0x10, newvideo );
  116.  
  117.     /* Free the PSP segment and terminate with program resident. */
  118.     _dos_freemem( _psp );
  119.     _dos_keep( 0, tsrsize );
  120. }
  121.  
  122. /* newtimer - Our timer interrupt compares current time to goal. If earlier
  123.  * it just continues. If later, it beeps and sets a flag to quit checking.
  124.  */
  125. void interrupt far newtimer()
  126. {
  127.     if( ftimesup )
  128.         _chain_intr( oldtimer );
  129.     else
  130.     {
  131.         /* First execute the original timer interrupt. */
  132.         (*oldtimer)();
  133.         if( *pcurtick > goaltick )
  134.         {
  135.             /* If time is up, set flag so we'll never return. */
  136.             ftimesup = TRUE;
  137.  
  138.             /* Save current stack of application, and set old stack of TSR.
  139.              * This is for safety since we don't know the state of the
  140.              * application stack, but we do know the state of our own stack.
  141.              * Turn off interrupts during the stack switch.
  142.              */
  143.             _disable();
  144.             _asm \
  145.             {
  146.                 mov  WORD PTR appstack[0], sp   ; Save current stack
  147.                 mov  WORD PTR appstack[2], ss
  148.                 mov  sp, WORD PTR tsrstack[0]   ; Load new stack
  149.                 mov  ss, WORD PTR tsrstack[2]
  150.             }
  151.             _enable();
  152.  
  153.             /* Make sure we're not in video interrupt, then BEEP. This check
  154.              * prevents the rare but potentially dangerous case of
  155.              * calling INT 10 to beep while INT 10 is already running.
  156.              */
  157.             while( finvideo )
  158.                 ;
  159.             BEEP();
  160.             BEEP();
  161.             BEEP();
  162.  
  163.             /* Restore application stack. */
  164.             _disable();
  165.             _asm \
  166.             {
  167.                 mov  sp, WORD PTR appstack[0]
  168.                 mov  ss, WORD PTR appstack[2]
  169.             }
  170.             _enable();
  171.         }
  172.     }
  173. }
  174.  
  175. /* newvideo - This routine protects against reentering INT 10 while it is
  176.  * already executing. This could be disastrous if the interrupt routine was
  177.  * interrupted while it was accessing a hardware register.
  178.  */
  179. void interrupt far newvideo( unsigned _es, unsigned _ds, unsigned _di,
  180.                              unsigned _si, unsigned _bp, unsigned _sp,
  181.                              unsigned _bx, unsigned _dx, unsigned _cx,
  182.                              unsigned _ax, unsigned _ip, unsigned _cs,
  183.                              unsigned _flags )
  184. {
  185.     static unsigned save_bp;
  186.  
  187.     /* If not already in (most of the time), chain to original. */
  188.     if( !finvideo )
  189.         _chain_intr( oldvideo );
  190.     else
  191.     {
  192.  
  193.         /* Set the inside flag, then make sure all the real registers
  194.          * that might be passed to an interrupt 10h match the parameter
  195.          * registers. Some of the real registers may be modified by the
  196.          * preceding code. Note that BP must be saved in a static (nonstack)
  197.          * variable so that it can be retrieved without modifying the stack.
  198.          */
  199.         finvideo = TRUE;
  200.         _asm \
  201.         {
  202.             mov ax, _ax
  203.             mov bx, _bx
  204.             mov cx, _cx
  205.             mov dx, _dx
  206.             mov es, _es
  207.             mov di, _di
  208.             mov save_bp, bp
  209.             mov bp, _bp
  210.         }
  211.  
  212.         /* Call the original interrupt. */
  213.         (*oldvideo)();
  214.  
  215.         /* Make sure that any values returned in real registers by the
  216.          * interrupt are updated in the parameter registers. Reset the flag.
  217.          */
  218.         _asm \
  219.         {
  220.             mov bp, save_bp
  221.             mov _bp, bp
  222.             mov _di, di
  223.             mov _es, es
  224.             mov _dx, dx
  225.             mov _cx, cx
  226.             mov _bx, bx
  227.             mov _ax, ax
  228.         }
  229.         finvideo = FALSE;
  230.     }
  231. }
  232.