*********************************************
* MC68HC705C8 Example Development Project   *
*   A Home Thermostat with indoor/outdoor   *
*   temperature and time-of-day             *
*                                           *
*   This example uses an LCD display, a 4x4 *
*   keypad, a piezo beeper, and an MC145041 *
*   serial A/D converter.                   *
*                                           *
*   Software is configured in a real-time   *
*   loop and demonstrates timing techniques *
*   and program modularity principles.      *
*                                           *
*   The project is complete enough to show  *
*   the development process but is not      *
*   intended to be a finished product.      *
*********************************************

* Register Equates
PORTA	EQU	$00	LCD display data
PORTB	EQU	$01	Keypad Row4,3,2,1;Col1,2,3,4
PORTC	EQU	$02	Fan*,Heat*,Cool*,Beep;ADen*,E,RS,R/W
PORTD	EQU	$03	in,-,SS*,SCK;MOSI,MISO,TxD,RxD
DDRA	EQU	$04	Data direction, Port A (all output)
DDRB	EQU	$05	Data direction, Port B (7-4in,3-0out)
DDRC	EQU	$06	Data direction, Port C (all output)
SPCR	EQU	$0A	SPIE,SPE,-,MSTR;CPOL,CPHA,SPR1,SPR0
SPSR	EQU	$0B	SPIF,WCOL,-,MODF;-,-,-,-
SPDR	EQU	$0C	SPI Data
BAUD	EQU	$0D	-,-,SCP1,SCP0;-,SCR2,SCR1,SCR0
SCCR1	EQU	$0E	R8,T8,-,M;WAKE,-,-,-
SCCR2	EQU	$0F	TIE,TCIE,RIE,ILIE;TE,RE,RWU,SBK
SCSR	EQU	$10	TDRE,TC,RDRF,IDLE;OR,NF,FE,-
SCDR	EQU	$11	SCI Data
RDR	EQU	$11	SCI Receive Data (same as SCDR)
TDR	EQU	$11	SCI Transmit Data (same as SCDR)
TCR	EQU	$12	ICIE,OCIE,TOIE,0;0,0,IEGE,OLVL
TSR	EQU	$13	ICF,OCF,TOF,0; 0,0,0,0
ICAP	EQU	$14	Input Capture Reg (Hi-$14, Lo-$15)
OCMP	EQU	$16	Output Compare Reg (Hi-$16, Lo-$17)
TCNT	EQU	$18	Timer Count Reg (Hi-$18, Lo-$19)
ALTCNT	EQU	$1A	Alternate Count Reg (Hi-$1A, Lo-$1B)

* RAM Equates
	ORG	$A0
* Using 'A6 to debug and monitor uses lower RAM

TEMPA	RMB	1	One byte temp storage location
TEMPX	RMB	1	One byte temp storage location
TIC	RMB	1	50mS Tics 00-19 20 Tics = 1 Sec
SEC	RMB	1	Current Time Seconds 00-59

BCDEQ	RMB	1	BCD equivalent of ENTRY
* it is easier to roll in new digits into a BCD buffer than binary.

* Next 7 entries are accessed by indexed addressing using a 1 byte
* offset from ENTRY. The offset is MODE (in X) and the value at
* ENTRY,X is the value that is subject to change in the selected mode.
ENTRY	RMB	1	Binary value being entered by user
HR	RMB	1	Current Time Hour 1-12
MIN	RMB	1	Current Time Minute 00-59
AMPM	RMB	1	Current Time AM=0, PM=1
DAY	RMB	1	Day of Wk 1=Sun...7=Sat
HVACM	RMB	1	HVAC Equipment Mode
* Modes 0 - Off
*	1 - Heat
*	2 - Cool 
*	3 - Fan Only

GOAL	RMB	1	Goal temp. setting (+)
* End of values accessed by offset from ENTRY

INTMP	RMB	1	Current Indoor Temperature (+)
OUTMP	RMB	1	Current Outdoor Temperature (+/-)

ASC100	RMB	1	ASCII hundreds digit (-,<sp>,1, or 2)
ASC10	RMB	1	ASCII tens digit (0 thru 9)
ASC1	RMB	1	ASCII ones digit (0 thru 9)

MODE	RMB	1	Current Mode (for user interfce)
* Modes 0 - Inactive; display shows current time/temp/etc.
*	1 - Set Time HR
*	2 - Set Time MIN 
*	3 - Set Time AM/PM
*	4 - Set Time DAY
*	5 - Set HVAC Mode - Off, Heat, Cool, Fan Only
*	6 - Set Target Temperature

HVACON	RMB	1	0=off, 1=on (running now)
KEYVAL	RMB	1	Keypad key (ASCII) or debounce state
BEEPM	RMB	1	Beeper request
* 2=>single 100mS beep, 8=>double beep, 26=>1 sec beep

ACTIMR	RMB	1	Activity timer
* Set=60 sec on key, decrement 1/sec, if 0 mode reverts to 0

ENTFLG	RMB	1	New entry flag, 0-new 1-old
* Entries default to current value when new. If user enters
* a single digit the tens digit is cleared. If user enters
* more digits they shift in from rt. so new digit is 1's, old
* 1's becomes 10's, and old 10's falls off left (lost).

	ORG	$0100	Program will start at $0100
* $0100 is the start of EPROM in the '705C8

* Initialization will be done at reset and upon detection of some errors

INIT	RSP		Reset stack pointer to $FF

