#include <stdint.h>
#include <msp430.h>

/*
    Commands:

    0x0D  mute
    0x2A  
    0x2B  "Full"
    0x2C  PC
    0x2D  side-by-side?
    0x2F  "i"
    0x38  AV
*/  

#define UART_BIT_TIME 104
#define UART_HALF_BIT_TIME 52

/*#define UART_BIT_TIME 833*/
/*#define UART_HALF_BIT_TIME 417*/

#define IR_PORT P2OUT
#define IR_BIT 0x40

#define RX_BIT 0x04

uint8_t rxbyte;
uint16_t rxbuf; // byte currently being received
uint8_t rxbit; // current bit
uint8_t rxdone; // done receiving byte

uint8_t state = 0;
uint8_t buf[20];
uint8_t start = 0;

void delay_ms(uint16_t ms) {
    while (ms--)
        __delay_cycles(1000);
}

void wait_for_button() {
    while (!(P1IN & 0x08)) ;
    delay_ms(20);
    while (P1IN & 0x08) ;
    delay_ms(20);
}

void send_bit() {
    int8_t i;
    for (i = 32; i > 0; i--) {
        IR_PORT |= IR_BIT;
        __delay_cycles(111);

        IR_PORT &= ~IR_BIT;
        __delay_cycles(105);
    }
}

void philips_rc5_send(uint16_t data) {
    //const uint16_t bit_cycles = 889 - 6;
    const uint16_t bit_cycles = 7112 - 6;
    
    IR_PORT &= ~IR_BIT;

    __delay_cycles(bit_cycles * 2);
    
    uint8_t bit = 14;
    while (bit--) {
        if (data & (1 << 13)) {
            __delay_cycles(bit_cycles);

            send_bit();
        } else {
            send_bit();

            __delay_cycles(bit_cycles);
        }

        data <<= 1;
    }

    IR_PORT &= ~IR_BIT;
}

int main() {
    // Disable watchdog
    WDTCTL = WDTPW | WDTHOLD;

    BCSCTL1 = CALBC1_1MHZ;
    DCOCTL = CALDCO_1MHZ;

    // Configure output port
    P2SEL = 0x00;
    P2SEL2 = 0x00;
    P2OUT &= ~IR_BIT;
    P2DIR |= IR_BIT;
    
    // Configure RX port (software UART)

    P1IES |= RX_BIT;
    P1IFG &= ~RX_BIT;
    P1IE |= RX_BIT;

    P1DIR |= 0x01;
    P1OUT &= ~0x01;

    __bis_status_register(GIE);

    while (1) {
        if (rxdone) {
            rxdone = 0;

            if (state == 0) {
                if (rxbyte == 0xFF)
                    state++;
            } else if (state == 1) {
                buf[0] = rxbyte;

                state++;
            } else {
                buf[1] = rxbyte;

                __disable_interrupt();
                philips_rc5_send((((uint16_t)buf[0]) << 8) | buf[1]);
                
                __enable_interrupt();

                state = 0;

                P1OUT |= 0x01;
                delay_ms(50);
                P1OUT &= ~0x01;
            }
        }

        if (!rxdone)
            __bis_status_register(CPUOFF | GIE);
    }

    return 0;
}

void __attribute__((interrupt (PORT1_VECTOR))) p1_isr() {
    // Disable port interrupt
    P1IE &= ~RX_BIT;
    P1IFG &= ~RX_BIT;

    // Clear buffers
    rxbuf = 0;
    rxbit = 9;

    // Enable timer, start at half bit time
    TACTL = TASSEL_2 | MC_2;
    TACCR0 = TAR + UART_HALF_BIT_TIME;
    TACCTL0 = OUTMOD1 | CCIE;
}

void __attribute__((interrupt (TIMER0_A0_VECTOR))) ta0_isr() {
    TACCR0 += UART_BIT_TIME;

    if (rxbit == 0) {
        TACTL = TASSEL_2;
        TACCTL0 &= ~CCIE;
        
        P1IFG &= ~RX_BIT;
        P1IE |= RX_BIT;

        // start bit = 0, stop bit = 1
        if ((rxbuf & 0x201) == 0x200) {
            rxbyte = (rxbuf >> 1) & 0xFF;
            rxdone = 1;
        }

        __bic_status_register_on_exit(CPUOFF);
    } else {
        if (P1IN & RX_BIT)
            rxbuf |= 0x400;

        rxbuf >>= 1;

        rxbit--;
    }
}
