Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции / Лекция 3 / Лекция 3.doc
Скачиваний:
28
Добавлен:
10.06.2015
Размер:
123.9 Кб
Скачать

Ввод из порта и вывод в порт

Физически порт ввода-вывода представляет регистр разрядностью 8, 16 или 32 бита. Доступ к устройствам ввода-вывода, системным устройствам компьютера осуществляется посредством этих регистров, причем каждый из этих регистров должен иметь возможность уникальной идентификации. С этой целью архитектурно процессор поддерживает так называемое адресное пространство ввода-вывода. Адресное пространство ввода-вывода физически независимо от пространства оперативной памяти и имеет ограниченный объем, составляющий 216, или 65 536, адресов ввода-вывода.

Таким образом, порт ввода-вывода можно определить как 8-, 16- или 32-разрядный аппаратный регистр, имеющий определенный адрес в адресном пространстве ввода-вывода. Вся работа системы с устройствами на самом низком уровне выполняется с использованием портов ввода-вывода.

Принципиально управлять устройствами напрямую через порты несложно.

Сведения о номерах портов, их разрядности, формате управляющей информации приводятся в техническом описании устройства. Необходимо знать лишь конечную цель своих действий, алгоритм, в соответствии с которым работает конкретное устройство, и порядок программирования его портов. То есть, фактически, нужно знать, что и в какой последовательности нужно послать в порт (при записи в него) или считать из него (при чтении) и как следует трактовать эту информацию. Для этого достаточно всего двух команд, присутствующих в системе команд процессора:

li in <аккумулятор>,<номер_порта> — ввод в аккумулятор из порта с номером <но-

мер_порта>;

out <номер_порта>,<аккумулятор> — вывод содержимого аккумулятора в порт

с номером <номер_порта>.

Возможные значения операндов этих команд приведены в приложении. Необходимо отметить, что использовать эти команды вы сможете без проблем только в программе, предназначенной для MS-DOS. При попытке их запуска в программе для Windows вы получите ошибку. Это не означает невозможности запуска исполняемого модуля описанной далее программы в сеансе Windows. Более того, Windows поддержит реализацию полного цикла разработки данной программы, но сделано это будет в специальном режиме работы — режиме виртуального процессора х86.

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

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

В большинстве компьютеров есть внутренний динамик. Раньше он использовался для того, чтобы издавать звуки при работе самых различный приложений, вплоть до игровых. Сейчас у него осталась единственная важная функция — воспроизведение звуков, которые генерирует BIOS на этапе тестирования и начальной загрузки.

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

-программируемого периферийного интерфейса (ППИ) 18255;

-таймера 18253.

Далее описана структура слова состояния:

-Бит 0 определяет тип константы пересчета: 0 — константа задана двоичным числом, 1 константа задана двоично-десятичным (BCD) числом. Константа пересчета — значение, загружаемое извне в регистр-фиксатор; в нашем случае загружаться будет двоичное число, поэтому значение этого поля будет равно 0.

-Биты 1-3 определяют режим работы микросхемы таймера. Всего можно определить шесть режимов, но обычно используется третий, поэтому для нашего случая значение поля — 011.

-Биты 4-5 определяют тип операции: 00 — передать значение счетчика в регистр фиксатор (то есть возможны не только операция записи значения в канал, но и извлечение значения регистра-счетчика из него), 10 — записать в регистр-фиксатор только старший байт, 01 — записать в регистр-фиксатор только младший байт, 11 — записать в регистр-фиксатор сначала старший байт, затем младший.

В нашем случае значение поля будет 11. Поэтому формирование 16-разрядного регистра-фиксатора через 8-разрядный регистр ввода-вывода производится следующим образом: запись производится в два приема, первый байт из регистра ввода-вывода записывается на место старшего байта регистра-фиксатора, второй байт — на место младшего байта. Нетрудно догадаться, что в регистр ввода-вывода эти байты помещаются командами IN и OUT.

-Биты 6-7 определяют номер программируемого канала. В нашем случае они равны 10.

