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 <optional>
7#include <span>
8#include <vector>
9
10namespace openmsx {
11
12class 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
64class RawTrack {
65public:
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 auto& 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, std::span<byte> destination) const;
134 void writeBlock(int idx, std::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
145private:
146 [[nodiscard]] std::optional<Sector> decodeSectorImpl(int idx) const;
147
148private:
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
This class calculates CRC numbers for the polygon x^16 + x^12 + x^5 + 1.
Definition: CRC16.hh:17
void readBlock(int idx, std::span< byte > destination) const
Like memcpy() but copy from/to circular buffer.
Definition: RawTrack.cc:157
std::vector< Sector > decodeAll() const
Get info on all sectors in this track.
Definition: RawTrack.cc:110
std::optional< Sector > decodeSector(byte sectorNum) const
Get a sector with a specific number.
Definition: RawTrack.cc:143
void addIdam(unsigned idx)
Definition: RawTrack.cc:24
byte read(int idx) const
Definition: RawTrack.hh:106
void write(int idx, byte val, bool setIdam=false)
Definition: RawTrack.cc:31
void serialize(Archive &ar, unsigned version)
Definition: RawTrack.cc:264
const byte * getRawBuffer() const
Definition: RawTrack.hh:116
word calcCrc(int idx, int size) const
Convenience method to calculate CRC for part of this track.
Definition: RawTrack.cc:183
const auto & getIdamBuffer() const
Definition: RawTrack.hh:117
void writeBlock(int idx, std::span< const byte > source)
Definition: RawTrack.cc:163
std::optional< Sector > decodeNextSector(unsigned startIdx) const
Get the next sector (starting from a certain index).
Definition: RawTrack.cc:132
RawTrack(unsigned size=STANDARD_SIZE)
Definition: RawTrack.cc:13
byte * getRawBuffer()
Definition: RawTrack.hh:115
static constexpr unsigned STANDARD_SIZE
Definition: RawTrack.hh:73
void applyWd2793ReadTrackQuirk()
Definition: RawTrack.cc:190
int wrapIndex(int idx) const
Definition: RawTrack.hh:108
void updateCrc(CRC16 &crc, int idx, int size) const
Definition: RawTrack.cc:170
void clear(unsigned size)
Clear track data.
Definition: RawTrack.cc:18
unsigned getLength() const
Get track length.
Definition: RawTrack.hh:99
This file implemented 3 utility functions:
Definition: Autofire.cc:9
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
SERIALIZE_CLASS_VERSION(CassettePlayer, 2)
size_t size(std::string_view utf8)
void serialize(Archive &ar, unsigned version)
Definition: RawTrack.cc:282