;Allura - [red light stimulated audio synthesiser]

#include <P16F628A.INC>

;SET THE CONFIGURATION WORD
		__CONFIG	_BOREN_OFF & _CP_OFF & _DATA_CP_OFF & _PWRTE_ON & _WDT_OFF & _LVP_OFF & _MCLRE_OFF & _INTOSC_OSC_NOCLKOUT

;RAM VARIABLE DEFINITIONS
MODE		EQU	H'0020'		;MODE FLAGS - OCTAVE BITS
MODE2		EQU	H'0021'		;MODE2 FLAGS - FILTER BITS
DEVSTAT		EQU	H'0022'		;DEVICE STATUS FLAGS
;7-3 - UNUSED, 2 - MODE2 VIBRATO DIRECTION, 1 - TAC_SW BOUNCE GUARD, 0 - TAC_SW ACTIVE
ROWACTV		EQU	H'0023'		;ACTIVE ROW
COLSACTV	EQU	H'0024'		;ACTIVE COLUMNS
PR2_MOD		EQU	H'0025'		;TIMER2 MODIFICATION REGISTER
VIBRATO		EQU	H'0026'		;VIBRATO DEPTH COUNTER
FRAMES		EQU	H'0027'		;REMAINING FRAMES
FRAMES2		EQU	H'0028'		;REMAINING FRAMES2
FRAME_LN	EQU	H'0029'		;FRAME LENGTH
SHIFTS		EQU	H'002A'		;REMAINING ANIMATION SHIFTS
ROW3COLS	EQU	H'0030'		;ROW 3 COLUMN BITS
ROW4COLS	EQU	H'0031'		;ROW 4 COLUMN BITS
ROW5COLS	EQU	H'0032'		;ROW 5 COLUMN BITS
ROW6COLS	EQU	H'0033'		;ROW 6 COLUMN BITS
ROW7COLS	EQU	H'0034'		;ROW 7 COLUMN BITS
ROW8COLS	EQU	H'0035'		;ROW 8 COLUMN BITS

;CONSTANT DEFINITIONS
MODE2_DF	EQU	B'00001000'	;MODE2 DEFAULT
ROWAC1_2	EQU	B'11101111'	;ROW 1 & 2 ACTIVE
ROWAC3		EQU	B'11111011'	;ROW 3 ACTIVE
ROWAC4		EQU	B'10111111'	;ROW 4 ACTIVE
ROWAC5		EQU	B'11110111'	;ROW 5 ACTIVE
ROWAC6		EQU	B'01111111'	;ROW 6 ACTIVE
ROWAC7		EQU	B'11111110'	;ROW 7 ACTIVE
ROWAC8		EQU	B'11111101'	;ROW 8 ACTIVE
ROWAC4_8	EQU	B'00010100'	;ROWS 4-8 ACTIVE
ROWINAC		EQU	B'11111111'	;ALL ROWS INACTIVE

;PORTA PINS
ROW7		EQU	H'0000'		;ROW 7 CONTROL
ROW8		EQU	H'0001'		;ROW 8 CONTROL
ROW3		EQU	H'0002'		;ROW 3 CONTROL
PIEZO		EQU	H'0002'		;PIEZO OUTPUT
ROW5		EQU	H'0003'		;ROW 5 CONTROL
ROW1_2		EQU	H'0004'		;ROW 1 & 2 CONTROL
TAC_SW		EQU	H'0005'		;ACTIVE LOW TACTILE SWITCH
ROW4		EQU	H'0006'		;ROW 4 CONTROL
ROW6		EQU	H'0007'		;ROW 6 CONTROL

;PORTB PINS
COL1		EQU	H'0000'		;COLUMN 1 CONTROL
COL2		EQU	H'0001'		;COLUMN 2 CONTROL
COL3		EQU	H'0002'		;COLUMN 3 CONTROL
COL4		EQU	H'0003'		;COLUMN 4 CONTROL
COL5		EQU	H'0004'		;COLUMN 5 CONTROL
COL6		EQU	H'0005'		;COLUMN 6 CONTROL
COL7		EQU	H'0006'		;COLUMN 7 CONTROL
COL8		EQU	H'0007'		;COLUMN 8 CONTROL

