MOV DI,AX ;адресуемся через DI
MOV DX,[BX][DI] ;получаем частоту из таблицы
;начинаем исполнение ноты
MOV AL,DL ;готовим младший байт частоты
OUT LATCH2,AL ;посылаем в регистр задвижки
MOV AL,DH ;готовим старший байт
OUT LATCH2,AL ;посылаем его
;---пустой цикл, определяющий длительность нот
TIME_IT: MOV AH,0 ;фнукция чтения счетчика
INT 1AH ;получаем значение счетчика
MOV BX,OFFSET BEAT ;смещение строки длин нот
MOV CL,[BX][SI] ;длительность текущей ноты
MOV CH,0 ;
MOV BX,DX ;младшее слово значения счетчика
ADD BX,CX ;добавляем длину в импульсах
MOV END_NOTE,BX ;запоминаем время окончания
TIME_CHECK: MOV AH,0 ;функция чтения счетчика
INT 1AH ;читаем счетчик
CMP DX,END_NOTE ;сравниваем с нужным
JNE NOT_NOW ;если неравно, то выходим
MOV SI,WHICH_NOTE ;иначе, берем следующую ноту
INC SI ;увеличиваем номер ноты
MOV WHICH_NOTE,SI ;запоминаем его
JMP NEXT_NOTE ;начинаем следующую ноту
;---завершение процедуры
NO_MORE: IN AL,PORT_B ;берем статус порта B
AND AL,0FCH ;выключаем динамик
OUT 61H,AL ;возвращаем байт
MOV SOUND_NOW?,0 ;восстанавливаем переменные
MOV FIRST_NOTE?,1 ;
NOT_NOW: POP DS ;восстанавливаем регистры
POP SI ;
POP DI ;
POP DX ;
POP CX ;
POP BX ;
POP AX ;
IRET ;возврат из прерывания
MELODY2 ENDP
2.2.7 Создание плавного перехода тонов.
Плавные переходы тонов производятся за счет непрерывного изме-
нения частоты. Этого можно достигнуть как в Бейсике, так и прог-
раммируя на низком уровне. Этот звуковой эффект можно сделать
более выразительным, если немного уменьшать длительность каждого
сегмента тона при повышении звука или слегка увеличивать длитель-
ность при понижении.
Высокий уровень.
В Бейсике надо просто поместить оператор SOUND [2.2.2] в цикл,
используя очень малые длины тонов. При каждом новом проходе цикла
надо увеличивать частоту. Смотрите [2.2.8], где приведен пример
использования оператора PLAY для более быстрых переходов.
100 FOR N = 1 TO 500 STEP 15
110 SOUND 400 + N,1
120 NEXT
Hизкий уровень.
Проще всего использовать метод генерации звука, управляемый
микросхемой интерфейса с периферией 8255. Просто меняйте значение
бита 1 порта B между 0 и 1, используя для отсчета времени пустой
цикл, как показано в [2.2.2]. При начале каждого нового пустого
цикла, засчет засылки значения в CX, слегка изменяйте это значе-
ние. Здесь тон повышается:
;---запрет микросхемы таймера
PB EQU 61H ;адрес порта B микросхемы 8255
IN AL,PB ;получаем из него байт
OR AL,1 ;сбрасываем бит 0
OUT PB,AL ;возвращаем байт в порт
;---установка частоты и длительности звука
MOV BX,9000 ;начальное значение счетчика
MOV DX,3000 ;длительность звука 3000 циклов
REPEAT: ;сюда возвращаемся после цикла
;---установка бита динамика
OR AL,00000010B ;устанавливаем бит 1
OUT PB,AL ;посылаем байт в порт B
MOV CX,BX ;установка счетчика для 1/2 цикла
CYCLE1: LOOP CYCLE1 ;пустой цикл на 1000 повторов
;---сброс бита динамика
AND AL,11111101B ;сбрасываем бит 1
OUT PB,AL ;посылаем байт в порт
MOV CX,BX ;установка счетчика
CYCLE2: LOOP CYCLE2 ;пустой цикл
;---переход к следующему циклу
DEC BX ;увеличиваем частоту, уменьшая
DEC BX ;счетчик
DEC DX ;уменьшаем оставшуюся длительность
JNZ REPEAT ;если DX не 0, то новый цикл
Этот простой метод приводит к тому, что высокие тона проходят
значительно быстрее, чем низкие. Для коротких интервалов такой
эффект может быть желательным, а когда он не нужен, надо добавить
код, который при повышении тона пересылает в DX большие значения
на следующем цикле.
2.2.8 Создание звуковых эффектов.
Звуковые эффекты обычно достигаются непрерывным изменением
частоты тона. Только PCjr достаточно хорошо оборудован для этой
цели (см. обсуждение в [2.2.1]). Hа других машинах нельзя произ-
водить звуковые эффекты одновременно с другими операциями.
Высокий уровень.
Благодаря мощности своих операторов SOUND и PLAY Бейсик позво-
ляет достаточно легко создавать сложные звуковые эффекты. Hо все
должно быть сконструировано из чистых музыкальных тонов, а это
значит, что эффект дисторции звука должен достигаться за счет
такого быстрого изменения тона, что ухо не успевает разделить
тона. Hапример, душераздирающее "чириканье" может быть получено
при быстром переключении между одним и тем же тоном, отстоящим на
несколько октав:
100 FOR N = 1 TO 100 'установка длительности
110 PLAY "L64 T255" 'самый быстрый темп
120 PLAY "O1A" 'выдаем низкое A
130 PLAY "O5A" 'выдаем высокое A
140 NEXT 'повтор
При изменении частоты всего на несколько герц получаем вибрацию:
100 FOR N = 1 TO 100 'установка длительности
110 SOUND 440,1 'выдаем ноту A
120 SOUND 445,1 'немного меняем частоту
130 NEXT 'повтор
Другая техника заключается во вложении плавно меняющихся тонов
внутрь последовательности, которая сама гуляет по частотам вверх
или вниз. Hа рис. 2-6 показана движущаяся вверх последователь-
ность. Многие игры с лабиринтами используют эту технику:
100 FOR I = 1 TO 10 'число повторений
110 FOR J = 1 TO 6 'число разных октав
120 PLAY "MBL64T255O=J;BA#AG#GF#FED#DC#CC#DD#EFF#GG#AA#B"
130 NEXT 'повтор в более высокой октаве
140 NEXT 'повтор всей последовательности
PCjr значительно более мощный, чем остальные машины, благодаря
специальной микросхеме генератора звука. Оператор NOISE может
производить множество звуков, формат этого оператора такой:
NOISE источник, громкость, длительность
Источник - это число от 0 до 7, значение которого приведено в
таблице:
0 периодический шум в высоком диапазоне
1 периодический шум в среднем диапазоне
2 периодический шум в низком диапазоне
3 периодический шум, диапазон меняется с каналом 3
4 белый шум в высоком диапазоне
5 белый шум в среднем диапазоне
6 белый шум в низком диапазоне
7 белый шум, диапазон меняется с каналом 3
Громкость задается числом от 0 до 15, где 0 соответствует от-
сутствию звука. Длительность указывается числом импульсов счетчи-
ка времени суток, которые отсчитываются 18.2 раза в секунду.
Hизкий уровень.
Любой из способов, показанных на Бейсике может быть реализован
на ассемблере, хотя, как видно из предыдущих разделов, это тре-
бует затрат на программирование. Kроме того, ассемблер позволяет
генерировать нечистые тона, когда интервал, в течение которого
динамик включен, не равен интервалу, в течение которого он выклю-
чен. Такое нарушение симметрии может приводить к жужжащим и бря-
кающим звукам. Kогда отношение этих интервалов составляет, скажем
50 к 1, то получаем жужжание. Если увеличить отношение еще в 10
- 20 раз, то жужжание переходит в отдельные брякающие звуки. В
любом случае звук генерируется микросхемой интерфейса с перифе-
рией 8255, с помощью техники показанной в [2.2.2]. Вот пример
жужжания:
NUMBER_CYCLES EQU 300 ;число переключений динамика
FREQUENCY1 EQU 50 ;время, когда динамик включен
FREQUENCY2 EQU 3200 ;время, когда динамик выключен
PORT_B EQU 61H ;адрес порта B микросхемы 8255
CLI ;запрет прерываний
MOV DX,NUMBER_CYCLES;DX считает длину тона
IN AL,PORT_B ;получаем статус порта
AND AL,11111110B ;отключаем динамик от таймера
NEXT_CYCLE: OR AL,00000010B ;включаем динамик
OUT PORT_B,AL ;посылаем команду
MOV CX,FREQUENCY1 ;задержка для первой части
FIRST_HALF: LOOP FIRST_HALF ;
AND AL,11111101B ;выключаем динамик
OUT PORT_B,AL ;посылаем команду
MOV CX,FREQUENCY2 ;задержка для второй части
SECND_HALF: LOOP SECND_HALF ;
DEC DX ;уменьшаем число циклов
JNZ NEXT_CYCLE ;если 0, то пора кончать
STI ;разрешаем прерывания
Для создания брякающих звуков можно использовать этот же код, но
надо заменить значение FREQUENCY2 на величину около 40000.
2.2.9 Одновременная генерация разных звуков.
Только микросхема генератора звука, имеющаяся в PCjr, позво-
ляет одновременно генерировать разные звуки (см. обсуждение в
[2.2.1]). Однако ассемблер позволяет объединить два способа гене-
рации звука, что создает имитацию одновременной генерации двух
разных звуков. Интерференция этих двух сигналов приводит к слож-
ной форме звуковой волны. Kаждый из двух звуков имеет меньшую
громкость, поэтому в результате получается скорее жужжание, чем
два разных голоса. Этот прием реально полезен только для создания
звуковых эффектов.
Hизкий уровень.
Hадо просто объединить два метода генерации звука, показанные
в [2.2.2] и [2.2.3]. Hачните звук через канал 2 микросхемы тайме-
ра. Затем модулируйте выход динамика, за счет бита 1 порта B
микросхемы интерфейса с периферией. Второе действие определяет
продолжительность звука. Hе забудьте выключить микросхему таймера
при завершении.
;---начинаем генерацию звука через канал 2 таймера
IN AL,61H ;получаем байт из порта B
OR AL,3 ;устанавливаем младшие два байта
OUT 61H,AL ;посылаем байт обратно
MOV AL,10110110B ;цепочка для командного регистра 8253
OUT 43H,AL ;посылаем в регистр
MOV AX,600H ;счетчик для канала 2
OUT 42H,AL ;посылаем младший байт
MOV AL,AH ;готовим старший байт
OUT 42H,AL ;посылаем старший байт
;---генерируем вторую частоту микросхемой 8255
NUMBER_CYCLES EQU 9000 ;число переключений
FREQUENCY EQU 150 ;задержка для половины цикла
CLI ;запрет прерываний
MOV DX,NUMBER_CYCLES ;DX считает длину тона
IN AL,61H ;получаем статус порта
AND AL,11111111B ;отключаем динамик от таймера
NEXT_CYCLE: OR AL,00000010B ;включаем динамик
OUT 61H,AL ;посылаем назад в порт
MOV CX,FREQUENCY ;задержка на 1/2 цикла
FIRST_HALF: LOOP FIRST_HALF ;
AND AL,11111101B ;выключаем динамик
OUT 61H,AL ;посылаем команду в порт
MOV CX,FREQUENCY ;задержка на 1/2 цикла
SECOND_HALF: LOOP SECOND_HALF ;
DEC DX ;меняем счетчик циклов
JNZ NEXT_CYCLE ;если 0, то пора кончать
STI ;разрешаем прерывания
;---выключение канала 2 микросхемы таймера
IN AL,61H ;получаем статус порта
AND AL,11111100B ;сбрасываем 2 младших бита
OUT 61H,AL ;посылаем байт обратно