* Set Port data patterns and directions
	LDA	#$E8	Fan*,Heat*,Cool*,Beep;ADen*,E,RS,R/W
	STA	PORTC	Initial values for Thermostat controls
	CLRA		Row3,2,1,0;Col1,2,3,4
	STA	PORTB	All cols initially off
	DECA		to $FF
	STA	DDRA	Port A all outputs
	STA	DDRC	Port C all outputs
	LDA	#$0F	Rows=in, Cols=outs
	STA	DDRB	Port B half ins, half outs

* Set up SPI to talk to ext serial A/D converter MC145041

**
** CAUTION !! S3,S4,& S5 on PGMR Board can conflict with SPI
**
WAITSW	LDA	PORTD	Wait 'till S3-on, S4&S5-off
	AND	#$38	only care about S3, S4, and S5
	CMP	#$20	S3-on, S4&S5-off ?
	BNE	WAITSW	If not wait till they are
* Previous 4 lines only needed for development on PGMR board

	LDA	#$50	SPIE,SPE,-,MSTR;CPOL,CPHA,SPR1,SPR0
	STA	SPCR	SPI on as Master, 2S norm low clock

* SCI not used in this application
* Timer output compare used to time 50mS loop
	CLRA		ICIE,OCIE,TOIE,0;0,0,IEGE,OLVL
	STA	TCR	no timer interrupts or pins used

* LCD display peripheral needs to be initialized
	LDA	#$01
	JSR	WCTRL	Clear
	LDA	#$02
	JSR	WCTRL	Home
	LDA	#$38
	JSR	WCTRL	Function Set- 8-bit,2-line,5X7
	LDA	#$0C
	JSR	WCTRL	Display on, Cursor off
	LDA	#$06
	JSR	WCTRL	Entry mode- Inc addr, no shift


* Set time to 12:00 AM SUN
	CLR	TIC	Init 50mS counter
	CLR	SEC	Init seconds to 0
	LDA	#12	Hr=12
	STA	HR
	CLR	MIN	Min=00
	CLR	AMPM	AM (AMPM=0)
	LDA	#1	Sun-1,Sat-7
	STA	DAY	Day=Sunday

	CLR	MODE	Set user interface to inactive
	CLR	KEYVAL	Say no key closed
	CLR	BEEPM	Set beeper request to off
	CLR	HVACON	Indicate HVAC Equip not running now
	CLR	HVACM	Set HVAC Equip mode to off
	LDA	#72
	STA	GOAL	Set default goal temp to 72F

** END of INITIALIZATION ******************************************

*********************************************************
* MAIN - Beginning of main program loop                 *
*	Loop is executed once every 50mS (exactly)      *
*	A pass through all major task routines takes    *
*	less than 50mS and then time is wasted until    *
*	the output compare flag gets set (every 50mS).  *
*	When an output compare triggers, the flag is    *
*	cleared & 12500 is added to the compare reg     *
*	so the next trigger will occur in exactly 50mS  *
*	(12500*4S/cnt = 50mS).                         *
*                                                       *
*	The variable TIC keeps track of 50mS periods    *
*	when TIC increments from 19 to 20 it is cleared *
*	to 0 and seconds are incremented.               *
*                                                       *
*	The keypad is checked every 50mS pass and a new *
*	closure or release is not acted upon until the  *
*	pass after it is first seen.  This acts as a    *
*	switch debounce.                                *
*                                                       *
*	The display is updated only when seconds change.*
*	Display call is at bottom of main loop so any   *
*       change caused by a key is reflected in the      *
*       display update.                                 *
*                                                       *
*	Temperature readings are only taken once/sec    *
*                                                       *
*********************************************************
MAIN	BRCLR	6,TSR,MAIN Loop here till OCF flag set
	LDA	OCMP+1	Low byte of OC register
	ADD	#$D4	Low half of 12500
	STA	TEMPA	Save till high half calculated
	LDA	OCMP	High byte of OC register
	ADC	#$30	High half of 12500 (+carry)
	STA	OCMP	Update OC reg
	LDA	TEMPA	Get low half of updated value
	STA	OCMP+1	Update low half of OC reg
* OC now = old OC + 12500, and OCF flag is clear
	LDA	TIC	Get current TIC value
	INCA		TIC=TIC+1
	STA	TIC	Update TIC
	CMP	#20	20th TIC ?
	BLO	ARNC1	If not, skip next clear
	CLR	TIC	Clear TIC on 20th
ARNC1	EQU	*
* End of synchronization to 50mS TIC; Run main tasks and
*  branch back to main within 50mS.  Sync OK as long as
*  no 2 consecutive passes take more than 100mS.

	JSR	TIME	Update time-of-day & day-of-week

	JSR	KYPAD	Check/service keypad
	
	JSR	BEEP	Update Beeper

	JSR	USER	User Interface to set time, temp, etc.

	JSR	A2D	Check Temp Sensors

	JSR	HVAC	Update Heat/Air Cond Outputs

	JSR	LCD	Update LCD display
	
	BRA	MAIN	Back to Top & wait for next TIC

** END of Main Loop ***********************************************

*********************************************************
* TIME - Update Time-of-day & Day-of-week               *
*  If TIC not = 0, just skip whole routine              *
*	When SEC rolls 59->0, inc MIN                   *
*	When MIN rolls 59->0, inc HR                    *
*	When HR rolls 11->12, change AMPM 1->0 or 0->1  *
*	When AMPM chgs 1->0, inc DAY                    *
*	When DAY rolls 7->8, set to 1 (Sun)             *
*********************************************************
TIME	EQU	*	Update Time-of-day & Day-of-week
	TST	TIC	Check for TIC=zero
	BNE	XTIME	If not; just exit
	INC	SEC	SEC=SEC+1
	LDA	#60
	CMP	SEC	Did SEC -> 60 ?
	BNE	XTIME	If not; just exit
	CLR	SEC	Seconds rollover
	INC	MIN	MIN=MIN+1
	CMP	MIN	A still 60; MIN=60 ?
	BNE	XTIME	If not; just exit
	CLR	MIN	Minutes rollover
	INC	HR	HR=HR+1
	LDA	HR	For comparisons
	CMP	#13	HR=13 ?
	BNE	ARNS1	If not; skip
	LDA	#1
	STA	HR	Set HR=1
	BRA	XTIME	Exit
