But what are the negative consequences of asserting an IRQ at the same time as another card?
Overhead and delay during the interrupt handling, but more important potential mixed up priorities.
After all, the 6502 provides only a single interrupt. If multiple cards issue interrupts at the same time, the handlers will be called in the sequence they hooked the vector. When called a handler has to check if it's its own card the interrupt is about - by whatever means. If not, the chain is continued. This means priority is handled in reverse sequence of handlers installed - read, the last one installed gets the highest priority.
Possible Ways to Screw Up Priority
When drivers are installed as part of initialization - like when searched by the Autostart Monitor, or some OS - they are usually installed top down, so from slot 7 down to slot 1 - which is the native priority on an Apple II. Due this sequence, lower slot number drivers will get higher priority - which is at least unexpected.
When drivers are installed after OS setup by some script or software, they come up in random order, resulting in random priority.
Both can be avoided when cards obey the hardware protocol as now priority is always fixed and following the same scheme which fis well the basic philosophy of geographic addressing/handling of the whole Apple II.
A good interrupt handling considers two factors
- Concurrent should be served in sequence of priority
- Lower priorized interrupts should give way as soon as possible if a higher one occures.
While the first can as well be handled in software, the second needs cooperation of software and hardware. Usually an interrupt can be sliced into 4 pahses:
-> Checking if the interrupt is about (one of) the handlers devices
-> All acivity that has to be done uninterrupted
-> Clearing the interrupt source
Whatever is needed to transport data and setup everything for the next interrupt round.
Getting ready for the next (own) interrupt
-> Whatever else needs to be done, but can happen without blocking other interrupts.
Usually phase 3 ends with a
CLIinstruction allowing other interrupts to happen. From this point on any other interrupt may occur, no matter if of higher or lower priority - as the CPU itself has no knowledge about priority. While this is in general a good idea, it might lead to a situation where lower priorized interrupts hit and get the full time to finish - including housekeeping, before a higher can have his part done.
With simple handlers/interrupts, this may work fine, but more often than not it leads to most housekeeping done during phase 2, and doing the
CLI only at the end of phase 5. As a result the the system will become a bit less responsive.
But if the hardware obeys not only the interrupt protocol (enabling priority), but active controls the interrupt protocol, over all responsiveness may/will improve.
What additional considerations must be made when designing a card which asserts IRQ regardless of the IRQ daisy chain input?
The Hardware should for one obey the priority chain, so only issue an interrupt if no higher priority one is active and pulling the chain if it got one, but as well make releasing the chain again a separate step.
This leaves it up to the software (interrupt handler) to act as cooperative as possible:
- Check for prior handler when installing
- Mark down that address
- Check if the interrupt is really from your source.
- If not, jump to any prior driver - if none, return (*1)
- Handle whatever of that interrupt processing must be done without any time delay. Usually this isn't much - if any at all.
- Up until here the handler is protected against any interrupt
- Release the interrupts (
CLI), but keep the chain pulled.
- From this point on higher priorized interrupts as well as any interrupt not following the protocol can happen.
- Continue with whatever has to be done.
- Release the chain
- From this point on any lower priorized interrupt (that follows the protocol) may happen as well.
- The handler is still protected against his own interrupts
- Reinitialize your hardware to be able to issue new interrupts
- Now everyone, including your own hardware can interrupt again (*2).
Sounds complicated, but isn't and easy to implement.
*1 - In general this is done by having the jump always point to an RTS and patching it when a prior driver existed.
*2 - It might be a good idea to disable interrupts right before doing so - to be released by RTI - to avoid interrupt stacking under high load/overload.