
;*******************************************************************************
;FILENAME:   PEG1.2.5.4 ASM                     (c) 2003-2004
;VERSION:   1.2.5.4
;DATE:    03-03-04
;AUTHOR:   SHANNON GOMES  & RED BYER        www.redstoyland.com
;
;SEE STATUS AND UPDATA AT END OF FILE
;
;DESRIPTION:  This is program for the Wedding Board's pegs.  Each 
;peg has an RGBB LED, controlled through p-ch FETs by an Atmel 
;AT Tiny12L-4SC microcontroller.  Because the LED is common anode,
;a LOW output from the Atmel part will turn that LED color on.
;
; Through a series of timed on/off cycles, this program can be put
;into a series of different modes:
;	Mode#	modereg		mode description
;	0   p000 0000	Solid White
;	1	p000 0001	Solid Red
;	2	p000 0010	Solid Yellow
;	3	p000 0011	Solid Green
;	4	p000 0100	Solid Cyan
;	5	p000 0101	Solid Blue
;	6	p000 0110	Solid Violet
;	7	p000 0111	Slow Colorwash
;	8	p000 1000	Medium Colorwash
;	9	p000 1001	Fast Colorwash  (default mode)
; ----the following modes can be overlayed onto any of Modes 0-9.----
; ----the overlay modes are removed by re-"programming" the peg-----
;	10	p001 xxxx	Slow Flash Overlay
;	11	p010 xxxx	Medium Flash Overlay
;	12	p011 xxxx	Fast Flash Overlay
;	13	p100 xxxx	Slow Pulse Overlay
;	14	p101 xxxx	Medium Pulse Overlay
;	15	p110 xxxx	Fast Pulse Overlay
;
;PROGRAMMING:   The peg is programmed by cycling the power (faster than 2Hz)
;The equation is 4X(PMCOUNT + MODE), where PMCOUNT is the number of cycles
;it takes for the peg to recognize it is being programmed.  
;     The 4Xis optional and is to provide some noise immunity and off-by-one
;error immunity.  Both the peg and the programmer must use the 4X at the
;same time.  HOWEVER, this will wear out the EEPROM 4x quicker and should
;only be enabled if it is a problem.
;
;    Bit #7 of the modereg is reserved and used for the Programming Mode Flag
;
;   As of 2/24/04, to recognize a programming cycle the peg had to:
;     be "ON" for a MINIMUM of 50ms (this is padded, could be as low as 10)
;     be turned "OFF" before 900ms (this is padded, may be 1000ms)
;
;
;FILE SETUP:  For an ATTINY12L-4SC, the brown-out indicator bit must
;be set for 2.7V (the Tiny12 is not specified down to 1.8V).  
;
;RC OSCILLATOR:  Use the internal oscillator for the tiny12 @ 1.2MHz
; 
;EEPROM SETUP:  EEPROM typically does not (and MUST NOT) start out all 0x00.
;Typically, the EEPROM is all 0xFF
;
;MODE DESCRIPTIONS:
;COLORWASH MODES:
;Modes 0 thru 9 are all based on a ColorWash cycle (0->6 just have all
;three colors of the wash set to be equal).
;     The wash program will ramp the duty cycle of three separate LED states in a
;continuous cycle as follows:
;There are three phases:  ramping up, ramping down, and idle
;ramping up = 	the duty cycle increases from 1/CYCLESPERSTEP to 
;		CYCLESPERSTEP/CYCLESPERSTEP
;ramping down =the duty cycle decreases from (CYCLESPERSTEP-1)/CYCLESPERSTEP
;		0/CYCLESPERSTEP
;idle =		LED is off for this phase
;
;    Each LED is in a different phase at all times.  Each LED will sequence through
;each phase in order.  The program loops forever.  Typical cycle is R->G->B
;See PEGDIAGRAM.pdf for a visual explanation.
;     After waking up to its mode, the program selects a random color to being
;the wash cycle with.
;
;FLASH OVERLAYS:
;	The FLASH OVERLAY MODES are timer-interrupt driven.  It alternates an
; ON/OFF mask on the PORTB outputs.  The duty cycle on FLASH is fixed for each flash
;speed.  The effect is strobe like.
;
;PULSE OVERLAYS:
;	The PULSE OVERLAY MODES are timer-interrupt driven.  These modes alternate
;an ON/OFF mask on the PORTB outputs that is duty-cycle varying.  The duty-cycle
;changes from  ON(1/stepsperseg) -> ON(stepsperseg/stepsperseg).   The effect
;is a gradual dimming and brightening.
;
;LED NOTE:  The LED that was selected for this version of the program is 
;	common cathode.  RT-0014 (PegPCB) has to P-CH enh mode MOSFETs and
;	so the Atmel pins drive the LEDs with a LOW!!!
;	To change to a common anode all you have to do is change the sense of
;	BLUE, GREEN, and RED.  i.e. BLUE=0xEB, GREEN=0xFD and RED=0xF7
;
;VOCABULARY:
;	Segment	- period of time for an LED to complete it's phase
;	Step	- a group of one dutycycle.  There are STEPSPERSEG steps in one Segment
;	Cycle	- we repeat a single dutycycle CYCLESPERSTEP times to make up
;		  one Step.  Each repeat is a Cycle.
;	Count	- there are STEPSPERSEG Counts per Cycle.  The reason
;		  it is the same as STEPSPERSEG is just to make things easier.
;	SK1	- the time on for the color in ramping up phase
;	SK2	- the time on for the color in ramping down phase
;
;FORMULAS:
;	SK1 + SK2 = STEPSPERSEG
;	STEPSPERSEG * CYCLESPERSTEP = ONE STEP
;	CYCLESPERSTEP * STEPSPERSEG = ONE SEGMENT
;	
;INPUTS:
;	There are not PORTB inputs to this program
;OUTPUTS:
;	see ".equ" section for the outputs from PORTB to LED pins for RT-0014 PegPCB.
;***************************************************************************************


.include "tn12def.inc"

