Глава 2. Таймеры и звук.

Раздел 2. Создание звука.

2.2.6 Генерация строки тонов, одновременно с другими операциями.

Хотя в Бейсике это делается очень просто, на самом деле это нетривиальный трюк программирования в реальном времени. Для решения этой задачи нужно использовать генерацию звука через микросхему 8253 {2.2.3}, так как метод, использующий микросхему 8255 {2.2.2}, занимает процессор. Соответственно, только строки чистых музыкальных тонов могут производиться таким методом - всякого рода звуковые эффекты при этом недоступны. Основная техника программирования в реальном времени показана в {2.1.7}. Программы, работающие в реальном времени, модифицируют прерывание таймера, которое останавливает процессор 18.2 раз в секунду, чтобы изменить показание счетчика времени суток. Расширение процедуры прерывания сравнивает новое значение счетчика времени суток со значением, показывающим время завершения генерации тона, и когда это значение достигнуто, прерывает звук, начинает генерацию другого тона и устанавливает время его окончания.

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

Приведенная процедура является развитием процедуры, показанной в предыдущем разделе, на случай реального времени. Она требует понимания, как перепрограммировать прерывание таймера, что обсуждалось в {2.1.7}. Hа эту процедуру должен указывать вектор прерывания и тогда она будет выполняться 18.2 раза в секунду, в те моменты, когда будет обновляться значение счетчика времени суток BIOS. Обычно, будут выполняться только несколько строчек, которых достаточно, чтобы определить, что время изменения звука еще не наступило, - и процедура освождает процессор для решения других задач.

Счетчик времени суток BIOS используется для измерения длительности каждой ноты. При переходе от одной ноты к другой, длительность новой ноты вычисляется как число импульсов счетчика и это значение добавляется к текущему его значению. Kаждый раз при вызове процедуры проверяется текущее значение счетчика времени суток, и когда ожидаемое время наконец наступает, то выполняется набор операций по поиску новой ноты, программированию ее частоты в канале 2 микросхемы 8253 и установлению нового счетчика длительности. Добавочный код требуется для обработки специальных случаев первой и последней нот в строке.
;---в сегменте данных
BEAT DB 10,9,8,7,6,5,4,3,2 ;длительность нот
FREQUENCY DW 2280,2031,1809,1709 ;таблица частот
DW 1521,1355,1207,1139 ;
MELODY DB 1,2,3,4,5,6,7,8,0FFH ;номер частоты в таблице
HOLDIP DW 0 ;запоминаем оригинальный
HOLDCS DW 0 ;вектор прерывания
SOUND_NOW? DB 1 ;звук включен?
FIRST_NOTE? DB 1 ;первая нота?
END_NOTE DW 0 ;счетчик конца ноты
WHICH_NOTE DW 0 ;указатель на текущую ноту
;---инициализация вектора прерывания
;изменение вектора
PUSH DS ;сохраняем регистр
MOV AX,SEG MELODY2 ;сегмент процедуры
MOV DS,AX ;помещаем в DS
MOV DX,OFFSET MELODY2 ;смещение процедуры
MOV AL,1CH ;номер вектора прерывания
MOV AH,25H ;функция установки вектора
INT 21H ;изменение вектора
POP DS ;восстановление регистра
;
;---программа работает дальше, постоянно вызывая процедуру ;
;---в конце программы восстанавливаем вектор прерывания
MOV DX,0FF53H ;восстанавливаем оригинальные
MOV AX,0F000H ;значения для вектора 1CH
MOV DS,AX ;
MOV AL,1CH ;номер прерывания
MOV AH,25H ;функция установки вектора
INT 21H ;восстанавливаем вектор
RET ;
;---это само прерывание
MELODY2 PROC FAR
PUSH AX ;сохраняем изменяемые регистры
PUSH BX ;
PUSH CX ;
PUSH DX ;
PUSH DI ;
PUSH SI ;
PUSH DS ;
MOV AX,SS:[114] ;берем начальный DS со стека
MOV DS,AX ;восстанавливаем его
CMP SOUND_NOW?,1 ;нужен ли звук?
JE PLAY_IT ;если нет, то выход из прерывания
JMP NOT_NOW ;
PLAY_IT: CMP FIRST_NOTE?,0 ;это первая нота?
JE TIME_CHECK ;если нет, то на установку времени
;---инициализация
PORT_B EQU 61H ;определяем имена портов
COMMAND_REG EQU 43H ;
LATCH2 EQU 42H ;
IN AL,PORT_B ;берем статус порта B
OR AL,00000011B ;разрешаем динамик и таймер
OUT PORT_B,AL ;посылаем байт обратно
MOV SI,0 ;указатель на строки
MOV AL,0B6H ;инициализация канала 2 таймера
OUT COMMAND_REG,AL ;посылаем в командный регистр
MOV FIRST_NOTE?,0 ;сбрасываем флаг первой ноты
;---ищем ноту, получаем ее частоту, посылаем в канал 2
NEXT_NOTE: LEA BX,MELODY ;берем смещение строки мелодии
MOV SI,WHICH_NOTE ;указатель на текущую ноту
MOV AL,[BX][SI] ;код текущей ноты строки
CMP AL,0FFH ;проверяем признак конца
JE NO_MORE ;если да, то на конец
CBW ;иначе в словный формат
;получаем частоту
MOV BX,OFFSET FREQUENCY ;смещение таблицы частот
DEC AX ;начинаем отсчет с нуля
SHL AX,1 ;умножаем на 2, т.к. словная
MOV DI,AX ;адресуемся через DI
MOV DX,[BX][DI] ;получаем частоту из таблицы
;начинаем исполнение ноты
MOV AL,DL ;готовим младший байт частоты
OUT LATCH2,AL ;посылаем в регистр задвижки
MOV AL,DH ;готовим старший байт
OUT LATCH2,AL ;посылаем его
;---пустой цикл, определяющий длительность нот
TIME_IT: MOV AH,0 ;фнукция чтения счетчика
INT 1AH ;получаем значение счетчика
MOV BX,OFFSET BEAT ;смещение строки длин нот
MOV CL,[BX][SI] ;длительность текущей ноты
MOV CH,0 ;
MOV BX,DX ;младшее слово значения счетчика
ADD BX,CX ;добавляем длину в импульсах
MOV END_NOTE,BX ;запоминаем время окончания
TIME_CHECK: MOV AH,0 ;функция чтения счетчика
INT 1AH ;читаем счетчик
CMP DX,END_NOTE ;сравниваем с нужным
JNE NOT_NOW ;если неравно, то выходим
MOV SI,WHICH_NOTE ;иначе, берем следующую ноту
INC SI ;увеличиваем номер ноты
MOV WHICH_NOTE,SI ;запоминаем его
JMP NEXT_NOTE ;начинаем следующую ноту
;---завершение процедуры
NO_MORE: IN AL,PORT_B ;берем статус порта B
AND AL,0FCH ;выключаем динамик
OUT 61H,AL ;возвращаем байт
MOV SOUND_NOW?,0 ;восстанавливаем переменные
MOV FIRST_NOTE?,1 ;
NOT_NOW: POP DS ;восстанавливаем регистры
POP SI ;
POP DI ;
POP DX ;
POP CX ;
POP BX ;
POP AX ;
IRET ;возврат из прерывания
MELODY2 ENDP


<~-2.2.5 Генерация набора тонов.
Содержание
2.2.7 Создание плавного перехода тонов.-~>

Сайт управляется системой uCoz