ARNS1	CMP	#12	HR=12 ?
	BNE	XTIME	If not; just exit
	LDA	AMPM
	EOR	#%00000001  Invert AM/PM bit
	STA	AMPM	0=AM, 1=PM
	BNE	XTIME	If not AM now; just exit
	INC	DAY	DAY=DAY+1
	LDA	DAY
	CMP	#8	Day rollover ?
	BNE	XTIME	If not; just exit
	LDA	#1
	STA	DAY	Set Day to 1 (SUN)
XTIME	RTS	** RETURN from TIME **

*********************************************************
* KYPAD - Check for & decode keys                       *
*	KEYVAL indicates ASCII equivalent of key or     *
*	debounce status as follows                      *
*  $00 - no key pressed, look for any closure           *
*  $01 - key closed 50mS ago (debounce), decode now     *
*  $20-$7F - key found, debounced, & decoded (not seen) *
*  $FE - key recognized by some task, wait for release  *
*  $FF - key released 50mS ago (debounce release)       *
*********************************************************
KYPAD	EQU	*	Check for & decode keys
	LDA	KEYVAL	KEYVAL indicates what to do
	BNE	CHK401	If not 0; Check for $01
	LDA	#$0F
	STA	PORTB	Turn on all cols
	LDA	PORTB	Reads rows in upper 4
	AND	#$F0	Mask away cols
	BEQ	XKYPAD	Exit if no key
	INC	KEYVAL	To $01
	BRA	XKYPAD	Exit, key will be decoded in 50mS
CHK401	CMP	#$01	KEYVAL=$01 ?
	BNE	CHK4FE	If not 0; Check for $FE
	LDX	#30	Pointer to last pair in KYTBL
KYLOOP	LDA	KYTBL,X	Get row/col pattern
	STA	PORTB	Drive cols
	CMP	PORTB	Check for row & col match
	BEQ	FOUND	If =; key found
	DECX		Point to next pair of entries
	DECX		in KYTBL
	BPL	KYLOOP	Loop if more entries
	CLR	KEYVAL	No key found; set KEYVAL=0
FOUND	LDA	KYTBL+1,X  Get key equiv from table
	STA	KEYVAL	$20  KEYVAL  $7F
	LDA	#2
	STA	BEEPM	Request beep as feedback
	BRA	XKYPAD	Exit
CHK4FE	CMP	#$FE	KEYVAL=$FE ?
	BNE	CHK4FF	If not check for $FF
	LDA	#$0F
	STA	PORTB	Turn on all cols
	LDA	PORTB	Reads rows in upper 4
	AND	#$F0	Mask away cols
	BNE	XKYPAD	Exit if key still closed
	LDA	#$FF
	STA	KEYVAL	Set KEYVAL=$FF
	BRA	XKYPAD	& Exit
CHK4FF	CMP	#$FF	KEYVAL=$FF ?
	BNE	XKYPAD	If not; exit
	CLR	KEYVAL	Set KEYVAL=$00
XKYPAD	RTS	** RETURN from KYPAD **

*********************************************************
* BEEP - Update audible beeper                          *
*	Single 100mS beep on key closure (feedback)     *
*	Beep (100mS/on, 200off, 100on) entry accepted   *
*	Beep 1 second to indicate entry error           *
*********************************************************
BEEP	EQU	*	Update audible beep
	LDA	BEEPM	BEEPM indicates what to do
	BNE	ACTIV	Branch if beeper active
	BCLR	4,PORTC	Turn off beeper
	BRA	XBEEP	& Exit

ACTIV	DEC	BEEPM	Times beeps
* Accumulator has undecremented version of BEEPM
* Beeper should be on unless BEEPM is between 3 and 6
	CMP	#3
	BLO	BPRON	If <3 turn beeper on
	CMP	#6
	BHI	BPRON	If >6 turn beeper on
	BCLR	4,PORTC	Turn beeper off
	BRA	XBEEP	& Exit
BPRON	BSET	4,PORTC	Turn beeper on
XBEEP	RTS	** RETURN from BEEP **

*********************************************************
* USER - User Interface to set time, temp, etc.         *
*  Variable named MODE identifies current user function *
*  0 - Inactive; display shows current time/temp/etc.   *
*  1 - Set Time HR                                      *
*  2 - Set Time MIN                                     *
*  3 - Set Time AM/PM                                   *
*  4 - Set Time DAY                                     *
*  5 - Set HVAC Mode - Off, Heat, Cool, Fan Only        *
*  6 - Set Target Temperature                           *
*	MODE reverts to 0-inactive if no keys for 1 min *
*	To activate modes press A until desired value   *
*       to be changed is blinking. Next enter desired   *
*       setting numbers and press enter (!).            *
* Current program does not use <,>,B, or C keys.        *
*********************************************************
USER	EQU	*	User Interface to set time, temp, etc.
	TST	SEC	Seconds = 0 ?
	BNE	CHKEY	If not, skip ACTIMR
	DEC	ACTIMR	Decrement activity timer
	BNE	ARMCLR	No activity for 1 minute
	CLR	MODE	Force to inactive