;RESET VECTOR
		ORG	H'0000'
		GOTO	RESET

;INTERRUPT VECTOR
		ORG	H'0004'
		GOTO	RESET

;RESET INITIALISATION
RESET		MOVLW	B'00000111'
		MOVWF	CMCON		;DISABLE COMPARATORS
		CLRF	INTCON		;DISABLE INTERRUPTS
		MOVLW	B'00001101'
		MOVWF	T2CON		;ENABLE TIMER2, 1:2 POSTSCALER, 1:4 PRESCALER
		BSF	STATUS,RP0	;CHANGE TO REGISTER BANK 1
		MOVLW	B'11000000'
		MOVWF	OPTION_REG	;SET OPTION REGISTER
		MOVLW	B'11111111'
		MOVWF	TRISA		;PORTA ALL INPUT
		CLRF	TRISB		;PORTB ALL OUTPUT
		BCF	STATUS,RP0	;CHANGE TO REGISTER BANK 0
		CLRF	PORTA		;SET DEFAULT REGISTER STATUS
		CLRF	PORTB
		CLRF	MODE
		MOVLW	MODE2_DF
		MOVWF	MODE2
		CLRF	DEVSTAT
		CLRF	ROWACTV
		CLRF	PR2_MOD
		CLRF	VIBRATO
		CALL	TMR0INIT
		CALL	TMR2INIT
		CALL	STARTUP
		GOTO	MAIN		;START MAIN LOOP

;TIMER0 INITIALISATION
TMR0INIT	CLRF	TMR0		;RESET TIMER0
		BCF	INTCON,T0IF	;RESET TIMER0 INTERRUPT FLAG
		RETURN

;TIMER2 INITIALISATION
TMR2INIT	CLRF	TMR2		;RESET TIMER2
		BCF	PIR1,TMR2IF	;RESET TIMER2 INTERRUPT FLAG
		RETURN

;LOAD FRAME 1 BITMAP
FRAME1		MOVLW	B'10100111'
		MOVWF	ROW3COLS
		MOVLW	B'10111010'
		MOVWF	ROW4COLS
		MOVLW	B'01001010'
		MOVWF	ROW5COLS
		MOVLW	B'10111010'
		MOVWF	ROW6COLS
		MOVLW	B'10101010'
		MOVWF	ROW7COLS
		MOVLW	B'00001000'
		MOVWF	ROW8COLS
		RETURN

;LOAD FRAME 2 BITMAP
FRAME2		MOVLW	B'01000010'
		MOVWF	ROW3COLS
		MOVLW	B'01100110'
		MOVWF	ROW4COLS
		MOVLW	B'01111110'
		MOVWF	ROW5COLS
		MOVLW	B'01011010'
		MOVWF	ROW6COLS
		MOVLW	B'00111100'
		MOVWF	ROW7COLS
		MOVLW	B'00011000'
		MOVWF	ROW8COLS
		RETURN

;LOAD FRAME 3 BITMAP
FRAME3		MOVLW	B'01000010'
		MOVWF	ROW3COLS
		MOVLW	B'01100110'
		MOVWF	ROW4COLS
		MOVLW	B'01111110'
		MOVWF	ROW5COLS
		MOVLW	B'01111010'
		MOVWF	ROW6COLS
		MOVLW	B'00111100'
		MOVWF	ROW7COLS
		MOVLW	B'00011000'
		MOVWF	ROW8COLS
		RETURN

;DISPLAY NEW ANIMATION FRAME
NEWFRAME	MOVF	FRAME_LN,W	;LOAD FRAME LENGTH
		MOVWF	FRAMES		;STORE IN FRAMES
FRMLOOP		MOVF	FRAME_LN,W	;LOAD FRAME LENGTH
		MOVWF	FRAMES2		;STORE IN FRAMES2
