
;Procedure playing synchronized distortion C sounds.
;The synchronization of timers with poly4 counter takes place on demand (syncWithPoly4Flag must be set to !=0).
;This is the implementation of synchronization method which does not use (writting to) STIMER register.
;The CPU just waits until timers are reloaded with 0 value. The wait time may be significantly reduced for channels 1 and 3 by switching them to be clocked by
;main phi2 clock (1.77MHz). It's not possible for channels 2 and 4. In case only channels 1 and 3 need to be synchronized the total wait time is about 400 CPU cycles (500 in this implementation). In case channel 2 or 4 needs to be synchronized the total wait time is about 7500 cycles.
;Generally this method of synchronization is complicated and very time consuming. The advantage is that when synchronization is applied to single channel then others are not affected - there is no audible click as the STIMER register is not written to.
;There is a possibility of reducing time consumed by this method by using IRQ handler. Instead of waiting until timers are reloaded we could turn on IRQs for the channels we want to synchronize. The IRQ handler would have to detect if all the timers awaited counted down to 0 (which fires the handler). If it is a case then the handler would run synchronization procedure.
;
;Author: Pawel Rosowski (pavros)
;Date: 2018.04.09

playAndSync
	lda vAudCtl
	sta audctl
	sta vTAudCtl

	ldx #3
plySnd1
	ldy voiceControlX,X
	lda voiceControlToAudC,Y
	sta vAudCx,X
	sta vTAudCx,X
	lda vAudFx,X
	sta vSAudFx,X
	sta vTAudFx,X
	dex
	bpl plySnd1

	lda syncWithPoly4Flag
	and syncWithPoly4MasterFlag
	bne plySnd2
	jmp plySnd4
plySnd2
	clc ; clear carry bit; it will be used to indicate whether any channel plays distortion C and needs to be synchronized with poly4 counter
	ldy #0
	sty longReset
	sty lowClock

	lda vAudCtl ; 0 = normal base clock (div28/64kHz), 1 = low base clock (div114/15kHz)
	and #$01
	beq plySndSynN1
	jmp plySndSynL1

; set synchronization point selectors (initial AUDFx values) based on selected Ex elements of poly4 sequence for normal base clock (div28/64kHz)
; set temporary AUDFx, AUDCx and AUDCTL values for resetting timers (vTAudFx, vTAudCx and vTAudCtl)
plySndSynN1
	lda voiceControlX + 0
	bne plySndSynN2 ; branch if voiceControlX != VoiceWaveC = 0
	ldx poly4SyncPointX + 0
	lda spToAudFCh1N,X
	sta vSAudF1
	sty vTAudF1
	sty vTAudC1
	lda vTAudCtl
	ora #$40 ; clock channel 1 with phi2/1.79MHz
	sta vTAudCtl
	sec
plySndSynN2
	lda voiceControlX + 1
	bne plySndSynN3
	ldx poly4SyncPointX + 1
	lda spToAudFCh2N,X
	sta vSAudF2
	sty vTAudF2
	sty vTAudC2
	inc longReset
	sec
plySndSynN3
	lda voiceControlX + 2
	bne plySndSynN4
	ldx poly4SyncPointX + 2
	lda spToAudFCh3N,X
	sta vSAudF3
	sty vTAudF3
	sty vTAudC3
	lda vTAudCtl
	ora #$20 ; clock channel 3 with phi2/1.79MHz
	sta vTAudCtl
	sec
plySndSynN4
	lda voiceControlX + 3
	bne plySndSynN5
	ldx poly4SyncPointX + 3
	lda spToAudFCh4N,X
	sta vSAudF4
	sty vTAudF4
	sty vTAudC4
	inc longReset
	sec
plySndSynN5
	bcs plySndSynN6 ; some voice requires sync
	jmp plySnd4 ; no voice requires sync
plySndSynN6
	jmp plySnd3 ; some voice requires sync

