;
; mode_trawrite.inc: handles track write mode
;
; author: Marko Kanala, raato ]a t[ mulletronic.com
;
; Note: current bank, pattern and repeat is kept
;       encoded in currentmeasuredata and editedmeasuredata
;       variables.
;
; 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 the track write mode is entered
mode_trackwrite_enter:
	SETDISPLAY	s_tw_main		; see lcd.inc
	return

; called in continuous loop when in track write mode
mode_trackwrite:
	; if the last step of the pattern has been
	;  reached read the next pattern from the
	;  flash memory if needed
	IFEVENTCALL	EVENT_LASTSTEP, tw_handleflash

	; refresh lcd display if needed	
	call	lcd_refresh

	; do we have key events ?
	IFNOEVENT	EVENT_KEY, tw_no_keyevents

	; note: step/bank keys ignored when running
;	KEYEVENT	KEY_S1, tw_stepkey
;	KEYEVENT	KEY_S2, tw_stepkey
;	KEYEVENT	KEY_S3, tw_stepkey
;	KEYEVENT	KEY_S4, tw_stepkey
;	KEYEVENT	KEY_S5, tw_stepkey
;	KEYEVENT	KEY_S6, tw_stepkey
;	KEYEVENT	KEY_S7, tw_stepkey
;	KEYEVENT	KEY_S8, tw_stepkey
;	KEYEVENT	KEY_S9, tw_stepkey
;	KEYEVENT	KEY_S10, tw_stepkey
;	KEYEVENT	KEY_S11, tw_stepkey
;	KEYEVENT	KEY_S12, tw_stepkey
;	KEYEVENT	KEY_S13, tw_stepkey
;	KEYEVENT	KEY_S14, tw_stepkey
;	KEYEVENT	KEY_S15, tw_stepkey
;	KEYEVENT	KEY_S16, tw_stepkey
	STEPKEYEVENT	tw_stepkey

	KEYEVENT	KEY_BANKA, tw_banka
	KEYEVENT	KEY_BANKB, tw_bankb
	KEYEVENT	KEY_BANKC, tw_bankc
	KEYEVENT	KEY_BANKD, tw_bankd

	KEYEVENT	KEY_START, tw_start
	KEYEVENT	KEY_STOPCONT, tw_stopcont	

	IFRUNNING	tw_running_enter
	; set/insert measure if not running
	KEYEVENT	KEY_ENTER, tw_setinsct
	bra			tw_enter_cont
tw_running_enter:
	; trigger a fill if running
	KEYEVENT	KEY_ENTER, tw_fill
tw_enter_cont:

	; note: ignored when running
	KEYEVENT	KEY_SCALE, tw_nextmeasure
	KEYEVENT	KEY_CLEAR, tw_cleartrack
	KEYEVENT	KEY_PATTPLAYWRITE, tw_pattplaywrite
	KEYEVENT	KEY_TRACKPLAYWRITE, tw_trackplaywrite

tw_no_keyevents:
; no specific keyevents, check for downkeys

	; hoax to generate a key events from downkeys
	IFKEYNOTDOWN DOWNKEY_LASTSTEP, tw_laststeplifted
	IFKEYDOWN	DOWNKEY_LASTSTEP, tw_laststeppressed
tw_laststepchecked:
	IFKEYNOTDOWN DOWNKEY_CLEAR, tw_clearlifted
	IFKEYDOWN	DOWNKEY_CLEAR, tw_clearpressed
tw_clearchecked:

tw_no_downkeys:
; no keyevents and no downkeys, all keys up
;  set default display
	SETDISPLAY	s_tw_main
	call	tw_refreshleds
	return

; all keyhandlers should call this afterwards
tw_keydone:
	call	tw_refreshleds
tw_keydone_keepleds:
	CLEAREVENT	EVENT_KEY
	return

; called when laststep downkey is pressed
tw_laststeppressed:
	; call tw_prevmeasure only, if the laststep downkey
	;  was lifted before
	btfsc	downkeypresses, DOWNKEY_LASTSTEP
	bra		tw_laststepchecked
	bsf		downkeypresses, DOWNKEY_LASTSTEP
	bra		tw_prevmeasure

; called when laststep downkey is not pressed
tw_laststeplifted:
	bcf		downkeypresses, DOWNKEY_LASTSTEP
	bra		tw_laststepchecked

; called when clear downkey is pressed
tw_clearpressed:
	; call tw_deletemeasure only, if the clear downkey
	;  was lifted before
	; OR if FUNC is pressed ask for clear track confirmation
	IFKEYDOWN DOWNKEY_SHIFTFUNC, tw_cleartrackconfirm
	btfsc	downkeypresses, DOWNKEY_CLEAR
	bra		tw_clearchecked
	bsf		downkeypresses, DOWNKEY_CLEAR
	bra		tw_deletemeasure

; called when clear downkey is not pressed
tw_clearlifted:
	bcf		downkeypresses, DOWNKEY_CLEAR
	bra		tw_clearchecked

; lights up the led for the measure pattern
tw_refreshleds:
	movf	editedmeasuredata, W
	cpfseq	currentmeasuredata
	bra		tw_refreshleds_blink	
	; not edited, no blink
	bra		tw_refreshleds_nob
tw_refreshleds_blink:
	tstfsz	blink
	bra		tw_refreshleds_boff
tw_refreshleds_nob:
	DECODE_BANK_FROM_MEASURE		; w = bank
	call	get_bit
	btfsc	leds_busb_1, LED_START	; preserve start led
	bsf		WREG, LED_START
	movwf	leds_busb_1				; write bank leds
	movf	editedmeasuredata, W
	DECODE_PATTERN_FROM_MEASURE		; w = pattern
	movwf	temp
	movlw	8
	cpfslt	temp
	bra		tw_refreshleds_edited_upper
	; lower 8
	movf	temp, W
	call	get_bit
	movwf	leds_temp_1
	clrf	leds_temp_2
	IFRUNNING	tw_refreshleds_running
	bra		tw_refreshleds_write
tw_refreshleds_edited_upper:
	subwf	temp, W
	call	get_bit
	clrf	leds_temp_1
	movwf	leds_temp_2
	IFRUNNING	tw_refreshleds_running
	bra		tw_refreshleds_write
tw_refreshleds_boff:
	clrf	leds_temp_1
	clrf	leds_temp_2
	andlw	b'00001111'
	andwf	leds_busb_1
	IFRUNNING	tw_refreshleds_running
	bra		tw_refreshleds_write
tw_refreshleds_running:
	movf	currentstep, W			; if currentstep is 0
	bz		tw_refreshleds_write	;  skip the xor					
	movlw	-9
	addwf	currentstep, W			; W = currentstep - 9
	bn		tw_r_lower_ledbus
	; over 8, upper ledbus, W ok (0 - 7)
	call	get_bit
	xorwf	leds_temp_2				; leds XOR currentstep
	bra 	tw_refreshleds_write
tw_r_lower_ledbus:
	addlw	8						; W = currentstep - 1
	call	get_bit
	xorwf	leds_temp_1
tw_refreshleds_write:
	movff	leds_temp_1, leds_busa_1
	movff	leds_temp_2, leds_busa_2
	return

; changes the fill pattern as a active pattern
tw_fill:
	SETMODE	MODE_FILL
	FORCEDISPLAYREFRESH
	bra 	tw_keydone

; switches between track play and -write modes
tw_trackplaywrite:
;	IFEVENTCALL	EVENT_DIRTY, pw_write_flash		; write current if needed
	SETMODE		MODE_TPLAY
	CLEARMODE	MODE_TWRITE
	call	mode_trackplay_enter
	bra		tw_keydone_keepleds

; switches between pattern play and -write modes
tw_pattplaywrite:
;	IFEVENTCALL	EVENT_DIRTY, pw_write_flash		; write current if needed
	SETMODE		MODE_PPLAY
	CLEARMODE	MODE_TWRITE
	call	mode_pattplay_enter
	bra		tw_keydone_keepleds

; encodes the current pattern / repeat to 
;  editedmeasuredata
tw_stepkey:
	IFRUNNING	tw_stepkey_skip
	; shift pressed, commands
	IFKEYDOWN	DOWNKEY_SHIFTFUNC, tw_shiftcommand
	; encode data to editedmeasuredata
	movlw	16
	movwf	temp
	decf	keypress, W
	bn		tw_keydone		; accept only keys 0 ...
	cpfsgt	temp			
	bra		tw_keydone		;					     to 15
	rlncf	WREG
	rlncf	WREG					; 00PPPP00
	movwf	temp
	FORCEDISPLAYREFRESH
	movlw	b'00111100'	
	andwf	editedmeasuredata, W	; current 00PPPP00
	cpfseq	temp
	bra		tw_stepkey_differentpatt
	; same pattern pressed, increment repeat
	movlw	b'00000011'
	andwf	editedmeasuredata, W	; current repeat
	incf	WREG					; increment
	movwf	temp
	movlw	3						; 0 - 3
	cpfsgt	temp
	bra		tw_stepkey_setrepeat
	clrf	temp					; rollover if necessary
tw_stepkey_setrepeat:
	movlw	b'11111100'
	andwf	editedmeasuredata, W
	iorwf	temp, W
	movwf	editedmeasuredata
	bra		tw_keydone
tw_stepkey_differentpatt:
	; different pattern, change pattern and clear repeat
	movlw	b'11000000'
	andwf	editedmeasuredata, W
	iorwf	temp, W					; bbPPPP00
	movwf	editedmeasuredata
	; refresh pattern name
	call	i2c_flash_read_trackpattern_name
tw_stepkey_skip:
	bra		tw_keydone

; moves to previous measure on track if possible
tw_prevmeasure:
	IFRUNNING	tw_prevmeasure_skip
	; check if we can go back one step
	tstfsz	nextmeasure				; xx__ == 0
	bra		tw_prevmeasure_dec
	tstfsz	nextmeasure + 1			; __xx == 0
	bra		tw_prevmeasure_dec
	; position == 0, rollover to the last position
	movff	currenttracklen, nextmeasure
	movff	currenttracklen + 1, nextmeasure + 1
tw_prevmeasure_dec:
	DECF16	nextmeasure
	; note: this discards current edit which is fine
	call	i2c_flash_read_measure
	movff	nextmeasure, currentmeasure
	movff	nextmeasure + 1, currentmeasure + 1
	; refresh pattern name
	call	i2c_flash_read_trackpattern_name
	FORCEDISPLAYREFRESH
tw_prevmeasure_skip:
	bra		tw_keydone

; moves to next measure on track if possible
tw_nextmeasure:
	IFRUNNING	tw_nextmeasure_skip
	INCF16	nextmeasure
	FORCEDISPLAYREFRESH
	; if currentmeasure == currenttracklen 
	;  rollover to position 1
	movf	nextmeasure, W
	cpfseq	currenttracklen
	bra		tw_nextmeasure_ok
	movf	nextmeasure + 1, W
	cpfseq	currenttracklen + 1
	bra		tw_nextmeasure_ok
	clrf	nextmeasure
	clrf	nextmeasure + 1
tw_nextmeasure_ok:
	call	i2c_flash_read_measure
	; update currentmeasure
	movff	nextmeasure, currentmeasure
	movff	nextmeasure + 1, currentmeasure + 1
	; refresh pattern name
	call	i2c_flash_read_trackpattern_name
tw_nextmeasure_skip:
	bra		tw_keydone

; deletes a measure
tw_deletemeasure:
	IFRUNNING	tw_deletemeasure_skip
	; allowed only if currenttracklen != 0x0001
	clrf	WREG
	cpfseq	currenttracklen
	bra		tw_deletemeasure_ok
	movlw	1
	cpfseq	currenttracklen + 1
	bra		tw_deletemeasure_ok
	bra		tw_keydone				; len == 1, abort
tw_deletemeasure_ok:	
	; backup current position
	movff	currentmeasure, temp
	movff	currentmeasure + 1, temp2
	
	movff	currentmeasure, nextmeasure
	movff	currentmeasure + 1, nextmeasure + 1
tw_deletemeasure_loop:
	; read at currentmeasure + 1
	INCF16	nextmeasure
	; done when nextmeasure reaches currenttracklen 
	movf	nextmeasure, W
	cpfseq	currenttracklen
	bra		tw_deletemeasure_loopcont
	movf	nextmeasure + 1, W
	cpfseq	currenttracklen + 1
	bra		tw_deletemeasure_loopcont
	bra		tw_deletemeasure_cont
tw_deletemeasure_loopcont:
	call	i2c_flash_read_measure
	; store to currentmeasure
	call	i2c_flash_save_measure
	INCF16	currentmeasure
	bra		tw_deletemeasure_loop
tw_deletemeasure_cont:
	; restore position
	movff	temp, currentmeasure
	movff	temp2, currentmeasure + 1
	DECF16	currenttracklen
	; if currentmeasure == currenttracklen go back one step
	movf	currenttracklen, W
	cpfseq	currentmeasure
	bra		tw_deletemeasure_notlast
	movf	currenttracklen + 1, W
	cpfseq	currentmeasure + 1
	bra		tw_deletemeasure_notlast
	; was last measure, go back one step
	DECF16	currentmeasure
tw_deletemeasure_notlast:
	movff	currentmeasure, nextmeasure
	movff	currentmeasure + 1, nextmeasure + 1
	; save track len
	call	i2c_flash_save_tracklength
	call	i2c_flash_read_measure
	; refresh pattern name
	call	i2c_flash_read_trackpattern_name
	FORCEDISPLAYREFRESH
	SETDISPLAY	s_tw_measuredeleted
tw_deletemeasure_skip:
	bra		tw_keydone

; asks for track clear confirmation
tw_cleartrackconfirm:
	IFRUNNING	tw_cleartrackconfirm_skip
	SETDISPLAY	s_tw_clearconfirm
tw_cleartrackconfirm_skip:
	bra		tw_keydone

;
; Enter key (Set / Ins) pressed
;
; if FUNC is pressed a measure is inserted to the
;  current position
;
 ; if CLEAR and FUNC are pressed clear the track
;  (track clear confirmed)
tw_setinsct:
	IFKEYSDOWN	DOWNKEY_CLEAR, DOWNKEY_SHIFTFUNC, tw_cleartrack
	IFKEYDOWN	DOWNKEY_SHIFTFUNC, tw_insertmeasure
	; move editedmeasuredata to currentmeasuredata
	;  and save to flash
	movff	editedmeasuredata, currentmeasuredata
	; store current measure data to flash
	call	i2c_flash_save_measure
	; if we're in the last measure and current track len <= 354
	;  1. add a new measure to the track (incr track len)
	;  2. copy current measure data to new measure
	;  3. advance to new measure

	; 354 = 0x162
	movlw	0x01
	cpfslt	currenttracklen
	bra		tw_keydone
	movlw	0x63
	cpfslt	currenttracklen + 1
	bra		tw_keydone
	; current measure <= 354
	; hack a bit because currentmeasure is always
	;  < track length
	movff	currentmeasure, temp
	movff	currentmeasure + 1, temp2
	INCF16	temp
	movf	temp, W
	cpfseq	currenttracklen
	bra		tw_keydone
	movf	temp2, W
	cpfseq	currenttracklen + 1
	bra		tw_keydone
	; we're in the last measure, let's do the "add"

	; hoax to set the edit mode :/
	;  this fucks up if we've just added D16x4, 
	;  el�m� on laiffii
	movlw	-1
	movwf	currentmeasuredata
	INCF16	currenttracklen
	INCF16	nextmeasure
	INCF16	currentmeasure
	; save track len
	call	i2c_flash_save_tracklength
	FORCEDISPLAYREFRESH
	bra		tw_keydone		

; inserts a measure at the current position
tw_insertmeasure:
	; if current track len <= 354
	movlw	0x01
	cpfslt	currenttracklen
	bra		tw_keydone
	movlw	0x63
	cpfslt	currenttracklen + 1
	bra		tw_keydone
	; current measure <= 354

	; backup editedmeasuredata as it is thrashed
	;  on insertion 
	movff	editedmeasuredata, tw_temp

	; backup current position
	movff	currentmeasure, temp
	movff	currentmeasure + 1, temp2

	; go to last measure
	movff	currenttracklen, nextmeasure
	movff	currenttracklen + 1, nextmeasure + 1
	movff	currenttracklen, currentmeasure
	movff	currenttracklen + 1, currentmeasure + 1

	; add a new measure and insert the
	;  data to the new measure at the end
	call	i2c_flash_read_measure		; *data now last measure
	INCF16	currenttracklen				; add a new measure
	INCF16	currentmeasure				; advance to new measure
	call	i2c_flash_save_measure		; last measure moved

	; nextmeasure = currentmeasure - 1
tw_insertmeasure_loop:
	DECF16	nextmeasure 
	call	i2c_flash_read_measure
	DECF16	currentmeasure			
	call	i2c_flash_save_measure
	; loop until nextmeasure == measure we started in
	movf	nextmeasure, W
	cpfseq	temp
	bra		tw_insertmeasure_loop
	movf	nextmeasure + 1, W
	cpfseq	temp2
	bra		tw_insertmeasure_loop
	; insertion done, store the data we were inserting
	DECF16	currentmeasure				; nextmeasure == currentmeasure
	movff	tw_temp, editedmeasuredata
	call	i2c_flash_save_measure

	; save track len
	call	i2c_flash_save_tracklength

	SETDISPLAY	s_tw_measureinserted
	bra		tw_keydone

; actually clears the current track
tw_cleartrack:
	IFRUNNING	tw_cleartrack_skip
	; 1. sets the track length to 1
	; 2. sets the measure at position 0 to A01x1
	; 3. moves the currentmeasure to 0
	clrf	currentmeasure
	clrf	currentmeasure + 1
	clrf	nextmeasure
	clrf	nextmeasure + 1
	clrf	editedmeasuredata
	clrf	currenttracklen
	movlw	1
	movwf	currenttracklen + 1
	call	i2c_flash_save_measure
	call	i2c_flash_save_tracklength
	; refresh pattern name
	call	i2c_flash_read_trackpattern_name
	SETDISPLAY	s_tw_trackcleared	
tw_cleartrack_skip:
	bra		tw_keydone

; alter bank to editedmeasuredata
tw_banka:
	movlw	BANK_A<<6
	bra		tw_setbank
tw_bankb:
	movlw	BANK_B<<6
	bra		tw_setbank
tw_bankc:
	movlw	BANK_C<<6
	bra		tw_setbank
tw_bankd:
	movlw	BANK_D<<6
tw_setbank:
	FORCEDISPLAYREFRESH
	movwf	temp
	movlw	b'00111111'
	andwf	editedmeasuredata, W
	iorwf	temp, W
	movwf	editedmeasuredata
	; refresh pattern name
	call	i2c_flash_read_trackpattern_name
	bra		tw_keydone

; starts the sequencer
tw_start:
	; trash edited data on start
	movff	currentmeasuredata, editedmeasuredata
	movf	currentmeasuredata, W
	DECODE_BANK_FROM_MEASURE
	movwf	currentbank
	movf	currentmeasuredata, W
	DECODE_PATTERN_FROM_MEASURE
	movwf	currentpattern
	movff	currentpattern, nextpattern
	movff	currentbank, nextbank
	; load first pattern from flash
	call	i2c_flash_read
	; reset repeat counter
	clrf	repeatcounter
	; and start
	call	player_start
	FORCEDISPLAYREFRESH
	bra		tw_keydone

; stops / continues
tw_stopcont:
	FORCEDISPLAYREFRESH
	IFNOTRUNNING	tw_stopcont_cont
	call	player_stop
	bra		tw_keydone
tw_stopcont_cont:
	call	player_continue
	bra		tw_keydone

;; if stopped go to measure 001
;tw_stopcont_reset:
;	; go to measure 001
;	clrf	currentmeasure
;	clrf	currentmeasure + 1
;	clrf	nextmeasure
;	clrf	nextmeasure + 1
;	call	i2c_flash_read_measure
;	; refresh pattern name
;	call	i2c_flash_read_trackpattern_name
;	bra		tw_keydone

; called always when last step of the pattern has been
;  triggered
; If incremented nextmeasure points to a different pattern than
;  the current the pattern is loaded to the current
tw_handleflash:
	CLEAREVENT	EVENT_LASTSTEP
	; check if repeats are played
	movf	currentmeasuredata, W
	DECODE_REPEAT_FROM_MEASURE	; W = repeat
	cpfseq	repeatcounter
	; repeat counter != current repeat
	;
	; advance only if all repeats are played
	;  otherwise just return
	return
	INCF16	nextmeasure
	; if currentmeasure == currenttracklen 
	;  rollover to position 1
	movf	nextmeasure, W
	cpfseq	currenttracklen
	bra		tw_handleflash_nm_ok
	movf	nextmeasure + 1, W
	cpfseq	currenttracklen + 1
	bra		tw_handleflash_nm_ok
	clrf	nextmeasure
	clrf	nextmeasure + 1
tw_handleflash_nm_ok:
	lfsr	FSR1, nextmeasuredata
	; read measure from "nextmeasure"
	call	i2c_flash_read_measure_fsr1
	movf	INDF1, W			; nextmeasuredata
	cpfseq	currentmeasuredata	; do we need a new pattern ?
	bra		tw_handleflash_loadpat
	return
tw_handleflash_loadpat:
	; pattern is changed in next measure,
	;  decode nextmeasuredata to nextbank/nextpattern
	;  and load
	movf	INDF1, W
	DECODE_BANK_FROM_MEASURE
	movwf	nextbank
	movf	INDF1, W
	DECODE_PATTERN_FROM_MEASURE
	movwf	nextpattern
	call	i2c_flash_read
	return

; -- Commands

; track write FUNCs
;
; s1 == enter name
tw_shiftcommand:
	KEYEVENT	KEY_S1, tw_cmd_entername
	bra		tw_keydone

tw_cmd_entername:
	; text destination
	SETDISPLAY	s_tw_entername
	lfsr	FSR1, track_name
	; read keyboard
	call	keyboard
	call	i2c_flash_save_trackname
	bra 	tw_keydone

External Labels :

lcd_refresh
tw_refreshleds
get_bit
mode_trackplay_enter
mode_pattplay_enter
i2c_flash_read_trackpattern_name
i2c_flash_read_measure
i2c_flash_save_measure
i2c_flash_save_tracklength
i2c_flash_read
player_start
player_stop
player_continue
i2c_flash_read_measure_fsr1
keyboard
i2c_flash_save_trackname