FRMLOOP2	MOVF	ROW3COLS,W	;LOAD ROW 3 COLUMN BITS
		MOVWF	COLSACTV	;SET ACTIVE COLUMNS
		MOVLW	ROWAC3		;LOAD ACTIVE ROW 3 BYTE
		CALL	ROWDISP
		MOVF	ROW4COLS,W	;LOAD ROW 4 COLUMN BITS...
		MOVWF	COLSACTV
		MOVLW	ROWAC4
		CALL	ROWDISP
		MOVF	ROW5COLS,W
		MOVWF	COLSACTV
		MOVLW	ROWAC5
		CALL	ROWDISP
		MOVF	ROW6COLS,W
		MOVWF	COLSACTV
		MOVLW	ROWAC6
		CALL	ROWDISP
		MOVF	ROW7COLS,W
		MOVWF	COLSACTV
		MOVLW	ROWAC7
		CALL	ROWDISP
		MOVF	ROW8COLS,W	;...LOAD ROW 4 COLUMN BITS
		MOVWF	COLSACTV	;SET ACTIVE COLUMNS
		MOVLW	ROWAC8		;LOAD ACTIVE ROW 8 BYTE
		CALL	ROWDISP
		DECFSZ	FRAMES2,F	;DECREMENT FRAMES2, TEST IF ZERO
		GOTO	FRMLOOP2	;IF FALSE REPEAT FRMLOOP2
		DECFSZ	FRAMES,F	;DECREMENT FRAMES, TEST IF ZERO
		GOTO	FRMLOOP		;IF FALSE REPEAT FRMLOOP
		RETURN

;DROP ANIMATION FRAME OFF DISPLAY
DRPFRAME	MOVLW	D'6'
		MOVWF	SHIFTS		;SET NUMBER OF SHIFTS
DRPLOOP		MOVF	ROW7COLS,W	;LOAD ROW 7 COLUMN BITS
		MOVWF	ROW8COLS	;STORE IN ROW 8 COLUMN BITS
		MOVF	ROW6COLS,W	;LOAD ROW 6 COLUMN BITS...
		MOVWF	ROW7COLS
		MOVF	ROW5COLS,W
		MOVWF	ROW6COLS
		MOVF	ROW4COLS,W
		MOVWF	ROW5COLS
		MOVF	ROW3COLS,W	;...LOAD ROW 3 COLUMN BITS
		MOVWF	ROW4COLS	;STORE IN ROW 4 COLUMN BITS
		CLRF	ROW3COLS	;CLEAR ROW 3 COLUMN BITS
		CALL	NEWFRAME
		DECFSZ	SHIFTS,F	;DECREMENT SHIFTS, TEST IF ZERO
		GOTO	DRPLOOP		;IF FALSE REPEAT DRPLOOP
		RETURN

;STARTUP DISPLAY ROUTINE
STARTUP		MOVLW	D'64'
		MOVWF	FRAME_LN	;SET FRAME LENGTH
		CALL	FRAME1
		CALL	NEWFRAME
		MOVLW	D'16'
		MOVWF	FRAME_LN	;SET FRAME LENGTH
		CALL	DRPFRAME
		MOVLW	D'64'
		MOVWF	FRAME_LN	;SET FRAME LENGTH
		CALL	FRAME2
		CALL	NEWFRAME
		MOVLW	D'32'
		MOVWF	FRAME_LN	;SET FRAME LENGTH
		CALL	FRAME3
		CALL	NEWFRAME
		MOVLW	D'64'
		MOVWF	FRAME_LN	;SET FRAME LENGTH
		CALL	FRAME2
		CALL	NEWFRAME
		MOVLW	D'16'
		MOVWF	FRAME_LN	;SET FRAME LENGTH
		CALL	DRPFRAME
		RETURN

;READY ACTIVE ROW FOR DISPLAY OR SENSING
ROWSET		CLRF	PORTB
		MOVF	ROWACTV,W	;LOAD ACTIVE ROW
		BSF	STATUS,RP0	;CHANGE TO REGISTER BANK 1
		MOVWF	TRISA		;LOAD ACTIVE ROW INTO TRISA
		BCF	STATUS,RP0	;CHANGE TO REGISTER BANK 0
		CLRF	PORTA
		RETURN

;CHECK IF MODE NEEDS UPDATING
MODECHK		BTFSS	PORTA,TAC_SW	;TEST IF TAC_SW ACTIVE
		GOTO	TAC_ACTV	;IF TRUE GOTO TAC_ACTV
		BTFSS	DEVSTAT,1	;TEST IF TAC_SW BOUNCE GUARD FLAG SET
		GOTO	$+3		;IF FALSE SKIP AHEAD
		BCF	DEVSTAT,1	;IF TRUE CLEAR TAC_SW BOUNCE GUARD FLAG
		RETURN
		BCF	DEVSTAT,0	;CLEAR TAC_SW ACTIVE FLAG
		RETURN
