VirtualC64 v5.0 beta
Commodore 64 Emulator
Loading...
Searching...
No Matches
Disk.h
1// -----------------------------------------------------------------------------
2// This file is part of VirtualC64
3//
4// Copyright (C) Dirk W. Hoffmann. www.dirkwhoffmann.de
5// This FILE is dual-licensed. You are free to choose between:
6//
7// - The GNU General Public License v3 (or any later version)
8// - The Mozilla Public License v2
9//
10// SPDX-License-Identifier: GPL-3.0-or-later OR MPL-2.0
11// -----------------------------------------------------------------------------
12
13#pragma once
14
15#include "DiskTypes.h"
16#include "DiskAnalyzerTypes.h"
17#include "FSTypes.h"
18#include "SubComponent.h"
19#include "PETName.h"
20
21
22namespace vc64 {
23
24class DiskAnalyzer;
25class FileSystem;
26
27class Disk final : public CoreObject {
28
29 friend class Drive;
30
31public:
32
33 //
34 // Constants and lookup tables
35 //
36
37 static const TrackDefaults trackDefaults[43];
38
39 // GCR encoding table. Maps 4 data bits to 5 GCR bits.
40 static constexpr u8 gcr[16] = {
41
42 0x0a, 0x0b, 0x12, 0x13, /* 0 - 3 */
43 0x0e, 0x0f, 0x16, 0x17, /* 4 - 7 */
44 0x09, 0x19, 0x1a, 0x1b, /* 8 - 11 */
45 0x0d, 0x1d, 0x1e, 0x15 /* 12 - 15 */
46 };
47
48 // Inverse GCR encoding table. Maps 5 GCR bits to 4 data bits.
49 static constexpr u8 invgcr[32] = {
50
51 255, 255, 255, 255, /* 0x00 - 0x03 */
52 255, 255, 255, 255, /* 0x04 - 0x07 */
53 255, 8, 0, 1, /* 0x08 - 0x0B */
54 255, 12, 4, 5, /* 0x0C - 0x0F */
55 255, 255, 2, 3, /* 0x10 - 0x13 */
56 255, 15, 6, 7, /* 0x14 - 0x17 */
57 255, 9, 10, 11, /* 0x18 - 0x1B */
58 255, 13, 14, 255 /* 0x1C - 0x1F */
59 };
60
61
62 //
63 // Disk properties
64 //
65
66private:
67
68 // Write protection mark
69 bool writeProtected = false;
70
71 // Indicates whether data has been written (data would be lost on eject)
72 bool modified = false;
73
74
75 //
76 // Disk data
77 //
78
79public:
80
81 // Data information for each halftrack on this disk
82 DiskData data = { };
83
84 // Length information for each halftrack on this disk
85 DiskLength length = { };
86
87
88 //
89 // Class functions
90 //
91
92public:
93
94 // Returns the number of sectors stored in a certain track or halftrack
95 static isize numberOfSectorsInTrack(Track t);
96 static isize numberOfSectorsInHalftrack(Halftrack ht);
97
98 // Returns the default speed zone of a track or halftrack
99 static isize speedZoneOfTrack(Track t);
100 static isize speedZoneOfHalftrack(Halftrack ht);
101
102 // Checks if the given pair is a valid (half)track / sector combination
103 static bool isValidTrackSectorPair(Track t, Sector s);
104 static bool isValidHalftrackSectorPair(Halftrack ht, Sector s);
105
106
107 //
108 // Methods
109 //
110
111public:
112
113 Disk();
114 Disk(const fs::path &path, bool wp = false) throws { init(path, wp); }
115 Disk(DOSType type, PETName<16> name, bool wp = false) { init(type, name, wp); }
116 Disk(const FileSystem &device, bool wp = false) { init(device, wp); }
117 Disk(const class G64File &g64, bool wp = false) { init(g64, wp); }
118 Disk(const class D64File &d64, bool wp = false) throws { init(d64, wp); }
119 Disk(class AnyCollection &archive, bool wp = false) throws { init(archive, wp); }
120 Disk(SerReader &reader) throws { init(reader); }
121
122private:
123
124 void init(const fs::path &path, bool wp) throws;
125 void init(DOSType type, PETName<16> name, bool wp);
126 void init(const class FileSystem &device, bool wp);
127 void init(const class G64File &g64, bool wp);
128 void init(const class D64File &d64, bool wp) throws;
129 void init(class AnyCollection &archive, bool wp) throws;
130 void init(SerReader &reader) throws;
131
132public:
133
134 Disk& operator= (const Disk& other) {
135
136 CLONE(writeProtected)
137 CLONE(modified)
138 CLONE(data)
139 CLONE(length)
140
141 return *this;
142 }
143
144
145 //
146 // Methods from Serializable
147 //
148
149public:
150
151 template <class T>
152 void serialize(T& worker)
153 {
154 if (isResetter(worker)) return;
155
156 worker
157
158 << writeProtected
159 << modified
160 << data
161 << length;
162 }
163
164
165 //
166 // Methods from CoreObject and Dumpable
167 //
168
169private:
170
171 const char *objectName() const override { return "Disk"; }
172 void _dump(Category category, std::ostream& os) const override;
173
174
175 //
176 // Accessing
177 //
178
179public:
180
181 bool isWriteProtected() const { return writeProtected; }
182 void setWriteProtection(bool b) { writeProtected = b; }
183 void toggleWriteProtection() { writeProtected = !writeProtected; }
184
185 bool isModified() const { return modified; }
186 void setModified(bool b);
187
188
189 //
190 // Handling GCR encoded data
191 //
192
193public:
194
195 // Converts a 4 bit binary value to a 5 bit GCR codeword or vice versa
196 static u8 bin2gcr(u8 value) { assert(value < 16); return gcr[value]; }
197 static u8 gcr2bin(u8 value) { assert(value < 32); return invgcr[value]; }
198
199 // Returns true if the provided 5 bit codeword is a valid GCR codeword
200 static bool isGcr(u8 value) { assert(value < 32); return invgcr[value] != 0xFF; }
201
202 /* Encodes a byte stream as a GCR bit stream. The first function encodes
203 * a single byte and the second functions encodes multiple bytes. For each
204 * byte, 10 bits are written to the specified disk position.
205 */
206 void encodeGcr(u8 value, Track t, HeadPos offset);
207 void encodeGcr(u8 *values, isize length, Track t, HeadPos offset);
208
209
210 //
211 // Accessing disk data
212 //
213
214 // Returns true if the provided drive head position is valid
215 bool isValidHeadPos(Halftrack ht, HeadPos pos) const;
216
217 // Fixes a wrapped over head position
218 HeadPos wrap(Halftrack ht, HeadPos pos) const;
219
220 /* Returns the duration of a single bit in 1/10 nano seconds. The returned
221 * value is the time span the drive head resists over the specified bit.
222 * The value is determined by the the density bits at the time the bit was
223 * written to disk. Function "_bitDelay" expects the head position to be
224 * inside the halftrack bounds.
225 */
226 u64 _bitDelay(Halftrack ht, HeadPos pos) const;
227 u64 bitDelay(Halftrack ht, HeadPos pos) const { return _bitDelay(ht, wrap(ht, pos)); }
228
229 /* Reads or writes a single bit. The functions come in two variants. The
230 * first variants expect the provided head position inside the valid
231 * halftrack bounds. The other variants wrap over the head position first.
232 */
233 u8 _readBitFromHalftrack(Halftrack ht, HeadPos pos) const {
234 assert(isValidHeadPos(ht, pos));
235 return (data.halftrack[ht][pos >> 3] & (0x80 >> (pos & 7))) != 0;
236 }
237 u8 readBitFromHalftrack(Halftrack ht, HeadPos pos) const {
238 return _readBitFromHalftrack(ht, wrap(ht, pos));
239 }
240 void _writeBitToHalftrack(Halftrack ht, HeadPos pos, bool bit) {
241 assert(isValidHeadPos(ht, pos));
242 if (bit) {
243 data.halftrack[ht][pos >> 3] |= (0x0080 >> (pos & 7));
244 } else {
245 data.halftrack[ht][pos >> 3] &= (0xFF7F >> (pos & 7));
246 }
247 }
248 void _writeBitToTrack(Track t, HeadPos pos, bool bit) {
249 _writeBitToHalftrack(2 * t - 1, pos, bit);
250 }
251 void writeBitToHalftrack(Halftrack ht, HeadPos pos, bool bit) {
252 _writeBitToHalftrack(ht, wrap(ht, pos), bit);
253 }
254 void writeBitToTrack(Track t, HeadPos pos, bool bit) {
255 _writeBitToHalftrack(2 * t - 1, pos, bit);
256 }
257
258 // Writes a bit multiple times
259 void writeBitToHalftrack(Halftrack ht, HeadPos pos, bool bit, isize count) {
260 for (isize i = 0; i < count; i++)
261 writeBitToHalftrack(ht, pos++, bit);
262 }
263 void writeBitToTrack(Track t, HeadPos pos, bool bit, isize count) {
264 writeBitToHalftrack(2 * t - 1, pos, bit, count);
265 }
266
267 // Writes a single byte
268 void writeByteToHalftrack(Halftrack ht, HeadPos pos, u8 byte) {
269 for (u8 mask = 0x80; mask != 0; mask >>= 1)
270 writeBitToHalftrack(ht, pos++, byte & mask);
271 }
272 void writeByteToTrack(Track t, HeadPos pos, u8 byte) {
273 writeByteToHalftrack(2 * t - 1, pos, byte);
274 }
275
276 // Writes a certain number of interblock bytes to disk
277 void writeGapToHalftrack(Halftrack ht, HeadPos pos, isize length) {
278 for (isize i = 0; i < length; i++, pos += 8)
279 writeByteToHalftrack(ht, pos, 0x55);
280 }
281 void writeGapToTrack(Track t, HeadPos pos, isize length) {
282 writeGapToHalftrack(2 * t - 1, pos, length);
283 }
284
285 // Clears a single halftrack
286 void clearHalftrack(Halftrack ht);
287
288 // Reverts to a factory-fresh disk
289 void clearDisk();
290
291
292 //
293 // Analyzing the disk
294 //
295
296public:
297
298 /* Checks whether a track or halftrack is cleared. Avoid calling these
299 * methods frequently, because they scan the whole track.
300 */
301 bool trackIsEmpty(Track t) const;
302 bool halftrackIsEmpty(Halftrack ht) const;
303 isize nonemptyHalftracks() const;
304
305 // Returns the length of a halftrack in bits
306 isize lengthOfTrack(Track t) const;
307 isize lengthOfHalftrack(Halftrack ht) const;
308
309
310 //
311 // Decoding disk data
312 //
313
314public:
315
316 /* Converts the disk into a byte stream and returns the number of bytes
317 * written. The byte stream is compatible with the D64 file format. By
318 * passing a null pointer, a test run is performed. Test runs are used to
319 * determine how many bytes will be written.
320 */
321 isize decodeDisk(u8 *dest);
322
323private:
324
325 isize decodeDisk(u8 *dest, isize numTracks, DiskAnalyzer &analyzer);
326 isize decodeTrack(Track t, u8 *dest, DiskAnalyzer &analyzer);
327 isize decodeHalfrack(Halftrack ht, u8 *dest, DiskAnalyzer &analyzer);
328 isize decodeSector(Halftrack ht, isize offset, u8 *dest, DiskAnalyzer &analyzer);
329
330
331 //
332 // Encoding disk data
333 //
334
335public:
336
337 // Encodes a G64 file
338 void encodeG64(const G64File &a);
339
340 /* Encodes a file system. The method creates sync marks, GRC encoded header
341 * and data blocks, checksums and gaps. If alignTracks is true, the first
342 * sector always starts at the beginning of a track.
343 */
344 void encode(const FileSystem &fs, bool alignTracks = false);
345
346private:
347
348 /* Encode a single track. This function translates the logical byte
349 * sequence of a single track into the native VC1541 byte representation.
350 * The native representation includes sync marks, GCR data etc.
351 * 'tailGapEven' specifies the number of tail bytes follwowing sectors with
352 * even sector numbers. 'tailGapOdd' specifies the number of tail bytes
353 * follwowing sectors with odd sector numbers. The number of written bits
354 * is returned.
355 */
356 isize encodeTrack(const FileSystem &fs, Track t, isize gap, HeadPos start);
357
358 /* Encode a single sector. This function translates the logical byte
359 * sequence of a single sector into the native VC1541 byte representation.
360 * The sector is closed by 'gap' tail gap bytes. The number of written bits
361 * is returned.
362 */
363 isize encodeSector(const FileSystem &fs, Track t, Sector sector, HeadPos start, isize gap);
364};
365
366}
367
VirtualC64 project namespace.
Definition CmdQueue.cpp:16