;16/24/64 program assembly code (8K 8051 with ctr2) 4/14/07 KB
;Fclk=6MHz, 12 clock mode

$nomod51				;include port names for this processor
$include (8052.MCU)

;declare names for remapping port pins to new layout:

	l2b	equ	p0.1	;declare LED display ports
	l2d	equ	p0.2	;LED digit number and segment letter
	l2f	equ	p0.3
	l2e	equ	p0.4
	l2a	equ	p0.5
	l2c	equ	p0.6
	l2g	equ	p0.7
	l1b	equ	p1.1
	l1d	equ	p1.2
	l1f	equ	p1.3
	l1e	equ	p1.4
	l1a	equ	p1.5
	l1c	equ	p1.6
	l1g	equ	p1.7
	enca	equ	p2.4	;encoder port A
	encb	equ	p2.5	;encoder port B
	effon	equ	p3.3	;effect on led (low to turn on)
	mute	equ	p3.4	;mute switch (goes low when pressed)
	muten	equ	p3.2	;stops muting of FV-1 when low
	sw3	equ	p2.6	;16 pgms if high, 24 if low
	sw2	equ	p2.7	;16/24 if high, 99 if low
	sel0	equ	p3.6	;program select bit 0
	select	equ	p2.3	;encoder select button
	sda	equ	p2.2	;serial EE data line
	sck	equ	p2.1	;serial EE clock line
	wprot	equ	p3.7	;serial EEPROM write protect

;declare registers (30H to 5FH):

	delay	equ	30h	;delay value for 1mS delay subroutine
	newprog	equ	31h	;new selected program number
	nprog	equ	32h	;running program number
	phase	equ	33h	;register for decoding encoder (4 states)
	counter	equ	34h	;general purpose register for counting
	effen	equ	35h	;0 to mute effects, if MS bit set, effects on
	nfv1	equ	36h	;fv-1 program number for nprog
	var1	equ	37h	;first variable value for nprog
	var2	equ	38h	;second variable value for nprog
	var3	equ	39h	;third variable value of nprog
	timeout	equ	3ah	;counter to time out ready routine
	temp	equ	3bh	;general purpose temp register
	bytectr	equ	3ch	;bytectr in dump routine
	pagectr	equ	3dh	;page counter in EEPROM dump routine
	reqeff	equ	3eh	;request for change in muting
	reqpgm	equ	3fh	;request for change in program
	twinkm	equ	40h	;twinkle mask for pending program display
	twinkc	equ	41h	;counter to time the twinkle process
	ectr	equ	42h	;counter for EEPROM read/write serial offload
	delay2	equ	43h	;second counter for timing 256 ms


;bit addressable registers (20H to 2FH):

	newenc	equ	20h
	oldenc	equ	21h	;encoder storage
	disp1	equ	22h	;first digit value to display
	disp2	equ	23h	;second digit value to display
	lstsel	bit	26h.0	;last effects-on key value (bit0)
	lsteff	bit	26h.1	;last select button value (bit1)

;********************************* RESET **********************************

org	0000			;reset entry point

	mov	sp,#60h		;set stack for 32 locations at top of memory
	mov	08eh,#0		;turn off ALE output
	lcall	start_fv1	;get conditions from last powerup, start clocking FV-1

;********************************* MAIN LOOP *****************************
lup:
	lcall	scan		;get new control inputs and display
	mov	a,reqpgm	;is there a request for program change?
	jz	lup1		;if not, skip over call to load program 
	mov	reqpgm,#0	;clear request
	mov	a,newprog	;get new program select number
	xrl	a,nprog		;XOR with current program number
	jz	lup1		;if no program change, don't reload
	mov	nprog,newprog	;move pending program to current program
				;because newprog is changed by scan routine,
				;but ldprog loads the nprog value
	lcall	ldpgm		;load new program into EEPROM 
	mov	a,effen		;see if effects are enabled
	jz	lup1		;if not, don't force an FV-1 program change
	setb	sel0		;force FV-1 to change program
	lcall	d1ms
	clr	sel0
lup1:
	mov	a,reqeff	;is there a request to change muting?	
	jz	lup2		;if not, jump over mute change routine
	mov	reqeff,#0	;clear request
	lcall 	ldeffen		;affect system with new mute condition
lup2:
	lcall	d1ms		;loop delay = 1 mS
	ljmp	lup		;repeat loop
	

;****************************** SUBROUTINES ******************************

