;
; Update track effects
;
ft_run_effects:

	; Volume slide
	lda var_ch_VolSlide, x
	beq @NoVolSlide
	; First calculate volume decrease
	lda var_ch_VolSlide, x
	and #$0F
	sta var_Temp
	sec
	lda var_ch_VolColumn, x
	sbc var_Temp
	bpl :+
	lda #$00
:	sta var_ch_VolColumn, x
	lda var_ch_VolSlide, x
	lsr a
	lsr a
	lsr a
	lsr a
	sta var_Temp
	clc
	lda var_ch_VolColumn, x
	adc var_Temp
	bpl :+
	lda #$7F
:	sta var_ch_VolColumn, x
@NoVolSlide:

.if 0
    lda var_ch_Effect, x
    bne :+
    ; No effect
    rts
:	asl a
	tay
	lda ft_effect_table - 2, y
	sta var_Temp_Pointer
	lda ft_effect_table - 1, y
	sta var_Temp_Pointer + 1
	jmp (var_Temp_Pointer)
.endif

;.if 0
	; Arpeggio and portamento
	lda var_ch_Effect, x
	beq @NoEffect
	cmp #EFF_ARPEGGIO
	beq @EffArpeggio
	cmp #EFF_PORTAMENTO
	beq @EffPortamento
	cmp #EFF_PORTA_UP
	beq @EffPortaUp
	cmp #EFF_SLIDE_UP
	beq @EffSlideUp
	cmp #EFF_SLIDE_DOWN
	beq @EffSlideDown

	cmp #EFF_SLIDE_UP_LOAD
	beq @EffLoadSlide
	cmp #EFF_SLIDE_DOWN_LOAD
	beq @EffLoadSlide

	jmp ft_portamento_down

@EffArpeggio:
	jmp ft_arpeggio
@EffPortamento:
	jmp ft_portamento
@EffPortaUp:
	jmp ft_portamento_up
@EffSlideUp:
	jmp	ft_slide_up
@EffSlideDown:
	jmp	ft_slide_down
@EffLoadSlide:
	jmp ft_load_slide
@NoEffect:
;.endif
ft_post_effects:
	rts

ft_effect_table:
    .word ft_arpeggio, ft_portamento, ft_portamento_up, ft_portamento_down
    .word ft_load_slide, ft_slide_up, ft_load_slide, ft_slide_down


ft_load_slide:

.ifdef USE_VRC7
	cpx #VRC7_CHANNEL
	bcc :+								; <
	cpx #VRC7_CHANNEL + 6
	bcs :+								; >
	jmp ft_vrc7_load_slide
:	; VRC7 skip
.endif

	lda var_ch_TimerPeriod, x
	pha
	lda var_ch_TimerPeriod + EFF_CHANS, x
	pha
	; Load note
	lda var_ch_EffParam, x			; Store speed
	and #$0F						; Get note
	sta var_Temp					; Store note in temp

	lda var_ch_Effect, x
	cmp #EFF_SLIDE_UP_LOAD
	beq :+
	lda var_ch_Note, x
	sec
	sbc var_Temp
	jmp :++
:	lda var_ch_Note, x
	clc
	adc var_Temp
:	sta var_ch_Note, x
	jsr	ft_translate_freq_only
	lda var_ch_TimerPeriod, x
	sta var_ch_PortaTo, x
	lda var_ch_TimerPeriod + EFF_CHANS, x
	sta var_ch_PortaTo + EFF_CHANS, x
	; Store speed
	lda var_ch_EffParam, x
	lsr a
	lsr a
	lsr a
	ora #$01
	sta var_ch_EffParam, x
	; Load old period
	pla
	sta var_ch_TimerPeriod + EFF_CHANS, x
	pla
	sta var_ch_TimerPeriod, x
	; change mode to sliding
	clc
	lda var_ch_Effect, x
	adc #01
