home *** CD-ROM | disk | FTP | other *** search
- /*
- * interrup.cf -- non-ANSI, cannot be used in ANSI mode!
- *
- * Facilities for interrupt handling.
- * Not supported for 80386 DOS.
- *
- * Copyright (c) 1990, MetaWare Incorporated
- */
-
- #ifndef _INTERRUP_CF
- #define _INTERRUP_CF
- #if __HIGHC__
- #include <language.cf>
- #include <implemen.cf>
-
- #pragma Global_aliasing_convention(_Private_routine_prefix "%r");
-
- /* In this package we implement both general interrupt handling
- * and a simple control/C trap routine.
- * If you're just interested in the latter, go to the end.
- * If you just want to call an arbitrary DOS interrupt and do not care
- * to write interrupt routines, see msdos.cf.
- */
-
- /* To get inline CLI and STI, use the intrinsic _inline: */
- #define CLI _inline(0xfa)
- #define STI _inline(0xfb)
-
- #if 0
- When an interrupt occurs, the registers in Machine_status are saved at
- the entry and restored at the exit of an interrupt routine. If the
- interrupt routine modifies the registers, the restored values will be
- the modified versions. Therefore it is possible to write an interrupt
- routine to completely replace, e.g., MS-DOS-s INT 21, which takes
- arguments from registers and returns results in registers.
-
- In addition to the registers, the address following the location of the
- interrupt and the flags register that were saved by the processor are
- available for modification or printing.
- #endif
-
- typedef struct {
- /* In reverse of the order in which they're pushed: */
- unsigned ES,DS,DI,SI,Waste1,Waste2,BX,DX,CX,AX;
- /* The wasted portions are due to the 286 PUSHA/POPA instructions */
- /* used to effect the save. We match the arrangement on the 8086 */
- /* for object code compatibility. */
- struct {
- unsigned Offset,Segment;
- } Interrupt_address;
- unsigned Flags;
- } Machine_status;
-
- /* Calling convention for interrupt routines: */
- #define C_interrupt (C_language | _INTERRUPT)
-
- /* Each interrupt is designated by an index into the hardware */
- /* interrupt vector located at address 0: */
- /* void *Interrupt_vector[0.. ...]; (must be 4-byte pointers). */
- /* Type Interrupt_index subscripts this array. */
-
- typedef unsigned Interrupt_index;
- #define Control_C_interrupt_index 0x23 /* MS-DOS. */
-
- #if 0
- To declare that a routine is an interrupt routine, it must
- have calling convention C_interrupt and be of routine type
- Interrupt_routine.
- Because the representation of interrupt routines differ
- depending upon the 8086 memory model used, and
- each 8086 interrupt entry always is a segment-offset pair,
- the Interrupt_vector_element type represents the latter.
- Thus the latter cannot be an Interrupt_routine.
- On more reasonable machines, these two types are identical.
- #endif
-
- #pragma Calling_convention(C_interrupt);
- typedef void Interrupt_routine(Machine_status S)!;
- typedef struct {unsigned Offset,Segment;} Interrupt_vector_element;
- #pragma Calling_convention();
-
- /* The next routines use the more efficient Pascal calling convention. */
- #pragma Calling_convention(PASCAL);
-
- /* Enable/disable interrupts. */
- extern void interrupt_enable(); /*STI*/
- extern void interrupt_disable(); /*CLI*/
- /* Generate an "int V" instruction and execute it. */
- extern void Int(Interrupt_index V);
-
- /* Store value of Interrupt_vector[V] into Result. */
- extern void vector_element(Interrupt_vector_element *Result, Interrupt_index V);
-
- /* Make P the interrupt handler for Interrupt_index V. */
-
- extern void install_interrupt_routine
- (Interrupt_index V, Interrupt_routine P);
- extern void install_interrupt_vector_element
- (Interrupt_index V, Interrupt_vector_element P);
- #if 0
- NOTE: It is not safe to install an Interrupt_routine P that is not a
- level-1 routine!!! The hardware will NOT supply the up-level context
- that P needs to access the local variables of its parent routine. It is
- OK to install P only if no up-level references are made -- and THIS
- INCLUDES any "goto" out of P. See examples below for a way to get
- around this.
-
- You can call either C interrupt routines or 8086
- Interrupt_vector_elements directly with the following procedures that
- take arguments from and return results in the passed Machine_status.
- #endif
- extern void call_interrupt_routine
- (Machine_status *S, Interrupt_routine P);
- extern void call_interrupt_vector_element
- (Machine_status *S, Interrupt_vector_element P);
-
- #if 0
- For example, to define a routine to catch the MS-DOS control/C
- interrupt:
- #pragma Calling_convention(C_interrupt);
- voide Catch_Control_C(Machine_status S) {
- #pragma Calling_convention();
- printf("Ouch!! You typed Control/C!!\n");
- writeln("Don't do it again!\n");
- }
- ...
- Install_interrupt_routine(Control_C_interrupt_index,Catch_Control_C);
- /* Now, when ^C is typed, Catch_control_C is invoked. */
-
- The interrupt routine may not generally be a non-level-1 procedure,
- since the up-level context is not loaded by the hardware when the
- interrupt occurs. (Note that non-level-1 procedures are a High C
- extension; if you never use them, you don-t need to read this.) To
- achieve the same effect, store the non-level-1 procedure in a global
- procedure variable, and call this variable from a level-1 interrupt
- routine. Since a procedure variable records the up-level context, such
- context can be re-loaded when the procedure variable is called. For
- example, here is a way to use ^C to get back into an editor command
- level:
-
-
- void Call_when_CC_occurs()!; /* Full procedure variable. */
- #pragma Calling_convention(C_interrupt);
- void Catch_Control_C(Machine_status S) {
- #pragma Calling_convention();
- Call_when_CC_occurs();
- }
- #include <setjmp.h>
- void Editor() {
- jmp_buf B;
- void CC() {
- printf("<INTERRUPT> -- command terminated.\n");
- longjmp(B); /* Requires up-level reference to B. */
- }
- Call_when_CC_occurs = CC;
- Install_interrupt_routine(Control_C_interrupt_index,Catch_Control_C);
- setjmp(B);
- while (1) {
- Read_editor_command;
- Process_editor_command;
- }
- }
-
- Use Vector_element to retrieve the MS-DOS control/C routine BEFORE
- you insert your own, so that you can restore it before exiting:
-
- Interrupt_vector_element Old_routine;
- ...
- {
- vector_element(&Old_routine,Control_C_interrupt_index);
- install_interrupt_routine(Control_C_interrupt_index,My_routine);
- ...
- install_interrupt_vector_element(Control_C_interrupt_index,Old_routine);
- }
- #endif
-
- /* Because MS assembler truncates at 31, we had to use shorter names: */
- #pragma Alias(install_interrupt_vector_element,_Private_routine_prefix "install_ive");
- #pragma Alias(call_interrupt_vector_element,_Private_routine_prefix "call_ive");
-
-
- /* Catching control/C: */
-
- void on_user_interrupt(char F()!);
- #if 0
-
- NOTE: The parameter F() must have Pascal calling convention. If it doesn-t,
- the compiler will complain with a type mismatch.
- For example:
- #include <interrup.cf>
- pragma calling_convention(PASCAL);
- char aha(void) {printf("OUCH! Don't hit that button!\n");}
- main () {
- on_user_interrupt(aha);
- }
- (Actually, it is bad practice to call a library routine like printf
- in an interrupt handler, since library routines may contain
- static data and are therefore not re-entrant.)
-
- When a user interrupt occurs (^C), function F will be invoked. If F
- returns 1 then the program will be immediately aborted; otherwise,
- execution will continue at the point that the interrupt occurred.
-
- If F is NOT a level-one (outermost) routine, read this: F may contain
- up-level addressing, i.e., access to ancestor-s local variables, and it
- may contain a non-local goto to an ancestor. HOWEVER, YOU MUST ENSURE
- that F-s ancestry is intact when the interrupt occurs. For example, if
- the routine that contains F returns, then up-level addressing or
- non-local gotos from F will no longer work. If F is a level-one routine,
- this is not an issue, since F will then have no ancestry.
-
- ALSO: MS-DOS does not support recursive interrupts.
- #endif
-
- #pragma Global_aliasing_convention();
- #pragma Calling_convention();
- #endif
- #endif /* _INTERRUP_CF */
-