What is an Interrupt? (65c816)

I am going to explain what an interrupt is and how it works exactly. For this section, I am going to reference the example explained in Programming the 65816 because it really hit the nail on the head and has stuck with me ever since I laid my eyes on those words. Ready? Here it goes.

What is an interrupt?

Imagine yourself hosting a party. You're getting ready for your guests to come. You have told everyone to start showing up at 4:00 PM. It’s 4:00 PM now, and as you're laying out your chips and dip. You stop for a moment to check on the door to see if any guests have arrived yet. So you briskly walk to the door and find that no one has arrived yet. Okay, that’s fine. It’s still very early! Only "lame" people show up on time, right? (No, don't ever do this!)

Now you go off and get ready to prepare some drinks. As you go to the refrigerator, you wonder whether or not a guest has arrived yet. So, now you walk back to the living room and check on the door. No one is here yet. Again, you think that is okay. Now, you go back to the kitchen to grab the drinks and complete this task. As you reach for the refrigerator door and grab the drinks, you go back to check on the door. No one yet has arrived yet.

Can you see how expensive it can be to constantly check for someone? The preparation for the party is slowed down immensely with the thought to always check on the door every so often. This is pretty much the polling approach to handle any high priority items — which in this case is the constant checking to see if any guests have arrived for the party.

Would there be a more efficient way to actually prepare for this party and let your guests in? Yes, there is. It's called a doorbell. I'm not being smug!

A doorbell can be thought of as a system interrupt. It is a signal to tell us whether or not we should stop what we are doing and take care of a higher priority item. In this case, our system interrupt is a doorbell. Our interrupt handler is now basically the routine in which we need to execute in order to fulfill the interrupt request (when the doorbell has rung). Our interrupt handler in our doorbell example is... wait for it... letting our guests in.

An interrupt request in the 65816 must be fulfilled only when the current instruction has been completed. So, when we receive the interrupt request, we finish our current execution cycle and then pause to handle the interrupt. In order to do that, we must perform the interrupt setup routine, then execute the interrupt handler and finally, go back and handle the normal execution of our program.

The 65816 has two types of interrupt requests. Hardware and software interrupts. In hardware, there is actually a pin that the 65816 provides for an external device to set an interrupt line to low. Our software interrupt essentially does a process in pushing bytes onto the stack and then moving to an address in memory to perform the interrupt request (IRQ).

The interrupt setup routine is of the following:

  1. The program bank register is pushed onto the stack.
  2. The high byte of the address held in the program counter is pushed onto the stack.
  3. The low byte of the address held in the program counter is pushed onto the stack.
  4. The P status register is pushed onto the stack.
  5. The D flag is cleared. (Decimal mode is set to off.)
  6. The I flag is then set. This tells us that interrupt mode is on and that the CPU can no longer service any incoming interrupts until the current interrupt has been completed.
  7. Retrieve the address of the interrupt handler by going to the appropriate interrupt vector.

All this gives us the information we need to actually transfer control back to the running program once the interrupt routine has been completed. With the combination of the PBR, high and low bytes on the stack, we can then form the program counter address in which the CPU was originally at before being interrupted. The RTI instruction essentially uses the constructed address to return control back to the appropriate instruction.

It is important to note that the RTI instruction does not increment the constructed effective address by one. This makes sense as there was no sort of jump instruction that actually caused a separate routine to run, but rather an "invisible call" due to an interrupt.

The P status register popped off the stack also puts the CPU in a state it originally was after processing the previous instruction.

The I flag is then cleared after the interrupt has been serviced.

To even find out which was the source of the interrupt in hardware, the CPU performs polling when an interrupt occurs. The hardware device will have their own status bit in telling us whether or not an interrupt request was pushed out to the CPU. I won't go into it that deep here.

One other thing to note is that when it comes to real-time programming. If the execution and response time of a specific piece of code is critical, we should perform as little interrupts as possible. This is due to the fact that setting up for interrupts to begin with causes a bit of latency, as detailed above. However, most of that latency is just due to letting the CPU finish its current cycle before setting up for the interrupt. There are ways to reduce the latency by letting the CPU expect an interrupt with an instruction such as WAI, where the CPU is put in a low power idle state and is just waiting for interrupts to happen.

Hopefully, this has been a useful high level introduction to how interrupts work. It not only applies to the 65816 CPU discussed above, but really is conceptually the same for most of computing.