; set synchronization point selectors (initial AUDFx values) based on selected Ex elements of poly4 sequence for low base clock (div114/15kHz)
plySndSynL1
	sta lowClock
	lda voiceControlX + 0
	bne plySndSynL2 ; branch if voiceControlX != VoiceWaveC = 0
	ldx poly4SyncPointX + 0
	ldy sp15To5,X
	lda spToAudFCh1L,Y
	sta vSAudF1
	lda #0
	sta vTAudF1
	sta vTAudC1
	lda vTAudCtl
	ora #$40 ; clock channel 1 with phi2/1.79MHz
	sta vTAudCtl
	sec
plySndSynL2
	lda voiceControlX + 1
	bne plySndSynL3
	ldx poly4SyncPointX + 1
	ldy sp15To5,X
	lda spToAudFCh2L,Y
	sta vSAudF2
	lda #0
	sta vTAudF2
	sta vTAudC2
	inc longReset
	sec
plySndSynL3
	lda voiceControlX + 2
	bne plySndSynL4
	ldx poly4SyncPointX + 2
	ldy sp15To5,X
	lda spToAudFCh3L,Y
	sta vSAudF3
	lda #0
	sta vTAudF3
	sta vTAudC3
	lda vTAudCtl
	ora #$20 ; clock channel 3 with phi2/1.79MHz
	sta vTAudCtl
	sec
plySndSynL4
	lda voiceControlX + 3
	bne plySndSynL5
	ldx poly4SyncPointX + 3
	ldy sp15To5,X
	lda spToAudFCh4L,Y
	sta vSAudF4
	lda #0
	sta vTAudF4
	sta vTAudC4
	inc longReset
	sec
plySndSynL5
	bcs plySnd3 ; some voice requires sync
	jmp plySnd4 ; no voice requires sync

;synchronize channels with poly4 counter
plySnd3

	; set AudFx, AudCx and AudCtl registers with temporary values in order to reset timers
	lda vTAudC1
	sta audC1
	lda vTAudC2
	sta audC2
	lda vTAudC3
	sta audC3
	lda vTAudC4
	sta audC4
	lda vTAudF1
	sta audF1
	lda vTAudF2
	sta audF2
	lda vTAudF3
	sta audF3
	lda vTAudF4
	sta audF4
	lda vTAudCtl
	sta audctl
	; wait until timers count down to 0
	lda longReset
	bne plySnd3A1
	; wait at least 259 phi2 cycles before restoring base clock so that timers count down to 0
	jsr waitAtLeast250Cycles
	jmp plySnd3A2
	
plySnd3A1
	jsr waitAtLeast7168Cycles
	lda lowClock
	beq plySnd3A2
	jsr waitAtLeast7168Cycles
	jsr waitAtLeast7168Cycles
	;jsr waitAtLeast7168Cycles

plySnd3A2
	; restore AUDCTL desired value
	lda vAudCtl
	sta audctl

	jsr waitAtLeast250Cycles
	lda lowClock
	beq plySnd3B
	jsr waitAtLeast250Cycles
	jsr waitAtLeast250Cycles
	jsr waitAtLeast250Cycles

plySnd3B
	lda #0
	sta skctl
	; the period of resetting POKEY (SKCTL = 0) should last for at least 17 cycles to clear the longest 17-bit polycounter;

	; set AUDFx registers with synchronization point selectors
	lda vSAudF1
	sta audF1
	lda vSAudF2
	sta audF2
	lda vSAudF3
	sta audF3
	lda vSAudF4
	sta audF4

	lda lowClock
	beq plySndSyncDiv28

plySndSyncDiv114
	lda #3
	sta skctl ; put POKEY back to normal work/finish resetting; timers are loaded with AUDFx values
	; wait 83 cycles for timers to be reloaded by first pulse of div114 signal
	:28 nop ;56 cycles
	jmp plySndSyncCommon ;3 cycles

