6. МУЗЫКАЛЬНЫЕ И ЗВУКОВЫЕ ЭФФЕКТЫ
В этой главе мы рассмотрим некоторые способы улучшения работы Ваших программ путем включения в них специальных звуковых эффектов или музыки. Но прежде мы должны разобрать способ представления чисел с плавающей запятой в компьютере "ZX-Spectrum", а также способы записи этих чисел в упакованной форме.
Интегральная форма записи действительных чисел.
Любые числа: целые, дробные, положительные, отрицательные, могут быть представлены в 5-байтной форме. Первый байт в такой форме записи числа определяет экспоненту, а оставшиеся четыре - мантиссу числа. Перевод обычной записи числа в 5-байтную форму осуществляется в четыре этапа:
1. Переводим число в его эквивалент в двоичной системе счисления.
Например:
11,375 = 1011,011.
Напомним, что в двоичной системе счисления
0,5 = 0,1
0,25 = 0,01
0,125 = 0,001 и т.д.
2. Определяем экспоненту числа. Для этого перемещаем десятичную точку влево или вправо, пока не дойдём до крайней единицы справа или слева. При этом считаем число шагов перемещений. Экспонента будет равна 128 плюс число шагов. Если движение производилось влево, то оно считается положительным, а если вправо - то отрицательным. В нашем примере получим после четырех шагов вправо:
.1011011
и, следовательно, экспонента равна 128+4 = 132.
3. Меняем состояние бита справа от десятичной точки в зависимости от того, положительным или отрицательным было наше исходное число. Если число было положительным, то этот бит надо выключить, а если отрицательным, то его надо оставить включенным. Таким образом, этот бит будет сигнализировать о знаке числа.
В нашем примере число было положительным и поэтому после смены состояния бита оно будет иметь вид:
.00110110
4. Определяем мантиссу числа. Для этого, не обращая внимание на десятичную точку, добавим справа определенное количеств нулей, чтобы получить 32-х разрядное число. Теперь распределим 32 разряда числа между четырьмя байтами и определим значение каждого байта:
00110100 (54) 00000000 (0)
00000000 (0) 00000000 (0)
Таким образом, 5-байтное представление числа 11,375 будет иметь следующий вид:
132 54 0 0 0
Если Вы хотите проверить все вышесказанное, то Вам необходимо ввести короткую программу, позволяющую считывать 5 последовательных байтов (например, через PEEK) из участка памяти, где записаны переменные BASIC-программы. Значения переменных в этой области хранятся именно в 5-ти байтной форме.
Теперь вернемся к главной теме этой главы. Операнды BASIC-команды BEEP, определяющие длительность и тональность звука, записываются в калькуляторный стек. Калькуляторный стек оперирует и хранит числа, записанные именно в 5-байтной форме. Поэтому, чтобы написать программу в машинных кодах для получения звука или музыки, необходимо значение этих операндов занести в калькуляторный стек именно в 5-байтной форме.
Процедура ВЕЕР, находящаяся по адресу 1016, извлекает эти два числа с калькуляторного стека и воспроизводит звуковой сигнал в соответствии со значениями этих чисел. Например, для получения эффекта, аналогичного команде БЕЙСИКа ВЕЕР .1,11, необходимо числа 0.1 и 11 вначале перевести в их 5-байтную форму:
0.1 = 125 76 204 204 204
11 = 132 48 0 0 0
Теперь необходимо передать последовательно эти 10 байтов на калькуляторный стек. Как это сделать, показано в программе 6.1. Здесь используется команда передачи блока LDIR. При этом в регистровую пару HL записывается адрес начала этих 10 байтов в памяти, а в регистровую пару DE - адрес вершины стека, который постоянно хранится в системной переменой STKEND (23653). В регистровую пару ВС записывается число перемещаемых байтов. Процедура ВЕЕР извлекает два числа в 5-байтной форме с вершины стека и адрес вершины стека восстанавливается, т.е. становится таким же, как и до записи туда чисел.
Листинг 6.1
ORG 23760
23760 |
ED |
5B |
65 |
5C |
LD DE, (23653) |
В DE установили то, что |
|
|
|
|
|
|
хранится в STKEND |
23764 |
21 |
E4 |
5C |
|
LD HL,DATA |
В HL - адрес наших 10 байтов |
23767 |
01 |
0A |
00 |
|
LD BC,10 |
В BC - количество пересылаемых байтов |
23770 |
ED |
B0 |
|
|
LDIR |
Переброска данных на машинный |
|
|
|
|
|
|
стек калькулятора |
23772 |
ED |
53 |
65 |
5C |
LD (23653),DE |
Переустановили новое значение STKEND |
23776 |
CD |
F8 |
03 |
|
CALL 1016 |
Вызов процедуры BEEP |
23779 |
C9 |
|
|
|
RET |
Выход |
23780 |
DATA |
|
|
DEFM 125, 76, 204, |
204, 204 ;Число 0.1 |
23785 |
|
|
|
|
DEFM 132, 48, 0, 0, |
0 ;Число 11 |
Другой способ передачи чисел на стек калькулятора основан на использовании команды LD A, n и процедуры ПЗУ STACK_A, находящейся по адресу 11560, если передаются целые числа в диапазоне 0.. .255.
Если надо передать целые числа из диапазона 0.65535, то можно воспользоваться переброской через регистровую пару ВС и вызов процедуры ПЗУ STACK_BC, находящейся по адресу 11563. В программе 6.2 показано, как можно передать в калькуляторный стек число 11, используя регистр А.
Листинг 6.2
ORG 23760
23760 |
ED |
5B |
65 |
5C |
LD DE,(23653) |
В DE установили то, что |
|
|
|
|
|
|
хранится в STKEND |
23764 |
21 |
E9 |
5C |
|
LD HL,DATA |
В HL - адрес наших 10 байтов |
23767 |
01 |
05 |
00 |
|
LD BC,5 |
В BC - количество пересылаемых байтов |
23770 |
ED |
B0 |
|
|
LDIR |
Переброска данных на машинный |
|
|
|
|
|
|
стек калькулятора |
23772 |
ED |
53 |
65 |
5C |
LD (23653),DE |
Переустановили новое значение STKEND |
23776 |
3E |
0B |
|
|
LD A,11 |
В аккумуляторе число 11 |
23778 |
CD |
28 |
2D |
|
CALL 11560 |
Число 11 помещено на вершину |
|
|
|
|
|
|
стека калькулятора |
23781 |
CD |
F8 |
03 |
|
CALL 1016 |
Вызов процедуры BEEP |
23784 |
C9 |
|
|
|
RET |
Выход |
23785 |
DATA |
|
|
DEFM 125, 76, 204, |
204, 204 ;Число 0.1 |
Упакованная форма записи действительных чисел.
Теперь познакомимся с записью чисел с плавающей запятой в упакованной форме и с использованием кода калькулятора 52DEC для работы с числами, находящимися на калькуляторном стеке в этой форме.
Перевод 5-байтного числа в упакованную форму включает в себя следующие этапы:
1. Начиная с пятого байта, убираем все числа, равные нулю до тех пор, пока не выйдем на ненулевое число или не достигнем второго байта 5-байтной записи числа.
2. Определяем число удаленных байтов k и рассчитываем число g = 3-k.
3. Вычитаем 80 из числа, записанного в первом байте и, если результат больше или равен 64, то переходим к п.6.
4. Меняем значение экспоненты (число в первом байте) на число: экспонента минус 80 плюс g*64.
5. Переходим к п.7.
6. Экспонента числа занимает 2 байта. Значение первого байта равно g*64, а второго 80.
7. Записываем оставшиеся байты после модифицированной экспоненты.
Для выполнения обратного перехода необходимо:
1. Разделить значение первого байта на 64.
2. Если будет остаток от деления, то экспонента будет равна 80+остаток.
3. Если остатка от деления на 64 не будет, то экспонента равна 80 плюс значение второго байта.
4. Частное от деления на 64 используется для определения количества последующих байтов, это количество равно частному от деления плюс 1.
Например, для числа 11 упакованная форма имеет вид:
52 48
Делим 52 на 64 и получаем: частное = 0, а остаток = 52. Значит, после экспоненты, равной 80+52 = 132, следует только 0+1= 1 байт. В упакованной форме записи он равен 48. Следовательно, 5-байтная запись числа 11 будет иметь вид:
132 48 0 0 0
Программа 6.3 показывает, как может использоваться упакованная форма для реализации команды ВЕЕР .1,11 в машинных кодах.
Листинг 6.3
МАШ.КОД
EF 34 ED 4C CC CC CC 34 34 30 38
CD FB 03 C9
АДРЕС
23760
23761 23767
23770
23771 23784
АССЕМБЛЕР ORG 23760 RST 40 stk_data stk_data endcalc CALL 1016 RET
КОММЕНТАРИЙ
Включение калькулятора. На стеке 0.1 На стеке 0.1, 11 Выключение калькулятора. Вызов процедуры BEEP. Выход.
Для получения различных звуковых эффектов можно использовать процедуру ВЕЕР с точкой входа 949. При этом значения длительности и тональности звука подаются не на стек калькулятора, а соответственно в регистровые пары DE и HL.
Программа 6.4 показывает, как можно программно менять тональность звука, используя процедуру ВЕЕР в цикле. При этом будет меняться и длительность, потому что длительность звучания является функцией значений, записанных в регистровых парах HL и DE.
Листинг 6.4
АДРЕС |
МАШ |
.КОД |
АССЕМБЛЕР КОММЕНТАРИЙ |
|
|
|
|
ORG 23760 |
|
23760 |
06 |
FF |
|
LD B,255 |
Инициализация счётчика. |
23762 |
21 |
01 |
00 |
LD HL,1 |
Инициализация высоты тона. |
23765 |
11 |
01 |
00 |
LOOP LD DE,1 |
Инициализация длительности звука |
23768 |
E5 |
|
|
PUSH HL |
Надо предохранить HL и BC |
23769 |
C5 |
|
|
PUSH BC |
от порчи процедурой BEEP. |
23770 |
CD |
B5 |
03 |
CALL 949 |
Вызов процедуры BEEP. |
23773 |
C1 |
|
|
POP HL |
|
23774 |
E1 |
|
|
POP BC |
|
23775 |
23 |
|
|
INC HL |
Повышение высоты тона. |
23776 |
10 |
F3 |
|
DJNZ LOOP |
Повтор цикла. |
23778 |
C9 |
|
|
RET |
Выход из процедуры. |
Существует ещё один метод воспроизведения звука в компьютерах Spectrum. Для этого нужно знать частоту смены высокого и низкого уровня для каждой ноты и отправлять сигнал прямо на динамик, минуя процедуры ПЗУ. Этот метод можно использовать в игровых программах для создания "белого шума", эффекта взрыва и т.д. При этом используется команда OUT (254),A. Причем этой командой можно не только управлять работой динамика, но и менять цвет рамки экрана (цвет BORDER).
1. Если только нулевой бит регистра A включен, то BORDER - синий.
2. Если только первый бит регистра A включен, то BORDER - красный.
3. Если только второй бит регистра A включен, то BORDER - зеленый.
4. Если бит 4 регистра A включен, то на динамик компьютера идет высокий сигнал, а
если этот бит сброшен - то низкий.
Понятно, что если периодически менять состояние 4-го бита регистра A и использовать команду OUT (254),А, то частота звука из динамика будет пропорциональна частоте смены состояния 4-го бита. Для создания "белого шума" период чередования состояния 4-го бита должен быть случайным, но при использовании стандартной процедуры генерации случайных чисел, программа будет выполняться слишком медленно. Поэтому лучше использовать в качестве случайных чисел содержимое в каких-то ячейках памяти и, тем самым, переключать уровень сигнала на динамике (см. программу 6.5). В этой программе в одном цикле трижды используется команда OUT (254),А,
а также ряд команд NOP, что необходимо для создания временной задержки.
Длительность звука ограничена. Для этого используется команда
CP 60
Цвет BORDER также меняется случайным образом, чтобы этот цвет оставался постоянным, необходимо переписать биты 3-5 с системной переменной BORDER в биты 0-2 регистра А.
Листинг 6.5
ORG 23760
23760 |
21 |
00 00 |
LD HL,00 |
|
Мы начинаем брать данные для "белого шума" прямо из ПЗУ. |
23763 |
7E |
|
L1 LD A,(HL) |
|
Очередной байт из ПЗУ. |
23764 |
00 |
|
NOP |
|
Пауза. |
23765 |
D3 |
FE |
OUT (254) |
A |
Выдача сигнала на динамик. |
23757 |
00 |
|
NOP |
|
|
23758 |
00 |
|
NOP |
|
|
23769 |
D3 |
FE |
OUT (254) |
A |
|
23771 |
00 |
|
NOP |
|
|
23772 |
00 |
|
NOP |
|
|
23773 |
D3 |
FE |
OUT (254) |
A |
|
23775 |
00 |
|
NOP |
|
|
23776 |
23 |
|
INC HL |
|
Переход к следующей ячейке ПЗУ |
23777 |
7C |
|
LD A,H |
|
|
23778 |
FE |
3C |
CP 60 |
|
Проверка на конец работы. |
23780 |
20 |
ED |
JR NZ,L1 |
|
Возврат к началу цикла. |
23782 |
C9 |
|
RET |
|
Выход из процедуры. |
Если Вы хотите поэкспериментировать с созданием музыки, используя этот метод, то вам необходимо помнить, что каждую 1/50 секунды ваша программа будет прерываться процедурой сканирования клавиатуры. Поэтому, чтобы сохранить частоту звука, необходимо в начале программы запретить прерывания командой DI, а в конце программы вновь разрешить их командой EI. Кстати, именно поэтому невозможно прервать выполнение команды ВЕЕР клавишей BREAK.
Помните также, что в игровых программах звуковые эффекты не должны быть слишком длинными, т.к. на это время может приостанавливаться движение персонажей.
(Продолжение следует)