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