Для формирования любого звука необходимо задать его длительность и высоту. После того как значение из регистра ввода-вывода попало в регистр-фиксатор, оно моментально записывается в регистр-счетчик. Сразу же после этого значение регистра-счетчика начинает уменьшаться на единицу с приходом каждого импульса от системных часов. На выходе любого из трех каналов таймера стоит схема логического умножения. Эта схема имеет два входа и один выход. Значение регистра- счетчика участвует в формировании сигнала на одном из входов схемы логического умножения И. Сигнал на втором входе этой схемы зависит от состояния бита 0 регистра микросхемы интерфейса с периферией (порт 61h). В свое время мы подробно разберемся с логическими операциями, сейчас следует лишь пояснить, что единица на выходе схемы логического умножения может появиться только в одном случае — когда на обоих входах единицы. Когда значение в регистре-счетчике становится равным нулю, на соответствующем входе схемы И формируется такая единица. И если при этом на втором входе, значение которого зависит от бита 0 порта 61h, также 1, то импульс от системных часов проходит на выход канала 2.

Одновременно с пропуском импульса в канале 2 немедленно производится загрузка содержимого регистра-фиксатора (которое не изменилось, если его не изменили извне) в регистр-счетчик. Весь процесс с уменьшением содержимого регистра-счетчика повторяется заново. Теперь вы понимаете, что чем меньшее значение загружено в регистр-фиксатор, тем чаще будет происходить обнуление регистра-счетчика и тем чаще импульсы будут проходить на выход канала 2. А это означает большее значение высоты звука. Понятно, что максимальное значение частоты на входе 1 динамика — 1,19 МГц. Таким образом, импульс с выхода канала 2 попадает на динамик, и если на последний подан ток, то возникает долгожданный звук. Подачей тока на динамик управляет бит 1 порта 61h. Как прервать звучание? Очевидно, для этого возможны два пути: первый — отключить ток, сбросив бит 1 порта 61h, второй — сбросить бит 0 порта 61h. Эти две возможности используют для создания различных звуковых эффектов. Сбрасывая и устанавливая эти биты, мы фактически определяем длительность звучания.

Если вы внимательно следили за всеми рассуждениями, то, наверное, без труда сможете понять, почему первый канал таймера формирует сигналы аппаратного прерывания от таймера 18,2 раза в секунду (на основании этих сигналов программы отслеживают время). Для этого BIOS во время загрузки после включения компьютера загружает в первый канал соответствующее значение.

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

1. Посредством порта 43h выбрать канал, задать режим работы и тип операции передачи значения в канал. В нашем случае соответствующее значение будет равно 10110110-0b6h.

2. Подать ток на динамик, установив бит 1 порта 61h.

3. Используя регистр АХ, поместить нужное значение в порт 42h, определив тем самым нужную высоту тона.

Далее в листинге 7.1, приведена программа, реализующая некоторые звуки.

Многие команды вам уже знакомы, некоторые мы пока еще не рассматривали, поэтому поясним их функциональное назначение. Подробно они будут рассмотрены в последующих главах. Для удобства в программе была использована макрокоманда delay, выполняющая задержку работы программы на заданное время. Сейчас только отметьте для себя, что введенная таким образом макрокоманда в тексте программы синтаксически ничем не отличается от других команд ассемблера, и это позволяет программисту при необходимости расширить стандартный набор команд ассемблера. Введите текст макрокоманды delay (строки 13-25) и воспринимайте ее чисто по функциональному назначению (задержка выполнения программы на промежуток времени, задаваемый значением ее операнда). Стоит отметить, что данная макрокоманда чувствительна к производительности процессора, из-за чего звуки на компьютерах с разными моделями процессоров могут не совпадать. Сегмент кода, как обычно, начинается с настройки сегментного регистра DS (строки 32-33) на начало сегмента данных. После этого строками 37-38 мы выполняем действия первого этапа — настройку канала 2, которая заключается в записи в регистр управления (порт 43h) байта состояния OB6h. На втором шаге мы должны установить биты 0 и 1 порта 61h. Предварительно необходимо извлечь содержи мое этого порта. Это делается для того, чтобы выполнять установку битов 0 и 1, не изменяя содержимого остальных битов порта 61h (строки 39-41). Принцип формирования сигнала сирены заключается в том, что в цикле на единицу изменяется содержимое регистра-счетчика и делается небольшая задержка для того, чтобы сигнал некоторое время звучал с нужной высотой. Постепенное повышение, а затем понижение высоты и дает нам эффект сирены. Строки 43-53 соответствуют циклу, в теле которого высота повышается, а строки 55-62 — циклу понижения тона. Оба цикла повторяются последовательно 5 раз. Контроль осуществляется с помощью переменной cnt, содержимое которой увеличивается на 1 в строке 69 и контролируется на равенство 5 в строках 71-72. Если cnt = 5, то команда СМР устанавливает определенные флаги. Последующая команда условного перехода ONE анализирует эти флаги и в зависимости от их состояния передает управление либо на метку, указанную в качестве операнда этой команды, либо на следующую за JNE команду. Цикл в программе ассемблера можно организовать несколькими способами; все они будут подробно рассмотрены в главах 10 и 11. В данном случае цикл организуется командой LOОР, которая в качестве операнда имеет имя метки.

