mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
The Amiga writer now generates valid flux files --- but it looks like the
writer's broken (both the Amiga and the Brother have failed).
This commit is contained in:
101
arch/amiga/amiga.cc
Normal file
101
arch/amiga/amiga.cc
Normal file
@@ -0,0 +1,101 @@
|
||||
#include "globals.h"
|
||||
#include "record.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "amiga.h"
|
||||
#include "bytes.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
uint32_t amigaChecksum(const Bytes& bytes)
|
||||
{
|
||||
ByteReader br(bytes);
|
||||
uint32_t checksum = 0;
|
||||
|
||||
assert((bytes.size() & 3) == 0);
|
||||
while (!br.eof())
|
||||
checksum ^= br.read_be32();
|
||||
|
||||
return checksum & 0x55555555;
|
||||
}
|
||||
|
||||
static uint8_t everyother(uint16_t x)
|
||||
{
|
||||
/* aabb ccdd eeff gghh */
|
||||
x &= 0x6666; /* 0ab0 0cd0 0ef0 0gh0 */
|
||||
x >>= 1; /* 00ab 00cd 00ef 00gh */
|
||||
x |= x << 2; /* abab cdcd efef ghgh */
|
||||
x &= 0x3c3c; /* 00ab cd00 00ef gh00 */
|
||||
x >>= 2; /* 0000 abcd 0000 efgh */
|
||||
x |= x >> 4; /* 0000 abcd abcd efgh */
|
||||
return x;
|
||||
}
|
||||
|
||||
Bytes amigaInterleave(const Bytes& input)
|
||||
{
|
||||
Bytes output;
|
||||
ByteWriter bw(output);
|
||||
|
||||
/* Write all odd bits. (Numbering starts at 0...) */
|
||||
|
||||
{
|
||||
ByteReader br(input);
|
||||
while (!br.eof())
|
||||
{
|
||||
uint16_t x = br.read_be16();
|
||||
x &= 0xaaaa; /* a0b0 c0d0 e0f0 g0h0 */
|
||||
x |= x >> 1; /* aabb ccdd eeff gghh */
|
||||
x = everyother(x); /* 0000 0000 abcd efgh */
|
||||
bw.write_8(x);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write all even bits. */
|
||||
|
||||
{
|
||||
ByteReader br(input);
|
||||
while (!br.eof())
|
||||
{
|
||||
uint16_t x = br.read_be16();
|
||||
x &= 0x5555; /* 0a0b 0c0d 0e0f 0g0h */
|
||||
x |= x << 1; /* aabb ccdd eeff gghh */
|
||||
x = everyother(x); /* 0000 0000 abcd efgh */
|
||||
bw.write_8(x);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
Bytes amigaDeinterleave(const uint8_t*& input, size_t len)
|
||||
{
|
||||
assert(!(len & 1));
|
||||
const uint8_t* odds = &input[0];
|
||||
const uint8_t* evens = &input[len/2];
|
||||
Bytes output;
|
||||
ByteWriter bw(output);
|
||||
|
||||
for (size_t i=0; i<len/2; i++)
|
||||
{
|
||||
uint8_t o = *odds++;
|
||||
uint8_t e = *evens++;
|
||||
|
||||
/* This is the 'Interleave bits with 64-bit multiply' technique from
|
||||
* http://graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN
|
||||
*/
|
||||
uint16_t result =
|
||||
(((e * 0x0101010101010101ULL & 0x8040201008040201ULL)
|
||||
* 0x0102040810204081ULL >> 49) & 0x5555) |
|
||||
(((o * 0x0101010101010101ULL & 0x8040201008040201ULL)
|
||||
* 0x0102040810204081ULL >> 48) & 0xAAAA);
|
||||
|
||||
bw.write_be16(result);
|
||||
}
|
||||
|
||||
input += len;
|
||||
return output;
|
||||
}
|
||||
|
||||
Bytes amigaDeinterleave(const Bytes& input)
|
||||
{
|
||||
const uint8_t* ptr = input.cbegin();
|
||||
return amigaDeinterleave(ptr, input.size());
|
||||
}
|
||||
@@ -33,4 +33,9 @@ public:
|
||||
|
||||
extern FlagGroup amigaEncoderFlags;
|
||||
|
||||
extern uint32_t amigaChecksum(const Bytes& bytes);
|
||||
extern Bytes amigaInterleave(const Bytes& input);
|
||||
extern Bytes amigaDeinterleave(const uint8_t*& input, size_t len);
|
||||
extern Bytes amigaDeinterleave(const Bytes& input);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -21,47 +21,6 @@
|
||||
|
||||
static const FluxPattern SECTOR_PATTERN(48, AMIGA_SECTOR_RECORD);
|
||||
|
||||
static Bytes deinterleave(const uint8_t*& input, size_t len)
|
||||
{
|
||||
assert(!(len & 1));
|
||||
const uint8_t* odds = &input[0];
|
||||
const uint8_t* evens = &input[len/2];
|
||||
Bytes output;
|
||||
ByteWriter bw(output);
|
||||
|
||||
for (size_t i=0; i<len/2; i++)
|
||||
{
|
||||
uint8_t o = *odds++;
|
||||
uint8_t e = *evens++;
|
||||
|
||||
/* This is the 'Interleave bits with 64-bit multiply' technique from
|
||||
* http://graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN
|
||||
*/
|
||||
uint16_t result =
|
||||
(((e * 0x0101010101010101ULL & 0x8040201008040201ULL)
|
||||
* 0x0102040810204081ULL >> 49) & 0x5555) |
|
||||
(((o * 0x0101010101010101ULL & 0x8040201008040201ULL)
|
||||
* 0x0102040810204081ULL >> 48) & 0xAAAA);
|
||||
|
||||
bw.write_be16(result);
|
||||
}
|
||||
|
||||
input += len;
|
||||
return output;
|
||||
}
|
||||
|
||||
static uint32_t checksum(const Bytes& bytes)
|
||||
{
|
||||
ByteReader br(bytes);
|
||||
uint32_t checksum = 0;
|
||||
|
||||
assert((bytes.size() & 3) == 0);
|
||||
while (!br.eof())
|
||||
checksum ^= br.read_be32();
|
||||
|
||||
return checksum & 0x55555555;
|
||||
}
|
||||
|
||||
AbstractDecoder::RecordType AmigaDecoder::advanceToNextRecord()
|
||||
{
|
||||
_sector->clock = _fmr->seekToPattern(SECTOR_PATTERN);
|
||||
@@ -78,22 +37,22 @@ void AmigaDecoder::decodeSectorRecord()
|
||||
|
||||
const uint8_t* ptr = bytes.begin() + 3;
|
||||
|
||||
Bytes header = deinterleave(ptr, 4);
|
||||
Bytes recoveryinfo = deinterleave(ptr, 16);
|
||||
Bytes header = amigaDeinterleave(ptr, 4);
|
||||
Bytes recoveryinfo = amigaDeinterleave(ptr, 16);
|
||||
|
||||
_sector->logicalTrack = header[1] >> 1;
|
||||
_sector->logicalSide = header[1] & 1;
|
||||
_sector->logicalSector = header[2];
|
||||
|
||||
uint32_t wantedheaderchecksum = deinterleave(ptr, 4).reader().read_be32();
|
||||
uint32_t gotheaderchecksum = checksum(rawbytes.slice(6, 40));
|
||||
uint32_t wantedheaderchecksum = amigaDeinterleave(ptr, 4).reader().read_be32();
|
||||
uint32_t gotheaderchecksum = amigaChecksum(rawbytes.slice(6, 40));
|
||||
if (gotheaderchecksum != wantedheaderchecksum)
|
||||
return;
|
||||
|
||||
uint32_t wanteddatachecksum = deinterleave(ptr, 4).reader().read_be32();
|
||||
uint32_t gotdatachecksum = checksum(rawbytes.slice(62, 1024));
|
||||
uint32_t wanteddatachecksum = amigaDeinterleave(ptr, 4).reader().read_be32();
|
||||
uint32_t gotdatachecksum = amigaChecksum(rawbytes.slice(62, 1024));
|
||||
|
||||
_sector->data.clear();
|
||||
_sector->data.writer().append(deinterleave(ptr, 512)).append(recoveryinfo);
|
||||
_sector->data.writer().append(amigaDeinterleave(ptr, 512)).append(recoveryinfo);
|
||||
_sector->status = (gotdatachecksum == wanteddatachecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ static DoubleFlag clockRateUs(
|
||||
static DoubleFlag postIndexGapMs(
|
||||
{ "--post-index-gap" },
|
||||
"Post-index gap before first sector header (milliseconds).",
|
||||
20.0);
|
||||
5.0);
|
||||
|
||||
static int charToInt(char c)
|
||||
{
|
||||
@@ -26,16 +26,13 @@ static int charToInt(char c)
|
||||
return 10 + tolower(c) - 'a';
|
||||
}
|
||||
|
||||
static uint32_t checksum(const Bytes& bytes)
|
||||
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
|
||||
{
|
||||
ByteReader br(bytes);
|
||||
uint32_t checksum = 0;
|
||||
|
||||
assert((bytes.size() & 3) == 0);
|
||||
while (!br.eof())
|
||||
checksum ^= br.read_be32();
|
||||
|
||||
return checksum & 0x55555555;
|
||||
for (bool bit : src)
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
bits[cursor++] = bit;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
|
||||
@@ -50,16 +47,49 @@ static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint64_t data,
|
||||
}
|
||||
}
|
||||
|
||||
static void write_sector(std::vector<bool> bits, unsigned& cursor, const Sector* sector)
|
||||
static void write_interleaved_bytes(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
|
||||
{
|
||||
assert(!(bytes.size() & 3));
|
||||
Bytes interleaved = amigaInterleave(bytes);
|
||||
encodeMfm(bits, cursor, interleaved);
|
||||
}
|
||||
|
||||
static void write_interleaved_bytes(std::vector<bool>& bits, unsigned& cursor, uint32_t data)
|
||||
{
|
||||
Bytes b(4);
|
||||
ByteWriter bw(b);
|
||||
bw.write_be32(data);
|
||||
write_interleaved_bytes(bits, cursor, b);
|
||||
}
|
||||
|
||||
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const Sector* sector)
|
||||
{
|
||||
write_bits(bits, cursor, AMIGA_SECTOR_RECORD, 6*8);
|
||||
|
||||
Bytes header(4);
|
||||
ByteWriter bw(header);
|
||||
bw.write_8(0xff); /* Amiga 1.0 format byte */
|
||||
bw.write_8((sector->logicalTrack<<1) | sector->logicalSide);
|
||||
bw.write_8(sector->logicalSector);
|
||||
bw.write_8(AMIGA_SECTORS_PER_TRACK - sector->logicalSector);
|
||||
std::vector<bool> headerBits(20*16);
|
||||
unsigned headerCursor = 0;
|
||||
|
||||
Bytes header =
|
||||
{
|
||||
0xff, /* Amiga 1.0 format byte */
|
||||
(uint8_t) ((sector->logicalTrack<<1) | sector->logicalSide),
|
||||
(uint8_t) sector->logicalSector,
|
||||
(uint8_t) (AMIGA_SECTORS_PER_TRACK - sector->logicalSector)
|
||||
};
|
||||
write_interleaved_bytes(headerBits, headerCursor, header);
|
||||
Bytes recoveryInfo(16);
|
||||
write_interleaved_bytes(headerBits, headerCursor, recoveryInfo);
|
||||
|
||||
std::vector<bool> dataBits(512*16);
|
||||
unsigned dataCursor = 0;
|
||||
write_interleaved_bytes(dataBits, dataCursor, sector->data);
|
||||
|
||||
write_bits(bits, cursor, headerBits);
|
||||
uint32_t headerChecksum = amigaChecksum(toBytes(headerBits));
|
||||
write_interleaved_bytes(bits, cursor, headerChecksum);
|
||||
uint32_t dataChecksum = amigaChecksum(toBytes(dataBits));
|
||||
write_interleaved_bytes(bits, cursor, dataChecksum);
|
||||
write_bits(bits, cursor, dataBits);
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> AmigaEncoder::encode(
|
||||
@@ -76,11 +106,11 @@ std::unique_ptr<Fluxmap> AmigaEncoder::encode(
|
||||
|
||||
for (int sectorId=0; sectorId<AMIGA_SECTORS_PER_TRACK; sectorId++)
|
||||
{
|
||||
const auto& sectorData = allSectors.get(physicalTrack, 0, sectorId);
|
||||
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
|
||||
write_sector(bits, cursor, sectorData);
|
||||
}
|
||||
|
||||
if (cursor > bits.size())
|
||||
if (cursor >= bits.size())
|
||||
Error() << "track data overrun";
|
||||
fillBitmapTo(bits, cursor, bits.size(), { true, false });
|
||||
|
||||
|
||||
@@ -154,7 +154,7 @@ std::unique_ptr<Fluxmap> BrotherEncoder::encode(
|
||||
write_sector_data(bits, cursor, sectorData->data);
|
||||
}
|
||||
|
||||
if (cursor > bits.size())
|
||||
if (cursor >= bits.size())
|
||||
Error() << "track data overrun";
|
||||
fillBitmapTo(bits, cursor, bits.size(), { true, false });
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ extern void setDecoderManualClockRate(double clockrate_us);
|
||||
|
||||
extern Bytes decodeFmMfm(std::vector<bool>::const_iterator start,
|
||||
std::vector<bool>::const_iterator end);
|
||||
extern void encodeMfm(std::vector<bool>& bits, unsigned& cursor, const Bytes& input);
|
||||
|
||||
static inline Bytes decodeFmMfm(const std::vector<bool> bits)
|
||||
{ return decodeFmMfm(bits.begin(), bits.end()); }
|
||||
|
||||
@@ -51,3 +51,25 @@ Bytes decodeFmMfm(
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
void encodeMfm(std::vector<bool>& bits, unsigned& cursor, const Bytes& input)
|
||||
{
|
||||
bool lastBit = false;
|
||||
unsigned len = bits.size()-1;
|
||||
|
||||
for (uint8_t b : input)
|
||||
{
|
||||
for (int i=0; i<8; i++)
|
||||
{
|
||||
bool bit = b & 0x80;
|
||||
b <<= 1;
|
||||
|
||||
if (cursor >= len)
|
||||
return;
|
||||
|
||||
bits[cursor++] = !lastBit && !bit;
|
||||
bits[cursor++] = bit;
|
||||
lastBit = bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +146,7 @@ buildlibrary libbackend.a \
|
||||
arch/aeslanier/decoder.cc \
|
||||
arch/amiga/decoder.cc \
|
||||
arch/amiga/encoder.cc \
|
||||
arch/amiga/amiga.cc \
|
||||
arch/apple2/decoder.cc \
|
||||
arch/brother/decoder.cc \
|
||||
arch/brother/encoder.cc \
|
||||
@@ -253,3 +254,4 @@ runtest fluxpattern-test tests/fluxpattern.cc
|
||||
runtest fmmfm-test tests/fmmfm.cc
|
||||
runtest kryoflux-test tests/kryoflux.cc
|
||||
runtest ldbs-test tests/ldbs.cc
|
||||
runtest amiga-test tests/amiga.cc
|
||||
|
||||
38
tests/amiga.cc
Normal file
38
tests/amiga.cc
Normal file
@@ -0,0 +1,38 @@
|
||||
#include "globals.h"
|
||||
#include "bytes.h"
|
||||
#include "record.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "arch/amiga/amiga.h"
|
||||
#include <assert.h>
|
||||
|
||||
static const Bytes testData = {
|
||||
0x52, /* 0101 0010 */
|
||||
0xff, /* 1111 1111 */
|
||||
0x4a, /* 0100 1010 */
|
||||
0x22, /* 0010 0010 */
|
||||
};
|
||||
static const Bytes testDataInterleaved = {
|
||||
0x1f, /* 0001 1111 */
|
||||
0x35, /* 0011 0101 */
|
||||
0xcf, /* 1100 1111 */
|
||||
0x80, /* 1000 0000 */
|
||||
};
|
||||
|
||||
static void testInterleave(void)
|
||||
{
|
||||
Bytes interleaved = amigaInterleave(testData);
|
||||
assert(interleaved == testDataInterleaved);
|
||||
}
|
||||
|
||||
static void testDeinterleave(void)
|
||||
{
|
||||
Bytes deinterleaved = amigaDeinterleave(testDataInterleaved);
|
||||
assert(deinterleaved == testData);
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
testDeinterleave();
|
||||
testInterleave();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user