Assembler - язык неограниченных возможностей

         

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


Каждый из последовательных портов обменивается данными с процессором через набор портов ввода-вывода: СОМ1 = 03F8h – 03FFh, COM2 = 02F8h – 02FFh, COM3 = 03E8H – 03EFh и COM4 = 02E8h – 02EFh. Имена портов СОМ1 – COM4 на самом деле никак не зафиксированы. BIOS просто называет порт СОМ1, адрес которого (03F8h по умолчанию) записан в области данных BIOS по адресу 0040h:0000h. Точно так же порт COM2, адрес которого записан по адресу 0040h:0002h, COM3 — 0040h:0004h и COM4 — 0040h:0006h. Рассмотрим назначение портов ввода-вывода на примере 03F8h – 03FFh.

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

03F8h для чтения и записи — если старший бит регистра управления линией = 1, это — младший байт делителя частоты порта.

03F9h для чтения и записи — если старший бит регистра управления линией = 0, это — регистр разрешения прерываний (IER):

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

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

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

бит 0: прерывание, если пришли новые данные

03F9h для чтения и записи — если старший бит регистра управления линией = 1, это — старший байт делителя частоты порта. Значение скорости порта определяется по значению делителя частоты (табл. 20).

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



Делитель частоты Скорость
0000h 115 200
0001h 57 600
0002h 38 400
0006h 19 200
000Ch 9 600
0010h 7 200
0018h 4 800
0020h 3 600
0030h 2 400

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


биты 7 – 6: 00 — FIFO отсутствует, 11 — FIFO присутствует

бит 3: тайм-аут FIFO приемника

биты 2 – 1: тип произошедшего прерывания:

11 — состояние BREAK или ошибка. Сбрасывается после чтения из 03FDh

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

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

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

бит 0: 0, если произошло прерывание, 1, если нет

03FAh для записи — регистр управления FIFO (FCR)

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

00 — 1 байт

01 — 4 байта

10 — 8 байт

11 — 16 байт

бит 2 — очистить FIFO приемника

бит 1 — очистить FIFO передатчика

бит 0 — включить режим работы через FIFO

03FBh для чтения и записи — регистр управления линией (LCR)

бит 7: если 1 — порты 03F8h и 03F9H работают, как делитель частоты порта

бит 6: состояние BREAK — порт непрерывно посылает нули

биты 5 – 3: четность:

? ? 0 — без четности

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

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

1 0 1 — фиксированная четность 1

1 1 1 — фиксированная четность 0

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

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

0 — 1 стоп-бит

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

биты 1 – 0: длина слова

00 — 5 бит

01 — 6 бит

10 — 7 бит

11 — 8 бит

03FBH для чтения и записи — регистр управления модемом (MCR)

бит 4: диагностика (выход СОМ-порта замыкается на вход)



бит 3: линия OUT2 — должна быть 1, чтобы работали прерывания

бит 2: линия OUT1 — должна быть 0

бит 1: линия RTS

бит 0: линия DTR

03FCH для чтения — регистр состояния линии (LSR)

бит 6: регистр сдвига передатчика пуст

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

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

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

бит 2: ошибка четности

бит 1: ошибка переполнения (пришел новый байт, хотя старый не был прочитан из 03F8h, при этом старый байт теряется)

бит 0: данные получены и готовы для чтения из 03F8h

03FDh для чтения — регистр состояния модема (MSR)

бит 7: линия DCD (несущая)

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

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

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

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

бит 2: изменилось состояние RI

бит 1: изменилось состояние DSR

бит 0: изменилось состояние CTS

02FFh для чтения и записи — запасной регистр. Не используется контроллером последовательного порта, любая программа может им пользоваться.

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

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

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



.model tiny .code .186 org 100h ; СОМ-программа

; следующие четыре директивы определяют, для какого последовательного порта ; скомпилирована программа (никаких проверок не выполняется - не запускайте этот ; пример, если у вас нет модема на соответствующем порту). Реальная программа ; должна определять номер порта из конфигурационного файла или из командной ; строки COM equ 02F8h ; номер базового порта (COM2) IRQ equ 0Bh ; номер прерывания (INT 0Bh для IRQ3) E_BITMASK equ 11110111b ; битовая маска для разрешения IRQ3 D_BITMASK equ 00001000b ; битовая маска для запрещения IRQ3

start: call init_everything ; инициализация линии и модема main_loop: ; основной цикл ; реальная терминальная программа в этом цикле будет выводить данные из буфера ; приема (заполняемого из обработчика прерывания) на экран, если идет обычная ; работа, в файл, если пересылается файл, или обрабатывать как-то по-другому. ; В нашем примере мы используем основной цикл для ввода символов, хотя лучше это ; делать из обработчика прерывания от клавиатуры mov ah,8 ; Функция DOS 08h int 21h ; чтение с ожиданием и без эха, test al,al ; если введен обычный символ, jnz send_char ; послать его, int 21h ; иначе - считать расширенный ASCII-код, cmp al,2Dh ; если это не Alt-X, jne main_loop ; продолжить цикл, call shutdown_everything ; иначе - восстановить все в ; исходное состояние ret ; и завершить программу

send_char: ; посылка символа в модем ; Реальная терминальная программа должна здесь только добавлять символ в буфер ; передачи и, если этот буфер был пуст, разрешать прерывания "регистр передачи ; пуст". Просто пошлем символ напрямую в порт mov dx,COM ; регистр THR out dx,al jmp short main_loop

