VirtualC64 v5.0 beta
Commodore 64 Emulator
Loading...
Searching...
No Matches
Drive.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 "DriveTypes.h"
16#include "C64Types.h"
17#include "CmdQueueTypes.h"
18#include "SubComponent.h"
19#include "CPU.h"
20#include "Disk.h"
21#include "DiskAnalyzer.h"
22#include "DriveMemory.h"
23#include "VIA.h"
24#include "PIA.h"
25
26namespace vc64 {
27
28/*
29 * This implementation is based on the following two documents written
30 * by Ruud Baltissen. Ruud, thank you for this excellent work!
31 *
32 * Description: http://www.baltissen.org/newhtm/1541a.htm
33 * Schematics: http://www.baltissen.org/images/1540.gif
34 */
35
36class Drive final : public SubComponent, public Inspectable<DriveInfo> {
37
38 Descriptions descriptions = {
39 {
40 .name = "Drive8",
41 .description = "First Floppy Drive"
42 },
43 {
44 .name = "Drive9",
45 .description = "Second Floppy Drive"
46 }
47 };
48
49 ConfigOptions options = {
50
67 };
68
69 friend class DriveMemory;
70 friend class VIA1;
71 friend class VIA2;
72
73 //
74 // Constants
75 //
76
77 /* Power-safe threshold measured in frames. If the drive was inactive for
78 * the specified number of frames, it is put into power-safe mode (if this
79 * option is enabled). In this mode, executing the drive is skipped inside
80 * the run loop. As a effect, the current drive state is frozen until the
81 * drive is woken up.
82 */
83 static constexpr i64 powerSafeThreshold = 100;
84
85 /* Time between two carry pulses of UE7 in 1/10 nano seconds. The VC1541
86 * drive is clocked by 16 Mhz. The base frequency is divided by N where N
87 * ranges from 13 (density bits = 11) to 16 (density bits = 00). On the
88 * logic board, this is done with a 4-bit counter of type 74SL193 whose
89 * reset value bits are connected to the two density bits (PB5 and PB6
90 * of VIA2). It follows that a single bit is ready after approx. 3.25 CPU
91 * cycles in the fastest zone and approx. 4 CPU cycles in the slowest zone.
92 */
93 const u64 delayBetweenTwoCarryPulses[4] = {
94
95 10000, // Density bits = 00: Carry pulse every 16/16 * 10^4 1/10 nsec
96 9375, // Density bits = 01: Carry pulse every 15/16 * 10^4 1/10 nsec
97 8750, // Density bits = 10: Carry pulse every 14/16 * 10^4 1/10 nsec
98 8125 // Density bits = 11: Carry pulse every 13/16 * 10^4 1/10 nsec
99 };
100
101 // Current configuration
102 DriveConfig config = { };
103
104
105 //
106 // Sub components
107 //
108
109public:
110
111 DriveMemory mem = DriveMemory(c64, *this);
112 CPU cpu = CPU(MOS_6502, c64);
113 VIA1 via1 = VIA1(c64, *this);
114 VIA2 via2 = VIA2(c64, *this);
115 PiaDolphin pia = PiaDolphin(c64, *this);
116
117 // The currently inserted disk (if any)
118 std::unique_ptr<Disk> disk;
119
120
121 //
122 // Disk change logic
123 //
124
125 // A disk waiting to be inserted
126 std::unique_ptr<Disk> diskToInsert;
127
128
129 //
130 // Drive state
131 //
132
133private:
134
135 // Indicates whether the disk is rotating
136 bool spinning = false;
137
138 // Indicates whether the red LED is on
139 bool redLED = false;
140
141 // Indicates if or how a disk is inserted
142 InsertionStatus insertionStatus = DISK_FULLY_EJECTED;
143
144
145 //
146 // Clocking logic
147 //
148
149 // Elapsed time since power up in 1/10 nano seconds
150 u64 elapsedTime = 0;
151
152 /* Indicates when the next drive clock cycle occurs. The VC1541 drive is
153 * clocked by 16 MHz. The clock signal is fed into a counter which serves
154 * as a frequency divider. It's output is used to clock the drive's CPU and
155 * the two VIA chips.
156 */
157 i64 nextClock = 0;
158
159 /* Indicates when the next carry output pulse occurs on UE7. The 16 MHz
160 * signal is also fed into UE7, a 74SL193 4-bit couter, which generates a
161 * carry output signal on overflow. The pre-load inputs of this counter are
162 * connected to PB5 and PB6 of VIA2 (the 'density bits'). This means that a
163 * carry signal is generated every 13th cycle (from the 16 Mhz clock) when
164 * both density bits are 0 and every 16th cycle when both density bits are
165 * 1. The carry signal drives uf4, a counter of the same type.
166 */
167 i64 nextCarry = 0;
168
169public:
170
171 /* Counts the number of carry pulses from UE7. In a perfect setting, a new
172 * bit is read from or written to the drive after four carry pulses.
173 */
174 i64 carryCounter = 0;
175
176 /* The second 74SL193 4-bit counter on the logic board. This counter is
177 * driven by the carry output of UE7. It has four outputs QA, QB, QC, and
178 * QD. QA and QB are used to clock most of the other components. QC and QD
179 * are fed into a NOR gate whose output is connected to the serial input
180 * pin of the input shift register.
181 */
182 u8 counterUF4 = 0;
183
184
185 //
186 // Read/Write logic
187 //
188
189 // The next bit will be ready after this number of cycles
190 i16 bitReadyTimer = 0;
191
192 /* Byte ready counter (UE3). The VC1540 logic board contains a 4 bit
193 * counter of type 72LS191 which is advanced whenever a bit is ready. By
194 * reaching 7, the counter signals that a byte is ready. In that case, the
195 * write shift register is loaded with new data and pin CA1 of VIA2 changes
196 * state. This state change causes the current contents of the read shift
197 * register to be latched into the input register of VIA2.
198 */
199 u8 byteReadyCounter = 0;
200
201 // Halftrack position of the drive head
202 Halftrack halftrack = 0;
203
204 // Position of the drive head inside the current track
205 HeadPos offset = 0;
206
207 /* Current disk zone. Each track belongs to one of four zones. Whenever the
208 * drive moves the r/w head, it computes the new number and writes into PB5
209 * and PB6 of via2. These bits are hard-wired to a 74LS193 counter on the
210 * logic board that breaks down the 16 Mhz base frequency. This mechanism
211 * is used to slow down the read/write process on inner tracks.
212 */
213 isize zone = 0;
214
215 /* The 74LS164 serial to parallel shift register. In read mode, this
216 * register is fed by the drive head with data.
217 */
218 u16 readShiftreg = 0;
219
220 /* The 74LS165 parallel to serial shift register. In write mode, this
221 * register feeds the drive head with data.
222 */
223 u8 writeShiftreg = 0;
224
225 /* Current value of the SYNC line. The SYNC signal plays an important role
226 * for timing synchronization. It becomes true when the beginning of a SYNC
227 * is detected. On the logic board, the SYNC signal is computed by a NAND
228 * gate that combines the 10 previously read bits from the input shift
229 * register and VIA2::CB2 (the r/w mode pin). Connecting CB2 to the NAND
230 * gates ensures that SYNC can only be true in read mode. When SYNC becomes
231 * false (meaning that a 0 was pushed into the shift register), the
232 * byteReadyCounter is reset.
233 */
234 bool sync = false;
235
236 /* Current value of the ByteReady line. This signal goes low when a byte
237 * has been processed.
238 */
239 bool byteReady = false;
240
241
242 //
243 // Speed logic (power-save)
244 //
245
246 // Idle counter
247 i64 watchdog = INT64_MAX;
248
249 // Indicates whether execute() should be called inside the run loop
250 bool needsEmulation = false;
251
252
253 //
254 // Methods
255 //
256
257public:
258
259 Drive(C64 &ref, isize id);
260
261 Drive& operator= (const Drive& other) {
262
263 CLONE(mem)
264 CLONE(cpu)
265 CLONE(via1)
266 CLONE(via2)
267
268 CLONE(spinning)
269 CLONE(redLED)
270 CLONE(elapsedTime)
271 CLONE(nextClock)
272 CLONE(nextCarry)
273 CLONE(carryCounter)
274 CLONE(counterUF4)
275 CLONE(bitReadyTimer)
276 CLONE(byteReadyCounter)
277 CLONE(halftrack)
278 CLONE(offset)
279 CLONE(zone)
280 CLONE(readShiftreg)
281 CLONE(writeShiftreg)
282 CLONE(sync)
283 CLONE(byteReady)
284 CLONE(watchdog)
285 CLONE(insertionStatus)
286
287 CLONE(config)
288
289 if (other.disk && !disk) disk = std::make_unique<Disk>();
290 if (!other.disk) disk = nullptr;
291 if (disk) *disk = *other.disk;
292
293 if (other.diskToInsert && !diskToInsert) diskToInsert = std::make_unique<Disk>();
294 if (!other.diskToInsert) diskToInsert = nullptr;
295 if (diskToInsert) *diskToInsert = *other.diskToInsert;
296
297 return *this;
298 }
299
300
301 //
302 // Methods from Serializable
303 //
304
305public:
306
307 template <class T>
308 void serialize(T& worker)
309 {
310 worker
311
312 << mem
313 << cpu
314 << via1
315 << via2
316
317 << spinning
318 << redLED
319 << elapsedTime
320 << nextClock
321 << nextCarry
322 << carryCounter
323 << counterUF4
324 << bitReadyTimer
325 << byteReadyCounter
326 << halftrack
327 << offset
328 << zone
329 << readShiftreg
330 << writeShiftreg
331 << sync
332 << byteReady
333 << watchdog;
334
335 if (isSoftResetter(worker)) return;
336
337 worker
338
339 << insertionStatus;
340
341 if (isResetter(worker)) return;
342
343 worker
344
345 << config.type
346 << config.ram
347 << config.parCable
348 << config.powerSave
349 << config.connected
350 << config.switchedOn
351 << config.ejectDelay
352 << config.swapDelay
353 << config.insertDelay
354 << config.pan
355 << config.powerVolume
356 << config.stepVolume
357 << config.insertVolume
358 << config.ejectVolume
359 << config.saveRoms;
360 }
361
362 void operator << (SerResetter &worker) override { serialize(worker); };
363 void operator << (SerChecker &worker) override;
364 void operator << (SerCounter &worker) override;
365 void operator << (SerReader &worker) override;
366 void operator << (SerWriter &worker) override;
367
368
369 //
370 // Methods from CoreComponent
371 //
372
373public:
374
375 const Descriptions &getDescriptions() const override { return descriptions; }
376
377private:
378
379 void _initialize() override;
380 void _dump(Category category, std::ostream& os) const override;
381 void _reset(bool hard) override;
382
383
384 //
385 // Methods from Inspectable
386 //
387
388public:
389
390 void cacheInfo(DriveInfo &result) const override;
391
392
393 //
394 // Methods from Configurable
395 //
396
397public:
398
399 const DriveConfig &getConfig() const { return config; }
400 const ConfigOptions &getOptions() const override { return options; }
401 i64 getFallback(Option opt) const override;
402 i64 getOption(Option opt) const override;
403 void checkOption(Option opt, i64 value) override;
404 void setOption(Option opt, i64 value) override;
405 void resetConfig() override;
406
407 // Updates the current configuration according to the installed ROM
408 void autoConfigure();
409
410
411 //
412 // Working with the drive
413 //
414
415public:
416
417 // Returns the device number
418 isize getDeviceNr() const { return objid; }
419
420 // Convenience wrappers
421 bool isDrive8() { return objid == DRIVE8; }
422 bool isDrive9() { return objid == DRIVE9; }
423 bool hasParCable() { return config.parCable != PAR_CABLE_NONE; }
424 ParCableType getParCableType() const { return config.parCable; }
425
426 // Checks whether the drive is ready to be connected
427 bool canConnect();
428
429 // Checks whether the drive is connected and switched on
430 bool connectedAndOn() { return config.connected && config.switchedOn; }
431
432 // Checks whether the drive has been idle for a while
433 bool isIdle() const { return watchdog < 0; }
434
435 // Returns true iff the red drive LED is on
436 bool getRedLED() const { return redLED; };
437
438 // Turns the red drive LED on or off
439 void setRedLED(bool b);
440
441 // Returns true iff the drive engine is on
442 bool isRotating() const { return spinning; };
443
444 // Turns the drive engine on or off
445 void setRotating(bool b);
446
447 // Wakes up the drive (clears the idle state)
448 void wakeUp(isize awakeness = powerSafeThreshold);
449
450
451 //
452 // Handling disks
453 //
454
455public:
456
457 // Checks whether the drive contains a disk of a certain kind
458 bool hasDisk() const;
459 bool hasPartiallyRemovedDisk() const;
460 bool hasProtectedDisk() const { return hasDisk() && disk->isWriteProtected(); }
461 bool hasModifiedDisk() const { return hasDisk() && disk->isModified(); }
462 bool hasUnmodifiedDisk() const { return hasDisk() && !hasModifiedDisk(); }
463 bool hasUnprotectedDisk() const { return hasDisk() && !hasProtectedDisk(); }
464
465 // Changes the modification state
466 void setModificationFlag(bool value);
467 void markDiskAsModified() { setModificationFlag(true); }
468 void markDiskAsUnmodified() { setModificationFlag(false); }
469
470 // Changes the write-protection state
471 void setProtection(bool value);
472 void protectDisk() { setProtection(true); }
473 void unprotectDisk() { setProtection(false); }
474 void toggleProtection();
475
476 /* Returns the current state of the write protection barrier. If the light
477 * barrier is blocked, the drive head is unable to modify bits on disk.
478 * Note: We block the write barrier on power up for about 1.5 sec, because
479 * the drive enters write mode during the power up phase. I'm unsure if
480 * this is normal drive behavior or an emulator bug. Any hint on this is
481 * very welcome!
482 */
483 bool getLightBarrier() const {
484 return
485 cpu.clock < 1500000
486 || hasPartiallyRemovedDisk()
487 || hasProtectedDisk();
488 }
489
490 /* Requests the emulator to inserts or eject a disk. Background: Many C64
491 * programs try to detect a disk change by checking the light barrier. This
492 * means that proper physical delays have to be taken in account. For that
493 * reason, these functions initiate a sequence of events that are processed
494 * one after another with a proper time delay. The sequence includes pulling
495 * the currently inserted disk halfway out before it is removed completely,
496 * and pushing the new disk halfway in before it is inserted completely.
497 */
498 void insertDisk(const fs::path &path, bool wp) throws;
499 void insertDisk(std::unique_ptr<Disk> disk);
500 void insertNewDisk(DOSType fstype, string name);
501 void insertMediaFile(class MediaFile &file, bool wp);
502 void insertD64(const class D64File &d64, bool wp);
503 void insertG64(const class G64File &g64, bool wp);
504 void insertCollection(class AnyCollection &archive, bool wp) throws;
505 void insertFileSystem(const class FileSystem &device, bool wp);
506 void ejectDisk();
507
508
509 //
510 // Emulating
511 //
512
513public:
514
515 /* Executes all pending cycles of the virtual drive. The number of cycles
516 * is determined by the target time which is elapsedTime + duration.
517 */
518 void execute(u64 duration);
519
520private:
521
522 // Emulates a trigger event on the carry output pin of UE7.
523 void executeUF4();
524
525public:
526
527 // Returns the current access mode of this drive (read or write)
528 bool readMode() const { return via2.getCB2(); }
529 bool writeMode() const { return !readMode(); }
530
531 // Returns the current halftrack or track number
532 Halftrack getHalftrack() const { return halftrack; }
533 Track getTrack() const { return (halftrack + 1) / 2; }
534
535 // Returns the number of bits in a halftrack
536 isize sizeOfHalftrack(Halftrack ht) {
537 return hasDisk() ? disk->lengthOfHalftrack(ht) : 0; }
538 isize sizeOfCurrentHalftrack() { return sizeOfHalftrack(halftrack); }
539
540 // Returns the position of the drive head inside the current track
541 HeadPos getOffset() const { return offset; }
542
543 // Moves head one halftrack up
544 void moveHeadUp();
545
546 // Moves head one halftrack down
547 void moveHeadDown();
548
549 // Returns the current value of the sync signal
550 bool getSync() const { return sync; }
551
552 /* Updates the byte ready line. The byte ready line is connected to pin CA1
553 * of VIA2. Pulling this signal low causes important side effects. Firstly,
554 * the contents of the read shift register is latched into the VIA chip.
555 * Secondly, the V flag is set inside the CPU. See also CA1action().
556 */
557 void updateByteReady();
558
559 // Raises the byte ready line
560 void raiseByteReady();
561
562 // Returns the current track zone (0 to 3)
563 bool getZone() const { return zone; }
564
565 // Sets the current track zone (0 to 3)
566 void setZone(isize value);
567
568 // Reads a single bit from the disk head (result is 0 or 1)
569 u8 readBitFromHead() const;
570
571 // Writes a single bit to the disk head
572 void writeBitToHead(u8 bit);
573
574 // Advances drive head position by one bit
575 void rotateDisk();
576
577 // Performs periodic actions
578 void vsyncHandler();
579
580
581 //
582 // Processing commands and events
583 //
584
585public:
586
587 // Processes a datasette command
588 void processCommand(const Cmd &cmd);
589
590 // Initiates the disk change procedure
591 void scheduleFirstDiskChangeEvent(EventID id);
592
593 // Carries out the disk change procedure
594 void processDiskChangeEvent(EventID id);
595};
596
597}
Inspection interface.
Definition Inspectable.h:32
VirtualC64 project namespace.
Definition CmdQueue.cpp:16
DOS_TYPE
File system type.
Definition FSTypes.h:27
OPT
Configuration option.
Definition OptionTypes.h:26
@ OPT_DRV_PARCABLE
Parallel cable type.
Definition OptionTypes.h:138
@ OPT_DRV_EJECT_DELAY
Disk ejection delay.
Definition OptionTypes.h:142
@ OPT_DRV_TYPE
Drive model.
Definition OptionTypes.h:135
@ OPT_DRV_SWAP_DELAY
Disk swap delay.
Definition OptionTypes.h:143
@ OPT_DRV_AUTO_CONFIG
Auto-configure drives based on the Drive Rom.
Definition OptionTypes.h:134
@ OPT_DRV_PAN
Pan.
Definition OptionTypes.h:145
@ OPT_DRV_INSERT_DELAY
Disk insertion delay.
Definition OptionTypes.h:144
@ OPT_DRV_POWER_SWITCH
Power switch (on/off)
Definition OptionTypes.h:140
@ OPT_DRV_RAM
Drive Ram.
Definition OptionTypes.h:136
@ OPT_DRV_SAVE_ROMS
Save Roms in snapshots.
Definition OptionTypes.h:137
@ OPT_DRV_INSERT_VOL
Volume (disk insertion)
Definition OptionTypes.h:148
@ OPT_DRV_EJECT_VOL
Volume (disk ejection)
Definition OptionTypes.h:149
@ OPT_DRV_CONNECT
Connection status.
Definition OptionTypes.h:139
@ OPT_DRV_POWER_VOL
Volume (power-up sound)
Definition OptionTypes.h:146
@ OPT_DRV_POWER_SAVE
Enable fast-paths.
Definition OptionTypes.h:141
@ OPT_DRV_STEP_VOL
Volume (head steps)
Definition OptionTypes.h:147
PAR_CABLE_TYPE
Parallel cable type.
Definition ParCableTypes.h:26
@ PAR_CABLE_NONE
No parallel cable attached.
Definition ParCableTypes.h:27