2.9. П р и л о ж е н и е к р а з д е л у "Ф о н о в о е м у з ы к а л ь н о е с о п р о в о ж д е н и е" ;────────────────────────────────────────────────────────────────── Len_Inform EQU 000DH ; Длина блока данных Jiffy EQU 0FC9EH ; Счетчик прерываний Vibrato EQU 008 ; Отклонение частоты при вибрато ;────────────────────────────────────────────────────────────────── ; О с н о в н а я п р о г р а м м а. Вход может быть параллельным, ; но можно "вешать" на Hook &HFD9A, &HFD9F ;────────────────────────────────────────────────────────────────── Music: PUSH HL ;──┐ PUSH DE ; │ PUSH BC ; ├─▶ Сохранение используемых регистров PUSH AF ; │ PUSH IX ; │ PUSH IY ;──┘ LD A,(Jiffy) ;──┐ LD B,A ; │ LD A,(Timer) ; ├─▶ Если нового тика не было, - то выход CP B ; │ JR Z,Exit ;──┘ LD HL,P_Slot ;──┐ DI ; │ IN A,(0A8H) ; │ LD (HL),A ; │ INC HL ; ├─▶ Запомним конфигурацию памяти LD A,(0FFFFH) ; │ CPL ; │ LD (HL),A ;──┘ PUSH HL LD DE,0FFAAH ;┬──▶ Устанавливаем свою конфигурацию: CALL Found_Slot ;┘ с л о т 3-2 LD IX,Inform LD IY,All_Data LD C,001H ;Начинаем с первого голоса EXX LD B,003H ;Будем работать с тремя музыкальн. каналами LD DE,Len_Inform Label_0:EXX LD A,C OR A ;Сброс битов регистра флагов DEC (IX+000H) PUSH AF CALL NZ,Tik_mus ;Если длительность ноты не кончилась, то вы- POP AF ;полняем "попутные эффекты". CALL Z,Change ;Иначе перемещаемся по очереди данных INC C ;Переходим к следующему каналу INC C ; 1──▶3──▶5, что означает 1,2,3 голос EXX ADD IX,DE ;Указатель на следующий информ. блок DJNZ Label_0 LD A,(Jiffy) ;─┬──▶Считать номер тика LD (Timer),A ;─┘ и сохранить для сравнения POP HL LD E,(HL) ;──┐ DEC HL ; │ LD D,(HL) ; ├─▶Восстанавливаем конфигурацию памяти CALL Found_Slot ;──┘ Exit: POP IY ; ──┐ POP IX ; │ POP AF ; │ POP BC ; ├─▶Восстанавливаем значение регистров POP DE ; │ POP HL ;───┘ RET ;───▶ Возврат из программы Timer: Defb 0 P_Slot: Defb 0 S_Slot: Defb 0 ;──────────────────────────────────────────────────────────────────── Tik_mus: ;Подпрограмма "попутных эффектов" LD A,(IX+000H) ;Остался ли один тик? DEC A ;Если да и это не 1/32, то "глушим" JR NZ,g_022 LD A,(IX+01H) ;──┐ CP 3 ; │ RET C ; │ OR A ; │ LD E,C ; │ RES 0,E ; │ LD A,(IY+01H) ; │ JR NZ,g_023 ; │ LD E,C ; ├─▶ Для отделения нот друг от друга g_023: OR E ; │ "глушим" соответствующий канал, RL E ; │ когда остается играть 1 тик RL E ; │ RL E ; │ OR E ; │ JP Label_3 ;──┘ g_022: LD A,(IX+07H) ;──┐ OR A ; ├▶Если не разрешено "фокусничать", RET NZ ;──┘ то уходим молча !◀───────────┐ LD A,(IX+12) ;──┐ │ OR A ; ├─▶ При "Legato" обычно тоже ──┘ RET Z ;──┘ CALL Select ;Можем делать вибрато, а можем и не делать ! LD A,(IX+08H) ;Если модуляция включена,то нам делать нечего CP 16 RET Z LD E,0 ;──┐ LD L,A ; │ LD A,(IX+001H) ; │ BIT 1,(IX+002H) ; │ JR Z,Tmp_1 ; │ RLCA ; │ Tmp_1: LD B,(IX+000H) ; │ SUB B ; │ LD B,A ; │ Постепенно (с каждым новым тиком) LD A,L ; ├──▶ глушим все каналы, чтобы сильно не CP B ; │ "пищали", так приятней слушается JR C,Gasi ; │ музыка, но не забываем, что темп SUB B ; │ у голосов может быть разный! RET M ; │ LD E,A ; │ Gasi: LD A,C ; │ RRCA ; │ ADD A,88H ; │ JP Play ;──┘ Select: PUSH IX ;Вибрато-частотное отклонение LD IX,Golos_1 ;(причем и "+" и "-") от заданного LD E,C ;значения частоты DEC E ;Но у нас только "-",- так приятней слушать LD D,0 ; ADD IX,DE LD H,(IX+0) LD L,(IX+1) LD IX,Vibr_1 RRC E ADD IX,DE LD A,(IX+0) OR A LD E,Vibrato JR Z,Vie DEC A DEC A SBC HL,DE Vie: INC A LD (IX+0),A POP IX PUSH HL JP Leba Vibr_1: Defb 0 Vibr_2: Defb 0 Vibr_3: Defb 0 ;────────────────────────────────────────────────────────── ; Подпрограмма обработки очереди ;────────────────────────────────────────────────────────── Change: LD L,(IX+05H) ;Достали текущий адрес данных LD H,(IX+06H) LD (IX+07H),00 ;Разрешили игру на время Label_5:CALL Command ;Анализ и выполнение команд JR Z,Label_5 ;Движемся пока на выходе Z=1 Restore:CALL Found_Len ;Установка следующей длительности LD (IX+05H),L ;Сохраняем адрес данных LD (IX+06H),H ;до следуюшего обнуления LD E,01111011B ;Запрещаем два соседних голоса LD B,C ;для "играния" обрабатываемого канала INC B Label_6:RLC E DEC B DJNZ Label_6 LD A,(IY+01H) OR E CALL Label_3 LD A,C RRCA ADD A,088H LD E,A LD A,(IX+07H) ;Если пауза или запрещен канал, OR A ;то канал молчит ! LD A,E LD E,B JR NZ,g_020 LD E,(IX+08H) g_020: CALL Play LD A,E CP 16 JR C,g_021 ;Выходим, если амплитуда <16 LD A,06H ;Восстановили значение 6 регистра LD E,(IY+00H) CALL Play ADD A,05H ;Восстановили значение 11 регистра LD E,(IY+02H) CALL Play INC A LD E,(IY+03H) ;Восстановили значение 12 регистра CALL Play INC A LD E,(IY+04H) ;Восстановили значение 13 регистра CALL Play g_021: LD A,(IY+01H) ;────────────────────────────────────────────────────────── Found_Micsher: ;Различные операции с 7 регистром PSG LD (IY+01H),A ; Label_3:LD E,A LD A,07H Play: OUT (0A0H),A PUSH AF LD A,E OUT (0A1H),A POP AF RET ;────────────────────────────────────────────────────────── Found_Slot: ;Установка слотовой LD A,D ;конфигурации OUT (0A8H),A LD A,E LD (0FFFFH),A RET ;────────────────────────────────────────────────────────── Found_Len: ;Установка количества тиков XOR A ;в зависимости от длительности LD B,(IX+02H) ;и темпа F_Len: ADD A,(IX+01H) ; DJNZ F_Len LD (IX+000H),A RET ;────────────────────────────────────────────────────────── Command: LD A,(HL) ;──┐ AND 0F0H ; │ RRCA ; │ RRCA ; │ RRCA ; │ RRCA ; │ LD E,A ; │ LD D,0 ; │ ADD A,A ; │ Анализ номера команды ADD A,E ; ├─▶ с последующим переходом LD E,A ; │ на подпрограмму выполенния PUSH HL ; │ той или иной команды LD HL,Vector ; │ с первым параметром ADD HL,DE ; │ EX DE,HL ; │ POP HL ; │ LD A,(HL) ; │ AND 00FH ; │ PUSH DE ; │ RET ;──┘ ;───────────────────────────────────────────────────────── ; Вектора переходов на подпрограммы выполнения команд ;───────────────────────────────────────────────────────── Vector: JP Com_0 JP Com_1 JP Com_2 JP Com_3 JP Com_4 JP Com_5 JP Com_6 JP Com_7 JP Com_8 JP Com_9 JP Com_10 JP Com_11 JP Com_12 JP Com_13 JP Com_14 JP Com_15 ;───────────────────────────────────────────────────────── ; Подпрограмма меняет значения частот, когда настало время ;───────────────────────────────────────────────────────── Com_0: PUSH HL LD HL,Notes DEC A ADD A,L LD L,A JR NC,g_001 INC H g_001: LD L,(HL) LD H,0 LD B,(IX+11) LD A,8 SUB B JR Z,g_002 LD B,A g_003: ADD HL,HL DJNZ g_003 g_002: Leiba: PUSH IX LD IX,Golos_1 LD E,C DEC E LD D,0 ADD IX,DE LD (IX+0),H LD (IX+1),L POP IX Leba: LD E,H LD A,C CALL Play LD E,L DEC A CALL Play POP HL INC HL INC A OR A RET Golos_1:Defw 0 Golos_2:Defw 0 Golos_3:Defw 0 ;───────────────────────────────────────────────────────── ; Подпрограмма меняет и запоминает значения 7 регистра PSG ;───────────────────────────────────────────────────────── Com_1: INC HL LD A,(HL) CALL Found_Micsher g_004: INC HL XOR A RET ;───────────────────────────────────────────────────────── ; Подпрограмма запоминает значение темпа 1 или 2 ;───────────────────────────────────────────────────────── Com_2: LD (IX+02H),A g_005: JR g_004 ;───────────────────────────────────────────────────────── ; Подпрограмма запоминает значения амлитуды ;───────────────────────────────────────────────────────── Com_3: INC A LD (IX+08H),A g_006: JR g_005 ;───────────────────────────────────────────────────────── ; Подпрограмма запоминает значения 6 регистра PSG ;───────────────────────────────────────────────────────── Com_4: ADD A,A LD (IY+00H),A g_007: JR g_006 ;───────────────────────────────────────────────────────── ; Подпрограмма запоминает значения 13 регистра PSG ;───────────────────────────────────────────────────────── Com_5: LD (IY+04H),A g_008: JR g_007 ;───────────────────────────────────────────────────────── ; Подпрограмма запоминает значения 11 регистра PSG ;───────────────────────────────────────────────────────── Com_6: LD E,A XOR A LD B,10 g_009: ADD A,E DJNZ g_009 LD (IY+02H),A g_010: JR g_008 ;───────────────────────────────────────────────────────── ; Подпрограмма запоминает значения 12 регистра PSG ;───────────────────────────────────────────────────────── Com_7: LD E,A XOR A ADD A,E ADD A,E LD (IY+03H),A g_011: JR g_010 ;───────────────────────────────────────────────────────── ; Подпрограмма меняет и запоминает длительность ;───────────────────────────────────────────────────────── Com_8: LD E,11000000B LD D,10111111B LD B,A RRC B SUB B CP B JR Z,g_013 RLC B INC B RRC B g_012: RRC E DJNZ g_012 LD A,B OR E JR g_014 g_013: RRC E RRC D DJNZ g_013 LD A,B OR E AND D g_014: LD (IX+01H),A g_015: JR g_011 ;───────────────────────────────────────────────────────── ; Подпрограмма выставляет паузу ;───────────────────────────────────────────────────────── Com_9: LD A,01H LD (IX+07H),A INC HL OR A RET ;───────────────────────────────────────────────────────── ; Подпрограмма переходит по адресу данных Call or Jump ;───────────────────────────────────────────────────────── Com_10: INC HL LD E,(HL) INC HL LD D,(HL) INC HL LD (IX+09H),L LD (IX+0AH),H LD HL,Admat ADD HL,DE XOR A RET ;───────────────────────────────────────────────────────── ; Подпрограмма возвращается из адреса ;───────────────────────────────────────────────────────── Com_11: LD L,(IX+09H) LD H,(IX+0AH) XOR A RET ;───────────────────────────────────────────────────────── ; Подпрограмма заглушки канала ;───────────────────────────────────────────────────────── Com_12: LD A,1 LD (IX+07H),A OR A RET ;───────────────────────────────────────────────────────── ; Подпрограмма меняет текущую октаву ;───────────────────────────────────────────────────────── Com_13: LD (IX+11),A g_016: JR g_015 Com_14: LD (IX+12),A JR g_016 Com_15: JR g_016 ;────────────────────────────────────────────────────────── ; 8 октава - образец частот (все остальные частоты ; вычисляются в зависимости от октавы и номера ноты) ;────────────────────────────────────────────────────────── Notes: Defb 01BH Defb 019H Defb 018H Defb 016H Defb 015H Defb 014H Defb 013H Defb 012H Defb 011H Defb 010H Defb 00FH Defb 00EH ;────────────────────────────────────────────────────────── Inform: Defb 1 ;Сколько тиков осталось играть 1 голос +0 Defb 8 ;Текущая длительность 1 голоса +1 Defb 1 ;Темп текущего голоса (1 OR 2) +2 Defw Admat_1 ;Адрес хранения данных 1 голоса +3 Defw Admat_1 ;Адрес текущих данных 1 голоса +5 Defb 0 ;1 Если музыка кончилась или пауза +7 Defb 0FH ;Громкость 1 голоса +8 Defw Admat_1 ;Хранение адресов для Call and Jump +9 Defb 05H ;Текущая октава +11 Defb 00 ;Легато или Стаккато +12 Defb 1 ;Сколько тиков осталось играть 2 голос +0 Defb 8 ;Текущая длительность 2 голоса +1 Defb 1 ;Темп текущего голоса (1 OR 2) +2 Defw Admat_2 ;Адрес хранения данных 2 голоса +3 Defw Admat_2 ;Адрес текущих данных 2 голоса +5 Defb 0 ;1 Если музыка кончилась или пауза +7 Defb 0FH ;Громкость 2 голоса +8 Defw Admat_2 ;Хранение адресов для Call and Jump +9 Defb 05H ;Текущая октава +11 Defb 00 ;Легато или Стаккато +12 Defb 1 ;Сколько тиков осталось играть 3 голос +0 Defb 8 ;Текущая длительность 3 голоса +1 Defb 1 ;Темп текущего голоса (1 OR 2) +2 Defw Admat_3 ;Адрес хранения данных 3 голоса +3 Defw Admat_3 ;Адрес текущих данных 3 голоса +5 Defb 0 ;1 Если музыка кончилась или пауза +7 Defb 0FH ;Громкость 3 голоса +8 Defw Admat_3 ;Хранение адресов для call and jump +9 Defb 05H ;Текущая октава +11 Defb 00 ;Легато или Стаккато +12 All_Data: Defb 15 ;Данные для 6 регистра +0 Defb 0B8H ;Данные для 7 регистра +1 Defb 200 ;Данные для 11 регистра +2 Defb 10 ;Данные для 12 регистра +3 Defb 1 ;Данные для 13 регистра +4 ;────────────────────────────────────────────────────────────────── Admat: Admat_1: Admat_2: Admat_3: