private void InitSeek(int destCylinder) { _deviceCheck = !SelectedDrive.Seek(destCylinder); }
private void ReadWordCallback(ulong timeNsec, ulong skewNsec, object context) { if (_readWordCount > 0) { // Enqueue data from disk. ushort dataWord; switch (_sectorBlock) { case SectorBlock.Header: dataWord = SelectedDrive.ReadHeader(_readIndex); break; case SectorBlock.Label: dataWord = SelectedDrive.ReadLabel(_readIndex); break; case SectorBlock.Data: dataWord = SelectedDrive.ReadData(_readIndex); break; default: throw new InvalidOperationException(String.Format("Unexpected Sector Block of {0} on read.", _sectorBlock)); } Log.Write(LogType.Verbose, LogComponent.TridentController, "Read word {0}:{1}", _readIndex, Conversion.ToOctal(dataWord)); EnqueueInputFIFO(dataWord); // // Compare data with check data in output fifo, if any. // From the microcode comments: // "Note that the first two words of a block are always checked, // followed by additional words until a zero word or the end of the block." // // If we hit a tag word, the check is also completed. // TODO: verify this w/schematic & microcode. if (_outputFifo.Count > 0) { int checkWord = _outputFifo.Peek(); if ((checkWord & 0x10000) == 0 && (!_checkDone || _checkedWordCount < 2)) { // Actually pull the word off checkWord = (ushort)DequeueOutputFIFO(); Log.Write(LogType.Verbose, LogComponent.TridentController, "Pulled checkword {0} from output FIFO", Conversion.ToOctal(checkWord)); // A zero word indicates the check is complete (we will // still read the minimum two words in) if (checkWord == 0) { _checkDone = true; } // Compare didn't. else if (checkWord != dataWord) { _compareError = true; } _checkedWordCount++; } } } else { // Last four words (0 through -3) are checksum and ECC words. // The checksum words are ignored by the microcode, and // since we're not simulating faulty disks, these are always zero. EnqueueInputFIFO(0); Log.Write(LogComponent.TridentController, "Read ECC/checksum word 0"); } _readIndex++; _readWordCount--; if (_readWordCount > -4) { // More words to read, queue up the next. _readWordEvent.TimestampNsec = _readWordDuration; _system.Scheduler.Schedule(_readWordEvent); } else { Log.Write(LogComponent.TridentController, "CHS {0}/{1}/{2} {3} read from drive {4} complete.", SelectedDrive.Cylinder, SelectedDrive.Head, _sector, _sectorBlock, _selectedDrive); _readState = ReadState.Idle; _commandState = CommandState.Command; _sectorBlock++; // // Kick the output event if necessary. // RescheduleOutputEvent(); } }
/// <summary> /// "Rotates" the emulated disk platter one clock's worth. /// </summary> private void SpinDisk() { // // Roughly: If transfer is enabled: // Select data word based on elapsed time in this sector. // On a new word, wake up the disk word task if not inhibited. // // If transfer is not enabled BUT the disk word task is enabled, // we will still wake up the disk word task if the appropriate clock // source is selected. // // We simulate the movement of a sector under the heads by dividing // the sector into word-sized timeslices. Not all of these slices // will actually contain valid data -- some are empty, used by the microcode // for lead-in or inter-record delays, but the slices are still used to // keep things in line time-wise; the real hardware uses a crystal-controlled clock // to generate these slices during these periods (and the clock comes from the // drive itself when actual data is present). For our purposes, the two clocks // are one and the same. // // // Pick out the word that just passed under the head. This may not be // actual data (it could be the pre-header delay, inter-record gaps or sync words) // and we may not actually end up doing anything with it, but we may // need it to decide whether to do anything at all. // DataCell diskWord = SelectedDrive.ReadWord(_sectorWordIndex); bool bWakeup = false; // // If the word task is enabled AND the write ("crystal") clock is enabled // then we will wake up the word task now. // if (!_seclate && !_wdInhib && !_bClkSource) { bWakeup = true; } // // If the clock is enabled OR the WFFO bit is set (go ahead and run the bit clock) // and we weren't late reading this sector, then we will wake up the word task // and read in the data if transfers are not inhibited. // if (!_seclate && (_wffo || _diskBitCounterEnable)) { if (!_xferOff) { if (!IsWrite()) { // Read operation: // Debugging: on a read/check, if we are overwriting a word that was never read by the // microcode via KDATA, log it. if (_debugRead) { Log.Write(LogType.Warning, LogComponent.DiskController, "--- missed sector word {0}({1}) ---", _sectorWordIndex, _kDataRead); } Log.Write(LogType.Verbose, LogComponent.DiskWordTask, "Sector {0} Word {1} read into KDATA", _sector, Conversion.ToOctal(diskWord.Data)); _kDataRead = diskWord.Data; _debugRead = diskWord.Type == CellType.Data; _lastDiskActivity = DiskActivityType.Read; } else { // Write Log.Write(LogType.Verbose, LogComponent.DiskController, "Sector {0} Word {1} (rec {2}) to be written with {3} from KDATA", _sector, _sectorWordIndex, _recNo, Conversion.ToOctal(_kDataWrite)); if (_kDataWriteLatch) { _kDataRead = _kDataWrite; _kDataWriteLatch = false; } if (_syncWordWritten) { // Commit actual data to disk now that the sync word has been laid down SelectedDrive.WriteWord(_sectorWordIndex, _kDataWrite); _lastDiskActivity = DiskActivityType.Write; } } } if (!_wdInhib) { bWakeup = true; } } // // If the WFFO bit is cleared (wait for the sync word to be read) // then we check the word for a "1" (the sync word) to enable // the clock. This occurs late in the cycle so that the NEXT word // (not the sync word) is actually read. // if (!IsWrite() && !_wffo && diskWord.Data == 1) { _diskBitCounterEnable = true; } else if (IsWrite() && _wffo && _kDataWrite == 1 && !_syncWordWritten) { Log.Write(LogType.Normal, LogComponent.DiskController, "Sector {0} Sync Word {1} (rec {2}) written.", _sector, _sectorWordIndex, _recNo); _syncWordWritten = true; // "Adjust" the write index to the start of the data area for the current record. // This is cheating. switch (_recNo) { case 0: _sectorWordIndex = DiabloDrive.HeaderOffset; break; case 1: _sectorWordIndex = DiabloDrive.LabelOffset; break; case 2: _sectorWordIndex = DiabloDrive.DataOffset; break; } } if (bWakeup) { Log.Write(LogType.Verbose, LogComponent.DiskWordTask, "Word task awoken for word {0}.", _sectorWordIndex); _system.CPU.WakeupTask(TaskType.DiskWord); } // Last, move to the next word. _sectorWordIndex++; }
internal static void MakeDriveValid(SelectedDrive item) { item.SelectedDrivePath = @"C:\"; Assert.IsTrue(String.IsNullOrWhiteSpace(item.Error), "SelectedDrive had validation errors: {0}", item.Error); }
internal static void MakeDriveInvalidNoImages(SelectedDrive item) { item.SelectedDrivePath = @"C:\"; Assert.IsTrue(!String.IsNullOrWhiteSpace(item.Error)); Assert.IsTrue(item.PhotoFiles.Count == 0); }
internal static void MakeDriveInvalidNoDriveAndNoImages(SelectedDrive item) { item.SelectedDrivePath = String.Empty; Assert.IsTrue(!String.IsNullOrWhiteSpace(item.Error)); Assert.IsTrue(item.PhotoFiles.Count == 0); }