Browse Source

fmtx: add send whole file command

H. Peter Anvin 3 năm trước cách đây
mục cha
commit
a61c536618
2 tập tin đã thay đổi với 140 bổ sung36 xóa
  1. 126 34
      USBCAS/fmtx.c
  2. 14 2
      USBCAS/usbcas.h

+ 126 - 34
USBCAS/fmtx.c

@@ -11,6 +11,17 @@
 
 #include "fmpat.h"
 
+static void fmtx_set_speed(uint8_t divisor);
+
+/* Post-block silent pause, in pattern times */
+static unsigned int block_pause(uint8_t blktype, uint8_t divisor)
+{
+	if (blktype == 0)	/* data block */
+		return 362 << BIT_PATTERN_LEN_LG2;
+	else			/* header block */
+		return (3*362) << BIT_PATTERN_LEN_LG2;
+}
+
 /* Triple buffer: one being transmitted, one being received, and one ready. */
 static struct cas_block block[3];
 
@@ -41,6 +52,7 @@ ISR(USART1_UDRE_vect)
 			UCSR1A = TX_IDLE;
 			UDR1   = parity;
 			fmtx_disable(); /* Nothing left to do */
+			return;
 		} else if (tx_offset < sizeof blk->data) {
 			tx_data = ((const uint8_t *)&blk->data)[tx_offset++];
 			tx_bits = 8;
@@ -48,11 +60,17 @@ ISR(USART1_UDRE_vect)
 			/* Interblock silence; hold the line */
 			UCSR1A = TX_IDLE;
 			UDR1   = parity;
-			if (!--blk->pause) {
-				blk->ready = false; /* Free to use */
-				blk = blk->next;
-				tx_offset = 0;
+			blk->pause--;
+			return;
+		} else {
+			if (blk->data.blktype == 0xff &&
+			    blk->data.hdr.divisor) {
+				fmtx_set_speed(blk->data.hdr.divisor);
 			}
+			blk->ready = false; /* Free to use */
+			blk = blk->next;
+			tx_offset = 0;
+			return;	/* Will probably re-trigger immediately */
 		}
 	}
 
@@ -129,13 +147,17 @@ static void fmtx_set_speed(uint8_t divisor)
 }
 
 static enum rx_state {
-	rx_idle,		/* Waiting for & */
-	rx_amp,			/* & received */
-	rx_cmd,			/* command byte received, getting args */
-	rx_data			/* receiving data */
+	rx_amp,		     /* Waiting for & */
+	rx_cmd,		     /* & received */
+	rx_filename,	     /* Getting filename */
+	rx_num,		     /* Getting numeric arguments */
+	rx_data,	     /* Receiving data */
+	rx_idle = rx_num     /* Root state */
 } rx_state;
 
+static unsigned int rx_blk_cnt;
 static struct cas_block *rx_blk;
+
 void fmtx_init(void)
 {
 	int i;
@@ -164,9 +186,39 @@ bool fmtx_full(void)
 	return rx_blk->ready;
 }
 
+static enum rx_state setup_block(unsigned int divisor, unsigned int blkno)
+{
+	uint8_t blktype;
+
+	fmtx_drain();
+	fmtx_set_speed(divisor);
+
+	rx_blk->data.blkno = blkno;
+	blktype = -(blkno == -1U);
+	rx_blk->data.blktype = blktype;
+
+	rx_blk->pause = block_pause(blktype, divisor);
+
+	/* Initialize checksum */
+	rx_blk->data.csum = 0x03  /* ETX is part of the checksum */
+		+ blktype + (uint8_t)blkno + (uint8_t)(blkno >> 8);
+
+	return rx_data;
+}
+
+static uint16_t checksum_range(const uint8_t *data, unsigned int len)
+{
+	uint16_t csum = 0;
+
+	while (len--)
+		csum += *data++;
+
+	return csum;
+}
+
 #define MAX_ARGS 4
 
