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