#include <stdio.h>

unsigned char a; // Accu register
unsigned char p; // P status register

void adc(unsigned char M) {

    unsigned int tmp = a + M + (p & 0x01);

    // Overflow flag (V):
    // set if sign bit is not equal
    if ((a ^ tmp) & 0x80) 
        p |= 0x40;
    else
        p &= 0xbf;
    
    // Negative flag (N):
    // set if bit 7 set
    if (a & 0x80)
        p |= 0x80;
    else
        p &= 0x7f;

    // Zero flag (Z):
    // set if A = 0
    if (a)
        p &= 0xfd;
    else
        p |= 0x02;


    if (p & 0x08) {
        // In BCD mode

        tmp = ((a & 0x0f) + (a >> 4) * 10) + // bin of BCD a
              ((M & 0x0f) + (M >> 4) * 10) + // bin of BCD M
              (p & 0x01);

        // Carry flag
        if (tmp > 99)
            p |= 0x01;
        else
            p &= 0xfe;

        // bin back to BCD
        tmp = (((tmp % 100) / 10) << 4) | (tmp % 10);
    } else {
        // Normal mode

        // Carry flag
        if (tmp > 0xff)
            p |= 0x01;
        else
            p &= 0xfe;
    }

    a = tmp & 0xff;
}

int main() {
    // BCD test
    a = 0x25;
    unsigned char m = 0x80;
    p = 0x00 | 0x08;

    printf("a=%i, m=%i, p=%i, ", a, m, p);
    adc(m);
    printf("a=%i, p=%i\n", a, p);

    // = 0x05 with overflow (0x25 + 0x80 = 0x105 > 0x99)

    // Normal test
    a = 21;
    m = 20;
    p = 0x01;

    printf("a=%i, m=%i, p=%i, ", a, m, p);
    adc(m);
    printf("a=%i, p=%i\n", a, p);

    // = 42 (21 + 20 + 1)

    return 0;
}