4.9. Звук на прерываниях
У всех подпрограмм, приведенных в пре-
дыдущих главах, есть один общий недоста-
ток: на время их звучания выполнение ос-
новной программы приостанавливается. Это
можно исправить, хотя и с трудом, исполь-
зуя второй режим прерываний. Те, кто зна-
ет, что это такое, могут смело пропустить
следующие несколько абзацев.
Как уже говорилось раньше, пятьдесят
раз в секунду процессор получает сигнал о
необходимости вызова прерывания. При этом
он вызывает некоторую подпрограмму, после
чего продолжает обрабатывать основную про-
грамму.
Существует три различных режима преры-
ваний: 0, 1 и 2, которые выбираются коман-
дами IM 0, IM 1 и IM 2. Стандартный режим
имеет номер 1. О нем уже было рассказано в
главе 3, но на всякий случай напомню, что
в этом режиме в качестве обработчика пре-
рываний используется подпрограмма ПЗУ по
адресу 56 (#38), которая следит за клавиа-
турой и счетчиком времени. Режим 0 нам не
интересен, так как в ZX-Spectrum он анало-
гичен режиму 1. А вот режим 2 и есть са-
мое интересное!
Во втором режиме прерываний пятьдесят
раз в секунду происходит следующее: мик-
ропроцессор считывает с шины данных байт,
называемый вектором прерывания. Он пере-
дается в младший байт шины адреса, а в
старший байт записывается содержимое ре-
гистра I. По полученному таким образом ад-
ресу процессор считывает из памяти два
байта, которые интерпретируются как адрес
подпрограммы обработки прерывания.
ZX-Spectrum устроен так, что вектор
прерывания обычно равен 255 (#FF), но не-
которые внешние устройства, например, AMX-
mouse, могут генерировать другие векто-
ра. Кроме того, в некоторых некачественных
спектрумах вектор прерывания может изме-
няться абсолютно случайным образом.
Исходя из всего вышесказанного, можно
предложить следующую последовательность
действий для установки собственной под-
программы обработки прерываний:
1. Запретить прерывания.
2. Записать в память адрес обработчика
прерываний.
3. Задать в регистре I старший байт ад-
реса указателя на обработчик.
4. Установить второй режим прерываний.
5. Разрешить прерывания.
А для возврата к стандартному режиму
обработки прерываний нужно выполнить такие
действия:
1. Запретить прерывания.
2. Записать в регистр I число 63.
3. Установить первый режим прерываний.
4. Разрешить прерывания.
Так как вектор прерывания может изме-
няться, вместо записи двух байтов по опре-
деленному адресу выстраивается целая таб-
лица размером 257 байт с таким расчетом,
чтобы при любом значении вектора считывал-
ся один и тот же адрес. Понятно, что для
этого все байты таблицы должны быть одина-
ковыми.
При составлении процедур обработки пре-
рываний следует придерживаться некоторых
правил. Во-первых, обработчик прерывания
должен выполняться за достаточно короткий
промежуток времени. Во-вторых, все регис-
тры, используемые в обработчике, перед
возвратом должны принять значения, нахо-
дившиеся в них до вызова прерывания. В
связи с этим не рекомендуется обращаться к
подпрограммам ПЗУ, по крайней мере до тех
пор, пока Вы не знаете совершенно точно,
какие в них используются регистры и какие
системные переменные при этом могут быть
изменены. Вызов подпрограмм ПЗУ не желате-
лен еще и потому, что некоторые из них
разрешают прерывания, что совершенно не
допустимо во избежание вызова обработчика
из самого себя. Любой обработчик должен
работать при запрещенных прерываниях.
Однако использовать команду DI в самом
начале процедуры не обязательно, так как
это делается автоматически и Вам надо по-
заботиться только о разрешении прерываний
перед выходом.
Если Вы не хотите лишаться возможнос-
тей, предоставляемых стандартным обработ-
чиком прерываний, можете использовать в
своей подпрограмме команду RST 56. А при
использовании прерываний в бейсик-програм-
мах это просто необходимо, иначе будет
заблокирована клавиатура.
Теперь разберемся, чем нам могут быть
полезны прерывания.
Если задать некоторую последователь-
ность звуков и в каждом прерывании вос-
производить короткую ее часть, то получит-
ся довольно хороший эффект параллельного с
программой звука. Причем каждая часть дол-
жна быть действительно очень короткой,
иначе толку от прерываний не будет.
Условимся, что последовательность зву-
ков будем задавать блоком данных в следую-
щем формате: для каждой ноты в блоке
данных должно быть по два байта. Первый
байт - частота (1...253), а второй байт -
длительность (0...255). Кроме того, могут
встречаться такие контрольные коды:
0 - переключение тон/шум
254 - начало цикла
255 - конец блока
Изначально программа настроена на вы-
вод чистого тона, но, если Вам необходимо
получить шум, Вы можете переключить ее
на воспроизведение шума, вставив в данные
байт, равный 0. Для повторного переключе-
ния на тон байт 0 должен встретиться еще
раз.
Когда программа встретит байт 255, вы-
вод звука либо прекратится, либо вся пос-
ледовательность повторится снова - в зави-
симости от заданного числа повторений.
Если в блоке данных встретится код 254,
то при очередном повторении эффект начнет-
ся не с начала, а с места, где этот код
встретился.
Теперь сама программа. Она является за-
конченным комплексом и может быть оттран-
слирована ассемблером без малейших измене-
ний:
1415.
10 ORG 60000
20 JP SINIT ; подключение прерываний
30 JP SSTOP ; отключение прерываний
40 JP NEWFX ; инициализация эффекта
50 MUTE LD (COUNT),A ; "заглушка"
60 RET
70 SINIT XOR A
80 LD (COUNT),A
90 LD A,24 ; код команды JR
100 LD (65535),A
110 LD A,195 ; код команды JP
120 LD (65524),A
130 LD HL,INTR ; HL=адрес обработчика
140 LD (65525),HL
150 LD HL,65024
160 LD DE,65025
170 LD BC,256
180 LD (HL),255 ; адрес прерывания - 65535
190 LD A,H
200 LDIR ; заполнение таблицы
210 DI
220 LD I,A
230 IM 2
240 EI
250 RET
260 SSTOP DI ; отключение прерываний
270 LD A,63
280 LD I,A
290 IM 1
300 EI
310 RET
320 NEWFX DI ; инициализация эффекта
330 LD (COUNT),A
340 XOR A
350 LD (FLAG),A
360 LD (ADDR),HL
370 LD (CURADD),HL
380 EI
390 RET
400 ADDR DEFW 0 ; начальный адрес блока данных
410 CURADD DEFW 0 ; текущий адрес в блоке данных
420 COUNT DEFB 0 ; количество повторений
430 FLAG DEFB 0 ; флаг тон/шум
440 INTR PUSH AF ; обработчик прерывания
450 PUSH BC ; сохранение регистров
460 PUSH DE
470 PUSH HL
480 TEST LD A,(COUNT) ; A=счетчик повторений
490 OR A ; есть что играть ?
500 JR Z,EXIT
510 LD HL,(CURADD); HL=текущий адрес
520 NEXT LD A,(HL)
530 INC HL
540 CP 254 ; A=254 ? (начало цикла)
550 JR NZ,CONT1
560 LD (ADDR),HL ; изменение начального адреса
570 JR NEXT
580 CONT1 CP 255 ; A=255 ? (конец)
590 JR NZ,CONT2
600 LD HL,(ADDR) ; восстановление начального
610 LD (CURADD),HL; адреса блока данных
620 LD HL,COUNT
630 DEC (HL) ; уменьшение счетчика
повторений
640 JR TEST
650 CONT2 OR A ; A=0 ? (переключатель)
660 JR NZ,CONT3
670 LD A,(FLAG)
680 CPL ; инвертирование A
690 LD (FLAG),A
700 JR NEXT
710 CONT3 LD B,A ; B=частота
720 LD C,(HL) ; C=длительность
730 INC HL
740 LD (CURADD),HL; сохранение текущего адреса
750 LD A,(FLAG) ; A=флаг
760 OR A ; A=0 ?
770 LD A,7 ; A=цвет бордюра
780 JR NZ,NOISE
790 TONE XOR 16 ; воспроизведение тона
800 OUT (254),A
810 PUSH BC
820 PAUSE DJNZ PAUSE
830 POP BC
840 DEC C
850 JR NZ,TONE
860 JR EXIT
870 NOISE LD HL,1000 ; воспроизведение шума
880 LD D,A
890 NOIS2 LD A,(HL)
900 AND 248
910 OR D
920 OUT (254),A
930 PUSH BC
940 PAUS2 DJNZ PAUS2
950 POP BC
960 INC HL
970 DEC C
980 JR NZ,NOIS2
990 EXIT POP HL ; восстановление регистров
1000 POP DE
1010 POP BC
1020 POP AF
1030 RST 56 ; вызов стандартного
обработчика
1040 RET ; возврат
2
Порядок использования этого пакета дол-
жен быть следующим. В начале работы нужно
вызвать подпрограмму SINIT, которая вклю-
чит 2-ой режим прерываний. В тот момент,
когда Вам нужно получить звук, надо занес-
ти в регистр HL адрес блока данных, в ре-
гистр A - число повторений и вызвать под-
программу NEWFX. Затем, если Вам по ка-
кой-либо причине надо выключить звук или
продлить его звучание, занесите в регистр
A новое число повторений и вызовите под-
программу MUTE. Кроме того, эту подпрог-
рамму можно использовать для включения
последнего звучавшего эффекта. По оконча-
нии работы (или если в программе предус-
мотрены обращения к дисководу) следует
вызвать подпрограмму SSTOP, которая вос-
становит стандартный режим прерываний.
Серия команд JP в начале пакета сдела-
на для удобства. Благодаря ей все подпрог-
раммы этого пакета могут вызываться по со-
седним адресам:
SINIT - 60000 (#EA60)
SSTOP - 60003 (#EA63)
NEWFX - 60006 (#EA66)
MUTE - 60009 (#EA69)
К сожалению, с помощью прерываний не-
возможно получить достаточно чистый тон.
Поэтому, а также из-за совсем не музыкаль-
ного формата данных, эта программа вряд ли
может использоваться для создания музыки.
Но для звуковых эффектов она в самый раз.
Вот пример использования приведенной
программы:
1415.
10 ORG 50000
20 CALL 60000
30 LD HL,SNDFX
40 LD A,3
50 CALL 60006
60 RET
70 SNDFX DEFB 200,5,250,4,200,5,100,10,75,13,50,20,255
2
Строку 70 можно заменить на следующую:
1415.
70 SNDFX DEFB 50,20,75,13,100,10,200,5,250,4,50,20,255
2
И последнее. Если в Вашей программе не
очень много движущихся объектов на экране
и длительность звукового эффекта достаточ-
но мала (около полсекунды или меньше), то
его можно использовать без всяких прерыва-
ний - задержка заметна не будет.