Interrupt Processing on MCS-51 and Derivatives  


This article is written in support of the interrupt tool I wrote for the MCS-51 series. Most of the MCS-51 (8051) derivatives have a similar architecture when it comes to external interrupt processing, so this article should be applicable to most of them.

What is an Interrupt?

An interrupt is a signal to the processor that the current code execution should be paused to handle a more urgent task. Usually this is signalled by a peripheral device that needs immediate attention, such as a timer overflow, data received on a serial port, or an external event like a button press. This event sets the interrupt request flag in the microcontroller’s interrupt system.

If the EA (Enable All) bit in corresponding IE (Interrupt Enable) register is set, and the specific interrupt is enabled, the processor will pause its current task and jump to a predefined memory location called the interrupt vector. The blog image shows all possible interrupt sources for a typical MCS-51 microcontroller. In modern variants there may be more interrupt sources, but the basic principles remain the same.

Many MCS-51 derivatives have programmable interrupt priorities, which allow the user to define which interrupt has precedence over another. If all interrupt priorities are the same the MCS-51 architecture uses a polling scheme to determine which interrupt to service first. By adjusting the priority levels, more critical interrupts can be serviced before less critical ones, and interrupts of the same priority are handled serially. The MCS-51 supports preemptive interrupt handling, meaning that a higher priority interrupt can interrupt the processing of a lower priority interrupt.

In instruction processing the following happens if an interrupt is serviced (meaning EA and the specific interrupt enable bit are set):

  1. The current instruction finishes executing.
  2. The Program Counter (PC) is pushed onto the stack to save the return address. The interrupt state of all other sources is preserved internally.
  3. The PC is loaded with the address of the interrupt service routine (ISR) corresponding to the interrupt source.
  4. The ISR executes, handling the interrupt.
  5. At the end of the ISR, a RETI (Return from Interrupt) instruction is executed, which pops the return address from the stack back into the PC.
  6. The processor resumes execution from where it left off before the interrupt occurred.

In addition to the above your compiler may perform additional context saving and restoring for registers that are used within the ISR. This is to ensure that the main program’s state is preserved correctly.

What makes the MCS-51 architecture interesting is that there is not much that needs to be preserved, as there are only a few registers. This leads to a practical exceptional low interrupt latency compared to more modern architectures. In an essence you give up processing speed (i.e. more efficient register use on most modern architectures) for latency. While other architectures, especially DSPs, use windowing to get around this problem, this also introduces consistency issues if the windowing runs out. A good example for this are Cadence DSPs

One thing to keep in mind is that the address of the interrupt service routine is fixed and that the interrupt vector itself does not contain any address mappings. As such the typical pattern is to have a small stub at the interrupt vector that jumps to the actual ISR, which can be located anywhere in memory. The following example is the address map of the STC89C52 microcontroller, which is a popular MCS-51 derivative.

STC89C52 Interrupt Map
SourceVector Address
RST0000H
IE00003H
TF0000BH
IE10013H
TF1001BH
RI+TI0023H
TF2+EXF2002BH
IE20033H
IE3003BH

As seen above the interrupt vectors are located at fixed memory addresses, this also includes the reset vector at 0000H. The common way to populate these (your compiler does this for you automatically) is to have a small jump instruction at the vector address that jumps to the actual ISR. Example,

    ORG 0
    LJMP MAIN    ; 3-byte instruction
    LJMP INT0INT ; EXT 0 vector address
    ORG 000BH    ;Timer 0 vector
    LJMP T0INT
    ORG 001BH    ;Timer 1 vector
    LJMP T1INT
    ORG 0030H
    ...

External Interrupts

Leaving aside the internal interrupts (timers, serial ports, etc.) for now, which get covered in other blog posts, let’s look at the external interrupts. The MCS-51 architecture supports two external interrupts, INT0 and INT1, which are mapped to pins on the microcontroller. These interrupts can be configured to trigger on either a low-level signal or a falling edge, depending on the settings in the TCON (Timer Control) register. Modern derivatives such as the STC89C5x series support up to 4 external interrupts (INT2 and INT3), but the principles remain the same. Some other implementations take this as far as providing interrupts on a per-port-pin basis, which can be useful in certain applications.

On most classic implementations these external interrupts are active low, meaning that they are triggered when the signal goes from high to low. A typical implementation is shown as follows.

8051 External Interrupt Circuit

Pressing the switch causes a negative edge transition as the voltage switches down from logic high (Vcc) to logic low (GND), which triggers the interrupt. Note that a pull-up resistor is used to ensure that the input pin is at a defined logic high level when the switch is not pressed.

This interrupt can be configured to be either level-triggered or edge-triggered using the IT0 and IT1 bits in the TCON register. When set to level-triggered, the interrupt is activated as long as the input pin remains low. When set to edge-triggered, the interrupt is activated only on the transition from high to low.

The button press is one example an external interrupt source, other examples include sensors, communication signals, or any other external event that requires immediate attention from the microcontroller.

In the Tools section of this blog I have created an interrupt configuration tool that allows you to easily configure external interrupts for MCS-51 derivatives, using the STC89C5x as a baseline case and generate code for SDCC.

So conceptually the external interrupt is like your significant other half pulling you away from your work to attend to something more urgent, like taking out the trash or answering the doorbell. You stop what you’re doing, handle the urgent task, and then return to your previous activity.

Who would not stop for this:

Youtube video:

I plan to post more on peripheral interrupts (timers, serial ports, etc.) in future articles.

Good references for further reading:


Published: 2025-10-26
Updated  : 2025-10-26
GPIOs on MCS-51 Derivatives →