ARMCLR	BPL	CHKEY	Did ACTIMR roll neg ?
	CLR	ACTIMR	If so clear it
CHKEY	LDA	KEYVAL	Get key value
	CMP	#$20	Ignore key if <$20 or >$7F
	BLO	XUSER2	Exit if <$20
	CMP	#$7F	? > $7F is invalid
	BLS	VALKEY	Valid
XUSER2	JMP	XUSER	May be too far to branch
* Valid key has been detected
VALKEY	LDX	#60	60 seconds
	STX	ACTIMR	Set to timeout in 1 min.
	CMP	#'A	KEYVAL = A ?
	BEQ	NXTMOD	Advance to next setting
	CMP	#$30	ASCII 0
	BLO	TRYENT	Branch if < 0
	CMP	#$39	ASCII 9
	BHI	TRYENT	BRANCH IF > 9
	TST	ENTFLG	First # in entry ?
	BNE	NOFST	Skip if not
	CLR	ENTRY	Clear ENTRY
	CLR	BCDEQ	& its BCD equivalent
	INC	ENTFLG	0->1 (NO LONGER 1st)
NOFST	ASLA		Get hex 0-9 in left nibble
	ASLA
	ASLA
	ASLA		nnnn 0000 & BCDEQ = xxxx yyyy
	ASLA		Roll new digit into BCD
	ROL	BCDEQ	Equiv of ENTRY
	ASLA		With 4 double byte
	ROL	BCDEQ	left shifts
	ASLA
	ROL	BCDEQ
	ASLA
	ROL	BCDEQ	BCDEQ now = yyyy nnnn
	LDA	BCDEQ
	AND	#$0F	Mask off 10's
	STA	ENTRY	Temp save 1's
	LDA	BCDEQ	Get BCD again
	LSRA		Right justify 10's
	LSRA
	LSRA
	LSRA
	LDX	#10
	MUL		A <- 10 * BCD 10's
	ADD	ENTRY	Add in ones
	STA	ENTRY	Now binary equiv of BCDEQ
	BRA	KEYFE	Acknowledge key and leave
TRYENT	CMP	#'!	Enter key ?
	BNE	KEYFE	If not, Ack key & leave
	JSR	CHKPNT	Check for legal entry
* On return N-bit indicates legal (Positive) & X points
* at applicable value to be changed (HR,MIN,AMPM,DAY etc.)
	BPL	LEGENT	Branch if legal
	LDA	ENTRY,X	Get current value
	STA	ENTRY	Revert to current (legal) value
	CLR	ENTFLG	So next # treated as first
	LDA	#26	26 * 50mS = 1.3 sec
	STA	BEEPM	Beep 1S/200mS-off/100mS-on
	BRA	KEYFE	Acknowledge entry attempt
LEGENT	STA	ENTRY,X	Update value being set
	LDA	#8	100mS-on/200mS-off/100mS-on
	STA	BEEPM	Double beep
NXTMOD	INC	MODE	Adv to next setting
	LDA	MODE	Check for past 6
	CMP	#7	<7?
	BLO	NOCLR	If OK skip clear
	CLR	MODE	Rollover to 0
NOCLR	LDX	MODE	Use as index to current
	LDA	ENTRY,X	Get current value of entry
	STA	ENTRY	Use current as default setting
	CLR	ENTFLG	Indicate next # is 1st
KEYFE	LDA	#$FE
	STA	KEYVAL	Acknowledge key closures
XUSER	RTS	** RETURN from USER **

***
* CHKPNT - a utility subroutine used by USER routine
*          Checks for entry within legal limits which
*          depend on value being changed. HR=1-12, MIN=0-59
*          and so on. If legal, N bit will be 0 (Positive).
*          On return A has enrty value (or $FF if illegal)
*          and X points at value to be changed. ENTRY,X
*          may be used to access value to be changed.
***
CHKPNT	LDA	ENTRY	For compares to chk limits
	LDX	MODE	For compares & as return pointer
	CPX	#1	Set HR ?
	BNE	TRI2	If not
	CMP	#1	<1?
	BLO	TRI2	illegal (will ripple through)
	CMP	#12	1-12 ?
	BLS	OKENT	Valid HR entry
TRI2	CPX	#2	Set MIN ?
	BNE	TRI3	If not
	TSTA		<0?
	BMI	TRI3	illegal (will ripple through)
	CMP	#59	0-59 ?
	BLS	OKENT	Valid MIN entry
TRI3	CPX	#3	Set AMPM ?
	BNE	TRI4	If not
	TSTA		<0?
	BMI	TRI4	illegal (will ripple through)
	CMP	#1	0 or 1 ?
	BLS	OKENT	Valid AMPM entry
TRI4	CPX	#4	Set DAY ?
	BNE	TRI5	If not
	CMP	#1	<1?
	BLO	TRI5	illegal (will ripple through)
	CMP	#7	1-7 ?
	BLS	OKENT	Valid DAY entry
TRI5	CPX	#5	Set HVAC Mode ?
	BNE	TRI6	If not
	TSTA		<0?
	BMI	TRI6	illegal (will ripple through)
	CMP	#3	0-3 ?
	BLS	OKENT	Valid HVACM entry
TRI6	CPX	#6	Set GOAL Temp ?
	BNE	BADENT	Illegal entry
	CMP	#50	<50F ?
	BLO	BADENT	illegal
	CMP	#99	< or = 99F ?
	BLS	OKENT	Valid goal temp
