M68K Interrupt Processing

Ask anything your want about Megadrive/Genesis programming.

Moderator: BigEvilCorporation

Post Reply
Mask of Destiny
Very interested
Posts: 615
Joined: Thu Nov 30, 2006 6:30 am

M68K Interrupt Processing

Post by Mask of Destiny » Sat Oct 31, 2015 9:59 am

It's known that you can execute at least one instruction after enabling VINT in the VDP with a pending VINT. Emulating this is required to make Sesame Street Counting Cafe to work. In an effort to understand exactly what's going on here, I've spent some time writing a test ROM and doing some measurements with a logic analyzer. In addition to answering the original question, I've got timing information for the various stages of interrupt processing and I've noticed something that is technically in the documentation, but not very obvious.

It turns out that the VDP asserts the relevant IPL lines almost immediately after the relevant 68K write ends. There is a small delay which seems to vary from around 90-210ns (presumably based on how in sync the 68K is with SC at the time of the write), but this is not the cause of the delay we have observed in the start of interrupt processing. This becomes clear if you use an instruction that has a couple of bus operations after the write that triggers an interrupt (I used move.l with the register write for enabling the interrupt in the upper word, this gives one extra bus operation to write the low word and a second one to prefetch the next instruction). Instead, it seems that contrary to at least one of the timing diagrams, there is a one instruction latency on interrupt processing. Since this is a 68K level delay, it applies equally to all interrupt sources (I haven't tested level 2 interrupts, but I don't see why it would not apply).

Once interrupt processing starts, the sequence is as follows:
6 cycles - idle bus
4 - save SR
4* - interrupt ack cycle
4 - idle bus
8 - save SP
8 - read vector address
4 - prefetch
2 - idle bus
4 - prefetch

This yields the documented 44 cycles with 5 reads and 3 writes. Obviously all the bus operations can be extended by !DTACK, so this is only the minimum time. The item marked with an asterisk bears some extra explanation though.

The main purpose of the interrupt ack cycle is to allow a peripheral to supply an interrupt vector; however, the 68K is designed to be compatible with old 6800 peripherals which do not support this functionality. The 6800 also had a synchronous bus and it's peripherals were slow by 68K standards. To deal with all these problems, a signal called !VPA (Valid Peripheral Address) was added. This is used instead of !DTACK to indicate that an address is serviced by a 6800 peripheral. It is also used to respond to an interrupt ack cycle to cause an autovector to be used rather than a value from the bus. Unfortunately, since an interrupt ack cycle is a real bus cycle and !VPA is also used to change the timing of a bus operation, this introduces some extra delay into interrupt processing even though the value from the bus is thrown away.

This is not really obvious from reading the sections on interrupt processing in the 68000 User Manual, but you can sort of figure it out by the use of !VPA and the timing information provided in Appendix B for 6800 peripheral access. According to the timing diagrams there, the best case for such an access is 10 cycles and the worst case is 19 cycles. In my testing, I have actually observed a best case of 9 cycles. I suspect the discrepency is due to some assumptions about how fast !VPA can be asserted which are not true on the Genesis/Megadrive.

Perhaps that's already known, but it was quite a surprise to me at least.

Anyway, here's a test ROM for the interrupt latency issue. Both the VINT and HINT lines yield a value of 1 instruction on the real hardware. The code for the two cases is a bit different to excercise some edge cases so even if you are treating both interrupt types the same you may get different values on your emulator.
Source
ROM

Some quick testing suggests that no emulators get the latency quite right, though a couple (Fusion and Genesis Plus GX) are good enough for Sesame Street and Genesis Plus GX is quite close (seems to be emulating roughly 4 cycles of delay, rather than a 1 instruction latency). Not sure about Exodus as I'm having a bit of trouble getting 2.0.X to run under Wine currently, but at least 1.0 did not run seem to emulate any interrupt delay.

Sik
Very interested
Posts: 939
Joined: Thu Apr 10, 2008 3:03 pm
Contact:

Re: M68K Interrupt Processing

Post by Sik » Sat Oct 31, 2015 8:36 pm

Dumb question, but could instruction fetch timings be involved in the delay? (with the fetch having already happened before the interrupt gets seen by the 68000) There's also the issue of during which cycles the 68000 pays attention to the interrupt signal for starters.
Sik is pronounced as "seek", not as "sick".

Mask of Destiny
Very interested
Posts: 615
Joined: Thu Nov 30, 2006 6:30 am

Re: M68K Interrupt Processing

Post by Mask of Destiny » Sat Oct 31, 2015 9:08 pm

Sik wrote:Dumb question, but could instruction fetch timings be involved in the delay? (with the fetch having already happened before the interrupt gets seen by the 68000)
In at least one of my test cases, the IPL lines are asserted well before the prefetch bus cycle at the end of the move instruction that triggers the interrupt, so it's at least not a direct consequence of prefetch. I've also update the test to include a case where the interrupt was already signaled by the VDP, but blocked by the interrupt mask. In that case, there is no latency.
Sik wrote:There's also the issue of during which cycles the 68000 pays attention to the interrupt signal for starters.
According to the MC68000 User Manual Section 5.1.4 "CPU Space Cycle", the IPL lines are sampled on the 3rd falling edge of the CPU clock of the last bus cycle of an instruction. It does not seem to show the latency that actually exists, though it also doesn't show the save of SR happening in between IPL being asserted and the interrupt acknowledge cycle either so perhaps it was intentionally simplified.