.ifdef USE_FDS
	; FDS's frequency reg is inverted
	cpx #FDS_CHANNEL
	bne :++
	cmp #EFF_SLIDE_UP
	bne :+
	lda #EFF_SLIDE_DOWN
	jmp :++
:	lda #EFF_SLIDE_UP
:
.endif
	sta var_ch_Effect, x
	rts

ft_calc_freq:

 	; Load frequency
	lda var_ch_TimerPeriod, x
	sta var_ch_TimerCalculated, x
	lda var_ch_TimerPeriod + EFF_CHANS, x
	sta var_ch_TimerCalculated + EFF_CHANS, x

.ifdef USE_VRC7
	cpx #VRC7_CHANNEL
	bcc :+
	lsr var_ch_TimerCalculated + EFF_CHANS, x
	ror var_ch_TimerCalculated, x
	lsr var_ch_TimerCalculated + EFF_CHANS, x
	ror var_ch_TimerCalculated, x
:
.endif

	; Apply fine pitch
 	lda var_ch_FinePitch, x
 	cmp #$80
 	beq @Skip
	lda var_ch_TimerCalculated, x
	adc #$80
	sta var_ch_TimerCalculated, x
	lda var_ch_TimerCalculated + EFF_CHANS, x
	adc #$00
	sta var_ch_TimerCalculated + EFF_CHANS, x
	sec
	lda var_ch_TimerCalculated, x
	sbc var_ch_FinePitch, x
	sta var_ch_TimerCalculated, x
	lda var_ch_TimerCalculated + EFF_CHANS, x
	sbc #$00
	sta var_ch_TimerCalculated + EFF_CHANS, x
@Skip:

	jsr ft_vibrato
	jsr ft_tremolo

	rts


;
; Portamento
;
ft_portamento:
	lda var_ch_EffParam, x						; Check portamento, if speed > 0
	beq @NoPortamento
	lda var_ch_PortaTo, x						; and if freq > 0, else stop
	ora var_ch_PortaTo + EFF_CHANS, x
	beq @NoPortamento
	lda var_ch_TimerPeriod + EFF_CHANS, x		; Compare high byte
	cmp var_ch_PortaTo + EFF_CHANS, x
	bcc @Increase
	bne @Decrease
	lda var_ch_TimerPeriod, x						; Compare low byte
	cmp var_ch_PortaTo, x
	bcc @Increase
	bne @Decrease
	;rts											; done
	jmp ft_post_effects
@Decrease:											; Decrease frequency
	sec
	lda var_ch_TimerPeriod, x
	sbc var_ch_EffParam, x
	sta var_ch_TimerPeriod, x
	lda var_ch_TimerPeriod + EFF_CHANS, x
	sbc #$00
	sta var_ch_TimerPeriod + EFF_CHANS, x
	; Check if sign bit has changed, if so load the desired frequency
	lda var_ch_TimerPeriod + EFF_CHANS, x			; Compare high byte
	cmp var_ch_PortaTo + EFF_CHANS, x
	bcc @LoadFrequency
	bmi @LoadFrequency
	bne @NoPortamento
	lda var_ch_TimerPeriod, x						; Compare low byte
	cmp var_ch_PortaTo, x
	bcc @LoadFrequency
;	rts												; Portamento is done at this point
	jmp ft_post_effects

@Increase:											; Increase frequency
	clc
	lda var_ch_TimerPeriod, x
	adc var_ch_EffParam, x
	sta var_ch_TimerPeriod, x
	lda var_ch_TimerPeriod + EFF_CHANS, x
	adc #$00
	sta var_ch_TimerPeriod + EFF_CHANS, x
	; Check if sign bit has changed, if so load the desired frequency
	lda var_ch_PortaTo + EFF_CHANS, x				; Compare high byte
	cmp var_ch_TimerPeriod + EFF_CHANS, x
	bcc @LoadFrequency
	bne @NoPortamento
	lda var_ch_PortaTo, x							; Compare low byte
	cmp var_ch_TimerPeriod, x
	bcc @LoadFrequency
