5.10.2. Последовательный порт

Каждый из последовательных портов обменивается данными с процессором че­рез набор портов ввода-вывода: СОМІ = 03F8h - 03FFh, COM2 = 02F8h - 02FFh, COM3 = 03E8h - 03EFh и COM4 = 02E8h - 02EFh. Имена портов СОМІ -COM4 на самом деле никак не зафиксированы. BIOS просто называет порт СОМІ, адрес которого (03F8h по умолчанию) записан в области данных BIOS по адресу Точно так же порт COM2, адрес которого записан по адресу

0040h:0002h, COM3 - 0040h:0004h и COM4 - 0040h:0006h. Рассмотрим назначе­ние портов ввода-вывода на примере 03F8h -

03F8h для чтения и записи - если старший бит регистра управления линией = О, то это регистр передачи данных (THR или RBR). Передача и прием данных через последовательный порт соответствуют записи и чтению именно в этот порт. 03F8h для чтения и записи - если старший бит регистра управления линией = 1, то это младший байт делителя частоты порта.

03F9h для чтения и записи - если старший бит регистра управления линией = О,

то это регистр разрешения прерываний (IER):

бит 3: прерывание по изменению состояния модема

бит 2: прерывание по состоянию BREAK или ошибке

бит 1: прерывание, если буфер передачи пуст

бит 0: прерывание, если пришли новые данные 03¥9кдля чтения и записи - если старший бит регистра управления линией = 1, то это старший байт делителя частоты порта. Значение скорости порта определя­ется по значению делителя частоты (см. табл. 20).

03FAh для чтения - регистр идентификации прерывания. Содержит информацию о причине прерывания для обработчика:

биты 7-6: 00 - FIFO отсутствует, 11 - FIFO присутствует бит 3: тайм-аут FIFO приемника биты      тип произошедшего прерывания: lib - состояние BREAK или ошибка. Сбрасывается после чтения из 03FDh

- пришли данные. Сбрасывается после чтения из 03F8h

- передачи пуст. Сбрасывается после записи в 03F8h

- изменилось состояние модема. Сбрасывается после чтения из 03FEh

бит 0: 0, если произошло прерывание; если нет 03FAh для записи - регистр управления FIFO (FCR)

биты 7-6: порог срабатывания прерывания о приеме данных:

00b - 1 байт

01b - 4 байта

10b - 8 байт

lib - 14 байт

бит 2: очистить FIFO передатчика бит 1: очистить FIFO приемника бит 0: включить режим работы через FIFO 03FBh для чтения и записи - регистр управления линией (LCR)

бит 7: если 1 - порты 03F8h и 03F9h работают, как делитель частоты порта бит 6: состояние BREAK - порт непрерывно посылает нули биты 5-3: четность:

? ? О - без четности

О О 1 - контроль на нечетность

0 1 1 - контроль на четность

1 О 1 - фиксированная четность 1 111 - фиксированная четность О

? ? 1 - программная (не аппаратная) четность

бит 2: число стоп-бит:

0- 1 стоп-бит

1- 2стоп-битадля 6-, 7-, 8-битных; 1,5 стоп-битадля 5-битных слов биты        длина слова:

00- 5 бит

01- ббит

10- 7 бит

11- 8 бит

03FCh для чтения и записи - регистр управления модемом (MCR) бит 4: диагностика (выход СОМ-порта замыкается на вход) бит 3: линия OUT2 - должна быть 1, чтобы работали прерывания бит 2: линия OUT1 - должна быть О бит 1: линия RTS бит 0: линия DTR

ОЗЕВкдля чтения - регистр состояния линии (LSR) бит 6: регистр сдвига передатчика пуст

бит 5: регистр хранения передатчика пуст - можно писать в 03F8h

бит 4: обнаружено состояние BREAK (строка нулей длиннее, чем старт-бит +

слово + четность + стоп-бит) бит 3: ошибка синхронизации (получен нулевой стоп-бит) бит 2: ошибка четности

бит 1: ошибка переполнения (пришел новый байт, хотя старый не был прочи­тан из 03F8h, при этом старый байт теряется) бит 0: данные получены и готовы для чтения из 03F8h 03FEh для чтения - регистр состояния модема (MSR) бит 7: линия DCD (несущая)

бит 6: линия RI (звонок)

бит 5: линия DSR (данные готовы)

бит 4: линия CTS (разрешение на посылку)

