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
|
||||
#define C64_H
|
||||
|
||||
#include "decoders/decoders.h"
|
||||
#include "encoders/encoders.h"
|
||||
|
||||
#define C64_SECTOR_RECORD 0xffd49
|
||||
#define C64_DATA_RECORD 0xffd57
|
||||
#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 Fluxmap;
|
||||
|
||||
@@ -18,4 +44,16 @@ public:
|
||||
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
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
/* 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;
|
||||
|
||||
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},
|
||||
{".imd", ImageReader::createIMDImageReader},
|
||||
{".IMD", ImageReader::createIMDImageReader},
|
||||
{".d64", ImageReader::createD64ImageReader},
|
||||
{".D64", ImageReader::createD64ImageReader},
|
||||
};
|
||||
|
||||
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> createJv3ImageReader(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);
|
||||
|
||||
|
||||
@@ -162,6 +162,7 @@ buildlibrary libbackend.a \
|
||||
lib/imagereader/imgimagereader.cc \
|
||||
lib/imagereader/jv3imagereader.cc \
|
||||
lib/imagereader/imdimagereader.cc \
|
||||
lib/imagereader/d64imagereader.cc \
|
||||
lib/imagewriter/d64imagewriter.cc \
|
||||
lib/imagewriter/diskcopyimagewriter.cc \
|
||||
lib/imagewriter/imagewriter.cc \
|
||||
@@ -176,6 +177,7 @@ buildlibrary libbackend.a \
|
||||
arch/brother/decoder.cc \
|
||||
arch/brother/encoder.cc \
|
||||
arch/c64/decoder.cc \
|
||||
arch/c64/encoder.cc \
|
||||
arch/f85/decoder.cc \
|
||||
arch/fb100/decoder.cc \
|
||||
arch/ibm/decoder.cc \
|
||||
@@ -256,6 +258,7 @@ buildlibrary libfrontend.a \
|
||||
src/fe-upgradefluxfile.cc \
|
||||
src/fe-writeamiga.cc \
|
||||
src/fe-writebrother.cc \
|
||||
src/fe-writec64.cc \
|
||||
src/fe-writeibm.cc \
|
||||
src/fe-writemac.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 mainWriteIbm;
|
||||
extern command_cb mainWriteMac;
|
||||
extern command_cb mainWriteC64;
|
||||
extern command_cb mainWriteTiDs990;
|
||||
extern command_cb mainWriteFlux;
|
||||
extern command_cb mainWriteTestPattern;
|
||||
@@ -98,6 +99,7 @@ static std::vector<Command> writeables =
|
||||
{
|
||||
{ "amiga", mainWriteAmiga, "Writes Amiga 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.", },
|
||||
{ "mac", mainWriteMac, "Writes Apple Macintosh disks.", },
|
||||
{ "tids990", mainWriteTiDs990, "Writes Texas Instruments DS990 disks.", },
|
||||
|
||||
Reference in New Issue
Block a user