;	rts
	jmp ft_post_effects

@LoadFrequency:										; Load the correct frequency
	lda var_ch_PortaTo, x
	sta var_ch_TimerPeriod, x
	lda var_ch_PortaTo + EFF_CHANS, x
	sta var_ch_TimerPeriod + EFF_CHANS, x
@NoPortamento:
	jmp ft_post_effects

ft_portamento_up:
	sec
	lda var_ch_TimerPeriod, x
	sbc var_ch_EffParam, x
	sta var_ch_TimerPeriod, x
	lda var_ch_TimerPeriod + EFF_CHANS, x
	sbc #$00
	sta var_ch_TimerPeriod + EFF_CHANS, x
	jsr ft_limit_freq
	jmp ft_post_effects
ft_portamento_down:
	clc
	lda var_ch_TimerPeriod, x
	adc var_ch_EffParam, x
	sta var_ch_TimerPeriod, x
	lda var_ch_TimerPeriod + EFF_CHANS, x
	adc #$00
	sta var_ch_TimerPeriod + EFF_CHANS, x	
	jsr ft_limit_freq
	jmp ft_post_effects
	
;
; Note slide
;
ft_slide_up:
	sec
	lda var_ch_TimerPeriod, x
	sbc var_ch_EffParam, x
	sta var_ch_TimerPeriod, x
	lda var_ch_TimerPeriod + EFF_CHANS, x
	sbc #$00
	sta var_ch_TimerPeriod + EFF_CHANS, x
	cmp var_ch_PortaTo + EFF_CHANS, x			; Compare high byte
	bcc ft_slide_done
	bne ft_slide_not_done
	lda var_ch_TimerPeriod, x
	cmp var_ch_PortaTo, x						; Compare low byte
	bcc ft_slide_done
	jmp ft_post_effects
ft_slide_down:
	clc
	lda var_ch_TimerPeriod, x
	adc var_ch_EffParam, x
	sta var_ch_TimerPeriod, x
	lda var_ch_TimerPeriod + EFF_CHANS, x
	adc #$00
	sta var_ch_TimerPeriod + EFF_CHANS, x
	cmp var_ch_PortaTo + EFF_CHANS, x			; Compare high byte
	bcc ft_slide_not_done
	bne ft_slide_done
	lda var_ch_TimerPeriod, x
	cmp var_ch_PortaTo, x						; Compare low byte
	bcs ft_slide_done
	jmp ft_post_effects
ft_slide_done:
	lda var_ch_PortaTo, x
	sta var_ch_TimerPeriod, x
	lda var_ch_PortaTo + EFF_CHANS, x
	sta var_ch_TimerPeriod + EFF_CHANS, x
	lda #EFF_NONE								; Reset effect
	sta var_ch_Effect, x
ft_slide_not_done:
	jmp ft_post_effects

;
; Arpeggio
;
ft_arpeggio:
	lda var_ch_ArpeggioCycle, x
	cmp #$01
	beq @LoadSecond
	cmp #$02
	beq @LoadThird
	lda var_ch_Note, x							; Load first note
	jsr ft_translate_freq_only
	inc var_ch_ArpeggioCycle, x
	jmp ft_post_effects
@LoadSecond:									; Second note (second nybble)
	lda var_ch_EffParam, x
	lsr a
	lsr a
	lsr a
	lsr a
	clc
	adc var_ch_Note, x
	jsr ft_translate_freq_only
	lda var_ch_EffParam, x						; see if cycle should reset here
	and #$0F
	bne @DoNextStep
	sta var_ch_ArpeggioCycle, x
	jmp ft_post_effects
@DoNextStep:
	inc var_ch_ArpeggioCycle, x
	jmp ft_post_effects
