249 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			249 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
#include <avr/io.h>
 | 
						|
#include <avr/interrupt.h>
 | 
						|
#include <avr/sleep.h>
 | 
						|
#include <util/delay.h>
 | 
						|
 | 
						|
#include <tabby/printer.h>
 | 
						|
#include <tabby/avr/uart.h>
 | 
						|
 | 
						|
#define TIMER1_INTERVAL 1953
 | 
						|
 | 
						|
enum {
 | 
						|
    TABBY_SEND_READING  =  0,
 | 
						|
    TABBY_SEND_BUFFERED = (1 << 0),
 | 
						|
    TABBY_SEND_COMPLETE = (1 << 1)
 | 
						|
};
 | 
						|
 | 
						|
static volatile uint8_t bits      = 0;
 | 
						|
static volatile uint8_t value_in  = 0x00; /* Data coming in from Game Boy */
 | 
						|
static volatile uint8_t value_out = 0x00; /* Data going out to Game Boy */
 | 
						|
 | 
						|
static volatile tabby_printer_packet header = {
 | 
						|
    .type        = 0,
 | 
						|
    .compression = 0,
 | 
						|
    .size        = 0
 | 
						|
};
 | 
						|
 | 
						|
static volatile uint8_t body[TABBY_PRINTER_MAX_PACKET_SIZE];
 | 
						|
 | 
						|
static volatile uint16_t sum = 0x0000;
 | 
						|
 | 
						|
static volatile uint8_t device = 0x00,
 | 
						|
                        status = 0x00,
 | 
						|
                        flags  = TABBY_SEND_READING;
 | 
						|
 | 
						|
static volatile uint16_t i = 0,
 | 
						|
                         b = 0;
 | 
						|
 | 
						|
static void spi_start() {
 | 
						|
    /*
 | 
						|
     * Configure MISO as input 
 | 
						|
     */
 | 
						|
    DDRB &= ~(1 << DDB4);
 | 
						|
 | 
						|
    /*
 | 
						|
     * Configure MOSI as output
 | 
						|
     */
 | 
						|
    DDRB |= (1 << DDB3);
 | 
						|
 | 
						|
    /*
 | 
						|
     * Configure SCK pin as output
 | 
						|
     */
 | 
						|
    DDRB |= (1 << DDB5);
 | 
						|
 | 
						|
    /*
 | 
						|
     * Set timer clock divider to 1/1
 | 
						|
     */
 | 
						|
    TCCR1B = (1 << CS10);
 | 
						|
 | 
						|
    /*
 | 
						|
     * Set timer interval
 | 
						|
     */
 | 
						|
    OCR1A = TIMER1_INTERVAL;
 | 
						|
 | 
						|
    /*
 | 
						|
     * Reset timer counter to zero
 | 
						|
     */
 | 
						|
    TCNT1 = 0;
 | 
						|
 | 
						|
    /*
 | 
						|
     * Enable timer interrupt vector for match on OCR1A
 | 
						|
     */
 | 
						|
    TIMSK1 = (1 << OCIE1A);
 | 
						|
}
 | 
						|
 | 
						|
static void spi_end() {
 | 
						|
    DDRB   &= ~((1 << DDB5) | (1 << DDB4) | (1 << DDB3));
 | 
						|
    TIMSK1 &= ~(1 << OCIE1A);
 | 
						|
    TCNT1   = 0;
 | 
						|
    OCR1A   = TIMER1_INTERVAL;
 | 
						|
    TCCR1B  = (1 << CS10);
 | 
						|
}
 | 
						|
 | 
						|
