home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c221 / 5.ddi / MWHC.005 / 41 < prev    next >
Encoding:
Text File  |  1992-01-07  |  8.2 KB  |  219 lines

  1. /*
  2.  *   interrup.cf -- non-ANSI, cannot be used in ANSI mode!
  3.  *
  4.  *   Facilities for interrupt handling.
  5.  *   Not supported for 80386 DOS.
  6.  *
  7.  *           Copyright (c) 1990, MetaWare Incorporated
  8.  */
  9.  
  10. #ifndef _INTERRUP_CF
  11. #define _INTERRUP_CF
  12. #if __HIGHC__
  13. #include <language.cf>
  14. #include <implemen.cf>
  15.  
  16. #pragma Global_aliasing_convention(_Private_routine_prefix "%r");
  17.  
  18. /* In this package we implement both general interrupt handling
  19.  * and a simple control/C trap routine.
  20.  * If you're just interested in the latter, go to the end.
  21.  * If you just want to call an arbitrary DOS interrupt and do not care
  22.  * to write interrupt routines, see msdos.cf.
  23.  */
  24.  
  25. /* To get inline CLI and STI, use the intrinsic _inline: */
  26. #define CLI _inline(0xfa)
  27. #define STI _inline(0xfb)
  28.  
  29. #if 0
  30. When an interrupt occurs, the registers in Machine_status are saved at
  31. the entry and restored at the exit of an interrupt routine.  If the
  32. interrupt routine modifies the registers, the restored values will be
  33. the modified versions.    Therefore it is possible to write an interrupt
  34. routine to completely replace, e.g., MS-DOS-s INT 21, which takes
  35. arguments from registers and returns results in registers.
  36.  
  37. In addition to the registers, the address following the location of the
  38. interrupt and the flags register that were saved by the processor are
  39. available for modification or printing.
  40. #endif
  41.  
  42. typedef struct {
  43.    /* In reverse of the order in which they're pushed:                */
  44.    unsigned ES,DS,DI,SI,Waste1,Waste2,BX,DX,CX,AX;
  45.    /* The wasted portions are due to the 286 PUSHA/POPA instructions  */
  46.    /* used to effect the save.    We match the arrangement on the 8086  */
  47.    /* for object code compatibility.                      */
  48.    struct {
  49.       unsigned Offset,Segment;
  50.       } Interrupt_address;
  51.    unsigned Flags;
  52.    }  Machine_status;
  53.  
  54. /* Calling convention for interrupt routines: */
  55. #define C_interrupt (C_language | _INTERRUPT)
  56.  
  57. /* Each interrupt is designated by an index into the hardware          */
  58. /* interrupt vector located at address 0:                  */
  59. /*    void *Interrupt_vector[0.. ...]; (must be 4-byte pointers).     */
  60. /* Type Interrupt_index subscripts this array.                  */
  61.  
  62. typedef unsigned Interrupt_index;
  63. #define Control_C_interrupt_index 0x23         /* MS-DOS. */
  64.  
  65. #if 0
  66. To declare that a routine is an interrupt routine, it must
  67. have calling convention C_interrupt and be of routine type
  68. Interrupt_routine.
  69. Because the representation of interrupt routines differ
  70. depending upon the 8086 memory model used, and
  71. each 8086 interrupt entry always is a segment-offset pair,
  72. the Interrupt_vector_element type represents the latter.
  73. Thus the latter cannot be an Interrupt_routine.
  74. On more reasonable machines, these two types are identical.
  75. #endif
  76.  
  77. #pragma Calling_convention(C_interrupt);
  78. typedef void Interrupt_routine(Machine_status S)!;
  79. typedef struct {unsigned Offset,Segment;} Interrupt_vector_element;
  80. #pragma Calling_convention();
  81.  
  82. /* The next routines use the more efficient Pascal calling convention. */
  83. #pragma Calling_convention(PASCAL);
  84.  
  85. /* Enable/disable interrupts.                          */
  86. extern void interrupt_enable();  /*STI*/
  87. extern void interrupt_disable(); /*CLI*/
  88. /* Generate an "int V" instruction and execute it.                    */
  89. extern void Int(Interrupt_index V);
  90.  
  91. /* Store value of Interrupt_vector[V] into Result.              */
  92. extern void vector_element(Interrupt_vector_element *Result, Interrupt_index V);
  93.  
  94. /* Make P the interrupt handler for Interrupt_index V.              */
  95.  
  96. extern void install_interrupt_routine
  97.    (Interrupt_index V, Interrupt_routine P);
  98. extern void install_interrupt_vector_element
  99.    (Interrupt_index V, Interrupt_vector_element P);
  100. #if 0
  101. NOTE:  It is not safe to install an Interrupt_routine P that is not a
  102. level-1 routine!!!  The hardware will NOT supply the up-level context
  103. that P needs to access the local variables of its parent routine.  It is
  104. OK to install P only if no up-level references are made -- and THIS
  105. INCLUDES any "goto" out of P.  See examples below for a way to get
  106. around this.
  107.  
  108. You can call either C interrupt routines or 8086
  109. Interrupt_vector_elements directly with the following procedures that
  110. take arguments from and return results in the passed Machine_status.
  111. #endif
  112. extern void call_interrupt_routine
  113.    (Machine_status *S, Interrupt_routine P);
  114. extern void call_interrupt_vector_element
  115.    (Machine_status *S, Interrupt_vector_element P);
  116.  
  117. #if 0
  118. For example, to define a routine to catch the MS-DOS control/C
  119. interrupt:
  120.          #pragma Calling_convention(C_interrupt);
  121.      voide Catch_Control_C(Machine_status S) {
  122.          #pragma Calling_convention();
  123.     printf("Ouch!!  You typed Control/C!!\n");
  124.     writeln("Don't do it again!\n");
  125.     }
  126.      ...
  127.      Install_interrupt_routine(Control_C_interrupt_index,Catch_Control_C);
  128.      /* Now, when ^C is typed, Catch_control_C is invoked. */
  129.  
  130. The interrupt routine may not generally be a non-level-1 procedure,
  131. since the up-level context is not loaded by the hardware when the
  132. interrupt occurs.  (Note that non-level-1 procedures are a High C
  133. extension; if you never use them, you don-t need to read this.) To
  134. achieve the same effect, store the non-level-1 procedure in a global
  135. procedure variable, and call this variable from a level-1 interrupt
  136. routine.  Since a procedure variable records the up-level context, such
  137. context can be re-loaded when the procedure variable is called. For
  138. example, here is a way to use ^C to get back into an editor command
  139. level:
  140.  
  141.  
  142.      void Call_when_CC_occurs()!;    /* Full procedure variable. */
  143.          #pragma Calling_convention(C_interrupt);
  144.      void Catch_Control_C(Machine_status S) {
  145.          #pragma Calling_convention();
  146.     Call_when_CC_occurs();
  147.     }
  148.      #include <setjmp.h>
  149.      void Editor() {
  150.     jmp_buf B;
  151.     void CC() {
  152.        printf("<INTERRUPT> -- command terminated.\n");
  153.        longjmp(B); /* Requires up-level reference to B. */
  154.        }
  155.     Call_when_CC_occurs = CC;
  156.     Install_interrupt_routine(Control_C_interrupt_index,Catch_Control_C);
  157.     setjmp(B);
  158.     while (1) {
  159.        Read_editor_command;
  160.        Process_editor_command;
  161.        }
  162.     }
  163.  
  164. Use Vector_element to retrieve the MS-DOS control/C routine BEFORE
  165. you insert your own, so that you can restore it before exiting:
  166.  
  167. Interrupt_vector_element Old_routine;
  168. ...
  169. {
  170. vector_element(&Old_routine,Control_C_interrupt_index);
  171. install_interrupt_routine(Control_C_interrupt_index,My_routine);
  172. ...
  173. install_interrupt_vector_element(Control_C_interrupt_index,Old_routine);
  174. }
  175. #endif
  176.  
  177. /* Because MS assembler truncates at 31, we had to use shorter names: */
  178. #pragma Alias(install_interrupt_vector_element,_Private_routine_prefix "install_ive");
  179. #pragma Alias(call_interrupt_vector_element,_Private_routine_prefix "call_ive");
  180.  
  181.  
  182. /* Catching control/C: */
  183.  
  184. void on_user_interrupt(char F()!);
  185. #if 0
  186.  
  187. NOTE:  The parameter F() must have Pascal calling convention.  If it doesn-t,
  188.        the compiler will complain with a type mismatch.
  189.        For example:
  190.         #include <interrup.cf>
  191.         pragma calling_convention(PASCAL);
  192.         char aha(void) {printf("OUCH!  Don't hit that button!\n");} 
  193.         main () {
  194.               on_user_interrupt(aha);
  195.               }
  196.        (Actually, it is bad practice to call a library routine like printf
  197.         in an interrupt handler, since library routines may contain
  198.         static data and are therefore not re-entrant.)
  199.        
  200. When a user interrupt occurs (^C), function F will be invoked. If F
  201. returns 1 then the program will be immediately aborted; otherwise,
  202. execution will continue at the point that the interrupt occurred.
  203.  
  204. If F is NOT a level-one (outermost) routine, read this: F may contain
  205. up-level addressing, i.e., access to ancestor-s local variables, and it
  206. may contain a non-local goto to an ancestor.  HOWEVER, YOU MUST ENSURE
  207. that F-s ancestry is intact when the interrupt occurs.  For example, if
  208. the routine that contains F returns, then up-level addressing or
  209. non-local gotos from F will no longer work. If F is a level-one routine,
  210. this is not an issue, since F will then have no ancestry.
  211.  
  212. ALSO: MS-DOS does not support recursive interrupts.
  213. #endif
  214.  
  215. #pragma Global_aliasing_convention();
  216. #pragma Calling_convention();
  217. #endif
  218. #endif /* _INTERRUP_CF */
  219.