start_fv1:			;get last values from EEPROM before starting FV-1 clock
	lcall	scan		;start by running keyscan once, which
				;establishes the register conditions
				;needed for the continuing process
				;now read the program value from the EEPROM,
				;along with the last effects-on bit:
	lcall	estart		;send start to EEPROM
	mov	a,#0A0h		;write command to set address pointer
	lcall	ewrbyte		;write to EEPROM
	mov	a,#0		;send the ms address
	lcall	ewrbyte		;write it to EEPROM
	mov	a,#2		;address of the program store
	lcall	ewrbyte		;write that byte too...
	lcall	estart		;initiate a start again
	mov	a,#0A1h		;read command
	lcall	ewrbyte		;write the command
	lcall	erdbyte		;read the byte
	lcall	estop		;stop the transmission
	mov	temp,a		;save the value, as it contains two portions...
	anl	a,#7fh		;preserve the lower 7 bits
	clr	c		;clear carry before subb
	subb	a,#99		;is greater than 99? if so, there was an EEPROM error
	jc	start1		;jump if carry (less than pgm100 (99)
	mov	a,temp		;get temp back, we're out of range
	anl	a,#80h		;preserve effect on bit, set pgm to 0 (fault)
	mov	temp,a		;put temp back
start1:
	mov	a,temp
	anl	a,#7fh		;mask again
	mov	newprog,a	;save program number
	mov	nprog,a		;in both current and pending registers
	mov	a,temp		;get read value back again
	anl	a,#80h		;preserve the msb
	mov	effen,a		;put the last effect-on value in place
	lcall	ldmute		;load the mute program into EEPROM slot #1
	lcall	ldpgm		;load the current program into EEPROM slot #0
	setb	sel0		;set FV-1 to mute program
				;start up FV-1:
	mov	rcap2h,#0ffH	;initialize 32KHz clock with ctr2 to T2 out
	mov	rcap2l,#0d2H	;set rollover rate (12 clock)
	mov	0C9H,#02H	;T2MOD
	mov	t2con,#04H	;start the clock output
	clr	sel0
	lcall	d1ms
	setb	sel0		;put in mute mode
	lcall	d250ms		;now delay 250ms to allow FV-1 to come up
	mov	reqeff,#1	;request mute change 
	ret

;***************************************************************************

d1ms:				;delay 1 millisecond 
	mov	delay,#248
d1mslup:
	djnz	delay,d1mslup
	ret


d250ms:				;delay 250ms
	mov	delay2,#250
d250ms1:
	lcall	d1ms
	djnz	delay2,d250ms1
	ret
	
;******************************* SCAN *************************************

;This routine, looking at the encoder, is troublesome. The Panasonic
;encoder produces 4 states per 'click', which is really dumb.
;the only encoder I could find (Digikey) which produces only 1 state per click
;costs way too much. I will assume the encoders in China are also poorly
;thought out, and I will compensate with the addtion of an extra register and
;some code to make the system work well with the Panasonic part.
;do manufacturers actually THINK about how these parts are used? We do not
;live in a world of up/down counters anymore!

scan:				;look at encoder 
				;affect newpgm, and limit to 16, 24 or 99,
				;depending on pin conditions.
	push	dph	
	push	dpl		;this subroutine uses the data pointer,
				;which may already be in use by the calling
				;program.
	mov	c,enca		;get encoder 'A' bit
	mov	acc.0,c		;put encoder 'A' bit into newenc bit 0
	mov	c,encb		;get encoder 'B' bit
	mov	acc.1,c		;put encoder 'B' bit into newenc bit 1
	anl	a,#3		;only lower two bits!
	mov	newenc,a	;save as new encoder value
	cjne	a,oldenc,chgenc	;compare new and old enc values
	ljmp	scansel		;if no change, go to test select button		
chgenc:				;encoder has changed
	mov	c,oldenc.1	;get last encoder 'B' value in carry
	mov	a,newenc	;load new encoder values
	anl	a,#01h		;mask off all but new 'A' bit
	addc	a,#0		;add carry (old 'B') to new 'A' enc bit
	jnb	acc.0,decphase	;if old B and new A are the same, then we're rotating CCW
				;else, we're roating CW, so we:
	inc	phase		;increment phase value
	ljmp	chgenc1		;jump over decphase.
decphase:			
	dec	phase		;decrement pahse value
chgenc1:
	mov	a,newenc	;load new encoder contitions
	mov	oldenc,a	;save the new conditions to the old register
	cjne	a,#3,scansel	;if both high, then we're in the detent, and can act
				;else, we've serviced the encoder, but are not
				;ready to act (user is turning knob)
	mov	a,phase
	jz	scansel		;if we switched back to original position, no action
	jnb	acc.7,incpgm	;if phase is positive, then increment program number,
				;else decrement program number:
decpgm:			
	mov	phase,#0	;we're in the detent, we're acting, zero out the phase byte	
	dec	newprog		;decrement new program number
	mov	c,sw2		;see if we're in 99 program mode
	jnc	dec99		;if pin is grounded, we're in 99 program mode	
	mov	c,sw3		;see if we're in 24 program mode
	jnc	dec24		;if pin is grounded, we're in 24 program mode
				;must be in 16 program mode instead
	mov	a,newprog	;load up new program number
	clr	c		;always clear carry before subb in 8051
	subb	a,#16		;is the new program number beyond 15? (0-15 max)
	jnc	dec16a		;jump to limit number if >15 (went below 0)
	ljmp	scansel		;else, newprog is within range, go scan select button	
dec16a:
	mov	newprog,#15	;force newprog to maximum value
	ljmp	scansel		;newprog has been limited, go scan select button
dec24:				;we're in 24 program mode, check if we dec'd below 0
	mov	a,newprog	;get the new program value
	clr	c		;clear carry before subb in 8051
	subb	a,#24		;subtract to see if the value is out of range
	jnc	dec24a		;if so, then jump to limit value to 23
	ljmp	scansel		;else, newprog is within range, go scan select button
dec24a:
	mov	newprog,#23	;force new program value to 23
	ljmp	scansel		;done, go scan select button
dec99:
	mov	a,newprog	;now do the same if in 99 program mode 
	clr	c		;clear carry
	subb	a,#99		;beyond 98? (0 to 98)
	jnc	dec99a		;if so, we need to fix it.
	ljmp	scansel		;else, newprog is within range, go scan select button
dec99a:
	mov	newprog,#98	;force new value (newprog is 0-98, display is 1-99)
	ljmp	scansel		;done, go scan select button

incpgm:
	mov	phase,#0	;we're in the detent, we're acting, zero out the phase byte
	inc	newprog		;increment new program number 
	mov	c,sw2		;see if we're in 99 program mode
	jnc	inc99		;if in 99 program mode, jump to that routine
	mov	c,sw3		;see if we're in 24 program mode
	jnc	inc24		;if in 24 program mode, jump to that routine
				;must be in 16 program mode
	mov	a,newprog	;get the newprog value
	clr	c		;clear carry before subb in 8051
	subb	a,#16		;beyond 15 (0-15 range)
	jnc	incout		;if beyond, force newprog to zero
	ljmp	scansel		;done, go scan select button
inc24:
	mov	a,newprog	;get the newprog value
	clr	c		;clear carry before subb in 8051
	subb	a,#24		;beyond 23 (0-23 range)
	jnc	incout		;if beyond, force newprog to zero
	ljmp	scansel		;done, go scan select button
inc99:
	mov	a,newprog	;get the newprog value
	clr	c		;clear carry before subb in 8051
	subb	a,#99		;beyond 98 (0-98 range)
	jnc	incout		;if beyond, force newprog to zero
	ljmp	scansel		;done, go scan select button
incout:
	mov	newprog,#0	;resolve the program number going too high by forcing 
				;to program 0 (numbers 'roll-around to zero'

scansel:			;now look at the select button in the encoder
				;if it goes down (closes) we need to tell the
				;main lup about the request. If it's just coming
				;up (opening) then we do nothing.
	mov	c,select	;get select key value in the carry bit
	jnc	scanselo	;if low, then button may have just been pushed
	setb	lstsel		;if high remember last condition in lstsel bit
	ljmp	scaneff		;go to scan effects enable key
scanselo:			;select key is low, see if it was low before..
	jb	lstsel,scanselx	;jump if last scan showed select to be high
	ljmp	scaneff		;else, it was low before, go scan effects on key
scanselx:			;select key has just gone down
	mov	reqpgm,#1	;tell main lup that we just asked for the new program
	clr	lstsel		;remember that the select key was low

scaneff:			;scan the effects on-off (mute) key:
	mov	c,mute		;get key value in carry
	jnc	scaneffo	;jump if key on to see if 'just' on
	setb	lsteff		;else, remember the value (high)
	ljmp	disp		;and jump to display routine
scaneffo:			;key is down (on)
	jb	lsteff,scaneffx	;if it was high before, then act
	ljmp	disp		;else, go on to display routine
scaneffx:
	mov	reqeff,#1	;tell main loop that we have a change.
	mov	a,effen		;get effect enable value
	jz	scaneffy	;if effects off, then skip to set on
	mov	effen,#0	;clear effects-on bit (msb of effen)
	ljmp	scaneffz	;and go to end of routine
scaneffy:			
	mov	effen,#80h	;turn effects on
scaneffz:
	clr	lsteff		;and remember that the key was down
				;continue on to display routine:

;***************************** display portion of scan ****************************

disp:				;display number from newprog, which may NOT be the
				;currently running program.
	mov	disp1,#0	;clear MS digit value for calculation
	mov	a,newprog	;get new current program number
	add	a,#1		;convert from 0-98 to 1-99 (or 0-16 or 0-24)
				;processor stores program numbers in binary,
				;but user doesn't like 0 as a program number...
displup:
	clr	c		;clear carry before subb
	subb	a,#10		;repetitively subtract 10
	jc	toofar		;subtract to below zero?
	inc	disp1		;if can be divided by 10, then increment first digit
	ljmp	displup		;try again
toofar:
	add	a,#10		;restore LS digit, by adding 10 back
	mov	disp2,a		;put value in place
	mov	dptr,#ledtab	;point to LED segment codes
	movc	a,@a+dptr	;get digit segment display from table
	orl	a,twinkm	;or with twinkle mask (see twinkle routine)
	mov	c,acc.0		;go bit by bit, as mapping may change
	mov	l2a,c
	mov	c,acc.1
	mov	l2b,c
	mov	c,acc.2
	mov	l2c,c
	mov	c,acc.3
	mov	l2d,c
	mov	c,acc.4
	mov	l2e,c
	mov	c,acc.5
	mov	l2f,c
	mov	c,acc.6
	mov	l2g,c		;done with second digit, now do first:
	mov	a,disp1		
	mov	dptr,#ledtab
	movc	a,@a+dptr	
	orl	a,twinkm	;or with twinkle mask (see twinkle routine)	
	mov	c,acc.0		
	mov	l1a,c
	mov	c,acc.1
	mov	l1b,c
	mov	c,acc.2
	mov	l1c,c
	mov	c,acc.3
	mov	l1d,c
	mov	c,acc.4
	mov	l1e,c
	mov	c,acc.5
	mov	l1f,c
	mov	c,acc.6
	mov	l1g,c		;done with first digit digit
				;now display effects enable condition:
	mov	a,effen		;get effects enable byte
	jz	dispeffz	;jump if effects are off
	clr	effon		;clear LED bit to turn on
	ljmp	twinkle		;jump to next routine
dispeffz:			;turn off effects-on LED
	setb	effon		;write led port hi
twinkle:			;cause display to twinkle, if nprog <> newprog
	mov	a,nprog		;get currently running program number
	xrl	a,newprog	;get displayed program number and XOR
	jnz	twink1		;if not same, jump to twink1
	mov	twinkm,#0	;if same, clear twinkle mask
	ljmp	twinkend	;and return
twink1:				;we need to twinkle display
	inc	twinkc		;increment twinkle counter
	mov	a,twinkc	;move to twinkle counter to acc
	clr	c		;clear carry before subb
	subb	a,#10		;10 ms count set here
	jnc	twink2		;if counter is greater than 10ms then change pattern
	ljmp 	twinkend	;else return
twink2:				;change twinkle pattern:
	mov	twinkc,#0	;reset twinkle counter
	mov	a,twinkm	;get twinkle mask
	jnz	twink3		;if zero, we need to 'seed' the mask
	mov	twinkm,#80H	;by setting the mask MSB
twink3:
	mov	a,twinkm	;get the mask back
	rr	a		;rotate the accumulator to the right
	mov	twinkm,a	;save the mew mask
twinkend:
	pop	dpl
	pop	dph		;pop the data pointer
	ret			;end of display routine
	

;******************************* load program *************************************

ldpgm:				;loads programs and variants
				;will write program in 32 byte pages
				;and wait for write completion each time.
				;will poll part to check for write completion.
				;and scan keys and update display while waiting.
				;first get program position in DPTR and variables.
				;depends on whether in 16, 24 or 99 mode.
				;first, find which table applies, from switch settings:

	mov	c,sw2		;look at 99 pgm switch
	jc	ldpgm1		;jump if not in 99 pgm mode
	mov	dptr,#tab99	;load data pointer with table address for 99 programs
	ljmp	ldpgm3		;jump around 16 and 24 options
ldpgm1:
	mov	c,sw3		;look at 16/24 switch
	jc	ldpgm2		;jump if not 24 program mode
	mov	dptr,#tab24	;load data pointer with table address for 24 programs
	ljmp	ldpgm3		;jump over 16 program pointer load
ldpgm2:
	mov	dptr,#tab16	;load data pointer with table address for 16 programs
ldpgm3:				;dptr is now set to proper table start
	mov	a,nprog		;get accepted program number to load
	clr	c		;clear carry for rotate
	rlc	a
	rlc	a		;4 bytes per entry, multiply acc by 4 (2 left shifts)
	mov	temp,a		;save accumulator, since result may have gone 
				;past 255 (99 pgm mode)
	mov	a,#0		;clear accumulator
	addc	a,dph		;add carry bit (if any) to dph from previous shift
	mov	dph,a		;save dph back
	mov	a,temp		;get shifted pgm #  back
	add	a,dpl		;add to dptr LS value
	mov	dpl,a		;move back to data pointer
	mov	a,#0		;clear accumulator
	addc	a,dph		;so any carry can flow to ms portion of data pointer
	mov	dph,a		;data pointer now set to proper table entry
	clr	a		;clear acc for next read
	movc	a,@a+dptr	;get table entry in acc
	mov	nfv1,a		;save FV-1 program number
	mov	a,#1		;next byte
	movc	a,@a+dptr	;get next table entry in acc
	mov	var1,a		;store 1st variable value
	mov	a,#2		;next byte
	movc	a,@a+dptr	;get next byte in acc
	mov	var2,a		;store 2nd variable value
	mov	a,#3		;next byte
	movc	a,@a+dptr	;get next byte in acc
	mov	var3,a		;store 3rd variable value

;at this point, we have the 4 bytes, stored in memory:
;nfv1=FV-1 program number, var1=1st variable, var2=second variable, var3=third variable.
;now set dptr to point to the first entry in the fv-1 program table:

	mov	dptr,#dpgm	;point to top of table
	mov	a,nfv1		;get the FV-1 program number	
	rl	a		;512 bytes per program, shift program # 1 bit left
	add	a,dph		;add to ms byte of dptr 
	mov	dph,a		;dptr now points to correct program sequence
				;in FV-1 code (include file)

;now offload the data, in 32 byte chunks, with variables inserted into positions
;0(var1), 2(nprog and effen), 8(var2) and 16(var3)

	mov	timeout,#0	;set up a counter to force SCK high if 'stuck'
				;due to a code fault or ESD upset
readylup:
	lcall	eready		;see if we're ready, will return with carry high
				;if the FV-1 is acessing EEPROM
	jnc	loadcode	;don't attempt to write to EE if FV-1 is accessing						;if not ready then test again, but watch the user 
				;controls while we wait:
	lcall	scan		;get new control inputs as newprog change
	lcall	d1ms		;delay
	djnz	timeout,readylup	;cycle until timeout rolls over
	setb	sck		;jam sck high; it must have been 'stuck'...

loadcode:			;now load the code sequence into the EEPROM:

	lcall	estart		;sends start transition to EEPROM
	mov	a,#0A0H		;byte value for writing to EEPROM
	lcall	ewrbyte		;send  the byte to EEPROM
	clr	a		;ms address = 0
	lcall	ewrbyte		;write the MS EEPROM address
	clr	a		;ls address = 0
	lcall	ewrbyte		;write the LS EEPROM address

	mov	a,var1		;overwrite with var1
	lcall	ewrbyte		;write to byte 0
	inc	dptr		;increment the data pointer, skipping that table value

	clr	a
	movc	a,@a+dptr	;get data from table
	lcall	ewrbyte		;write to byte 1
	inc	dptr		;increment the data pointer

	mov	a,nprog		;get current program
	orl	a,effen		;or with effect-on bit
	lcall	ewrbyte		;write value to byte 2
	inc	dptr		;increment the data pointer, skipping that table value

	clr	a		;continue offloading table values to EEPROM buffer
	movc	a,@a+dptr
	lcall	ewrbyte		;write to byte 3
	inc	dptr

	clr	a
	movc	a,@a+dptr
	lcall	ewrbyte		;write to byte 4
	inc	dptr

	clr	a
	movc	a,@a+dptr
	lcall	ewrbyte		;write to byte 5
	inc	dptr

	clr	a
	movc	a,@a+dptr
	lcall	ewrbyte		;write to byte 6
	inc	dptr

	clr	a
	movc	a,@a+dptr
	lcall	ewrbyte		;write to byte 7
	inc	dptr

	mov	a,var2		;variable 2 location
	lcall	ewrbyte		;write var2 to byte 8
	inc	dptr		;increment the data pointer, skipping that table value

	clr	a
	movc	a,@a+dptr
	lcall	ewrbyte		;write to byte 9
	inc	dptr

	clr	a
	movc	a,@a+dptr
	lcall	ewrbyte		;write to byte 10
	inc	dptr

	clr	a
	movc	a,@a+dptr
	lcall	ewrbyte		;write to byte 11
	inc	dptr

	clr	a
	movc	a,@a+dptr
	lcall	ewrbyte		;write to byte 12
	inc	dptr

	clr	a
	movc	a,@a+dptr
	lcall	ewrbyte		;write to byte 13
	inc	dptr

	clr	a
	movc	a,@a+dptr
	lcall	ewrbyte		;write to byte 14
	inc	dptr

	clr	a
	movc	a,@a+dptr
	lcall	ewrbyte		;write to byte 15
	inc	dptr

	mov	a,var3		;variable 3 location
	lcall	ewrbyte		;write var3 to byte 16
	inc	dptr		;increment the data pointer, skipping that table value

;now write the remaining 15 bytes:

	mov	bytectr,#15

loadcode1:

	clr	a		;zero out accumulator
	movc	a,@a+dptr	;get byte from table
	lcall	ewrbyte		;write byte to EEPROM buffer
	inc	dptr		;increment the data pointer
	djnz	bytectr,loadcode1	;loop until remaining bytes are loaded

	lcall	estop		;begins EERPOM write cycle
	lcall	chkwr		;returns only when write process is done.
dump:				;dumps 15 x 32 byte pages to EEPROM, using dptr

	mov	pagectr,#1	;counter indicates page # (32 bytes each)
dumplup:
	lcall	estart		;start a transfer frame
	mov	a,#0a0h		;load the write code to accumulator
	lcall	ewrbyte		;write command to EEPROM
	mov	a,pagectr	;get page counter
	clr	c		;clear carry
	rlc	a		;rotate accumulator 5 times to the left
	rlc	a
	rlc	a
	rlc	a
	rlc	a		
	mov	temp,a		;store LS address in temp reg
	clr	a		;clear accumulator
	rlc	a		;get carry bit into accumulator from previous shift
	lcall	ewrbyte		;send MS address
	mov	a,temp		;get LS back from temp
	lcall	ewrbyte		;send LS address

	mov	bytectr,#32	;going to send 32 bytes
dumplup2:
	clr	a
	movc	a,@a+dptr
	lcall	ewrbyte
	inc	dptr
	djnz	bytectr,dumplup2
	
	lcall	estop		;begin EEPROM write process
	lcall	chkwr		;watch when write is done, while scanning
	lcall	estop
	inc	pagectr
	mov	a,pagectr
	cjne	a,#16,dumplup	;cycle until done
	ret

;******************************* load mute ****************************************

ldmute:				;offloads mute program to second program space
				;as a repetitive wrax dacl,0 and wrax dacr,0
	mov	pagectr,#0	;set up a counter of EEPROM pages (32 bytes each)
ldmute1:
	lcall	estart		;start an EEPROM transfer
	mov	a,#0a0H		;load acc with write code
	lcall	ewrbyte		;send to EEPROM
	clr	c
	mov	a,pagectr	;load pagecounter
	rlc	a		;and shift 5 places to the left (x32)
	rlc	a
	rlc	a
	rlc	a
	rlc	a
	mov	temp,a		;store result in temp
	mov	a,#0
	rlc	a		;shift 'spilled' bit from above into ACC LS bit
	orl	a,#02h		;set bit 1 of acc (second FV-1 area of EEPROM)
	lcall	ewrbyte		;write MS portion of EEPROM address
	mov	a,temp		;recall LS portion
	lcall	ewrbyte		;write LS address to EEPROM
	mov	dptr,#dmute	;address the mute page table of FV-1 code
	mov	bytectr,#32	;32 bytes per page
ldmute2:
	clr	a
	movc	a,@a+dptr	;get table data value
	lcall	ewrbyte		;write to EEPROM
	inc	dptr		;inc data pointer to next table entry
	djnz	bytectr,ldmute2	;loop until all 32 transferred
	lcall	estop		;stop command, begin write process
	lcall	chkwr		;monitor write process and scan until done
	lcall	estop
	inc	pagectr		;next page
	mov	a,pagectr	;load page number to acc
	cjne	a,#16,ldmute1	;have we done 16 yet?
	ret

;**************************** load effect-on-off condition ****************************

ldeffen:			;called only if effect-on change has occurred
	jb	muten,ldeff1	;check port pin to see if muting is enabled
	clr	sel0		;always run program if external switch is low
	ljmp 	wreffen		;if cleared, we don't want to mute the program
				;because the mute will be done in the returns (analog)
				;just write the condition to the enable program 
				;byte in EEPROM
ldeff1:				;handle software mute
	mov	a,effen		;load acc with the effects-on byte
	jz	ldeff2		;jump if effects are to go mute
	clr	sel0		;set FV-1 to run program
	ljmp	wreffen		;write the change to EEPROM
	
ldeff2:				;here, we mute the effects:
	setb	sel0		;switch FV-1 to mute program

wreffen:			;change EEPROM value to reflect change in muting:
	lcall	eready		;make sure bus is inactive
	jc	wreffen		;if carry, then bus is active, test until clear
	lcall	estart		;start a transfer
	mov	a,#0A0h		;acc=write code
	lcall	ewrbyte		;write the code to EEPROM control
	mov	a,#0		;MS EEPROM address
	lcall	ewrbyte		;write MS to EEPROM control
	mov	a,#2		;LS EEPROM address
	lcall	ewrbyte		;write LS to EEPROM control
	mov	a,nprog		;get current program number
	orl	a,effen		;or with the effects-on byte
	lcall	ewrbyte		;write to EEPROM data buffer
	lcall	estop		;begin write process
	ret

;******************************************************************************


chkwr:				;routine to check if EEPROM write is done,
				;polls switches and updates display in process
				;in the process				
	lcall	estart		;send start command to EEPROM
	mov	a,#0a0h		;ask for write
	lcall	ewrbyte		;write request to EEPROM
	jc	chkwr1		;carry indicates no acknowledge from ewrbyte routine
	ret			;return if ack comes back
chkwr1:
	lcall	scan		;process new control inputs
	lcall	d1ms		;delay
	ljmp	chkwr

;********************************************************************************

;EEPROM COMMUNICATION
;all routines leave clk high, but data is only gauranteed high before sync and after stop.
;data is always low after ack, which terminates each command.
;EEPROM sees data change while clk=1 as start (falling) or stop (rising).
;valid data can only change while clk is low, and clocked on next clk rising edge.
;data transfers are MSB first.
;ALL USES OF THESE ROUTINES REQUIRE THAT THE BUS IS CLEAR BY CALLING READY AND
;CHECKING FOR CARRY=0, CALLING START FIRST, AND ALWAYS TERMINATING BY CALLING STOP.
;This leaves SDA and SCK high while not accessing the EEPROM.

estart:
	clr	sck		;clock should be high 
	setb	sda		;but we need to make sure data is high
	setb	sck		;make clock high for start transition
	clr	sda		;drop data to indicate start.
	ret

ewrbyte:			;sends A to EEPROM, returns ack in C, leaves sda low
	mov	ectr,#8		;establish counter for number of bits
ewrlup:
	clr	sck		;data can now change
	rlc	a		;rotate ACC through carry
	mov	sda,c		;transfer bit to data line
	setb	sck		;clock bit
	djnz	ectr,ewrlup	;loop until done
	clr	sck		;check for ack
	setb	sda		;allow sda to go high
	nop			;delay (8051 convention)
	mov	c,sda		;get ack into carry
	setb	sck		;process over
	clr	wprot		;will be set again after stop 
	ret

erdbyte:			;returns byte in A, sends ack, leaves sda low
	mov	ectr,#8		;establish counter for number of bits
erdlup:
	clr	sck		;data can now change
	setb	sda		;allow data to be high
	nop			;delay (8051 convention)
	mov	c,sda		;transfer bit to carry
	rlc	a		;rotate ACC through carry
	setb	sck		;clock bit
	djnz	ectr,erdlup	;loop until done		
	clr	sck		;clock ack bit
	setb	sck		;process over
	ret

estop:				;sends stop bit
				;clock is high, but ack holds SDA low
	clr	sck		;release ack
	clr	sda		;hold data low, after ack releases
	setb	sck		;bring sck high
	setb	sda		;bring data high for stop, bus should be clear
	setb	wprot		;return write enable high again 
	ret

eready:				;routine to determine if FV-1 is accessing EEPROM
				;exits with carry set (busy), or clear (no activity)
	mov	counter,#0	;do 256 tests
eready1:
	jnb	sck,eready2		;is sck low? (means activity)
	djnz	counter,eready1
	clr	c		;clear carry
	ret			;returns with carry clear
eready2:
	setb	c
	ret

;********************************* DATA TABLES *******************************

ledtab:				;table is 'bit low to turn on', seg A = LSB
	db	40h		;0
	db	79h		;1
	db	24h		;2
	db	30h		;3
	db	19h		;4
	db	12h		;5
	db	02h		;6
	db	78h		;7
	db	00h		;8
	db	18h		;9

;********************************* FV-1 Programs *****************************
;
;There are 8 FV-1 programs, stored in 'dpgm'. Each entry for the program
;selection consists of an FV-1 program number (0-7), and three variables.
;the module programs can be assigned in this section.
;all variables must fall within the range of 0 to 127

;FV-1 program		var1			var2		var3
;0 Cathedral		predelay 		RT		damping
;1 Hall			predelay 		RT		damping
;2 Room			predelay 		RT		damping
;3 Plate		predelay 		RT		damping
;4 Gated		predelay) 		RT		damping
;5 Del/Rev		delay 			repeat		reverb
;6 Cho/Fla/Rev		cho(0-63),Fla(64-127) 	rate		reverb
;7 multidelay		delay			taps		reverb
;cho function has a delay tap that will output zero if var1 is zero, but
;will cause delay tap as the var1 increases; at var1=63, delay = 585mS

;select which of the 8 programs appears in the 16, 24 and 64 program sets,
;along with the 3 variable byte values:

tab16:
	db	3,	0,	60,	30	;program 1  	plate reverb short
	db	3,	0,	85,	30	;program 2	plate reverb long
	db	2,	0,	90,	80	;program 3	small room warm
	db	2,	0,	90,	0	;program 4	small room bright
	db	2,	40,	95,	70	;program 5	medium room warm
	db	2,	40,	95,	0	;program 6	medium room bright
	db	1,	20,	90,	80	;program 7	large room warm
	db	1,	20,	90,	10	;program 8	large room bright
	db	0,	90,	90,	40	;program 9	cathedral
	db	4,	20,	50,	40	;program 10	gated reverd
	db	5,	10,	0,	0	;program 11	doubler
	db	5,	20,	0,	0	;program 12	slapback delay
	db	5,	30,	0,	0	;program 13	single short delay
	db	7,	50,	0,	0	;program 14	few short delays
	db	5,	127,	0,	0	;program 15	single long delay
	db	6,	0,	70,	0	;program 16	chorus

tab24:
	db	3,	0,	60,	30	;program 1	plate reverb short
	db	3,	0,	85,	30	;program 2	plate reverb long
	db	2,	0,	90,	80	;program 3	small room warm
	db	2,	0,	90,	0	;program 4	small room bright
	db	2,	40,	95,	70	;program 5	medium room warm
	db	2,	40,	95,	0	;program 6	medium room bright
	db	1,	20,	90,	80	;program 7	large room warm
	db	1,	20,	90,	10	;program 8	large room bright
	db	0,	90,	90,	40	;program 9	cathedral
	db	4,	20,	50,	40	;program 10	gated reverb
	db	5,	10,	0,	0	;program 11	doubler
	db	5,	20,	0,	0	;program 12	slapback delay
	db	5,	30,	0,	0	;program 13	single short delay
	db	7,	20,	20,	0	;program 14	few short delays
	db	7,	30,	80,	0	;program 15	many short delays
	db	5,	120,	0,	0	;program 16	single long delay
	db	7,	120,	50,	0	;program 17	few long delays
	db	7,	120,	120,	0	;program 18	many long delays
	db	5,	40,	30,	90	;program 19	short delay + reverb
	db	5,	127,	0,	90	;program 20	long delay + reverb
	db	6,	0,	50,	0	;program 21	chorus
	db	6,	30,	50,	0	;program 22	chorus + short delay
	db	6,	63,	50,	0	;program 23	chorus + long delay
	db	6,	0,	50,	50	;program 24	chorus + reverb

tab99:
	db	0,	127,	100,	50	;program 1	Cavern (long, dark)
	db	0,	127,	100,	0	;program 2	Cavern (long, bright)
	db	0,	127,	80,	100	;program 3	Cavern (medium, dark)
	db	0,	127,	80,	40	;program 4	Cavern (medium)
	db	0,	127,	80,	0	;program 5	Cavern (medium, bright)
	db	0,	127,	50,	100	;program 6	Cavern (short, dark)
	db	0,	127,	50,	40	;program 7	Cavern (short)
	db	0,	127,	50,	0	;program 8	Cavern (short, bright)
	db	0,	0,	90,	120	;program 9	Cathedral (long, dark)
	db	0,	0,	90,	60	;program 10	Cathedral (long)
	db	0,	0,	90,	0	;program 11	Cathedral (long, bright)
	db	0,	0,	60,	120	;program 12	Cathedral (medium, dark)
	db	0,	0,	60,	60	;program 13	Cathedral (medium)
	db	0,	0,	60,	0	;program 14	Cathedral (medium, bright)
	db	1,	0,	90,	100	;program 15	Hall (long, dark)
	db	1,	0,	90,	50	;program 16	Hall (long)
	db	1,	0,	90,	0	;program 17	Hall (long, bright)
	db	1,	0,	60,	120	;program 18	Hall (medium, dark)
	db	1,	0,	60,	40	;program 19	Hall (medium,)
	db	1,	0,	60,	0	;program 20	Hall (medium, bright)
	db	1,	0,	40,	100	;program 21	Hall (short, dark)
	db	1,	0,	40,	40	;program 22	Hall (short)
	db	1,	0,	40,	0	;program 23	Hall (short, bright)
	db	0,	0,	10,	40	;program 24	Ambient 1 
	db	1,	0,	10,	40	;program 25	Ambient 2 
	db	2,	0,	10,	40	;program 26	Ambient 3 
	db	2,	0,	100,	90	;program 27	Room (long, dark)
	db	2,	0,	100,	40	;program 28	Room (long)
	db	2,	0,	100,	0	;program 29	Room (long, bright)
	db	2,	0,	70,	90	;program 30	Room (medium, dark)
	db	2,	0,	70,	40	;program 31	Room (medium)
	db	2,	0,	70,	0	;program 32	Room (medium, bright)
	db	2,	100,	70,	90	;program 33	Room (medium, predelay, dark)
	db	2,	100,	70,	40	;program 34	Room (medium, predelay)	
	db	2,	100,	70,	0	;program 35	Room (medium, predelay, bright)	
	db	2,	0,	40,	100	;program 36	Room (short, dark)
	db	2,	0,	40,	50	;program 37	Room (short)	
	db	2,	0,	40,	0	;program 38	Room (short, bright)	
	db	2,	10,	20,	120	;program 39	Ambient 4 (dark)
	db	2,	10,	20,	70	;program 40	Ambient 4 
	db	2,	10,	20,	0	;program 41	Ambient 4 (bright)
	db	3,	0,	110,	70	;program 42	Plate (long, dark)
	db	3,	0,	110,	30	;program 43	Plate (long)
	db	3,	0,	110,	0	;program 44	Plate (long, bright)
	db	3,	120,	110,	70	;program 45	Plate (long, predelay, dark)
	db	3,	120,	110,	30	;program 46	Plate (long, predelay)
	db	3,	120,	110,	0	;program 47	Plate (long, predelay, bright)
	db	3,	0,	90,	70	;program 48	Plate (medium, dark)
	db	3,	0,	90,	40	;program 49	Plate (medium)
	db	3,	0,	90,	0	;program 50	Plate (medium, bright)
	db	3,	127,	90,	70	;program 51	Plate (medium, predelay, dark)
	db	3,	127,	90,	40	;program 52	Plate (medium, predelay)
	db	3,	127,	90,	0	;program 53	Plate (medium, predelay, bright)
	db	3,	0,	70,	120	;program 54	Plate (short, dark)
	db	3,	0,	70,	80	;program 55	Plate (short)
	db	3,	0,	70,	0	;program 56	Plate (short, bright)
	db	3,	0,	20,	127	;program 57	Ambient 5 (dark)
	db	3,	0,	20,	60	;program 58	Ambient 5 
	db	3,	0,	20,	0	;program 59	Ambient 5 (bright)
	db	4,	0,	0,	70	;program 60	Gated 1 (dark)
	db	4,	0,	0,	0	;program 61	Gated 1 (bright)
	db	4,	0,	20,	70	;program 62	Gated 2 (dark)
	db	4,	0,	20,	0	;program 63	Gated 2 (bright)
	db	4,	0,	35,	60	;program 64	Gated 3 (dark)
	db	4,	0,	35,	0	;program 65	Gated 3 (bright)
	db	4,	0,	50,	50	;program 66	Gated 4 (dark)
	db	4,	0,	50,	0	;program 67	Gated 4 (bright)
	db	4,	0,	70,	50	;program 68	Gated 5 (dark)
	db	4,	0,	70,	0	;program 69	Gated 5 (bright)
	db	4,	0,	90,	50	;program 70	Gated 6 (dark)
	db	4,	0,	90,	0	;program 71	Gated 6 (bright)
	db	4,	0,	100,	50	;program 72	Gated 7 (dark)
	db	4,	0,	100,	0	;program 73	Gated 7 (bright)
	db	4,	0,	115,	50	;program 74	Gated 8 (dark)
	db	4,	0,	115,	0	;program 75	Gated 8 (bright)
	db	4,	0,	125,	40	;program 76	Gated 9 (dark)
	db	4,	0,	125,	0	;program 77	Gated 9 (bright)
	db	4,	127,	35,	50	;program 78	Gated (predelay)
	db	5,	15,	0,	0	;program 79	delay (double)
	db	5,	30,	0,	0	;program 80	delay (slap)
	db	5,	60,	0,	0	;program 81	delay 3
	db	5,	80,	0,	0	;program 82	delay 4
	db	5,	100,	0,	0	;program 83	delay 5
	db	5,	127,	0,	0	;program 84	delay 6
	db	5,	40,	40,	0	;program 85	delay 3 (repeat)
	db	5,	60,	40,	0	;program 86	delay 4 (repeat)
	db	5,	80,	40,	0	;program 87	delay 5 (repeat)
	db	5,	127,	40,	0	;program 88	delay 6 (repeat)
	db	5,	40,	0,	50	;program 89	delay 3 (reverb)
	db	5,	60,	0,	50	;program 90	delay 4 (reverb)
	db	5,	80,	0,	50	;program 91	delay 5 (reverb)
	db	5,	127,	0,	50	;program 92	delay 6 (reverb)
	db	7,	60,	50,	0	;program 93	multiecho 1
	db	7,	80,	70,	0	;program 94	multiecho 2
	db	7,	120,	120,	0	;program 95	multiecho 3
	db	6,	0,	50,	0	;program 96	chorus
	db	6,	0,	50,	40	;program 97	chorus (reverb 1)
	db	6,	0,	50,	80	;program 98	chrous (reverb 2)
	db	6,	100,	70,	0	;program 99	flange 

;now include the basic programs to offload.
;each program is 512 bytes long, and may be modified
;by the offloading code to mute or vary parameters.
;there are 8 programs in the set:

dpgm:

$include(a16_24.asm)

;and now the clear "fill" for mute, as an offloadable EEPROM page:

dmute:

	db	00,00,02h,0c6h	;wrax	dacl,0
	db	00,00,02h,0e6h	;wrax	dacr,0
	db	00,00,02h,0c6h	;wrax	dacl,0
	db	00,00,02h,0e6h	;wrax	dacr,0
	db	00,00,02h,0c6h	;wrax	dacl,0
	db	00,00,02h,0e6h	;wrax	dacr,0
	db	00,00,02h,0c6h	;wrax	dacl,0
	db	00,00,02h,0e6h	;wrax	dacr,0

	end


















