© Иван Рощин, Москва
| ZXNet | : 500:95/462.53 |
| : bestview@mtu-net.ru | |
| WWW | : http://www.ivr.da.ru |
Дата последнего редактирования: 12.11.2002.
Вы удивитесь, если узнаете, сколько программ не распознают ошибки при
работе с диском, неправильно распознают их, а также зависают или сбрасываются
при их возникновении. Разумеется, при написании собственных программ таких
ситуаций следует избегать, хотя это не
В этой статье пойдёт речь об одной из самых распространённых
ошибок
Известно, что перед началом любой дисковой операции, связанной с чтением или записью данных, необходимо, чтобы диск был установлен в дисковод и дверца дисковода была закрыта. Если это условие не выполняется, программа (если она грамотно написана) обычно выдаёт одно из следующих сообщений:
Специальная процедура для определения наличия диска в дисководе была бы
очень полезной. Приведу лишь один из возможных примеров её использования: если
ваша программа использует интерпретатор системных функций
Рассмотрим несколько возможных способов реализации такой процедуры:
Недостатки: неудачное считывание сектора ещё не значит, что диск отсутствует. Вполне может быть, что сектор был записан с ошибкой, или сектора с таким номером вообще нет на дорожке (т.к. в общем случае формат диска может быть произвольным).
Достоинства: при работе с диском фиксированного формата этот способ вполне подходит.
Недостатки: в общем случае диск может быть и неформатированным, при этом ничего прочитать не удастся, и мы получим неверный результат.
Достоинства: этот способ годится для любого форматированного диска и достаточно прост в реализации.
Прежде чем говорить о достоинствах и недостатках этого метода, вспомним, что такое регистр состояния и что можно узнать с его помощью.
Регистр состояния отражает корректность заданной команды, а также состояние микроконтроллера при её выполнении. Каждый бит в нём указывает на определённый параметр и связан с выполнением конкретной команды. При выполнении команд восстановления и позиционирования биты регистра состояния имеют следующее назначение:
| 0 | занято, идёт выполнение команды. |
| 1 | индексный импульс. |
| 2 | магнитная головка находится в исходном положении. |
| 3 | ошибка в контрольном коде. |
| 4 | ошибка позиционирования. |
| 5 | магнитная головка находится в рабочем положении. |
| 6 | защита записи. |
| 7 | указывает на готовность дисковода к выполнению команды. |
Нас будут интересовать лишь первый и шестой биты регистра состояния, поэтому поговорим о них подробнее.
Если посмотреть на пятидюймовую дискету, можно увидеть большое отверстие в центре, а рядом с ним маленькое круглое отверстие в корпусе дискеты и отверстие чуть меньшего диаметра на самом магнитном диске. Это так называемое индексное отверстие, которое служит для ориентации магнитной головки дисковода на дискете. Когда отверстия в корпусе и на самом диске совпадают (а это происходит при каждом обороте диска), контроллер считает, что магнитная головка находится в начале дорожки.
Значение первого бита регистра состояния определяется состоянием индексного отверстия:
| Ситуация | Значение первого бита |
|---|---|
| Диска нет. | 1 |
| Диск есть, отверстия в корпусе и на самом диске совпадают. | 1 |
| Диск есть, отверстия в корпусе и на самом диске не совпадают. | 0 |
Шестой бит регистра состояния отражает состояние прорези для защиты записи:
| Ситуация | Значение шестого бита |
|---|---|
| Диска нет. | 0 |
| Диск есть, прорезь открыта. | 0 |
| Диск есть, прорезь закрыта. | 1 |
Ясно, что если первый бит равен 0 или шестой бит равен 1, диск
присутствует. Но,
Теперь мы легко можем указать искомый алгоритм:
| s1 | s2 | s3 | Номер ситуации |
|---|---|---|---|
| 0 | 0 | 0 | 2 |
| 0 | 0 | 1 | 3 |
| 0 | 1 | 0 | 2 |
| 0 | 1 | 1 | 3 |
| 1 | 0 | 0 | 1 |
| 1 | 0 | 1 | 3 |
| 1 | 1 | 0 | 2 |
| 1 | 1 | 1 | 3 |
Расшифровка номера ситуации:
| 1 | в дисководе нет диска. |
| 2 | диск есть, но дверца дисковода не закрыта. |
| 3 | диск есть и дверца дисковода закрыта. |
Примечание: с вероятностью примерно 3% ситуация 2 может быть распознана как ситуация 1 это происходит, если индексное отверстие на диске случайно оказывается как раз напротив отверстия на корпусе дискеты.
Ну что же, осталось только привести текст процедуры на ассемблере:
;***********************************************
;Процедура D_READY определяет, вставлен ли
;диск в дисковод и закрыта ли дверца дисковода.
;В регистре A возвращается число 1, 2 или 3,
;обозначающее номер ситуации:
;
; 1 - в дисководе нет диска.
; 2 - диск есть, но дверца дисковода не
; закрыта.
; 3 - диск есть и дверца дисковода закрыта.
;
; Примечание: с вероятностью примерно 3%
;ситуация 2 может быть распознана как
;ситуация 1.
D_READY XOR A ;Устанавливаем один и тот же
LD C,#3F ;номер цилиндра
CALL TO_WG93 ;в регистре дорожки
LD C,#7F ;и в регистре данных.
CALL TO_WG93
LD A,#18 ;Позиционирование. Головка не будет
CALL TO_1F ;двигаться, но двигатель включится.
CALL READY ;Ждем выполнения...
CALL STATUS ;Читаем регистр состояния
LD B,A ;и сохраняем в регистре B.
;Теперь читаем регистр состояния в цикле,
;чтобы определить, вращается ли диск.
;Если #300 раз будет считано одно и то же
;значение, значит, диск не вращается.
;
; Примечание: число #300 подобрано опытным путем,
;исходя из максимального времени работы цикла
;(это чуть больше времени одного оборота диска,
;или 200 ms). Если процессор работает быстрее или
;диск вращается медленнее, число нужно увеличить.
LD HL,#300 ;счетчик
LOOP_D PUSH HL
PUSH BC
CALL STATUS
POP BC
POP HL
DEC HL
CP B ;Сравниваем считанное и ранее
;полученное значения.
LD A,1 ;Формируем 0-й бит регистра A.
JR NZ,DISK_R ;Если диск крутится.
LD A,H
OR L
JR NZ,LOOP_D ;Продолжаем считывать значение...
;Формируем в регистре A байт с таким содержимым:
;
; Бит 0: 0 - диск не крутится, 1 - крутится.
; Бит 1: то же, что было в 6-м бите регистра состояния.
; Бит 2: то же, что было в 1-м бите регистра состояния.
;
;В результате в регистре A получаем число от 0 до 7,
;и далее определяем номер ситуации по таблице.
DISK_R BIT 6,B
JR Z,READY1
SET 1,A
;Установлено значение 1-го бита.
READY1 BIT 1,B
JR Z,READY2
SET 2,A
;В A получено нужное значение.
READY2 LD (THIS_B+2),A ;Модифицируем команду.
;Но сначала отключаем дисковод:
XOR A ;Эти команды,
CALL TO_1F ;в принципе,
LD A,#D0 ;можно
CALL TO_1F ;выбросить.
;Эта команда соответствует LD A,(IX+0)...LD A,(IX+7):
LD IX,TABL
THIS_B LD A,(IX) ;Получили номер ситуации.
EI
RET
;Таблица для определения номера ситуации:
TABL DB 2,3,2,3,1,3,2,3
;***************************************
;Вспомогательные процедуры:
TO_1F LD C,#1F
TO_WG93 LD IX,#2A53
JR TO_DOS
READY LD IX,#3EF5
TO_DOS PUSH IX
JP #3D2F
;***************************************
;Процедура STATUS возвращает содержимое
;регистра состояния.
;Вход: A - содержимое регистра дорожки,
; B - содержимое регистра сектора,
; которые будут установлены после
; выхода из процедуры.
;Выход: A - значение, считанное из порта #1F.
;Прерывания после выхода запрещены!
STATUS DI
LD C,#7F ;A=N цилиндра.
CALL TO_WG93 ;В регистр данных.
LD (RG_D+1),A ;дорожка
LD A,B
LD (RG_S+1),A ;сектор
;Сохраняем содержимое ячеек, которые
;могут быть испорчены:
LD A,(#5D0E)
LD (ST1+1),A
LD A,(#5D0C)
LD (ST2+1),A
LD A,(#5CB6)
LD (ST3+1),A
LD A,(#5D1F)
LD (ST4+1),A
LD A,(#5C3A)
LD (ST5+1),A
LD A,(#5D17)
LD (ST6+1),A
LD HL,(#5D1A)
LD (ST7+1),HL
LD HL,(#5D1C)
LD (ST8+1),HL
LD HL,(#5CF8)
LD (ST9+1),HL
;Устанавливаем содержимое некоторых
;ячеек для правильной работы:
LD A,#FF
LD (#5D0C),A
LD (#5D1F),A
DEC A
LD (#5D0E),A
LD A,#F4
LD (#5CB6),A
LD HL,S_SPEC
LD (#5D1A),HL
LD HL,0
ADD HL,SP
LD DE,-12
ADD HL,DE
LD (#5D1C),HL
LD A,0 ;0 в регистр
LD C,#3F ;дорожки.
CALL TO_WG93
LD A,#0A ;#A в регистр
LD C,#5F ;сектора.
CALL TO_WG93
LD D,1
LD IX,16179
CALL TO_DOS ;Определили #1F.
;Теперь восстанавливаем содержимое
;регистров дорожки и сектора:
RG_D LD A,0
LD C,#3F
CALL TO_WG93
RG_S LD A,0
LD C,#5F
CALL TO_WG93
;Восстанавливаем ранее запомненное
;содержимое ячеек:
ST1 LD A,0
LD (#5D0E),A
ST2 LD A,0
LD (#5D0C),A
ST3 LD A,0
LD (#5CB6),A
ST4 LD A,0
LD (#5D1F),A
ST5 LD A,0
LD (#5C3A),A
ST6 LD A,0
LD (#5D17),A
ST7 LD HL,0
LD (#5D1A),HL
ST8 LD HL,0
LD (#5D1C),HL
ST9 LD HL,0
LD (#5CF8),HL
LD A,B
RET
;Сюда будет передано управление, если
;0-й бит регистра состояния равен 1:
S_SPEC POP BC ;Содержимое порта.
LD HL,(#5D1C)
LD DE,12 ;Восстанавливаем
ADD HL,DE ;указатель
LD SP,HL ;стека.
JR RG_D
Вам наверняка встречались программы, контролирующие смену диска в дисководе
(например, Jemmini Commander). В них проверяются именно указанные выше биты
системного регистра (в принципе, достаточно следить за изменением лишь
Примечание: программа работоспособна при условии, что индексное отверстие на диске не находится напротив соответствующего отверстия на корпусе дискеты (это связано с использованием первого бита регистра состояния).
Кстати, у такой программы есть одна особенность, связанная с
чтением регистра состояния. Чтобы прочесть его значение, нужно, чтобы
двигатель дисковода работал. Поэтому используется следующий приём: выполняется
команда позиционирования на дорожку, номер которой уже записан в регистр
дорожки. При этом головка не будет двигаться, но двигатель дисковода
включится. После окончания выполнения этой команды читается регистр состояния.
Сразу после этого двигатель выключается, например, с помощью записи 0 в порт
#FF (или в порт #1F). Между двумя последовательными чтениями регистра
состояния делается пауза, обычно 1/50 секунды. При этом двигатель не успевает
раскрутиться, и лампочка дисковода не загорается. Но если внимательно
присмотреться, видно, что лампочка всё же
Привожу текст небольшой демонстрационной программы, контролирующей смену диска по вышеуказанному алгоритму. Программа выводит на экран сообщения «DISK PRESENT» и «DISK NOT PRESENT». Выход по нажатию любой клавиши (когда диск присутствует).
CALL 3435 ;CLS
LD A,2
CALL 5633
M_1 CALL READ_S
BIT 1,A
JR NZ,M_4
M_2 LD A,1
LD (N),A
LD DE,TEXT1
LD BC,23
CALL 8252 ;"DISK PRESENT"
M_3 XOR A
IN A,(254)
AND 31
CP 31
RET NZ ;Если нажата клавиша, выходим.
CALL READ_S
BIT 1,A
JR Z,M_3
M_4 XOR A
LD (N),A
LD DE,TEXT2
LD BC,23
CALL 8252 ;"DISK NOT PRESENT"
M_5 CALL READ_S
BIT 1,A
JR NZ,M_5
JR M_2
N DB 0 ;1 - есть диск, 0 - нет.
TEXT1 DB 22,0,0,16,7,17,0,"DISK PRESENT "
TEXT2 DB 22,0,0,16,7,17,0,"DISK NOT PRESENT"
;***************************************
;Процедура READ_S осуществляет чтение
;регистра состояния. Перед чтением
;производится включение двигателя
;дисковода, а после чтения - выключение
;и пауза в 1/50 секунды.
READ_S XOR A ;Устанавливаем один и тот же
LD C,#3F ;номер цилиндра
CALL TO_WG93 ;в регистре дорожки
LD C,#7F ;и в регистре данных.
CALL TO_WG93
LD A,#18 ;Позиционирование. Головка не будет
CALL TO_1F ;двигаться, но двигатель включится.
CALL READY ;Ждем выполнения...
CALL STATUS ;Читаем регистр состояния.
EI ;Разрешаем ранее запрещенные прерывания.
PUSH AF
XOR A
CALL TO_1F ;Выключаем двигатель.
LD A,#D0
CALL TO_1F
HALT ;Пауза в 1/50 секунды.
POP AF
RET
;***************************************
;Вспомогательные процедуры:
TO_1F LD C,#1F
TO_WG93 LD IX,#2A53
JR TO_DOS
READY LD IX,#3EF5
TO_DOS PUSH IX
JP #3D2F
<Далее следует текст процедуры STATUS>
|
Другие мои статьи о
|