6.2. Программирование
Все звуковые эффекты и музыка програм-
мируются путем постоянной смены значений
регистров с необходимыми задержками. Осу-
ществить это может, например, такая под-
программа:
1415.
10 LD HL,60000 ; HL=адрес данных
20 LOOP LD A,(HL) ; A=байт данных
30 INC HL ; HL=HL+1
40 CP 255 ; A=255 ?
50 RET Z ; если да, то возврат
60 CP 16 ; A=16 ?
70 JR NZ,REG ; если нет, то перейти на REG
80 LD B,(HL) ; B=длительность паузы
90 PAUSE HALT ; ожидание прерывания
100 DJNZ PAUSE ; цикл
110 JR CONT ; перейти на CONT
120 REG LD BC,65533 ; BC=адрес порта регистра
130 OUT (C),A ; записать номер регистра
140 LD B,191 ; BC=адрес порта данных
150 LD A,(HL) ; A=значение регистра
160 OUT (C),A ; записать данные в регистр
170 CONT INC HL ; HL=HL+1
180 JR LOOP ; переход к началу
2
Эта программа довольно примитивна, и
для воспроизведения музыки не очень подхо-
дит, но для создания простых эффектов - в
самый раз. Перед ее запуском не забудьте
подготовить блок данных по адресу 60000,
состоящий из пар данных и заканчивающийся
числом 255. Первым значением в каждой па-
ре должен быть номер регистра, а вторым -
число, которое нужно записать в этот ре-
гистр. Кроме того, если первое значение
равно 16, то второе интерпретируется как
задержка (в пятидесятых долях секунды).
Для воспроизведения музыки используют-
ся гораздо более сложные подпрограммы, как
правило, работающие во втором режиме пре-
рываний. Данные для этих подпрограмм обыч-
но хранятся в значительно более удобной
форме, раздельно для каждого из трех кана-
лов. Привести полностью хотя бы простей-
ший пример такой подпрограммы в этой кни-
ге не представляется возможным из-за его
сложности, но, если Вы умеете обращаться с
ассемблером, Вам ничего не стоит сочинить
такую подпрограмму, а я могу подсказать,
как это сделать.
Сначала разберемся с форматом данных. Я
предлагаю следующую систему: мелодия бу-
дет задаваться тремя (по числу голосов)
основными блоками данных. Так как практи-
чески любая мелодия состоит из одинаковых
фрагментов, повторяющихся в разном поряд-
ке, логично было бы в главных блоках зада-
вать не саму мелодию, а адреса таких фраг-
ментов. Эти фрагменты обычно называют пат-
тернами (pattern - трафарет, шаблон).
Итак, в главных блоках могут содер-
жаться следующие двухбайтовые значения:
65535 (#FFFF) - конец мелодии
0 (#0000) - начало цикла
addr (#XXXX) - адрес следующего паттерна
Код "начало цикла" (0) отмечает место,
с которого начнет воспроизводиться мело-
дия при повторении.
Теперь займемся паттернами. Не вда-
ваясь в теорию, приведу разработанный мной
формат:
128 (#80) - конец паттерна
129,n (#81,#XX) - установить длительность n
130,n (#82,#XX) - шум с частотой n (0...31)
131,n1,n2 (#83,#XX,#XX) - шум с частотой n (0...31) +
нота (0...100)
132,n1,n2 (#84,#XX,#XX) - прямое задание частоты тона
(0...4095)
133,addr (#85,#XXXX) - задание блока изменения частоты
тона
134,addr (#86,#XXXX) - задание блока изменения частоты
шума
135,addr (#87,#XXXX) - задание блока изменения
громкости
136,n1,n2,n3 (#88,#XX,#XXXX) - управление генератором
огибающей
0...100 (#00...#64) - ноты от ЛЯ субконтроктавы
Поясню некоторые коды:
129 - Как Вы, наверное, заметили, при за-
дании ноты ее длительность не указы-
вается. Дело в том, что использует-
ся длительность, установленная зара-
нее с помощью этой команды. Дли-
тельность измеряется в пятидесятых
долях секунды.
131 - С помощью этого кода Вы можете од-
новременно воспроизводить тон и шум.
133,134,135 - Эти коды задают дополни-
тельные блоки данных, указывающие
способ изменения частоты тона, час-
тоты шума и громкости на протяжении
звучания ноты. Если после кодов 133
или 134 вместо адреса блока находит-
ся 0, то изменение частот отключает-
ся. Число от 0 до 15 после кода 135
указывает на необходимость поддержа-
ния постоянного уровня громкости,
соответствующего этому числу.
136 - Этот код управляет генератором оги-
бающей. После него должно нахо-
диться однобайтовое число от 0 до 7,
указывающее на форму огибающей в
соответствии с таблицей 5 и двухбай-
товое число, задающее период измене-
ния огибающей.
0...100 - Эти коды задают ноты. Код 0
соответствует ноте ЛЯ субконтрокта-
вы, 1 - ЛЯ#, 2 - СИ и т.д.
В блоке, описывающем изменение частоты
тона, используются следующие значения:
128 (#80) - конец блока
-127...127 (#81...#79) - смещения частоты
Блок изменения частоты шума будет зада-
ваться в следующем формате:
128 (#80) - конец блока
-31...31 (#E1...#1F) - смещения частоты
А блок изменения громкости пусть зада-
ется так:
128 (#80) - конец блока
0...15 (#00...#0F) - значения громкости
Вполне логично было бы сделать эту про-
цедуру работающей во втором режиме преры-
ваний. Исходя из этого, а также из предло-
женного формата данных, можно понять, что
для каждого из каналов понадобится массив
переменных. Я предлагаю следующий его фор-
мат:
смещение размер значение
0 2 начальный адрес основного блока данных
2 2 текущий адрес в основном блоке данных
4 2 начальный адрес блока изменения частоты тона
6 2 текущий адрес в блоке изменения частоты тона
8 2 начальный адрес блока изменения частоты шума
10 2 текущий адрес в блоке изменения частоты шума
12 2 начальный адрес блока изменения громкости
14 2 текущий адрес в блоке изменения громкости
16 2 текущий адрес в текущем паттерне
18 1 значение текущей длительности
19 1 счетчик длительности
20 1 число оставшихся повторений
Кроме того, понадобится еще по три бай-
та на каждый канал для хранения темпа,
счетчика темпа и флага разрешения звука.
В этот пакет будут входить следующие
процедуры:
SINIT - Инициализация таблиц, подключение
второго режима прерываний.
SSTOP - Выключение сопроцессора, восстано-
вление стандартного режима преры-
ваний.
SNEW - Запуск всех трех каналов. При вы-
зове этой подпрограммы в регис-
трах HL, DE и BC должны быть адре-
са главных блоков данных для кана-
лов A, B и C соответственно. В ре-
гистре A должно находиться число
повторений мелодии от 1 до 254 или
255, если Вы хотите, чтобы мело-
дия повторялась бесконечно.
SNEWA - Запуск канала A. В регистре HL
должен быть адрес блока данных, а
в регистре A - число повторений
(аналогично SNEW). Работу каналов
B и C эта процедура не затрагива-
ет.
SNEWB - Запуск канала B. Все аналогично
SNEWA.
SNEWC - Запуск канала C. Все аналогично
SNEWA.
MUTE - Запрет/разрешение работы. В регис-
тре A должен быть номер канала от
0 до 2 или 3, если Вы обращаетесь
ко всем каналам. В регистре B дол-
жен быть код режима работы: 0 -
остановка, 1 - беззвучная работа,
2 - воспроизведение.
STATUS - Получение состояния канала. В ре-
гистре A должен быть номер канала
от 0 до 2. По возвращению из про-
цедуры STATUS регистр A содержит
код режима работы выбранного кана-
ла (аналогично B в MUTE).
TEMPO - Установка темпа. В регистре A дол-
жен быть номер канала от 0 до 2
или 3, если Вы обращаетесь ко всем
каналам. В регистре B должно нахо-
диться значение темпа.
Итак, начало пакета может выглядеть так:
1415.
10 ORG 60000
20 JP SINIT
30 JP SSTOP
40 JP SNEW
50 JP SNEWA
60 JP SNEWB
70 JP SNEWC
80 JP MUTE
90 JP STATUS
2
Процедура TEMPO может выглядеть так:
1415.
100 TEMPO DI
110 PUSH HL
120 PUSH AF
130 LD HL,TEMPS ; HL=указатель на темпы
140 CP A,3
150 JR Z,TEMP3
160 ADD A,L
170 LD L,A
180 JR NC,TEMP1
190 INC H
200 TEMP1 LD (HL),B
210 INC HL
220 INC HL
230 INC HL
240 LD (HL),B
250 TEMP2 POP AF
260 POP HL
270 EI
280 RET
290 TEMP3 PUSH DE
300 LD D,6
310 TEMP4 LD (HL),B
320 INC HL
330 DEC D
340 JR NZ,TEMP4
350 POP DE
360 JR TEMP2
2
Раз уж пошли обращения к данным, надо
привести строки с их описанием:
1415.
370 CHAN_A DEFS 21 ; массив переменных для канала A
380 CHAN_B DEFS 21 ; массив переменных для канала B
390 CHAN_C DEFS 21 ; массив переменных для канала C
400 MUTS DEFS 3 ; флаги разрешения звука
410 TEMPS DEFS 3 ; темпы
420 CURTS DEFS 3 ; счетчики темпов
430 AYREGS DEFS 14 ; копии регистров сопроцессора
440 ENVS DEFB 0,4,11,13,8,12,14,10 ; формы огибающей
450 SVOLS DEFW #8000,#8001,#8002 ; таблицы измене-
460 DEFW #8003,#8004,#8005 ; ния громкости
470 DEFW #8006,#8007,#8008 ; для стандартных
480 DEFW #8009,#800A,#800B ; значений
490 DEFW #800C,#800D,#800E
500 DEFW #800F
510 NOTES DEFW ... ; сюда необходимо записать все значе-
; ния из таблицы 9 от левого верхнего
; угла в порядке сверху - вниз, спра-
; ва - налево
2
Строка 430 содержит область данных, ко-
торая используется для вывода звука. Сна-
чала в ней формируются значения всех ре-
гистров сопроцессора с помощью следующей
подпрограммы:
1415.
520 SETAY PUSH HL
530 PUSH AF
540 LD AL,AYREGS
550 ADD A,L
560 LD L,A
570 JR NC,SETAY1
580 INC HL
590 SETAY1 LD (HL),B
600 POP AF
610 POP HL
620 RET
2
Ей надо передать номер регистра в A и
его значение в B.
Затем содержимое этой области копирует-
ся в реальные регистры сопроцессора другой
подпрограммой:
1415.
630 AYOUT PUSH HL
640 PUSH DE
650 PUSH BC
660 LD HL,AYREGS+13
670 LD D,13
680 AYOUT1 LD BC,65533
690 OUT (C),D
700 LD B,191
710 LD E,(HL)
720 OUT (C),E
730 DEC HL
740 DEC D
750 JP P,AYOUT1 ; если D>=0, то перейти на
AYOUT1
760 POP BC
770 POP DE
780 POP HL
790 RET
2
Любой из регистров в этой области можно
также прочитать:
1415.
800 GETAY PUSH HL
810 PUSH AF
820 LD HL,AYREGS
830 ADD A,L
840 LD L,A
850 JR NC,GETAY1
860 INC H
870 GETAY1 LD B,(HL)
880 POP AF
890 POP HL
900 RET
2
Этой подпрограмме нужно передать номер
требуемого регистра в A и она вернет его
значение в B.
Строка 440 понадобится для дешифрации
кода огибающей. Если любое число из табли-
цы 5 прибавить к метке ENVS и по получен-
ному адресу прочитать один байт, Вы полу-
чите значение, которое надо записать в R13.
Строка 450 будет нужна для дешифрации
стандартных уровней громкости. Для этого
надо число от 0 до 15 умножить на 2 (мож-
но использовать команду SLA) и прибавить к
метке SVOLS. Полученное значение нужно ис-
пользовать в качестве адреса блока измене-
ния громкости.
Строка 510 будет полезна для дешифра-
ции нот. Если код ноты (от 0 до 100) умно-
жить на 2 (команда SLA) и прибавить к мет-
ке NOTES, а по полученному адресу считать
двухбайтовое число, Вы получите значения
младшего и старшего регистров частоты.
Теперь займемся основными процедурами:
1415.
910 STATUS PUSH HL
920 LD HL,MUTS
930 ADD A,L
940 LD L,A
950 JR NC,STAT1
960 INC H
970 STAT1 LD A,(HL)
980 POP HL
990 RET
2
Эта маленькая, но полезная процедура
поможет программисту узнать, в каком сос-
тоянии находится тот или иной канал. Нап-
ример, чтобы найти, какой из них свободен.
Следующая процедура - MUTE:
1415.
1000 MUTE DI
1010 PUSH HL
1020 PUSH AF
1030 LD HL,MUTS
1040 CP A,3
1050 JR Z,MUT2
1060 ADD A,L
1070 LD L,A
1080 JR NC,MUT1
1090 INC H
1100 MUT1 LD (HL),B
1110 POP AF
1120 POP HL
1130 EI
1140 RET
1150 MUT2 LD (HL),B
1160 INC HL
1170 LD (HL),B
1180 INC HL
1190 JR MUT1
2
Эта процедура понадобится, например,
для временной остановки работы одного из
каналов.
Теперь - процедуры инициализации кана-
лов:
1415.
1200 SNEWC PUSH IX
1210 LD IX,CHAN_C
1220 PUSH BC
1230 LD B,2
1240 JR SNEW1
1250 SNEWB PUSH IX
1260 LD IX,CHAN_B
1270 PUSH BC
1280 LD B,1
1290 JR SNEW1
1300 SNEWA PUSH IX
1310 LD IX,CHAN_A
1320 PUSH BC
1330 LD B,0
1340 SNEW1 DI
1350 PUSH BC
1360 PUSH DE
1370 PUSH HL
1380 PUSH IX
1390 POP HL
1400 PUSH HL
1410 POP DE
1420 INC DE
1430 LD BC,20
1440 LD (HL),B
1450 LDIR
1460 POP HL
1470 PUSH HL
1480 LD (IX+20),A
1490 LD (IX+0),L
1500 LD (IX+1),H
1510 LD (IX+19),0
1520 LD E,(HL)
1530 INC HL
1540 LD D,(HL)
1550 INC HL
1560 LD (IX+2),L
1570 LD (IX+3),H
1580 LD (IX+16),E
1590 LD (IX+17),D
1600 LD HL,SVOLS+30
1610 LD (IX+12),L
1620 LD (IX+13),H
1630 LD (IX+14),L
1640 LD (IX+15),H
1650 LD (IX+18),13 ; длительность по умолчанию
= 1/4
1660 POP HL
1670 POP DE
1680 POP BC
1690 LD A,B
1700 LD B,2
1710 CALL MUTE
1720 LD B,1
1730 CALL TEMPO
1740 POP BC
1750 POP IX
1760 EI
1770 RET
2
И, наконец, инициализация всех трех ка-
налов:
1415.
1780 SNEW PUSH HL
1790 CALL SNEWA
1800 PUSH DE
1810 POP HL
1820 CALL SNEWB
1830 PUSH BC
1840 POP HL
1850 CALL SNEWC
1860 POP HL
1870 RET
2
Подпрограммы инициализации подготавли-
вают в массивах переменных все необходи-
мые для работы данные. Они устанавливают
адреса блоков изменения частоты тона и шу-
ма в положение "не используется" (заносят
в них 0). Выбирают постоянный уровень
громкости (15). А также устанавливают дли-
тельность нот по умолчанию равной 1/4 се-
кунды.
Вот подпрограмма подключения второго
режима прерываний:
1415.
1880 SINIT LD A,24
1890 LD (65535),A
1900 LD A,195
1910 LD (65524),A
1920 LD HL,INTR ; HL=адрес обработчика
1930 LD (65525),HL
1940 LD HL,65024
1950 LD DE,65025
1960 LD BC,256
1970 LD (HL),255
1980 LD A,H
1990 LDIR
2000 DI
2010 LD I,A
2020 IM 2
2030 LD HL,MUTS ; запрет
2040 XOR A
2050 LD (HL),A ; работы
2060 INC HL
2070 LD (HL),A ; всех
2080 INC HL
2090 LD (HL),A ; каналов
2100 EI
2110 RET
2
А это подпрограмма, возвращающая стан-
дартный режим прерываний и выключающая
сопроцессор:
1415.
2120 SSTOP DI
2130 LD A,63
2140 LD I,A
2150 IM 1
2160 EI
2170 LD HL,AYREGS
2180 LD DE,AYREGS+1
2190 LD BC,13
2200 LD (HL),B ; B=0
2210 LDIR ; очистка области регистров
2220 LD A,7
2230 DEC B ; B=255
2240 CALL SETAY ; R7=255 (выключение микшера)
2250 JP AYOUT ; вывод регистров в сопроцессор
2
Теперь займемся обработчиком прерыва-
ний. Так как он должен обслуживать три ка-
нала, а массивы переменных легче всего ад-
ресовать регистром IX, можно предложить
такую подпрограмму:
1415.
2260 INTR PUSH AF ; сохранение регистров
2270 PUSH HL
2280 PUSH DE
2290 PUSH BC
2300 PUSH IX
2310 LD IX,CHAN_A ; подготовка регистров
2320 XOR A
2330 CALL DISPAT ; сопроцессора
2340 LD IX,CHAN_B
2350 LD A,1 ; для всех каналов
2360 CALL DISPAT
2370 LD IX,CHAN_C
2380 LD A,2
2390 CALL DISPAT
2400 CALL AYOUT ; вывод регистров в сопроцессор
2410 POP IX ; восстановление регистров
2420 POP BC
2430 POP DE
2440 POP HL
2450 POP AF
2460 RST 56 ; вызов стандартного обработчика
2470 RET
2
Этот обработчик для каждого из каналов
вызывает процедуру-диспетчер (DISPAT), за-
нося в регистр A номер канала, а в регистр
IX - адрес массива его переменных.
Роль процедуры DISPAT заключается в об-
работке переменных TEMPS, CURTS и MUTS для
указанного канала, а также в вызове основ-
ной подпрограммы создания звука - GETSND.
Вот текст процедуры DISPAT:
1415.
2480 DISPAT PUSH AF
2490 LD E,A
2500 LD D,0
2510 CALL STATUS
2520 OR A ; канал остановлен ?
2530 JR NZ,DISP1
2540 POP AF
2550 LD B,A
2560 JR DISP3
2570 DISP1 LD HL,CURTS
2580 ADD HL,DE
2590 LD A,(HL)
2600 OR A
2610 JR Z,DISP2
2620 DEC (HL) ; уменьшение счетчика темпа
2630 POP AF
2640 RET
2650 DISP2 DEC HL ; обновление
2660 DEC HL
2670 DEC HL ; счетчика
2680 LD A,(HL)
2690 INC HL ; темпа
2700 INC HL
2710 INC HL
2720 LD (HL),A
2730 POP AF
2740 CALL GETSND ; вызов основной процедуры
2750 LD B,A
2760 CALL STATUS
2770 CP 1 ; надо "заглушать" ?
2780 RET NZ
2790 DISP3 LD C,B ; "заглушение"
2800 LD HL,AYREGS+6
2810 LD A,9 ; канала
2820 DISP4 SLA A
2830 DJNZ DISP4
2840 OR (HL)
2850 LD (HL),A
2860 INC HL
2870 ADD HL,BC
2880 LD (HL),0
2890 RET
2
Итак, все сервисные процедуры приведе-
ны. Осталась только одна - GETSND:
1415.
2900 GETSND ...
2
Вот ее-то написание я и предлагаю Вам.
Но не пугайтесь - я все подробно объясню.
Скорее всего, процедура GETSND будет
достаточно велика. Может быть, даже больше
всех приведенных процедур, вместе взятых.
Но ничего трудного в ней нет, а ее объем
обусловлен достаточно сложным форматом
данных.
Задача процедуры GETSND сводится к фор-
мированию в определенных ячейках области
AYREGS данных одного из каналов для после-
дующего копирования их в регистры сопро-
цессора.
В качестве параметров этой процедуре
передается номер канала в регистре A (что-
бы она знала, в каких ячейках размещать
данные для частоты, громкости и т.п.) и
адрес массива переменных в регистре IX.
Обратите внимание, что она обязана сохра-
нить значение регистра A! Возможно, Вам
придется даже завести дополнительную пере-
менную для его хранения.
Итак, порядок действий в процедуре
GETSND следующий:
1. Проверить, не равен ли нулю счетчик
длительности (IX+19).
2. Если нет, то продолжить воспроизведе-
ние текущей ноты.
3. Если равен, то выбрать новую ноту и
начать ее воспроизведение.
В понятие "продолжить воспроизведение
текущей ноты" входит следующее:
1. Уменьшить счетчик длительности.
2. Если адрес одного из дополнительных
блоков равен 0, то пункты 3...5 для
этого блока выполнять не надо.
3. Выбрать очередные значения из блоков
изменения частот и громкости, исполь-
зуя переменные IX+6/IX+7, IX+10/IX+11
и IX+14/IX+15.
4. В соответствии с выбранными значения-
ми и номером канала обновить с по-
мощью процедур GETAY и SETAY область
AYREGS (обратите внимание, что смеще-
ния частот могут быть и отрицательны-
ми).
5. Обновить переменные по адресам IX+6/
IX+7, IX+10/IX+11 и IX+14/IX+15 в со-
ответствии с пунктом 3.
"Начать воспроизведение ноты" включает
в себя только один пункт:
1. Переписать переменные по адресам IX+4/
IX+5, IX+8/IX+9, IX+12/IX+13 и IX+18 в
их счетчики - дубли (IX+6/IX+7, IX+10/
IX+11, IX+14/IX+15 и IX+19).
А вот пункт "выбрать новую ноту" - пос-
ложнее:
1. Выбрать очередной байт из текущего
паттерна (адрес - IX+16/IX+17).
2. Если он не равен 0...100, 130, 131 и
132 - обработать как соответствующий
управляющий код и перейти к пункту 1.
3. Соответственно с байтом или его пара-
метрами и номером канала обновить об-
ласть AYREGS и переменную IX+16/IX+17.
Теперь об обработке управляющих кодов:
Код 128:
1. Выбрать адрес следующего паттерна из
основного блока (адрес в основном бло-
ке содержится в IX+2/IX+3).
2. Обновить переменную IX+2/IX+3.
3. Если адрес паттерна равен нулю, скопи-
ровать переменную IX+2/IX+3 в IX+0/IX+
1 и перейти к пункту 1.
4. Если адрес равен 65535, скопировать
переменную IX+0/IX+1 в IX+2/IX+3 и
уменьшить счетчик повторений (IX+20).
Если счетчик повторений равен нулю,
установить канал в состояние "останов-
лен" с помощью процедуры MUTE. Перейти
к пункту 1.
5. Записать выбранный адрес в IX+16/IX+17
и "выбрать новую ноту".
Код 129:
1. Взять байт, следующий за этим кодом, и
занести в IX+18. "Выбрать новую ноту".
Код 133 (134):
1. Взять адрес, следующий за этим кодом,
и поместить его в IX+4/IX+5 (IX+8/IX+
9). "Выбрать новую ноту".
Код 135:
1. Взять адрес, следующий за этим кодом.
2. Если он меньше 16, вычислить соответ-
ствующий адрес в таблице SVOLS.
3. Поместить полученный адрес в IX+12/IX+
13.
4. "Выбрать новую ноту".
Код 136:
1. Установить в регистре громкости задан-
ного канала значение 16.
2. Записать в IX+14/IX+15 ноль.
3. Взять байт, следующий за этим кодом.
4. Вычислить по таблице ENVS значение R13
и обновить область AYREGS.
5. Взять два байта, следующие за кодом
формы, и занести их в ячейки 11 и 12
области AYREGS.
6. "Выбрать новую ноту".
Теперь о том, как можно рассчитать но-
мера регистров для заданного канала. Что-
бы рассчитать номер регистра частоты, дос-
таточно номер канала умножить на 2 (ADD
A,A). Полученное число будет номером ре-
гистра младшего байта частоты. Чтобы полу-
чить номер регистра старшего байта часто-
ты, полученное значение надо увеличить на
1 (INC A).
Чтобы вычислить номер регистра громкос-
ти, надо к номеру канала прибавить 8 (ADD
A,8).
Если Вы напишете процедуру GETSND, в
Ваших руках окажется довольно мощная прог-
рамма, пригодная для написания как музыки,
так и эффектов.
И в конце описания этой программы - со-
веты по составлению для нее блоков данных.
Чтобы ноту повысить или понизить на ок-
таву, необходимо ее значение соответствен-
но увеличить или уменьшить на 12. Ноте ДО
первой октавы соответствует число 39.
Значения длительностей Вы можете взять
из таблицы 11.
Теперь несколько советов по программи-
рованию сопроцессора. Для формирования но-
вых тембров можно использовать генератор
огибающей, настроенный на периодически из-
меняющуюся громкость и большую частоту.
Особенно хороших результатов можно до-
биться, настроив его на частоту, кратную
частоте основного сигнала.
Для заглушения музыкального сопроцессо-
ра довольно часто используется запрещение
всех функций микшера (вывод байта 255 в
R7), но такой способ не очень надежен.
Если генератор огибающей настроен на пе-
риодически изменяющуюся громкость, то этот
трюк не пройдет: останется слышным щел-
канье. Для полного заглушения сопроцессо-
ра могу посоветовать такую подпрограмму:
1415.
10 LD HL,DATA ; HL=адрес данных
20 LD E,10 ; E=номер первого регистра
30 LOOP LD BC,65533 ; BC=адрес порта регистров
40 OUT (C),E ; вывод E в порт BC
50 LD A,(HL) ; A=значение очередного
регистра
60 LD B,191 ; BC=адрес порта данных
70 OUT (C),A ; вывод A в порт BC
80 INC HL ; HL=HL+1
90 DEC E ; E=E-1
100 LD A,E ; E=
110 CP 6 ; 6 ?
120 JR NZ,LOOP ; если нет, то цикл
130 RET ; возврат
140 DATA DEFB 0,0,0,255 ; данные для регистров AY
2
Во многих случаях требуется определить,
присутствует ли сопроцессор в данном ком-
пьютере. Некоторые делают это проверяя тип
компьютера (48K/128K), но этот способ не
совсем справедлив, ведь AY может стоять и
на старом добром Спектруме. Вот подпрог-
рамма, определяющая присутствие сопроцес-
сора более достоверно:
1415.
10 LD BC,65533 ; BC=адрес порта регистров
20 XOR A ; A=0
30 OUT (C),A ; выбор регистра 0
40 LD B,191 ; BC=адрес порта данных
50 OUT (C),A ; вывод 0 в выбранный регистр
60 LD B,255 ; BC=адрес порта регистров
70 IN A,(C) ; ввод значения из выбранного
регистра
80 OR A ; A=0 ?
90 RET ; возврат
2
Если после вызова этой подпрограммы
флаг Z сброшен, то сопроцессор присут-
ствует. В противном случае - нет.
И напоследок хочу рассказать об одном
довольно интересном приеме. Он очень час-
то используется во многих программах. Чи-
тая данные из регистров громкости и преоб-
разовывая их соответствующим образом в
графическую информацию одновременно с вос-
произведением музыки, можно сделать цвето-
музыку или пиковые индикаторы уровня сиг-
нала.