Файл: jourdain_spravochnik_programmista.docx

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 04.07.2020

Просмотров: 3173

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

на ассемблере, хотя, как видно из предыдущих разделов, это тре-

бует затрат на программирование. Кроме того, ассемблер позволяет

генерировать нечистые тона, когда интервал, в течение которого

динамик включен, не равен интервалу, в течение которого он выклю-

чен. Такое нарушение симметрии может приводить к жужжащим и бря-

кающим звукам. Когда отношение этих интервалов составляет, скажем

50 к 1, то получаем жужжание. Если увеличить отношение еще в 10

- 20 раз, то жужжание переходит в отдельные брякающие звуки. В

любом случае звук генерируется микросхемой интерфейса с перифе-

рией 8255, с помощью техники показанной в [2.2.2]. Вот пример

жужжания:


NUMBER_CYCLES EQU 300 ;число переключений динамика

FREQUENCY1 EQU 50 ;время, когда динамик включен

FREQUENCY2 EQU 3200 ;время, когда динамик выключен

PORT_B EQU 61H ;адрес порта B микросхемы 8255

CLI ;запрет прерываний

MOV DX,NUMBER_CYCLES;DX считает длину тона

IN AL,PORT_B ;получаем статус порта

AND AL,11111110B ;отключаем динамик от таймера

NEXT_CYCLE: OR AL,00000010B ;включаем динамик

OUT PORT_B,AL ;посылаем команду

MOV CX,FREQUENCY1 ;задержка для первой части

FIRST_HALF: LOOP FIRST_HALF ;

AND AL,11111101B ;выключаем динамик

OUT PORT_B,AL ;посылаем команду

MOV CX,FREQUENCY2 ;задержка для второй части

SECND_HALF: LOOP SECND_HALF ;

DEC DX ;уменьшаем число циклов

JNZ NEXT_CYCLE ;если 0, то пора кончать

STI ;разрешаем прерывания


Для создания брякающих звуков можно использовать этот же код, но

надо заменить значение FREQUENCY2 на величину около 40000.




2.2.9 Одновременная генерация разных звуков.




Только микросхема генератора звука, имеющаяся в PCjr, позво-

ляет одновременно генерировать разные звуки (см. обсуждение в

[2.2.1]). Однако ассемблер позволяет объединить два способа гене-

рации звука, что создает имитацию одновременной генерации двух

разных звуков. Интерференция этих двух сигналов приводит к слож-

ной форме звуковой волны. Каждый из двух звуков имеет меньшую

громкость, поэтому в результате получается скорее жужжание, чем

два разных голоса. Этот прием реально полезен только для создания

звуковых эффектов.


Низкий уровень.


Надо просто объединить два метода генерации звука, показанные

в [2.2.2] и [2.2.3]. Начните звук через канал 2 микросхемы тайме-

ра. Затем модулируйте выход динамика, за счет бита 1 порта B

микросхемы интерфейса с периферией. Второе действие определяет

продолжительность звука. Не забудьте выключить микросхему таймера

при завершении.


;---начинаем генерацию звука через канал 2 таймера

IN AL,61H ;получаем байт из порта B

OR AL,3 ;устанавливаем младшие два байта

OUT 61H,AL ;посылаем байт обратно

MOV AL,10110110B ;цепочка для командного регистра 8253

OUT 43H,AL ;посылаем в регистр

MOV AX,600H ;счетчик для канала 2

OUT 42H,AL ;посылаем младший байт


MOV AL,AH ;готовим старший байт

OUT 42H,AL ;посылаем старший байт

;---генерируем вторую частоту микросхемой 8255

NUMBER_CYCLES EQU 9000 ;число переключений

FREQUENCY EQU 150 ;задержка для половины цикла

CLI ;запрет прерываний

MOV DX,NUMBER_CYCLES ;DX считает длину тона

IN AL,61H ;получаем статус порта

AND AL,11111111B ;отключаем динамик от таймера