-static uint8_t do_cmd(uint8_t cmd, const uint16_t *arg)
+static enum rx_state do_cmd(uint8_t cmd, const uint16_t *arg)
 {
 	switch (cmd) {
 	case 'U':
@@ -182,27 +234,36 @@ static uint8_t do_cmd(uint8_t cmd, const uint16_t *arg)
 		return rx_idle;
 
 	case 'H':
-		/* Begin header block */
+		/* Send header block */
 		/* &H divisor */
-		rx_blk->pause = (3*362) << BIT_PATTERN_LEN_LG2;
-		rx_blk->data.blktype = rx_blk->data.blkno = -1;
-		goto blk_common;
+		rx_blk_cnt = 1;
+		return setup_block(arg[0], -1);
 
 	case 'D':
-		/* Begin data block */
+		/* Send data block */
 		/* &D divisor,blocknr */
-		rx_blk->pause = 362 << BIT_PATTERN_LEN_LG2;
-		rx_blk->data.blktype = 0;
-		rx_blk->data.blkno = arg[1];
-
-	blk_common:
-		fmtx_drain();
-		fmtx_set_speed(arg[0]);
-		rx_blk->data.csum = 0x03  /* ETX is part of the checksum */
-			+ rx_blk->data.blktype
-			+ (uint8_t)rx_blk->data.blkno
-			+ (rx_blk->data.blkno >> 8);
-		return rx_data;
+		rx_blk_cnt = 1;
+		return setup_block(arg[0], arg[1]);
+
+	case 'F':
+		/* Send file */
+		/* &F filename,blocks[,divisor] */
+		rx_blk_cnt = arg[0];
+		setup_block(arg[1], -1);
+		rx_blk->data.hdr.divisor =
+			(arg[1] == CAS_DIVISOR_ABC80) ? 0 : arg[1];
+		memset(rx_blk->data.hdr.zero, 0, sizeof rx_blk->data.hdr.zero);
+		rx_blk->data.hdr.nblocks = rx_blk_cnt;
+
+		/* Checksum filename and divisor */
+		rx_blk->data.csum += checksum_range(rx_blk->data.data, 12);
+		rx_blk->data.csum += (uint8_t)rx_blk_cnt
+			+ (uint8_t)(rx_blk_cnt >> 8);
+
+		rx_blk->ready = true;
+		rx_blk = rx_blk->next;
+		return rx_blk_cnt ? rx_data : rx_idle;
+
 	default:
 		return rx_idle;	/* Unknown/unimplemented command */
 	}
@@ -215,20 +276,49 @@ void fmtx_recv_byte(uint8_t byte)
 	static uint16_t arg[4];
 
 	switch (rx_state) {
-	case rx_idle:
+	case rx_amp:
 		if (byte == '&')
-			rx_state = rx_amp;
+			rx_state = rx_cmd;
 		break;
-	case rx_amp:
+	case rx_cmd:
 		ctr = 0;
 		memset(arg, 0, sizeof arg);
 		cmd = byte;
-		if (byte <= ' ' || byte > '~')
-			rx_state = rx_idle;
+		if (byte == 'F') {
+			memset(rx_blk->data.hdr.filename, ' ',
+			       sizeof rx_blk->data.hdr.filename);
+			rx_state = rx_filename;
+		} else if (byte > ' ' && byte <= '~')
+			rx_state = rx_num;
 		else
-			rx_state = rx_cmd;
+			rx_state = rx_idle;
 		break;
-	case rx_cmd:
+	case rx_filename:
+		switch (byte) {
+		case '\n':
+			ctr = 0;
+			rx_state = do_cmd(cmd, arg);
+			break;
+		case '.':
+			memset(rx_blk->data.hdr.filename+8, ' ', 3);
+			ctr = 8;
+			break;
+		case ':':
+			ctr = 0;
+			break;
+		case ',':
+			rx_state = rx_num;
+			break;
+		default:
+			if (byte <= ' ' || byte > '~' || ctr >= 11)
+				break;
+			if (byte & 0x40)
+				byte &= ~0x20; /* Upper case */
+			rx_blk->data.hdr.filename[ctr++] = byte;
+			break;
+		}
+		break;
+	case rx_num:
 		if (byte == '\n') {
 			ctr = 0;
 			rx_state = do_cmd(cmd, arg);
@@ -247,7 +337,9 @@ void fmtx_recv_byte(uint8_t byte)
 		if (ctr == 253) {
 			rx_blk->ready = true;
 			rx_blk = rx_blk->next;
-			rx_state = rx_idle;
+			if (!--rx_blk_cnt) {
+				rx_state = rx_idle;
+			}
 			fmtx_enable();
 		}
 		break;

+ 14 - 2
USBCAS/usbcas.h

@@ -49,6 +49,14 @@
 #include <LUFA/Drivers/USB/USB.h>
 #include <LUFA/Platform/Platform.h>
 
+/* Cassette header block data */
+struct cas_hdr_block {
+	uint8_t filename[11];	    /* Mangled filename */
+	uint8_t divisor;	    /* Baudrate divisor */
+	uint8_t zero[238];	    /* Unused, must be zero */
+	uint16_t nblocks;	    /* Data block count (for CASDISK) */
+};
+
 /* Cassette block data format */
 struct cas_block_data {
 	uint8_t leadin[32];         /* 0x00 */
@@ -56,12 +64,16 @@ struct cas_block_data {
 	uint8_t stx;                /* 0x02 */
 	uint8_t blktype;            /* 0x00 for data, 0xff for filename */
 	uint16_t blkno;		    /* Block number (littleendian) */
-	uint8_t data[253];          /* Actual data */
+	union {
+		uint8_t data[253]; /* Actual data */
+		struct cas_hdr_block hdr;
+	};
 	uint8_t etx;                /* 0x03 */
-	uint16_t csum;		    /* Checksum */
+	uint16_t csum;		    /* Checksum (littleendian) */
 	uint8_t leadout[1];	    /* 0x00 */
 } __attribute__((packed));
 
+/* Cassette block plus metadata */
 struct cas_block {
 	volatile bool ready;	/* Filled with data */
 	unsigned int pause;	/* Post-block delay */