mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Refactor the GreaseWeazle converter to allow it to be tested.
This commit is contained in:
144
lib/usb/greaseweazle.cc
Normal file
144
lib/usb/greaseweazle.cc
Normal file
@@ -0,0 +1,144 @@
|
||||
#include "globals.h"
|
||||
#include "usb.h"
|
||||
#include "protocol.h"
|
||||
#include "bytes.h"
|
||||
#include "fmt/format.h"
|
||||
#include "greaseweazle.h"
|
||||
|
||||
Bytes fluxEngineToGreaseWeazle(const Bytes& fldata, nanoseconds_t clock)
|
||||
{
|
||||
Bytes gwdata;
|
||||
ByteWriter bw(gwdata);
|
||||
ByteReader br(fldata);
|
||||
uint32_t ticks_fl = 0;
|
||||
uint32_t ticks_gw = 0;
|
||||
|
||||
auto write_28 = [&](uint32_t val) {
|
||||
bw.write_8(1 | (val<<1) & 0xff);
|
||||
bw.write_8(1 | (val>>6) & 0xff);
|
||||
bw.write_8(1 | (val>>13) & 0xff);
|
||||
bw.write_8(1 | (val>>20) & 0xff);
|
||||
};
|
||||
|
||||
while (!br.eof())
|
||||
{
|
||||
uint8_t b = br.read_8();
|
||||
ticks_fl += b & 0x3f;
|
||||
if (b & F_BIT_PULSE)
|
||||
{
|
||||
uint32_t newticks_gw = ticks_fl * NS_PER_TICK / clock;
|
||||
uint32_t delta = newticks_gw - ticks_gw;
|
||||
if (delta < 250)
|
||||
bw.write_8(delta);
|
||||
else
|
||||
{
|
||||
int high = (delta-250) / 255;
|
||||
if (high < 5)
|
||||
{
|
||||
bw.write_8(250 + high);
|
||||
bw.write_8(1 + (delta-250) % 255);
|
||||
}
|
||||
else
|
||||
{
|
||||
bw.write_8(255);
|
||||
bw.write_8(FLUXOP_SPACE);
|
||||
write_28(delta - 249);
|
||||
bw.write_8(249);
|
||||
}
|
||||
}
|
||||
ticks_gw = newticks_gw;
|
||||
}
|
||||
}
|
||||
bw.write_8(0); /* end of stream */
|
||||
return gwdata;
|
||||
}
|
||||
|
||||
Bytes greaseWeazleToFluxEngine(const Bytes& gwdata, nanoseconds_t clock)
|
||||
{
|
||||
Bytes fldata;
|
||||
ByteReader br(gwdata);
|
||||
ByteWriter bw(fldata);
|
||||
|
||||
auto read_28 = [&]() {
|
||||
return ((br.read_8() & 0xfe) >> 1)
|
||||
| ((br.read_8() & 0xfe) << 6)
|
||||
| ((br.read_8() & 0xfe) << 13)
|
||||
| ((br.read_8() & 0xfe) << 20);
|
||||
};
|
||||
|
||||
uint32_t ticks_gw = 0;
|
||||
uint32_t lastevent_fl = 0;
|
||||
uint32_t index_gw = ~0;
|
||||
|
||||
while (!br.eof())
|
||||
{
|
||||
uint8_t b = br.read_8();
|
||||
if (!b)
|
||||
break;
|
||||
|
||||
uint8_t event = 0;
|
||||
if (b == 255)
|
||||
{
|
||||
switch (br.read_8())
|
||||
{
|
||||
case FLUXOP_INDEX:
|
||||
index_gw = ticks_gw + read_28();
|
||||
break;
|
||||
|
||||
case FLUXOP_SPACE:
|
||||
ticks_gw += read_28();
|
||||
break;
|
||||
|
||||
default:
|
||||
Error() << "bad opcode in GreaseWeazle stream";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (b < 250)
|
||||
ticks_gw += b;
|
||||
else
|
||||
{
|
||||
int delta = 250 + (b-250)*255 + br.read_8() - 1;
|
||||
ticks_gw += delta;
|
||||
}
|
||||
event = F_BIT_PULSE;
|
||||
}
|
||||
|
||||
if (event)
|
||||
{
|
||||
uint32_t index_fl = (index_gw * clock) / NS_PER_TICK;
|
||||
uint32_t ticks_fl = (ticks_gw * clock) / NS_PER_TICK;
|
||||
if (index_gw != ~0)
|
||||
{
|
||||
if (index_fl < ticks_fl)
|
||||
{
|
||||
uint32_t delta_fl = index_fl - lastevent_fl;
|
||||
while (delta_fl > 0x3f)
|
||||
{
|
||||
bw.write_8(0x3f);
|
||||
delta_fl -= 0x3f;
|
||||
}
|
||||
bw.write_8(delta_fl | F_BIT_INDEX);
|
||||
lastevent_fl = index_fl;
|
||||
index_gw = ~0;
|
||||
}
|
||||
else if (index_fl == ticks_fl)
|
||||
event |= F_BIT_INDEX;
|
||||
}
|
||||
|
||||
uint32_t delta_fl = ticks_fl - lastevent_fl;
|
||||
while (delta_fl > 0x3f)
|
||||
{
|
||||
bw.write_8(0x3f);
|
||||
delta_fl -= 0x3f;
|
||||
}
|
||||
bw.write_8(delta_fl | event);
|
||||
lastevent_fl = ticks_fl;
|
||||
}
|
||||
}
|
||||
|
||||
return fldata;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
|
||||
#define GREASEWEAZLE_VERSION 22
|
||||
|
||||
extern Bytes fluxEngineToGreaseWeazle(const Bytes& fldata, nanoseconds_t clock);
|
||||
extern Bytes greaseWeazleToFluxEngine(const Bytes& gwdata, nanoseconds_t clock);
|
||||
|
||||
/* Copied from https://github.com/keirf/Greaseweazle/blob/master/inc/cdc_acm_protocol.h. */
|
||||
|
||||
/*
|
||||
|
||||
@@ -302,133 +302,23 @@ public:
|
||||
|
||||
Bytes buffer;
|
||||
ByteWriter bw(buffer);
|
||||
for (;;)
|
||||
{
|
||||
uint32_t ticks_gw = 0;
|
||||
uint32_t lastevent_fl = 0;
|
||||
uint32_t index_gw = ~0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
uint8_t b = read_byte();
|
||||
if (!b)
|
||||
break;
|
||||
|
||||
uint8_t event = 0;
|
||||
if (b == 255)
|
||||
{
|
||||
switch (read_byte())
|
||||
{
|
||||
case FLUXOP_INDEX:
|
||||
index_gw = ticks_gw + read_28();
|
||||
break;
|
||||
|
||||
case FLUXOP_SPACE:
|
||||
ticks_gw += read_28();
|
||||
break;
|
||||
|
||||
default:
|
||||
Error() << "bad opcode in GreaseWeazle stream";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (b < 250)
|
||||
ticks_gw += b;
|
||||
else
|
||||
{
|
||||
int delta = 250 + (b-250)*255 + read_byte() - 1;
|
||||
ticks_gw += delta;
|
||||
}
|
||||
event = F_BIT_PULSE;
|
||||
}
|
||||
|
||||
if (event)
|
||||
{
|
||||
uint32_t index_fl = (index_gw * _clock) / NS_PER_TICK;
|
||||
uint32_t ticks_fl = (ticks_gw * _clock) / NS_PER_TICK;
|
||||
if (index_gw != ~0)
|
||||
{
|
||||
if (index_fl < ticks_fl)
|
||||
{
|
||||
uint32_t delta_fl = index_fl - lastevent_fl;
|
||||
while (delta_fl > 0x3f)
|
||||
{
|
||||
bw.write_8(0x3f);
|
||||
delta_fl -= 0x3f;
|
||||
}
|
||||
bw.write_8(delta_fl | F_BIT_INDEX);
|
||||
lastevent_fl = index_fl;
|
||||
index_gw = ~0;
|
||||
}
|
||||
else if (index_fl == ticks_fl)
|
||||
event |= F_BIT_INDEX;
|
||||
}
|
||||
|
||||
uint32_t delta_fl = ticks_fl - lastevent_fl;
|
||||
while (delta_fl > 0x3f)
|
||||
{
|
||||
bw.write_8(0x3f);
|
||||
delta_fl -= 0x3f;
|
||||
}
|
||||
bw.write_8(delta_fl | event);
|
||||
lastevent_fl = ticks_fl;
|
||||
}
|
||||
}
|
||||
uint8_t b = read_byte();
|
||||
if (!b)
|
||||
break;
|
||||
bw.write_8(b);
|
||||
}
|
||||
|
||||
do_command({ CMD_GET_FLUX_STATUS, 2 });
|
||||
return buffer;
|
||||
return greaseWeazleToFluxEngine(buffer, _clock);
|
||||
}
|
||||
|
||||
void write(int side, const Bytes& fldata)
|
||||
{
|
||||
Bytes gwdata;
|
||||
ByteWriter bw(gwdata);
|
||||
ByteReader br(fldata);
|
||||
uint32_t ticks_fl = 0;
|
||||
uint32_t ticks_gw = 0;
|
||||
|
||||
auto write_28 = [&](uint32_t val) {
|
||||
bw.write_8(1 | (val<<1) & 255);
|
||||
bw.write_8(1 | (val>>6) & 255);
|
||||
bw.write_8(1 | (val>>13) & 255);
|
||||
bw.write_8(1 | (val>>20) & 255);
|
||||
};
|
||||
|
||||
while (!br.eof())
|
||||
{
|
||||
uint8_t b = br.read_8();
|
||||
ticks_fl += b & 0x3f;
|
||||
if (b & F_BIT_PULSE)
|
||||
{
|
||||
uint32_t newticks_gw = ticks_fl * NS_PER_TICK / _clock;
|
||||
uint32_t delta = newticks_gw - ticks_gw;
|
||||
if (delta < 250)
|
||||
bw.write_8(delta);
|
||||
else
|
||||
{
|
||||
int high = (delta-250) / 255;
|
||||
if (high < 5)
|
||||
{
|
||||
bw.write_8(250 + high);
|
||||
bw.write_8(1 + (delta-250) % 255);
|
||||
}
|
||||
else
|
||||
{
|
||||
bw.write_8(255);
|
||||
bw.write_8(FLUXOP_SPACE);
|
||||
write_28(delta - 249);
|
||||
bw.write_8(249);
|
||||
}
|
||||
}
|
||||
ticks_gw = newticks_gw;
|
||||
}
|
||||
}
|
||||
bw.write_8(0); /* end of stream */
|
||||
|
||||
do_command({ CMD_HEAD, 3, (uint8_t)side });
|
||||
do_command({ CMD_WRITE_FLUX, 3, 1 });
|
||||
write_bytes(gwdata);
|
||||
write_bytes(fluxEngineToGreaseWeazle(fldata, _clock));
|
||||
read_byte(); /* synchronise */
|
||||
|
||||
do_command({ CMD_GET_FLUX_STATUS, 2 });
|
||||
|
||||
@@ -199,6 +199,7 @@ buildlibrary libbackend.a \
|
||||
lib/fluxsource/streamfluxsource.cc \
|
||||
lib/usb/usb.cc \
|
||||
lib/usb/fluxengineusb.cc \
|
||||
lib/usb/greaseweazle.cc \
|
||||
lib/usb/greaseweazleusb.cc \
|
||||
lib/globals.cc \
|
||||
lib/hexdump.cc \
|
||||
@@ -272,6 +273,7 @@ buildsimpleprogram brother240tool \
|
||||
libemu.a \
|
||||
libfmt.a \
|
||||
|
||||
runtest amiga-test tests/amiga.cc
|
||||
runtest bitaccumulator-test tests/bitaccumulator.cc
|
||||
runtest bytes-test tests/bytes.cc
|
||||
runtest compression-test tests/compression.cc
|
||||
@@ -279,6 +281,6 @@ runtest dataspec-test tests/dataspec.cc
|
||||
runtest flags-test tests/flags.cc
|
||||
runtest fluxpattern-test tests/fluxpattern.cc
|
||||
runtest fmmfm-test tests/fmmfm.cc
|
||||
runtest greaseweazle-test tests/greaseweazle.cc
|
||||
runtest kryoflux-test tests/kryoflux.cc
|
||||
runtest ldbs-test tests/ldbs.cc
|
||||
runtest amiga-test tests/amiga.cc
|
||||
|
||||
109
tests/greaseweazle.cc
Normal file
109
tests/greaseweazle.cc
Normal file
@@ -0,0 +1,109 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include "globals.h"
|
||||
#include "fluxmap.h"
|
||||
#include "lib/usb/greaseweazle.h"
|
||||
|
||||
static Bytes operator + (const Bytes& left, const Bytes& right)
|
||||
{
|
||||
Bytes output;
|
||||
ByteWriter bw(output);
|
||||
bw += left;
|
||||
bw += right;
|
||||
return output;
|
||||
}
|
||||
|
||||
static Bytes operator * (const Bytes& left, size_t count)
|
||||
{
|
||||
Bytes output;
|
||||
ByteWriter bw(output);
|
||||
while (count--)
|
||||
bw += left;
|
||||
return output;
|
||||
}
|
||||
|
||||
#define E28(val) \
|
||||
(1 | (val<<1) & 0xff), \
|
||||
(1 | (val>>6) & 0xff), \
|
||||
(1 | (val>>13) & 0xff), \
|
||||
(1 | (val>>20) & 0xff)
|
||||
|
||||
|
||||
static void test_convert(const Bytes& gwbytes, const Bytes& flbytes)
|
||||
{
|
||||
Bytes gwtoflbytes = greaseWeazleToFluxEngine(gwbytes, 2*NS_PER_TICK);
|
||||
Bytes fltogwbytes = fluxEngineToGreaseWeazle(flbytes, 2*NS_PER_TICK);
|
||||
|
||||
if (gwtoflbytes != flbytes)
|
||||
{
|
||||
std::cout << "GreaseWeazle to FluxEngine conversion failed.\n";
|
||||
std::cout << "GreaseWeazle bytes:" << std::endl;
|
||||
hexdump(std::cout, gwbytes);
|
||||
std::cout << std::endl << "Produced this:" << std::endl;
|
||||
hexdump(std::cout, gwtoflbytes);
|
||||
std::cout << std::endl << "Expected this:" << std::endl;
|
||||
hexdump(std::cout, flbytes);
|
||||
abort();
|
||||
}
|
||||
|
||||
if (fltogwbytes != gwbytes)
|
||||
{
|
||||
std::cout << "FluxEngine to GreaseWeazle conversion failed.\n";
|
||||
std::cout << "FluxEngine bytes:" << std::endl;
|
||||
hexdump(std::cout, flbytes);
|
||||
std::cout << std::endl << "Produced this:" << std::endl;
|
||||
hexdump(std::cout, fltogwbytes);
|
||||
std::cout << std::endl << "Expected this:" << std::endl;
|
||||
hexdump(std::cout, gwbytes);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void test_conversions()
|
||||
{
|
||||
/* Simple one-byte intervals. */
|
||||
|
||||
test_convert(
|
||||
Bytes{ 1, 1, 1, 1, 0 },
|
||||
Bytes{ 0x82, 0x82, 0x82, 0x82 }
|
||||
);
|
||||
|
||||
/* Larger one-byte intervals. */
|
||||
|
||||
test_convert(
|
||||
Bytes{ 32, 0 },
|
||||
Bytes{ 0x3f, 0x81 }
|
||||
);
|
||||
|
||||
test_convert(
|
||||
Bytes{ 64, 0 },
|
||||
Bytes{ 0x3f, 0x3f, 0x82 }
|
||||
);
|
||||
|
||||
test_convert(
|
||||
Bytes{ 128, 0 },
|
||||
Bytes{ 0x3f, 0x3f, 0x3f, 0x3f, 0x84 }
|
||||
);
|
||||
|
||||
/* Two-byte intervals. */
|
||||
|
||||
test_convert(
|
||||
Bytes{ 250, 1, 0 },
|
||||
Bytes{ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xbb }
|
||||
);
|
||||
|
||||
/* Very long intervals. */
|
||||
|
||||
test_convert(
|
||||
Bytes{ 255, FLUXOP_SPACE, E28(2048 - 249), 249, 0 },
|
||||
Bytes{ 0x3f }*0x41 + Bytes{ 0x81 }
|
||||
);
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
test_conversions();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user