A curious case of missing interrupts: bit-fields vs bit-masks


Printed Circuit Board Assembly used for the project

While working with a PIC24F microcontroller, I came upon a situation where interrupts of UARTs 3 and 4 started disappearing. I reviewed my own code and even checked silicon errata for the MCU, but to no avail. Therefore, I turned my focus towards some third-party code I had incorporated in my project.

I found the problem in the USB host stack of Microchip Libraries for Applications (MLA) version 2013-06-15. The cause became apparent only with a disassembly listing, which I will use here for demonstration.

This is the original statement from the library, which clears the USB interrupt flag using a mask:

IFS5 &= 0xFFBF;

It was compiled by XC16 v1.24 at optimization level -O0 into this:

00365A 800475 MOV IFS5, W5
00365C 2FFBF4 MOV #0xFFBF, W4
00365E 628204 AND W5, W4, W4
003660 880474 MOV W4, IFS5

In this scenario, clearing the interrupt flag takes four instructions. This command sequence can unintentionally clear a flag if it is set by the hardware in-between the read and write of IFS5. The chances of this happening are not low because the USB interrupt occurs frequently, sometimes at 10,000 Hz. As it happens, the interrupt flags for UARTs 3 and 4 are present in IFS5, which solves the mystery behind this bug.

I fixed the problem by changing the original statement to the one below, which accesses the flag through a bit-field:

IFS5bits.USB1IF = 0;

This statement was compiled as:

00365A A9C08E BCLR IFS5, #6

In this case, the compiler has used an atomic bit clear instruction to clear the interrupt flag. IFS5, like other SFRs, is a bit-addressable register.

However, if I raise the compiler’s optimization level to -O1, even the original bit-mask statement is translated into a bit clear instruction.

The compiler appears to be acting inconsistently at -O0, as both the mask and bit-field C statements are logically equivalent. After searching the web as well as I could, I have found an explanation of this peculiar behavior. It is easier for the compiler to determine if a bit clear instruction can be used in case of a bit-field, as compared to a bit-mask. The use of BCLR is ultimately an optimization, albeit a very simple one.


Graphical analog clock written in Assembly

A screenshot of the clock running on DOSBox.

A screenshot of the clock running on DOSBox.

While I took a course about microprocessor architecture at college, I worked on a program that showed time in analog and digital format. The code was written in assembly language (MASM in particular). I did not write the code from scratch. The original code is available from multiple Chinese websites. I have not been able to determine the original writer.

The original code consisted of a customizable analog clock. It used basic DOS interrupts to draw the graphics. Its size and colour could be changed during runtime. It also had some sound functionality. The code was written for Intel’s 80286 processor and a compatible version of DOS. The program ran fine as long as one did not play with the its customizable features.

I removed the buggy features from the code and added some new (non-buggy) ones. I stripped the code of runtime customizations and sound. And I added a needle for milliseconds, along with a digital clock. I used MASM 6.15 to compile the code. The program can be run in Windows XP. For newer versions of windows, a virtual machine is not able to run it very smoothly. A better option is DOSBox. Be sure to set cycles=max in the configuration file (options).

Following is a simple flowchart for the code.

Flowchart for the clock's assembly code.

Flowchart for the clock’s assembly code.

Download the source: masm-clock at GitHub