239 lines
4.7 KiB
C
239 lines
4.7 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>
|
|
|
|
static tabby_printer_packet header = {
|
|
.type = 0,
|
|
.compression = 0,
|
|
.size = 0
|
|
};
|
|
|
|
static tabby_printer_response response;
|
|
|
|
static uint8_t body[TABBY_PRINTER_MAX_PACKET_SIZE];
|
|
|
|
static uint16_t sum = 0x0000;
|
|
|
|
/*
|
|
* So like, we're abusing pins here. So badly, it's beyond. So here's what's
|
|
* going down: We want to retain the same physical I/O connections between
|
|
* both the sending and receiving flavors of the whole Game Boy Printer
|
|
* endeavor, and that mean/s that we must absolutely bit bang MOSI and MISO
|
|
* opposite from their ordinary roles. The reason being: There is no notion
|
|
* of MISO or MOSI from the Game Boy's (nor its peripherals') point of view.
|
|
* The SI and SO pins over a link cable are crossed over, so SI on one end
|
|
* always connects to SO on the other, and vice-versa.
|
|
*/
|
|
static void spi_init() {
|
|
/*
|
|
* Set SC to output
|
|
*/
|
|
DDRB |= (1 << DDB5);
|
|
|
|
/*
|
|
* Configure SO as output
|
|
*/
|
|
DDRB |= (1 << DDB4);
|
|
|
|
/*
|
|
* Configure SI as input with pullup
|
|
*/
|
|
DDRB &= ~(1 << DDB3);
|
|
PORTB |= (1 << DDB3);
|
|
|
|
/*
|
|
* Set SO clear
|
|
*/
|
|
PORTB &= ~(1 << PORTB4);
|
|
|
|
/*
|
|
* Set SC high
|
|
*/
|
|
PORTB |= (1 << PORTB5);
|
|
}
|
|
|
|
static uint8_t spi_send_byte(uint8_t value) {
|
|
uint8_t i, ret = 0;
|
|
|
|
for (i=0; i<8; i++) {
|
|
PORTB &= ~(1 << PORTB5);
|
|
|
|
if (value & 0x80) {
|
|
PORTB |= (1 << PORTB4);
|
|
} else {
|
|
PORTB &= ~(1 << PORTB4);
|
|
}
|
|
|
|
value <<= 1;
|
|
ret <<= 1;
|
|
|
|
_delay_us(60);
|
|
|
|
PORTB |= (1 << PORTB5);
|
|
|
|
if (PINB & (1 << PINB3)) {
|
|
ret |= 1;
|
|
}
|
|
|
|
_delay_us(60);
|
|
}
|
|
|
|
_delay_us(270);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void spi_send_packet() {
|
|
int i;
|
|
|
|
for (i=0; i<sizeof(header); i++) {
|
|
(void)spi_send_byte(header.data[i]);
|
|
}
|
|
|
|
for (i=0; i<header.size; i++) {
|
|
(void)spi_send_byte(body[i]);
|
|
}
|
|
|
|
(void)spi_send_byte( sum & 0x00ff);
|
|
(void)spi_send_byte((sum & 0xff00) >> 8);
|
|
|
|
response.device = spi_send_byte(0);
|
|
response.status = spi_send_byte(0);
|
|
|
|
_delay_us(270);
|
|
}
|
|
|
|
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 i = 0,
|
|
b = 0,
|
|
c;
|
|
|
|
uart_init();
|
|
spi_init();
|
|
|
|
sei();
|
|
|
|
while (1) {
|
|
uart_putchar('O', NULL);
|
|
|
|
c = uart_getchar(NULL);
|
|
|
|
if (c == 'K') {
|
|
break;
|
|
}
|
|
}
|
|
|
|
while (1) {
|
|
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: {
|
|
switch (c) {
|
|
case 0x01:
|
|
case 0x02:
|
|
case 0x04:
|
|
case 0x08:
|
|
case 0x0f: {
|
|
header.type = c;
|
|
i++;
|
|
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
i = 0;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
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) {
|
|
body[b++] = c;
|
|
} else if (b == header.size) {
|
|
sum = c;
|
|
b++;
|
|
} else if (b == header.size + 1) {
|
|
sum |= c << 8;
|
|
|
|
i = 0;
|
|
b = 0;
|
|
|
|
if (sum == checksum()) {
|
|
spi_send_packet();
|
|
|
|
uart_putchar(response.device, NULL);
|
|
uart_putchar(response.status, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|