	module ayfx
	
;-Minimal ayFX player2 v0.16 20.06.06--------------------------;
;                                                              ;
; Простейший плеер эффектов. Проигрывает эффекты на одном AY,  ;
; без музыки на фоне. Громкость проигрывания эффектов задаётся ;
; при их вызове. Приоритет выбора каналов: если есть свободные ;
; каналы, выбирается один из них. Если свободных каналов нет,  ;
; выбирается наиболее давно звучащий. Процедура проигрывания   ;
; использует регистры AF,BC,DE,HL,IX.                          ;
;P.S. от Alone Coder'а: задействовал IY и еще 2 байта, чтобы
;не играть эффект, когда он тише музыки (убрано для TFM музыки)
;                                                              ;
; Инициализация:                                               ;
;   ld hl, адрес банка эффектов                                ;
;   call AFXINIT                                               ;
;                                                              ;
; Запуск эффекта:                                              ;
;   ld a, номер эффекта (0..255)                               ;
;   ld c, относительная громкость (-15..15)                    ;
;   call AFXPLAY                                               ;
;                                                              ;
; В обработчике прерывания:                                    ;
;   call AFXFRAME                                              ;
;                                                              ;
;--------------------------------------------------------------;


; описатели каналов, по 5+2 байт на канал:
; +0 (2) текущий адрес (канал свободен, если старший байт =#00)
; +2 (2) время звучания эффекта
; +4 (1) громкость эффекта
; +6 (2) ПЕРИОД ТОНАЛЬНИКА
; ...

afxChDesc       DS 3*(5+2)




;--------------------------------------------------------------;
; Инициализация плеера эффектов.                               ;
; Выключает все каналы, устанавливает переменные.              ;
; Вход: HL = адрес банка с эффектами                           ;
;--------------------------------------------------------------;

INIT
	ld a,(hl)
	ld (afxEffectsAll),a
        INC HL
        LD (afxBnkAdr+1),HL    ;сохраняем адрес таблицы смещений

        LD HL,afxChDesc         ;помечаем все каналы как пустые
        LD DE,#00FF
        LD B,3
afxInit0
        LD (HL),D
        INC HL
        LD (HL),D
        INC HL
        LD (HL),E
        INC HL
        LD (HL),E
        INC HL
        LD (HL),D
        INC HL
       LD (HL),D
       INC HL
       LD (HL),D
       INC HL
        DJNZ afxInit0
	ifdef TFM
	ld a,(TURBOFMON)
	or a
	ret nz ;TFM present
	ld a,#c9 ;ret
	ld (waitstatus),a
	endif
        RET ;TFM absent

;--------------------------------------------------------------;
; Проигрывание текущего кадра.                                 ;
; Параметров не имеет.                                         ;
;--------------------------------------------------------------;

FRAME
        LD BC,#03FD
        LD IX,afxChDesc
afxFrame0
        PUSH BC
        LD H,(IX+1)                    ;сравнение старшего байта
                                                 ;адреса на <11
        ld a,h
		or a
        jp z,afxFrame7         ;канал не играет, пропускаем
        LD L,(IX+0)

        LD E,(HL)          ;берём значение информационного байта
        INC HL

        LD A,11
        SUB B                       ;выбираем регистр громкости:
        LD D,B                        ;(11-3=8, 11-2=9, 11-1=10)
        LD B,#FF                         ;начало вывода значения
	ifndef TFM	;для TurboSound!!! выключено для TFM
       OUT (C),B ;AY #1(0)      ;т.к.может вырубиться при HY=#FF
       LD HY,B                                   ;выкл громкости
	endif
                                ;
	ifdef TFM
	call waitstatus
	endif
        OUT (C),A ;выбрали регистр громкости
	ifdef TFM
	call waitstatus
	endif
	ifndef TFM	;для TurboSound!!! выключено для TFM
       IN A,(C)

       CP 16
       JR NC,$+4
       SUB 3;4
       JR NC,$+3
       XOR A
       LD (curvolCHN),A
	endif
        LD A,E
        AND #0F
        ADD A,(IX+4)         ;применяем текущую громкость канала
        CP 15
        JP M,$+5
        LD A,15
        OR A
        JP P,$+4
        XOR A
	ifndef TFM	;для TurboSound!!! выключено для TFM
      CP 13 ;громкую часть эффекта не глушим
      JR NC,curvON
curvolCHN=$+1
       CP 0
       JR C,curvOFF
curvON
       LD HY,#BF ;вкл
curvOFF
        LD B,HY
	else
        LD B,#BF
	endif
        OUT (C),A                    ;собственно вывод громкости

        BIT 5,E                         ;будет изменение тона?
        JR Z,afxFrame1          ;тон не изменяется
        LD A,(HL)
        LD (IX+5),A
        INC HL
        LD A,(HL)
        LD (IX+6),A
        INC HL
afxFrame1
        LD A,3                          ;выбираем регистры тона:
        SUB D                           ;3-3=0, 3-2=1, 3-1=2
        ADD A,A                         ;0*2=0, 1*2=2, 2*2=4

        LD B,#FF                        ;выводим значения тона
	ifdef TFM
	call waitstatus
	endif
        OUT (C),A
	ifdef TFM
	call waitstatus
	endif
	ifndef TFM	;для TurboSound!!! выключено для TFM
        LD B,HY
	else
        LD B,#BF
	endif
       LD D,(IX+5)
        OUT (C),D
        LD B,#FF
        INC A
	ifdef TFM
	call waitstatus
	endif
        OUT (C),A
	ifdef TFM
	call waitstatus
	endif
	ifndef TFM	;для TurboSound!!! выключено для TFM
        LD B,HY
	else
        LD B,#BF
	endif
       LD D,(IX+6)
        OUT (C),D
;afxFrame1
        BIT 6,E                         ;будет изменение шума?
        JR Z,afxFrame3          ;шум не изменяется

        LD A,(HL)                       ;читаем значение шума
        SUB #20
        JR C,afxFrame2          ;меньше #20, играем дальше
        LD H,A                          ;иначе конец эффекта
        LD (IX+2),#FE           ;делаем время наибольшим-1
        LD (IX+3),#FF
        LD E,#90                ;выключаем тон и шум в микшере
        JR afxFrame3

afxFrame2
        INC HL
        LD B,#FF                        ;вывод значения шума
        LD D,6
	ifdef TFM
	call waitstatus
	endif
        OUT (C),D
	ifdef TFM
	call waitstatus
	endif
	ifndef TFM	;для TurboSound!!! выключено для TFM
        LD B,HY
	else
        LD B,#BF
	endif
        OUT (C),A

afxFrame3
        POP BC               ;восстанавливаем значение цикла в B
        PUSH BC
        INC B ;4..2            ;количество сдвигов для флагов TN

        LD A,%01101111          ;маска для флагов TN
afxFrame4
        RRC E                           ;сдвигаем флаги и маску
        RRCA 
        DJNZ afxFrame4
        LD D,A
       LD BC,#FFFD                     ;читаем регистр микшера
        LD A,7
	ifdef TFM
	call waitstatus
	endif
        OUT (C),A
	ifdef TFM
	call waitstatus
	endif
        IN A,(C)
        XOR E                          ;накладываем флаги канала
        AND D
        XOR E
	ifndef TFM	;для TurboSound!!! выключено для TFM
        LD B,HY
	else
        LD B,#BF
	endif
        OUT (C),A                      ;выводим обратно в микшер
afxFrame5
        LD C,(IX+2)                 ;увеличиваем счётчик времени
        LD B,(IX+3)
        INC BC

afxFrame6
        LD (IX+2),C
        LD (IX+3),B

        LD (IX+0),L                ;сохраняем изменившийся адрес
        LD (IX+1),H

afxFrame7
        LD BC,5+2                 ;переходим к следующему каналу
        ADD IX,BC
        POP BC
        DEC B
        JP NZ,afxFrame0
        RET 

;--------------------------------------------------------------;
; Запуск эффекта на свободном канале. При отсутствии           ;
; свободных каналов выбирается наиболее давно звучащий.        ;
; Вход: A = номер эффекта 0..255                               ;
;       С = относительная громкость -15..15                    ;
;--------------------------------------------------------------;

PLAY
afxEffectsAll=$+1
	cp 0
	ret nc
	push ix
        PUSH BC                         ;сохраняем громкость
        LD DE,0                ;в DE наибольшее время при поиске
        LD H,E
        LD L,A
        ADD HL,HL
afxBnkAdr
        LD BC,0                 ;адрес таблицы смещений эффектов
        ADD HL,BC
        LD C,(HL)
        INC HL
        LD B,(HL)
        ADD HL,BC                    ;адрес эффекта получен в hl
        PUSH HL                ;сохраняем адрес эффекта на стеке

        LD HL,afxChDesc         ;поиск пустого канала
        LD B,3
afxPlay0
        INC HL
        INC HL
        LD A,(HL)          ;сравниваем время канала с наибольшим
        INC HL
        INC HL
        CP E
        JR C,afxPlay1
        LD C,A
        LD A,(HL)
        CP D
        JR C,afxPlay1
        LD E,C                      ;запоминаем наибольшее время
        LD D,A
        PUSH HL                 ;запоминаем адрес канала +3 в IX
        POP IX
afxPlay1
        INC HL
       INC HL
       INC HL
        DJNZ afxPlay0

        POP DE                  ;забираем адрес эффекта из стека
        LD (IX-4),E                  ;заносим в описатель канала
        LD (IX-3),D
        LD (IX-2),B                     ;зануляем время звучания
        LD (IX-1),B
        POP BC                          ;забираем громкость
        LD (IX-0),C             ;и заносим её в описатель канала
	pop ix
	ret
 	
	ifdef TFM
waitstatus
;bc=#fffd
        IN F,(C)
        JP M,$-2
		ret
	endif

	endmodule