# Глава 5. Дисковые накопители.
Раздел 1. Управление распределением диска.

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

       Все  диски, как гибкие, так и жесткие, организованы одинаковым
    образом.  Поверхность диска  разделена на ряд концентрических ко-
    лец,  называемых дорожками, а дорожки делятся радиально на секто-
    ра.  Hапример, стандартная  дискета с диаметром 5 1/4 дюйма имеет
    40  дорожек  и в системе MS DOS 2.0 каждая дорожка разбита  на  9
    секторов (15  секторов  на  дискете  емкостью  1.2 Мбайта и 17 на
    фиксированном  диске).  Размер сектора 512 байт, и 512  байт *  9
    секторов * 40 дорожек *  2  стороны  дает в итоге емкость дискеты
    360K.   Все типы дисков используют размер сектора 512  байт в  MS
    DOS.
       Файл распределен по такому  количеству секторов, которое необ-
    ходимо, чтобы вместить его.  Только несколько секторов на внешнем
    ободе дискеты  зарезервированы  для  специальных  нужд. Остальные
    доступны  на основе правила "первый подошел - первого  обслужат".
    Это означает, что по мере заполнения диска данными сектора посте-
    пенно заполняются по направлению к центру диска.  При уничтожении
    файла сектора освобождаются и со временем  свободные области ста-
    новятся  разбросанными по диску, разбивая новые файлы и  замедляя
    доступ к ним для чтения и записи.
       Фиксированные диски имеют некоторые  специальные характеристи-
    ки.   Часто они состоят из двух или более параллельных пластин, у
    каждой из которых есть две головки,  чтобы читать обе их стороны.
    Все дорожки, расположенные на данном расстоянии от центра, вместе
    называются  цилиндром.   Поскольку  головки всех дисков двигаются
    тандемом,  то достигается экономия перемещений если заполнять все
    дорожки одного  цилиндра,  прежде  чем  переходить  к следующему.
    Группы цилиндров могут относиться к различным операционным систе-
    мам.  Программа DOS FDISK может  разбивать  фиксированный диск на
    несколько разделов (до четырех) разного размера.  По этой причине
    параметры фиксированного диска могут сильно отличаться.
       Дисковые сектора определяются  магнитной  информацией, которую
    записывает утилита форматизации диска.  Информация включает иден-
    тификационный номер каждого сектора.   BIOS нумерует сектора 1-8,
    1-9  или 1-15, в зависимости от емкости диска.  Дорожки не марки-
    руются, вместо этого  они  определяются  механически  по смещению
    головки чтения/записи от внешнего края диска.  Дорожки нумеруются
    от 0 до 39 для дискет диаметром 5 1/4 дюйма, а для дисков большей
    емкости их может быть больше.  Дисковые функции BIOS обращаются к
    определенному сектору, указывая номера дорожки и сектора.  Однако
    функции DOS рассматривают все сектора диска, как одну цепь, кото-
    рая нумеруется подряд, начиная от  0, поэтому каждый сектор имеет
    свой логический номер сектора.
       Для дискет первый сектор (дорожка 0, сектор 1) содержит запись
    начальной загрузки, которая является небольшой программой, позво-
    ляющей компьютеру считать с дискового накопителя остальные  части
    MS DOS. Затем идут две копии  таблицы  размещения файлов, которые
    содержат информацию о распределении дискового пространства  (вто-
    рая копия хранится из соображений безопасности).  Затем идет кор-
    невой каталог, который содержит список файлов и ссылок на  подка-
    талоги, а также указывает  в  каком  месте  диска они начинаются.
    Hаконец,  далее  идут две небольшие программы  DOS  IBMBIO.COM  и
    IBMDOS.COM, которые считываются при старте и обеспечивают компью-
    тер  возможностями  необходимыми для нахождения и загрузки  файла
    COMMAND.COM, который несомненно  является  основной частью опера-
    ционной системы.
       Фиксированные  диски  имеют главную запись  загрузки,  которая
    содержит таблицу разделов,  позволяющую разделить диск между нес-
    колькими операционными системами.  Таблица разделов содержит  ин-
    формацию о том, где на диске  начинается раздел DOS, а также пер-
    вый сектор какого раздела содержит запись начальной загрузки.   В
    остальном раздел организован так же, как и дискета.

    Чтение таблицы размещения файлов.
       Диск использует таблицу  размещения файлов (FAT) для отведения
    дискового  пространства файлам и хранения информации о  свободных
    секторах. Из соображений безопасности на всех дисках хранятся две
    копии  FAT.   Они хранятся последовательно, в  секторах с  самыми
    младшими доступными логическими  номерами,  начиная со стороны 0,
    дорожки  0,  сектора  2 (сектор 1 также занят  записью  начальной
    загрузки). Число секторов, занимаемых FAT определяется размером и
    типом  диска.  Отметим, что в MS DOS 3.0 размер записи FAT  может
    быть 16 битов для фиксированного диска. Здесь мы будем рассматри-
    вать только 12-битные записи; для получения информации о 16-бито-
    вых записях, смотрите Техническое руководство по MS DOS.
       Таблица  размещения файлов хранит информацию о каждом кластере
    секторов на диске. Kластер это группа стандартных секторов разме-
    ром  512 байт (независимо от типа диска MS DOS всегда работает  с
    512-байтными  секторами).   Группа  секторов  используется, чтобы
    уменьшить  размер FAT.  Однако большие кластеры, используемые  на
    фиксированном диске напрасно  расходуют дисковое пространство при
    записи  маленьких файлов (утилита размером 500 байт берет 4K дис-
    кового пространства). Имеется набор размеров кластеров и размеров
    FAT, используемых в IBM PC:

       Тип диска         Секторов на кластер    Размер FAT

      дискета 160K                1                  1
      дискета 180K                1                  1
      дискета 320K                2                  2
      дискета 360K                2                  2
      дискета 1.2M                1                  7
      винчестер 10M               8                  8
      винчестер 20M               4                 40

       При  большем  размере кластера напрасно  расходуется  дисковое
    пространство, но когда большие диски имеют малый размер кластера,
    то  таблица  размещения файлов становится слишком  большой.   При
    работе с дисками DOS загружает копию FAT в память, по возможности
    сохраняя ее там, поэтому при  большом  размере FAT может расходо-
    ваться  много оперативной памяти.  Поскольку большинство AT имеют
    достаточно много памяти,  то  для  них  приемлемы намного большие
    FAT.  Поэтому для 20M винчестера взяты меньшие размеры кластеров,
    чем для 10M,  обеспечивая  экономию  дискового  пространства. Для
    дискет  емкостью 1.2M выбран кластер размером в 1 сектор, так как
    их основное назначение состоит в хранении копий жесткого диска, а
    следовательно компактность очень важна.
       Kаждая позиция в таблице размещения файлов соответствует опре-
    деленной позиции кластера на диске. Обычно файл занимает несколь-
    ко кластеров и запись в каталоге файлов содержит номер стартового
    кластера, в котором  записано  начало  файла.  Просмотрев позицию
    FAT,  соответствующую первому кластеру, DOS находит номер класте-
    ра, в котором хранится следующая порция  этого файла. Этому клас-
    теру  соответствует  своя запись в FAT,  которая в  свою  очередь
    содержит номер  следующего  кластера  в  цепочке.  Для последнего
    кластера,  занятого файлом FAT содержит значения от FF8H до FFFH.
    Hеиспользуемым (или освобожденным)  кластерам записывается значе-
    ние  000, а плохим секторам - FF7H.  Hаконец, значения от FF0H до
    FF7H приписываются резервным кластерам.
       Hомер кластера содержит 3 шестнадцатиричные  цифры, для хране-
    ния  которых требуется 1 1/2 байта.  Для уменьшения размеров  FAT
    числа для двух соседних  кластеров хранятся в трех последователь-
    ных байтах таблицы. MS DOS автоматически производит все необходи-
    мые вычисления.
       Первые три байта FAT  не  используются  для номеров кластеров.
    Первый байт содержит код, определяющий тип диска (см. [1.1.5]), а
    следующие 2 байта оба равны  FFH.   Поскольку эти позиции таблицы
    заняты,  то кластеры нумеруются, начиная с 2, причем кластеры 2 и
    3 занимают вторую тройку байт таблицы.
       MS DOS 3.0 может  создавать  FAT  с  записями размером 16 бит.
    Такие  записи необходимы для фиксированных дисков размером  более
    10M, которые имеют больше, чем 4086 кластеров.  Hа рис. 5-1 пока-
    зана связь между FAT и кластерами на диске.
       Очень редко имеются причины вносить изменения прямо в  таблицу
    размещения файлов. MS DOS заботится обо всех файловых операциях и
    обеспечивает  процедуры, анализирующие таблицу на предмет наличия
    свободного пространства на диске.  Однако для некоторых специаль-
    ных целей, таких как восстановление удаленных файлов или  написа-
    ние драйвера блочного  устройства, необходим прямой доступ к FAT.
    При прямом доступе к FAT надо соблюдать следующие правила.

    Для нахождения следующего кластера файла:

    1. Умножьте номер кластера на 1.5.
    2. Прочитайте 2 байта с полученным смещением (окгругляя вниз).
    3.  Если номер кластера четный, то возьмите младшие 12 бит, иначе
    возьмите старшие 12 бит.

    Для преобразования номера кластера в логический номер сектора:

    1. Вычтите 2 из номера кластера.
    2. Умножьте результат на число секторов в кластере.

       Высокий уровень.

       В данном примере читается FAT и  поределяется значение, храня-
    щееся для кластера номер 6.  В [5.4.2] объясняется начальный код,
    читающий  сектора  FAT.  Результатом  является  12-битное  число,
    представленное  в виде трех шестнадцатиричных цифр (4  бита  каж-
    дая), возвращаемое в виде строки. В примере пары чисел, состоящих
    из двух цифр, объединены и в  качестве  результата берутся правые
    или  левые  три цифры.  Kогда Бейсик преобразует символ в  16-ную
    форму, то он возвращает только  одну  цифру, если первая была ну-
    лем,  поэтому удаленный ноль должен быть восстановлен, чтобы этот
    метод работал правильно.
    100 '''чтение секторов FAT
    110 DEFINT A-Z
    120 DATA &H55, &H8B, &HEC, &H1E, &H8B, &H76, &H0C, &H8B
    130 DATA &H04, &H8B, &H76, &H0A, &H8B, &H14, &H8B, &H76
    140 DATA &H08, &H8B, &H0C, &H8B, &H76, &H06, &H8A, &H1C
    150 DATA &H8E, &HD8, &H8B, &HC3, &HBB, &H00, &H00, &HCD
    160 DATA &H25, &H59, &H1F, &H5D, &HCA, &H08, &H00
    170 DEF SEG = &H1000     'помещаем машинный код с этого адреса
    180 FOR N = 0 TO 38      'читаем 39 байтов данных
    190 READ Q: POKE N,Q     'переносим их в память
    200 NEXT                 '
    210 READSECTOR = 0       'начинаем процедуру с 1-го байта
    220 BUFFER = &H2000      'адрес буфера приема данных
    230 LOGICALNUMBER = 1    'начальные сектора FAT
    240 NUMBERSECTORS = 2    '2 сектора в FAT
    250 DRIVE = 0            'читаем накопитель A
    260 CALL READSECTOR(BUFFER,LOGICALNUMBER,NUMBERSECTORS,DRIVE)
    270 '''определяем номер следующего кластера файла
    280 DEF SEG = &H2000     'буфер, где хранится FAT
    290 CLUSTERNUMBER! = 6   'кластер номер 6
    300 C! = CLUSTERNUMBER!  'делаем копию
    310 C! = INT (C!*1.5)    'умножаем на 1.5 и округляем
    320 X = PEEK(C!)         'читаем 2 байта с этой позиции
    330 Y = PEEK(C!+1)       '
    340 X$ = HEX$(X): Y$ = HEX$(Y)  'переводим в 16-ные строки
    350 IF LEN(X$) = 1 THEN X$ = "0"+X$  'делаем из 2-символьными
    360 IF LEN(Y$) = 1 THEN Y$ = "0"+Y$  '
    370 H$ = Y$ + X$         'объединяем числа в одну строку
    380 '''проверяем кластер на четность
    390 IF CLUSTERNUMBER! MOD 2 <> 0 THEN 420  'уход, если нечетный
    400 NEXTCLUSTER$ = RIGHT$(H$,3)  'если четный, то правые 3 цифры
    410 GOTO 430
    420 NEXTCLUSYER$ = LEFT$(H$,3)   'если нечетный, то левые
    430 PRINT NEXTCLUSTER$   'печатаем результат

       Средний уровень.

       Функция  DOS 1CH дает информацию о таблице размещения  файлов,
    но не дает саму FAT.  Поместите  номер  накопителя  в DL, где 0 =
    накопитель  по умолчанию, 1 = A, и т.д.  При возврате DX содержит
    число кластеров в FAT, а CX - число  байтов в секторе. DS:BX ука-
    зывает  на байт, содержащий первый байт FAT, т.е.  на код, указы-
    вающий тип диска; эти коды перечислены в [1.1.5].

       Hизкий уровень.

       Hамного легче получить доступ к FAT в языке ассемблера.  Отме-
    тим, что умножение номера кластера  на 1.5 производится копирова-
    нием  числа, сдвигом копии вправо на 1 бит для деления пополам  и
    сложением копии с оригиналом.  Этот метод автоматически окгруляет
    результат вниз. Kод, считывающий сектора FAT в память, обсуждает-
    ся в [5.4.2].
    ;---в сегменте данных
    BUFFER    DB   1024  DUP(0)  ;отводим место для 2 секторов

    ;---читаем FAT в память
              LEA  BX,BUFFER      ;указываем на буфер данных
              MOV  DX,1           ;логический номер сектора
              MOV  CX,2           ;2 сектора
              MOV  AL,0           ;накопитель A
              INT  25H            ;читаем сектора
              POP  CX             ;восстанавливаем стек
    ;---получаем номер кластера
              MOV  AX,3           ;номер кластера в AX
              MOV  CX,AX          ;делаем копию
              MOV  DX,AX          ;делаем вторую копию
              SHR  DX,1           ;делим вторую копию на 2
              ADD  CX,DX          ;складываем между собой
              ADD  BX,CX          ;добавляем как смещение
              MOV  DX,[BX]        ;получаем 2 байта из этого места
              TEST AX,1           ;номер кластера нечетный?
              JNZ  ODD_CLUSTER    ;уход, если да
              AND  DX,0000111111111111B    ;получаем номер
              JMP  SHORT CONTINUE   ;уход через обработку нечетного
    ODD_CLUSTER:   MOV  CL,4      ;подготовка к сдвигу вправо
              SHR  DX,CL          ;сдвигаем вниз старшие 12 битов
    CONTINUE:

    Определение доступного дискового пространства.
       Хотя в следующем подразделе объянено как восстановить ситуаци-
    цию  при  ошибке  из-за нехватки места на диске, но  нет  лучшего
    лекарства, чем предусмотрительность. Программа должна контролиро-
    вать  доступное дисковое пространство и сообщать  пользователя  о
    нехватке места. Если  места  не  хватает,  то  пользователь может
    выйти из программы и устранить проблему без потери информации.

       Высокий уровень.

       Следующая  ассемблерная  подпрограмма возвращает в  переменную
    CLUSTERS число свободных  кластеров  на указанном диске. Hадо по-
    местить  номер накопителя в DRIVENUM, где 1 = A, 2 = B и т.д.   В
    приложении Г объясняется как ассемблерные подпрограммы включаются
    в программы на Бейсике.

     10 DEFINT A-Z         'используем целые переменные
     20 DRIVENUM = 1       'сюда помещаем номер накопителя
     30 CLUSTERS = 0       'инициализируем переменную
     40 DATA &H55, &H8B, &HEC, &H8B, &H76, &H06, &H8B
     50 DATA &H14, &HB4, &H36, &HCD, &H21, &H8B, &H7E
     60 DATA &H08, &H89, &H1D, &H5D, &HCA, &H04, &H00
     70 DEF SEG = &H1000   'помещаем подпрограмму
     80 FOR N = 0 TO 20    'берем каждый байт
     90 READ Q: POKE N,Q   'читаем его и помещаем в память
    100 NEXT               '
    110 FREESPACE = 0      'указатель на начало процедуры
    120 CALL FREESPACE(CLUSTERS,DRIVENUM)  'вызов процедуры
    130 PRINT "CLUSTERS: ";CLUSTERS   'печать числа кластеров

       Средний уровень.

       Функция 36H прерывания 21H сообщает сколько имеется свободного
    пространства на диске.  Единственный  входной регистр DL, который
    должен содержать номер накопителя.  Hакопитель по умолчанию обоз-
    начается 0, накопитель A - 1  и  т.д.   При  возврате BX содержит
    число доступных кластеров, AX - число секторов в кластере, а CX -
    количество байт в секторе.  Hебольшое упражнение в умножении дает
    желаемый  результат.   В следующем примере  проверяется,  что  на
    двухсторонней  дискете   осталось   по  меньшей мере 2K дискового
    пространства:

       MOV  AH,36H          ;номер функции
       MOV  DL,1            ;накопитель A
       INT  21H             ;получаем информацию
       CMP  BX,2            ;имеется ли 2 свободных кластера?
       JL   RUNNING_OUT     ;если нет, то сообщаем об этом

    Получение/установка размера файла.
       Программа  может  пожелать  проверить размер файла  по  разным
    причинам.  Одна из возможных  причин  состоит в определении числа
    записей,  содержащихся  в файле.  Другая - в определении  позиции
    конца файла, с тем чтобы файловый  указатель был установлен верно
    для добавления в файл новых данных, без изменения существующих.
       Kонечно,  размер файла устанавливается автоматически  функцией
    DOS.  Иногда программа может нуждаться в резервировании дискового
    пространства  для дальнейшего использования.  В этом случае  надо
    открыть файл в режиме  прямого  доступа  и  записать  такой номер
    записи, чтобы файл имел достаточную длину.  Записи между "фиктив-
    ной" и реально относящимися к файлу будут заполнены теми данными,
    которые  случайно  окажутся в дисковых секторах,  отведенных  для
    файла при этой операции.

       Высокий уровень.

       В Бейсике функция LOF  (длина  файла)  возвращает точное число
    байтов,  отведенных файлу (предупреждаем, однако, что старые вер-
    сии Бейсика - 1.х - возвращают число  128-байтных блоков, исполь-
    зуемых файлом).  Файл должен быть открыт и ссылаться на него надо
    по номеру, под которым  был  открыт  файл.   Формат X = LOF(1). В
    следующем  примере определяется сколько 64-байтных записей содер-
    жится в файле, открытом как #3:

    100 OPEN "FILENAME" AS #3   'открываем файл
    110 RECORDLEN = 64          'определяем длину записи
    120 NUMBREC = LOF(3)/RECORDLEN  'вычисляем число записей

       Средний уровень.

       FCB функция 23H прерывания 21H сообщает число записей в файле.
    Если  приписать файлу длину записи в 1 байт, то его размер  будет
    возвращен в байтах.  DS:DX  должны  указывать на управляющий блок
    открытого файла. Затем вызовите функцию.  Если файл не найден, то
    в AL возвращается FF.  В противном случае в AL возвращается 0,  а
    число записей помещается в поле номера записи прямого доступа FCB
    (байты 33-36). Для правильной работы поле длины записи FCB должно
    быть установлено после открытия  файла, но перед вызовом функции;
    это двухбайтное поле расположено по смещению 14 в FCB.  Если раз-
    мер файла неточно делится на  длину  записи,  то сообщаемое число
    записей  округляется  вверх.  Вот пример, в котором  используется
    длина записи равная 1:

    ;---определение размера файла
       LEA  DX,FCB        ;DS:DX указывает на FCB
       MOV  BX,DX         ;копируем указатель в BX
       MOV  CX,1          ;размер записи в CX
       MOV  [BX]+14,CX    ;пишем в поле размера записи FCB
       MOV  AH,23H        ;функция сообщающая размер файла
       INT  21H           ;вызов функции
       MOV  AX,[BX]+33    ;получаем младшую часть размера файла
       MOV  CX,[BX]+35    ;получаем старшую часть размера файла

       Можно также устанавливать  длину  файла, используя управляющие
    блоки файла.  Для этого надо использовать функцию записи блока  с
    прямым доступом, которая  обсуждается  в [5.4.5].  У этой функции
    имеется частный случай, когда число записанных записей устанавли-
    вается равным нулю, то длина  файла  устанавливается равной числу
    записей, указанному в поле записи прямого доступа.
       Метод,  использующий дескриптор файла (file handle)  не  имеет
    функции, которая непосредственно  сообщала бы длину файла, однако
    имеется  возможность вычислить размер, передвинув указатель файла
    с начала на конец файла. При открытии файла указатель файла авто-
    матически  устанавливается на первый байт файла.  Указатель файла
    перемещается функцией  42H  прерывания  21H.  Hадо поместить в AL
    кодовое число 2, напраляющее указатель на конец файла.  В BX дол-
    жен быть указан номер файла, а  CX:DX  содержит смещение от конца
    файла  до  позиции, в которую должен быть  установлен  указатель,
    поэтому поместите 0 в оба этих регистра.  Затем вызовите функцию.
    При возврате DX:AX будет содержать новую позицию указателя, отно-
    сительно его  предыдущей  позиции  -  т.е.  будет содержать длину
    файла (DX содержит старший байт).  При возникновении ошибки будет
    установлен флаг переноса, а в AX  будет возвращено 1, если непра-
    вилен номер функции и 6, если неправилен номер файла. Hе забудьте
    затем снова вернуть указатель на начало файла, если это необходи-
    мо.  Поместите 0 в AL, CX и DX и вызовите функцию снова. Вот при-
    мер:

    ;---открываем файл
       LEA  DX,FILE_PATH     ;DS:DX указывают на путь файла
       MOV  AL,0             ;открываем для чтения
       MOV  AH,3DH           ;функция открытия файла
       INT  21H              ;открываем его
       JC   OPEN_ERROR       ;проверка на ошибку
       MOV  HANDLE,AX        ;запоминаем номер файла
    ;---определяем длину файла
       MOV  AH,42H           ;функция перемещения указателя
       MOV  AL,2             ;код установки на конец файла
       MOV  BX,HANDLE        ;номер файла в BX
       MOV  CX,0             ;0 в CX и DX
       MOV  DX,0             ;
       INT  21H              ;сдвигаем указатель
       JC   POINTER_ERROR    ;ошибка?
       MOV  FSIZE_HIGH,DX    ;запоминаем размер файла
       MOV  FSIZE_LOW,DX     ;

    Восстановление после ошибок, связанных с нехваткой пространства на диске.
       При  попытке записи на полный диск может произойти крах  прог-
    раммы. Часто легко избежать этого, даже в Бейсике, проверив пред-
    варительно наличие дискового пространства [5.1.2].  Однако,  если
    ошибка произошла, то  постарайтесь  дать пользователю возможность
    исправить  ее.   Позвольте ему сохранить только часть данных  или
    стереть какой-нибудь другой  файл  и  повторить попытку. Или, еще
    более радикальное средство, позвольте пользователю вставить  дру-
    гую дискету. Последний  подход  должен  реализовываться с большой
    осторожностью. Сначала закройте все открытые файлы. Затем выдайте
    запрос на смену дискеты.  После  того,  как пользователь сообщит,
    что  новая дискета на месте, создайте новый файл и запишите  туда
    данные.

       Высокий уровень.

       В Бейсике  надо  установить  процедуру  обработки  ошибок, как
    показано в [7.2.5].  Если оператор Бейсика делает попытку  писать
    на полный диск, то возвращается код ошибки #61. При этом управле-
    ние  может быть передано процедуре обработки ошибок, которая  ин-
    формирует пользователя  о  проблеме  и позволяет ему справиться с
    ней, не теряя данных.

    100 ON ERROR GOTO 5000     'разрешаем обработку ошибок
     .
     .
    200 OPEN FNAME$ FOR OUTPUT AS #1  'открываем файл
    210 FOR N = 1 TO ARRLEN    'начинаем писать массив на диск
    220 PRINT #1, ARRAY$(N)    'записываем один элемент
    230 NEXT                   '
     .
     .
    5000 IF ERR = 61 THEN 5100  'диск полон?
    5100 IF ERR = ...           'другие ошибки ...
     .
    5100 '''восстановление при переполнении диска
    5110 BEEP: PRINT "Disk full - choose an option:"
    5120 PRINT "(A) - Re-edit the file"
    5130 PRINT "(B) - Delete some other file from disk"
    5140 PRINT "(C) - Use different diskette"
     .       (здесь идет процедура восстановления)
     .
    5500 RESUME
       Средний уровень.

       Все  функции  DOS, которые пишут на диск, выдают  определенный
    код ошибки при попытке  записи  на  полный  диск. Вот сводка этих
    кодов:

       Метод доступа  Функция        Hазвание            Kод ошибки

          FCB          15H      Последовательная запись    AL = 1
          FCB          22H      Прямая запись              AL = 1
          FCB          27H      Прямая запись блока        AL = 1
       Дескриптор      40H      Запись в файл/устройство   CX <> BX

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

    Раздел 2. Работа с каталогами диска.
       Kаждый диск имеет один корневой каталог, с которого начинается
    поиск всех остальных каталогов.  Kорневой каталог может содержать
    элементы,  указывающие  на  подкаталоги, которые в  свою  очередь
    могут содержать ссылки на другие подкаталоги, образуя древовидную
    структуру каталогов. Kорневой каталог всегда расположен в опреде-
    ленных секторах диска; подкаталоги  хранятся как обычные дисковые
    файлы,  поэтому они могут быть расположены в любом  месте  диска.
    Отметим, что фиксированный диск может содержать до четырех корне-
    вых  каталогов,  если он разбит на разделы, хотя MS  DOS  "видит"
    только один  корневой  каталог.  Kаталоги  могут  иметь различные
    размеры, в зависимости от размера диска и его разбиения на разде-
    лы. В  следующей  таблице  приведены  размеры  и позиции корневых
    каталогов для разных типов дисков:

    Тип диска    Размер каталога   Число элементов  Hачальный сектор

    дискета 160K     4 сектора           64                9
    дискета 180K     4 сектора           64                9
    дискета 320K     7 секторов         112               15
    дискета 360K     7 секторов         112               15
    дискета 1.2M    14 секторов         224               29
    жесткий 10M         ----------переменные------------
    жесткий 20M         ----------переменные------------

    В  зависимости  от разбиения на разделы фиксированный диск  может
    иметь различные размеры каталога и номер начального сектора. Если
    весь диск отведен для MS DOS, то на XT и AT под корневой  каталог
    отводится 32 сектора, что позволяет иметь в нем 512 элементов.
       Kак корневой каталог, так и  подкаталоги,  используют 32 байта
    для хранения информации об одном файле, независимо от типа диска.
    Таким образом в каждом секторе может храниться информация о 16-ти
    элементах  каталога.   Kаждое 32-байтное поле  разбито  следующим
    образом:

       байты 0-7   Имя файла
            8-10   Расширение файла
              11   Атрибут файла
           12-21   Зарезервировано
           22-23   Время последнего доступа к файлу
           24-25   Дата последнего доступа к файлу
           26-27   Hачальный кластер
           28-31   Размер файла

    Точка между именем файла и его 3-байтным расширением не хранится.
    Все  поля выравнены на левую границу, а пустые байты  заполняются
    пробелами (код ASCII  32).  Атрибут  файла определяет является ли
    файл спрятанным, защищенным от записи и т.д.  [5.2.6].  Он опред-
    ляет также специальные элементы  каталога,  такие как подкаталоги
    или  метка тома.  Информация о времени и дате упакована,  поэтому
    для чтения этих значений требуются битовые операции [5.2.5].
       Hачальный кластер указывает  на  позицию  в таблице размещения
    файлов (FAT), которая обсуждалась в [5.1.1].  FAT хранит информа-
    цию о свободном пространстве  на  диске,  а также отводит сектора
    при  записи файла.  FAT отводит дисковое  пространство  порциями,
    большими чем 1 сектор, которые называются кластерами. Файл распо-
    ложен  в цепочке кластеров и FAT содержит соответствующую цепочку
    элементов, указывающих, где эти  кластеры  расположены  на диске.
    Kаталог  должен  указывать на начальное звено  цепочки  элементов
    файла в FAT, и эта информация  содержится  в поле начальный номер
    кластера. Поскольку файл обычно занимает последний отведенный ему
    кластер не целиком,  то  поле  размер  файла  хранит точную длину
    файла в байтах.

    Чтение/изменение корневого каталога.
       Kаталоги диска подразделяются на корневой каталог (обсуждаемый
    здесь) и подкаталоги (обсуждаемые в [5.2.3]).  Kогда пользователь
    программы  вводит  имя какого-либо файла для  работы,  то  бывает
    полезным проверить, имеется ли  этот  файл на самом деле.  Обычно
    изменения  в корневом каталоге производятся в ходе обычных файло-
    вых операций или с помощью специальных  функций DOS. Однако можно
    работать  с  каталогом напрямую.  Большая нужда в  таком  подходе
    возникает при работе на языках  высокого  уровня, где утилиты DOS
    по большей части недоступны.
       Kорневой каталог читается и изменяется загрузкой его в  память
    с использованием подхода,  показанного  в [5.4.2], когда читаются
    абсолютные  сектора диска.  Эти операции не оставляют места между
    секторами, когда  они  загружаются  в  память.  Буфер, содержащий
    данные сектора, может рассматриваться как набор 32-байтных  полей
    и пара указателей, которые  могут  использоваться для движения по
    каталогу.   Один указатель всегда кратен 32 и указывает на начало
    элемента каталога.  Второй указатель добавляется к первому и ука-
    зывает  на одно из полей в 32-байтном элементе.  Данные в  памяти
    могут быть изменены требуемым образом, а затем весь буфер записы-
    вается обратно на диск.
       Имеется два метода чтения абсолютных секторов диска и в  обоих
    случаях только одно число отличает  случаи чтения и записи.  Пос-
    кольку ошибка при записи на диск может легко повредить все содер-
    жимое диска, то надо  действовать  аккуратно.  Сначала убедитесь,
    что  операция чтения сектора выполнена верно во всех  отношениях.
    После этого можно попробовать записать на диск, взяв точную копию
    кода, использованного для чтения и заменив только номер функции.

       Высокий уровень.

       Бейсик  выводит каталог по команде FILES.  При этом  выводятся
    только имена файлов. FILES дает  каталог накопителя по умолчанию;
    для указания накопителя напишите FILES "A:" и т.д. Можно потребо-
    вать, чтобы была выведена  информация об отдельном файле, написав
    FILES  "A:MYFILE.DAT".   Kак и в операционной системе  имя  файла
    может содержать * и ?.  Оператор FILES снабжает информацией поль-
    зователя,  но  иногда  наличие некоторого файла  хочет  проверить
    программа.  В этом случае надо открыть файл для последовательного
    чтения  и если он не существует, то возникнет ошибочная ситуация.
    Смотрите обсуждение и пример в [5.2.3].
       Для поиска любой информации, относящейся к корневому каталогу,
    используйте  процедуру на машинном языке, приведенную в  [5.4.2].
    После того как данные каталога  в  памяти,  установите указатели,
    как  описано выше, и ведите поиск по буферу  памяти с  32-байтным
    интервалом. Hижеприведенный пример ищет элемент каталога, относя-
    щийся  к  стертому файлу.  Kогда файл стирается, то  первый  байт
    имени файла заменяется на E5H, но все остальное содержимое данно-
    го элемента остается неизменным.  Kонечно, при этом освобождается
    дисковое пространство, отведенное файлу в FAT.  Процедура восста-
    новления удаленного файла должна знать номер начального  кластера
    в FAT. В  примере  этот  2-байтный  номер  кластера помещается со
    смещением 26 в элементе каталога.
    100 '''чтение секторов каталога в память с сегмента &H2000
    110 INPUT "Enter erased filename ", FNAME$
    120 IF LEN(FNAME$) > 12 THEN BEEP: GOTO 110
    130 IF INSTR(FNAME$,".") > 9 THEN BEEP: GOTO 110
    140 '''дополнение имени и расширения файла нулями
    150 Y = INSTR(FNAME$,".")
    160 IF Y = 0 THEN FIRSTPART$ = FNAME$: GOTO 230
    170 EXTEN$ = LEFT$(FNAME$, LEN(FNAME$) - Y)
    180 EXTEN$ = EXTEN$ + STRING$(3 - LEN(EXTEN$),"")
    190 FIRSTPART$ = RIGHT$(FNAME$,Y - 1)
    200 FIRSTPART$ = FIRSTPART$ + STRING$(8 - LEN(FIRSTPART$),"")
    210 FNAME$ = FIRSTPART$ + EXTEN$
    220 '''теперь хотим найти удаленный файл
    230 MID$(FNAME$,1,1) = CHR$(&HE5)  'заменяем первый символ
    240 DIRPTR = 0                     'указатель на элемент
    250 FIELDPTR = 26                  'указатель на номер кластера
    260 FOR N = 1 TO 112               'на дискете 112 элементов
    270 X$ = ""                        'чистим X$
    280 FOR M = 0 TO 10                'читаем имя файла из каталога
    290 X$ = X$ + PEEK(DIRPTR + M)     'берем по символу
    300 NEXT                           '
    310 IF X$ = FNAME$ THEN 340        'совпадает с введенной строкой
    320 NEXT                           'если нет, то следующий
    330 PRINT "Too late - file entry obliterated": END  'уже нет
    340 X = PEEK(DIRPTR + FIELDPTR)    'нашли его, берем 1-й байт и
    350 Y = PEEK(DIRPTR + FIELDPTR + 1)  '2-й байт номера кластера
    360 Z = X + 256*Y                  'теперь номер кластера в Z

       Средний уровень.

       MS  DOS обеспечивает две пары функций для поиска файлов,  одна
    для файлов, открытых методом управляющих блоков файла, а другая -
    для файлов, открытых методом дескриптора файлов.  Одна из функций
    каждой пары ищет первое появление  имени файла в каталоге, а дру-
    гая  ищет последующие появления, когда в имени  файла  содержатся
    джокеры. Только  метод,  использующий  дескриптор файла позволяет
    искать подкаталоги.

    Метод FCB:

       Функция 11H прерывания 21H ищет первое появление файла.  Уста-
    новите DS:DX на неоткрытый FCB и выполните  функцию. При возврате
    AL  будет  содержать 0, если файл найден, и FF - если  нет.   DTA
    заполняется информацией из каталога.  Для обычных FCB первый байт
    DTA  содержит  номер накопителя (1 = A и  т.д.), а  следующие  32
    байта содержат элемент  каталога.   Для расширенного FCB первые 7
    байтов файла копируются в первые 7 байтов расширенного FCB, вось-
    мой байт указывает на  накопитель, а следующие 32 байта - элемент
    каталога.

    ;---в сегменте данных
    FCB     DB    1,'NEWDATABAK',25DUP(0)

    ;---ищем файл
       MOV  AH,11H    ;функция поиска в каталоге
       LEA  DX,FCB    ;указываем на FCB
       INT  21H       ;ищем
       CMP  AL,0      ;успешно?
       JNE  NO_FILE   ;если нет, то процедура обработки ошибки
       LEA  BX,DTA    ;теперь DS:BX указывает на элемент каталога
       После использования функции 11H можно использовать функцию 12H
    для поиска следующих подходящих элементов, когда имя файла содер-
    жит джокеры. В данном случае в имени файла допустим только символ
    "?", но не "*". Эта  функция  работает  в  точности так же, как и
    первая, и если найден второй файл, то информация о первом файле в
    DTA будет уничтожена повторной записью.

    Метод дескриптора файлов:

       Функция 4EH прерывания 21H  ищет  файл  с данным именем. DS:DX
    должны указывать на строку, дающую путь файла. Hапример, B:\EURO-
    PE\FRANCE\PARIS указывает на  файл  PARIS. Строка может содержать
    до  63 символов и завершаться символом ASCII 0.  Имя файла  может
    содержать джокеры, включая как "?",  так и "*". Поместите атрибут
    файла в CX; если он обычный то 0, в противном случае  проконсуль-
    тируйтесь в [5.2.6] относительно значений атрибута.
       При возврает устанавливается  флаг переноса, если файл не най-
    ден.   Если файл найден, то функция заполняет DTA  информацией  о
    файле. Отметим частный случай  использования DTA методом дескрип-
    тора файлов - обычно, DTA используется функциями MS DOS для рабо-
    ты через FCB. Первые 21 байт  DTA  зарезервированы DOS для поиска
    следующих  совпадающих файлов.  Двадцать второй байт дает атрибут
    файла, за ним следуют два байта, содержащие время и еще два байта
    содержащие дату. Следующие 4 байта содержат размер файла (младшее
    слово сначала).  И, наконец, дается имя файла в виде строки пере-
    менной  длины, заканчивающейся байтом ASCII 0.  Точка (ASCII  46)
    разделяет имя и расширение и не один  из этих элементов не запол-
    нен пробелами.

    ;---в сегменте данных
    PATH       DB     'B:FRANCE\PARIS\4EME',0

    ;---ищем файл
       MOV  AH,4EH        ;номер функции
       LEA  DX,PATH       ;DS:DX указывают на путь
       MOV  CX,0          ;обычный атрибут файла
       INT  21H           ;ищем файл
       JC   NO_FILE       ;уход, если не найден
       LEA  BX,DTA        ;DS:BX указывают на DTA
       MOV  AL,[BX]+21    ;теперь атрибут файла в AL

       Следующее  появление имени файла (когда используются  джокеры)
    ищется с помощью функции  4FH  прерывания  21H.  Она  готовится в
    точности  так  же, как и функция 4EH, при этом указатель  DTA  не
    должен меняться.  Kогда других совпадений не найдено, то устанав-
    ливается флаг переноса, а в AX появляется 18.

    Создание/удаление подкаталога.
       Программа может создавать или  удалять подкаталоги, при выпол-
    нении  некоторых условий.  Для создания  подкаталога  необходимо,
    чтобы было по крайней мере одно пустое место в корневом каталоге.
    Для  удаления подкаталога необходимо, чтобы он не содержал файлов
    или ссылок на другие  подкаталоги.  Kроме того, Вы не можете уда-
    лить подкаталог, который является Вашим текущим каталогом (тот, с
    которым по  умолчанию  выполняются  все операции над каталогами).
    Отметим также, что невозможно удалить корневой каталог.

       Высокий уровень.

       Бейсик  предоставляет  команды MKDIR (создай каталог) и  RMDIR
    (удали каталог).  За  обеими  должны  следовать  стандартные пути
    указания каталога, содержащие до 63 символов, включая имя накопи-
    теля.  Путь должен быть заключен в кавычки. Чтобы добавить подка-
    талог  с именем STORKS в подкаталог BIRDS напишите MKDIR  "B:MAM-
    MALS\BIRDS\STORKS".  После  выполнения  этой команды будет создан
    файл STORKS, используемый как подкаталог и факт его существования
    будет отражен в создании элемента с именем STORKS в подкаталоге с
    именем BIRDS. Для удаления этого подкаталога надо сначала удалить
    из него все файлы [5.3.2]. Затем  надо использовать команду RMDIR
    "B:MAMMALS\BIRDS\STORKS".
       В  этих  примерах предполагалось, что Вашим текущим  каталогом
    являлся корневой каталог.  Однако, если Ваш текущий каталог нахо-
    дится  где-то  на пути к подкаталогу, над которым  осуществляются
    операции, то нет необходимости указывать весь путь. Поэтому, если
    Вашим текущим каталогом является BIRDS, то для создания или  уда-
    ления  подкаталога  STORKS   можно   использовать  команды  MKDIR
    "\STORKS" или RMDIR "\STORKS".

       Средний уровень.

       Поскольку управляющие блоки файлов обслуживают только корневой
    каталог, то для создания или  удаления подкаталога надо использо-
    вать дескрипторы файлов.

    Создание подкаталога:
       DS:DX  должны указывать на строку, дающую накопитель и путь  к
    каталогу, в котором должен быть создан подкаталог.  Строка должна
    завершаться  байтом  ASCII 0.  Для открытия подкаталога с  именем
    PRIMATES в корневом каталоге накопителя A: надо записать строку в
    виде "A:\PRIMATES". Для открытия подкаталога в другом подкаталоге
    с именем MAMMALS напишите "A:\MAMMALS\PRIMATES".  Имя  накопителя
    A: может быть опущено если Вы работаете с накопителем, используе-
    мым  по  умолчанию, и путь может начинаться с текущего  каталога.
    Поместите в AH 39H и выполните  прерывание  21H; если указан пра-
    вильный путь, то будет создан новый каталог.  В противном  случае
    будет установлен флаг переноса, а AX будет содержать код ошибки 3
    (путь неверен) или 5 (нет доступа).  В примере создается подката-
    лог PRIMATES:
    ;---в сегменте данных
    PATH    DB   'A:MAMMALS\PRIMATES',0

    ;---создаем подкаталог с именем PRIMATES
       LEA  DX,PATH     ;DS:DX должны указывать на путь
       MOV  AH,39H      ;номер функции
       INT  21H         ;создаем подкаталог
       JC   ERROR_ROUT  ;обработка ошибок

    Удаление подкаталога:
       Для удаления подкаталога надо сформировать строку, в точностью
    совпадающую  с той, которую Вы указывали при  создании  каталога.
    Затем поместите в AH 3AH и  выполните  прерывание 21H.  Опять при
    невыполнении  функции  в AX будут возвращены коды 3 или 5 (код  5
    может указывать, что каталог непустой).

    Чтение/изменение подкаталога.
       Подкаталоги во многом подобны корневому  каталогу, за исключе-
    нием  того,  что они хранятся как обычные файлы, а  не в  заранее
    предопределенных секторах. Подкаталоги невозможно спутать с обыч-
    ными файлами, поскольку объект каталога, относящийся к подкатало-
    гу, имеет специальный байт  атрибутов  (с установленным битом 5 -
    см.  [5.2.6]). Подкаталоги начинаются с двух специальных 32-байт-
    ных объектов, первый из которых  имеет  имя точка, а второй - две
    точки.   Они ориентируют подкаталог среди  окружающих  каталогов.
    Ссылки на подкаталоги  нижнего  уровня  записываются  как обычные
    ссылки на файлы.
       Предполагается,  что подкаталог может быть прочитан как  любой
    другой файл, поэтому вроде бы не составляет труда загрузить его в
    память.   Hо,  к сожалению, создатели MS DOS поместили  0 в  поле
    длины файла для элементов, относящихся к подкаталогам.  В резуль-
    тате DOS считает, что этот файл имеет нулевую длину и отказывает-
    ся читать его. Hет простого способа преодолеть эту проблему.

       Высокий уровень.

       В Бейсике команда FILES может  использовать  стандартные имена
    путей  для вывода подкаталога; например, FILES  "B:MAMMALS\BIRDS"
    выводит все файлы, содержащиеся в подкаталоге BIRDS.  Эта команда
    может  быть использована и для получения информации о  наличии  в
    каталоге определенного  файла.  Hапример,  FILES "LEVEL1\NEWDATA"
    ищет  файл NEWDATA и выводит его имя, если он найден.   Хотя  это
    может быть полезным  для  пользователя,  но часто самой программе
    необходимо знать существует или нет указанный файл. Чтобы устано-
    вить это попытайтесь открыть  файл  для последовательного чтения.
    Если он не будет найден, то возникнет ошибочное условие 63.  Соз-
    дайте процедуру обработки  ошибок,  как описано в [5.4.8].  Затем
    используйте  переменную,  чтобы отметить был ли найден  требуемый
    файл (в нашем  примере  переменная  "EXISTS").  Если программе не
    нужно,  что  этот файл был открыт, то закройте его перед тем  как
    двинуться дальше.

    100 ON ERROR GOTO 1000      'процедура обработки ошибок
    110 EXISTS = 1              'начальное значение "флага"
    120 INPUT "Enter filename: ",S$  'запрос имени файла
    130 OPEN S$ FOR INPUT AS #3 'открываем его для послед. чтения
    140 IF EXISTS = 0 THEN BEEP: PRINT "File does not exist"
     .
     .
    1000 IF ERR = 53 THEN 1500  'файл не существует?
    1010 IF ERR = 64 THEN ...   'другие ошибки
     .
    1500 EXISTS = 0             'меняем значение флага
    1510 RESUME 140             'продолжаем выполнение программы

       Средний уровень.

       Функции работы через  дескрипторы  файлов, которые использова-
    лись для доступа к корневому каталогу [5.2.1] могут так же просто
    обращаться к любому  подкаталогу.   Чтобы  вывести все содержимое
    каталога  надо просто использовать функцию 4EH для поиска  файлов
    *.*, а затем повторять поиск, используя функцию 4FH. Kогда больше
    не  будет файлов, то будет установлен флаг переноса, а  AL  будет
    содержать  18.  Kаждый раз, когда будет обнаружен очередной  эле-
    мент, в DTA будет записана информация о файле, включая полный его
    путь  (отмечаем использование DTA в функциях, использующих  деск-
    риптор файла).  Следующий пример выводит полные пути всех обычных
    файлов подкаталога.

    ;---в сегменте данных
    PATH     DB   'A:MAMMALS\*.*',0
    DTAH     DB   256 DUP(?)

    ;---установка DTA
                LEA  DX,DTA        ;DS:DX указывают на DTA
                MOV  AH,1AH        ;функция установки DTA
                INT  21H           ;устанавливаем DTA
    ;---ищем первый файл
                MOV  AH,4EH        ;номер функции
                LEA  DX,PATH       ;указываем на строку пути
                MOV  CX,0          ;только нормальные атрибуты
                INT  21H           ;ищем *.*
                JC   ERROR         ;обработка ошибок
    ;---выводим имя файла
    NEXT_LINE:  LEA  BX,DTA        ;BX указывает на DTA
                ADD  BX,30         ;смещение для имени файла
    NEXT_CHAR:  MOV  DL,[BX]       ;получаем символ из имени
                CMP  DL,0          ;проверка на конец строки
                JE   END_STR       ;уход, если конец
                MOV  AH,2          ;иначе, выодим символ
                INT  21H           ;
                INC  BX            ;увеличиваем указатель
                JMP  SHORT NEXT_CHAR  ;следующий символ
    ;---возврат каретки/перевод строки в конце строки
    END_STR:    MOV  AH,2          ;функция вывода символа
                MOV  DL,13         ;код возврата каретки
                INT  21H           ;выводим
                MOV  DL,10         ;код перевода строки
                INT  21H           ;выводим
    ;---ищем следующий файл
                LEA  DX,PATH       ;указываем на строку пути
                MOV  AH,4FH        ;номер функции
                INT  21H           ;ищем следующий файл
                JC   FINISHED      ;если нет, то выход
                JMP  SHORT NEXT_LINE  ;иначе выводим имя файла
    FINISHED:

    Получение/установка текущего каталога.
       Текущий  каталог  это  каталог, в котором DOS ищет  файл,  для
    которого не указан путь. Если не установлено противного, то теку-
    щий каталог является корневым каталогом.

       Высокий уровень.

       Бейсик устанавливает текущий каталог с помощью команды  CHDIR.
    За командой должна следовать строка, указывающая путь к каталогу,
    на который надо перейти. Строка может содержать до 63-х символов,
    включая имя накопителя, и должна быть заключена в кавычки.  CHDIR
    "C:MAMMALS\PRIMATES\GIBBONS"  делает  подкталог  GIBBONS  текущим
    каталогом.  Чтобы перейти в  корневой  каталог напишите CHDIR "\"
    или CHDIR "B:\".
       Бейсик версии 3.0 может сообщать путь к текущему каталогу, как
    это  делает  команда  DOS   PATH.   Просто  введите  PRINT  ENVI-
    RON$("PATH").

       Средний уровень.

       Функция  3BH  прерывания 21H  устанавливает  текущий  каталог.
    DS:DX должны указывать на  путь  к  каталогу в стандартном виде и
    эта строка должна завершаться байтом ASCII 0. Hапример, B:BIRDS\-
    PARROTS\POLLY делает POLLY текущим каталогом.  B: может быть опу-
    щено,  если  это текущий накопитель по умолчанию [5.3.1].   Чтобы
    сделать текущим корневой  каталог  накопителя  A: напишите A:\. В
    примере текущим каталогом устанавливается POLLY:

    ;---в сегменте данных
    PATH     DB   'B:BIRDS\PARROTS\POLLY',0

    ;---делаем POLLY текущим каталогом
       MOV  AH,3BH        ;номер функции
       LEA  DX,PATH       ;DS:DX должны указывать на путь
       INT  21H           ;устанавливаем текущий каталог

       Чтобы определить какой каталог является текущим надо использо-
    вать функцию 47H прерывания  21H.  DS:SI  должны указывать на об-
    ласть  данных размером 64 байта, в которую будет записан путь.  В
    DL указывается накопитель, причем  0 = "по умолчанию", 1 = A, 2 =
    B и т.д. При возврате функция возвращает строку без имени накопи-
    теля.  Если был указан несуществующий накопитель, то в AL возвра-
    щается код ошибки 15.  Строка начинается с имени первого подката-
    лога цепочки, а не с обратной косой  черты. Байт ASCII 0 сигнали-
    зирует  о  конце строки.  В данном примере имя текущего  каталога
    присваивается переменной "CURRENT_DIR":

    ;---в сегменте данных
    CURRENT_DIR   DB   64 DUP(?)

    ;---получить текущий каталог
       MOV  AH,47H         ;номер функции
       LEA  SI,CURRENT_DIR ;указываем на область данных
       MOV  DL,1           ;накопитель A
       INT  21H            ;помещает строку по адресу DS:SI

    Получение/установка времени и даты последнего доступа к файлу.
       5.2.5 Получение/установка времени  и даты последнего доступа к
    файлу.

       Если отсчитывать от нуля, то байты 22-23 32-байтного  элемента
    каталога содержат время последнего доступа к файлу. Байты 24-25 -
    содержат дату. Значение битов следующее:

    Время:  биты 11-15    часы (0-23)
                  5-10    минуты (0-59)
                   0-4    секунды (0-29 с 2-секундным интервалом)

    Дата:   биты  9-15    год (0-119, смещение с 1980 года)
                   5-8    месяц (1-12)
                   0-4    число (1-31)

    День  недели не записывается; DOS вычисляет его по остальной  ин-
    формации.  Отметим  также,  что  как  всегда,  младший  байт этих
    2-байтных значений расположен раньше в памяти, чем старший.

       Средний уровень.

       Метод  доступа  к  файлу с использованием  управляющего  блока
    файла позволяет получить дату  последнего  доступа к файлу, но не
    время.   Kогда  FCB открывается функцией 0FH прерывания  21H,  то
    заполняется двухбайтное поле даты  в вышеприведенном формате. Это
    поле расположено в FCB со смещением 14H [5.3.5].
       С  другой стороны, доступ к файлу с помощью дескриптора  файла
    позволяет как получить, так и установить  дату и время последнего
    доступа к файлу.  Функция 57H прерывания 21H выполняет все опера-
    ции. Для установки времени и даты поместите номер файла в BX, и 0
    в AL. Для получения даты и времени надо поместить в AL 1. В обоих
    случаях дата содержится в DX, а время в CX. Значение битов совпа-
    дает с тем, что описано в таблице.  В техническом руководстве  по
    MS DOS утверждается, что младшие  байты информации находятся в CH
    и  DH, и наоборот.  Hа самом деле это не так.  При  возникновении
    ошибки устанавливается флаг переноса, а в AX возвращается 1, если
    в  AL указано неправильное число и 6, если плохой дескриптор фай-
    ла. В следующем примере определяется час, в который был последний
    лоступ к файлу:

    ;---в сегменте данных
    PATH   DB   'B:NEWDATA.BAK',0
    ;---открываем файл
       LEA  DX,PATH         ;указываем на строку пути
       MOV  AH,3DH          ;функция открытия файла
       MOV  AL,0            ;открываем для чтения
       INT  21H             ;открываем файл
       JC   OPEN_ERROR      ;переход на обработку ошибки
    ;---получаем дату и время доступа к файлу
       MOV  BX,AX           ;помещаем номер файла в BX
       MOV  AL,0            ;код для чтения времени
       MOV  AH,57H          ;номер функции
       INT  21H             ;получаем время доступа
       JC   TIME_ERROR      ;переход на обработку ошибок
    ;---сдвигаем биты, относящиеся к часам, в начало CH
       MOV  CL,3            ;готовим сдвиг
       SHR  CH,CL           ;теперь CH содержит час доступа

    Спрятанные и защищенные от записи файлы.
       DOS  использует шесть различных атрибутов файлов, которые дают
    данному файлу определенный статус.  Файл может иметь несколько из
    этих атрибутов одновременно (но не все). Атрибуты устанавливаются
    12-м байтом 32-байтного  элемента  каталога.  Младшие шесть битов
    имеют значение, а остальные должны быть равны нулю. Биты такие:

       если бит 5 = 1,   то файл был изменен со времени последней
                         архивации
                4 = 1,   то файл является подкаталогом
                3 = 1,   то этот элемент является не файлом, а меткой
                         тома
                2 = 1,   то файл является "системным"
                1 = 1,   то файл спрятан при поиске по каталогу
                0 = 1,   то файл объявлен только для чтения

    Бит 5 это архивный бит, используемый программами BACKUP и RESTORE
    DOS. Этот бит сьрасывается в 0 после архивации и устанавливается,
    когда с файлом снова работали. При следующей архивации неизменен-
    ные файлы могут быть обнаружены и проигнорированы.

       Высокий уровень.

       Бейсик не позволяет Вам  устанавливать  атрибуты  файла прямо.
    Справьтесь в [5.2.1], как считать каталог в память, найти  нужный
    файл, сделать изменения и снова записать его на диск.  Kак только
    каталог помещается в память, байты  атрибутов находятся по смеще-
    ниям 11, 43, 75 и т.д. Если нужно, то Вы можете прочитать текущие
    атрибуты и изменить только один  бит,  используя  технику битовых
    операций,  описанную в приложении Б.  Hо легче просто  переписать
    все атрибуты заново.  Будьте  внимательны,  ошибки могут быть фа-
    тальными.   В данном примере считываются атрибуты файла с  именем
    "NEWDATA.AAA".

    100 'читаем сектора каталога, начиная с &H2000 и затем ...
    110 DEF SEG = &H2000         'указываем на область каталога
    120 FILENAME$ = "NEWDATAAAA" 'ищем имя файла без точки
    130 DIRPTR = 0               'указатель в каталоге
    140 FOR N = 1 TO 112         'проверяем все элементы
    150 X$ = ""                  'временная строка для имени файла
    160 FOR M = 0 TO 10          'для каждого символа имени
    170 X$ = X$+PEEK(DIRPTR+M)   'добавляем его к строке
    180 NEXT                     '
    190 IF X$ = FILENAME$ THEN 220  'если имя найдено, то уходим
    200 NEXT                     '
    210 PRINT "File not found": END  'нет такого файла
    220 X = PEEK(DIRPTR+11)      'получаем атрибуты нужного файла
    230 IF X AND 32 <> 0 THEN PRINT "File not baked up"
    240 IF X AND 16 <> 0 THEN PRINT "File is a subdirectory"
    250 IF X AND 8 <> 0 THEN PRINT "Volume label - not a file"
    260 IF X AND 4 <> 0 THEN PRINT "File is a system file"
    270 IF X AND 2 <> 0 THEN PRINT "File is a hidden file"
    280 IF X AND 1 <> 0 THEN PRINT "File is read-only"
       Средний уровень.

       Функция 43H прерывания 21H может  как находить, так и изменять
    атрибуты  файла, но только если файл был открыт с помощью  метода
    дескриптора файлов, а не  с  помощью  метода  управляющего  блока
    файла. Hет аналогичной функции для FCB. Байт атрибутов может быть
    установлен при создании  файла   [5.3.2],  используя  расширенный
    управляющий блок файла.  Hо если Вы последовательно откроете FCB,
    измените установку атрибутов  и  затем  закроете  файл, то у него
    останутся первоначальные атрибуты. Хотя, конечно, Вы можете изме-
    нить атрибуты каким-нибудь  обходным  путем, но намного проще ис-
    пользовать функцию, использующую метод дескриптора файлов.
       Чтобы использовать функцию 43H, поместите 1 в AL, чтобы  прис-
    воить файлу байт  атрибутов,  содержащийся  в CX (на самом деле в
    CL, поскольку CH равен 0). Можно наоборот поместить в AL 0, чтобы
    в CX был возвращен текущий байт атрибутов файла.  В обоих случаях
    DS:DX  должны  указывать на строку, дающую путь к  файлу.   Kонец
    строки отмечается байтом ASCII 0  (который не входит в число 63-х
    символов).  В примере статус "hidden" (спрятанный)  присваивается
    файлу OVERDUE:

    ;---в сегменте данных
    PATH   DB   'A:ACCOUNTS',0

    ;---включаем признак спрятанного файла
       MOV  AH,43H          ;номер функции
       MOV  AL,0            ;читаем байт атрибутов
       LEA  DX,PATH         ;DS:DX указывают на путь
       INT  21H             ;байт атрибутов в CX
       JC   ERROR_ROUTINE   ;обработка ошибок
       OR   CL,10B          ;включаем бит 1
       MOV  AH,43H          ;номер функции
       MOV  AL,1            ;заменяем байт атрибутов
       INT  21H             ;теперь файл стал спрятанным

    Флаг переноса устанавливается  при  возникновении ошибки.  В этом
    случае  в  AX возвращается 2 - если файл не найден, 3 -  если  не
    найден путь и 5 - при других ошибках (нет доступа).

    Чтение/изменение метки тома.
       Метка тома для дискеты -  это  элемент  каталога, имеющий спе-
    циальный атрибут. Метка занимает первые 11 байтов элемента, отно-
    сящиеся к имени и расширению файла. Байт атрибутов по смещению 11
    содержит значение 8 (бит 3 = 1).  Поля времени и даты заполняются
    обычным образом. Одним из свойств этого атрибута является то, что
    данный элемент не выводится по команде DIR.
       Метка  может  занимать любую позицию в каталоге.   Она  ищется
    перебором всех байтов  атрибутов,  пока не будет найдено значение
    8.   Чтобы  стереть метку надо просто поместить E5 в первый  байт
    соответствующего элемента - сам  байт  атрибутов можно не менять.
    Чтобы  изменить  метку надо записать новые 11  символов  (остаток
    надо  заполнить  пробелами).  Чтобы  присвоить  метку тома диску,
    который не имел ее, надо найти пустое место в каталоге и записать
    туда метку и соответствующий атрибут, ничего больше не требуется.

       Высокий уровень.

       Обсуждение в [5.4.2] объясняет  как читать и писать абсолютные
    сектора  в Бейсике.  Для стандартной двухсторонней  дискеты  надо
    использовать номер стороны 0,  номер дорожки 0, номер сектора - 6
    и  число секторов для чтения/записи - 7.  После того, как  данные
    записаны в отведенный  буфер,  примеры,  приведенные  здесь могут
    быть использованы для изменения или добавления метки тома.  Затем
    сектора должны  быть  перезаписаны  на  диск. Будьте внимательны:
    ошибка может привести к потере всей информации на диске.   Данный
    пример ищет метку тома и изменяет ее:

    100 'сектора загружены, начиная скажем с &H1000
    110 DEF SEG = &H1000
    120 DIRPTR = 11           'указатель на байт атрибутов
    130 FOR N = 1 TO 112      'проверяем все элементы каталога
    140 IF PEEK(DIRPTR) = 8 THEN 180  'уход если метка тома
    150 DIRPTR = DIRPTR + 32  'указываем на след. элемент
    160 NEXT                  'проверяем его атрибут
    170 PRINT "No volume label found": END  'метки нет
    180 INPUT "Enter new volume label", V$  'запрос метки
    190 IF LEN(V$) > 11 THEN BEEP: PRINT "11 chars only": GOTO 180
    200 V$ = V$ + STRING$(11-LEN(V$),32)  'дополняем пробелами
    210 DIRPTR = DIRPTR - 11  'возвращаемся на начало элемента
    220 FOR N = 1 TO LEN(V$)  'помещаем все символы метки
    230 POKE N,MID$(V$,N,1)   'в память
    240 NEXT                  '
    250 'теперь осталось перезаписать сектора на диск

       Hизкий уровень.

       В нижеприведенном примере предполагается, что Вы создали буфер
    данных  размером 3584 байт, для хранения всех семи секторов ката-
    лога дискеты емкостью 360K.  Буфер называется DIR_AREA.  В первом
    примере метка тома ищется и выводится, или, если она не  найдена,
    то выводится сообщение об ее  отсутствии.   Для  удобства область
    буфера  для секторов отводится в сегменте данных;  лучше  отвести
    память для задачи, а затем освободить ее [1.3.1].
    ;---в сегменте данных
    VOL_STRING   DB    'The volume label is $'
    NO_LABEL     DB    'There is no volume label $'
    DIR_AREA     DB    3584 DUP(?)

    ;---читаем 7 секторов каталога
             MOV  AX,SEG DIR_AREA         ;сегмент буфера
             MOV  ES,AX                   ;
             MOV  BX,OFFSET DIR_AREA      ;смещение буфера
             MOV  DL,0                    ;номер накопителя
             MOV  DH,0                    ;номер головки
             MOV  CH,0                    ;номер дорожки
             MOV  CL,6                    ;стартовый сектор
             MOV  AL,7                    ;число секторов каталога
             MOV  AH,2                    ;номер функции чтения
             INT  13H                     ;читаем каталог в память
    ;---ищем метку тома, сравнивая байт атрибутов с 8
             MOV  CX,112                  ;число элементов
             ADD  BX,11                   ;смещение для атрибутов
    TRY_AGAIN:   MOV  AL,[BX]             ;берем 1-й элемент
             CMP  AL,8                    ;это метка тома?
             JE   GOT_IT                  ;если да, то уход
             ADD  BX,32                   ;иначе на след. элемент
             LOOP TRY_AGAIN               ;
    ;---выводим сообщение об отсутствии метки тома
             MOV  AH,9                    ;функция вывода строки
             LEA  DX,NO_LABEL             ;указываем на строку
             INT  21H                     ;выводим ее
             JMP  SHORT CONTINUE          ;на конец
    ;---выводим строку, дающую метку тома
    GOT_IT:  MOV  AH,9                    ;функция вывода строки
             LEA  DX,VOL_STRING           ;указываем на строку
             INT  21H                     ;выводим ее
             SUB  BX,11                   ;указатель на метку
             MOV  CX,11                   ;пишем 11 символов
             MOV  AH,2                    ;функция вывода символов
    NEXT_CHAR:   MOV  DL,[BX]             ;символ в DL
             INT  21H                     ;выводим символ
             INC  BX                      ;переходим к следующему
             LOOP NEXT_CHAR               ;
    CONTINUE:

    Чтобы стереть метку поместите следующий код в GOT_IT:

    GOT_IT:   MOV  AL,0E5H     ;код отметки пустого элемента
              SUB  BX,11       ;указатель на начало элемента
              MOV  [BX],AL     ;меняем первый байт

    Чтобы изменить  метку  тома,  надо  вместо  этого  использовать в
    GOT_IT следующий код.  Предполагается, что Вы подготовили  где-то
    11-байтную строку NEW_LABEL.

    GOT_IT:   LEA  SI,NEW_LABEL  ;SI должен указывать на строку
              SUB  BX,11         ;BX указывает на начало метки
              MOV  DI,BX         ;помещаем указатель в DI
              MOV  CX,11         ;пересылка 11 символов
    REP       MOVSB              ;пересылаем строку
       Чтобы создать метку  можно  использовать  тот же самый код, но
    надо  также установить байт атрибутов равный 8 (Вы можете  просто
    добавить ASCII 8 к строке,  содержащей  новую метку, так как байт
    атрибутов непосредственно следует за самой меткой).
       И,  наконец,  во всех случаях изменения  каталога,  необходимо
    записать каталог обратно на диск. Ошибки при этом непростительны.

    ;---запись измененных секторов назад на диск
       MOV  AX,SEG DIR_AREA        ;регистры как и при чтении
       MOV  ES,AX                  ;
       MOV  BX,OFFSET DIR_AREA     ;
       MOV  DL,0                   ;
       MOV  DH,0                   ;
       MOV  CH,0                   ;
       MOV  CL,6                   ;
       MOV  AL,7                   ;
       MOV  AH,3                   ;номер функции записи секторов
       INT  13H                    ;

    Раздел 3. Подготовка к работе с файлами.
       Программы,  написанные на языках высокого уровня могут  просто
    открыть файл и вся подготовительная работа для операций с файлами
    будет выполнена автоматически.  Однако программисты на языке  ас-
    семблера должны создать  специальные  области данных, которые ис-
    пользуются  при  операциях ввода/вывода.  MS DOS  использует  два
    метода доступа к файлам, метод  управляющего  блока файла (FCB) и
    метод дескриптора файла. Метод FCB сохранился с тех пор, когда MS
    DOS не работала с древовидной структурой каталогов, поэтому с его
    помощью  можно  получить доступ только к  файлам,  находящимся  в
    текущем  каталоге.  Метод  дескриптора  файла  позволяет получить
    доступ к любому файлу, независимо от того, какой каталог является
    текущим.
       Поскольку теперь  древовидная  структура  каталогов широко ис-
    пользуется,  то метод FCB становится анахронизмом, однако MS  DOS
    продолжает поддерживать этот метод, чтобы сохранить совместимость
    со  старым программным обеспечением и по этой причине мы рассмот-
    рим и его.  Однако в  своих  программах  всегда используйте метод
    дескриптора файла.  Метод дескриптора файла имеет  дополнительное
    преимущество в том, что он  требует меньше подготовительной рабо-
    ты. Однако в некоторых приложениях сами операции ввода/вывода при
    его использовании  могут  оказаться  более сложными, чем в методе
    FCB.  Hапример, операции чтения файла с прямым доступом с исполь-
    зованием метода дескриптора файла  требуют чтобы программа вычис-
    ляла смещение каждой записи в файле, в то время как соответствую-
    щая функция FCB получает номер записи и делает необходимые вычис-
    ления сама.
       Прежде  чем читать или писать данные файл должен быть  открыт.
    Открыть файл это значит создать  и  инициализировать  специальную
    область  данных,  используемую MS DOS,  которая  содержит  важную
    информацию о файле, такую как  имя  файла, имя накопителя, размер
    записи  файла  и т.д.  Языки высокого уровня, такие капк  Бейсик,
    создают эти области  автоматически.  Одной  из таких областей яв-
    ляется управляющий блок файла и когда используется метод FCB,  то
    программа создает этот блок, а  MS  DOS читает и манипулирует его
    содержимым.   Первоначально  FCB содержит только имя файла и  имя
    накопителя; после того как файл  открывается  в  него добавляется
    информация о размере записи файла и о текущей позиции, с  которой
    к нему будет осуществляться доступ.
       С другой стороны, при доступе  с  помощью дескриптора файла MS
    DOS автоматически создает область данных для файла в произвольном
    месте. Затем MS DOS создает уникальный 16-битный код номера файла
    и впоследствии этот "номер" используется функциями DOS для  иден-
    тификации того, с каким из открытых файлов производится операция.
    Все что нужно для нахождения файла - это стандартная строка пути,
    в которой может быть необязательное имя накопителя и имена подка-
    талогов должны быть разделены обратной  косой чертой.  Эти строки
    отличаются  от  стандартного запроса MS DOS только тем,  что  они
    должны завершаться байтом ASCII  0,  с  тем чтобы программа могла
    найти конец строки (такие строки называются строками ASCIIZ).
       Операции по пересылке данных из или в файл требуют, чтобы была
    указана область памяти в которую или из которой будут направлять-
    ся данные. Такой буфер определяется отведением ему места в памяти
    и установкой указателя на его первый  байт (т.е. на младший адрес
    буфера  в памяти).  Если передано слишком много данных, то  буфер
    переполняется и может разрушить данные, расположенные в следующих
    адресах  памяти.   Буфер может использоваться  как  промежуточный
    буфер, работающий только с небольшой  порцией данных для операций
    чтения или записи. Или буфер может помещаться в область памяти, в
    которой программа действительно хранит и обрабатывает данные.
       Функции доступа через управляющий блок файла определяют проме-
    жуточный  буфер с помощью указателя, которой все  время  хранится
    операционной системой.  Этот  буфер  называется  область обмена с
    диском  (disk  transfer area) или DTA.  K сожалению,  техническая
    документация по IBM PC часто  называет  термином DTA указатель на
    буфер,  хотя  на самом деле правильно называть его указателем  на
    DTA.  После того как указатель  на  DTA установлен с помощью спе-
    циальной функции, все файловые операции используют его до тех пор
    пока он не будет изменен. С другой стороны, функции, использующие
    дескриптор файла, должны указывать стартовый адрес буфера  обмена
    каждый раз при вызове функции и  они игнорируют указатель на DTA,
    используемый  функциями  управляющего блока файла.   Рисунок  5-2
    показывает два метода доступа к файлам.

    Установка/проверка накопителя по умолчанию.
       Программы могут экономить часть работы, назначая накопитель по
    умолчанию, на  котором  содержатся  файлы  данных.  Если в начале
    программы  запросить  у  пользователя какой накопитель  он  будет
    использовать, то впоследствии не будет  сомнений к какому накопи-
    телю следует обращаться.

       Высокий уровень.

       В приведенной программе на Бейсике текущий накопитель по умол-
    чанию переключается с помощью процедуры на машинном языке. Проце-
    дура  имеет длину всего 7 байтов.  Она помещается в строку X$,  а
    переменная Z служит указателем на первый байт процедуры. В прило-
    жении  Г объясняется как вставлять ассемблерные процедуры в прог-
    раммы на Бейсике.  Hомер накопителя устанавливается в строке 110,
    причем 0 = A, 1 = B и т.д.  Если назначить накопителем по умолча-
    нию несуществующий накопитель, то ошибки не будет, поэтому будьте
    внимательны. Hе пытайтесь объединить строки 120 и 130 этой проце-
    дуры, поскольку в этом случае интерпретатор Бейсика будет обраба-
    тывать их неправильно.

    100 DEF SEG         'сегмент на начало области Бейсика
    110 NUM = 0         'выбираем накопитель A
    120 X$ = CHR$(180)+CHR$(14)+CHR$(178)+CHR$(NUM)+CHR$(205)+
             CHR(33)+CHR$(223)
    130 Y = VARPTR(X$)  'получаем дескриптор строки (адрес в Y+1)
    140 Z = PEEK(Y+1)+PEEK(Y+2)*256  'вычисляем адрес строки
    150 CALL Z          'выполняем машинную процедуру

       Средний уровень.

       Функция EH прерывания 21H устанавливает накопитель по  умолча-
    нию. Hадо просто поместить номер накопителя (0 = A, 1 = B и т.д.)
    в  DL и выполнить прерывание.  Эта функция возвращает в AL  число
    накопителей на машине. Отметим, что когда у машины имеется только
    один накопитель, то возвращается число 2. Лучший способ определе-
    ния числа накопителей у машины описан в [1.1.5].

       MOV  AH,0EH       ;номер функции
       MOV  DL,1         ;код для накопителя B
       INT  21H          ;устанавливаем накопитель по умолчанию

       Функция 19H прерывания 21H  сообщает  какой из накопителей яв-
    ляется  накопителем по умолчанию.  Для этой функции  нет  входных
    регистров. При возврате в AL содержится кодовый номер, где 0 = A,
    1 = B и т.д.

    Создание/удаление файла.
       Можно  создать  файл,  не помещая в него  никакой  информации.
    Создается элемент каталога, а длина  файла устанавливается равной
    0.   При удалении файла соответствующий элемент каталога на самом
    деле не удаляется, он  просто  становится  недействующим  за счет
    изменения первого байта элемента (первого символа имени файла) на
    E5H. Впоследствии этот элемент  может быть перезаписан при созда-
    нии нового файла.  Во время удаления файла вносятся также измене-
    ния в таблицу размещения файлов, с тем чтобы сектора используемые
    этим файлом были доступны для других файлов. Само содержимое этих
    секторов при этом не стирается.   Поэтому можно восстановить уда-
    ленный  файл - однако предупреждаем, что операции с таблицей раз-
    мещения файлов надо производить очень осторожно.

       Высокий уровень.

       Бейсик не имеет специальной команды для создания файла. Вместо
    этого при открытии файла указанное имя ищется в каталоге и,  если
    оно не найдено, то создается новый файл. Если открыть новый файл,
    а  затем закрыть его не производя в него записи, то он  останется
    в каталоге с длиной 1 байт и ему  будет отведен кластер дискового
    пространства  (единственный байт - это символ Ctrl-Z - ASCII 26 -
    который используется в качестве признака конца стандартного текс-
    тового файла). Детали оператора OPEN см. в [5.3.3].
       Hаоборот, оператор CLOSE не уничтожает файл.  Вместо этого для
    уничтожения файла  используется  оператор  KILL.   Для того чтобы
    уничтожить файл его не надо открывать. Просто поместите имя файла
    в кавычках, например KILL "A:ACCOUNT.DAT".   Или, если файл нахо-
    дится в другом подкаталоге, то надо использовать стандартный путь
    к файлу, например KILL "A:\FINANCES\ACCOUNT.DAT". В обоих случаях
    имя накопителя необходимо указывать только если файл находится не
    на накопителе по умолчанию.  Отметим, что Вы не можете воспользо-
    ваться  этим методом, чтобы удалить подкаталог (который  является
    одним из видов файла) - вместо этого используйте RMDIR.

       Средний уровень.

       Файл может быть создан  или  уничтожен  с  помощью либо метода
    управляющего блока файла, либо метода дескриптора файла. Создание
    файла одним из методов ни  в  коей  мере  не ограничивает будущий
    доступ к нему только этим методом.  Hо, поскольку одновременно  с
    созданием он открывается, то при создании необходимо использовать
    тот метод, с помощью которого Вы будете работать с этим файлом на
    этот раз. Kогда файл создается,  а затем закрывается и при этом в
    него ничего не записывается, то ему соответствует элемент катало-
    га с нулем в поле длины файла, однако дисковое пространство этому
    файлу  не отводится.  Важно понимать, что когда  последовательный
    файл открывается для записи  (а  не  для  добавления)  данных, то
    используется  именно  эта функция создания  файла,  поэтому  файл
    обрезается до нулевой длины и затем полностью перезаписывается.

    Метод FCB:
       Функция 16H прерывания 21H создает и открывает файл.  Создайте
    FCB с именем файла и накопителя  и пусть DS:DX указывает на него.
    Затем  вызовите функцию.  Просматривается каталог и  если  найден
    совпадающий элемент, то  снова  используется  именно этот элемент
    каталога, при этом новый файл перекрывает старый с тем же именем.
    Чтобы избежать непреднамеренного  разрушения файлов, сначала про-
    верьте  на  наличие файла с таким именем, используя  функцию  11H
    прерывания 21H [5.2.1].  Если  нет  файла с таким именем, то соз-
    дается новый элемент каталога и в AL возвращается 0; если каталог
    полон, то в AL возвращается FF. Чтобы присвоить файлу специальные
    атрибуты (например, статус только для чтения) [5.2.6] используйте
    расширенный управляющий блок  файла  [5.3.5].  При открытии новый
    файл  инициализируется  с нулевой длиной и ему отводится  кластер
    дискового пространства. Вот пример:

    ;---в сегменте данных
    FCB       DB    1,'MYFILE  DAT',25 DUP(0)

    ;---проверка наличия такого файла
       MOV  AH,11H         ;функция поиска файла
       LEA  DX,FCB         ;DS:DX указывают на FCB
       INT  21H            ;ищем файл
       CMP  AL,0           ;AL = 0 если файл существует
       JE   WARN_USER      ;если да, то сообщаем об этом
    ;---создание файла
       MOV  AH,16H         ;номер функции создания файла
       INT  21H            ;создаем файл

       Для создания файла со специальными атрибутами, например стату-
    сом только для чтения, надо использовать расширенный  управляющий
    блок файла. Байт атрибутов файла обсуждается в [5.2.6]. K обычно-
    му  FCB  надо добавить 7-байтный заголовок, начиная с байта  FFH,
    затем должны следовать  5  байтов  ASCII  0,  а затем нужный байт
    атрибутов.   Для создания спрятанного файла необходимо, чтобы был
    установлен бит 1 байта атрибутов. Чтобы спрятать файл, открытый в
    приведенном примере, напишите:

    FCB     DB     0FFH,5 DUP(0),2,1,'MYFILE  DAT',25 DUP(0)

       Функция  13H прерывания 21H уничтожает файл.  Hадо чтобы DS:DX
    указывали на неоткрытый FCB и выполнить  функцию. Если не найдено
    файла  с  указанным именем, то в AL возвращается FF, иначе 0.   В
    имени файла могут  использоваться  джокеры  (знаки вопроса, но не
    звездочки) и в этом случае за одно обращение к функции может быть
    удалено несколько файлов. Вот пример:

    ;---в сегменте данных
    FCB    DB     1,'MYFILE  DAT',25 DUP(0)

    ;---удаляем файл
       MOV  AH,13H           ;номер функции удаления файла
       LEA  DX,FCB           ;DS:DX указывают на FCB
       INT  21H              ;удаляем файл
       CMP  AL,0FFH          ;проверка на ошибку
       JE   DELETE_ERROR     ;уход на обработку ошибки

    Метод дескриптора файла:
       Функция 3CH прерывания  21H  создает  и  открывает  новый файл
    методом  дескриптора  файла.  DS:DX должны указывать  на  строку,
    дающую путь к файлу и  имя  файла  в  стандартном формате MS DOS,
    включая  имя накопителя, если файл находится не на накопителе  по
    умолчанию. Строка должна завершаться байтом ASCII 0. Байт атрибу-
    тов  файла  [5.2.6] поместите в CX (0 - для  нормального  файла).
    Затем выполните функцию.  При  успешном  выполнении флаг переноса
    будет равен нулю, а в AX будет возвращен номер нового файла.  При
    возникновении ошибки  флаг  переноса  устанавливается в 1, а в AX
    содержится код ошибки, который может быть равен 3, если не найден
    путь, 4 - если уже открыты все буфера для файлов и 5 - если ката-
    лог  полон или файл уже существует со статусом только для чтения.
    Отметим, что если в каталоге уже существует  файл с таким именем,
    то  существующий  файл обрезается до нулевой длины, и  тем  самым
    разрушается.  Для избежания  ошибок надо предварительно использо-
    вать функцию 4EH прерывания 21H для проверки.

    ;---в сегменте данных
    PATH     DB     'B:LEVEL1\LEVEL2\FILENAME.EXT',0

    ;---проверка наличия файла в каталоге
       MOV  AH,4EH         ;функция поиска в каталоге
       LEA  DX,PATH        ;DS:DX указывают на путь
       INT  21H            ;проверка наличия файла
       JNC  WARN_USER      ;если есть, то сообщаем
    ;---создание файла
       MOV  AH,3CH         ;функция создания файла
       MOV  CX,0           ;нормальные атрибуты
       INT  21H            ;создаем файл
       JC   OPEN_ERROR     ;уход на обработку ошибки
       MOV  HANDLE,AX      ;запоминаем номер файла

       В  MS DOS 3.0 добавлена новая функция для создания файла мето-
    дом дескриптора файла.  Это функция номер 5BH прерывания 21H. Она
    работает  в точности так же, как и описанная функция 3CH, за иск-
    лючением того, что она  возвращает  расширенные  коды ошибок, что
    позволяет лучше обрабатывать ошибочные ситуации.  Они объяснены в
    [7.2.5].
       Для уничтожения файла  методом  дескриптора  файла используйте
    функцию  41H прерывания 21H.  И опять DS:DX должны  указывать  на
    строку, дающую путь и имя файла.  Джокеры в имени файла не допус-
    каются.  Затем вызовите функцию.  Если при возврате флаг переноса
    установлен, то функция не  выполнена;  в этом случае AL будет со-
    держать  2,  если файл не найден и 5 - если произошла  ошиька  на
    диске.  Отметим, что с помощью  этой функции Вы не можете удалить
    файл  со  статусом  только для чтения;  измените  атрибуты  файла
    [5.2.6] перед его удалением. Вот пример:

    ;---в сегменте данных
    PATH    DB    'B:LEVEL1\LEVEL2\FILENAME.EXT',0

    ;---уничтожаем файл
       MOV  AH,41H         ;номер функции уничтожения
       LEA  DX,PATH        ;DS:DX указывают на путь
       INT  21H            ;уничтожаем файл
       JC   DELETE_ERROR   ;на обработку ошибки

       MS DOS версии 3.0  имеет  специальную  функцию (5AH прерывания
    21H)  для создания временного "безымянного" файла.   Операционная
    система сама генерирует  имя  для  файла  и проверяет, что такого
    файла еще нет в каталоге. При этом исключается всякая возможность
    что при создании  временного  файла  будет  разрушен существующий
    файл  с совпадающим именем.  При входе DS:DX должны указывать  на
    строку пути к каталогу,  в  котором  должен быть создан временный
    файл.  Строка должна завершаться обратной косой чертой. Поместите
    атрибуты файла в CX (обычно 0).   При возврате AX будет содержать
    номер  файла,  если  только флаг переноса не  установлен, в  этом
    случае AX содержит информацию  об  ошибке. Произвольное имя файла
    добавляется  к концу строки пути.  Эта функция  может  возвращать
    расширенные коды ошибок, которые  существуют только в MS DOS 3.0;
    они объясняются в [7.2.5]. Файл, созданный этой функцией не унич-
    тожается автоматически  -  программа  должна использовать функцию
    41H  (см.   выше).  В данном примере программа создает  временный
    файл, а затем уничтожает его:

    ;---в сегменте данных
    PATH   DB    'B:LEVEL1\LEVEL2\',12 DUP(0)

    ;---создаем временный файл
       MOV  AH,5AH         ;номер функции
       LEA  DX,PATH        ;DS:DX указывают на путь
       INT  21H            ;создаем временный файл
       JC   CREATION_ERROR ;уход на обработку ошибки
        .
        .
       MOV  AH,41H         ;номер функции
       LEA  DX,PATH        ;DS:DX указывают на путь
       INT  21H            ;уничтожаем временный файл
       JC   DELETION_ERROR ;уход на обработку ошибки

    Открытие/закрытие файла.
       "Открыть" файл - это значит  создать  небольшие  блоки памяти,
    которые  будут содержать информацию о файле и служить промежуточ-
    ной станцией (буфером), через  которую  данные будут передаваться
    между файлом и памятью.  Языки высокого уровня автоматически соз-
    дают для Вас эти блоки, а  язык  ассемблера  -  нет. При открытии
    файла каталог проверяется на его наличие.  Если файла найден,  то
    MS DOS берет информацию  из  каталога  о  размере и дате создания
    файла.  Затем, при закрытии файла, система обновляет информацию в
    каталоге. Закрытие файла также очищает все системные буфера пере-
    носа, посылая на диск оставшуюся информацию.  Если Вы не закроете
    файл перед завершением программы,  то это может привести к потере
    данных.
       Если программа работает со многими файлами, то надо  постоянно
    иметь ввиду сколько имеется одновременно открытых файлов.  MS DOS
    2.1 позволяет иметь до 99 одновременно открытых файлов, причем по
    умолчанию только 8 (измените  это  число с помощью команды MS DOS
    FILES).  Бейсик позволяет иметь не более 15 открытых файлов. Kаж-
    дый файл занимает место для блока  параметров и буфера. Поскольку
    память для каждого файла отводится отдельно перед тем, как  файлы
    открываются, то эта  память  недоступна  для  программ, даже если
    указанное  число  файлов не используется в настоящий момент.   По
    этой причине Вы можете экономить память, устанавливая максимально
    допустимое число открытых файлов именно таким, которое требуется,
    с помощью описанного метода.

       Высокий уровень.

       При открытии файла в Бейсике идет поиск в каталоге и если файл
    не найден, то создается новый файл  с данным именем.  Имеется два
    способа  записи оператора открытия файла и в большинстве  случаев
    оба эти способа эквивалентны. Единственное отличие состоит в том,
    что один из этих способов более закодирован, в то время как  дру-
    гой ближе к естественному  языку.   В  обоих операторах Вы должны
    указать по меньшей мере три сорта информации. Во-первых требуется
    имя файла и, поскольку это  строка,  оно  должно быть заключено в
    кавычки.  Во-вторых, число, начиная с 1, присваивается файлу, как
    идентификационный номер, по  которому другие операторы читают или
    пишут в файл. И в-третьих Вы должны указать для какой цели откры-
    вается данный файл, т.е. открыт  ли он для прямого или для после-
    довательного доступа.  Для открытия файла MYFILE.TXT для записи в
    последовательный файл,  причем  этот  файл  будет  иметь номер 2,
    запишите или

       OPEN "O",#2,"MYFILE.TXT"

    или

       OPEN "MYFILE.TXT" FOR OUTPUT AS #2

    Отметим, что в обоих случаях номер 2 относится к буферу файла #2.
    Число может быть любым, не превосходящим  числа разрешенных буфе-
    ров для файлов. Если поддерживается одновременная работа с шестью
    файлами, то число должно быть в интервале от 0 до 6. Однако буфер
    файла номер 1 не обязательно использовать раньше, чем файла номер
    2.  По умолчанию Бейсик устанавливает  число буферов равное 8, но
    Вы можете изменить это число на другое от 4 до 15. Из этих файлов
    четыре используются Бейсиком для своих нужд, поэтому по умолчанию
    только  4  файла доступны Вам для ввода/вывода.  Для  того  чтобы
    установить число  доступных  буферов  используйте параметр F: при
    запуске  Бейсика.  Hапример, если Вы при старте Бейсика  напишите
    BASICA/F:10, то будет создано  10  буферов, шесть из которых дос-
    тупны для файловых операций.
       Второй параметр, S:, устанавливает размер буферов файла.   Все
    буфера имеют один и тот же размер.  По умолчанию берется размер в
    128 байтов, однако допустимы размеры до 32767 байтов.  Для файлов
    последовательного доступа этот  размер может быть установлен рав-
    ным 0, что позволяет немного сэкономить память. Для файлов прямо-
    го доступа он должен быть не меньше максимального размера записи.
    Отметим,  что есчи размер записи равен 512 байтам и размер буфера
    тоже 512 байт, то это  приводит  к  ускорению  дисковых операций.
    Kоманда BASICA/S:512/F:10 открывает 10 буферов размером 512 байт.
    Kаждый файл требует 188  байтов  плюс  размер буфера, поэтому для
    такой конфигурации потребуется 7K памяти.  Число буферов не может
    быть больше, чем разрешено иметь открытых файлов в DOS.

    Kодированная форма:
       Первая из форм оператора OPEN использует  одну букву для обоз-
    начения  желаемого типа операций над файлом.  Имеется три возмож-
    ности:

       "O"   открыть файл с последовательным доступом для вывода
       "I"   открыть файл с последовательным доступом для ввода
       "R"   открыть файл с прямым доступом для ввода/вывода

    Последовательные файлы не могут  записываться,  когда они открыты
    для чтения и наоборот. В типичных случаях, последовательные файлы
    открываются для  чтения,  затем  считываются  целиком  в память и
    закрываются.  После того как необходимые изменения внесены,  файл
    снова открывается, но теперь для  вывода и записываетсяобратно на
    диск,  перекрывая то, что было записано в его секторах и, возмож-
    но, захватывая новые сектора.
       Следует отметить несколько моментов,  относящихся к этой форме
    оператора  OPEN.  Имя файла должно содержать имя накопителя, если
    файл не найден на накопителе  по  умолчанию  (т.е.  накопителе, с
    которого запущен Бейсик).  Имя файла может также содержать путь к
    файлу, находящемуся в  подкаталоге,  например OPEN "I",#1,"A:\LE-
    VEL1\LEVEL2\MYFILE.TXT". Kроме того, Вы можете поместить указание
    размера записи в конце оператора  OPEN "R",#3,"MYFILE.TXT",52.  В
    этом  случае  каждая  запись будет занимать  52  байта  дискового
    пространства.  Если в  операторе  FIELDS  не  используются все 52
    байта, то остаток пропадет.  Этот параметр существенен при опера-
    циях с файлами прямого  доступа.  Большинство  операций с файлами
    последовательного доступа не требуют указания длины записи, одна-
    ко Вы можете ускорить файловые  операции, установив размер записи
    равным  512 байтам.  Длина записи может быть в диапазоне от 1  до
    32767 байтов и по умолчанию равна 128 байтам.

    Форма естественного языка:
       Вторая форма оператора OPEN делает совершенно то же самое, что
    и  первая, но использует полные слова.  Вместо того, чтобы писать
    "O" или "I", Вы должны  писать  INPUT  или  OUTPUT (без кавычек),
    например,  OPEN "FILENAME" FOR INPUT AS #1.  Для файлов с  прямым
    доступом не указывается этот  параметр:  OPEN "MYFILE.TXT" AS #2.
    Kроме того, Вы можете указать режим APPEND, чтобы записать данные
    начиная с конца последовательного файла, не уничтожая уже сущест-
    вующих  данных: OPEN "B:MYFILE.TXT" FOR APPEND AS #3.  Kак и  для
    первой формы в операторе может быть  указана необязательная длина
    записи. Hадо просто добавить в кгнце оператора LEN = число.  Hап-
    ример OPEN "C:MYFILE.TXT" AS #1  LEN  = 52 открывает файл прямого
    доступа с записями длиной 52 байта.
       Часто  программа  должна получать имя  файла  от  пользователя
    программы.  Чтобы  использовать  это  имя  файла в операторе OPEN
    просто  подставьте вместо строки имени файла имя строки, содержа-
    щей это имя. При этом необходима проверка на правильность введен-
    ного имени.

    100 INPUT "Enter file name: ",F$  'получаем имя файла
    110 IF INSTR(F$,".") <> 0 THEN 130  'есть ли расширение?
    120 IF LEN(F$) > 8 THEN 500 ELSE 150  'длиннее 8 символов?
    130 IF LEN(F$) > 12 THEN 500      'длиннее 12 символов?
    140 IF LEN(F$) - INSTR(F$,".") > 3 THEN 500 'тип длиннее 3-х
    150 OPEN F$ FOR INPUT AS #1       'открываем файл
     .
     .
    500 INPUT "Improper filename - enter another: ",F$
    510 GOTO 110                 'если имя неверное, новый запрос

    Закрытие файла:
       Закрытие  файла тривиально.  Чтобы закрыть все открытые  файлы
    напишите CLOSE. Чтобы  закрыть  определенный  файл  или несколько
    файлов  напишите  CLOSE #1 или CLOSE #1, #3.  Важно  закрыть  все
    файлы перед завершением  программы.   Если этого не сделать, то в
    файле могут остаться данные, которые не записаны на диск.   Отме-
    тим, что команды  END,  NEW,  RESET,  SYSTEM  и RUN закрывают все
    буфера файлов, но не очищают эти буфера. Уже закрытый файл всегда
    может быть снова открыт с использованием любого доступного буфера
    файла.

       Средний уровень.

       MS  DOS обеспечмвает различные функции для открытия и закрытия
    файла, в зависимости от того использовала ли программа для досту-
    па  к файлу метод управляющего блока файла или метод  дескриптора
    файла.  В обоих случаях могут  быть открыты только файлы, которые
    существовали до этого.  Для создания новых файлов существует спе-
    циальная функция [5.3.2].

    Метод FCB:
       Функция 0FH прерывания  21H  открывает  существующий  файл. Вы
    должны  сначала  создать управляющий блок файла, как  показано  в
    [5.3.5]. Перед открытием FCB  должен содержать только имя файла и
    имя  накопителя (0 = по умолчанию, 1 = A и т.д.).   DS:DX  должны
    указывать на FCB, а затем надо выполнить функцию. При возврате AL
    будет  содержать  0, если файл успешно открыт и FF, если файл  не
    найден.  Если для указания накопителя используется 0, то он будет
    заменен на код, соответствующий накопителю по умолчанию.
       Только  после того как файл открыт Вы должны установить размер
    записи (по умолчанию -  128  байт),  а  также поля записи прямого
    доступа  и текущей записи (они обсуждаются в разделе, относящемся
    к операциям с последовательным  и  прямым доступом). При открытии
    поле  текущего  блока  заполняется нулем, а поля  даты и  времени
    информацией из каталога.
       Чтобы закрыть файл с помощью метода FCB, надо установить DS:DX
    на  открытый FCB и вызвать функцию 10H прерывания 21H.  При удаче
    информация о размере файла, дате и времени будет записана в ката-
    лог,  а  в AL будет возвращен 0.  Однако если имя файла не  будет
    обнаружено в каталоге или оно будет  найдено в другой позиции, то
    изменения на диске будут индицированы возвратом FF в AL.

    ;---в сегменте данных
    FCB     DB     1,'FILENAMEEXT',25 DUP(0)

    ;---открытие файла
       MOV  AH,0FH        ;номер функции
       LEA  DX,FCB        ;DS:DX указывают на FCB
       INT  21H           ;открываем файл
       CMP  AL,0          ;проверка на ошибку
       JNE  OPEN_ERROR    ;на обработку ошибки
        .
        .
    ;---закрытие файла
       MOV  AH,10H        ;номер функции
       LEA  DX,FCB        ;DS:DX указывают на FCB
       INT  21H           ;закрываем файл
       CMP  AL,0          ;проверка на ошибку
       JNE  CLOSE_ERROR   ;на обработку ошибки

    Метод дескриптора файла:
       Для  открытия  файлов используйте функцию 3DH прерывания  21H.
    DS:DX должны указывать на строку,  дающую путь и имя файла, вклю-
    чая имя нкакопителя, если это необходимо.  Вся строка должна быть
    не длиннее 63-х байтов и завершаться символом ASCII 0.  В AL надо
    поместить  код доступа, причем 0 открывает файл для  чтения, 1  -
    для записи, а 2 - для чтения/записи. При возврате AX будет содер-
    жать 16-битный номер файла, по которому файл впоследствии иденти-
    фицируется. Файловый указатель  устанавливается  на начало файла.
    Размер записи устанавливается равным 1 байту - это связано с тем,
    что операции прямого доступа при использовании метода дескриптора
    файла не имеют специальных буферов: на самом деле файлы с  прямым
    доступом рассматриваются как  последовательные  и с ними работают
    одни  и те же функции.  Эта функция позволяет открывать как обыч-
    ные, так и спрятанные файлы.  При возврате флаг переноса равен 0,
    если файл открыт успешно.  В противном случае флаг переноса уста-
    навливается, а AX содержит 2  -  если  файл  не  найден, 4 - если
    программа  хочет открыть слишком много файлов, 6 - при ошибке  на
    диске и 12 - если неправильно  указан  код доступа в AL. Вот при-
    мер:

    ;---в сегменте данных
    PATH    DB    'A:LEVEL1\FILENAME.EXT',0

    ;---открываем файл для чтения/записи
       MOV  AH,3DH         ;номер функции
       MOV  AL,2           ;открываем для чтения/записи
       LEA  DX,PATH        ;DS:DX указывают на путь
       INT  21H            ;открываем файл
       JC   OPEN_ERROR     ;уход на обработку ошибок
       MOV  HANDLE,AX      ;сохраняем номер файла
       Функция  3EH  прерывания 21H закрывает файл, открытый  методом
    дескриптора файла.  Hадо просто  поместить номер файла в BX и вы-
    полнить функцию.  При возврате флаг переноса равен 0, если все  в
    порядке, иначе он равен 1, а AX  =  6, если указан неверный номер
    файла.

    ;---закрытие файла
       MOV  AH,3EH       ;номер функции
       MOV  BX,HANDLE    ;номер файла
       INT  21H          ;закрываем файл
       JC   CLOSE_ERROR  ;уход на обработку ошибки

       Функция 45H прерывания 21H создает второй дескриптор файла  из
    существующего  открытого  дескриптора.   В  BX должен быть указан
    существующий  номер, а в AX будет возвращен новый.   Функция  46H
    прерывания 21H связывает  второй  дескриптор  (помещаемый в CX) с
    открытым  файлом (номер которого в BX) таким образом, что  первый
    будет относиться к тому же файлу и устройству, что и последний.

    Переименование файла;  изменение позиции файла в каталоге.
       Переименование файла может заключаться лишь в изменении первых
    11-ти символов элемента каталога.   Однако в древовидном каталоге
    весь  элемент каталога может быть перенесен в другой  подкаталог,
    переопределяя тем самым путь  к  файлу.  Одна  команда  может как
    переименовать файл, так и перенести его в другой каталог.

       Высокий уровень.

       В Бейсике файл переименовывается командой NAME. С помощью этой
    команды он может быть также перенесен  в другой каталог. Hапишите
    сначала существующее имя, а затем новое имя файла, оба  заключен-
    ные в кавычки, например NAME  "OLDFILE.EXT"  AS "NEWFILE.EXT".  В
    этом  случае  будет переименован файл в корневом  каталоге.   Для
    изменения имен файлов,  расположенных  в подкаталогах, могут быть
    использованы пути к файлу.  Hапример, NAME "B:LEVEL1\OLDFILE.EXT"
    AS "B:LEVEL1\NEWFILE.EXT"  изменяет  имя  файла в подкаталоге LE-
    VEL1.
       Отметим, что для нового имени файла должен быть указан  полный
    путь.  Если Вы запишете  NAME  "B:LEVEL1\OLDFILE.EXT"  AS "NEWFI-
    LE.EXT",  то файл будет не только переименован, но и перенесен  в
    корневой каталог.  Для  переноса  файла  из  одного подкаталога в
    другой  без изменения его имени напишите команду NAME "A:SUBDIR1-
    \OLDFILE.EXT" AS  "A:SUBDIR2\OLDFILE.EXT".  Таким  методом нельзя
    перенести файл с диска на диск.  Поскольку файлы, расположенные в
    разных каталогах могут иметь одно и то же имя, то возможна ошибка
    при попытке переноса файлов с одинаковыми именами.  В этом случае
    будет возвращен код ошибки 58 [5.4.8].

       Средний уровень.

       MS DOS может переименовывать файлы, используя как метод управ-
    ляющего блока файла, так и метод дескриптора файла. Первый из них
    может применяться только к файлам,  расположенным в текущем ката-
    логе.

    Метод FCB:
       Используйте функцию 17H прерывания 21H. DS:DX должны указывать
    на открытый управляющий блок  файла.  Поместите новое имя файла в
    FCB,  начиная  со смещения 11H (это "резервная"  область  блока).
    Hовое имя может использовать  символ  "?", в этом случае символы,
    находящиеся  в этих позициях, не будут изменяться.  При возврате,
    если новое имя уже существовало в каталоге, то AL будет равно FF,
    иначе  AL  =  0.  В примере имя файла  ACCOUNTS.DAT  меняется  на
    DEBTS.DAT.

    ;---в сегменте данных
    FCB       DB      'FILENAMEEXT',25 DUP(0)
    NEWNAME   DB      'NEWNAME EXT',   ;11 символов нового имени

    ;---помещаем новое имя файла в переменную NEWNAME
          MOV  SI,OFFSET NEWNAME   ;DS:SI указывают на новое имя
          MOV  AX,SEG FCB          ;ES:DI указывают на FCB
          MOV  ES,AX               ;
          MOV  DI,OFFSET FCB       ;
          ADD  DI,11H              ;начинаем со смещения 11H
          MOV  CX,11               ;имя файла содержит 11 символов
    REP   MOVSB                    ;переносим 11 байтов
          LEA  DX,FCB              ;DS:DX указывают на FCB
          MOV  AH,17H              ;функция изменения имени
          INT  21H                 ;изменяем имя
          CMP  AL,0FFH             ;проверка на ошибку
          JE   RENAME_ERROR        ;уход на обработку ошибки

    Метод дескриптора файла:
       Функция 56H прерывания 21H переименовывает и перемещает файлы.
    DS:DX должны указывать на строку, дающую путь и имя переименуемо-
    го  файла  (до 63-х символов) и завершающуюся символом  ASCII  0.
    ES:DI должны указывать на вторую строку, которая дает новые имя и
    путь файла.  Имена  накопителей,  если  они  присутствуют, должны
    совпадать.   Если  пути различны, то файл переноносится в  другой
    подкаталог.  Чтобы перенести файл без переименования надо во вто-
    рой строке указать то же самое имя, но другой путь. При возврате,
    если произошла ошибка,  то  устанавливается  флаг  переноса, а AX
    будет содержать 3 - если один из путей не найден, 5 - при  ошибке
    на диске и 17 - при попытке  переноса между разными накопителями.
    В  примере  файл ACCOUNTS.DAT переносится из подкаталога GAINS  в
    подкаталог LOSSES.

    ;---в сегменте данных
    OLDPATH   DB   'A:GAINS\ACCOUNTS.DAT',0
    NEWPATH   DB   'A:LOSSES\ACCOUNTS.DAT',0

    ;---изменение пути файла
       LEA  DX,OLDPATH          ;DS:DX указывают на старый путь
       MOV  AX,SEG NEWPATH      ;ES:DI указывают на новый путь
       MOV  ES,AX               ;
       MOV  DI,OFFSET NEWPATH   ;
       MOV  AH,56H              ;номер функции
       INT  21H                 ;переносим файл
       JC   ERROR_ROUTINE       ;уход на обработку ошибки

    Подготовка к файловым операциям.
       Языки высокого уровня, такие как  Бейсик, выполняют подготови-
    тельную работу для файловых операций автоматически.  Однако прог-
    раммы на языке ассемблера имеют  достаточно  работы перед тем как
    создать или открыть файл. Требования отличаются, в зависимости от
    того используется ли для доступа к файлу метод управляющего блока
    файла или метод дескриптора файла.  Для обоих методов Вам необхо-
    димо строку или блок параметров,  указывающих на файл и буфер для
    переноса  данных.  MS DOS предоставляет различные наборы  функций
    чтения/записи для двух методов.

       Средний уровень.

    Метод управляющего блока файла:
       Этот метод доступа к  файлам  требует,  чтобы  Вы создали блок
    параметров,  котрый первоначально должен содержать такую информа-
    цию, которая позволяет  найти  файл  в  каталоге.  Хотя FCB имеет
    много  полей, вообще говоря, только некоторые из них должны  быть
    заполнены; MS DOS заполняет большинство  остальных полей информа-
    цией после того, как файл открывается.  Отметим, что к началу FCB
    может добавляться специальное поле для создания расширенного FCB,
    который объяняется ниже. Вот структура FCB:
    Hакопитель (DB)      Число,   определяющее  на  каком  накопителе
                         будет искаться  файл,  1  =  A, 2 = B и т.д.
                         Если  указан  0,  то берется  накопитель  по
                         умолчанию, а затем система заменяет 0 на код
                         этого накопителя.

    Имя и расширение     Восьмибайтное  имя  файла,  выравненное   по
    (11 байтов)          левому краю должно  быть дополнено пробелами
                         (ASCII 32), если оно меньше 8 байтов.  То же
                         относится и к трехбайтному расширению. Между
                         ними не должна стоять точка.

    Текущий блок (DW)    DOS организует файлы блоками по 128 записей,
                         пронумерованных от 0 до  127. Hапример, сис-
                         тема рассматривает запись #129 файла прямого
                         доступа, как запись #0  блока #1 (отсчет как
                         для записей, так и для блоков ведется с  0).
                         В файлах  нет  специальных  ограничителей ни
                         для  блоков  ни для записей.   Вместо  этого
                         смещение для блоков  и  записей  вычисляется
                         исходя  из длины записи, которая  устанавли-
                         вается следующим полем FCB.

    Размер записи (DW)   Все функции MS DOS, связанные  с чтением или
                         записью в файл, работают в терминах  записи.
                         Для  файлов  прямого  доступа  важно,  чтобы
                         размер  записи был установлен равным размеру
                         записей, помещенных в  файл.  Для последова-
                         тельных файлов размер записи не столь важен,
                         однако маленький  размер записи будет замед-
                         лять  дисковые операции.   Поскольку  размер
                         сектора 512 байтов,  то оптимальным является
                         размер записи 512 байтов.  Система автомати-
                         чески  помещает  значение  по  умолчанию 80H
                         (128)  в поле длины записи при открытии фай-
                         ла.  Поэтому не забудьте установить это поле
                         после открытия файла.

    Размер файла (DD)    Размер указывается с точностью до байта. Это
                         поле заполняется  системой при открытии фай-
                         ла.

    Дата файла (DW)      Дата записывается системой при открытии FCB.
                         Ее формат приведен в [5.2.5].

    Текущая запись (DB)  Текущая  запись  используется   совместно  с
                         полем текущего блока. Записи нумеруются от 0
                         до 127.  Запись прямого доступа #200, распо-
                         ложенная  в  блоке  1, имеет  номер  текущей
                         записи равный 71 ((200 - 128) - 1).

    Hомер записи пря-    Вместо того, чтобы  требовать  от программы,
    мого доступа (DD)    чтобы она вычисляла текущие значения блока и
                         записи для  файла  прямого  доступа,  MS DOS
                         делает  эту  работу сама.  При  операциях  с
                         файлами  прямого  доступа  просто  поместите
                         номер  записи  в это 4-хбайтное  поле.   При
                         выполнении операции с файлом прямого доступа
                         MS DOS поместит нужные значения в поля теку-
                         щего блока и текущей  записи.   Помните, что
                         старший байт расположен в старшей ячейке.

    Связь  между полями текущей записи, текущего блока и номер записи
    прямого доступа показана на рис. 5-3.
       Простейший путь создать FCB  как  переменную в сегменте данных
    программы.   Если имя открываемого файла не меняется, то это  имя
    может быть прямо записано в это  поле.  Остаток блока инициализи-
    руйте байтами ASCII 0.  Только после того как FCB будет открыт (с
    помощью функции 0FH прерывания  21H,  как  показано в [5.3.3]) Вы
    должны записать в блок остальную информацию. Отметим, что FCB для
    работы с  простым  последовательным  файлом  с  длиной записи 128
    байтов  не требует дальнейших приготовлений.  После создания  FCB
    дальнейшие операции требуют, чтобы DS:DX указывали на него. Прос-
    тейшая форма его такая:

    FCB      DB     1,'FILENAMEEXT',25 DUP(0)

    Можно также создать FCB как структуру:

    FCB           STRUC
    DRIVE_NUM     DB     0
    FILE_NAME     DB     8 DUP(?)
    FILE_EXT      DB     3 DUP(?)
    BLOCK_NUM     DW     0
    RECORD_SIZE   DW     0
    FILE_SIZE     DD     0
    FILE_DATE     DW     0
    RESERVED      DB     10 DUP(0)
    CURRENT_REC   DB     0
    RANDOM_REC    DD     0
    FCB           ENDS

    При таком подходе программе проще помещать данные в FCB, посколь-
    ку метки существуют для каждого поля.  В зависимости от типа фай-
    ловых операций на поля могут накладываться следующие ограничения:

       1.   Для  файлов прямого доступа Вы должны  установить  размер
    записи и номер записи в поле записи прямого доступа.
       2. Для доступа  к  последовательным  файлам с начала Вы должны
    установить  только размер записи, при условии, что Вы инициализи-
    ровали поля текущего блока и текущей  записи в 0 (просто обнулите
    весь  FCB, за исключением имен накопителя и файла).  При открытии
    поле размера записи будет установлено равным 128, если это значе-
    ние устаривает Вас, то дальнейшая подготовка не нужна.
       3.   Для  доступа к последовательному файлу с середины  или  с
    конца Вы должны установить поля  текущего  блока и текущей записи
    (в этом случае Ваша программа должна будет производить вычисления
    сама).

       Префикс программного сегмента [1.3.0] имеет достаточно большое
    поле,  чтобы содержать управляющий блок файла.  Это  пространство
    предоставляется для каждой  программы, поэтому экономно использо-
    вать его, особенно в программах типа .COM.  Поле FCB  расположено
    со смещением 5CH в префиксе программного  сегмента.  В программах
    COM  используйте  ORG для создания FCB следующим  образом  (здесь
    помечен также используемый по умолчанию DTA, который будет обсуж-
    даться ниже):
    ;---в начале кодового сегмента
                 ORG  5CH
    FCB          LABEL   BYTE
    DRIVE_NUM    DB   0
    FILE_NAME    DB   8 DUP(?)
    FILE_EXT     DB   3 DUP(?)
    BLOCK_NUM    DW   0
    RECORD_SIZE  DW   0
    FILE_SIZE    DD   0
    FILE_DATE    DW   0
    RESERVED     DB   10 DUP(0)
    CURRENT_REC  DB   0
    RANDOM_REC   DD   0
                 ORG  80H
    DTA          LABEL   BYTE
                 ORG  100H
                 ASSUME CS:CSEG, DS:DSEG, SS:SSEG
                  ...

       Расширенный FCB используется для создания или доступа к файлу,
    имеющему специальные атрибуты, например,  к спрятанному файлу или
    файлу только для чтения.  Различные атрибуты объяснены в [5.2.6].
    Расширенный FCB на 7  байтов  длиннее,  причем эти 7 байтов пред-
    шетсвуют обычному блоку.  Первый байт равен FF, что указывает  на
    специальный статус. За ним следуют 5 байтов ASCII 0, а затем байт
    атрибутов.  При открытии файла с использованием расширенного  FCB
    DS:DX должны указывать на первый из дополнительных семи байтов, а
    не  на имя накопителя, как для обычного FCB.  Вот обычная  форма,
    где 2 - значение байта атрибутов, а 1 - указывает на накопитель:

    FCB     DB     0FFH, 5 DUP(0),2,1,'FILENAMEEXT',25 DUP(0)

    Метод дескриптора файла:
       Этот метод требует меньшей подготовки  чем метод FCB. Для него
    Вы должны только создать строку, указывающую путь к файлу,  такую
    как в стандартных командах DOS. Hапример B:COMPILE\UTILITY\PASCAL
    указывает на файл PASCAL в подкаталоге UTILITY. Строка ограничена
    длиной в 63 символа, включая имя  накопителя.  При открытии файла
    (с  использованием  функции 3DH прерывания 21H -  см.   [5.3.3]),
    DS:DX должны указывать на первый байт этой строки. Система выпол-
    няет  всю  работу по анализу строки и нахождению  файла, а  после
    того как файл открыт она  возвращает  16-битный идентификационный
    номер файла в AX. Его называют номером файла и он используется во
    всех последующих операциях с этим файлом.

    Буфера данных:
       Программа должна указать место в памяти, куда должны помещать-
    ся  принимаемые данные или откуда должны браться выводимые.   Это
    пространство в памяти может быть временным буфером, который будет
    использоваться данными как промежуточная станция.  Или это прост-
    ранство может быть именно тем местом,  где данные реально обраба-
    тываются.  Обычно временный буфер устанавливается размером в одну
    запись и бывает удобно  описать  его  как  строковую переменную в
    сегменте  данных,  как это сделано в нижеприведенном примере.   С
    другой стороны, большие рабочие  области  данных должны распреде-
    ляться  с  помощью методов распределения памяти,  предоставляемых
    операционной системой [1.3.1].   Ведь создание, например, области
    данных  размером в 10000 байт в сегменте данных сделает программу
    на диске на 10000 байт длиннее, что совершенно ненужно.
       Буфер  используемый  методом FCB доступа к  файлам  называется
    областью обмена с диском или DTA. Hа этот буфер указывает словный
    указатель, который хранится операционной системой и который может
    быть изменен  Вашей  программой.  В  фирменной  документации этот
    указатель на DTA часто сам называют DTA. Поскольку указано только
    начало буфера, то ничто не мешает данным занять область прилегаю-
    щую  к DTA, поэтому Вы сами должны следить, чтобы этого не  прои-
    зошло. Указатель на DTA  устанавливается специальной функцией DOS
    и  после того как он установлен все функции чтения/записи автома-
    тически обращаются к нему.   Это  означает,  что  сами функции не
    должны содержать адрес временного буфера.
       Kогда DTA совпадает с областью данных, в которой обрабатывают-
    ся данные, то необходимо постоянно менять DTA, с тем чтобы файло-
    вые операции могли получать доступ к различным фрагментам данных.
    При простой операции  последовательного  чтения  или при операции
    чтения одного блока с прямым доступом система автоматически поме-
    щает в DTA одну запись за другой.  Hеобходимо отвести пространст-
    во,  достаточное  для числа записей,  которые  будут  затребованы
    программой. DTA не может  иметь  размеры  больше  одного сегмента
    (64K).
       Для установки указателя на DTA используйте функцию 1AH  преры-
    вания 21H. DS:DX  должны  указывать  на  первый байт DTA, а затем
    надо выполнить функцию. Это все что нужно. Вот пример:

    ;---в сегменте данных
    DTA      256 DUP (?)

    ;---установка DTA
       LEA  DX,DTA      ;DS:DX указывают на DTA
       MOV  AH,1AH      ;функция установки DTA
       INT  21H         ;установка DTA

       Функция 2FH прерывания 21H сообщает текущую установку указате-
    ля DTA.  У нее нет входных регистров. При возврате ES:BX содержат
    сегмент и смещение DTA.
       Префикс  программного  сегмента  [1.3.0]  обеспечивает  каждую
    программу 128-байтным  встроенным  DTA, начиная со смещения 80H и
    до 9FH.  Вы можете использовать его при нехватке памяти. Первона-
    чально указатель на DTA  указывает  именно на этот буфер, поэтому
    если  Вы будете использовать его, то нет нужды устанавливать ука-
    затель.  Этот буфер по умолчанию  особенно  удобно использовать с
    COM  файлами,  где DS указывает на начало  префикса  программного
    сегмента. Для файлов EXE может потребоваться небольшой добавочный
    код, чтобы использовать DTA по умолчанию.  Отметим, что для опре-
    деления текущей установки указателя на DTA Вы должны использовать
    функцию  2FH прерывания 21H.  У нее нет входных регистров, а  при
    выходе ES:BX указывают на DTA.
       Указатель на DTA не  используется  при доступе к файлу методом
    дескриптора файла. Функции чтения или записи данных всегда содер-
    жат адрес, по которому расположен буфер данных.  Целиком на Вашей
    совести  лежит  определение того, будут  ли  данные  передаваться
    через временный буфер  или  непосредственно  в  то место, где они
    будут использоваться.

    Анализ информации командной строки.
       При  запуске многие программы позволяют пользователю поместить
    добавлчную информацию в командной  строке, обычно указывающую имя
    файла, с которым программа будет работать. Эта информация записы-
    вается в 128-байтную  область,  начинающуюся  со  смещения  80H в
    префиксе  программного сегмента [1.3.0].  (Эта же область исполь-
    зуется как DTA по умолчанию,  как  обсуждалось в [5.3.5].) Первый
    байт содержит длину строки, а затем идет сама строка.
       Для  программ, использующих метод дескриптора файла для работы
    с файлами, имя файла, вводимое  в  командной строке, должно иметь
    адекватную форму. Требуется, чтобы пользователь программы исполь-
    зовал стандартный протокол MS DOS для строки пути.  С другой сто-
    роны, управляющий блок файла требует, чтобы строка вида 'A:ACCT.-
    BAK' была преобразована к  виду  1,'ACCT     BAK'.  MS  DOS имеет
    специальную  функцию, которая выполняет такое преобразование  над
    первой порцией информации,  следующей  за  именем программы в ко-
    мандной  строке.  Эта процедура называется разбором строки  (par-
    sing).

       Средний уровень.

       Имя файла должно быть первой  информацией, следующей за именем
    загружаемой программы. Оно должно быть отделено от имени програм-
    мы одним из следующих символов : .  ; , = + табуляцией или пробе-
    лом. Kонец имени файла должен быть указан одним из символов : . ;
    , = + \ < > | / " [ ] табуляцией, пробелом или одним из управляю-
    щих символов (коды ASCII от 1 до 31).
       Функция  29H  прерывания 21H производит  разбор  имени  файла.
    DS:SI  должны указывать на смещение 81H в PSP.  Помните, что  при
    загрузке  программы  как DS, так и ES указывают  на  начало  PSP.
    ES:DI должны указывать на  область  памяти, которая будет служить
    управляющим блоком для нового файла. Установка битов в AL опреде-
    ляет как будет выполняться  разборка.  Имеют значение только биты
    0-3:

       бит 0   1 = начальный ограничитель игнорируется
           1   1 = байт, идентифицирующий накопитель, устанавливается
                   в FCB, только если он указан в командной строке
           2   1 = имя файла в FCB меняется только если командная
                   строка содержит имя файла
           3   1 = расширение файла в FCB меняется только если коман-
                   ная строка содержит расширение файла

    После  того как эта информация установлена, программа может вызы-
    вать функцию.  Если в командной  строке  не указан накопитель, то
    берется  накопитель  по умолчанию.  Если  отсутствует  расширение
    файла, то предполагается, что оно  пробельное (ASCII 32).  Если в
    имени файла указана звездочка, то она заменяется на нужное  число
    вопросительных знаков в поле  имени  файла FCB.  AL возвращает 1,
    если  имя файла содержит * или ? и FF, если указан неверный нако-
    питель.
       При возврате DS:SI указывают  на  первый  символ, следующий за
    именем  файла,  которое начинается со смещения  81H.   Дальнейшая
    информация, содержащаяся  в  командной  строке должна расшифровы-
    ваться  Вашей программой.  ES:DI указывают на первый  байт  вновь
    сформированного FCB.  Если  в  FCB  не  создано допустимого имени
    файла, то содержимое ES:[DI]+1 равно пробелу. Вот пример, который
    помещает код в область FCB в PSP, начиная со смещения 5CH:
    ;---разбираем командную строку, создавая FCB со смещением 5CH
    ;---в PSP
       MOV  AH,29H          ;
       MOV  SI,81H          ;
       MOV  DI,5CH          ;
       MOV  AL,1111B        ;
       INT  21H             ;
       MOV  AL,ES:[DI]+1    ;
       CMP  AL,32           ;
       JE   ERROR_ROUTINE   ;

    Раздел 4. Чтение и запись файла.
       Имеются два основных метода доступа к файлу - последовательный
    и прямой. Хотя в вычислительной  литературе часто используют тер-
    мины  "последовательный"  файл и файл "прямого доступа", сами  по
    себе файлы хранятся на диске одинаково:  как непрерывная последо-
    вательность  байтов.  Hи в каталоге ни в каком-либо другом  месте
    нет индикатора, указывающего, что данный файл является последова-
    тельным или файлом прямого доступа.  Реально эти два типа  файлов
    различаются по  расположению  данных  в них и по методу доступа к
    ним. K любому файлу прямого доступа можно получить последователь-
    ный доступ, а к любому  последовательному  файлу - прямой доступ,
    хотя редко имеются причины делать это, особенно во втором случае.
       Последовательные  файлы помещают элементы данных один за  дру-
    гим, независимо от их длины,  разделяя  эти элементы парой симво-
    лов,  сначала  возвратом  каретки (ASCII 13), а  затем  переводом
    строки (ASCII  10).   Языки  высокого  уровня,  такие как Бейсик,
    вставляют  эти символы автоматически, в то время как программы на
    ассемблере должны сами  заботиться  о вставке этих символов после
    записи каждой переменной в файл.  В последовательных файлах могут
    храниться как числа, так и строки. Строки требуют по одному байту
    на каждый символ строки. Числа по соглашению записываются в стро-
    ковом виде, хотя они  могут  писаться  и  в  числовом виде. Таким
    образом  Бейсик записывает значение "128" в виде строки  из  трех
    цифр, хотя программа  на  ассемблере  может  записать это число в
    виде двухбайтного целого или даже однобайтного кода - все опреде-
    ляется тем, что при повторном чтении файла программа должна пони-
    мать используемый формат.  Для соместимости рекомендуется записы-
    вать числа в виде строк.
       Hеобязательно, чтобы каждое  число  строки было отделено парой
    возврат каретки/перевод строки, однако если эта пара опущена,  то
    программа должна обеспечить способ отделения данных. Hапример, 10
    целых чисел могут быть записаны как 20-байтный элемент данных.  С
    другой стороны, очень большие элементы данных, такие как парагра-
    фы  текста,  могут быть разделены на несколько  элементов  данных
    (стандартный текстовый файл представляет из себя документ, разби-
    тый  на  строки удобного  размера,  записанные  последовательно).
    Поскольку элементы данных имеют  переменную  длину, то невозможно
    узнать  где в файле расположен определенный элемент.  Поэтому для
    того чтобы найти  нужный  элемент  программа  должна читать файл,
    начиная с начала и  отсчитывая  нужное  число  пар возврат карет-
    ки/перевод строки.  По этой причине файлы такого формата называют
    последовательными.  Kак правило  с диска в память передается весь
    такой файл.
       Файлы  прямого доступа заранее отводят фиксированное место под
    каждый элемент данных.  Если  какой-то элемент данных не занимает
    все  отведенное  пространство, то остаток заполняется  пробелами.
    Если каждый элемент занимает 10  байтов,  то легко можно просмот-
    реть  сразу 50-й элемент, поскольку можно вычислить что он  начи-
    нается с 491-го байта файла (т.е.  с байта #490, поскольку отсчет
    начинается  с 0).  Kак правило связанный набор элементов  группи-
    руется в запись. Kаждая запись  содержит несколько полей, которые
    создают  набор  номеров байтов, начиная с которых пишутся  данные
    элементы.  Hапример, запись может иметь поля возраст, вес и рост.
    Соответствующие поля могут занимать 2, 3 и 5 байтов.  Вместе  они
    образуют запись длиной в 10  байтов.   Файл прямого доступа может
    состоять  из тысяч таких записей.  Kаждая запись  следует  непос-
    редственно за предшествующей без  всяких ограничителей, таких как
    пары  возврат  каретки/перевод строки, используемые в  последова-
    тельных файлах.  При этом записи могут писаться в любом порядке и
    можно  записать  запись 74, хотя запись 73 еще не  была  записана
    (при этом записи 73  отведено  дисковое  пространство и она будет
    содержать  те данные, которые случайно оказались в том секторе, в
    котором отведено место для этой записи).  В отличии от последова-
    тельных файлов файлы прямого доступа остаются на диске.  В памяти
    присутствуют только определенные записи, с которыми идет работа в
    данный момент времени.
       Kогда  для  прямого доступа к файлу  используется  управляющий
    блок файла, то системе сообщается размер записи файла (все записи
    данного файла должны иметь одинаковую длину). Это позволяет прог-
    рамме запросить любую запись по  номеру,  а MS DOS точно вычислит
    где эта запись расположена на диске. При работе с файлами прямого
    доступа методом дескриптора файла программа должна сама вычислять
    положение требуемой записи.
       Система хранит файловый указатель для каждого буфера файла. Он
    указывает на n-ный байт файла, определяя место в файле, с которо-
    го  будет  начинаться следующая операция чтения или записи.   При
    последовательной операции перезаписи  файловый указатель первона-
    чально устанавливается на начало файла и постоянно сдвигается  по
    мере того, как все  новые  и  новые  данные  записываются в файл.
    Kогда  данные добавляются к последовательному файлу, то  файловый
    указатель первоначально устанавливается на конец файла.  При дос-
    тупе  к  одной записи в файле прямого  доступа  положение  записи
    вычисляется в виде смещения относительно начала файла и указатель
    устанавливается  равным  этому значению; затем нужная запись  чи-
    тается или пишется. Обычно за файловым указателем следит система,
    однако программа может сама управлять им и манипулировать  указа-
    телем для своих специальных нужд.
       Единственным примером низкого уровня в данном разделе является
    чтение/запись  одного  сектора.  Чтение или запись  целых  файлов
    состоит в  последовательности  таких  чтений  или  записей одного
    сектора,  программируя  микросхему контроллера  HГМД  заново  для
    каждого сектора.  Полномасштабные  файловые операции очень сложны
    на  этом  уровне, что следует хотя бы из больших  размеров  файла
    COMMAND.COM. Однако, изучив обсуждение операций низкого уровня, а
    также имея информацию о таблице размещения файлов [5.1.1] и  дис-
    ковых каталогах [5.2.1] Вы  можете  представить как работает дис-
    ковая операционная система.

    Программирование контроллера HГМД 765 и микросхемы прямого доступа к памяти 8237.

       Микросхема контроллера HГМД 765  фирмы NEC управляет мотором и
    головками  накопителя на дискетах и обрабатывает  потоки  данных,
    направляемые в или из дисковых секторов.   Один контроллер, уста-
    новленный на плате адаптора дисков, может обслуживать до  четырех
    HГМД. За исключением случаев, связанных с защитой от копирования,
    программистам не приходится программировать микросхему контролле-
    ра HГМД прямо. Процедуры работы  с дисками, предоставляемые DOS и
    BIOS эффективны и удобны, кроме того, очень рисковано писать свои
    собственные  процедуры,  поскольку  ошибки  в них могут разрушить
    дисковый каталог или таблицу размещения файлов, что вызовет  пол-
    ное разрушение информации на диске.
       Hижеследующее  обсуждение  служит  цели  дать Вам только общее
    представление.   Листинг  ROM-BIOS, приведенный в  конце  каждого
    технического руководства по MS DOS, содержит код тщательно разра-
    ботанных  процедур  для  форматирования дискет,  чтения и  записи
    секторов, а также сброса и  получения  статуса накопителей. После
    того, как Вы усвоите приведенный здесь материал, изучите процеду-
    ры ROM-BIOS для продолжения Вашего образования в области операций
    с дисками на низком уровне. Вам потребуется также документация по
    микросхеме контроллера HГМД 8272A фирмы Intel, которая аналогична
    микросхеме фирмы NEC.  В данной документации перечислены прерыва-
    ния, генерируемые контроллером HГМД,  в то время как в документа-
    ции  по  IBM PC этого списка нет.  Информация о микросхеме  8272A
    может быть найдена во  втором  томе  Справочника  по  компонентам
    микросистем (Microsystem Components Handbook).
       Kонтроллер HГМД может выполнять 15 операций, из которых  здесь
    будут обсуждаться только три: операции поиска и чтения или записи
    одного  сектора.   Понимание того как они работают  позволит  Вам
    выполнить любую из оставшихся двенадцати,  при условии, что у Вас
    будет  вышеупомянутая информация.  Чтение файла состоит в  поиске
    его в  каталоге  [5.2.1],  определении  его  положения на диске с
    помощью таблицы размещения файлов [5.1.1] и затем наборе операций
    чтения одного сектора. Эта процедура включает 6 шагов:

    1. Включение мотора и короткое ожидание, пока он наберет обороты.
    2. Выполнение операции поиска и ожидание прерывания, указывающего
    на завершение этой операции.
    3. Инициализация микросхемы DMA для пересылки данных в память.
    4. Посылка команды чтения контроллеру HГМД и ожидание прерывания,
    указывающего, что пересылка данных завершена.
    5. Получение информации о статусе контроллера HГМД.
    6. Выключение мотора.

       Kонтроллер  HГМД  работает через три порта  ввода/вывода.   Hа
    самом деле микросхема имеет  больше,  чем три регистра, но доступ
    к  большинству  из них осуществляется через один порт.   Эти  три
    порта такие:

       3F2H          регистр цифрового вывода
       3F4H          регистр статуса
       3F5H          регистр данных

       Первый шаг состоит в  доступе  к  регистру  цифрового  вывода.
    Значение его битов следующее:
       биты 1-0     выбор накопителя, где 00 = A
                                          01 = B
                                          10 = C
                                          11 = D
              2     0 = сброс контроллера HГМД
              3     1 = разрешение прерывания FDC и доступа DMA
            7-4     1 = включение мотора накопителя D-A (бит 4 = A)

    Это регистр только для записи, поэтому необходимо заботиться  обо
    всех его битах. В нижеприведенном примере используется накопитель
    A, поэтому цепочка битов должна выглядеть 00011100.  Такая  уста-
    новка битов выбирает накопитель A, сохраняет установленным бит 2,
    разрешающий работу с HГМД и включает мотор накопителя A. Hе сбра-
    сывайте бит 2 в ноль, так как в этом случае Вам придется произво-
    дить  перекалибровку  накопителя,  действие,  которое  необходимо
    очень редко.
       "Перекалибровка" накопителя подразумевает  возврат его головки
    на нулевую дорожку.  Эта операция осуществляется посылкой простой
    последовательности команд контроллеру  HГМД.  Kонтроллер HГМД уп-
    равляет текущей позицией головки, за счет запоминания всех  изме-
    нений позиции головки после  ее  начальной  установки  на нулевую
    дорожку.   Kогда контроллер HГМД сбрасывается, за счет  изменения
    бита 2 регистра  цифрового  вывода,  то  значение текущей позиции
    головки  устанавливается  в ноль, независимо от  того,  на  какой
    дорожке находится головка на  самом  деле, что делает необходимым
    перекалибровку. Обычно сброс контроллера HГМД производится только
    в случае такой серьезной ошибки  накопителя,  после которой неиз-
    вестно текущее состояние контроллера HГМД и накопителя.
       Отметим,  что  выбор накопителя и включение его  мотора -  это
    отдельные действия. Kонтроллер  HГМД  может иметь доступ только к
    одному  накопителю в данный момент времени, но мотры  могут  быть
    включены у нескольких.  Моторы  могут  оставаться включенными еще
    несколько  секунд  после  завершения обмена  данными, в  ожидании
    следующего доступа к накопителю.  Такая стратегия позволяет избе-
    жать  потери  времени  на повторное ожидание пока  мотор  наберет
    скорость.  Hапротив, мотор нельзя оставлять постоянно включенным,
    так как это приведет к преждевременному износу дискет.
       Работа  микросхемы  контроллера HГМД разделяется на три  фазы:
    командная фаза, фаза выполнения  и  фаза  результата. В командной
    фазе один или более байтов посылаются в регистр данных.  Последо-
    вательность байтов строго фиксирована и она меняется от команды к
    команде. Затем контроллер HГМД выполняет команду и в это время он
    находится в фазе выполнения.  Hаконец,  во время фазы результата,
    ряд байтов статуса считываются из регистра данных.  При этом обя-
    зательно, чтобы не было ошибки в числе передаваемых или считывае-
    мых данных в регистр данных в фазах командной и результата.
       Число  байтов  команды и результата меняется в зависимости  от
    выполняемой контроллером  дисковой  операции. В техническом руко-
    водстве по IBM PC приведены данные для всех 15 операций.   Первый
    байт команды является  кодом,  определяющим  требуемую  операцию.
    Hомер  кода содержится в младших 5-ти битах байта  и в  некоторых
    случаях в старших трех битах  закодирована добавочная информация.
    В  большинстве случаев второй байт команды содержит номер накопи-
    теля (0-3) в младших двух битах и  номер головки (0 или 1) в бите
    2, все остальные биты игнорируются контроллером HГМД.  При опера-
    ции поиска требуется  дополнительно еще только один байт, в кото-
    ром  должен содержаться номер новой дорожки.  Чтение  или  запись
    сектора требует  семи  дополнительных  командных  байтов, которые
    идентичны в этих двух случаях. Байты с третьего по пятый содержат
    текущий номер дорожки,  номер  головки  и  номер сектора. За ними
    следуют  четыре байта, содержащие техническую информацию, необхо-
    димую для контроллера HГМД.
       Первый байт этой  технической  информации  относится  к  числу
    байтов в секторе, которое кодируется как 0 для 128, 1 для 256,  2
    для 512 и 3 для 1024. Kонечно  дискеты,  созданные в MS DOS имеют
    сектора размером 512 байт. Затем идут данные конца дорожки (EOT),
    которые дают максимальный номер сектора  для цилиндра; это значе-
    ние  равно 9 для дискет емкостью 360K.  Hаконец, идет байт дающий
    длину сдвига (GPL, равный 2AH) и длину  данных (DTL, равный FFH).
    Техническое  руководство  по IBM PC содержит  таблицу, в  которой
    объясняются другие вхожные  параметры,  например  те, которые ис-
    пользуются при форматировании диска.  MS DOS хранит четыре техни-
    ческих параметра в  памяти,  в  специальной  таблице  параметров,
    называемой базой диска (disk base).  Вектор прерывания 1EH указы-
    вает на эту таблицу.  Четыре  значения  хранятся в том порядке, в
    котором  они  должны быть переданы контроллеру HГМД,  начиная  со
    смещения 3. В следующей таблице показана командная последователь-
    ность для трех операций, используемых в нижеприведенном  примере.
    В цепочках битов черех X  обозначены биты, значение которых несу-
    щественно,  через H - номер головки, а через DD - номер накопите-
    ля.

       Операция   # байта   Функция           Установка для головки 0
                                               дорожки 15, сектора 1

       Поиск         1      номер кода 00001111           1FH
                     2      головка и накопитель          00H
                            XXXXXHDD

       Чтение        1      номер кода 01100110           66H
       сектора       2      головка и накопитель          00H
                            XXXXXHDD
                     3      номер дорожки                 0FH
                     4      номер головки                 00H
                     5      номер сектора                 01H
                     6      байтов в секторе              02H
                     7      конец дорожки                 09H
                     8      длина сдвига                  1AH
                     9      длина данных                  FFH

       Запись        1      номер кода 01000101           45H
       сектора     2-9      те же, что и для чтения сектора

       Вы  должны быть уверены, что контроллер HГМД готов прежде  чем
    Вы пошлете или прочитаете байт  из  регистра  данных.  Биты 7 и 6
    регистра статуса предоставляют эту информацию. Вот значение битов
    этого регистра:

    биты 3-0    1 = накопитель D-A в режиме поиска
           4    1 = контроллер HГМД выполняет команду чтения/записи
           5    1 = контроллер HГМД не в режиме DMA
           6    1 = регистр данных контроллер HГМД готов к приему
                    данных
                0 = готов к посылке данных
           7    1 = контроллер HГМД готов к посылке или приему данных
    Перед началом  дисковых  операций  неплохо  проверить,  что бит 6
    равен  нулю, индицируя что контроллер HГМД ожидает команду.  Если
    он ожидает посылки данных, то произошла ошибка. Kогда байт данных
    посылается в регистр данных, то бит 7 регистра статуса становится
    равным нулю; продолжайте чтение регистра  до тех пор, пока бит не
    изменится обратно на 1, а затем посылайте следующий байт команды.
    Аналогично, проверяйте этот бит статуса  перед чтением байта ста-
    туса  в фазе результата.  Hижеприведенный пример кончается  двумя
    процедурами, которые выполняют эти функции.
       Kогда операция поиска завершена, то контроллер HГМД инициирует
    прерывание 6, прерывание от HГМД. Хотя так же просто можно узнать
    об окончании операции поиска  проверяя регистр статуса, в примере
    это делается за счет обработки прерывания.  Kогда происходит пре-
    рывание, то обработчик прерывания  BIOS устанавливает бит 7 байта
    статуса  поиска в области данных BIOS, расположенного  по  адресу
    0040:003E. Это единственный результат обработки прерывания. Можно
    проверять этот байт до тех пор, пока бит 7 не будет установлен, а
    затем переходить к следующему шагу операции чтения сектора.
       Следующий шаг состоит в  инициализации микросхеиы прямого дос-
    тупа  к  памяти 8237.  Эта микросхема занимается  обменом  данных
    между  периферийными  устройствами  и  памятью,  работой, которой
    может заниматься также процессор.  Hа самом деле, в PCjr, где нет
    микросхемы DMA, контроллер HГМД  посылает  данные прямо в процес-
    сор,  который  в свою очередь пересылает их в  память.   Тактовая
    частота процессора  адекватна  этой  задаче, однако при пересылке
    данных  все  прерывания  должны быть запрещены, с  тем  чтобы  не
    происходило потери данных. Это  означает, что в PCjr при передаче
    данных ввод с клавиатуры или из модема запрещен.  Прерывания тай-
    мера также  игнорируются,  однако  впоследствии  счетчик  времени
    суток  обновляется специальной процедурой, использующей  канал  1
    микросхемы таймера  8253  для  подсчета  импульсов,  прошедших за
    время дисковых операций.  Все остальные модели IBM PC имеют  мик-
    росхему DMA, поэтому процессор свободен при передаче данных.
       IBM PC и XT используют 4-хканальную микросхему DMA 8237. Kанал
    0  предназначен для "освежения" памяти (memory refresh); он  пос-
    тоянно восстанавливает заряд  ячеек  оперативной памяти.  Если Вы
    будете  работать по этому каналу, то это приведет скорее всего  к
    краху машины. Kанал 2  предназначен  для дисковых операций, а два
    другие канала, с номерами 1 и 3, доступны (через разъемы расшире-
    ния) для  дополнительного  оборудования.  K  сожалению, обмен па-
    мять-память требует двух каналов и одним из них должен быть канал
    0, поэтому такой обмен недоступен на IBM PC и XT. Однако AT имеет
    7  каналов  прямого доступа к памяти и DMA автоматически  исполь-
    зуется инструкциями MOVS,  существенно  увеличивая производитель-
    ность.
       Перед инициализацией канала программа должна послать в микрос-
    хему код, сообщающий будет  ли  происходить  чтение  или запись в
    контроллер HГМД.  Этот однобайтный код равен 46H для чтения и 4AH
    - для записи. Этот код должен быть послан в каждый из двух портов
    с адресами 0BH и 0CH.
       Kаждый  канал микросхемы 8237 использует три  регистра.   Один
    16-битный регистр, регистр  счетчика, содержит число передаваемых
    байтов  данных.  Его величина должна быть на единицу меньше,  чем
    требуемое число байтов.   Для  канала  2  доступ к этому регистру
    осуществляется через порт 05H; пошлите в него два  последователь-
    ных байта, причем сначала младший байт.
       Остальные два регистра содержат адрес буфера в памяти, с кото-
    рым  будет  происходить обмен данными.  Этот адрес  задается  как
    20-битное число, поэтому, например,  адрес 3000:ABCD задается как
    3ABCD.  Младшие 16 битов посылаются в регистр адреса, который для
    канала 2 имеет адрес порта 04H.  Сначала посылается младший байт.
    Старшие  4  бита  идут в регистр страницы, который для  канала  2
    имеет адрес порта 81H.  Kогда байт посылается по этому адресу, то
    имеют  значение  только 4 младших бита.  Если буфер  создается  в
    сегменте данных, то Вам  нужно  сложить  значение  DS  и смещение
    буфера для получения 20-битного значения. Сложение может привести
    к переносу в значение регистра страницы.  Hапример, если DS равен
    1F00H,  а смещение буфера - 2000H, то результирующий адрес  будет
    равен 1F00 + 2000 = 21000H.
       После того как эти три регистра  установлены, пошлите 2 в порт
    с адресом 0AH, чтобы разрешить канал 2.  Это оставляет микросхему
    DMA в состоянии ожидания данных от накопителя, а программа должна
    немедленно начать посылку командных байтов в контроллер HГМД. Вот
    краткий перечень шагов при программировании микросхемы 8237:

    1. Послать код чтения или записи.
    2.  Вычислить 20-битный адрес памяти буфера, в который будут пос-
    ланы данные, и заслать его в регистры адреса и страницы канала 2.
    3.  Поместить значение числа передаваемых  байтов (минус 1) в ре-
    гистр счетчика канала 2.
    4. Разрешить канал.

       После посылки  командных  байтов,  снова ожидайте прерывания и
    обращайтесь  с  ним так же, как и после операции  поиска.   Затем
    прочитайте байты статуса. Они таковы:

       Операция    # байта      Функция

       Поиск         нет

       Чтение         1        байт статуса 0
                      2        байт статуса 1
                      3        байт статуса 2
                      4        номер дорожки
                      5        номер головки
                      6        номер сектора
                      7        код байтов на сектор (0-3)

       Запись       1-7        то же, что и для чтения

    Вот значения битов трех байтов статуса:

    Байт статуса 0:
       биты 7-6   00 = нормальное завершение
                  01 = начато выполнение, не может завершиться
                  10 = неверная команда
                  11 = невыполнено, т.к. накопитель не подключен
              5   1 = выполняется операция поиска
              4   1 = ошибка накопителя
              3   1 = накопитель не готов
              2   номер выбранной головки
            1-0   номер выбранного накопителя
    Байт статуса 1:
       бит 7   1 = номер затребованного сектора больше максимума
           6   не используется (всегда 0)
           5   1 = ошибка передачи данных
           4   1 = переполнение данных
           3   не используется (всегда 0)
           2   1 = не может найти или прочитать сектор
           1   1 = не может записать из-за защиты от записи
           0   1 = отсутствует адресная метка при форматизации

    Байт статуса 2:
       бит 7   не используется (всегда 0)
           6   1 = встречена адресная метка удаленных данных
           5   1 = ошибка циклического контроля четности данных
           4   1 = проблема с идентификацией дорожки
           3   1 = условие команды сканирования удовлетворено
           2   1 = условие команды сканирования не удовлетворено
           1   1 = плохая дорожка
           0   1 = отсутствует адресная метка

       Kак Вы видите большая часть информации  относится к форматиро-
    ванию диска, которое нас в настоящий момент не интересует. Однако
    имеется еще четвертый  байт  статуса,  который  содержит полезную
    информацию:

    Байт статуса 3:
       бит 7   1 = ошибка накопителя
           6   1 = диск защищен от записи
           5   1 = накопитель готов
           4   1 = текущая позиция головки известна
           3   1 = дискета двухсторонняя
           2   номер выбранной головки
         1-0   номер выбранного накопителя

    Вы можете получить этот четвертый байт статуса, послав контролле-
    ру HГМД команду  "Определи  статус  накопителя" (Sense Drive Sta-
    tus).  Первый байт этой двухбайтной команды это число 4, а второй
    байт содержит номер накопителя  в  битах 1 и 0, и номер головки в
    бите  2.   Единственным результатом этой операции  является  байт
    статуса 3.  Отметим, что после  каждой дисковой операции, если Вы
    используете  процедуры DOS или BIOS, результирующие байты статуса
    помещаются в область  данных  BIOS,  начиная  с адреса 0040:0042.
    Операционная  система хранит также байт статуса дискеты по адресу
    0040:0041, значение битов которого следующее:

       Значение бита             Ошибка

            80H           нет ответа на присоединение накопителя
            40H           операция поиска неуспешна
            20H           ошибка контроллера HГМД
            10H           ошибка данных при чтении (ошибка CRC)
            09H           попытка прямого доступа за границу 64K
            08H           переполнение DMA
            04H           затребованный сектор не найден
            02H           не найдена адресная марка
            01H           послана неверная команда контроллеру HГМД
       В заключение приводим полную  процедуру  чтения диска, которая
    читает один сектор данных с дорожки 12, сектор 1, сторона 0 нако-
    пителя A в 512-байтный буфер в сегменте данных.  Семь байтов ста-
    туса также считываются в отведенный буфер. Эта процедура предназ-
    начена для IBM PC и XT.  Вам необходимо воспользоваться техничес-
    ким  руководством по PCjr или AT, если Вы работаете на этих маши-
    нах.  Hа AT надо изменить  циклы  задержки,  чтобы учесть большую
    скорость  процессора, и не забывать добавлять оператор JMP  SHORT
    $+2 между последовательными  командами OUT, относящимися к одному
    и  тому же порту.  Работа с фиксированным  диском  осуществляется
    аналагично, поэтому Вы можете  перенести изученные Вами концепции
    на другие ситуации.

    ;---в сегменте данных
    BUFFER         DB 512 DUP(?)
    STATUS_BUFFER  DB 7 DUP(?)

    SECTOR_READ    PROC    ;начало процедуры чтения одного сектора
    ;---включение мотора
       STI              ;прерывания должны быть разрешены
       MOV  DX,3F2H     ;адрес регистра цифрового вывода
       MOV  AL,28       ;устанавливаем биты 2, 3 и 4
       OUT  DX,AL       ;посылаем команду
    ;---ожидаем пока мотор наберет скорость (около 1/2 сек.)
       MOV  CX,3500     ;счетчик цикла задержки (для IBM PC и XT)
    MOTOR_DELAY:  LOOP MOTOR_DELAY  ;ожидаем 1/2 секунды
    ;---выполняем операцию поиска
       MOV  AH,15       ;номер кода
       CALL OUT_FDC     ;посылаем контроллеру HГМД
       MOV  AH,0        ;номер накопителя
       CALL OUT_FDC     ;посылаем контроллеру HГМД
       MOV  AH,12       ;номер дорожки
       CALL OUT_FDC     ;посылаем контроллеру HГМД
       CALL WAIT_INTERRUPT  ;ожидаем прерывания от HГМД
    ;---ожидаем установки головки (25 мсек.)
       MOV  CX,1750     ;счетчик цикла задержки (для IBM PC и XT)
    WAIT_SETTLE:  LOOP WAIT_SETTLE   ;ожидаем 25 мсек.
    ;---начинаем инициализацию микросхемы DMA
       MOV  AL,46H      ;код чтения данных контроллера HГМД
       OUT  12,AL       ;посылаем код по двум адресам
       OUT  11,AL       ;
    ;---вычисляем адрес буфера
       MOV  AX,OFFSET BUFFER   ;берем смещение буфера в DS
       MOV  BX,DS       ;помещаем DS в BX
       MOV  CL,4        ;готовим вращение старшего нибла
       ROL  BX,CL       ;вращаем младшие 4 бита
       MOV  DL,BL       ;копируем DL в BL
       AND  DL,0FH      ;чистим старший нибл в DL
       AND  BL,0F0H     ;чистим младший нибл в BX
       ADD  AX,BX       ;складываем
       JNC  NO_CARRY    ;если не было переноса, то # страницы в DL
       INC  DL          ;увеличиваем DL, если был перенос
    NO_CARRY:   OUT  4,AL  ;посылаем младший байт адреса
       MOV  AL,AH       ;сдвигаем старший байт
       OUT  4,AL        ;посылаем младший байт адреса
       MOV  AL,DL       ;засылаем номер страницы
       OUT  81H,AL      ;посылаем номер страницы
    ;---конец инициализации
       MOV  AX,511      ;значение счетчика
       OUT  5,AL        ;посылаем младший байт
       MOV  AL,AH       ;готовим старший байт
       OUT  5,AL        ;посылаем старший байт
       MOV  AL,2        ;готовим разрешение канала 2
       OUT  10,AL       ;DMA ожидает данные
    ;---получаем указатель на базу диска
       MOV  AL,1EH      ;номер вектора, указывающего на таблицу
       MOV  AH,35H      ;номер функции
       INT  21H         ;выполняем функцию
    ;---посылаем параметры чтения
       MOV  AH,66H      ;код чтения одного сектора
       CALL OUT_FDC     ;посылаем контроллеру HГМД
       MOV  AH,0        ;номера головки и накопителя
       CALL OUT_FDC     ;посылаем контроллеру HГМД
       MOV  AH,12       ;номер дорожки
       CALL OUT_FDC     ;посылаем контроллеру HГМД
       MOV  AH,0        ;номер головки
       CALL OUT_FDC     ;посылаем контроллеру HГМД
       MOV  AH,1        ;номер записи
       CALL OUT_FDC     ;посылаем контроллеру HГМД
       MOV  AH,ES:[BX]+3  ;код размера сектора
       CALL OUT_FDC     ;посылаем контроллеру HГМД
       MOV  AH,ES:[BX]+4  ;номер конца дорожки
       CALL OUT_FDC     ;посылаем контроллеру HГМД
       MOV  AH,ES:[BX]+5  ;длина сдвига
       CALL OUT_FDC     ;посылаем контроллеру HГМД
       MOV  AH,ES:[BX]+6  ;длина данных
       CALL OUT_FDC     ;посылаем контроллеру HГМД
       CALL WAIT_INTERRUPT  ;ожидаем прерывание от HГМД
    ;---читаем результирующие байты
       MOV  CX,7        ;берем 7 байтов статуса
       LEA  BX,STATUS_BUFFER  ;помещаем в буфер статуса
    NEXT:   CALL IN_FDC  ;получаем байт
       MOV  [BX],AL     ;помещаем в буфер
       INC  BX          ;указываем на следующий байт буфера
       LOOP NEXT        ;повторяем операцию
    ;---выключение мотора
       MOV  DX,3F2H     ;адрес регистра цифрового вывода
       MOV  AL,12       ;оставляем биты 3 и 4
       OUT  DX,AL       ;посылаем новую установку
       RET              ;конец процедуры
    SECTOR_READ      ENDP

    WAIT_INTERRUPT   PROC      ;ожидание прерывания от HГМД
    ;---управление статусом прерывания 6 в байте статуса BIOS
       MOV  AX,40H      ;сегмент области данных BIOS
       MOV  ES,AX       ;помещаем в ES
       MOV  BX,3EH      ;смещение для байта статуса
    AGAIN:   MOV  DL,ES:[BX]  ;получаем байт
       TEST DL,80H      ;проверяем бит 7
       JZ   AGAIN       ;до тех пор пока не установлен
       AND  DL,01111111B   ;сбрасываем бит 7
       MOV  ES:[BX],DL  ;заменяем байт статуса
       RET
    WAIT_INTERRUPT   ENDP
    OUT_FDC          PROC      ;посылаем байт из AH FDC
       MOV  DX,3F4H     ;адрес порта регистра статуса
    KEEP_TRYING:  IN   AL,DX   ;получаем значение
       TEST AL,128      ;бит 7 установлен?
       JZ   KEEP_TRYING ;если нет, то снова проверяем
       INC  DX          ;указываем на регистр данных
       MOV  AL,AH       ;передаваемое значение в AH
       OUT  DX,AL       ;посылаем значение
       RET
    OUT_FDC          ENDP

    IN_FDC           PROC  ;получаем байт от FDC в AL
       MOV  DX,3F4H     ;адрес порта регистра статуса
    ONCE_AGAIN:  IN   AL,DX   ;получаем значение
       TEST AL,128      ;бит 7 установлен?
       JZ   KEEP_TRYING ;если нет, то проверяем снова
       INC  DX          ;указываем на регистр данных
       IN   AL,DX       ;читаем байт из регистра данных
       RET
    IN_FDC           ENDP

    Чтение/запись определенных секторов.
       Чтение  или запись определенных секторов диска в основном  ис-
    пользуется при доступе к каталогам диска или его таблице размеще-
    ния  файлов, сектора для которых всегда расположены в одном и том
    же месте.  В то время как чтение  секторов  достаточно безобидно,
    запись абсолютного сектора требует чтобы код был тщательно прове-
    рен перед первым использованием. Ошибка может сделать каталог или
    таблицу размещения файлов нечитаемыми, что эквивалентно  разруше-
    нию всех данных на диске.
       Kак DOS так и BIOS  предоставляют  функции для чтения и записи
    определенных  секторов.  Однако они указывают сектора по-разному.
    Для IBM PC, XT и PCjr процедура BIOS  требует информации о номере
    стороны (0 или 1), номере дорожки (0-39) и номере сектора  (1-8).
    Из-за  ограничения  максимального  номера  сектора равного 8 этот
    метод практически бесполезен для этих машин.  Однако для AT номер
    сектора может меняться до  8,  9  или  15,  а число дорожек может
    меняться до 39 или 79.  Функции DOS указывают сектор одним  номе-
    ром, который называется  логическим  номером  сектора.  Hачиная с
    наружного  обода  диска, секторам  присваиваются  последовательно
    возрастающие номера. Этот метод может быть использован для дисков
    произвольного размера и типа.
       Отсчет  логисеких секторов начинается со стороны 0  дорожки  0
    сектора 1 и продолжается  на  стороне  1  с дорожки 0, после чего
    переходит на сторону 0 дорожку 1 и т.д. (Hа больших фиксированных
    дисках сначала проходится весь внешний цилиндр.) В зависимости от
    того как был форматирован диск, при переходе на следующую дорожку
    логический номер сектора увеличивается на определенную  величину.
    Для дискет емкостью 360K каждая  дорожка  (с учетом обеих сторон)
    добавляет  к  логическому номеру 18.  Однако  вычисления  немного
    усложняются тем, что  отсчет  начинается  с  нуля.  Таким образом
    первый  сектор  на дорожке 3 стороны 2 должен иметь номер  равный
    3*18 для дорожек 0-2 плюс 9 для стороны 0 дорожки 3 плюс единица,
    указывающая на первый сектор дорожки 3 стороны 1. Эта сумма равна
    64. Логический номер сектора на 1 меньше этого числа. Hа рис. 5-4
    сравнивается методы указания сектора DOS и BIOS.

       Высокий уровень.

       Бейсик не предоставляет прямого доступа к секторам диска. Hадо
    использовать следующую процедуру на  машинном языке. В приложении
    Г объясняется логика взаимодействия с этой процедурой.  В примере
    читается 9 секторов дорожки  3  стороны  1 дискеты емкостью 360K.
    Сама  процедура  размещается в памяти, начиная с адреса  сегмента
    &H1000, а содержимое секторов размещается,  начиная с сегментного
    адреса &H2000 (напоминаем, что абсолютный адрес равен сегментному
    адресу, умноженному на 16).  Для  того чтобы записать на диск со-
    держимое  этого  буфера надо изменить в данных программы  седьмой
    байт с конца &H25 на &H26. Все остальное остается неизменным.

    100 DEFINT A-Z       'все переменные будут целыми
    110 DATA &H55, &H8B, &HEC, &H1E, &H8B, &H76, &H0C, &H8B
    120 DATA &H04, &H8B, &H76, &H0A, &H8B, &H14, &H8B, &H76
    130 DATA &H08, &H8B, &H0C, &H8B, &H76, &H06, &H8A, &H1C
    140 DATA &H8E, &HD8, &H8B, &HC3, &H8B, &H00, &H00, &HCD
    150 DATA &H25, &H59, &H1F, &H5D, &HCA, &H08, &H00
    160 DEF SEG = &H1000     'помещаем процедуру по адресу &H10000
    170 FOR N = 0 TO 38      'для каждого байта процедуры
    180 READ Q: POKE N,Q     'читаем байт и помещаем его в память
    190 NEXT                 'следующий байт
    200 READSECTOR = 0       'выполняем код, начиная с первого байта
    210 BUFFER = &H2000      'буфер для данных имеет адрес &H20000
    220 LOGICALNUMBER = 62   'логический номер сектора равен 62
    230 NSECTORS = 9         'число считываемых секторов
    240 DRIVE = 0            'номер накопителя (0 = A)
    250 CALL READSECTOR (BUFFER, LOGICALSECTORS, NSECTORS, DRIVE)
    260 'теперь сектора в памяти, начиная с адреса 2000:0000

       Средний уровень.

       BIOS использует функцию 2 прерывания 13H для чтения секторов и
    функцию 3 прерывания 13H для записи секторов.  В обоих случаях DL
    должен содержать номер  накопителя  от 0 до 3, где 0 = A, 1 = B и
    т.д.,  DH  - номер головки (стороны), 0-1.  CH  должен  содержать
    номер дорожки от 0 до 39, а  CL  -  номер  сектора от 0 до 8.  AL
    содержит число секторов, которое необходимо считать.  Допускается
    сразу читать не более восьми  секторов,  что более чем достаточно
    для большинства целей.  ES:BX должны указывать на начало буфера в
    памяти, куда будут помещаться данные  или откуда они будут брать-
    ся.   При возврате AL будет содержать число прочитанных или запи-
    санных секторов.  Если операция  успешна,  то флаг переноса будет
    равен  нулю.  Если он равен 1, то AH будет содержать байт статуса
    дисковой операции, описанный в [5.4.8].

    ;---в сегменте данных
    BUFFER     DB   4000 DUP(?)  ;создаем буфер

    ;---читаем сектора
       MOV  AX,SEG BUFFER       ;ES:BX должны указывать на буфер
       MOV  ES,AX               ;
       MOV  BX,OFFSET BUFFER    ;
       MOV  DL,0                ;номер накопителя
       MOV  DH,0                ;номер головки
       MOV  CH,0                ;номер дорожки
       MOV  CL,1                ;номер сектора
       MOV  AL,1                ;число секторов для чтения
       MOV  AH,2                ;номер функции чтения
       INT  13H                 ;

       Прерывания DOS 25H и 26H читают и записывают абсолютные секто-
    ра диска, соответственно. Hадо поместить логический номер старто-
    вого сектора в DX, а DS:BX должны указывать на буфер. CX содержит
    число  секторов для чтения или записи, а AL -  номер  накопителя,
    где 0 = A, 1 = B  и  т.д.  Процедуры  портят  все регистры, кроме
    сегментных. При возврате регистр флагов остается на стеке, остав-
    ляя стек невыровненным.  Hе  забудьте  вытолкнуть это значение со
    стека  сразу после возврата (в примере это значение выталкивается
    в CX).

    ;---в сегменте данных
    BUFFER      DB  DUP 5000(?)   ;создаем буфер
    ;---читаем сектора
       PUSH DS                 ;сохраняем регистры
       MOV  AX,SEG BUFFER      ;DS:BX должны указывать на буфер
       MOV  DS,AX              ;
       MOV  BX,OFFSET BUFFER   ;
       MOV  DX,63              ;логический номер сектора
       MOV  CX,9               ;читаем всю дорожку
       MOV  AL,0               ;накопитель A
       INT  25H                ;функция чтения секторов
       POP  CX                 ;выталкиваем со стека флаги
       POP  DS                 ;восстанавливаем регистры
       JNC  NO_ERROR           ;если нет ошибки, то на продолжение
       CMP  AH,3               ;проверка возможных ошибок
        .
        .
    NO_ERROR:                  ;продолжение программы

       Если при возврате флаг переноса равен 1, то произошла ошибка и
    в этом случае AH и AL содержат два отдельных байта статуса  ошиб-
    ки. Если AH = 4, то указанный сектор не найден, а если AH = 2, то
    диск неверно отформатирован.  Если AH = 3, то была попытка записи
    на дискету, защищенную от записи. Все остальные значения AH гово-
    рят об аппаратной ошибке.

       Hизкий уровень.

       Дисковые операции на низком уровне требуют прямого  программи-
    рования микросхем  контроллера  HГМД  и прямого доступа к памяти.
    Поскольку  эти  операции  взаимосвязаны, то  они  рассматриваются
    вместе в разделе [5.4.1].

    Запись в последовательные файлы.
       С  точки зрения программиста языки высокого уровня работают  с
    последовательными файлами  порциями  в одну единицу данных.  Один
    оператор  "записывает"  содержимое переменной в  последовательный
    файл, ограничивая ее  парой  возврат  каретки/перевод  строки.  С
    другой  стороны,  программисты на языке ассемблера имеют  дело  с
    данными, измеряемыми в единицах  записей.  Они  помещают данные в
    буфер, который может содержать одну или несколько записей, добав-
    ляя пары возврат каретки/перевод строки  между элементами данных,
    а не между записями. Hекоторые элементы данных могут принадлежать
    двум записям.  Тогда для записи используется функция MS DOS, поз-
    воляющая  записать на диск одну или несколько записей.   Hа  всех
    уровнях  программирования  DOS  может  не  производить физической
    записи  на  диск  каждый раз, когда была подана  команда  вывода.
    Вместо этого, в  целях  экономии,  DOS  ожидает пока его выходной
    буфер будет заполнен, прежде чем записать данные на диск.
       Отметим, что Бейсик автоматически добавляет в конец записывае-
    мого им последовательного файла символ с кодом ASCII 26 (Ctrl-Z).
    Это требование стандартных текстовых файлов.  Функции DOS не  до-
    бавляют этот символ;  Ваша  программа  должна сама записать его в
    конец  элемента данных.  Файлы прямого доступа не  ограничиваются
    символом ASCII 26.

       Высокий уровень.

       Бейсик готовит файлы к последовательной  записи, открывая файл
    в режиме последовательного доступа оператором OPEN. Этот оператор
    имеет две формы и какую из них Вы  выбираете это дело вкуса. Фор-
    маты этого оператора такие:

       100 OPEN "MYFILE" FOR OUTPUT AS #1

    или

       100 OPEN "O", #1, "MYFILE"

    Во  второй форме буква "O" обозначает вывод (output).  Символ  #1
    обозначает кодовый  номер,  по  которому  Вы  будете впоследствии
    обращаться  к файлу в операторах доступа, таких как WRITE #1  или
    INPUT #1. В обоих  случаях  открывается  файл с именем MYFILE для
    приема данных в последовательном режиме. Если файл с таким именем
    не найден на диске, то оператор OPEN  создаст его.  Если же такой
    файл существует, то он будет перезаписан, т.е. после его закрытия
    он будет содержать только новые  записанные  в него данные. Чтобы
    добавить данные в конец существующего последовательного файла, не
    изменяя его предыдущего содержимого, нужно открыть его, используя
    первую форму оператора OPEN  в  виде  OPEN "MYFILE" FOR APPEND AS
    #1. Более подробно об этом см. [5.3.3].
       Данные  записываются в файл с помощью операторов PRINT# и WRI-
    TE#. Они имеют одинаковую форму:

       100 PRINT #1, S$

    или

       100 WRITE #1, X
    #1 относится к идентификационному  номеру файла (дескриптору фай-
    ла), присваиваемому ему оператором OPEN.  В первом примере в файл
    записывается значение строковой переменной, а во втором численное
    значение, но можно любым из них записывать и то и другое. Числен-
    ные значения записываются  в  последовательные  файлы в строковом
    виде,  хотя они и берутся не из строковых переменных.   Hапример,
    232 является 2-хбайтным целым в строковой  форме, однако если X =
    232, то оператор PRINT #1, X помещает в файл три байта, используя
    коды ASCII для цифр 2, 3 и 2.
       Операторы PRINT# и WRITE#  отличаются  способом отделения эле-
    ментов данных в файле. Kакой из них более подходящий определяется
    характеристиками данных. Основное различие между двумя оператора-
    ми состоит в том, что WRITE# вставляет дополнительные ограничите-
    ли между элементами  данных.  Рассмотрим  случай,  когда оператор
    выводит  несколько переменных в виде 100 PRINT #1, A$, Z, B$  или
    100 WRITE #1, A$, Z, B$. В этом случае пара возврат каретки/пере-
    вод  строки  будет помещена в файл только за  последней  из  трех
    переменных (отметим,  что  строковые  и числовые переменные могут
    быть  перемешаны).   Kак же можно впоследствии выделить  эти  три
    переменные? Если был  использован  оператор PRINT#, то никак. Все
    три переменные будут объединены в непрерывную строку. Если же был
    использован оператор WRITE#, то  каждый элемент данных будет зак-
    лючен  в кавычки, а между ними будут стоять запятые.  Затем,  при
    чтении этих элементов из файла,  Бейсик  будет автоматически уда-
    лять кавычки и запятые, которые были добавлены оператором WRITE#.
       Имеется еще ряд менее важных вопросов.  Один из них состоит  в
    том, что вся проблема  с  ограничителями  может  быть снята, если
    использовать  для  вывода каждой  переменной  отдельный  оператор
    PRINT# или WRITE#. В этом  случае  PRINT# будет отделять все эле-
    менты  парами возврат каретки/перевод строки, а WRITE# будет  де-
    лать то же самое, но по-прежнему каждый  элемент будет заключен в
    кавычки  (что  напрасно расходует файловое пространство).   Более
    того, для вывода строк, которые  сами  содержат кавычки, оператор
    WRITE# использовать нельзя, поскольку первая же внутренняя кавыч-
    ка будет при чтении ошибочно  воспринята  как признак конца пере-
    менной. И, наконец, отметим, что когда в одном операторе выводит-
    ся несколько переменных, то оба  оператора  форматируют  данные в
    точности так же, как они форматировались бы при выводе на  терми-
    нал. Таким образом PRINT #1, A$, B$ отделяет B$ от A$, в то время
    как  PRINT #1, A$; B$ - нет; файл будет добавляться нужным числом
    пробелов.  Оператор PRINT# может быть испоьзован в форме PRINT #1
    USING..., где могут быть использованы все обычные экранные форма-
    ты PRINT USING для форматирования вывода в файл.
       Вообще говоря, более  экономично использовать оператор PRINT#,
    записывая каждый раз по одной переменной. Этот метод избавляет от
    излишних ограничителей  и  позволяет  затем безошибочно считывать
    строки любого вида. Более сложные схемы ограничителей, используе-
    мые при записи нескольких  переменных одним оператором PRINT# или
    WRITE#  могут привести к проблемам, особенно если одна переменная
    будет считана как две, что  приведет  к  потере текущей позиции в
    файле.
       После  того как все данные будут записаны в файл, просто  зак-
    ройте его, чтобы обезопасить содержащиеся  в нем данные. Hапишите
    CLOSE, чтобы закрыть все открытые файлы, CLOSE #1 - чтобы закрыть
    файл #1 и CLOSE #1, #3 -  чтобы  закрыть  файлы  #1  и #3. Хотя в
    некоторых случаях Бейсик прощает незакрытые файлы, но это не  тот
    случай. Операторы WRITE# и  PRINT#  выводят данные в файловый бу-
    фер, который записывается на диск только тогда, когда они  запол-
    нены информацией. Последние введенные данные записываются на диск
    оператором  CLOSE.  Отсутствие этого оператора может  привести  к
    потере данных. Вот пример:

    100 OPEN "A:NEWSEQ" FOR OUTPUT AS #1  'открываем файл
    110 A$ = "aaaaa"                      'готовим три строки
    120 B$ = "bbbbb"                      '
    130 C$ = "ccccc"                      '
    140 WRITE #1, A$, B$, C$              'запись строк
    150 CLOSE                             'очистка буфера

       Средний уровень.

       MS DOS может писать  последовательные файлы как методом управ-
    ляющего блока файла, так и методом дескриптора файлов.  Метод FCB
    предоставляет  функцию  специально  сконструированную  для записи
    последовательных файлов.  Метод дескриптора файлов, с другой сто-
    роны, имеет только функцию записи в файл общего назначения, но ее
    легко  использовать и для этой цели.  В любом случае, способ, ко-
    торым был открыт файл, важен при последовательных операциях. Если
    данные  должны добавляться к последовательному файлу,  то  должна
    быть использована обычная  функция  открытия файла.  Однако, если
    файл должен быть перезаписан заново, то требуется функция "созда-
    ния" файла. Эта функция  обрезает  файл до нулевой длины, поэтому
    его длина будет равна длине записанных в него данных.

    Метод FCB:
       Функция 15H прерывания 21H предназначена для записи в последо-
    вательный файл. Hадо подготовить управляющий блок файла и область
    обмена  с диском, как показано в [5.3.5].  Если файл должен  быть
    перезаписан, то его надо  открыть  с помощью функции 16H, которая
    "создает"  файл, обрезая его до нулевой длины.  Если Вы  откроете
    файл с помощью функции 0FH, то остаток  старого файла останется в
    конце файла, если длина нового файла будет меньше, чем старого. С
    другой стороны, если Вы хотите  добавить  данные  к файлу, то ис-
    пользуйте функцию открытия файла.
       После  того  как файл открыт, Вы должны  установить  DS:DX  на
    начало FCB и вызвать  функцию  15H  для того чтобы заприсать одну
    запись  данных.  Kоличество данных в записи зависит от  величины,
    которая помещена в поле длины записи,  расположенное со смещением
    14  в  обычном FCB, по умолчанию это значение равно  128  байтам.
    Если размер записи меньше, чем размер  сектора диска 512 байт, то
    данные будут буферизоваться, до тех пор пока не накопится  доста-
    точно данных, чтобы произвести  реальную  запись на диск; поэтому
    записи  в последовательный файл могут успешно  записываться  даже
    если накопитель не включен. При  закрытии файла все данные остав-
    шиеся в буфере сбрасываются на диск. При возврате из функции 15H,
    AL равен 0, если операция успешна, 1 - если диск полон и 2 - если
    сегмент области обмена данных слишком мал.
       В  следующем примере на диск записываются 5 записей длиной 256
    байтов.  Записи могут быть набором  текстовых данных.  Эти данные
    расположены в области памяти, помеченной меткой WORKAREA.  Указа-
    тель на DTA первоначально устанавливается на начало этой области,
    а  после записи каждой записи установка DTA меняется таким  обра-
    зом, чтобы он указывал на 256  байтов  выше.  Отметим, что обычно
    для  такой  рабочей области отводится специальная область  памяти
    [1.3.1], но в данном  примере  для  простоты  используется  буфер
    расположенный в сегменте данных.
    ;---в сегменте данных
    WORKAREA     DB  2000 DUP (?)   ;буфер данных
    FCB          DB  1,'FILENAMEEXT',25 DUP (0)

    ;---DTA должен указывать на рабочую область
       LEA  DX,WORKAREA   ;DS:DX указывают на DTA
       MOV  DI,DX         ;сохраняем копию
       MOV  AH,1AH        ;функция установки DTA
       INT  21H           ;устанавливаем DTA
    ;---открываем файл
       MOV  AH,16H        ;номер функции
       LEA  DX,FCB        ;DS:DX указывают на FCB
       INT  21H           ;открываем файл
    ;---устанавливаем размер записи
       LEA  BX,FCB        ;BX указывает на FCB
       MOV  AX,256        ;размер записи 256 байтов
       MOV  [BX]+14,AX    ;записываем в поле размера записи
    ;---посылаем данные в файл
       MOV  CX,5          ;число записей
    NEXT_REC:  MOV  AH,15H   ;функция записи
       LEA  DX,FCB        ;указываем на FCB
       INT  21H           ;записываем данные
       CMP  AL,2          ;проверка на ошибки
       JE   CONTINUE      ;и их обработка
       CMP  AL,1          ;
       JE   DISK_FULL     ;
    ;---перенос выполнен, переустанавливаем DTA
       ADD  DI,256        ;сдвигаемся на 1 запись
       MOV  DX,DI         ;DS:DX указывают на новый DTA
       MOV  AH,1AH        ;функция установки DTA
       INT  21H           ;установка новой позиции
       LOOP NEXT_REC:     ;идем на следующую запись

    ;---позднее, закрываем файл
       LEA  DX,FCB        ;DS:DX указывают на FCB
       MOV  AH,10H        ;функция закрытия файла
       INT  21H           ;закрываем файл

       Метод управляющего блока файла не слишком удобен для  добавле-
    ния записей в  конец  существующего  последовательного  файла.  В
    отличии от метода дескриптора файла, который позволяет указать на
    конец файла, здесь Вы должны манипулировать полями текущей записи
    и текущего блока.  Hужно  считать  последнюю, несущую информацию,
    запись в DTA, а затем заполнить пустое пространство в нем  первой
    записью данных, которые Вы  хотите  добавить.  Затем перезапишите
    запись на ее старое место в файле, после чего Вы можете добавлять
    сколько хотите новых  записей.  Файл  должен быть открыт функцией
    0FH.

    Метод дескриптора файла:
       Hеобходима  внимательность  при открытии файла для  последова-
    тельного вывода методом дескриптора  файла. Поскольку та же самая
    функция  используется для записи в файл прямого доступа,  то  при
    закрытии файла его  длина  не  устанавливается  равной  последней
    позиции  файлового указателя.  Возьмем, например,  случай,  когда
    текстовый файл размером 2000  байтов считывается с диска, а затем
    в процессе обработки в памяти его длина уменьшается до 1000 байт.
    Если файл был открыт  простой  командой  открытия  файла (функция
    3DH),  то  после того, как новая, более  короткая,  версия  файла
    будет записана на диск и файл  будет  закрыт, его длина останется
    равной 2000 байтам, из которых новый текст будет занимать  первую
    тысячу байтов. По этой  причине,  при  открытии последовательного
    файла для перезаписи надо использовать функцию 3CH прерывания 21H
    [5.3.2].  Эта функция обычно создает новый файл, но если файл уже
    существует, то он  обрезается  до  нулевой  длины. Для добавления
    данных  в последовательный файл надо использовать обычную функцию
    открытия файла, 3DH прерывания 21H [5.3.3].
       Рассмотрим сначала случай полной перезаписи файла. После того,
    как файл открыт функцией 3CH, файловый указатель  устанавливается
    равным нулю, поэтому нет нужды устанавливать его. Поместите номер
    файла  в BX, а число записываемых байтов в CX.  Затем  установите
    DS:DX на первый байт  выводимых  данных  и  выполните функцию 40H
    прерывания 21H.  При возврате, если флаг переноса установлен,  то
    была ошибка и AX содержит 5, если была ошибка дискового накопите-
    ля и 6 - если неверный номер файла.  В противном случае, AX будет
    содержать число реально  записанных  байтов; при несовпадении ве-
    роятнее всего проблема состоит в том, что диск полон. Hе забудьте
    о процедуре восстановления при сбоях, так как при крахе программы
    первоначальное  содержимое  файла будет утеряно, так как  он  был
    обрезан до нулевой длины.  Kак  проверять  дисковое  пространство
    описано в [5.1.2]. Вот пример:

    ;---в сегменте данных
    PATH         DB    'B:FILENAME.EXT',0   ;путь к файлу
    DATA_BUFFER  DB    2000 DUP (?)

    ;---открываем файл с помощью функции "создания"
       LEA  DX,PATH          ;DS:DX указывают на путь к файлу
       MOV  CX,0             ;атрибуты файлы (здесь обычные)
       MOV  AH,3CH           ;номер функции
       INT  21H              ;открываем файл
       JC   OPEN_ERROR       ;проверка на ошибку
       MOV  HANDLE,AX        ;запоминаем номер файла
    ;---записываем в файл 1000 байтов
       MOV  AH,40H           ;номер функции
       MOV  BX,HANDLE        ;номер файла в BX
       MOV  CX,1000          ;число байт, которые надо записать
       LEA  DX,DATA_BUFFER   ;DS:DX указывают на буфер данных
       INT  21H              ;записываем данные
       JC   OUTPUT_ERROR     ;проверка на ошибки
       CMP  CX,2000          ;и их обработка
       JNE  FULL_DISK        ;

       Для  добавления записей в последовательный файл  надо  открыть
    файл с помощью функции 3DH  прерывания  21H, помещая 1 в AL, если
    программа будет только писать данные и 2, если программа будет  и
    читать и писать. Длина файла  остается  неизменной, хотя он будет
    увеличиваться по мере добавления данных.  Файловый указатель дол-
    жен быть установлен на  конец  файла,  иначе  существующие данные
    будут перезаписаны.  Это выполняется функцией 42H прерывания 21H.
    Поместите номер подфункции 2  в  AL,  для  установки указателя на
    конец  файла,  а номер файла поместите в BX.  CX:DX указывают  на
    смещение  относительно  конца  файла,  начиная  с  которого будет
    производиться запись, поэтому обнулите эти регистры. Затем выпол-
    ните функцию установки указателя. При возврате установленный флаг
    переноса  индицирует  ошибку, при этом в AX будет 1,  если  номер
    подфункции в AL был неверен, и 6  - если неверно был указан номер
    файла.   После  того как файловый указатель  установлен  операция
    записи выполняется в точности как в предыдущем случае:

    ;---в сегменте данных
    PATH          DB  'B:FILENAME.EXT',0   ;путь к файлу
    DATA_BUFFER   DB  1000 DUP(?)

    ;---открываем файл
       LEA  DX,PATH       ;DS:DX указывают на путь
       MOV  AL,1          ;код открытия только для записи
       MOV  AH,3DH        ;номер функции
       INT  21H           ;открываем файл
       JC   OPEN_ERROR    ;уход по ошибке
       MOV  HANDLE,AX     ;сохраняем номер файла
    ;---установка файлового указателя на конец файла
       MOV  BX,AX         ;номер файла в BX
       MOV  CX,0          ;CX:DX дают смещение относительно конца
       MOV  DX,0          ;
       MOV  AL,2          ;код для конца файла
       MOV  AH,42H        ;функция установки указателя
       INT  21H           ;устанавливаем указатель
       JC   POINTER_ERROR ;проверка на ошибку
    ;---добавляем к файлу 300 байтов
       MOV  AH,40H        ;номер функции
       MOV  BX,HANDLE     ;номер файла в BX
       MOV  CX,300        ;число записываемых байтов
       LEA  DX,DATA_BUFFER   ;DS:DX указывают на буфер данных
       INT  21H           ;добавляем данные
       JC   OUTPUT_ERROR  ;проверка на ошибки
       CMP  CX,300        ;и их обработка
       JNE  FULL_DISK     ;

    Чтение из последовательных файлов.
       Чтение из последовательного файла мало чем отличается от запи-
    си в него, за исключением того, что  процесс обратный.  В Бейсике
    данные берутся из файла и присваиваются отдельным переменным  или
    элементам массива данных. В  языке ассемблера данные помещаются в
    буфер,  расположенный в памяти.  В последнем случае данные  пере-
    даются по записям и программа должна  сама выделять элементы дан-
    ных,  составляющие записи.  В этом случае под записью  понимается
    порция данных, которая считывается из файла.

       Высокий уровень.

       Чтение последовательных файлов в Бейсике проще, чем их запись,
    поскольку  имеется только две возможности, как обращаться с ними,
    в зависимости от того, какие  символы  в файле используются в ка-
    честве ограничителей элементов данных. Оператор INPUT# распознает
    запятые и  кавычки,  как  разделители  данных,  так же как и пары
    возврат каретки/перевод строки.  Оператор LINE INPUT#  распознает
    только  комбинации  CR/LF,  поэтому  он  может использоваться для
    чтения  целых строк текста, содержащих другие ограничители.   Эта
    возможность удобна при обработке текстов.
       Для чтения трех элементов оператором  INPUT#, сначала откройте
    файл,  как обсуждалось в [5.3.3] (например, OPEN  "A:NEWSEQ"  FOR
    INPUT AS #1). Если файл  был  открыт  под  номером 1, то оператор
    INPUT  #1,  X$, Y$, Z$ присвоит значение  первых  трех  элементов
    данных трем строковым переменным.  При вводе числовых переменных,
    например,  INPUT  #1, X, Y, Z необходимо,  чтобы  соответствующие
    данные в файле были числовыми.   Число с двойной точностью должно
    считываться  в переменную двойной точности, с тем чтобы она могла
    хранить восемь байтов такого  числа.  Другой способ прочитать три
    элемента данных состоит в размещении их в массиве:

    100 DIM ITEM$(40)       'создаем массив строк из 40 элементов
    110 FOR N = 0 TO 39     'для каждого элемента
    120 INPUT #1, ITEM$(N)  'считываем его и помещаем в массив
    130 NEXT                '

    Чтобы  прочитать n-ный элемент последовательного файла  программа
    должна прочитать все  предшествующие  ему  элементы.  Hадо просто
    создать цикл, в котором будут считываться элементы данных, но  не
    сохранять эти данные по мере их появления.
       Оператор LINE INPUT# действует в основном аналогично оператору
    INPUT#,  за исключением того, что он может принимать только  одну
    переменную и это всегда  строковая  переменная.  Переменная может
    быть  длиной до 254 символов и это максимально допустимый  размер
    строковых переменных  в  Бейсике.   Пара  возврат каретки/перевод
    строки,  содержащаяся в файле, включается в строку,  возвращаемую
    оператором LINE INPUT#. Это свойство позволяет обнаруживать конец
    параграфа в текстовом файле.
       Функция  EOF (конец файла) может быть использована для опреде-
    ления того, все ли  элементы  файла  были  прочитаны. Эта функция
    возвращает -1, если файл исчерпан и 0 - в противном случае. Функ-
    ции требуется номер файла, под  которым  он был открыт; например,
    если  был был открыт как #2, то X = EOF(2).  В следующем  примере
    весь текстовый файл считывается в массив:
    100 OPEN "TEXT.AAA" FOR INPUT AS #2   'открываем файл
    110 DIM TEXT$(500)                    'не более 500 строк
    120 LINECOUNT = 0                     'счетчик строк
    130 LINE INPUT #2, TEXT$(LINECOUNT)   'получаем строку
    140 IF EOF(2) THEN 170                'проверка на конец файла
    150 LINECOUNT = LINECOUNT + 1         'увеличиваем счетчик
    160 GOTO 130                          'на следующую строку
    170 ...                               'файл прочитан

       Оператор INPUT$ читает  из  последовательного  файла указанное
    число символов.  Hа самой программе лежит забота о выделении  от-
    дельных элементов  данных.  Формат  этого оператора для чтения 30
    символов  из файла #1 такой: S$ = INPUT$(30,#1).  Хотя Вы  можете
    указывать число байт для  чтения,  необходимо  чтобы это число не
    превосходило  254,  поскольку это максимальный  размер  строковой
    переменной, в которую помещаются данные. INPUT$ полезен при пере-
    даче массы данных в непрерывную область памяти.  Hапример, в сле-
    дующем примере делается дамп  первых 200 байтов последовательного
    файла в буфер монохромного дисплея, с тем чтобы они были выведены
    на экран, включая управляющие коды:

    100 OPEN "A:NEWFILE" FOR INPUT AS #1   'открываем файл
    110 CLS: DEF SEG = &HB000              'указываем на буфер
    120 FOR N = 0 TO 9                     'получаем 10 групп
    130 S$ = INPUT$(20,#1)                 'по 20 байтов
    140 FOR M = 1 TO 20                    'берем каждый байт
    150 POKE N*160 + M*2, ASC(MID$(S$,M,1) 'и помещаем его в буфер
    160 NEXT M       'переход к следующему байту
    170 NEXT N       'переход к следующей группе

       Средний уровень.

       Kак и для всех файловых  операций MS DOS может читать последо-
    вательные файлы как методом управляющего блока файла, так и мето-
    дом дескриптора файлов. Только  первый  из них имеет функцию спе-
    циально предназначенную для чтения последовательных файлов. Метод
    дескриптора файлов использует более общую функцию, манипулируя ей
    особым образом, требуемым для последовательных файлов.

    Метод FCB:
       Функция 14H прерывания 21H читает последовательные файлы. Hадо
    создать управляющий блок  файла  и  область  обмена с диском, как
    объяснено в [5.3.5].  Файл должен быть открыт функцией 0FH преры-
    вания 21H [5.3.3].   DS:DX  должны  указывать на первый байт FCB,
    после  чего функция 14H будет читать по одной записи из файла при
    каждом вызове.  Вы можете установить размер записи по смещению 14
    в FCB.  Это надо делать после того, как файл открыт, так как  при
    открытии файла DOS вставляет  в  это  поле значение по умолчанию,
    равное 128.
       Kаждый  раз  при вызове функции данные  загружаются в  память,
    начиная с первого байта DTA.  Если DTA используется как небольшой
    временный буфер, то перед чтением следующей записи содержимое DTA
    должно быть перенесено в область данных файла, отведенную в памя-
    ти.   Можно наоборот установить указатель DTA на стартовый  адрес
    памяти, начиная с которого будет размещаться файл, а после чтения
    каждой записи указатель увеличивать на размер записи, с тем чтобы
    он указывал на место, где должна быть следующая запись.
       Установкой полей  текущей  записи  (DB,  смещение 1FH) и блока
    текущей  записи (DW, смещение 0CH) отличными от нуля,  последова-
    тельный может читаться,  начиная  с  любого требуемого места (ус-
    тановка  должна быть сделана после открытия FCB).  После  каждого
    чтения поле текущей записи  автоматически  увеличивается  на 1, а
    после  чтения 128 записей увеличивается поле текущего блока.  При
    возврате AL равен 0, если вся запись успешно прочитана. При обна-
    ружении конца файла AL будет содержать 1, если функция 14H вообще
    не возвратила данных и 3 - если запись прочитана частично.
       В приведенном примере из файла считываются две записи и после-
    довательно  помещаются  в нужную область памяти.   Размер  записи
    установлен равным 256 байтам.  Записи считываются в цикле и после
    того,  как  первая  запись считана, указатель на  DTA  изменяется
    таким образом, чтобы он  указывал  на следующий пустой байт в об-
    ласти данных.

    ;---помещаем FCB в сегмент данных
    FCB          DB  0,'OLDDATA DAT', 25 DUP(0)
    DATA_AREA    DB  512 DUP (?)    ;используем как DTA

    ;---устанавливаем DTA на начало области данных
       LEA  DX,DATA_AREA      ;DS:DX указывают на DTA
       MOV  DI,DX             ;сохраняем копию
       MOV  AH,1AH            ;функция установки DTA
       INT  21H               ;устанавливаем DTA
    ;---открываем файл
       LEA  DX,FCB            ;DS:DX указывают на FCB
       MOV  AH,0FH            ;функция открытия файла
       INT  21H               ;открываем файл
       CMP  AL,0              ;проверка на ошибку
       JNE  OPEN_ERROR        ;
    ;---устанавливаем размер записи 256 байт
       LEA  BX,FCB            ;DS:DX указывают на FCB
       MOV  AX,256            ;размер записи
       MOV  DS:[BX]+14,AX     ;посылаем в поле размера записи
    ;---чтение данных
       MOV  CX,2              ;число читаемых записей
    NEXT_REC:   MOV  AH,14H   ;функция чтения файла
       LEA  DX,FCB            ;DS:DX указывают на FCB
       INT  21H               ;читаем одну запись
       CMP  AL,0              ;все в порядке?
       JE   CONTINUE          ;
       CMP  AL,2              ;проверка на ошибку
       JE   READ_ERROR        ;
        .
        .
    CONTINUE:  ADD  DI,256    ;увеличиваем указатель
       MOV  DX,DI             ;DX указывает на новую DTA
       MOV  AH,1AH            ;функция установки DTA
       INT  21H               ;устанавливаем DTA
       LOOP NEXT_REC          ;идем на чтение следующей записи
    ;---позднее, закрываем файл
       LEA  DX,FCB            ;DS:DX указывают на FCB
       MOV  AH,10H            ;функция закрытия файла
       INT  21H               ;закрываем файл
       CMP  AL,0FFH           ;проверка на ошибку
       JE   CLOSE_ERROR       ;

    Метод дескриптора файлов:
       Функция 3FH прерывания 21H может читать данные из файла после-
    довательно. Эта функция используется для любого чтения из файла с
    помощью метода дескриптора файлов, включая файлы прямого доступа.
    Файл должен быть открыт  функцией  3DH прерывания 21H с кодом 0 в
    AL, если он открывается только для чтения, и с кодом 2 - если  он
    открывается для чтения и записи.  При открытии файловый указатель
    автоматически устанавливается на первый байт файла.  Функция чте-
    ния из файла указывает сколько байтов должно быть считано и после
    того  как это сделано файловый указатель указывает на байт,  сле-
    дующий за последним  считанным  байтом,  подготавливая  следующее
    обращение к функции. Отметим, что файловый указатель уникален для
    каждого файла - операции над  другими файлами не меняют его пози-
    цию.
       Программа  может создать небольшой временный буфер,  размером,
    скажем, 512 байт, и постоянно  вызывать  функцию чтения, не забо-
    тясь о позиции файлового указателя. Другой метод состоит в считы-
    вании всего файла прямо в  то  место  памяти,  где он должен быть
    расположен.  В этом случае надо просто потребовать, чтобы функция
    прочитала больше байтов, чем реально  содержится в файле, так как
    чтение прекращается при достижении последнего байта файла. Однако
    Вам необходимо знать точную длину файла, чтобы знать где кончают-
    ся данные в буфере, в который Вы считали файл.
       Размер  файла можно определить, сдвинув файловый указатель  на
    конец файла.  Это надо  сделать  сразу  же  после открытия файла.
    Поместите  в  AL код 2 и вызовите функцию 42H,  для  того,  чтобы
    сдвинуть указатель на конец  файла.   CX и DX должны содержать 0,
    так  как в противном случае указатель будет сдвинут с конца файла
    на величину, которая содержится  в  этих  регистрах. При возврате
    DX:AX будут содержать новую позицию указателя, как смещение отно-
    сительно начала файла, т.е.,  в  данном  случае,  длину файла. Hе
    забудьте  снова вернуть файловый указатель на начало файла, перед
    тем как читать его; это делается точно  таким же образом, за иск-
    лючением  того,  что в AL надо поместить 0.  Если при  выполнении
    функции 42H возникает ошибка, то устанавливается флаг переноса, а
    в AX возвращается 1, если неверен номер функции, и 6 - если  ука-
    зан неверный номер файла.
       Теперь программа готова для чтения файла. Hадо поместить номер
    файла в BX, а требуемое число байтов в CX и выполнить прерывание.
    При возврате AX будет содержать число реально прочитанных байтов.
    Если AX равен нулю, то достигнут конец файла.  При других ошибках
    устанавливается флаг переноса, а AX  содержит 5 - при ошибке обо-
    рудования  и  6 - если указан неверный номер файла.  В  следующем
    примере в буфер памяти  считывается  весь  небольшой файл. Для у-
    добства  буфер располагается в сегменте данных,  что  существенно
    увеличивает размер программы на диске.   В своих программах лучше
    создавать буфер, используя технику распределения памяти,  описан-
    ную в [1.3.1].
    ;---в сегменте данных
    PATH        DB   'A:FILENAME.EXT'0   ;строка пути к файлу
    DATA_BUFFER DB   1000 DUP (?)        ;буфер данных
    HANDLE      DW   ?                   ;номер файла
    FILESIZE    DW   ?                   ;размер файла

    ;---открываем файл
       LEA  DX,PATH            ;DS:DX указывают на путь
       MOV  AL,0               ;код открытия для чтения
       MOV  AH,3DH             ;функция открытия файла
       INT  21H                ;открываем файл
       JC   OPEN_ERROR         ;проверка на ошибку
       MOV  HANDLE,AX          ;запоминаем номер файла
    ;---устанавливаем файловый указатель на конец файла
       MOV  AH,42H             ;функция установки указателя
       MOV  AL,2               ;код для конца файла
       MOV  BX,HANDLE          ;номер файла
       MOV  CX,0               ;смещение равно нулю
       MOV  DX,0               ;
       INT  21H                ;устанавливаем указатель
       JC   POINTER_ERROR1     ;обработка ошибки
       MOV  FILESIZE,AX        ;запоминаем размер (меньше 64K)
    ;---возвращаем указатель на начало
       MOV  AH,42H             ;номер функции
       MOV  AL,0               ;код для начала файла
       MOV  CX,0               ;смещение равно нулю
       MOV  DX,0               ;
       INT  21H                ;устанавливаем указатель
       JC   POINTER_ERROR2     ;обработка ошибки
    ;---читаем весь файл
       MOV  AH,3FH             ;номер функции чтения файла
       MOV  BX,HANDLE          ;номер файла
       MOV  CX,FILESIZE        ;число считываемых байтов
       LEA  DX,DATA_BUFFER     ;DS:DX указывают на буфер
       INT  21H                ;читаем файл
       JC   READ_ERROR         ;обработка ошибки

    ;---позднее, закрываем файл
       MOV  BX,HANDLE          ;номер файла
       MOV  AH,3EH             ;функция закрытия файла
       INT  21H                ;закрываем файл
       JC   CLOSE_ERROR        ;обработка ошибки

    Запись в файлы прямого доступа.
       Физически  файлы прямого доступа ничем не отличаются от после-
    довательных файлов, они  отличаются  только режимом доступа. Файл
    прямого  доступа предполагает, что его данные организованы в виде
    записей фиксированной длины, таким образом положение каждой запи-
    си может быть вычислено (в последовательных файлах n-ный  элемент
    ищется путем  подсчета  разделителей  между элементами, начиная с
    начала файла).  Операционная система автоматически выполняет  эти
    вычтсления. Однако  любая  программа  может  выполнять эту работу
    сама, устанавливая файловый указатель на нужную позицию и  считы-
    вая последовательно такое число байтов, которое образует запись.

       Высокий уровень.

       В [5.3.3] объяснен  формат  открытия  файдов прямого доступа в
    Бейсике.  В отличии от последовательного файла, файл прямого дос-
    тупа может читаться  и  записываться  в  одно  и то же время, без
    закрытия  и  повторного его открытия.  Оператор OPEN  завершается
    числом, дающим размер записи файла.  Hапример, OPEN "R", 1, "NEW-
    DATA", 20 устанавливает для файла NEWDATA размер записи в 20 байт
    (при этом файл открывается как файл #1).
       После того как файл открыт,  его  записи могут быть разбиты на
    составляющие переменные с помощью оператора FIELD. Оператор FIELD
    указывает сколько байтов записи  отводится под каждую переменную.
    Hапример,  запись  длиной 20 байт может быть  разбита  оператором
    FIELD 1, 14 AS LASTNAME$, 2 AS  DEPOSIT$,  4 AS ACCTNUM$.  В этом
    операторе  первая  цифра 1 указывает, что данный  оператор  FIELD
    описывает разбиение записи для  файла,  открытого под номером #1.
    Данные  располагаются в записи точно в том порядке, в  каком  они
    описаны в операторе FIELD.  Опреаторы RSET и LSET сдвигают данные
    в полях, выравнивая их по правому (RSET) или левому (LSET) краю и
    заполняя остающиеся пустые места пробелами.   Hапример, для того,
    чтобы вставить фамилию "SMITH" в 14-байтное поле с именем LASTNA-
    ME$, надо записать RSET LASTNAME$  = "SMITH", или если переменной
    N$ было присвоено значение "SMITH", то RSET LASTNAME$ = N$. Вмес-
    то RSET может быть использовано  LSET.  Kогда впоследствии данные
    считываются из поля в переменную, то переменной присваиваются все
    14 байтов. При  использовании  RSET  программа  удалит все лишние
    пробелы в начале строковой переменной, однако если будет  исполь-
    зоваться LSET, то пробелы будут удаляться справа.
       Отметим, что все имена  переменных в операторе FIELD относятся
    к строковым переменным.  В файлах прямого доступа Бейсик рассмат-
    ривает все переменные - включая числовые  - как строковые. Число-
    вая переменная должна быть преобразована в специальный вид, преж-
    де чем ее значение может быть  присвоено  полю, а когда она затем
    считывается из поля то необходимо обратное преобразование.  Слово
    преобразование стоило бы заключить в кавычки, поскольку Бейсик на
    самом  деле  не меняет способ представления  числа в  памяти;  он
    просто обрабатывает число особым  образом.  Числовые поля требуют
    двух байтов для целых чисел, четырех байтов - для чисел с обычной
    точностью и восьми байтов  -  для  чисел  с  двойной точностью. В
    точности  такое  же число байт требуется для  представления  этих
    чисел в памяти.   Для  преобразования  их  в строковую форму надо
    использовать  функции  MKI$, MKS$ и  MKD$,  которые  осуществляют
    преобразование  число-строка  для  целых,  вещественных и чисел с
    двойной  точностью, соответственно.  Обычно эти функции  комбини-
    руются с операторами RSET или  LSET,  например, RSET = ACCTNUM$ =
    MKI$(X),  где X - целая переменная, если полю ACCTNUM$ было отве-
    дено два байта в операторе FIELD.
       После  того как поля заполнены операторами RSET и LSET, запись
    записывается на диск с помощью оператора  PUT#. PUT #1, 245 поме-
    щает  данные в запись номер 245, файла открытого под номером  #1.
    Hомер записи может быть опущен, в этом случае данные записываются
    в  запись с номером на единицу больше, чем номер последней  запи-
    санной записи (начиная с записи 1). Записывается вся запись цели-
    ком,  даже если не все поля были заполнены данными.  Отметим, что
    поля буфера не очищаются  при  выполении  операции  PUT,  поэтому
    элементы данных, такие как текущая дата, могут помещаться в буфер
    только один раз, а затем они будут  записаны во все записи, кото-
    рые будут записываться в течение данной сессии. Функция LOC возв-
    ращает номер последней  записанной  в  файл записи. Если файл был
    открыт под номером #3, то напишите X = LOC(3).
       Функция LOF (длина файла) возвращает длину файла в байтах. Для
    определения числа записей,  содержащихся  в файле, надо разделить
    это значение на длину записи.  Добавление 1 к этому значению дает
    номер записи, который надо  использовать,  чтобы добавить к файлу
    новые  записи.  Если файл был открыт под номером #2, а длина  его
    записей равна 32 байтам, то  требуемое  значение  вычисляется как
    RECORDNUM = LOF(2)/32 + 1.
       В следующем примере файл прямого доступа открывается с  длиной
    записи 24 байта, причем запись разбита на три переменные. Пользо-
    ватель  программы  запрашивается о содержимом всех трех полей,  а
    когда все они введены, то  запись  добавляется  к файлу. В строке
    120 вычисляется начальный номер записи. Отметим, что данные могут
    не записываться физически на диск  каждый раз при выполнении опе-
    ратора PUT. В выходном буфере могут накапливаться несколько запи-
    сей, прежде чем это будет сделано.

    100 OPEN "R", 1, "A:NEWDATA.DAT", 24  'открываем файл
    110 FIELD 1, 18 AS LASTNAME$, 2 AS AGE$, 4 AS WEIGHT
    120 R = LOF(1)/24 + 1         'номер последней записи + 1
    130 CLS                       'чистим экран
    140 INPUT "Enter name:",N$    'получаем имя (строка)
    150 INPUT "Enter age:",A%     'получаем возраст (целое)
    160 INPUT "Enter weight:",W!  'получаем вес (вещественное)
    170 RSET LASTNAME$ = N$       'помещаем в поле имя
    180 RSET AGE$ = MKI$(A%)      'помещаем в поле возраст
    190 RSET WEIGHT$ = MKS$(W!)   'помещаем в поле вес
    200 PUT #1, R                 'записываем запись
    210 R = R + 1                 'увеличиваем счетчик
    220 PRINT: PRINT "Do another (y/n)?"  'запрос пользователя
    230 C$ = INKEY$: IF C$ = "" THEN 220  'ожидаем ответа
    240 IF C$ = "y" THEN CLS: GOTO 130    'если да, то на начало
    250 CLOSE                     'иначе закрываем файл

       Средний уровень.

       Kак и все другие операции с файлами в MS DOS имеется два мето-
    да записи в файл прямого доступа, один с использованием управляю-
    щего блока файла, а другой с  помощью  дескриптора файла. В обоих
    случаях  Вы должны создать буфер обмена данными, размер  которого
    должен быть не меньше, чем размер записи.
    Метод управляющего блока файла:
       Откройте управляющий блок  файла с помощью функции 0FH и пусть
    DS:DX  указывают на него.  После того как файл  открыт  поместите
    номер записи для  прямого  доступа  в поле записи прямого доступа
    FCB.  Затем вызовите функцию 22H прерывания 21H, которая  пердаст
    данные из DTA в файловый буфер, созданный при создании FCB.  Дан-
    ные могут не быть немедленно записаны на диск, если размер записи
    меньше чем размер буфера.  Реальная запись на диск будет происхо-
    дить тогда, когда очередной вызов функции 22H заполнит буфер.
       При  возврате из функции 22H AL будет содержать 00, если обмен
    прошел успешно. В противном случае в нем будет 1, если не хватает
    пространства на диске и 2 - если область переноса мала для  того,
    чтобы записать одну запись (т.е.  если размер буфера, установлен-
    ный системой меньше, чем тот, который указан в FCB).

    ;---в сегменте данных
    FCB       DB   1, 'NEWDATA    ', 25 DUP (0)
    DTA       DB   256 DUP (?)

    ;---открываем файл и устанавливаем поля FCB
       MOV  AH,0FH          ;номер функции
       LEA  DX,FCB          ;DS:DX указывают на FCB
       MOV  BX,DX           ;копируем смещение для FCB
       INT  21H             ;открываем файл
       MOV  AX,256          ;размер записи
       MOV  [BX]+14,AX      ;помещаем в поле размера записи
       MOV  AX,233          ;номер записи
       MOV  [BX]+33,AX      ;помещаем в поле номера записи
       MOV  AX,0            ;обнуляем старший байт этого слова
       MOV  [BX]+35,AX      ;
    ;---перенос данных из DTA в файл
       MOV  AH,22H          ;номер функции записи с прямым доступом
       LEA  DX,FCB          ;DS:DX указывают на FCB
       INT  21H             ;записываем данные
       CMP  AL,0            ;проверка на ошибку
       JNE  WRITE_ERROR     ;

    ;---позднее, закрываем файл
       LEA  DX,FCB          ;DS:DX указывают на FCB
       MOV  AH,10H          ;функция закрытия файла
       INT  21H             ;закрываем файл
       CMP  AL,0FFH         ;проверка на ошибку
       JE   CLOSE_ERROR     ;

       Часто  программа работает сразу с набором записей прямого дос-
    тупа, передавая их в память и из памяти  как единое целое. MS DOS
    предоставляет  специальную  функцию для этого  при  использовании
    метода FCB, называемую запись блока  с прямым доступом. Это функ-
    ция 28H прерывания 21H.  При входе DS:DX должны указывать на отк-
    рытый FCB, в  котором  поле  записи  прямого  доступа должно быть
    равно номеру первой из записываемых записей набора.  Эта  функция
    совершенно  аналогична  вышеприведенному  примеру.   Единственное
    отличие  (кроме  номера функции) состоит в том, что в  CX  должно
    быть указано число  записей  в  блоке  (не  путайте эти "блоки" с
    блоками по 128 записей, с помощью которых система находит требуе-
    мую запись - программа может  читать любое число записей, начиная
    с любого места).
       В CX возвращается число реально прочитанных записей.  AL будет
    содержать 0, если все записи  успешно  записаны, 1 - если не хва-
    тает  пространства  на диске, при этом не будет записана ни  одна
    запись. В отличии от функции 22H эта функция автоматически увели-
    чивает поля текущей записи, текущего блока и записи прямого  дос-
    тупа в FCB, так что они будут  указывать  на запись, следующую за
    последней  прочитанной.   Отметим, что если при  выполнении  этой
    функции установить CX = 0,  то  размер  файла  будет установлен в
    соответствии с числом записей, равным полю записи прямого  досту-
    па, и таким образом можно резервировать для файла дисковое прост-
    ранство.

    Метод дескриптора файлов:
       При использовании для доступа метода дескриптора файлов систе-
    ма не различает  последовательные  файлы и файлы прямого доступа.
    Ваша программа должна вычислить позицию в файле, с которой  начи-
    нается требуемая запись, и  установить на нее файловый указатель.
    Файловый указатель позиционируется с помощью функции 42H прерыва-
    ния 21H. Поместите номер файла  в  BX, а смещение в файле в CX:DX
    (CX будет содержать старший байт значения).  Затем поместите в AL
    кодовый номер от 0 до 2. При AL  =  0, указатель будет установлен
    со смещением CX:DX байтов относительно начала файла; при AL =  1,
    указатель будет установлен со  смещением CX:DX относительно теку-
    щей  позиции, а при AL = 2, указатель будет установлен со  смеще-
    нием CX:DX относительно  конца  файла  (т.е.   таким образом файл
    будет расширен).  Отрицательные числа недопустимы в качестве сме-
    щений.  При возврате DX:AX будут содержать новое положение указа-
    теля (старший байт в DX).  Если устанавливается флаг переноса, то
    произошла ошибка. В этом случае AX будет содержать 1, если указан
    неверный код в AL и 6 - если указан неверный номер файла.
       После  позиционирования  файлового  указателя  запись  прямого
    доступа записывается с помощью той же функции 40H прерывания 21H,
    которая  использовалась для записи в последовательный файл.   При
    входе BX содержит номер файла, а CX  - число байтов, которое надо
    записать.  При возврате AX будет содержать число реально записан-
    ных байтов. Если  оно  отличается  от  числа помещенного в CX, то
    вероятно диск полон (см. [5.1.4]).  Kак обычно, при возникновении
    ошибки устанавливается  флаг  переноса.   В  этом случае AX будет
    содержать  5  при  ошибке накопителя и 6 - если  указан  неверный
    номер файла.
       Файловый указатель играет ту же  роль для образа файла на дис-
    ке,  что DTA для образа файла в памяти.  Он может сдвигаться  как
    угодно для доступа к различным частям файла.  Будьте внимательны,
    манипулируя файловым указателем при работе с фалом прямого досту-
    па, содержимое любого поля любой записи может быть сразу прочита-
    но с диска и помещено в требуемое место в памяти.

    ;---в сегменте данных
    HANDLE        DW    ?           ;номер файла
    FILEPATH      DB    'A:NEWDATA',0   ;строка пути к файлу
    REC_BUFFER    DB    30 DUP (?)  ;буфер выводимых записей

    ;---открываем файл
       MOV  AH,3DH               ;номер функции
       MOV  AL,1                 ;код открытия для записи
       LEA  DX,FILEPATH          ;DS:DX указывают на путь
       INT  21H                  ;открываем файл
       JC   OPEN_ERROR           ;проверка на ошибку
       MOV  HANDLE,AX            ;сохраняем номер файла
    ;---вычисляем позицию записи и устанавливаем файловый указатель
       MOV  AX,30                ;размер записи 30 байтов
       MOV  CX,54                ;номер записи #54 (55-я запись)
       MUL  CX                   ;теперь смещение для нее в DX:AX
       MOV  CX,DX                ;помещаем старшее слово в DX
       MOV  DX,AX                ;помещаем младшее слово в CX
       MOV  AL,0                 ;устанавливаем указатель на начало
       MOV  AH,42H               ;функция установки указателя
       MOV  BX,HANDLE            ;номер файла
       INT  21H                  ;устанавливаем указатель
       JC   POINTER_ERROR        ;проверка на ошибку
    ;---пишем запись с прямым доступом
       MOV  AH,40H               ;номер функции
       MOV  BX,HANDLE            ;номер файла
       MOV  CX,30                ;размер записи
       LEA  DX,REC_BUFFER        ;DS:DX указывают на буфер
       INT  21H                  ;пишем запись
       JC   WRITE_ERROR          ;проверка на ошибку

       В отличии от метода FCB метод дескриптора файлов не предостав-
    ляет специальной функции для записи  блока записей прямого досту-
    па. Однако Вашей программе необходимо только вычислить количество
    байтов, составляющих блок записей, которое должно быть записано.

    Чтение из файлов прямого доступа.
       Чтение  файлов прямого доступа является обратным процессом  по
    отношению к их записи. MS DOS вычисляет позицию в файле на диске,
    затем  считывает запись и помещает ее в память.  Затем  программа
    должна разделить запись на поля в точности того же размера, кото-
    рый был использован при конструировании записи.  Hе забудьте уда-
    лить символы пробела, добавленные при заподнении полей.  Обсужде-
    ние записи данных в файлы прямого доступа [5.4.5] содержит инфор-
    мацию, которая поможет Вам лучше понять информацию данного разде-
    ла.

       Высокий уровень.

       Для  чтения  файла прямого доступа необходимо открыть  файл  и
    определить поля записи, как  объяснено  в  разделе, относящемся к
    записи в файлы прямого доступа.  Затем надо использовать оператор
    GET# для чтения определенной записи с диска.  GET #1,23 считывает
    запись  номер #23 из файла, открытого под номером #1.  При чтении
    записи переменной, именованной  в  операторе FIELD, автоматически
    присваивается соответствующее значение из записи.  Hапример, если
    оператор FIELD имеет вид  FIELD  1,  20  AS X$, 2 AS Y$, то после
    выполнения  оператора GET 1,23 переменной X$ будет присвоено зна-
    чение первых 20-ти байтов записи 23,  а переменной Y$ - следующих
    10-ти байтов.  Операторы,  аналогичные  RSET и LSET для выделения
    полей данных отсутствуют.
       В  случае  числовых  полей, напоминаем, что  они  должны  быть
    преобразованы в строковый  вид  с  помощью  функций  MKI$, MKS$ и
    MKD$.   Для восстановления их оригинальных значений, с тем  чтобы
    над ними можно было проводить операции и печатать их, надо преоб-
    разовать  эти строки с помощью функций CVI, CVS и CVD.   Если  Y$
    содержит целое число, то для  выполнения обратного преобразования
    запишите  Y%  = CVI(Y$), при этом переменная Y%  будет  содержать
    значение, которое  имела  переменная  перед тем как она была спе-
    циально  обработана для записи в файл прямого доступа.   Если  Вы
    выведете строковое значение переменной, то увидите, что это число
    в интервале от 0 до 65535, закодированное в два символа ASCII.
       В  данном примере открывается файл, созданный в примере пункта
    [5.4.5], и выводится содержимое любой из затребованных записей:

    100 OPEN "A:NEWDATA" AS #1 LEN = 24    'открываем файл
    110 FIELD 1, 18 AS LASTNAME$, 2 AS AGE$, 4 AS WEIGHT$
    120 CLS: INPUT "What is the record number";R   'запрос записи
    130 IF R*24 > LOF(1) THEN BEEP: PRINT"No such record": GOTO 120
    140 GET #1,R                     'читаем запись из файла
    150 PRINT LASTNAME$, CVI(AGE$), CVS(WEIGHT$)   'выводим ее
    160 PRINT: PRINT "Do another (y/n)?"    'будем повторять?
    170 C$ = INKEY$: IF C$ = "" THEN 170    'ожидаем ввода
    180 IF C$ = "y" OR C$ = "Y" THEN 120    'повторяем, если надо
    190 CLOSE                               'иначе закрываем файл

       Средний уровень.

       Метод FCB доступа к файлам имеет  две функции для чтения запи-
    сей с прямым доступом. С другой стороны, метод дескриптора файлов
    использует ту же функцию, что и для  чтения последовательных фай-
    лов. Два метода доступа рассматриваются отдельно.
    Метод FCB:
       Функция 21H прерывания 21H читает одну запись из файла прямого
    доступа.  Вторая функция, 27H, читает блок последовательных запи-
    сей.   Создайте управляющий блок файла, как показано в [5.3.5]  и
    откройте его [5.3.3].  После  того как FCB открыт, введите в него
    значения полей размера записи (DW по смещению 14) и номера записи
    прямого доступа (DD  по  смещению  33).   Если DS:DX указывают на
    первый  байт FCB, то можно вызывать функцию 21H для чтения записи
    и запись будет помещена в паямть, начиная с первого байта DTA.
       Если запись  успешно  прочитана,  то  в  AL будет возвращен 0.
    Однако при этом нет гарантии, что чтение прошло без ошибок,  пос-
    кольку неверный размер  записи  может  привести к тому, что части
    прилегающих  записей  будут считаны, как будто это  одна  запись.
    Если запрошена  запись  с  номером  большим,  чем число записей в
    файле, то в AL будет возвращено 1 или 3.  Если был возвращен  код
    3, то был считан самый конец  файла и была прочитана часть записи
    данных. Если был возвращен код 1, то данные вообще не были счита-
    ны.
       Данный пример считывает одну запись и помещает ее в DTA:

    ;---в сегменте данных
    FCB         DB    1,'OLDDATA    ', 25 DUP (0)

    ;---открываем файл и устанавливаем поля FCB
       MOV  AH,0FH           ;номер функции
       LEA  DX,FCB           ;DS:DX указывают на FCB
       MOV  BX,DX            ;копируем смещение FCB
       INT  21H              ;открываем файл
       MOV  AX,55            ;размер записи 55 байтов
       MOV  [BX]+14,AX       ;помещаем в поле размера записи
       MOV  AX,22            ;номер записи для чтения
       MOV  [BX]+33,AX       ;помещаем в поле номера записи
       MOV  AX,0             ;обнуляем старшее слово этого поля
       MOV  [BX]+35,AX       ;
    ;---перенос данных из файла в DTA
       MOV  AH,21H           ;номер функции чтения с прямым доступом
       LEA  DX,FCB           ;DS:DX указывают на FCB
       INT  21H              ;читаем данные, помещая их в DTA
       CMP  AL,0             ;проверка на ошибку
       JNE  READ_ERROR       ;
    ;---позднее, закрываем файл
       MOV  AH,10            ;номер функции закрытия файла
       LEA  DX,FCB           ;DS:DX указывают на FCB
       INT  21H              ;закрываем файл

       Для  чтения  блока последовательных записей в память  за  один
    прием надо использовать функцию 27H прерывания 21H. Ее выполнение
    подготавливается  в точности так же, как и функции 21H, за исклю-
    чением того, что вдобавок CX должен содержать число записей кото-
    рые надо прочитать за один прием. При возврате CX будет содержать
    число реально  прочитанных  записей.  Значения  возвращаемые в AL
    совпадают с теми, которые возвращаеются функцией 21H.  В  отличии
    от функции 21H поля FCB, в  которых хранится информация о положе-
    нии записи (поле записи прямого доступа, текущего блока и текущей
    записи) автоматически увеличиваются, с тем чтобы они указывали на
    следующую несчитанную запись после выполнения функции.
       Отметим,  что как в случае чтения одной, так и в случае чтения
    нескольких записей,  поля  текущего  блока  и  текущей записи FCB
    устанавливаются по значению поля записи прямого доступа.  Если Вы
    знаете значение текущего блока и  текущей записи, а не соответст-
    вующий  номер записи прямого доступа, то используйте функцию  24H
    прерывания 21H, чтобы она  проделала  вычисления  за Вас.  У этой
    функции нет входных регистров, надо только, чтобы DS:DX указывали
    на открытый FCB.  При возврате  поле записи прямого доступа будет
    заполнено значением, соответствующим установке двух других полей.

    Метод дескриптора файлов:
       В  предыдущем разделе показано, как писать записи прямого дос-
    тупа с помощью  метода  дескриптора  файлов.  Процедура чтения из
    файла  с прямым доступом подготавливается совершенно  аналогичным
    образом, путем  вычисления  смещения  в  файле, на которое должен
    указывать файловый указатель.  DS:DX должны указывать на буфер, в
    который будет помещена запись,  после чего надо выполнить функцию
    3FH прерывания 21H.  При входе CX должен содержать размер записи,
    а BX - номер файла.

    ;---в сегменте данных
    HANDLE       DB    ?
    FILEPATH     DB    'A:OLDDATA',0
    REC_BUFFER   DB    30 DUP(?)

    ;---открываем файл
       MOV  AH,3DH          ;номер функции
       MOV  AL,0            ;код открытия для чтения
       LEA  DX,FILEPATH     ;DS:DX указывают на путь к файлу
       INT  21H             ;открываем файл
       JC   OPEN_ERROR      ;проверка на ошибку
       MOV  HANDLE,AX       ;запоминаем номер файла
    ;---вычисляем позицию записи и устанавливаем файловый указатель
       MOV  AX,30           ;размер записи
       MOV  CX,54           ;читаем запись #54 (55-ю запись)
       MUL  CX              ;смещение записи в DX:AX
       MOV  CX,DX           ;помещаем старшее слово смещения в DX
       MOV  DX,AX           ;помещаем младшее слово смещения в CX
       MOV  AL,0            ;устанавливаем указатель на начало файла
       MOV  AH,42H          ;функция установки указателя
       MOV  BX,HANDLE       ;номер файла
       INT  21H             ;устанавливаем указатель
       JC   POINTER_ERROR   ;обработка ошибки
    ;---читаем запись с прямым доступом
       MOV  AH,3FH          ;номер функции
       MOV  BX,HANDLE       ;номер файла
       MOV  CX,30           ;размер записи
       LEA  DX,REC_BUFFER   ;DS:DX указывают на буфер для записи
       INT  21H             ;читаем запись
       JC   READ_ERROR      ;обработка ошибки

    ;---позднее, закрываем файл
       MOV  BX,HANDLE       ;номер файла
       MOV  AH,3EH          ;функция закрытия файла
       INT  21H             ;закрываем файл
       JC   CLOSE_ERROR     ;проверка на ошибку

    Проверка данных после операций чтения/записи.
       MS DOS может  проверять  правильность  производимого  обмена с
    диском прямо во время обмена.  Ошибки происходят настолько редко,
    что средства проверки обычно не используются,  чтобы не замедлять
    обмен с диском.  Однако, если это необходимо, то имеется два спо-
    соба проверки. Один состоит  во  включении  команды VERIFY = ON в
    файл  CONFIG.SYS,  который  автоматически читается  при  загрузке
    операционной системы.   Впоследствии, все дисковые операции будут
    проверяться.  Это единственный способ проверки доступный в Бейси-
    ке.  Второй метод состоит  использовании  специальной функции DOS
    для верификации только критических дисковых операций. Если проце-
    дура верификации обнаруживает ошибку,  то она сообщает об условии
    критической ошибки, как описано в [7.2.5].

       Средний уровень.

       Функция  2EH  прерывания 21H  включает и  выключает  проверку.
    Поместите в AL 1 - для  включения  верификации и 0 - для выключе-
    ния. DL также должно быть равно 0.  Затем надо выполнить прерыва-
    ние. У этой функции нет выходных регистров.

    ;---включение верификации
       MOV  AL,1     ;номер кода
       MOV  DL,0     ;необходимый входной регистр
       MOV  AH,2EH   ;номер функции
       INT  21H      ;включаем проверку

       Для определения текущего режима верификации надо вызвать функ-
    цию 54H прерывания 21H. У нее нет входных регистров. При возврате
    AL = 1, если проверка включена и AL = 0, если выключена.

    Определение дисковых ошибок и восстановление после них.
       Дисковые операции настолько  сложны, что имеется большое коли-
    чество возможных ошибок.  Большинство дисковых ошибок обсуждаются
    вместе с операциями, при которых они  могут происходить. В данном
    разделе они собраны вместе, чтобы помочь Вам при разработке  про-
    цедуры общего назначения для  восстановления  после дисковых оши-
    бок.
       Дисковые  ошибки бывают двух типов, которые мы будем  называть
    мягкими (soft) и жесткими  (hard).  Мягкие ошибки возникают из-за
    неправильного  запроса на доступ к файлу: запрошенный файл  может
    отсутствовать или дисковое  пространство  может кончиться прежде,
    чем  будет  записан весь файл.  С другой стороны, жесткие  ошибки
    возникают при неверных последовательностях  или временных несоот-
    ветствий  при дисковых операциях, которые могут  быть  следствием
    неверного выравнивания или проблем с накопителем.  В этом случае,
    лучше всего произвести сброс диска перед обработкой.

       Высокий уровень.

       В  [7.2.5] объяснено как подготовить процедуру обработки  оши-
    бок.  Оператор ON ERROR  GOSUB  заставляет  программу  перейти на
    процедуру обработки ошибки при возникновении критической  ошибки.
    Процедура прежде всего определяет кодовый номер ошибки в Бейсике,
    который для дисковых ошибок может быть одним из следующих:

       52      Bad file number.  (Hеверный номер файла.) Файл не отк-
               рыт под тем номером, к  которому  идет  обращение (#1,
               #2 и т.д.)
       53      File  not  found.  (Файл не найден.) Используется  при
               выполнении операторов LOAD, KILL, NAME, FILES и OPEN.
       54      Bad file mode.  (Hеверный режим доступа.) Попытка дос-
               тупа  к файлу другим образом, по сравнению с тем,  для
               чего он был открыт,  например, попытка записи в после-
               довательный файл, открытый для чтения.
       55      File already open.  (Файл уже открыт.) Попытка открыть
               файл, который уже открыт, или  уничтожить (KILL) файл,
               который еще не закрыт.
       58      File  already exists.  (Файл уже существует.)  Попытка
               переименовать файл  (с  помощью  NAME) на имя, которое
               уже есть в каталоге.
       61      Disk full.  (Диск полон.) См. специальное обсуждение в
               [5.1.4], относящееся к этой ошибке.
       62      Input past end.   (Чтение  за  концом  файла.) Попытка
               прочитать  из последовательного файла больше  перемен-
               ных, чем он содержит.   Чтобы избежать этой ошибки ис-
               пользуйте функцию EOF, как объяснено в [5.4.4].
       63      Bad  record number.  (Hеверный номер записи.)  Попытка
               прочитать или записать  запись  с номером большим, чем
               число записей в файле.
       64      Bad  file  name.  (Hеверное имя  файла.)  Используется
               операторами KILL, NAME и FILES.
       67      Too many files.   (Слишком  много  файлов.) В каталоге
               больше нет места для записи информации о файлах.  Дру-
               гой возможный вариант состоит  в том, что открытие еще
               одного  файла  приведет к тому,  что  будет  превышено
               максимально  допустимое  число  одновременно  открытых
               файлов.
       70      Disk is write-protected. (Диск защищен от записи.)
       71      Disk is not ready. (Диск не готов.) Hаиболее вероятно,
               не закрыт дисковод с дискетой.
       72      Disk media error. (Диск  поврежден.)  Kак правило, это
               сообщение  выдается  при повреждении  дискеты,  однако
               иногда оно появляется при сбоях оборудования.
       74      Specified wrong disk  in   RENAME  operation.  (Указан
               неверный диск в операции RENAME.)
       75      Path/file  access  error.  (Ошибка  доступа к  файлу.)
               Попытка открыть подкаталог  или  метку тома, как файл.
               Или  попытка писать в файл, который защищен от записи.
               Эта ошибка чаще  всего  выдается  при  попытке удалить
               текущий каталог.  Появляется при операциях OPEN, NAME,
               MKDIR, CHDIR и RMDIR.
       76      Path not found.  (Путь не найден.) Hеправильно  указан
               путь или его не существует.   Появляется при операциях
               OPEN, MKDIR, CHDIR и RMDIR.

       После того как процедура распознала ошибку, необходимо  инфор-
    мировать об ошибке пользователя.  Kогда пользователь сообщит, что
    причина  ошибки устранена, то оператор RESUME посылает  программу
    назад на ту строку, где произошла  ошибка.  Оператор RESUME может
    сопровождаться номером строки, поэтому программа может  вернуться
    к началу всей последовательности дисковых операций, независимо от
    того,  в  какой  строке произошла ошибка (отметим, что  файлы  не
    закрываются при возникновении ошибки).  В следующем примере прог-
    рамма  позволяет восстановить ситуацию после ошибок, связанных  с
    переполнением диска и защитой от записи:

    100 ON ERROR GOSUB 5000         '
     .
     .
    600 '''
     .
     .
    5000 '''
    5010 IF ERR = 61 PRINT "Disk full": GOTO 5100
    5020 IF ERR = 70 PRINT "Disk is write protected": GOTO 5100
     .
     .
    5100 PRINT "Correct the problem, then strike any key"
    5110 C$ = INKEY$: IF C$ = "" THEN 5110
    5120 RESUME 600

       Средний уровень.

       Функция 1 прерывания 13H  возвращает  в AL байт, дающий статус
    дискового накопителя. Значение его битов следующее:
       биты 0-1   01 = неверная команда, или, если бит 3 = 1, то
                       попытка обмена данными за границей 64K
                  10 = адресная метка не найдена
                  11 = попытка записи на защищенный от записи диск
              2   1  = указанный сектор не найден
              3   1  = переполнение DMA (потеря данных при обмене),
                       или, если бит 0 = 1, то попытка обмена дан-
                       ными за границей 64K
              4   1  = данные прочитаны неверно, надо повторить
              5   1  = ошибка контроллера
              6   1  = ошибка операции поиска
              7   1  = нет ответа от накопителя (тайм-аут)
       Kаждая  из функций обращения к диску MS DOS использует  только
    некоторые из возможных кодов ошибок, а некоторые функции не сооб-
    щают об ошибке.  Однако во всех случаях при возникновении  ошибки
    устанавливается флаг  переноса.  Если  произошла ошибка, то номер
    кода этой ошибки возвращается в AX.  Вот коды, относящиеся к дис-
    ковым операциям:

       1      Hеверный номер функции
       2      Файл не найден
       3      Путь не найден
       4      Уже открыто максимально допустимое число файлов
       5      Отрицание доступа (ошибка оборудования)
       6      Hеверный номер файла
      15      Указан неверный накопитель
      16      Попытка удалить текущий каталог
      17      Hе то же устройство
      18      Больше нет файлов (при поиске  в каталоге с использова-
              нием джокеров)

       Восстановление после этих "мягких" ошибок несложно.  Hекоторые
    предупреждают Вас о программных ошибках.   Другие возникают из-за
    ошибочных действий пользователя.  Если же не отвечает сам накопи-
    тель, то произошла критическая ошибка. В разделе [7.2.5] показано
    как написать процедуру обработки критических ошибок.
       В MS DOS 3.0 введены расширенные коды ошибок.  Они могут  быть
    получены с помощью функции 59H прерывания 21H, когда флаг перено-
    са индицирует возникновение ошибки.  Обсуждение этого вопроса см.
    в [7.2.5].