;---The following are for ACTIVE LOW PORTB outputs
;---this is default:  i.e. PORTB-> LOW thru p-ch FET & common anode LED
.equ	BLUE		=0b11101011	;pins 3(PB4) and 7(PB2) are blue
;.equ	BLUE		=0b11111011	;pin 7(PB2) is blue test only
.equ	GREEN		=0b11111101	;pin 6(PB1) is green
.equ	RED		=0b11110111	;pin 2(PB3) is red
.equ	CYAN		=0b11101001	;blue and green
.equ	YELLOW		=0b11110101	;green and red
.equ	VIOLET		=0b11100011	;blue and red
.equ	WHITE		=0b11100001	;all on
.equ	BLACK		=0b11111111	;all off  
					;ORed with output to turn off all LEDs

;---The following are for ACTIVE HIGH PORTB outputs.
;.equ	BLUE		=0b00010100	;pins 3(PB4) and 7(PB2) are blue
;.equ	GREEN		=0b00000010	;pin 6(PB1) is green
;.equ	RED		=0b00001000	;pin 2(PB3) is red
;.equ	CYAN		=0b00010110	;blue and green
;.equ	YELLOW		=0b00001010	;green and red
;.equ	VIOLET		=0b00011100	;blue and red
;.equ	WHITE		=0b00011110	;all on
;.equ	BLACK		=0b00000000	;all off
					;ANDed with output to turn off all LEDs

;---The following are general mode speed globals
.equ	SLOW		=0x20		;32 dutycycles per segment  (used in tabledata)
.equ	MED		=0x10		;16 dutycycles per segment  (used in tabledata)
.equ	FAST		=0x08		; 8 dutycycles per segment  (used in tabledata)
.equ	STEPSPERSEG	=0xFF		;number of dutycycles per segment (overall speed)
.equ	CYCLESPERSTEP	=0x10		;number of repeated dutycycles per step (overall speed)

;---The following are eeprom address globals-----
;             pointer addresses:	0x01 -> 0x1F
;             buffer addresses:	0x20 -> 0x3E
;-------------------------------------------------
.equ	PNTRSTART	=0x01		;Pointer Start: Never use address 0x00 in an Atmel eeprom
.equ	PNTREND		=0X1F		;End of Pointer addresses
.equ	BFFRSTART 	=0x20		;Buffer Start, also PointerEndPlusOne (did we overflow?)	
.equ	BFFREND		=0x3E		;Location of Buffer's End
.equ	BFFRENDPLUSONE	=0x3F		;Buffer overflow plus one checkpoint
.equ	SPANMINUSONE	=0x1E		;Span between pointer & buffer MINUS ONE (gets last pcc)
.equ	MODEREGADDR	=0x3F		;address in EEPROM where we keep the mode number and PMF

;---The following are program control globals
.equ	PMCOUNT		=0x10		;number of power cycles it takes to get into programming mode
								;PMCOUNT here is +1 PMCOUNT on the programmer board (0 mode counts)
								;A CHANGE TO PMCOUNT ALSO CHANGES MAXCOUNT ACCORDINGLY
