Физически порт ввода-вывода представляет регистр разрядностью 8, 16 или 32 бита. Доступ к устройствам ввода-вывода, системным устройствам компьютера осуществляется посредством этих регистров, причем каждый из этих регистров должен иметь возможность уникальной идентификации. С этой целью архитектурно процессор поддерживает так называемое адресное пространство ввода-вывода. Адресное пространство ввода-вывода физически независимо от пространства оперативной памяти и имеет ограниченный объем, составляющий 216, или 65 536, адресов ввода-вывода.
Таким образом, порт ввода-вывода можно определить как 8-, 16- или 32-разрядный аппаратный регистр, имеющий определенный адрес в адресном пространстве ввода-вывода. Вся работа системы с устройствами на самом низком уровне выполняется с использованием портов ввода-вывода. На рис. 1 показана сильно упрощенная концептуальная схема управления оборудованием компьютера.
Программа пользователя
Функции операционной системы
Функции BIOS *
Пространство портов ввода-вывода | ||||
Аппаратура компьютера |
Рис. 1. Концептуальная схема управления оборудованием компьютера
Как видно из рисунка, самым нижним уровнем является уровень BIOS, на котором работа с оборудованием ведется напрямую через порты. Тем самым реализуется концепция независимости от оборудования. При замене оборудования потребуется лишь подправить соответствующие функции BIOS, переориентировав их на новые адреса и логику работы портов.
Принципиально управлять устройствами напрямую через порты несложно.
Сведения о номерах портов, их разрядности, формате управляющей информации приводятся в техническом описании устройства. Необходимо знать лишь конечную цель своих действий, алгоритм, в соответствии с которым работает конкретное устройство, и порядок программирования его портов. То есть, фактически, нужно знать, что и в какой последовательности нужно послать в порт (при записи в него) или считать из него (при чтении) и как следует трактовать эту информацию. Для этого достаточно всего двух команд, присутствующих в системе команд процессора:
in <аккумулятор>,<номер_порга> — ввод в аккумулятор из порта с номером <номер_порта>;
out <номер_порта>,<аккумулятор> — вывод содержимого аккумулятора в порт с номером <номер_порта>.
Возможные значения операндов этих команд приведены в приложении. Необходимо отметить, что использовать эти команды вы сможете без проблем только в программе, предназначенной для MS-DOS. При попытке их запуска в программе для Windows вы получите ошибку. Это не означает невозможности запуска исполняемого модуля описанной далее программы в сеансе Windows. Более того, Windows поддержит реализацию полного цикла разработки данной программы, но сделано это будет в специальном режиме работы — режиме виртуального процессора х86.
В качестве примера рассмотрим, как на уровне аппаратуры заставить компьютер издавать звуки через свой внутренний динамик. На большинстве компьютеров читателей это будет некоторый треск. Изменяя различные параметры программы, в идеале, вы можете получить звук, напоминающий сирену.
Вначале мы перечислим, какие аппаратные ресурсы будут задействованы и как ими надо управлять. В большинстве компьютеров есть внутренний динамик. Раньше он использовался для того, чтобы издавать звуки при работе самых различный приложений, вплоть до игровых. Сейчас у него осталась единственная важная функция — воспроизведение звуков, которые генерирует BIOS на этапе тестирования и начальной загрузки.
Несмотря на то что прямой доступ к портам ввода-вывода доступен только из среды MS-DOS, сведения о номерах портов и особенностях работы с ними полезны и при программировании для Windows. Если системы Windows 95/98 практически не закрывают доступ к портам, то в Windows NT/2000/XP любая попытка обращения к ним приведет к возникновению ошибки. Причина в том, что порты являются критически важным ресурсом, и механизмы защиты Windows NT/2000/XP не могут допустить их монополизацию каким-либо приложением. Операционная система Windows NT/2000/XP предоставляет программисту функции API для работы с устройствами, посредством которых в конечном итоге и осуществляется доступ к портам посредством команд IN и OUT. Попытка использовать эти команды в программе пользователя в среде Windows NT/2000/XP приведет к возникновению исключения по недопустимому коду операции. Важно понимать, что на низком уровне управление аппаратурой компьютера ведется с использованием тех же портов, что и при работе в MS-DOS, то есть физика остается, меняется логика управления аппаратурой. Если программе удастся получить уровень привилегий ядра (такой уровень имеют драйверы устройств), то в этом случае она может беспрепятственно использовать команды IN и OUT и работать с устройством так же, как в среде MS-DOS. Существуют специальные программы, которые позволяют получить доступ к портам ввода/вывода из программы пользователя, исключая необходимость написания драйвера.
Как это ни удивительно, но специальной схемы генерации звука для внутреннего динамика нет. Сигнал для управления динамиком формируется в результате совместной работы следующих микросхем:
SS программируемого периферийного интерфейса (ППИ) І8255; таймера
Каналы таймера имеют одинаковую структуру, основу которой составляют три регистра:регистр ввода-вывода разрядностью 8 битов,регистр-фиксатор (latch register) ^регистр-счетчик (counter register), оба по 16 битов. Все ре-гистры связаны между собой следующим образом. В регистр ввода-вывода извне помещается некоторое значение. Источником этого значения может быть либо системное программное обеспечение, либо программа пользователя. Каждый регистр ввода-вывода имеет адрес в адресном пространстве ввода-вывода (номер порта ввода-вывода). Регистр ввода-вывода канала 2 имеет номер порта ввода-вывода 42h. Помещаемые в него значения немедленно попадают в регистр-фиксатор, где значение сохраняется до тех пор, пока в регистр ввода-вывода не будет записано новое значение. Далее описана структура слова состояния:
Бит 0 определяет тип константы пересчета: 0 — константа задана двоичным числом, 1 — константа задана двоично-десятичным (BCD) числом. Константа пересчета — значение, загружаемое извне в регистр-фиксатор; в нашем случае загружаться будет двоичное число, поэтому значение этого поля будет равно 0.
Биты 1-3 определяют режим работы микросхемы таймера. Всего можно определить шесть режимов, но обычно используется третий — 011.
Биты 4-5 определяют тип операции: 00 — передать значение счетчика в регистр-фиксатор (то есть возможны не только операция записи значения в канал, но и извлечение значения регистра-счетчика из него), 10 — записать в регистр-фиксатор только старший байт, 01 - записать в регистр-фиксатор только младший байт, 11 - записать в регистр-фиксатор сначала старший байт, затем младший. В нашем случае значение поля будет 11. Поэтому формирование 16-разрядного регистра-фиксатора через 8-разрядный регистр ввода-вывода производится следующим образом: запись производится в два приема, первый байт из регистра ввода-вывода записывается на место старшего байта регистра-фиксатора, второй байт — на место младшего байта. Нетрудно догадаться, что в регистр ввода-вывода эти байты помещаются командами IN и OUT.
Биты 6-7 определяют номер программируемого канала. В нашем случае они равны 10.
Для формирования любого звука необходимо задать его длительность и высоту. После того как значение из регистра ввода-вывода попало в регистр-фиксатор, оно моментально записывается в регистр-счетчик. Сразу же после этого значение регистра-счетчика начинает уменьшаться на единицу с приходом каждого импульса от системных часов. На выходе любого из трех каналов таймера стоит схема логического умножения. Эта схема имеет два входа и один выход. Значение регистра-счетчика участвует в формировании сигнала на одном из входов схемы логического умножения И. Сигнал на втором входе этой схемы зависит от состояния бита О регистра микросхемы интерфейса с периферией (порт 61h). Когда значение в регистре-счетчике становится равным нулю, на соответствующем входе схемы И формируется единица. И если при этом на втором входе, значение которого зависит от бита О порта 61h, также 1, то импульс от системных часов проходит на выход канала 2. Одновременно с пропуском импульса в канале 2 немедленно производится загрузка содержимого регистра-фиксатора (которое не изменилось, если его не изменили извне) в регистр-счетчик. Весь процесс с уменьшением содержимого регистра-счетчика повторяется заново. Чем меньшее значение загружено в регистр-фиксатор, тем чаще будет происходить обнуление регистра-счетчика и тем чаще импульсы будут проходить на выход канала 2. А это означает большее значение высоты звука. Понятно, что максимальное значение частоты на входе 1 динамика — 1,19 МГц. Таким образом, импульс с выхода канала 2 попадает на динамик, и если на последний подан ток, то возникает долгожданный звук. Подачей тока на динамик управляет бит 1 порта 61h.
Таким образом, наметились три последовательных действия, необходимые для программирования звукового канала таймера (они применимы и к остальным каналам).
1. Посредством порта 43h выбрать канал, задать режим работы и тип операции передачи значения в канал. В нашем случае соответствующее значение будет равно
2. Подать ток на динамик, установив бит 1 порта 61h.
3. Используя регистр АХ, поместить нужное значение в порт 42h, определив тем самым нужную высоту тона.
Далее в листинге приведена программа, реализующая некоторые звуки. Для удобства в программе была использована макрокоманда delay, выполняющая задержку работы программы на заданное время Введенная таким образом макрокоманда в тексте программы синтаксически ничем не отличается от других команд ассемблера, и это позволяет программисту при необходимости расширить стандартный набор команд ассемблера. Стоит отметить, что данная макрокоманда чувствительна к производительности процессора, из-за чего звуки на компьютерах с разными моделями процессоров могут не совпадать. Сегмент кода, как обычно, начинается с настройки сегментного регистра DS на начало сегмента данных. После этого строками 37-38 мы выполняем действия первого этапа — настройку канала 2, которая заключается в записи в регистр управления (порт 43h) байта состояния OB6h. На втором шаге мы должны установить биты 0 и 1 порта 61h. Предварительно необходимо извлечь содержимое этого порта. Это делается для того, чтобы выполнять установку битов 0 и 1, не изменяя содержимого остальных битов порта 61h. Принцип формирования сигнала сирены заключается в том, что в цикле на единицу изменяется содержимое регистра-счетчика и делается небольшая задержка для того, чтобы сигнал некоторое время звучал с нужной высотой. Постепенное повышение, а затем понижение высоты и дает нам эффект сирены. Контроль осуществляется с помощью переменной cnt, содержимое которой увеличивается на 1 и контролируется на равенство 5. Если cnt = 5, то команда СМР устанавливает определенные флаги. Последующая команда условного перехода анализирует эти флаги и в зависимости от их состояния передает управление либо на метку, указанную в качестве операнда этой команды, либо на следующую за JNE команду. В данном случае цикл организуется командой for, которая в качестве операнда имеет имя метки. На эту метку и передается управление при выполнении команды for. Но до того как передать управление, команда for
анализирует содержимое регистра ЕСХ/СХ, и если оно равно нулю, управление передается не на метку, а на следующую за ШОР команду. Если содержимое регистра ЕСХ/СХ не равно нулю, то оно уменьшается на единицу и управление передается на метку. Таким образом в ЕСХ/СХ хранится счетчик цикла..
model small
stack 100h
delay macro time
Local ext,і ter
Push cx
Mov ex.time
ext:
push cx
mov cx,5O00
iter:
loop і ter
pop cx
loop ex
Pop cx
Endm
.data; сегмент данных
tonelow dw 2651; нижняя граница звучания 450 Гц
cnt db 0; счетчик для выхода из программы
temp dw 7; верхняя граница звучания
. code; сегмент кода
maіn:; точка входа в программу
mov ax, @data; связываем регистр ds с сегментом
mov ds, ax; данных через регистр ах
mov ax,0;очищаем ах
ао-заносим слово состояния 10110110b(0B6h) в командный регистр (порт 43h),
raovaL0B6h
out 43h,al
in al,61h
or al,3
out 61h,al
mov cx,2083
musicup:
addtonelow.l delay 1
mov dx,tonelow
bop musicІЇр
mOV CX.2083
rausicdown:
mov ax,temp
out 42h,al
mov al,ah
out 42h,al
sub temp.l
delay 1
loop
musicdown
nosound:
in al,61h
and al,0FCh
out 61h,al
mov dx,2651
mov inc
cmp
jne
exit: mov int
end main
tonelow.dx
cnt
cnt,5
go