TAC_ACTV	BTFSC	DEVSTAT,0	;TEST IF TAC_SW ACTIVE FLAG SET
		RETURN			;IF TRUE RETURN
		BSF	DEVSTAT,0	;IF FALSE SET TAC_SW ACTIVE FLAG
		BSF	DEVSTAT,1	;SET TAC_SW BOUNCE GUARD FLAG
		RLF	MODE,F		;ROTATE MODE BITS LEFT
		BSF	MODE,0		;SET MODE BIT0
		BTFSS	MODE,3		;TEST IF MODE HAS REACHED BIT3
		RETURN			;IF FALSE RETURN
		CLRF	MODE		;IF TRUE RESET MODE
		BTFSS	MODE2,7		;TEST IF MODE2 HAS REACHED BIT7
		GOTO	$+4		;IF FALSE SKIP AHEAD
		MOVLW	MODE2_DF
		MOVWF	MODE2		;IF TRUE RESET MODE2 TO DEFAULT
		GOTO	$+2
		RLF	MODE2,F		;ROTATE MODE2 BITS LEFT
		;MOVLW	B'11111000'
		;ANDWF	MODE2,F
		RETURN

;DISPLAY MODE STATUS
MODEDISP	MOVLW	ROWAC1_2	;LOAD ACTIVE ROW 1 & 2 BYTE
		MOVWF	ROWACTV		;SET ACTIVE ROWS
		CALL	ROWSET
		MOVF	MODE,W		;LOAD MODE BITS
		IORWF	MODE2,W		;INCLUSIVE OR WITH MODE2 BITS
		MOVWF	PORTB		;DISPLAY TOTAL MODE STATUS
		RETURN

;SCAN THE PAD FOR INPUT
PADSCAN		MOVLW	ROWAC4_8	;LOAD ACTIVE ROWS 4-8 BYTE
		MOVWF	ROWACTV		;SET ACTIVE ROWS
		CALL	ROWSET
		COMF	PORTA,F		;SET ALL PORTA OUTPUTS HIGH
		BSF	STATUS,RP0	;CHANGE TO REGISTER BANK 1
		MOVLW	B'11111111'
		MOVWF	TRISB		;PORTB ALL INPUT
		BCF	STATUS,RP0	;CHANGE TO REGISTER BANK 0
		CALL	TMR0INIT
		MOVLW	ROWINAC		;LOAD ALL ROWS INACTIVE BYTE
		MOVWF	ROWACTV		;SET INACTIVE ROWS
		CALL	ROWSET
		CLRW
SCANLOOP	BTFSC	INTCON,T0IF	;TEST IF TIMER0 HAS OVERFLOWED
		GOTO	SCAN_END	;IF TRUE END SCANNING LOOP
		BTFSS	PORTB,COL1	;TEST IF COLUMN 1 HAS TRIGGERED
		GOTO	$+3		;IF FALSE GOTO NEXT COLUMN TEST
		MOVLW	B'00000001'	;IF TRUE LOAD COLUMN 1 ACTIVE BIT
		GOTO	SCAN_END	;END SCANNING LOOP
		BTFSS	PORTB,COL2	;TEST IF COLUMN 2 HAS TRIGGERED...
		GOTO	$+3
		MOVLW	B'00000010'
		GOTO	SCAN_END
		BTFSS	PORTB,COL3
		GOTO	$+3
		MOVLW	B'00000100'
		GOTO	SCAN_END
		BTFSS	PORTB,COL4
		GOTO	$+3
		MOVLW	B'00001000'
		GOTO	SCAN_END
		BTFSS	PORTB,COL5
		GOTO	$+3
		MOVLW	B'00010000'
		GOTO	SCAN_END
		BTFSS	PORTB,COL6
		GOTO	$+3
		MOVLW	B'00100000'
		GOTO	SCAN_END
		BTFSS	PORTB,COL7
		GOTO	$+3
		MOVLW	B'01000000'
		GOTO	SCAN_END
		BTFSS	PORTB,COL8	;...TEST IF COLUMN 8 HAS TRIGGERED
		GOTO	SCANLOOP	;IF FALSE REPEAT SCANLOOP
		MOVLW	B'10000000'	;IF TRUE LOAD COLUMN 8 ACTIVE BIT
