4.5. Многоголосые мелодии Хотя в предыдущей главе я старательно доказывал Вам тщетность попыток программи- рования музыки на ассемблере, Вы все-таки должны знать основные принципы этого заня- тия. Сейчас я опишу создание многоголосой музыки, то есть музыки, в которой может звучать две или больше ноты одновременно. Первым примером будет подпрограмма, имитирующая двухголосое звучание: 1415. 10 DI ; запрет прерываний 20 LD D,200 ; D=частота 1 30 LD E,50 ; E=частота 2 40 LD H,150 ; H=длительность 50 XOR A ; A=бордюр для голоса 1 (0) 60 EX AF,AF' ; смена регистров A и F на альтернативные 70 XOR A ; A=бордюр для голоса 2 (0) 80 LD C,E ; C=счетчик 1 90 LD B,D ; B=счетчик 2 100 BEEP EX AF,AF' ; смена регистров A и F (смена голоса) 110 DEC C ; C=C-1 120 JR NZ,CONT ; если C<>0, то перейти на CONT 130 LD C,E ; восстановить счетчик 1 140 XOR 16 ; инвертировать бит D4 голоса 1 150 CONT OUT (254),A ; вывести A в порт 254 160 EX AF,AF' ; смена регистров A и F (смена голоса) 170 DEC B ; B=B-1 180 JR NZ,CONT2 ; если B<>0, то перейти на CONT2 190 LD B,D ; восстановить счетчик 2 200 XOR 16 ; инвертировать бит D4 голоса 2 210 CONT2 OUT (254),A ; вывести A в порт 254 220 INC L ; L=L+1 230 JR NZ,BEEP ; если L<>0, то перейти на BEEP 240 NOP ; резерв 250 NOP ; резерв 260 DEC H ; H=H-1 270 JR NZ,BEEP ; если H<>0, то перейти на BEEP 280 EI ; разрешение прерываний 290 RET ; возврат 2 Теперь поясню некоторые детали. Два альтернативных регистра A использу- ются первым и вторым голосами для хранения цвета бордюра и состояния динамика. Странная операция с регистром L перед завершением цикла предназначена для "рас- тягивания" длительности. Если ее убрать, то звучание этой подпрограммы станет нас- только кратковременным, что Вы можете его даже не услышать. Два байта резерва (операции NOP) Вы мо- жете использовать для изменения частоты обоих голосов во время воспроизведения. Для этого вместо "NOP" нужно вставить INC D или DEC D для первого голоса и INC E или DEC E для второго. Эту подпрограмму можно использовать на ассемблере (см. предыдущую главу) или на бейсике (в данном случае параметры сле- дует заносить оператором POKE в следующие ячейки : ADDR+2 - частота 1, ADDR+4 - час- тота 2, ADDR+6 - длительность, где ADDR - адрес расположения подпрограммы в памяти). Следующие две многоголосые подпрограм- мы очень похожи друг на друга, но первая звучит значительно тише и несколько чище второй. Вот они: 1415. 10 DI ; запрет прерываний 20 LD HL,2000 ; HL=длительность 30 LOOP1 LD IX,FRQS ; IX=адрес таблицы частот 40 LD B,5 ; B=число голосов 50 LOOP2 DEC (IX+0) ; байт по адресу IX+0 уменьшить на 1 60 JR NZ,NOSIGN ; если не 0, то перейти на NOSIGN 70 LD A,24 ; A=цвет бордюра + 24 80 OUT (254),A ; вывод A в порт 254 90 LD A,(IX+5) ; обновление счетчика 100 LD (IX+0),A ; текущего канала 110 XOR A ; A=цвет бордюра (0) 120 OUT (254),A ; вывод A в порт 254 130 NOSIGN INC IX ; IX=IX+1 140 DJNZ LOOP2 ; цикл 150 DEC HL ; HL=HL-1 160 LD A,H ; HL= 170 OR L ; 0 ? 180 JR NZ,LOOP1 ; если нет, то цикл 190 EI ; разрешение прерываний 200 RET ; возврат 210 FRQS DEFB 1,1,1,1,1 ; счетчики для 5 каналов 220 DEFB 10,20,30 ; частоты для 230 DEFB 40,50 ; 5 каналов 10 DI ; запрет прерываний 20 LD HL,2000 ; HL=длительность 30 LOOP1 LD IX,FRQS ; IX=адрес таблицы частот 40 LD B,5 ; B=число голосов 50 LOOP2 DEC (IX+0) ; байт по адресу IX+0 уменьшить на 1 60 JR NZ,NOSIGN ; если не 0, то перейти на NOSIGN 70 LD A,(IX+5) ; A=содержимое IX+5 80 XOR 1 ; инвертирование бита D0 90 LD (IX+5),A ; содержимое IX+5=A 100 LD A,0 ; A=цвет бордюра 110 JR Z,NOSIGN ; если D0 содер. IX+5=0, то перейти на NOSIGN 120 OR 24 ; установить в 1 биты D3 и D4 регистра A 130 OUT (254),A ; вывод A в порт 254 140 LD A,(IX+10) ; обновление счетчика 150 LD (IX+0),A ; текущего канала 160 NOSIGN INC IX ; IX=IX+1 170 DJNZ LOOP2 ; цикл 180 DEC HL ; HL=HL-1 190 LD A,H ; HL= 200 OR L ; 0 ? 210 JR NZ,LOOP1 ; если нет, то цикл 220 EI ; разрешение прерываний 230 RET ; возврат 240 FRQS DEFB 1,1,1,1,1 ; счетчики для 5 каналов 250 DEFB 0,0,0,0,0 ; буферы для 5 каналов 260 DEFB 10,20,30 ; частоты для 270 DEFB 40,50 ; 5 каналов 2 Эти подпрограммы заводят счетчики для каждого из каналов и последовательно их уменьшают. Если какой-либо из счетчиков становится равным 0, на динамик выводится сигнал, а в данный счетчик заносится час- тота из таблицы. В данных примерах подпрограммы вос- производят по пять голосов, но Вы може- те легко изменить их число, модифицировав значения в строках 40 и 90 для первой под- программы или 40,70,90 и 140 (в строку 140 надо вставить число голосов, умноженное на 2) - для второй. При этом надо также скор- ректировать таблицы частот. Хотя голосов может быть довольно мно- го (до 85), увлекаться их количеством не стоит, так как чем их больше, тем хуже ка- чество. Впрочем, вряд ли найдется гений, способный написать мелодию на 85 голосов. Если Вы хотите использовать многоголо- сие, но в конкретный момент времени Вам надо воспроизвести меньшее число голосов, чем задано, то неиспользуемым голосам на- до присвоить частоты, совпадающие с часто- тами используемых голосов.