.equ	NUMMODES	=0x0F		;There are 15 modes (#0 -> #15). THis is *not* used in the program

.equ	MAXCOUNT = PMCOUNT + NUMMODES		
								; = PMCOUNT + #ofmodes  (d8 + d15 = d23 = 0x17)
								;example 2:  d16(x10) + d15(x0F) = 0x1F
								;Note:  mode #0 is a valid mode, hence the d15

.equ	PMF			=7			;bit number in mode register for the program mode flag
.equ	DEFAULTMODE	=0x09		; fast colorwash mode 0x09 is default


;---REGISTER DECLARATIONS------------------------
;the lower registers must be loaded from another register (mov command)
;the lower registers can NOT use the "ldi" command
;------------------------------------------------
.def	tabledata	=R00		;this register is reserved for the LPM instruction on the attiny12
.def	sklcnt		=R01		;cycle counter
.def	stpcnt		=R02		;step counter
.def	count		=R03		;generic counter variable
.def	skl2		=R04		;period of time that the ramp down color is on
.def	skl1		=R05		;period of time that the ramp up color is on
					;skl1 + skl2 = one cycle (STEPSPERSEG)
.def	hldr		=R06		;place holder for when we change phases
.def	idle		=R07		;this LED is not on for this segment
.def	go_n_dn		=R08		;this LED is ramping down its dutycycle
.def	go_n_up		=R09		;this LED is ramping up its dutycycle
.def	stppseg		=R10		;holds the STEPSPERSEG variable
.def	sklpstp		=R11		;holds the CYCLESPERSTEP variable
.def	ontime		=R12		;duration of pulse on time
.def	offtime		=R13		;duration of pulse off time
.def	updown		=R14		;direction of pulse on time

;---these registers can be loaded directly (ldi command)
.def	temp		=R16		;generic temp variable, used to hold LED state during wash
.def	tempa		=R17		;generic temp variable
.def	tempb		=R18		;generic temp variable
.def	tempc		=R19		;generic temp variable
.def	eeadr		=R20		;EEPROM address variable for reads and writes
.def	eedat		=R21		;EEPROM data variable for reads and writes
.def	pcc			=R22		;power cycle count
.def	modereg		=R23		;holds the mode number(0:3), flash/pulse mode(4:6), program mode flag(7)
.def	pccptr		=R24		;holds the pointer to the active pcc address once it's found
.def	seed		=R25		;seed variable for random number generator
.def	flashflag	=R26		;ANDed with portb to override output for flashing and pulsing
.def	fpmode		=R27		;holds flash or pulse mode #
.def	flashcnt	=R28		;holds number of times we need to count through timer0
;---Note:  R30 & R31 must be reserved for "Z" register

;INTERRUPT VECTORS
;.org $0000  Optional to put the 0000 location pointer declaration.
	rjmp reset 	;Reset Vector
	rjmp reset	;int0
	rjmp reset	;I/O pin change interrupt
	rjmp tim0_ovf	;Timer0 overflow
	rjmp reset	;EE_rdy
	rjmp reset	;ana_comp


;-------------------------------------------------------------------------------
;---TIMER OVERFLOW INTERRUPT SECTION--------------------------------------------
;
;This section used for the FLASH and PULSE mode overlays---------------------
;  
;  REGISTERS USED:  fpmode, flashflag, flashcnt, updown, ontime, offtime, 
;  temp, tempa, TCNT0, 
;-------------------------------------------------------------------------------
tim0_ovf:			;assumes fpmode is loaded from topnibble of modereg
	cpi fpmode, 0x04	;Pulse mode is 4, 5, 6
	brlo int_flash		;if 1,2,or 3 go to flash handler

;-----------------------------------------
;---------PULSE HANDLER SECTION-----------
;
;REFERENCES:  flashcnt, updown,tempa
;DESTROYS: ontime, offtime, updown, tempc (not used by wash),
;-----------------------------------------
int_pulse:
	tst flashflag		;is mask on or off?  
	breq int_on		;if flashflag == 0, LEDs are on for this interrupt
	dec flashcnt		;!=0, so decrement delay counter
	breq int_off		;if it is 0, time to do something
	out TCNT0,offtime	;!=0, do nothing, delay interrupt another offtime
	reti
int_off:			;flashflag !=0 && flashcnt ==0 (offcycle)
	tst updown		;what phase is the off-state in?
	breq int_offup		;updown == 0, then offtime is increasing
		 
int_offdn:			;LEDs in "OFF" state, offtime increasing
				;flashflag!=0 && flashcnt==0 && updown!=0
	inc offtime		;OFFTIME increasing (shorter time to interrupt when off)
	dec ontime		;ONTIME decreasing (longer time to interrupt when on)
	brne int_offend		;if ontime !=0, no phase change
				;ontime reached 0.  Flip phase "direction"
	clr updown		;Paranoia:  updown = 0, ontime increasing
	ldi tempc, 255		;load up temp
	mov offtime, tempc	;offtime = 255
	ldi tempc, 1		;load up temp
	mov ontime, tempc	;ontime = 1
	rjmp int_offend

int_offup:			;LEDs in "OFF" state, ontime increasing
				;flashflag!=0 && flashcnt==0 && updown==0 
				;LEDs in OFF state, but brightness ramping up
	inc ontime		;ONTIME increasing (shorter time to interrupt when on)
	dec offtime		;OFFTIME decreasing (longer time to interrupt when off)
	brne int_offend		;if offtime !=0, no phase change
				;offtime reached 0.  Flip phase "direction"
	ldi tempc, 255		;load up temp
	mov updown, tempc	;updown = 255, offtime increasing
				;"ser" command doesn't work for lower registers
	ldi tempc, 255
	mov ontime, tempc	;ontime = 255
	ldi tempc, 1
	mov offtime, tempc	;offtime = 1
	rjmp int_offend

int_offend:			;flashlag!=0 && flashcnt==0
				;"off" duty cycle complete,
	out TCNT0,ontime	;Next interrupt is (0xFF - ontime) delay away
;	com flashflag		;make flashflag = 0 (LEDs will turn on)
	clr flashflag		;Paranoia.  0 = LEDs turn on next time
				;tempa is currently active LED, passed from wash routine
	out PORTB,tempa		;if it was flashflag, all LEDs would turn on.
				;set PORTB to whatever color wash routine is using
	rjmp int_pulse_end	;branch to reload counter variables
	reti			;just in case

int_on:				;LEDs are in "ON" state. We don't mess with
				;the duty cycles during this half of the on/off period
				;flashflag==0..
	dec flashcnt		;decrement flashcnt and check
	breq int_on2		;flashcnt == 0, do handle the 'on' phase
	out TCNT0,ontime	;flashcnt !=0, more delay needed, reload timer counter register
	reti

int_on2:			;LEDs were ON, this "ON cycle is done
				;flashflag==0 && flashcnt==0
	out TCNT0,offtime	;next interrupt is (0xFF - offtime) delay away...
;	com flashflag		;make flashflag = 255
	ser flashflag		;paranoia.  255 = LEDs to be off next time
	out PORTB,flashflag	;turn all LEDs off for the "OFF" cycle
	rjmp int_pulse_end	;branch to reload counter variables
	reti
	
int_pulse_end:			;figure out and reset counter variables	
	cpi fpmode,0x05		;check which pulse mode we're in (4,5 or 6)
	breq int_pulsefour	; for PULSE2X, Pulse Overlay 0101
	ldi flashcnt,0x01	; Slow & Fast (4 & 6) Pulse use same flashcnt.
	reti
int_pulsefour:			;Medium Pulse uses a different flashcnt.
	ldi flashcnt,0x04
	reti


;-------------------------------------------
;----------FLASH HANDLER SECTION------------
;
;USES:  flashcnt, flashflag, tempa
;DESTROYS:  tempc  (unused by wash section)
;-------------------------------------------
int_flash:			;fpmode < 0x04
	dec flashcnt		;count this cycle (delay loop based on flashcnt)
	breq int_flash_rdy	;are we done delaying?
	reti			;guess not
int_flash_rdy:
	com flashflag		;on to off or off to on  where is flashflag loaded?
	mov tempc,tempa		;get required state...can we just get this from portb???
				;tempa is used during wash section for this purpose
	or tempc,flashflag	;turn them on or off
	out portb,tempc		;right now! 
	cpi fpmode,0x02		;which flash mode?
	breq int_flashtwo
	ldi flashcnt,0x02	;flash modes 1&3 count twice
	reti
int_flashtwo:
	ldi flashcnt,0x01	;mode 2 counts once
	reti

;----------------------------------------------------------------------------	
;----------------------------------------------------------------------------
;-----------RESET VECTOR-----------------------------------------------------
;----------------------------------------------------------------------------
;----------------------------------------------------------------------------
reset:
	cli			;disable interupts
	clr temp
	ser temp		;portb dir = 0xFF
	out DDRB,temp		;... we set PORTB to output
	out PORTB, temp		;all LEDsS OFF 
;	ldi flashflag,0xFF	;outputs will be allowed to turn on by default (active high)
	ldi flashflag,0x00	;outputs will be allowed to turn on by default (active low)
				;flashflag is a key variable
					
;------------------------------------------------------------------------------------
;-----------------------EEPROM BUFFER / PROGRAM MODE CHECK SECTION-------------------
;
;We are looking for the pointer into our program count.  This section scans through the 
;buffer (0x01->BUFFEND) looking for a "cliff" in values.  It is this cliff location
;that tells us where we last left off.  When it finds this cliff, "tempa" will contain
;the pointer to the top of the cliff and tempb will contain the pointer to the
;base (the next address to be used in the eeprom).  The pcc location is a direct offset
;(BUFFOFFSET) from the pointer location found during the buffer search. 
;------------------------------------------------------------------------------------
;------------------------------------------------------------------------------------
	ldi seed, 0x00		;we'll use this to check for buffer loop later
	ldi eeadr, 0x01		;start looking for the index pointer at 0x01 in the EEPROM
	rcall ee_read		;read EEPROM (we start by comparing addr0x01 to addr0x02
	mov tempa,eedat		;get data
nextptr:
	inc eeadr		;look at next address
	cpi eeadr, BFFRSTART	;did we go past the end of the buffer (i.e. past 1F)?
	brbc 1,notatend		;branch if bit cleared (eeadr!=0x20)
	ldi eeadr, PNTRSTART	;reset to start, we were at end of buffer region.
				;AND since we've tried all the addresses, 
	ldi seed, 0xFF		;set (flag) seed to 0xFF. Check that address 0x01 isn't 
				;valid. If not, break out of the loop
				;(in the 'notatend' function
notatend:
	rcall ee_read		;read EEPROM
	mov tempb,eedat		;get data, put into tempb
	cp tempb,tempa		;compare last 2 eeprom reads from buffer
	brlo greater		;branch if tempb < tempa  (or more to the point tempa > tempb)
	mov tempc,tempa		;move to another temp variable because I don't want to change tempa
	inc tempc
	cp tempc,tempb		;this is really comparing tempb to tempa+1
	brlo done		;branch if tempb is > tempa +1 (i.e. it's waaayyy bigger)
				;the thinking here is that we've rolled over from 255 and we're
				;starting up again from 0.  So buffer end will look something like
				;0,1,2,3,243,244,...etc  "3" being buffer end.  OR This is our first
				;time through and tempa=255 and tempb=255 (tempc=0)
	cpi seed, 0xFF		;have we done a full loop?
	breq done		;Yes, no valid data, break out!!!


next:
	mov tempa, tempb	;tempb is our next candidate
	rjmp nextptr		;let's go again!
greater:
	cpi tempa,0xff		;is tempa = 255?
	breq isff		;255 is our rollover condition let's handle it separately
	rjmp done		;we know tempa is > tempb and it's not 255 so we must be done!
isff:
	tst tempb		;is tempb = 0?
	breq next		;tempa=255 and tempb=0	so let's keep looking
	rjmp done		;tempa=255 and tempb<>0 so we're done!
;------------------------------------------------------------------------------------------
;--EXPLANATION:  At this point, tempa holds the peak value from the top of the cliff.
;-------tempb is either < a or =255
;-------eeadr is pointing to the cliff peak (where tempa got its data).
;------------------------------------------------------------------------------------------
done:
	inc tempa		;tempa holds previous pointer count. 
				;increase it to count this one (raise the cliff)
	mov seed,tempa		;seed is used for randomize function
	tst seed
	brne nonzero		;we're saving tempa at this point to use as a seed for a random # later
				;the seed can't be zero
	inc seed		;if it was 0, set it to one to be sure.
nonzero:	
	mov eedat,tempa
	rcall ee_write		;eeadr has already been incremented when loading tempb,
				; so its pointing to the NEXT spot (bottom of cliff)
	ldi temp,SPANMINUSONE	;this is the difference from current pointer address to LAST PCC address
	add eeadr,temp		;add it to eeadr. Now we're pointing into the buffer from last power on
	cpi eeadr, PNTREND	;are we looking below the buffer (i.e. end of pointers)
	brbc 1,readit		;skip if we're ok
	ldi eeadr, BFFREND	;point to end of buffer
readit:
	rcall ee_read
	mov pcc,eedat			;get count from last power on
	inc eeadr			;point to next location in the buffer
	cpi eeadr, BFFRENDPLUSONE	;check to see if we've gone past the end of the buffer
	brne chkfordata			;skip if we're not there yet
	ldi eeadr,BFFRSTART		;over top of buffer, so point to buffer start

chkfordata:
;----------------------------********------------------------------------------
;the following code provides the 4X noise immunity option, but since it will wear out
;the EEPROM that much faster should only be enabled if the off-by-one errors are common
;when programming multiple pegs.   Both the programmer and the peg must have their
;respective sections enabled
;----------------------------********-------------------------------------------
;
; 	lsr pcc
;	lsr pcc
;
;----------------------------********-------------------------------------------
	cpi pcc,MAXCOUNT	;is the count legit?  (or is there is no data)
	brlo thrsdata		;if <MAXCOUNT, still okay..
	ldi pcc, 0x00		;If >=MAXCOUNT, this cycle is invalid, the inc will overprogram 
				;Since it's not legit, reset it to zero 
thrsdata:	  		;if we're here pcc < MAXOUNT 
	inc pcc			;inc programming count to count this power cycle
	ldi temp, MAXCOUNT	;preparing for a check on pcc
	inc temp		;temp = MAXCOUNT+1...checking for overcount
	cp pcc, temp		;Paranoia:  are we >=MAXCOUNT+1 (overprogrammed???)
	brlo goodcnt 		;OK! pcc <= MAXCOUNT, (not overprogrammed)
	ldi pcc, 0x00		;pcc >MAXCOUNT:  Paranoia:  somehow we have overcounted, set to max this time thru
;-------------------------------------------------------------------------------------------
;---EXPLANATION:  At this point, the program counter (pcc) has been incremented to include
;-----the current power cycle.  pcc has also been check to make sure it is in range.
;------eeadr is pointing to the correct location in the buffer (same as hinted by pointer cliff)
;-------------------------------------------------------------------------------------------
goodcnt:
	mov eedat,pcc		;setup pcc data to store into buffer.
;====================================================
	rcall ee_write		;THIS SAVES PCC TO EEPROM write PCC out to buffer
						;we could off/on cycle this quickly
;====================================================
	mov pccptr,eeadr	;save pointer to the active pcc address for use later

;****************************************************************************
;****************************************************************************
;THE FOLLOWING LINES ARE FOR TEST PURPOSES ONLY
;
;	ldi eeadr, MODEREGADDR	;get ready to force something into that eeprom
;	ldi eedat, 0x69			;this is MODE we're going to put into eeprom
;	rcall ee_write			;write it out to MODEREGADDR
;
;eewaitfortest:
;	sbic EECR,EEWE		;waitloop on eeprom
;	rjmp eewaitfortest
;****************************************************************************
;****************************************************************************	
	ldi temp,WHITE			;let's turn on the LEDs to let them know we're here
	out PORTB,temp
	ldi eeadr,MODEREGADDR		;so...uhh...what mode are we in?
	rcall ee_read			;this loads up eedat
	mov modereg,eedat		;store mode data in a local register
	mov temp,modereg		;move it into temp so that we don't mess with original
	andi temp,0x0F			;mask off upper nibble
	cpi temp,0x0A			;is the stored mode a legit mode?
	brlo getpmf			;YES, mode is legit, BRANCH and check for overlays

	bst modereg, PMF		;NO, modereg >=0x0A is not legit, save PMF into T flag
	ldi modereg, DEFAULTMODE	;Then set modereg to some legit mode.
	bld modereg, PMF		;put that T flag back into our modereg 
	ldi eeadr, MODEREGADDR		;prepare. . .
	mov eedat, modereg		;to. . .
	rcall ee_write			;save our new legit mode now, so its correct next power cycle
getpmf:				;legit mode, check to see if we're being programmed
	bst modereg,PMF		;get PMF from modereg and put into T flag
	brts programmingmode	;branch if the PMF is set already (so we don't wear out eeprom)
	cpi pcc,PMCOUNT		;PMF not set, have we power cycled enough times?
	brlo getcolors		;MAJOR BRANCH.  pcc < PMCOUNT, just another power cycle.

	set			;pcc >= PMCOUNT, so set T bit in SREG
	bld modereg,PMF		;set Programming Mode Flag in modereg
	ldi eeadr,MODEREGADDR	;setup address
	mov eedat,modereg	;setup data
	rcall ee_write		;write our mode plus the PMF.

	rjmp programmingmode	;we are in programming mode

;----------------------------------------------------------------------------------------
;--EXPLANATION:  We are now entering programming mode, the programmer must wait at least 
;--this long for each power cycle.  The code has to get here and set the flag.  
;--Technically it must wait for the write to finish which could	be another 4ms!
;------pcc >= PMCOUNT, || PMF == 1 to enter programmingmode.
;----------------------------------------------------------------------------------------	
programmingmode:			
	ldi tempc,0x04		;wait for one second (@internal RC of 1.2MHz)
	rcall wait		;tempa,b,c can be reused for this delay loop since we are done with them otherwise
				;This loop will delay for approx. one second.  This will give the programmer enough
				;time to turn off the power if it has to but if we get to the end then it will be
				;assumed that the programmer is done and we can take the count to be the mode# (adjusted
				;for the # of power cycles it takes to get into programming mode.)
	
	ldi temp,BLACK
	out PORTB,temp		;turn off LEDs in preparation for flashing them to signal end of programming
	subi pcc,PMCOUNT	;adjust pcc downward for however many counts it took to get to programming mode
	cpi pcc,0x0A
	brlo basicmode		;if pcc <0x0A, branch because the new mode is one of the basic modes
calculateoverlay:
	subi pcc,0x09		;pcc >= 0x0A, NOT BASIC, so subtract off the basic mode 
				;portion and leave the flash/pulse mode bits
	andi pcc,0x0F		;clear the high nibble of pcc (paranoia)
	swap pcc		;swap high and low nibbles, this puts our overlay bits into upper nibble only.
	andi modereg,0x0F	;clear the high nibble of modereg (this also clears PMF)
	or modereg,pcc		;insert high nibble of pcc (flash/pulse mode#) into high nibble of modereg
	rjmp writeout		;we're done so let's write out our changes
basicmode:			;pcc< 0x0A to get here
	andi pcc,0x0F		;clear the high nibble of pcc (paranoia. this also clears PMF)
	clr fpmode		;more paranoia, just to be sure
	mov modereg,pcc		;set new mode.  This also clears:  PMF and flash/pulse mode#
	rjmp writeout		;paranoia
	
;-----------SAVING THE NEW MODE----------------------------------
;     if we write out here, we have accepted that we have
;     been on long enough and progamming has finished.
;----------------------------------------------------------------
writeout:			;time to clear that programming mode
	ldi eeadr,MODEREGADDR		
	mov eedat,modereg               
	rcall ee_write       	;SAVE THE NEW MODE!
resetcount:
	mov eeadr, pccptr	;set address to what we saved earlier
	clr pcc			;clear pcc, setting count to 0 to close out programming.
	mov eedat, pcc		;load up the eedat with our zeroed pcc.
	rcall ee_write		;write it
eewait4:
	sbic EECR,EEWE		;wait until the last write is done before we signal user
	rjmp eewait4
flashleds:			;lets signal the end of programming
	ldi temp,RED
	out PORTB,temp
	ldi tempc,0x02		;wait for about 0.5 second
	rcall wait
	ldi temp,GREEN	
	out PORTB,temp
	ldi tempc,0x02		;wait for about 0.5 second
	rcall wait
	ldi temp,BLUE	
	out PORTB,temp
	ldi tempc,0x02		;wait for about 0.5 second
	rcall wait
	ldi temp,BLACK		;Pause for dramatic effect
	out PORTB,temp
	ldi tempc,0x02		;wait for about 0.25 second
	rcall wait
	rjmp getcolors		;JUMP TO THE NEW COLOR MODE...WE'RE DONE
	
	
;-------------------------------------------------------------------------------------------------
;--------GETCOLORS routine-----------------------------------------------------------------------
;  
;            This is where the modereg gets parsed colorwash loop.
;            Solid colors are a colorwash between 3 identical colors
;--------------------------------------------------------------------------------------------------	

getcolors:				;at this point modereg holds the mode# and flash/pulse mode# 
					;we are not in programming mode.  pccptr holds EEPROM address for
					;current power cycle count value. pcc holds the current count
	ldi ZH,high(2*tablestart)	;multiplying by two here accomplishes the left shift necessary
					;we are addressing words not bytes
	ldi ZL,low(2*tablestart)
	
;*****************************************************************************
;*****************************************************************************
;THIS NEXT LINE IS FOR DEBUGGING PURPOSES ONLY..forces the mode
;                           ;this will not get written out to eeprom, though
;	ldi modereg, 0x01		;change this value to force into different modes
;*****************************************************************************
;*****************************************************************************
	
	mov temp,modereg
	andi temp,0x0F		;clear the high nibble of modereg to leave just mode#
	lsl temp		;two words per mode so we multiply by two
	lsl temp		;need to left shift to move into Z register properly
	adc ZL,temp		;add with carry offset based on mode number
	brcc nooverflow1	;all this 'overflow' nonsense is needed since the tiny12 doesn't
				;have the 'adiw' command! or the 'lpm Rd,Z+' command!
	inc ZH			;if we had a carry condition, increment the high reg
nooverflow1:
	lpm			;get first byte and put it into R0 ("tabledata")
	mov go_n_up,tabledata	;color1 fromj tabledata to go-n-up
	inc ZL			;we can increment without fear of overflow since we know lsb = zero at this point
	lpm			;once again, put byte into R0
	mov go_n_dn,tabledata	;color2 is loaded into go_n_down
	inc ZL
	brne nooverflow2	;if not zero, branch
	inc ZH			;in case there was an overflow condition
nooverflow2:
	lpm			;load byte into R0
	mov idle,tabledata	;color3 moved to idle
	inc ZL			;no worries about overflow again
	lpm			;load speed value from table to R0
	mov sklpstp,tabledata	;speed is loaded into cycles per step
	
;----------------------------------------------------------------------------------
;--------FINDMODE routine------------------------------------------------------
;
; Initially handles any flash/pulse overlay condition, then goes back a second
;time to handle the 'normal' mode 
;  This is where all the counter registers are pre-loaded/setup.
;----------------------------------------------------------------------------------
	
findmode:	
	cpi modereg,0x07	;<7 = solid color mode
	brsh nextmode		;branch if >=7 (same or higher)
	rjmp prewash		;had to do it this way because wash is too far away for 'brlo'
nextmode:
	cpi modereg,0x0A	;6 < mode $ < A = simple wash (7,8,9 = wash)
	brsh nextmode2		;>= A o to nextmode2
	rjmp prerandomize	;had to do it this way becasue randomize is too far away for 'brlo'
nextmode2:
	cpi modereg,0x40	;9 < mode# < 0x40 (0b0100 0000) = flash modes
	brlo flashset		;so, if it's <0x40, we're in flash mode
pulseset:			; >=0b0100 0000 to get here
	ldi temp,0xFF		;load 255 for start condition
	mov ontime,temp		;ONTIME = 255
	ldi temp,0x01		;preload 01 for start condition
	mov offtime,temp	;OFFTIME = 1
;	clr updown		;updown = 0x00 = ontime increasing  (ontime == 1, offtime == 255)
	ldi temp, 0xFF
	mov updown, temp	;updown = 0xFF = offtime increasing (ontime == 255, offtime == 1)
	out TCNT0,ontime
	ldi temp,0x02		;prepare to turn on timer counter (ldi TIMSK (1<<TCNT0) may work)
	out TIMSK,temp		;enable timer overflow interupt  (sbi TIMS, TCNT0 may work)
	mov temp,modereg		
	andi temp,0x70		;0b01110000, strip off all but pulse bits
	swap temp		;put the pulse mode in lower 4 bits
	cpi temp,0x04		;0b0000 0100?
	breq pulse1x		;if so, pulse 1X
	cpi temp,0x05		;0b0000 0101?
	breq pulse2x		;if so, pulse 2x
pulse3x:			;..temp's gotta be 0x06, maybe 0x06? should we check???????
	ldi flashcnt,0x01	;count once
	ldi temp,0x02			
	out TCCR0,temp		; 2 == Clock/8
	rjmp fpend		;setup, go to flash/pulse end
pulse2x:			;to get here temp == 0x04
	ldi flashcnt,0x04	;count fourtimes
	ldi temp,0x02		
	out TCCR0,temp		; 2 == Clock/8 
	rjmp fpend		;setup, go to flash/pulse end
pulse1x:			;to get here temp == 0x04
	ldi flashcnt,0x01	;count once
	ldi temp,0x03
	out TCCR0,temp		; 3 == Clock/64
	rjmp fpend		;setup, go to flash/pulse end
flashset:
	mov temp,modereg	;coming here from nextmode2
	andi temp,0x30		;AND with 0b0011 0000, strip all but flash bits
	swap temp		;put the flashpulse bits into lower nibble
	cpi temp,0x01		;flash mode 1X (mode A / 0b0000 0001)
	breq flash1x		;
	cpi temp,0x02		;flash mode 2X (mode B / 0b0000 0010)
	breq flash2x
flash3x:			;temp == 0x03 
	ldi flashcnt,0x02	;count twice
	ldi temp,0x00
	out TCNT0,temp		;PRELOAD COUNTER for full 256 counts
	ldi temp,0x02			
	out TIMSK,temp		;enable overflow interupt
	ldi temp,0x04
	out TCCR0,temp		;  0x04 == CK/256 clock source
	rjmp fpend		; done setup, jump to flash/pulse end
flash2x:			; temp == 0x02
	ldi flashcnt,0x01	;count once
	ldi temp,0x00
	out TCNT0,temp		;PRELOAD COUNTER for full 256 counts
	ldi temp,0x02
	out TIMSK,temp		;enable overflow interupt
	ldi temp,0x05
	out TCCR0,temp		; 0x05 == CK/1024 clock source
	rjmp fpend		;done with setup, go to flash/pulse end
flash1x:			;temp == 0x01
	ldi flashcnt,0x02	;count twice
	ldi temp,0x00			
	out TCNT0,temp		;full 256 counts
	ldi temp,0x02
	out TIMSK,temp		;enable overflow interupt
	ldi temp,0x05
	out TCCR0,temp		; 0x05 == CK/1024 clock source
	rjmp fpend		;done here, go to flash pulse end

fpend:
	mov temp,modereg	;****this is where flashflag should be set, eh?? 
				;****it appears to be set to 0x00 at beginning of the reset cycle
	andi temp,0x70		;strip off all but pulse bits (0b01110000)
	swap temp		;putting flash setting into lower nibble
	mov fpmode,temp		;store flash/pulse mode#
	andi modereg,0x0F	;we don't need the f/p bits anymore

;	sei			;set interrupts....this now appears in wash section

	rjmp findmode		;now into findmode...deal with basic mode portion


;------------------------------------------------------------
;-------RANDOMIZE function-----------------------------------
;
;      This little section keeps the peg from coming up
;on the same color in the color wash every time.  While
;not a perfect randomization, it is enough to appear so.
;REQUIRES:   seed <> 0  
;DESTROYS:  temp, tempa, tempb, seed
;REFERENCES:  hldr, idle, go_n_dn, go_n_up
;------------------------------------------------------------

prerandomize:			;this section added for paranoia purpose
	cpi seed, 0x00		;check for the evil 0 condition
	brne randomize		;okay, branch if not equal
	ldi seed, 0x02		;not okay, set it to 2 (why not)

randomize:			;assumes seed <>0....arriving here from nextmode
	clr temp
	clr tempa
	bst seed,0		;get bit "8"
	bld temp,0
	bst seed,2		;get bit "6"
	bld tempa,0		;now temp holds one bit and tempa the other
	eor temp,tempa		;exclusive or them together and store it in temp
	bst seed,3		;get bit "5"
	bld tempa,0		;now tempa holds next bit
	eor temp,tempa		;exclusive or it with temp
	bst seed,4		;get bit "4"
	bld tempa,0		;last bit
	eor temp,tempa		;exclusive or it with temp
	lsr temp		;move this result into the carry bit
	mov temp,seed		;move the seed into the now empty temp
	ror temp		;shift right. this also moves the carry bit into the MSB
	mov tempb,temp		;save the new 'random' number for a sec
	andi temp,0x03		;mask off all but the last two bits (seems like a waste eh?)
	brne mix		;move on if the result isn't zero.  we want to mix the colors one,
				;two or three times but not four -- that's the same as just once.
	mov seed,tempb		;use this result as the new seed and...
	rjmp randomize		;go again
mix:				;here only if seed !=0
	mov hldr,idle		;?????HAVE THESE BEEN LOADED?????
	mov idle,go_n_dn
	mov go_n_dn,go_n_up
	mov go_n_up,hldr
	dec temp		;why?  now it's just a random#-1?  
	breq prewash		;start washing when we're done mixing
	rjmp mix


;--------------------------------------------------------------------------------------------------
;-------WASH SUBROUTINE----------------------------------------------------------------------------
;  
;This is the infinite loop that does all the work
;Note: AtTiny12 have hardware stack 3 deep only! 
;
;DESTROYS: temp, tempa, count, skl1, skl2, sklcnt, stppseg, stpcnt
;--------------------------------------------------------------------------------------------------

prewash:			;last thing we do before cutting loose in enabling interrupts
	cpi fpmode, 0x00	;see if fpmode == 0
	breq wash		;if fpmode is clear, then no interrupts needed so go to wash
	sei			;else, if fpmode !=0, set interrupts and away we go

wash:
	ldi temp,STEPSPERSEG
	mov stppseg,temp	;x steps/seg, indirect load for lower registers
	mov stpcnt,stppseg	;init step counter
	mov skl2,stppseg	;indirect load
	dec skl2		;init skl2 counter to (steps/seg)-1
	mov skl1,stppseg	;indirect load
	sub skl1,skl2		;init skl1 counter to skl1 = (steps/seg)-(skl2) (=1) ***possible problem area?	
step:	
	mov sklcnt,sklpstp	;init cycle count
cycle:				;<<<<<<<<<<<<<<<<it might be bad to have an interrupt during this section?
	mov count,skl1		;load counter with skl1
	mov temp,go_n_up	;turn on ramp up color only
	mov tempa,temp		;save it into tempa
;	and temp,flashflag	;..if allowed...
	or temp,flashflag	;temp = go_n_up || 0b00000000 ***WHY???**doesnothing?***
	out portb,temp		;send it out to LEDs (0 = on with RT-0014)
cycle1:				;count is already initialized to skl1
	dec count		;this is a really tight loop for on/off pulses, @ 4MHz, 2.5 cycles = 0.6us
	tst count		;are we done with this part of the cycle?
	brne cycle1		;if <>0 keep going
	mov count,skl2		;init counter to skl2
	mov temp,go_n_dn	;turn on ramp down color only
	mov tempa,temp		;save it
;	and temp,flashflag	;..if allowed...
	or temp,flashflag	;..if allowed...  **using OR because -1- is LED-off**
	out portb,temp		;send it out to LEDs
	
cycle2:				;almost identical to cycle 1
	dec count		;a really tight loop for cycle counting
	tst count		;are we done with this part of the cycle?
	brne cycle2		;if <>0 keep going
	dec sklcnt		;decrement the cycles per step counter
	tst sklcnt		;are we done with this step?
	brne cycle		;if <>0 keep going...Yes--we're onto the next step, so. . .
	inc skl1		;increase skl1 time - ramping up
	dec skl2		;decrease skl2 time - ramping down
	tst skl2		;are we done with this seg?
	brne step		;if <>0 go to next step
nxtseg:				;skl2 == 0, onto the next segment, so. . 
	mov hldr,idle		;shuffle -- temp becomes idle color
	mov idle,go_n_dn	;ramp down color becomes idle
	mov go_n_dn,go_n_up	;ramp up color becomes ramp down
	mov go_n_up,hldr	;idle color beomes ramp up
	
	tst pcc			;is pcc = zero?  we've been on for awhile.
	breq wash		;if yes, loop forever

	clr pcc			;no, so set it to zero  **this is where the program decides that
				;this power cycle is not intended for programming.  pcc is now cleared

	mov eedat,pcc		;setup data
	mov eeadr,pccptr	;setup address
	rcall ee_write		;write it(this is the most risky moment...if we lose power now
				;very bad things can happen.
	rjmp wash		;loop forever



;-----------------------------------------------------------------------------------------------------------
;------WAIT subroutine-----------remember, AtTiny12 have hardware stack 3 deep only!
;
;    input:  tempc   
;    DESTROYS: tempa, tempb, tempc
;-----------------------------------------------------------------------------------------------------------
wait:				;long wait loop.  calling routine must load tempc with wait time before call.
				;@1.2MHz 0x04 is about one second(ish)
	dec tempc
	ldi tempb,0xe9
outer:
	dec tempb
	ldi tempa,0x00
inner:
	dec tempa
	tst tempa
	breq done1
	rjmp inner
done1:
	nop
	nop
	nop
	tst tempb
	breq done2
	rjmp outer
done2:
	tst tempc
	breq done3
	rjmp wait
done3:
	ret

;-----------------------------------------------------------------------------------------------------------
;-----EE_READ subroutine:    input->eeadr  output->eedat
;-----------------------------------------------------------------------------------------------------------
ee_read:			;assumes eeadr holds address
				;data returned in eedat
	sbic EECR,EEWE		;is last write done?
	rjmp ee_read		;no, keep waiting
	bclr 6			;clear T bit (T is our flag that interupts were enabled)
	brid ee_read_setup	;are interupts disabled? (branch if interrupts disabled)
	bset 6			;Nope, so set T bit as a flag that interupts were enabled
	cli			;disable interupts
ee_read_setup:			
	out EEAR,eeadr		;setup address
	sbi EECR,EERE		;set read strobe
	in eedat,EEDR		;get data
	brtc ee_read_end	;were interupts enabled before?
	sei			;enable interupts
ee_read_end:
	ret

;-----------------------------------------------------------------------------------------------------------
;-----EE_WRITE subroutine:    input->eeadr, eedat  
;-----------------------------------------------------------------------------------------------------------
ee_write:			;assumes eeadr holds address
				;assumes eedat holds data
	sbic EECR,EEWE		;is last write done?
	rjmp ee_write		;no, keep waiting
	bclr 6			;clear T bit (T is our flag that interupts were enabled)
	brid ee_write_setup	;are interupts disabled?  (branch if interrupts disabled)
	bset 6			;Nope, so set T bit as a flag that interupts were enabled
	cli			;disable interupts
ee_write_setup:
	out EEAR,eeadr		;setup address
	out EEDR,eedat		;setup data
	sbi EECR,EEMWE		;set master write enable
	sbi EECR,EEWE		;set write strobe
	brtc ee_write_end	;were interupts enabled before?
	sei			;enable interupts
ee_write_end:
	ret

;-----------------------------------------------------------------------------------------------------------
;----- MODE TABLE DATA--------------------------------------------------------------------------------------  
;-----------------------------------------------------------------------------------------------------------
tablestart:
.db	WHITE, WHITE, WHITE, MED	;mode 0x00
.db	RED, RED, RED, MED		;mode 0x01
.db	YELLOW, YELLOW, YELLOW, MED	;mode 0x02
.db	GREEN, GREEN, GREEN, MED	;mode 0x03
.db	CYAN, CYAN, CYAN, MED		;mode 0x04
.db	BLUE, BLUE, BLUE, MED		;mode 0x05
.db	VIOLET, VIOLET, VIOLET, MED	;mode 0x06
.db	BLUE, GREEN, RED, SLOW		;mode 0x07
.db	BLUE, GREEN, RED, MED		;mode 0x08
.db	BLUE, GREEN, RED, FAST		;mode 0x09
	
tableend:

;-------------------------------------------------------------------------------------------------------------
;-------------------------------------end---------------------------------------------------------------------
;-------------------------------------------------------------------------------------------------------------

;##############################################################################################
;###############STATUS AND UPDATA##############################################################
;##############################################################################################
;
;###STATUS###
;BOARD				PEG CODE REVISION PROGRAMMED
;Red's				03-03-04		1.2.5.4
;Eileen & Eljas		03-03-04		1.2.5.4
;Shannon & Larinda	03-03-04		1.2.5.4
;Various "Car" Pegs 03-03-04		1.2.5.4
;
;
;###UPDATES###
;1.2.5.3	added support for LSR/LSR (divide by 4) should it be needed.  Testing on pegs
;to date (programming 3 at a time) show that this off-by-one error is rare.  Also changed
;programming count to an initial 0x10 and added the ".equ" of MAXCOUNT to be calculated 
;
;
;################################################################################################