SCAN_END	MOVWF	COLSACTV	;STORE W IN COLSACTV
		BSF	STATUS,RP0	;CHANGE TO REGISTER BANK 1
		CLRF	TRISB		;PORTB ALL OUTPUT
		BCF	STATUS,RP0	;CHANGE TO REGISTER BANK 0
		CLRF	PORTB
		BTFSS	INTCON,T0IF	;TEST IF TIMER0 HAS OVERFLOWED
		GOTO	$-1		;IF FALSE TEST AGAIN
		RETURN

;DISPLAY SINGLE PAD ROW
ROWDISP		MOVWF	ROWACTV		;SET ACTIVE ROW
		CALL	ROWSET
		MOVF	COLSACTV,W
		MOVWF	PORTB		;DISPLAY ACTIVE COLUMNS
		RETURN

;DISPLAY PAD FEEDBACK
PADDISP		MOVLW	ROWAC4		;LOAD ACTIVE ROW 4 BYTE
		CALL	ROWDISP
		MOVLW	ROWAC5		;LOAD ACTIVE ROW 5 BYTE...
		CALL	ROWDISP
		MOVLW	ROWAC6
		CALL	ROWDISP
		MOVLW	ROWAC7
		CALL	ROWDISP
		MOVLW	ROWAC8		;...LOAD ACTIVE ROW 8 BYTE
		CALL	ROWDISP
		RETURN

;AUDIO SYNTHESISER
SYNTH		MOVF	COLSACTV,F	;USE COLSACTV TO SET STATUS REGISTER
		BTFSC	STATUS,Z	;TEST IF COLSACTV IS ZERO
		RETURN			;IF TRUE RETURN
		MOVLW	ROWAC3		;IF FALSE LOAD ACTIVE ROW 3 BYTE
		MOVWF	ROWACTV		;SET ACTIVE ROW
		CALL	ROWSET
		CLRW
		BTFSC	COLSACTV,COL8	;TEST IF COLSACTV IS COL8
		MOVLW	D'120'		;IF TRUE LOAD UPPER-C NOTE
		BTFSC	COLSACTV,COL7	;TEST IF COLSACTV IS COL7...
		MOVLW	D'127'		;B
		BTFSC	COLSACTV,COL6
		MOVLW	D'142'		;A
		BTFSC	COLSACTV,COL5
		MOVLW	D'159'		;G
		BTFSC	COLSACTV,COL4
		MOVLW	D'179'		;F
		BTFSC	COLSACTV,COL3
		MOVLW	D'190'		;E
		BTFSC	COLSACTV,COL2
		MOVLW	D'213'		;D
		BTFSC	COLSACTV,COL1	;...TEST IF COLSACTV IS COL1
		MOVLW	D'239'		;IF TRUE LOAD LOWER-C NOTE
		BTFSC	MODE2,3		;TEST IF MODE2 STANDARD NOTE BIT IS SET
		MOVWF	PR2_MOD		;IF TRUE STORE W IN PR2_MOD
		BTFSC	MODE2,4		;TEST IF MODE2 FALLING NOTE BIT IS SET	
		INCF	PR2_MOD,F	;IF TRUE INCREMENT PR2_MOD
		BTFSC	MODE2,5		;TEST IF MODE2 RISING NOTE BIT IS SET
		DECF	PR2_MOD,F	;IF TRUE DECREMENT PR2_MOD
		BTFSS	MODE2,6		;TEST IF MODE2 FAST VIBRATO BIT IS SET
		GOTO	MODE_7		;IF FALSE GOTO MODE_7
		BTFSC	DEVSTAT,2	;IF TRUE TEST VIBRATO DIRECTION
		GOTO	VIB_UP		;IF UP GOTO VIB_UP
		INCF	PR2_MOD,F	;IF DOWN INCREMENT PR2_MOD
		INCF	VIBRATO,F	;INCREMENT VIBRATO
		BTFSC	VIBRATO,4	;TEST IF VIBRATO HAS REACHED BIT4
		BSF	DEVSTAT,2	;IF TRUE SET VIBRATO DIRECTION TO DOWN
		GOTO	MODE_END	;END MODE PROCESSING
