Страница 16 из 19
2.2.6 Генерация строки тонов, одновременно с другими операциями. Хотя в Бейсике это делается очень просто, на самом деле это нетривиальный трюк программирования в реальном времени. Для реше- ния этой задачи нужно использовать генерацию звука через микрос- хему 8253 [2.2.3], так как метод, использующий микросхему 8255 [2.2.2], занимает процессор. Соответственно, только строки чистых музыкальных тонов могут производиться таким методом - всякого рода звуковые эффекты при этом недоступны. Основная техника прог- раммирования в реальном времени показана в [2.1.7]. Программы, работающие в реальном времени, модифицируют прерывание таймера, которое останавливает процессор 18.2 раз в секунду, чтобы изме- нить показание счетчика времени суток. Расширение процедуры пре- рывания сравнивает новое значение счетчика времени суток со зна- чением, показывающим время завершения генерации тона, и когда это значение достигнуто, прерывает звук, начинает генерацию другого тона и устанавливает время его окончания.
Высокий уровень. Генерация строки звуков одновременно с другими операциями является одной из возможностей очень мощного оператора PLAY, который детально обсуждался в [2.2.5]. Надо просто добавить в начало управляющей строки MB. Это сокращение от Music Background (фоновая музыка); для того чтобы заставить PLAY прекратить все другие операции, пока генерация звуковой строки не будет заверше- на, вставьте MF. В нижеприведенном примере во время рисования и заполнения рамки исполняется гамма (для его работы требуется наличие графических возможностей).
100 PLAY "MB T100 O3 L4;CDEFG>ABC" 'исполняем набор нот 110 LINE (10,10)-(80,80),1,BF 'одновременно рисуем рамку
Низкий уровень. Приведенная процедура является развитием процедуры, показанной в предыдущем разделе, на случай реального времени. Она требует понимания, как перепрограммировать прерывание таймера, что обсуж- далось в [2.1.7]. На эту процедуру должен указывать вектор преры- вания и тогда она будет выполняться 18.2 раза в секунду, в те моменты, когда будет обновляться значение счетчика времени суток BIOS. Обычно, будут выполняться только несколько строчек, которых достаточно, чтобы определить, что время изменения звука еще не наступило, - и процедура освождает процессор для решения других задач. Счетчик времени суток BIOS используется для измерения длитель- ности каждой ноты. При переходе от одной ноты к другой, длитель- ность новой ноты вычисляется как число импульсов счетчика и это значение добавляется к текущему его значению. Каждый раз при вызове процедуры проверяется текущее значение счетчика времени суток, и когда ожидаемое время наконец наступает, то выполняется набор операций по поиску новой ноты, программированию ее частоты в канале 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
|