/* * MWC86 CPS Version 3.1.1. * Copyright (c) 1982-1986 by Mark Williams Company, Chicago. * All rights reserved. May not be copied or disclosed without permission. */ /* * These routines provide an interface between * the MSDOS hardware interrupt system and MWC86 C. * Works only with SMALL model as of now. * Example compilation: * cc count.c int.c intdis.s */ #include #include #define SIZE(x) (sizeof(x)/sizeof(x[0])) /* Size in elements */ #define END(x) (&x[SIZE(x)]) /* Address of end */ /* * The interrupts are set to jump to these assembly entry points first. */ extern intdis1(); extern intdis2(); extern intdis3(); extern intdis4(); extern intdis5(); extern intdis6(); extern intdis7(); extern intdis8(); extern intdis9(); extern intdis10(); extern intdis11(); extern intdis12(); extern intdis13(); extern intdis14(); extern intdis15(); extern intdis16(); /* * Interrupt information. * Knowledge of this structure is hardcoded in intdis.s. */ struct INTINFO { int (*i_cfunc)(); /* C function to be called */ int i_stacksize; /* Stack needed by C func */ void (*i_intdis)(); /* Assembly dispatch routine */ int i_intnum; /* 8086 int number (-1 if free) */ unsigned i_oldoff; /* Old vector offset */ unsigned i_oldseg; /* Old vector segment */ char *i_stack; /* Pointer to start of stack */ char *i_curstack; /* Current sp (grows down) */ char *i_endstack; /* Pointer to end of stack */ } intinfo[] = { { NULL, 0, &intdis1, -1, 0, 0, NULL, NULL, NULL }, { NULL, 0, &intdis2, -1, 0, 0, NULL, NULL, NULL }, { NULL, 0, &intdis3, -1, 0, 0, NULL, NULL, NULL }, { NULL, 0, &intdis4, -1, 0, 0, NULL, NULL, NULL }, { NULL, 0, &intdis5, -1, 0, 0, NULL, NULL, NULL }, { NULL, 0, &intdis6, -1, 0, 0, NULL, NULL, NULL }, { NULL, 0, &intdis7, -1, 0, 0, NULL, NULL, NULL }, { NULL, 0, &intdis8, -1, 0, 0, NULL, NULL, NULL }, { NULL, 0, &intdis9, -1, 0, 0, NULL, NULL, NULL }, { NULL, 0, &intdis10, -1, 0, 0, NULL, NULL, NULL }, { NULL, 0, &intdis11, -1, 0, 0, NULL, NULL, NULL }, { NULL, 0, &intdis12, -1, 0, 0, NULL, NULL, NULL }, { NULL, 0, &intdis13, -1, 0, 0, NULL, NULL, NULL }, { NULL, 0, &intdis14, -1, 0, 0, NULL, NULL, NULL }, { NULL, 0, &intdis15, -1, 0, 0, NULL, NULL, NULL }, { NULL, 0, &intdis16, -1, 0, 0, NULL, NULL, NULL } }; static char iovermsg[] = "Interrupt nesting capacity exceeded\r\n"; /* * Link a c function with an 8086 interrupt. * Up to 16 C functions can be linked to interrupts at once. * * Parameters: * * intnum -- 8086 interrupt number. * func -- C function pointer for interrupt service routine. * stacksize -- Amount of stack (in bytes) that C function needs. * level -- Maximum number of levels that interrupt will be nested. * * Returns: * * The C function should return 1 if it wants the old interrupt * service routine to be called too. Otherwise, it should return 0. * This is useful for tapping an interrupt but not affecting its * old functionality. * This function returns a number from 0 to 15 on success. This * unique interrupt identifier is passed to clearint when * you want to dereference the C routine from the interrupt. * Returns -1 on failure (Bad intnum, all available ints in use, bad * level value, not enough memory for stack). * * Notes: * * (1) If the interrupt service routine returns 0 and the interrupt * is a real hardware interrupt (eg. interrupts 0x8 to 0xF on the * IBM-PC), the C service routine must output an EOI to the * interrupt controller. * * (2) All C service routines are called with interrupts disabled. You * may call cli or sti to clear or set the interrupt enable bit * respectively. * * (3) Most library functions cannot be called by an interrupt service * routine. * * (4) The use of floating point arithmetic within * an interrupt service routine is not recommended. * * (5) Caution should be exercised when interrupt routine(s) * and the main program access the same writeable data structures. * * (6) Interrupts can be linked. That is, setint may be called * several times with the same interrupt number. When that * interrupt occurs, if all C service routines for that interrupt * return 1, all such routines will be executed. When you link * interrupts, it is vital that setint/clearint calls are not crossed. * That is, between a setint and its corresponding clearint, no other * setints on that interrupt should have been performed which * weren't cleared. * * (7) No stack checking is performed. If stacksize is too small, the * system will crash. If level is too small (the interrupt * nests more than level levels), the system will print * an error message and hang with interrupts disabled. * The stack size (in bytes) needed by one function is equal to * 6 + sizeof parameters + sizeof automatics. * Allow at least 256 bytes excess stack for a safety margin. * * (8) It's ok to use this system after calling MS-DOS function 0x31 * (Terminate and remain resident). The routine termres, provided * here, does this. */ setint(intnum, func, stacksize, level) register int intnum; int (*func)(); unsigned stacksize; int level; { register struct INTINFO *ip; register int i = 0; register unsigned totalstack = stacksize * level; struct reg r; char *malloc(); if (intnum < 0 || intnum >= 256 || level < 0) return(-1); savedsreg(); for (ip = intinfo; ip < END(intinfo); ++ip, ++i) if (ip->i_intnum == -1) { /* Free spot? */ if ((ip->i_stack = malloc(totalstack)) == NULL) return(-1); r.r_ax = GETVEC | intnum; intcall(&r, &r, DOSINT); /* Get old vector */ ip->i_oldseg = r.r_es; /* Save it */ ip->i_oldoff = r.r_bx; ip->i_intnum = intnum; ip->i_cfunc = func; ip->i_stacksize = stacksize; ip->i_curstack = ip->i_endstack = ip->i_stack + totalstack; ptoreg(csreg, r.r_dx, r.r_ds, ip->i_intdis); r.r_ax = SETINT | intnum; intcall(&r, &r, DOSINT); /* Set new int */ return(i); } return(-1); /* Table full */ } /* * Clear an interrupt previously set by setint. * id -- unique identifier which setint returned. * Returns 0 on success. * Returns -1 on failure (id out of range, interrupt never set). */ clearint(id) register int id; { if (id < 0 || id >= SIZE(intinfo)) return(-1); return(_clearint(&intinfo[id])); } /* * Clear all interrupts. * You should not call this routine if you have linked several * interrupt service routines to the same interrupt with setint. */ doneint() { register struct INTINFO *ip; for (ip = intinfo; ip < END(intinfo); ++ip) _clearint(ip); } /* * Internal routine to restore an interrupt to its old value. * Returns 0 if ok, -1 if interrupt is clear. */ static _clearint(ip) register struct INTINFO *ip; { struct reg r; if (ip->i_intnum != -1) { r.r_ax = SETINT | ip->i_intnum; r.r_ds = ip->i_oldseg; r.r_dx = ip->i_oldoff; intcall(&r, &r, DOSINT); ip->i_intnum = -1; free(ip->i_stack); return(0); } return(-1); } /* * Internal routine called by the assembly interrupt linkage * on interrupt nesting overflow. Called in interrupt context. * At this point, system may be foobared. */ isover() { write(1, iovermsg, sizeof(iovermsg) - 1); } /* * Terminate process and remain resident. * This routine exits, but allocates all program and data memory * before doing so. You must call this routine if you want * to handle interrupts after normal program exit. * Basically figure out the total number of paragraphs that program, * data, stack, and malloc arena occupy and pass it to the MS-DOS * TERMRES function. * status -- Byte exit code. */ termres(status) int status; { struct reg r; extern int _pspbase; extern char *__end; r.r_ax = TERMRES | (status & 0xFF); r.r_dx = dsreg() + ((int)__end >> 4) + 1 - _pspbase; intcall(&r, &r, DOSINT); }