VIB_UP		DECF	PR2_MOD,F	;DECREMENT PR2_MOD
		DECFSZ	VIBRATO,F	;DECREMENT VIBRATO, TEST IF ZERO
		GOTO	MODE_END	;IF FALSE END MODE PROCESSING
		MOVWF	PR2_MOD		;IF TRUE STORE W IN PR2_MOD
		BCF	DEVSTAT,2	;SET VIBRATO DIRECTION TO UP
		GOTO	MODE_END	;END MODE PROCESSING
MODE_7		BTFSS	MODE2,7		;TEST IF MODE2 SLOW VIBRATO BIT IS SET
		GOTO	MODE_END	;IF FALSE END MODE PROCESSING
		BTFSC	DEVSTAT,2	;IF TRUE TEST VIBRATO DIRECTION
		GOTO	VIB_DOWN	;IF DOWN GOTO VIB_DOWN
		DECF	PR2_MOD,F	;IF UP DECREMENT PR2_MOD
		INCF	VIBRATO,F	;INCREMENT VIBRATO
		BTFSC	VIBRATO,5	;TEST IF VIBRATO HAS REACHED BIT5
		BSF	DEVSTAT,2	;IF TRUE SET VIBRATO DIRECTION TO UP
		GOTO	MODE_END	;END MODE PROCESSING
VIB_DOWN	INCF	PR2_MOD,F	;INCREMENT PR2_MOD
		DECFSZ	VIBRATO,F	;DECREMENT VIBRATO, TEST IF ZERO
		GOTO	MODE_END	;IF FALSE END MODE PROCESSING
		MOVWF	PR2_MOD		;IF TRUE STORE W IN PR2_MOD
		BCF	DEVSTAT,2	;SET VIBRATO DIRECTION TO DOWN
		GOTO	MODE_END	;END MODE PROCESSING
MODE_END	MOVF	PR2_MOD,F	;USE PR2_MOD TO SET STATUS REGISTER
		BTFSC	STATUS,Z	;TEST IF PR2_MOD IS ZERO
		MOVWF	PR2_MOD		;IF TRUE STORE NOTE IN PR2_MOD
		MOVF	PR2_MOD,W	;LOAD PR2_MOD
		BSF	STATUS,RP0	;CHANGE TO REGISTER BANK 1
		MOVWF	PR2		;STORE NOTE IN PR2
		BCF	STATUS,RP0	;CHANGE TO REGISTER BANK 0
		BSF	PORTA,PIEZO	;SET PIEZO OUTPUT HIGH
		BTFSS	PIR1,TMR2IF	;TEST IF SCALED TIMER2 HAS OVERFLOWED
		GOTO	$-1		;IF FALSE TEST AGAIN
		MOVLW	B'00001101'
		MOVWF	T2CON		;ADJUST POSTSCALER TO 5TH OCTAVE
		BTFSC	MODE,0
		BSF	T2CON,4		;ADJUST POSTSCALER TO 4TH OCTAVE
		BTFSC	MODE,1
		BSF	T2CON,5		;ADJUST POSTSCALER TO 3RD OCTAVE
		BTFSC	MODE,2
		BSF	T2CON,6		;ADJUST POSTSCALER TO 2ND OCTAVE
		MOVLW	D'14'
		MOVWF	TMR2		;ADVANCE TIMER2 TO COMPENSATE FOR INTER-CYCLE DELAY
		BCF	PIR1,TMR2IF	;RESET SCALED TIMER2 INTERRUPT FLAG
		RETURN

;MAIN PROGRAM LOOP
MAIN		CALL	PADSCAN
		CALL	PADDISP
		CALL	MODECHK
		CALL	MODEDISP
		CALL	SYNTH
		GOTO	MAIN		;RESTART MAIN LOOP

		END			;IF YOU GET TO HERE, SOMETHING HAS GONE WRONG -_-