Как написать игру для ZX Spectrum на ассемблере

       

ПОЛУЧЕНИЕ ЧИСТОГО ТОНА



ПОЛУЧЕНИЕ ЧИСТОГО ТОНА

Наиболее простой способ получения звука определенной длительности и высоты- обратиться к подпрограмме ПЗУ, ответственной за выполнение оператора Бейсика BEEP. Мы уже упоминали о ней в шестой главе, но тем не менее напомним, что располагается она по адресу 949 и требует определения регистровых пар HL и DE. Например:

LD DE,440 LD HL,964 CALL 949 RET

Таким способом можно получить звук практически любой высоты и продолжительности - ограничения Бейсика здесь отсутствуют. Однако при этом отсутствуют и удобства, предоставляемые интерпретатором. Чтобы написать даже очень коротенькую музыкальную фразу, придется немало попотеть, рассчитывая значения задаваемых параметров. А рассчитываются они так. В регистровую пару DE заносится число, определяемое как fґt, где f - частота, измеряемая в герцах, а t - время в секундах (при извлечении звука ЛЯ первой октавы, который имеет частоту 440 Гц, длительностью в 1 секунду получится 440ґ1=440). Пара HL на входе должна содержать число, равное 437500/f-30.125 (если выполнить указанные вычисления, то получим величину 964). Таким образом, приведенная выше программа делает то же самое, что и оператор Бейсика

BEEP 1,9

Чтобы упростить задачу, можно предложить небольшую программку на Бейсике, которая, конечно, не может претендовать на роль музыкального редактора, но, по крайней мере, автоматизирует расчеты требуемых значений. После ее запуска на экране появится некоторое подобие меню. Нажав цифровую клавишу 1, вы сможете прослушать свое произведение. При нажатии клавиши 2 на экран выводится два столбика чисел: значения из левого столбика предназначены для загрузки пары DE, а для HL числа берутся из правого столбика. Клавиша 0 позволяет выйти в редактор Бейсика. После нажатия клавиш 1 или 2 нужно ввести желаемый темп исполнения по метроному (количество четвертных нот, исполняемых в минуту).

Ноты записываются в операторе DATA, начиная со строки 1000, парами чисел, первое из которых определяет высоту и задается так же, как и в операторе BEEP (0 - нота ДО первой октавы), а второе представляет собой относительную длительность звуков, то есть четверти, например, записываются дробью 1/4, восьмушки - 1/8 и так далее. Для обозначения конца блока данных в самом его конце нужно написать два нуля (строка 8990).


Ниже приводится текст программы, в котором для примера уже включены несколько строк данных короткой музыкальной фразы (строки 1010...1030). Наберите и сохраните программу без этих строк, а затем, дописав их, можете проверить ее работу.

10 DIM f(12): RESTORE 9000 20 FOR n=1 TO 12: READ f(n): NEXT n 50 CLS : PRINT "1. Listen"'"2. Code"'"0. Stop" 60 PAUSE 0: IF INKEY$="1" THEN CLS : GO TO 100 70 IF INKEY$="2" THEN CLS : GO TO 200 80 IF INKEY$="0" THEN STOP 90 GO TO 60 100 REM Прослушивание 110 RESTORE 1000: INPUT "TEMPO: (M.M.) = ";temp 120 READ n,d: IF d THEN BEEP d/temp*240,n: GO TO 120 130 GO TO 50 200 REM Расчет значений для DE и HL. 210 RESTORE 1000: INPUT "TEMPO: (M.M.) = ";temp 220 READ n,d: IF NOT d THEN GO TO 300 230 LET d=d/temp*240 240 LET f1=INT (n/12): LET f2=n-f1*12 250 LET f=f(f2+1)/2­(4-f1) 260 PRINT "LD DE,";INT (f*d+.5),"LD HL,";INT ((437500/f-30.125)+.5) 270 GO TO 220 300 PRINT #0;"Press any key": PAUSE 0: GO TO 50 1000 REM Данные мелодии. 1010 DATA 7,1/16,5,1/16,4,1/16,2,1/16 1020 DATA 4,1/8,7,1/8 1030 DATA -5,1/8,-1,1/8,0,1/4 8990 DATA 0,0 9000 REM Частота в герцах для звуков одной октавы 9010 DATA 4186.01,4434.92,4698.64,4978.03 9020 DATA 5274.04,5587.65,5919.91,6271.93 9030 DATA 6644.87,7040,7458.62,7902.13

