Страница Ивана Рощина > Статьи >

© Иван Рощин, Москва

ZXNet: 500:95/462.53
E-mail: bestview@mtu-net.ru
WWW: http://www.ivr.da.ru

TR-DOS: как не допустить ошибки?

(«ZX-Ревю» 5—6/1997)
Дата последнего редактирования: 12.11.2002.

Вы удивитесь, если узнаете, сколько программ не распознают ошибки при работе с диском, неправильно распознают их, а также зависают или сбрасываются при их возникновении. Разумеется, при написании собственных программ таких ситуаций следует избегать, хотя это не так-то просто. Дело в том, что корректная обработка ошибок становится возможной лишь при работе напрямую с микроконтроллером дискового интерфейса, а это доступно далеко не каждому. Несмотря на это, надёжность программ можно значительно увеличить, если использовать наряду с обычными функциями TR-DOS специальные процедуры для распознавания ошибочных ситуаций.

В этой статье пойдёт речь об одной из самых распространённых ошибок ввода-вывода — отсутствии диска в дисководе. Также будет сказано несколько слов о прямом программировании ВГ93 и о программах, контролирующих смену диска.

Известно, что перед началом любой дисковой операции, связанной с чтением или записью данных, необходимо, чтобы диск был установлен в дисковод и дверца дисковода была закрыта. Если это условие не выполняется, программа (если она грамотно написана) обычно выдаёт одно из следующих сообщений:

Специальная процедура для определения наличия диска в дисководе была бы очень полезной. Приведу лишь один из возможных примеров её использования: если ваша программа использует интерпретатор системных функций TR-DOS и не осуществляет перехват ошибок, то при их возникновении (в частности, при отсутствии диска) может произойти сброс компьютера (что очень нежелательно). Но если с помощью указанной процедуры обнаружить отсутствие диска ещё до вызова интерпретатора, можно избежать неприятных последствий (конечно, если не произойдёт какой-нибудь другой ошибки).

Рассмотрим несколько возможных способов реализации такой процедуры:

  1. Пробуем прочитать какой-нибудь сектор (естественно, не с помощью вызова интерпретатора системных функций, а при прямом программировании контроллера). Так как считанная информация не понадобится, чтение можно проводить, скажем, в область ПЗУ. В случае неудачи считаем, что диска нет.

Недостатки: неудачное считывание сектора ещё не значит, что диск отсутствует. Вполне может быть, что сектор был записан с ошибкой, или сектора с таким номером вообще нет на дорожке (т.к. в общем случае формат диска может быть произвольным).

Достоинства: при работе с диском фиксированного формата этот способ вполне подходит.

  1. Делаем попытку прочитать первый встреченный заголовок сектора. В случае неудачи считаем, что диска нет.

Недостатки: в общем случае диск может быть и неформатированным, при этом ничего прочитать не удастся, и мы получим неверный результат.

Достоинства: этот способ годится для любого форматированного диска и достаточно прост в реализации.

  1. Используем регистр состояния микроконтроллера.

Прежде чем говорить о достоинствах и недостатках этого метода, вспомним, что такое регистр состояния и что можно узнать с его помощью.

Регистр состояния отражает корректность заданной команды, а также состояние микроконтроллера при её выполнении. Каждый бит в нём указывает на определённый параметр и связан с выполнением конкретной команды. При выполнении команд восстановления и позиционирования биты регистра состояния имеют следующее назначение:

0 —  занято, идёт выполнение команды.
1 —  индексный импульс.
2 —  магнитная головка находится в исходном положении.
3 —  ошибка в контрольном коде.
4 —  ошибка позиционирования.
5 —  магнитная головка находится в рабочем положении.
6 —  защита записи.
7 —  указывает на готовность дисковода к выполнению команды.

Нас будут интересовать лишь первый и шестой биты регистра состояния, поэтому поговорим о них подробнее.

Если посмотреть на пятидюймовую дискету, можно увидеть большое отверстие в центре, а рядом с ним — маленькое круглое отверстие в корпусе дискеты и отверстие чуть меньшего диаметра на самом магнитном диске. Это так называемое индексное отверстие, которое служит для ориентации магнитной головки дисковода на дискете. Когда отверстия в корпусе и на самом диске совпадают (а это происходит при каждом обороте диска), контроллер считает, что магнитная головка находится в начале дорожки.

Значение первого бита регистра состояния определяется состоянием индексного отверстия:

