mirror of
				https://github.com/davidgiven/fluxengine.git
				synced 2025-10-24 11:11:02 -07:00 
			
		
		
		
	
		
			
				
	
	
		
			150 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			150 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "lib/globals.h"
 | |
| #include "lib/decoders/decoders.h"
 | |
| #include "lib/encoders/encoders.h"
 | |
| #include "amiga.h"
 | |
| #include "lib/crc.h"
 | |
| #include "lib/readerwriter.h"
 | |
| #include "lib/image.h"
 | |
| #include "arch/amiga/amiga.pb.h"
 | |
| #include "lib/encoders/encoders.pb.h"
 | |
| 
 | |
| static bool lastBit;
 | |
| 
 | |
| 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())
 | |
|             lastBit = bits[cursor++] = bit;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void write_bits(
 | |
|     std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
 | |
| {
 | |
|     cursor += width;
 | |
|     lastBit = data & 1;
 | |
|     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_bits(
 | |
|     std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
 | |
| {
 | |
|     ByteReader br(bytes);
 | |
|     BitReader bitr(br);
 | |
| 
 | |
|     while (!bitr.eof())
 | |
|     {
 | |
|         if (cursor < bits.size())
 | |
|             bits[cursor++] = bitr.get();
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void write_sector(std::vector<bool>& bits,
 | |
|     unsigned& cursor,
 | |
|     const std::shared_ptr<const Sector>& sector)
 | |
| {
 | |
|     if ((sector->data.size() != 512) && (sector->data.size() != 528))
 | |
|         error("unsupported sector size --- you must pick 512 or 528");
 | |
| 
 | |
|     uint32_t checksum = 0;
 | |
| 
 | |
|     auto write_interleaved_bytes = [&](const Bytes& bytes)
 | |
|     {
 | |
|         Bytes interleaved = amigaInterleave(bytes);
 | |
|         Bytes mfm = encodeMfm(interleaved, lastBit);
 | |
|         checksum ^= amigaChecksum(mfm);
 | |
|         checksum &= 0x55555555;
 | |
|         write_bits(bits, cursor, mfm);
 | |
|     };
 | |
| 
 | |
|     auto write_interleaved_word = [&](uint32_t word)
 | |
|     {
 | |
|         Bytes b(4);
 | |
|         b.writer().write_be32(word);
 | |
|         write_interleaved_bytes(b);
 | |
|     };
 | |
| 
 | |
|     write_bits(bits, cursor, 0xaaaa, 2 * 8);
 | |
|     write_bits(bits, cursor, AMIGA_SECTOR_RECORD, 6 * 8);
 | |
| 
 | |
|     checksum = 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(header);
 | |
|     Bytes recoveryInfo(16);
 | |
|     if (sector->data.size() == 528)
 | |
|         recoveryInfo = sector->data.slice(512, 16);
 | |
|     write_interleaved_bytes(recoveryInfo);
 | |
|     write_interleaved_word(checksum);
 | |
| 
 | |
|     Bytes data = sector->data.slice(0, 512);
 | |
|     write_interleaved_word(
 | |
|         amigaChecksum(encodeMfm(amigaInterleave(data), lastBit)));
 | |
|     write_interleaved_bytes(data);
 | |
| }
 | |
| 
 | |
| class AmigaEncoder : public Encoder
 | |
| {
 | |
| public:
 | |
|     AmigaEncoder(const EncoderProto& config):
 | |
|         Encoder(config),
 | |
|         _config(config.amiga())
 | |
|     {
 | |
|     }
 | |
| 
 | |
| public:
 | |
|     std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
 | |
|         const std::vector<std::shared_ptr<const Sector>>& sectors,
 | |
|         const Image& image) override
 | |
|     {
 | |
|         /* Number of bits for one nominal revolution of a real 200ms Amiga disk.
 | |
|          */
 | |
|         int bitsPerRevolution = 200e3 / _config.clock_rate_us();
 | |
|         std::vector<bool> bits(bitsPerRevolution);
 | |
|         unsigned cursor = 0;
 | |
| 
 | |
|         fillBitmapTo(bits,
 | |
|             cursor,
 | |
|             _config.post_index_gap_ms() * 1000 / _config.clock_rate_us(),
 | |
|             {true, false});
 | |
|         lastBit = false;
 | |
| 
 | |
|         for (const auto& sector : sectors)
 | |
|             write_sector(bits, cursor, sector);
 | |
| 
 | |
|         if (cursor >= bits.size())
 | |
|             error("track data overrun");
 | |
|         fillBitmapTo(bits, cursor, bits.size(), {true, false});
 | |
| 
 | |
|         auto fluxmap = std::make_unique<Fluxmap>();
 | |
|         fluxmap->appendBits(bits,
 | |
|             calculatePhysicalClockPeriod(_config.clock_rate_us() * 1e3, 200e6));
 | |
|         return fluxmap;
 | |
|     }
 | |
| 
 | |
| private:
 | |
|     const AmigaEncoderProto& _config;
 | |
| };
 | |
| 
 | |
| std::unique_ptr<Encoder> createAmigaEncoder(const EncoderProto& config)
 | |
| {
 | |
|     return std::unique_ptr<Encoder>(new AmigaEncoder(config));
 | |
| }
 |