// Pi1541 - A Commodore 1541 disk drive emulator // Copyright(C) 2018 Stephen White // // This file is part of Pi1541. // // Pi1541 is free software : you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Pi1541 is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Pi1541. If not, see . #ifndef DRIVE_H #define DRIVE_H #include "m6522.h" #include "DiskImage.h" #include #if defined(EXPERIMENTALZERO) inline int ceil(float num) { int inum = (int)num; if (num == (float)inum) { return inum; } return inum + 1; } #endif class Drive { public: Drive(); void SetVIA(m6522* pVIA) { m_pVIA = pVIA; pVIA->GetPortB()->SetPortOut(this, OnPortOut); } static void OnPortOut(void*, unsigned char status); bool Update(); #if defined(EXPERIMENTALZERO) void DriveLoopWrite(); void DriveLoopRead(); void DriveLoopReadNoFluxNoCycles(); void DriveLoopReadNoFlux(); void DriveLoopReadNoCycles(); #endif void Insert(DiskImage* diskImage); inline const DiskImage* GetDiskImage() const { return diskImage; } void Eject(); void Reset(); inline unsigned Track() const { return headTrackPos; } inline unsigned SectorPos() const { return headBitOffset >> 3; } inline unsigned GetHeadBitOffset() const { return headBitOffset; } inline bool IsMotorOn() const { return motor; } inline bool IsLEDOn() const { return LED; } inline unsigned char GetLastHeadDirection() const { return lastHeadDirection; } // For simulated head movement sounds private: #if defined(EXPERIMENTALZERO) int32_t localSeed; inline void ResetEncoderDecoder(unsigned int min, unsigned int /*max*/span) { UE7Counter = 16 - CLOCK_SEL_AB; // A and B inputs of UE7 come from the VIA's CLOCK SEL A/B outputs (ie PB5/6) UF4Counter = 0; localSeed = ((localSeed * 1103515245) + 12345) & 0x7fffffff; fluxReversalCyclesLeft = (span) * (localSeed >> 11) + min; } #else inline float GenerateRandomFluxReversalTime(float min, float max) { return ((max - min) * ((float)rand() / RAND_MAX)) + min; } // Inputs in micro seconds inline void ResetEncoderDecoder(float min, float max) { UE7Counter = CLOCK_SEL_AB; // A and B inputs of UE7 come from the VIA's CLOCK SEL A/B outputs (ie PB5/6) UF4Counter = 0; randomFluxReversalTime = GenerateRandomFluxReversalTime(min, max); } #endif inline void UpdateHeadSectorPosition() { // Disk spins at 300rpm = 5rps so to calculate how many 16Mhz cycles one rotation takes;- // 16000000 / 5 = 3200000; static const float CYCLES_16Mhz_PER_ROTATION = 3200000.0f; bitsInTrack = diskImage->BitsInTrack(headTrackPos); headBitOffset %= bitsInTrack; cyclesPerBit = CYCLES_16Mhz_PER_ROTATION / (float)bitsInTrack; #if defined(EXPERIMENTALZERO) cyclesPerBitInt = cyclesPerBit; cyclesPerBitErrorConstant = (unsigned int)((cyclesPerBit - ((float)cyclesPerBitInt)) * static_cast(0xffffffff)); cyclesForBitErrorCounter = (unsigned int)(((cyclesForBit)-(int)(cyclesForBit)) * static_cast(0xffffffff)); #endif } inline void MoveHead(unsigned char headDirection) { if (lastHeadDirection != headDirection) { if (((lastHeadDirection - 1) & 3) == headDirection) { if (headTrackPos > 0) headTrackPos--; // else head bang } else if (((lastHeadDirection + 1) & 3) == headDirection) { if (headTrackPos < HALF_TRACK_COUNT - 1) headTrackPos++; } lastHeadDirection = headDirection; UpdateHeadSectorPosition(); } } void DumpTrack(unsigned track); // Used for debugging disk images. #if defined(EXPERIMENTALZERO) inline u32 AdvanceSectorPosition(int& byteOffset) { if (++headBitOffset == bitsInTrack) headBitOffset = 0; byteOffset = headBitOffset >> 3; return (~headBitOffset) & 7; } #else inline u32 AdvanceSectorPosition(int& byteOffset) { ++headBitOffset %= bitsInTrack; byteOffset = headBitOffset >> 3; return (~headBitOffset) & 7; } #endif unsigned cachedheadTrackPos = -1; int cachedbyteOffset = -1; unsigned char cachedByte = 0; inline bool GetNextBit() { int byteOffset; int bit = AdvanceSectorPosition(byteOffset); //Why is it faster to check both conditions here than to update the cache when moving the head? if (byteOffset != cachedbyteOffset || cachedheadTrackPos != headTrackPos) { cachedByte = diskImage->GetNextByte(headTrackPos, byteOffset); cachedbyteOffset = byteOffset; cachedheadTrackPos = headTrackPos; } return ((cachedByte >> bit) & 1) != 0; //return diskImage->GetNextBit(headTrackPos, byteOffset, bit); } inline void SetNextBit(bool value) { int byteOffset; int bit = AdvanceSectorPosition(byteOffset); diskImage->SetBit(headTrackPos, byteOffset, bit, value); } DiskImage* diskImage; // When swapping disks some code waits for the write protect signal to go high which will happen if a human ejects a disk. // Emulate this by asserting the write protect signal for a few cycles before inserting the new disk image. u32 newDiskImageQueuedCylesRemaining; // CA1 (input) // - !BYTE SYNC // CA2 (output) // - BYTE SYNC enable // CB1 (NC) // - check pulled H/L // CB2 (output) // - R/!W m6522* m_pVIA; #if defined(EXPERIMENTALZERO) unsigned int cyclesLeftForBit; unsigned int fluxReversalCyclesLeft; unsigned int UE7Counter; u32 writeShiftRegister; unsigned int cyclesForBitErrorCounter; unsigned int cyclesPerBitErrorConstant; unsigned int cyclesPerBitInt; #else int UE7Counter; u8 writeShiftRegister; #endif float cyclesForBit; u32 readShiftRegister; unsigned headTrackPos; u32 headBitOffset; float randomFluxReversalTime; int UF4Counter; int UE3Counter; int CLOCK_SEL_AB; bool SO; unsigned char lastHeadDirection; u32 bitsInTrack; float cyclesPerBit; bool motor; bool LED; }; #endif