summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2012-08-12 16:48:33 (GMT)
committerH. Peter Anvin <hpa@zytor.com>2012-08-12 16:48:33 (GMT)
commit9d7cdd8ed9e4645548b87b7224dc0356e2d8cf13 (patch)
tree128eb3e7e29790ef626e4fa3f752278331ea0289
downloadmfmdecode-9d7cdd8ed9e4645548b87b7224dc0356e2d8cf13.zip
mfmdecode-9d7cdd8ed9e4645548b87b7224dc0356e2d8cf13.tar.gz
mfmdecode-9d7cdd8ed9e4645548b87b7224dc0356e2d8cf13.tar.bz2
mfmdecode-9d7cdd8ed9e4645548b87b7224dc0356e2d8cf13.tar.xz
Initial version
Program to decode ABC80 cassette images
-rw-r--r--mfmdecode.c262
1 files changed, 262 insertions, 0 deletions
diff --git a/mfmdecode.c b/mfmdecode.c
new file mode 100644
index 0000000..e092bdc
--- /dev/null
+++ b/mfmdecode.c
@@ -0,0 +1,262 @@
+/*
+ * Decode ABC80 cassette files
+ *
+ * Convert to 44100 Hz single channel 16-bit with appropriate endianness
+ * and normalized gain first:
+ *
+ * sox muzak.wav --endian little -c 1 -s muzak.raw
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <inttypes.h>
+
+#define THRESH 14300
+static unsigned int xtime = 44100/700; /* Samples/baud */
+
+static const char my_tolower[256] =
+ "\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017"
+ "\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
+ " !\"#$%&'()*+,-./0123456789:;<=>?"
+ "\351abcdefghijklmnopqrstuvwxyz\344\366\345\374_"
+ "\351abcdefghijklmnopqrstuvwxyz\344\366\345\374\377"
+ "\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217"
+ "\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237"
+ "\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257"
+ "\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277"
+ "\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317"
+ "\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337"
+ "\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357"
+ "\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377";
+
+static void unmangle_filename(char *out, const uint8_t *in)
+{
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ if (*in != ' ')
+ *out++ = my_tolower[*in];
+ in++;
+ }
+
+ if (memcmp(in, " ", 3)) {
+ *out++ = '.';
+ for (i = 0; i < 3; i++) {
+ if (*in != ' ')
+ *out++ = my_tolower[*in];
+ in++;
+ }
+ }
+
+ *out = '\0';
+}
+
+static void output_block(uint8_t *buf, uint8_t *csum)
+{
+ uint16_t csum1, csum2;
+ int i;
+ static FILE *f;
+ static uint16_t block;
+ static char filename[12];
+ int err = 0;
+
+ if (!buf) {
+ err = 1; /* Notification of a bad block */
+ } else {
+ csum2 = csum[0] + (csum[1] << 8);
+
+ csum1 = 0x03; /* Checksum includes ETX */
+ for (i = 0; i < 256; i++)
+ csum1 += buf[i];
+
+ if (csum1 == csum2) {
+ if (!memcmp(buf, "\xff\xff\xff", 3)) {
+ if (f)
+ fclose(f);
+
+ unmangle_filename(filename, buf+3);
+ f = fopen(filename, "wb");
+ if (!f) {
+ perror(filename);
+ exit(1);
+ }
+ block = 0;
+ printf("File: %s\n", filename);
+ } else if (!f || buf[0] != 0x00 ||
+ (buf[1] + (buf[2] << 8)) != block) {
+ printf("Bad block ID: %02x %02x %02x\n",
+ buf[0], buf[1], buf[2]);
+ err = 1;
+ } else {
+ block++;
+ fwrite(buf+3, 1, 253, f);
+ }
+ } else {
+ printf("Checksum error: got %04x data %04x\n",
+ csum2, csum1);
+ err = 1;
+ }
+ }
+
+ if (err && f) {
+ fclose(f);
+ remove(filename);
+ f = NULL;
+ }
+}
+
+static void output_bit(unsigned int bit)
+{
+ enum state {
+ st_leader,
+ st_sync,
+ st_stx,
+ st_data,
+ st_etx,
+ st_csum
+ };
+ static enum state st = st_leader;
+ static unsigned int ctr = 0;
+ static unsigned int bits = 0;
+ static uint8_t data = 0;
+ static uint8_t buf[256];
+ static uint8_t csum[2];
+
+ data = (data >> 1) + (bit << 7); /* Bigendian bit order... */
+ bits++;
+
+ switch (st) {
+ case st_leader:
+ ctr++;
+ if (bit == 1)
+ ctr = 0;
+ if (ctr > 128) /* After 128 zeroes start looking for sync */
+ st = st_sync;
+ break;
+
+ case st_sync:
+ if (data == 0x16) {
+ st = st_stx; /* Sync acquired */
+ bits = 0;
+ }
+ break;
+
+ case st_stx:
+ if (bits < 8)
+ break;
+ bits = 0;
+ ctr = 0;
+ if (data == 0x02) {
+ st = st_data;
+ } else if (data == 0x16) {
+ /* Got another SYNC */
+ } else {
+ printf("Got %02x when expecting SYNC or STX\n", data);
+ output_block(NULL, NULL);
+ st = st_leader; /* ERROR */
+ }
+ break;
+
+ case st_data:
+ if (bits < 8)
+ break;
+ bits = 0;
+ buf[ctr++] = data;
+ if (ctr == 256)
+ st = st_etx;
+ break;
+
+ case st_etx:
+ if (bits < 8)
+ break;
+ bits = 0;
+ ctr = 0;
+ if (data == 0x03)
+ st = st_csum;
+ else {
+ printf("Got %02x when expecting ETX\n", data);
+ output_block(NULL, NULL);
+ st = st_leader; /* ERROR */
+ }
+ break;
+
+ case st_csum:
+ if (bits < 8)
+ break;
+ bits = 0;
+ csum[ctr++] = data;
+ if (ctr == 2) {
+ output_block(buf, csum);
+ ctr = 0;
+ st = st_leader;
+ }
+ break;
+ }
+}
+
+static void process_flank(unsigned int time)
+{
+ static unsigned int since_start = 0;
+ static unsigned int captured = 0;
+
+ since_start += time;
+
+ if (since_start < xtime/4) {
+ printf("Quick flank: %u:%u\n", since_start, xtime);
+ } else if (since_start < xtime*3/4) {
+ captured = 1;
+ } else if (since_start < xtime*5/4) {
+ output_bit(captured);
+ since_start = captured = 0;
+ } else {
+ printf("Slow flank: %u:%u\n", since_start, xtime);
+ since_start = 0;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ FILE *f;
+ int16_t samp, osamp;
+ double v;
+ unsigned int state;
+ unsigned int time, ptime;
+ const double k = exp(-10000.0/44100);
+
+ f = fopen(argv[1], "rb");
+
+ state = time = ptime = 0;
+ samp = osamp = 0;
+ v = 0.0;
+
+ while (fread(&samp, 1, 2, f) == 2) {
+ /* Emulate high pass filter in ABC80 */
+#if 0
+ v = (v * k) + (samp - osamp);
+#else
+ v = samp;
+#endif
+ osamp = samp;
+
+ if (v >= THRESH) {
+ if (!state) {
+ state = 1;
+ process_flank(time - ptime);
+ ptime = time;
+ }
+ } else if (v <= -THRESH) {
+ if (state) {
+ state = 0;
+ process_flank(time - ptime);
+ ptime = time;
+ }
+ }
+ time++;
+ }
+
+ fclose(f);
+
+ return 0;
+}