openMSX
RawTrack.hh
Go to the documentation of this file.
1 #ifndef RAWTRACK_HH
2 #define RAWTRACK_HH
3 
4 #include "openmsx.hh"
5 #include "serialize_meta.hh"
6 #include "span.hh"
7 #include <optional>
8 #include <vector>
9 
10 namespace openmsx {
11 
12 class CRC16;
13 
14 // This class represents a raw disk track. It contains the logical sector
15 // content, but also address blocks, CRC checksums, sync blocks and the data
16 // in the gaps between these blocks.
17 //
18 // The internal representation is based on the DMK disk image file format. See:
19 // http://www.trs-80.com/wordpress/dsk-and-dmk-image-utilities/
20 // (at the bottom of the page)
21 //
22 // Besides the raw track data, this format also stores the positions of the
23 // 'address marks' in the track (this roughly corresponds with the start of a
24 // sector). Of course a real disk doesn't have such a list. In a real disk this
25 // information is stored as 'MFM encodings with missing clock transitions'.
26 //
27 // Normal MFM encoding goes like this: An input bit of '0' is encoded as 'x0'
28 // with x the inverse of the previously encoded bit. A '1' input bit is encoded
29 // as '01'. (More in detail: a '1' encoded bit indicates a magnetic flux
30 // transition, a '0' bit is no flux transition). So for example the input byte
31 // 0xA1 (binary 10100001) is MFM encoded as '01 00 01 00 10 10 10 01'. (Note
32 // that decoding is simply taking every 2nd bit (the data bits), the other bits
33 // (the clock bits) ensure that between encoded '1' bits is always at least 1
34 // and at most 3 zero bits. So no too dense flux transitions and not too far
35 // apart to keep the stream synchronized).
36 //
37 // Now for the missing clock transitions: besides the encodings for the 256
38 // possible input bytes, the FDC can write two other encodings, namely:
39 // 01 00 01 00 10 00 10 01 (0xA1 with missing clock between bit 4 and 5)
40 // 01 01 00 10 00 10 01 00 (0xC2 with missing clock between bit 3 and 4)
41 //
42 // So in principle we should store each of these special 0xA1 or 0xC2 bytes.
43 // Instead we only store the locations of '0xA1 0xA1 0xA1 0xFE' sequences (the
44 // 0xA1 bytes have missing clocks, this sequence indicates the start of an
45 // address mark). So we don't store the location of the special 0xC2 bytes, nor
46 // the location of each special 0xA1 byte. These certainly do occur on a real
47 // disk track, but the WD2793 controller only reacts to the full sequence
48 // above, so for us this is good enough. (The FDC also uses these special
49 // encodings to re-synchronize itself with the input stream, e.g. figure out
50 // which bit is the start bit in a byte, but from a functional emulation point
51 // of view we can ignore this).
52 //
53 // Also note that it's possible to create real disks that have still completely
54 // different magnetic flux patterns than the 256+2 possible MFM patterns
55 // described above. Such disks cannot be described by this class. But for
56 // openMSX that's not a problem because the WD2793 or TC8566AF disk controllers
57 // anyway can't handle such disks (they would always interpret the flux pattern
58 // as one of the 256+2 MFM patterns). Other systems (like Amiga) have disk
59 // controllers that allow more direct access to the disk and could for example
60 // encode the data in a more efficient way than MFM (e.g. GCR6). That's why
61 // Amiga can fit more data on the same disk (even more than simply storing 10
62 // or 11 sectors on a track by making the gaps between the sectors smaller).
63 
64 class RawTrack {
65 public:
66  // Typical track length is 6250 bytes:
67  // 250kbps, 300rpm -> 6250 bytes per rotation.
68  // The IBM Disk Format Specification confirms this number.
69  // Of course this is in ideal circumstances: in reality the rotation
70  // speed can vary and thus the disk can be formatted with slightly more
71  // or slightly less raw bytes per track. This class can also represent
72  // tracks of different lengths.
73  static constexpr unsigned STANDARD_SIZE = 6250;
74 
75  struct Sector
76  {
77  // note: initialize to avoid UMR on savestate
78  int addrIdx = 0;
79  int dataIdx = 0;
80  byte track = 0;
81  byte head = 0;
82  byte sector = 0;
83  byte sizeCode = 0;
84  bool deleted = false;
85  bool addrCrcErr = false;
86  bool dataCrcErr = false;
87 
88  template<typename Archive>
89  void serialize(Archive& ar, unsigned version);
90  };
91 
92  /* Construct a (cleared) track. */
93  explicit RawTrack(unsigned size = STANDARD_SIZE);
94 
96  void clear(unsigned size);
97 
99  [[nodiscard]] unsigned getLength() const { return unsigned(data.size()); }
100 
101  void addIdam(unsigned idx);
102 
103  // In the methods below, 'index' is allowed to be 'out-of-bounds',
104  // it will wrap like in a circular buffer.
105 
106  [[nodiscard]] byte read(int idx) const { return data[wrapIndex(idx)]; }
107  void write(int idx, byte val, bool setIdam = false);
108  [[nodiscard]] int wrapIndex(int idx) const {
109  // operator% is not a modulo but a remainder operation (makes a
110  // difference for negative inputs). Hence the extra test.
111  int tmp = idx % int(data.size());
112  return (tmp >= 0) ? tmp : int(tmp + data.size());
113  }
114 
115  [[nodiscard]] byte* getRawBuffer() { return data.data(); }
116  [[nodiscard]] const byte* getRawBuffer() const { return data.data(); }
117  [[nodiscard]] const std::vector<unsigned>& getIdamBuffer() const { return idam; }
118 
120  [[nodiscard]] std::vector<Sector> decodeAll() const;
121 
123  [[nodiscard]] std::optional<Sector> decodeNextSector(unsigned startIdx) const;
124 
130  [[nodiscard]] std::optional<Sector> decodeSector(byte sectorNum) const;
131 
133  void readBlock (int idx, span<byte> destination) const;
134  void writeBlock(int idx, span<const byte> source);
135 
137  [[nodiscard]] word calcCrc(int idx, int size) const;
138  void updateCrc(CRC16& crc, int idx, int size) const;
139 
141 
142  template<typename Archive>
143  void serialize(Archive& ar, unsigned version);
144 
145 private:
146  [[nodiscard]] std::optional<Sector> decodeSectorImpl(int idx) const;
147 
148 private:
149  // Index into 'data'-array to positions where an address mark
150  // starts (it points to the 'FE' byte in the 'A1 A1 A1 FE ..'
151  // sequence.
152  std::vector<unsigned> idam;
153 
154  // MFM-decoded raw data, this does NOT include the missing clock
155  // transitions that can occur in the encodings of the 'A1' and
156  // 'C2' bytes.
157  std::vector<byte> data;
158 };
160 
161 } // namespace openmsx
162 
163 #endif
openmsx::RawTrack::decodeAll
std::vector< Sector > decodeAll() const
Get info on all sectors in this track.
Definition: RawTrack.cc:112
openmsx.hh
openmsx::RawTrack::getRawBuffer
byte * getRawBuffer()
Definition: RawTrack.hh:115
utf8::unchecked::size
size_t size(std::string_view utf8)
Definition: utf8_unchecked.hh:233
serialize_meta.hh
openmsx::RawTrack
Definition: RawTrack.hh:64
openmsx::RawTrack::serialize
void serialize(Archive &ar, unsigned version)
Definition: RawTrack.cc:266
openmsx::RawTrack::Sector::sector
byte sector
Definition: RawTrack.hh:82
openmsx::RawTrack::Sector::head
byte head
Definition: RawTrack.hh:81
openmsx::RawTrack::getLength
unsigned getLength() const
Get track length.
Definition: RawTrack.hh:99
span
Definition: span.hh:126
openmsx::SERIALIZE_CLASS_VERSION
SERIALIZE_CLASS_VERSION(CassettePlayer, 2)
openmsx::RawTrack::Sector::sizeCode
byte sizeCode
Definition: RawTrack.hh:83
openmsx::RawTrack::write
void write(int idx, byte val, bool setIdam=false)
Definition: RawTrack.cc:33
openmsx::RawTrack::applyWd2793ReadTrackQuirk
void applyWd2793ReadTrackQuirk()
Definition: RawTrack.cc:192
openmsx::RawTrack::updateCrc
void updateCrc(CRC16 &crc, int idx, int size) const
Definition: RawTrack.cc:172
openmsx::CRC16
This class calculates CRC numbers for the polygon x^16 + x^12 + x^5 + 1.
Definition: CRC16.hh:17
openmsx::RawTrack::calcCrc
word calcCrc(int idx, int size) const
Convenience method to calculate CRC for part of this track.
Definition: RawTrack.cc:185
openmsx::RawTrack::Sector::dataCrcErr
bool dataCrcErr
Definition: RawTrack.hh:86
openmsx::RawTrack::read
byte read(int idx) const
Definition: RawTrack.hh:106
openmsx::RawTrack::Sector::track
byte track
Definition: RawTrack.hh:80
openmsx::RawTrack::Sector::addrIdx
int addrIdx
Definition: RawTrack.hh:78
openmsx::RawTrack::addIdam
void addIdam(unsigned idx)
Definition: RawTrack.cc:26
openmsx::RawTrack::readBlock
void readBlock(int idx, span< byte > destination) const
Like memcpy() but copy from/to circular buffer.
Definition: RawTrack.cc:159
openmsx::RawTrack::wrapIndex
int wrapIndex(int idx) const
Definition: RawTrack.hh:108
span.hh
openmsx::RawTrack::Sector::deleted
bool deleted
Definition: RawTrack.hh:84
openmsx::RawTrack::STANDARD_SIZE
static constexpr unsigned STANDARD_SIZE
Definition: RawTrack.hh:73
openmsx::RawTrack::Sector
Definition: RawTrack.hh:76
openmsx::RawTrack::Sector::addrCrcErr
bool addrCrcErr
Definition: RawTrack.hh:85
openmsx::RawTrack::decodeSector
std::optional< Sector > decodeSector(byte sectorNum) const
Get a sector with a specific number.
Definition: RawTrack.cc:145
openmsx::word
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
openmsx::RawTrack::getRawBuffer
const byte * getRawBuffer() const
Definition: RawTrack.hh:116
openmsx::RawTrack::RawTrack
RawTrack(unsigned size=STANDARD_SIZE)
Definition: RawTrack.cc:15
openmsx::RawTrack::writeBlock
void writeBlock(int idx, span< const byte > source)
Definition: RawTrack.cc:165
openmsx::RawTrack::getIdamBuffer
const std::vector< unsigned > & getIdamBuffer() const
Definition: RawTrack.hh:117
openmsx::RawTrack::Sector::dataIdx
int dataIdx
Definition: RawTrack.hh:79
openmsx::RawTrack::decodeNextSector
std::optional< Sector > decodeNextSector(unsigned startIdx) const
Get the next sector (starting from a certain index).
Definition: RawTrack.cc:134
openmsx
This file implemented 3 utility functions:
Definition: Autofire.cc:5
openmsx::RawTrack::Sector::serialize
void serialize(Archive &ar, unsigned version)
Definition: RawTrack.cc:284
openmsx::RawTrack::clear
void clear(unsigned size)
Clear track data.
Definition: RawTrack.cc:20