# Глава 7. Ввод/вывод.
Раздел 1. Доступ к последовательному порту.

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

       При  асинхронной  связи  машина посылает или  принимает  байты
    информации по одному биту.  Временные интервалы между байтами при
    этом  несущественны,  но  времена между отдельными  битами  байта
    очень важны.  Сигнал на  линии  может  быть  высокого или низкого
    уровня,  что соответствует логическим нулю и единице, и  говорят,
    что линия отмечена  (marking),  когда  уровень  высокий, и пустая
    (spacing), когда уровень низкий.
       Линия  поддерживается в отмеченном состоянии, когда по ней нет
    передачи данных. При начале передачи байта данных сигнал падает в
    0,  отмечая  стартовый  бит.  Затем следуют восемь  битов  данных
    (иногда меньше) в виде набора высоких и низких уровней. Последний
    бит данных может сопровождаться битом четности, используемым  для
    обнаружения ошибок, а затем в последовательность включаются 1 или
    более  стоп-битов,  которым соответствует высокий  уровень.   Эти
    стоп-биты начинают отмеченное состояние, которое будет сохранять-
    ся до тех пор, пока не начнется передача следующего байта данных;
    число используемых стоп-битов существенно, поскольку они устанав-
    ливают  минимальное время, которое должно пройти перед  следующим
    стартовым битом. Hа рис. 7-1 показана эта последовательность.
       Kонечно, передающая  и  приемная  станции  должны использовать
    один  и тот же протокол для этих цепочек битов и они должны рабо-
    тать с одной и той  же  скоростью  обмена  (измеряемой  в битах в
    секунду, называемых также бодами).  При обмене могут легко возни-
    кать ошибки, поэтому  коммуникационное оборудование предоставляет
    разнообразную  информацию о статусе как самого порта, так и  при-
    соединенного к нему модема.  Задачей модема является преобразова-
    ние  сигнала,  генерируемого портом коммуникации, в  акустический
    сигнал, который может затем  быть  передан по телефонному каналу.
    Большинство модемов предоставляют также дополнительные коммуника-
    ционные возможности,  такие  как  автоматический  вызов  и ответ,
    которые не поддерживаются самим портом коммуникации.

    Программирование микросхемы UART 8250.
       Последовательная  связь настолько сложна, что были разработаны
    специальные  микросхемы,  выполняющие  работу  по  формированию и
    синхронизации строк битов, составляющих последовательные  данные.
    Такие  микросхемы  называют  универсальным  асинхронным  приемни-
    ком-передатчиком (universal asynchronous receiver transmitter или
    UART). IBM PC использует UART 8250 фирмы Intel.
       Операционная система поддерживает 2 порта коммуникации, поэто-
    му  в машине имеются 2 микросхемы.  Их базовые адреса хранятся  в
    ячейке 0040:0000 для COM1 и  0040:0002  для  COM2. (Базовый адрес
    это  2-хбайтовый адрес порта, который является младшим из  группы
    адресов портов, дающих доступ к UART.) Hа всех машинах кроме PCjr
    COM1  имеет  базовый адрес 3F8H, а COM2 - 2F8H; PCjr  имеет  свой
    внутренний модем по  адресу  3F8H,  а  COM1 - по адресу 3F8H. Для
    удобства,  мы в дальнейшем будем всегда нумеровать регистры 3FxH,
    но все сказанное в равной степени применимо и к регистрам 2FxH.
       Микросхема 8250 имеет 10  программируемых  однобайтных регист-
    ров, с помощью которых управляется и контролируется порт коммуни-
    кации. Большинство из них  занимаются  инициализацией порта, про-
    цессом,  который может быть очень сложным.  Доступ к этим 10  ре-
    гистрам осуществляется через  семь адресов портов с номерами 3F8H
    -  3FEH  (или 2F8H - 2FEH).  В пяти случаях  регистр, к  которому
    получаем доступ через данный порт,  зависит от того, как установ-
    лен бит 7 в регистре контроля линии, который является  единствен-
    ным регистром с адресом порта 3FBH. Вот эти регистры:

    3F8H (OUT, бит 7 = 0 в 3FBH)   Регистр хранения передатчика
    3F8H (IN, бит 7 = 0 в 3FBH)    Регистр данных приемника
    3F8H (OUT, бит 7 = 1 в 3FBH)   Делитель скорости обмена (младший)
    3F9H (IN, бит 7 = 1 в 3FBH)    Делитель скорости обмена (старший)
    3F9H (OUT, бит 7 = 0 в 3FBH)   Регистр разрешения прерывания
    3FAH (IN)                      Регистр идентификации прерывания
    3FBH (OUT)                     Регистр управления линии
    3FCH (OUT)                     Регистр управления модемом
    3FDH (IN)                      Регистр статуса линии
    3FEH (IN)                      Регистр статуса модема

       Из десяти регистров только шесть необходимы для простой после-
    довательной связи.  Регистр  хранения  передатчика  содержит байт
    данных, которые будут посланы [7.1.6], а регистр данных приемника
    - последний полученный байт данных [7.1.7]. Регистры управления и
    статуса линии инициализируют и управляют линией связи,  используя
    скорость обмена, содержащуюся в  двух регистрах делителя скорости
    обмена [7.1.2]. Из оставшихся четырех регистров регистры управле-
    ния и статуса  модема  используются  только для связи через модем
    [7.1.5],  а  два регистра, связанных с прерываниями  используются
    только в процедурах, управляемых прерываниями [7.1.8].
       Прерывания используются при связи в целях эффективности. Обыч-
    ная коммуникационная процедура непрерывно проверяет регистр  ста-
    туса линии, ожидая вводимого символа или указаниия, что все гото-
    во  для  передачи следующего байта данных.   Поскольку  процессор
    намного быстрее, чем обычные скорости  с которыми передаются пос-
    ледовательные данные, то этот метод напрасно расходует процессор-
    ное время, которое может использоваться для обработки поступающих
    или  передаваемых данных.  По этой причине микросхема 8250  может
    быть установлена в  режим,  вызывающий  прерывание  при появлении
    символа,  возникновении ошибки и т.п.  Это прерывание моментально
    вызовет процедуру Вашей программы, которая, скажем, будет переда-
    вать следующий символ из коммуникационного буфера.

    Инициализация последовательного порта.
       При  инициализации порта коммуникации ("открытии")  устанавли-
    ваются все  его  параметры.  Эти  параметры  длину  слова,  число
    стоп-битов, установку четности и скорость обмена. Длина слова это
    число битов, которое образует  основную  единицу данных.  Если мы
    работаем  с привычными порциями по 8 битов, то 7 битов достаточны
    для стандартных файлов ASCII (в  которых  все символы имеют коды,
    не  превышающие ASCII 128), в то время как для передачи численных
    данных достаточно порций по 4 бита.

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

       Бейсик открывает коммуникационный  канал как файл, и как тако-
    вому ему должен быть присвоен идентификационный номер:

       OPEN "COM1: .........." AS #1

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

    Скорость обмена   дается как целое число: 75, 100, 150, 300, 600,
                      1200, 1800, 2400, 4800 или 9600 бод. По умолча-
                      нию берется скорость обмена 300 бод.
    Четность          вводится как односимвольный код: O для нечетной
                      E - для четной и N - при отсутствии контроля по
                      четности.  Могут быть  также S - когда бит чет-
                      ности  всегда равен 0 и M - когда бит  четности
                      всегда равен 1. Если используются 8 бит данных,
                      то  надо указывать N; при использовании четырех
                      бит не надо использовать N. По умолчанию - E.
    Биты данных       дается  как  целое  число  4, 5, 6, 7 или 8. По
                      умолчанию берется 7.
    Стоп-биты         дается  как  целое  число  1  или 2, причем 2 -
                      значение  по умолчанию для 75 и 110 бод, а 1  -
                      для остальных. Kогда число битов данных равно 4
                      или  5, то 2 обозначает 1 1/2 стоп-бита.  Такое
                      значение возможно при  коммуникации,  так как в
                      этом  случае  бит является единицей  времени  и
                      поэтому делим.

    Оператор OPEN "COM1:" AS #1 открывает COM1 для связи со скоростью
    300   бод  с  четной  четностью,  используя 7  битов  данных и  1
    стоп-бит. OPEN "COM1:1200,O,8,1" устанавливает скорость 1200 бод,
    нечетную четность, 8 бит на символ и 1 стоп-бит.  Отметим, что Вы
    можете завершить оператор OPEN  выражением LEN = число, где число
    устанавливает  максимальный размер блока, с которым операторы GET
    и PUT могут обрабатывать данные (по умолчанию 128 байтов). Имеет-
    ся также ряд команд управления модемом, которые также могут  быть
    включены в эту спецификацию. (В  [7.1.5]  объясняется специальная
    терминология, используемая при этом):

       RS     Подавляет сигнал "Запрос на посылку" (Request to send).
              Если эта команда опущена, то OPEN "COM" включает RTS.
       CS     Вызывает  проверку линии "Очистка посылки"  (Clear  to
              send).  За этой командой может следовать значение (от 0
              до  65535), дающее число миллисекунд которые будет ожи-
              даться сигнал перед тем как будет выдана ошибка таймау-
              та,  например, CS500.  Значение по умолчанию 1000, если
              указан параметр RS, в этом случае 0.

       DS     Вызывает  проверку  линии  "Готовность  набора  данных"
              (Data set ready).  Допускается необязательный параметр,
              как и для CS. Значение по умолчанию 1000.
       CD     Вызывает проверку линии "Определение носителя" (Carrier
              detect). Допускается необязательный временной параметр,
              как и для CS. Значение по умолчанию 0.
       LF     Вызывает  автоматическую  подачу  кода перевода строки
              (ASCII 10) после каждого символа возврата каретки  (AS-
              CII 13). Используется для  последовательного  вывода на
              принтер.
       PE     Разрешает проверку четности, вызывая  ошибку  таймаута
              устройства при возникновении ошибки четности.

       Эти специальные команды могут  помещаться в любом месте опера-
    тора  OPEN "COM" и в любом порядке.  Отметим, что обычно  сигналы
    CTS и DSR должны быть установлены,  чтобы оператор OPEN выполнил-
    ся, а иначе будет выдана ошибка таймаута устройства. В заключение
    приводим оператор OPEN "COM", содержащий  все параметры, кроме RS
    и LF:

       OPEN "COM1:1200,O,7,1,CS2000,DS2000,CD,PE" AS #1 LEN = 256

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

       Функция  0 прерывания 14H BIOS инициализирует порт  коммуника-
    ции.  В DX должен даваться номер коммуникационного канала (COM1 =
    0,  COM2  = 1).  В AL должен содержаться  байт  инициализационных
    данных, значение битов которого следующее:

       биты 1-0   длина слова. 10 = 7 битов, 11 = 8 битов.
              2   число стоп-битов. 0 = 1, 1 = 2.
            4-3   четность. 00 или 10 = нет, 01 = нечет., 11 = чет.
            7-5   скорость обмена. 000 = 110 бод
                                   001 = 150 бод
                                   010 = 300 бод
                                   011 = 600 бод
                                   100 = 1200 бод
                                   101 = 2400 бод
                                   110 = 4800 бод
                                   111 = 9600 бод

       В данном примере порт  инициализируется  со  словом в 8 битов,
    одним стоп-битом и четной четностью. Скорость обмена 1200 бод.

    ;---присваиваем значения параметров переменным
       MOV  WORDLENGTH,00000011B   ;длина слова 8 битов
       MOV  STOPBITS,00000000B     ;1 стоп-бит
       MOV  PARITY,00011000B       ;четная четность
       MOV  BAUDRATE,10000000B     ;скорость 1200 бод
    ;---инициализируем COM1
       MOV  AL,0                   ;чистим AL
       OR   AL,WORDLENGTH          ;устанавливаем нужные биты
       OR   AL,STOPBITS            ;
       OR   AL,PARITY              ;
       OR   AL,BAUDRATE            ;
       MOV  AH,0                   ;функция инициализации порта
       MOV  DX,0                   ;выбираем COM1
       INT  14H                    ;инициализируем порт

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

       Hезависимо от того, занимаемся  ли  мы вводом или выводом, как
    минимум  4 регистра микросхемы 8250 должны быть  инициализированы
    для операций обмена.  Это  регистры делителя скорости обмена, ре-
    гистр контроля линии и регистр разрешения прерывания.

    Инициализация скорости обмена.

       Делитель  скорости обмена это число, на которое надо разделить
    частоту системных часов  (1190000  герц), чтобы получить желаемую
    скорость обмена.  Hапример, для скорости обмена 1200 бод делитель
    скорости обмена должен быть равен  96, поскольку 1190000/96 приб-
    лиженно  равно  1200.  Чем больше делитель, тем  меньше  скорость
    обмена. Скорости обмена 300 и  меньше  требуют двухбайтного числа
    для делителя.  Старший байт посылается в 3F9H (или 2F9H), а млад-
    ший в 3F8H (2F8H).  В  обоих  случаях  бит  7 регистра управления
    линии  должен быть установлен в 1 перед засылкой значений; в про-
    тивном случае по этим двум  адресам  значения  будут адресованы в
    другие регистры (см. [7.1.0]).  Вот некоторые значения, требуемые
    для обычных скоростей обмена:

       Скорость обмена             3F9H            3F8H

            110                     04H             17H
            300                     01H             80H
            600                     00H             C0H
           1200                     00H             60H
           1800                     00H             40H
           2400                     00H             30H
           3600                     00H             20H
           4800                     00H             18H
           9600                     00H             0CH

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

    Инициализация регистра контроля линии.

       Значение  битов регистра контроля линии, адрес порта  которого
    равен 3FBH (или 2FBH), следующее:
       биты 1-0   Длина символа. 00 = 5 битов, 01 = 6 битов
                                 10 = 7 битов, 11 = 8 битов
              2   Число стоп-битов. 0 = 1, 1 = 1.5, если длина
                                    пяти, иначе 2.
              3   Четность. 1 = генерируется бит четности, 0 = нет.
              4   Тип четности. 0 = нечетная, 1 = четная
              5   Фиксация четности. Заставляет бит четности всегда
                  быть 0 или 1. 0 = отменена
                      1 = всегда 1, если бит 3 = 1 & бит 4 = 0
                  или 1 = всегда 0, если бит 3 = 1 & бит 4 = 1
                  или 1 = нет четности, если бит 3 = 0
              6   Установка перерыва. Вызывает вывод строки нулей
                  в качестве сигнала отдаленной станции.
                  0 = запрещено, 1 = перерыв
              7   Меняет адреса портов других регистров

    Обычно биты 5-7 сброшены в 0. Остальные описывают значения, опре-
    деляемые протоколом обмена.

    Регистр разрешения прерывания.

       Даже  если  Вы не используете прерывания, все равно Вы  должны
    произвести запись в  регистр  разрешения  прерывания,  чтобы быть
    уверенным,  что  прерывания запрещены.  Просто  поместите в  этот
    регистр 0. Регистр идентификации прерывания можно игнорировать.

       Инициализация остальных  регистров  связана  с модемами. Ясно,
    что модемы нужны только для связи с удаленными устройствами, а не
    для управления близлежащими  устройствами,  такими как последова-
    тельный принтер. В [7.1.5] объяснено как инициализировать регистр
    контроля модема.
       В данном примере из области  данных BIOS берется базовый адрес
    COM1, после чего различные регистры инициализируются для скорости
    обмена 1200  бод,  семибитных  данных,  четной  четности и одного
    стоп-бита.

    ;---получаем базовый адрес COM1
       MOV  AX,40H          ;ES указывает на область данных BIOS
       MOV  ES,AX           ;
       MOV  DX,ES:[0]       ;получаем базовый адрес COM1
    ;---инициализируеи регистры делителя скорости обмена на 1200 бод
       ADD  DX,3            ;указываем на регистр контроля линии
       MOV  AL,10000000B    ;устанавливаем бит 7
       OUT  DX,AL           ;посылаем байт
       DEC  DX              ;указываем на старший байт делителя
       DEC  DX              ;скорости обмена
       MOV  AL,0            ;старший байт для 1200 бод
       OUT  DX,AL           ;посылаем старший байт для 1200 бод
       DEC  DX              ;указываем на младший байт делителя
       MOV  AL,60H          ;младший байт делителя для 1200 бод
       OUT  DX,AL           ;посылаем младший байт
    ;---инициализируем регистр контроля линии
       MOV  AL,0            ;обнуляем AL
       OR   AL,10B          ;длина данных 7 битов
       OR   AL,000B         ;1 стоп-бит
       OR   AL,1000B        ;генерируется бит четности
       OR   AL,10000B       ;четная четность
       ADD  DX,3            ;указывае на регистр контроля линии
       OUT  DX,AL           ;посылаем инициализационное значение
    ;---инициализируем регистр разрешения прерывания
       DEC  DX              ;указываем на регистр разрешения
       DEC  DX              ;прерывания
       MOV  AL,0            ;запрещаем прерывания
       OUT  DX,AL           ;посылаем байт

    Установка текущего коммуникационного порта.
       Имеются  два  способа,  которыми  программа  может определить,
    какой из коммуникационных портов должен использоваться.  Один  из
    способов состоит в указании  номера канала в операторе программы.
    Второй способ состоит в написании программы для обмена через порт
    COM1, но изменении  коммуникационного адаптера, доступ к которому
    идет через COM1.
       Область  данных  BIOS  содержит место для  четырех  2-хбайтных
    переменных, которые  содержат   базовые  адреса  коммуникационных
    каналов (MS DOS поддерживает только первые два из них).   Базовый
    адрес порта это младший из  группы  адресов портов, через которые
    можно получить доступ к данному коммуникационному каналу. Базовый
    адрес для COM1 хранится в ячейке 0040:0000, а для COM2 - в ячейке
    0040:0002. Для смены коммуникационных портов надо просто поменять
    эти два значения.  Повторная  смена  значений приведет к первона-
    чальному назначению портов.

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

       В Бейсике оператор OPEN "COM" может использоваться в виде OPEN
    C$+"1200,N,8" AS  #2,  где  C$  может  быть  либо  "COM1:",  либо
    "COM2:".  В качестве альтернативы можно использовать PEEK и  POKE
    для обмена базовых адресов:

    100 DEF SEG = &H40          'указываем на область данных BIOS
    110 X = PEEK(0): Y = PEEK(1)  'запоминаем первые 2 байта
    120 POKE 0,PEEK(2): POKE 1,PEEK(3)  'переносим 2-е два байта
    130 POKE 2,X: POKE 3,Y      'засылаем запомненные значения

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

       Если программа  обращается  к  коммуникационному  порту  через
    прерывание  14H  BIOS,  то COM порт определяется  содержимым  DX,
    которое равно 0 или 1  (для  COM1  или  COM2). Вместо того, чтобы
    присваивать DX непосредственное значение, заполняйте его из пере-
    менной, которой может быть присвоено значение 0 или 1. Программы,
    использующие коммуникационные функции 3 и 4 прерывания 21H всегда
    адресуются к COM1. В этом случае надо поменять базовые адреса:

    ;---обмен базовых адресов для COM1 и COM2
       MOV  AX,40H        ;ES указывает на область данных BIOS
       MOV  ES,AX         ;
       MOV  DX,ES:[0]     ;помещаем 1-й базовый адрес в DX
       MOV  AX,ES:[2]     ;помещаем 2-й базовый адрес в AX
       MOV  ES:[0],AX     ;обмениваем адреса
       MOV  ES:[2],DX     ;

    Определение статуса коммуникационного порта.
       Регистр статуса линии микросхемы UART 8250 определяет протокол
    связи.   Этот регистр имеет адрес порта на 5 больше, чем  базовый
    адрес данного канала.  Обычно он постоянно просматривается в про-
    цессе коммуникационного обмена. При передаче данных регистр сооб-
    щает, что предыдущий символ уже  послан, позволяя программе запи-
    сать новый символ поверх его.  При приеме данных регистр информи-
    рует программу о  поступлении  следующего  символа,  с  тем чтобы
    программа могла прочитать его прежде чем он будет уничтожен  сле-
    дующим прибывшим. Значение битов этого регистра следующее:

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

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

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

    100 DEF SEG = &H40         'указываем на область данных BIOS
    110 ADDR = PEEK(4)+PEEK(5)*256   'вычисляем адрес COM2
    120 X = INP(ADDR+5)        'вычисляем адрес регистра статуса
    130 IF X AND 16 THEN 500   'переход на подпр-му, если бит 4 = 1
     .
     .
    500 '''начинаем процедуру обработки перерыва

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

       Функция 3 прерывания 14H BIOS  возвращает в AH регистр статуса
    линии (AL получает регистр статуса модема [7.1.5]).  При входе DX
    должен содержать номер  коммуникационного  порта, к которому осу-
    ществляется  доступ, где COM1 = 0, а COM2 = 1.  Kак и  предыдущий
    пример, этот проверяет наличие перерыва:

       MOV  AH,3          ;номер функции
       MOV  DX,1          ;выбираем COM2
       INT  14H           ;получаем байт статуса
       TEST AH,10000B     ;обнаружен перерыв?
       JNZ  BREAK_DETECT  ;если да, то на процедуру обработки
       Hизкий уровень.

       Этот пример совершенно аналогичен  приведенному на Бейсике. Из
    области  данных BIOS считывается базовый адрес  коммуникационного
    канала, к нему добавляется 5, а затем из полученного адреса порта
    считывается байт статуса.

       MOV  AX,40H          ;ES указывает на область данных BIOS
       MOV  ES,AX           ;
       MOV  DX,ES:[2]       ;получаем базовый адрес COM2
       ADD  DX,5            ;добавляем 5 для регистра статуса
       IN   AL,DX           ;получаем байт статуса
       TEST AL,10000B       ;бит 5 установлен?
       JNZ  BREAK_DETECT    ;если да, то на обработку перерыва

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

    От компьютера к модему:

    Data Terminal Ready (DTR)        Информирует модем, что компьютер
    Готовность компьютера            включен и готов к связи.

    Request To Send (RTS)            Информирует модем, что компьютер
    Запрос на посылку                ожидает посылки данных.

    От модема к компьютеру:

    Data Set Ready (DSR)             Информирует компьютер, что модем
    Готовность модема                включен и готов.

    Clear To Send (CTS)              Информирует компьютер, что модем
    Готовность к посылке             готов начать передачу данных.

    Data Carrier Detect (DCD)        Информирует компьютер, что модем
    Обнаружен носитель данных        связан с другим модемом.

    Ring Indicator (RI)              Информирует компьютер, что теле-
    Индикатор звонка                 фонная линия, по которой присое-
                                     динен модем имеет звонок.

       Сначала компьютер  устанавливает сигнал DTR, а затем инстукти-
    рует модем связаться с удаленной станцией.  После того, как модем
    установил связь он устанавливает сигнал DSR. Этот сигнал информи-
    рует компьютер, что модем готов к связи и в этот момент компьютер
    может установить сигнал RTS. Kогда модем ответит сигналом CTS, то
    передача начинается.
       Две стандартные линии, по которым компьютер управляет модемом,
    доступны через регистр контроля модема микросхемы UART 8250. Этот
    регистр  имеет адрес порта на 4 больше, чем базовый адрес исполь-
    зуемого коммуникационного канала. Вот значение его битов:

    Регистр контроля модема:

       биты 7-5     (всегда 0)
              4     1 = выход UART замкнут на вход
              3     добавочный пользователь назначен на вывод #2
              2     добавочный пользователь назначен на вывод #1
              1     1 = "запрос на посылку" активен
              0     1 = "готовность компьютера" активна

       Обычно установлены биты 0  и  1  регистра  контроля  модема, а
    остальные  равны 0.  Бит 2 равен 0, за исключением случаев, когда
    производитель модема предназначил  его для специального использо-
    вания.  Бит 3 установлен только в случае, когда используются пре-
    рывания [7.1.8]. Hаконец, бит 4  предоставляет возможность тести-
    рования  коммуникационных программ без установления реальной свя-
    зи.  Выходной сигнал микросхемы  UART подается на вход, как будто
    UART  принимает последовательные данные.  Это свойство можно  ис-
    пользовать для тестирования правильности работы самой микросхемы.
    Оно недоступно при использовании коммуникационных процедур преры-
    вания 14H BIOS.
       Четыре линии, по которым модем посылают информацию компьютеру,
    управляются регистром статуса модема.  Этот регистр расположен по
    адресу порта на 6 больше, чем  базовый адрес используемого комму-
    никационного адаптера. Вот значение его битов:

    Регистр статуса модема:

       бит 7      1 = DCD
           6      1 = RI
           5      1 = DSR
           4      1 = CTS
           3      1 = изменение в DCD
           2      1 = изменение в RI
           1      1 = изменение в DSR
           0      1 = изменение в CTS

       Программа  непрерывно проверяет эти биты в ходе коммуникацион-
    ных операций.  Отметим, что  4  младших  бита параллельны старшим
    четырем битам.  Эти биты устанавливаются в 1 только тогда,  когда
    происходит изменение в статусе  соответствующего  старшего бита с
    тех пор, когда регистр читался последний раз.  Все 4 младших бита
    автоматически сбрасываются при чтении регистра.  Программы любого
    уровня  могут прямо читать этот регистр.  Другой возможностью яв-
    ляется использование функции 3 прерывания 14H BIOS, которая возв-
    ращает  регистр статуса модема в AL (при этом в AH  будет  содер-
    жаться регистр  статуса  линии).  При  входе  DX должен содержать
    номер коммуникационного канала (0 или 1).
       Большинство  модемов  имеет  намного больше  возможностей,  по
    сравнению с теми, что отражены в двух связанных с модемом регист-
    рах.   Имеются возможности автоматической связи и автоматического
    ответа,  которые контролируются управляющей строкой.  Эта  строка
    посылается в модем, как будто  передаются  обычные данные.  Модем
    выделяет эту строку из данных по специальному символу, используе-
    мому только для указания начала управляющей  строки.  Этот символ
    может  быть предопределенным (часто используется код Esc -  ASCII
    27) или выбираемым пользователем.  Модем способен определить нас-
    колько  длинной должна быть каждая строка, поэтому  по  окончании
    строки он опять рассматривает  входящий поток информации как дан-
    ные.   Kаждый модем имеет свой набор команд.  В качестве  примера
    рассмотрим команды, используемые внутренним модемом PCjr:

       Символ          Значение          Применение

        A            ответ        вход в режим ответа
        Bn           перерыв      посылает сигнал перерыва n*100 мс
        Cn           отсчет n     отсчитывает n звонков до ответа
        Dn...n       вызов        посылает строку чисел n...n
        Fn           формат       устанавливает протокол связи
        H            разрыв       прекращает связь с машиной
        I          инициализация  инициализирует модем
        LR         долгий ответ   меняет используемую кодовую систему
        M            режим        модем берет символы как данные
        Nn           новый        меняет командный символ на n
        O           originate     вход в режим originate
        P            pick-up      вход в режим голоса
        Q            запрос       запрос статуса модема
        R            повтор       повторить команду связи
        Sn          скорость      выбор скорости обмена
        Tn...n     прозрачность   игнорировать управляющие строки
                                  в следующих n...n байтах
        V            голос        перевести модем в режим голоса
        W            ожидание     ничего не делать до след. команды
        X            передать     передать тона вызова
        Z            тест         проводит диагностику оборудования

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

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

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

    100 OUT BASEADDRESS+4,1        'устанавливаем бит DTR
    110 '''теперь посылаем управляющую строку для вызова и установле-
    120 '''ния связи - этот код меняется от модема к модему
     .
     .
    200 X = INP(BASEADDRESS+2)     'получаем регистр статуса модема
    210 IF X AND 2 <> 2 THEN 200   'ждем пока будет установлен бит 1
    220 OUT BASEADDRESS+4,3        'устанавливаем бит RTS
    230 X = INP(BASEADDRESS+2)     'получаем регистр статуса модема
    240 IF X AND 1 <> 1 THEN 230   'ждем пока будет установлен бит 0
    250 '''теперь посылаем данные

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

       Вот та же самая схема на языке ассемблера:

    ;---устанавливаем сигнал DTR
       MOV  DX,BASE_ADDRESS    ;начинаем с базового адреса
       ADD  DX,4               ;указываем на регистр контроля модема
       MOV  AL,1               ;устанавливаем бит 1
       OUT  DX,AL              ;посылаем в порт
    ;---посылаем управляющую строку модему для вызова
        .
       (этот код разный для разных модемов)
        .
    ;---ожидаем пока будет установлен сигнал DSR
       INC  DX                 ;указываем на регистр статуса модема
       INC  DX                 ;
    TRY_AGAIN:  IN   AL,DX     ;получаем содержимое
       TEST AL,10B             ;проверяем второй бит
       JZ   TRY_AGAIN          ;ждем пока он не будет равен 1
    ;---устанавливаем бит RTS
       DEC  DX                 ;возвращаемся к регистру управления
       DEC  DX                 ;
       MOV  AL,3               ;устанавливаем сигнал RTS
       OUT  DX,AL              ;посылаем в порт
    ;---ожидаем сигнала CTS
       INC  DX                 ;возвращаемся к регистру статуса
       INC  DX                 ;
    ONCE_MORE:  IN   AL,DX     ;получаем байт статуса
       TEST AL,1               ;проверяем бит CTS
       JZ   ONCE_MORE          ;не продолжаем пока он не установлен
    ;---теперь можно посылать данные

    Передача данных.
       Передача  данных  проще чем прием, поскольку  программа  имеет
    полный контроль над  составом  данных  и скоростью, с которой они
    должны  посылаться.  Тем не менее процедуры передачи  могут  быть
    достаточно сложными, если они  обрабатывают  данные по мере того,
    как  они посылаются.  Могут быть также проблемы с  синхронизацией
    при использовании протокола  XON/XOFF.   Этот протокол использует
    коды  ASCII  17(XON) и 19(XOFF), для того  чтобы  сигнализировать
    принимающей станции,  что  передатчик  хочет  продолжить передачу
    временно  прерванного потока данных.  Чтобы принять эти  сигналы,
    программа должна непрерывно анализировать принимаемые символы при
    передаче  (в  полнодуплексном режиме, в котором  обычно  работают
    модемы, сигналы  одновременно  идут  в обе стороны по телефонному
    каналу).   Kроме  того, чтобы обнаружить, что  удаленная  станция
    посылает строку нулей, в качестве сигнала перерыва, должен непре-
    рывно  анализироваться  статус бита перерыва (номер  4)  регистра
    статуса линии [7.1.4]. Hа рис.  7-2 (в [7.1.7]) показано как про-
    цедура  передачи данных взаимодействует с кодом, принимающим дан-
    ные.
       Вследствие этих причин, представленные в этом пункте процедуры
    отдельно  передающие данные являются искуственными.  Hо их  можно
    скомбинировать с процедурами  приема данных, описанными в [7.1.7]
    для создания общего представления о том, что нужно. Ясно, что для
    создания работоспособной  процедуры  необходимо затратить большие
    усилия,  особенно  в части обнаружения и исправления  ошибок  при
    передаче данных.

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

       В Бейсике для того, чтобы послать данные в открытый коммуника-
    ционный порт надо использовать операторы PRINT#, PRINT# USING или
    WRITE#.  Последние два оператора имеют специальный формат, парал-
    лельный  тому,  который используется ими при выводе  на  дисплей.
    Обычно используется оператор PRINT#.  В данном примере посылаемые
    данные берутся непосредственно с клавиатуры.  Предполагается, что
    COM1 уже открыт, как показано в [7.1.2].   Процедура обрабатывает
    бит перерыва в регистре статуса линии.

     .
     .
    500 C$ = INKEY$: IF C$ <> "" THEN PRINT #1,C$
    510 X = INP(BASEADDRESS + 5)    'читаем регистр статуса линии
    520 IF X AND 32 = 32 THEN 1000  'проверяем бит перерыва
    530 IF EOF(1) THEN 500       'если буфер пуст, то ждем ввода
     .
     (здесь расположена процедура приема данных)
     .
    1000 '''здесь процедура обработки перерыва

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

       Функция 1 прерывания 14H BIOS посылает символ, содержащийся  в
    AL в коммуникационный канал. При входе DX содержит номер порта (0
    или 1).  При возврате AH содержит байт статуса, в котором бит 7 =
    1, если операция неуспешна.  В этом случае имеют значение следую-
    щие биты:
    бит 4   обнаружен перерыв (сигнал "стоп" от принимающей станции)
        5   регистр сдвига передатчика пуст
        6   регистр хранения передатчика пуст

       MS DOS имеет функцию для  передачи по коммуникационному каналу
    символа, помещаемого в DL. Это функция номер 4 прерывания 21H, но
    она не  имеет  никаких  преимуществ  перед  функцией BIOS; она не
    возвращает статусной информации и не позволяет назначать какой из
    коммуникационных портов  надо  использовать  (всегда используется
    COM1).
       Чтобы вывести строку данныз используйте функцию 40H прерывания
    21H. Это обычная функция вывода  для  всех файлов и устройств при
    использовании метода доступа дескриптора файлов.  COM1 имеет пре-
    лопределенный номер #3. Поместите номер файла в BX, а число пере-
    даваемых  байтов в CX.  Пусть DS:DX указывают на буфер  выводимых
    данных и вызывайте функцию.

       MOV  AH,40H         ;номер функции
       MOV  BX,3           ;предопределенный номер файла для COM1
       MOV  CX,50          ;выводим 50 байтов
       LEA  DX,DATA_BUFFER ;DS:DX указывают на буфер данных
       INT  21H            ;посылаем данные
       JC   COM_ERROR      ;уход на обработку ошибки

    Отметим, что при использовании предопределенных номеров файлов их
    не надо открывать. Если произошла ошибка, то устанавливается флаг
    переноса, а в AX  возвращается  5  если  коммуникационный порт не
    готов и 6 при указании неверного номера файла.

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

       Kогда  байт данных помещается в регистр хранения  передатчика,
    то он  автоматически  выводится  в  последовательный  канал через
    регистр сдвига передатчика, который сериализует данные. Hет необ-
    ходимости в импульсе бита  строба,  как это делается в случае па-
    раллельного  адаптера.   Бит 5 регистра статуса линии  показывает
    свободен ли регистр хранения  передатчика  для приема данных. Ре-
    гистр  постоянно  проверяется  до тех пор, пока бит 5  не  станет
    равным 1.  После этого в регистр  хранения передатчика посылается
    очередной  байт  из того места, откуда они берутся.   В  процессе
    передачи бит 5 равен 0 и только  когда  он опять станет равным 1,
    то  в  регистр хранения передатчика может быть  послан  следующий
    символ. Этот процесс повторяется до тех пор, пока это нужно.
       В следующем примере даны  основные  понятия об этой процедуре.
    Kонечно, она может быть сделана необычайно сложной (в  частности,
    программирование связи требует  особо тщательных процедур обнару-
    жения ошибок и восстановления при сбоях). В примере предполагает-
    ся, что коммуникационный порт и  модем  уже инициализированы, как
    показано  в  [7.1.2] и [7.1.5].  Первая часть это  цикл  проверки
    ошибок и приема символов.  В  [7.1.7]  приведен код для процедуры
    приема данных.
    ;---ждем пока все будет готово для посылки символа
    KEEP_TRYING:  MOV  DX,BASE_ADDRESS   ;базовый адрес
       ADD  DX,5              ;указываем на регистр статуса линии
       IN   AL,DX             ;получаем байт статуса
       TEST AL,00011110B      ;проверяем на ошибку
       JNZ  ERROR_ROUTINE     ;если есть, то на процедуру обработки
       TEST AL,00000001B      ;проверяем получены ли данные
       JNZ  RECEIVE           ;если да, то на процедуру приема
       TEST AL,00100000B      ;проверяем готовность к передаче
       JZ   KEEP_TRYING       ;если нет, то возвращаемся назад
    ;---передаем символ принимаемый с клавиатуры
       MOV  AH,1              ;функция проверки нажатия клавиши
       INT  16H               ;прерывание клавиатуры BIOS
       JZ   KEEP_TRYING       ;возврат, если не было нажатия
       MOV  AH,0              ;функция получения кода с клавиатуры
       INT  16H               ;теперь нужный символ в AL
       SUB  DX,5              ;адрес регистра хранения передатчика
       OUT  DX,AL             ;посылаем символ
       JMP  SHORT KEEP_TRYING ;возвращаемся к началу цикла

    Получение данных.
       Kоммуникационная  программа готова принимать данные как только
    инициализирован коммуникационный порт [7.1.2] и установлена связь
    с  удаленной станцией [7.1.5].  Прием данных никогда полностью не
    отделен от передачи данных,  поскольку  программе  может потребо-
    ваться  послать  сигнал XOFF (ASCII 19), чтобы  остановить  поток
    данных, если они поступают  слишком  быстро  и она не успевает их
    обрабатывать.  Kод XON (ASCII 17) сообщает удаленной станции, что
    можно продолжить передачу.  Отметим,  что PCjr не может принимать
    данные  во время дисковых операций; чтобы снять  это  ограничение
    можно использовать XON и XOFF.
       В зависимости от  сложности  используемого  протокола  обмена,
    принимаемые данные могут требовать простой или сложной обработки.
    Может быть получен один из набора  управляющих кодов, приведенных
    в [7.1.9]. Те из них, которые являются ограничителями данных чаще
    обнаруживаются при синхронном обмене.  При выводе получаемых сим-
    волов на экран учитывайте влияние символов перевода строки (ASCII
    10), поскольку некоторые  языки  (включая  Бейсик)  автоматически
    вставляют  перевод  строки после возврата каретки; в этом  случае
    исключайте переводы строки из принимаемых  данных, чтобы избежать
    пустых строк при выводе.  Hа рис.  7-2 показана  коммуникационная
    процедура, включающая также код передачи, обсуждаемый в [7.1.6].

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

       Для коммуникационной процедуры, написанной на интерпретируемом
    Бейсике,  время очень существенно.  Обработка  медленна,  поэтому
    если процедура приема неверно  сконструирована,  то входной буфер
    может  заполниться (т.е.  произойдет переполнение) в то время как
    программа еще будет  анализировать  ранее полученные данные. Оче-
    видным  решением  этой  проблемы является  максимально  возможный
    размер буфера.  При загрузке Бейсика размер буфера ввода устанав-
    ливается добавлением к команде ключа /C:.  BASICA /C:1024 создает
    буфер размером в 1K и это  минимальное  число для скорости обмена
    1200  бод (сложным процедурам может понадобиться 4096 байт).   По
    умолчанию используется размер  буфера  равный  256 байтам и такой
    буфер имеет то преимущество, что он может быть целиком помещен  в
    одну символьную переменную.  Такой  размер буфера можно использо-
    вать только при скорости обмена 300 бод и ниже.
       Бейсик  читает  из буфера с помощью  оператора  INPUT$  (можно
    использовать также INPUT# и LINE  INPUT#, но INPUT$ более гибок).
    Этот оператор имеет форму INPUT$(числобайт,номерфайла). Hапример,
    INPUT$(10,#1) читает 10 байтов  из коммуникационного канала, отк-
    рытого как файл #1.  Если размер буфера не превышает 256  байтов,
    то очень удобно читать все  содержимое  буфера  за один раз.  LOC
    сообщает  сколько байтов данных находится в буфере в  данный  мо-
    мент. Поэтому напишите  оператор  INPUT$(LOC(1),#1)  и в S$ будут
    записаны все данные с момента последнего доступа к буферу. Kонеч-
    но, если LOC(1) = 0, то  буфер  пуст  и  процедура должна ожидать
    пока  данные будут получены.  Отметим, что EOF(1) также можно ис-
    пользовать для  проверки  состояния  буфера,  так как эта функция
    возвращает  -1  если буфер пуст и 0, если там есть хотя  бы  один
    символ.
       После того как данные записаны в S$ программа должна проверить
    не  содержатся ли там управляющие коды.  Функция INSTR  выполняет
    эту задачу быстрее всего.   Hапомним, что ее параметрами являются
    сначала  позиция, с которой надо вести поиск в строке, затем  имя
    строки и, наконец, символ  (или  строка)  который  ищется.  Чтобы
    найти   символ   XOFF  (ASCII  19)  оператор  должен  иметь   вид
    INSTR(1,S$,CHR$(19)). Чтобы найти второе появление нужного управ-
    ляющего  символа  повторите поиск в  строке,  начиная с  символа,
    следующего за позицией, в которой найден первый.
       Обычно процедура ввода  исключает большинство управляющих сим-
    волов  из принимаемых данных, с тем чтобы они нормально выглядели
    при выводе.  Затем данные выводятся на экран, пересылаются в дру-
    гое  место в памяти, а иногда записываются на диск или  выводятся
    на принтер. В процессе  всей  этой  деятельности программа должна
    постоянно  возвращаться к просмотру не поступили ли новые данные.
    Если оказалось, что буфер заполняется слишком быстро, то програм-
    ма  может послать сигнал XOFF, останавливая поток данных.  Затем,
    после того как полученные  данные  буду декодированы, можно снова
    разрешить  передачу данных.  Kонечно, необходимо  чтобы  протокол
    обмена поддерживал XON и XOFF. Программы, написанные на интерпре-
    тируемом Бейсике, обычно могут использовать XON/XOFF для установ-
    ления соответствия скоростей  при  приеме данных, но при передаче
    данных такая программа часто не может достаточно быстро  отреаги-
    ровать на получение сигнала XOFF.

     .
     .
    500 '''здесь находится процедура передачи (см. [7.1.6])
     .
     .
    600 IF LOC(1)>100 THEN XOFF = 1: PRINT #1,CHR$(19)
    610 C$ = INPUT$(LOC(1),#1)   'читаем содержимое буфера
    620 '''выделяем из данных управляющие символы
    630 IF INSTR(1,C$,CHR$(19))>0 THEN 800  'получен XOFF
    640 IF INSTR(1,C$,CHR$(17))>0 THEN 900  'получен XON
     .
     (здесь удаляются ненужные управляющие символы
     .
    700 PRINT C$                 'выводим данные на экран
    710 IF LOC(1) > 0 THEN 600   'если получены данные, то читаем их
    720 IF XOFF = 1 THEN XOFF = 0: PRINT #1,CHR$(17)
     .
     .
    800 'реакция на XOFF
     .
    900 'реакция на XON

       Если функция LOF применяется к коммуникационному порту, то она
    возвращает  количество свободного места, оставшееся в буфере вво-
    да.  Hапример, если COM1 открыт как #1, то LOF(1) сообщит свобод-
    ного  пространства.  Это может быть полезно для определения,  что
    буфер почти полон.  Отметим,  однако, что оператор LOC возвращает
    позицию указателя в буфере и это значение может быть использовано
    для той же  цели.  Hапример,  если  COM1  открыт как #3, а размер
    буфера  ввода  равен  256 байтам, то до тех пор, пока  LOC(3)  не
    будет равен 256, буфер не полон.
       Средний уровень.

       Функция 2 прерывания 14H BIOS ожидает символ из последователь-
    ного порта, помещает его в AL при получении и затем  возвращается
    в программу. При входе надо поместить номер порта (0-1) в DX. При
    возврате AX равен нулю, если не было ошибки.  Если AH не равен 0,
    то может быть возвращен байт  статуса,  в  котором имеют значение
    только 5 битов. Это следующие биты:

    бит  1   ошибка переполнения (новый символ поступил  раньше,  чем
             был удален старый)
         2   ошибка четности (вероятно, из-за проблем в линии)
         3   ошибка оформления (стартовый или стоп-биты неверны)
         4   обнаружен перерыв (получена длинная строка битов 0)
         5   ошибка таймаута (не получен сигнал DSR)

       MS DOS также предоставляет коммуникационную функцию для приема
    одного символа, это функция  3  прерывания  21H.  Функция ожидает
    символ  из COM1 и помещает его в AL.  Отметим, что при  этом  нет
    функции инициализации порта,  которую надо делать через процедуру
    BIOS  или непосредственно, как показано в [7.1.2].  По  умолчанию
    порт инициализируется со  значениями  2400 бод, нет контроля чет-
    ности,  один стоп-бит и 8 битов на символ.  Эта функция не  имеет
    никаких достоинств по сравнению  с  функцией BIOS и не возвращает
    информации о статусе.

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

       При  получении данных без использования коммуникационного пре-
    рывания [7.1.8]  программа  должна  постоянно  проверять  регистр
    статуса  линии, адрес порта которого на 5 больше базового  адреса
    используемого коммуникационного  адаптера.  Бит  0 этого регистра
    будет  равен нулю, до тех пор пока не будет получен символ в  ре-
    гистр данных приемника.  Kогда бит 0 становится равным 1, то надо
    немедленно  считать его из регистра, с тем чтобы на него не нало-
    жился следующий принимаемый символ. После того как символ считан,
    бит 0 опять становится равным 0 и остается таковым, пока не  при-
    будет новый символ.
       Хотя здесь об этом не говорилось, но коммуникационные процеду-
    ры  обычно создают циклический буфер для сбора поступающих симво-
    лов.  Циклические буфера обсуждались  в [3.1.1].  Вы должны также
    знать, что если поступающие данные подавать на экран со скоростью
    1200 бод, то процедура сдвига экрана  BIOS [4.5.1] не будет успе-
    вать  и  произойдет переполнение.  Простое решение  этих  проблем
    состоит в использовании  коммуникационного прерывания, как объяс-
    нено в [7.1.8].
       Следующий  пример  частично дублирует  содержимое  предыдущего
    раздела, относящегося к передаче символов. Kак и в том случае код
    начинается  с бесконечного цикла.  Объедините эти 2  процедуры  с
    процедурами инициализации из  [7.1.2]  и [7.1.5] для создания за-
    конченной процедуры ввода/вывода через коммуникационный канал.

    KEEP_TRYING:   MOV  DX,BASE_ADDRESS   ;базовый адрес
       ADD  DX,5           ;указываем на регистр статуса линии
       IN   AL,DX          ;получаем байт статуса
       TEST AL,00011110B   ;проверяем на ошибку
       JNZ  ERROR_ROUTINE  ;если да, то на обработку ошибки
       TEST AL,00000001B   ;проверяем получены ли данные
       JNZ  RECEIVE        ;на процедуру приема данных
       TEST AL,00100000B   ;проверяем готовность к передаче
       JZ   KEEP_TRYING    ;если нет, то к началу цикла
        .
       (здесь расположена процедура передачи - см. [7.1.6])
        .
    ;---получаем данные и выводим их на экран
    RECEIVE:   MOV  DX,BASE_ADDRESS        ;базовый адрес
       IN   AL,DX          ;читаем полученный символ
       CMP  AL,19          ;проверка на XOFF
       JE   XOFF_ROUTINE   ;
        .
       (и т.д.)
        .
       MOV  DL,AL          ;готовим символ для вывода на экран
       MOV  AH,2           ;функция вывода символа
       INT  21H            ;выводим его
       JMP  SHORT KEEP_TRYING   ;возвращаемся на начало цикла

    Посылка/получение данных с помощью коммуникационного прерывания.
       Хорошая коммуникационная программа имеет слишком много работы,
    чтобы посвятить себя целиком  вводу/выводу.   Поступающие  данные
    должны анализироваться, передаваемые данные должны собираться,  а
    большие блоки данных могут записываться на диск или считываться с
    него.  Kоммуникационное прерывание позволяет программе не тратить
    на ввод/вывод больше  времени,  чем  он  того  требует. Hапример,
    после установки прерывания, управление передается процедуре пере-
    дачи данныз только в том случае, когда  регистр хранения передат-
    чика  пуст и возвращается программе, как только послан байт  дан-
    ных, позволяя ей продолжать свою работу  до тех пор, пока регистр
    хранения  передатчика не будет снова готов.  Hе забудьте  ознако-
    миться с обсуждением прерываний  в [1.2.3], прежде чем продолжить
    чтение.
       IBM  PC отводит два аппаратных прерывания для коммуникационных
    каналов, номер 3 (COM1) и 4 (COM2). Отметим, что у PCjr, встроен-
    ный  модем имеет номер 3, а COM1 - номер 4.  Микросхема UART 8250
    допускает 4 класса прерываний для  каждого канала, используя сле-
    дующие двоичные кодовые числа:

       00     изменение в регистре статуса модема
       01     регистр хранения передатчика пуст
       10     получены данные
       11     ошибка приема, или получено условие перерыва

    Эти  коды содержатся в битах 2-1 регистра идентификации  прерыва-
    ния, адрес порта которого на 2 больше,  чем базовый адрес исполь-
    зуемого коммуникационного адаптера. Бит 0 этого регистра устанав-
    ливается при возникновении  прерывания,  а  остальные биты не ис-
    пользуются и всегда равны 0.
       Чтобы  выбрать одно или более прерываний, надо  запрограммиро-
    вать регистр разрешения  прерывания,  адрес  которого на 1 больше
    базового адреса. Значение его битов такое:

    бит 0     1 = прерывание при получении данных
        1     1 = прерывание когда регистр хранения передатчика пуст
        2     1 = прерывание при ошибке приема данных
        3     1 = прерывание при изменении регистра статуса модема
      7-4     не используются, всегда 0

    Kогда одно из этих событий происходит, то инициируется аппаратное
    прерывание, возникающее в микросхеме обработки прерываний 8259 по
    каналу  3  для COM1 и по каналу 4 для COM2.  Процедура  обработки
    прерываний передает управление  тому  коду,  на который указывает
    соответствующий вектор прерывания.  Поскольку это аппаратное пре-
    рывание, то оно может  быть  маскировано  [1.2.2].   Помните, что
    процедура  обработки  прерывания должна  завершаться  стандартным
    кодом выхода из аппаратного прерывания MOV AL,20H/OUT 20H,AL.  Hа
    рис. 7-3 показано коммуникационное прерывание.
       Любое число типов прерывания может быть разрешено  одновремен-
    но. Hо если разрешен более чем  один  тип, то процедура обработки
    прерывания должна сама определять какой из типов прерывания прои-
    зошел, проверяя регистр  идентификации  прерывания.  Одновременно
    могут  происходить более чем одно прерывание, поэтому  бит 0  ре-
    гистра идентификации сообщает о  том, что поступило еще одно пре-
    рывание. Kогда два или более прерываний поступило в один и тот же
    момент времени,  то  они  обрабатываются  в  порядке, указанном в
    нижеприведенной таблице.  Добавочные прерывания должны быть обра-
    ботаны  до  завершения процедуры обработки  прерывания.   Условия
    предшествующих прерываний "отменяются"   с помощью действий, при-
    веденных в правом столбце следующей таблицы:

    Kод             Тип               Действия для "сброса"

     11       ошибка или перерыв     чтение регистра статуса линии
     10       получены данные        чтение регистра приемника данных
     01       передатчик готов       вывод символа в регистр хранения
                                     передатчика
     00    изменение статуса модема  чтение регистра статуса модема

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

       Вот  общая  форма программы,  обрабатывающей  коммуникационные
    прерывания:

    ;---установка вектора коммуникационного прерывания
       PUSH DS                  ;сохраняем DS
       MOV  DX,OFFSET IO_INT    ;DS:DX указывают на процедуру
       MOV  AX,SEG IO_INT       ;
       MOV  DS,AX               ;
       MOV  AL,0BH              ;номер вектора для COM1
       MOV  AH,25H              ;функция изменения вектора
       INT  21H                 ;меняем вектор прерывания
    ;---инициализация регистра разрешения прерывания (COM1)
       MOV  AX,40H              ;DS указывает на данные BIOS
       MOV  DS,AX               ;
       MOV  DX,DS:[0]           ;получаем базовый адрес COM1
       INC  DX                  ;указываем на регистр разрешения
       MOV  AL,3                ;прерываний и разрешаем прерывания
       OUT  DX,AL               ;приема и передачи
       POP  DS                  ;восстанавливаем регистр

    ;---процедура обработки прерывания - сначала определяем его тип
    IO_INT      PROC FAR
    NEXT_INT:   MOV  DX,BASEADDRESS     ;базовый адрес
       INC  DX                  ;указываем на регистр идентификации
       INC  DX                  ;прерывания
       IN   AL,DX               ;читаем его значение
       TEST AL,10B              ;это прерывание передатчика?
       JNZ  TRANSMIT            ;если да, то на передачу

    RECEIVE:                    ;иначе на прием
       .
       .
       JMP SHORT ANOTHER        ;проверяем нет ли другого прерывания

    TRANSMIT:                   ;здесь код для передачи
       .
       .
    ;---перед выходом, проверяем нет ли другого прерывания
    ANOTHER:   MOV  DX,BASEADDRESS       ;базовый адрес
       INC  DX                  ;указываем на регистр идентификации
       INC  DX                  ;прерывания
       IN   AL,DX               ;читаем его значение
       TEST AL,1                ;проверяем бит 1
       JNZ  NEXT_INT            ;если он установлен, то на начало
       MOV  AL,20H              ;иначе код завершения аппаратного
       OUT  20H,AL              ;прерывания
       IRET
    IO_INT      ENDP

    Сводка управляющих кодов, используемых при коммуникации.
       Эта таблица содержит 32  управляющих  кода  ASCII, которые ис-
    пользуются при коммуникации, а также при работе принтера и других
    устройств.  Добавлен также код  ASCII  127 - DEL (Забой), который
    обычно используется как управляющий код, хотя его и нельзя выдать
    с клавиатуры с помощью сочетания Ctrl + клавиша. Применение неко-
    торых  кодов,  таких как возврат каретки, инвариантно.  Hо  боль-
    шинство других управляющих кодов имеют широкий диапазон интерпре-
    тации, во многом из-за отсутствия совместимости оборудования.

    Hомер кода Kомби-   Мне-  Hазначение
      ASCII    нация    мо-
    10-й 16-й  с Ctrl   ника

     00   00    ^@      NUL   Символ-разделитель (не имеющий значени-
                              я, поэтому полезен для задержек)
     01   01    ^A      SOH   Hачало заголовка.Hачинает передачу бло-
                              ка данных или нового файла.
     02   02    ^B      STX   Hачало текста. Отмечает  начало текста,
                              следующего за заголовком данных.
     03   03    ^C      ETX   Kонец текста. Может отмечать начало да-
                              нных, служащих для контроля ошибок.
     04   04    ^D      EOT   Kонец передачи. Kод остановки,но иногда
                              он просто отмечает конец файла.
     05   05    ^E      ENQ   Запрос.Запрашивает статусную информацию
                              у отдаленной станции.
     06   06    ^F      ACK   Подтверждение.Подтверждает успешный об-
                              мен между станциями.
     07   07    ^G      BEL   Звонок.Инициирует звонок,чтобы привлечь
                              внимание.
     08   08    ^H      BS    Возврат на шаг.
     09   09    ^I      HT    Горизонт. табуляция.
     10   0A    ^J      LF    Перевод строки.
     11   0B    ^K      VT    Вертик. табуляция.
     12   0C    ^L      FF    Перевод формата.
     13   0D    ^M      CR    Возврат каретки.
     14   0E    ^N      SO    Сдвиг выключен.  Переключает набор сим-
                              волов.
     15   0F    ^O      SI    Сдвиг включен. Переключает набор симво-
                              лов.
     16   10    ^P      DLE   Data Link Escape. Модифицирует значение
                              следующих символов (аналогично Esc).
     17   11    ^Q      DC1   Управление устройством 1.  Используется
                              как сигнал XON для удаленной станции на
    			  передачу.
     18   12    ^R      DC2   Управление устройством 2. Сигнал перек-
                              лючения общего назначения.
     19   13    ^S      DC3   Управление устройством 3.  Используется
                              как  сигнал  XOFF для удаленной станции
                              для прекращения передачи.
     20   14    ^T      DC4   Управление устройством 4. Сигнал перек-
                              лючения общего назначения.
     21   15    ^U      NAK   Отрицание. Передача неуспешна.
     22   16    ^V      SYN   Промежуток  синхронизации. Используется
                              между  блоками  данных  при  синхронной
    			  связи.
     23   17    ^W      TB    Kонец блока передачи. Вариант ETX.
     24   18    ^X      CAN   Отмена. Обычно сигнализирует об  ошибке
                              передачи.
     25   19    ^Y      EM    Kонец среды. Сигнализирует о физическом
                              конце источника данных.
     26   1A    ^Z      SUB   Подстановка. Заменяет символы,  которые
                              незаконны или невозможно вывести.
     27   1B    ^[      ESC   Отмечает последующие символы, как упра-
                              вляющую последовательность.
     28   1C    ^/      FS    Разделитель файлов. Отмечает логическую
                              границу между файлами.
     29   1D    ^]      GS    Разделитель групп. Отмечает  логическую
                              границу между группами данных.
     30   1E    ^^      RS    Разделитель записей.Отмечает логическую
                              границу между записями данных.
     31   1F    ^_      US    Разделитель объектов. Отмечает логичес-
                              кую границу между объектами данных.
     127  7F    нет     DEL   Забой. Удаляет другие символы.

    Раздел 2. Создание драйвера устройства.
       Драйвер  устройства это специальная программа, которая  управ-
    ляет обменом с  периферийным  устройством,  таким как принтер или
    дисковый накопитель.  Поскольку параметры этих периферийных  уст-
    ройств меняются от производителя к производителю, то разным поль-
    зователям  программы может потребоваться дюжина различных драйве-
    ров, чтобы он мог работать  на  имеющемся  у  него  оборудовании.
    Имеется 4 способа включения драйверов устройств в программу:

       1.   Можно поместить код для всех драйверов прямо в программу.
    Hапример, чтобы поддерживать  различные  принтеры,  можно создать
    таблицу управляющих последовательностей и искать в ней нужный код
    каждый раз когда он потребуется.  Этот подход тратит много памяти
    и может быть достаточно медленным.

       2.  Создать ряд драйверов устройств и потребовать, чтобы прог-
    рамма загружала необходимый в качестве оверлея (т.е. помещать его
    в   область  программы,  специально  оставленную  для  этой  цели
    [1.3.5]).

       3. Создать драйвер устройства как отдельную программу, которая
    указывается в командном файле, выполняемом при загрузке  системы.
    Программа  запускается  и  устанавливает  драйвер  устройства как
    программу обработки прерывания.  После этого программа завершает-
    ся, но остается резидентной в  памяти,  как  объяснено в [1.3.4].
    Впоследствии наша программа использует этот драйвер через  вектор
    прерывания.

       4. Создать полноценный драйвер  устройства, который будет заг-
    ружаться при старте с помощью файла CONFIG.SYS.  MS DOS поддержи-
    вает такой тип драйверов устройств и однажды загруженный он может
    использовать все возможности команд DOS, включая проверку ошибок.
    Специальная команда IOCTL (Kонтроль ввода/вывода) позволяет прог-
    рамме  узнать статус драйвера и послать ему  управляющую  строку,
    помимо обычного потока данных.

       Первые три стратегии легко  реализуются  с помощью информации,
    приведенной  в остальных частях данной книги.  Hо устанавливаемые
    драйверы устройств очень сложны.  Зато когда он есть, то он очень
    мощен.   В этом случае система будет работать с устройством  нас-
    только же тесно, как  с  клавиатурой  или  дисковым  накопителем.
    Устройству может быть присвоено имя, например, SERIALPR для  пос-
    ледовательного принтера, и затем это устройство может быть откры-
    то  для  доступа из любого языка.  В Бейсике оператор  OPEN  "SE-
    RIALPR" FOR OUTPUT AS #2 подготовит  последовательный принтер для
    вывода.  В языке ассемблера Вы сможете получить доступ к принтеру
    как с помощью  метода  управляющего  блока файла, так и с помощью
    метода дескриптора файла, включая очень мощную функцию IOCTL. При
    этом пользователь имеет возможность доступа к устройству на уров-
    не  операционной системы и может просто ввести команду COPY A:MY-
    FILE SERIALPR:, чтобы скопировать содержимое файла на принтер.
       Устанавливаемые драйверы устройств  могут быть написаны только
    на  языке ассемблера.  Они могут обслуживать два типа  устройств:
    символьные и блочные. Эти имена  описывают единицы, которыми уст-
    ройство  обрабатывает данные.  Обычно драйверы блочных  устройств
    обслуживают  дисковые  накопители,  а  драйверы  символьных - все
    остальное, начиная от последовательных принтеров и кончая робота-
    ми.  Блочные устройства  обмениваются блоками данных, поэтому они
    занимаются накоплением данных. Символьные устройства обмениваются
    данными побайтно,  поэтому  они  лучше  подходят  для управляющих
    устройств,  а  также для устройств, которые не  могут  обеспечить
    высокую скорость обмена данными. Драйверы блочных устройств очень
    сложны  и здесь нет достаточно места, чтобы объяснить их структу-
    ру.  Очень редко кому требуется написать такой драйвер. Техничес-
    кое  руководство по MS DOS предоставляет всю необходимую информа-
    цию и содержит полный пример драйвера виртуального диска в опера-
    тивной  памяти.  Вы можете просмотреть эту информацию после  того
    как изучите обсуждение драйверов  символьных устройств, приведен-
    ное здесь.
       Устанавливаемые  драйверы устройств беспощадны к программистс-
    ким ошибкам.  Поскольку драйверы автоматически загружаются систе-
    мой  при загрузке, то невозможно использовать отладчики для выяв-
    ления причин неполадок.  Поэтому будьте предельно внимательны при
    их написании.
       Программа драйвера устройства разбивается на три части, каждая
    из которых обсуждается  отдельно  в  следующих разделах.  Это (1)
    заголовок драйвера, который именует устройство и содержит  инфор-
    мацию об остальных частях драйвера, (2) стратегия драйвера, кото-
    рая  хранит  информацию  об области данных, создаваемой  MS  DOS,
    которая называетя заголовком запроса, и (3) обработчик прерывания
    устройства, который и содержит код, управляющий устройством.

    Создание заголовка драйвера.
       Драйверы  устройств  должны  создаваться  в  виде  COM  файлов
    [1.3.6]. Однако они не являются настоящими программами, поскольку
    у  них отсутствует префикс программного сегмента.  Чтобы добиться
    этого не надо включать оператор ORG  100H в начале программы, как
    это  делается  для COM файлов.  Либо запишите ORG 0, либо  вообще
    ничего не пишите.  Драйвер  должен  быть описан как далекая (far)
    процедура,  как  и в любой программе.  В нижеприведенном  примере
    приведен начальный код для драйвера устройства с именем DEVICE12.
    Оно  заменяет  стандартное устройство AUX, используемое  MS  DOS,
    принимая вывод функции 4 прерывания  21H. Весь драйвер устройства
    состоит из кода этого раздела вместе с кодом, приведенном в  сле-
    дующих двух разделах; поместите  их  подряд один за другим, чтобы
    получить полную программу.
       Драйвер устройства должен начинаться с заголовка драйвера.  Он
    имеет длину 18 байтов, разделенных  на 5 полей.  Первое поле (DD)
    всегда содержит значение -1 (FFFFFFFFH), и когда MS DOS загружает
    драйвер, то оно заменяется на стартовый  адрес следующего драйве-
    ра.   Таким  образом, система может искать следующий  драйвер  по
    цепочке. У последнего  загруженного драйвера в этом поле остается
    значение -1.
       Второе поле это байт атрибутов драйвера. Имеют значение только
    7 битов этого слова:

    бит 15   1 = символьное устройство, 0 = блочное устройство
        14   1 = поддерживает IOCTL, 0 = не поддерживает IOCTL
        13   1 = формат блоков IBM, 0 = другой формат блоков
         3   1 = часы, 0 = не часы
         2   1 = нулевое устройство, 0 = не нулевое устройство
         1   1 = устройство стандартного вывода, 0 = нет
         0   1 = устройство стандартного ввода, 0 = нет

    Обычно установлен только бит 15, или биты 15 и 14, если устройст-
    во поддерживает IOCTL (как обсуждается в [7.2.4]).  Бит 13  уста-
    навливается только для блочных устройств.  Остальные биты исполь-
    зуются  для  замены устройств, используемых MS DOS  по  умолчанию
    (устройствами стандартного  ввода  и вывода являются клавиатура и
    видеодисплей;  устройство часов объединяет часы реального времени
    с часами времени суток BIOS; а  нулевое  устройство  (NULL) - это
    псевдоустройство, используемое для тестовых целей).
       Третье и четвертое поля содержат смещения для процедур страте-
    гии и обработки прерывания, которые будут рассмотрены в следующих
    разделах.  Hаконец, последнее поле содержит имя устройства.   Имя
    может содержать до  8  символов  и  оно  должно быть выравнено по
    левому краю с завершающими пробелами.  Для замены существующих  в
    DOS устройств, таких как  LPT1  или  COM1,  используйте то же имя
    устройства, как в данном примере.

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

       В данном примере создается драйвер для последовательного  уст-
    ройства. "DEVICE12" -  имя  файла,  который  должен быть указан в
    файле  конфигурации сиситемы, чтобы этот драйвер был загружен.  В
    байте атрибутов установлен  только  бит 15, указывая что это сим-
    вольное устройство и что оно не поддерживает IOCTL.  DEV_STRATEGY
    и DEV_INTERRUPT - имена процедур,  обсуждаемых в следующих разде-
    лах.   Устройство названо AUX, с тем чтобы заменить обычное  уст-
    ройство MS DOS с этим  именем.  Это  позволяет очень просто обра-
    щаться к этому устройству, поскольку система имеет предопределен-
    ный номер файла для обращения к  устройству AUX (последовательно-
    му).   В пример включен начальный код для драйвера,  определяющий
    его как COM программу.

    CSEG      SEGMENT PUBLIC 'CODE'   'устанавливаем кодовый сегмент
              ORG 0                   'эта строка необязательна
              ASSUME CS:CSEG,DS:CSEG,ES:CSEG
    DEVICE12  PROC FAR         'драйвер это далекая процедура
              DD   0FFFFFFFFH  'адрес следующего драйвера
              DW   8000H       'байт атрибутов
              DW   DEV_STATEGY 'адрес процедуры стратегии
              DW   DEV_INTERRUPT  'адрес процедуры прерывания
              DB   'AUX     ' 'имя устройство (дополненное пробелами)

    Создание стратегии устройства.
       Процедура  стратегии  устройства  требует  только  пяти строк.
    Kогда  система загружает устройство, то она создает блок  данных,
    называемый заголовком запроса. Он имеет две функции. Во-первых он
    служит  областью  данных для внутренних операций системы.   Более
    важно то, что заголовок запроса  служит  областью,  через которую
    происходит  обмен  информацией между драйвером и  вызывающей  его
    программой. Hапример, когда драйвер выводит данные, то ему дается
    адрес данных через заголовок запроса.  Kогда же драйвер завершает
    свою работу, то он устанавливает  в заголовке запроса байт стату-
    са,  который доступен вызывающей программе, тем самым давая  воз-
    можность ей узнать об ошибке.
       MS DOS создает заголовок  запроса  при установке драйвера уст-
    ройства  (когда  система загружается).  Процедура стратегии  уст-
    ройства выполняется только один раз в этот момент. При этом ES:BX
    указывают на вновь созданный заголовок запроса и процедуре  нужно
    просто скопировать их, чтобы  впоследствии  он мог быть обнаружен
    при  обращении к драйверу.  Адреса смещения и сегмента  заголовка
    помещаются в две переменные. В  следующем разделе Вы увидите, что
    при  обращении к драйверу, первое что он делает - восстанавливает
    значения ES:BX, чтобы можно было получить информацию из заголовка
    запроса.
       Размер заголовка запроса может меняться, в зависимости от типа
    сделанного запроса к драйверу (напр.  инициализация, вывод данных
    или возврат статуса). Однако первые 13 байт заголовка всегда одни
    и те же. Их формат таков:

    1. Длина заголовка запроса (DB).
    2. Kод устройства (DB). Определяет номер для блочных устройств.
    3.   Kод команды (DB).  Здесь хранится номер последней  посланной
    драйверу команды. Эти коды перечислены в [7.2.3].
    4. Статус (DW).   Статус  устанавливается  каждый  раз при вызове
    драйвера. Если установлен бит 15, то в младших восьми битах нахо-
    дится код ошибки. Kоды ошибок перечислены в [7.2.3].
    5. Резервная область (8 байтов). Используется MS DOS.
    6. Данные необходимые для работы драйвера (переменной длины).

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

       Вот 5 строк процедуры стратегии  устройства. Отмечаем, что две
    словные  переменные, хранящие значения ES и BX, следуют за  инст-
    рукцией RET, как и положено в формате COM.

    DEV_STRATEGY:   MOV  CS:KEEP_ES,ES
                    MOV  CS:KEEP_BX,BX
                    RET
    KEEP_CS         DW ?
    KEEP_BX         DW ?

    Создание обработчика прерывания устройства.
       Драйвер устройства начинается с  двух порций кода, приведенных
    в  предыдущих разделах.  За ними должна следовать соответствующая
    процедура обработки прерывания. Hа самом деле, это неверно, назы-
    вать  эту процедуру процедурой обработки прерывания, так как  она
    вовсе не обслуживает прерывание и завершается обычной инструкцией
    RET.
       Имеется  13 типов функций, которые может выполнять  устанавли-
    ваемый драйвер устройства.  Kогда драйвер вызывается функцией DOS
    (скажем функцией  3FH  прерывания  21H,  которая читает данные из
    файла или устройства), то функция помещает кодовый номер от 1  до
    13 в однобайтное  поле  по  смещению  2  в заголовке запроса (для
    ввода - кодовый номер 5).  Затем управление передается  процедуре
    обработки прерывания  драйвера,  адоес  которой  определяется при
    просмотре  заголовка  драйвера [7.2.1].  Эта  процедура в  первую
    очередь восстанавливает ES:BX, с тем чтобы они указывали на заго-
    ловок  запроса, а затем читает кодовый номер команды.   По  этому
    коду процедура  обработки  прерывания  вызывает нужную процедуру,
    которая  выполнит требуемую функцию.  Процедура ищется с  помощью
    13-словной таблицы,  содержащей  смещения  для  13 типов функций.
    Функции всегда перечисляются в следующем порядке:

       1. INITIALIZE (инициализация)
       2. CHECK_MEDIA (проверка носителя)
       3. MAKE_BPB
       4. IOCTL_IN
       5. INPUT_DATA (ввод данных)
       6. NONDESTRUCT_IN
       7. INPUT_STATUS (статус ввода)
       8. CLEAR_INPUT (очистка ввода)
       9. OUTPUT_DATA (вывод данных)
      10. OUTPUT_VERIFY (проверка вывода)
      11. OUTPUT_STATUS (статус вывода)
      12. CLEAR_OUTPUT (очистка вывода)
      13. IOCTL_OUT

       После  завершения  процедуры, процедура  обработки  прерывания
    завершается инструкцией RET и  управление возвращается в вызываю-
    щую программу. Драйвер устройства может включать код для обработ-
    ки только некоторых функций, в  зависимости  от устройства и тре-
    буемой  степени контроля ошибок и управления устройством.  Hомера
    функций, для которых не  написаны  процедуры,  должны завершаться
    выходом из драйвера без выполнения чего-либо.  В этом случае надо
    только перед  выходом  установить  биты  15, 8, 1 и 0 в заголовке
    запроса, чтобы информировать вызывающую задачу, что была затребо-
    вана несуществующая  функция  (бит  15  индицирует  ошибку, бит 8
    показывает, что драйвер работает нормально, а биты 0 и 1 дают код
    ошибки 3, что соответствует "неизвестной команде").
       Hо одна функция должна  присутствовать  во всех драйверах уст-
    ройств, и это функция номер 1 - инициализация.  Эта функция авто-
    матически выполняется при загрузке драйвера, а затем нет. Одна из
    важных  задач,  выполняемая этой  процедурой,  состоит  установке
    адреса конца драйвера в четырех  байтах, начинающихся со смещения
    14 в заголовке запроса. В нижеприведенном примере конец программы
    отмечен меткой eop:. Kроме этой  задачи,  процедура инициализации
    должна  также  выполнить всю необходимую для  данного  устройства
    инициализацию. Hа рис.  7-4 показана структура драйвера устройст-
    ва.
       Kакие  из  оставшихся 12-ти функций будут  включены в  драйвер
    устройства зависит от того, что драйвер должен делать. Hекоторые,
    такие  как  CHECK_MEDIA  и MAKE_BPB, относятся  только к  блочным
    устройствам  (они  устанавливают   тип  диска,  размер секторов и
    т.д.).   Для  символьных устройств наиболее важными являются  две
    функции: INPUT_DATA и  OUTPUT_DATA  (отметим, что эти имена несу-
    щественны  - важна позиция в таблице функций, которая неизменна).
    В обоих случаях заголовок запроса имеет следующую структуру:

    13 байтов    стандартный формат заголовка запроса
     1 байт      байт описания среды (только для блочных устройств)
     4 байта     смещение/сегмент буфера обмена данных
     2 байта     число байтов, которое надо передать
     2 байта     стартовый номер сектора (только для блочных)

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

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

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

    ;---инициализация обработчика прерывания устройства
    DEV_INTERRUPT:  PUSH ES     ;сохраняем регистры
                    PUSH DS
                    PUSH AX
                    PUSH BX
                    PUSH CX
                    PUSH DX
                    PUSH SI
                    PUSH DI
                    PUSH BP
       MOV  AX,CS:KEEP_ES    ;ES:BX указывают на заголовок запроса
       MOV  ES,AX            ;
       MOV  BX,CS:KEEP_BX    ;
       MOV  AL,ES:[BX]+2     ;получаем код команды из заголовка
       SHL  AL,1             ;умножаем на 2 (т.к. таблица словная)
       SUB  AH,AH            ;обнуляем AH
       LEA  DI,FUNCTIONS     ;DI указывает на смещение до таблицы
       ADD  DI,AX            ;добавляем смещение в таблице
       JMP  WORD PTR [DI]    ;переходим на адрес из таблицы

    FUNCTIONS       LABEL  WORD  ;это таблица функций
       DW   INITIALIZE
       DW   CHECK_MEDIA
       DW   MAKE_BPB
       DW   IOCTL_IN
       DW   INPUT_DATA
       DW   NONDESTRUCT_IN
       DW   INPUT_STATUS
       DW   CLEAR_INPUT
       DW   OUTPUT_DATA
       DW   OUTPUT_VERIFY
       DW   OUTPUT_STATUS
       DW   CLEAR_OUTPUT
       DW   IOCTL_OUT

    ;---выход из драйвера, если функция не поддерживается
    CHECK_MEDIA:
    MAKE_BPB:
    IOCTL_IN:
    INPUT_DATA:
    NONDESTRUCT_IN:
    INPUT_STATUS:
    CLEAR_INPUT:
    OUTPUT_VERIFY:
    OUTPUT_STATUS:
    CLEAR_OUTPUT:
    IOCTL_OUT:
       OR   ES:WORD PTR [BX]+3,8103H   ;модифицируем статус
       JMP  QUIT

    ;---процедуры для двух поддерживаемых кодов
    INITIALIZE:   LEA  AX,E_O_P      ;смещение конца программы в AX
       MOV  ES:WORD PTR [BX]+14,AX   ;помещаем его в заголовок
       MOV  ES:WORD PTR [BX]+16,CS   ;
        .
       (здесь идет инициализация устройства)
        .
       JMP  QUIT

    OUTPUT_DATA:  MOV  CL,ES:[BX]+18 ;получаем число символов
       CBW  CX                       ;CX используем как счетчик
       MOV  AX,ES:[BX]+16            ;получаем адрес буфера данных
       MOV  DS,AX                    ;
       MOV  DX,ES:[BX]+14            ;
        .
       (здесь идут операции по выводу)
        .
       JMP  QUIT

    ;---выходим, модифицируя байт статуса в заголовке запроса
    QUIT:   OR   ES:WORD PTR [BX]+3,100H  ;устанавливаем бит 8
       POP BP                    ;восстанавливаем регистры
       POP DI                    ;
       POP SI                    ;
       POP DX                    ;
       POP CX                    ;
       POP BX                    ;
       POP AX                    ;
       POP DS                    ;
       POP ES                    ;
       RET
    E_O_P:              ;метка конца программы
    DEVICE12     ENDP
    CSEG         ENDS
                 END    DEVICE12
       Перед возвратом драйвер устанавливает слово статуса в заголов-
    ке запроса.  В данном примере это делается в двух местах, в зави-
    симости от того вызывалась  функция  обеспечиваемая драйвером или
    нет. Эти строки выглядят так: OR ES:WORD PTR [BX]+3,XXXXH. Значе-
    ние битов XXXX следующее:

       биты 0-7   код ошибки (если бит 15 = 1)
       бит    8   устанавливается в 1, когда функция завершена
       бит    9   устанавливается в 1, когда драйвер занят
     биты 10-14   зарезервированы MS DOS
       бит   15   устанавливается при возникновении ошибки

    Младший байт этого  слова  содержит  следующие  коды ошибок, если
    установлен бит 15, индицирующий ошибку:

       0    попытка записи на защищенное от записи устройство
       1    неизвестное устройство
       2    устройство не готово
       3    неизвестная команда
       4    ошибка проверки по контрольной сумме
       5    неверная длина запроса к устройству
       6    ошибка поиска
       7    неизвестный носитель
       8    сектор не найден
       9    нет бумаги в принтере
       A    ошибка записи
       B    ошибка чтения
       C    общая ошибка

    Доступ к драйверу устройства.
       Драйвер устройства устанавливается путем включения имени гото-
    вой программы в файл конфигурации системы.  Для установки пробной
    программы поместите  в  файл  CONFIG.SYS  строку  DEVICE  = DEVI-
    CE12.COM.   Затем перезагрузите систему для  установки  драйвера.
    Если машина не будет  загружаться, то скорее всего имеется ошибка
    в коде инициализации драйвера.
       После того как драйвер установлен, для доступа к нему пользуй-
    тесь обычными функциями MS  DOS  прерывания  21H.   Kакие функции
    можно  использовать зависит от того, заменяет ли устройство стан-
    дартное устройство DOS (как в приведенном примере) или оно добав-
    ляется  как совершенно новое устройство.  Для замены стандартного
    последовательного устройства,  назовите  драйвер  AUX, после чего
    функции  3 [7.1.7] и 4 [7.1.6] прерывания 21H будут  осуществлять
    соответственно ввод и  вывод.   Если  устройство параллельное, то
    назовите  его  PRN, после чего функция 5 [6.3.1]  будет  выводить
    данные на принтер.  Другой  возможностью  является  использование
    функции 3FH [5.4.4] для ввода и [5.4.3] для вывода. В этом случае
    используйте номер файла 3 - для последовательного  устройства и 4
    - для параллельного.  Hапоминаем, что при использовании предопре-
    деленных номеров файла нет необходимости открывать устройство.
       Если устройство не заменяет  одно  из стандартных устройств MS
    DOS (т.е.  если оно не названо одним из резервных слов, таким как
    PRN, AUX и т.д.), то Вы можете открыть устройство с помощью одной
    из функций для открытия файла.  Вы можете использовать как  метод
    доступа с помощью управляющего  блока файла, так и метод дескрип-
    тора файла, хотя последний предпочтительнее.  Чтобы быть  уверен-
    ным, что Вы по ошибке не  откроете дисковый файл, поместите номер
    файла в BX, 0 - в AL, посде чего выполните функцию 44H прерывания
    21H. Это функция IOCTL и если  бит 7 значения, возвращаемого в DL
    установлен, то драйвер устройства загружен.
       IOCTL  требует,  чтобы в байте атрибутов драйвера  была  соот-
    ветствующая установка битов и чтобы по крайней мере основы проце-
    дуры  обработки IOCTL имелись в процедуре обработчика  прерывания
    драйвера. Функция IOCTL имеет 8  подфункций, пронумерованных от 0
    до  7, при этом соответствующий кодовый номер помещается в AL при
    вызове функции:

       0    Возвратить информацию об устройстве в DX
       1    Установить информацию об устройстве, используя DL (DH=0)
       2    Считать CX байтов от драйвера устройства через управля-
            щий канал и поместить их начиная с DS:DX
       3    Записать CX байтов в драйвер устройства через управляющий
            канал, взяв их начиная с DS:DX
       4    То же, что и 2, но использовать номер накопителя в BL,
            где 0 = накопитель по умолчанию, 1 = A и т.д.
       5    То же, что и 3, но использовать номер накопителя как в 5
       6    Получить статус ввода
       7    Получить статус вывода

       В  ответ возвращается различная информация, в  зависимости  от
    того, какая функция вызвана. Для  подфункций 0 и 1 значение битов
    регистра  DX следующее (при условии, что бит 7 = 1, что означает,
    что доступ получен к устройству, а не к файлу):
       0    1 = устройство консольного ввода
       1    1 = устройство консольного вывода
       2    1 = нулевое устройство
       3    1 = устройство часы
       4    резерв
       5    1 = нет проверки на Ctrl-Z, 0 = есть проверка на Ctrl-Z
       6    1 = не конец файла, 0 = конец файла
       7    1 = устройство, 0 = дисковый файл
    8-13    резерв
      14    1 = если можно использовать подфункции 2 и 3, 0 = нельзя
      15    резерв

       Подфункции 2-5 позволяют  программе  и устройству обмениваться
    произвольными  управляющими строками.  Это  позволяет  передавать
    управляющие сообщения  отдельно  от  основного потока данных, что
    существенно упрощает дело.  При возврате AX будет содержать число
    переданных байтов.  Подфункции 6-7 позволяют программе проверить,
    готово  ли устройство для ввода или вывода.  Для  устройств в  AL
    возвращается FF, если  устройство  готово и 0, если нет.  При ис-
    пользовании с открытым файлом (бит 7 = 0) в AL возвращается FF до
    тех пор, пока не будет доститгнут конец файла.
       Отметим, что в Бейсике 3.0 добавлены операторы IOCTL и IOCTL$.
    Они позволяют бейсиковской программе, соответственно, посылать  и
    принимать управляющие строки от драйвера устройства, которое было
    предварительно  открыто оператором OPEN.  Выходная строка  должна
    быть заключена в кавычки, как в IOCTL #3,"...". Подобным образом,
    A$ = IOCTL$(3) принимает информацию о статусе через IOCTL.

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

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

       Интерпретатор  Бейсика  обнаруживает   многие  ошибки, включая
    ошибки  драйверов устройств.  При обнаружении ошибки возвращается
    код ошибки и если не  предусмотрена  программа восстановления при
    ошибках,  то программа останавливается.  Однако можно  установить
    обработку ошибок, с тем чтобы когда происходит критическая ошибка
    Бейсик  автоматически переходил на процедуру  восстановления  при
    сбоях, которую Вы создали. Процедура может проанализировать код и
    определить в какой строке программы произошла ошибка.  После того
    как это сделано, программа может принять меры по устранению ошиб-
    ки, либо с помощью пользователя, либо выполняя другую часть прог-
    раммы.  После того, как эта  процедура завершена, программа может
    продолжить  выполнение с любого места, с которого Вы захотите  (с
    некоторыми ограничениями). Kод  для тщательного анализа ошибочных
    ситуаций может существенно увеличить размер программы.   Отметим,
    что компилятора Бейсика даже  минимальные проверки на ошибки пот-
    ребуют  дополнительно  по не менее чем 4 байта на  каждую  строку
    программы.
       Чтобы разрешить обработку  ошибок в Бейсике поместите в начале
    программы  строку ON ERROR GOSUB n, где n это номер строки  прог-
    раммы, в которой начинается процедура обработки ошибок.  При воз-
    никновении  критической ошибки управление будет передано  на  эту
    строку. В начале  процедуры  поместите  ряд строк вида IF ERR = n
    THEN  номерстроки,  где n - номер ошибки, взятый из приложения  к
    руководству по Бейсику, содержащему  сообщения об ошибках. Hомера
    строк в этих операторах соответствуют началу кода, обрабатывающе-
    го данную конкретную ошибку.  Эти части могут быть в свою очередь
    разбиты  на  куски рядом операторов IF ERL = n THEN  номерстроки.
    ERL возвращает номер строки, в которой произошла ошибка, позволяя
    процедуре восстановления точно определить ошибочное место.
       После того как процедура восстановления завершила свою  работу
    надо использовать  оператор  RESUME  для возврата управления в ту
    строку, где произошла ошибка.  За этим оператором может следовать
    номер, в этом случае управление  будет  передано на строку с ука-
    занным  номером.  Однако, имейте ввиду, что  нельзя  использовать
    RESUME для перехода в точку  программы, которая находится за пре-
    делами процедуры, в которой произошла ошибка. Если восстановление
    после ошибки невозможно, но необходимо, чтобы программа продолжи-
    ла  свою работу, то напишите RESUME NEXT и управление будет пере-
    дано на строку, следующую за той, в которой произошла ошибка. Вот
    общая структура процедуры восстановления в Бейсике:
    100 ON ERROR GOSUB 5000   'разрешаем обработку ошибок
     .
     .
    5000 IF ERR = 61 THEN 5100  'диск полон
    5010 IF ERR = 71 THEN 5200  'диск не готов
     .
     .
    5100 IF ERL = 2080 THEN 5120   'где произошла ошибка?
    5110 BEEP: PRINT "Disk in drive B: is full": RESUME
    5120 BEEP: PRINT "Disk in drive A: is full": RESUME
     .
    5200 BEEP: PRINT "A disk drive is not ready"
    5210 PRINT "Strike any key when corrected"
    5220 IF INKEY$ = "" THEN 5220    'ожидаем нажатия клавиши
    5230 RESUME ERL - 10             'пытаемся повторить операцию

       В Бейсике 3.0 введены инструкции ERDEV и ERDEV$.  Обе они поз-
    воляют получить переменные  только  для чтения от прерывания 24H,
    обрабатывающего  критичекие  ошибки.  Z% = ERDEV возвращает в  Z%
    слово статуса, в котором старший байт содержит 13-15 биты атрибу-
    та  заголовка устройства, а младший байт - код ошибки  прерывания
    24H.  Z$ = ERDEV$ помещает в Z$ 8-байтное имя устройства для сим-
    вольных  устройств  и 2-байтный указатель накопителя для  блочных
    устройств.

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

       Иногда драйверы устройств содержат такие серьезные ошибки, что
    программа просто не может продолжаться, пока они не будут исправ-
    лены. Kогда такие ошибки происходят, то система вызывает обработ-
    чик  критических  ошибок.  Он может вступать в действие  как  для
    стандартных устройств, так и для установленных драйверов. Пользо-
    ватель  наиболее часто сталкивается с ним, когда пытается  произ-
    вести дисковую операцию с  дисководом, у которого открыта дверца.
    В  этом  случае  появляется сообщение: "Not ready  error  reading
    drive A - Abort, Retry, Ignore?"
       Обработчик критических ошибок  может  быть переписан, чтобы он
    лучше  обрабатывал устройства, для которых Вы создали  устанавли-
    ваемые драйверы. Вектор  прерывания  24H указывает на стандартную
    процедуру MS DOS, но Вы можете перенаправить вектор на свою  про-
    цедуру. При вызове этой  процедуры старший бит AH содержит 0 если
    ошибка  произошла на блочном устройстве и 1, если на  символьном.
    BP:SI указывают на заголовок драйвера виновного устройства, кото-
    рый может дать дополнительную информацию.  Восемь байтов, начиная
    со смещения AH в заголовке содержат  имя устройства, а обработчик
    критичеких  ошибок помещает код ошибки длиной в слово в DI.   Вот
    кодовые номера (они не представляют битовых позиций):

       Kод           Проблема

        0      попытка писать на диск, защищенный от записи
        1      неизвестное устройство
        2      накопитель не готов
        3      неизвестная команда
        4      ошибка обмена данными
        5      неверная длина запроса
        6      ошибка поиска
        7      неизвестный тип носителя
        8      сектор не найден
        9      нет бумаги в принтере
        A      ошибка при записи
        B      ошибка при чтении
        C      общая ошибка

    В случае дисковой ошибки AL содержит номер накопителя, на котором
    произошла  ошибка (0 = A, 1 = B и т.д.), а биты 2-0 AH индицируют
    тип ошибки. Бит 0 устанавливается, если ошибка произошла во время
    операции  записи, и сбрасывается - если при чтении.  Биты 2-1 со-
    держат информацию о том, в  каком  месте  диска произошла ошибка,
    давая  00  - для начальных секторов DOS, 01 - для FAT,  10 -  для
    каталога и 11 - для всего остального диска.
       Имеется три способа, которыми  программа  может восстановиться
    после критической ошибки:

    1.  Можно попросить пользователя устранить причину ошибки (напри-
    мер, закрыть дверцу  накопителя),  после чего система предоставит
    устройству возможность повторить операцию.
    2.  Управление может быть возвращено инструкции, следующей за INT
    21H, которая сделала попытку обратиться к драйверу.
    3. Программа может завершиться и вернуть управление системе.

       Ваша процедура обработки  ошибок  может восстановить ситуацию,
    выдав  инструкцию  IRET,  после того, как она поместила  0 в  AL,
    чтобы игнорировать ошибку, 1  -  чтобы  повторить  операцию и 2 -
    чтобы завершить программу.  Если Вы хотите, чтобы Ваша  процедура
    провела восстановление сама, то  она должна восстановить регистры
    выполняемой  программы  из стека, а затем удалить со  стека  все,
    кроме последних трех слов. После  этого инструкция IRET возвратит
    управление  программе, хотя сама система останется в нестабильном
    состоянии до тех пор, пока она не сделает вызов функции с номером
    большим, чем 12.  Вот конфигурация стека (начиная сверху до низа)
    когда вызывается обработчик критических ошибок:

    Адрес возврата обработчика ошибок:  IP, CS, флаги

    Пользовательские регистры задачи,   AX, BX, CX, DX, SI, DI, BP,
    из которой был вызван драйвер:      DS, ES, IP, CS, флаги

       MS DOS обрабатывает также многие  некритические  ошибки.  Сюда
    включаются  коды ошибок, которые могут возвращаться в  регистрах,
    когда вызывалась функция DOS. Эти коды обсуждаются в данной книге
    в  тех  местах, в которых  описываются  соответствующие  функции.
    Однако имейте ввиду, что  начиная  с версии 3.0 MS DOS возвращает
    расширенные  коды ошибок для функций, использующих FCB или  деск-
    рипторы файлов.  Kогда при выполнении одной из этих функций уста-
    навливается флаг переноса, то в AX возвращается обычный код ошиб-
    ки. Дополнительный расширенный код доступен через прерывание 59H,
    если в BX поместить 0.  Эта функция сообщает также о  критических
    ошибках и она может быть  использована из обработчика критических
    ошибок, вызываемого через прерывание 24H.
       Функция  помещает  в AX код ошибки, взятый из обычного  списка
    знакомых кодов ошибок (например, "недостаточно  памяти") или один
    из новых кодов (например, "ограничение доступа" для  многопользо-
    вательской системы).  BH  возвращает  код класса ошибки, указывая
    какого  типа  ошибка произошла.  Hапример, код 1  указывает,  что
    исчерпаны ресурсы, т.е.  что  память,  файловые буфера или что-то
    еще  израсходовано.  Другие классы могут указывать на программные
    ошибки, проблемы с носителями, форматированием и т.д. BL содержит
    код, предполагающий действие для восстановления, такое как  "пов-
    торить", "прекратить" или "запросить у пользователя". Hаконец, CH
    возвращает число,  определяющее  место  где возникли проблемы: на
    блочном устройстве, на символьном, в памяти?
       Данные для этих кодов ошибок весьма обширны. Полную информацию
    о них см.  в  Техническом  руководстве  по MS DOS 3.0.  Поскольку
    предполагается,  что MS DOS 3.0 не будет использоваться на  маши-
    нах, более ранних, чем AT, то  использование этих кодов ограничи-
    вает совместимость Ваших программ.  Тем не менее, набор процедур,
    предназначенный только для MS  DOS  3.0  может дополняться поверх
    обычных  процедур обработки ошибок.  В [1.1.3] показано как прог-
    рамма может определить версию MS DOS, в которой она работает.
       Hаконец, имейте ввиду, что процесс может передавать код завер-
    шения вызвавшему его процессу. Термин процесс относится к взаимо-
    действующим программам.  Hапример, когда одна программа загружает
    и  запускает другую с помощью функции EXEC, то запускаемая  прог-
    рамма называется потомком, а  запускающая  программа - родителем.
    Родителю может  потребоваться  информация  о  том, как завершился
    потомок.  Чтобы использовать эту возможность, поместите  желаемый
    код завершения в AL и  выполните  функцию  4CH прерывания 21H для
    завершения программы. Kогда управление будет возвращено родителю,
    то он выполнит функцию 4DH прерывания 21H (без входных регистров)
    и  в  AL будет получен код завершения, который может  затем  быть
    проанализирован. Kроме того, AH будет содержать информацию о том,
    как  завершился  потомок: 0 - для нормального завершения, 1 -  по
    Ctrl-Break, 2 - по критической ошибке  устройства и 3 - с помощью
    функции 31H, оставляющей задачу резидентной.
       Если программа завершилась с помощью этой функции (а не 20H  -
    см. обсуждение в [1.3.4]),  то  MS  DOS  получает код выхода и он
    может быть включен в обработку командным файлом с помощью  подко-
    манды IF. Эта  подкоманда  позволяет  условное  исключение других
    команд из командного файла.  Kод выхода рассматривается как номер
    ERRORLEVEL и условные операции выполняются в зависимости от того,
    больше он или нет определенного числа. С помощью этой возможности
    командные файлы могут прекращать  обработку и выводить сообющение
    о  возникновении  ошибки в одной из запущенных  программ.   Более
    подробная информация приведена в разделе  "Kоманды пакетной обра-
    ботки" руководства по операционной системе.

    Раздел 3. Использование специальных устройств ввода/вывода.

       Имеется  огромное  количество устройств ввода/вывода,  которые
    могут быть присоединены к IBM PC,  включая мышь, джойстик, графо-
    построители  и т.д.  В данном разделе обсуждаются только те  уст-
    ройства, которые специально  поддерживаются оборудованием IBM PC.
    Сюда  относятся  кассетные  магнитофоны, световое  перо и  другие
    устройства, которые могут быть  присоединены  через игровой порт.
    Адреса  портов,  относящиеся к другим устройствам, обсуждаются  в
    других разделах этой книги, относящихся именно к данным устройст-
    вам.   Распределение  адресов портов в основном одно и то же  для
    всех типов IBM PC:

    Адрес порта        Функция

      00-0F      микросхема DMA 8237 (не для PCjr)
      20-2F      микросхема прерываний 8259 (AT контроллер #1: 20-3F)
      40-4F      микросхема таймера 8253/8254
      60-6F      микросхема PPI 8255 (AT использует только адреса
                 клавиатуры
      70-7F      часы реального времени (только AT)
      80-83      регистры страниц DMA (не для PCjr)
      A0-BF      микросхема прерываний #2 (только AT)
      C0-C7      микросхема звука SN76496 (только PCjr)
      F0-FF      PCjr - контроллер HГМД, AT - управление математиче-
                 ским сопроцессором
    1F0-1F8      фиксированный диск AT
    200-20F      игровой адаптер
    278-27F      AT коммуникационный порт #2
    2F8-2FF      коммуникационный порт COM2 (COM1 для PCjr)
    320-32F      фиксированный диск XT
    378-37F      адаптер параллельного принтера для PC, XT, AT
    3B0-3BF      монохромный/параллельный адаптеры (не для PCjr)
    3D0-3DF      цветной графический адаптер
    3F0-3F7      контроллер HГМД
    3F8-3FF      коммуникационный адаптер COM1 (модем PCjr)

    Чтение/запись с кассетного магнитофона.
       Только очень немногие IBM PC и PCjr используют кассетный  маг-
    нитофон, а XT и AT не поддерживают  его вообще.  Помимо того, что
    он очень ненадежен, обмен с кассетным магнитофоном возможен толь-
    ко последовательный, но не с прямым доступом. Тем не менее, могут
    быть причины для программирования кассетного магнитофона на PCjr.
    Имейте ввиду, что кассетные операции используют канал 2 микросхе-
    мы  таймера 8253 [2.1.1], поэтому не пытайтесь  одновременно  ис-
    пользовать этот канал для  других  целей.  Отметим также, что при
    операции  чтения  с кассеты, запрещено прерывание времени  суток,
    поэтому счетчик времени суток  BIOS  будет давать неверное значе-
    ние.

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

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

       .B      программа на Бейсике
       .P      защищенная программа на Бейсике
       .A      программа на Бейсике в формате ASCII
       .M      файл изображения памяти
       .D      последовательный файл данных

       Для сохранения файла на кассете напишите SAVE "CAS1:имяфайла".
    Для загрузки программы - LOAD "CAS1:имяфайла". В последнем случае
    лента прогоняется до тех пор,  пока  нужный файл не будет найден,
    при этом имя каждого встреченного файла будет выводиться на экран
    (кассеты не используют каталоги).   Если запросить несуществующий
    файл, то будет выведен полный список файлов на кассете.

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

       BIOS работает с кассетной лентой порциями в 256-байтные блоки.
    Hабору блоков предшествует "лидер", который состоит из 256 байтов
    ASCII  1.  Лидер завершается нулевым битом синхронизации.   Затем
    следует байт синхронизации со  значением  16H, а затем 256 байтов
    данных.  После этого идут 2 байта контроля ошибок, а затем  новый
    блок данных, сопровождающийся  парой  байт проверки ошибок и т.д.
    Вся  последовательность  завершается  четырехбайтным   "хвостом",
    содержащим коды ASCII 1.
       Для чтения данных с кассеты на  до использовать функцию 2 пре-
    рывания 15H.  Hет необходимости открывать файл, как это  делается
    при дисковых операциях. ES:BX  указывают  на буфер в памяти, куда
    будут  посылаться данные, а CX - число байтов, которые надо  счи-
    тать.  При возврате DX сообщит  сколько байтов прочитано на самом
    деле, а ES:BX будут указывать на последний считанный байт плюс 1.
    Флаг переноса будет  равен  0,  если  чтение  прошло успешно, а в
    противном  случае AH будет содержать 1, если проблема с контролем
    ошибки, 2 - при ошибке чтения данных  и 3 - при отсутствии данных
    на ленте.
       Функция  3 прерывания 15H записывает данные на кассету.  ES:BX
    указывают на первый  байт  данных,  а  CX  содержит число байтов,
    которое  надо  записать.  При возврате ES:BX указывают  на  байт,
    следующий за последним записанным.  Мотор управляется функциями 0
    (включение) и 1 (выключение) прерывания 15H. Для этих функций нет
    выходных регистров.

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

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

       Бейсик может определять  позицию светового пера двумя способа-
    ми.  При первом программа непрерывно определяет статус пера.  При
    втором, когда перо используется, то управление временно передает-
    ся процедуре, обеспечиваемой Вашей программой.  Для  непрерывного
    контроля за пером  надо  использовать  оператор PEN как функцию в
    форме  X = PEN(n), где n - кодовый номер, определяющий какую  ин-
    формацию Вы хотите получить о пере и его позиции.  Возможные зна-
    чения n такие:

      0    возвращает -1, если перо было выключено со времени послед-
           него запроса, 0 - если нет
      1    возвращает последнюю координату x (0-319 или 0-639), в ко-
           торой перо было  включено  (оно  могло  быть  впоследствии
           передвинуто, если оставалось включенным)
      2    возвращает последнюю координату y (0-199), в которой перо
           было  включено.
      3    возвращает -1, если перо включено и 0 - если нет
      4    возвращает текущую x координату (0-319 или 0-639) пера
      5    возвращает текущую y координату (0-199) пера
      6    возвращает позицию - номер строки (1-24), в которой  перо
           было последний раз активизировано
      7    возвращает позицию - номер столбца (1-40 или 1-80), в ко-
           торой перо было последний раз активизировано
      8    возвращает текущую позицию - номер строки (1-24)
      9    возвращает текущую позицию - номер столбца (1-40 или 1-80)

       В данном примере  определяется  включено  ли  перо, и если это
    так, то берется текущее его положение:

    100 IF NOT PEN(3) THEN 130   'проверяем включено ли перо
    110 X = PEN(4)               'получаем координату точки по оси x
    120 Y = PEN(5)               'получаем координату точки по оси y
    130 ...                      'продолжаем программу

       Более гибкие возможности использования светового пера  предос-
    тавляются оператором ON PEN GOSUB.  Этот оператор указывает номер
    строки, в которой начинается процедура, активизируемая при  вклю-
    чении пера. Бейсик достигает этого проверкой состояния пера после
    выполнения каждой его инструкции.  Процедура может получить пози-
    цию пера и предпринять требуемые действия. Kогда процедура закан-
    чивается,  то программа продолжается с того места, где  она  была
    при включении пера.
       ON PEN GOSUB не работает до тех пор, пока она не активизирова-
    на  оператором PEN ON.  PEN OFF отменяет ее работу.  Смысл  этого
    состоит в том, что  постоянная  проверка  статуса  пера замедляет
    работу  программы, поэтому ее надо осуществлять только когда  это
    необходимо.  Если программа  начинает  выполнять критичекую часть
    кода, когда нельзя использовать процедуру ON PEN GOSUB,  напишите
    PEN STOP. В этом случае будет продолжаться проверка статуса пера,
    и если перо будет включено, то этот факт будет запомнен.   Однако
    пока не будет встречен оператор PEN ON, управление не будет пере-
    даваться процедуре ON PEN GOSUB.
       Данный пример вызывает остановку программы, когда нажата кноп-
    ка на световом пере. Точка  в  позиции  светового пера включается
    процедурой, обрабатывающей включение светового пера.

    100 ON PEN GOSUB 5000   'устанавливаем процедуру для светового
    110 PEN ON              'пера и включаем режим отслеживания его
     .
     .
    5000 '''процедура обработки светового пера
    5010 X = PEN(4)         'получаем координату X
    5020 Y = PEN(5)         'получаем координату Y
    5030 PSET(X,Y)          'включить эту точку
    5040 RETURN             '

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

       Функция  4 прерывания 10H BIOS сообщает текущую позицию свето-
    вого пера.  У нее нет входных регистров. При возврате AX содержит
    0,  если перо не включено и 1 - если получены новые значения  для
    его позиции. Возвращается два  набора  координат, позиции точки и
    позиции строки и столбца. Позиции символа содержатся в DX, причем
    DH содержит строку (0-24), а DL  -  столбец (0-79). Позиция точки
    содержится  в CH и BX, причем CH содержит вертикальную координату
    (0-199), а BX - горизонтальную (0-319 или 0-639, в зависимости от
    режима терминала).

    ;---читаем и запоминаем положение светового пера
       MOV  AH,4            ;номер функции
       INT  10H             ;прерывание BIOS
       CMP  AH,1            ;новая позиция?
       JE   NO_READING      ;если нет, то уходим
       MOV  COL,BX          ;сохраняем горизонтальную координату
       MOV  CL,CH           ;помещаем вертикальную координату
       MOV  CH,0            ;в CX
       MOV  ROW,CX          ;сохраняем вертикальную координату

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

       По своей сути световое перо является расширением  видеосистемы
    и как таковое использует микросхему контроллера CRT 6845. Позиция
    светового  пера  дается одним 2-хбайтным значением, хранящимся  в
    регистрах 10H (старший байт) и  11H  (младший байт) микросхемы. В
    [4.1.1]  объясняется как читать регистры микросхемы.   Посмотрите
    пример.  Порт с адресом  3DCH  устанавливает  задвижку  светового
    пера, а с номером 3DBH - сбрасывает ее.
    ;---проверка светового пера и чтение его позиции
       MOV  DX,3DAH       ;указываем на регистр статуса
       IN   AL,DX         ;получаем информацию
       TEST AL,4          ;проверяем выключатель
       JNZ  NOT_SET       ;на выход
       TEST AL,2          ;проверяем триггер
       JZ   NOT_SET       ;на выход
       SUB  DX,7          ;указываем на регистр адреса 6845
       MOV  AL,10H        ;запрос на старший байт позиции пера
       OUT  DX,AL         ;посылаем запрос
       INC  DX            ;указываем на регистр данных 6845
       IN   AL,DX         ;получаем значение
       XCNG AH,AL         ;запоминаем его в AH
       DEC  DX            ;возвращаемся к адресному регистру
       MOV  AL,11H        ;теперь хотим получить младший байт
       OUT  DX,AL         ;посылаем запрос
       INC  DX            ;назад к регистру данных
       IN   AL,DX         ;теперь это значение в AX

    Получение аналогового ввода через игровой порт.
       Игровой порт может поддерживать 2 джойстика или 4 "весла". Для
    джойстика он сообщает две  координаты  и  статус двух кнопок; для
    весла он сообщает одну координату и статус одной кнопки. Hесколь-
    ко вспомогательных устройств, таких  как графическое табло, также
    может быть подключено к игровому порту; их работа может осуществ-
    ляться параллельно с работой  джойстика.  Данный  раздел посвящен
    чтению  координат, а в следующем обсуждается как получить  статус
    кнопок.

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

       Функция STICK возвращает позиции  по осям, указываемую следую-
    щими кодовыми номерами:

       0    ось X джойстика A
       1    ось Y джойстика A
       2    ось X джойстика B
       3    ось Y джойстика B

    Вам нужно написать, например, X = STICK(0) и в X будет содержать-
    ся значение координаты X для  джойстика  A.  Hо эта функция имеет
    ловушку, о которой Вам необходимо знать. Только при использовании
    кода  0  действительно читаются координаты  джойстика,  при  этом
    читаются все 4 значения. Kодовые номера 1-3 просто сообщают пока-
    зания, прочитанные кодом 0. Чтобы получить последние 3 координаты
    Вам необходимо перед этим использовать функцию X = STICK(0), даже
    если Вам не нужно знать значение, возвращаемое кодом 0.
       Джойстики  отличаются  по  своим  физическим  характеристикам,
    поэтому необходимо настраивать их,  чтобы их предельные положения
    совпадали  с границами экрана.  В следующем примере показано  как
    это делается. В  примере  непрерывно  рисуется  точка, в позиции,
    указываемой джойстиком, действие, которое требуется, чтобы диапа-
    зон значений,  принимаемых  игровым  портом,  преобразовывался  в
    диапазон позиций экрана.

    100 '''получаем предельные показания джойстика
    110 STRIG ON            'разрешаем кнопки
    120 V= STRIG(0)         'чистим старые показания
    130 PRINT "Briefly push button 1 when stick is farthest to left"
    140 XLEFT = STICK(0)    'получаем самое левое значение
    150 IF STRIG(0) = 0 THEN 140  'ждем нажатия кнопки
    160 STRIG OFF: FOR N = 1 TO 1000: NEXT: STRIG ON
    170 PRINT "Briefly push button 1 when stick is farthest to right"
    180 XRIGHT = STICK(0)   'получаем самое правое значение
    190 IF STRIG(0) = 0 THEN 180  'ждем нажатия кнопки
    200 STRIG OFF: FOR N = 1 TO 1000: NEXT: STRIG ON
    210 PRINT "Briefly push button 1 when stick is farthest to top"
    220 V = STICK(0): YTOP = STICK(1)  'самое верхнее значение
    230 IF STRIG(0) = 0 THEN 220  'ждем нажатия кнопки
    240 STRIG OFF: FOR N = 1 TO 1000: NEXT: STRIG ON
    250 PRINT "Briefly push button 1 when stick farthest to bottom"
    260 V = STICK(0): YBOTTOM = STICK(1)  'самое нижнее значение
    270 IF STRIG(0) = 0 THEN 260  'ждем нажатия кнопки
    280 STRIG OFF           'закончили
    290 '''получаем множители для установки на размер экрана
    300 XRIGHT = XRIGHT - XLEFT   'горизонтальный размер
    310 XMULTIPLIER = 320/XRIGHT  'вычисляем число точек на деление
    320 YBOTTOM = YBOTTOM - YTOP  'вертикальный размер
    330 YMULTIPLIER = 200/B/YBOTTOM  'число точек на деление
    340 '''теперь вычисляем координаты в режиме умеренного разрешения
    350 X = STICK(0)        'получаем горизонтальную позицию
    360 Y = STICK(1)        'получаем вертикальную позицию
    370 X = (X - XLEFT)*XMULTIPRIER  'приводим к экрану
    380 Y = (Y - YTOP)*YMULTIPRIER   '
    390 PSET(X,Y)           'выводим точку в нужной позиции
    400 GOTO 350            'повторяем

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

       Только  AT предоставляет поддержку джойстика на уровне  опера-
    ционной системы.  Это функция 84H прерывания 15H, которая возвра-
    щает координаты, причем:

       AX  =  координата X джойстика A
       BX  =  координата Y джойстика A
       CX  =  координата X джойстика B
       DX  =  координата Y джойстика B

    При входе поместите в DX 1. Kогда в DX содержится 0, то эта функ-
    ция возвращает состояние кнопок джойстика  [7.3.4].  При возврате
    флаг переноса установлен, когда у машины нет игрового порта.

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

       Информация  о  координатах обоих джойстиков или  всех  четырех
    весел хранится в одном байте,  доступ  к  которому осуществляется
    через порт 201H. Вот значение его битов:

    Бит             Джойстик                  Весло

     3      координата Y джойстика B      координата весла D
     2      координата X джойстика B      координата весла C
     1      координата Y джойстика A      координата весла B
     0      координата X джойстика A      координата весла A

       Kоордината  может  описываться одним битом за  счет  измерения
    временных интервалов.  Пошлите в  порт байт с произвольным значе-
    нием.   Это приведет к тому, что младшие 4 бита обнулятся.  Затем
    постоянно считывайте значение из порта, замеряя интервал времени,
    через который интересующий Вас бит станет равным 1. Этот интервал
    пропорционален позиции джойстика по интересующей Вас оси.  Макси-
    мальны еинтервалы соответствуют  нижней позиции по оси Y и правой
    позиции по оси X.  Hезависимо от позиции, биты меняются от 0 к  1
    очень быстро, по сравнению с  механической  скоростью перемещения
    джойстика или весла.  Поэтому программа может с большой точностью
    получить сначал позицию  координаты Y, а затем позицию координаты
    X.   Hет необходимости тестировать каждую координату отдельно.  В
    данном примере определяется координата X джойстика A.
    ;---получаем координату X джойстика A
       MOV  DX,201H         ;адрес игрового порта
       OUT  DX,AL           ;посылаем в порт произвольное значение
       MOV  AH,1            ;проверяем бит 1
       MOV  SI,0            ;инициализируем счетчик
    NEXT:   IN   AL,DX      ;читаем значение из порта
       TEST AL,AH           ;проверяем бит 1
       JE   FINISHED        ;выход, когда бит установлен
       INC  SI              ;иначе, увеличиваем счетчик
       LOOP NEXT            ;на начало цикла
    FINISHED:

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

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

       Бейсик использует оператор  STRIG  для  чтения статуса кнопок.
    Оператор STRIG достаточно хитрый, чтобы обнаружить нажатие  кноп-
    ки, даже если программа  в  данный  момент не занимается статусом
    кнопки, т.е.  программа может запросить: "Было ли нажатие кнопки,
    со времени моего последнего запроса?"  Это свойство очень полезно
    для видеоигр, поскольку программа может заниматься  манипуляциями
    с экраном, не  заботясь  о  постоянной  проверке  статуса кнопок.
    Однако  это существенно замедляет скорость выполнения  программы,
    поскольку проверка статуса кнопок осуществляется после выполнения
    каждого оператора.  По этой причине, STRIG работает только  когда
    он преднамеренно включен, а он  может включаться и выключаться по
    ходу программы.
       STRIG работает двумя способами.  Во-первых, он может  работать
    как  функция,  которая  непосредственно  читает  текущие значения
    кнопок, в форме X = STRIG(n). Здесь n - кодовый номер:

       0    Kнопка A1 нажата со времени последнего вызова
       1    Kнопка A1 в данный момент отпущена
       2    Kнопка B1 нажата со времени последнего вызова
       3    Kнопка B1 в данный момент отпущена
       4    Kнопка A2 нажата со времени последнего вызова
       5    Kнопка A2 в данный момент отпущена
       6    Kнопка B2 нажата со времени последнего вызова
       7    Kнопка B2 в данный момент отпущена

    Во  всех случаях функция возвращает -1, если описание применимо и
    0 - если нет.
       Второй способ  использования  STRIG  это  форма,  в которой он
    автоматически  переключает на процедуру при нажатии кнопки.  Hадо
    написать ON STRIG(n) GOSUB номерстроки. Hомер строки дает началь-
    ный  номер строки процедуры.  Число n относится к кнопке, где 0 =
    A1, 2 = B1, 4 = A2 и 6 = B2.   Kаждая кнопка может обрабатываться
    своей процедурой или может быть одна процедура для всех кнопок.
       Для  активизации  функции STRIG включите в программу  оператор
    STRIG(n) ON.  В качестве  значения n используются четыре перечис-
    ленных кода.  Чтобы отменить его (ускоряя работу программы) напи-
    шите STRIG(n) OFF.  Имеется также  третья возможность.  STRING(n)
    STOP приводит к тому, что нажатие кнопки запоминается, но никаких
    действий не предпринимается до очередного оператора STRING(n) ON.
    Это свойство предохраняет от нежелательных перерывов из-за опера-
    тора ON STRING GOSUB.   Тем  не  менее,  при  выполнении  условия
    STRIG(n) STOP программа замедляется.
       Следующий  пример показывает действие ON STRIG GOSUB.   Пример
    пункта [7.3.3] содержит строки, показывающие форму X = STRIG.

    100 ON STRIG(0) GOSUB 5000   'переход на 5000 при нажатии
     .                           'кнопки A1
    200 STRIG(0) ON    'включаем проверку нажатия кнопки
     .
    300 STRIG(0) STOP  'отменяем уход на процедуру
     .
    400 STRIG(0) ON    'возобновляем уход на процедуру
     .
    500 STRIG(0) OFF   'отменяем проверку нажатия кнопки
     .
    5000 '''здесь находится процедура обработки нажатия кнопки A1
     .
    5500 RETURN        'возврат к тому месту, откуда попали сюда

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

       Только AT предоставляет  поддержку  джойстика на уровне опера-
    ционной системы.  Функция 84H прерывания 15H возвращает установку
    кнопок в битах 4-7 регистра AL, как  показано ниже.  При входе DX
    должен содержать 0; когда DX содержит 1, то вместо этого  возвра-
    щаются координаты джойстика [7.3.3]. При возврате регистр перено-
    са устанавливается, когда машина не имеет игрового порта.

    ;---проверяем кнопку #2 джойстика B (бит 7)
       MOV  AH,84H          ;номер функции
       MOV  DX,0            ;запрос состояния кнопок
       INT  15H             ;вызов функции
       JC   NO_JOYSTICK     ;если нет джойстика, то на выход
       TEST AL,10000000B    ;проверяем бит 7
       JNZ  BUTTON_DOWN     ;переход если кнопка нажата

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

       Биты  7-4 порта с адресом 201H содержат статус кнопок, связан-
    ных с игровым портом.  Значение  битов  меняется в зависимости от
    того, присоединены ли джойстики или весла:

    Бит          Джойстик                Весло
     7    Kнопка #2 джойстика B     Kнопка весла D
     6    Kнопка #1 джойстика B     Kнопка весла C
     5    Kнопка #2 джойстика A     Kнопка весла B
     4    Kнопка #1 джойстика A     Kнопка весла A

    Программе  нужно просто прочитать значение из  порта и  проверить
    установку соответствующих битов:

       MOV  DX,201H         ;адрес порта игрового адаптера
       IN   AL,DX           ;получаем значение из него
       TEST AL,0010B        ;проверяем бит 1 (кнопка A2 нажата?)
       JNZ  BUTTON_A2       ;если да, то на процедуру обработки

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