Understanding the Auxiliary Carry Flag in BCD Arithmetic

This article provides an overview of the Auxiliary Carry Flag (AF), its operational mechanism within processor architecture, and its specialized role in Binary-Coded Decimal (BCD) operations. You will learn how central processing units (CPUs) monitor arithmetic transitions between 4-bit nibbles, how hardware detects intermediate carries, and how assembly instructions utilize the AF flag to ensure accurate decimal representations within the binary number system.

What Is the Auxiliary Carry Flag?

The Auxiliary Carry Flag (AF), also known as the Half-Carry Flag in architectures such as ARM or the 8080/Z80, is a single-bit status indicator located in a CPU’s status register (such as the FLAGS or EFLAGS register in x86 architectures).

Unlike the standard Carry Flag (CF), which tracks overflow or borrow conditions at the most significant bit (MSB) of an entire byte, word, or doubleword, the Auxiliary Carry Flag specifically tracks carry and borrow conditions at the nibble level. A nibble consists of 4 bits. In an 8-bit byte (bits 0 through 7):

The AF is set to 1 if an arithmetic operation generates a carry out of bit 3 into bit 4 during addition, or requires a borrow from bit 4 to bit 3 during subtraction. If no carry or borrow occurs across this 4-bit boundary, the AF is cleared to 0.

The Nibble-Level Detection Mechanism

Binary arithmetic operates strictly in base-2. When adding two binary numbers, each bit column adds corresponding bits along with any carry generated by the previous column:

\[\text{Bit } 3 \text{ sum} = A_3 + B_3 + \text{Carry}_2\]

If the resulting value of the bit 3 addition equals or exceeds 2 (in base-10), a carry bit is produced and fed directly into the bit 4 calculation:

\[\text{Carry}_3 = (A_3 \land B_3) \lor (\text{Carry}_2 \land (A_3 \oplus B_3))\]

The processor’s Arithmetic Logic Unit (ALU) directly samples this intermediate carry line (\(\text{Carry}_3\)). When \(\text{Carry}_3 = 1\), the hardware automatically asserts the Auxiliary Carry Flag.

How BCD Uses the Auxiliary Carry Flag

Binary-Coded Decimal (BCD) is an encoding scheme where each decimal digit (\(0\) through \(9\)) is represented by a dedicated 4-bit binary sequence (\(0000_2\) through \(1001_2\)). Values from \(1010_2\) (\(10_{10}\)) through \(1111_2\) (\(15_{10}\)) are invalid in standard BCD.

When adding two BCD numbers using standard binary addition, two types of errors can occur in the lower nibble:

  1. The sum of the nibble exceeds \(15_{10}\): A carry is generated into bit 4. The lower nibble wraps around, and the AF is set to 1.
  2. The sum falls between \(10_{10}\) and \(15_{10}\): No carry is generated into bit 4 (AF remains 0), but the nibble contains an invalid BCD digit.

Because standard binary addition does not automatically handle base-10 modular arithmetic, processors provide adjustment instructions (such as DAA—Decimal Adjust AL after Addition in x86). These instructions inspect both the value of the lower nibble and the state of the Auxiliary Carry Flag to apply a correction factor:

Adding \(6\) skips the six invalid states (\(1010_2\) to \(1111_2\)), forcing the lower nibble to retain the correct decimal unit digit and properly propagating the carry into the upper decimal digit (the upper nibble).

Step-by-Step Example

Consider the addition of two packed BCD digits: \(8\) (\(0000\ 1000_2\)) and \(9\) (\(0000\ 1001_2\)). In decimal, \(8 + 9 = 17\) (represented in packed BCD as \(0001\ 0111_2\)).

  1. Binary Addition: \[\begin{array}{r@{\quad}l} 0000\ 1000_2 & (8) \\ + 0000\ 1001_2 & (9) \\ \hline 0001\ 0001_2 & (17_{10} \text{ in pure binary, or } 11_{16}) \end{array}\]

  2. Flag Evaluation: During the addition of bit 3 (\(1 + 1 = 10_2\)), a carry is passed to bit 4. The ALU sets the Auxiliary Carry Flag: AF = 1.

  3. BCD Adjustment (DAA): The processor executes the adjustment routine. Even though the lower nibble contains 0001 (which is \(\le 9\)), the CPU sees that AF = 1.

    The CPU adds \(0110_2\) (\(6\)) to the result: \[\begin{array}{r@{\quad}l} 0001\ 0001_2 \\ + 0000\ 0110_2 \\ \hline 0001\ 0111_2 \end{array}\]

  4. Final Result: The upper nibble is 0001 (\(1_{10}\)) and the lower nibble is 0111 (\(7_{10}\)), yielding the correct packed BCD representation for \(17\).