На эту метку и передается управление при выполнении команды LOОР. Но до того как передать управление, командаLOОР анализирует содержимое регистра ЕСХ/СХ, и если оно равно нулю, управление передается не на метку, а на следующую заLOОР команду. Если содержимое регистра ЕСХ/СХ не равно нулю, то оно уменьшается на единицу и управление передается на метку. Таким образом в ЕСХ/СХ хранится счетчик цикла. В нашей программе он загружается в ЕСХ/СХ в строках 42 и 54.

Листинг 7.1. Реализация сирены

<1>;-----------prg_7_1.asm---------

<2> ;Программа, имитирующая звук сирены.

<3> ;Изменение высоты звука от 450 до 2100 Гц.

<4>;Используется макрос delay (задержка).

<5>;При необходимости

<6>;можно поменять значение задержки (по умолчанию - для процессора Pentium).

<7>;

<8> masm

<9> modelsmall

<10> stack100h

<11> ;макрос задержки, его текст ограничивается директивами macro и endm.

<12> ;На входе — значение задержки (в икс)

<13> delay macro time

<14> local ext.iter

<15> push cx

<16> mov cx.time

<17> ext:

<18> push cx

<19> mov cx,500

<20> iter:

<21> loop iter

<22> pop cx

<23> loop ext

<24> popcx

<25> endm

<26> .data; сегмент данных

<27> tonelowdw; нижняя граница звучания 450 Гц

<28> cntdb0 ; счетчик для выхода из программы

<29> tempdw? ; верхняя граница звучания

<30> .code; сегмент кода

<31> main: ; точка входа в программу

<32> movax,@data; связываем регистр ds с сегментом

<33> movds,ax; данных через регистр ах

<34> mov ax, 0 ; очищаем ах

<35> go:

<3б> ;заносим слово состояния 10110110b(0B6h) в командный регистр (порт 43h)

<37> mov al,0B6h

<38> out 43h,al

<39> inal,61h;получим значение порта 61h в al

<40> oral,3 ; инициализируем динамик и подаем ток в порт 61h

<41> out61h,al

<42> movex,2083 ;количество шагов ступенчатого изменения тона

<43> musicup:

<44> ;в ах значение нижней границы частоты

<45> movax.tonelow

<46> out42h,al;в порт 42п младшее слово ax:al

<47> xchgal.ah;обмен между al и ah

<48> out42h.al;в порт 42h старшее слово ах:ah

<49> addtonelow.l;повышаем тон

<50> delay1 ;задержка на 1 мкс

<51> movdx.tonelow;в dx текущее значение высоты

<52> movtemp.dx;temp — верхнее значение высоты

<53> loopmusicup;повторить цикл повышения

<54> movex,2083 ;восстановить счетчик цикла

<55> musicdown:

<56> movax,temp;в ах верхнее значение высоты

<57> out42h,al;в порт 42h младшее слово ax:al

<58> moval.ah;обмен между al и ah

<59> out42h,al;в порт 42h старшее слово ax:ah

<60> subtemp.l;понижаем высоту

<61> delay1 ;задержка на 1 мкс

<62> loopmusicdown;повторить цикл понижения

<63> nosound:

<64> inal,61h;получим значение порта 61h в AL

<65> andal.0FCh;выключить динамик

<66> out61h,al;в порт 61h

<67> movdx,2651 ;для последующих циклов

<68> mov tonelow.dx

<69> inc cnt ;увеличиваем счетчик проходов, то есть

<70> ; количество звучаний сирены

<71> cmpcnt,5 ;5 раз ?

<72> jnego;если нет, идти на метку go

<73> exit:

<74> mov ах,4с0011 ;стандартный выход

<75> int 21h

<76> end main ;конец программы

увеличиваем счетчик проходов, то есть

Среди файлов, прилагаемых к книге, в каталоге данной главы есть еще один пример использования команд ввода из порта и вывода в порт — это редактор CMOS-памяти. На данном этапе изучения ассемблера не стоит пытаться разобраться с ним, но впоследствии обязательно стоит к нему вернуться.

Соседние файлы в папке Лекция 3