;-------------------------------------------------------------------------------
;
;      THE WEDDING BOARD:   PROGRAMMER BOARD CODE
;         initial code by:  Shannon Gomes    version:  1.1.2
;         updated by:  Red Byer              last update:  03/01/04
;	copyright 2004    all rights reserved   www.redstoyland.com
;
;     DESCRIPTION:   This code drives an Atmel ATTiny12.  The code
;  'programs' pegs into one of 16 modees by simply power cycling
;   the peg 4X(PMCOUNT + MODE#) times.  The 4X offset helps remove
;   off-by-one errors and provides additional immunity to power
;   bouncing upon peg insertion.
;       PMCOUNT is a fixed offset that it takes to put the 
;peg into a programming mode. This is intended to make peg immune to bounces
;and minor noise.  
;       MODE# is 0->15 and is the mode number.
;      
;As of PegCode 1.2.5 on 2/24/04:
;In order for a peg to recognize a power cycle:
;     (1) The peg must be left on a MINIMUM of 50ms (padded, may be as low as 10ms)
;			 ^^^75ms on-time has been tested and proven to work.
;			 ^^^50ms is the current prefferred time.
;			 ^^^27ms on-time has been tested and appears to work.
;	  (1b) DELAYSCALER is used to set the general wait loop scale
;     (2) The peg must be turned off BEFORE 900ms (padded, may be 1000ms)
;
;The Atmel ignores Power On Resets.  When waking up from
;a reset...if non-PowerOnReset, then program polls the
;4 bit input (active-low), and sends the result onto the peg.
;
;   INPUTS:   PB5:Reset Pin  (i.e the program button)
;             PB0->PB3:  Input from rotary switch  (binary encoded, 4bit)
;				Input is inversed (0x00 = all switches open)
;				so inputs PB0::PB3 must have pullups enabled
;       
;   OUTPUT:   PB4:  Output to p-ch FET & pegs
;
;
;  NOTE:  the switch has to be in the zero position when ISP programming.
;-----------------------------------------------------------------------------------

.include "tn12def.inc"

.equ	PMCOUNT		=0x0F	;number of power cycles (minus 1 compared to the peg code) 
							;it takes to put peg into programming mode
							;cycling the peg PMCOUNT times will set the peg to MODE 0

.equ	DELAYSCALER  = 0x27	;see wait subroutine for timing values 
;   AT 1.0 MHZ with tempc==0x01, tempb==0x27,  tempa==0x00   approx 0.050s 

.equ	DEBOUNCEDELAY = 0x06 ;for prepping wait loop (tempc)
.equ 	PROGDELAY	  = 0x0F ;short off delay to stabilize pins, signal start of power cycles
.equ	ONTIME		  = 0x01 ;wait loop on time  (tempc)
.equ 	OFFTIME		  = 0x01 ;wait loop off time (tempc)

.def	temp	=R16
.def	tempa	=R17
.def	tempb	=R18
.def	tempc	=R19
.def	tempout =R20
.def	status	=R21
	

;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 reset	;Timer0 overflow
	rjmp reset	;EE_rdy
	rjmp reset	;ana_comp


reset:

;-----short forced wait----------------------------------------------------
    ldi tempc, DEBOUNCEDELAY	;~.3s, allow power to stabilize
	rcall wait		;
;---------------------------------------------------------------------------

	
	ldi tempout,0b00010000	;prepare to setup data direction registor for portB.
	out DDRB,tempout		;make PB0,1,2,3,5,6,7 as INPUTS, PB4 as output
	ldi tempout,0b11111111	;Prepare to set outputs for PortB
	out PORTB,tempout		;Enable pullups on PB0,1,2,3,5,6,7. 
							;Turn PB4 OFF (active low)

;-----STEP ONE:  FIGURE OUT THE SOURCE OF THE RESET--------------------------
	cli						;clear interrupts
	in status,MCUSR			;get MCU status register into status
	mov temp, status		;copy status into temp
	clt						;clear T register
	bld temp, PORF			;get ready to clear last 2 bits of MCUSR
	bld temp, EXTRF			
	out MCUSR, temp			;This clears the PORF and EXTRF in MCUSR



	ldi tempout,0b00010000	;Paranoia: prepare to setup data direction registor for portB.
	out DDRB,tempout		;make PB0,1,2,3,5,6,7 as INPUTS, PB4 as output


;-----STEP TWO:  BRANCH ACCORDING TO THE SOURCE OF THE RESET-------------------
							;check power on reset flag FIRST 
							;(or this doesn't work right)
	bst status, PORF		;was it a power on reset??? <MUST check this first>
	brts not_button			;PowerONRest means NOT the button.	

							;check external reset flag second
	bst status, EXTRF		;Bit Store the ExternalResetFlag bit from status to "T" flag
	brts button_press		;branch if t-flag is set (EXTRF = button press)
;	rjmp reset			 	;for debuggin..should never ever get here..normally disabled


;-----POWER ON RESET HANDLER SECTION--------------------------------------------							
not_button:					;NOT A BUTTON PRESS, (yes PORF)
	clt
	ldi tempout,0b11101111	;Prepare to set outputs for PortB
	out PORTB,tempout		;Enable pullups on PB0,1,2,3,5,6,7. 
							;Turn PB4 on (active low)
forever:					;fall thru and
	rjmp forever 			;wait for the external reset



;-----EXTERNAL RESET PIN HANDLER SECTION--------------------------------------------
							;BUTTON PRESS RESET,  (not PORF)
button_press:				;Not a power on reset, so...
	clt
	ldi tempout,0b11111111	;Prepare to set outputs for PortB
	out PORTB,tempout		;Enable pullups on PB0,1,2,3,5,6,7. 
							;Turn PB4 OFF (active low) 
							;this tells user we're about to program
    ldi tempc,PROGDELAY		;prep the wait loop for short wait 
	rcall wait				;stay off for a short bit, signal start of programming

	nop						;allow pins to stabilize in off position
	nop
	nop
	nop
	nop
	nop
	in temp,PINB		;load status of pins
	com temp			;invert the inputs from the 4bit rot. switch
	andi temp,0x0F		;mask off all but PB0::PB3 
	ldi tempa,PMCOUNT	;**ADD IN OUR PMCOUNT OFFSET**
	add temp,tempa		;add offset to input value and put into temp
						;TRANSLATION:  we have to start with a "null period"
						;before entering "program state" to keep bounces
						;and pegging from avoid "program state" being
						;entered mistakenly.

;----------------------------------------------------------------------
;----------------------------------------------------------------------
;The following lines provide noise immunity.  The corresponding
;lsr/lsr lines (no sub needed) must be in the peg code in the
;appropriate location (prior to parsing PCC)
;  The two lsl/lsl effectively multiply by 4 (multiplying by 3 
;and targeting for the middle would have been more logical
;for off-by-one in either direction, but 4 is easier).
;Adding one to the value places the count in the middle of the
;range of 4.   
;    At the peg end, a simple lsr/lsr is all that is needed.
;
;NOTE:  this will wear out the EEPROM 4 times faster, use only
;if needed to program 3 pegs at the same time.
;-----------------------------------------------------------------------

;	inc temp			;add 1 to temp.
;	lsl temp			;multiply temp x 2
;	lsl temp			;multiply temp x 2  (effectively multiplied by 4)
						;REASON:  The multiplication is to provide immunity
						;to off-by-one errors.  A multiply by 3 and targeting for
						;the middle would be enough, but it works just as well
						;to multiply by 4 with two left-shifts and then have the
						;peg simply conduct two right-shifts to divde by 4
;-----------------------------------------------------------------------
;-----------------------------------------------------------------------


;----FINALLY:  PROGRAMMING THE TARGET PEG------------------------------
					;CYCLE THE POWER to program the peg
cycle:
	cbi PORTB,4			;*ON*   PB4=LOW (activelow turns peg ON)
	ldi tempc,ONTIME	;prep the wait loop for about 250ms
	rcall wait			;enter wait loop

	sbi PORTB,4			;*OFF*  PB4=HIGH (activelow: high turns peg OFF)
	ldi tempc,OFFTIME	;prep the wait loop for short duration
	rcall wait			;enter the wait loop

	dec temp		;**cycle loops**
	brne cycle		;IF not zero...keep cycling

	cbi PORTB,4		;ELSE IF ZERO, PB4=LOW (activelow = ON)    
end:
	rjmp end		;loopity loop



;-----------------------------------------------------------------------------
;-------------WAIT SUBROUTINE-------------------------------------------------
;
;Using internal RC oscillator on tiny12 = 1.2MHz
;With this waitloop, if tempc = 4 it takes approx 960ms
;to get through
;   REQUIRES:  tempc  must be loaded prior to call
;   DESTROYS:  tempa, tempb, tempc
;
;   AT 1.2 MHz with tempc==0x04, tempb==0xE9,  tempa==0x00   approx 1 second
;
;   AT 1.0 MHZ with tempc==0x01, tempb==0x4E,  tempa==0x00   approx 0.100s  (100ms)   
;
;   AT 1.0 MHZ with tempc==0x01, tempb==0x3a,  tempa==0x00   approx 0.075s  
;
;   AT 1.0 MHZ with tempc==0x01, tempb==0x27,  tempa==0x00   approx 0.050s 
;
;   AT 1.0 MHZ with tempc==0x01, tempb==0x15,  tempa==0x00   approx 0.027s  
;------------------------------------------------------------------------------
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,DELAYSCALER
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


;############################################################################
;          CODE UPDATES AND STATUS
;
;
;1.1.2			Original Shipping Version
;
;