BADENT	LDA	#$FF	A negative value to set N
OKENT	STA	ENTRY	Sets/or clears N
	RTS		** Return from CHKPNT **
*** !!! There is more to this exit than is obvious. X=MODE
* so X points at entry to be changed HR,MIN,AMPM,DAY,HVACM,GOAL
* A has entry (or $FF if it was illegal). After return N-bit
* of CCR indicates whether entry was OK or not.
* STA ENTRY was used to make N bit reflect sign of ENRTY
* rather than the result of a compare.
***


*********************************************************
* A2D - Check temp. sensors (via SPI and MC145041)      *
*	If TIC = 0, send addr 0 ignore return data      *
*	If TIC = 1, send addr 1 return data is ch.0 val *
*	If TIC = 2, send addr 2 return data is ch.1 val *
*	If TIC > 2, skip A2D routine                    *
*  To compensate for sensor & op-amp offset, A/D result *
*   will be modified by subtracting an offset constant  *
*********************************************************
A2D	EQU	*	Check temp. sensors
	LDA 	TIC	If Tic = 0, 1, or 2 write to SPI
	CMP	#2
	BHI	XA2D	If Tic > 2; Exit
	ASLA		Move TIC # 0-2 to upper nibble
	ASLA
	ASLA
	ASLA		4 bit left shift
	TST	SPSR	Reads SPIF (part of SPIF clear)
	BCLR	3,PORTC	Drive low true SA/D CE* to 0
	STA	SPDR	Initiates a transfer
* Requests conversion of next channel and returns data
*   from previous channel Ch.0=Indoor Ch.1=Outdoor
SPIFLP	BRCLR	7,SPSR,SPIFLP  Wait for SPI Xfer complete
	BSET	3,PORTC	Drive low true SA/D CE* to 1
	LDA	TIC	If 0-Exit, 1 or 2 Read A/D data
	BEQ	XA2D	0 so exit
	LDA	SPDR	Get A/D data
	BRSET	1,TIC,ADCH1  If Tic=2, data is Ch.1
	SUB	OFF0	A/D Ch.0; subtract offset
	STA	INTMP	Update indoor temperature
	BRA	XA2D	& Exit
ADCH1	SUB	OFF1	A/D Ch.1; subtract offset
	STA	OUTMP	Update outdoor temperature
XA2D	RTS	** RETURN from A2D **

*********************************************************
* HVAC - Update Fan, Heat, and Cool outputs             *
*   Low-true outputs will be buffered to drive 24VAC    *
*    relay coils in HVAC equipment.                     *
*   Heat and Cool requests should not permit short-     *
*    cycle (ie a min delay is required between changes) *
*    Once Heat or Cool requested, do not turn off for   *
*    at least 30 sec.  Also enforce 30 sec. minimum     *
*    off time to restart.                               *
*   Allow 1 around target temp as hysteresis.         *
* HVACM = 0 - Off, 1 - Heat, 2 - Cool, 3 - Fan Only     *
*********************************************************
HVAC	EQU	*	Update Fan, Heat, and Cool outputs
	LDA	SEC	Exit unless sec = 0 or 30
	BEQ	DOHVAC	0 so do HVAC
	CMP	#30	
	BNE	XHVAC	Exit if not 0 or 30
DOHVAC	LDA	HVACM	0-off, 1-heat, 2-cool, 3-fan
	BNE	HM1Q	If not 0 go see if 1
	LDA	PORTC	Fan*,Heat*,Cool*,Beep;ADen*,E,RS,R/W
	ORA	#$E0	Set fan, heat, cool all high (off)
	STA	PORTC	Update port
	BRA	XHVAC	& Exit
HM1Q	CMP	#1	Check for mode 1 - heat
	BNE	HM2Q	If not go see if 2
	BSET	5,PORTC	Turn off cool output
	LDA	GOAL	Get target temp
	BRSET	6,PORTC,HONQ  If not; see if it should be
* Heat on; turn off when indoor temp > goal + 1
	INCA		Goal+1 for hysteresis
	CMP	INTMP	GOAL+1 < INTMP ? Turn off ?
	BHS	XHVAC	NO; just leave
	BSET	6,PORTC	Turn off heat
	BSET	7,PORTC	Turn off fan
	CLR	HVACON	Turn off flag to indicate off
	BRA	XHVAC	Then leave
* Heat off; turn on when indoor temp < goal - 1
HONQ	DECA		Goal-1 for hysteresis
	CMP	INTMP	GOAL-1 > INTMP ? Turn on ?
	BLS	XHVAC	NO; just leave
	BCLR	7,PORTC	Turn on fan
	BCLR	6,PORTC	Turn on heat
	LDA	#1
	STA	HVACON	Set flag to indicate on
	BRA	XHVAC	Then leave
HM2Q	CMP	#2	Check for mode 2 - cool
	BEQ	HCOOL	Branch if cool mode 2
	BCLR	7,PORTC	Turn on fan
	BSET	6,PORTC	Turn off heat
	BSET	5,PORTC	Turn off cool
	BRA	XHVAC	Then leave
HCOOL	BSET	6,PORTC	Turn off heat output
	LDA	GOAL	Get target temp
	BRSET	5,PORTC,CONQ  If not; see if it should be
* Cool on; turn off when indoor temp < goal - 1
	INCA		Goal-1 for hysteresis
	CMP	INTMP	GOAL-1 > INTMP ? Turn off ?
	BLS	XHVAC	NO; just leave
	BSET	5,PORTC	Turn off cool
	BSET	7,PORTC	Turn off fan
	CLR	HVACON	Turn off flag to indicate off
	BRA	XHVAC	Then leave