Получив тем или иным способом ряд чисел для загрузки регистров, нужно каким-то образом обратить их в мелодию. Для этих целей напишем процедуру, последовательно считывающую из блока данных, адресованного парой HL, двухбайтовые значения регистровых пар и извлекающую соответствующие звуки. Подпрограмма завершит свою работу либо при достижении конца блока данных (два байта 0) либо при нажатии на любую клавишу:

MELODY XOR A ;опрос клавиатуры IN A,(254) CPL AND #1F JR NZ,MELODY ;пока все клавиши не отпущены MELOD1 XOR A ;опрос клавиатуры IN A,(254) CPL AND #1F RET NZ ;выход, если нажата любая клавиша LD E,(HL) ;считывание в пару DE INC HL LD D,(HL) LD A,D OR E RET Z ;выход, если конец блока данных (DE=0) INC HL LD A,(HL) ;считывание данных для пары HL INC HL PUSH HL LD H,(HL) ;загрузка пары HL LD L,A CALL 949 ;вывод звука POP HL INC HL JR MELODY ;следующая нота





Прежде чем обратиться к данной процедуре выпишем числа, полученные с нашим импровизированным «редактором» при заданном темпе, равном 150, в виде блока двухбайтовых данных, который завершим числом 0 (тоже двухбайтовым):

DATMEL DEFW 39,1086,35,1223,33,1297 DEFW 29,1460,66,1297,78,1086 DEFW 39,2202,49,1742,105,1642 DEFW 0 ;маркер конца блока данных

Теперь можно вызвать процедуру MELODY, например, таким образом:

ORG 60000 ENT $ LD HL,DATMEL CALL MELODY RET

Существует и другой способ извлечения звуков, при котором все необходимые расчеты выполняются в самой программе. Хотя он и несколько сложнее только что описанного, но может показаться вам более привлекательным. Во всяком случае он более привычен, так как исходными данными при этом являются те же числа, что и параметры оператора BEEP. Подпрограмма, расположенная по адресу 1016, сама выполняет приведенные выше вычисления и обращается к процедуре вывода звука 949, а значения длительности и высоты звука передаются ей через стек калькулятора:

LD A,1 ;загружаем в аккумулятор значение ; длительности звучания (1 секунда). CALL 11560 ;содержимое аккумулятора заносим ; в стек калькулятора. LD A,12 ;нота ДО второй октавы CALL 11560 ;посылаем в стек калькулятора CALL 1016 ;вызываем процедуру извлечения звука RET

Этот метод хорош всем, за исключением одной «мелочи» - числа, записываемые в аккумулятор могут быть только целыми и не отрицательными. Чтобы исправить этот недостаток, можно условиться, что длительности будут задаваться не в секундах, а в сотых долях секунды, а значение высоты звука будем интерпретировать как число со знаком. Такой подход позволит получать продолжительность звучания нот примерно до двух с половиной секунд, что в большинстве случаев вполне достаточно; диапазон звуков останется таким же, как и в Бейсике: от -60 до +68. Параметры для этой подпрограммы будем задавать в регистрах B (длительность в сотых долях секунды) и C (высота в полутонах):

BEPER PUSH BC LD A,B ;берем в аккумулятор первый параметр CALL 11560 ;заносим его в стек калькулятора LD A,100 CALL 11560 ;помещаем в стек число 100 RST 40 DEFB 5,56 ;выполняем деление POP BC LD A,C ;берем второй параметр AND A JP M,BEPER1 ;если отрицательный, переходим на BEPER1 CALL 11560 ; иначе помещаем в стек без изменений JP 1016 ; и извлекаем звук BEPER1 NEG ;получаем абсолютное значение CALL 11560 ;отправляем в стек RST 40 DEFB 27,56 ;меняем знак JP 1016 ; и извлекаем звук



