mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Merge from master.
This commit is contained in:
@@ -103,8 +103,9 @@ at least, check the CRC so what data's there is probably good.
|
||||
| [AES Superplus / No Problem](doc/disk-aeslanier.md) | 🦖 | | hard sectors! |
|
||||
| [Durango F85](doc/disk-durangof85.md) | 🦖 | | 5.25" |
|
||||
| [DVK MX](doc/disk-mx.md) | 🦖 | | Soviet PDP-11 clone |
|
||||
| [Victor 9000](doc/disk-victor9k.md) | 🦖 | | 8-inch |
|
||||
| [Zilog MCZ](doc/disk-zilogmcz.md) | 🦖 | | 8-inch _and_ hard sectors |
|
||||
| [TI DS990 FD1000](doc/disk-tids990.md) | 🦄 | 🦄 | 8" |
|
||||
| [Victor 9000](doc/disk-victor9k.md) | 🦖 | | 8" |
|
||||
| [Zilog MCZ](doc/disk-zilogmcz.md) | 🦖 | | 8" _and_ hard sectors |
|
||||
{: .datatable }
|
||||
|
||||
### Notes
|
||||
|
||||
87
arch/tids990/decoder.cc
Normal file
87
arch/tids990/decoder.cc
Normal file
@@ -0,0 +1,87 @@
|
||||
#include "globals.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "encoders/encoders.h"
|
||||
#include "tids990/tids990.h"
|
||||
#include "crc.h"
|
||||
#include "fluxmap.h"
|
||||
#include "decoders/fluxmapreader.h"
|
||||
#include "sector.h"
|
||||
#include "record.h"
|
||||
#include "track.h"
|
||||
#include <string.h>
|
||||
#include <fmt/format.h>
|
||||
|
||||
/* The Texas Instruments DS990 uses MFM with a scheme similar to a simplified
|
||||
* version of the IBM record scheme (it's actually easier to parse than IBM).
|
||||
* There are 26 sectors per track, each holding a rather weird 288 bytes.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Sector record:
|
||||
* data: 0 1 0 1 0 1 0 1 .0 0 0 0 1 0 1 0 = 0x550a
|
||||
* mfm: 00 01 00 01 00 01 00 01.00 10 10 10 01 00 01 00 = 0x11112a44
|
||||
* special: 00 01 00 01 00 01 00 01.00 10 00 10 01 00 01 00 = 0x11112244
|
||||
* ^^
|
||||
* When shifted out of phase, the special 0xa1 byte becomes an illegal
|
||||
* encoding (you can't do 10 00). So this can't be spoofed by user data.
|
||||
*/
|
||||
const FluxPattern SECTOR_RECORD_PATTERN(32, 0x11112244);
|
||||
|
||||
/*
|
||||
* Data record:
|
||||
* data: 0 1 0 1 0 1 0 1 .0 0 0 0 1 0 1 1 = 0x550c
|
||||
* mfm: 00 01 00 01 00 01 00 01.00 10 10 10 01 00 01 01 = 0x11112a45
|
||||
* special: 00 01 00 01 00 01 00 01.00 10 00 10 01 00 01 01 = 0x11112245
|
||||
* ^^
|
||||
* When shifted out of phase, the special 0xa1 byte becomes an illegal
|
||||
* encoding (you can't do 10 00). So this can't be spoofed by user data.
|
||||
*/
|
||||
const FluxPattern DATA_RECORD_PATTERN(32, 0x11112245);
|
||||
|
||||
const FluxMatchers ANY_RECORD_PATTERN({ &SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN });
|
||||
|
||||
AbstractDecoder::RecordType TiDs990Decoder::advanceToNextRecord()
|
||||
{
|
||||
const FluxMatcher* matcher = nullptr;
|
||||
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
|
||||
if (matcher == &SECTOR_RECORD_PATTERN)
|
||||
return RecordType::SECTOR_RECORD;
|
||||
if (matcher == &DATA_RECORD_PATTERN)
|
||||
return RecordType::DATA_RECORD;
|
||||
return RecordType::UNKNOWN_RECORD;
|
||||
}
|
||||
|
||||
void TiDs990Decoder::decodeSectorRecord()
|
||||
{
|
||||
auto bits = readRawBits(TIDS990_SECTOR_RECORD_SIZE*16);
|
||||
auto bytes = decodeFmMfm(bits).slice(0, TIDS990_SECTOR_RECORD_SIZE);
|
||||
|
||||
ByteReader br(bytes);
|
||||
uint16_t gotChecksum = crc16(CCITT_POLY, bytes.slice(1, TIDS990_SECTOR_RECORD_SIZE-3));
|
||||
|
||||
br.seek(2);
|
||||
_sector->logicalSide = br.read_8() >> 3;
|
||||
_sector->logicalTrack = br.read_8();
|
||||
br.read_8(); /* number of sectors per track */
|
||||
_sector->logicalSector = br.read_8();
|
||||
br.read_be16(); /* sector size */
|
||||
uint16_t wantChecksum = br.read_be16();
|
||||
|
||||
if (wantChecksum == gotChecksum)
|
||||
_sector->status = Sector::DATA_MISSING; /* correct but unintuitive */
|
||||
}
|
||||
|
||||
void TiDs990Decoder::decodeDataRecord()
|
||||
{
|
||||
auto bits = readRawBits(TIDS990_DATA_RECORD_SIZE*16);
|
||||
auto bytes = decodeFmMfm(bits).slice(0, TIDS990_DATA_RECORD_SIZE);
|
||||
|
||||
ByteReader br(bytes);
|
||||
uint16_t gotChecksum = crc16(CCITT_POLY, bytes.slice(1, TIDS990_DATA_RECORD_SIZE-3));
|
||||
|
||||
br.seek(2);
|
||||
_sector->data = br.read(TIDS990_PAYLOAD_SIZE);
|
||||
uint16_t wantChecksum = br.read_be16();
|
||||
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
|
||||
176
arch/tids990/encoder.cc
Normal file
176
arch/tids990/encoder.cc
Normal file
@@ -0,0 +1,176 @@
|
||||
#include "globals.h"
|
||||
#include "record.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "encoders/encoders.h"
|
||||
#include "tids990.h"
|
||||
#include "crc.h"
|
||||
#include "sectorset.h"
|
||||
#include "writer.h"
|
||||
#include <fmt/format.h>
|
||||
|
||||
FlagGroup tids990EncoderFlags;
|
||||
|
||||
static IntFlag trackLengthMs(
|
||||
{ "--tids990-track-length-ms" },
|
||||
"Length of a track in milliseconds.",
|
||||
166);
|
||||
|
||||
static IntFlag sectorCount(
|
||||
{ "--tids990-sector-count" },
|
||||
"Number of sectors per track.",
|
||||
26);
|
||||
|
||||
static IntFlag clockRateKhz(
|
||||
{ "--tids990-clock-rate-khz" },
|
||||
"Clock rate of data to write.",
|
||||
500);
|
||||
|
||||
static HexIntFlag am1Byte(
|
||||
{ "--tids990-am1-byte" },
|
||||
"16-bit RAW bit pattern to use for the AM1 ID byte",
|
||||
0x2244);
|
||||
|
||||
static HexIntFlag am2Byte(
|
||||
{ "--tids990-am2-byte" },
|
||||
"16-bit RAW bit pattern to use for the AM2 ID byte",
|
||||
0x2245);
|
||||
|
||||
static IntFlag gap1(
|
||||
{ "--tids990-gap1-bytes" },
|
||||
"Size of gap 1 (the post-index gap).",
|
||||
80);
|
||||
|
||||
static IntFlag gap2(
|
||||
{ "--tids990-gap2-bytes" },
|
||||
"Size of gap 2 (the post-ID gap).",
|
||||
21);
|
||||
|
||||
static IntFlag gap3(
|
||||
{ "--tids990-gap3-bytes" },
|
||||
"Size of gap 3 (the post-data or format gap).",
|
||||
51);
|
||||
|
||||
static StringFlag sectorSkew(
|
||||
{ "--tids990-sector-skew" },
|
||||
"Order to emit sectors.",
|
||||
"1mhc72nid83oje94pkfa50lgb6");
|
||||
|
||||
static int charToInt(char c)
|
||||
{
|
||||
if (isdigit(c))
|
||||
return c - '0';
|
||||
return 10 + tolower(c) - 'a';
|
||||
}
|
||||
|
||||
void TiDs990Encoder::writeRawBits(uint32_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;
|
||||
}
|
||||
}
|
||||
|
||||
void TiDs990Encoder::writeBytes(const Bytes& bytes)
|
||||
{
|
||||
encodeMfm(_bits, _cursor, bytes, _lastBit);
|
||||
}
|
||||
|
||||
void TiDs990Encoder::writeBytes(int count, uint8_t byte)
|
||||
{
|
||||
Bytes bytes = { byte };
|
||||
for (int i=0; i<count; i++)
|
||||
writeBytes(bytes);
|
||||
}
|
||||
|
||||
static uint8_t decodeUint16(uint16_t raw)
|
||||
{
|
||||
Bytes b;
|
||||
ByteWriter bw(b);
|
||||
bw.write_be16(raw);
|
||||
return decodeFmMfm(b.toBits())[0];
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> TiDs990Encoder::encode(
|
||||
int physicalTrack, int physicalSide, const SectorSet& allSectors)
|
||||
{
|
||||
double clockRateUs = 1e3 / clockRateKhz / 2.0;
|
||||
int bitsPerRevolution = (trackLengthMs * 1000.0) / clockRateUs;
|
||||
_bits.resize(bitsPerRevolution);
|
||||
_cursor = 0;
|
||||
|
||||
uint8_t am1Unencoded = decodeUint16(am1Byte);
|
||||
uint8_t am2Unencoded = decodeUint16(am2Byte);
|
||||
|
||||
writeBytes(gap1, 0x55);
|
||||
|
||||
bool first = true;
|
||||
for (char sectorChar : sectorSkew.get())
|
||||
{
|
||||
int sectorId = charToInt(sectorChar);
|
||||
if (!first)
|
||||
writeBytes(gap3, 0x55);
|
||||
first = false;
|
||||
|
||||
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
|
||||
if (!sectorData)
|
||||
Error() << fmt::format("format tried to find sector {} which wasn't in the input file", sectorId);
|
||||
|
||||
/* Writing the sector and data records are fantastically annoying.
|
||||
* The CRC is calculated from the *very start* of the record, and
|
||||
* include the malformed marker bytes. Our encoder doesn't know
|
||||
* about this, of course, with the result that we have to construct
|
||||
* the unencoded header, calculate the checksum, and then use the
|
||||
* same logic to emit the bytes which require special encoding
|
||||
* before encoding the rest of the header normally. */
|
||||
|
||||
{
|
||||
Bytes header;
|
||||
ByteWriter bw(header);
|
||||
|
||||
writeBytes(12, 0x55);
|
||||
bw.write_8(am1Unencoded);
|
||||
bw.write_8(sectorData->logicalSide << 3);
|
||||
bw.write_8(sectorData->logicalTrack);
|
||||
bw.write_8(sectorCount);
|
||||
bw.write_8(sectorData->logicalSector);
|
||||
bw.write_be16(sectorData->data.size());
|
||||
uint16_t crc = crc16(CCITT_POLY, header);
|
||||
bw.write_be16(crc);
|
||||
|
||||
writeRawBits(am1Byte, 16);
|
||||
writeBytes(header.slice(1));
|
||||
}
|
||||
|
||||
writeBytes(gap2, 0x55);
|
||||
|
||||
{
|
||||
Bytes data;
|
||||
ByteWriter bw(data);
|
||||
|
||||
writeBytes(12, 0x55);
|
||||
bw.write_8(am2Unencoded);
|
||||
|
||||
bw += sectorData->data;
|
||||
uint16_t crc = crc16(CCITT_POLY, data);
|
||||
bw.write_be16(crc);
|
||||
|
||||
writeRawBits(am2Byte, 16);
|
||||
writeBytes(data.slice(1));
|
||||
}
|
||||
}
|
||||
|
||||
if (_cursor >= _bits.size())
|
||||
Error() << "track data overrun";
|
||||
while (_cursor < _bits.size())
|
||||
writeBytes(1, 0x55);
|
||||
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(_bits, clockRateUs*1e3);
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
47
arch/tids990/tids990.h
Normal file
47
arch/tids990/tids990.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#ifndef TIDS990_H
|
||||
#define TIDS990_H
|
||||
|
||||
#define TIDS990_PAYLOAD_SIZE 288 /* bytes */
|
||||
#define TIDS990_SECTOR_RECORD_SIZE 10 /* bytes */
|
||||
#define TIDS990_DATA_RECORD_SIZE (TIDS990_PAYLOAD_SIZE + 4) /* bytes */
|
||||
|
||||
class Sector;
|
||||
class SectorSet;
|
||||
class Fluxmap;
|
||||
class Track;
|
||||
|
||||
class TiDs990Decoder : public AbstractDecoder
|
||||
{
|
||||
public:
|
||||
virtual ~TiDs990Decoder() {}
|
||||
|
||||
RecordType advanceToNextRecord();
|
||||
void decodeSectorRecord();
|
||||
void decodeDataRecord();
|
||||
};
|
||||
|
||||
class TiDs990Encoder : public AbstractEncoder
|
||||
{
|
||||
public:
|
||||
virtual ~TiDs990Encoder() {}
|
||||
|
||||
private:
|
||||
void writeRawBits(uint32_t data, int width);
|
||||
void writeBytes(const Bytes& bytes);
|
||||
void writeBytes(int count, uint8_t value);
|
||||
void writeSync();
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
|
||||
|
||||
private:
|
||||
std::vector<bool> _bits;
|
||||
unsigned _cursor;
|
||||
bool _lastBit;
|
||||
};
|
||||
|
||||
extern FlagGroup tids990EncoderFlags;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -45,22 +45,20 @@ Reading discs
|
||||
Just do:
|
||||
|
||||
```
|
||||
fluxengine read mac
|
||||
fluxengine read mac -o mac.diskcopy
|
||||
```
|
||||
|
||||
You should end up with an `mac.img` which is 1001888 bytes long (for a normal
|
||||
DD disk). If you want the single-sided variety, use `-s :s=0`.
|
||||
You should end up with a `mac.diskcopy` file which is compatible with DiskCopy
|
||||
4.2, which most Mac emulators support.
|
||||
|
||||
**Big warning!** The image may not work in an emulator. Mac disk images are
|
||||
complicated due to the way the tracks are different sizes and the odd sector
|
||||
size. FluxEngine chooses to store them in a simple 524 x 12 x 2 x 80 layout,
|
||||
with holes where missing sectors should be. This was easiest. If anyone can
|
||||
suggest a better way, please [get in
|
||||
touch](https://github.com/davidgiven/fluxengine/issues/new).
|
||||
|
||||
The 12 bytes of metadata _follow_ the 512 bytes of user payload in the sector
|
||||
image. If you don't want it, specify a geometry in the output file with a
|
||||
512-byte sectore size like `-o mac.img:c=80:h=1:s=12:b=512`.
|
||||
**Big warning!** Mac disk images are complicated due to the way the tracks are
|
||||
different sizes and the odd sector size. If you use a normal `.img` file, then
|
||||
FluxEngine will store them in a simple 524 x 12 x 2 x 80 layout, with holes
|
||||
where missing sectors should be; this was easiest, but is unlikely to work with
|
||||
most Mac emulators and other software. In these files, the The 12 bytes of
|
||||
metadata _follow_ the 512 bytes of user payload in the sector image. If you
|
||||
don't want it, specify a geometry in the output file with a 512-byte sectore
|
||||
size like `-o mac.img:c=80:h=1:s=12:b=512`.
|
||||
|
||||
Useful references
|
||||
-----------------
|
||||
@@ -74,3 +72,7 @@ Useful references
|
||||
|
||||
- [Les Disquettes et le drive Disk II](http://www.hackzapple.com/DISKII/DISKIITECH.HTM), an
|
||||
epicly detailed writeup of the Apple II disk format (which is closely related)
|
||||
|
||||
- [The DiskCopy 4.2
|
||||
format](https://www.discferret.com/wiki/Apple_DiskCopy_4.2), described on
|
||||
the DiskFerret website.
|
||||
|
||||
46
doc/disk-tids990.md
Normal file
46
doc/disk-tids990.md
Normal file
@@ -0,0 +1,46 @@
|
||||
Disk: TI DS990 FD1000
|
||||
=====================
|
||||
|
||||
The Texas Instruments DS990 was a multiuser modular computing system from 1998,
|
||||
based around the TMS-9900 processor (as used by the TI-99). It had an 8" floppy
|
||||
drive module, the FD1000, which was a 77-track, 288-byte sector FM/MFM system
|
||||
with 26 sectors per track. The encoding scheme was very similar to a simplified
|
||||
version of the IBM scheme, but of course not compatible. A double-sided disk
|
||||
would store a very satisfactory 1126kB of data; here's one at <a
|
||||
href="https://www.old-computers.com/museum/computer.asp?st=1&c=1025">old-computers.com</a>:
|
||||
|
||||
<div style="text-align: center">
|
||||
<a href="https://www.old-computers.com/museum/computer.asp?st=1&c=1025">
|
||||
<img src="tids990.jpg" style="max-width: 60%" alt="A DS990 at old-computers.com"></a>
|
||||
</div>
|
||||
|
||||
FluxEngine will read and write these (but only the DSDD MFM variant).
|
||||
|
||||
Reading discs
|
||||
-------------
|
||||
|
||||
Just do:
|
||||
|
||||
```
|
||||
fluxengine read tids990
|
||||
```
|
||||
|
||||
You should end up with an `tids990.img` which is 1153152 bytes long.
|
||||
|
||||
Writing discs
|
||||
-------------
|
||||
|
||||
Just do:
|
||||
|
||||
```
|
||||
fluxengine write tids990 -i tids990.img
|
||||
```
|
||||
|
||||
|
||||
Useful references
|
||||
-----------------
|
||||
|
||||
- [The FD1000 Depot Maintenance
|
||||
Manual](http://www.bitsavers.org/pdf/ti/990/disk/2261885-9701_FD1000depotVo1_Jan81.pdf)
|
||||
|
||||
|
||||
BIN
doc/tids990.jpg
Normal file
BIN
doc/tids990.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
@@ -132,6 +132,10 @@ exact format varies according to the extension:
|
||||
FluxEngine's D64 support is currently limited to write only. It will work
|
||||
with up to 40 logical tracks.
|
||||
|
||||
- `.diskcopy`: a Macintosh DiskCopy 4.2 file. This is a special-purpose
|
||||
format due to the weird layout of Mac GCR disks, but it can also support
|
||||
720kB and 1440kB IBM disks (although there's no real benefit).
|
||||
|
||||
### High density disks
|
||||
|
||||
High density disks use a different magnetic medium to low and double density
|
||||
|
||||
13
lib/bytes.h
13
lib/bytes.h
@@ -1,6 +1,8 @@
|
||||
#ifndef BYTES_H
|
||||
#define BYTES_H
|
||||
|
||||
#include <string.h>
|
||||
|
||||
class ByteReader;
|
||||
class ByteWriter;
|
||||
|
||||
@@ -10,6 +12,7 @@ public:
|
||||
Bytes();
|
||||
Bytes(unsigned size);
|
||||
Bytes(const uint8_t* ptr, size_t len);
|
||||
Bytes(const std::string& data);
|
||||
Bytes(std::initializer_list<uint8_t> data);
|
||||
Bytes(std::shared_ptr<std::vector<uint8_t>> data);
|
||||
Bytes(std::shared_ptr<std::vector<uint8_t>> data, unsigned start, unsigned end);
|
||||
@@ -271,6 +274,16 @@ public:
|
||||
|
||||
ByteWriter& operator += (std::istream& stream);
|
||||
|
||||
ByteWriter& append(const char* data)
|
||||
{
|
||||
return *this += Bytes((const uint8_t*)data, strlen(data));
|
||||
}
|
||||
|
||||
ByteWriter& append(const std::string& data)
|
||||
{
|
||||
return *this += Bytes(data);
|
||||
}
|
||||
|
||||
ByteWriter& append(const Bytes data)
|
||||
{
|
||||
return *this += data;
|
||||
|
||||
169
lib/imagewriter/diskcopyimagewriter.cc
Normal file
169
lib/imagewriter/diskcopyimagewriter.cc
Normal file
@@ -0,0 +1,169 @@
|
||||
#include "globals.h"
|
||||
#include "flags.h"
|
||||
#include "dataspec.h"
|
||||
#include "sector.h"
|
||||
#include "sectorset.h"
|
||||
#include "imagewriter/imagewriter.h"
|
||||
#include "fmt/format.h"
|
||||
#include "ldbs.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
static const char LABEL[] = "FluxEngine image";
|
||||
|
||||
static void write_and_update_checksum(ByteWriter& bw, uint32_t& checksum, const Bytes& data)
|
||||
{
|
||||
ByteReader br(data);
|
||||
while (!br.eof())
|
||||
{
|
||||
uint32_t i = br.read_be16();
|
||||
checksum += i;
|
||||
checksum = (checksum >> 1) | (checksum << 31);
|
||||
bw.write_be16(i);
|
||||
}
|
||||
}
|
||||
|
||||
class DiskCopyImageWriter : public ImageWriter
|
||||
{
|
||||
public:
|
||||
DiskCopyImageWriter(const SectorSet& sectors, const ImageSpec& spec):
|
||||
ImageWriter(sectors, spec)
|
||||
{}
|
||||
|
||||
void writeImage()
|
||||
{
|
||||
bool mfm = false;
|
||||
|
||||
if (spec.bytes == 524)
|
||||
{
|
||||
/* GCR disk */
|
||||
}
|
||||
else if (spec.bytes == 512)
|
||||
{
|
||||
/* MFM disk */
|
||||
mfm = true;
|
||||
}
|
||||
else
|
||||
Error() << "this image is not compatible with the DiskCopy 4.2 format";
|
||||
|
||||
|
||||
std::cout << "writing DiskCopy 4.2 image\n"
|
||||
<< fmt::format("{} tracks, {} heads, {} sectors, {} bytes per sector; {}\n",
|
||||
spec.cylinders, spec.heads, spec.sectors, spec.bytes,
|
||||
mfm ? "MFM" : "GCR");
|
||||
|
||||
auto sectors_per_track = [&](int track) -> int
|
||||
{
|
||||
if (mfm)
|
||||
return spec.sectors;
|
||||
|
||||
if (track < 16)
|
||||
return 12;
|
||||
if (track < 32)
|
||||
return 11;
|
||||
if (track < 48)
|
||||
return 10;
|
||||
if (track < 64)
|
||||
return 9;
|
||||
return 8;
|
||||
};
|
||||
|
||||
Bytes image;
|
||||
ByteWriter bw(image);
|
||||
|
||||
/* Write the actual sectr data. */
|
||||
|
||||
uint32_t dataChecksum = 0;
|
||||
uint32_t tagChecksum = 0;
|
||||
uint32_t offset = 0x54;
|
||||
uint32_t sectorDataStart = offset;
|
||||
for (int track = 0; track < spec.cylinders; track++)
|
||||
{
|
||||
for (int head = 0; head < spec.heads; head++)
|
||||
{
|
||||
int sectorCount = sectors_per_track(track);
|
||||
for (int sectorId = 0; sectorId < sectorCount; sectorId++)
|
||||
{
|
||||
const auto& sector = sectors.get(track, head, sectorId);
|
||||
if (sector)
|
||||
{
|
||||
bw.seek(offset);
|
||||
write_and_update_checksum(bw, dataChecksum, sector->data.slice(0, 512));
|
||||
}
|
||||
offset += 512;
|
||||
}
|
||||
}
|
||||
}
|
||||
uint32_t sectorDataEnd = offset;
|
||||
if (!mfm)
|
||||
{
|
||||
for (int track = 0; track < spec.cylinders; track++)
|
||||
{
|
||||
for (int head = 0; head < spec.heads; head++)
|
||||
{
|
||||
int sectorCount = sectors_per_track(track);
|
||||
for (int sectorId = 0; sectorId < sectorCount; sectorId++)
|
||||
{
|
||||
const auto& sector = sectors.get(track, head, sectorId);
|
||||
if (sector)
|
||||
{
|
||||
bw.seek(offset);
|
||||
write_and_update_checksum(bw, tagChecksum, sector->data.slice(512, 12));
|
||||
}
|
||||
offset += 12;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
uint32_t tagDataEnd = offset;
|
||||
|
||||
/* Write the header. */
|
||||
|
||||
uint8_t encoding;
|
||||
uint8_t format;
|
||||
if (mfm)
|
||||
{
|
||||
format = 0x22;
|
||||
if (spec.sectors == 18)
|
||||
encoding = 3;
|
||||
else
|
||||
encoding = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (spec.heads == 2)
|
||||
{
|
||||
encoding = 1;
|
||||
format = 0x22;
|
||||
}
|
||||
else
|
||||
{
|
||||
encoding = 0;
|
||||
format = 0x02;
|
||||
}
|
||||
}
|
||||
|
||||
bw.seek(0);
|
||||
bw.write_8(sizeof(LABEL));
|
||||
bw.append(LABEL);
|
||||
bw.seek(0x40);
|
||||
bw.write_be32(sectorDataEnd - sectorDataStart); /* data size */
|
||||
bw.write_be32(tagDataEnd - sectorDataEnd); /* tag size */
|
||||
bw.write_be32(dataChecksum); /* data checksum */
|
||||
bw.write_be32(tagChecksum); /* tag checksum */
|
||||
bw.write_8(encoding); /* encoding */
|
||||
bw.write_8(format); /* format byte */
|
||||
bw.write_be16(0x0100); /* magic number */
|
||||
|
||||
image.writeToFile(spec.filename);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<ImageWriter> ImageWriter::createDiskCopyImageWriter(
|
||||
const SectorSet& sectors, const ImageSpec& spec)
|
||||
{
|
||||
return std::unique_ptr<ImageWriter>(new DiskCopyImageWriter(sectors, spec));
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ std::map<std::string, ImageWriter::Constructor> ImageWriter::formats =
|
||||
{".adf", ImageWriter::createImgImageWriter},
|
||||
{".d64", ImageWriter::createD64ImageWriter},
|
||||
{".d81", ImageWriter::createImgImageWriter},
|
||||
{".diskcopy", ImageWriter::createDiskCopyImageWriter},
|
||||
{".img", ImageWriter::createImgImageWriter},
|
||||
{".ldbs", ImageWriter::createLDBSImageWriter},
|
||||
};
|
||||
|
||||
@@ -29,6 +29,8 @@ private:
|
||||
const SectorSet& sectors, const ImageSpec& spec);
|
||||
static std::unique_ptr<ImageWriter> createD64ImageWriter(
|
||||
const SectorSet& sectors, const ImageSpec& spec);
|
||||
static std::unique_ptr<ImageWriter> createDiskCopyImageWriter(
|
||||
const SectorSet& sectors, const ImageSpec& spec);
|
||||
|
||||
static Constructor findConstructor(const ImageSpec& spec);
|
||||
|
||||
|
||||
@@ -154,6 +154,7 @@ buildlibrary libbackend.a \
|
||||
lib/imagereader/imagereader.cc \
|
||||
lib/imagereader/imgimagereader.cc \
|
||||
lib/imagewriter/d64imagewriter.cc \
|
||||
lib/imagewriter/diskcopyimagewriter.cc \
|
||||
lib/imagewriter/imagewriter.cc \
|
||||
lib/imagewriter/imgimagewriter.cc \
|
||||
lib/imagewriter/ldbsimagewriter.cc \
|
||||
@@ -171,6 +172,8 @@ buildlibrary libbackend.a \
|
||||
arch/ibm/encoder.cc \
|
||||
arch/macintosh/decoder.cc \
|
||||
arch/mx/decoder.cc \
|
||||
arch/tids990/decoder.cc \
|
||||
arch/tids990/encoder.cc \
|
||||
arch/victor9k/decoder.cc \
|
||||
arch/zilogmcz/decoder.cc \
|
||||
lib/bytes.cc \
|
||||
@@ -222,6 +225,7 @@ buildlibrary libfrontend.a \
|
||||
src/fe-readibm.cc \
|
||||
src/fe-readmac.cc \
|
||||
src/fe-readmx.cc \
|
||||
src/fe-readtids990.cc \
|
||||
src/fe-readvictor9k.cc \
|
||||
src/fe-readzilogmcz.cc \
|
||||
src/fe-rpm.cc \
|
||||
@@ -233,6 +237,7 @@ buildlibrary libfrontend.a \
|
||||
src/fe-writeamiga.cc \
|
||||
src/fe-writebrother.cc \
|
||||
src/fe-writeibm.cc \
|
||||
src/fe-writetids990.cc \
|
||||
src/fe-writeflux.cc \
|
||||
src/fe-writetestpattern.cc \
|
||||
src/fluxengine.cc \
|
||||
|
||||
27
src/fe-readtids990.cc
Normal file
27
src/fe-readtids990.cc
Normal file
@@ -0,0 +1,27 @@
|
||||
#include "globals.h"
|
||||
#include "flags.h"
|
||||
#include "reader.h"
|
||||
#include "fluxmap.h"
|
||||
#include "encoders/encoders.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "sector.h"
|
||||
#include "sectorset.h"
|
||||
#include "record.h"
|
||||
#include "tids990/tids990.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
int mainReadTiDs990(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-76");
|
||||
setReaderDefaultOutput("tids990.img");
|
||||
setReaderRevolutions(2);
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
TiDs990Decoder decoder;
|
||||
readDiskCommand(decoder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
23
src/fe-writetids990.cc
Normal file
23
src/fe-writetids990.cc
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "globals.h"
|
||||
#include "flags.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "encoders/encoders.h"
|
||||
#include "tids990/tids990.h"
|
||||
#include "writer.h"
|
||||
#include "fmt/format.h"
|
||||
#include <fstream>
|
||||
|
||||
static FlagGroup flags { &writerFlags, &tids990EncoderFlags };
|
||||
|
||||
int mainWriteTiDs990(int argc, const char* argv[])
|
||||
{
|
||||
setWriterDefaultInput(":c=77:h=2:s=26:b=288");
|
||||
setWriterDefaultDest(":d=0:t=0-76:s=0-1");
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
TiDs990Encoder encoder;
|
||||
writeDiskCommand(encoder);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ extern command_cb mainReadFB100;
|
||||
extern command_cb mainReadIBM;
|
||||
extern command_cb mainReadMac;
|
||||
extern command_cb mainReadMx;
|
||||
extern command_cb mainReadTiDs990;
|
||||
extern command_cb mainReadVictor9K;
|
||||
extern command_cb mainReadZilogMCZ;
|
||||
extern command_cb mainRpm;
|
||||
@@ -32,6 +33,7 @@ extern command_cb mainUpgradeFluxFile;
|
||||
extern command_cb mainWriteAmiga;
|
||||
extern command_cb mainWriteBrother;
|
||||
extern command_cb mainWriteIbm;
|
||||
extern command_cb mainWriteTiDs990;
|
||||
extern command_cb mainWriteFlux;
|
||||
extern command_cb mainWriteTestPattern;
|
||||
|
||||
@@ -77,6 +79,7 @@ static std::vector<Command> readables =
|
||||
{ "ibm", mainReadIBM, "Reads the ubiquitous IBM format disks.", },
|
||||
{ "mac", mainReadMac, "Reads Apple Macintosh disks.", },
|
||||
{ "mx", mainReadMx, "Reads MX disks.", },
|
||||
{ "tids990", mainReadTiDs990, "Reads Texas Instruments DS990 disks.", },
|
||||
{ "victor9k", mainReadVictor9K, "Reads Victor 9000 disks.", },
|
||||
{ "zilogmcz", mainReadZilogMCZ, "Reads Zilog MCZ disks.", },
|
||||
};
|
||||
@@ -86,6 +89,7 @@ static std::vector<Command> writeables =
|
||||
{ "amiga", mainWriteAmiga, "Writes Amiga disks.", },
|
||||
{ "brother", mainWriteBrother, "Writes 120kB and 240kB Brother word processor disks.", },
|
||||
{ "ibm", mainWriteIbm, "Writes the ubiquitous IBM format disks.", },
|
||||
{ "tids990", mainWriteTiDs990, "Writes Texas Instruments DS990 disks.", },
|
||||
};
|
||||
|
||||
static std::vector<Command> convertables =
|
||||
|
||||
Reference in New Issue
Block a user