Preface: I have a bit of a headach with this, as it feels a bit more like a personal rant toped with a question. At the same time he's diving right into the real beauty of the II.
One of the weirdest misfeatures of the Apple II - and perhaps the dumbest thing Woz ever designed - is the vestigial priority system for IRQ and DMA on the Apple II expansion bus.
Vestical might be quite missleading as a description. It is offering the ability to implement a priority chain with minimal effort. For sure this is better than no provision at all, don't you think so?
And accusing Woz of being dumb is usually only possible if one hasn't looked at the whole picture.
Each slot has four pins devoted to this system: an input and output for each priority signal. Expansion cards are supposed to check these signals and refrain from signalling IRQ or DMA when a higher priority card is doing so.
Right. It follows the basic implementation decicion of Woz to use geographic addressing. That is, each slot has fixed resources assigned. This goes for I/O address area and ROM addresses as well as IRQ and DMA priority. Unlike a random (parallel) design like the ISA Bus or S100, where every card is equal - and has to burden to decode everything they need in full. Circuitry to decode are needed on each and every card again. Also, each card needs to be configured before usage to answer the right addresses.
Geographical decoding in contrast needs no hardware configuration. This is done the moment a card is plugged. It's not wrong to call the Apple II a Plug and Play system before the term was coined (*1). Since Woz moved decoding to the main board, buildign an expansion card becale easy. A simple Port could be added without any glue logic at all. On the PC in contrast it did need several TTL, or a PAL to add even the most simple device.
It's worth noting, that Apple cards could of course use different addresss schemes by spending more hardware. In fact, unlike with the PC, a card could even disable the wole o board selection and do it's own thing.
And the same principle is added to Interrupts and DMA. To allow more than one card issueing interrupts or taking over the buss a simple priority chanin was added. There is no difference to what otehr systems did, except for the simplicity of design. Where the PC used a 8259 to priorize and handle multiple interrupt lines, pls the need to spend many slot lines to carry them, the Apple only needed two contacts.
To me it's a great example about squeezing out the most versatile concept with the least effort.
It might be interesting to notice that next to every other bus system evolutionary switched to some form of geographic addrssing at some point. Motorola did it with VME64, IBM with MCA while compatibles added it due PCI.
(There are several other strategies, all of them do need more hardware/software to implement)
For IRQ, this system is completely useless. Software interrupt handlers check all interrupt sources in sequence anyway,
And usually break as soon as one handler assumes responsibility for a detected source, not handign over to the next.
and the IRQ signal to the CPU is open-collector and level-triggered (thus multiple simultaneous IRQs are safe and will work correctly).
No, they are not. It's way depending on software structure and the way a certain hardware offers and handles interrupt. Especially in a modular system there are endless ways to screw this - lazy programmer not expecting otehr interactions not being the least of.
So there is no need to avoid contention, and no particular reason a card should pay any attention to this signal.
It sure is a great way to make sure that there is only one interrupt at a given time, and more important, that no lower priority card is issueing an interrupt when a higher one is still handleld. From a certain point of view (and when handles accodingly) the INT-Chain works much like the priority decoder and locking like within an Intel 8259. Interrupts are served in order of priority to the CPU and new interrupts are only issued when the last one is being acknowledged.
Ofc, all of this can also be handled by sophisticated software - at the expens of slower handling and way more complexity. Threfor the Int-Chain can be seen as optional if the goal does not include optimal performance.
For DMA, though, it's potentially more important. Only one card can use DMA at a time, so you need some way to avoid contention. Unfortunately it still doesn't work very well. One major problem is that the system only works if all the cards using DMA are in adjacent slots, because an empty slot breaks the chain.
Right, that's one of the requirements. The basic idea of the slots is to fill them left to right.
This, combined with the use of separate in and out signals in the first place, implies that you are supposed to only signal downstream if you want to perform DMA. But on the other hand, if you only signal downstream, a potential upstream card doesn't know that you've started DMA, and might interrupt your DMA transaction with its own. Apparently, the intention was that a higher priority card could actually do this and you were supposed to watch for it and get off the bus.
Exactly that's the premise of a simple priority chain. Higher priority card overwrites lower priority. All is handles on a single byte level synchronized by the system clock. This decision is to be done for each cycle. There is no such thing as a multi byte transactio - well, unless for the card in Slot 1 tat is :))
Unfortunately, there's no timing information provided in any documentation I've found. A compliant card could simply signal at the same time it begins a transaction, giving the downstream card no time to respond and leading to a potential bus fight.
There is plenty time, as all it needs to do in the first place is disabling the drivers of a lower priority card.
This would also leave memory in an inconsistent state, with the downstream card's transaction half done.
There is no such thing as a half done transaction. Either the byte is read or written, or not.
The reference manuals usually mumble something about "cards should usually connect the IN and OUT lines together" without saying anything about how to actually use them to initiate DMA.
I thought of it as pretty self explanatory.
The whole Phi1 high pase is available for arbitation, so if taking away a few hundret ns for the address generation etc, there is still plenty time to disable the address output buffers before the active access starts if a higher priorized cad signals that it will use the bus.
It would be easy to read that to mean you should signal both ways. Making matters worse, the schematic shows lower numbered slots having higher priority, but at least one source (IIe tech reference) says HIGHER numbered slots have priority!
Mind to supply a page and wording? I just checked my IIe Ref and couldn't find it. My version got the section about the chains on page 170.
Unsurprisingly, DMA cards in the Apple II were frequently incompatible with each other.
As so often with many cards on many systems. If you think the Apple II is bad documented, never try to debug a complex S100 Bus system :))
Beside cards ignoring DMA piority at all, the most common problem are DMA using cards not taking into account that other may need the bus. A typical carelessly acting card is Microsofts Softcard (Z80 board) in default seting (S1-2 off). When activated it grabs the bus full time, only releasing it for one cycle during Opcode decoding (M1), so 6502 refresh gets a chance. If there's another, well behaving DMA card down the line, it will grab the bus during that cycle and the 6502 will soon starve to death.
Now, if switched to obey DMA_IN (S2-1 on), the Soft-Card is prety handsome, and can give way for an unlimited time, as the Z80 is still clocked, just halted. Ofc, if the higher priority card reliquishes the bus only one clock, the Softcard will take over again and do at least two cycles (one 6502 cycle). As Z80 instructions may have several cycles (up to 14?), the 6502 may have to wait for a cycle until one Z80 instruction is finished that way, which may again restult in starvation (see #1 in additional reading).
The whole DMA protocoll is designed for cards that sometimes need fast access. Cads taking over the system are an anomaly. It thanks to the Softcard default seting many other CPU cards (see #2 in additional reading) didn't much care about higher priority DMA
In a perfect world (or at least with well behaving cards), a CPU card should be installed in slot#7 (*2) and obey DMA_IN. Now it would realy behave as a CPU replacement and being able to use other DMA devices. Sure, it might need a bit more logic to keep the 6502 alive.
In fact, if we want to point something out where Woz went short it's keeping the 6502 alive during lengthy DMA cycles. A provision on the main board might have been a good idea. Then again, it would have not only made the protocol way more complex (now a back chanel for a lower priority usit to request the buss against pirority would be needed and managed), also, at least one additional counter would have been needed on the mainboard. Considering that other computers of thesame time (and even way later) had no provision for prioritizing DMA - or even DMA at all, I think he found a great way to enable this functionality without spending much hardware for an exotic case. I say it was impossible to imagine that Z80 cards later became defacto standard in most II's.
An equaly common cause for incompatibility of DMA cards have been subtile differences between the II, IIe and IIgs. The buffer circuitry has changed between these machines, resulting in a slightly diferent DMA timing (see #3 in additional reading).
Did cards just ignore the DMA priority lines, did they signal one way or both ways,
Some did some don't. After all, the number of active cards are rather rare, and multiple of them even more.
- Most non-DMA cards followed Apple's recomendations and loped DMA_IN/DMA_OUT.
- Some didn't and will produce problems in complex combinations
- Many simple CPU Cards do just grab DMA and go on without obeying DMA_IN
- Some cards can do, but wont't always, like the Softcard wich must be switched (S1-2 on) to do so.
- Some always do.
So pick your combination :)
or could they even potentially be damaged (bus fight) if someone tried to drive signals upstream on the "IN" line?
That depends on the individual card design,but I wouldn't recomend it, as it doesn't make any sense.
Did higher-priority cards watch the open-collector DMA line to avoid interrupting existing transactions?
Not realy usefull, as they could as well trust the protocoll. A case like that would be hard to resolve anyway.
Did cards observe some informal software protocol to deal with the problem?
No, at least not in the beginning. With the Apple Mouse card a software protocoll for interrupt sharing was establishe using so called Screen Holes.
Or was it more of a "pray, and tell the user to turn off DMA if there's a problem" sort of thing?
I'd say it was something restricted to high class use cases that usually did requite more thinking and experiments anyway.
The grandfather of all DMA cards is no doubt Microsoft's Softcard. Typicly for back then a rather good documentation was available, including a thruout hardware description and schematics. Since scans are somewhat bad to read, people even redid the schematics.
There is a real well made description by the German magazin mc issue Sept.1985 of a 68008 card. The article starts with a thruout description of DMA access. The Bus handling is like most (simple) CPU cards do. 1 in 8 cycles are given to the 6502 (or more correct lower priority cards).It does handle DMA_OUT while ignoring DMA_IN. Wonders of the internet, there is an accaptable English translation available.
Apple also added some information in 1983 via the Apple II Technical Note called Apple IIe #2: Hardware Protocol for Doing DMA. Sadly it focuses on DMA timing itself rather than cooperation of cards.
*1 - I'd even say the major reason to come up with such a phrase was due the complicated procedures needed to be handled on the PC before the work arounds where added.
*2 - Microsoft recomends slot#4 and #7 only if #4 is occupied. Nonetheless it worked fine in any slot.