/* This is the fast interrupt dispatcher */ /* The dispatcher is complicated by CBUG. An interrupt can occur in */ /* at any point in a program. The interrupt causes a push of the return */ /* address on the PC stack. CBUG requires that a corresponding frame push*/ /* onto the C runtime stack occur. The first few instructions of the */ /* dispatcher take care of the frame push. */ /* */ /* The next problem occurs because the interrupt number is needed */ /* as a parameter to the interrupt handler. There are several ways that */ /* this can be accomplished: */ /* 1) Calling the dispatcher from the interrupt vector table */ /* This places the PC address of the vector table on the */ /* PC stack. The PC stack can be read to determine the */ /* interrupt that was invoked. The disadvantage is that */ /* an additional level of stack usage (CALL/RTS) is used */ /* up. */ /* 2) Pusing the interrupt number of the C runtime stack. */ /* The problem here is that the 020_hdr.asm file would */ /* need to be reassembled for each type of runtime stack */ /* that is supported. At the moment a single runtime */ /* header exists, which makes life much easier. */ /* 3) Test the right most (highest priority) bit in the */ /* interrupt latch. This can be a problem because we can */ /* not easily test which bit is the right most. To get */ /* around this problem, we bit reverse the IMASKP register*/ /* and then test for the left most bit. This seems to */ /* work well, and it is the technique implemented in this */ /* code. */ /* */ /* Another problem occurs with the compiler ability to pass */ /* parameters in registers. It is difficult to change this dispatcher at */ /* compile time, so we need a solution that works for both register */ /* passing on and off. To accomplish this we push the parameter (the */ /* interrupt number) on the stack, as well as in R4 (the first register */ /* used for parameter passing). */ /* */ /* Another issue the the default handling of interrupts after a */ /* single interrupt has been serviced by use of the signal() function. */ /* The interrupt slots hold a new IMASK mask word. This word is ANDed */ /* with the IMASK during the servicing. This will cause interrupts */ /* enabled with signal() to be serviced only once, while interrupts set */ /* with the interrupt() routine will be serviced until disabled. */ /* */ /* There is at least one more open issue with interrupts. If a */ /* library routine or user assembly code changes the value of a L-register*/ /* (not the scratch DAGS) it may be a problem for the interrupt handler. */ /* The compiler should be changed to save L-registers for associated */ /* I-registers that are used. The L-registers should be saved and */ /* restored at function begin and end. */ /* */ /* Gordon A. Sterling (617) 461 - 3076 February 28, 1992*/ /* */ /* Another infamous interrupt problem. The dispatcher needs to */ /* save the ASTAT register during an interrupt, and restore ASTAT before*/ /* returning to the interrupted code. The problem is the state of the */ /* flag outputs is stored in ASTAT. If the application changes the */ /* state of a flag output in the interrupt routine, (which seems to */ /* happen alot), the flag output will be restored to the same state it */ /* was before the interrupt. It is possible to AND out the flag bits, */ /* and OR in the current flag bits before returning. This takes some */ /* time, but there seems to be no alternative. */ /* While fixing this bug, I have moved a few memory accesses around*/ /* to minizime the timing impact. There general idea is to mask out the*/ /* flag bits in the saved ASTAT, and OR in the current state of the */ /* flags before restoring ASTAT. */ /* */ /* There is another issue (not a bug). The dispatcher assumes that*/ /* the default set of scratch registers are used, no user optional */ /* scratch. If the interrupt handler (or any of the routines that it */ /* calls use optional scratch registers, some values may (will?) get */ /* trashed. The interrupt handler MUST be complied with only the fixed */ /* scratch registers, and all routines call by the handler MUST use the */ /* fixed scratch register set only. */ /* */ /* Gordon A. Sterling (617) 461 - 3076 August 20, 1992 */ /* */ /* Change by Matt Johnson, 11/24/92: move save of r4, r8, and r12 into protected section at entry so as to simplify unwinding register parameters amidst interrupts during a backtrace or scoping context change under CBUG. Doing this in the protected section guarantees resolution of that register now, not some other instance of interrupt context (as in nested interrupts). CBUG now relies upon the following save sequence after the pseudo-frame save: 1st... old frame which was interrupted 2nd... return address of dispatcher 3rd... return address of interrupted function 4th... r4 5th... r8 6th... r12 ... not critical for CBUG */ #include "lib_glob.h" #include "sig_glob.h" .SEGMENT/CODE Code_Space_Name; .FILE RTL_FILENAME; .GLOBAL ___lib_faster_int_cntrl; ___lib_faster_int_cntrl:BIT SET MODE1 0x1000; /*Re-enable interrupts */ put(R0); put(R1); put(R2); put(R4); put(R8); put(R12); put(ASTAT); /*Save status register */ put(pm_ptr); /*If the stack is in DM */ put(pm_bse); /* the PM DAGs can be */ put(pm_mdf); /* pushed directly on */ put(pm_lnt); /* the stack. */ R0=dm_ptr; /*The DM DAGs must be */ put(R0); /* pushed indirectly. */ R0=dm_bse; put(R0); R0=dm_mdf; put(R0); R0=dm_lnt; put(R0); dm_lnt=0; pm_lnt=0; pm_ptr=I13; I13=int_cont; /*In case people still need the I13 usage*/ CALLER_HOLD(R2) CALLER_SWAP RTLCALL (pm_0, pm_ptr) (DB); /*Call function*/ SAVE_OLD_FRAME(R2) SAVE_RET_ADDR /* Here is the confusing part with the flags. The flag bits need */ /* to be removed from the saved ASTAT, everything else needs to be */ /* removed from the current ASTAT, and those two things need to be ORed */ /* together, and restored in ASTAT. All of this must be done with */ /* interrupts disabled. Oh boy */ int_cont: R8=0xff87ffff; /*Mask to remove flags */ R12=NOT R8, get(R4,9); /*Get old ASTAT */ R4=R4 AND R8, R2=ASTAT; R0=R2 AND R12, get(R2,1); /*Remove flag bits*/ R0=R0 OR R4, dm_lnt=R2; /*Compute new ASTAT */ get(R2,2); dm_mdf=R2; R2=IMASK; I5=I15; R8=dm(I5,dm_0); /*Get Int slot next_mask*/ R0=R2 AND R8, ASTAT=R0; /*Write new ASTAT, AND IMASK*/ IMASK=R0; /*(Done for signal() support*/ get(dm_bse,3); /*Be sure to write base */ get(dm_ptr,4); /* before writting ptr */ get(pm_lnt,5); /*Restore PM DAG scratch*/ get(pm_mdf,6); get(pm_bse,7); /*Be sure to write base */ get(pm_ptr,8); /* before writting ptr */ get(R12,10); /*restore r12*/ get(R8,11); /*restore r8*/ get(R4,12); /*restore r4*/ get(R2,13); get(R1,14); get(R0,15); get(I15,16); /*rest scratch used in int disp*/ get(I13,17); /* " " */ RTI(DB); /*Return from interrupt */ BIT CLR MODE2 0x00080000;/* Re-enable cache */ alter(17); .ENDSEG;