Глава 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 Создание плавного перехода тонов.-~>