27 bool signalsNeedMotorOn_,
bool doubleSided,
29 : syncLoadingTimeout(motherBoard_.getScheduler())
30 , syncMotorTimeout (motherBoard_.getScheduler())
31 , motherBoard(motherBoard_)
33 motherBoard.getReactor().getGlobalSettings().getThrottleManager())
34 , motorTimeout(motorTimeout_)
35 , motorTimer(getCurrentTime())
36 , doubleSizedDrive(doubleSided)
37 , signalsNeedMotorOn(signalsNeedMotorOn_)
38 , trackMode(trackMode_)
43 while ((*drivesInUse)[i]) {
48 (*drivesInUse)[i] =
true;
49 std::string driveName =
"diskX"; driveName[4] = char(
'a' + i);
52 throw MSXException(
"Duplicated drive name: ", driveName);
55 changer.emplace(motherBoard, driveName,
true, doubleSizedDrive,
56 [
this]() { invalidateTrack(); });
67 doSetMotor(
false, getCurrentTime());
71 const auto& driveName = changer->getDriveName();
74 unsigned driveNum = driveName[4] -
'a';
75 assert((*drivesInUse)[driveNum]);
76 (*drivesInUse)[driveNum] =
false;
81 auto typeStr = [&]() -> std::string_view {
82 if (
dynamic_cast<DummyDisk*
>(&(changer->getDisk()))) {
84 }
else if (
dynamic_cast<DirAsDSK*
>(&(changer->getDisk()))) {
94 "readonly", changer->getDisk().isWriteProtected(),
95 "doublesided", changer->getDisk().isDoubleSided());
96 if (!
dynamic_cast<DMKDiskImage*
>(&(changer->getDisk()))) {
99 if (
const auto* disk = changer->getSectorAccessibleDisk()) {
102 return p.getResolved();
122 if (!doubleSizedDrive && (side != 0))
return false;
124 return !changer->getDisk().isDummyDisk();
131 if (signalsNeedMotorOn && !motorStatus)
return false;
132 return changer->getDisk().isWriteProtected();
137 return doubleSizedDrive ? changer->getDisk().isDoubleSided()
144 side = side_ ? 1 : 0;
152unsigned RealDrive::getMaxTrack()
const
154 constexpr unsigned MAX_TRACK = 85;
160 return MAX_TRACK * 4 + 3;
166std::optional<unsigned> RealDrive::getDiskReadTrack()
const
179 if ((headPos >= 2) && ((headPos % 4) != 2)) {
180 return (headPos - 2) / 4;
189std::optional<unsigned> RealDrive::getDiskWriteTrack()
const
196 if ((headPos >= 4) && ((headPos % 4) == 0)) {
197 return (headPos - 4) / 4;
212 if (headPos < getMaxTrack()) {
224 if (motorStatus) setLoading(time);
233 if (signalsNeedMotorOn && !motorStatus)
return false;
255 if (syncMotorTimeout.removeSyncPoint()) {
268 doSetMotor(
true, time);
275 if (syncMotorTimeout.pendingSyncPoint()) {
284 syncLoadingTimeout.removeSyncPoint();
285 loadingIndicator.
update(
false);
288 syncMotorTimeout.setSyncPoint(time + motorTimeout);
300unsigned RealDrive::getCurrentAngle(EmuTime::param time)
const
305 return narrow_cast<unsigned>((startAngle + deltaAngle) % TICKS_PER_ROTATION);
312void RealDrive::doSetMotor(
bool status, EmuTime::param time)
318 startAngle = getCurrentAngle(time);
319 motorStatus = status;
328void RealDrive::setLoading(EmuTime::param time)
331 loadingIndicator.
update(
true);
336 syncLoadingTimeout.removeSyncPoint();
340void RealDrive::execLoadingTimeout()
342 loadingIndicator.
update(
false);
345void RealDrive::execMotorTimeout(EmuTime::param time)
347 doSetMotor(
false, time);
358 return getCurrentAngle(time) < INDEX_DURATION;
364 return EmuTime::infinity();
366 unsigned delta = TICKS_PER_ROTATION - getCurrentAngle(time);
369 return time + dur1 + dur2;
372void RealDrive::invalidateTrack()
376 }
catch (MSXException&) {
382void RealDrive::getTrack()
390 if (
auto rdTrack = getDiskReadTrack()) {
391 changer->getDisk().readTrack(narrow<uint8_t>(*rdTrack),
392 narrow<uint8_t>(side),
413 track.
write(idx, val, addIdam);
420 return trackValid ? track.
read(idx) : 0;
423static constexpr unsigned divUp(
unsigned a,
unsigned b)
425 return (a + b - 1) / b;
430 unsigned currentAngle = getCurrentAngle(time);
432 unsigned idx = divUp(currentAngle * trackLen, TICKS_PER_ROTATION);
444 return EmuTime::infinity();
446 unsigned sectorAngle = divUp(sector.
addrIdx * TICKS_PER_ROTATION, trackLen);
450 auto delta = narrow<int>(sectorAngle) - narrow<int>(currentAngle);
451 if (delta < 4) delta += TICKS_PER_ROTATION;
452 assert(4 <= delta); assert(
unsigned(delta) < (TICKS_PER_ROTATION + 4));
459 if (trackValid && trackDirty) {
460 if (
auto wrTrack = getDiskWriteTrack()) {
461 changer->getDisk().writeTrack(narrow<uint8_t>(*wrTrack),
462 narrow<uint8_t>(side),
471 return changer->diskChanged();
476 return changer->peekDiskChanged();
501template<
typename Archive>
504 if (ar.versionAtLeast(version, 4)) {
505 ar.serialize(
"syncLoadingTimeout", syncLoadingTimeout,
506 "syncMotorTimeout", syncMotorTimeout);
510 ar.serialize(
"motorTimer", motorTimer,
514 "motorStatus", motorStatus);
515 if (ar.versionAtLeast(version, 3)) {
516 ar.serialize(
"startAngle", startAngle);
518 assert(Archive::IS_LOADER);
521 if (ar.versionAtLeast(version, 5)) {
522 ar.serialize(
"track", track);
523 ar.serialize(
"trackValid", trackValid);
524 ar.serialize(
"trackDirty", trackDirty);
526 if constexpr (Archive::IS_LOADER) {
constexpr uint64_t getTicksTillUp(EmuTime::param e) const
Calculate the number of ticks this clock has to tick to reach or go past the given time.
static constexpr EmuDuration duration(unsigned ticks)
Calculates the duration of the given number of ticks at this clock's frequency.
constexpr void advance(EmuTime::param e)
Advance this clock in time until the last tick which is not past the given time.
static constexpr EmuDuration sec(unsigned x)
void setLed(Led led, bool status)
void update(bool newState)
Called by the device to indicate its loading state may have changed.
void update(UpdateType type, std::string_view name, std::string_view value) override
bool hasCommand(std::string_view command) const
std::shared_ptr< T > getSharedStuff(std::string_view name, Args &&...args)
Some MSX device parts are shared between several MSX devices (e.g.
void registerMediaInfo(std::string_view name, MediaInfoProvider &provider)
Register and unregister providers of media info, for the media info topic.
void unregisterMediaInfo(MediaInfoProvider &provider)
MSXCliComm & getMSXCliComm()
LedStatus & getLedStatus()
MSXCommandController & getMSXCommandController()
void write(int idx, uint8_t val, bool setIdam=false)
uint8_t read(int idx) const
std::optional< Sector > decodeNextSector(unsigned startIdx) const
Get the next sector (starting from a certain index).
void applyWd2793ReadTrackQuirk()
void clear(unsigned size)
Clear track data.
unsigned getLength() const
Get track length.
This class implements a real drive, single or double sided.
void applyWd2793ReadTrackQuirk() override
See RawTrack::applyWd2793ReadTrackQuirk()
static constexpr unsigned MAX_DRIVES
bool peekDiskChanged() const override
void serialize(Archive &ar, unsigned version)
static std::shared_ptr< DrivesInUse > getDrivesInUse(MSXMotherBoard &motherBoard)
bool isTrack00() const override
Head above track 0.
void step(bool direction, EmuTime::param time) override
Step head.
bool isDoubleSided() override
Is disk double sided?
void invalidateWd2793ReadTrackQuirk() override
bool isWriteProtected() const override
Is disk write protected?
bool getMotor() const override
Returns the previously set motor status.
uint8_t readTrackByte(int idx) override
void setMotor(bool status, EmuTime::param time) override
Set motor on/off.
void setSide(bool side) override
Side select.
std::bitset< MAX_DRIVES > DrivesInUse
bool diskChanged() override
Is disk changed?
bool indexPulse(EmuTime::param time) override
Gets the state of the index pulse.
RealDrive(MSXMotherBoard &motherBoard, EmuDuration::param motorTimeout, bool signalsNeedMotorOn, bool doubleSided, DiskDrive::TrackMode trackMode)
EmuTime getTimeTillIndexPulse(EmuTime::param time, int count) override
Return the time till the start of the next index pulse When there is no disk in the drive or when the...
bool isDummyDrive() const override
Is there a dummy (unconnected) drive?
void flushTrack() override
void writeTrackByte(int idx, uint8_t val, bool addIdam) override
bool isDiskInserted() const override
Is drive ready?
EmuTime getNextSector(EmuTime::param time, RawTrack::Sector §or) override
unsigned getTrackLength() override
bool getSide() const override
void getMediaInfo(TclObject &result) override
This method gets called when information is required on the media inserted in the media slot of the p...
static void restoreOld(Archive &ar, std::vector< Schedulable * > schedulables)
static constexpr size_t SECTOR_SIZE
void addListElements(ITER first, ITER last)
void addDictKeyValue(const Key &key, const Value &value)
void addDictKeyValues(Args &&... args)
This file implemented 3 utility functions:
constexpr auto transform(Range &&range, UnaryOp op)
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)