-
Notifications
You must be signed in to change notification settings - Fork 73
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #121 from davidgiven/amiga
Add support for writing Amiga disks.
- Loading branch information
Showing
14 changed files
with
370 additions
and
54 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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()); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
#include "globals.h" | ||
#include "record.h" | ||
#include "decoders/decoders.h" | ||
#include "encoders/encoders.h" | ||
#include "amiga.h" | ||
#include "crc.h" | ||
#include "sectorset.h" | ||
#include "writer.h" | ||
|
||
FlagGroup amigaEncoderFlags; | ||
|
||
static DoubleFlag clockRateUs( | ||
{ "--clock-rate" }, | ||
"Encoded data clock rate (microseconds).", | ||
2.00); | ||
|
||
static DoubleFlag postIndexGapMs( | ||
{ "--post-index-gap" }, | ||
"Post-index gap before first sector header (milliseconds).", | ||
0.5); | ||
|
||
static int charToInt(char c) | ||
{ | ||
if (isdigit(c)) | ||
return c - '0'; | ||
return 10 + tolower(c) - 'a'; | ||
} | ||
|
||
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src) | ||
{ | ||
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) | ||
{ | ||
cursor += width; | ||
for (int i=0; i<width; i++) | ||
{ | ||
unsigned pos = cursor - i - 1; | ||
if (pos < bits.size()) | ||
bits[pos] = data & 1; | ||
data >>= 1; | ||
} | ||
} | ||
|
||
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) | ||
{ | ||
if ((sector->data.size() != 512) && (sector->data.size() != 528)) | ||
Error() << "unsupported sector size --- you must pick 512 or 528"; | ||
|
||
write_bits(bits, cursor, AMIGA_SECTOR_RECORD, 6*8); | ||
|
||
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); | ||
if (sector->data.size() == 528) | ||
recoveryInfo = sector->data.slice(512, 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( | ||
int physicalTrack, int physicalSide, const SectorSet& allSectors) | ||
{ | ||
if ((physicalTrack < 0) || (physicalTrack >= AMIGA_TRACKS_PER_DISK)) | ||
return std::unique_ptr<Fluxmap>(); | ||
|
||
int bitsPerRevolution = 200000.0 / clockRateUs; | ||
std::vector<bool> bits(bitsPerRevolution); | ||
unsigned cursor = 0; | ||
|
||
fillBitmapTo(bits, cursor, postIndexGapMs * 1000 / clockRateUs, { true, false }); | ||
|
||
for (int sectorId=0; sectorId<AMIGA_SECTORS_PER_TRACK; sectorId++) | ||
{ | ||
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId); | ||
write_sector(bits, cursor, sectorData); | ||
} | ||
|
||
if (cursor >= bits.size()) | ||
Error() << "track data overrun"; | ||
fillBitmapTo(bits, cursor, bits.size(), { true, false }); | ||
|
||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap); | ||
fluxmap->appendBits(bits, clockRateUs*1e3); | ||
return fluxmap; | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.