dillon@CORY.BERKELEY.EDU (Matt Dillon) (04/14/88)
Ok... remember, I haven't extensively tested this and it isn't incredibly optimized at the moment. Please report any bugs found: #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # qint.doc # qint.c # test.c # This archive created: Wed Apr 13 20:40:22 1988 export PATH; PATH=/bin:/usr/bin:$PATH echo shar: "extracting 'qint.doc'" '(4986 characters)' if test -f 'qint.doc' then echo shar: "will not over-write existing file 'qint.doc'" else cat << \!Funky!Stuff! > 'qint.doc' QINT DOCUMENTATION Matthew Dillon dillon@ucbvax.berkeley.edu ARPA ...!ihnp4!ucbvax!dillon USENET NOTE!!!!! Lattice Users must replace the 'jsr _geta4' call with the appropriate call to retrieve the address register for the small data model before compiling! You might have to make other modifications as well (I don't know since I don't have Lattice). The Calls: char oldpri; range -128 to 127 char newpri; char pri; long signum; 0 .. 31 void (*vector)(); function vector returning nothing void (*oldvec)(); While active Q interrupt vectors exist, the tc_ExceptCode in the task structure will be modified. The old tc_ExceptCode is used if an unknown exception occurs (one that is not a Q interrupt). oldpri = SetQPri(newpri) Set the task's current Q priority. Any Q interrupts of lower or equal priority that occur will be queued until the priority is dropped sufficiently. The initial task priority is -128 (essentially allowing all Q interrupts -127 to 127 to occur). oldvec = SetQVector(signum, vector, arg, pri) If vector is non-null, enables the exception at the specified priority (-127 to 127). specified vector is called in a C compatible way with one user argument (arg). Specifying a priority of -128 does not make sense because this is the lowest allowed priority and the Q interrupt will thus never occur. If vector is null, the exception and Q interrupt is disabled. After the last Q interrupt is removed, tc_ExceptCode is restored to its original value. BUGS: The only bug that I know of is a problem with EXEC. What is Good: An exception will not occur while one is Forbid()n What is Bad: If an exception comes in while Forbid()n, it will NOT be immediately entered when you Permit(). Whoops. The exception *will* occur when EXEC next checks its signals and exceptions, which occurs on the obvious EXEC library calls (SetExcept(), SetSignal(), etc...) and perhaps Wait(). In most cases you can ignore the problem. GENERAL WORKINGS OF Q INTERRUPTS: If you know how EXEC signals and the 68000 interrupt structure works, then you know how Q interrupts work. Simply replace "processor" with "task" and "0-7" with "-128 to 127" for task Q interrupt priorities (this is different from the Task's scheduler priority). Q interrupts work just like 68000 interrupts. If the task is currently running at a Q interrupt priority N, only Q interrupts of HIGHER priority can occur. Q interrupts of LOWER OR EQUAL priority are queued and will occur as soon as the priority is lowered. Everything occurs in priority order, the highest priority pending interrupt is always the one running (or the task's main routine if it has the highest priority). Thus, while a Q interrupt handler is running at some specific priority, other Q interrupts at the same priority will wait until the first one finishes and then execute in a FIFO fashion. THE INTERRUPT VECTOR ROUTINE: A specific Q interrupt vector is a C subroutine called with one longword argument (that specified when you SetQVector()'d it). This works fine with the small model. The handler runs with all normal EXEC processing enabled, and in the context of its task. It can be viewed almost as a subroutine call from the task. However, you must be careful about the reentrancy of certain functions. STDIO, DOS, and many other library calls are not reentrant, and you must use SetQPri() to ensure such calls are not interrupted. NOTE that you CAN mix certain calls. I don't have much info on what combinations work, but certainly most library calls that do not depend on being called singularly from tasks will work (for example, GetMsg()). And, of course, if all your handler does is make some small calculations this can interrupt anything. If you cause an exception (the same exception) from within the handler, it will be remembered. (That is, the signal is cleared before the handler is called), and occur after your handler returns. USES FOR Q INTERRUPTS: Use #1: To be able to execute menu options which do not effect the 'current' operation. E.G. if you are doing a ray tracing you could make the intuition window's signal bit a Q interrupt and handle Intuition messages that way.... The main loop generating the ray tracing would not have to continuously check for Intuition messages. Use #2: Lets say you are writing a terminal program and want to display data comming in smoothly while sending a file. While this can be done easily with the asynchronous ability of IO devices, it would be even easier if you handled the receive with a Q interrupt... that way, you could display received data (SendIO to the console device) even if the file sender is in the middle of a DOS call to read the next file block. Many more uses (I hope!). -Matt !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'qint.c'" '(8360 characters)' if test -f 'qint.c' then echo shar: "will not over-write existing file 'qint.c'" else cat << \!Funky!Stuff! > 'qint.c' /* * QINT.C * * Prioritized Interrupt scheme based on exceptions (and therefore * standard signals). * * NOTE the following bugs in EXEC exception handling: * -If you ever use Forbid()/Permit() pair, An exception which comes * in within the pair will not get processed after the Permit() * until one of several system calls is made to force a re-evaluation. * * -We cannot simply return a mask at the end of the exception * handler because if a signal comes in after the exception occurs * but before we return, the same problem as with Forbid()/Permit() * comes up. * * Calling SetExcept() while NOT Forbid()n appears to fix the problem. */ #include <exec/types.h> /* Includes. You might have to add some */ #include <exec/nodes.h> /* If I missed any. */ #include <exec/lists.h> #include <exec/tasks.h> typedef unsigned char ubyte; typedef unsigned short uword; typedef unsigned long ulong; typedef struct Task TASK; typedef struct Node NODE; typedef struct List LIST; typedef void (*FPTR)(); extern TASK *FindTask(); #define QINT struct _QINT QINT { /* 32 bytes total */ NODE Node; /* 14 bytes */ FPTR vector; /* Function to call */ long sigmask; /* signal mask (1 bit set) */ long arg; /* argument to handler */ ubyte filler2[6]; }; /* * Note: Since -128 is the lowest priority possible, setting an * exception's priority to -128 means it will never occur. */ static char QPri = -128; /* Current process Q interrupt priority */ static QINT QInt[32]; /* Q interrupts which are really exceptions */ static LIST QList; /* List of pending Q interrupts */ static APTR QSaveExcept; static short QInts; static short QInHan; /* Currently in the exception queue handler */ void except(); void QInit(); #asm ; _EXCEPT ; ; Exception handler. ; ; ALL data and address registers are saved by EXEC. D0 ; holds the exceptions that occured on entry, and A1 ; holds the exception data frame (which we do not use, ; but need to save in case there are exceptions that ; we do not own). public _LVOForbid public _LVOPermit public _LVOEnqueue public _LVOSetSignal public _geta4 ;Aztec: Load proper address reg. public _QSaveExcept public _QInHan _except: move.l A1,-(sp) ;save except data segment move.l 4,A6 ;A6 = Exec Base move.l D0,D6 ;D6 = Exception Bit Mask jsr _geta4 ;get global base register (Aztec small code) ;;sub.w #1,_QLevel ;level down! ;;bpl .ex0 ;; ; STACKING LIMIT REACHED ;;add.w #1,_QLevel ;down too far! ;;add.l #1,_QError ;mark it ;;jsr _LVOForbid(A6) ;Reload all exceptions that occured ;;move.l D6,D0 ;;move.l D6,D1 ;;jsr _LVOSetSignal(A6) ;;jsr _LVOPermit(A6) ;;move.l D6,D0 ;Reenable all exceptions that ;; ; occured by returning the mask in D0 ;;addq.l #4,sp ;restore stack frame ;;rts ; Queue any exceptions which are interrupts. Exceptions ; for interrupts which are queued are NOT reenabled until ; they are actually run. ; ; Note that in the loop we must loop to .ex2 to decrement ; D4, which doesn't occur when we find a '1'. The Z bit ; must be set when we loop to .ex2 .ex0 moveq.l #31,D4 ;D4 = BIT NUMBER .ex1 btst.l D4,D6 ; test bits .ex2 dbne D4,.ex1 ; until found a '1' beq .ex10 ;or loop exhausted (D4 == -1) move.l D4,D5 ;Calculate address of QINT. asl.l #5,D5 ;D5 = index * sizeof(QINT) add.l #_QInt,D5 ; + Address move.l D5,A3 ;A3 = QINT address tst.l 14(A3) ;Is this exception vectored? beq .ex2 ;no, somebody else owns it .ex3 bclr.l D4,D6 ;clear exception bit. jsr _LVOForbid(A6) ;Important operation! move.l A3,A1 ;A1 = node lea.l _QList,A0 ;A0 = List base move.b #5,8(A1) ;mark as being queued jsr _LVOEnqueue(A6) jsr _LVOPermit(A6) ;enable exceptions clr.w D7 ;Force Z cc set. bra .ex2 ; Call the handler. NOTE that handler() need only ; save/restore D6. Both Lattice and Aztec will do this. .ex10 tst.w _QInHan ;no need to call handler? bne .ex11 bsr _handler ;call handler .ex11 move.l D6,D0 ;D0 = exception mask beq .ex12 ;we processed all exceptions move.l _QSaveExcept,A0 ;somebody else owns some exceptions move.l (sp)+,A1 ;restore exception data pointer ;;add.w #1,_QLevel jmp (A0) ;call him with remaining exceptions. .ex12 addq.l #4,sp ;from push at top ;;add.w #1,_QLevel rts #endasm /* * Exception Queue handler! * * Note: The places I set QInHan may appear to be strange, but keep * in mind that exceptions will not occur while we are Forbid()n. */ static void handler() { register QINT *qint; register char savepri; QInHan = 1; Forbid(); while ((qint = (QINT *)QList.lh_Head) != (QINT *)&QList.lh_Tail) { if (qint->Node.ln_Pri <= QPri) /* priority not high enough */ break; savepri = QPri; /* save old priority */ Remove(qint); /* remove from queue */ qint->Node.ln_Type = 0; /* mark as such */ QPri = qint->Node.ln_Pri; /* up the priority */ Permit(); (*qint->vector)(qint->arg); /* call handler */ QPri = savepri; /* restore priority */ if (qint->vector) /* reenable if still exists */ SetExcept(qint->sigmask, qint->sigmask); Forbid(); } QInHan = 0; Permit(); /* * Exceptions are not checked on return, so we must call SetExcept() * here to 'force' a check (i.e. the exception signal comes in before * the exception handler returns). */ SetExcept(0,0); } static void QInit() { NewList(&QList); } /* * ---------------------------------------------------------- * * SETQVECTOR() * * Vector a signal at a specified priority. Setting the vector to NULL * Removes the Q Interrupt. Exceptions are automatically enabled or * disabled for the signal bit. */ FPTR SetQVector(signo, vector, arg, pri) FPTR vector; long arg; long signo; char pri; { register QINT *qint = QInt + signo; register long sigmask = 1 << signo; register FPTR oldvector; Forbid(); if (!QList.lh_Head) /* Init (first call) */ QInit(); oldvector = qint->vector; if (!vector) { /* kill the Q int */ if (oldvector) { /* one less */ SetExcept(0, sigmask); /* disable exception */ if (qint->Node.ln_Type == 5) { /* If queued to go */ Remove(qint); /* Remove from queue */ qint->Node.ln_Type = 0; } if (--QInts == 0) /* No more Q Ints */ FindTask(NULL)->tc_ExceptCode = QSaveExcept; } } else { if (!oldvector) { /* New Interrupt? */ if (++QInts == 1) { /* First Interrupt? */ QSaveExcept = FindTask(NULL)->tc_ExceptCode; FindTask(NULL)->tc_ExceptCode = (APTR)except; } } qint->arg = arg; qint->Node.ln_Pri = pri; /* set priority */ qint->sigmask = sigmask; /* set signal mask */ SetExcept(sigmask, sigmask); /* enable exception */ } qint->vector = vector; /* set vector */ Permit(); /* reenable exceptions */ SetExcept(0,0); /* force mask check */ return(oldvector); } /* * SETQPRI() * * Set the task's Q priority * * Note: I *could* use a Forbid()/Permit() pair, but due to the * exception handling bug noted above, I would then have to do a * SetExcept(0,0). */ char SetQPri(newpri) char newpri; { char oldpri; if (!QList.lh_Head) /* Init (first call) */ QInit(); Disable(); /* Modify the priority */ oldpri = QPri; QPri = newpri; Enable(); /* * Don't bother calling the handler unless there is something * queued. */ if (QList.lh_Head != (APTR)&QList.lh_Tail) handler(); return(oldpri); } !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'test.c'" '(1824 characters)' if test -f 'test.c' then echo shar: "will not over-write existing file 'test.c'" else cat << \!Funky!Stuff! > 'test.c' /* * QINT TEST ROUTINE * * 1> test * Ctl D -quit * Ctl E -cause test interrupt #1 * Ctl F -cause test interrupt #0 * * (see printf's to understand what it is printing) * * Basic idea: Cycle through various priorities. The Q interrupts * are both set for priority 16, and thus only work when the counter * is 0...15 * * If you set one of the Q interrupts to, say, 17, you would then * have to worry about the level 17 Q interrupt interrupting the * level 16 interrupt, and thus surround the level 16 interrupt * with: * char oldpri = SetQPri(127); * puts("blah"); * SetQPri(oldpri); * * As it is, with both at level 16, they cannot interrupt each other * (if one occurs while the other is running, it is queued till the * other one finishes). */ #include <exec/types.h> #include <exec/tasks.h> typedef struct Task TASK; extern TASK *FindTask(); int X; int Enable_Abort; void myhan(sig) { ++X; printf("test# %ld\n", sig); } main() { short i; int j; Enable_Abort = 0; printf("tcexcept: %08lx\n", FindTask(NULL)->tc_ExceptCode); SetQVector(SIGBREAKB_CTRL_F, myhan, 0, 16); SetQVector(SIGBREAKB_CTRL_E, myhan, 1, 16); printf("tcexcept: %08lx\n", FindTask(NULL)->tc_ExceptCode); printf("tcexmask: %08lx\n", FindTask(NULL)->tc_SigExcept); for (i = 0; ; ++i) { i &= 31; SetQPri(127); printf("i = %2ld rcvd: %08lx excp: %08lx cnt: %6ld\n", i, FindTask(NULL)->tc_SigRecvd, FindTask(NULL)->tc_SigExcept, X ); SetQPri(i); for (j = 0; j < 100; ++j); if (SetSignal(0,SIGBREAKF_CTRL_D) & SIGBREAKF_CTRL_D) break; } SetQVector(SIGBREAKB_CTRL_E, NULL, 0, 0); SetQVector(SIGBREAKB_CTRL_F, NULL, 0, 0); puts("exiting"); printf("tcexcept: %08lx\n", FindTask(NULL)->tc_ExceptCode); } !Funky!Stuff! fi # end of overwriting check exit 0 # End of shell archive
dillon@CORY.BERKELEY.EDU (Matt Dillon) (04/15/88)
Oh, I forgot to mention... compile the program with 32 bit integers! -Matt