plySndSyncDiv28
	lda #3
	sta skctl ; put POKEY back to normal work/finish resetting; timers are loaded with AUDFx values
	; wait 24 cycles for timers to be reloaded by first pulse of div28 signal

plySndSyncCommon
	:12 nop ;24 cycles

	; POKEY AUDFx setup time starts here
plySnd4
	; set AUDFx registers with desired frequency divisors
	lda vAudF1
	sta audF1
	lda vAudF2
	sta audF2
	lda vAudF3
	sta audF3
	lda vAudF4
	sta audF4
	; POKEY AUDFx setup time ends here

; Constant required by advanced formulas in pokeyDistortionCSynchronizationConstants2.asm
maxPokeyAudFSetupTime = 32 + 9 ;maximum number of cycles needed for CPU to setup all POKEY registers after finishing POKEY resetting (storing 3 to SKCTL) - arbitrary value, just what's needed; here it is 4 * (lda + sta) + 9 refresh cycles
; Value of maxPokeyAudFSetupTime may be not sufficient in case of synchronizing on low clock div114 as the synchronization may happen in arbitrary place within screen frame. It's recommended to use basic constants from pokeyDistortionCSyncronizationConstants2.asm file which assume setup time of 180 cycles - the worst case comprising bad line DMA cycles.

	lda vAudC1
	sta audC1
	lda vAudC2
	sta audC2
	lda vAudC3
	sta audC3
	lda vAudC4
	sta audC4

	lda syncWithPoly4Flag
	beq plySnd5
	lda #0
	sta syncWithPoly4Flag
	lda syncWithPoly4MasterFlag
	beq plySnd5
	;activate key debuncing after POKEY reset
	jsr activateKeyDebouncing

plySnd5
	lda continuousPhaseMod
	beq plySnd6
	jsr delayAccuratePhase
plySnd6
	rts


syncWithPoly4Flag
	.db 1
syncWithPoly4MasterFlag
	.db 1
poly4SyncPointX
	:4 .db 1 ; Default is E1 which belongs to ST1 sampling track
longReset
	.db 0
lowClock
	.db 0

vSAudFx
vSAudF1
	.db 0
vSAudF2
	.db 0
vSAudF3
	.db 0
vSAudF4
	.db 0

vTAudFx
vTAudF1
	.db 0
vTAudF2
	.db 0
vTAudF3
	.db 0
vTAudF4
	.db 0

vAudFx
vAudF1
	.db $3E ;$F5 ;$1F ;$BF
vAudF2
	.db $3E
vAudF3
	.db $3E
vAudF4
	.db $3E

vAudCx
vAudC1
	.db 0
vAudC2
	.db 0
vAudC3
	.db 0
vAudC4
	.db 0

vTAudCx
vTAudC1
	.db 0
vTAudC2
	.db 0
vTAudC3
	.db 0
vTAudC4
	.db 0

vAudCtl
	.db 0 ; 0 = normal base clock (div28/64kHz), 1 = low base clock (div114/15kHz)

vTAudCtl  ;temporary AudCtl register for synchronization purposes
	.db 0

voiceControlToAudC
	.db $C8, $A8, $A0

voiceControlX
	.db VoiceOff
	.db VoiceOff
	.db VoiceWaveC ; initially audible
	.db VoiceOff

scale
	.db 0 ; 0 - diatonic (8 steps), 1 - chromatic (13 steps)
octave
	.db 2
continuousPhaseMod
	.db 0

;mapping: synchronization point number from range 0-14 (normal base clock) -> range 0-5 (low base clock); GUI handles 15 sync points but we need only 5 for low base clock
sp15To5
	.db 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4


waitAtLeast250Cycles
	ldx #49
wt250C1
	dex
	bne wt250C1
	rts

waitAtLeast7168Cycles
	ldy #29
wt7168C1
	jsr waitAtLeast250Cycles
	dey
	bne wt7168C1
	rts
