Глава 7. Доступ к дискетам
ГЛАВА 7. ДОСТУП К ДИСКЕТАМ
[ Оглавление ]
С этой главы начинается подробное по-элементное
рассмотрение возможностей IBM/PC - начиная с дисководов для
гибких магнитных дисков. Изложение материала этой и четырех
последующих глав будет следовать организации служебных
процедур системы BIOS в ПЗУ. Однако, наше изложение не
ограничивается только описанием возможностей, которые
предоставляются системой BIOS. Для каждой из возможностей
будет рассматриваться вся техническая информация, полезная
или интересная любому, кто хочет понять особенности
реализации этой возможности и особенности ее использования.
Если обратиться к трем уже упоминавшимся областям
интересов, то следует отметить, что вся информация полностью
относится только к IBM/PC. Что касается совместимых с IBM/PC
компьютеров, можно ожидать, что большая часть информации
применима и к ним тоже, но степень применимости будет
зависеть от того, насколько точно конкретный компьютер
копирует IBM/PC; для всех компьютеров, которые рекламируются
как совместимые по системе BIOS , данная информация полностью
применима. Для семейства компьютеров, работающих под
управлением операционной системы MS-DOS, приведенная в этих
главах информация будет типичной с точки зрения возможностей,
которые должен предоставлять любой компьютер, но нельзя
рассчитывать, что все детали полностью совпадут.
7.1. ТРИ УРОВНЯ ДОСТУПА К ДИСКЕТЕ
Имеются три способа, позволяющие осуществлять доступ к
данным, хранящимся на дискете, - из программы на языке
программирования высокого уровня (такого, как Паскаль или
БЕЙСИК), посредством вызова функции ДОС и посредством
обращения к служебной процедуре системы BIOS в ПЗУ. Все эти
способы образуют трехуровневую иерархию, в которой служебные
процедуры системы BIOS образуют нижний, наиболее примитивный
уровень. Уровень функций ДОС строится на основе служебных
процедур системы BIOS. Уровень языков программирования
является верхним уровнем и строится на основе функций ДОС и
служебных процедур системы BIOS.
Может существовать еще один, более высокий уровень,
обеспечиваемый системой управления базами данных того или
иного типа.
Операции ввода/вывода с дискетой на уровне языков
программирования обычно удовлетворяют все потребности по
манипулированию данными, хранящимися на дискете. В тех
случаях, когда этого оказывается недостаточно, не хватает как
раз не "атомарных" функций низкого уровня, а процедур более
высокого уровня, прредоставляемых только системами управления
базами данных. Однако, иногда требуется доступ к дискете на
более низком уровне, например, для прямого чтения или записи
секторов дискеты. Для выполнения таких операций необходимо
обращаться к двум другим уровням ввода/вывода с дискетой - к
уровням ДОС И BIOS.
В главе 4 мы уже рассмотрели доступ ко всем функциям
ДОС, включая и функции ввода/вывода с дискетой. Одно из
главных достоинств функций ввода/вывода на уровне ДОС
заключается, как Вы могли уже заметить изучая главу 4, в
ширине диапазона их возможностей. Функции ДОС позволяют
выполнять как операции довольно высокого уровня (например,
поиск в справочнике файлов по родовым именам), так и операции
среднего уровня (чтение и запись логических записей) и даже
операции низкого уровня (чтение и запись секторов по
абсолютному номеру).
Широкие возможности уровня функций ДОС делают их
особенно привлекательными для использования. Языки
программирования, такие как Паскаль, обычно организуют все
свои средства ввода/вывода с помощью функций ДОС. Если Вы
хотите освободиться от ограничений, накладываемых
организацией системы ввода/вывода выбранного Вами языка
программирования, я советую обратиться к функциям
ввода/вывода ДОС.
Если главная причина обращения к специальным операциям
ввода/вывода связана с необходимостью чтения или записи
секторов дискеты, то лучше воспользоваться функциями ДОС, чем
процедурами BIOS. Это связано только с тем, что
дополнительный сервис, предоставляемый функциями ДОС, избавит
Вас от необходимости заботиться о таких деталях как
повторение попытки выполнения операции в случае ошибки или
выдержки времени, необходимого для разгона двигателя
дисковода. Тем не менее, Вам может потребоваться прямой
доступ к процедурам уровня BIOS, например, если нужно обойти
процедуры обнаружения ошибок ДОС или когда нужно выполнить
операцию, которая не выполняется средствами ДОС. По этой
причине ниже приводится описание служебных процедур системы
BIOS в ПЗУ, предназначеннных для работы с дискетами.
7.2. Служебные процедуры обслуживания дискет системы
BIOS в ПЗУ.
Для обслуживания дисководов в системе BIOS имеется шесть
процедур. Доступ ко всем осуществляется с помощью прерывания
номер 19 (шестнадцатиричное значение 13). В этом разделе мы
рассмотрим каждую из них и увидим некоторые примеры их
использования. Программы 7.101 и 7.102, включенные в дисковый
пакет прилагающийся к этой книге, обеспечивают необходимые
ассемблерные подпрограммы для доступа к этим процедурам BIOS,
а также определения и вспомогательные программы на Паскале,
облегчающие использование ассемблерных подпрограмм.
Первая служебная процедура, которая имеет код 0,
сбрасывает дисковую подсистему в исходное состояние. Эта
процедура может использоваться для восстановления после
различных ошибок и сбоев. Эта операция аналогична операции
сброса в ДОС (вызов функции номер 13), но она выполняется на
более ниэком уровне системы BIOS. Операция сброса диска
посылает команду контроллеру дисков, приводящую его в
исходное состояние. Заметим, что здесь не выполняются такие
действия как установление дисковода, выбираемого ДОС по
умолчанию, - для этого должна выполняться операция сброса на
уровне ДОС.
Наиболее очевидное использование операции сброса - в
процедуре восстановления после ошибки. Часто наилучшим
способом действий программы в случае ошибки операций с
дискетой является быстрое прекращение всех действий, с
предоставлением возможности разбираться в происшедшем
пользователю компьютера. В конце концов, большинство дисковых
ошибок связаны с причинами, которые невозможно устранить
программно, например, с механическими повреждениями дискеты
или неправильной работой механики дисковода. Однако, Ваша
программа должна использовать все имеющиеся возможности для
восстановления после ошибки и процедура сброса может стать
важной составной частью такой программы восстановления.
Вторая служебная процедура, с кодом 1, позволяет
прочитать код состояния дисковой подсистемы. Состояние
изменяется любой дисковой операцией, так что эта процедура
отражает последнюю выполненную операцию с дискетой. Эту
процедуру можно использовать для слежения за теми
операциями ввода/вывода, которыми Вы не можете управлять
непосредственно. Например, если выполняется обычная
операция ввода/вывода на уровне языка высокого уровня и
получено сообщение об ошибке, вызов этой процедуры поможет
лучше понять, что произошло, в чем состоит ошибка, так
чтобы программа могла выполнить необходимые корректирующие
действия. Ниже приведены коды состояний, которые могут
возвращаться, сами по себе или в некоторой комбинации,
этой процедурой.
КОД ШЕСТНАДЦАТИ- СМЫСЛ
РИЧНОЕ ЗНА-
ЧЕНИЕ
___ _____________ ___________________________________
1 01 Была выдана неправильная команда
2 02 Не найден адресный маркер (исполь-
зуемый для обнаружения сектора)
3 03 Была запрошена операция записи
для дискеты, защищенной от записи
4 04 Запрашиваемый сектор не найден
8 08 Неправильный адрес при операции
прямого доступа к памяти (ПДП)
9 09 При ПДП перейдена граница 64К па-
мяти
16 10 Ошибка чтения данных, обнаружен-
ная при проверке циклического из-
быточного кода
32 20 Ошибка контроллера гибких дисков
64 40 Ошибка поиска указанной дорожки
128 80 Тайм-аут: ответ от дисковода не
получен в положенное время
Третья и четвертая процедуры, с кодами 2 и 3, считывают
и записывают один или несколько секторов (объединяемых в
блок) на одной дорожке. Я советую использовать эквивалентные
функции ДОС, что избавит Вас от необходимости проверять
возникновение ошибок или повторять выполнение операций.
Заметим что эти две операции, а также соответствующие функции
ДОС позволяют считывать или записывать одновременно несколько
секторов. Если Вам необходимо выполнить очень быстрый ввод
или вывод данных, эти операции позволяют переслать вплоть до
целой дорожки данных за время одного оборота дискеты.
Чтение/запись секторов по одному требуют затраты времени
одного оборота на каждый сектор.
Впрочем чтение/запись нескольких секторов имеет и свои
недостатки. Один из таких недостатков - необходимость
организации большого буфера, размером до 4К байт при чтении
целой дорожки. Если можно работать с данными в том же месте
памяти, куда они считываются или откуда они записываются,
такой недостаток не будет существенным, но если требуется
выделить отдельную буферную область для работы с дискетой,
тогда чтение или запись нескольких секторов увеличат
требования к памяти, занимаемой Вашей программой. Имеются и
другие недостатки, например, если происходит ошибка, то ее
обнаружение и восстановление после нее могут оказаться
значительно сложнее, поскольку трудно установить на каком из
секторов произошла ошибка. Эта сложность несколько меньше при
использовании служебных процедур BIOS, поскольку они ведут
подсчет числа переданных секторов, который может быть
проанализирован программой; функции ДОС не обеспечивают такой
возможности. Проблема ошибки в процессе выполнения
многосекторной операции не слишком существенна при чтении, но
при записи она может весьма осложнить задачу восстановления,
если программа не может определить сколько секторов уже было
записано.
Пятая служебная процедура, с кодом 4, используется для
верификации данных после операции чтения или записи. Она
повторно считывает сектора, используя для проверки
циклические избыточные коды. Эта процедура используется по
ключу "/V" в команде ДОС COPY. Верификацией не следует
злоупотреблять и на то есть все основания. Во-первых,
операции с дискетами достаточно надежны. Во-вторых,
практически все ошибки при пересылке данных обнаруживаются и
сообщения о них передаются программам; очень редко операция
чтения или записи завершается успешно, а данные при этом
как-то искажаются. Однако, когда гарантия безошибочности
действительно важна, необходимо выполнять верификацию после
пересылки данных. Эта операция, помимо всего прочего,
значительно увеличивает время выполнения операции. Для
верификации чтения или записи сектора эту процедуру следует
вызывать сразу же после завершения операции пересылки.
Шестая, и последняя, операция, с кодом 6, особенно
интересна. Эта процедура форматирует дорожку дискеты,
записывая адресные маркеры секторов и заполняя сегмент данных
каждого сектора стандартным шестнадцатиричным значением F6
или CHR$(246). Должны указываться отдельно для каждого
сектора такие спецификации как дорожка, сторона и номер
сектора, а также код, задающий длину сектора. Поскольку
размер каждого сектора может быть задан независимо от
остальных, можно сформатировать дорожку с одним или
несколькими секторами нестандартного размера - что является
обычным средством для защиты от копирования. Обычными
средствами ДОС нельзя прочитать сектор, размер которого
отличается от стандартного 512-байтного, поэтому многие схемы
защиты от копирования пользуются именно такими средствами.
Более подробно мы рассмотрим это в следующем разделе,
посвященном параметрам, управляющим операциями с дискетой.
Форматировать нужно целую дорожку сразу - поскольку
промежутки между секторами и адресные маркеры взаимозависимы.
Однако, если необходимо переформатировать всего один сектор,
способ для этого все же есть. Например, если нужно изменить
формат последнего сектора на дорожке, сохранив данные в
первых семи секторах, поступите так: напишите программу для
чтения первых семи секторов, затем переформатируйте всю
дорожку и перезапишите данные. Такая процедура позволит
защитить от копирования уже существующие данные, хотя внешне
вроде бы ничего не изменяется.
Для форматирования всех секторов на дорожке должны быть
заданы следующие параметры, каждый в виде однобайтного числа:
1 - номер дорожки (от 0 до 39)
2 - сторона (0 или 1; для односторонних дискет всегда 0)
3 - номер сектора (начиная с 1; сектор 0 означает
управляющую информацию)
4 - код длины (0=128 байт, 1=256 байт, 2=512 байт
[стандартное], 3=1024)
Поскольку каждый сектор имеет собственный код длины,
включить сектор нестандартной длины в дорожку не представляет
особого труда (либо создать целую дорожку из секторов
нестандартной длины для ДОС).
Если для форматирования дорожки используется процедура
уровня BIOS, следите за правильным указанием всех параметров.
7.3. Параметры дискеты и защита от копирования
Работа дисковода для гибких дисков частично управляется
таблицей, известной как база дисков или таблица параметров
дискеты (терминология фирмы "ИБМ" пока не устоялась)
База дисков - это таблица, состоящая из одиннадцати
байт, которая содержит ряд параметров, необходимых для работы
дисководов, включая и определенную информацию о формате
дискет. В листинге 7.1 можно найти определение этой таблицы,
комментарии к которому описывают назначение каждого байта.
Как упоминалось в главе 6, один вектор прерывания,-
номер 29, хранящийся в ячейке с адресом 120,- используется
для указания местонахождения этой таблицы. Исходная версия
этой таблицы, использовавшаяся версией ДОС 1.00, находилась в
ПЗУ вместе с системой BIOS, и поэтому ее можно видеть в
листинге BIOS, приведенном в "Техническом руководстве".
Начиная с версии ДОС 1.10 вместо этой таблицы стала
использоваться таблица, размещаемая в оперативной памяти.
Такое изменение, кстати, дает нам одно преимущество. Это
позволяет экспериментировать со значениями в таблице, не
прибегая к различным ухищрениям для подмены таблицы в ПЗУ.
Любая программа может установить собственную базу диска, но
вот создать ее таким образом, чтобы эта таблица продолжала
использоваться после окончания программы, довольно сложно.
Для достижения это цели необходимо зарезервировать небольшую
область памяти так, чтобы ДОС не использовала ее. Однако, с
тех пор как ДОС стала размещать таблицу базы диска в
оперативной памяти, во всех этих приемах нет необходимости.
По сравнению с версией ДОС 1.00 в версии 1.10 сделано
всего два изменения в таблице базы диска, но они позволили
значительно увеличить скорость использования дискет.
Во-первых, они на 25% уменьшили время, выделенное дисководу
для перемещения головки с дорожки на дорожку. Это время
получило название "время шага" (SRT). В версии таблицы,
размещаемой в ПЗУ, которая использовалась в ДОС версии 1.00,
время шага равнялось 8 миллисекундам. Новое значение,
использующееся начиная с ДОС 1.10, равняется 6 миллисекундам.
Этот параметр в таблице занимает первый полубайт. Его
исходное значение было 12 (шестнадцатиричное значение C), а
новое значение 13 (шестнадцатиричное значение D). Я сначала
предположил, что такое изменение значения означает увеличение
времени шага, однако Роберт Бэттен сообщил мне, что
используется инверсное значение этого параметра, так что
большее значение означает меньшее время шага. Время
шага управляется приращениями по 2 миллисекунды, так что
изменение времени шага с 12 на 13 в таблице соответствует
уменьшению действительного времени с 8 до 6 миллисекунд.
Второе изменение резко увеличило скорость доступа к
дискете. "Время установления головки", то есть, пауза,
необходимая для стабилизации головок чтения/записи, была
уменьшена с 25 миллисекунд до нуля. Первоначально фирма "ИБМ"
чересчур предубежденно относилась к быстродействию
дисководов, так что за счет ликвидации времени установления
удалось добиться сразу большого увеличения быстродействия.
Большую часть параметров этой таблицы изменять нельзя,
однако, некоторые из них предоставляют широкие возможности
для экспериментаторов. В девятом байте хранится значение,
которое используется операцией форматирования для
инициализации секторов дискеты. Стандартное значение F6, но
Вы можете его изменить. Некоторые программы проверяют наличие
кода F6, чтобы найти неиспользовавшиеся сектора, так что это
значение вряд ли разумно изменять, если для этого нет веских
оснований. Такое изменение целесообразно применять для защиты
от копирования.
Еще один интересный параметр - это байт размера сектора.
Он определяет размер сектора, который должен считываться или
записываться. Это позволяет работать с секторами, размер
которых отличается от стандартного 512-байтного формата ДОС. В
других операционных системах могут использоваться другие
размеры секторов за счет изменения этого байта. Изменение
этого значения является наиболее простым способом организации
защиты записи. Для формирования сектора с размером, отличным
от 512 байт, необходимо использовать служебную процедуру
форматирования, описанную в предыдущем разделе. В процессе
форматирования размер каждого сектора определяется отдельно.
Однако, чтобы в дальнейшем Ваши программы могли читать или
записывать нестандартные сектора, необходимо изменять
указатель размера сектора в таблице базы диска.
Этот параметр может принимать одно из следующих значений:
Значение Размер сектора в байтах
________ _______________________
0 128
1 256
2 512 (стандартный для ДОС)
3 1024
Листинг программы 7.1 демонстрирует (на Паскале), как
осуществляется доступ к таблице базы диска и как изменяются
ее параметры. То же самое можно сделать на БЕЙСИКе, хотя
здесь требуется побайтное извлечение адресных значений. Ниже
приведена программа на БЕЙСИКе, которая находит и
распечатывает таблицу базы диска.
100 REM Программа на БЕЙСИКе для поиска и распечатки
таблицы базы диска
110 REM
120 REM Сначала необходимо установить указатель вектора
прерываний
130 REM
140 DEF SEG=0 'начало памяти
150 OFFSET1=30*4 'смещение до вектора прерывания
160 REM
170 REM Далее нужно определить смещение адреса вектора
180 REM
190 OFFSET2=PEEK(OFFSET1)+256*PEEK(OFFSET1+1)
200 REM
210 REM Теперь нужно определить сегментную часть
адреса вектора
220 REM
230 DEFSEG=PEEK(OFFSET1+2)+256*PEEK(OFFSET1+3)
240 REM
250 REM Теперь можно начинать просмотр таблицы
260 REM
270 PRINT "Ниже следуют данные талицы базы диска в
шестнадцатиричном виде"
280 FOR I=0 TO 10
290 PRINT " ";
300 IF PEEK(OFFSET2+1)=16 THEN PRINT"0"; 'добавмть
ноль для четности
310 PRINT HEX$(PEEK(OFFSET2+1));
320 NEXT I
330 PRINT:PRINT
Если Вам нужны средства для защиты от копирования, мы
предоставляем их Вам в двух формах.
Программы 7.103 и 7.104, имеющиеся на дискете,
прилагаемой к этой книге, предоставляют процедуры на
ассемблере и на Паскале, которые позволят организовать защиту
записи и приспособить ее к Вашей схеме защиты. Кроме того, в
пакет входит готовая программа защиты, которую можно вызывать
как на БЕЙСИКе, так и на Паскале.
{ Листинг 7.1 -- процедуры на Паскале для доступа к пара-}
{ метрам "базы диска" }
{ Этот фрагмент программы демонстрирует возможность доступа }
{ к "базе диска" или таблице управления дискетами. Этот }
{ листинг разрабатывался для обеспечения наиболее легкой }
{ настройки на конкретные нужды пользователей перед его ис- }
{ пользованием. }
module Листинг 7.1;
type
disk_base_type = array [1..11] of byte;
{ Ниже кратко описаны все 11 байт таблицы : }
{ 1 : время шага; время вывода головки }
{ 2 : время ввода головки; режим ПДП }
{ 3 : время ожидания отключения двигателя }
{ 4 : длина сектора в байтах: 0=128,1=256,2=512,3=1024 }
{ 5 : конец дорожки (номер последнего сектора) }
{ 6 : длина межсекторного промежутка }
{ 7 : длина сегмента данных (когда не задана длина сектора) }
{ 8 : длина промежутка для форматирования }
{ 9 : байт заполнитель для форматирования }
{ 10: время установления головки }
{ 11: время запуска двигателя }
var
disk_base : disk_base_type;
disk_base_pointer : ads of disk_base_type; {сегментированный
адрес}
vector_pointrer : ads of adsmem; {сегментированный адрес}
procedure disk_base_access;
begin
{сначала указатель устанавливается на вектор прерывания базы}
{ диска}
vector_pointer.s := 0; {раздел сегмента 0, начало памяти}
vector_pointer.r := 30 * 4; {смещение для вектора 30}
{далее считывается вектор, чтобы установить местоположение}
{ таблицы}
disk_base_pointer := vector_pointer ^;
{указатель базы диска ("disk_base_pointer") теперь содержит }
{сегментированный адрес, на который указывал указатель век- }
{тора ("vector_pointer") -- это означает, что указатель базы}
{диска теперь укаазывает на адрес фактического местонахожде-}
{ния таблицы }
{Теперь будет выполняться проверка того. находится ли таблица}
{в оперативной памяти. Это можно установить попытавшись из- }
{менить значение в таблице и затем проверив, изменилось ли }
{оно на самом деле. Вместо этого мы просто проверим, в каких }
{адресах памяти располагается таблица. }
if disk_base_pointer.s >= #F000 then
begin
{адрес слишком велик - таблица в ПЗУ и изменить ее не удастся}
end
else
begin
{все в порядке - таблица наверняка в ОЗУ}
end;
{Теперь скопируем таблицу в свой буфер }
disk_base := disk_base_pointer ^;
{База диска теперь содержит копию данных таблицы, на которую}
{указывал сегментированный адрес в указателе базы диска }
{Далее, изменим размер на 1024 байта и байт-заполнитель для}
{форматирования на АА. Эти изменения мы выполняем в своей }
{копии, которую затем переносим в реальную таблицу. Впрочем,}
{то же самое изменение можно было бы выполнить и непосред- }
{ственно в самой таблице.}
disk_base [4] := 3; {установить размер сектора 1024 }
disk_base [9] := #AA {шестнадцатиричное значение AA }
{или десятичное 170 выбрано произвольно}
disk_base_pointer ^ := disk_base; {возвратить таблицу на }
{место}
end;
end. {конец модуля listing_7_1 }
[ Оглавление ]