бит 3: изменилось состояние DCD бит 2: изменилось состояние Rбит 1: изменилось состояние

бит 0: изменилось состояние СТБ 03Е¥кдля чтения и записи - запасной регистр. Не используется контроллером последовательного порта, любая программа может им пользоваться.

Таблица 20. Делители частоты последовательного порта

Делитель частоты

Скорость

0001 h

115 200

0002h

57 600

ОООЗп

38 400

0006h

19 200

ОООСп

9 600

0010h

7 200

0018h

4 800

0020h

3 600

0030h

2 400

Итак, первое, что должна сделать программа, работающая с последовательным портом, - проинициализировать его, записав в регистр управления линией (03FBh) число 80h, делитель частоты — в порты 03F8h и 03F9h, режим — в порт 03FBh, а также указав разрешенное прерывание в порту 03F9h. Если программа вообще не пользуется прерываниями - надо записать в этот порт 0.

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

; term2.asm '

; Минимальная терминальная программа, использующая прерывания. ;  Выход - Alt-X

tiny

.code .186

org      lOOh ;

;   Следующие четыре директивы определяют,   для какого последовательного порта

;   скомпилирована программа  (никаких проверок не выполняется - не запускайте этот

;   пример,   если у вас нет модема на соответствующем порту). Реальная

; должна определять номер порта из конфигурационного файла или из командной строки.

COM equ 02F8h ; Номер базового порта (COM2).

IRQ equ OBh ; Номер прерывания   (INT OBh для IRQ3).

E„BITMASK equ 11110111b      ; Битовая маска для разрешения IRQ3.

D_BITMASK equ 00001000b ; Битовая маска для запрещения IRQ3.

ММШі

Сложные

start:

call      init_everything ;  Инициализация линии и модема.

;  Основной цикл.

Реальная терминальная программа в этом цикле будет выводить данные из буфера

приема (заполняемого из обработчика прерывания) на экран, если идет обычная работа, в файл, если пересылается файл, или обрабатывать как-то по-другому.

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

main_loop: ; ; ; ; ;

mov

ah,8

Функция DOS O8h:

int

21h

чтение с ожиданием и без эха.

test

al,al

Если введен обычный символ,

jnz

send_char

послать его.

int

2lh

Иначе - считать расширенный ASCII-код.

-cmp

al,2Dh

Если это не Alt-X,

jne

main_loop

продолжить цикл.

call

shutdown_everything

Иначе - восстановить все в

 

 

исходное состояние

ret

 

и завершить программу.

зепа_сЬаг: ; Отправка символа в модем.

; Реальная терминальная программа должна здесь только добавлять символ в буфер ; передачи и, если этот буфер был пуст, разрешать прерывания "регистр передачи ;  пуст". Просто пошлем символ напрямую в порт.

mov out jmp

old_irq

dx.COM dx, al

short

dd ?

; Регистр THR.

; Здесь будет храниться адрес старого ;обработчика.

; Упрощенный обработчик прерывания от последовательного порта.

irq_handler

pusha

mov

in

repeat_handler:

and

mov

call

proc far

dx,C0M+2 al, dx

ax,00000110b di, ax

word  ptr cs:handlers[di]

mov dx,C0M+2 in al.dx test al.1 jz repeat_handler mov al,20h out 20h,al popa iret

; Таблица адресов процедур, обслуживающих разные варианты прерывания handlers dw        offset line_h,  offset trans h <

dw offset recvji,  offset modem_h

Сохранить регистры.

Прочитать регистр идентификации

прерывания.

Обнулить все биты,  кроме 1 и 2, ■ отвечающие за 4 основные ситуации. Косвенный вызов процедуры для обработки ситуации. Еще раз прочитать регистр идентификации прерывания.

Если младший бит не 1, надо обработать еще одно прерывание. Иначе - завершить аппаратное прерывание посылкой команды Е01  (см.  раздел 5.10.10).

;  Эта процедура вызывается при изменении состояния линии.

line_h proc near

mov       dx,C0M+5 ;  Пока не будет прочитан LSR,

in ;  прерывание считается незавершившимся.

;  Здесь можно проверить,  что случилось,  и,  например,  прервать связь, если

; состояние BREAK.

ret

line_h endp

;  Эта процедура вызывается при приеме новых данных. proc near

mov       dx.COM ;  Пока не будет прочитан RBR,

