|
@@ -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;
|