Plus Too Interrupt Bug
Mark McDougall (tcdev) discovered what looks like a serious bug in the way Plus Too handles interrupts. It appears my design causes the 68000 CPU to use the wrong interrupt handler vector! How it could work at all under those circumstances isn’t clear, since I would expect it to crash the moment an interrupt is first triggered, but I fixed the bug anyway. I had hoped it would eliminate the mysterious freeze-ups I’ve been getting with Plus Too after a few minutes of active mouse movements in the Finder, but sadly it didn’t appear to make any difference.
Vectored Interrupts
Here’s what’s happening. Plus Too (and the Macintosh it replicates) use vectored interrupts. When an interrupt is triggered, the 68000 responds with an interrupt acknowledge cycle. It sets the 24-bit address bus to all 1’s, except for A3-A1, which are set to the level of the interrupt being acknowledged. There is no A0 output from the CPU, since it uses upper/lower byte strobes instead. So to acknowledge a level 1 interrupt (the VIA), the CPU would set the address bus to:
1111 1111 1111 1111 1111 001x
with X being the invisible A0 bit. The memory interface (in this case, my Plus Too design) is supposed to respond by placing an interrupt vector number on the 16-bit data bus. The CPU then multiplies the vector number by 4 internally, in order to get the memory address of the interrupt vector. It then uses that vector to find the location of the interrupt handler routine to execute.
In the case of the Macintosh, the external interrupt handlers begin with vector number $18, which when multiplied by 4 is the vector found at memory address $60. The level 1 VIA interrupt vector is number $19 found at $64, and so on. So to select the proper vector, the memory interface should respond to interrupt acknowledge cycles with $18 + the interrupt level.
A Missing Bit
That’s what I intended to do, but somewhere during development, my Plus Too code lost an address bit. The relevant piece of Verilog code looked something like:
input [1:0] addrLo; // A2-A1 output [15:0] dataOut;
...
assign dataOut = { 13'h3, addrLo }; // use A3-A1 to construct an interrupt number offset from $18
Oops. That code doesn’t do what the comment says. It ignores A3, meaning that interrupt levels 4-7 will never be handled properly. These correspond to the programmer’s debug switch on the Mac. Worse, it generates interrupt numbers that are offset from $C, not $18. So for interrupt level 1 (the VIA), it will generate a response of interrupt number $D, which is at memory address $38.
According to my docs, $38 is an unassigned/reserved vector. In fact, all the vectors from $30 to $5C are reserved or unassigned. So how does that work at all? Why doesn’t it crash the moment a VIA interrupt is first triggered? Is it possible that the reserved vector entry just happens to contain the right value somehow? That seems very unlikely.
Fixed?
The fix is pretty simple: addrLo should be three bits instead of two, and contain A3-A1. I made this change, and Plus Too behaves no differently than before as far as I can tell. It still kind of mostly works, but exhibits frequent freeze-ups after a few minutes of use, that seem to be related to mouse movements somehow. Maybe the two problems are totally unrelated, but I’d hoped the interrupt vector problem might explain the freeze-ups.
I still can’t explain how Plus Too ever worked before, with external interrupt numbers given the wrong offset.
Read 6 comments and join the conversation6 Comments so far
Leave a reply. For customer support issues, please use the Customer Support link instead of writing comments.
At least on the real classic Mac hardware, logic asserts VPA in response to an interrupt which causes the 68000 to automatically generate the vector number based on the values on IPL0, IPL1, and IPL2 rather than look to the data bus for a vector number.
You’re right. The TG68 soft-core 68000 I’m using in Plus Too doesn’t have the virtual “pin” needed to choose between auto-vectors and normal vectored interrupts, though. That’s why I’m manually generating interrupt vectors that should match the missing auto-vectors. But maybe TG68 *always* does auto-vectors? That might explain how it worked before even with this bug. I can’t find the answer in a quick search of TG68 info, so I’ll need to look at its code.
Ugh, this is embarassing. It appears that TG68 always does auto-vectoring, and my interrupt vector handler had no effect on anything. I’d thought the absence of interrupt control pins meant there was no way to enable auto-vectoring, but it seems the opposite is true. The bug in my Verilog made no difference, then, because the generated vector number wasn’t even used by the CPU.
how’s progress Steve? its been a while and I still drop in every 3 weeks or so to check it out.
Progress is not so good I’m afraid. Getting the OUYA console build is eating 100% of my time!
Very belated question: if the 68k doesn’t expose VPA, how are you forcing ~1Mhz access to the 6522?