@LoadThird:										; Third note (first nybble)
	lda var_ch_EffParam, x
	and #$0F
	clc
	adc var_ch_Note, x
	jsr ft_translate_freq_only	
	lda #$00
	sta var_ch_ArpeggioCycle, x
	jmp ft_post_effects

; Vibrato calculation
;
ft_vibrato:
	lda var_ch_VibratoSpeed, x
	bne :+
	rts
:	clc
	adc var_ch_VibratoPos, x		; Get next position
	and #$3F
	sta var_ch_VibratoPos, x
	cmp #$10
	bcc @Phase1
	cmp #$20
	bcc @Phase2
	cmp #$30
	bcc @Phase3
; Phase 4
	; - 15 - (Phase - 48) + depth
	sec
	sbc #$30
	sta var_Temp
	sec
	lda #$0F
	sbc var_Temp
	;and #$0F
	ora var_ch_VibratoDepth, x
	tay
	lda ft_vibrato_table, y
	eor #$FF
	sta var_Temp16
	lda #$FF
	sta var_Temp16 + 1
	jmp @Calculate
@Phase1:
	; Phase + depth
	ora var_ch_VibratoDepth, x
	tay
	lda ft_vibrato_table, y
	sta var_Temp16
	lda #$00
	sta var_Temp16 + 1
	jmp @Calculate
@Phase2:
	; 15 - (Phase - 16) + depth
	sec
	sbc #$10
	sta var_Temp
	sec
	lda #$0F
	sbc var_Temp
	ora var_ch_VibratoDepth, x
	tay 
	lda ft_vibrato_table, y
	sta var_Temp16
	lda #$00
	sta var_Temp16 + 1
	jmp @Calculate
@Phase3:
	; - (Phase - 32) + depth
	sec
	sbc #$20
	ora var_ch_VibratoDepth, x
	tay 
	lda ft_vibrato_table, y
	eor #$FF
	sta var_Temp16
	lda #$FF
	sta var_Temp16 + 1

@Calculate:

	; Remove this if you don't need support for old vibrato
	lda var_SongFlags
	and #$02
	beq :+
	lda #$0F
	clc
	adc var_ch_VibratoDepth, x
	tay
	lda ft_vibrato_table, y		; add depth + 1
	clc
	adc #$01
;	clc
	adc var_Temp16
	sta var_Temp16
	lda var_Temp16 + 1
	adc #$00
	sta var_Temp16 + 1
	lsr var_Temp16 + 1			; divide by 2
	ror var_Temp16
:
	sec
	lda var_ch_TimerCalculated, x
	sbc var_Temp16
	sta var_ch_TimerCalculated, x
	lda var_ch_TimerCalculated + EFF_CHANS, x
	sbc var_Temp16 + 1
	sta var_ch_TimerCalculated + EFF_CHANS, x
	rts
	
	
; Tremolo calculation
;
ft_tremolo:
	lda var_ch_TremoloSpeed, x
	bne @DoTremolo
	lda var_ch_Volume, x
	sta var_ch_OutVolume, x
	rts
@DoTremolo:
	clc
	adc var_ch_TremoloPos, x		; Get next position
	and #$3F
	sta var_ch_TremoloPos, x
	lsr a							; Divide by 2
	cmp #$10
	bcc @Phase1
; Phase 2
	; 15 - (Phase - 16) + depth
	sec
	sbc #$10
	sta var_Temp
	sec
	lda #$0F
	sbc var_Temp
	ora var_ch_TremoloDepth, x
	tay 
	lda ft_vibrato_table, y
	lsr a
	sta var_Temp
	jmp @Calculate
@Phase1:
	; Phase + depth
	ora var_ch_TremoloDepth, x
	tay
	lda ft_vibrato_table, y
	lsr a
	sta var_Temp
@Calculate:
	sec
	lda var_ch_Volume, x
	sbc var_Temp
	bmi :+
	sta var_ch_OutVolume, x
	rts
:	lda #$00
	sta var_ch_OutVolume, x
	rts
	