Табл. 1
Ситуация Значение первого бита
Диска нет. 1
Диск есть, отверстия в корпусе и на самом диске совпадают. 1
Диск есть, отверстия в корпусе и на самом диске не совпадают. 0

Шестой бит регистра состояния отражает состояние прорези для защиты записи:

Табл. 2
Ситуация Значение шестого бита
Диска нет. 0
Диск есть, прорезь открыта. 0
Диск есть, прорезь закрыта. 1

Ясно, что если первый бит равен 0 или шестой бит равен 1, диск присутствует. Но, во-первых, при наличии диска эти биты могут принимать, вообще говоря, любые значения, а во-вторых, надо ещё узнать, закрыта ли дверца дисковода. Как же определить её состояние? Если она закрыта и двигатель дисковода включен, то диск будет вращаться. Если же дверца открыта, диск не будет вращаться. Ну а как определить, вращается диск или нет, мы уже знаем: нужно циклически проверять значение первого бита регистра состояния. При каждом обороте диска этот бит меняет своё значение с 0 (отверстие на корпусе диска не совпадает с отверстием на диске) на 1 (отверстия совпадают), и далее — опять на 0. Если этот бит не будет менять своего значения — либо в дисководе нет диска, либо дверца открыта.

Теперь мы легко можем указать искомый алгоритм:

Табл. 3
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). В них проверяются именно указанные выше биты системного регистра (в принципе, достаточно следить за изменением лишь какого-нибудь одного из двух битов). Вот простейший алгоритм работы такой программы:

  1. Прочитать значение регистра состояния. Если первый бит равен 1, перейти к шагу 4.
  2. N=«Диск есть».
  3. Прочитать значение регистра состояния. Если первый бит равен 0, перейти к шагу 3.
  4. N=«Диска нет».
  5. Прочитать значение регистра состояния. Если первый бит равен 1, перейти к шагу 5, иначе к шагу 2.

Примечание: программа работоспособна при условии, что индексное отверстие на диске не находится напротив соответствующего отверстия на корпусе дискеты (это связано с использованием первого бита регистра состояния).

Кстати, у такой программы есть одна особенность, связанная с чтением регистра состояния. Чтобы прочесть его значение, нужно, чтобы двигатель дисковода работал. Поэтому используется следующий приём: выполняется команда позиционирования на дорожку, номер которой уже записан в регистр дорожки. При этом головка не будет двигаться, но двигатель дисковода включится. После окончания выполнения этой команды читается регистр состояния. Сразу после этого двигатель выключается, например, с помощью записи 0 в порт #FF (или в порт #1F). Между двумя последовательными чтениями регистра состояния делается пауза, обычно 1/50 секунды. При этом двигатель не успевает раскрутиться, и лампочка дисковода не загорается. Но если внимательно присмотреться, видно, что лампочка всё же чуть-чуть светится (яркость свечения обратно пропорциональна длине паузы). Так вот, на некоторых дисководах (а именно — на ЕС 5323.01) было замечено, что при работе программ, контролирующих смену диска, лампочка постоянно горит и двигатель работает.

Привожу текст небольшой демонстрационной программы, контролирующей смену диска по вышеуказанному алгоритму. Программа выводит на экран сообщения «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>

Другие мои статьи о TR-DOS:

1. 

«Доступ к порту #1F в TR-DOS 5.03». «ZX-Ревю» 1—2/1997.

2. 

«О сокращении времени форматирования». «ZX-Ревю» 1—2/1997.

3. 

«Работа с диском при включённых прерываниях». Adventurer #9, «Чёрная ворона» #3, «Радиолюбитель. Ваш компьютер» 6/2000 (дополненная версия).

4. 

«Вывод трёхсимвольных расширений файлов в операционной системе TR-DOS». «Радиолюбитель. Ваш компьютер» 7/2000.

5. 

«Усовершенствованный алгоритм определения смены диска». «Радиолюбитель. Ваш компьютер» 9/2000.

6. 

«Расширения файлов TR-DOS». «Радиолюбитель. Ваш компьютер» 12/2000, «Радиомир. Ваш компьютер» 9/2001 (под псевдонимом BV_Creator).

7. 

«Обрубаем файлам „хвост“». «Радиомир. Ваш компьютер» 4/2002.

8. 

«Использование избыточной информации для защиты файлов от повреждений». «Радиомир. Ваш компьютер» 11/2002.

9. 

«Проверка корректности файловой структуры дисков TR-DOS». «Радиомир. Ваш компьютер» 6/2004.

Страница Ивана Рощина > Статьи >