NEXT_CYCLE: OR AL,00000010B ;включаем динамик

OUT 61H,AL ;посылаем назад в порт

MOV CX,FREQUENCY ;задержка на 1/2 цикла

FIRST_HALF: LOOP FIRST_HALF ;

AND AL,11111101B ;выключаем динамик

OUT 61H,AL ;посылаем команду в порт

MOV CX,FREQUENCY ;задержка на 1/2 цикла

SECOND_HALF: LOOP SECOND_HALF ;

DEC DX ;меняем счетчик циклов

JNZ NEXT_CYCLE ;если 0, то пора кончать

STI ;разрешаем прерывания

;---выключение канала 2 микросхемы таймера

IN AL,61H ;получаем статус порта

AND AL,11111100B ;сбрасываем 2 младших бита

OUT 61H,AL ;посылаем байт обратно



Глава 3. Клавиатура.





Раздел 1. Управление клавиатурой.




Клавиатура содержит интелевский микропроцессор, который восп-

ринимает каждое нажатие на клавишу и выдает скан-код в порт A

микросхемы интерфейса с периферией [1.1.1], расположенной на

системной плате. Скан-код это однобайтное число, младшие 7 битов

которого представляют идентификационный номер, присвоенный каждой

клавише. Таблица скан-кодов приведена в [3.3.2]. На всех машинах,

кроме AT, старший бит кода говорит о том, была ли клавиша нажата

(бит = 1, код нажатия) или освобождена (бит = 0, код освобожде-

ния). Например, 7-битный скан-код клавиши B - 48, или 110000 в

двоичной системе. Когда эта клавиша нажимается, то в порт A посы-

лается код 10110000, а когда ее отпустили - код 00110000. Таким

образом, каждое нажатие на клавишу дважды регистрируется в мик-

росхеме 8255. И каждый раз микросхема 8255 выдает подтверждение

микропроцессору клавиатуры. AT работает немного по-другому, посы-

лая в обоих случаях один и тот же скан-код, но предваряя его

кодом F0H, когда клавиша отпускается.

Когда скан-код выдается в порт A, то вызывается прерывание

клавиатуры (INT 9). Процессор моментально прекращает свою работу

и выполняет процедуру, анализирующую скан-код. Когда поступает

код от клавиши сдвига или переключателя, то изменение статуса

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

формируется в код символа, при условии, что он подается при нажа-

тии клавиши (в противном случае, скан-код отбрасывается). Конеч-

но, процедура сначала определяет установку клавиш сдвига и перек-

лючателей, чтобы правильно получить вводимый код (это "a" или

"A"?). После этого введенный код помещается в буфер клавиатуры,

который является областью памяти, способной запомнить до 15 вво-

димых символов, пока программа слишком занята, чтобы обработать

их. На рис. 3-1 показан путь, который проходит нажатие на клавишу


перед тем, как покасть в Вашу программу.

Имеется два типа кодов символов, коды ASCII и расширенные

коды. Коды ASCII - это байтные числа, которые соответствуют рас-

ширенному набору кодов ASCII для IBM PC, который приведен в

[3.3.3]. Для IBM PC этот набор включает обычные символы пишущей

машинки, а также ряд специальных букв и символов псевдографики.

ASCII коды включают также 32 управляющих кода, которые обычно

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

выводятся как символы на экране; однако каждый из них имеет соот-

ветствующий символ, который может быть выведен на дисплей, с

использованием прямой адресации дисплейной памяти [4.3.1]. (Стро-

го говоря, только первые 128 символов являются настоящими симво-

лами ASCII, так как ASCII - это аббревиатура от Американский

стандартный код для обмена информацией. Но программисты обычно

говорят о кодах ASCII, чтобы отличить их от других чисел. Напри-

мер, "ASCII 8" относится к клавише "Backspace", в то время как

"8" - это цифра, которой соответствует ASCII 56).

Второй набор кодов, расширенные коды, присвоен клавишам или

комбинациям клавиш, которые не имеют представляющего их символа