* Cool off; turn on when indoor temp > goal + 1
CONQ	DECA		Goal+1 for hysteresis
	CMP	INTMP	GOAL+1 < INTMP ? Turn on ?
	BHS	XHVAC	NO; just leave
	BCLR	7,PORTC	Turn on fan
	BCLR	5,PORTC	Turn on cool
	LDA	#1
	STA	HVACON	Set flag to indicate on
XHVAC	RTS	** RETURN from HVAC **

*********************************************************
* LCD - LCD Display Update                              *
*  If value is being set now, display ENTRY rather than *
*  the current value and flash it like time colon.      *
*  Flash time colon if time not being set now (else:on) *
*  Update current time if time not being set now        *
*  Update HVAC active '*' unless HVAC mode being set now*
*  Flash value to set if user is changing a setting     *
*********************************************************
LCD	EQU	*	LCD Display Update
	LDA	#$80	Left end of 1st row
	JSR 	WCTRL	Position entry point
	LDA	TIC	50mS periods 0-19
	BEQ	TIC0	Only update once/sec
	CMP	#10	TIC = 10 at mid second
	BNE	XLCD	If not 0 or 10, just leave
	JSR	BLINKR	Blanks colon or value being set
	BRA	XLCD	Exit
TIC0	JSR	DSPLAY	Update the LCD display
XLCD	RTS		** RETURN from LCD **

***
* Following subroutines support the LCD main task
***

BLINKR	EQU	*	Blink colon or user entry
	LDX	MODE	Mode 0 ?
	BNE	CIF1	If not see if mode 1
	LDA	#$82	Cursor position of colon
	JSR	WCTRL	Send cursor position to LCD
	BRA	SP1	Send 1 ASCII space and leave
CIF1	DECX		Mode 1 ?
	BNE	CIF2	If not see if mode 2
	LDA	#$80	Cursor position of HR
	JSR	WCTRL	Send cursor position to LCD
	BRA	SP2	Send 2 ASCII spaces and leave
CIF2	DECX		Mode 2 ?
	BNE	CIF3	If not see if mode 3
	LDA	#$83	Cursor position of MIN
	JSR	WCTRL	Send cursor position to LCD
	BRA	SP2	Send 2 ASCII spaces and leave
CIF3	DECX		Mode 3 ?
	BNE	CIF4	If not see if mode 4
	LDA	#$86	Cursor position of AMPM
	JSR	WCTRL	Send cursor position to LCD
	BRA	SP1	Send 1 ASCII space and leave
CIF4	DECX		Mode 4 ?
	BNE	CIF5	If not see if mode 5
	LDA	#$88	Cursor position of DAY
	JSR	WCTRL	Send cursor position to LCD
	BRA	SP4	Send 4 ASCII spaces and leave
CIF5	DECX		Mode 5 ?
	BNE	MUSTB6	If not, mode must be 6
	LDA	#$C0	Cursor position of HVAC Mode
	JSR	WCTRL	Send cursor position to LCD
	BRA	SP5	Send 5 ASCII spaces and leave
MUSTB6	LDA	#$C6	Must be mode 6
	JSR	WCTRL	Cursor position of Goal Temp
	BRA	SP2	Send 2 ASCII spaces and leave
SP5	LDA	#$20	ASCII space <sp>
	JSR	WDAT	Send a space to LCD
SP4	LDA	#$20	ASCII space <sp>
	JSR	WDAT	Send a space to LCD
	JSR	WDAT	Send a space to LCD
SP2	LDA	#$20	ASCII space <sp>
	JSR	WDAT	Send a space to LCD
SP1	LDA	#$20	ASCII space <sp>
	JSR	WDAT	Send a space to LCD
	RTS		** RETURN from BLINKR **


*********************************************************
* DSPLAY - Writes full 40 character display of current  *
*	system conditions to the LCD display peripheral *
*                                                       *
* Following is a typical LCD display...                 *
* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _               *
* 1 2 : 0 0   A   S U N     I N   7 5  F               *
*   O F F     7 2      O U T   - 2 2  F               *
* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _               *
*********************************************************
DSPLAY	LDA	#$00	Left end of 1st line on LCD
	JSR	WCTRL	Position entry point
	LDX	MODE	Use for mode compares
	LDA	HR
	CPX	#1	Mode= HR set ?
	BNE	AE1	Skip if not 1
	LDA	ENTRY	Use ENTRY rather than HR
AE1	JSR	CNVERT	Convert HRs to ASCII
	JSR	SHOW2	Display as 2 digits
	LDA	#':	ASCII colon
	JSR	WDAT	To LCD
	LDA	MIN
	CPX	#2	Mode= MIN set ?
	BNE	AE2	Skip if not 2
	LDA	ENTRY	Use ENTRY rather than MIN
AE2	JSR	CNVERT	Convert MINs to ASCII
	JSR	SHOW2	Display as 2 digits
	LDA	#$20	ASCII <sp>
	JSR	WDAT	<sp> to LCD
	LDA	AMPM	Current AMPM indicator
	CPX	#3	Mode= AMPM set ?
	BNE	AE3	Skip if not 3
	LDA	ENTRY	Use ENTRY rather than AMPM
AE3	TSTA		Check for AM (0)
	BNE	ITSPM	If not its PM
	LDA	#'A	ASCII A
	BRA	SHOWAP	Display A for AM