ISR(TIMER1_COMPA_vect) {
 | 
						|
    value_in >>= 1;
 | 
						|
 | 
						|
    PORTB |= (1 << PORTB5);
 | 
						|
 | 
						|
    if (PORTB & (1 << PORTB4)) {
 | 
						|
        value_in |= 0x80;
 | 
						|
    }
 | 
						|
 | 
						|
    if (value_out & 0x80) {
 | 
						|
        PORTB |=  (1 << PORTB3);
 | 
						|
    } else {
 | 
						|
        PORTB &= ~(1 << PORTB3);
 | 
						|
    }
 | 
						|
 | 
						|
    value_out <<= 1;
 | 
						|
 | 
						|
    if (--bits == 0) {
 | 
						|
        if (i < sizeof(header)) {
 | 
						|
            value_out = header.data[i++];
 | 
						|
        } else if (b < header.size) {
 | 
						|
            value_out = body[b++];
 | 
						|
        } else if (b == header.size) {
 | 
						|
            value_out = sum & 0x00ff;
 | 
						|
            b++;
 | 
						|
        } else if (b == header.size + 1) {
 | 
						|
            value_out = (sum & 0xff00) >> 8;
 | 
						|
            b++;
 | 
						|
        } else if (b == header.size + 2) {
 | 
						|
            device    = value_in;
 | 
						|
            value_out = 0x00;
 | 
						|
            b++;
 | 
						|
        } else if (b == header.size + 3) {
 | 
						|
            status    = value_in;
 | 
						|
            value_out = 0x00;
 | 
						|
 | 
						|
            i      = 0;
 | 
						|
            b      = 0;
 | 
						|
            flags &= ~TABBY_SEND_BUFFERED;
 | 
						|
            flags |=  TABBY_SEND_COMPLETE;
 | 
						|
 | 
						|
            spi_end();
 | 
						|
        }
 | 
						|
 | 
						|
        bits = 8;
 | 
						|
    }
 | 
						|
 | 
						|
    /*
 | 
						|
     * Chill out for 60µsec, then ride out the rest of the time until the next
 | 
						|
     * interrupt with a high as fuck SCK
 | 
						|
     */
 | 
						|
    _delay_us(60);
 | 
						|
 | 
						|
    PORTB &= ~(1 << PORTB5);
 | 
						|
}
 | 
						|
 | 
						|
static uint16_t checksum() {
 | 
						|
    uint16_t sum = 0;
 | 
						|
    size_t i;
 | 
						|
 | 
						|
    for (i=2; i<6; i++) {
 | 
						|
        sum += header.data[i];
 | 
						|
    }
 | 
						|
 | 
						|
    for (i=0; i<header.size; i++) {
 | 
						|
        sum += body[i];
 | 
						|
    }
 | 
						|
 | 
						|
    return sum;
 | 
						|
}
 | 
						|
 | 
						|
int main() {
 | 
						|
    uint16_t sum = 0;
 | 
						|
 | 
						|
    uart_init();
 | 
						|
 | 
						|
    sei();
 | 
						|
 | 
						|
    while (1) {
 | 
						|
        uint8_t c;
 | 
						|
 | 
						|
        while (flags != TABBY_SEND_READING) {
 | 
						|
            sleep_mode();
 | 
						|
        }
 | 
						|
 | 
						|
        if (flags & TABBY_SEND_COMPLETE) {
 | 
						|
            uart_putchar(device, NULL);
 | 
						|
            uart_putchar(status, NULL);
 | 
						|
 | 
						|
            flags = TABBY_SEND_READING;
 | 
						|
        }
 | 
						|
 | 
						|
        c = uart_getchar(NULL);
 | 
						|
 | 
						|
        switch (i) {
 | 
						|
            case 0: {
 | 
						|
                if (c == 0x88) {
 | 
						|
                    header.data[0] = c;
 | 
						|
                    i++;
 | 
						|
 | 
						|
                    b = 0;
 | 
						|
                }
 | 
						|
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            case 1: {
 | 
						|
                if (c == 0x33) {
 | 
						|
                    header.data[1] = c;
 | 
						|
                    i++;
 | 
						|
                } else {
 | 
						|
                    i = 0;
 | 
						|
                }
 | 
						|
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            case 2: {
 | 
						|
                header.type = c;
 | 
						|
                i++;
 | 
						|
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            case 3: {
 | 
						|
                header.compression = c;
 | 
						|
                i++;
 | 
						|
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            case 4: {
 | 
						|
                header.size = c;
 | 
						|
                i++;
 | 
						|
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            case 5: {
 | 
						|
                header.size |= c << 8;
 | 
						|
                i++;
 | 
						|
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            default: {
 | 
						|
                if (b < header.size) {
 | 
						|
                    b++;
 | 
						|
                } else if (b == header.size) {
 | 
						|
                    sum = c;
 | 
						|
                } else if (b == header.size + 1) {
 | 
						|
                    sum |= c << 8;
 | 
						|
 | 
						|
                    if (sum == checksum()) {
 | 
						|
                        i      = 0;
 | 
						|
                        b      = 0;
 | 
						|
                        flags |= TABBY_SEND_BUFFERED;
 | 
						|
 | 
						|
                        spi_start();
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 |