in ;   прерывание считается

; Здесь следует поместить принятый байт в буфер приема для основной программы,

;  но мы просто сразу выведем его на экран.

int        29h ;   Вывод на экран.

ret

recv_h endp

;   Эта процедура вызывается по окончании передачи данных.

trans_h proc near

;   Здесь следует записать в THR следующий символ из буфера передачи и, если ;  буфер после этого оказывается пустым,   запретить этот тип прерывания. ret

transji endp

;   Эта процедура вызывается при изменении состояния модема.

modem_h proc near

mov       dx,C0M+6 ;  Пока MCR не будет прочитан,

in al.dx ;   прерывание считается незавершившимся.

;  Здесь можно определить состояние звонка и поднять трубку, определить ; потерю несущей и перезвонить, и т. ret

modem_h endp irq_handler endp

; Инициализация всего, что требуется инициализировать.

init_everything proc near

; Установка нашего обработчика прерывания.

mov ax,3500h+IRQ ; AH = 35h, AL = номер прерывания.

int- 21h ; Получитв  адрес  старого обработчика

mov word ptr old_irq,bx и сохранить в old_irq.

mov word ptr old_irq+2,es

mov ax,2500h+IRQ ; AH = 251a,  AL = номер прерывания,

mov dx,offset irq_handler : DS:DX - наш обработчик.

int 21h Установить новый обработчик.

Сбросить все регистры порта.

mov dx,COM+l ; Регистр IER.

mov al,0

out dx,al : Запретить  все прерывания,

mov dx,C0M+4 ; MCR.

out dx,al ; Сбросить все линии модема в О

mov dx,C0M+5 ;-и выполнитв чтение из LSR,

Сложные приемы программирования

in

al, dx

 

mov

dx,COM+0

из RBR

in

al.dx

 

mov

dx,C0M+6

и из MSR

in

al, dx

на тот случай, если они недавно изменялись,

mov

dx,COM+2 ■

а также послать 0 в регистр FCR,

mov

al,0

чтобы выключить FIFO.

out

dx,al

 

Установка

скорости СОМ-порта.

 

mov

dx,C0M+3

Записать в регистр LCR

mov

.

любое число со старшим битом 1.

out

dx, al

 

mov

dx,C0M+0

Теперь записать в регистр DLL

mov

al,2

младший байт делителя скорости,

out

dx, al

 

mov

dx,COM+1

; а в DLH - ■

mov

al,0

; старший байт

out

dx, al

;   (мы записали О002И - скорость порта 57 600).

Инициализация линии.

 

mov

dx,C0M+3

Записать теперь в LCR

mov

al,0011b

число,  соответствующее режиму 8N1

out

dx, al

;   (наиболее часто используемому).

Инициализация модема."

 

mov

dx,C0M+4

Записать в регистр

mov

al,1011b

битовую маску, активизирующую DTR, RTS

out

dx, al

и OUT2.

Здесь следует выполнить проверку на наличие модема на этом порту (читатв регистр Ю, пока не будут установлены линии СТБ и СОТ или не кончится время), а затем послать в модем (то есть поместить в буфер передачи) инициализирующую строку, например "А1/1",ООп.

; Разрешение прерываний.

mov

dx,СОМ+1

; Записатв в Ш битовую маску, разрешающую

mov

al,1101b

; все прерывания, ' кроме "регистр передачи пуст"

out

dx, al

 

in

al,21h

; Прочитатв ОСИП (см. раздел 5.10.10).

and

al,E BITMASK

; Размаскироватв прерывание.

out

21h,al

; Записать ОСШ.

ret

 

 

init_everything endp

; Возвращение всего в исходное состояние.

shutdown_everything proc

; Запрещение прерываний.

in al,21h

or al,D_BITMASK

out 21h,al

mov dx,COM+1

mov al,0

near

Прочитатв ОС1М1.

Замаскироватв прерывание.

Записать ООдП. Записатв в регистр Ш

НОЛЬ.

; Сброс линий модема DTR и CTS.

mov       dx,C0M+4 ;   Записать в регистр MCR

; ноль.

out dx.al

;  Восстановление предыдущего обработчика прерывания.

mov       ax,2500h+IRQ       ;  АН = 25h,  AL = номер прерывания. Ids       dx,olcLirq ;   DS:DX - адрес обработчика,

int        21 h ret

shutdown_everything endp

end start