ITSPM	LDA	#'P	If it wasn't AM
SHOWAP	JSR	WDAT	Show A or P
	LDA	#$20	ASCII	<sp>
	JSR	WDAT	To LCD
	LDA	#-4	Offset from MDAY
	CPX	#4	Mode= DAY set ?
	BNE	AE4	Skip if not 4
	LDX	ENTRY	Use ENTRY rather than DAY
	BRA	DAYLP	Print Entry day
AE4	LDX	DAY	DAY = 1 to 7
DAYLP	ADD	#4	Advance pointer to next MDAY entry
	DECX		1->0 or n->(n-1)
	BNE	DAYLP	Loop till X=0 (A will = 4*DAY)
	TAX		Move offset to X
SHODAY	LDA	MDAY,X	Get next char
	CMP	#4	End of message ?
	BEQ	DUNDAY	If done printing day
	JSR	WDAT	Send char to LCD
	INCX		Point at next char
	BRA	SHODAY	Loop till $04 found
DUNDAY	CLRX		Loop index
LPSIN	LDA	MSINS,X	Get next ASCII char
	JSR	WDAT	Loop prints '  IN '
	INCX
	CPX	#5
	BNE	LPSIN	Loop till 5 chars
	LDA	INTMP	Indoor temp
	JSR	CNVERT	Convert to ASCII
	JSR	SHOW2	Display as 2 digits
	JSR	LCDDF	Display 'F'
	LDA	#$C0	Left end of 2nd line
	JSR	WCTRL	Reposition entry point
	LDA	#$20	ASCII <sp>
	TST	HVACON	Heat/cool running ?
	BEQ	ARNAST	If not go around asterisk
	LDA	#'*	ASCII asterisk
ARNAST	JSR	WDAT	Show <sp> or *
	CLRX		Message offset from MHVAC
	LDA	MODE	Get Mode in A
	CMP	#5	Mode= HVACM set ?
	BNE	AE5	Skip if not 5
	LDA	ENTRY	Use ENTRY rather than HVACM
	BRA	AE5B
AE5	LDA	HVACM	HVAC mode
* HVACM=... 0-Off, 1-Heat, 2-Cool, 3-Fan only
AE5B	BEQ	HVD	If HVACM=0 display 'OFF  '
	LDX	#6	Offset to 'HEAT '
	CMP	#1	Heat mode ?
	BEQ	HVD	If so; display
	LDX	#12	Offset to 'COOL '
	CMP	#2	Cool mode ?
	BEQ	HVD	If so; display
	LDX	#18	Offset to 'FAN  ' (must be)
HVD	LDA	MHVAC,X
	CMP	#4	End of message ?
	BEQ	DUNHVD	If so, skip ahead
	JSR	WDAT	Else display nxt char
	INCX		Point at next
	BRA	HVD	Continue loop
DUNHVD	LDA	GOAL	Goal temp setting
	LDX	MODE	Get mode in X
	CPX	#6	Mode= GOAL set ?
	BNE	AE6	Skip if not 6
	LDA	ENTRY	Use ENTRY rather than GOAL
AE6	JSR	CNVERT	Convert to ASCII
	JSR	SHOW2	Display as 2 digits
	JSR	LCDDF	Display 'F'
	CLRX		Loop index
LPSOT	LDA	MSOUT,X	Get message character
	JSR	WDAT	Send to LCD
	INCX		Nxt char of ' OUT '
	CPX	#5	Check for done
	BNE	LPSOT	Loop for 5 characters
	LDA	OUTMP	Outdoor temp
	JSR	CNVERT	Convert to ASCII
	JSR	SHOW3	Display as 3 digits
	JSR	LCDDF	Display 'F'
	RTS		** RETURN from DSPLAY **

** END of LCD Subasks *********************************************

** END of Main Tasks **********************************************

	ORG	$0600	Temporary ORG to get subs away from main

*********************************
*                               *
* SUBROUTINES & CONSTANT TABLES *
*                               *
*********************************

* Keypad Correspondance Table
*  1st entry of each pair is Row/Col bit pattern
*  2nd entry of each pair is ASCII equiv of key
*
* COL # ->   1  2  3  4
*            v  v  v  v
*                        This is layout of keypad
* ROW 1 ->   1  2  3  A
* ROW 2 ->   4  5  6  B
* ROW 3 ->   7  8  9  C
* ROW 4 ->   <  0  >  !
*
* Port B layout is  R4,R3,R2,R1; C1,C2,C3,C4  R's=ins, C's=outs
*
KYTBL	FCB	$18,'1	Row 1, Col 1 (Top Left)
	FCB	$28,'4	Row 2, Col 1
	FCB	$48,'7	Row 3, Col 1
	FCB	$88,'<	Row 4, Col 1
	FCB	$14,'2	Row 1, Col 2
	FCB	$24,'5	Row 2, Col 2
	FCB	$44,'8	Row 3, Col 2
	FCB	$84,'0	Row 4, Col 2
	FCB	$12,'3	Row 1, Col 3
	FCB	$22,'6	Row 2, Col 3
	FCB	$42,'9	Row 3, Col 3
	FCB	$82,'>	Row 4, Col 3
	FCB	$11,'A	Row 1, Col 4
	FCB	$21,'B	Row 2, Col 4
	FCB	$41,'C	Row 3, Col 4
	FCB	$81,'!	Row 4, Col 4 (Bot Right)

********************************************************
* WCTRL - Write control word to LCD peripheral         *
*	Enter with control word in accumulator         *
*	Return with original value of X                *
*	Delay ~4.5mS if A=$01 or $02 else delay ~120S *
********************************************************
WCTRL	STX	TEMPX	Save X
	STA	PORTA	Write control word to LCD
	BSET	2,PORTC	E->1
	BCLR	2,PORTC	E->0
	LDX	#20	20*6~*1S/~=120S
