1.7. П р о г р а м м и р о в а н и е з в у к о в ы х э ф ф е к т о в Мы уже говорили о возможностях работы с Программируемым Звуковым Гене- ратором - PSG . Здесь мы расскажем Вам о работе с PSG при программирова- нии в машинных кодах. Вначале кратко п о в т о р и м основные сведения. Назначение регистров PSG Вы легко вспомните, если взгляните на таблицу: ┌─────────┬───────────────────────────────┐ │ Регистр │ Н а з н а ч е н и е │ ├─────────┼───────────────────────────────┤ │ 0 │ Младший байт частоты канала A │ │ 1 │ Старший байт частоты канала A │ │ 2 │ Младший байт частоты канала B │ │ 3 │ Старший байт частоты канала B │ │ 4 │ Младший байт частоты канала C │ │ 5 │ Старший байт частоты канала C │ │ 6 │ Частота шума │ │ 7 │ Выбор звучания каналов │ │ 8 │ Громкость канала A │ │ 9 │ Громкость канала B │ │ 10 │ Громкость канала C │ │ 11 │ Младший байт периода волны │ │ 12 │ Старший байт периода волны │ │ 13 │ Указатель формы волны │ └─────────┴───────────────────────────────┘ Звуки бывают двух типов, которые мы назовем "звук" и "шум". За сочета- ние звучания "звука" и "шума" отвечает 7-й регистр PSG: бит 7 бит 6 бит 5 бит 4 бит 3 бит 2 бит 1 бит 0 ┌─────┬─────┬────────┬────────┬────────┬────────┬────────┬────────┐ │ ∗ │ ∗ │ канал C│ канал B│ канал A│ канал C│ канал B│ канал A│ └──▲──┴──▲──┴───▲────┴───▲────┴────▲───┴───▲────┴────▲───┴────▲───┘ │ │ │ │ │ │ │ │ Не используются └─── Выбор"шума"───┘ └── Выбор"звука" ──┘ Частота звучания "звука" из некоторого канала (A,B или C) определяется по следующей формуле: 1789772.5 Гц ──────────── = значение в регистрах для канала A, B или C 16·частота По той же формуле вычисляется частота звучания "шума"(значение берется из регистра с номером 6). Период волны определяется по следующей формуле: 1789772.5 Гц ───────────── = значение в регистрах 11 и 12. 256·период Работа звукового генератора поддерживается процессором через порты вво- да-вывода со следующими адресами: ┌──────┬───────────────────────────────────────┐ │ Порт │ Н а з н а ч е н и е │ ├──────┼───────────────────────────────────────┤ │ A0h │ Номер регистра PSG для записи числа │ │ A1h │ Число для записи в отмеченный регистр │ │ A2h │ Последнее число, записанное в PSG │ └──────┴───────────────────────────────────────┘ Существует два способа записи чисел в PSG средствами языка MSX-BASIC: α) SOUND reg, data β) OUT &HA0,reg:OUT &HA1,data П р и м е р 1. Программа в машинных кодах, записывающая число 254 в ───────────── звуко-генератор (в регистр 0). 10 CLEAR 200,&HF000:DEFUSR=&HF000 20 DATA 3E,00 :' LD A,0 30 DATA D3,A0 :' OUT (A0h),A 40 DATA 3E,FE :' LD A,FEh 50 DATA D3,A1 :' OUT (A1h),A 60 DATA C9 :' RET 70 DATA RET 80 READ Z$ 90 IF Z$="RET" THEN 110 100 POKE &HF000+T,VAL("&h"+Z$):T=T+1:GOTO 80 110 A=USR(A) Следующая таблица поможет Вам при моделировании оператора PLAY в ма- шинных кодах (данные приведены для канала A): ┌───────┬────┬─────┬─────┐ ┌───────┬────┬─────┬─────┐ │Октава │Нота│Рег.0│Рег.1│ │Октава │Нота│Рег.0│Рег.1│ ├───────┼────┼─────┼─────┤ ├───────┼────┼─────┼─────┤ │ │ C │ 93 │ 13 │ │ │ C │ 214 │ 0 │ │ O1 │ C# │ 156 │ 12 │ │ O5 │ C# │ 202 │ 0 │ │ │ D │ 231 │ 11 │ │ │ D │ 190 │ 0 │ │(контр-│ D# │ 60 │ 11 │ │ (вто- │ D# │ 180 │ 0 │ │октава)│ E │ 155 │ 10 │ │ рая) │ E │ 170 │ 0 │ │ │ F │ 2 │ 10 │ │ │ F │ 160 │ 0 │ │ │ F# │ 115 │ 9 │ │ │ F# │ 151 │ 0 │ │ │ G │ 235 │ 8 │ │ │ G │ 143 │ 0 │ │ │ G# │ 107 │ 8 │ │ │ G# │ 135 │ 0 │ │ │ A │ 242 │ 7 │ │ │ A │ 127 │ 0 │ │ │ A# │ 128 │ 7 │ │ │ A# │ 120 │ 0 │ │ │ B │ 20 │ 7 │ │ │ B │ 113 │ 0 │ ├───────┼────┼─────┼─────┤ ├───────┼────┼─────┼─────┤ │ │ C │ 175 │ 6 │ │ │ C │ 107 │ 0 │ │ O2 │ C# │ 78 │ 6 │ │ O6 │ C# │ 101 │ 0 │ │ │ D │ 244 │ 5 │ │ │ D │ 95 │ 0 │ │ (боль-│ D# │ 158 │ 5 │ │ (тре- │ D# │ 90 │ 0 │ │ шая) │ E │ 78 │ 5 │ │ тья) │ E │ 85 │ 0 │ │ │ F │ 1 │ 5 │ │ │ F │ 80 │ 0 │ │ │ F# │ 186 │ 4 │ │ │ F# │ 76 │ 0 │ │ │ G │ 118 │ 4 │ │ │ G │ 71 │ 0 │ │ │ G# │ 54 │ 4 │ │ │ G# │ 67 │ 0 │ │ │ A │ 249 │ 3 │ │ │ A │ 64 │ 0 │ │ │ A# │ 192 │ 3 │ │ │ A# │ 60 │ 0 │ │ │ B │ 138 │ 3 │ │ │ B │ 57 │ 0 │ ├───────┼────┼─────┼─────┤ ├───────┼────┼─────┼─────┤ │ │ C │ 87 │ 3 │ │ │ C │ 53 │ 0 │ │ O3 │ C# │ 39 │ 3 │ │ O7 │ C# │ 50 │ 0 │ │ │ D │ 250 │ 2 │ │ │ D │ 48 │ 0 │ │(малая)│ D# │ 207 │ 2 │ │ (чет- │ D# │ 45 │ 0 │ │ │ E │ 167 │ 2 │ │ вер- │ E │ 42 │ 0 │ │ │ F │ 129 │ 2 │ │ тая) │ F │ 40 │ 0 │ │ │ F# │ 93 │ 2 │ │ │ F# │ 38 │ 0 │ │ │ G │ 59 │ 2 │ │ │ G │ 36 │ 0 │ │ │ G# │ 27 │ 2 │ │ │ G# │ 34 │ 0 │ │ │ A │ 253 │ 1 │ │ │ A │ 32 │ 0 │ │ │ A# │ 224 │ 1 │ │ │ A# │ 30 │ 0 │ │ │ B │ 197 │ 1 │ │ │ B │ 28 │ 0 │ ├───────┼────┼─────┼─────┤ ├───────┼────┼─────┼─────┤ │ │ C │ 172 │ 1 │ │ │ C │ 27 │ 0 │ │ O4 │ C# │ 148 │ 1 │ │ O8 │ C# │ 25 │ 0 │ │ │ D │ 125 │ 1 │ │ │ D │ 24 │ 0 │ │ (пер- │ D# │ 104 │ 1 │ │(пятая)│ D# │ 22 │ 0 │ │ вая) │ E │ 83 │ 1 │ │ │ E │ 21 │ 0 │ │ │ F │ 64 │ 1 │ │ │ F │ 20 │ 0 │ │ │ F# │ 46 │ 1 │ │ │ F# │ 19 │ 0 │ │ │ G │ 29 │ 1 │ │ │ G │ 18 │ 0 │ │ │ G# │ 13 │ 1 │ │ │ G# │ 17 │ 0 │ │ │ A │ 254 │ 0 │ │ │ A │ 16 │ 0 │ │ │ A# │ 240 │ 0 │ │ │ A# │ 15 │ 0 │ │ │ B │ 227 │ 0 │ │ │ B │ 14 │ 0 │ └───────┴────┴─────┴─────┘ ├───────┼────┼─────┼─────┤ │(шестая) - │ 13 │ 0 │ └───────┴────┴─────┴─────┘ П р и м е р 2. Программа, позволяющая прочесть данные из звукогенера- ────────────── тора и проверить приведенную таблицу. 10 CLEAR 200,&HF000:DEFUSR=&HF000 20 DATA CD,1F,52 :'CALL 521F ; 30 DATA CD,96,00 :'CALL 0096 ; Чтение числа из PSG 40 DATA 26,00 :'LD H,0 ; 50 DATA 6F :'LD L,A ; 60 DATA C3,99,2F,"RET" :'JP 2F99 ; 70 READ Z$ 80 IF Z$="RET" THEN 100 90 POKE &HF000+T,VAL("&h"+Z$):T=T+1:GOTO 70 100 PLAY "O4 C#" 110 PRINT USR(0);USR(1) 'Читаем содержимое 0-го и 1-го регистров Ф о н о в о е м у з ы к а л ь н о е с о п р о в о ж д е н и е \fc (Данный раздел написан А.Н.Никитиным) \f-c- Существует несколько вариантов реализации фонового сопровождения про- граммы. Вспомним один из рутинных вариантов воплощения фонового музыкаль- ного сопровождения. Зная, что длительность нот намного превышает время вы- полнения процессором арифметических операций, можно установить соответст- вие между музыкальной длительностью и определенным количеством арифмети- ческих операций, выполняемых процессором. Далее остается последовательно вставлять музыкальные фрагменты (их длина, разумеется, должна быть ограни- чена настолько,чтобы программа не "зависала"!) между операторами основной программы. Ясно, что такой способ вызывает массу неудобств: значительно усложняется редактирование программы, программирование требует огромных затрат времени и труда, об универсальности программы не может быть и речи. Предлогаемый нами алгоритм реализации фонового музыкального сопровожде- ния полностью и с к л ю ч а е т перечисленные неудобства! Расскажем Вам идею алгоритма. Во время работы процессор совершает мас- кируемые прерывания частотой 5О Гц и опрашивает область ловушек (hooks),а точнее происходят обращения (CALL ...) по адресам FD9Ah, FD9Fh. Напомним Вам, что эти прерывания разрешаются командой ассемблера EI, а запрещаются командой DI. Начальный адрес Таблицы ловушек - &hFD9A. По этому адресу си- стемной области хранится подпрограмма перехода на программу сетевого обме- на у компьютеров серии MSX-2 и число &HC9(машинный код команды RET) у ком- пьютеров серии MSX-1, поэтому при обращении к данной ловушке либо из под- программы обработки прерываний (которая расположена по адресу 0038h),либо из Вашей программы означает мгновенный возврат в основную программу (если MSX-2 не ведет "сетевой диалог"). Если по адресу FD9Ah "положить" подпрограмму: RST 30h DEFB N_slot DEFW Adres RET , где N_slot - это номер слота, в котором будет расположена Ваша программа, а Adres - это адрес Вашей подпрограммы обработки прерываний, то 50 раз в секунду управление будет передаваться Вашей подпрограмме, в которой можно управлять музыкой, печатью, графикой или опрашивать клавиатуру. Вообще говоря,число 50 не очень удобно для точного отсчета музыкальной длительности (так как 50 не делится на 8, 16, 32; в этом смысле идеальным было бы число 64), но при сравнении длительностей между собой (а не с мет- рономом) искажение звука практически незаметно. Структура музыкальной подпрограммы довольно проста: она должна обраба- тывать данные и управлять музыкальными очередями. Вся сложность состоит в том, чтобы построить такую структуру музыкальных данных, которая удовлет- воряла бы следующим требованиям: объем данных должен быть минимальным,воз- можности управления музыкальным генератором должны быть большими, должны присутствовать элементы программирования (циклы,переходы и т.д.), должно выполняться большинство музыкальных выражений (Legato, Staccato и т.д.). В предложенном ниже описании структуры музыкальных данных, мы постара- лись удовлетворить лишь части названных требований.Структура данных очень напоминает стандарт MIDI-интерфейса(Musical Instrument Digital Interface). Порядок размещения данных в очереди следующий: <Команда>,{Данное},{Данное},<Команда>,{Данное},{Данное},... Следует отметить, что {Данное} может быть опущено. Приведем описание команд и данных. К о м а н д а 0. Установка частоты (высоты) звучания: 1 полубайт - команда 00 ; 2 полубайт - номер ноты 1÷12. К о м а н д а 1. Микширование (регистр 7 PSG,по умолчанию - &hb10111000): 1 полубайт - команда 01 , 2 полубайт - формальный , следующий байт - байт состояния (структура аналогична структуре данных, находящихся в регистре 7): &b10 ∗∗∗ ∗∗∗ ▲▲▲ ▲▲▲ │││ │││ cba cba - номера каналов Шум Сигнал К о м а н д а 2. Установка темпа воспроизведения (по умолчанию - 1): 1 полубайт - команда 02 ; 2 полубайт - данное (1÷2). К о м а н д а 3. Установка амплитуды звучания (по умолчанию - 8): 1 полубайт - команда 03, 2 полубайт - данное (0÷15) (амплитуда = данное + 1). К о м а н д а 4. Установка частоты шума (по умолчанию - 20): 1 полубайт - команда 04, 2 полубайт - данное (1÷15, частота = данное·2). К о м а н д а 5. Установка формы волны (пакета): 1 полубайт - команда 05 , 2 полубайт - данное (1÷15). К о м а н д а 6. Установка значения для 11 регистра PSG(по умолчанию-100) 1 полубайт - команда 06, 2 полубайт - данное (0÷15, значение = данное · 10). К о м а н д а 7. Установка значения для 12 регистра PSG(по умолчанию-10): 1 полубайт - команда 07, 2 полубайт - данное (0÷15, значение=данное·2). К о м а н д а 8. Установка текущей длительности звучания: 1 полубайт - команда 08, 2 полубайт - данное (1÷15), где 1 - целая с точкой, 2 - целая, 3 - половинная с точкой, 4 - половинная, 5 - четвертная с точкой, 6 - четвертная, 7 - восьмая с точкой, 8 - восьмая, 9 - шестнадцатая с точкой, 10 - шестнадцатая, 11 - тридцатьвторая с точкой, 12 - тридцатьвторая. К о м а н д а 9. Пауза: 1 полубайт - команда 09, 2 полубайт - формальный. К о м а н д а 10. Переход (CALL или JUMP) по адресу данных 1 полубайт - команда 10 , 2 полубайт - формальный, следующие два байта- относительный адрес данных от расположения всех музыкальных данных. К о м а н д а 11. Возврат из подпрограммы (адреса хранятся "у себя") Возвращаться можно сколь угодно раз (ибо это не стек)! 1 полубайт - команда 11 , 2 полубайт - формальный. К о м а н д а 12. Стоп, выключить канал: 1 полубайт - команда 12, 2 полубайт - формальный. К о м а н д а 13. Установка текущей октавы: 1 полубайт - команда 13, 2 полубайт - номер октавы (1,2,3,...,8). К о м а н д а 14. Прием игры (Staccato, Legato): 1 полубайт - команда 14, 2 полубайт - 1, если Staccato и 0, если Legato. Заметим, что числа от 2 до 15 могут выражать и другие приемы игры! К о м а н д а 15. Резервная: 1 полубайт - команда 15, 2 полубайт - ... (можно использовать как пере- дачу других команд, т.е. 15-я команда становится п р е ф и к с н о й). Таким образом, Ваша подпрограмма должна распознавать номер команды, ис- полнять ее, исходя из параметров, следующих за командой и помещать указа- тель данных на следующую команду. Если Вы желаете увеличить музыкальные возможности программы,а команд (в полубайт можно поместить число от 0 до 15) не хватает, то необходимо выделить префиксную команду (например, 15-ю, так как она резервная)и теперь порядок размещения данных в очереди станет следующим: <Команда>,<Команда>,{данное},{данное},... <Команда>,<Команда>,{данное},{данное},... Кроме аппаратной реализации фонового музыкального сопровождения,сущест- вует и программная. Вся прелесть аппаратной реализации в том, что уменьше- ние длительности нот происходит в режиме реального времени.Программно мож- но запараллелить многие процессы (в том числе и музыку): Process: CALL ... CALL Music CALL ... JR Process , но дело в том, что тактовая частота процесора гораздо выше, чем частота маскируемых прерываний и без задержки (которая очень часто просто не нужна) казалось бы не обойтись. Н о в ы х о д е с т ь! Маскируемые прерывания оставляют след в области системных переменных, и этим можно воспользоваться. Ячейка с адресом 0FC9Eh (Jiffy) хранит этот след. Содержимое Jiffy (2 байта) при каждом новом прерывании увеличивает- ся на единицу, а значит, момент нового "тика" (это выражение произошло из принципа работы подпрограммы часов компьютера)всегда можно регистрировать. В Приложении приведена программа обработки музыкальных очередей, на- писанная на ассемблере Z80 для компьютера MSX-2.