Взяв за основу эту подпрограмму, можно написать процедуру, аналогичную MELODY, которая бы проигрывала произвольный музыкальный фрагмент, считывая параметры из заранее подготовленного блока данных. Приведем такой пример:

ORG 60000 ENT $ LD HL,D_MEL1 CALL MELBEP RET MELBEP LD B,(HL) ;в B - длительность INC B ;один из способов проверки DEC B ; содержимого регистра на 0 RET Z INC HL LD C,(HL) ;в C - высота звука INC HL PUSH HL CALL BEPER ;извлечение звука POP HL JR MELBEP

D_MEL1 DEFB 10,-5,10,0,10,1,10,2 DEFB 20,5,10,2,20,5,10,7 DEFB 0

Во всех приведенных выше фрагментах в конечном итоге использовалась подпрограмма ПЗУ 949. В этом случае, пока звучит очередная нота, компьютер оказывается полностью выключен из работы. Поэтому иногда бывает очень важно уметь получать чистый тон на низком уровне, непосредственно программируя порт динамика. Принцип получения звука таким способом предельно прост: достаточно с определенной частотой попеременно то включать, то выключать динамик. С таким способом в некоторой степени вы также уже знакомы, поэтому отметим лишь некоторые важные моменты, без знания которых невозможно получить качественный звук. Во-первых, напомним, что динамик управляется четвертым битом 254-го порта. При установке или сбросе этого бита слышны короткие щелчки, которые при быстром чередовании сливаются в сплошной звук. Но кроме динамика этот же порт отвечает и за цвет бордюра, поэтому кроме четвертого бита нужно правильно устанавливать и три младших разряда выводимого байта. Не менее важно при извлечении звука помнить о необходимости запрета прерываний. Если этого не сделать, то невозможно будет получить чистый тон. Это связано с тем, что 50 раз в секунду микропроцессор будет отвлекаться на выполнение подпрограммы обработки прерываний, что неминуемо скажется на частоте создаваемого звука.

Теперь перейдем к делу и создадим программку, извлекающую различные звуки при нажатии цифровых клавиш. При этом звучание будет продолжаться до тех пор, пока клавиша не будет отпущена.



ORG 60000 ENT $ DI LD A,(23624) ;получаем в аккумуляторе цвет бордюра AND #38 ;выделяем биты 3, 4 и 5 RRA ;сдвигаем на место битов 0, 1 и 2 RRA RRA LD E,A ;запоминаем в регистре E ORGAN1 CALL 8020 ;проверка нажатия клавиши BREAK JR NC,EXIT ;если нажата, выход из программы PUSH DE LD DE,#100 ;счетчики для определения ; нажатых клавиш LD A,#F7 ;опрос полуряда 1...5 CALL KEYS LD A,#EF ;опрос полуряда 6...0 CALL KEYS LD D,0 ;выбор высоты звука из таблицы LD HL,DATNOT ADD HL,DE POP DE LD B,(HL) INC B DEC B JR Z,ORGAN1 ;если в B ноль, клавиши не нажаты, звука нет LD A,E OUT (254),A ;извлечение звука XOR 16 LD E,A ORGAN2 LD C,20 ;цикл задержки для получения звука ; определенной высоты ORGAN3 DEC C JR NZ,ORGAN3 DJNZ ORGAN2 JR ORGAN1 EXIT EI ;выход из программы RET KEYS IN A,(254) ;опрос выбранного полуряда LD B,5 ;5 клавиш в полуряду KEYS1 RRCA ;сдвигаем биты вправо JR C,KEYS2 ;если младший бит установлен, ; клавиша отпущена LD E,D ;иначе запоминаем номер нажатой клавиши KEYS2 INC D ;увеличиваем номер определяемой клавиши DJNZ KEYS1 ;переходим к следующей RET ; Данные для получения необходимой задержки для каждого звука DATNOT DEFB 0,55,49,44,41,36 DEFB 21,24,27,29,32


Содержание раздела