--- /dev/null
+#include <avr/io.h>
+#include "interface.h"
+#include "ringbuffer.h"
+#include "s88.h"
+#include "serial.h"
+#include "timer.h"
+
+#define RESET PORTD4
+#define LOAD PORTD5
+#define CLOCK PORTD6
+#define DATA PIN7
+#define BIT(x) (1<<(x))
+
+volatile uint8_t s88_read_count = 0;
+uint8_t s88_read_bit = 0;
+volatile uint8_t s88_read_phase = 0;
+uint8_t s88_data = 0;
+RINGBUFFER(s88_buffer, 8);
+uint8_t s88_out_index = 0;
+
+void s88_init(void)
+{
+ DDRD = (DDRD&0x0F)|0x70;
+ PORTD &= 0x0F;
+
+ timer_start_hz(0, 80000, 1);
+}
+
+void s88_check(void)
+{
+ // Only send one packet per check to avoid blocking
+ if(ringbuffer_fill(s88_buffer)>0)
+ {
+ uint8_t reply[3];
+ reply[0] = S88_DATA;
+ reply[1] = s88_out_index++;
+ reply[2] = ringbuffer_pop(s88_buffer);
+ interface_send(reply, sizeof(reply));
+ }
+}
+
+uint8_t s88_command(const uint8_t *cmd_buf, uint8_t cmd_length)
+{
+ if(cmd_buf[0]==S88_READ)
+ {
+ if(cmd_length!=2)
+ return LENGTH_ERROR;
+
+ while(s88_read_count || s88_read_phase) ;
+
+ s88_out_index = 0;
+ s88_read_phase = 0xF0;
+ s88_read_count = cmd_buf[1];
+ }
+ else
+ return INVALID_COMMAND;
+
+ return COMMAND_OK;
+}
+
+static inline void s88_tick(void)
+{
+ if(s88_read_phase==0)
+ {
+ if(!s88_read_count || ringbuffer_space(s88_buffer)==0)
+ return;
+
+ PORTD |= BIT(CLOCK);
+ }
+ else if(s88_read_phase==1)
+ {
+ uint8_t bit = (PIND>>7)&1;
+ s88_data = (s88_data<<1)|bit;
+ ++s88_read_bit;
+ if(s88_read_bit==8)
+ {
+ ringbuffer_push(s88_buffer, s88_data);
+
+ --s88_read_count;
+ s88_read_bit = 0;
+ }
+ }
+ else if(s88_read_phase==2)
+ PORTD &= ~BIT(CLOCK);
+ else if(s88_read_phase>3)
+ {
+ if(s88_read_phase==0xF0)
+ PORTD |= BIT(LOAD);
+ else if(s88_read_phase==0xF4)
+ PORTD |= BIT(CLOCK);
+ else if(s88_read_phase==0xF6)
+ PORTD &= ~BIT(CLOCK);
+ else if(s88_read_phase==0xFA)
+ PORTD |= BIT(RESET);
+ else if(s88_read_phase==0xFC)
+ PORTD &= ~BIT(RESET);
+ else if(s88_read_phase==0xFF)
+ {
+ s88_data = (PIND>>7)&1;
+ ++s88_read_bit;
+ PORTD &= ~BIT(LOAD);
+ }
+ ++s88_read_phase;
+ return;
+ }
+
+ s88_read_phase = (s88_read_phase+1)&3;
+}
+
+TIMER_SET_CALLBACK(0, s88_tick)