mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
The USB capture/replay interface now uses timestamps rather than intervals;
this makes the hardware considerably simpler and more reliable (as I don't need to spend time resetting the timers between pulses). Still doesn't help writes, though. Simplify and improve clock detection; add an abortive attempt at an FM decoder (turns out that the Brother doesn't use FM).
This commit is contained in:
@@ -2542,27 +2542,27 @@
|
||||
<CyGuid_ebc4f06d-207f-49c2-a540-72acf4adabc0 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFolderSerialize" version="3">
|
||||
<CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtBaseContainerSerialize" version="1">
|
||||
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="REPLAY_COUNTER" persistent="">
|
||||
<Hidden v="False" />
|
||||
<Hidden v="True" />
|
||||
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
|
||||
<CyGuid_0820c2e7-528d-4137-9a08-97257b946089 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemListSerialize" version="2">
|
||||
<dependencies>
|
||||
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
|
||||
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="REPLAY_COUNTER.c" persistent="Generated_Source\PSoC5\REPLAY_COUNTER.c">
|
||||
<Hidden v="False" />
|
||||
<Hidden v="True" />
|
||||
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
|
||||
<build_action v="SOURCE_C;CortexM3;;;" />
|
||||
<PropertyDeltas />
|
||||
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
|
||||
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
|
||||
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="REPLAY_COUNTER.h" persistent="Generated_Source\PSoC5\REPLAY_COUNTER.h">
|
||||
<Hidden v="False" />
|
||||
<Hidden v="True" />
|
||||
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
|
||||
<build_action v="HEADER;;;;" />
|
||||
<PropertyDeltas />
|
||||
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
|
||||
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
|
||||
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="REPLAY_COUNTER_PM.c" persistent="Generated_Source\PSoC5\REPLAY_COUNTER_PM.c">
|
||||
<Hidden v="False" />
|
||||
<Hidden v="True" />
|
||||
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
|
||||
<build_action v="SOURCE_C;CortexM3;;;" />
|
||||
<PropertyDeltas />
|
||||
@@ -2841,6 +2841,39 @@
|
||||
</CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8>
|
||||
<filters />
|
||||
</CyGuid_ebc4f06d-207f-49c2-a540-72acf4adabc0>
|
||||
<CyGuid_ebc4f06d-207f-49c2-a540-72acf4adabc0 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFolderSerialize" version="3">
|
||||
<CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtBaseContainerSerialize" version="1">
|
||||
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="REPLAY_TIMESTAMP" persistent="">
|
||||
<Hidden v="False" />
|
||||
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
|
||||
<CyGuid_0820c2e7-528d-4137-9a08-97257b946089 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemListSerialize" version="2">
|
||||
<dependencies>
|
||||
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
|
||||
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="REPLAY_TIMESTAMP.h" persistent="Generated_Source\PSoC5\REPLAY_TIMESTAMP.h">
|
||||
<Hidden v="False" />
|
||||
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
|
||||
<build_action v="HEADER;;;;" />
|
||||
<PropertyDeltas />
|
||||
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
|
||||
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
|
||||
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="REPLAY_TIMESTAMP.c" persistent="Generated_Source\PSoC5\REPLAY_TIMESTAMP.c">
|
||||
<Hidden v="False" />
|
||||
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
|
||||
<build_action v="SOURCE_C;CortexM3;;;" />
|
||||
<PropertyDeltas />
|
||||
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
|
||||
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
|
||||
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="REPLAY_TIMESTAMP_PM.c" persistent="Generated_Source\PSoC5\REPLAY_TIMESTAMP_PM.c">
|
||||
<Hidden v="False" />
|
||||
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
|
||||
<build_action v="SOURCE_C;CortexM3;;;" />
|
||||
<PropertyDeltas />
|
||||
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
|
||||
</dependencies>
|
||||
</CyGuid_0820c2e7-528d-4137-9a08-97257b946089>
|
||||
</CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8>
|
||||
<filters />
|
||||
</CyGuid_ebc4f06d-207f-49c2-a540-72acf4adabc0>
|
||||
</dependencies>
|
||||
</CyGuid_0820c2e7-528d-4137-9a08-97257b946089>
|
||||
</CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8>
|
||||
|
||||
Binary file not shown.
@@ -351,7 +351,7 @@ static void init_replay_dma(void)
|
||||
|
||||
CyDmaTdSetConfiguration(td[i], BUFFER_SIZE, td[nexti],
|
||||
CY_DMA_TD_INC_SRC_ADR | REPLAY_DMA__TD_TERMOUT_EN);
|
||||
CyDmaTdSetAddress(td[i], LO16((uint32)&dma_buffer[i]), LO16((uint32)REPLAY_COUNTER_COUNTER_LSB_PTR));
|
||||
CyDmaTdSetAddress(td[i], LO16((uint32)&dma_buffer[i]), LO16((uint32)REPLAY_TIMESTAMP_Control_PTR));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,8 +373,6 @@ static void cmd_write(struct write_frame* f)
|
||||
int packets = f->bytes_to_write / FRAME_SIZE;
|
||||
int count_read = 0;
|
||||
int count_written = 0;
|
||||
REPLAY_COUNTER_Start();
|
||||
REPLAY_COUNTER_WriteCounter(0);
|
||||
dma_writing_to_td = 0;
|
||||
dma_reading_from_td = -1;
|
||||
dma_underrun = false;
|
||||
@@ -468,7 +466,6 @@ static void cmd_write(struct write_frame* f)
|
||||
CyDmaChSetRequest(dma_channel, CY_DMA_CPU_TERM_CHAIN);
|
||||
while (CyDmaChGetRequest(dma_channel))
|
||||
;
|
||||
REPLAY_COUNTER_Stop();
|
||||
CyDmaChDisable(dma_channel);
|
||||
}
|
||||
|
||||
@@ -481,7 +478,6 @@ static void cmd_write(struct write_frame* f)
|
||||
print(" packets read\r");
|
||||
}
|
||||
|
||||
|
||||
DECLARE_REPLY_FRAME(struct write_reply_frame, F_FRAME_WRITE_REPLY);
|
||||
r.bytes_actually_written = count_written*FRAME_SIZE;
|
||||
|
||||
|
||||
1
Makefile
1
Makefile
@@ -12,6 +12,7 @@ SRCS = \
|
||||
cmd_read.c \
|
||||
cmd_write.c \
|
||||
cmd_mfmdecode.c \
|
||||
cmd_fmdecode.c \
|
||||
cmd_testpattern.c \
|
||||
cmd_fluxdump.c \
|
||||
|
||||
|
||||
256
cmd_fmdecode.c
Normal file
256
cmd_fmdecode.c
Normal file
@@ -0,0 +1,256 @@
|
||||
#include "globals.h"
|
||||
#include "sql.h"
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#define DEBOUNCE_TICKS 0x28
|
||||
|
||||
#define CLOCK_LOCK_BOOST 6 /* arbitrary */
|
||||
#define CLOCK_LOCK_DECAY 1 /* arbitrary */
|
||||
#define CLOCK_DETECTOR_AMPLITUDE_THRESHOLD 60 /* arbi4rary */
|
||||
#define CLOCK_ERROR_BOUNDS 0.50
|
||||
|
||||
static const char* inputfilename = NULL;
|
||||
static const char* outputfilename = NULL;
|
||||
static bool verbose = false;
|
||||
static sqlite3* indb;
|
||||
static sqlite3* outdb;
|
||||
|
||||
static const uint8_t* inputbuffer;
|
||||
static int inputlen;
|
||||
static int cursor;
|
||||
static int elapsed_ticks;
|
||||
|
||||
static int clock_period; /* mfm cell width */
|
||||
static int period0; /* lower limit for a short transition */
|
||||
static int period1; /* between short and long transitions */
|
||||
static int period2; /* upper limit for a long transition */
|
||||
|
||||
static uint8_t outputbuffer[5*1024];
|
||||
static int outputbufferpos;
|
||||
static uint8_t fifo = 0;
|
||||
static int bitcount = 0;
|
||||
static bool phaselocked = false;
|
||||
|
||||
static int thislength = 0;
|
||||
static int nextlength = 0;
|
||||
|
||||
static void syntax_error(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"syntax: fluxclient fmdecode <options>:\n"
|
||||
" -i <filename> input filename (.flux)\n"
|
||||
" -o <filename> output filename (.rec)\n"
|
||||
" -v verbose decoding\n"
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static char* const* parse_options(char* const* argv)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
switch (getopt(countargs(argv), argv, "+i:o:v"))
|
||||
{
|
||||
case -1:
|
||||
return argv + optind - 1;
|
||||
|
||||
case 'i':
|
||||
inputfilename = optarg;
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
outputfilename = optarg;
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
verbose = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
syntax_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void close_files(void)
|
||||
{
|
||||
if (indb)
|
||||
sqlite3_close(indb);
|
||||
if (outdb)
|
||||
sqlite3_close(outdb);
|
||||
}
|
||||
|
||||
static void open_files(void)
|
||||
{
|
||||
indb = sql_open(inputfilename, SQLITE_OPEN_READONLY);
|
||||
outdb = sql_open(outputfilename, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
|
||||
sql_prepare_record(outdb);
|
||||
atexit(close_files);
|
||||
}
|
||||
|
||||
static void queue_bit(bool bit)
|
||||
{
|
||||
fifo <<= 1;
|
||||
fifo |= bit;
|
||||
bitcount++;
|
||||
}
|
||||
|
||||
static bool read_bit(void)
|
||||
{
|
||||
/*
|
||||
* FM is incredibly stupid. Every cell has a pulse at the beginning;
|
||||
* cells which contain a 1 have a pulse in the middle; cells which don't
|
||||
* have a 0. Therefore we have these two states:
|
||||
*
|
||||
* Data: 0 1
|
||||
* Signal: 10 11
|
||||
*
|
||||
* Therefore, a *long* interval represents a 0, and two *short* intervals
|
||||
* represent a 1.
|
||||
*/
|
||||
|
||||
if (cursor >= inputlen)
|
||||
return false;
|
||||
uint8_t t = inputbuffer[cursor++];
|
||||
elapsed_ticks += t;
|
||||
|
||||
if ((t < period0) || (t > period2))
|
||||
{
|
||||
/* Garbage data: our clock's wrong. */
|
||||
phaselocked = false;
|
||||
return false;
|
||||
}
|
||||
else if (t < period1)
|
||||
{
|
||||
/* That was the first short interval of a 1: now consume any additional
|
||||
* until we reach the clock pulse again. */
|
||||
if (cursor >= inputlen)
|
||||
return false;
|
||||
while (t < period1)
|
||||
{
|
||||
if (cursor >= inputlen)
|
||||
return false;
|
||||
uint8_t tt = inputbuffer[cursor++];
|
||||
elapsed_ticks += tt;
|
||||
t += tt;
|
||||
}
|
||||
|
||||
/* If the clock pulse didn't show up, give up. */
|
||||
if (t > period2)
|
||||
{
|
||||
phaselocked = false;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* A long transition. */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t read_byte(void)
|
||||
{
|
||||
while (phaselocked && (bitcount < 8))
|
||||
queue_bit(read_bit());
|
||||
bitcount = 0;
|
||||
return fifo;
|
||||
}
|
||||
|
||||
static void log_record(char type)
|
||||
{
|
||||
if (verbose)
|
||||
printf("\n % 8.3fms [0x%05x]: ",
|
||||
(double)elapsed_ticks / (TICKS_PER_US*1000.0), cursor);
|
||||
putchar(type);
|
||||
}
|
||||
|
||||
static bool process_byte(uint8_t b)
|
||||
{
|
||||
outputbuffer[outputbufferpos++] = b;
|
||||
if (outputbufferpos == sizeof(outputbuffer))
|
||||
goto abandon_record;
|
||||
|
||||
error("unimplemented --- can't handle FM yet");
|
||||
|
||||
abandon_record:
|
||||
if (verbose && (outputbufferpos > 4))
|
||||
printf(" misread");
|
||||
phaselocked = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void decode_track_cb(int track, int side, const struct fluxmap* fluxmap)
|
||||
{
|
||||
printf("Track %02d side %d: ", track, side);
|
||||
|
||||
inputbuffer = fluxmap->intervals;
|
||||
inputlen = fluxmap->bytes;
|
||||
cursor = 0;
|
||||
elapsed_ticks = 0;
|
||||
int record = 0;
|
||||
|
||||
while (cursor < inputlen)
|
||||
{
|
||||
if (!phaselocked)
|
||||
{
|
||||
while (cursor < inputlen)
|
||||
{
|
||||
clock_period = fluxmap_seek_clock(fluxmap, &cursor, 16);
|
||||
|
||||
/*
|
||||
* Okay, this looks good. We'll assume this is clock/2 --- 250kHz
|
||||
* for HD floppies; this is one long transition.
|
||||
*/
|
||||
|
||||
double short_time = clock_period / 2.0;
|
||||
double long_time = short_time * 2.0;
|
||||
|
||||
period0 = short_time - short_time * CLOCK_ERROR_BOUNDS;
|
||||
period1 = (short_time + long_time) / 2.0;
|
||||
period2 = long_time + long_time * CLOCK_ERROR_BOUNDS;
|
||||
phaselocked = true;
|
||||
|
||||
while (phaselocked && (cursor < inputlen))
|
||||
{
|
||||
if (read_bit())
|
||||
goto found_byte;
|
||||
}
|
||||
}
|
||||
found_byte:;
|
||||
fifo = 1;
|
||||
bitcount = 1;
|
||||
outputbufferpos = 0;
|
||||
}
|
||||
|
||||
if (process_byte(read_byte()))
|
||||
{
|
||||
sql_write_record(outdb, track, side, record, outputbuffer, outputbufferpos);
|
||||
record++;
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
printf("\n ");
|
||||
printf(" = %d records\n", record);
|
||||
}
|
||||
|
||||
void cmd_fmdecode(char* const* argv)
|
||||
{
|
||||
argv = parse_options(argv);
|
||||
if (countargs(argv) != 1)
|
||||
syntax_error();
|
||||
if (!inputfilename)
|
||||
error("you must supply a filename to read from");
|
||||
if (!outputfilename)
|
||||
error("you must supply a filename to write to");
|
||||
|
||||
open_files();
|
||||
sql_stmt(outdb, "BEGIN");
|
||||
sql_stmt(outdb, "DELETE FROM records");
|
||||
sql_for_all_flux_data(indb, decode_track_cb);
|
||||
sql_stmt(outdb, "COMMIT");
|
||||
}
|
||||
|
||||
125
cmd_mfmdecode.c
125
cmd_mfmdecode.c
@@ -100,90 +100,6 @@ static void open_files(void)
|
||||
atexit(close_files);
|
||||
}
|
||||
|
||||
static void find_clock(void)
|
||||
{
|
||||
/* Somewhere in the bitstream there'll be a sync sequence of 16 0 bytes
|
||||
* followed by a special index byte, beginning with a 1 bit.
|
||||
*
|
||||
* These zeroes will be encoded as 10 10 10 10..., so forming a nice
|
||||
* simple signal that should be easy to detect. This routine scans the
|
||||
* bitstream until it finds one of these windows, and sets the clock
|
||||
* accordingly. Remember that the routine can be easily spoofed by bad
|
||||
* data, so you need to check for the marker byte afterwards.
|
||||
*
|
||||
* ...btw, the standard fill byte is 0x4e:
|
||||
*
|
||||
* 0 1 0 0 1 1 1 0
|
||||
* 10 01 00 10 01 01 01 00
|
||||
*
|
||||
* That's four medium transitions vs two short ones. So we know we're going
|
||||
* to miscalculate the clock the first time round.
|
||||
*/
|
||||
|
||||
uint32_t buckets[256] = {};
|
||||
for (;;)
|
||||
{
|
||||
if (cursor >= inputlen)
|
||||
return;
|
||||
uint8_t data = inputbuffer[cursor++];
|
||||
elapsed_ticks += data;
|
||||
if (data >= 2)
|
||||
buckets[data-2] += CLOCK_LOCK_BOOST * 1 / 3;
|
||||
if (data >= 1)
|
||||
buckets[data-1] += CLOCK_LOCK_BOOST * 2 / 3;
|
||||
buckets[data] += CLOCK_LOCK_BOOST;
|
||||
if (data <= 0x7e)
|
||||
buckets[data+1] += CLOCK_LOCK_BOOST * 2 / 3;
|
||||
if (data <= 0x7d)
|
||||
buckets[data+2] += CLOCK_LOCK_BOOST * 1 / 3;
|
||||
|
||||
/* The bucket list slowly decays. */
|
||||
|
||||
for (int i=0; i<128; i++)
|
||||
if (buckets[i] > 0)
|
||||
buckets[i] -= CLOCK_LOCK_DECAY;
|
||||
|
||||
/*
|
||||
* After 10 bytes, we'll have seen 80 bits. So there should be a nice
|
||||
* sharp peak in our distribution. The amplitude is chosen by trial and
|
||||
* error. *
|
||||
*/
|
||||
|
||||
uint32_t maxvalue = 0;
|
||||
clock_period = 0;
|
||||
for (int i=0; i<128; i++)
|
||||
{
|
||||
if (buckets[i] > maxvalue)
|
||||
{
|
||||
maxvalue = buckets[i];
|
||||
clock_period = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (maxvalue > CLOCK_DETECTOR_AMPLITUDE_THRESHOLD)
|
||||
{
|
||||
/*
|
||||
* Okay, this looks good. We'll assume this is clock/2 --- 250kHz
|
||||
* for HD floppies; this is one short transition. We're also going
|
||||
* to assume that this was a 0 bit and set the phase, god help us.
|
||||
*/
|
||||
|
||||
double short_time = clock_period;
|
||||
double medium_time = short_time * 1.5;
|
||||
double long_time = short_time * 2.0;
|
||||
|
||||
period0 = short_time - short_time * CLOCK_ERROR_BOUNDS;
|
||||
period1 = (short_time + medium_time) / 2.0;
|
||||
period2 = (medium_time + long_time) / 2.0;
|
||||
period3 = long_time + long_time * CLOCK_ERROR_BOUNDS;
|
||||
phase = true;
|
||||
phaselocked = true;
|
||||
has_queued = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void queue_bit(bool bit)
|
||||
{
|
||||
fifo <<= 1;
|
||||
@@ -301,11 +217,13 @@ static bool process_byte(uint8_t b)
|
||||
if (outputbufferpos == sizeof(outputbuffer))
|
||||
goto abandon_record;
|
||||
|
||||
#if 0
|
||||
if (outputbufferpos < 4)
|
||||
{
|
||||
if ((b != 0xA1) && (b != 0xC2))
|
||||
goto abandon_record;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (outputbufferpos == 4)
|
||||
{
|
||||
@@ -387,7 +305,44 @@ static void decode_track_cb(int track, int side, const struct fluxmap* fluxmap)
|
||||
{
|
||||
while (cursor < inputlen)
|
||||
{
|
||||
find_clock();
|
||||
/* Somewhere in the bitstream there'll be a sync sequence of 16 0 bytes
|
||||
* followed by a special index byte, beginning with a 1 bit.
|
||||
*
|
||||
* These zeroes will be encoded as 10 10 10 10..., so forming a nice
|
||||
* simple signal that should be easy to detect. This routine scans the
|
||||
* bitstream until it finds one of these windows, and sets the clock
|
||||
* accordingly. Remember that the routine can be easily spoofed by bad
|
||||
* data, so you need to check for the marker byte afterwards.
|
||||
*
|
||||
* ...btw, the standard fill byte is 0x4e:
|
||||
*
|
||||
* 0 1 0 0 1 1 1 0
|
||||
* 10 01 00 10 01 01 01 00
|
||||
*
|
||||
* That's four medium transitions vs two short ones. So we know we're going
|
||||
* to miscalculate the clock the first time round.
|
||||
*/
|
||||
|
||||
clock_period = fluxmap_seek_clock(fluxmap, &cursor, 16);
|
||||
|
||||
/*
|
||||
* Okay, this looks good. We'll assume this is clock/2 --- 250kHz
|
||||
* for HD floppies; this is one short transition. We're also going
|
||||
* to assume that this was a 0 bit and set the phase, god help us.
|
||||
*/
|
||||
|
||||
double short_time = clock_period;
|
||||
double medium_time = short_time * 1.5;
|
||||
double long_time = short_time * 2.0;
|
||||
|
||||
period0 = short_time - short_time * CLOCK_ERROR_BOUNDS;
|
||||
period1 = (short_time + medium_time) / 2.0;
|
||||
period2 = (medium_time + long_time) / 2.0;
|
||||
period3 = long_time + long_time * CLOCK_ERROR_BOUNDS;
|
||||
phase = true;
|
||||
phaselocked = true;
|
||||
has_queued = false;
|
||||
|
||||
while (phaselocked && (cursor < inputlen))
|
||||
{
|
||||
if (read_bit())
|
||||
|
||||
32
fluxmap.c
32
fluxmap.c
@@ -37,3 +37,35 @@ void fluxmap_append_intervals(struct fluxmap* fluxmap, const uint8_t* intervals,
|
||||
|
||||
fluxmap->length_us = fluxmap->length_ticks / (TICK_FREQUENCY / 1000000);
|
||||
}
|
||||
|
||||
int fluxmap_seek_clock(const struct fluxmap* fluxmap, int* cursor, int pulses)
|
||||
{
|
||||
int count = 0;
|
||||
int value = 0;
|
||||
|
||||
while (*cursor < fluxmap->bytes)
|
||||
{
|
||||
uint8_t t = fluxmap->intervals[(*cursor)++];
|
||||
if (value == 0)
|
||||
value = t;
|
||||
else
|
||||
{
|
||||
count++;
|
||||
int delta = abs(value - t) / 3 + 1;
|
||||
if (delta > (value / 8))
|
||||
count = value = 0;
|
||||
else
|
||||
{
|
||||
if (t < value)
|
||||
value -= delta;
|
||||
else
|
||||
value += delta;
|
||||
}
|
||||
}
|
||||
|
||||
if (count >= pulses)
|
||||
return value;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ extern struct fluxmap* create_fluxmap(void);
|
||||
extern void free_fluxmap(struct fluxmap* fluxmap);
|
||||
extern void fluxmap_clear(struct fluxmap* fluxmap);
|
||||
extern void fluxmap_append_intervals(struct fluxmap* fluxmap, const uint8_t* intervals, int count);
|
||||
extern int fluxmap_seek_clock(const struct fluxmap* fluxmap, int* cursor, int pulses);
|
||||
|
||||
extern void error(const char* message, ...);
|
||||
extern double gettime(void);
|
||||
@@ -62,6 +63,7 @@ extern void cmd_usbbench(char* const* argv);
|
||||
extern void cmd_read(char* const* argv);
|
||||
extern void cmd_write(char* const* argv);
|
||||
extern void cmd_mfmdecode(char* const* argv);
|
||||
extern void cmd_fmdecode(char* const* argv);
|
||||
extern void cmd_testpattern(char* const* argv);
|
||||
extern void cmd_fluxdump(char* const* argv);
|
||||
|
||||
|
||||
2
main.c
2
main.c
@@ -76,6 +76,8 @@ int main(int argc, char* const* argv)
|
||||
cmd_write(argv);
|
||||
else if (strcmp(argv[0], "mfmdecode") == 0)
|
||||
cmd_mfmdecode(argv);
|
||||
else if (strcmp(argv[0], "fmdecode") == 0)
|
||||
cmd_fmdecode(argv);
|
||||
else if (strcmp(argv[0], "testpattern") == 0)
|
||||
cmd_testpattern(argv);
|
||||
else if (strcmp(argv[0], "fluxdump") == 0)
|
||||
|
||||
24
usb.c
24
usb.c
@@ -147,6 +147,17 @@ struct fluxmap* usb_read(int side)
|
||||
|
||||
uint8_t buffer[1024*1024];
|
||||
int len = large_bulk_transfer(FLUXENGINE_DATA_IN_EP, buffer, sizeof(buffer));
|
||||
|
||||
/* Convert from absolute timestamps to intervals. */
|
||||
|
||||
uint8_t now = 0;
|
||||
for (int i=0; i<len; i++)
|
||||
{
|
||||
uint8_t t = buffer[i];
|
||||
buffer[i] = t - now;
|
||||
now = t;
|
||||
}
|
||||
|
||||
fluxmap_append_intervals(fluxmap, buffer, len);
|
||||
|
||||
await_reply(F_FRAME_READ_REPLY);
|
||||
@@ -157,6 +168,17 @@ struct fluxmap* usb_read(int side)
|
||||
int usb_write(int side, struct fluxmap* fluxmap)
|
||||
{
|
||||
int safelen = fluxmap->bytes & ~(FRAME_SIZE-1);
|
||||
|
||||
/* Convert from intervals to absolute timestamps. */
|
||||
|
||||
uint8_t buffer[1024*1024];
|
||||
uint8_t clock = 0;
|
||||
for (int i=0; i<safelen; i++)
|
||||
{
|
||||
clock += fluxmap->intervals[i];
|
||||
buffer[i] = clock;
|
||||
}
|
||||
|
||||
struct write_frame f = {
|
||||
.f = { .type = F_FRAME_WRITE_CMD, .size = sizeof(f) },
|
||||
.side = side,
|
||||
@@ -164,7 +186,7 @@ int usb_write(int side, struct fluxmap* fluxmap)
|
||||
};
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
|
||||
large_bulk_transfer(FLUXENGINE_DATA_OUT_EP, fluxmap->intervals, safelen);
|
||||
large_bulk_transfer(FLUXENGINE_DATA_OUT_EP, buffer, safelen);
|
||||
|
||||
struct write_reply_frame* r = await_reply(F_FRAME_WRITE_REPLY);
|
||||
return r->bytes_actually_written;
|
||||
|
||||
Reference in New Issue
Block a user