Programming the Microprocessor for the Oven Based Coffee Roaster

This page describes the programming of the roaster. The hardware is elsewhere, with separate pages for electronic aspects and mechanical and electrical aspects.

Processor

The physical electronics are discussed on the electronic aspects page.

The program was developed using the gnupic utilities project gputils to program in assembly. This and lots of other open-source stuff for pic development an be easily found for free at www.gnupic.org.

To physically program the device, I use a Microchip PICKit2 programmer (a genuine one, though I understand the chinese copies are just as good), driven by the Microchip official but unsupported command-line utility pk2cmd.

There's an opensource simulator gpsim, but it depends upon a gtk toolkit that's a bit of a pig to get compiled (at least, it was for me) so I've done a page of notes about compiling gtkextra and gpsim on opensuse 11.1. Actually, although it's a very impressive piece of work, I prefer to just burn the program to the hardware and see if it does what I want - I don't often use a simulator.

If you're still stuck with Microsoft, Microchip give away an assembler-based IDE (MPLAB) for windows platforms.

Major Modes

The program memory in the pic is in four pages, and jumps from one page to another are a multi-instruction process, so I try and minimise them. Consequently, the program code is laid out within the pages. Effectively, I have a number of major modes, and the whole of a mode resides within one page.

Page 0

This is initialisations and utility functions (things like lcd control routines, routines to read and write the i2c bus.

Page 1

This is 'idle mode', where it sits when doing nothing else.

There is also a 'settings mode' which has such utility functions as erasing all program memory on the eeproms and setting the system clock time.

Page 2

This is the main number-crunching that controls everything while roasting

The display when running has elapsed time and current temperature on the top row. Below that is the current program step label, and the current target temperature. At far right (across both lines) is a bar-chart of current heat input, here just below 50% because the actual temperature is just above the target.

All this and more is also sent out the serial port (USB) every second, which is what enables the graphs elsewhere on these pages to be generated.

Page 3

This is the routines used to define and manipulate the defined roast profiles.

Code

Does anyone want code? This is a recentish development version. The comments should describe current stage of development.

This is 4249 instructions, out of a theoretical capacity of 8192.

 	;; Ian's astounding digital coffee roaster system
	;; $Id: roast.asm,v 1.71 2009/05/04 08:12:15 ian Exp ian $

#define banner "Roaster    v0.22"	;; this must be 16 characters
	;;     "123456789ABCDEF^"

	;; compiler confguration
LIST n=0			;no page breaks in .lst file
ERRORLEVEL -302			;suppress register not in bank 0 message
ERRORLEVEL -306			;suppress crossing page boundary message

	;; Hardware requirements:
	;; PIC 16F877A, 16 Mhz oscillator
	;; see below for pin usage
	
	;; Version History
	;; 0.01-0.12 progressive development of initialisation routines
	;; 	with associated test routines at intermediate stages:
	;; 		lcd initialise, utility functions & banner
	;; 		arithmetic functions, debounce, analogue input
	;; 		i2c RTC functions, timer0 config, program eeprom
	;; 		usart routines, test dynamic user characters
	;; 0.13	add main block at program page 1 with button actions
	;; 0.14	add program routines in program page 2
	;; 0.15	add settings mode and set time
	;; 0.16	add erase all memory to settings mode
	;; 0.17	program mode completed
	;; 0.18	test mode inserted in place of running mode
	;;	if any program is run, enters test mode, in which the current
	;; 	temperatures on ambient and both thermocouple inputs are displayed
	;; 0.19	running mode partly completed, but pprog mode needs moving to ppage3
	;; 0.20	prog mode moved to ppage3 and runprog moved to ppage2
	;; 	this is first fully functional version
	;; 	though does not have drum, fan, eject or cooling control
	;; 0.21	add drum and element fan control
	;; 0.22 add output of running info to serial port
	
	;; Todo
	;; TODO01	save last started program in RTC ram and default to this on startup
	;; TODO02	make the display temperature average of two thermocouples
	;; 		and multiple readings	;
	;; TODO03	output info about program step to USART
	
	;; prog memory usage
	;; ppage0	initialise and library of utility routines
	;; ppage1	idle mode, settings mode
	;; ppage2	program running
	;; ppage3	programming mode (define profiles)
	
	;; Pin Usage
	;; pin name	pin no	Use in roaster
	;; OSC1/CLKI	13	clock
	;; OSC2/CLKO	14	clock
	;; MCLR/VPP	1	reset button on back panel
	;;	
	;; Port A
	;; RA0/AN0	2	cold junction temperature sensor
	;; RA1/AN1	3	in-beans probe thermocouple
	;; RA2/AN2	4	air inlet thermocouple
	;; RA3/AN3	5	voltage reference
	;; RA4/T0CKI	6	timer clock input at 256 Hz
	;; RA5/AN4	7	front panel light
	;;
	;; Port B
	;; RB0/INT	33	LCD E line
	;; RB1		34	LCD R/W line
	;; RB2		35	LCD RS line
	;; RB3/PGM	36	rotary encoder direction - 1=clockwise, 0=anticlock
	;; RB4 (ioc)	37	rotary encoder change - flips as encoder changes
	;; RB5 (ioc)	38	front panel button near knob - buttonK
	;; RB6 (ioc)	39	front panel button to right - buttonR
	;; RB7 (ioc)	40	front panel button to left - buttonL
	;;
	;; Port C
	;; RC0		15	** reserved for future use - cooling? **
	;; RC1		16	Door open – mosfet small heatsink (has a LED in parallel)
	;; RC2		17	mosfet w large heatsink (has a LED in parallel) - cooling
	;; RC3/SCK	18	i2c clock
	;; RC4/SDI	23	i2c data
	;; RC5		24	piezo sounder
	;; RC6/TX	25	USART TX
	;; RC7/RX	26	USART RX
	;;
	;; Port D
	;; RDx		19-22 & 27-30	LCD data lines bit x
	;;
	;; Port E
	;; RE0		8	drive to SSR (has a LED in parallel)
	;; RE1		9	top element heater fan control
	;; RE2		10	rotisserie control
	;;
	;; others
	;; VSS		12 & 31
	;; VDD		11 & 32


	
	;; operating instructions
	;;
	;; in standby mode:
	;; knob changes program number
	;; K -press->	run from currently displayed step
	;;   -hold->	go to program mode
	;; L -press->	go to program 1
	;;   -hold->	go to manual mode
	;; R -press->	does nothing
	;;   -hold->	go to settings mode
	;;
	;; program mode
	;; in general, L and R buttons either step between parameters on screen,
	;; in which case the knob adjusts value,
	;; or L and R select indicated options (in which case knob not functional)
	;; K button advances to next screen
	;; 
	;; manual mode [not currently coded]
	;;
	;; settings mode
	;; answer questions to change major settings:
	;;   change syustem time & date
	;;   erase all program memory
	;; in general, keys operate as for program mode
	;; note that can only make one settings mode change, then are returned
	;; to idle mode (re-enter settings mode to change another item)
	

	;; program definition as stored on eeprom
	;; 
	;; byte 0 not used
	;;
	;; bytes 1-63 are jump table for programs
	;; specify a program step from which to start running
	;;
	;; bytes 64 onwards store program steps
	;; first byte of each step is at 32*step number
	;; (meaning valid step numbers are 2-255)
	;; each step bytes:
	;; 0	flags 	0 = step is blank
	;; 1	not in use
	;; 2,3	duration of step in seconds (2=low, 3=high)
	;; 4,5	target temperature at start in 10ths degree (4=low)
	;; 6,7	target temperature at end in 10ths degree (6=low)
	;; 8	equip flag bit	0 - rotisserie on
	;; 			1 - top element fan on
	;; 			4 - door open (ie eject)
	;; 9	cooling fans setting (might be bitwise)
	;; 10-13 not in use
	;; 14	proportional band in degrees
	;; 15	max heat proportion
	;; 16	min heat proportion
	;; 17-21 not in use
	;; 22	number of previous step in program (0 if this is first)
	;; 23	number of next step in program (0 if this is final)
	;; 24-31 8 characters, name of this step

	
	;; #############################
	;; set up the parameters
	;; #############################

	LIST P=PIC16F877A
	include 
	__CONFIG _HS_OSC & _PWRTE_ON & _WDT_OFF & _CP_OFF & _BODEN_OFF & _LVP_OFF


	;; #############################
	;; definitions of port use
	;; #############################
	
	;; lcd control stuff
lcd_data	equ	PORTD	; port with lcd data on (uses 8 bit wide)
lcd_ctrl	equ	PORTB	;  port with all lcd control lines on
lcd_e		equ	0	;  pin number for Enable
lcd_rw		equ	1	;  pin number for Read/Write
lcd_rs		equ	2	;  pin number for Regsister Select
	
	;; #############################
	;; definitions of ram working locations
	;; #############################

	;; context saves in locations reached from all 4 banks
w_temp		equ	0x070
status_temp	equ	0x071
pclath_temp	equ	0x072

	;; general temp space high byte and low byte
lowb		equ	0x20
highb		equ	0x21

temp1		equ	0x22
temp2		equ	0x23
temp3		equ	0x24
temp4		equ	0x25

	;; bytes for bcd packed to nybbles
bcdtempl	equ	0x30
bcdtemph	equ	0x31
	
bcdut		equ	0x32
bcdht		equ	0x33
bcdtt		equ	0x34	

	;; specific temporary stuff
interrupt_temp	equ	0x35	; temporary value reserved for interrupt routine use
delay_temp1	equ	0x36	; used in delay routines
delay_temp2	equ	0x37	;  used in delay routine
lcd_temp	equ	0x38	; used for sending text from program memory to lcd
deb_temp	equ	0x39	;  used in debounce routine
portbbuff	equ	0x3A
	

	;; values used regarding prog flow
prognum		equ	0x40
progstep        equ     0x41

prevstep	equ	0x42
newstep		equ	0x43
nextstep	equ	0x44

pmflags		equ	0x45	
	
	
        ;; values used in definition and calculation of the running step
el_secs         equ     0x50   ; elapsed seconds
el_minsl   	equ     0x51   ; minutes low byte
el_minsh	equ     0x52   ; minutes high byte or hours (depends on context)

elt_l       equ     0x53	; elapsed this step
elt_h       equ     0x54

maxheat		equ	0x55	;max allowed heat for this step
minheat       	equ     0x56  ;min limit of heater power (defined in program)
heatpower       equ     0x57   ; desired power setting
heaton          equ     0x58   ; counter in heater loop
heatoff         equ     0x59   ; counter in heater loop

w_a		equ	0x5A	;24 bit arithmentic register lowest byte
w_b		equ	0x5B
w_c		equ	0x5C
w_d		equ	0x5D
w_e		equ	0x5E	
	
	;; temperatures
t0_l		equ	0x60	;t0 is the cold juction sensor
t0_h		equ	0x61
t1_l		equ	0x62	;t1 is first thermocouple
t1_h		equ	0x63
t2_l		equ	0x64	;t2 is second thermocouple
t2_h		equ	0x65
td_l		equ	0x66	;td is the temperature for display
td_h		equ	0x67
tt_l		equ	0x68	;tt is the target temperature at this time
tt_h		equ	0x69

	
	;; buffer for screen
lcdbuf		equ	0xA0	; unspecified number of bytes used
				; but no good reason to exceed 32
	
	;; i2c and usart stuff is generally in register bank 2
i2caddr		equ	0x110	; address of i2c device (7bit) + R/W bit
i2chigh		equ	0x111	; high byte, normally address within device
i2clow		equ	0x112	; low byte, normally address within device
i2ccount	equ	0x113	; count of bytes to read
i2ctemp		equ	0x114	; temporary byte

usartaddr	equ	0x118	; address of next byte to send out
usartcount	equ	0x119	; number of bytes (including next) to send

i2cbuf		equ	0x120	; start of read / write buffer ram	

	;; #############################
	;; define macros for register banks and program banks
	;; #############################

rbank0 	MACRO
	BCF	STATUS,RP0	; clear bank select bits
	BCF	STATUS,RP1
	ENDM

rbank1 	MACRO
	BSF	STATUS,RP0
	BCF	STATUS,RP1 
	ENDM

rbank2 	MACRO
	BCF	STATUS,RP0 
	BSF	STATUS,RP1
	ENDM

rbank3 	MACRO
	BSF	STATUS,RP0
	BSF	STATUS,RP1
	ENDM

rbank0to2 MACRO
	bsf	STATUS,RP1
	ENDM

rbank2to0 MACRO
	bcf	STATUS,RP1
	ENDM
	
ppage0	MACRO
	BCF	PCLATH,3
	BCF	PCLATH,4
	ENDM

ppage1	MACRO
	BSF	PCLATH,3
	BCF	PCLATH,4
	ENDM

ppage2	MACRO
	BCF	PCLATH,3
	BSF	PCLATH,4
	ENDM

ppage3	MACRO
	BSF	PCLATH,3
	BSF	PCLATH,4
	ENDM

	;; #############################
	;; some data label definitions
	;; ############################

	;; these must be at 0x0700 and onwards - see lcdtext routine
	org	0x0700		; encode some constant data strings near end of prog bank 0
banner_label
	da	banner		; this is defined at top of file for ease of updating version code
init_label
	da	"initialising ..." ; used when initialising
standby_label
	da	"Run program     " ; used in standby mode

arrows
	;; this is five spaces, arrow, four spaces, arrow, five spaces
	da	"    ",a' '*0x80+1,"    ",2*0x80+a' ',"    "
yesnolabel
        da      " yes",a' '*0x80+1,"    ",2*0x80+a' ',"no  "
onofflabel
        da      "  on",a' '*0x80+1,"    ",2*0x80+a' ',"off "

dowlabels	
Sunlabel
	da	"Sun "
Monlabel
	da	"Mon "
Tuelabel
	da	"Tue "
Wedlabel
	da	"Wed "
Thulabel
	da	"Thu "
Frilabel
	da	"Fri "
Satlabel
	da	"Sat "
	

progmodeselect
	da	"Edit program    "
progmode01a
	da	"Edit step   New "
progmode01b
	da	"    ",a' '*0x80+1,"    ",d'2'*0x80+a' ',"step"
progmode02
	da	"Add to curr prg?"
progmode03
	da	"Set step duratn."
progmode04
	da	"Set temperatures"
progmode09
	da	"Proportionl band"
progmode10
	da	"Power max & min%"
progmode11
	da	"Set step name:  "
progmode12
	da	"Write to memory?"
progmode13
	da	"More editing?   "
	
	;; These are six pairs of characters long
	;; because they have yes/no or on/off after them on top row:
progmode05
	da	"Drum rotate:"
progmode06
	da	"Element fan:"
progmode07
	da	"Open door:  "
progmode08
	da	"Cooling fan:"



setmode01
	da	"Set clock?      "
setmode90
	da	"Erase all progs?"
setmode91
	da	"Cancel erase?   "
setmode92
	da	"Wait, erasing..."
	
	
	
	;; #############################
	;;  start code
	;; #############################
	org	0x000  
	goto	start		; ie on power up, start!

	org	0x004		; i think this is the reset vector
interrupt_vec
	; do context save as per microchip data sheet
	movwf	w_temp
	swapf	STATUS,w
	clrf	STATUS		; which forces bank0
	movwf	status_temp
	movf	PCLATH,w
	movwf	pclath_temp
	clrf	PCLATH		; which forces ppage0

	;;  check interrupts we are handling
	;;  and clear any for which we have not got the enable bit set
	;;  eg:	  btfss   INTCON,RBIE
	;;   	  bcf     INTCON,RBIF

	;; now check for each interrupt we care about
	btfsc	PIR1,TXIF	;is it tehg flag saying ready for next byte?
	goto	usart_transmit	;if so, send it

	;; could drop through list above to here & return
	;; if so, it seems that something unexpected triggered interrupt
interrupt_done
	; recover context, as per microchip datasheet
	;; note this resest rbank to whatever it was
	movf	pclath_temp,w
	movwf	PCLATH
	swapf	status_temp,w
	movwf	STATUS
	swapf	w_temp,f
	swapf	w_temp,w

	retfie			; back into the program & enable gie


	;; ############################
	;; action interrupts
	;; ############################


	;; action usart transmit interrupt
	;; ###############################

	;; process to use interrupt driven transmit:
	;; check if transmissionn is current by testing if usartcount >0
	;; 1: load a buffer in register bank 2 or 3 with ascii code
	;; 2: set register usartaddr with address of first byte
	;; 3: set register usartcount with number of bytes to send
	;; 4: execute 'bsf PIE1,TXIE', should start transmission running
	
usart_transmit
        rbank2                  ;where the serial port registers are
        movf    usartaddr,w     ; get address of next byte to send
        movwf   FSR             ; put it in indirect addressing pointer
        bsf     STATUS,IRP      ; point to banks 2&3
        rbank0
        movf    INDF,w          ; get the byte
        movwf   TXREG           ; put byte in transmit buffer, which will send it on its way
        rbank2
        incf    usartaddr,f     ; point to next byte
        decf    usartcount,f    ; reduce count of bytes still to go
	rbank1
        btfsc   STATUS,Z        ; check if we have sent all bytes
        bcf     PIE1,TXIE       ; if so, switch off the flag so we don't get more interrupts
        goto   	interrupt_done  ; note that loading new data resets the flag automatically

	
	

	;; #############################
	;; utility functions used elsewhere in code
	;; #############################

	;; waitpress waits until a button has been pressed or the knob turned
	;; if button, it then waits for button release
	;; it returns with a value in portbbuff, where bits 7-3 reflect button & knob state
	;; and bit 0 reflects if held for more than 1 second
	;; note that this resets T0 to do the timing,
	;; and really doesn't want interrupts running when called
	;; otherwise all hell will break loose
waitpress
	rbank0
	clrf	portbbuff
	;; record start state of knob
	btfsc	PORTB,4		; bit 4 is the knob toggle channel
	bsf	portbbuff,0	;set the lsbit to match knob start state
	
	bcf	INTCON,RBIF	;wipe any residual flag
	btfss	INTCON,RBIF	;look at port b interrupt flag
	goto	$-1		;wait here until interrupt flag is set
	clrf	TMR0		; clear timer 0
	bcf	INTCON,T0IF	;clear overflow flag

	;; if was knob, do not need the debounce and so on
	;; (knob signal has separate processor doing debounce and so on)
	swapf	PORTB,w		;get port b with swapped nybbles
	xorwf	portbbuff,f	;xor current state with previous state
	btfss	portbbuff,0	;see if knob has chnaged
	goto	waitisbutton	;if not, assume is buttons

	;; if we get here, we have decided it was a knob change
	clrf	portbbuff
	bsf	portbbuff,4	;set the knob indicator bit
	btfsc	PORTB,3		;check direction
	bsf	portbbuff,3	; set direction if required

	return

waitisbutton	
	call	debounce	;wait for switches to be stable
	bcf	INTCON,RBIF	;clear port b change flag
	
	movf	PORTB,w		;get port b status
	andlw	b'11100000'	;mask out all but buttons
	movwf	portbbuff	;set that aside

waitrelease
	btfsc	INTCON,T0IF	;see if timer has overflowed
	bsf	PORTA,5		;switch on light
	movf	PORTB,w		;get button state
	andlw	b'11100000'	;mask for just buttons
	btfss	STATUS,Z	;see if buttons all off
	goto	waitrelease 	;if not carry on waiting

	call	debounce	;wait for buttons to stabilise after release
	bcf	INTCON,RBIF	;mop up any residual bounce effects

	bcf	PORTA,5		;switch off light (whether set or not)

	btfsc	INTCON,T0IF
	bsf	portbbuff,0	;set lsbit in portbbuff if > 1 sec

	return

	
	;; delaywms delays by value in W register milliseconds
	;; it assumes a 16MHz crystal / oscillator
	;; requires ram locations delay_temp1 and delay_temp2
	;; note that it destroys the value in W
delaywms
	movwf	delay_temp1		; 1 cycle
delay_outer_loop
	movlw	d'235'			; 1 cycles
	movwf	delay_temp2		; 1 cycles
delay_inner_loop
	nop				; 1 cycle
	nop				; +1 = 2
	nop				; +1 = 3
	nop				; +1 = 4
	nop				; +1 = 5
	nop				; +1 = 6
	nop				; +1 = 7
	nop				; +1 = 8
	nop				; +1 = 9
	nop				; +1 = 10
	nop				; +1 = 11
	nop				; +1 = 12
	nop				; +1 = 13
	nop				; +1 = 14
	nop				; +1 = 15
	DECFSZ	delay_temp2, F		; +1 = 16
	GOTO	delay_inner_loop	; +2 = 17
		; so inner loop takes 17*235 = 3995 cycles
	decfsz	delay_temp1, F		; 1 cycle
	goto	delay_outer_loop	; 2 cycles
		; so outer takes 2 setup + 3995 inner + 3 outer = 4000 cycles
delay_end
	return	; 1 cycle plus it took one extra to get out of outer loop
		; so total delay = 4000 x W + 3 cycles


	;; debounce monitors switches on the top three bits of port b
	;; when called, it waits until they have remained unchanged
	;; for eight sequential samples 1mS appart
	;; note, button state in upper nybble, read counter in lower nybble
debounce
	movf	PORTB,w		; get the port b state
	andlw	b'11100000'	; mask out the lower nybble
	movwf	deb_temp	; set that aside
deb_loop
	movlw	0x1
	call	delaywms	; wait one millisecond
	movf	PORTB,w		; get portb state
	xorwf	deb_temp,w	;  so w now has what's different from first time we looked
	andlw	b'11100000'	;  limit our investigation to just the three switch bits
	btfss	STATUS,Z	; check if the answer was zero (ie, no change)
	goto	debounce	;  if not, ie change occurred, start from scratch
	incf	deb_temp,f	;  otherwise we increment temp (ie, we count up in the low nybble)
	btfss	deb_temp,3	;  if bit three is set, have been through the loop 8 times
	goto	deb_loop	; if not yet, go round the loop
	return			; otherwise, consider the port b state to be stable

	
	;; #################################
	;; lcd control routines
	;; #################################

	;; lcd busy waits until the lcd is not busy
lcdbusy
	rbank1			; register bank 1
	movlw	0xff		; all input
	movwf	lcd_data	;  ie, I am relying on tris being in same place but bank 1
	rbank0
	bcf	lcd_ctrl,lcd_rs	;  set busy flag mode
	bsf	lcd_ctrl,lcd_rw	;  set read mode
	bsf	lcd_ctrl,lcd_e	; latch command state
	nop
	nop
	btfss	lcd_data,7	;  if busy flag in bit 7 is set, lcd is busy so don't ...
	goto	lcdnotbusy	;  break out
	nop			; at this point a bit more delaying won't hurt
	bcf	lcd_ctrl,lcd_e	; pull the enable line down
	goto	lcdbusy		; loop round for another check
lcdnotbusy
	bcf	lcd_ctrl,lcd_e	; pull the enable line down
	rbank1			; move to register bank 1
	clrf	lcd_data	;  lcd data port back to output
	rbank0			; back to register bank 0
	return			; return to whatever called us

	;; sends a command byte on w to the lcd as a command
lcdsendcmd
	rbank0
	movwf	lcd_data
	call	lcdbusy		; wait until it's ready for us
	bcf	lcd_ctrl,lcd_rw	; rw low so writing
	bcf	lcd_ctrl,lcd_rs	;  rs low means command
	bsf	lcd_ctrl,lcd_e	;  latch command state
	nop
	nop
	bcf	lcd_ctrl,lcd_e	;  latch data
	nop
	return

	;; sends a character byte on w to the lcd
lcdsendchar
	rbank0
	movwf	lcd_data
	call	lcdbusy		; wait until it's ready for us
	bcf	lcd_ctrl,lcd_rw	; rw low so writing
	bsf	lcd_ctrl,lcd_rs	;  rs high means character not command
	bsf	lcd_ctrl,lcd_e	;  latch command state
	nop
	nop
	bcf	lcd_ctrl,lcd_e	;  latch data
	nop
	return

	;; call with number of characters reqd on w
	;; will send bytes from lcdbuf onwards to lcd
lcdsendbuf
	rbank0
	movwf	lcd_temp
	movlw	low(lcdbuf)
	movwf	FSR
	bcf	STATUS,IRP
lcdsendbufloop
	movf	INDF,w
	movwf	lcd_data
	call	lcdbusy		; wait until it's ready for us
	bcf	lcd_ctrl,lcd_rw	; rw low so writing
	bsf	lcd_ctrl,lcd_rs	;  rs high means character not command
	bsf	lcd_ctrl,lcd_e	;  latch command state
	nop
	nop
	bcf	lcd_ctrl,lcd_e	;  latch data
	nop
	incf	FSR,f		;point to next byte
	decfsz	lcd_temp,f
	goto	lcdsendbufloop
	;; now finished
	return


	
	
	
	;; clear screen and move curosr to start of first line
	;; can also move to start of first line without clear screen
lcdclear
	movlw	0x01
	call	lcdsendcmd
lcdline1	
	movlw	0x02
	call	lcdsendcmd
	return

	;; move cursor to start of second line
lcdline2
	movlw	0xc0
	call	lcdsendcmd
	return

	;; an address should be in W, set cursor to that positin
	;; 00-0F is first line, 40-4F is second line
lcdaddress
	iorlw	b'10000000'	;set first bit
	call	lcdsendcmd
	return
	
	;; send a block of text from program space to the LCD
	;; text is packed 2-bytes to word, as by 'da'
	;; located at 0x07xx where xx is indicated by fsr
	;; w contains number of program words to read (ie, bytes/2)
lcdtext
	rbank0			; select register bank 0
	movwf 	lcd_temp	; save how many reads to do
	rbank2			; eeaddress control is in bank 2
	movlw	0x07		;  high byte address
	movwf	EEADRH		; high byte of eeprom address register
	movf	FSR,w 		; get low byte from fsr
	movwf	EEADR		; set low byte of address
lcdtextloop
	rbank3			; register bank 3
	bsf	EECON1,EEPGD	; looking at program memory
	bsf	EECON1,RD	;  doing a read
	nop			; wait
	nop			; wait some more
	rbank2			; back to bank 2
	movf	EEDATH,w	; get high byte of data
	rbank0			; bank 0 is where my general storage space is
	movwf	highb		; store it
	rbank2  		; bank 2
        movf    EEDATA,w        ; get the low byte
        rbank0                  ; bank 0
        movwf   lowb            ; set it to ram too
        bcf     STATUS,C        ; clear the carry flag
        rlf     lowb,f          ; roll low byte up
        rlf     highb,f         ; roll high byte up, getting bit from low byte and so has ascii
        bcf     STATUS,C        ; clear carry so we roll in a 0
        rrf     lowb,f          ; roll low byte back down so now we have ascii
        movf   	highb,w         ; get the high ascii
	call    lcdsendchar     ; put it on display
        movf    lowb,w          ; get the low ascii
        call    lcdsendchar     ; put that on the display too

        rbank2                  ; bank 2
        incf    EEADR,F         ; so we get the next pair of characters
        rbank0                  ; back to bank 0

        decfsz  lcd_temp,f      ; decrement our counter and loop 'till we've done required reads
        goto    lcdtextloop      ; loop around
        return                  ; when we've finished

	

	;; #######################
	;; numbers and arithmetic routines
	;; ######################

	;; binary to bcd
	;; unsigned 16 bit binary in FSR:FSR+1 converted to bcd in three bytes
	;; byte in FSR is low byte
	;; uses double dabble (see wikipedia)
	;; trashes the temp1 register (so that can't be one of the input bytes)
b2bcd
	rbank0
	movf	0,w		; get the low byte
	movwf	bcdtempl	; set it in our working space
	incf	FSR,f
	movf	0,w		; get the high byte
	movwf	bcdtemph	; set that into our working space
	movlw	d'16'		; number of binary digits, so number of loops
	movwf	temp1		; now put it where we will count
	clrf	bcdut		;  initialise all bcd digits to zero
	clrf	bcdht
	clrf	bcdtt

bcdloop
bcdunits	
	movf	bcdut,w		; get two digits into w
	andlw	0x0f		; so now we have units in w
	sublw	d'4'		; subtract w from 4 and put answer in w
	btfsc	STATUS,C	; if carry is clear answer was -ve
	goto	bcdtens		; otherwise consider next digit
	movlw	d'3'		; if answer was -ve, add three
	addwf	bcdut,f		;  add it and put it back in f
bcdtens
	swapf	bcdut,w		; swapped nybbles into w
	andlw	0x0f		; now have just tesn digit in low nybble of w
	sublw	d'4'		; subtract w from 4 and put answer in w
	btfsc	STATUS,C	; if carry is clear answer was -ve
	goto	bcdhundreds	; otherwise consider next digit
	movlw	0x30		; if answer was -ve, add three to the bcd byte
	addwf	bcdut,f		; add it and put it back in f
bcdhundreds	
	movf	bcdht,w		; get two digits into w
	andlw	0x0f		; so now we have hundreds in w
	sublw	d'4'		; subtract w from 4 and put answer in w
	btfsc	STATUS,C	; if carry is clear answer was -ve
	goto	bcdthousands	; otherwise consider next digit
	movlw	d'3'		; if answer was -ve, add three
	addwf	bcdht,f		;  add it and put it back in f
bcdthousands
	swapf	bcdht,w		; swapped nybbles into w
	andlw	0x0f		; now have thousands digit in low nybble of w
	sublw	d'4'		; subtract w from 4 and put answer in w
	btfsc	STATUS,C	; if carry is clear answer was -ve
	goto	bcdtenthous	; otherwise consider next digit
	movlw	0x30		; if answer was -ve, add three to the bcd byte
	addwf	bcdht,f		; add it and put it back in f
bcdtenthous
	movf	bcdtt,w		; get two digits into w
	andlw	0x0f		; so now we have ten-thousands in w
	sublw	d'4'		; subtract w from 4 and put answer in w
	btfsc	STATUS,C	; if carry is clear answer was -ve
	goto	bcdshifts	; otherwise consider next digit
	movlw	d'3'		; if answer was -ve, add three
	addwf	bcdtt,f		;  add it and put it back in f
	;; note that there is no 6th digit becuase can't get there with 16 bit binary
bcdshifts
	bcf	STATUS,C	; clear carry flag to start
	rlf	bcdtempl,f	; do series of roll left across the working space
	rlf	bcdtemph,f
	rlf	bcdut,f
	rlf	bcdht,f
	rlf	bcdtt,f
	;; thats the end of the loop
	decfsz	temp1,f		; after decrement, we have number of times more too loop
	goto	bcdloop		; if that's nonzero, do the loop
	return			; otherwise we're finished



	;; binary to hours minutes seconds
	;; unsigned 16 bit binary in FSR:FSR+1 converted to bcd h:m:s in three bytes
	;; byte in FSR is low byte
	;; uses double dabble (see wikipedia) modified for base 6 in tens units
	;;  does this by saying if have 30 minutes, when double, we want 1 hr
	;;  so any tens of minutes >2 we add 0x50 before doubling
	;;  thus, 0x030 -add-> 0x080 -double-> 0x100 
	;; trashes the temp register
	;; seconds placed in bcdut, minutes in bcdht, hrs in bcdtt
b2hms
	movf	0,w		; get the low byte
	movwf	bcdtempl	; set it in our working space
	incf	FSR,f
	movf	0,w		; get the high byte
	movwf	bcdtemph	; set that into our working space
	movlw	d'16'		; number of binary digits, so number of loops
	movwf	temp1		; now put it where we will count
	clrf	bcdut		;  initialise all bcd digits to zero
	clrf	bcdht
	clrf	bcdtt

hmsloop
hmsunits	
	movf	bcdut,w		; get two digits into w
	andlw	0x0f		; so now we have units of seconds in w
	sublw	d'4'		; subtract w from 4 and put answer in w
	btfsc	STATUS,C	; if carry is clear answer was -ve
	goto	hmstens		; otherwise consider next digit
	movlw	d'3'		; if answer was -ve, add three
	addwf	bcdut,f		;  add it and put it back in f
hmstens
	swapf	bcdut,w		; swapped nybbles into w
	andlw	0x0f		; now have just tens of seconds in low nybble of w
	sublw	d'2'		; subtract w from 2 and put answer in w
	btfsc	STATUS,C	; if carry is clear answer was -ve
	goto	hmshundreds	; otherwise consider next digit
	movlw	0x50		; if answer was -ve, add five to the tens
	addwf	bcdut,f		; add it and put it back in f
hmshundreds	
	movf	bcdht,w		; get two digits into w
	andlw	0x0f		; so now we have units of minutes in w
	sublw	d'4'		; subtract w from 4 and put answer in w
	btfsc	STATUS,C	; if carry is clear answer was -ve
	goto	hmsthousands	; otherwise consider next digit
	movlw	d'3'		; if answer was -ve, add three
	addwf	bcdht,f		;  add it and put it back in f
hmsthousands
	swapf	bcdht,w		; swapped nybbles into w
	andlw	0x0f		; now have tens of minutes in low nybble of w
	sublw	d'2'		; subtract w from 4 and put answer in w
	btfsc	STATUS,C	; if carry is clear answer was -ve
	goto	hmstenthous	; otherwise consider next digit
	movlw	0x50		; if answer was -ve, add three to the bcd byte
	addwf	bcdht,f		; add it and put it back in f
hmstenthous
	movf	bcdtt,w		; get two digits into w
	andlw	0x0f		; so now we have units of hours
	sublw	d'4'		; subtract w from 4 and put answer in w
	btfsc	STATUS,C	; if carry is clear answer was -ve
	goto	hmsshifts	; otherwise consider next digit
	movlw	d'3'		; if answer was -ve, add three
	addwf	bcdtt,f		;  add it and put it back in f
	;; note that there is no 6th digit becuase can't get there with 16 bit binary
	;; - max is 18 hrs plus minutes so tens of hours never exceeds 1
hmsshifts
	bcf	STATUS,C	; clear carry flag to start
	rlf	bcdtempl,f	; do series of roll left across the working space
	rlf	bcdtemph,f
	rlf	bcdut,f
	rlf	bcdht,f
	rlf	bcdtt,f
	;; thats the end of the loop
	decfsz	temp1,f		; after decrement, we have number of times more too loop
	goto	hmsloop		; if that's nonzero, do the loop
	return			; otherwise we're finished



	
	
	;#############################
	; i2c_rtc routine reads or writes the real-time-clock chip
	; use general registers in bank 2:
	;       i2caddr         only lsbit consulted to decide if read or write
	; buffers reads & writes through GPRs 0x120 - 0x127
	; also note that this routine disables interrupts, so might need to re-enable gie
	;
	; to use:
	;       set i2caddr with r/w bit (0=write)
	;       if writing, load 0x0120 and onwards with bytes to transmit
	;       call routine i2c_rtc
	;       if reading (ie bit 0 of i2caddr was set), 0x0120 onwards contains read bytes
	;       re-enable gie if appropriate
	;#############################
i2c_rtc
        rbank0                  ; register bank 0
        movf    INTCON,w        ; get current interrupts flag
        clrf    INTCON          ; switch off all interrupts
        rbank2
        movwf   i2ctemp         ; stash the safe intcon somewhere
	
	;; we need to load the i2c registers with the data particular to the rtc chip
	movf	i2caddr,w	; get address and r/w bit
	andlw	b'00000001'	; mask out all but the r/w bit
	iorlw	b'11010000'	; put in the address of the rtc chip
	movwf	i2caddr		;put that back as the address to use
	movlw	d'8'		;clock has 8 bytes of data
	movwf	i2ccount	;into the byte counter
	rbank0
	
        movlw   low(i2cbuf)     ; address of read/write multi-byte buffer
        bsf     STATUS,IRP      ; set this so fsr points at banks 2&3
        movwf   FSR             ; into fsr register

	;;  generate start		
        rbank1
        bsf     SSPCON2,SEN     ; set start enable bit
        call    i2c_idle
	
        ; load device target
        rbank0                  ; bank 0 selected now
	movlw	b'11010000'	; 1101000 address plus last bit=0 (ie write)
        movwf   SSPBUF          ; device address put into transmit buffer as a write
        call    i2c_idle        ; wait 'till transmit complete

        ; now, if chip was busy, it will not have acknowledged
	;; actually, I don't think the RTC chip ever is busy,
	;; but I had the code, so stick it in
        rbank1                  ; so register bank 1
        btfss   SSPCON2,6       ; this is what the slave sent back
        goto    i2c_rtc_address ; carry on with what we wanted
        bsf     SSPCON2,PEN     ; otherwise generate stop condition
        call    i2c_idle        ; wait for that to happen
	nop
	nop			; no good reason for this
	nop
        goto 	i2c_rtc         ; try again

i2c_rtc_address
	rbank0
	clrf	SSPBUF		;sspbuf set to zero
	call	i2c_idle	;wait for that to go out

	;; from here, the process is the same for rtc or for eeprom
	;; (the difference being only in the addressing)
	;; this is why we loaded the device address and byte count,
	;; so that we can jump into the eeprom routines
	goto	i2ceeprom_data


;#############################
; I2C eeprom read and write routines
; use general registers in bank 2:
;       i2caddr         i2c control byte (7 bit address of chip and R/W)
;       i2chigh         high byte of instruction to chip
;       i2clow          low byte of instruction to chip
;       i2ccount        number of bytes to be read / written
; buffers reads & writes through GPRs 0x0120 - whatever
; note there is no error checking, do not read or write more than 80 bytes
; otherwise you start overwriting other stuff
; also note that this routine disables GIE, so you might need to re-enable gie
;
; to use:
;       set i2caddr with target address and r/w bit (0=write)
;       set i2chigh and i2clow with address bytes
;       set i2ccount with number of bytes to read or write
;       if writing, load 0x0120 and onwards with bytes to transmit
;       call routine i2ceeprom
;       if reading (ie bit 0 of i2caddr was set), 0x0120 onwards contains read bytes
;       re-enable gie if appropriate
;#############################

i2ceeprom
        rbank0                  ; register bank 0
        movf    INTCON,w        ; get current interrupts flag
        rbank2
        movwf   i2ctemp         ; stash that somewhere
        rbank0
        clrf    INTCON          ; switch off all interrupts

        movlw   low(i2cbuf)     ; address of read/write multi-byte buffer
        bsf     STATUS,IRP      ; set this so fsr points at banks 2&3
        movwf   FSR             ; into fsr register

        ; generate start
        rbank1
        bsf     SSPCON2,SEN     ; set start enable bit
        call    i2c_idle
        ; load device target
        rbank2
        movf    i2caddr,w       ; get the device address / control byte
        rbank0                  ; bank 0 selected now
        andlw   b'11111110'     ; force bit 7 low (ie, for a write)
        movwf   SSPBUF          ; device address put into transmit buffer as a write
        call    i2c_idle        ; wait 'till transmit complete

        ; now, if memory chip was busy, it will not have acknowledged
        rbank1                  ; so register bank 1
        btfss   SSPCON2,6       ; this is what the slave sent back
        goto    i2ceeprom_address	; carry on with what we wanted
        bsf     SSPCON2,PEN     ; otherwise generate stop condition
        call    i2c_idle        ; mwait for that to happen
	nop
	nop			;no good reason
	nop
	goto    i2ceeprom       ; try again - not ethat if a problem on the bus, we'll hang here

i2ceeprom_address
        ; now load data -  high byte of memory location
        rbank2
        movf    i2chigh,w
        rbank0                  ; back to bank 0
        movwf   SSPBUF          ; high byte into buffer
        call    i2c_idle        ; wait 'till byte sent
        ; now load 2nd byte data -  low byte of memeory location
        rbank2
        movf    i2clow,w
        rbank0
        movwf   SSPBUF          ; byte into buffer
        call    i2c_idle        ; wait till transmit is over

i2ceeprom_data
        ; now we need to decide if we're reading or writing, and how much
        rbank2
        btfss   i2caddr,0       ; if this is high we are reading
        goto    i2ceeprom_write	;otherwise jump forward to write routine

i2ceeprom_read
        ; generate a repeated start to cancel the write
        rbank1
        bsf     SSPCON2,RSEN    ; generate a restart
        call    i2c_idle        ; wait 'till complete
        ; load device target
        rbank2
        movf    i2caddr,w       ; get the device address / control byte
        rbank0
        movwf   SSPBUF          ; device address put into transmit buffer
        call    i2c_idle        ; wait 'till transmit complete
        ; now program toi receive
i2ceeprom_rbyte
        rbank1
        bsf     SSPCON2,RCEN    ; enable for receive
        call    i2c_idle        ; wait 'till complete (note, this switches to bank 0)
        movf    SSPBUF,w        ; get the data byte
        movwf   INDF            ; write into ram
        incf    FSR,f           ; move forward to the next byte
        ; now, if we want another byte, must acknowledge
        rbank2
        decfsz  i2ccount,f 	; bytes to receive
        goto    i2ceeprom_ack           ; acknowledge & loop
        goto    i2ceeprom_readdone      ; generate a stop
i2ceeprom_ack
        rbank1
        bcf     SSPCON2, ACKDT  ; set ack bit state to 0
        bsf     SSPCON2, ACKEN  ; generate a n acknowledge
        call    i2c_idle        ; wait 'till done
        goto    i2ceeprom_rbyte ; loop around

i2ceeprom_write
        rbank0
        movf    INDF,w          ; get the byte
        movwf   SSPBUF          ; data byte into buffer
        call    i2c_idle        ; wait for transmit to finish
        incf    FSR,f           ; point to next byte for next write
        rbank2
        decfsz  i2ccount,f	; number of bytes still to read
        goto    i2ceeprom_write ; loop around if there's more
        goto    i2ceeprom_writedone     ; otherwise generate a stop

i2ceeprom_readdone
        ; generate  no acknowledge then stop
        rbank1
        bsf     SSPCON2, ACKDT  ; set ack bit state to 1 (ie, no acknowledge)
        bsf     SSPCON2, ACKEN  ; generate a not acknowledge
        call    i2c_idle        ; wait 'till done
i2ceeprom_writedone
        rbank1
        bsf     SSPCON2,PEN     ; generate stop condition
        call    i2c_idle
        rbank2
        movf    i2ctemp,w       ; get saved interrupts setup
        rbank0
        movwf   INTCON          ; re-enable interrupts
        return                  ; note, will return with bank 0 selected

; wait until i2c module is idle
i2c_idle
        rbank0                  ; bank 0
        bcf     PIR1,SSPIF      ; clear any old interrupt flag
        btfss   PIR1,SSPIF      ; check for flag going high
        goto    $-1             ; by looping
        return                  ; return, since must now be idle


	;; #####################
	;; powerbar routine
	;; #####################
powerbar
	;; this builds a vertical bar chart in lcd user defined characters 3 & 4
	;;  3 is upper character, 4 is lower
	rbank0
	;; we want 6 bits (64 states) so rolldown twice times
	rrf	heatpower,w	;power rolled down once
	movwf	temp1
	rrf	temp1,f		;power rolled right twice, but we didn't clean top bits
	movlw	b'00111111'
	andwf	temp1,w		;top bits clean

	sublw	d'63'		;63-n gives number of white segments at top of chart
	;; note that if power=0, will force to 64 white segments furtehr down
	movwf	temp1

	movlw	b'01011000'	; 01=cgram, 011=character3, 000=first byte
	call	lcdsendcmd
	
        movlw   b'11110'
        call    lcdsendchar
        movlw   b'11110'
        call    lcdsendchar
        movlw   b'11110'
        call    lcdsendchar
        movlw   b'11110'
        call    lcdsendchar
        movlw   b'11110'
        call    lcdsendchar
        movlw   b'11110'
        call    lcdsendchar
        movlw   b'11110'
        call    lcdsendchar
        movlw   b'11110'
        call    lcdsendchar
        movlw   b'11110'
        call    lcdsendchar
        movlw   b'11110'
        call    lcdsendchar
        movlw   b'11110'
        call    lcdsendchar
        movlw   b'11110'
        call    lcdsendchar
        movlw   b'11110'
        call    lcdsendchar
        movlw   b'11110'
        call    lcdsendchar
        movlw   b'11110'
        call    lcdsendchar
        movlw   b'11110'
        call    lcdsendchar

	movlw	b'01011000'	; 01=cgram, 011=character3, 000=first byte
	call	lcdsendcmd
	
	movf	temp1,f		;look at value
	btfsc	STATUS,Z	;is it zero?
	goto	bar_done	;if so, solid bar is waht we want

barchartbyte
        decfsz  temp1,f
        goto    bar_test_two
        goto    bar_one_done
bar_test_two
        decfsz  temp1,f
        goto    bar_test_three
	goto	bar_two_done
bar_test_three
        decfsz  temp1,f
        goto    bar_test_four
	goto	bar_three_done
bar_test_four	
        decfsz  temp1,f
        goto    bar_four_loop
bar_four_done
        movlw   b'00000'
        call    lcdsendchar
        goto    bar_done
bar_three_done
        movlw   b'01000'
        call    lcdsendchar
        goto    bar_done
bar_two_done
        movlw   b'01100'
        call    lcdsendchar
        goto    bar_done
bar_one_done
        movlw   b'11100'
        call    lcdsendchar
        goto    bar_done
bar_four_loop
        movlw   b'00000'
        call    lcdsendchar
        goto    barchartbyte
bar_done
	;; just check if power was actually zero
	movf	heatpower,f	;look at power setting
	btfss	STATUS,Z	;was it zero?
	return			;if not, return
	movlw	b'01100111'	;last byte of character 4
	call	lcdsendcmd
	movlw	0
	call	lcdsendchar
	return
	
	


	
	
	;; ####################
	;; main program operation starts here
	;; ####################
	
start				; we should get here very soon on power up

initports
	rbank0			; switch to register bank 0
	clrf	PORTA		; all ports cleared at initialisation	
	clrf	PORTB
	clrf	PORTC
	clrf	PORTD
	clrf	PORTE
	clrf	INTCON		; disable all interrupts

	rbank1			; select register bank 1 for tris values
	
	;; port a is almost all inputs (will be analogue & T0 clock in)
	movlw	b'00011111'	; two unused, front panel light, rest inputs (analogue & T0CKI)	
	movwf	TRISA		; port a control register

	;; port b is LCD control lines (outputs) and user controls
	bsf	OPTION_REG, NOT_RBPU	; disable PORTB pull-ups
	movlw	B'11111000'	; 3 buttons & 2 knob inputs, 3 LCD control outputs 
	movwf	TRISB		; set it into the control byte

	;; port c is assorted mainly outputs
	;; usart requires bits <7:6> to be set
	;; <5>=out (sounder), <4:3> set for I2C, <2:0> are outputs (mosfets & front LED)
	movlw	b'11011000'
	movwf	TRISC

	;; port d is entirely lcd display data, all set to outputs 
	clrf	TRISD		; TRISD all 0 so port d all ouputs

	;; port e all outputs
	clrf	TRISE		; set pins
		; note that TRISE also controls parallel slave port config
		; but they all default to 0 anyway, so this is OK

; adc setup - all off at this stage, but make port E digital
	; power-up state is adcon0 all 0, so converter module off
	;; note that we use porta,5 as a digital output,
	;; but trisa will over-ride ADC settings, so that's OK
	movlw	b'10000011'	; right justified, 4 analogue, one references,
	movwf	ADCON1 

; initially disable all interrupts
	clrf	PIE1		; diable peripheral interrupts
	clrf	PIE2		; disable the rest of the peripheral interrupts
	clrf	INTCON		; disable global interrupts
	bsf	INTCON,PEIE	; peripherals interrupt enable,
				; so can just set peripheral enables as required

	rbank0			; return to register bank 0

	;; flash front panel light before lcd is available
first_post
	bsf	PORTA,5		; heartbeat light on
	movlw	d'70'
	call	delaywms	; short pause
	bcf	PORTA,5		; light off
	movlw	d'250'
	call	delaywms	; longer pause 250ms

initlcd	
	;; lcd is in 8 bit mode
	;; initialisation is a bit cryptic, but is taken from data sheet
	;; note, explicit power-on delay not needed because it's over 250 mS since power-on
	;; the first three steps are explicit, because the busy line is not working
	;; after that, can use the utility functions defined above

	clrf	lcd_data	;  all data starts zero
	bcf	lcd_ctrl,lcd_e	; set control lines low
	bcf	lcd_ctrl,lcd_rw	; set control lines low
	bcf	lcd_ctrl,lcd_rs	; set control lines low

initlcd_step1	
	movlw	b'00110000'	;
	bsf	lcd_ctrl,lcd_e	; latch control state
	nop
	nop
	nop
	bcf	lcd_ctrl,lcd_e	; latch the data
	movlw	d'5'
	call	delaywms	;  required is at least 4.1mS
	
initlcd_step2	
	movlw	b'00110000'	;
	bsf	lcd_ctrl,lcd_e	; latch control state
	nop
	nop
	nop
	bcf	lcd_ctrl,lcd_e	; latch the data
	movlw	d'1'
	call	delaywms	;  required is at least 0.1mS
	
initlcd_step3	
	movlw	b'00110000'	;
	bsf	lcd_ctrl,lcd_e	; latch control state
	nop
	nop
	nop
	bcf	lcd_ctrl,lcd_e	; ltach the data
	movlw	d'1'
	call	delaywms	; not sure if is required, but it seems a good idea
	
initlcd_step4
	movlw	b'00111000'	; 001, 8 bit, 2 line, 5x7, x, x
	call	lcdsendcmd	;  send to the lcd
	
	movlw	b'00001000'	; 00001, display off, underline off, blink off
	call	lcdsendcmd	;  send to the lcd
	
	movlw	b'00000001'	; clear display
	call	lcdsendcmd	;  send to the lcd
	
	movlw	b'00000110'	; 000001, increment cursor, don't shift display
	call	lcdsendcmd	;  send to the lcd

	movlw	b'00001100'	; 00001, display on, underline off, blink off
	call	lcdsendcmd	;  send to the lcd

	;; should now be ready to do something
	;; put up banner
	movlw	low(banner_label)
	movwf	FSR
	movlw	0x08
	call	lcdtext
	call	lcdline2
	movlw	low(init_label)
	movwf	FSR
	movlw	0x08
	call	lcdtext


        movlw   b'01000000'     ; should be command to first character in cgram
        call    lcdsendcmd      ; move to ram

	movlw	b'00100'	;character 0 = vertical bar
	call	lcdsendchar
	movlw	b'00100'
	call	lcdsendchar
	movlw	b'00100'
	call	lcdsendchar
	movlw	b'00100'
	call	lcdsendchar
	movlw	b'00100'
	call	lcdsendchar
	movlw	b'00100'
	call	lcdsendchar
	movlw	b'00100'
	call	lcdsendchar
	movlw	b'00100'
	call	lcdsendchar

	
	movlw   b'00000'        ; this is downwards arrow, left tail
        call    lcdsendchar
        movlw   b'00000'
        call    lcdsendchar
        movlw   b'11000'
        call    lcdsendchar
        movlw   b'00100'
        call    lcdsendchar
        movlw   b'00100'
        call    lcdsendchar
        movlw   b'10101'
        call    lcdsendchar
        movlw   b'01110'
        call    lcdsendchar
        movlw   b'00100'
        call    lcdsendchar

        movlw   b'00000'        ; this is downwards arrow, right tail
        call    lcdsendchar
        movlw   b'00000'
        call    lcdsendchar
        movlw   b'00011'
        call    lcdsendchar
        movlw   b'00100'
        call    lcdsendchar
        movlw   b'00100'
        call    lcdsendchar
        movlw   b'10101'
        call    lcdsendchar
        movlw   b'01110'
        call    lcdsendchar
        movlw   b'00100'
        call    lcdsendchar

	;; characters 3 & 4 are power bar chart, but set inoitial to chequer
        movlw   b'01010'        
        call    lcdsendchar
        movlw   b'10101'        
        call    lcdsendchar
        movlw   b'01010'        
        call    lcdsendchar
        movlw   b'10101'        
        call    lcdsendchar
        movlw   b'01010'        
        call    lcdsendchar
        movlw   b'10101'        
        call    lcdsendchar
        movlw   b'01010'        
        call    lcdsendchar
        movlw   b'10101'        
        call    lcdsendchar

        movlw   b'01010'        
        call    lcdsendchar
        movlw   b'10101'        
        call    lcdsendchar
        movlw   b'01010'        
        call    lcdsendchar
        movlw   b'10101'        
        call    lcdsendchar
        movlw   b'01010'        
        call    lcdsendchar
        movlw   b'10101'        
        call    lcdsendchar
        movlw   b'01010'        
        call    lcdsendchar
        movlw   b'10101'        
        call    lcdsendchar
	
	;; set up mssp for i2c comms to access external eeprom & rtc chip
initi2c
        movlw   b'00101000'     ; x, x, i2c enabled, x, 12c master with manual clock
        movwf   SSPCON          ; set to register
        rbank1                  ; select bank 1
        movlw   d'39'           ; 16MHz clock, 39=100kHz bus (9=400kHz but RTC not work)
        movwf   SSPADD          ; set clock multiplier
        movlw   b'10000000'     ; slew rate off, i2c spec levels, rest are results
        movwf   SSPSTAT         ; set rate and levels
	rbank0
	
        ;; this is probably not needed, but I somehow feel
        ;; that letting the bus settle is a good idea
        movlw   d'5'
	call    delaywms

       ;; now initialise the clock chip
        rbank2
        bsf     i2caddr,0       ;set lsbit so say reading
        call    i2c_rtc         ;get existing clock data

	;; note:
	;; to hard-code a default time, set it into i2cbuf here
	
	;; adjust read time data to ensure chip runs as required
	rbank2
	bcf     i2cbuf,7        ;clear the bit clock halt bit
	;; check if already in 24 hour mode
	btfss	i2cbuf+2,6	;see if currently in 24 hour mode
	goto	rtccontrolbyte	;if so jump forwards
	btfss	i2cbuf+2,5	;test if it is am
	goto	rtcset24	; if so, just change to 24 hour mode
	movlw	b'00011111'	; mask for all but the hours
	andwf	i2cbuf+2,w	; get that into w
	btfss	i2cbuf+2,3	;test if hours were 8 or 9
	goto	rtcadd12	; if not, simply ad 12 in bcd
	andlw	1		;mask all but lsb
	addlw	0x20		;add 20 in bcd
	movwf	i2cbuf+2	;put that in register
	goto	rtcset24	;then change to 24 hour mode
rtcadd12	
	addlw	0x12		;add 12 hours
	movwf	i2cbuf+2	;write that back into clock register
rtcset24	
        bcf     i2cbuf+2,6      ;clear the 12/24 hour bit to set 24 hr mode	
rtccontrolbyte
        movlw   b'10010011'
        movwf   i2cbuf+7        ;set oscillator output at 32768Hz

	;; write modified values back to chip
	bcf     i2caddr,0       ;clear lsbit so that we write
        call    i2c_rtc         ;write that settings into the clock chip


	;; set up usart for broadcast of running commentary
	;; note that does not read anything back from usart
	;; so receive enable bit is not set
initusart
        rbank1
        movlw   b'00100100'     ;x, 8-bit, transmit enabled, asynch, x, high speed, x,x
        movwf   TXSTA           ; configure transmit
        movlw   d'16'           ; for baud rate 57.6k
        movwf   SPBRG           ; set baud rate
        rbank0
        movlw   b'10000000'     ; enabled, 8-bit, x, receive off, no address, x,x,x
        movwf   RCSTA           ; configure general & receive
                ; should now be configured

        ;; as for i2c bus this is probably not needed, but do it anyway
        movlw   d'5'
        call    delaywms

	;; now greet the world
	;; note that this is not interrupt driven - blocks until complete

        ; send out two CR-LF pairs to wake up whatever's on the serial port
        movlw   d'13'           ; carriage return
        movwf   TXREG           ; out serial port
        nop
        btfss   PIR1,TXIF       ; wait 'till it's gone
        goto    $-1
        movlw   d'10'           ; line feed
        movwf   TXREG           ; out serial port
        nop
        btfss   PIR1,TXIF       ; wait 'till it's gone
        goto    $-1
        movlw   d'13'           ; carriage return
        movwf   TXREG           ; out serial port
        nop
        btfss   PIR1,TXIF       ; wait 'till it's gone
        goto    $-1
        movlw   d'10'           ; line feed
        movwf   TXREG           ; out serial port
        nop
        btfss   PIR1,TXIF       ; wait 'till it's gone
        goto    $-1

	;; now talk to the world - just announce 'Roaster'
	movlw	a'R'
	movwf	TXREG
	nop
	btfss	PIR1,TXIF
	goto	$-1
	movlw	a'o'
	movwf	TXREG
	nop
	btfss	PIR1,TXIF
	goto	$-1
	movlw	a'a'
	movwf	TXREG
	nop
	btfss	PIR1,TXIF
	goto	$-1
	movlw	a's'
	movwf	TXREG
	nop
	btfss	PIR1,TXIF
	goto	$-1
	movlw	a't'
	movwf	TXREG
	nop
	btfss	PIR1,TXIF
	goto	$-1
	movlw	a'e'
	movwf	TXREG
	nop
	btfss	PIR1,TXIF
	goto	$-1
	movlw	a'r'
	movwf	TXREG
	nop
	btfss	PIR1,TXIF
	goto	$-1
        movlw   d'13'           ; carriage retuirn
        movwf   TXREG           ; out serial port
        nop
        btfss   PIR1,TXIF       ; wait 'till it's gone
        goto    $-1
        movlw   d'10'           ; line feed
        movwf   TXREG           ; out serial port
        nop
        btfss   PIR1,TXIF       ; wait 'till it's gone
        goto    $-1
	
	;; initialise timer 0
	;; clock chip should be providing 256Hz signal
	;; (actually, clock chip provides 32768Hz and separate divider drops it to 256Hz)
initt0
	rbank0
	clrf    TMR0            ; start with zeroed timer
        rbank1
        bsf     OPTION_REG,PSA  ; prescaler set to wdt, not timer
        bcf     OPTION_REG,T0SE ; increment on low-to-high
        bsf     OPTION_REG,T0CS ; use t0cki pin to trigger
	rbank0

	
second_post
	movlw	d'250'
	call	delaywms	; longer pause 250ms
	bsf	PORTA,5		; heartbeat light on
	movlw	d'70'
	call	delaywms	; short pause
	bcf	PORTA,5		; light off
	movlw	d'70'
	call	delaywms	; short pause
	bsf	PORTA,5		; light on
	movlw	d'70'
	call	delaywms	; short pause
	bcf	PORTA,5
	movlw	d'70'
	call	delaywms	; short pause
	
	;;  at this point everything is initialised
	;; so jump to main program block
	ppage1
	goto	idlemodestart

	;; #######################
	;; program page 1
	;; #######################

	org	0x0800
idlemodestart
	rbank0			;this already set, but just to be sure...
	;; switch off buttons and timer 0 interrupt
	bcf	INTCON,RBIE
	bcf	INTCON,T0IE

	;; switch off all heat and all fans
	clrf	PORTE		; SSR, top element fan, rotisserie off
	bcf	PORTC,1		; door eject off
	bcf	PORTC,2		; cooling fans off
	bcf	PORTA,5		;front panel light off

	movlw	1		;start at step 1 TODO01
	movwf	prognum
	
standbyloop
	rbank0
	;; find what step selected prognum matches
	movf	prognum,w
	rbank2
	movwf	i2clow
	clrf	i2chigh
	movlw	1
	movwf	i2ccount
        movlw   b'10100001'     ; memory, chip 0, read
        movwf   i2caddr         ; control byte set
	ppage0
	call	i2ceeprom	; get the byte from the eeprom
	;; get starting step for current program from lookup
	rbank2
	movf	low(i2cbuf),w	;offset to get to i2cbuf
	rbank0
	movwf	progstep	;put it into program step register

	;; now set up to read that step
        swapf   progstep,w      ; swap nybbles of step into w
        andlw   b'00001111'     ; and mask out upper nybble, so w now has high nybble of progstep
        rbank2                  ; select register bank 2
        movwf   i2chigh         ; and put that in high byte
        rbank0                  ; back to bank 0
        swapf   progstep,w      ; swap nybbles of step into w
        andlw   b'11110000'     ; and mask out lower nybble, so w now has (low nybble of progstep)*16
        rbank2                  ; select register bank 2
        movwf   i2clow          ; and put that in low byte
        ;i2chigh:i2clow now contain step*16
        bcf     STATUS,C        ; clear carry
        rlf     i2clow,f        ; low*2
        rlf     i2chigh,f       ; high*2 so bytes have proper address
	movlw   b'10100001'     ; memory, chip 0, read
        movwf   i2caddr         ; control byte set
	movlw	d'32'
	movwf	i2ccount
	call    i2ceeprom       ; do the read
	rbank0

	;; top line of display is just the 'run from' label
	call	lcdclear
	bcf	STATUS,IRP
	movlw	low(standby_label)
	movwf	FSR
	movlw	8
	call	lcdtext

	;; second line of display
	call	lcdline2

	;; display program number
	clrf	highb
	movf	prognum,w
	movwf	lowb
	movlw	lowb
	movwf	FSR
	bcf	STATUS,IRP
	call	b2bcd		;convert counter to bcd
	;; put that on screen
	swapf	bcdut,w
	andlw	0x0f
	addlw	a'0'		; so w now has ascii code for tens digit
	call	lcdsendchar	;  send it
	movf	bcdut,w
	andlw	0x0f
	addlw	a'0'		; so w now has ascii code for units digit
	call	lcdsendchar	;  send it

	movlw	a'-'
	call	lcdsendchar

	;; display program step
	clrf	highb
	movf	progstep,w
	movwf	lowb
	;; convert to bcd
	movlw	lowb
	movwf	FSR
	bcf	STATUS,IRP
	call	b2bcd		;convert counter to bcd
	;; put that on display
	movf	bcdht,w
	andlw	0x0f
	addlw	a'0'		; so w now has ascii code for hundreds digit
	call	lcdsendchar	;  send it
	swapf	bcdut,w
	andlw	0x0f
	addlw	a'0'		; so w now has ascii code for tens digit
	call	lcdsendchar	;  send it
	movf	bcdut,w
	andlw	0x0f
	addlw	a'0'		; so w now has ascii code for units digit
	call	lcdsendchar	;  send it

	movlw	a':'
	call	lcdsendchar
	movlw	a' '
	call	lcdsendchar

	;; display step name
	movlw	low(i2cbuf+d'24')
	movwf	FSR
	bsf     STATUS,IRP
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar
	bcf	STATUS,IRP

standbywait
	ppage0		     ;we need this because sometimes do a goto to standbywait
	;; now we need to wait for a button to be pressed
	call waitpress
	;; so now portbbuff has button status and if bit0 is set, button was pressed > 1 sec
	;; decide what we should do about it
	ppage1
	btfsc	portbbuff,0
	goto	standbylongpress
standbyshortpress

	btfsc	portbbuff,4	;indicates knob changed
	goto	standbyknob
	btfsc	portbbuff,5		;button by knob runs the current program
	goto	runmode
	btfsc	portbbuff,6		;right button does nothing
	goto	standbywait
	btfsc	portbbuff,7		;left button jumps to prog1
	goto	standbystep1
	goto	standbywait
standbylongpress	
	btfsc	portbbuff,5 		;button by knob engages program mode
	goto	standbygotoprogmode
	btfsc	portbbuff,6		;right button goes to settings mode
	goto	settingsmode 
	btfsc	portbbuff,7		;left button goes to manualmode
	goto	standbygotomanual
	goto	standbywait

runmode
	ppage2
	goto	runprog

	
standbyknob
	btfss	portbbuff,3
	goto	standbyminus
standbyplus
	incf	prognum,f	;add one to program number
	btfss	prognum,6	;check if that gets us to 64
	goto	standbyloop
standbystep1			;long press on left button also gets here
	movlw	1		;if we got to 64 loop to 1
	movwf	prognum
	goto	standbyloop
standbyminus
	decfsz	prognum,f	;subtract one and check if got to zero
	goto	standbyloop
	movlw	d'63'		;if we got to zero, loop round to 63
	movwf	prognum
	goto	standbyloop

standbygotoprogmode
	ppage3			;program mode is in program page 2
	goto	progmodestart	; go there


	
standbygotomanual		;currently does nothing
	goto	standbyloop




settingsmode
	;; this does such things as set the time and erase all memory
set01	
	ppage0
	rbank0
	call	lcdclear
	bcf	STATUS,IRP
	movlw	low(setmode01)	; asks about setting clock
	movwf	FSR
	movlw	8
	call	lcdtext
set01row2
	ppage0
	call	lcdline2
	movlw	low(yesnolabel)	;
	movwf	FSR
	movlw	8
	call	lcdtext
set01wait
	;; now check button-presses
	ppage0
	call	waitpress	; also waits for knob change
	;; now process key press
	ppage1
	btfsc	portbbuff,6	;right button adances to next question
	goto	set02
	btfsc	portbbuff,7	;left button is 'yes' - set clock
	goto	set01gettime	;
	goto	set01wait	; if something else, simply ignore keypress

set01gettime
	;; first get the current time
	ppage0
	rbank2
	bsf	i2caddr,0	;set ls bit so we read time
	call	i2c_rtc
	rbank0
	clrf	pmflags

	
set01showtime
	;; put that on screen
	ppage0
	call	lcdclear
	movlw	b'00001100'	; 00001, display on, underline off, blink off
	call	lcdsendcmd	;  send to the lcd
	movlw	a' '
	call	lcdsendchar
	movlw	a' '
	call	lcdsendchar

	
	;; day name
	rbank2
	bcf	STATUS,C
	rlf	low(i2cbuf+3),w	;so w has day of week * 2
	addlw	low(dowlabels-2)	;these are in order, so now pointing to right one
	movwf	FSR
	movlw	2		;two pairs of characters
	call	lcdtext		;onto screen
	
	;; date
	rbank2
	swapf	low(i2cbuf+4),w
	andlw	0x0f
	addlw	a'0'
	call	lcdsendchar
	rbank2
	movf	low(i2cbuf+4),w
	andlw	0x0f
	addlw	a'0'
	call	lcdsendchar
	movlw	a'/'
	call	lcdsendchar
	rbank2
	swapf	low(i2cbuf+5),w
	andlw	0x0f
	addlw	a'0'
	call	lcdsendchar
	rbank2
	movf	low(i2cbuf+5),w
	andlw	0x0f
	addlw	a'0'
	call	lcdsendchar
	movlw	a'/'
	call	lcdsendchar
	rbank2
	swapf	low(i2cbuf+6),w
	andlw	0x0f
	addlw	a'0'
	call	lcdsendchar
	rbank2
	movf	low(i2cbuf+6),w
	andlw	0x0f
	addlw	a'0'
	call	lcdsendchar

	;; second line
	call	lcdline2
	movlw	a' '
	call	lcdsendchar
	movlw	a' '
	call	lcdsendchar
	movlw	a' '
	call	lcdsendchar
	movlw	a' '
	call	lcdsendchar

	;; time
	rbank2
	swapf	low(i2cbuf+2),w
	andlw	0x0f
	addlw	a'0'
	call	lcdsendchar
	rbank2
	movf	low(i2cbuf+2),w
	andlw	0x0f
	addlw	a'0'
	call	lcdsendchar
	movlw	a':'
	call	lcdsendchar
	rbank2
	swapf	low(i2cbuf+1),w
	andlw	0x0f
	addlw	a'0'
	call	lcdsendchar
	rbank2
	movf	low(i2cbuf+1),w
	andlw	0x0f
	addlw	a'0'
	call	lcdsendchar
	movlw	a':'
	call	lcdsendchar
	rbank2
	swapf	low(i2cbuf),w
	andlw	0x0f
	addlw	a'0'
	call	lcdsendchar
	rbank2
	movf	low(i2cbuf),w
	andlw	0x0f
	addlw	a'0'
	call	lcdsendchar

	;; now need to show cursor
	movlw	b'00001110'	; 00001, display on, underline on, blink off
	call	lcdsendcmd	;  send to the lcd
	
	;; computed lookup table coming up
	;; if we are too close to a rollover of pcl put in an org
	;; allow space for 20 instructions to end of lookup table
	if (low($)>d'235')	;
	   org ((high($)+1)*0x100)
	endif
	
	ppage1
	movlw	high(set01table)
	movwf	PCLATH
	movf	pmflags,w		; so w has flags value
	addwf	PCL,f		; add that into prog counter - gives computed goto
set01table
	goto	set01seconds
	goto	set01minutes
	goto	set01hours


set01seconds
	movlw	0x4B
	ppage0
	call	lcdaddress
	;; now wait for something to happen
	call	waitpress
	ppage1
	btfsc	portbbuff,4	;indicates knob changed
	goto	set01secknob
	btfsc	portbbuff,5	;button by knob proceeds to deal with this step
	goto	set01write
	btfsc	portbbuff,6	;right button increments the step to edit
	goto	set01secR
	btfsc	portbbuff,7	;left button decrements the step
	goto	set01secL	;
	goto	set01seconds	;shouldn't get here, but just in case, simply ignore keypress
set01secknob
	btfss	portbbuff,3
	goto	set01seckminus
set01seckplus
	rbank2	
	incf	low(i2cbuf+0),f	;increment in place
	movf	low(i2cbuf+0),w	;get the incremented value
	andlw	0x0f		;mask out upper nybble
	sublw	d'9'		;so now has 9 - units digit, should be +ve or zero
	btfsc	STATUS,C
	goto	set01showtime	;C set, so +ve or zero, so OK
	movf	low(i2cbuf+0),w	;get incremented count
	addlw	6		;this converts 0x0A to 0x10 (etc)
	movwf	low(i2cbuf+0)	;put that in
	sublw	0x60		; have we got to 60?
	btfss	STATUS,Z
	goto	set01showtime
	clrf	low(i2cbuf+0)
	goto	set01minkplus
set01seckminus	
	rbank2
	movf	low(i2cbuf+0),f	;look at byte
	btfsc	STATUS,Z
	goto	set01seckmzero
	decf	low(i2cbuf+0),f
	movf	low(i2cbuf+0),w
	andlw	0x0f
	sublw	d'9'	
	btfsc	STATUS,C
	goto	set01showtime
	movf	low(i2cbuf+0),w
	addlw	d'-6'
	movwf	low(i2cbuf+0)
	goto	set01showtime
set01seckmzero
	movlw	0x59
	movwf	low(i2cbuf+0)
	goto	set01minkminus

set01secR
	rbank0
	movlw	2
	movwf	pmflags
	goto	set01showtime

set01secL
	rbank0
	movlw	1
	movwf	pmflags
	goto	set01showtime

set01minutes
	movlw	0x48
	ppage0
	call	lcdaddress
	;; now wait for something to happen
	call	waitpress
	ppage1
	btfsc	portbbuff,4	;indicates knob changed
	goto	set01minknob
	btfsc	portbbuff,5	;button by knob proceeds to deal with this step
	goto	set01write
	btfsc	portbbuff,6	;right button increments the step to edit
	goto	set01minR
	btfsc	portbbuff,7	;left button decrements the step
	goto	set01minL	;
	goto	set01minutes	;shouldn't get here, but just in case, simply ignore keypress
set01minknob
	btfss	portbbuff,3
	goto	set01minkminus
set01minkplus
	rbank2	
	incf	low(i2cbuf+1),f	;increment in place
	movf	low(i2cbuf+1),w	;get the incremented value
	andlw	0x0f		;mask out upper nybble
	sublw	d'9'		;so now has 9 - units digit, should be +ve or zero
	btfsc	STATUS,C
	goto	set01showtime	;C set, so +ve or zero, so OK
	movf	low(i2cbuf+1),w	;get incremented count
	addlw	6		;this converts 0x0A to 0x10 (etc)
	movwf	low(i2cbuf+1)	;put that in
	sublw	0x60		; have we got to 60?
	btfss	STATUS,Z
	goto	set01showtime
	clrf	low(i2cbuf+1)
	goto	set01hourkplus
set01minkminus	
	rbank2
	movf	low(i2cbuf+1),f	;look at byte
	btfsc	STATUS,Z
	goto	set01minkmzero
	decf	low(i2cbuf+1),f
	movf	low(i2cbuf+1),w
	andlw	0x0f
	sublw	d'9'	
	btfsc	STATUS,C
	goto	set01showtime
	movf	low(i2cbuf+1),w
	addlw	d'-6'
	movwf	low(i2cbuf+1)
	goto	set01showtime
set01minkmzero
	movlw	0x59
	movwf	low(i2cbuf+1)
	goto	set01hourkminus

set01minR
	rbank0
	movlw	0
	movwf	pmflags
	goto	set01showtime

set01minL
	rbank0
	movlw	2
	movwf	pmflags
	goto	set01showtime


set01hours
	movlw	0x45
	ppage0
	call	lcdaddress
	;; now wait for something to happen
	call	waitpress
	ppage1
	btfsc	portbbuff,4	;indicates knob changed
	goto	set01hourknob
	btfsc	portbbuff,5	;button by knob proceeds to deal with this step
	goto	set01write
	btfsc	portbbuff,6	;right button increments the step to edit
	goto	set01hourR
	btfsc	portbbuff,7	;left button decrements the step
	goto	set01hourL	;
	goto	set01hours	;shouldn't get here, but just in case, simply ignore keypress
set01hourknob
	btfss	portbbuff,3
	goto	set01hourkminus
set01hourkplus
	rbank2	
	incf	low(i2cbuf+2),f	;increment in place
	movf	low(i2cbuf+2),w	;get the incremented value
	andlw	0x0f		;mask out upper nybble
	sublw	d'9'		;so now has 9 - units digit, should be +ve or zero
	btfsc	STATUS,C
	goto	set01check24	;C set, so +ve or zero, so OK
	movf	low(i2cbuf+2),w	;get incremented count
	addlw	6		;this converts 0x0A to 0x10 (etc)
	movwf	low(i2cbuf+2)	;put that in
set01check24
	movf	low(i2cbuf+2),w	; we need to do this in case we jumped to label
	sublw	0x24		; have we got to 24?
	btfss	STATUS,Z
	goto	set01showtime
	movlw	0x23
	movwf	low(i2cbuf+2)
	movlw	0x59
	movwf	low(i2cbuf+1)
	movwf	low(i2cbuf+0)
	goto	set01showtime
set01hourkminus	
	rbank2
	movf	low(i2cbuf+2),f	;look at byte
	btfsc	STATUS,Z
	goto	set01hourkmzero
	decf	low(i2cbuf+2),f
	movf	low(i2cbuf+2),w
	andlw	0x0f
	sublw	d'9'	
	btfsc	STATUS,C
	goto	set01showtime
	movf	low(i2cbuf+2),w
	addlw	d'-6'
	movwf	low(i2cbuf+2)
	goto	set01showtime
set01hourkmzero
	clrf	low(i2cbuf+2)
	clrf	low(i2cbuf+1)
	clrf	low(i2cbuf+0)
	goto	set01showtime

set01hourR
	rbank0
	movlw	1
	movwf	pmflags
	goto	set01showtime

set01hourL
	rbank0
	movlw	0
	movwf	pmflags
	goto	set01showtime

set01write
	ppage0
	rbank2
	bcf	i2caddr,0	; clear ls bit so we write time
	call	i2c_rtc		;do the write
	ppage1
	goto	idlemodestart	;note that if we set time, we jump back to idel mode
	
set02









set90
	;; this should be last setp in settings mode
	;; is erasing all memory
	ppage0
	rbank0
	call	lcdclear
	bcf	STATUS,IRP
	movlw	low(setmode90)	; asks whether to erase programs
	movwf	FSR
	movlw	8
	call	lcdtext
set90row2
	ppage0
	call	lcdline2
	movlw	low(yesnolabel)	;
	movwf	FSR
	movlw	8
	call	lcdtext
set90wait
	;; now check button-presses
	ppage0
	call	waitpress	; also waits for knob change
	;; now process key press
	ppage1
	btfsc	portbbuff,6	;right button adances to next question
	goto	set99
	btfsc	portbbuff,7	;left button is 'yes' - advane to check question
	goto	set91	;
	goto	set90wait	; if something else, simply ignore keypress


set91
	;; confirmation prior to erasing all memory
	ppage0
	rbank0
	call	lcdclear
	bcf	STATUS,IRP
	movlw	low(setmode91)	; asks whether to erase programs
	movwf	FSR
	movlw	8
	call	lcdtext
set91row2
	ppage0
	call	lcdline2
	movlw	low(yesnolabel)	;
	movwf	FSR
	movlw	8
	call	lcdtext
set91wait
	;; now check button-presses
	ppage0
	call	waitpress	; also waits for knob change
	;; now process key press
	ppage1
	btfsc	portbbuff,6	; right button odes erase
	goto	set92
	btfsc	portbbuff,7	; left button is 'yes' - cancel so bail out
	goto	set99	;
	goto	set91wait	; if something else, simply ignore keypress



set92
	;; it has been confirmed to erase all program memory
	ppage0
	call	lcdclear
	bcf	STATUS,IRP
	movlw	low(setmode92)	; is the 'wait, erasing' message
	movwf	FSR
	movlw	8
	call	lcdtext

	;; erase 32 bytes of i2cbuffer
	rbank2
        clrf    i2cbuf
        clrf    i2cbuf+0x1
        clrf    i2cbuf+0x2
        clrf    i2cbuf+0x3
        clrf    i2cbuf+0x4
        clrf    i2cbuf+0x5
        clrf    i2cbuf+0x6
        clrf    i2cbuf+0x7
        clrf    i2cbuf+0x8
        clrf    i2cbuf+0x9
        clrf    i2cbuf+0xA
        clrf    i2cbuf+0xB
        clrf    i2cbuf+0xC
        clrf    i2cbuf+0xD
        clrf    i2cbuf+0xE
        clrf    i2cbuf+0xF
        clrf    i2cbuf+0x10
        clrf    i2cbuf+0x11
        clrf    i2cbuf+0x12
        clrf    i2cbuf+0x13
        clrf    i2cbuf+0x14
        clrf    i2cbuf+0x15
        clrf    i2cbuf+0x16
        clrf    i2cbuf+0x17
        clrf    i2cbuf+0x18
        clrf    i2cbuf+0x19
        clrf    i2cbuf+0x1A
        clrf    i2cbuf+0x1B
        clrf    i2cbuf+0x1C
        clrf    i2cbuf+0x1D
        clrf    i2cbuf+0x1E
        clrf    i2cbuf+0x1F

	;; initialise paramters
        movlw   b'10100000'     ; memory, chip 0, write
        movwf   i2caddr         ; control byte set
        clrf    i2clow
        clrf    i2chigh

	rbank0
	clrf	temp1
	movlw	0xff
	movwf	temp2
	
set92dowrite
	rbank2
        movlw   b'10100000'     ; memory, chip 0, write
        movwf   i2caddr         ; control byte set

        movlw   d'32'
        movwf   i2ccount

	ppage0
        call    i2ceeprom

	rbank0
	ppage1
        incfsz  temp1,f
        goto    set92donext
	goto	idlemodestart		;have erased everything so exit settings mode

set92donext
	rbank2
        movlw   d'32'
        addwf   i2clow,f
        btfsc   STATUS,Z
        incf    i2chigh,f
        goto    set92dowrite

set99
	
	
setend
	;; if we get here simply return to idle mode
	ppage1
	goto	idlemodestart


	;; #######################
	;; program page 2
	;; #######################

	org	0x1000
	
	;; this is routine that sets a program running
runprog
	rbank0
	clrf	el_secs		;clear elapsed time counters
	clrf	el_minsl
	clrf	el_minsh

	;; report that have read step
	rbank0to2
	movlw	a'R'
	movwf	i2cbuf+.40
	movlw	a'u'
	movwf	i2cbuf+.41
	movlw	a'n'
	movwf	i2cbuf+.42
	movlw	a' '
	movwf	i2cbuf+.43
	movlw	a'p'
	movwf	i2cbuf+.44
	movlw	a'r'
	movwf	i2cbuf+.45
	movlw	a'o'
	movwf	i2cbuf+.46
	movlw	a'g'
	movwf	i2cbuf+.47
	movlw	d'13'
	movwf	i2cbuf+.48
	movlw	d'10'
	movwf	i2cbuf+.49

	movlw	.10
	movwf	usartcount
	movlw	low(i2cbuf+.40)
	movwf	usartaddr

	bsf	INTCON,PEIE	;enable peripheral interrupts
	bsf	INTCON,GIE	;enable interrupts globally
	rbank1
	bsf	PIE1,TXIE	;enable serial port, should now transmit
	rbank0

	clrf	elt_h		;clear elapsed this step
	clrf	elt_l	

	bcf	INTCON,T0IF	;clear timer overflow flag
	clrf	TMR0		;clear timer clock

	;; idle mode has already read the step into i2cbuf
	;; so jump to the pre-calculation step
	goto	precalc

nextprogstep
	;; read the next step from the i2c eeprom
	rbank2
	movf	i2cbuf+d'22',w	;this is number of next step
	rbank0
	movwf	progstep	;set it into the register as current step
	btfss	STATUS,Z
	goto	readnextstep
	;; if nextstep=0, then program is over
	ppage1
	goto	idlemodestart	;return to idle mode as if after power-on init
	
readnextstep	
	;; now set up to read that step
        swapf   progstep,w      ; swap nybbles of step into w
        andlw   b'00001111'     ; and mask out upper nybble, so w now has high nybble of progstep
        rbank2                  ; select register bank 2
        movwf   i2chigh         ; and put that in high byte
        rbank0                  ; back to bank 0
        swapf   progstep,w      ; swap nybbles of step into w
        andlw   b'11110000'     ; and mask out lower nybble, so w now has (low nybble of progstep)*16
        rbank2                  ; select register bank 2
        movwf   i2clow          ; and put that in low byte
        ;i2chigh:i2clow now contain step*16
        bcf     STATUS,C        ; clear carry
        rlf     i2clow,f        ; low*2
        rlf     i2chigh,f       ; high*2 so bytes have proper address
	movlw   b'10100001'     ; memory, chip 0, read
        movwf   i2caddr         ; control byte set
	movlw	d'32'
	movwf	i2ccount
	ppage0
	call    i2ceeprom       ; do the read
	ppage2
	rbank0

	;; zero the elapsed-this-step count
	clrf	elt_h
	clrf	elt_l

	;; report that have read step
	rbank0to2
	movlw	a'S'
	movwf	i2cbuf+.40
	movlw	a't'
	movwf	i2cbuf+.41
	movlw	a'e'
	movwf	i2cbuf+.42
	movlw	a'p'
	movwf	i2cbuf+.43
	movlw	a' '
	movwf	i2cbuf+.44
	movlw	a'r'
	movwf	i2cbuf+.45
	movlw	a'e'
	movwf	i2cbuf+.46
	movlw	a'a'
	movwf	i2cbuf+.47
	movlw	a'd'
	movwf	i2cbuf+.48
	movlw	d'13'
	movwf	i2cbuf+.49
	movlw	d'10'
	movwf	i2cbuf+.50

	;; TODO03	output at least program step number, name to usart
	;; probably also start and end temps and duration
	
	movlw	.11
	movwf	usartcount
	movlw	low(i2cbuf+.40)
	movwf	usartaddr

	bsf	INTCON,PEIE	;enable peripheral interrupts
	bsf	INTCON,GIE	;enable interrupts globally
	rbank1
	bsf	PIE1,TXIE	;enable serial port, should now transmit
	
precalc
	;; calculates values that are unchanging for duration of
	;; the current program step

	;; power limits are saved as 0-100, need to be converted to 0-256
	;; but limited to 255 (ie, max single-byte value)
	;; put these into i2cbuf+.34 for max and i2cbuf+.35 for min
	;; actually calculate 2 9/16 x value,

	rbank2
pcalcmax	
	bcf	STATUS,C
	rlf	i2cbuf+.15,w
	movwf	i2cbuf+.34	;limit x 2 in register
	bcf	STATUS,C
	rrf	i2cbuf+.15,w
	addwf	i2cbuf+.34,f	;limit x 2.5 in register
	bcf	STATUS,C
	rrf	i2cbuf+.15,w	;limit x 0.5 in W
	movwf	i2cbuf+.32	;limit x 1/2 in temporary space
	bcf	STATUS,C
	rrf	i2cbuf+.32,f	;limit x 1/4
	bcf	STATUS,C
	rrf	i2cbuf+.32,f	;limit x 1/8
	bcf	STATUS,C
	rrf	i2cbuf+.32,w	;limit x 1/16 in w
	addwf	i2cbuf+.34,f	;added into register
	btfss	STATUS,C
	goto	pcalcmin
	movlw	0xff
	movwf	i2cbuf+.34

pcalcmin	
	bcf	STATUS,C
	rlf	i2cbuf+.16,w
	movwf	i2cbuf+.35	;limit x 2 in register
	bcf	STATUS,C
	rrf	i2cbuf+.16,w
	addwf	i2cbuf+.35,f	;limit x 2.5 in register
	bcf	STATUS,C
	rrf	i2cbuf+.16,w	;limit x 0.5 in W
	movwf	i2cbuf+.32	;limit x 1/2 in temporary space
	bcf	STATUS,C
	rrf	i2cbuf+.32,f	;limit x 1/4
	bcf	STATUS,C
	rrf	i2cbuf+.32,f	;limit x 1/8
	bcf	STATUS,C
	rrf	i2cbuf+.32,w	;limit x 1/16
	addwf	i2cbuf+.35,f	;added into register
	btfss	STATUS,C
	goto	runloop
	movlw	0xff
	movwf	i2cbuf+.35
	
	
	;; main running loop, ie roaster is running
runloop
	rbank0
	bsf	PORTA,5
	bsf	PORTA,5		;front panel light on while we work

	;; some information sent out usart is calculated as we go along
	;; so pre-load the usart buffer with constants / defaults
	;; note that spaces in first 20 characters done later so do not overwrite
	;; 'read step' message, which could still be buffering out at this time
	rbank2
	movlw	a' '
	movwf	i2cbuf+.40+.23
	movwf	i2cbuf+.40+.25
	movwf	i2cbuf+.40+.28
	movwf	i2cbuf+.40+.30
	movlw	a'-'
	movwf	i2cbuf+.40+.24	;this is flag to say if power limit applied
	movlw	a'_'
	movwf	i2cbuf+.40+.26	;this is flag to say if drum is running
	movwf	i2cbuf+.40+.27	;this is flag to say if fan is running
	movwf	i2cbuf+.40+.29	;this is flag to say if door is opening
	movwf	i2cbuf+.40+.31	;this is flag to say if cooling is running
	movlw	a'?'
	movwf	i2cbuf+.40+.20	;this is power display, not yet set
	movwf	i2cbuf+.40+.21
	movwf	i2cbuf+.40+.22
	
	
get_temps
	rbank0
	; read the on-board temperature (cold junction)
        movlw   b'10000001'     ; switch on adc channel 0, Fosc/32
        movwf   ADCON0
        ; need a 20 uS wait, 80 instructions
        movlw   d'27'
        movwf   temp1
        decfsz  temp1,f 	; 1 cycle
        goto    $-1             ; +2 = 3cycles*27=81 cycles
        ; set conversion running
        bsf     ADCON0,2        ; set conversion running
        btfsc   ADCON0,2        ; poll the bit
        goto    $-1             ; loop 'till it's clear
        movf    ADRESH,w        ; get high byte
        movwf   t0_h            ; save it in temperature register
        rbank1
        movf    ADRESL,w        ; get low byte
        rbank0
        movwf   t0_l            ; save it in temperature register

        clrf    t1_h            ; clear the registers holding temps 1 and 2
        clrf    t1_l
        clrf    t2_h
        clrf    t2_l

        movlw   d'4'
        movwf   temp2           ; use to count number of times we sample ADCs

        ; the two thermocouple inputs are read 4 times and summed
        ; this is equivalent to using average of 4 reads as value
        ; note we want to multiply the read value by four anyway, so this is convenient
t1_t2_read
        ; wait 3.2uS before next acquisition - 13 instruction cycles
        movlw   d'5'
        movwf   temp1
        decfsz  temp1,f 	; 1 cycle
        goto    $-1             ; +2 = 3cycles*5=15 cycles
        ; obtain temperature sensor 1 value
        movlw   b'10001001'     ; switch on adc channel 1, Fosc/32
        movwf   ADCON0
        ; need a 20 uS wait, 80 instructions
        movlw   d'27'
        movwf   temp1
        decfsz  temp1,f 	; 1 cycle
        goto    $-1             ; +2 = 3cycles*27=81 cycles
        ; set conversion running
        bsf     ADCON0,2        ; set conversion running
        btfsc   ADCON0,2        ; poll the bit
        goto    $-1             ; loop 'till it's clear
        bcf     STATUS,C        ; clear carry so we don't bring anything in accidently
        rbank1
        movf    ADRESL,W        ; get low byte of ADC result
        rbank0
        addwf   t1_l,f          ; add low sample to total
        btfsc   STATUS,C        ; check carry
        incf    t1_h,f          ; increment high byte if set
        movf    ADRESH,W        ; get high byte of ADC result
        addwf   t1_h,f          ; add to total

        ; wait 3.2uS before next acquisition - 13 instruction cycles
        movlw   d'5'
        movwf   temp1
        decfsz  temp1,f ; 1 cycle
        goto    $-1             ; +2 = 3cycles*5=15 cycles
        ; obtain temperature sensor 1 value
        movlw   b'10010001'     ; switch on adc channel 2, Fosc/32
        movwf   ADCON0
        ; need a 20 uS wait, 80 instructions
        movlw   d'27'
        movwf   temp1
        decfsz  temp1,f ; 1 cycle
        goto    $-1             ; +2 = 3cycles*27=81 cycles
        ; set conversion running
        bsf     ADCON0,2        ; set conversion running
        btfsc   ADCON0,2        ; poll the bit
        goto    $-1             ; loop 'till it's clear
        bcf     STATUS,C        ; clear carry so we don't bring anything in accidently
        rbank1
        movf    ADRESL,w        ; get low byte of ADC result
        rbank0
        addwf   t2_l,f          ; add low sample to total
        btfsc   STATUS,C        ; check carry
        incf    t2_h,f          ; increment high byte if set
        movf    ADRESH,W        ; get high byte of ADC result
        addwf   t2_h,f          ; add to total

        decfsz  temp2,f         ; count down to zero
        goto    t1_t2_read      ; loop so we do four reads

        ; add t0 to t1 and t2 to give direct reading of t1 & t2
        movf    t0_l,w
        addwf   t1_l,f
        btfsc   STATUS,C
        incf    t1_h,f
        movf    t0_h,w
        addwf   t1_h,f

        movf    t0_l,w
        addwf   t2_l,f
        btfsc   STATUS,C
        incf    t2_h,f
        movf    t0_h,w
        addwf   t2_h,f
	
	;; now t0 should have controller ambient temperature
	;; and t1, t2 should have thermocouple temperatures
	;; t0 t1 and t2 all in tenths of degree C

	;; let the measured temperature (for calcs & display) = t2
	;; TODO02 could have a better definintion of measured temperature
	movf	t2_l,w
	movwf	td_l
	movf	t2_h,w
	movwf	td_h

	;; do calculations
	
calculate_tt
	rbank2
	movf	i2cbuf+4,w
	xorlw	0xff
	rbank0
	movwf	w_a		;inverse of step start temperature low byte into w_a
	rbank2
	movf	i2cbuf+5,w
	xorlw	0xff
	rbank0
	movwf	w_b		;inverse of step start t high byte into w_b
	movlw	1
	addwf	w_a,f		;add 1 to give 2s complemet
	btfsc	STATUS,Z
	incf	w_b,f
	;; w_a:b now has 0 - step start temperature
	rbank2
	movf	i2cbuf+6,w	;low byte of end temperature
	rbank0
	addwf	w_a,f
	btfsc	STATUS,C	;was carry set?
	incf	w_b,f		;if so increment high byte too
	rbank2
	movf	i2cbuf+7,w	;high byte of end temperature
	rbank0
	addwf	w_b,f
	;; w_a:b now has ((step end t) - (step start t))
	;; note that this may be negative

	movf	w_a,w		;copy (end-start) into target temperature register
	movwf	tt_l
	movf	w_b,w
	movwf	tt_h

	;; now do 16 bit multiply
	;;  zero where answer will build
	clrf	w_a		;this is lsB
	clrf	w_b
	clrf	w_c
	clrf	w_d		;
	clrf	w_e		; msB (clearing this is redundant - it gets overwritten below)
	;; nice little trick from from Bob Fehrenbach & Scott Dattalo c/o piclist
	bsf	w_b,7		;when this drops out from rrf routine is at end
	;; use temp1 as negative flag
	clrf	temp1

	btfss	tt_h,7		;is tt_a:b negative?
	goto	tt_positive	;if not, jump forward
	;; tt is negative, invert it
	incf	temp1,f		;set flag to remember to invert answer
	movlw	0xff
	xorwf	tt_h,f
	xorwf	tt_l,f
	incf	tt_l,f
	btfsc	STATUS,Z
	incf	tt_h,f		;tt_h:l is now +ve
	bcf	STATUS,C
tt_positive	
	rrf	tt_h,f
	rrf	tt_l,f
	btfss	STATUS,C
	goto	tt_nextbit
	movf	elt_l,w
	addwf	w_c,f
	movf	elt_h,w
	btfsc	STATUS,C
	incfsz	elt_h,w
	addwf	w_d,f
tt_nextbit
	bcf	STATUS,C
	rrf	w_d,f
	rrf	w_c,f
	rrf	w_b,f
	rrf	w_a,f
	btfss	STATUS,C
	goto	tt_positive
	;; now w_d:c:b:a contains (end-start)*t, always +ve
	
	movf	w_d,w
	movwf	w_e
	movf	w_c,w
	movwf	w_d
	movf	w_b,w
	movwf	w_c
	movf	w_a,w
	movwf	w_b
	clrf	w_a
	;;  w now multiplied by 256

	;; divide routine taken from piclist by Peter Hemsley and modified to be 40 bits by 16
tt_divide
	movlw	d'40'
	movwf	temp2		;temp2 is bitcount (temp1 is still negative flag)
	clrf	lowb
	clrf	highb		;lowb and highb are the remainder
	;; use tt to contain Divisor, being step duration
	rbank2
	movf	i2cbuf+2,w
	rbank0
	movwf	tt_l
	rbank2
	movf	i2cbuf+3,w
	rbank0
	movwf	tt_h
	
tt_divloop
	clrc
	rlf	w_a,f
	rlf	w_b,f
	rlf	w_c,f
	rlf	w_d,f
	rlf	w_e,f		;msb rolled out of dividend
	rlf	lowb,f		;and into partial remainder
	rlf	highb,f

	skpnc			;check overflow
	goto	tt_subd
	movfw	tt_h  		; Compare partial remainder and divisor
	subwf 	highb,w
	skpz 
	goto 	tt_testgt   	; Not equal so test if remdrH is greater
	movfw 	tt_l  		; High bytes are equal, compare low bytes
	subwf 	lowb,w
tt_testgt
	skpc          		; Carry set if remdr >= divis
	goto 	tt_remrlt
tt_subd
	movfw 	tt_l  		; Subtract divisor from partial remainder
	subwf 	lowb,f
	skpc          		; Test for borrow
	decf 	highb,f   	; Subtract borrow
	movfw 	tt_h
	subwf 	highb,f
	bsf 	w_a,0  		; Set quotient bit to 1
				; Quotient replaces dividend which is lost
tt_remrlt
	decfsz 	temp2,f		;bit count is in tenp2
	goto 	tt_divloop
	;; w should now contain 256 x the modulus of required change from start temperature
	;; alternatively, view w_e:d:c:b as difference and w_a as fractional part

	btfss	temp1,0		;check if it should be negative
	goto	tt_addstart
	;; we need to change w to -ve, ie multiply current by -1
	movlw	0xff
	xorwf	w_e,f
	xorwf	w_d,f
	xorwf	w_c,f
	xorwf	w_b,f
	xorwf	w_a,f
	incf	w_a,f
	btfsc	STATUS,Z
	incf	w_b,f
	btfsc	STATUS,Z
	incf	w_c,f
	btfsc	STATUS,Z
	incf	w_d,f
	btfsc	STATUS,Z
	incf	w_e,f
tt_addstart
	;; get start temperature out of i2cbuffer
	rbank2
	movf	i2cbuf+4,w	;start temperature low byte
	rbank0
	addwf	w_b,f		;add it in to w, remembering that w_a is fractional part
	btfsc	STATUS,C	
	incf	w_c,f
	btfsc	STATUS,Z
	incf	w_d,f
	btfsc	STATUS,Z
	incf	w_e,f
	;; high byte
	rbank2
	movf	i2cbuf+5,w	;start temperature high byte
	rbank0
	addwf	w_c,f
	btfsc	STATUS,C
	incf	w_d,f
	btfsc	STATUS,Z
	incf	w_e,f
	;; w should now contain 256 x target temperature
	;; alternatively, view w_e:d:c:b as temperature and w_a as fractional part

	;; now define the target temperature for display purposes
	;; (calculation continues with value in w)
	movf	w_c,w
	movwf	tt_h
	movf	w_b,w
	movwf	tt_l
	btfss	w_a,7		;check if fractional part >= 0.5
	goto	calcerror	;if not jump forwards
	incf	tt_l,f		;otherwise round displayed temperature up
	btfsc	STATUS,Z
	incf	tt_h,f

	;; can also drop w_e and w_d from subsequent calculation
	;; since target temperature cannot exceed 400, so value is 0-4000
	
calcerror
	;; calculate difference from desired
	;; measured temperature is in td_h:l
	;; multiply w by -1
	movlw	0xff
	xorwf	w_c,f
	xorwf	w_b,f
	xorwf	w_a,f
	incf	w_a,f
	btfsc	STATUS,Z
	incf	w_b,f
	btfsc	STATUS,Z
	incf	w_c,f
	;; w_c:a now has -1 x target temperature
	movf	td_l,w		;get low byte of measured
	addwf	w_b,f		;add it in to w, remembering that w_a is fractional part
	btfsc	STATUS,C	
	incf	w_c,f
	movf	td_h,w		;get high byte of measured
	addwf	w_c,f		;add it in to w
	;; w now has measured-desired temperature (fractional part in w_a)
	;; it may be negative
	clrf	temp1		;will hold sign flag in this again
	btfss	w_c,7		;check negative indication
	goto	err_positive
	;; error is negative
	incf	temp1,f		;set negative flag
	movlw	0xff
	xorwf	w_c,f
	xorwf	w_b,f
	xorwf	w_a,f
	incf	w_a,f
	btfsc	STATUS,Z
	incf	w_b,f
	btfsc	STATUS,Z
	incf	w_c,f		;w now been inverted, so is +ve
err_positive			;but temp1 indicates if should be negative

err_divide
	movlw	d'24'
	movwf	temp2		;this will count bits
	clrf	lowb
	clrf	highb		;lowb and highb are the remainder
	;; use w_e and w_d as divisor, which is bandwidth
	;; however, bandwidth in memory is in units of degree C not tenths of degree
	rbank2
	movf	i2cbuf+d'14',w
	rbank0
	movwf	temp3		;temp1 is -ve flag, temp2 iscounting bits, temp 3 is bandwidth
	movwf	w_d
	clrf	w_e		;w_e:d has bandwidth*1, d is lsbyte
	bcf	STATUS,C
	rlf	w_d,f
	rlf	w_e,f		;bandwidth x 2
	rlf	w_d,f
	rlf	w_e,f		;bandwidth x 4
	movf	temp3,w
	addwf	w_d,f
	btfsc	STATUS,C
	incf	w_e,f		;bandwidth x 5
	bcf	STATUS,C
	rlf	w_d,f
	rlf	w_e,f		;bandwidth x 10

err_divloop
	clrc
	rlf	w_a,f
	rlf	w_b,f
	rlf	w_c,f		;msb rolled out of dividend
	rlf	lowb,f		;and into partial remainder
	rlf	highb,f

	skpnc			;check overflow
	goto	err_subd
	movfw	w_e  		; Compare partial remainder and divisor
	subwf 	highb,w
	skpz 
	goto 	err_testgt   	; Not equal so test if remdrH is greater
	movfw 	w_d  		; High bytes are equal, compare low bytes
	subwf 	lowb,w
err_testgt
	skpc          		; Carry set if remdr >= divis
	goto 	err_remrlt
err_subd
	movfw 	w_d  		; Subtract divisor from partial remainder
	subwf 	lowb,f
	skpc          		; Test for borrow
	decf 	highb,f   	; Subtract borrow
	movfw 	w_e
	subwf 	highb,f
	bsf 	w_a,0  		; Set quotient bit to 1
				; Quotient replaces dividend which is lost
err_remrlt
	decfsz 	temp2,f		;bit count is in temp2
	goto 	err_divloop
	;; w_c:b:a should now have modulus of error as proportion of bandwidth
	;; w_a is fractional part
	
err_fixsign
	;; if (measured-desired)is +ve then temp1 == 0
	;; in this case power should be 0.5-(error as prop of bandwidth)
	;; so reverse sign of proportion
	btfsc	temp1,0		;check if it should be negative
	goto	add_half
	;; we need to change w to -ve, ie multiply current by -1
	movlw	0xff
	xorwf	w_c,f
	xorwf	w_b,f
	xorwf	w_a,f
	incf	w_a,f
	btfsc	STATUS,Z
	incf	w_b,f
	btfsc	STATUS,Z
	incf	w_c,f

add_half
	;; now add 0.5
	movlw	d'128'
	addwf	w_a,f
	btfsc	STATUS,C
	incf	w_b,f
	btfsc	STATUS,Z
	incf	w_c,f
	;; w now has the power proportion required
	
	;; if w is -ve, power should be zero
	btfss	w_c,7
	goto	pow_not_neg
	clrf	heatpower
	goto	pow_done
pow_not_neg
	movf	w_c,f		;look at w_c
	btfss	STATUS,Z
	goto	pow_full
	movf	w_b,f
	btfss	STATUS,Z
	goto	pow_full
pow_fractional
	;; we are within the proportional band
	movf	w_a,w
	movwf	heatpower
	goto	pow_done
pow_full
	movlw	d'255'	
	movwf	heatpower
pow_done
	;; power is now defined as 0 (off) to 255 (which is either full or 255/256ths)

	;; now need to limit power as program requires
	;; power limits scaled to 0-255 in precalc
maxpowlimit
	rbank2
	movf	i2cbuf+.34,w	;w now has maximum power limit
	rbank0
	subwf	heatpower,w	;w now has heatpower-limit
	btfss	STATUS,C	;is answer +ve?
	goto	minpowlimit	;if not, check min limit,
	rbank2
	movlw	a'u'
	movwf	i2cbuf+.40+.24
	movf	i2cbuf+.34,w
	rbank0
	movwf	heatpower
	goto	powlimitdone
minpowlimit
	rbank2
	movf	i2cbuf+.35,w	;w now has minimum power limit
	rbank0
	subwf	heatpower,w	;w now has heatpower-limit
	btfsc	STATUS,C	;is answer -ve?
	goto	powlimitdone	;if not, limit is done
	rbank2
	movlw	a'l'
	movwf	i2cbuf+.40+.24
	movf	i2cbuf+.35,w
	rbank0
	movwf	heatpower
powlimitdone	
	
	;; now build the bar for display
	;; this has lots of jumping to and fro, so it's more easily done in ppage0
	ppage0
	call	powerbar
	ppage2
	
runningdisplay
	;; display time and current temperature on first line
	ppage0
	call	lcdclear

	movlw	el_minsl
	movwf	FSR
	bcf	STATUS,IRP
	call	b2bcd
	;; if we are over 9999 minutes, do a different display
	ppage2
	movf	bcdtt,f		;look at ten thousands digit
	btfss	STATUS,Z	;will generally be zero
	goto	timer_outrange	; but if not, display '++++'
	ppage0
	swapf	bcdht,w
	andlw	0x0f
	addlw	a'0'
	rbank0to2			;one operatiuon quicker than rbank2 macro
	movwf	i2cbuf+.40
	call	lcdsendchar
	movf	bcdht,w
	andlw	0x0f
	addlw	a'0'
	rbank0to2			;one operatiuon quicker than rbank2 macro
	movwf	i2cbuf+.41
	call	lcdsendchar
	swapf	bcdut,w
	andlw	0x0f
	addlw	a'0'
	rbank0to2			;one operatiuon quicker than rbank2 macro
	movwf	i2cbuf+.42
	call	lcdsendchar
	movf	bcdut,w
	andlw	0x0f
	addlw	a'0'
	rbank0to2			;one operatiuon quicker than rbank2 macro
	movwf	i2cbuf+.43
	call	lcdsendchar
	ppage2
	goto	timersecs
timer_outrange
	ppage0
	movlw	a'+'
	call	lcdsendchar
	movlw	a'+'
	call	lcdsendchar
	movlw	a'+'
	call	lcdsendchar
	movlw	a'+'
	call	lcdsendchar
	rbank0to2
	movlw	a'+'
	movwf	i2cbuf+.40
	movwf	i2cbuf+.41
	movwf	i2cbuf+.42
	movwf	i2cbuf+.43
timersecs
	ppage0
	movlw	a':'
	rbank0to2			;one operatiuon quicker than rbank2 macro
	movwf	i2cbuf+.44
	call	lcdsendchar
	ppage3			; w2bcd is in program mode
	movf	el_secs,w
	call	w2bcd
	ppage0
	movwf	temp1
	swapf	temp1,w
	andlw	0x0f
	addlw	a'0'
	rbank0to2			;one operatiuon quicker than rbank2 macro
	movwf	i2cbuf+.45
	call	lcdsendchar
	movf	temp1,w
	andlw	0x0f
	addlw	a'0'
	rbank0to2			;one operatiuon quicker than rbank2 macro
	movwf	i2cbuf+.46
	call	lcdsendchar
	
	movlw	a' '
	rbank0to2
	movwf	i2cbuf+.40+.7
	call	lcdsendchar
	movlw	a' '
	call	lcdsendchar

	;; print current display temperature
	movlw	td_l
	movwf	FSR
	bcf	STATUS,IRP
	call	b2bcd
	swapf	bcdht,w
	andlw	0x0f
	addlw	a'0'
	rbank0to2
	movwf	i2cbuf+.40+.14
	call	lcdsendchar
	movf	bcdht,w
	andlw	0x0f
	addlw	a'0'
	rbank0to2
	movwf	i2cbuf+.40+.15
	call	lcdsendchar
	swapf	bcdut,w
	andlw	0x0f
	addlw	a'0'
	rbank0to2
	movwf	i2cbuf+.40+.16
	call	lcdsendchar
	movlw	a'.'
	rbank0to2
	movwf	i2cbuf+.40+.17
	call	lcdsendchar
	movf	bcdut,w
	andlw	0x0f
	addlw	a'0'
	rbank0to2
	movwf	i2cbuf+.40+.18
	call	lcdsendchar

	movlw	a' '
	rbank0to2
	movwf	i2cbuf+.40+.19
	call	lcdsendchar
	movlw	3		;top half of power bar chart
	call	lcdsendchar

	;; second line has step numbers or name and target temperature
	call	lcdline2
	ppage2
	
	btfss	el_secs,1	; 2 seconds of each
	goto	rd_stepname	;otherwise show name
rd_stepnumbs	
	ppage0
	movlw	a' '
	call	lcdsendchar

	;; display program number
	clrf	highb
	movf	prognum,w
	movwf	lowb
	movlw	lowb
	movwf	FSR
	bcf	STATUS,IRP
	call	b2bcd		;convert counter to bcd
	;; put that on screen
	swapf	bcdut,w
	andlw	0x0f
	addlw	a'0'		; so w now has ascii code for tens digit
	call	lcdsendchar	;  send it
	movf	bcdut,w
	andlw	0x0f
	addlw	a'0'		; so w now has ascii code for units digit
	call	lcdsendchar	;  send it

	movlw	a'-'
	call	lcdsendchar

	;; display program step
	clrf	highb
	movf	progstep,w
	movwf	lowb
	;; convert to bcd
	movlw	lowb
	movwf	FSR
	bcf	STATUS,IRP
	call	b2bcd		;convert counter to bcd
	;; put that on display
	movf	bcdht,w
	andlw	0x0f
	addlw	a'0'		; so w now has ascii code for hundreds digit
	call	lcdsendchar	;  send it
	swapf	bcdut,w
	andlw	0x0f
	addlw	a'0'		; so w now has ascii code for tens digit
	call	lcdsendchar	;  send it
	movf	bcdut,w
	andlw	0x0f
	addlw	a'0'		; so w now has ascii code for units digit
	call	lcdsendchar	;  send it

	movlw	a' '
	call	lcdsendchar
	
	ppage2
	goto	rd_target
rd_stepname
	ppage0
	;; display step name
	movlw	low(i2cbuf+d'24')
	movwf	FSR
	bsf     STATUS,IRP
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar
	bcf	STATUS,IRP

rd_target	
	ppage0
	;; print current display temperature
	movlw	a' '
	call	lcdsendchar
	movlw	tt_l
	movwf	FSR
	bcf	STATUS,IRP
	call	b2bcd
	swapf	bcdht,w
	andlw	0x0f
	addlw	a'0'
	rbank0to2
	movwf	i2cbuf+.40+.8
	call	lcdsendchar
	movf	bcdht,w
	andlw	0x0f
	addlw	a'0'
	rbank0to2
	movwf	i2cbuf+.40+.9
	call	lcdsendchar
	swapf	bcdut,w
	andlw	0x0f
	addlw	a'0'
	rbank0to2
	movwf	i2cbuf+.40+.10
	call	lcdsendchar
	movlw	a'.'
	rbank0to2
	movwf	i2cbuf+.40+.11
	call	lcdsendchar
	movf	bcdut,w
	andlw	0x0f
	addlw	a'0'
	rbank0to2
	movwf	i2cbuf+.40+.12
	call	lcdsendchar

	movlw	a' '
	rbank0to2
	movwf	i2cbuf+.40+.13
	call	lcdsendchar
	movlw	4		;bottom half of power bar chart
	call	lcdsendchar

	;; front panel light
	rbank0
	bcf	PORTA,5		;front panel light off when procssing is complete

	ppage2
	;; decide whether the drum, fan etc need to be running
	rbank2
	movf	i2cbuf+.8,w	;this has the bits for on/off eqpt.
	rbank0
	movwf	temp1		;so we can interrogate it

checkdrum	
	btfss	temp1,0		;this is rotisserie bit
	goto	drumoff
drumon
	movlw	a'd'
	rbank0to2
	movwf	i2cbuf+.40+.26
	rbank2to0
	bsf	PORTE,2
	goto	checkfan
drumoff
	bcf	PORTE,2

checkfan	
	btfss	temp1,1		;this is top element fan bit
	goto	fanoffmaybe
fanon	
	movlw	a'f'
	rbank0to2
	movwf	i2cbuf+.40+.27
	rbank2to0
	bsf	PORTE,1
	goto	checkdoor
fanoffmaybe
	;; we force the fan on for at least 1 second if any heater input
	movf	heatpower,f
	btfsc	STATUS,Z
	goto	fanoff
	;; power on, so fan goes on for this second
	movlw	a'F'		;upper case F indicates forced fan
	rbank0to2
	movwf	i2cbuf+.40+.27
	rbank2to0
	bsf	PORTE,1
	goto	checkdoor
fanoff
	bcf	PORTE,1

checkdoor
	btfss	temp1,4		;this is door control bit
	goto	dooroff
	;; if got here, door may be open
	;; however, we only activate control for 1 second, 
	;; do this if we are in first second of step, ie if elapsed this = 0
	rbank2
	movlw	a'e'
	movwf	i2cbuf+.40+.29
	rbank0
	
	movf	elt_h,f
	btfss	STATUS,Z
	goto	dooroff		; because high byte is non-zero
	movf	elt_l,f
	btfss	STATUS,Z
	goto	dooroff		; because low byte is non-zero
	;; door control bit set, and in first second of step, soactivate
	bsf	PORTC,1
	goto	doordone
dooroff
	bcf	PORTC,1		;door control off
doordone
	

	;; put all information out serial port
	;; time, current temp and required temp has been buffered as LCD display generated
	;; others buffered as calc'd or as actioned
	;; note this is interrupt driven, so does not block actioning power
	rbank2

	movlw	d'13'
	movwf	i2cbuf+.40+.32
	movlw	d'10'
	movwf	i2cbuf+.40+.33

	movlw	.34
	movwf	usartcount
	movlw	low(i2cbuf+.40)
	movwf	usartaddr
	rbank1
	bsf	INTCON,PEIE	;enable peripheral interrupts
	bsf	INTCON,GIE	;enable interrupts globally
	bsf	PIE1,TXIE	;enable serial port, should now transmit

	;; now action heat power
checkfullpower
	rbank0
	movf	heatpower,w
	sublw	0xff		;check if it is full
	btfss	STATUS,Z
	goto	checkzeropower

	;; is set to full power
	bsf	PORTE,0		;SSR on
	goto	waitfortick

checkzeropower
	movf	heatpower,w
	btfsc	STATUS,Z
	goto	ssroff

	;; switch on output
	bsf	PORTE,0		;SSR on
	
checkclockcount
	movf	heatpower,w	;get required power
	subwf	TMR0,w		;w now has (timer0 - power)
	btfss	STATUS,C	;if carry set, result=0 or +ve, ie timer0 >= power
	goto	checkclockcount

ssroff
	;; switch off output
	bcf	PORTE,0

waitfortick
	btfss	INTCON,T0IF
	goto	$-1		;wait here until the timer 0 overflow flag is set
	bcf	INTCON,T0IF	;clear the flag

increment_time
	;; increment elapsed-this-step time
	incf	elt_l,f
	btfsc	STATUS,Z
	incf	elt_h,f
	
	; increment count of hours, minutes and seconds
        incf    el_secs,f       ; increment seconds
        movf    el_secs,w       ; get current count of seconds
        sublw   d'60'           ; w now contains 60-seconds
        btfss   STATUS,Z        ; if zero set, we need to do something
        goto    checkifstepend  ; otherwise bail out
        clrf    el_secs         ; set seconds to 0
        incf    el_minsl,f      ; and increment minutes
	btfsc	STATUS,Z
	incf	el_minsh,f	; increment high byte of minutes
	
checkifstepend
	rbank2
	movf	i2cbuf+.3,w	;get high byte of step duration
	rbank0
	subwf	elt_h,w		;so w now has (elt_h-duration_h)
	btfss	STATUS,C
	goto	runloop		;result was -ve ie cannot have expired
	
	btfss	STATUS,Z	;check that it is zero
	goto	nextprogstep	;if non-zero, have somehow overshot, so next step
	;; at this point, high bytes match
	rbank2
	movf	i2cbuf+.2,w
	rbank0
	subwf	elt_l,w		;so w now has (elt_l-duration_l)
	btfss	STATUS,C
	goto	runloop		;result was -ve, ie duration > elapsed
	;; step has elapsed
	goto	nextprogstep
	
carryon
	goto	runloop
	
	
	


	
	


	;; #######################
	;; program page 3
	;; contains programming mode
	;; #######################
	org	0x1800
	
	;; routines used exclusively in programming mode

findnewstep
	;; find first unused program step
	movlw	2		; 2 is the minimum possible program step number
	rbank0
	movwf	newstep
	;; now set up to read that step
PMtrynextstep
	ppage0
        swapf   newstep,w      ; swap nybbles of step into w
        andlw   b'00001111'     ; and mask out upper nybble, so w now has high nybble of progstep
        rbank2                  ; select register bank 2
        movwf   i2chigh         ; and put that in high byte
        rbank0                  ; back to bank 0
        swapf   newstep,w      ; swap nybbles of step into w
        andlw   b'11110000'     ; and mask out lower nybble, so w now has (low nybble of progstep)*16
        rbank2                  ; select register bank 2
        movwf   i2clow          ; and put that in low byte
        ;i2chigh:i2clow now contain step*16
        bcf     STATUS,C        ; clear carry
        rlf     i2clow,f        ; low*2
        rlf     i2chigh,f       ; high*2 so bytes have proper address
	movlw   b'10100001'     ; memory, chip 0, read
        movwf   i2caddr         ; control byte set
	movlw	d'1'		;we only need to read first byte to determine if is available
	movwf	i2ccount
	call    i2ceeprom       ; do the read
	rbank2
	ppage3
	movf	i2cbuf,f	;look at first byte
	btfsc	STATUS,Z	;is it zero?
	return			;if so, we've found step number
	rbank0
	incf	newstep,f
	goto	PMtrynextstep
	
	;; w to bcd
	;; unsigned 8 bit binary in W converted to bcd in W (hundreds dropped)
	;; trashes the temp & temp3 register
w2bcd
	rbank0
	movwf	bcdtempl	; set it in our working space
	movlw	d'8'		; number of binary digits, so number of loops
	movwf	temp1		; now put it where we will count
	clrf	temp3		; initialise bcd digits to zero

w2bcdloop
w2bcdunits	
	movf	temp3,w		; get two digits into w
	andlw	0x0f		; so now we have units in w
	sublw	d'4'		; subtract w from 4 and put answer in w
	btfsc	STATUS,C	; if carry is clear answer was -ve
	goto	w2bcdtens	; otherwise consider next digit
	movlw	d'3'		; if answer was -ve, add three
	addwf	temp3,f		;  add it and put it back in f
w2bcdtens
	swapf	temp3,w		; swapped nybbles into w
	andlw	0x0f		; now have just tesn digit in low nybble of w
	sublw	d'4'		; subtract w from 4 and put answer in w
	btfsc	STATUS,C	; if carry is clear answer was -ve
	goto	w2bcdshifts	; otherwise consider next digit
	movlw	0x30		; if answer was -ve, add three to the bcd byte
	addwf	temp3,f		; add it and put it back in f
w2bcdshifts
	bcf	STATUS,C	; clear carry flag to start
	rlf	bcdtempl,f	; do series of roll left across the working space
	rlf	temp3,f
	;; thats the end of the loop
	decfsz	temp1,f		; after decrement, we have number of times more too loop
	goto	w2bcdloop	; if that's nonzero, do the loop
	movf	temp3,w		; get the result back in w
	return			; otherwise we're finished




	
	
	;; ############################
	;; start of program mode proper
	;; ############################
	
progmodestart
	rbank0			;this already set, but just to be sure...
	;; switch off buttons and timer 0 interrupt
	bcf	INTCON,RBIE
	bcf	INTCON,T0IE

	;; if the current step is not set to zero
	;; we move around within current program
	movf	progstep,f
	btfss	STATUS,Z
	goto	PMselectstep
	;; otherwise, prognum is defined but progstep is zero
	;; so we start a new program at this prognum
	call	findnewstep
	goto	PMwriteprognum

PMselectstep
	;; the program number is defined by calling routine
	;; now we choose which step to deal with
	ppage0
	;; prognum and progstep should be defined
	;; now set up to read that step
        swapf   progstep,w      ; swap nybbles of step into w
        andlw   b'00001111'     ; and mask out upper nybble, so w now has high nybble of progstep
        rbank2                  ; select register bank 2
        movwf   i2chigh         ; and put that in high byte
        rbank0                  ; back to bank 0
        swapf   progstep,w      ; swap nybbles of step into w
        andlw   b'11110000'     ; and mask out lower nybble, so w now has (low nybble of progstep)*16
        rbank2                  ; select register bank 2
        movwf   i2clow          ; and put that in low byte
        ;i2chigh:i2clow now contain step*16
        bcf     STATUS,C        ; clear carry
        rlf     i2clow,f        ; low*2
        rlf     i2chigh,f       ; high*2 so bytes have proper address
	movlw   b'10100001'     ; memory, chip 0, read
        movwf   i2caddr         ; control byte set
	movlw	d'32'
	movwf	i2ccount
	call    i2ceeprom       ; do the read
	rbank0

	;; put display on screen
	ppage0
	call	lcdclear
	bcf	STATUS,IRP
	movlw	low(progmodeselect)
	movwf	FSR
	movlw	6
	call	lcdtext
	movlw	a' '
	call	lcdsendchar

	;; display program number
	clrf	highb
	movf	prognum,w
	movwf	lowb
	movlw	lowb
	movwf	FSR
	bcf	STATUS,IRP
	call	b2bcd		;convert counter to bcd
	;; put that on screen
	swapf	bcdut,w
	andlw	0x0f
	addlw	a'0'		; so w now has ascii code for tens digit
	call	lcdsendchar	;  send it
	movf	bcdut,w
	andlw	0x0f
	addlw	a'0'		; so w now has ascii code for units digit
	call	lcdsendchar	;  send it

	;; second line of display
	call	lcdline2
	movlw	a' '
	call	lcdsendchar

	;; display program step
	clrf	highb
	movf	progstep,w
	movwf	lowb
	;; convert to bcd
	movlw	lowb
	movwf	FSR
	bcf	STATUS,IRP
	call	b2bcd		;convert counter to bcd
	;; put that on display
	movf	bcdht,w
	andlw	0x0f
	addlw	a'0'		; so w now has ascii code for hundreds digit
	call	lcdsendchar	;  send it
	swapf	bcdut,w
	andlw	0x0f
	addlw	a'0'		; so w now has ascii code for tens digit
	call	lcdsendchar	;  send it
	movf	bcdut,w
	andlw	0x0f
	addlw	a'0'		; so w now has ascii code for units digit
	call	lcdsendchar	;  send it

	movlw	a' '
	call	lcdsendchar
	movlw	a':'
	call	lcdsendchar
	movlw	a' '
	call	lcdsendchar

	;; display step name
	movlw	low(i2cbuf+d'24')
	movwf	FSR
	bsf     STATUS,IRP
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar
	bcf	STATUS,IRP

PMselectstepwait	
	;; wait for key press
	call	waitpress

	;; action key press
	ppage3
	btfsc	portbbuff,4
	goto	PMselectstepknob
	btfsc	portbbuff,5	;button by knob proceeds to deal with this step
	goto	PM01
	btfsc	portbbuff,6	;right button increments the step to edit
	goto	PMnextstep	;
	btfsc	portbbuff,7	;left button decrements the step
	goto	PMprevstep	;
	goto	PMselectstepwait	;shouldn't get here, but just in case, try again


PMselectstepknob	
	btfss	portbbuff,3	;check knob direction
	goto	PMprevstep	;if not previous step, withh overflow to next
	
PMnextstep
	rbank2
	movf	i2cbuf+d'22',w	;
	rbank0
	btfss	STATUS,Z	;if next would be zero, just stay where we are
	movwf	progstep
	goto	PMselectstep	;show that step
	
PMprevstep
	rbank2
	movf	i2cbuf+d'23',w	;
	rbank0
	btfss	STATUS,Z	;if previous would be zero, just stay where we are
	movwf	progstep
	goto	PMselectstep	;show that step


	
PM01
	;; asks if editing existing step
	;; or creating a new one
	;; routines generally in prog page 0
	ppage0
	;; top line of display is defined label
	call	lcdclear
	bcf	STATUS,IRP
	movlw	low(progmode01a)	
	movwf	FSR
	movlw	8
	call	lcdtext

	;; put the arrows on line 2
	call	lcdline2
	movlw	low(progmode01b)
	movwf	FSR
	movlw	8
	call	lcdtext
	
	;; put current progstep on line2
	call	lcdline2
	movlw	a' '
	call	lcdsendchar

	clrf	highb
	movf	progstep,w
	movwf	lowb
	;; convert to bcd
	movlw	lowb
	movwf	FSR
	bcf	STATUS,IRP
	call	b2bcd		;convert counter to bcd
	;; put that on display
	movf	bcdht,w
	andlw	0x0f
	addlw	a'0'		; so w now has ascii code for hundreds digit
	call	lcdsendchar	;  send it
	swapf	bcdut,w
	andlw	0x0f
	addlw	a'0'		; so w now has ascii code for tens digit
	call	lcdsendchar	;  send it
	movf	bcdut,w
	andlw	0x0f
	addlw	a'0'		; so w now has ascii code for units digit
	call	lcdsendchar	;  send it

	;; now wait for a key to be pressed
	call	waitpress
	ppage3
	btfsc	portbbuff,5		;button by knob returns to idlemode
	goto	PM01gotoidle
	btfsc	portbbuff,6		;right button creates a new step
	goto	PM02			;we need to create that
	btfsc	portbbuff,7		;left button edits current step
	goto	PM03			;so jump to editing stage
	goto	PM01			;shouldn't get here, but just in case, try again

PM01gotoidle
	ppage1
	goto	idlemodestart

PM02
	;; we will need a new step location and write it into newstep register
	call	findnewstep	; note that this only reads 1 byte out of each step
	
	;; we need to ask if this is a new program
	;; or inserted in current program
	;; top line of display is defined label, second line is yes/no
PM02display	
	ppage0
	call	lcdclear
	bcf	STATUS,IRP
	movlw	low(progmode02)	; is "add to current program?"
	movwf	FSR
	movlw	8
	call	lcdtext
	
	call	lcdline2
	movlw	low(yesnolabel)
	movwf	FSR
	movlw	8
	call	lcdtext

	;; wait for key press
	call	waitpress
	;; action key press
	ppage3
	btfsc	portbbuff,6	;right button 'no' means start a new program 
	goto	PMnewprog	;
	btfsc	portbbuff,7	;left button 'yes' means insert into current program
	goto	PMinsertstep	;
	goto	PM02display	; button K does nothing, so could get here, just try again


	;; creating a new program at current step number
	;;  we need to find a new prog number
PMnewprog
	ppage0
	;; we already know what the new step will be numbered
	;; need to decide about program number
	movf	prognum,f	;look at program number
	btfsc	STATUS,Z	;see if it is zero
	goto	PMwriteprognum	;if so, define this program

	;; we need to find a new program to write to
	;; read whole table into i2cbuffer and work through
	rbank2
	clrf	i2clow
	clrf	i2chigh		;start at byte zero
	movlw	d'64'
	movwf	i2ccount	;read 64 bytes
        movlw   b'10100001'     ; memory, chip 0, read
        movwf   i2caddr         ; control byte set
	ppage0
	call	i2ceeprom	; get the byte from the eeprom
	ppage3
	bsf	STATUS,IRP
	movlw	low(i2cbuf)
	movwf	FSR
	rbank0
	clrf	prognum
PMtrythisprog
	incf	prognum,f
	incf	FSR,f		;
	movf	INDF,w		;look at possible candidate
	btfss	STATUS,Z	;is it zero?
	goto	PMtrythisprog	;if not try the next
	;; now prognum is set to one that's free
	
PMwriteprognum
	;; set current program to refer to new step

	rbank0
	ppage0
	call	lcdclear
	movlw	a'w'
	call	lcdsendchar
	movlw	a' '
	call	lcdsendchar
	swapf	prognum,w
	andlw	0x0f
	addlw	a'0'
	call	lcdsendchar
	movf	prognum,w
	andlw	0x0f
	addlw	a'0'
	call	lcdsendchar
	movlw	a' '
	call	lcdsendchar
	swapf	newstep,w
	andlw	0x0f
	addlw	a'0'
	call	lcdsendchar
	movf	newstep,w
	andlw	0x0f
	addlw	a'0'
	call	lcdsendchar
	call	waitpress

	movf	prognum,w
	rbank2
	movwf	i2clow		
	clrf	i2chigh		;byte address = prognum
	rbank0
	movf	newstep,w	;get new step number
	rbank2
	movwf	i2cbuf		;put it into the buffer
	movlw	1
	movwf	i2ccount	;just one byte to write
        movlw   b'10100000'     ; memory, chip 0, write
        movwf   i2caddr         ; control byte set
	ppage0
	call	i2ceeprom	; write that byte into the eeprom

	rbank0
	clrf	nextstep
	clrf	prevstep
	ppage3
	goto	PMinitialisestep
	
	;; inserting a new step between current and next step
	;; need to find a blank step, modify current and next
	;; then set up the new step with the same values as current
PMinsertstep
	ppage0
	;; already know what number our new step will be
	;; note that although we've been reading into i2cbuf, we only over-wrote
	;; the first byte when we got to here, so bytes 22 and 23 should be OK
	rbank2
	movf	i2cbuf+d'22',w
	rbank0
	movwf	nextstep
	rbank2
	movf	i2cbuf+d'23',w
	rbank0
	movwf	prevstep

	;; now we need to rewrite previous and next steps
	;; to refer to the new step

	;; start with previous, which is already loaded
	movf	newstep,w
	rbank2
	movwf	i2cbuf+d'22'	;set next step in prog to be the new step
	movlw	1
	movwf	i2cbuf		;reset first byte flag (shouldn't be needed)
		;; now set up to read that step
        swapf   progstep,w      ; swap nybbles of step into w
        andlw   b'00001111'     ; and mask out upper nybble, so w now has high nybble of progstep
        rbank2                  ; select register bank 2
        movwf   i2chigh         ; and put that in high byte
        rbank0                  ; back to bank 0
        swapf   progstep,w      ; swap nybbles of step into w
        andlw   b'11110000'     ; and mask out lower nybble, so w now has (low nybble of progstep)*16
        rbank2                  ; select register bank 2
        movwf   i2clow          ; and put that in low byte
        ;i2chigh:i2clow now contain step*16
        bcf     STATUS,C        ; clear carry
        rlf     i2clow,f        ; low*2
        rlf     i2chigh,f       ; high*2 so bytes have proper address
	movlw   b'10100000'     ; memory, chip 0, write
        movwf   i2caddr         ; control byte set
	movlw	d'32'
	movwf	i2ccount
	call    i2ceeprom       ; write the whole step

	;; now load what was the next step
	rbank0
	swapf   nextstep,w      ; swap nybbles of step into w
        andlw   b'00001111'     ; and mask out upper nybble, so w now has high nybble of progstep
        rbank2                  ; select register bank 2
        movwf   i2chigh         ; and put that in high byte
        rbank0                  ; back to bank 0
        swapf   nextstep,w      ; swap nybbles of step into w
        andlw   b'11110000'     ; and mask out lower nybble, so w now has (low nybble of progstep)*16
        rbank2                  ; select register bank 2
        movwf   i2clow          ; and put that in low byte
        ;i2chigh:i2clow now contain step*16
        bcf     STATUS,C        ; clear carry
        rlf     i2clow,f        ; low*2
        rlf     i2chigh,f       ; high*2 so bytes have proper address
	movlw   b'10100001'     ; memory, chip 0, read
        movwf   i2caddr         ; control byte set
	movlw	d'32'
	movwf	i2ccount
	call    i2ceeprom       ; do the read
	;;  update the program step
	movf	newstep,w
	rbank2
	movwf	i2cbuf+d'23'	;set previous step in prog to be the new step
	movlw	1
	movwf	i2cbuf		;reset first byte flag (shouldn't be needed)
	;; i2clow and i2chigh should still be set
	movlw   b'10100000'     ; memory, chip 0, write
        movwf   i2caddr         ; control byte set
	movlw	d'32'
	movwf	i2ccount
	call    i2ceeprom       ; do the write

PMinitialisestep
	;; initialise our new step
	rbank2
	movlw	1
	movwf	i2cbuf		;byte 0 lsbit says this step is in use
	clrf	i2cbuf+1	;byte 1 not in use
	movwf	i2cbuf+2	; low byte duration = 1
	clrf	i2cbuf+3	; high byte = 0 so step duration = 1 second
	movlw	d'250'
	movwf	i2cbuf+4	;bytes 4&5 = 250 (25 C start temperature)
	clrf	i2cbuf+5
	movwf	i2cbuf+6	;bytes 6&7 = 250 (25C end temperature)
	clrf	i2cbuf+7
	movlw	b'00000011'
	movwf	i2cbuf+8	;byte 8 - x,x,x, shut, x,x, fan on, rot on
	clrf	i2cbuf+9	;cooling fans off
	movlw	d'20'
	movwf	i2cbuf+d'14'	;byte 14 = prop band = 20C
	movlw	d'100'
	movwf	i2cbuf+d'15'	;byte 15 - no maximum heat limit (ie 100% set)
	clrf	i2cbuf+d'16'	;byte 16 - no minimum heat limit (ie 0% set)
	rbank0
	movf	nextstep,w
	rbank2
	movwf	i2cbuf+d'22'	; set next step value
	rbank0
	movf	progstep,w	; previous to the new step will be the current step
	rbank2
	movwf	i2cbuf+d'23'	; set previous step value
	movlw	a' '
	movwf	i2cbuf+d'24'	;label is all blanks
	movwf	i2cbuf+d'25'
	movwf	i2cbuf+d'26'
	movwf	i2cbuf+d'27'
	movwf	i2cbuf+d'28'
	movwf	i2cbuf+d'29'
	movwf	i2cbuf+d'30'
	movwf	i2cbuf+d'31'

	rbank0
	movf	newstep,w	;get the proposed new step
	movwf	progstep	;make it the current step
	
PM03	;; this is start of editing the current step
	ppage0
	rbank0
	call	lcdclear
	bcf	STATUS,IRP
	movlw	low(progmode03)	; is "set step duration"
	movwf	FSR
	movlw	8
	call	lcdtext


	;; start by setting hours
	clrf	pmflags

PM03row2
	rbank1
	movlw 	a' '
	movwf	lcdbuf
	movwf	lcdbuf+1
	movwf	lcdbuf+3
	movwf	lcdbuf+5
	movwf	lcdbuf+8
	movwf	lcdbuf+0x0A
	movwf	lcdbuf+0x0D
	movwf	lcdbuf+0x0E
	movwf	lcdbuf+0x0F
	movlw	a':'
	movwf	lcdbuf+4
	movwf	lcdbuf+9
	
	;; decide whether setting h, m or s and highlight accordingly
	ppage3
	rbank0
	movf	pmflags,w
	btfsc	STATUS,Z
	goto	PM03sethrs
	addlw	0xff		;which will roll to 0 if pmflags was 1
	btfsc	STATUS,Z
	goto	PM03setmins
	addlw	0xff		;which will roll to 0 if pmflags was 2
	btfsc	STATUS,Z
	goto	PM03setsecs
	goto	PM03		;if not a recognised value, reset and try again
	
PM03sethrs
	rbank1
	movlw	a'>'
	movwf	lcdbuf+1
	movlw	a'<'
	movwf	lcdbuf+3
	goto	PM03showval
	
PM03setmins
	rbank1
	movlw	a'>'
	movwf	lcdbuf+5
	movlw	a'<'
	movwf	lcdbuf+8
	goto	PM03showval
	
PM03setsecs
	rbank1
	movlw	a'>'
	movwf	lcdbuf+0x0A
	movlw	a'<'
	movwf	lcdbuf+0x0D
	goto	PM03showval

PM03showval
	;; convert step duration to hms
	rbank0
	movlw	low(i2cbuf)+2
	movwf	FSR
	bsf	STATUS,IRP
	ppage0
	call	b2hms
	
	;; fill in time digits
	rbank0
	movf	bcdtt,w
	andlw	0x0f		;mask all but low digit
	addlw	a'0'
	rbank1
	movwf	lcdbuf+2

	rbank0
	swapf	bcdht,w
	andlw	0x0f		;mask all but low digit
	addlw	a'0'
	rbank1
	movwf	lcdbuf+6

	rbank0
	movf	bcdht,w
	andlw	0x0f		;mask all but low digit
	addlw	a'0'
	rbank1
	movwf	lcdbuf+7

	rbank0
	swapf	bcdut,w
	andlw	0x0f		;mask all but low digit
	addlw	a'0'
	rbank1
	movwf	lcdbuf+0x0b

	rbank0
	movf	bcdut,w
	andlw	0x0f		;mask all but low digit
	addlw	a'0'
	rbank1
	movwf	lcdbuf+0x0c

	call	lcdline2
	movlw	d'16'
	call	lcdsendbuf

	;; now check button-presses
	call	waitpress	; also waits for knob change
	;; now process key press
	ppage3
	btfsc	portbbuff,4	;indicates knob changed
	goto	PM03knob
	btfsc	portbbuff,5	;button by knob proceeds to deal with this step
	goto	PM04
	btfsc	portbbuff,6	;right button increments the step to edit
	goto	PM03incflags	;
	btfsc	portbbuff,7	;left button decrements the step
	goto	PM03decflags	;
	goto	PM03row2	;shouldn't get here, but just in case, simply ignore keypress

PM03knob
	;; need to check if set to h, m or s
	rbank0			;register bank 0
	movlw	low(i2cbuf)+2
	movwf	FSR
	bsf	STATUS,IRP	;but INDF set up to address bank 2

	movf	pmflags,w
	btfsc	STATUS,Z
	goto	PM03khrs
	addlw	0xff		;which will roll to 0 if pmflags was 1
	btfsc	STATUS,Z
	goto	PM03kmins
	addlw	0xff		;which will roll to 0 if pmflags was 2
	btfsc	STATUS,Z
	goto	PM03ksecs
	goto	PM03		;if not a recognised value, reset and try again

PM03khrs
	movlw	high(d'3600')
	movwf	temp2
	movlw	low(d'3600')
	movwf	temp1
	btfss	portbbuff,3	;check direction
	goto	PM03kminus
	goto	PM03kplus

PM03kmins
	clrf	temp2
	movlw	d'60'
	movwf	temp1
	btfss	portbbuff,3	;check direction
	goto	PM03kminus
	goto	PM03kplus
	
PM03ksecs
	clrf	temp2
	movlw	1
	movwf	temp1
	btfss	portbbuff,3	;check direction
	goto	PM03kminus
	goto	PM03kplus

PM03kplus	
PM03kpluslow
	movf	temp1,w		;this is low byte of addition
	addwf	INDF,f		;add it in
	btfss	STATUS,C
	goto	PM03kplushigh
	incf	FSR,f
	incf	INDF,f
	decf	FSR,f		;put pointer back to low byte
PM03kplushigh
	incf	FSR,f
	movf	temp2,w
	addwf	INDF,f
	;; check we have not exceeded 9:59:59 = 35999
	movlw	high(d'35999')
	subwf	INDF,w
	btfss	STATUS,C	;if carry clear, result -ve, ie high byte < 140
	goto	PM03row2	;so go round again
	;; high byte >= 140
	btfss	STATUS,Z	;if zero, then high byte was equal to limit
	goto	PM03limitmax	;if not, high byte > 140 so apply max  limit
	;; high byte == 140
	decf	FSR,f
	movlw	low(d'35999')
	subwf	INDF,w
	btfss	STATUS,C	;if carry clear, result=-ve ie low byte < 159
	goto	PM03row2	;so go round again
PM03limitmax	
	rbank2
	movlw	low(d'35999')
	movwf	i2cbuf+2
	movlw	high(d'35999')
	movwf	i2cbuf+3
	goto	PM03row2

PM03kminus
PM03kminuslow
	movf	temp1,w		;low byte to be subtracted
	subwf	INDF,f		
	btfsc	STATUS,C	;checkif went negative
	goto	PM03kminushigh	;if C was set, result still +ve (or zero)
	incf	FSR,f
	movf	INDF,w		;get high byte into W
	btfsc	STATUS,Z	;if high byte already zero...
	goto	PM03limitmin
	addlw	0xff		;equivalent to subtracting 1
	movwf	INDF		; put answer back into byte
	decf	FSR,f		;point back at low byte
PM03kminushigh
	incf	FSR,f
	movf	temp2,w
	subwf	INDF,f
	btfss	STATUS,C	;if c is clear, has gone negative
	goto	PM03limitmin	;so limit
	;; now check if zero (minimum step duration=1)
	rbank2
	movf	low(i2cbuf+3),f	;look at high byte
	btfss	STATUS,Z	;see if it is zero
	goto	PM03row2	;if not, loop around
	movf	low(i2cbuf+2),f	;look at low byte
	btfss	STATUS,Z	; see if it is zero
	goto	PM03row2	;if not loop around
	;; otherwise, run through to set minimum:
PM03limitmin
	rbank2
	movlw	1
	movwf	low(i2cbuf+2)
	clrf	low(i2cbuf+3)
	goto	PM03row2
	
PM03incflags
	incf	pmflags,f
	movf	pmflags,w
	sublw	d'4'
	btfsc	STATUS,Z
	clrf	pmflags
	goto	PM03row2

PM03decflags
	decf	pmflags,f
	movf	pmflags,w
	sublw	d'255'
	btfss	STATUS,Z
	goto	PM03row2
	movlw	3
	movwf	pmflags
		
	goto	PM03row2


	
	;; set temperatures
PM04
	ppage0
	rbank0
	call	lcdclear
	bcf	STATUS,IRP
	movlw	low(progmode04)	; is "set temperatures"
	movwf	FSR
	movlw	8
	call	lcdtext

	;; start by setting start temperature
	clrf	pmflags

PM04row2
	;; load up lcd buffer with display
	rbank1
	movlw 	a' '
	movwf	lcdbuf
	movwf	lcdbuf+6
	movwf	lcdbuf+7
	movwf	lcdbuf+8
	movwf	lcdbuf+9
	movwf	lcdbuf+0x0F
	movlw	a'.'
	movwf	lcdbuf+4
	movwf	lcdbuf+0x0D

	;; decide what digits we are setting
	ppage3
	rbank0
	btfsc	pmflags,0	;since we just have two alternatives, we can look at lsbit
	goto	PM04setend
	
PM04setstart
	rbank1
	movlw	a'>'
	movwf	lcdbuf
	movlw	a'<'
	movwf	lcdbuf+6
	goto	PM04showval

PM04setend	
	rbank1
	movlw	a'>'
	movwf	lcdbuf+9
	movlw	a'<'
	movwf	lcdbuf+0x0f
	
PM04showval
	;; start temperature
	rbank0
	movlw	low(i2cbuf)+4
	movwf	FSR
	bsf	STATUS,IRP
	ppage0
	call	b2bcd

	rbank0
	swapf	bcdht,w
	andlw	0x0f		;mask all but low digit
	addlw	a'0'
	rbank1
	movwf	lcdbuf+1

	rbank0
	movf	bcdht,w
	andlw	0x0f		;mask all but low digit
	addlw	a'0'
	rbank1
	movwf	lcdbuf+2

	rbank0
	swapf	bcdut,w
	andlw	0x0f		;mask all but low digit
	addlw	a'0'
	rbank1
	movwf	lcdbuf+3

	rbank0
	movf	bcdut,w
	andlw	0x0f		;mask all but low digit
	addlw	a'0'
	rbank1
	movwf	lcdbuf+5

	;; now end temperature
	rbank0
	movlw	low(i2cbuf)+6
	movwf	FSR
	bsf	STATUS,IRP
	call	b2bcd

	rbank0
	swapf	bcdht,w
	andlw	0x0f		;mask all but low digit
	addlw	a'0'
	rbank1
	movwf	lcdbuf+0x0a

	rbank0
	movf	bcdht,w
	andlw	0x0f		;mask all but low digit
	addlw	a'0'
	rbank1
	movwf	lcdbuf+0x0b

	rbank0
	swapf	bcdut,w
	andlw	0x0f		;mask all but low digit
	addlw	a'0'
	rbank1
	movwf	lcdbuf+0x0c

	rbank0
	movf	bcdut,w
	andlw	0x0f		;mask all but low digit
	addlw	a'0'
	rbank1
	movwf	lcdbuf+0x0e

	call	lcdline2
	movlw	d'16'
	call	lcdsendbuf

	;; now check button-presses
	call	waitpress	; also waits for knob change
	;; now process key press
	ppage3
	btfsc	portbbuff,4	;indicates knob changed
	goto	PM04knob
	btfsc	portbbuff,5	;button by knob proceeds to deal with this step
	goto	PM05
	btfsc	portbbuff,6	;right button increments the step to edit
	goto	PM04incflags	;
	btfsc	portbbuff,7	;left button decrements the step
	goto	PM04decflags	;
	goto	PM04row2	;shouldn't get here, but just in case, simply ignore keypress

PM04knob
	;; start or end temnperature?
	btfsc	pmflags,0
	goto	PM04ke

PM04ks
	;; action knob when set to start temperature
	;; point indf at start temperature
	movlw	low(i2cbuf+4)
	movwf	FSR
	bsf	STATUS,IRP
	goto	PM04kdirection

PM04ke
	;; point indf at end temperature
	movlw	low(i2cbuf+6)
	movwf	FSR
	bsf	STATUS,IRP

PM04kdirection
	btfss	portbbuff,3
	goto	PM04kminus

PM04kplus
	movlw	5
	addwf	INDF,f		;increment
	btfss	STATUS,C	;check if overflow
	goto	PM04kpluscheck
	incf	FSR,f
	incf	INDF,f
	decf	FSR,f
PM04kpluscheck
	;; check we have not exceeded 350C, which is 3500
	incf	FSR,f		;so looking at high byte
	movlw	high(d'3500')
	subwf	INDF,w
	btfss	STATUS,C	;if carry clear, result -ve, ie high byte < limit
	goto	PM04row2	;so go round again
	;; high byte >= limit, 
	btfss	STATUS,Z	;if zero, then high byte was equal to limit
	goto	PM04limitmax	;if not, high byte > 140 so apply max  limit
	;; high byte == limit
	decf	FSR,f
	movlw	low(d'3500')
	subwf	INDF,w
	btfss	STATUS,C	;if carry clear, result=-ve ie low byte < 159
	goto	PM04row2	;so go round again
	incf	FSR,f
PM04limitmax	
	movlw	high(d'3500')
	movwf	INDF
	decf	FSR,f
	movlw	low(d'3500')
	movwf	INDF
	goto	PM04row2

	
PM04kminus
	movlw	0xfb
	addwf	INDF,f		;adding 255 equals decrement by one
	btfsc	STATUS,C	;check if not overflow
	goto	PM04row2	;if not decrement high byte, cannot be at limit
	incf	FSR,f
	decf	INDF,f
	decf	FSR,f
PM04kminuslimit
	incf	FSR,f
	btfss	INDF,7		;if msbit is set, have wrapped
	goto	PM04row2
	clrf	INDF
	decf	FSR,f
	clrf	INDF
	goto	PM04row2

PM04incflags
	incf	pmflags,f
	goto	PM04row2
PM04decflags
	decf	pmflags,f
	goto	PM04row2
	
	
	
PM05	
	ppage0
	rbank0
	call	lcdclear
	bcf	STATUS,IRP
	movlw	low(progmode05)	; is "drum rotate"
	movwf	FSR
	movlw	6
	call	lcdtext

	movlw	a' '
	call	lcdsendchar
	movlw	a'O'
	call	lcdsendchar
	
	rbank2
	ppage3
	btfss	low(i2cbuf+8),0
	goto	PM05labeloff
PM05labelon
	ppage0
	movlw	a'N'
	call	lcdsendchar
	movlw	a' '
	call	lcdsendchar
	ppage3
	goto	PM05row2
PM05labeloff
	ppage0
	movlw	a'F'
	call	lcdsendchar
	movlw	a'F'
	call	lcdsendchar
PM05row2
	ppage0
	call	lcdline2
	movlw	low(onofflabel)	;
	movwf	FSR
	movlw	8
	call	lcdtext

	;; now check button-presses
	call	waitpress	; also waits for knob change
	;; now process key press
	ppage3
	btfsc	portbbuff,5	;button by knob proceeds to deal with this step
	goto	PM06
	btfsc	portbbuff,6	;right button increments the step to edit
	goto	PM05setoff
	btfsc	portbbuff,7	;left button decrements the step
	goto	PM05seton	;
	goto	PM05		;shouldn't get here, but just in case, simply ignore keypress
	
PM05seton
	rbank2
	bsf	low(i2cbuf+8),0
	goto	PM06

PM05setoff
	rbank2
	bcf	low(i2cbuf+8),0
	goto	PM06


PM06
	ppage0
	rbank0
	call	lcdclear
	bcf	STATUS,IRP
	movlw	low(progmode06)	; asks about element fan
	movwf	FSR
	movlw	6
	call	lcdtext

	movlw	a' '
	call	lcdsendchar
	movlw	a'O'
	call	lcdsendchar
	
	rbank2
	ppage3
	btfss	low(i2cbuf+8),1
	goto	PM06labeloff
PM06labelon
	ppage0
	movlw	a'N'
	call	lcdsendchar
	movlw	a' '
	call	lcdsendchar
	ppage3
	goto	PM06row2
PM06labeloff
	ppage0
	movlw	a'F'
	call	lcdsendchar
	movlw	a'F'
	call	lcdsendchar
PM06row2
	ppage0
	call	lcdline2
	movlw	low(onofflabel)	;
	movwf	FSR
	movlw	8
	call	lcdtext

	;; now check button-presses
	call	waitpress	; also waits for knob change
	;; now process key press
	ppage3
	btfsc	portbbuff,5	;button by knob proceeds to deal with this step
	goto	PM07
	btfsc	portbbuff,6	;right button increments the step to edit
	goto	PM06setoff
	btfsc	portbbuff,7	;left button decrements the step
	goto	PM06seton	;
	goto	PM06		;shouldn't get here, but just in case, simply ignore keypress
	
PM06seton
	rbank2
	bsf	low(i2cbuf+8),1
	goto	PM07

PM06setoff
	rbank2
	bcf	low(i2cbuf+8),1
	goto	PM07


PM07
	ppage0
	rbank0
	call	lcdclear
	bcf	STATUS,IRP
	movlw	low(progmode07)	; asks about door
	movwf	FSR
	movlw	6
	call	lcdtext

	movlw	a' '
	call	lcdsendchar
	
	rbank2
	ppage3
	btfss	low(i2cbuf+8),4
	goto	PM07labeloff
PM07labelon
	ppage0
	movlw	a'Y'
	call	lcdsendchar
	movlw	a'E'
	call	lcdsendchar
	movlw	a'S'
	call	lcdsendchar
	ppage3
	goto	PM07row2
PM07labeloff
	ppage0
	movlw	a'N'
	call	lcdsendchar
	movlw	a'O'
	call	lcdsendchar
PM07row2
	ppage0
	call	lcdline2
	movlw	low(yesnolabel)	;
	movwf	FSR
	movlw	8
	call	lcdtext

	;; now check button-presses
	call	waitpress	; also waits for knob change
	;; now process key press
	ppage3
	btfsc	portbbuff,5	;button by knob proceeds to deal with this step
	goto	PM08
	btfsc	portbbuff,6	;right button increments the step to edit
	goto	PM07setoff
	btfsc	portbbuff,7	;left button decrements the step
	goto	PM07seton	;
	goto	PM07		;shouldn't get here, but just in case, simply ignore keypress
	
PM07seton
	rbank2
	bsf	low(i2cbuf+8),4
	goto	PM08

PM07setoff
	rbank2
	bcf	low(i2cbuf+8),4
	goto	PM08


PM08
	ppage0
	rbank0
	call	lcdclear
	bcf	STATUS,IRP
	movlw	low(progmode08)	; asks about cooling fans
	movwf	FSR
	movlw	6
	call	lcdtext

	movlw	a' '
	call	lcdsendchar
	movlw	a'O'
	call	lcdsendchar

	
	rbank2
	ppage3
	btfss	low(i2cbuf+9),0
	goto	PM08labeloff
PM08labelon
	ppage0
	movlw	a'N'
	call	lcdsendchar
	ppage3
	goto	PM08row2
PM08labeloff
	ppage0
	movlw	a'F'
	call	lcdsendchar
	movlw	a'F'
	call	lcdsendchar
PM08row2
	ppage0
	call	lcdline2
	movlw	low(onofflabel)	;
	movwf	FSR
	movlw	8
	call	lcdtext

	;; now check button-presses
	call	waitpress	; also waits for knob change
	;; now process key press
	ppage3
	btfsc	portbbuff,5	;button by knob proceeds to deal with this step
	goto	PM09
	btfsc	portbbuff,6	;right button increments the step to edit
	goto	PM08setoff
	btfsc	portbbuff,7	;left button decrements the step
	goto	PM08seton	;
	goto	PM08		;shouldn't get here, but just in case, simply ignore keypress
	
PM08seton
	rbank2
	bsf	low(i2cbuf+9),0
	goto	PM09

PM08setoff
	rbank2
	bcf	low(i2cbuf+9),0
	goto	PM09

	
PM09
	ppage0
	rbank0
	call	lcdclear
	bcf	STATUS,IRP
	movlw	low(progmode09)	; proportional band label
	movwf	FSR
	movlw	8
	call	lcdtext

PM09row2
	ppage0			;this is needed whern we goto PM09row2
	rbank2
	movf	low(i2cbuf+d'14'),w
	rbank0
	movwf	lowb
	clrf	highb
	bcf	STATUS,IRP
	movlw	lowb
	movwf	FSR
	call	b2bcd

	movlw	0x49
	call	lcdaddress
	movlw	a'>'
	call	lcdsendchar
	
	movf	bcdht,w
	andlw	0x0f		;mask all but low digit
	addlw	a'0'
	call	lcdsendchar

	swapf	bcdut,w
	andlw	0x0f		;mask all but low digit
	addlw	a'0'
	call	lcdsendchar
	
	movf	bcdut,w
	andlw	0x0f		;mask all but low digit
	addlw	a'0'
	call	lcdsendchar

	movlw	a'<'
	call	lcdsendchar

	;; now wait for action
	call	waitpress
	ppage3
	btfsc	portbbuff,4	;indicates knob changed
	goto	PM09knob
	btfsc	portbbuff,5	;button by knob proceeds to deal with this step
	goto	PM10
	goto	PM09row2	;could get here, becasue L & R don't do anything

PM09knob
	btfss	portbbuff,3
	goto	PM09kminus
PM09kplus
	rbank2
	incf	low(i2cbuf+d'14'),w
	addlw	d'55'		;carry will be clear if increment went > 200
	btfsc	STATUS,C
	movlw	d'255'
	addlw	d'-55'
	movwf	low(i2cbuf+d'14')
	goto	PM09row2
PM09kminus
	rbank2
	decf	low(i2cbuf+d'14'),w
	addlw	d'-1'
	btfss	STATUS,C
	movlw	0
	addlw	1
	movwf	low(i2cbuf+d'14')
	goto	PM09row2

	
PM10	
	ppage0
	rbank0
	call	lcdclear
	bcf	STATUS,IRP
	movlw	low(progmode10)	; asks abouut power limits
	movwf	FSR
	movlw	8
	call	lcdtext

	;; start by setting start temperature
	clrf	pmflags

PM10row2
	ppage0
	call	lcdline2
	movlw	'm'
	call	lcdsendchar
	movlw	'i'
	call	lcdsendchar
	movlw	'n'
	call	lcdsendchar
	movlw	' '
	btfss	pmflags,0
	movlw	'>'
	call	lcdsendchar
	rbank2
	movf	low(i2cbuf+d'16'),w
	ppage3
	call	w2bcd
	rbank2
	movwf	low(i2cbuf+d'33') ;we know this is spare - it's beyond our buffer

	;; now decide if we are at 100%, which needs special treatment
	;; because we only have two digits
	movf	low(i2cbuf+d'33'),f ;look at the value
	btfss	STATUS,Z
	goto	PM10mindigits	;if it's not 00 then show digits
	movf	low(i2cbuf+d'16'),f ;look at actual value
	btfsc	STATUS,Z	;
	goto	PM10mindigits	;if it is actually zero, show as digits
	;; since bcd digits are 00 but value is not 0, must be 100%
	ppage0
	movlw	a'1'
	call	lcdsendchar
	movlw	a'0'
	call	lcdsendchar
	movlw	a'0'
	call	lcdsendchar
	ppage3
	goto	PM10max
	
PM10mindigits	
	ppage0
	swapf	low(i2cbuf+d'33'),w
	andlw	0x0f
	addlw	a'0'
	call	lcdsendchar
	rbank2
	movf	low(i2cbuf+d'33'),w
	andlw	0x0f
	addlw	a'0'
	call	lcdsendchar
	movlw	' '
	btfss	pmflags,0
	movlw	'<'
	call	lcdsendchar
	
PM10max
	ppage0
	movlw	' '
	call	lcdsendchar
	movlw	' '
	call	lcdsendchar
	
	movlw	'm'
	call	lcdsendchar
	movlw	'a'
	call	lcdsendchar
	movlw	'x'
	call	lcdsendchar
	movlw	' '
	btfsc	pmflags,0
	movlw	'>'
	call	lcdsendchar
	rbank2
	movf	low(i2cbuf+d'15'),w
	ppage3
	call	w2bcd
	rbank2
	movwf	low(i2cbuf+d'33') ;we know this is spare - it's beyond our buffer

	;; now decide if we are at 100%, which needs special treatment
	;; because we only have two digits
	movf	low(i2cbuf+d'33'),f ;look at the value
	btfss	STATUS,Z
	goto	PM10maxdigits	;if it's not 00 then show digits
	movf	low(i2cbuf+d'15'),f ;look at actual value
	btfsc	STATUS,Z	;
	goto	PM10maxdigits	;if it is actually zero, show as digits
	;; since bcd digits are 00 but value is not 0, must be 100%
	ppage0
	movlw	a'1'
	call	lcdsendchar
	movlw	a'0'
	call	lcdsendchar
	movlw	a'0'
	call	lcdsendchar
	ppage3
	goto	PM10wait

PM10maxdigits
	ppage0
	rbank2
	swapf	low(i2cbuf+d'33'),w
	andlw	0x0f
	addlw	a'0'
	call	lcdsendchar
	rbank2
	movf	low(i2cbuf+d'33'),w
	andlw	0x0f
	addlw	a'0'
	call	lcdsendchar
	movlw	' '
	btfsc	pmflags,0
	movlw	'<'
	call	lcdsendchar

PM10wait
	ppage0
	;; now check button-presses
	call	waitpress	; also waits for knob change
	;; now process key press
	ppage3
	btfsc	portbbuff,4	;indicates knob changed
	goto	PM10knob
	btfsc	portbbuff,5	;button by knob proceeds to deal with this step
	goto	PM11
	btfsc	portbbuff,6	;right button increments the step to edit
	goto	PM10incflags	;
	btfsc	portbbuff,7	;left button decrements the step
	goto	PM10decflags	;
	goto	PM10row2	;shouldn't get here, but just in case, simply ignore keypress

PM10knob
	;; setting min or max?
	btfsc	pmflags,0
	goto	PM10kmax

PM10kmin
	;; point indf at min limit
	movlw	low(i2cbuf+d'16')
	movwf	FSR
	bsf	STATUS,IRP
	goto	PM10kdirection

PM10kmax
	;; point indf at max limit
	movlw	low(i2cbuf+d'15')
	movwf	FSR
	bsf	STATUS,IRP

PM10kdirection
	btfss	portbbuff,3
	goto	PM10kminus
PM10kplus
	rbank2
	incf	INDF,w
	addlw	d'155'		;carry will be clear if increment went > 100
	btfsc	STATUS,C
	movlw	d'255'
	addlw	d'-155'
	movwf	INDF	     ; record that in register
	;; now check that we don't exceed the defined max
	;; this doesn't limit self, becasue we won't be bigger than self
	;; but it does limit min not to exceed max
	subwf	low(i2cbuf+d'15'),w ; so w is now max - current, should not be -ve
	btfsc	STATUS,C	    ;if c=0, result was -ve
	goto	PM10row2	    ;otherwise loop round
	movf	low(i2cbuf+d'15'),w ; get our limiting value
	movwf	INDF		    ;set that as curent value
	goto	PM10row2	    ;loop round

PM10kminus
	rbank2
	decf	INDF,w
	addlw	d'1'
	btfsc	STATUS,C
	movlw	d'1'
	addlw	d'255'
	movwf	INDF
	;; INDF should not go less than min - see discussion in PM10kplus
	movf	low(i2cbuf+d'16'),w ;min limit
	subwf	INDF,w		    ; so  now current-min, should not be -ve
	btfsc	STATUS,C	    ;if c=0, result was -ve
	goto	PM10row2	    ; done - loop round
	movf	low(i2cbuf+d'16'),w ; get our limiting value
	movwf	INDF		    ;set that as curent value
	goto	PM10row2	    ;done -loop round

PM10incflags
	incf	pmflags,f
	goto	PM10row2
PM10decflags
	decf	pmflags,f
	goto	PM10row2


PM11
	ppage0
	rbank0
	call	lcdclear
	bcf	STATUS,IRP
	movlw	low(progmode11)	; asks for step name
	movwf	FSR
	movlw	8
	call	lcdtext

	;; start by byte 0
	clrf	pmflags

PM11row2
	ppage0
	movlw	b'00001100'	; 00001, display on, underline off, blink off
	call	lcdsendcmd	;  send to the lcd
	call	lcdline2
	rbank0
	movf	pmflags,w
	andlw	7		;ie, pmflags will be 0-7
	movwf	pmflags

	movlw	a' '
	call	lcdsendchar
	movlw	a' '
	call	lcdsendchar
	movlw	a' '
	call	lcdsendchar
	movlw	a'>'
	call	lcdsendchar

	;; display step name
	movlw	low(i2cbuf+d'24')
	movwf	FSR
	bsf     STATUS,IRP
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar
	incf	FSR,f
	movf	INDF,w
	call	lcdsendchar

	movlw	a'<'
	call	lcdsendchar

	
	;; now switch on cursor
	movlw	b'00001110'	; 00001, display on, underline on, blink off
	call	lcdsendcmd	;  send to the lcd

	movf	pmflags,w	;so w has which character to edit
	addlw	0x44		;this is address of first character of label
	call	lcdaddress

	movf	pmflags,w
	addlw	low(i2cbuf+d'24')
	movwf	FSR

	;; now check button-presses
	call	waitpress	; also waits for knob change
	;; now process key press
	ppage3
	btfsc	portbbuff,4	;indicates knob changed
	goto	PM11knob
	btfsc	portbbuff,5	;button by knob proceeds to deal with this step
	goto	PM12
	btfsc	portbbuff,6	;right button increments the step to edit
	goto	PM11plus
	btfsc	portbbuff,7	;left button decrements the step
	goto	PM11minus	;
	goto	PM11row2	;shouldn't get here, but just in case, simply ignore keypress


PM11knob
	btfss	portbbuff,3
	goto	PM11kminus
PM11kplus
	incf	INDF,w
	andlw	0x7f
	btfsc	STATUS,Z
	movlw	0x20
	movwf	INDF
	goto	PM11row2
		
PM11kminus
	decf	INDF,w
	addlw	d'-31'
	btfsc	STATUS,Z
	movlw	(0x7f-d'31')
	addlw	d'31'
	movwf	INDF
	goto	PM11row2

PM11plus
	incf	pmflags,f
	goto	PM11row2
PM11minus
	decf	pmflags,f
	goto	PM11row2
	
	

	

	
PM12
	ppage0
	rbank0
	call	lcdclear
	bcf	STATUS,IRP
	movlw	low(progmode12)	; asks whether to write to memory
	movwf	FSR
	movlw	8
	call	lcdtext

	call	lcdline2
	movlw	low(yesnolabel)	;
	movwf	FSR
	movlw	8
	call	lcdtext

		;; now check button-presses
	call	waitpress	; also waits for knob change
	;; now process key press
	ppage3
	btfsc	portbbuff,6	;saying no loops round to edit again
	goto	PM03
	btfsc	portbbuff,7	;saying yes does the write
	goto	PM12write	;
	goto	PM12		;would get here by knob or K button, ignore

PM12write
	;; now we actually send stuff to the i2ceeprom
	ppage0
	rbank0
        swapf   progstep,w      ; swap nybbles of step into w
        andlw   b'00001111'     ; and mask out upper nybble, so w now has high nybble of progstep
        rbank2                  ; select register bank 2
        movwf   i2chigh         ; and put that in high byte
        rbank0                  ; back to bank 0
        swapf   progstep,w      ; swap nybbles of step into w
        andlw   b'11110000'     ; and mask out lower nybble, so w now has (low nybble of progstep)*16
        rbank2                  ; select register bank 2
        movwf   i2clow          ; and put that in low byte
        ;i2chigh:i2clow now contain step*16
        bcf     STATUS,C        ; clear carry
        rlf     i2clow,f        ; low*2
        rlf     i2chigh,f       ; high*2 so bytes have proper address
	movlw   b'10100000'     ; memory, chip 0, write
        movwf   i2caddr         ; control byte set
	movlw	d'32'
	movwf	i2ccount
	call    i2ceeprom       ; do the write
	rbank0

PM13
	ppage0
	rbank0
	call	lcdclear
	bcf	STATUS,IRP
	movlw	low(progmode13)	; asks if there is more editing to do
	movwf	FSR
	movlw	8
	call	lcdtext

	call	lcdline2
	movlw	low(yesnolabel)	;
	movwf	FSR
	movlw	8
	call	lcdtext

	;; now check button-presses
	call	waitpress	; also waits for knob change
	;; now process key press
	ppage3
	btfsc	portbbuff,6	;saying no returns to standby mode
	goto	progtostandby
	btfsc	portbbuff,7	;saying yes does the write
	;; completed step edit or definition
	;; prognum and progstep must be defined to get here
	;; so we can just go to the select step process
	goto	PMselectstep	;
	goto	PM13		;would get here by knob or K button, ignore


progtostandby
	;; return from program mode to standby4
	ppage1			;idle mode is in prog page 1
	goto	standbyloop	; return to prog mode with current program selected
	
	
	
		
	end		; that's all folks!



	


and this is as far as it has got ... tune in again for another thrilling installment some time.


back to the main oven roaster page
back to the top roaster page
back to Ian's contents page
back to www.astounding.org.uk top contents page

To comment on anything (please do) email ian@astounding.org.uk