;
; mode_pattplay.inc: handles pattern play mode
;
; author: Marko Kanala, raato ]a t[ mulletronic.com
;
; LICENSE
; Creative Commons Attribution-NonCommercial-ShareALike 2.5
;
; You are free:
;
; * to copy, distribute, display, and perform the work
; * to make derivative works
;
; Under the following conditions:
;	
; 1. Attribution. You must attribute the work in the manner specified by the 
;    author or licensor.
; 2. Noncommercial. You may not use this work for commercial purposes.
; 3. Share Alike. If you alter, transform, or build upon this work, you may 
;    distribute the resulting work only under a license identical to this one.
;
; * For any reuse or distribution, you must make clear to others the license 
;   terms of this work.
; * Any of these conditions can be waived if you get permission from the 
;   copyright holder.
;
; Your fair use and other rights are in no way affected by the above.
;
; This is a human-readable summary of the Legal Code.
; See full license at http://creativecommons.org/licenses/by-nc-sa/2.5/legalcode
;
; Copyright: Marko Kanala (raato@mulletronic.com).
;

; called when pattern play mode is entered
mode_pattplay_enter:
	SETDISPLAY	s_pp_main		; see lcd.inc
	IFRUNNING	mode_pattplay_enter_running
	movff	currentpattern, nextpattern
	CLEARMODE	MODE_FILL
mode_pattplay_enter_running:
	; if we come from pattern write mode we probably
	;  don't want to handle the laststep events we've gotten
	CLEAREVENT	EVENT_LASTSTEP
	return

; called in continuous loop when in pattern play mode
mode_pattplay:
	; if the last step of the pattern has been
	;  reached read the next pattern from the
	;  flash memory if needed
	IFEVENTCALL	EVENT_LASTSTEP, pp_handleflash

	; refresh lcd display if needed	
	call	lcd_refresh

	; do we have key events ?
	IFNOEVENT	EVENT_KEY, pp_no_keyevents

; shift is pressed, handle shifted keys at pp_shiftdown
;	IFKEYDOWN	DOWNKEY_SHIFTFUNC, pp_shiftdown

	; starts the sequencer
	KEYEVENT	KEY_START, pp_start
	; stops or continues the sequencer
	KEYEVENT	KEY_STOPCONT, pp_stopcont
	; trigger a fill
	KEYEVENT	KEY_ENTER, pp_fill
	; clears pattern chain
	KEYEVENT	KEY_SCALE, pp_clearchain

;	KEYEVENT	KEY_S1, pp_stepkey
;	KEYEVENT	KEY_S2, pp_stepkey
;	KEYEVENT	KEY_S3, pp_stepkey
;	KEYEVENT	KEY_S4, pp_stepkey
;	KEYEVENT	KEY_S5, pp_stepkey
;	KEYEVENT	KEY_S6, pp_stepkey
;	KEYEVENT	KEY_S7, pp_stepkey
;	KEYEVENT	KEY_S8, pp_stepkey
;	KEYEVENT	KEY_S9, pp_stepkey
;	KEYEVENT	KEY_S10, pp_stepkey
;	KEYEVENT	KEY_S11, pp_stepkey
;	KEYEVENT	KEY_S12, pp_stepkey
;	KEYEVENT	KEY_S13, pp_stepkey
;	KEYEVENT	KEY_S14, pp_stepkey
;	KEYEVENT	KEY_S15, pp_stepkey
;	KEYEVENT	KEY_S16, pp_stepkey
	; optimized for space
	; if stopped step keys change pattern
	; if running step keys select the next pattern to be played
	STEPKEYEVENT	pp_stepkey

	KEYEVENT	KEY_BANKA, pp_banka
	KEYEVENT	KEY_BANKB, pp_bankb
	KEYEVENT	KEY_BANKC, pp_bankc
	KEYEVENT	KEY_BANKD, pp_bankd

	KEYEVENT	KEY_PATTPLAYWRITE, pp_pattplaywrite
	KEYEVENT	KEY_TRACKPLAYWRITE, pp_trackplaywrite

pp_no_keyevents:
; no specific keyevents, check for downkeys
	IFKEYDOWN	DOWNKEY_LASTSTEP, pp_setchaindown

pp_no_downkeys:
; no keyevents and no downkeys, all keys up
;  set default display
	SETDISPLAY	s_pp_main
	call	pp_refreshleds
	call	lcd_refreshbpm
	return

; all keyhandlers should call this afterwards
pp_keydone:
	call	pp_refreshleds
pp_keydone_keepleds:
	CLEAREVENT	EVENT_KEY
	return

; downkeyhandlers should call this afterwards
pp_downkeydone:
	call	pp_refreshleds
pp_downkeydone_keepleds:
	return

; ----------------------

; lights up the led of the selected pattern
;  and xors the current step to the step leds if
;  we're in the running mode
; additionally takes care of the bank selector leds
pp_refreshleds:
	clrf	leds_temp_1
	clrf	leds_temp_2
	movlw	-8
	addwf	currentpattern, W	; currentpattern = 0 - 15
	bn		pp_lower_ledbus
	; over 8, upper ledbus, W ok (0 - 7)
	call	get_bit
	iorwf	leds_temp_2
	bra 	pp_pat_ok
pp_lower_ledbus:
	addlw	8
	call	get_bit
	iorwf	leds_temp_1
pp_pat_ok:
	IFRUNNING	pp_refreshleds_running
pp_writeleds:
	; write leds to bus
	movff	leds_temp_1, leds_busa_1
	movff	leds_temp_2, leds_busa_2
	; write bank leds (leds 7 - 4 in leds_busb_1)
	movlw	15						; %00001111
	andwf	leds_busb_1, W
	movwf	leds_temp_1	
	movf	currentbank, W
	call	get_bit					; W = bit mask to busb_1
	iorwf	leds_temp_1
	IFNOTRUNNING	pp_bankled_done
	; blink next bank only if running
	movf	nextbank, W
	cpfseq	currentbank	
	bra 	pp_bank_blinknext
pp_bankled_done:
	movff	leds_temp_1, leds_busb_1
	return
pp_bank_blinknext:
	tstfsz	blink
	bra 	pp_bankled_done
	; blink next bank led if applicable (W=nextbank)
	call	get_bit					; W = bit mask to busb_1
	iorwf	leds_temp_1
	bra 	pp_bankled_done

pp_refreshleds_running:
	; we are running, xor currentstep
	movf	currentstep, W			; if currentstep is 0
	bz		pp_writeleds			;  skip the xor					
	movlw	-9
	addwf	currentstep, W			; W = currentstep - 9
	bn		pp_r_lower_ledbus
	; over 8, upper ledbus, W ok (0 - 7)
	call	get_bit
	xorwf	leds_temp_2				; leds XOR currentstep
	bra 	pp_r_blinks
pp_r_lower_ledbus:
	addlw	8						; W = currentstep - 1
	call	get_bit
	xorwf	leds_temp_1
pp_r_blinks:
	movf	nextpattern, W
	cpfseq	currentpattern
	bra 	pp_pattern_blinknext
	bra 	pp_writeleds
pp_pattern_blinknext:
	tstfsz	blink
	bra 	pp_writeleds
	; blink next pattern led if applicable (W=nextpattern)
	movlw	-8
	addwf	nextpattern, W	; nextpattern = 0 - 15
	bn		pp_np_lower_ledbus
	; over 8, upper ledbus, W ok (0 - 7)
	call	get_bit
	iorwf	leds_temp_2
	bra 	pp_writeleds
pp_np_lower_ledbus:
	addlw	8
	call	get_bit
	iorwf	leds_temp_1
	bra 	pp_writeleds

pp_stepkey:
	; laststep pressed, set pattern chain
	IFKEYDOWN	DOWNKEY_LASTSTEP, pp_setchain
; changes the active pattern in the current bank
	; clear chain mode
	CLEARMODE MODE_CHAIN
	movlw	16
	movwf	temp
	decf	keypress, W
	bn		pp_keydone	; accept only keys 0 ...
	cpfsgt	temp
	bra 	pp_keydone	; ... to 15	
	FORCEDISPLAYREFRESH
	IFRUNNING	pp_stepkey_running
	movwf	currentpattern
	movwf	nextpattern
	call	i2c_flash_read
	bra 	pp_keydone
pp_stepkey_running:
	movwf	nextpattern
	bra 	pp_keydone

; changes the active pattern bank
pp_banka:
	movlw	BANK_A
	bra 	pp_set_bank	
pp_bankb:
	movlw	BANK_B
	bra 	pp_set_bank	
pp_bankc:
	movlw	BANK_C
	bra 	pp_set_bank	
pp_bankd:
	movlw	BANK_D
pp_set_bank:
	; if set chain down skip bank change
	IFKEYDOWN	DOWNKEY_LASTSTEP, pp_keydone
	FORCEDISPLAYREFRESH
	IFRUNNING	pp_set_bank_running
	; clear chain mode
	CLEARMODE MODE_CHAIN
	movwf	currentbank
	movwf	nextbank
	call	i2c_flash_read
	bra 	pp_keydone
pp_set_bank_running:
	movwf	nextbank
	bra 	pp_keydone

; starts the sequencer
pp_start:
	; make sure the sequencer is not running
	;  while messing with chainbuffer
	CLEARMODE MODE_RUN
	movf	chainbuffer, W
	bn		pp_start_nochain
	; something in chain buffer, let's play the chain
	movlw	b'00001111'
	movwf	temp
	andwf	chainbuffer, W
	movwf	currentpattern		; low nybble == pattern
	swapf	chainbuffer, W
	andwf	temp, W
	movwf	nextbank			; high nybble == bank
	; set next pattern too
	movf	chainbuffer+1, W
	bnn		pp_start_chainnext
	; chain with length of 1 ?? not a real chain
	bra		pp_start_nochain
pp_start_chainnext:
	; read the first pattern from flash
	movff	currentpattern, nextpattern
	movff	currentbank, nextbank
	call	i2c_flash_read		; i2c_flash_read uses next-vars
	movlw	b'00001111'
	andwf	chainbuffer+1, W
	movwf	nextpattern
	; set chain position to a correct value
	movlw	2
	movwf	chainposition
	SETMODE MODE_CHAIN
	FORCEDISPLAYREFRESH
	bra		pp_start_ok
pp_start_nochain:
	CLEARMODE	MODE_CHAIN
	movff	currentbank, nextbank
	movff	currentpattern, nextpattern
pp_start_ok:
	; set the running mode on
	call	player_start
	bra 	pp_keydone

; stops or continues the sequencer
pp_stopcont:
	FORCEDISPLAYREFRESH
	IFRUNNING	pp_stopcont_running
	; sequencer is not running, we should continue
	call	player_continue
	bra 	pp_keydone
pp_stopcont_running:
	; sequencer is running, stop the player
	call	player_stop
	bra 	pp_keydone

; changes the fill pattern as a active pattern
pp_fill:
	SETMODE	MODE_FILL
	FORCEDISPLAYREFRESH
	bra 	pp_keydone

; switches between pattern play and -write modes
pp_pattplaywrite:
	SETMODE		MODE_PWRITE
	CLEARMODE	MODE_PPLAY
	call	mode_pattwrite_enter
	bra 	pp_keydone_keepleds

; switches to track play mode
pp_trackplaywrite:
	SETMODE		MODE_TPLAY
	CLEARMODE	MODE_PPLAY
	call	mode_trackplay_enter
	bra 	pp_keydone_keepleds

; sets a pattern chain: when the last step key
;  is pressed down the step keys can be used to
;  program a chain of patterns played in sequence
;
; note: the chain play mode is activated when
;       the key is lifted
pp_setchaindown:
	SETDISPLAY	s_pp_setchain
	; show the chain with step leds
	lfsr	FSR1, chainbuffer
	movf	chainshowpos, W
	addwf	FSR1L

	movf	chainshowblink, W
	cpfseq	blink
	bra		pp_setchaindown_noadv
	; advance only when blink changes
	incf	chainshowpos
	btg		chainshowblink, 0	
pp_setchaindown_noadv:
	movf	INDF1, W			; show this pos
	bn		pp_setchaindown_neg
	; not negative, blink this led
	andlw	0x0f				; clear bank bits
	btfsc	WREG, 3
	bra		pp_setchaindown_upper
	; < 8
	call	get_bit
	movwf	leds_busa_1
	clrf	leds_busa_2
	bra 	pp_downkeydone_keepleds
pp_setchaindown_upper:
	andlw	0x07
	call	get_bit
	clrf	leds_busa_1
	movwf	leds_busa_2
	bra 	pp_downkeydone_keepleds
pp_setchaindown_neg:
	; negative, reset to chainbuffer start
	clrf	chainshowpos
	clrf	leds_busa_1			; and clear leds
	clrf	leds_busa_2
	bra 	pp_downkeydone_keepleds

pp_setchain:
	SETMODE	MODE_CHAIN
	movlw	16
	mulwf	currentbank
	decf	PRODL, W			; this substracts one
								;  from keypress
	addwf	keypress, W			; W = bank*4 + step key press
	; W must be 0 - 63
	movwf	temp
	movlw	64
	cpfslt	temp
	bra 	pp_keydone			; 64 or more, ignore key press
	; append temp to chainbuffer if possible
	movlw	16
	movwf	temp2
	lfsr	FSR1, chainbuffer
pp_setchain_find:
	movf	POSTINC1, W
	bn		pp_setchain_endfound
	decfsz	temp2
	bra		pp_setchain_find
	; chain buffer is full (16 positions used)
	; => do nothing
pp_setchain_end:
;	FORCEDISPLAYREFRESH
	bra 	pp_keydone_keepleds
pp_setchain_endfound:
	decf	FSR1L
	; FSR1 now the first empty position of chain buffer
	movff	temp, INDF1
	; append done
	bra		pp_setchain_end

; scale key can be used to clear the programmed
;  chain
pp_clearchain:
	; chain is cleared by setting the buffer positions
	;  to -1

	; if we've already exited chain mode don't mess with
	;  nextpattern
	IFNOTINMODE MODE_CHAIN, pp_clearchain_patres
	; set current pattern to be the next if chaining
	movff	currentpattern, nextpattern
	movff	currentbank, nextbank
	CLEARMODE	MODE_CHAIN		; clear chain mode
pp_clearchain_patres:
	clrf	chainposition		; chainposition = 0
	movlw	16
	movwf	temp
	lfsr	FSR1, chainbuffer
	movlw	-1
pp_clearchainloop:
	movwf	POSTINC1			; chainbuffer = -1
	decfsz	temp
	bra		pp_clearchainloop
	SETDISPLAY	s_pp_chaincleared
	FORCEDISPLAYREFRESH
	bra 	pp_keydone

; called always when last step of the pattern has been
;  triggered
pp_handleflash:
	CLEAREVENT	EVENT_LASTSTEP	; ... or after readflash?
	movf	nextpattern, W
	cpfseq	currentpattern
	bra 	pp_readflash
	movf	nextbank, W
	cpfseq	currentbank
	bra 	pp_readflash
	; flash read not needed
	return
pp_readflash:
	; read the pattern at nextbank/nextpattern
	;  from the flash memory
	call	i2c_flash_read
	return

External Labels :

lcd_refresh
pp_refreshleds
lcd_refreshbpm
get_bit
i2c_flash_read
player_start
player_continue
player_stop
mode_pattwrite_enter
mode_trackplay_enter