private void ShowSector() { richTextBox1.Clear(); SectorInfo sector = image[track][sectorIndex]; TrackLV.Text = track.ToString(); SectorLV.Text = sector.SectorNumber.ToString(); SizeLV.Text = sector.SizeBytes.ToString(); int strs = sector.SizeBytes / 16; byte[] temp = new byte[16]; StringBuilder sb = new StringBuilder(); for (int i = 0; i < strs; i++) { sb.Length = 0; sb.Append(BitConverter.ToString(sector.Data, i * 16, 16)); sb.Append(" "); for (int t = 0; t < 16; t++) { temp[t] = sector.Data[i * 16 + t] < 32 ? (byte)'.' : sector.Data[i * 16 + t]; } sb.AppendLine(Encoding.ASCII.GetString(temp, 0, 16)); richTextBox1.AppendText(sb.ToString()); } }
public void LoadFormatFromXml(string fileName) { int track = 0; MList <SectorInfo> layout = new MList <SectorInfo>(50); MList <SectorInfo> temp = new MList <SectorInfo>(50); using (XmlTextReader xml = new XmlTextReader(fileName)) { while (xml.Read()) { if (xml.NodeType == XmlNodeType.EndElement) { break; } if (xml.NodeType != XmlNodeType.Element) { continue; } if (xml.Name == "DiskFormat") { string name = xml.GetAttribute("Name"); if (string.IsNullOrEmpty(Name) && !string.IsNullOrEmpty(name)) { Name = name; } while (xml.Read()) { if (xml.NodeType == XmlNodeType.EndElement) { break; } if (xml.NodeType != XmlNodeType.Element) { continue; } switch (xml.Name) { case "Track": TrackFormat tf = Tracks[track]; tf.Layout.EnsureCapacity(Int32.Parse(xml.GetAttribute("SectorCount"))); layout.Cnt = 0; if (!xml.IsEmptyElement) { while (xml.Read()) { if (xml.NodeType == XmlNodeType.EndElement) { break; } if (xml.NodeType != XmlNodeType.Element) { continue; } switch (xml.Name) { case "Sector": SectorInfo s = new SectorInfo() { Cylinder = Int32.Parse(xml.GetAttribute("Cylinder")), Head = Int32.Parse(xml.GetAttribute("Head")), SectorNumber = Int32.Parse(xml.GetAttribute("Number")), SizeCode = Int32.Parse(xml.GetAttribute("Size")), TimeMs = Double.Parse(xml.GetAttribute("Time"), NumberStyles.Any, CultureInfo.InvariantCulture) }; layout.Add(s); break; } } } if (tf.Layout.Cnt > 0) { tf.Layout.CopyTo(temp); layout.CopyTo(tf.Layout); for (int i = 0; i < temp.Cnt; i++) { int index = tf.FindSectorIndex(temp[i].SectorNumber); if (index < 0) { Log.Info?.Out($"Сектор не найден. Трек: {track} | Сектор: {temp[i].SectorNumber}"); continue; } tf.Layout.Data[index].Data = temp[i].Data; tf.Layout.Data[index].ProcessResult = temp[i].ProcessResult; tf.Layout.Data[index].MapCellValue = temp[i].MapCellValue; tf.Layout.Data[index].TimeCalculated = false; } } else { layout.CopyTo(tf.Layout); for (int i = 0; i < layout.Cnt; i++) { tf.Layout.Data[i].MapCellValue = MapCell.Unprocessed; tf.Layout.Data[i].TimeCalculated = false; } } tf.FormatName = tf.GetFormatName(); tf.SpinTime = tf.GetSpinTime(); tf.MapModified = true; track++; break; } } } } } }
public unsafe int ReadRandomSectors(TimeSpan timeout, int stopOnNthFail = 0) { int goodSectors = Params.Image.GoodSectors; int imageTracks = Params.Image.SizeTracks; try { bool useTimeout = timeout > TimeSpan.Zero; DateTime timeoutTime = DateTime.Now.Add(timeout); Timer sectorTimer = new Timer(); Random random = new Random(); MList <Point> sectorArray = new MList <Point>(Params.Image.SizeSectors); for (int track = Params.FirstTrack; track < Params.LastTrack; track++) { if (Params.Side == DiskSide.Side0 && track % 2 != 0) { continue; } if (Params.Side == DiskSide.Side1 && track % 2 != 1) { continue; } for (int sector = 0; sector < Params.Image[track].Layout.Cnt; sector++) { if (Params.Image[track][sector].ProcessResult != SectorProcessResult.Good) { sectorArray.Add(new Point(track, sector)); } } } int prevCylinder = -1; int failCounter = 0; while (sectorArray.Cnt > 0 && ((useTimeout && DateTime.Now < timeoutTime) || !useTimeout) && (stopOnNthFail == 0 || (failCounter < stopOnNthFail))) { int index = random.Next(sectorArray.Cnt); int track = sectorArray[index].X; int sectorIndex = sectorArray[index].Y; SectorInfo sector = Params.Image[track][sectorIndex]; TrackFormat tf = Params.Image[track]; int error = 23; Params.Map.MarkSectorAsProcessing(track, sectorIndex); bool badWritten = sector.ProcessResult == SectorProcessResult.Bad; for (int attempt = 0; attempt < Params.SectorReadAttempts; attempt++) { if (Aborted) { goto abort; } int cylinder = track / 2; if (cylinder == prevCylinder) { int tempCylinder = cylinder + (random.Next(2) == 0 ? -1 : 1); tempCylinder = Math.Max(0, tempCylinder); tempCylinder = Math.Min(tempCylinder, Params.LastTrack / 2); Driver.Seek(DriverHandle, tempCylinder * 2); if (Aborted) { goto abort; } Thread.Sleep(random.Next((int)TrackFormat.SpinTimeStandard)); // Ждем случайное время чтобы приехать на нужный цилиндр в случайной точке. } prevCylinder = cylinder; Driver.Seek(DriverHandle, track); if (Aborted) { goto abort; } WinApi.RtlZeroMemory(memoryHandle, (UIntPtr)sector.SizeBytes); sectorTimer.Start(); error = Driver.ReadSectorF(DriverHandle, memoryHandle, sector.Cylinder, sector.SectorNumber, sector.SizeCode, track & 1, sector.Head, 0x0a, 0xff); sectorTimer.Stop(); if (error == 0) { Params.Image.WriteGoodSector(memoryHandle, tf, sectorIndex); sectorArray.RemoveAt(index); failCounter = 0; timeoutTime = DateTime.Now.Add(timeout); break; } // Ошибка 27 может быть как в случае отсутствия заголовка, так и в случае когда заголовок имеет ошибку CRC. // Тут сложный вопрос что с этим делать: писать сектор как CrcError или как NoHeader. Пишется как NoHeader. // (проверить была ошибка CRC в заголовке или заголовок вообще не был найден можно сравнив sectorTimer.ElapsedMs с временем вращения tf.SpinTime) bool noHeader = error == 21 || error == 1112 || error == 27; Make30HeadPositionings(error, track); if (error == 23) { Params.Image.WriteBadSector(memoryHandle, tf, sectorIndex); badWritten = true; } else if (noHeader && !badWritten) { Params.Image.WriteNoHeader(memoryHandle, tf, sectorIndex); } } failCounter++; } return(Params.Image.GoodSectors - goodSectors); abort: Log.Info?.Out("Чтение прервано."); } finally { Params.Map?.ClearHighlight(MapCell.Processing); } return(Params.Image.GoodSectors - goodSectors); }
/// <summary> /// Сканирование трека. Сканировать может в двух режимах: по времени оборота диска и по зацикливанию потока секторов. /// При сканировании по времени оборота диска скорость диска должна быть в районе 300 об/мин, т.е. замедлять диск не надо, иначе будут ошибки. /// Если byLooping == true, то конец трека определяется по зацикливанию потока секторов, но сектора сканируются не менее 150 мс и не более 1000 мс. /// </summary> /// <param name="track"></param> /// <returns></returns> public unsafe bool ScanFormat(TrackFormat result, int track, bool byLooping) { int ilayoutCnt = 0; scanFormatBuffer.Layout.Cnt = 0; SectorInfo[] layout = scanFormatBuffer.Layout.Data; tagFD_READ_ID_PARAMS pars = new tagFD_READ_ID_PARAMS() { flags = Driver.FD_OPTION_MFM, head = (byte)(track & 1) }; tagFD_CMD_RESULT cmdResult = new tagFD_CMD_RESULT(); scanFormatStopwatch.Restart(); scanFormatTotalTime.Stop(); int firstIndex = 1; for (int i = 0; i < scanFormatBuffer.Layout.Capacity; i++) { if (Aborted) { return(false); } if (!Driver.ReadId(DriverHandle, pars, out cmdResult)) { int error = Marshal.GetLastWin32Error(); Log.Trace?.Out($"Функция ReadId вернула false. i={i}. LastError={error}"); if (Aborted) { return(false); } Make30HeadPositionings(error, track); if (scanFormatBuffer.Layout.Cnt == 0) { goto success; } return(false); } double timeMs = scanFormatStopwatch.ElapsedMs; scanFormatStopwatch.Restart(); if (!scanFormatTotalTime.IsRunning) { scanFormatTotalTime.Restart(); } if (!byLooping && scanFormatTotalTime.ElapsedMs > 209) { goto success; } layout[ilayoutCnt] = new SectorInfo() { Cylinder = cmdResult.cyl, Head = cmdResult.head, SectorNumber = cmdResult.sector, SizeCode = cmdResult.size, TimeMs = timeMs }; ilayoutCnt++; scanFormatBuffer.Layout.Cnt = ilayoutCnt; // Бывают случаи когда заголовок сектора читается на первом обороте и не читается на втором. // Из-за этого алгоритм, в котором повтор сектора определяется сравнением с первым прочитанным заголовком, иногда даёт сбои. // Поэтому ищем встечался ли ранее только что прочитанный сектор среди всех прочитанных заголовков, а не только сравниваем с первым. // По отношению к найденному сектору замеряем время вращения. Если оно больше 250 мс, значит этот сектор был пропущен на одном из оборотов // и такую последовательность брать нельзя. Если время меньше 150, значит на треке есть сектора с одинаковыми параметрами. if (byLooping) { for (int u = 0; u < ilayoutCnt - 1; u++) { if (layout[u].Cylinder == cmdResult.cyl && layout[u].Head == cmdResult.head && layout[u].SectorNumber == cmdResult.sector && layout[u].SizeCode == cmdResult.size) { double spinTime = 0; for (int p = u + 1; p < ilayoutCnt; p++) { spinTime += layout[p].TimeMs; } if (spinTime > 250 || spinTime < 150) { continue; } firstIndex = u + 1; goto success; } } if (scanFormatTotalTime.ElapsedMs > 1000) { Log.Trace?.Out($"Не удалось найти цикл в последовательности секторов из-за нестабильного чтения. Сканироваие прервано по таймауту."); return(false); } } } return(false); success: result.AssignLayout(scanFormatBuffer, track, cmdResult.sector, firstIndex); Log.Trace?.Out($"Время сканирования трека {track}: {GP.ToString(scanFormatTotalTime.ElapsedMs, 2)}"); return(true); }
public unsafe int ReadRandomSectors(TimeSpan timeout, int stopOnNthFail = 0) { int goodSectors = Params.Image.GoodSectors; int imageTracks = Params.Image.SizeTracks; int *trackHead = stackalloc int[imageTracks]; for (int i = 0; i < imageTracks; i++) { trackHead[i] = -1; } try { bool useTimeout = timeout > TimeSpan.Zero; DateTime timeoutTime = DateTime.Now.Add(timeout); Timer sectorTimer = new Timer(); Random random = new Random(); UpperSideHead upperSideHead = Params.UpperSideHead; bool upperSideHeadScanned = false; MList <int> sectorArray = new MList <int>(Params.Image.SizeSectors); for (int i = Params.FirstSectorNum; i < Params.LastSectorNum; i++) { int track = i / Params.Image.SectorsOnTrack; if (Params.Side == DiskSide.Side0 && track % 2 != 0) { continue; } if (Params.Side == DiskSide.Side1 && track % 2 != 1) { continue; } if (Params.Image.Sectors[i] != SectorProcessResult.Good) { sectorArray.Add(i); } } int prevCylinder = -1; int failCounter = 0; while (sectorArray.Cnt > 0 && ((useTimeout && DateTime.Now < timeoutTime) || !useTimeout) && (stopOnNthFail == 0 || (failCounter < stopOnNthFail))) { int index = random.Next(sectorArray.Cnt); int sectorNum = sectorArray.Data[index]; int track = sectorNum / Params.SectorsOnTrack; SectorInfo sector = Params.Image.StandardFormat.Layout.Data[sectorNum - track * Params.SectorsOnTrack]; int error = 23; Params.Image.Map?.ClearHighlight(MapCell.Processing); Params.Image.Map?.MarkAsProcessing(sectorNum); if (Params.UpperSideHeadAutodetect && trackHead[track] != -1) { upperSideHead = (UpperSideHead)trackHead[track]; } if (Params.UpperSideHeadAutodetect && !upperSideHeadScanned && (track & 1) != 0 && trackHead[track] == -1) { UpperSideHead ush = upperSideHead; if (ScanHeadParameter(ref ush, track, Params.CurrentTrackFormat) == 0) { upperSideHead = ush; trackHead[track] = (int)upperSideHead; Log.Trace?.Out($"Параметр Head для трека {track} определен: {(int)upperSideHead}"); } else { Log.Trace?.Out($"Параметр Head для трека {track} определить не удалось."); } upperSideHeadScanned = true; } bool noHeader = false; bool badWritten = Params.Image.Sectors[sectorNum] == SectorProcessResult.Bad; for (int attempt = 0; attempt < Params.SectorReadAttempts; attempt++) { if (Aborted) { goto abort; } int cylinder = track / 2; if (cylinder == prevCylinder) { int tempCylinder = cylinder + (random.Next(2) == 0 ? -1 : 1); tempCylinder = Math.Max(0, tempCylinder); tempCylinder = Math.Min(tempCylinder, Params.LastTrack / 2); Driver.Seek(DriverHandle, tempCylinder * 2); if (Aborted) { goto abort; } Thread.Sleep(random.Next((int)TrackFormat.SpinTimeStandard)); // Ждем случайное время чтобы приехать на нужный цилиндр в случайной точке. } prevCylinder = cylinder; Driver.Seek(DriverHandle, track); if (Aborted) { goto abort; } sectorTimer.Start(); error = Driver.ReadSector(DriverHandle, memoryHandle, track, sector.SectorNumber, upperSideHead, sector.SizeCode); sectorTimer.Stop(); if (error == 0) { goto sectorReadSuccessfully; } noHeader = error == 21 || (error == 27 && sectorTimer.ElapsedMs > Params.CurrentTrackFormat.SpinTime); Make30HeadPositionings(error, track); if (error == 23) { Params.Image.WriteBadSectors(sectorNum, memoryHandle, 1, false); badWritten = true; } if (noHeader && track % 2 == 1 && Params.UpperSideHeadAutodetect && trackHead[track] == -1) { UpperSideHead ush = upperSideHead; if (ScanHeadParameter(ref ush, track, Params.CurrentTrackFormat) == 0) { upperSideHead = ush; trackHead[track] = (int)upperSideHead; Log.Trace?.Out($"Параметр Head для трека {track} определен: {(int)upperSideHead}"); attempt--; } else { Log.Trace?.Out($"Параметр Head для трека {track} определить не удалось."); } } } failCounter++; if (!badWritten) { Params.Image.WriteBadSectors(sectorNum, 1, noHeader); } continue; sectorReadSuccessfully: Params.Image.WriteGoodSectors(sectorNum, memoryHandle, 1); trackHead[track] = (int)upperSideHead; sectorArray.RemoveAt(index); failCounter = 0; timeoutTime = DateTime.Now.Add(timeout); } return(Params.Image.GoodSectors - goodSectors); abort: Log.Info?.Out("Чтение прервано."); } finally { Params.Image.Map?.ClearHighlight(MapCell.Processing); } return(Params.Image.GoodSectors - goodSectors); }
public static unsafe int ReadSectorF(IntPtr driverHandle, IntPtr memoryHandle, int cyl, int sector, int sizeCode, int phead, int head, int gap, int datalen) { uint dwRet; tagFD_READ_WRITE_PARAMS readParams = new tagFD_READ_WRITE_PARAMS() { flags = FD_OPTION_MFM, phead = (byte)phead, cyl = (byte)cyl, head = (byte)head, sector = (byte)sector, size = (byte)sizeCode, eot = (byte)(sector + 1), gap = (byte)gap, datalen = (byte)datalen, }; bool r = DeviceIoControl(driverHandle, IOCTL_FDCMD_READ_DATA, (IntPtr)(&readParams), (uint)sizeof(tagFD_READ_WRITE_PARAMS), memoryHandle, (uint)SectorInfo.GetSizeBytes(sizeCode), out dwRet, IntPtr.Zero); int error = !r?Marshal.GetLastWin32Error() : 0; Log.Trace?.Out($"Cyl: {cyl} | PHead: {phead} | Head: {head} | Sector: {sector} | Gap: {gap} | DataLen: {datalen} | Error: {error} | Bytes Read: {dwRet}"); return(error); }
public static unsafe int ReadSector(IntPtr driverHandle, IntPtr memoryHandle, int track, int sector, UpperSideHead head, int sizeCode) { uint dwRet; tagFD_READ_WRITE_PARAMS readParams = new tagFD_READ_WRITE_PARAMS() { flags = FD_OPTION_MFM, phead = (byte)(track & 1), cyl = (byte)(track >> 1), head = head == UpperSideHead.Head1 ? (byte)(track & 1) : (byte)0, sector = (byte)sector, size = (byte)sizeCode, eot = (byte)(sector + 1), gap = 0x0a, datalen = 0xff, }; bool r = DeviceIoControl(driverHandle, IOCTL_FDCMD_READ_DATA, (IntPtr)(&readParams), (uint)sizeof(tagFD_READ_WRITE_PARAMS), memoryHandle, (uint)SectorInfo.GetSizeBytes(sizeCode), out dwRet, IntPtr.Zero); int error = !r?Marshal.GetLastWin32Error() : 0; Log.Trace?.Out($"Track: {track} | Sector: {sector} | Error: {error} | Bytes Read: {dwRet}"); return(error); }
/// <summary> /// Получение ближайшего к головке дисковода сектора в соответствии с моделью вращения диска. /// </summary> /// <param name="track">Трек</param> /// <param name="waitTimeMs">Время которое надо пропустить.</param> /// <param name="skip">Количество секторов которые надо пропустить.</param> /// <param name="timeAfterSync">Время которое должно пройти с момента последней синхронизации до конца возвращенного сектора.</param> /// <returns></returns> public SectorInfo GetClosestSector(int track, double waitTimeMs, int skip, out double timeAfterSync, out bool error) { error = false; timeAfterSync = 0; int index = (syncSectorIndex + 1) % Layout.Cnt; int indexNext = index; if (!IsSync) { return(Layout.Data[indexNext]); } double time0 = Timer.ElapsedMs + waitTimeMs; double time = time0 > 0 ? time0 % SpinTime : time0; int newLongSectorIndex = LongSectorIndex; if (FormatName == TrackFormatName.TrDos5_04T) { // Вычисление смещения длинного сектора для формата TR-DOS 5.04T относительно трека для которого была определена схема расположения секторов. int diff = -2 * (track - LayoutTrack); newLongSectorIndex = Mod(LongSectorIndex + diff, Layout.Cnt); // Вычисление смещения точки синхронизации. int diff0 = -2 * (track - syncTrack); index = Mod(index + diff0, Layout.Cnt); } for (int i = 0; i < Layout.Cnt * 10; i++) { int x = index; if (FormatName == TrackFormatName.TrDos5_04T) { // Для формата TR-DOS 5.04T здесь происходит подмена секторов налету если попали на длинный сектор, имитируя его смещение. // Всё делается в расчете на то что все короткие сектора имеют одинаковую длительность порядка 12 мс, и длинный сектор только один. if (index == newLongSectorIndex) { x = LongSectorIndex; // newLongSectorIndex подменяем на LongSectorIndex, делая его длинным. } else if (index == LongSectorIndex) { x = newLongSectorIndex; // Берем короткий сектор (или длинный, если индексы совпадают) если оказались на старом длинном. } } timeAfterSync += Layout.Data[x].TimeMs; if (time < Layout.Data[x].TimeMs && time <= Layout.Data[x].GetGapTimeSpan()) { if (skip == 0) { SectorInfo r = Layout.Data[index]; r.TimeMs = Layout.Data[x].TimeMs; return(r); } skip--; } time -= Layout.Data[x].TimeMs; index = (index + 1) % Layout.Cnt; } // Здесь раньше выбрасывалось исключение в предположении что оно никогда не выбросится, пока у одного из пользователей оно не выбросилось (в августе 2020). // Почему так произошло до конца непонятно. Произошло это в режиме чтения Fast. Возможно в этом случае надо будет переключаться в режим чтения Standard. // Пока решено возвращать ошибку. error = true; return(Layout.Data[indexNext]); }