ASCII, таким как функциональные клавиши или комбинации с клавишей

Alt. Расширенные коды имеют длину 2 байта, причем первый байт

всегда ASCII 0. Второй байт - номер расширенного кода, список

которых приведен в [3.3.5]. Например, код 0:30 представляет

Alt-A. Начальный ноль позволяет программе принадлежит ли данный

код набору ASCII или расширенному набору.



Имеется несколько комбинаций клавиш, которые выполняют спе-

циальные функции и не генерируют скан-коды. Эти комбинации вклю-

чают <Ctrl-Break>, <Ctrl-Alt-Del> и <PrtSc>, плюс <SysReq> для AT

и <Ctrl-Alt-стрелка влево, -стрелка вправо, -CapsLock, -Ins> для

PCjr. Эти исключения приводят к заранее предопределенным резуль-

татам [3.3.2]. Все остальные нажатия клавиш должны интерпретиро-

ваться Вашей программой и если они имеют специальное назначение,

скажем сдвинуть курсор влево, то Ваша программа должна содержать

код, обеспечивающий достижение этого эффекта.

К счастью операционная система предоставляет различные проце-

дуры для чтения кодов из буфера клавиатуры, включая средства для

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

делать практически все, что Вы можете пожелать, то практически

бессмысленно писать свои процедуры обработки ввода с клавиатуры и

поэтому в данной главе имеется очень мало примеров программирова-

ния на низком уровне. Однако содержится обсуждение вопроса о том,

как перепрограммировать прерывание клавиатуры.




3.1.1 Очистка буфера клавиатуры.




Программа должна очистить буфер клавиатуры, перед тем, как

выдать запрос на ввод, исключая тем самым посторонние нажатия

клавиш, которые могут к тому времени накопиться в буфере. Буфер

может накапливать до 15 нажатий на клавишу, независимо от того,


являются ли они однобайтными кодами ASCII или двухбайтными расши-

ренными кодами. Таким образом, буфер должен отвести два байта

памяти для каждого нажатия на клавишу. Для однобайтных кодов

первый байт содержит код ASCII, а второй - скан-код клавиши. Для

расширенных кодов первый байт содержит ASCII 0, а второй номер

расширенного кода. Этот код обычно совпадает со скан-кодом клави-

ши, но не всегда, поскольку некоторые клавиши могут комбиниро-

ваться с клавишами сдвига для генерации различных кодов.

Буфер устроен как циклическая очередь, которую называют также

буфером FIFO (первый вошел - первый ушел). Как и любой буфер он

занимает непрерывную область адресов памяти. Однако не имеется

определенной ячейки памяти, которая хранит "начало строки" в

буфере. Вместо этого два указателя хранят позиции головы и хвоста

строки символов, находящейся в буфере в текущий момент. Новые

нажатия клавиш запасаются в позициях, следующих за хвостом (в

более старших адресах памяти) и соответственно обновляется указа-

тель хвоста буфера. После того, как израсходовано все буферное

пространство, новые символы продолжают вставляться, начиная с

самого начала буферной области; поэтому возможны ситуации, когда

голова строки в буфере имеет больший адрес, чем хвост. После того

как буфер заполнен, новые вводимые символы игнорируются, при этом

прерывание клавиатуры выдает гудок через динамик. На рис. 3-2

показаны некоторые возможные конфигурации данных в буфере.

В то время как указатель на голову установлен на первый вве-

денный символ, указатель на хвост установлен на позицию за пос-

ледним введенным символом. Когда оба указателя равны, то буфер

пуст. Чтобы разрешить ввод 15 символов требуется 16-я пустая

позиция, 2 байта которой всегда содержат код возврата каретки

(ASCII 13) и скан-код клавиши <Enter>, равный 28. Эта пустая

позиция непосредственно предшествует голове строки символов. 32

байта буфера начинаются с адреса 0040:001E. Указатели на голову и

хвост расположены по адресам 0040:001A и 0040:001C, соответствен-

