;-Minimal ayFX player v0.16 20.06.06---------------------------;
;                                                              ;
; Простейший плеер эффектов. Проигрывает эффекты на одном AY,  ;
; без музыки на фоне. Приоритет выбора каналов: если есть      ;
; свободные каналы, выбирается один из них. Если свободных     ;
; каналов нет, выбирается наиболее давно звучащий. Процедура   ;
; проигрывания использует регистры AF,BC,DE,HL,IX.             ;
;                                                              ;
; Инициализация:                                               ;
;   ld hl, адрес банка эффектов                                ;
;   call AFXINIT                                               ;
;                                                              ;
; Запуск эффекта:                                              ;
;   ld a, номер эффекта (0..255)                               ;
;   call AFXPLAY                                               ;
;                                                              ;
; В обработчике прерывания:                                    ;
;   call AFXFRAME                                              ;
;                                                              ;
;--------------------------------------------------------------;


; описатели каналов, по 4 байта на канал:
; +0 (2) текущий адрес (канал свободен, если старший байт =#00)
; +2 (2) время звучания эффекта
; ...

afxChDesc	DS 3*4




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

AFXINIT
	inc hl
	ld (afxBnkAdr+1),hl	;сохраняем адрес таблицы смещений
	
	ld hl,afxChDesc		;помечаем все каналы как пустые
	ld de,#00ff
	ld bc,#03fd
afxInit0
	ld (hl),d
	inc hl
	ld (hl),d
	inc hl
	ld (hl),e
	inc hl
	ld (hl),e
	inc hl
	djnz afxInit0

	ld hl,#ffbf			;инициализируем AY
	ld e,#15
afxInit1
	dec e
	ld b,h
	out (c),e
	ld b,l
	out (c),d
	jr nz,afxInit1

	ld (afxNseMix+1),de	;сбрасываем переменные плеера

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

AFXFRAME
	ld bc,#03fd
	ld ix,afxChDesc

afxFrame0
	push bc
	
	ld a,11
	ld h,(ix+1)			;сравнение старшего байта адреса на <11
	cp h
	jr nc,afxFrame7		;канал не играет, пропускаем
	ld l,(ix+0)
	
	ld e,(hl)			;берём значение информационного байта
	inc hl
			
	sub b				;выбираем регистр громкости:
	ld d,b				;(11-3=8, 11-2=9, 11-1=10)

	ld b,#ff			;выводим значение громкости
	out (c),a
	ld b,#bf
	ld a,e
	and #0f
	out (c),a
	
	bit 5,e				;будет изменение тона?
	jr z,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			;выводим значения тона
	out (c),a
	ld b,#bf
	ld d,(hl)
	inc hl
	out (c),d
	ld b,#ff
	inc a
	out (c),a
	ld b,#bf
	ld d,(hl)
	inc hl
	out (c),d
	
afxFrame1
	bit 6,e				;будет изменение шума?
	jr z,afxFrame3		;шум не изменяется
	
	ld a,(hl)			;читаем значение шума
	sub #20
	jr c,afxFrame2		;меньше #20, играем дальше
	ld h,a				;иначе конец эффекта
	ld b,#ff
	ld b,c				;в BC заносим наибольшее время
	jr afxFrame6
	
afxFrame2
	inc hl
	ld (afxNseMix+1),a	;сохраняем значение шума
	
afxFrame3
	pop bc				;восстанавливаем значение цикла в B
	push bc
	inc b				;количество сдвигов для флагов TN
	
	ld a,%01101111		;маска для флагов TN
afxFrame4
	rrc e				;сдвигаем флаги и маску
	rrca
	djnz afxFrame4
	ld d,a
	
	ld bc,afxNseMix+2	;сохраняем значения флагов
	ld a,(bc)
	xor e
	and d
	xor e				;E маскируется с помощью D
	ld (bc),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,4				;переходим к следующему каналу
	add ix,bc
	pop bc
	djnz afxFrame0

	ld hl,#ffbf			;выводим значение шума и микшера
afxNseMix
	ld de,0				;+1(E)=noise, +2(D)=mixer
	ld a,6
	ld b,h
	out (c),a
	ld b,l
	out (c),e
	inc a
	ld b,h
	out (c),a
	ld b,l
	out (c),d
	
	ret



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

AFXPLAY
	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
	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
	djnz afxPlay0

	pop de				;забираем адрес эффекта из стека
	ld (ix-3),e			;заносим в описатель канала
	ld (ix-2),d
	ld (ix-1),b			;зануляем время звучания
	ld (ix-0),b

	ret