обы правильно использовать команды управления модемом и информацию о его стату- се надо тщательно изучить документацию на данный модем. Модем PCjr описан в техническом руководстве по PCjr. Нижеприведенные примеры дают только голую схему установления связи через модем. Высокий уровень. Поскольку телефонная связь очень медленная, то связь с модемом это одна из областей, где программирование связи на Бейсике ничем не хуже, чем на языке ассемблера. Вот грубая схема: 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 '''теперь посылаем данные Низкий уровень. Вот та же самая схема на языке ассемблера: ;---устанавливаем сигнал 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 ;не продолжаем пока он не установлен ;---теперь можно посылать данные 7.1.6 Передача данных. Передача данных проще чем прием, поскольку программа имеет полный контроль над составом данных и скоростью, с которой они должны посылаться. Тем не менее процедуры передачи могут быть достаточно сложными, если они обрабатывают данные по мере того, как они посылаются. Могут быть также проблемы с синхронизацией при использовании протокола XON/XOFF. Этот протокол использует коды ASCII 17(XON) и 19(XOFF), для того чтобы сигнализировать принимающей станции, что передатчик хочет продолжить передачу временно прерванного потока данных. Чтобы принять эти сигналы, программа должна непрерывно анализировать принимаемые символы при передаче (в полнодуплексном режиме, в котором обычно работают модемы, сигналы одновременно идут в обе стороны по телефонному каналу). Кроме того, чтобы обнаружить, что удаленная станция посылает строку нулей, в качестве сигнала перерыва, должен непре- рывно анализироваться статус бита перерыва (номер 4) регистра статуса линии [7.1.4]. На рис. 7-2 (в [7.1.7]) показано как про- цедура передачи данных взаимодействует с кодом, принимающим дан- ные. Вследствие этих причин, представленные в этом пункте процедуры отдельно передающие данные являются искуственными. Но их можно скомбинировать с процедурами приема данных, описанными в [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 при указании неверного номера файла. Низкий уровень. Когда байт данных помещается в регистр хранения передатчика, то он автоматически выводится в последовательный канал через регистр сдвига передатчика, который сериализует данные. Нет необ- ходимости в импульсе бита строба, как это делается в случае па- раллельного адаптера. Бит 5 регистра статуса линии показывает свободен ли регистр хранения передатчика для приема данных. Ре- гистр постоянно проверяется до тех пор, пока бит 5 не станет равным 1. После этого в регистр хранения передатчика посылается очередной байт из того места, откуда они берутся. В процессе передачи бит 5 равен 0 и только когда он опять станет равным 1, то в регистр хранения передатчика может быть послан следующий символ. Этот процесс повторяется до тех пор, пока это нужно. В следующем примере даны основные понятия об этой процедуре. Конечно, она может быть сделана необычайно сложной (в частности, программирование связи требует особо тщательных процедур обнару- жения ошибок и восстановления при сбоях). В примере предполагает- ся, что коммуникационный порт и модем уже инициализированы, как показано в [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 ;возвращаемся к началу цикла 7.1.7 Получение данных. Коммуникационная программа готова принимать данные как только инициализирован коммуникационный порт [7.1.2] и установлена связь с удаленной станцией [7.1.5]. Прием данных никогда полностью не отделен от передачи данных, поскольку программе может потребо- ваться послать сигнал XOFF (ASCII 19), чтобы остановить поток данных, если они поступают слишком быстро и она не успевает их обрабатывать. Код XON (ASCII 17) сообщает удаленной станции, что можно продолжить передачу. Отметим, что PCjr не может принимать данные во время дисковых операций; чтобы снять это ограничение можно использовать XON и XOFF. В зависимости от сложности используемого протокола обмена, принимаемые данные могут требовать простой или сложной обработки. Может быть получен один из набора управляющих кодов, приведенных в [7.1.9]. Те из них, которые являются ограничителями данных чаще обнаруживаются при синхронном обмене. При выводе получаемых сим- волов на экран учитывайте влияние символов перевода строки (ASCII 10), поскольку некоторые языки (включая Бейсик) автоматически вставляют перевод строки после возврата каретки; в этом случае исключайте переводы строки из принимаемых данных, чтобы избежать пустых строк при выводе. На рис. 7-2 показана коммуникационная процедура, включающая также код передачи, обсуждаемый в [7.1.6]. Высокий уровень. Для коммуникационной процедуры, написанной на интерпретируемом Бейсике, время очень существенно. Обработка медленна, поэтому если процедура приема неверно сконструирована, то входной буфер может заполниться (т.е. произойдет переполнение) в то время как программа еще будет анализировать ранее полученные данные. Оче- видным решением этой проблемы является максимально возможный размер буфера. При загрузке Бейсика размер буфера ввода устанав- ливается добавлением к команде ключа /C:. BASICA /C:1024 создает буфер размером в 1K и это минимальное число для скорости обмена 1200 бод (сложным процедурам может понадобиться 4096 байт). По умолчанию используется размер буфера равный 256 байтам и такой буфер имеет то преимущество, что он может быть целиком помещен в одну символьную переменную. Такой размер буфера можно использо- вать только при скорости обмена 300 бод и ниже. Бейсик читает из буфера с помощью оператора INPUT$ (можно использовать также INPUT# и LINE INPUT#, но INPUT$ более гибок). Этот оператор имеет форму INPUT$(числобайт,номерфайла). Например, INPUT$(10,#1) читает 10 байтов из коммуникационного канала, отк- рытого как файл #1. Если размер буфера не превышает 256 байтов, то очень удобно читать все содержимое буфера за один раз. LOC сообщает сколько байтов данных находится в буфере в данный мо- мент. Поэтому напишите оператор INPUT$(LOC(1),#1) и в S$ будут записаны все данные с момента последнего доступа к буферу. Конеч- но, если LOC(1) = 0, то буфер пуст и процедура должна ожидать пока данные будут получены. Отметим, что EOF(1) также можно ис- пользовать для проверки состояния буфера, так как эта функция возвращает -1 если буфер пуст и 0, если там есть хотя бы один символ. После того как данные записаны в S$ программа должна проверить не содержатся ли там управляющие коды. Функция INSTR выполняет эту задачу быстрее всего. Напомним, что ее параметрами являются сначала позиция, с которой надо вести поиск в строке, затем имя строки и, наконец, символ (или строка) который ищется. Чтобы найти символ XOFF (ASCII 19) оператор должен иметь вид INSTR(1,S$,CHR$(19)). Чтобы найти второе появление нужного управ- ляющего символа повторите поиск в строке, начиная с символа, следующего за позицией, в которой найден первый. Обычно процедура ввода исключает большинство управляющих сим- волов из принимаемых данных, с тем чтобы они нормально выглядели при выводе. Затем данные выводятся на экран, пересылаются в дру- гое место в памяти, а иногда записываются на диск или выводятся на принтер. В процессе всей этой деятельности программа должна постоянно возвращаться к просмотру не поступили ли новые данные. Если оказалось, что буфер заполняется слишком быстро, то програм- ма может послать сигнал XOFF, останавливая поток данных. Затем, после того как полученные данные буду декодированы, можно снова разрешить передачу данных. Конечно, необходимо чтобы протокол обмена поддерживал 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 применяется к коммуникационному порту, то она возвращает количество свободного места, оставшееся в буфере вво- да. Например, если COM1 открыт как #1, то LOF(1) сообщит свобод- ного пространства. Это может быть полезно для определения, что буфер почти полон. Отметим, однако, что оператор LOC возвращает позицию указателя в буфере и это значение может быть использовано для той же цели. Например, если 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 и не возвращает информации о статусе. Низкий уровень. При получении данных без использования коммуникационного пре- рывания [7.1.8] программа должна постоянно проверять регистр статуса линии, адрес порта которого на 5 больше базового адреса используемого коммуникационного адаптера. Бит 0 этого регистра будет равен нулю, до тех пор пока не будет получен символ в ре- гистр данных приемника. Когда бит 0 становится равным 1, то надо немедленно считать его из регистра, с тем чтобы на него не нало- жился следующий принимаемый символ. После того как символ считан, бит 0 опять становится равным 0 и остается таковым, пока не при- будет новый символ. Хотя здесь об этом не говорилось, но коммуникационные процеду- ры обычно создают циклический буфер для сбора поступающих симво- лов. Циклические буфера обсуждались в [3.1.1]. Вы должны также знать, что если поступающие данные подавать на экран со скоростью 1200 бод, то процедура сдвига экрана BIOS [4.5.1] не будет успе- вать и произойдет переполнение. Простое решение этих проблем состоит в использовании коммуникационного прерывания, как объяс- нено в [7.1.8]. Следующий пример частично дублирует содержимое предыдущего раздела, относящегося к передаче символов. Как и в том случае код начинается с бесконечного цикла. Объедините эти 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 ;возвращаемся на начало цикла 7.1.8 Посылка/получение данных с помощью коммуникационного прерывания. Хорошая коммуникационная программа имеет слишком много работы, чтобы посвятить себя целиком вводу/выводу. Поступающие данные должны анализироваться, передаваемые данные должны собираться, а большие блоки данных могут записываться на диск или считываться с него. Коммуникационное прерывание позволяет программе не тратить на ввод/вывод больше времени, чем он того требует. Например, после установки прерывания, управление передается процедуре пере- дачи данныз только в том случае, когда регистр хранения передат- чика пуст и возвращается программе, как только послан байт дан- ных, позволяя ей продолжать свою работу до тех пор, пока регистр хранения передатчика не будет снова готов. Не забудьте ознако- миться с обсуждением прерываний в [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 Когда одно из этих событий происходит, то инициируется аппаратное прерывание, возникающее в микросхеме обработки прерываний 8259 по каналу 3 для COM1 и по каналу 4 для COM2. Процедура обработки прерываний передает управление тому коду, на который указывает соответствующий вектор прерывания. Поскольку это аппаратное пре- рывание, то оно может быть маскировано [1.2.2]. Помните, что процедура обработки прерывания должна завершаться стандартным кодом выхода из аппаратного прерывания MOV AL,20H/OUT 20H,AL. На рис. 7-3 показано коммуникационное прерывание. Любое число типов прерывания может быть разрешено одновремен- но. Но если разрешен более чем один тип, то процедура обработки прерывания должна сама определять какой из типов прерывания прои- зошел, проверяя регистр идентификации прерывания. Одновременно могут происходить более чем одно прерывание, поэтому бит 0 ре- гистра идентификации сообщает о том, что поступило еще одно пре- рывание. Когда два или более прерываний поступило в один и тот же момент времени, то они обрабатываются в порядке, указанном в нижеприведенной таблице. Добавочные прерывания должны быть обра- ботаны до завершения процедуры обработки прерывания. Условия предшествующих прерываний "отменяются" с помощью действий, при- веденных в правом столбце следующей таблицы: Код Тип Действия для "сброса" 11 ошибка или перерыв чтение регистра статуса линии 10 получены данные чтение регистра приемника данных 01 передатчик готов вывод символа в регистр хранения передатчика 00 изменение статуса модема чтение регистра статуса модема Низкий уровень. Вот общая форма программы, обрабатывающей коммуникационные прерывания: ;---установка вектора коммуникационного прерывания 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 7.1.9 Сводка управляющих кодов, используемых при коммуникации. Эта таблица содержит 32 управляющих кода ASCII, которые ис- пользуются при коммуникации, а также при работе принтера и других устройств. Добавлен также код ASCII 127 - DEL (Забой), который обычно используется как управляющий код, хотя его и нельзя выдать с клавиатуры с помощью сочетания Ctrl + клавиша. Применение неко- торых кодов, таких как возврат каретки, инвариантно. Но боль- шинство других управляющих кодов имеют широкий диапазон интерпре- тации, во многом из-за отсутствия совместимости оборудования. Номер кода ASCII Комбинация Мнемо- 10-й 16-й Символ с Ctrl ника Назначение 00 00 ^@ NUL Символ-разделитель (не имеющий значения, поэтому полезен для задержек) 01 01 ^A SOH Начало заголовка. Начинает передачу блока данных или но- вого файла. 02 02 ^B STX Начало текста. Отмечает начало текста, следующего за за- головком данных. 03 03 ^C ETX Конец текста. Может отмечать начало данных, служащих для контроля ошибок. 04 04 ^D EOT Конец передачи. Код остановки, но иногда он просто отме- чает конец файла. 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 Конец блока передачи. Вариант ETX. 24 18 ^X CAN Отмена. Обычно сигнализирует об ошибке передачи. 25 19 ^Y EM Конец среды. Сигнализирует о физическом конце источника данных. 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. Можно поместить код для всех драйверов прямо в программу. Например, чтобы поддерживать различные принтеры, можно создать таблицу управляющих последовательностей и искать в ней нужный код каждый раз когда он потребуется. Этот подход тратит много памяти и может быть достаточно медленным. 2. Создать ряд драйверов устройств и потребовать, чтобы прог- рамма загружала необходимый в качестве оверлея (т.е. помещать его в область программы, специально оставленную для этой цели [1.3.5]). 3. Создать драйвер устройства как отдельную программу, которая указывается в командном файле, выполняемом при загрузке системы. Программа запускается и устанавливает драйвер устройства как программу обработки прерывания. После этого программа завершает- ся, но остается резидентной в памяти, как объяснено в [1.3.4]. Впоследствии наша программа использует этот драйвер через вектор прерывания. 4. Создать полноценный драйвер устройства, который будет заг- ружаться при старте с помощью файла CONFIG.SYS. MS DOS поддержи- вает такой тип драйверов устройств и однажды загруженный он может использовать все возможности команд DOS, включая проверку ошибок. Специальная команда IOCTL (Контроль ввода/вывода) позволяет прог- рамме узнать статус драйвера и послать ему управляющую строку, помимо обычного потока данных. Первые три стратегии легко реализуются с помощью информации, приведенной в остальных частях данной книги. Но устанавливаемые драйверы устройств очень сложны. Зато когда он есть, то он очень мощен. В этом случае система будет работать с устройством нас- только же тесно, как с клавиатурой или дисковым накопителем. Устройству может быть присвоено имя, например, SERIALPR для пос- ледовательного принтера, и затем это устройство может быть откры- то для доступа из любого языка. В Бейсике оператор OPEN "SE- RIALPR" FOR OUTPUT AS #2 подготовит последовательный принтер для вывода. В языке ассемблера Вы сможете получить доступ к принтеру как с помощью метода управляющего блока файла, так и с помощью метода дескриптора файла, включая очень мощную функцию IOCTL. При этом пользователь имеет возможность доступа к устройству на уров- не операционной системы и может просто ввести команду COPY A:MY- FILE SERIALPR:, чтобы скопировать содержимое файла на принтер. Устанавливаемые драйверы устройств могут быть написаны только на языке ассемблера. Они могут обслуживать два типа устройств: символьные и блочные. Эти имена описывают единицы, которыми уст- ройство обрабатывает данные. Обычно драйверы блочных устройств обслуживают дисковые накопители, а драйверы символьных - все остальное, начиная от последовательных принтеров и кончая робота- ми. Блочные устройства обмениваются блоками данных, поэтому они занимаются накоплением данных. Символьные устройства обмениваются данными побайтно, поэтому они лучше подходят для управляющих устройств, а также для устройств, которые не могут обеспечить высокую скорость обмена данными. Драйверы блочных устройств очень сложны и здесь нет достаточно места, чтобы объяснить их структу- ру. Очень редко кому требуется написать такой драйвер. Техничес- кое руководство по MS DOS предоставляет всю необходимую информа- цию и содержит полный пример драйвера виртуального диска в опера- тивной памяти. Вы можете просмотреть эту информацию после того как изучите обсуждение драйверов символьных устройств, приведен- ное здесь. Устанавливаемые драйверы устройств беспощадны к программистс- ким ошибкам. Поскольку драйверы автоматически загружаются систе- мой при загрузке, то невозможно использовать отладчики для выяв- ления причин неполадок. Поэтому будьте предельно внимательны при их написании. Программа драйвера устройства разбивается на три части, каждая из которых обсуждается отдельно в следующих разделах. Это (1) заголовок драйвера, который именует устройство и содержит инфор- мацию об остальных частях драйвера, (2) стратегия драйвера, кото- рая хранит информацию об области данных, создаваемой MS DOS, которая называетя заголовком запроса, и (3) обработчик прерывания устройства, который и содержит код, управляющий устройством. 7.2.1 Создание заголовка драйвера. Драйверы устройств должны создаваться в виде 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) - это псевдоустройство, используемое для тестовых целей). Третье и четвертое поля содержат смещения для процедур страте- гии и обработки прерывания, которые будут рассмотрены в следующих разделах. Наконец, последнее поле содержит имя устройства. Имя может содержать до 8 символов и оно должно быть выравнено по левому краю с завершающими пробелами. Для замены существующих в DOS устройств, таких как LPT1 или COM1, используйте то же имя устройства, как в данном примере. Низкий уровень. В данном примере создается драйвер для последовательного уст- ройства. "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 ' 'имя устройство (дополненное пробелами) 7.2.2 Создание стратегии устройства. Процедура стратегии устройства требует только пяти строк. Когда система загружает устройство, то она создает блок данных, называемый заголовком запроса. Он имеет две функции. Во-первых он служит областью данных для внутренних операций системы. Более важно то, что заголовок запроса служит областью, через которую происходит обмен информацией между драйвером и вызывающей его программой. Например, когда драйвер выводит данные, то ему дается адрес данных через заголовок запроса. Когда же драйвер завершает свою работу, то он устанавливает в заголовке запроса байт стату- са, который доступен вызывающей программе, тем самым давая воз- можность ей узнать об ошибке. MS DOS создает заголовок запроса при установке драйвера уст- ройства (когда система загружается). Процедура стратегии уст- ройства выполняется только один раз в этот момент. При этом ES:BX указывают на вновь созданный заголовок запроса и процедуре нужно просто скопировать их, чтобы впоследствии он мог быть обнаружен при обращении к драйверу. Адреса смещения и сегмента заголовка помещаются в две переменные. В следующем разделе Вы увидите, что при обращении к драйверу, первое что он делает - восстанавливает значения ES:BX, чтобы можно было получить информацию из заголовка запроса. Размер заголовка запроса может меняться, в зависимости от типа сделанного запроса к драйверу (напр. инициализация, вывод данных или возврат статуса). Однако первые 13 байт заголовка всегда одни и те же. Их формат таков: 1. Длина заголовка запроса (DB). 2. Код устройства (DB). Определяет номер для блочных устройств. 3. Код команды (DB). Здесь хранится номер последней посланной драйверу команды. Эти коды перечислены в [7.2.3]. 4. Статус (DW). Статус устанавливается каждый раз при вызове драйвера. Если установлен бит 15, то в младших восьми битах нахо- дится код ошибки. Коды ошибок перечислены в [7.2.3]. 5. Резервная область (8 байтов). Используется MS DOS. 6. Данные необходимые для работы драйвера (переменной длины). Низкий уровень. Вот 5 строк процедуры стратегии устройства. Отмечаем, что две словные переменные, хранящие значения ES и BX, следуют за инст- рукцией RET, как и положено в формате COM. DEV_STRATEGY: MOV CS:KEEP_ES,ES MOV CS:KEEP_BX,BX RET KEEP_CS DW ? KEEP_BX DW ? 7.2.3 Создание обработчика прерывания устройства. Драйвер устройства начинается с двух порций кода, приведенных в предыдущих разделах. За ними должна следовать соответствующая процедура обработки прерывания. На самом деле, это неверно, назы- вать эту процедуру процедурой обработки прерывания, так как она вовсе не обслуживает прерывание и завершается обычной инструкцией RET. Имеется 13 типов функций, которые может выполнять устанавли- ваемый драйвер устройства. Когда драйвер вызывается функцией DOS (скажем функцией 3FH прерывания 21H, которая читает данные из файла или устройства), то функция помещает кодовый номер от 1 до 13 в однобайтное поле по смещению 2 в заголовке запроса (для ввода - кодовый номер 5). Затем управление передается процедуре обработки прерывания драйвера, адоес которой определяется при просмотре заголовка драйвера [7.2.1]. Эта процедура в первую очередь восстанавливает