но. Хотя под указатели отведено 2 байта, используется только

младший байт. Значения указателей меняются от 30 до 60, что соот-

ветствует позициям в области данных BIOS. Для очистки буфера надо

просто установить значение ячейки 0040:001A равным значению ячей-

ки 0040:001C.

Отметим, что программа имеет возможность вставлять символы в

буфер, завершая строку символом возврата каретки и соответственно

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

перед завершением программы, то при возврате управления в MS DOS

эти символы будут считаны и может быть автоматически загружена

другая программа.


Низкий уровень.


В Бейсике для получения и изменения значений указателей буфера

используются операторы PEEK и POKE:



100 DEF SEG = &H40 'устанавливаем значение сегмента


110 POKE &H1C, PEEK(&H1A) 'выравниваем указатели


Этот метод не самый лучший. Некоторые программы могут создавать

буфер где-нибудь в другом месте памяти, а кроме того, всегда

существует возможность, что посреди строки 110 произойдет преры-

вание клавиатуры, которое изменит указатель хвоста. По этим при-

чинам лучше оставить указатели буфера в покое. Вместо этого,

лучше читать из буфера до тех пор, пока не будет возвращен символ

ASCII 0, показывающий, что буфер пуст:


100 IF INKEY$<>"" THEN 100 'берем следующее если не нуль


Средний уровень.


Функция 0C прерывания 21H выполняет любую из функций ввода с

клавиатуры 1, 6, 7, 8 и A (описанных в этой главе), но перед этим

чистит буфер клавиатуры. Надо просто поместить номер функции

ввода в AL (в этом примере - 1):


;---очистка буфера перед ожиданием нажатия клавиши

MOV AH,0CH ;выбираем функцию DOS 0CH

MOV AL,1 ;выбираем функцию ввода символа

INT 21H ;чистим буфер, ждем ввода


Низкий уровень.


Как и в примере высокого уровня делаем значение указателя на

хвост равным значению указателя на голову. Для избежания влияния

прерывания клавиатуры запрещаем прерывания на время модификации

указателя:


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

CLI ;запрещаем прерывания

SUB AX,AX ;обнуляем регистр

MOV ES,AX ;добавочный сегмент - с начала памяти

MOV AL,ES:[41AH] ;берем указатель на голову буфера

MOV ES:[41CH],AL ;посылаем его в указатель хвоста

STI ;разрешаем прерывания




3.1.2 Проверка символов в буфере.




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

из буфера клавиатуры. Буфер использует два указателя, которые

отмечают голову и хвост очереди символов, находящихся в буфере в

текущий момент. Когда значения этих указателей равны, то буфер

пуст. Надо просто сравнить содержимое ячеек памяти 0040:001A и

0040:001C. (Нельзя просто проверить символ, находящийся в голове

очереди, поскольку буфер организован в виде циклической очереди и

позиция ее головы постоянно меняется [3.1.1].)


Высокий уровень.


Надо просто использовать оператор PEEK для получения значений,

а затем сравнить их:


100 DEF SEG = &H40 'устанавливаем сегмент на начало памяти

110 IF PEEK(&H1A)<>PEEK(&H1C) THEN ... '...то буфер не пуст


Средний уровень.


Функция 0BH прерывания 21H возвращает значение 0FFH в регистре

AL, когда буфер клавиатуры содержит один или более символов и

значение 0, когда буфер пуст:


;---проверка наличия символа в буфере

MOV AH,0BH ;номер функции

INT 21H ;вызываем прерывание 21H

CMP AL,0FFH ;сравниваем с 0FFH

JE GET_KEYSTROKE ;переход если буфер не пуст


Функция 1 прерывания BIOS 16H предоставляет ту же возможность,

но, кроме того, показывает какой символ в буфере. Флаг нуля (ZF)

сбрасывается, если буфер пуст, и устанавливается, если в буфере

имеется символ. В последнем случае копия символа, находящегося в

голове буфера, помещается в AX, но символ из буфера не удаляется.