L120U	DECX		Delay loop ~120S
	BNE	L120U	20-19,19-18...1-0
	CMP	#$02	Commands $01 & $02 req extra delay
	BHI	ARN5M	If command > $02 skip long delay
L5M	JSR	ANRTS	JSR+RTS TAKES 12~ (just want delay)
	DECX		TAKES 3~ (X=0->1 on first pass)
	BNE	L5M	3~ Loop 256*18~*1S/~=4.608mS Delay
ARN5M	LDX	TEMPX	Restore X
ANRTS	RTS		** RETURN **

********************************************************
* WDAT - Write data word to LCD peripheral             *
*	Enter with data word in accumulator            *
*	Return with original values of X & A           *
*	Delay ~120S after data write                  *
********************************************************
WDAT	STX	TEMPX	Save X
	STA	TEMPA	Save A
	STA	PORTA	Write data word to LCD
	BSET	1,PORTC	RS->1
	BSET	2,PORTC	E->1
	BCLR	2,PORTC	E->0
	BCLR	1,PORTC	RS->0
	LDX	#20	20*6~*1S/~=120S
L120	DECX		Delay loop ~120S
	BNE	L120	20-19,19-18...1-0
	LDA	TEMPA	Restore A
	LDX	TEMPX	Restore X
	RTS		** RETURN **

********************************************************
* SHOW3 - Display 3 ASCII chars on LCD                 *
*	ASC100, ASC10; ASC1                            *
* SHOW2 - Display 2 ASCII chars on LCD                 *
*	ASC10; ASC1                                    *
********************************************************
SHOW3	LDA	ASC100	Get ASCII 100's digit
	BSR	WDAT	Send to LCD
SHOW2	LDA	ASC10	Get ASCII 10's digit
	BSR	WDAT	Send to LCD
	LDA	ASC1	Get ASCII 1's digit
	BSR	WDAT	Send to LCD
	RTS		** RETURN **

********************************************************
* LCDDF - Display F on LCD                            *
********************************************************
LCDDF	LDA	#$DF	Get ASCII degrees symbol
	BSR	WDAT	Send to LCD
	LDA	#'F	Get ASCII capitol F
	BSR	WDAT	Send to LCD
	RTS		** RETURN **

* Normal LCD display format...
* H H : M M   A   D A Y   I N   1 0 0  F
* _ H E A T _ 7 2  _ _ O U T   - 2 2  F
*	1st line of display is $00 (left) - $13
*	2nd line of display is $40 - $53

* Miscillaneous LCD message segments (Used in DSPLAY sub)
MHVAC	FCC	'OFF  '	These 4 messages accessed by
	FCB	$04	X offset from MHVAC.  $04 is
	FCC	'HEAT '	used to mark the end of a string
	FCB	$04
	FCC	'COOL '
	FCB	$04
	FCC	'FAN  '
	FCB	$04
MSINS	FCC	'  IN '
MSOUT	FCC	' OUT '
MDAY	FCC	'SUN'	These messages accessed by
	FCB	$04	X offset from MDAY.  $04 is
	FCC	'MON'	used to mark the end of a string
	FCB	$04
	FCC	'TUE'
	FCB	$04
	FCC	'WED'
	FCB	$04
	FCC	'THU'
	FCB	$04
	FCC	'FRI'
	FCB	$04
	FCC	'SAT'
	FCB	$04

********************************************************
* CNVERT - Convert a binary value to ASCII             *
*	Enter with binary value in A                   *
*	Result stored in ASC100, ASC10, ASC1           *
*	ASC100 (100's digit) defaults to blank (<sp>)  *
*	 but could be 1 or minus (-) depending on valu *
*	ASC10 and ASC1 digits default to zeros         *
*	Result can be -99 through 127.                 *
********************************************************
CNVERT	STA	TEMPA	Save original binary value
	LDA	#$20	ASCII <sp>
	STA	ASC100	Tenative 100's digit
	LDA	#'0	ASCII zero
	STA	ASC10	Tenative 10's
	STA	ASC1	Tenative 1's
	LDA	TEMPA	Get value to convert
	BPL	CVPOS	Branch if value positive
	LDA	#'-	ASCII minus sign
	STA	ASC100
	LDA	TEMPA	Get orig value again
LP10S	INC	ASC10	Loop to find 10's digit
	ADD	#10	Trial addition
	BMI	LP10S	Loop till addition fails
	BEQ	XVERT	If 0 conversion done; exit
	DEC	ASC10	Too far; back up
	SUB	#10	Now between -9 & -1
	NEGA		Change to positive
	ADD	ASC1	Add to 1's digit
	STA	ASC1	Update RAM location
	BRA	XVERT	Conversion done; exit

CVPOS	CMP	#100	Value >100 ?
	BLO	LPAS10	If less; skip 100's
	LDA	#'1
	STA	ASC100	Put ASCII 1 in 100's
	LDA	TEMPA	Get value again
	SUB	#100	Take 100 away
LPAS10	INC	ASC10	Increments 10's
	SUB	#10	Trial subtraction
	BPL	LPAS10	Loop till trial sub fails
	DEC	ASC10	Too far
	ADD	#10	Add back, now 0-9
	ADD	ASC1	Add to ASCII 1's
	STA	ASC1	Update RAM location
XVERT	RTS		** RETURN from CNVERT **


* A/D Offsets to compensate sensors
*	Analog temp = (A/D reading) - (Offset)
OFF0	FCB	60	Offset correction for sensor 1
OFF1	FCB	60	Offset correction for sensor 2