Charles MacDonald
Very interested
Posts: 292
Joined: Sat Apr 21, 2007 1:14 am

Re: M68K Interrupt Processing

Post by Charles MacDonald » Sat Oct 31, 2015 9:35 pm

I had a similar problem in a non-Genesis related system. I posted on the Freescale forums a while back and learned that the autovectored interrupts are acknowledged synchronously to the E clock (as a remnant of using VPA for 6800-type synchronous bus cycles), and as the E clock can't be reset to any known value, you get a different amount of cycles wasted each time.

https://community.freescale.com/thread/29078

Later I found one of the old (photocopied, not native PDF) 68000 manuals spelled this out clearly, but the newer manuals Freescale offers fail to mention it which is what I had been reading. Perhaps it was removed due to 6800 peripherals falling out of favor? Their 020 and 030 manuals also became more 'lean' over time too with less application specific information, example circuits, etc.

Nemesis
Very interested
Posts: 791
Joined: Wed Nov 07, 2007 1:09 am
Location: Sydney, Australia

Re: M68K Interrupt Processing

Post by Nemesis » Mon Nov 02, 2015 12:56 am

I had no idea there was any instruction-level latency between interrupts being asserted and processed by the M68K. I knew the VDP raised interrupts as soon as they were marked as pending, but in Exodus I followed the official docs, and process non-masked interrupts after the current instruction is finished executing. Thanks for doing this research. I wonder if this delay is intentional or accidental? I'm also curious if the 68020 and such produce the same behaviour.

Sik
Very interested
Posts: 939
Joined: Thu Apr 10, 2008 3:03 pm
Contact:

Re: M68K Interrupt Processing

Post by Sik » Mon Nov 02, 2015 4:43 am

Well, my theory was that it would fetch an instruction the same moment it notices the interrupt internally (meaning the instruction starts and the interrupt gets processed right after it), but that was just a guess. That'd put the latency at "right after the next instruction is done".

Honestly dunno what's going on actually, although when the 68000 acknowledges the interrupt externally doesn't have to match when it actually decides to start processing it, maybe?
Sik is pronounced as "seek", not as "sick".

Eke
Very interested
Posts: 884
Joined: Wed Feb 28, 2007 2:57 pm
Contact:

Re: M68K Interrupt Processing

Post by Eke » Mon Nov 02, 2015 11:52 am

Mask of Destiny wrote: Some quick testing suggests that no emulators get the latency quite right, though a couple (Fusion and Genesis Plus GX) are good enough for Sesame Street and Genesis Plus GX is quite close (seems to be emulating roughly 4 cycles of delay, rather than a 1 instruction latency).
It should be exactly one instruction latency, at least that's how I implemented it. It is a simplified model though as it does not handle MOVE.L instructions or tricky stuff like the next instruction disabling interrupt, which might explain the differences you are getting with it when running your test ROM.
Sik wrote:Well, my theory was that it would fetch an instruction the same moment it notices the interrupt internally (meaning the instruction starts and the interrupt gets processed right after it), but that was just a guess. That'd put the latency at "right after the next instruction is done".
Yes, that's mostly what I thought was happening in that Sesame's Street game. At the time the memory write is done to VDP ctrl port, 68k has already fetched the next instruction and latched IPL lines state so any IRQ would be processed one instruction later. However, if the same thing indeed happens with the first write of a MOVE.L, it means it's really more an hard-coded delay which require IPL lines to remain asserted for at least one instruction before triggering interrupt.

Mask of Destiny
Very interested
Posts: 615
Joined: Thu Nov 30, 2006 6:30 am

Re: M68K Interrupt Processing

Post by Mask of Destiny » Tue May 23, 2017 6:21 am

After I implemented this, it seemed my interrupt latency was now too high, at least in some cases. I dug back into the 4,325,121 patent and realized that my description above is not correct. Like most things in the 68000, latching of the interrupt state is controlled by microcode. Specifically, it's controlled by the I (bit 0) and C (bit 4) fields. It seems that the TVN (Trap Vector Number) encoder is fully updated when C is 0 and I is 1 (mnemonic dbi will be present in the NMA field of textual listings) and "partially" updated when both are 1 (mnemonic dbl). These two bits also cause the value stored in IRC to get transferred to IR whenever they are not both zero. The patent isn't entirely clear what does and doesn't get updated with the "dbl" version, but it's only used in a single microword (itlx6) in the patent version of the CPU. This appears to be apart of some kind of exception related routine, but I'm not sure which exception it's for.

The bad news is that this technically means that the latching can take place at any arbitrary point in a given instruction. The good news is that, in practice things seem to follow a pattern. For most instructions, this latching happens on the first microinstruction that isn't part of a generic operand reading subroutine (effectively after all the memory operands have been read or on the first microinstruction if there are no memory operands). The one exception seems to be move.l. For those instructions, the latch point seems to happen at the beginning of the micro-cycle for the second write. This seems to be the weird exception Genesis Plus GX is trying to handle. It seems though that even if the first word written triggers an interrupt in the VDP, the IPL lines are asserted just slightly two late to catch this latch point.

Post Reply