mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Added option to write d64 images back to disk
This commit is contained in:
@@ -1,10 +1,36 @@
|
|||||||
#ifndef C64_H
|
#ifndef C64_H
|
||||||
#define C64_H
|
#define C64_H
|
||||||
|
|
||||||
|
#include "decoders/decoders.h"
|
||||||
|
#include "encoders/encoders.h"
|
||||||
|
|
||||||
#define C64_SECTOR_RECORD 0xffd49
|
#define C64_SECTOR_RECORD 0xffd49
|
||||||
#define C64_DATA_RECORD 0xffd57
|
#define C64_DATA_RECORD 0xffd57
|
||||||
#define C64_SECTOR_LENGTH 256
|
#define C64_SECTOR_LENGTH 256
|
||||||
|
|
||||||
|
#define C64_FORMAT_ID_BYTE1 0x00
|
||||||
|
#define C64_FORMAT_ID_BYTE2 0x00
|
||||||
|
/* Source: http://www.unusedino.de/ec64/technical/formats/g64.html
|
||||||
|
1. Header sync FF FF FF FF FF (40 'on' bits, not GCR)
|
||||||
|
2. Header info 52 54 B5 29 4B 7A 5E 95 55 55 (10 GCR bytes)
|
||||||
|
3. Header gap 55 55 55 55 55 55 55 55 55 (9 bytes, never read)
|
||||||
|
4. Data sync FF FF FF FF FF (40 'on' bits, not GCR)
|
||||||
|
5. Data block 55...4A (325 GCR bytes)
|
||||||
|
6. Inter-sector gap 55 55 55 55...55 55 (4 to 12 bytes, never read)
|
||||||
|
1. Header sync (SYNC for the next sector)
|
||||||
|
*/
|
||||||
|
#define C64_HEADER_DATA_SYNC 0xFF
|
||||||
|
#define C64_HEADER_BLOCK_ID 0x08
|
||||||
|
#define C64_DATA_BLOCK_ID 0x07
|
||||||
|
#define C64_ENCODED_HEADER_LENGTH 10
|
||||||
|
#define C64_HEADER_GAP 0x55
|
||||||
|
#define C64_DATA_BLOCK_LENGTH 325
|
||||||
|
#define C64_INTER_SECTOR_GAP 0x55
|
||||||
|
#define C64_PADDING 0x0F
|
||||||
|
|
||||||
|
#define C64_TRACKS_PER_DISK 40
|
||||||
|
|
||||||
|
|
||||||
class Sector;
|
class Sector;
|
||||||
class Fluxmap;
|
class Fluxmap;
|
||||||
|
|
||||||
@@ -18,4 +44,16 @@ public:
|
|||||||
void decodeDataRecord();
|
void decodeDataRecord();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Commodore64Encoder : public AbstractEncoder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~Commodore64Encoder() {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern FlagGroup Commodore64EncoderFlags;
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
332
arch/c64/encoder.cc
Normal file
332
arch/c64/encoder.cc
Normal file
@@ -0,0 +1,332 @@
|
|||||||
|
#include "globals.h"
|
||||||
|
#include "record.h"
|
||||||
|
#include "decoders/decoders.h"
|
||||||
|
#include "encoders/encoders.h"
|
||||||
|
#include "c64.h"
|
||||||
|
#include "crc.h"
|
||||||
|
#include "sectorset.h"
|
||||||
|
#include "writer.h"
|
||||||
|
#include "fmt/format.h"
|
||||||
|
#include <ctype.h>
|
||||||
|
#include "bytes.h"
|
||||||
|
|
||||||
|
FlagGroup Commodore64EncoderFlags;
|
||||||
|
|
||||||
|
static DoubleFlag postIndexGapUs(
|
||||||
|
{ "--post-index-gap-us" },
|
||||||
|
"Post-index gap before first sector header (microseconds).",
|
||||||
|
0);
|
||||||
|
|
||||||
|
static DoubleFlag clockCompensation(
|
||||||
|
{ "--clock-compensation-factor" },
|
||||||
|
"Scale the output clock by this much.",
|
||||||
|
1.0);
|
||||||
|
|
||||||
|
static bool lastBit;
|
||||||
|
|
||||||
|
static double clockRateUsForTrack(unsigned track)
|
||||||
|
{ //what are the clockrateUsfortrack for a c64 disk... ????
|
||||||
|
/*
|
||||||
|
Track # Sectors/Track Speed Zone bits/rotation
|
||||||
|
1 – 17 21 3 61,538.4
|
||||||
|
18 – 24 19 2 57,142.8
|
||||||
|
25 – 30 18 1 53,333.4
|
||||||
|
31 – 35 17 0 50,000.0
|
||||||
|
*/
|
||||||
|
if (track < 17)
|
||||||
|
return 3.25; //200000.0/61538.4
|
||||||
|
if (track < 24)
|
||||||
|
return 3.50; //200000.0/57142.8
|
||||||
|
if (track < 30)
|
||||||
|
return 3.750; //200000.0/53,333.4
|
||||||
|
return 4.00; //200000.0/50.000.0
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned sectorsForTrack(unsigned track)
|
||||||
|
/*
|
||||||
|
Track Sectors/track # Sectors Storage in Bytes
|
||||||
|
----- ------------- --------- ----------------
|
||||||
|
1-17 21 357 7820
|
||||||
|
18-24 19 133 7170
|
||||||
|
25-30 18 108 6300
|
||||||
|
31-40(*) 17 85 6020
|
||||||
|
---
|
||||||
|
683 (for a 35 track image)
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
if (track < 17)
|
||||||
|
return 21;
|
||||||
|
if (track < 24)
|
||||||
|
return 19;
|
||||||
|
if (track < 30)
|
||||||
|
return 18;
|
||||||
|
return 17;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int encode_data_gcr(uint8_t data)
|
||||||
|
{
|
||||||
|
switch (data)
|
||||||
|
{
|
||||||
|
#define GCR_ENTRY(gcr, data) \
|
||||||
|
case data: return gcr;
|
||||||
|
#include "data_gcr.h"
|
||||||
|
#undef GCR_ENTRY
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
|
||||||
|
{
|
||||||
|
for (bool bit : src) //Range-based for loop
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bindump(std::ostream& stream, std::vector<bool>& buffer)
|
||||||
|
{
|
||||||
|
size_t pos = 0;
|
||||||
|
|
||||||
|
while ((pos < buffer.size()) and (pos <520))
|
||||||
|
{
|
||||||
|
stream << fmt::format("{:5d} : ", pos);
|
||||||
|
for (int i=0; i<40; i++)
|
||||||
|
{
|
||||||
|
if ((pos+i) < buffer.size())
|
||||||
|
stream << fmt::format("{:01b}", (buffer[pos+i]));
|
||||||
|
else
|
||||||
|
stream << "-- ";
|
||||||
|
if ((((pos + i + 1) % 8) == 0) and i != 0)
|
||||||
|
stream << " ";
|
||||||
|
|
||||||
|
}
|
||||||
|
stream << std::endl;
|
||||||
|
pos += 40;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static std::vector<bool> encode_data(uint8_t input)
|
||||||
|
/*
|
||||||
|
Four 8-bit data bytes are converted to four 10-bit GCR bytes at a time by the 1541 DOS.
|
||||||
|
RAM is only an 8-bit storage device though. This hardware limitation prevents a 10-bit
|
||||||
|
GCR byte from being stored in a single memory location. Four 10-bit GCR bytes total
|
||||||
|
40 bits - a number evenly divisible by our overriding 8-bit constraint. Commodore sub-
|
||||||
|
divides the 40 GCR bits into five 8-bit bytes to solve this dilemma. This explains why
|
||||||
|
four 8-bit data bytes are converted to GCR form at a time. The following step by step
|
||||||
|
example demonstrates how this bit manipulation is performed by the DOS.
|
||||||
|
STEP 1. Four 8-bit Data Bytes
|
||||||
|
$08 $10 $00 $12
|
||||||
|
STEP 2. Hexadecimal to Binary Conversion
|
||||||
|
1. Binary Equivalents
|
||||||
|
$08 $10 $00 $12
|
||||||
|
00001000 00010000 00000000 00010010
|
||||||
|
STEP 3. Binary to GCR Conversion
|
||||||
|
1. Four 8-bit Data Bytes
|
||||||
|
00001000 00010000 00000000 00010010
|
||||||
|
2. High and Low Nybbles
|
||||||
|
0000 1000 0001 0000 0000 0000 0001 0010
|
||||||
|
3. High and Low Nybble GCR Equivalents
|
||||||
|
01010 01001 01011 01010 01010 01010 01011 10010
|
||||||
|
4. Four 10-bit GCR Bytes
|
||||||
|
0101001001 0101101010 0101001010 0101110010
|
||||||
|
STEP 4. 10-bit GCR to 8-bit GCR Conversion
|
||||||
|
1. Concatenate Four 10-bit GCR Bytes
|
||||||
|
0101001001010110101001010010100101110010
|
||||||
|
2. Five 8-bit Subdivisions
|
||||||
|
01010010 01010110 10100101 00101001 01110010
|
||||||
|
STEP 5. Binary to Hexadecimal Conversion
|
||||||
|
1. Hexadecimal Equivalents
|
||||||
|
01010010 01010110 10100101 00101001 01110010
|
||||||
|
$52 $56 $A5 $29 $72
|
||||||
|
STEP 6. Four 8-bit Data Bytes are Recorded as Five 8-bit GCR Bytes
|
||||||
|
$08 $10 $00 $12
|
||||||
|
are recorded as
|
||||||
|
$52 $56 $A5 $29 $72
|
||||||
|
*/
|
||||||
|
|
||||||
|
{
|
||||||
|
std::vector<bool> output(10, false);
|
||||||
|
uint8_t hi =0;
|
||||||
|
uint8_t lo =0;
|
||||||
|
uint8_t lo_GCR =0;
|
||||||
|
uint8_t hi_GCR =0;
|
||||||
|
|
||||||
|
//Convert the byte in high and low nibble
|
||||||
|
lo = input >> 4; //get the lo nibble shift the bits 4 to the right
|
||||||
|
hi = input & 15; //get the hi nibble bij masking the lo bits (00001111)
|
||||||
|
|
||||||
|
|
||||||
|
lo_GCR = encode_data_gcr(lo); //example value: 0000 GCR = 01010
|
||||||
|
hi_GCR = encode_data_gcr(hi); //example value: 1000 GCR = 01001
|
||||||
|
//output = [0,1,2,3,4,5,6,7,8,9]
|
||||||
|
//value = [0,1,0,1,0,0,1,0,0,1]
|
||||||
|
// 01010 01001
|
||||||
|
|
||||||
|
int b = 4;
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
if (i < 5) //01234
|
||||||
|
{ //i = 0 op
|
||||||
|
output[4-i] = (lo_GCR & 1); //01010
|
||||||
|
|
||||||
|
//01010 -> & 00001 -> 00000 output[4] = 0
|
||||||
|
//00101 -> & 00001 -> 00001 output[3] = 1
|
||||||
|
//00010 -> & 00001 -> 00000 output[2] = 0
|
||||||
|
//00001 -> & 00001 -> 00001 output[1] = 1
|
||||||
|
//00000 -> & 00001 -> 00000 output[0] = 0
|
||||||
|
lo_GCR >>= 1;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
output[i+b] = (hi_GCR & 1); //01001
|
||||||
|
//01001 -> & 00001 -> 00001 output[9] = 1
|
||||||
|
//00100 -> & 00001 -> 00000 output[8] = 0
|
||||||
|
//00010 -> & 00001 -> 00000 output[7] = 0
|
||||||
|
//00001 -> & 00001 -> 00001 output[6] = 1
|
||||||
|
//00000 -> & 00001 -> 00000 output[5] = 0
|
||||||
|
hi_GCR >>= 1;
|
||||||
|
b = b-2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const Sector* sector)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
/* Source: http://www.unusedino.de/ec64/technical/formats/g64.html
|
||||||
|
1. Header sync FF FF FF FF FF (40 'on' bits, not GCR)
|
||||||
|
2. Header info 52 54 B5 29 4B 7A 5E 95 55 55 (10 GCR bytes)
|
||||||
|
3. Header gap 55 55 55 55 55 55 55 55 55 (9 bytes, never read)
|
||||||
|
4. Data sync FF FF FF FF FF (40 'on' bits, not GCR)
|
||||||
|
5. Data block 55...4A (325 GCR bytes)
|
||||||
|
6. Inter-sector gap 55 55 55 55...55 55 (4 to 12 bytes, never read)
|
||||||
|
1. Header sync (SYNC for the next sector)
|
||||||
|
*/
|
||||||
|
if ((sector->data.size() != C64_SECTOR_LENGTH))
|
||||||
|
Error() << fmt::format("unsupported sector size {} --- you must pick 256", sector->data.size());
|
||||||
|
|
||||||
|
//1. Write header Sync (not GCR)
|
||||||
|
for (int i=0; i<6; i++)
|
||||||
|
write_bits(bits, cursor, C64_HEADER_DATA_SYNC, 1*8); /* sync */
|
||||||
|
|
||||||
|
//2. Write Header info 10 GCR bytes
|
||||||
|
/*
|
||||||
|
The 10 byte header info (#2) is GCR encoded and must be decoded to it's
|
||||||
|
normal 8 bytes to be understood. Once decoded, its breakdown is as follows:
|
||||||
|
|
||||||
|
Byte $00 - header block ID ($08)
|
||||||
|
01 - header block checksum 16 (EOR of $02-$05)
|
||||||
|
02 - Sector
|
||||||
|
03 - Track
|
||||||
|
04 - Format ID byte #2
|
||||||
|
05 - Format ID byte #1
|
||||||
|
06-07 - $0F ("off" bytes)
|
||||||
|
*/
|
||||||
|
uint8_t encodedTrack = ((sector->logicalTrack) + 1); // C64 track numbering starts with 1. Fluxengine with 0.
|
||||||
|
uint8_t encodedSector = sector->logicalSector;
|
||||||
|
uint8_t formatByte1 = C64_FORMAT_ID_BYTE1;
|
||||||
|
uint8_t formatByte2 = C64_FORMAT_ID_BYTE2;
|
||||||
|
uint8_t headerChecksum = (encodedTrack ^ encodedSector ^ formatByte1 ^ formatByte2);
|
||||||
|
write_bits(bits, cursor, encode_data(C64_HEADER_BLOCK_ID));
|
||||||
|
write_bits(bits, cursor, encode_data(headerChecksum));
|
||||||
|
write_bits(bits, cursor, encode_data(encodedSector));
|
||||||
|
write_bits(bits, cursor, encode_data(encodedTrack));
|
||||||
|
write_bits(bits, cursor, encode_data(formatByte1));
|
||||||
|
write_bits(bits, cursor, encode_data(formatByte2));
|
||||||
|
write_bits(bits, cursor, encode_data(C64_PADDING));
|
||||||
|
write_bits(bits, cursor, encode_data(C64_PADDING));
|
||||||
|
|
||||||
|
//3. Write header GAP not GCR
|
||||||
|
for (int i=0; i<9; i++)
|
||||||
|
write_bits(bits, cursor, C64_HEADER_GAP, 1*8); /* header gap */
|
||||||
|
|
||||||
|
//4. Write Data sync not GCR
|
||||||
|
for (int i=0; i<6; i++)
|
||||||
|
write_bits(bits, cursor, C64_HEADER_DATA_SYNC, 1*8); /* sync */
|
||||||
|
|
||||||
|
//5. Write data block 325 GCR bytes
|
||||||
|
/*
|
||||||
|
The 325 byte data block (#5) is GCR encoded and must be decoded to its
|
||||||
|
normal 260 bytes to be understood. The data block is made up of the following:
|
||||||
|
|
||||||
|
Byte $00 - data block ID ($07)
|
||||||
|
01-100 - 256 bytes data
|
||||||
|
101 - data block checksum (EOR of $01-100)
|
||||||
|
102-103 - $00 ("off" bytes, to make the sector size a multiple of 5)
|
||||||
|
*/
|
||||||
|
write_bits(bits, cursor, encode_data(C64_DATA_BLOCK_ID));
|
||||||
|
uint8_t dataChecksum = xorBytes(sector->data);
|
||||||
|
ByteReader br(sector->data);
|
||||||
|
int i = 0;
|
||||||
|
for (i = 0; i < C64_SECTOR_LENGTH; i++)
|
||||||
|
{
|
||||||
|
uint8_t val = br.read_8();
|
||||||
|
write_bits(bits, cursor, encode_data(val));
|
||||||
|
}
|
||||||
|
write_bits(bits, cursor, encode_data(dataChecksum));
|
||||||
|
write_bits(bits, cursor, encode_data(C64_PADDING));
|
||||||
|
write_bits(bits, cursor, encode_data(C64_PADDING));
|
||||||
|
|
||||||
|
//6. Write inter-sector gap 9 - 12 bytes nor gcr
|
||||||
|
for (int i=0; i<9; i++)
|
||||||
|
write_bits(bits, cursor, C64_INTER_SECTOR_GAP, 1*8); /* sync */
|
||||||
|
}
|
||||||
|
catch(const std::exception& e)
|
||||||
|
{
|
||||||
|
std::cerr << e.what() << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Fluxmap> Commodore64Encoder::encode(
|
||||||
|
int physicalTrack, int physicalSide, const SectorSet& allSectors)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if ((physicalTrack < 0) || (physicalTrack >= C64_TRACKS_PER_DISK))
|
||||||
|
return std::unique_ptr<Fluxmap>();
|
||||||
|
|
||||||
|
double clockRateUs = clockRateUsForTrack(physicalTrack) * clockCompensation;
|
||||||
|
|
||||||
|
int bitsPerRevolution = 200000.0 / clockRateUs;
|
||||||
|
|
||||||
|
std::vector<bool> bits(bitsPerRevolution);
|
||||||
|
unsigned cursor = 0;
|
||||||
|
|
||||||
|
fillBitmapTo(bits, cursor, postIndexGapUs / clockRateUs, { true, false });
|
||||||
|
lastBit = false;
|
||||||
|
|
||||||
|
unsigned numSectors = sectorsForTrack(physicalTrack);
|
||||||
|
for (int sectorId=0; sectorId<numSectors; sectorId++)
|
||||||
|
{
|
||||||
|
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
|
||||||
|
write_sector(bits, cursor, sectorData);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursor >= bits.size())
|
||||||
|
Error() << fmt::format("track data overrun by {} bits", cursor - bits.size());
|
||||||
|
fillBitmapTo(bits, cursor, bits.size(), { true, false });
|
||||||
|
|
||||||
|
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||||
|
fluxmap->appendBits(bits, clockRateUs*1e3);
|
||||||
|
return fluxmap;
|
||||||
|
}
|
||||||
|
catch(const std::exception& e)
|
||||||
|
{
|
||||||
|
std::cerr << e.what() << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -58,6 +58,25 @@ Fluxmap& Fluxmap::appendIndex()
|
|||||||
|
|
||||||
void Fluxmap::precompensate(int threshold_ticks, int amount_ticks)
|
void Fluxmap::precompensate(int threshold_ticks, int amount_ticks)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/* PRECOMP 432PRECOMPENSATION DELAY https://www.mouser.com/datasheet/2/268/37c78-468028.pdf
|
||||||
|
111 0.00 ns-DISABLED
|
||||||
|
001 41.67 ns
|
||||||
|
010 83.34 ns
|
||||||
|
011 125.00 ns
|
||||||
|
100 166.67 ns
|
||||||
|
101 208.33 ns
|
||||||
|
110 250.00 ns
|
||||||
|
000 Default (See Table 12)
|
||||||
|
|
||||||
|
Table 12 - Default Precompensation Delays
|
||||||
|
DATA RATEPRECOMPENSATION DELAYS
|
||||||
|
2 Mbps 20.8 ns
|
||||||
|
1 Mbps 41.67 ns
|
||||||
|
500 Kbps 125 ns
|
||||||
|
300 Kbps 125 ns
|
||||||
|
250 Kbps 125 ns
|
||||||
|
*/
|
||||||
uint8_t junk = 0xff;
|
uint8_t junk = 0xff;
|
||||||
|
|
||||||
for (unsigned i=0; i<_bytes.size(); i++)
|
for (unsigned i=0; i<_bytes.size(); i++)
|
||||||
|
|||||||
81
lib/imagereader/d64imagereader.cc
Normal file
81
lib/imagereader/d64imagereader.cc
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#include "globals.h"
|
||||||
|
#include "flags.h"
|
||||||
|
#include "dataspec.h"
|
||||||
|
#include "sector.h"
|
||||||
|
#include "sectorset.h"
|
||||||
|
#include "imagereader/imagereader.h"
|
||||||
|
#include "fmt/format.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
class D64ImageReader : public ImageReader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
D64ImageReader(const ImageSpec& spec):
|
||||||
|
ImageReader(spec)
|
||||||
|
{}
|
||||||
|
|
||||||
|
SectorSet readImage()
|
||||||
|
{
|
||||||
|
std::ifstream inputFile(spec.filename, std::ios::in | std::ios::binary);
|
||||||
|
if (!inputFile.is_open())
|
||||||
|
Error() << "cannot open input file";
|
||||||
|
|
||||||
|
Bytes data;
|
||||||
|
data.writer() += inputFile;
|
||||||
|
ByteReader br(data);
|
||||||
|
unsigned numCylinders = 39;
|
||||||
|
unsigned numHeads = 1;
|
||||||
|
unsigned numSectors = 0;
|
||||||
|
|
||||||
|
std::cout << "reading D64 image\n"
|
||||||
|
<< fmt::format("{} cylinders, {} heads\n",
|
||||||
|
numCylinders, numHeads);
|
||||||
|
|
||||||
|
uint32_t offset = 0;
|
||||||
|
|
||||||
|
auto sectorsPerTrack = [&](int track) -> int
|
||||||
|
{
|
||||||
|
if (track < 17)
|
||||||
|
return 21;
|
||||||
|
if (track < 24)
|
||||||
|
return 19;
|
||||||
|
if (track < 30)
|
||||||
|
return 18;
|
||||||
|
return 17;
|
||||||
|
};
|
||||||
|
|
||||||
|
SectorSet sectors;
|
||||||
|
for (int track = 0; track < 40; track++)
|
||||||
|
{
|
||||||
|
int numSectors = sectorsPerTrack(track);
|
||||||
|
for (int head = 0; head < numHeads; head++)
|
||||||
|
{
|
||||||
|
for (int sectorId = 0; sectorId < numSectors; sectorId++)
|
||||||
|
{
|
||||||
|
br.seek(offset);
|
||||||
|
Bytes payload = br.read(256);
|
||||||
|
offset += 256;
|
||||||
|
|
||||||
|
std::unique_ptr<Sector>& sector = sectors.get(track, head, sectorId);
|
||||||
|
sector.reset(new Sector);
|
||||||
|
sector->status = Sector::OK;
|
||||||
|
sector->logicalTrack = sector->physicalTrack = track;
|
||||||
|
sector->logicalSide = sector->physicalSide = head;
|
||||||
|
sector->logicalSector = sectorId;
|
||||||
|
sector->data.writer().append(payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sectors;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<ImageReader> ImageReader::createD64ImageReader(
|
||||||
|
const ImageSpec& spec)
|
||||||
|
{
|
||||||
|
return std::unique_ptr<ImageReader>(new D64ImageReader(spec));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -21,6 +21,8 @@ std::map<std::string, ImageReader::Constructor> ImageReader::formats =
|
|||||||
{".st", ImageReader::createImgImageReader},
|
{".st", ImageReader::createImgImageReader},
|
||||||
{".imd", ImageReader::createIMDImageReader},
|
{".imd", ImageReader::createIMDImageReader},
|
||||||
{".IMD", ImageReader::createIMDImageReader},
|
{".IMD", ImageReader::createIMDImageReader},
|
||||||
|
{".d64", ImageReader::createD64ImageReader},
|
||||||
|
{".D64", ImageReader::createD64ImageReader},
|
||||||
};
|
};
|
||||||
|
|
||||||
ImageReader::Constructor ImageReader::findConstructor(const ImageSpec& spec)
|
ImageReader::Constructor ImageReader::findConstructor(const ImageSpec& spec)
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ private:
|
|||||||
static std::unique_ptr<ImageReader> createImgImageReader(const ImageSpec& spec);
|
static std::unique_ptr<ImageReader> createImgImageReader(const ImageSpec& spec);
|
||||||
static std::unique_ptr<ImageReader> createJv3ImageReader(const ImageSpec& spec);
|
static std::unique_ptr<ImageReader> createJv3ImageReader(const ImageSpec& spec);
|
||||||
static std::unique_ptr<ImageReader> createIMDImageReader(const ImageSpec& spec);
|
static std::unique_ptr<ImageReader> createIMDImageReader(const ImageSpec& spec);
|
||||||
|
static std::unique_ptr<ImageReader> createD64ImageReader(const ImageSpec& spec);
|
||||||
|
|
||||||
static Constructor findConstructor(const ImageSpec& spec);
|
static Constructor findConstructor(const ImageSpec& spec);
|
||||||
|
|
||||||
|
|||||||
@@ -162,6 +162,7 @@ buildlibrary libbackend.a \
|
|||||||
lib/imagereader/imgimagereader.cc \
|
lib/imagereader/imgimagereader.cc \
|
||||||
lib/imagereader/jv3imagereader.cc \
|
lib/imagereader/jv3imagereader.cc \
|
||||||
lib/imagereader/imdimagereader.cc \
|
lib/imagereader/imdimagereader.cc \
|
||||||
|
lib/imagereader/d64imagereader.cc \
|
||||||
lib/imagewriter/d64imagewriter.cc \
|
lib/imagewriter/d64imagewriter.cc \
|
||||||
lib/imagewriter/diskcopyimagewriter.cc \
|
lib/imagewriter/diskcopyimagewriter.cc \
|
||||||
lib/imagewriter/imagewriter.cc \
|
lib/imagewriter/imagewriter.cc \
|
||||||
@@ -176,6 +177,7 @@ buildlibrary libbackend.a \
|
|||||||
arch/brother/decoder.cc \
|
arch/brother/decoder.cc \
|
||||||
arch/brother/encoder.cc \
|
arch/brother/encoder.cc \
|
||||||
arch/c64/decoder.cc \
|
arch/c64/decoder.cc \
|
||||||
|
arch/c64/encoder.cc \
|
||||||
arch/f85/decoder.cc \
|
arch/f85/decoder.cc \
|
||||||
arch/fb100/decoder.cc \
|
arch/fb100/decoder.cc \
|
||||||
arch/ibm/decoder.cc \
|
arch/ibm/decoder.cc \
|
||||||
@@ -256,6 +258,7 @@ buildlibrary libfrontend.a \
|
|||||||
src/fe-upgradefluxfile.cc \
|
src/fe-upgradefluxfile.cc \
|
||||||
src/fe-writeamiga.cc \
|
src/fe-writeamiga.cc \
|
||||||
src/fe-writebrother.cc \
|
src/fe-writebrother.cc \
|
||||||
|
src/fe-writec64.cc \
|
||||||
src/fe-writeibm.cc \
|
src/fe-writeibm.cc \
|
||||||
src/fe-writemac.cc \
|
src/fe-writemac.cc \
|
||||||
src/fe-writetids990.cc \
|
src/fe-writetids990.cc \
|
||||||
|
|||||||
24
src/fe-writec64.cc
Normal file
24
src/fe-writec64.cc
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#include "globals.h"
|
||||||
|
#include "flags.h"
|
||||||
|
#include "decoders/decoders.h"
|
||||||
|
#include "encoders/encoders.h"
|
||||||
|
#include "c64/c64.h"
|
||||||
|
#include "writer.h"
|
||||||
|
#include "fmt/format.h"
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
static FlagGroup flags { &writerFlags, &Commodore64EncoderFlags };
|
||||||
|
|
||||||
|
int mainWriteC64(int argc, const char* argv[])
|
||||||
|
{
|
||||||
|
setWriterDefaultInput(":c=40:h=1:s=21:b=256");
|
||||||
|
setWriterDefaultDest(":d=0:t=0-39:s=0");
|
||||||
|
flags.parseFlags(argc, argv);
|
||||||
|
|
||||||
|
Commodore64Encoder encoder;
|
||||||
|
writeDiskCommand(encoder);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -39,6 +39,7 @@ extern command_cb mainWriteAmiga;
|
|||||||
extern command_cb mainWriteBrother;
|
extern command_cb mainWriteBrother;
|
||||||
extern command_cb mainWriteIbm;
|
extern command_cb mainWriteIbm;
|
||||||
extern command_cb mainWriteMac;
|
extern command_cb mainWriteMac;
|
||||||
|
extern command_cb mainWriteC64;
|
||||||
extern command_cb mainWriteTiDs990;
|
extern command_cb mainWriteTiDs990;
|
||||||
extern command_cb mainWriteFlux;
|
extern command_cb mainWriteFlux;
|
||||||
extern command_cb mainWriteTestPattern;
|
extern command_cb mainWriteTestPattern;
|
||||||
@@ -98,6 +99,7 @@ static std::vector<Command> writeables =
|
|||||||
{
|
{
|
||||||
{ "amiga", mainWriteAmiga, "Writes Amiga disks.", },
|
{ "amiga", mainWriteAmiga, "Writes Amiga disks.", },
|
||||||
{ "brother", mainWriteBrother, "Writes 120kB and 240kB Brother word processor disks.", },
|
{ "brother", mainWriteBrother, "Writes 120kB and 240kB Brother word processor disks.", },
|
||||||
|
{ "c64", mainWriteC64, "Writes Commodore 64 disks.", },
|
||||||
{ "ibm", mainWriteIbm, "Writes the ubiquitous IBM format disks.", },
|
{ "ibm", mainWriteIbm, "Writes the ubiquitous IBM format disks.", },
|
||||||
{ "mac", mainWriteMac, "Writes Apple Macintosh disks.", },
|
{ "mac", mainWriteMac, "Writes Apple Macintosh disks.", },
|
||||||
{ "tids990", mainWriteTiDs990, "Writes Texas Instruments DS990 disks.", },
|
{ "tids990", mainWriteTiDs990, "Writes Texas Instruments DS990 disks.", },
|
||||||
|
|||||||
Reference in New Issue
Block a user