old_irq dd ? ; здесь будет храниться адрес старого обработчика

; упрощенный обработчик прерывания от последовательного порта irq_handler proc far pusha ; сохранить регистры mov dx,COM+2 ; прочитать регистр идентификации in al,dx ; прерывания repeat_handler: and ax,00000110b ; обнулить все биты, кроме 1 и 2, mov di,ax ; отвечающие за 4 основные ситуации call word ptr cs:handlers[di] ; косвенный вызов процедуры ; для обработки ситуации mov dx,COM+2 ; еще раз прочитать регистр идентификации in al,dx ; прерывания, test al,1 ; если младший бит не 1, jz repeat_handler ; надо обработать еще одно прерывание, mov al,20h ; иначе - завершить аппаратное прерывание out 20h,al ; посылкой команды EOI (см. главу 5.10.10) рора iret ; таблица адресов процедур, обслуживающих разные варианты прерывания handlers dw offset line_h, offset trans_h dw offset recv_h, offset modem_h



; эта процедура вызывается при изменении состояния линии line_h proc near mov dx,COM+5 ; пока не будет прочитан LSR, in al,dx ; прерывание не считается завершившимся ; здесь можно проверить, что случилось, и, например, прервать связь, если ; обнаружено состояние BREAK ret line_h endp ; эта процедура вызывается при приеме новых данных recv_h proc near mov dx,COM ; пока не будет прочитан RBR, in al,dx ; прерывание не считается завершившимся ; здесь следует поместить принятый байт в буфер приема для основной программы, ; но мы просто сразу выведем его на экран int 29h ; вывод на экран ret recv_h endp ; эта процедура вызывается по окончании передачи данных trans_h proc near ; здесь следует записать в THR следующий символ из буфера передачи и, если ; буфер после этого оказывается пустым, запретить этот тип прерывания ret trans_h endp ; эта процедура вызывается при изменении состояния модема modem_h proc near mov dx,COM+6 ; пока MCR не будет прочитан, in al,dx ; прерывание не считается завершившимся ; здесь можно определить состояние звонка и поднять трубку, определить ; потерю несущей и перезвонить, и т.д. ret modem_h endp irq_handler endp

; инициализация всего, что требуется инициализировать init_everything proc near ; установка нашего обработчика прерывания mov ax,3500h+IRQ ; АН = 35h, AL = номер прерывания int 21h ; получить адрес старого обработчика mov word ptr old_irq,bx ; и сохранить в old_irq mov word ptr old_irq+2,es mov ax,2500h+IRQ ; AH = 25h, AL = номер прерывания mov dx,offset irq_handler ; DS:DX - наш обработчик int 21h ; установить новый обработчик ; сбросить все регистры порта mov dx,COM+1 ; регистр IER mov al,0 out dx,al ; запретить все прерывания mov dx,COM+4 ; MCR out dx,al ; сбросить все линии модема в О mov dx,COM+5 ; и выполнить чтение из LSR, in al,dx mov dx,COM+0 ; из RBR in al,dx mov dx,COM+6 ; и из MSR in al,dx ; на тот случай, если они недавно ; изменялись, mov dx,COM+2 ; а также послать 0 в регистр FCR, mov al,0 ; чтобы выключить FIFO out dx,al



; установка скорости СОМ-порта mov dx,COM+3 ; записать в регистр LCR mov al,80h ; любое число со старшим битом 1 out dx,al mov dx,COM+0 ; теперь записать в регистр DLL mov al,2 ; младший байт делителя скорости, out dx,al mov dx,COM+1 ; а в DLH - mov al,0 ; старший байт out dx,al ; (мы записали 0002h - ; скорость порта 57 600) ; инициализация линии mov dx,COM+3 ; записать теперь в LCR mov al,0011b ; число, соответствующее режиму 8N1 out dx,al ; (наиболее часто используемому) ; инициализация модема mov dx,COM+4 ; записать в регистр MCR mov al,1011b ; битовую маску, активирующую DTR, RTS out dx,al ; и OUT2 ; здесь следует выполнить проверку на наличие модема на этом порту (читать ; регистр MSR, пока не будут установлены линии CTS и DSR или не кончится время), ; а затем послать в модем (то есть поместить в буфер передачи) инициализирующую ; строку, например "ATZ",0Dh

; разрешение прерываний mov dx,COM+1 ; записать в IER - битовую маску, mov al,1101b ; разрешающую все прерывания, кроме ; "регистр передачи пуст" out dx,al in al,21h ; прочитать OCW1 (см. главу 5.10.10) and al,E_BITMASK ; размаскировать прерывание out 21h,al ; записать OCW1 ret init_everything endp

; возвращение всего в исходное состояние shutdown_everything proc near ; запрещение прерываний in al,21h ; прочитать OCW1 or al,D_BITMASK ; замаскировать прерывание out 21h,al ; записать OCW1 mov dx,COM+1 ; записать в регистр IER mov al,0 ; ноль out dx,al ; сброс линий модема DTR и CTS mov dx,COM+4 ; записать в регистр MCR mov al,0 ; ноль out dx,al ; восстановление предыдущего ; обработчика прерывания mov ax,2500h+IRQ ; АН = 25h, AL = номер прерывания lds dx,old_irq ; DS:DX - адрес обработчика int 21h ret shutdown_everything endp end start


Содержание раздела