Index: V-USB_Dev/firmwares/CrO2/main.c
===================================================================
--- V-USB_Dev/firmwares/CrO2/main.c	(revision 1fabcce0f751c00ce479b124a5b7ebde1bd8b81e)
+++ V-USB_Dev/firmwares/CrO2/main.c	(revision 4af90bf3e0de7b93f82542e2dfb8525077a87b63)
@@ -10,16 +10,19 @@
 #include "usbdrv/usbdrv.h"
 
-#define DDRIN DDRB
-#define PORTIN PORTB
-#define PININ PINB
+static uint8_t ioblock[275]; // Max. Size of an MO5 standard block including sync header
+static uint8_t status; // status bits
+static uint16_t writePos;
+static uint16_t blksz;
 
-#define DDROUT DDRC
-#define PORTOUT PORTC
+static uint16_t readPos;
+static uint8_t bitpos;
+static uint8_t bit; // Currently generated bit
 
-static uint8_t reportBuffer[5];
-static uint8_t buttons[6];
-static uint8_t idleRate;
+int main() {
+	// Init
+	status = 0;
+	writePos = 0;
+	blksz = 0;
 
-void main() {
 	wdt_enable(WDTO_2S);
 
@@ -27,19 +30,21 @@
 	usbInit();
 	sei();
-	bool doReport;
 
-	DDROUT = 0; // Keyboard matrix out
-	PORTOUT = 255; // Enable pull up
-		// We put all pins as input then output a 0 in only one at a time.
-		// All the other pins are high-Z to avoid short circuits when many
-		// buttons are pressed.
-	DDRIN = 0; // Keyboard matrix in
-	PORTIN = 255; // Enable pull up
+	bit = 1;
+	bitpos = 0;
+	readPos = 0;
 
-    // configure timer 0 for a rate of 16M/(256 * 256) = ~244Hz
-    TCCR0 = 4;          // timer 0 prescaler: 256
+	// Setup TIMER 1 for MFM pulses generation
+	TCCR1A = (1<<COM1A0); // Enable OC1A output
+	// CTC mode with OCR1A as MAXregister
+	TCCR1B = (1<<WGM12) | (1<<CS10);
+	OCR1A = 12800; // 800us bit clock
+	OCR1B = 6400; // Half-clock for 1 bits
+	TIMSK = (1 << OCIE1B) | (1 << OCIE1A); // interrupts on both timer matches.
 
-	reportBuffer[0] = 0;
-	doReport = false;
+	// TODO only start the timer when actually needed (start of block)
+	// and stop it when done.
+	DDRB |= 2; // OC1A/PB1 as output
+
 
 	while(1) {
@@ -47,110 +52,70 @@
 		usbPoll();
 
-		for(int i = 0; i != 6; i++) {
-			DDROUT = 1<<i;
-			PORTOUT = ~(1<<i);
-			_delay_us(63);
-			if (buttons[i] != ((~PININ)&0x3F))
-			{
-				doReport = true;
-				buttons[i] = (~PININ)&0x3F;
-			}
-		}
-		DDROUT = 0;
-		PORTOUT = 255;
-
-		// Copy lines 1 to 3 to the same lines in the report
-		for(int i=1; i != 4; i++) {
-			reportBuffer[i] = buttons[i];
-		}
-
-		// Dispatch line 0 to report 1,2,3 (2 buttons each)
-		reportBuffer[1] |= (buttons[0] << 4)&0xC0;
-		reportBuffer[2] |= (buttons[0] << 2)&0xC0;
-		reportBuffer[3] |= (buttons[0])&0xC0;
-
-		// Copy part line 6
-		reportBuffer[4] = buttons[5]; // 2 btns left here
-		//reportBuffer[0] = buttons[4]; // this line is unused anyway, report as
-									  // axis
-
-		if (doReport && usbInterruptIsReady()) {
-			usbSetInterrupt(reportBuffer, sizeof(reportBuffer));
-			doReport = false;
-		}
+		// TODO send data to tape !
 	}
 }
 
 
-static uint8_t protocolVer = 1;
-uint8_t expectReport = 0;
+uint8_t usbFunctionSetup(uint8_t data[8]) {
+	usbRequest_t *rq = (void *)data;
 
-
-char PROGMEM usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] = {
-	0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
-    0x09, 0x04,                    // USAGE (Joystick)
-    0xa1, 0x01,                    // COLLECTION (Application)
-    0xa1, 0x02,                    //   COLLECTION (Logical)
-
-    0x75, 0x04,	                   //     REPORT_SIZE (8)
-    0x95, 0x02,                    //     REPORT_COUNT (2)
-    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
-    0x25, 0x0F,                    //     LOGICAL_MAXIMUM (15)
-    0x35, 0x00,                    //     PHYS_MINIMUM (0)
-    0x45, 0x0F,                    //     PHYS_MAXIMUM (15)
-    0x09, 0x30,                    //     USAGE (X)
-    0x09, 0x31,                    //     USAGE (Y)
-    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
-
-    0x75, 0x01,                    //     REPORT_SIZE (1)
-    0x95, 0x20,                    //     REPORT_COUNT (32)
-    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
-    0x45, 0x01,                    //     PHYSMAX (1)
-    0x05, 0x09,                    //     USAGE_PAGE (Button)
-    0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)
-    0x29, 0x20,                    //     USAGE_MAXIMUM (Button 32)
-    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
-    0xc0,                          //   END_COLLECTION
-    0xc0                           // END_COLLECTION
-};
-
-
-uint8_t usbFunctionSetup(uint8_t data[8]) {
-    usbRequest_t *rq = (void *)data;
-    usbMsgPtr = reportBuffer;
-    if ((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) {
-        // class request type
-        if (rq->bRequest == USBRQ_HID_GET_REPORT) {
-            // wValue: ReportType (highbyte), ReportID (lowbyte)
-            // we only have one report type, so don't look at wValue
-            return sizeof(reportBuffer);
-        } else if (rq->bRequest == USBRQ_HID_SET_REPORT) {
-            if (rq->wLength.word == 1) {
-                // We expect one byte reports
-                expectReport = 1;
-                return 0xff; // Call usbFunctionWrite with data
-            }
-        } else if (rq->bRequest == USBRQ_HID_GET_IDLE) {
-            usbMsgPtr = &idleRate;
-            return 1;
-        } else if (rq->bRequest == USBRQ_HID_SET_IDLE) {
-            idleRate = rq->wValue.bytes[1];
-        } else if (rq->bRequest == USBRQ_HID_GET_PROTOCOL) {
-            if (rq->wValue.bytes[1] < 1) {
-                protocolVer = rq->wValue.bytes[1];
-            }
-        } else if(rq->bRequest == USBRQ_HID_SET_PROTOCOL) {
-            usbMsgPtr = &protocolVer;
-            return 1;
-        }
-    } else {
-        // no vendor specific requests implemented
-    }
-    return 0;
+	usbMsgPtr = ioblock;
+	switch(rq->bRequest)
+	{
+		case 0:
+			{  /* CONFIGURE */
+				status = rq->wValue.bytes[0];
+				return 0;
+			}
+		case 1:
+			{  /* RECEIVE/POLL DATA (TAPE > USB) */
+				return 254; 
+			}
+		case 2:
+			{
+				/* SEND DATA (USB > TAPE) */
+				/* Get ready, actual data will come in usbFunctionWrite */
+				// TODO store type of MO5 block + size in ioblock
+				writePos = 0;
+				blksz = rq->wLength.word;
+				return USB_NO_MSG; // Call usbFunctionWrite to send the data
+			}
+	}
+	return 0;
 }
 
-uint8_t usbFunctionWrite(uchar *data, uchar len) {
-    expectReport = 0;
-    return 0x01;
+uint8_t usbFunctionWrite(uint8_t* data, uint8_t len)
+{
+	// The data for a single request will be sent as slices of up to 8 bytes.
+	for (int i = 0; i < len; i++)
+	{
+		// TODO leave place for sync header and block type+size
+		ioblock[writePos++] = data[i];
+	}
+
+	// TODO compute checksum as we go
+
+	return writePos>= blksz ? 1:0;
+	// returns 0: "needs more data"
+	// returns 1: "done"
+	// returns FF: "error"
 }
 
+ISR (TIMER1_COMPA_vect)
+{
+	// generate next bit
+	bit = (ioblock[readPos] >> bitpos) & 1;
+	bitpos++;
+	if (bitpos > 7)
+	{
+		bitpos = 0;
+		readPos++;
+		if (readPos > 270)
+			readPos = 0; // TODO stop timer
+	}
+}
+
+ISR (TIMER1_COMPB_vect)
+{
+	TCCR1A |= (bit << FOC1A); // Force toggle of A on B compare when generating a 1 bit
+}
