; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation; either version 2 of the License, or
; (at your option) any later version.

; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
; GNU General Public License for more details.

; You should have received a copy of the GNU General Public License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

; Panteltje io-0.1
; copyright Jan Panteltje 2007-always
; integer math copyright Microchip, some other routines copyright others, my part is GPL.
;
;
; This is analog and digital IO for control via RS232, for example from a PC.
; Uses the PIC internal 8MHz oscillator, and seems quite accurate,
; The soft has a serial 19200 Baud, 8 bits no parity interface, and accepts
; the following commands:
;
; unnENTER	  sets hour
; mnnENTER    sets minute
; annENTER    displays AD channel nn count
; vnnENTER    displays AD channel nn converted to voltage
; GnnnENTER   sets clock calibration, timer1 reload low byte, use 175 for nominal, is saved in EEPROM
; g           displays clock calibration (timer1 reload low byte)
;             at first power up (directly after programming) the EEPROM is set with the default value for G(175).
; h           help message tells you to read README, there is no README yet :-)
; pnnnENTER   sets PWM output value.
; snENTER	  sets output n
; cnENTER	  clears output n
; inENTER     reads input n
;
; If you need more analog inputs you need to set the related bits in TRISA / TRISB / TRISC,
; and ANSEL and ANSELH (about line 902 in the code).
; There are too many banksel instructions, and I likely forgot some.
; The math is done in integer, the floating point lib (copyright Microchip) is defined out.



; CODE IS FOR GPASM, may work on MPLAB too.


; set this to your supply voltage (use LM317 for example, ADC uses supply as reference).
; Vdd PIC measured 5.06V
; #define VREF	5060	; mV
#define VREF_H	D'19'	; high byte
#define VREF_L	D'196'	; low byte

; Command list:
; analog in:
; a0ENTER		AN0			pin 19
; a1ENTER		AN1			pin	18
; a2ENTER		AN2			pin	17
; a3ENTER		AN3			pin	 3
; a5ENTER		AN5			pin	15
; a7ENTER		AN7			pin	 7
; a8ENTER		AN8			pin	 8
; a9ENTER		AN9			pin	 9
;
; digital in:
; i0ENTER		RA3			pin  4
; i1ENTER		RA5			pin	 2
;
;
; digital out:
; S0ENTER		RB4			pin	13
; C0ENTER		RB4			pin	13
;
; s1ENTER		B6			pin	11
; c1ENTER		B6			pin	11
;
; s2ENTER		C0			pin	16
; c2ENTER		C0			pin	16
;
; s3ENTER		C2			pin	14
; c3ENTER		C2			pin	14
;
; s4ENTER		C4			pin	 6
; c4ENTER		C4			pin	 6
;
;
; PWM out:
; p100ENTER	CCP1		pin	 5



TRUE    equ     1
FALSE   equ     0
MSB		equ		7
SIGNED  equ     FALSE           ; Set This To 'TRUE' if the routines
;                               ; for Multiplication & Division needs
;                               ; to be assembled as Signed Integer
;                               ; Routines. If 'FALSE' the above two
;                               ; routines ( D_mpy & D_div ) use
;                               ; unsigned arithmetic.


; for commands with numeric arguments.
#define COMMAND_OFF									D'0'
#define COMMAND_READ_ADC							D'1'
#define COMMAND_PRINT_IIME							D'2'
#define COMMAND_SET_PWM								D'4'
#define COMMAND_PRINT_MILLIVOLTS					D'5'
#define COMMAND_SET_BIT								D'6'
#define COMMAND_CLEAR_BIT							D'7'
#define COMMAND_SET_MINUTES							D'8'
#define COMMAND_SET_HOURS							D'9'
#define COMMAND_CALIBRATE_CLOCK						D'10'
#define COMMAND_PRINT_CLOCK_CALIBRATION				D'11'
#define COMMAND_GET_BIT								D'12'

#define MODE_OFF									D'0'
#define MODE_READ_ADC								D'1'
#define MODE_ADJUST_PWM								D'2'
#define MODE_PRINT_MILLIVOLTS						D'3'
#define MODE_SET_BIT								D'4'
#define MODE_CLEAR_BIT								D'5'
#define MODE_CALIBRATE_CLOCK						D'6'
#define	MODE_PRINT_CLOCK_CALIBRATION				D'7'
#define MODE_PRINT_TIME								D'8'
#define MODE_GET_BIT								D'9'


; ** NOTE:                                                                            **

; define config fuses
; IF YOU WANT TO RUN VERIFY, BETTER HAVE COPY PROTECTION OFF ;-)
; Internal OSC, OSC clock output on pin 3 (RA4,AN3,T1G,OSC2,CLKOUT).
;	__CONFIG  _FCMEN_OFF & _IESO_OFF & _BOR_OFF & _CPD_OFF & _CP_OFF & _MCLRE_OFF & _PWRTE_OFF & _WDT_OFF & _INTOSC

; Internal OSC, pin 3 (RA4,AN3,T1G,OSC2,CLKOUT) available for IO.
	__CONFIG  _FCMEN_OFF & _IESO_OFF & _BOR_OFF & _CPD_OFF & _CP_OFF & _MCLRE_OFF & _PWRTE_OFF & _WDT_OFF & _INTOSCIO

; when !MRCLRE is asserted in INTOSC or RC mode, the internal clock oscillator is disabled.


; include PIC register definitions, and some macros
	PROCESSOR	p16f690
	include	<p16f690.inc>

; How about 8 analog in, 5 binary out, 2 binary in, 1 PWM out, TX, RX, ground, +Vdd, makes 20 pins?


; IO pin bit assignment PORTA, PORTB, PORTC
; RA and RB have change interrupt

; PORTA 6 bit wide
;RA0	pin 19  AN0		ICSPDAT			I?					analog input
;RA1	pin 18	AN1		ICSPCLK			I?					analog input
;RA2	pin 17	AN2		Vpp									analog input
;RA3	pin  4	MCLR			I							input				change interrupt
;RA4	pin  3	AN3		CLOCK OUT IN USE					analog input
;RA5	pin  2					I?							input				change interrupt
;RA6	
;RA7


; PORTB 4 bit wide
;RB4	pin 13	AN10										output
;RB5	pin	12	AN11	RX	
;RB6	pin	11												output
;RB7	pin 10	TX


; PORTC 8 bit wide

; TRISA inputs: 	RA0, RA1, RA2, RA3, RA4, RA5  
; TRISA output: 	none

; TRISB inputs: 	RB5
; TRISB outputs:	RB4, RB6, RB7


; TRISC inputs:		RC1, RC3, RC5, RC6, RC7
; TRISC outputs:	RC0, RC2, RC4, RC5


; Analog inputs:	AN0, AN1, AN2, AN3,    AN5,    AN7, AN8, AN9
; PWM outputs:		RC5

; Digital outputs:	RB4, RB6, RC0, RC2, RC4
; Digital inputs:	RA3, RA5


; define register file variables
; start at 0x20 hex, 32 dec, total 256 bytes

; 0x20 - 0x7f in bank 0						;  32 - 127
; 0x70 - 0x7f in each bank the same			; 112 - 127
; 0xa0 - 0xef in bank 1						; 160 - 239
; 0x120 - 0x16f in bank 2					; 288 - 376


;flags						equ	D'32'			; flags
delay_count1				equ	D'33'			; 1ms delay
delay_count2				equ	D'34'
RESULTLO					equ	D'35'
RESULTHI					equ	D'36'
tx_timeout					equ D'37'
flags1						equ	D'38'
temp1						equ	D'39'
count						equ	D'40'
temp_h						equ	D'41'
temp_l						equ	D'42'
temp						equ D'43'		; temp for binary to BCD
bin							equ	D'44'
H_byte						equ D'44'		; bin 16 in for binary to BCD
L_byte						equ D'45'		; bin 16 in for binary to BCD	
bcd							equ	D'46'
R0							equ D'46'		; BCD digit 5 right justified
R1							equ D'47'		; BCD digit 4 and 3
R2							equ D'48'		; BCD digit 2 and 1
ii							equ	D'49'
timer1_reload_l				equ	D'50'		; saved in EEPROM clock calibration
outside_temp_calibration	equ	D'51'
inside_temp_calibration		equ	D'52'
temp_correction				equ	D'53'
bit_number					equ	D'54'
status_display_timer		equ	D'55'
command						equ	D'56'
digit_in					equ	D'57'
digit_cnt					equ	D'58'
temp3						equ	D'59'
value						equ	D'60'
flags2						equ D'61'
flags3						equ	D'62'
pwm_pulse_width				equ	D'63'
analog_select				equ	D'64'
minutes						equ	D'65'
seconds_5					equ	D'66'
seconds						equ	D'67'
ana0_h						equ	D'68'
ana0_l						equ	D'69'
ana1_h						equ	D'70'
ana1_l						equ	D'71'
ana2_h						equ	D'72'
ana2_l						equ	D'73'
ana3_h						equ	D'74'
ana3_l						equ	D'75'
ana4_h						equ	D'76'
ana4_l						equ	D'77'
ana5_h						equ	D'78'
ana5_l						equ	D'79'
ana6_h						equ	D'80'
ana6_l						equ	D'81'
ana7_h						equ	D'82'
ana7_l						equ	D'83'
ana8_h						equ	D'84'
ana8_l						equ	D'85'
ana9_h						equ	D'86'
ana9_l						equ	D'87'
ana10_h						equ	D'88'
ana10_l						equ	D'89'
ana11_h						equ	D'90'
ana11_l						equ	D'91'
pri_h						equ	D'92'
pri_l						equ	D'93'
pri_cnt						equ	D'94'
fsr_save					equ	D'95'
fsr_save2					equ	D'96'
mode						equ	D'97'
ad_h						equ	D'98'
ad_l						equ	D'99'


temp_s_h					equ	D'102'
temp_s_l					equ	D'103'
vref_h						equ	D'104'				; to be saved in EEPROM
vref_l						equ	D'105'				; to be saved in EEPROM
millivolt_h					equ	D'106'
millivolt_l					equ	D'107'
kelvin_h					equ	D'108'
kelvin_l					equ	D'109'
temp_int					equ	D'110'
hours						equ	D'111'

; SAME IN ALL BANKS 112 - 127

eeprom_address				equ	D'113'
eeprom_data					equ	D'114'

ACCaLO						equ	D'115'
ACCaHI						equ	D'116'
ACCbLO						equ	D'117'
ACCbHI						equ	D'118'
ACCcLO						equ	D'119'
ACCcHI						equ	D'120'
ACCdLO						equ	D'121'
ACCdHI						equ	D'122'
sign						equ	D'123'
s_pclath					equ	D'124'
offset						equ	D'125'
temp_w						equ	D'126'
temp_s						equ	D'127'


; define  flags1
ONE_MINUTE_FLAG				equ	D'0'
FIRST_ZERO_SUPPRESSED_FLAG  equ D'2'
HAVE_INTERRUPT_FLAG			equ	D'4'
EEPROM_WRITE_ERROR_FLAG     equ D'5'
ONE_SECOND_FLAG				equ	D'7'

; define flags

; macros to save and restore W and status register in interrupt.
save_w_stat macro
	movwf   temp_w
	swapf   STATUS,W
	clrf	STATUS			; extra force bank 0 clears IRP, RP1, RP0
	movwf   temp_s
	movfw	PCLATH
	movwf	s_pclath
	endm

restore_w_stat macro
	movfw	s_pclath
	movwf	PCLATH
	swapf   temp_s,W
	movwf   STATUS
	swapf   temp_w,F
	swapf   temp_w,W
	endm

; code start
	org	0
	goto	reset_entry

	org	4
; interrupt entry point
; 8 MHz internal OSC
; OPTION_REG is 0xff at power up!

; do interrupt processing here

	save_w_stat					; save W and status

	banksel	0

; what interrupt?
; test for timer 1 interrupt
test_timer1_interrupt:
;	banksel	PIR1				; bank 0
	btfss	PIR1, TMR1IF
	goto	test_rx_interrupt	

; decrement seconds / 5, if 0 reload 5, decrement seconds.
	bcf	flags1, ONE_SECOND_FLAG
	decfsz	seconds_5
	goto	in_5

	bsf	flags1, ONE_SECOND_FLAG
	movlw	D'5'
	movwf	seconds_5

; increment seconds
	incf	seconds
; test 60 seconds	
	movlw	D'60'
	subwf	seconds, w
	btfss	STATUS, Z
	goto	in_5

one_minute:
	clrf	seconds	
	incf	minutes

	bsf	flags1, ONE_MINUTE_FLAG			; to be checked and cleared in main

; test 60 minutes
	movlw	D'60'
	subwf	minutes,	w
	btfss	STATUS,	Z	
	goto	in_5

	clrf	minutes
	incf	hours
; test 24 hours
	movlw	D'24'
	subwf	hours,	w
	btfss	STATUS, Z
	goto	in_5

	clrf	hours

in_5:

; reload 65535 - 50000 makes 5Hz = 200mS

; Calibrate clock speed here
;	banksel	TMR1H				; bank 0 65535 - 50000 = 15535 reload (upcounter int on overflow), makes 60 175
	movlw	D'60'
	movwf	TMR1H

;	movlw	D'175'
	movfw	timer1_reload_l
	movwf	TMR1L

;	banksel	T1CON				; in bank 0
	bsf	T1CON,	T1CKPS1			; 11 = 1:8 prescaler 
	bsf	T1CON,	T1CKPS0			; 

;	banksel	0
;	goto	int_end
	
test_rx_interrupt:
;	banksel	PIR1
;	btfss	PIR1, RCIF	
;	goto	int_end


; read rx status	
; test framing error
	banksel	RCSTA
	btfss	RCSTA,	FERR
	goto	test_overrun
; framing error
	goto	reset_rx_circuit

test_overrun:
	btfss	RCSTA,	OERR
	goto	get_rx_char
; overrun error

reset_rx_circuit:
; clear error flags
	bcf	RCSTA,	FERR
	bcf	RCSTA,	OERR
; restart rx
	bcf	RCSTA,	CREN
	bsf	RCSTA,	CREN
;	goto	int_end					; discard character

; send Bell audio signal immediatly
	movlw	D'7'					; ring bell
	call	tx_w
	goto	int_end

get_rx_char:
; test_serial_port_interrupt
	btfss	PIR1,	RCIF				; test if serial port interrupt
	goto	int_end

; have serial char in RCREG
;	movfw	RCREG
; TEST echo for TEST	
;	call tx_w

	movlw	D'13'
	subwf	RCREG,	W
	btfsc	STATUS, Z
	goto	cr_command

	movlw	'a'
	subwf	RCREG,	W
	btfsc	STATUS, Z
	goto	read_adc_command

	movlw	'c'
	subwf	RCREG,	W
	btfsc	STATUS, Z
	goto	clear_bit_command

;	movlw	'E'
;	subwf	RCREG,	W
;	btfsc	STATUS, Z
;	goto	mark_eeprom_erased_command

	movlw	'g'
	subwf	RCREG,	W
	btfsc	STATUS, Z
	goto	print_clock_calibration_command

	movlw	'G'
	subwf	RCREG,	W
	btfsc	STATUS, Z
	goto	calibrate_clock_command

	movlw	'h'
	subwf	RCREG,	W
	btfsc	STATUS, Z
	goto	print_help

	movlw	'i'
	subwf	RCREG,	W
	btfsc	STATUS, Z
	goto	get_bit_command

	movlw	'm'
	subwf	RCREG,	W
	btfsc	STATUS, Z
	goto	set_minutes_command

	movlw	'p'
	subwf	RCREG,	W
	btfsc	STATUS, Z
	goto	set_pwm_command

	movlw	's'
	subwf	RCREG,	W
	btfsc	STATUS, Z
	goto	set_bit_command

	movlw	't'
	subwf	RCREG,	W
	btfsc	STATUS, Z
	goto	print_time_command

	movlw	'u'
	subwf	RCREG,	W
	btfsc	STATUS, Z
	goto	set_hours_command

	movlw	'v'
	subwf	RCREG,	W
	btfsc	STATUS, Z
	goto	print_millivolts_command

	movlw	'0'
	subwf	RCREG,	W
	btfsc	STATUS, Z
	goto	process_digits_command

	movlw	'1'
	subwf	RCREG,	W
	btfsc	STATUS, Z
	goto	process_digits_command

	movlw	'2'
	subwf	RCREG,	W
	btfsc	STATUS, Z
	goto	process_digits_command

	movlw	'3'
	subwf	RCREG,	W
	btfsc	STATUS, Z
	goto	process_digits_command

	movlw	'4'
	subwf	RCREG,	W
	btfsc	STATUS, Z
	goto	process_digits_command

	movlw	'5'
	subwf	RCREG,	W
	btfsc	STATUS, Z
	goto	process_digits_command

	movlw	'6'
	subwf	RCREG,	W
	btfsc	STATUS, Z
	goto	process_digits_command

	movlw	'7'
	subwf	RCREG,	W
	btfsc	STATUS, Z
	goto	process_digits_command

	movlw	'8'
	subwf	RCREG,	W
	btfsc	STATUS, Z
	goto	process_digits_command

	movlw	'9'
	subwf	RCREG,	W
	btfsc	STATUS, Z
	goto	process_digits_command

	goto	int_end


cr_command:
; If zero digits were entered, do nothing

	movlw	COMMAND_OFF
	subwf	digit_cnt,	W
	btfsc	STATUS,	Z						; if zero digits do nothing
	goto	int_end				

	movlw	COMMAND_READ_ADC
	subwf	command,	W
	btfsc	STATUS, Z
	goto	end_read_adc_command

	movlw	COMMAND_SET_PWM
	subwf	command,	W
	btfsc	STATUS, Z
	goto	end_set_pwm_command

	movlw	COMMAND_PRINT_MILLIVOLTS
	subwf	command,	W
	btfsc	STATUS, Z
	goto	end_print_millivolts_command

	movlw	COMMAND_SET_MINUTES
	subwf	command,	W
	btfsc	STATUS, Z
	goto	end_set_minutes_command

	movlw	COMMAND_SET_HOURS
	subwf	command,	W
	btfsc	STATUS, Z
	goto	end_set_hours_command

	movlw	COMMAND_GET_BIT
	subwf	command,	W
	btfsc	STATUS,	Z
	goto	end_get_bit_command

	movlw	COMMAND_SET_BIT
	subwf	command,	W
	btfsc	STATUS,	Z
	goto	end_set_bit_command

	movlw	COMMAND_CLEAR_BIT
	subwf	command,	W
	btfsc	STATUS,	Z
	goto	end_clear_bit_command

	movlw	COMMAND_CALIBRATE_CLOCK
	subwf	command,	W
	btfsc	STATUS, Z
	goto	end_calibrate_clock_command

	goto	int_end


; these commands have no numeric argument
print_help:
	call	help_pri
	goto	int_end_clr

print_clock_calibration_command:
	movlw	COMMAND_PRINT_CLOCK_CALIBRATION
	movwf	command
	movlw	MODE_PRINT_CLOCK_CALIBRATION
	movwf	mode
	goto	int_end_clr

print_time_command:
	movlw	COMMAND_PRINT_IIME
	movwf	command
	movlw	MODE_PRINT_TIME
	movwf	mode
	goto	int_end_clr

;mark_eeprom_erased_command:

;	banksel	0

;	movlw	D'255'
;	movwf	eeprom_address
		
;	movlw	D'255'
;	movwf	eeprom_data
	
;	call	eeprom_write

;	goto	int_end_clr


; these commands have a numeric argument
set_pwm_command:
	movlw	COMMAND_SET_PWM
	movwf	command
	goto	command_end

read_adc_command:
	movlw	COMMAND_READ_ADC
	movwf	command
	goto	command_end

print_millivolts_command:
	movlw	COMMAND_PRINT_MILLIVOLTS
	movwf	command
	goto	command_end

set_minutes_command:
	movlw	COMMAND_SET_MINUTES
	movwf	command
	goto	command_end

set_hours_command:
	movlw	COMMAND_SET_HOURS
	movwf	command
	goto	command_end

calibrate_clock_command:
	movlw	COMMAND_CALIBRATE_CLOCK
	movwf	command
	goto	command_end

set_bit_command:
	movlw	COMMAND_SET_BIT
	movwf	command
	goto	command_end

clear_bit_command:
	movlw	COMMAND_CLEAR_BIT
	movwf	command
	goto	command_end

get_bit_command:
	movlw	COMMAND_GET_BIT
	movwf	command
	goto	command_end

; final processing of commands with numeric arguments, we have the number in 'value' now.
end_set_pwm_command:
	movfw	value
	movwf	pwm_pulse_width
	movlw	MODE_ADJUST_PWM
	movwf	mode
	goto	command_end_status

end_read_adc_command:
	movfw	value
	movwf	analog_select
	movlw	MODE_READ_ADC
	movwf	mode
	goto	command_end_status

end_print_millivolts_command:
	movfw	value
	movwf	analog_select
	movlw	MODE_PRINT_MILLIVOLTS
	movwf	mode
	goto	command_end_status

end_set_minutes_command:
	movfw	value
	movwf	minutes
	goto	command_end_status

end_set_hours_command:
	movfw	value
	movwf	hours
	goto	command_end_status

end_calibrate_clock_command:
	movfw	value
	movwf	timer1_reload_l
	call	save_settings
	goto	command_end_status

end_inside_temp_calibration_command:
	movfw	value
	movwf	inside_temp_calibration
	call	save_settings
	goto	command_end_status
	
end_outside_temp_calibration_command:
	movfw	value
	movwf	outside_temp_calibration
	call	save_settings
	goto	command_end_status

end_set_bit_command:
	movfw	value
	movwf	bit_number
	movlw	MODE_SET_BIT
	movwf	mode
	goto	command_end_status

end_clear_bit_command:
	movfw	value
	movwf	bit_number
	movlw	MODE_CLEAR_BIT
	movwf	mode
	goto	command_end_status

end_get_bit_command:
	movfw	value
	movwf	bit_number
	movlw	MODE_GET_BIT
	movwf	mode
	goto	command_end_status

process_digits_command:
 	movlw	D'48'
	subwf	RCREG,	W			; digit now in W

	movwf	digit_in

; value = value * 10
; value += RCREG

	movfw	value				; W = original value
; look, I know about rlf, but we have 4096 words available.
	addwf	value				; value * 2
	addwf	value				; value * 3
	addwf	value				; value * 4
	addwf	value				; value * 5
	addwf	value				; value * 6
	addwf	value				; value * 7
	addwf	value				; value * 8
	addwf	value				; value * 9
	addwf	value				; value * 10

	movfw	digit_in			; input digit in W
	addwf	value				; add W to value

	incf	digit_cnt
	goto	int_end

; end of interrupt routines
command_end_status:
	banksel	0
	clrf	status_display_timer

command_end:
; reset the value, so we can continue for x, y, f, or z with 200ENTER
	banksel	0
	clrf	value
	clrf	digit_cnt
	goto	int_end

int_end_clr:					; for non numeric commands, no digits expected.
	banksel	0
	clrf	status_display_timer
	clrf	command

int_end:
	banksel	0
;	OLD IF COMPARATOR movf	CMCON,	W			; read CMCON to end mismatch because of comparator output change
; INTCON:  GIE PEIE T0IE INTE RABIE T0IF INTF RABIF
	bcf INTCON,	GIE
;	bcf	INTCON,	PEIE
	bcf INTCON,	T0IE
	bcf INTCON,	INTE
	bcf INTCON,	RABIE
	bcf	INTCON,	T0IF			; timer 0 overflow interrupt flag
	bcf	INTCON,	INTF			; GP2/INT
	bcf	INTCON,	RABIF			; port change interrupt flag bit

; PIR1   --   ADIF RCIF TXIF  SSPIF CCP1IF TMR2IF TMR1IF
	bcf	PIR1,	ADIF
	bcf	PIR1,	RCIF
	bcf	PIR1,	TXIF
	bcf	PIR1,	SSPIF
	bcf	PIR1,	CCP1IF
	bcf	PIR1,	TMR2IF
	bcf	PIR1,	TMR1IF

; PIR2  OSFIF C2IF C1IF EEIF   --     --     --     --
	bcf	PIR2,	OSFIF
	bcf	PIR2,	C2IF
	bcf	PIR2,	C1IF
	bcf	PIR2,	EEIF

	restore_w_stat				; get back W and status
	retfie


;	org 600
reset_entry:

; set internal OSC speed, the output on pin 3 (OSC2) is this clock / 4. 
; SELECT BANK
	banksel OSCCON
;	crlf OSCCON			; all zero
;	bcf	OSCCON, 7		; bit 7  not implemented

	bsf	OSCCON, 6		; bit 6 osc speed  111=8 MHz, 110=4MHz (default), 101=2MHz, 100=1MHz, 011=500kHz, 010=250kHz, 001=125kHz, 000=32kHz (LFINTOSC) 
	bsf	OSCCON, 5		; bit 5 osc speed
	bsf	OSCCON, 4		; bit 4 osc speed

	bcf	OSCCON, 3		; bit 3, HTS 0 = internal OSC, 1 = external

;	bcf	OSCCON, 2		; bit 2, HTS HFINTOSC status, 1 = stable
;	bcf	OSCCON, 1		; bit 1, LTS  LFINTOSC status, 1 = stable

	bcf	OSCCON, 0		; bit 0, SCS 0 = clock source defined by FOSC<2:0> of the CONFIG register, 1 = internal osc used for system clock


; initialize IO output latches

	banksel	0


; load settings from eeprom 0
	banksel	0

; fake erased eeprom
;	movlw	D'255'
;	movwf	eeprom_address

;	movlw	D'255'
;	movwf	eeprom_data

;	call	eeprom_write

;	call	load_settings			; this sets flags1


; GPIO bank 0, address 0x05
	clrf	PORTA				; init all output latches to zero
	clrf	PORTB
	clrf	PORTC


; portb init (4 to 7)
	banksel	0

	CLRF  PORTB      		; Init PORTB

	banksel TRISB
	MOVLW 00h				; Set RB<7:4> as outputs
	MOVWF TRISB

;	banksel	0

; init async serial out 
;12.1.1.6        Asynchronous Transmission Set-up:
;1.  Initialize the SPBRGH, SPBRG register pair and
;    the BRGH and BRG16 bits to achieve the desired
;    baud rate (see Section 12.3 "EUSART Baud
;    Rate Generator (BRG)").
;2.  Enable the asynchronous serial port by clearing
;    the SYNC bit and setting the SPEN bit.
;3.  If 9-bit transmission is desired, set the TX9 con-
;    trol bit. A set ninth data bit will indicate that the 8
;    Least Significant data bits are an address when
;    the receiver is set for address detection.
;4.  Enable the transmission by setting the TXEN
;    control bit. This will cause the TXIF interrupt bit
;    to be set.
;5.  If interrupts are desired, set the TXIE interrupt
;    enable bit. An interrupt will occur immediately
;    provided that the GIE and PEIE bits of the
;    INTCON register are also set.
;6.  If 9-bit transmission is selected, the ninth bit
;    should be loaded into the TX9D data bit.
;7.  Load 8-bit data into the TXREG register. This
;    will start the transmission.

; 8 bit async
; SYNC = 0, BRG16 = 0, BRGH=1, at 8MHz: 19231 Baud, 0.16% for SPBRG=25
;	banksel TRISB
;	bcf TRISB, TRISB7			; pin 10 (RB7,TX,CK) output	

	banksel SPBRG
	movlw	D'25'
	movwf	SPBRG

;	default 8 bit mode, SPBRGH not used
;	banksel SPBRGH
	
	banksel BAUDCTL
	bcf	BAUDCTL, BRG16

	banksel TXSTA
	bsf TXSTA, BRGH
	bcf TXSTA, SYNC

	banksel RCSTA
	bsf RCSTA, SPEN
	
	banksel TXSTA
	bsf TXSTA, TXEN


;12.1.2.8        Asynchronous Reception Set-up:
;1.  Initialize the SPBRGH, SPBRG register pair and
;    the BRGH and BRG16 bits to achieve the
;    desired baud rate (see Section 12.3 "EUSART
;    Baud Rate Generator (BRG)").
;2.  Enable the serial port by setting the SPEN bit.
;    The SYNC bit must be clear for asynchronous
;    operation.
;3.  If interrupts are desired, set the RCIE interrupt
;    enable bit and set the GIE and PEIE bits of the
;    INTCON register.
;4.  If 9-bit reception is desired, set the RX9 bit.
;5.  Enable reception by setting the CREN bit.
;6.  The RCIF interrupt flag bit will be set when a
;    character is transferred from the receive shift
;    register to the receive buffer. An interrupt will be
;    generated if the RCIE interrupt enable bit was
;    also set.
;7.  Read the RCSTA register to get the error flags
;    and, if 9-bit data reception is enabled, the ninth
;    data bit.
;8.  Get the received 8 Least Significant data bits
;    from the receive buffer by reading the RCREG
;    register.
;9.  If an overrun occurred, clear the OERR flag by
;    clearing the CREN receiver enable bit.


	banksel RCSTA
	bsf	RCSTA, CREN
; SYNC and SPEN already done.	

	banksel	0

; Load 5 Hz ticck
	movlw	D'5'
	movwf	seconds_5

; clock to 00:00
	clrf	seconds
	clrf	minutes
	clrf	hours


; init PWM
	call	pwm_init

;This code block configures the ADC
;for polling, Vdd reference, Frc clock
;and AN0 input.
;
;Conversion start & polling for completion
; are included.
;
	BANKSEL   ADCON1       ;
	MOVLW     B'00100000'	; not used, :32, 
							; bit 7	not used, zero
							; bit 6 clock speed
							; bit 5 clock speed
							; bit 4 clock speed
							; bit 3 not used
							; bit 2 not used
							; bit 1 not used
							; bit 0 not used
	MOVWF     ADCON1



; TRISA inputs: 	RA0, RA1, RA2, RA3, RA4, RA5  
; TRISA output: 	none

; TRISB inputs: 	RB5
; TRISB outputs:	RB4, RB6, RB7


; TRISC inputs:		RC1, RC3, RC5, RC6, RC7
; TRISC outputs:	RC0, RC2, RC4, RC5


; Analog inputs:	AN0, AN1, AN2, AN3,    AN5,    AN7, AN8, AN9
; PWM outputs:		RC5

; Digital outputs:	RB4, RB6, RC0, RC2, RC4
; Digital inputs:	RA3, RA5

	banksel	TRISA
	movlw	B'111111'
	movwf	TRISA

	banksel	TRISB
	movlw	B'1000'
	movwf	TRISB


	banksel TRISC
; 1 = input, 0 = output
	movlw	B'11001010'
; io port direction
						; bit 7 AN7			analog in	
						; bit 6	RC6			analog in
						; bit 5	RC5	CCP1	PWM out
						; bit 4 RC4			digital out
						; bit 3	RC3			analog in
						; bit 2	RC2			digtal out 
						; bit 1 AN5			analog in 
						; bit 0 RC0 		digital out
	movwf	TRISC

; select which port c pins are analog inputs
; Analog inputs:	AN0, AN1, AN2, AN3,    AN5,    AN7, AN8, AN9
	BANKSEL   ANSEL			; default is 1111 1111
	bsf	ANSEL, 0			; ASN 0
	bsf	ANSEL, 1			; ASN 1
	bsf	ANSEL, 2			; ASN 2
	bsf	ANSEL, 3			; ASN 3
	bcf	ANSEL, 4			; ASN 4
	bsf	ANSEL, 5			; ASN 5 	pin 15	analog in AN5
	bcf	ANSEL, 6			; ASN 6
	bsf	ANSEL, 7			; ASN 7		pin 7	analog in AN7

	BANKSEL   ANSELH		; default is 1111
	bsf	ANSELH, 0			; ASN 8
	bsf	ANSELH, 1			; ASN 9
	bcf	ANSELH, 2			; ASN 10
	bcf	ANSELH, 3			; ASN 11	UART RX
							; only lower 4 bits used	
; right justify analog result
	BANKSEL   ADCON0
	clrf ADCON0
	bsf ADCON0, ADFM		; ADFM right justified	
	bcf ADCON0, VCFG		; Vdd reference		

; select an analog channel for the ADC
	bcf	ADCON0, 5			; 0   0101= AN5
	bsf	ADCON0, 4			; 1
	bcf	ADCON0, 3			; 0
	bsf	ADCON0, 2			; 1

; bit 1 is A/D conversion status bit	
; start the ADC
	bsf ADCON0,	ADON		; AD on

; OPTION_REG bank 1, address 0x81
; !GPPU INTEDG T0CS T0SE PSA PS2 PS1 PS0

; serial com init
; OPTION_REG:  RABPU INTEDG T0CS T0SE PSA PS2 PS1 PS0
	banksel OPTION_REG			; PSA: 1= prescaler to WDT, 0 = prescaler to TIMER0
	movlw	B'00000001'			; prescaler 1
;	movlw	B'00001111'			; watchdog prescaler 128
	movwf	OPTION_REG
;	bsf	OPTION_REG,	NOT_GPPU	; no pullups 
; watchdog prescaler 128  111 bit<2-0>
;	bsf	OPTION_REG, PS2
;	bsf	OPTION_REG, PS1
;	bsf	OPTION_REG, PS0			; Watchdog prescaler 128


; from pdf page 84
;	BANKSEL TMR0           ;
;	CLRWDT                 ;Clear WDT
;	CLRF    TMR0           ;Clear TMR0 and prescaler
;	BANKSEL OPTION_REG     ;
;	BSF     OPTION_REG, PSA ;Select WDT
;	CLRWDT                 ;
;	MOVLW   b'11111000'    ;Mask prescaler
;	ANDWF   OPTION_REG, W  ; bits
;	IORLW   b'00000111'    ;Set WDT prescaler 1:128
;;	IORLW   b'00000101'    ;Set WDT prescaler
;	MOVWF   OPTION_REG     ; to 1:32


; serial in interrupt enable
	banksel	PIE1
	clrf	PIE1
	bsf PIE1,   RCIE            ; USART receive interrupt enable
;	bsf	PIE1,	ADIE			; AD converter interrupt enable (12F675 only)
	bsf	PIE1,	TMR1IE			; timer 1 overflow interrupt enable
;	bsf	PIE1,	CMIE			; comparator interrupt enable

; PIE2: OSFIE C2IE C1IE EEIE -- -- -- --
	banksel	PIE2
	bcf	PIE2,	EEIE			; EEPROM write complete interrupt enable
	
; set binary ouputs high (not active)
	banksel	PORTB
	bsf	PORTB, 4
	bsf	PORTB, 6
	bsf	PORTB, 7

	banksel	PORTC
	bsf	PORTC, 0
	bsf	PORTC, 2
	bsf	PORTC, 4
;	bsf	PORTC, 5				; PWM


; flags to zero
;	clrf 	flags
	clrf	flags1

; OPTION_REG bank 1, address 0x81
; !GPPU INTEDG T0CS T0SE PSA PS2 PS1 PS0

	banksel OPTION_REG
	movlw	B'00000001'			; prescaler 1
	movwf	OPTION_REG
;	bsf	OPTION_REG,	NOT_GPPU	; no pullups 

; timer 1  :4 clock = 2MHz, :8 prescaler=250000 Hz, :50000 reload=5Hz interrupt 
; T1CON:	T1GINV TMR1GE T1CKPS1 T1CKPS0 T1OSCEN T1SYNC TMR1CS TMR1ON
	banksel T1CON
	bcf	T1CON,	T1GINV			; non invert
	bcf	T1CON,	TMR1GE			; no gate enable
	bsf	T1CON,	T1CKPS1			; 11 = 1:8 prescaler 
	bsf	T1CON,	T1CKPS0			; 
	bcf	T1CON,	T1OSCEN			; no external clock
	bcf	T1CON,	NOT_T1SYNC		; no sync
	bcf	T1CON,	TMR1CS			; select :4 internal osc = 2MHz	
	bsf	T1CON,	TMR1ON			; timer 1 on

; set timer 1 reload 0, and clear TMR1IF as on page 88 pdf	
	movlw	D'0'
	banksel	TMR1H			; 50000 reload
	movwf	TMR1H
	banksel	TMR1L
	movwf	TMR1L
; reset timer 1 interrupt flag	
	banksel PIR1
	bcf	PIR1,	TMR1IF

	banksel 0

; enable gobal and peripheral interrupt
	banksel	INTCON
	clrf	INTCON
	bsf	INTCON,	PEIE			; peripheral interrupts enable
;	bsf	INTCON,	T0IE			; timer 0 overflow interrupt enable
;	bsf	INTCON,	INTE			; GP2/INT interrupt enable
;	bsf	INTCON,	GPIE			; port change interrupt enable
	bsf	INTCON,	GIE				; global interrupt enable

;	banksel	WDTCON
; configure watchdog timer
; WDTCON:  -- -- --  WDTPS3  WDTPS2 WDTPS1 WDTPS0 SWDTEN
;	movlw	B'00011111'		; software enable watchdog timer, : 65535 prescaler 31kHz osc, second prescaler : 128, 268 seconds
;	movwf	WDTCON	

	banksel	0
; print ID 
	call	print_id

; load settings from eeprom 0
	banksel	0
	call	load_settings			; this sets flags1

	banksel	0
;	clrf	mode
main_loop:
	banksel	0

; test_print_time
	movlw	MODE_PRINT_TIME
	subwf	mode,	w
	btfss	STATUS,	Z
	goto	test_adjust_pwm

; print time
	call	print_time

	banksel	0
	call	tx_crlf

	goto	main_end

; configure watchdog timer
;	banksel	WDTCON
; WDTCON:  -- -- --  WDTPS3  WDTPS2 WDTPS1 WDTPS0 SWDTEN
;	movlw	B'00011111'		; software enable watchdog timer, : 65535 prescaler 31kHz osc, second prescaler : 128, 268 seconds
;	movwf	WDTCON	

; from pdf page 84
;	BANKSEL	TMR0
;	CLRWDT								; Clear WDT
;	CLRF	TMR0						; Clear TMR0 and prescaler
;	BANKSEL	OPTION_REG
;	BSF		OPTION_REG, PSA				; Select WDT
;	CLRWDT
;	MOVLW	b'11111000'					; Mask prescaler
;	ANDWF	OPTION_REG, W				; bits
;	IORLW	b'00000111'					; Set WDT prescaler 1:128
;;	IORLW	b'00000101'					; Set WDT prescaler
;	MOVWF	OPTION_REG					; to 1:32
	
test_adjust_pwm:
	banksel	0
	movlw	MODE_ADJUST_PWM
	subwf	mode, w
	btfss	STATUS,	Z
	goto	test_read_adc

; adjust pwm, wait for correct reload moment here?
	movfw	pwm_pulse_width
	banksel	CCPR1L
	movwf	CCPR1L
	goto	main_end

test_read_adc:
	banksel	0
	movlw	MODE_READ_ADC
	subwf	mode, w
	btfss	STATUS, Z
	goto	test_print_millivolts

	call	read_adc

	banksel	0

	call	print_adc

	banksel	0

	call	tx_crlf

	goto	main_end


test_print_millivolts:
	banksel	0
	movlw	MODE_PRINT_MILLIVOLTS
	subwf	mode, w
	btfss	STATUS, Z
	goto	test_set_bit

	call	read_adc

	banksel	0
	call	ad_to_millivolts

	banksel	0
	call	print_millivolts

	banksel	0
	call	tx_crlf

	banksel	0

	goto	main_end


test_set_bit:
; set bit bitnumber to 1 
	banksel	0
	movlw	MODE_SET_BIT
	subwf	mode, w
	btfss	STATUS, Z
	goto	test_clear_bit

; Digital outputs:	RB4, RB6, RC0, RC2, RC4
;                     0,   1,   2,   3,   4
test_set_output_0:
	movfw	bit_number
	xorlw	D'0'	
	btfss	STATUS,	Z
	goto	test_set_output_1
; set output RB4
	bsf		PORTB,	D'4'
	goto	main_end

test_set_output_1:
	movfw	bit_number
	xorlw	D'1'	
	btfss	STATUS,	Z
	goto	test_set_output_2
; set output RB6
	bsf		PORTB,	D'6'
	goto	main_end

test_set_output_2:
	movfw	bit_number
	xorlw	D'2'	
	btfss	STATUS,	Z
	goto	test_set_output_3
; set output RC0
	bsf		PORTC,	D'0'
	goto	main_end

test_set_output_3:
	movfw	bit_number
	xorlw	D'3'	
	btfss	STATUS,	Z
	goto	test_set_output_4
; set output RC2
	bsf		PORTC,	D'2'
	goto	main_end
	
test_set_output_4:
	movfw	bit_number
	xorlw	D'4'	
	btfss	STATUS,	Z
	goto	main_end				; wrong request
; set output RC4
	bsf		PORTC,	D'4'
	goto	main_end
	
test_clear_bit:
; set bit bit_number to zero
	banksel	0
	movlw	MODE_CLEAR_BIT
	subwf	mode, w
	btfss	STATUS, Z
	goto	test_get_bit
; Digital outputs:	RB4, RB6, RC0, RC2, RC4
;                     0,   1,   2,   3,   4

test_clear_output_0:
	movfw	bit_number
	xorlw	D'0'	
	btfss	STATUS,	Z
	goto	test_clear_output_1
; clear output RB4
	bcf		PORTB,	D'4'
	goto	main_end

test_clear_output_1:
	movfw	bit_number
	xorlw	D'1'	
	btfss	STATUS,	Z
	goto	test_clear_output_2
; set output RB6
	bcf		PORTB,	D'6'
	goto	main_end

test_clear_output_2:
	movfw	bit_number
	xorlw	D'2'	
	btfss	STATUS,	Z
	goto	test_clear_output_3
; set output RC0
	bcf		PORTC,	D'0'
	goto	main_end

test_clear_output_3:
	movfw	bit_number
	xorlw	D'3'	
	btfss	STATUS,	Z
	goto	test_clear_output_4
; set output RC2
	bcf		PORTC,	D'2'
	goto	main_end

test_clear_output_4:
	movfw	bit_number
	xorlw	D'4'	
	btfss	STATUS,	Z
	goto	main_end					; wrong request
; set output RC4
	bcf		PORTC,	D'4'
	goto	main_end

test_get_bit:
; print bit bit_number
	banksel	0
	movlw	MODE_GET_BIT
	subwf	mode, w
	btfss	STATUS, Z
	goto	test_print_clock_calibration

; Digital inputs:	RA3, RA5
;                     0,   1
test_get_bit_0:
	movfw	bit_number
	xorlw	D'0'
	btfss	STATUS,	Z
	goto	test_get_bit_1

	movfw	PORTA
	andlw	B'00001000'
	btfss	STATUS,	Z
	goto	print_a_one
	goto	print_a_zero

test_get_bit_1:
	xorlw	D'1'
	btfss	STATUS,	Z
	goto	main_end				; wrong request
	
	movfw	PORTA
	andlw	B'00100000'
	btfss	STATUS,	Z
	goto	print_a_one
;	goto	print_a_zero	

print_a_zero
	movlw	'0'
	goto	print_one_or_zero
print_a_one:
	movlw	'1'
print_one_or_zero:
	call	tx_w
	call	tx_crlf
	goto	main_end

test_print_clock_calibration:
	banksel	0
	movlw	MODE_PRINT_CLOCK_CALIBRATION
	subwf	mode,	w
	btfss	STATUS, Z
	goto	main_loop						; IF NOT FOUND, ONLY CLEAR mode if something found.

	call	clock_calibration_pri	
	
	banksel	0

	call	tx_crlf	

	goto	main_end


main_end:
	banksel	0
	clrf	mode

	goto	main_loop

; ***************** subroutines ********************

read_adc:									; value for AD channel analog_select to ad_h and ad_l
	banksel	0
	movfw	analog_select

; TRISC or other ports must be set for input if used for ADC
; ANSEL and ANSELH must have bits set for analog input

; select an analog channel for the ADC

; set bit<5-2> in ADCONO for the selected input number
; mask out lowest 4 bits
	andlw	D'15'
	movwf	temp3

; to position
; RLF: carry to LSB, MSB to carry	
; clear carry	
	bcf	STATUS, C				; x 2
	rlf	temp3, 1
; clear carry	
	bcf	STATUS, C
	rlf	temp3, 1				; x 4

; to ADCON0
	movfw	temp3

	BANKSEL   ADCON0
	clrf	ADCON0
	movwf	ADCON0

; select an analog channel for the ADC
;	bcf	ADCON0, 5				; 0   0101= AN5
;	bsf	ADCON0, 4				; 1
;	bcf	ADCON0, 3				; 0
;	bsf	ADCON0, 2				; 1

	bsf ADCON0, ADFM			; ADFM right justified		bit 7
	bcf ADCON0, VCFG			; Vdd reference				bit 6

; bit 1 is A/D conversion status bit	
; start the ADC
	bsf ADCON0,	ADON		; AD on							bit	0

; sample time delay
	movlw	D'255'
	movwf	delay_count1	
adc_sample_delay:
	decfsz	delay_count1, F
	goto	adc_sample_delay

; set ADCON0 GO / !DONE bit to start the conversion
	BSF		ADCON0, GO			; Start conversion			bit	1

	BTFSC	ADCON0, GO			; Is conversion done?
	GOTO	$-1					; No, test again

	BANKSEL	ADRESH
	MOVF	ADRESH, W			; Read upper 2 bits

	banksel	0
	MOVWF	ad_h				; store in GPR space
	
	BANKSEL	ADRESL
	MOVF	ADRESL, W			; Read lower 8 bits

	banksel	0
	MOVWF	ad_l				; Store in GPR space

	return



#ifdef OLD_CODE
delay10us:						; delays W * 10 us 
	movlw	D'6'				; 10 us
	movwf	delay_count1
delay_loop1:
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	decf	delay_count1,f
	btfss	STATUS,	Z		
	goto	delay_loop1		

	return


delay_1ms:
;	movlw	D'100'				; 100 x 10 us = 1 ms
	movwf	delay_count2
delay_loop2:

	call	delay10us

	decf	delay_count2,f
	btfss	STATUS,	Z		
	goto	delay_loop2		
	return
#endif
		; OLD_CODE

; send byte in W
tx_digit_in_w:
	banksel	TXREG

	addlw	'0'				; zero
tx_w:
	movwf	TXREG
;	call	wait_tx_empty
; inline
	banksel	0
; make sure we have a timeout
	movlw	D'255'	
	movwf	tx_timeout
test_tx_empty:
	decfsz	tx_timeout,	1
	goto	test_txsta
	goto	tx_has_timed_out

test_txsta:
	btfss	TXSTA,	TRMT	
	goto	test_tx_empty

tx_has_timed_out:
	banksel	0

	return


print_w_ascii_dec:				; prints register W in ASCII decimal
	bsf	flags1,	FIRST_ZERO_SUPPRESSED_FLAG
	movwf	temp1
	clrf	count				; number of hundreds found
loop_hundreds:
	movlw	D'100'
	subwf	temp1
	btfss	STATUS,	C			; if no carry flag, no more hundreds, go count tenth		
	goto	count10				; substraction failed
	incf	count	
	goto	loop_hundreds
count10:
	movlw	D'0'
	subwf	count,	W
	btfsc	STATUS,	Z
	goto	suppress_first_zero

	bcf	flags1,	FIRST_ZERO_SUPPRESSED_FLAG

	movfw	count
	call	tx_digit_in_w			; print hundreds

suppress_first_zero:
	movlw	D'100'				; restore temp1 from one substract to many
	addwf	temp1	

	clrf	count				; number of tenth found
loop_tenth:
	movlw	D'10'
	subwf	temp1
	btfss	STATUS,	C			; if no carry flag no more tenth, only units left	
	goto	count1		
	incf	count
	goto	loop_tenth
count1:
	movlw	D'0'
	subwf	count,	W
	btfss	STATUS,	Z
	goto	print_tenth			; tenth not zero

; tenth zero
; test if zero supression was active in hundreds (first digit)
	btfsc	flags1,	FIRST_ZERO_SUPPRESSED_FLAG
	goto	print_units			; if first digit was not zero, print this zero	

print_tenth:
	movfw	count
	call	tx_digit_in_w			; print tenth

print_units:
	movlw	D'10'				; restore temp1 from 1 substract to many
	addwf	temp1

; units
	movfw	temp1
	call	tx_digit_in_w			; print units

	return



print_16_ascii_dec:				; prints 16 bit value in registers Hi_byte and LO_byte in ASCII decimal
	call	b2bcd

; suppress leading zeros
	bsf		flags1,	FIRST_ZERO_SUPPRESSED_FLAG

	movfw	bcd
	call	print_bcd_in_w_decimal_2_digits

	movfw	bcd+1
	call	print_bcd_in_w_decimal_2_digits

	movfw	bcd+2
	call	print_bcd_in_w_decimal_2_digits

; if the zero suppress flag is still set we had zero, so print zero
	btfss	flags1, FIRST_ZERO_SUPPRESSED_FLAG
	return

	movlw	D'0'
	call	tx_digit_in_w

	return


; Convert 32-bit binary number at <bin> into a bcd number
; at <bcd>. Uses Mike Keitz's procedure for handling bcd 
; adjust; Modified Microchip AN526 for 32-bits.
b2bcd:
	bcf		STATUS, C
	movlw	D'16'			; 32 for 32-bits
	movwf	ii				; make cycle counter
	clrf	bcd				; clear result area
	clrf	bcd+1
	clrf	bcd+2
;	clrf	bcd+3
;	clrf	bcd+4
	
b2bcd2:
	movlw	bcd				; make pointer
	movwf	FSR
	movlw	3				; was 5
	movwf	count

; Mike's routine:
b2bcd3:
	movlw	0x33		
	addwf	INDF, f			; add to both nybbles
	btfsc	INDF, 3			; test if low result > 7
	andlw	0xf0			; low result >7 so take the 3 out
	btfsc	INDF, 7			; test if high result > 7
	andlw	0x0f			; high result > 7 so ok
	subwf	INDF, f			; any results <= 7, subtract back
	incf	FSR, f			; point to next
	decfsz	count
	goto	b2bcd3
	
;	rlf	bin+3,f				; get another bit
;	rlf	bin+2,f
	rlf	bin+1,f
	rlf	bin+0,f

;	rlf	bcd+4,f				; put it into bcd
;	rlf	bcd+3,f
	rlf	bcd+2,f
	rlf	bcd+1,f
	rlf	bcd+0,f	
	decfsz	ii,f			; all done?
	goto	b2bcd2			; no, loop

	movlw	0
	movwf	bcd

	return


print_bcd_in_w_decimal_2_digits:

;	high digit first	
	movwf	temp				; save w
	swapf	temp, w
	andlw	0x0f
; test if zero
	btfss	STATUS, Z
; if not zero print it	
	goto	print_high_nibble	

test_lead_zero:
; test if leading zero suppress flag set
	btfsc	flags1, FIRST_ZERO_SUPPRESSED_FLAG
	goto	low_nibble		

print_high_nibble:
	bcf		flags1, FIRST_ZERO_SUPPRESSED_FLAG
	call    tx_digit_in_w

low_nibble:
; low digit second
	movfw	temp				; get w
	andlw	0x0f
; test if zero
	btfss	STATUS, Z
; if not zero print it
	goto print_low_nibble

; test if leading zero suppress flag is set
	btfsc	flags1, FIRST_ZERO_SUPPRESSED_FLAG
; return if n ozero print
	return	

print_low_nibble
	bcf		flags1, FIRST_ZERO_SUPPRESSED_FLAG
	call	tx_digit_in_w

	return


tx_crlf:
	movlw	D'13'
	call	tx_w				; CR
	movlw	D'10'				; LF
	call	tx_w
	return


;id_text:
;	DT	"panteltje hcs_pic-0.1"	; generates RETLW instructions
print_id:
	movlw	'P'
	call	tx_w
	movlw	'a'
	call	tx_w
	movlw	'n'
	call	tx_w
	movlw	't'
	call	tx_w
	movlw	'e'
	call	tx_w
	movlw	'l'
	call	tx_w
	movlw	't'
	call	tx_w
	movlw	'j'
	call	tx_w
	movlw	'e'
	call	tx_w
	movlw	' '
	call	tx_w
	movlw	'I'
	call	tx_w
	movlw	'O'
	call	tx_w
	movlw	'-'
	call	tx_w
	movlw	'0'
	call	tx_w
	movlw	'.'
	call	tx_w
	movlw	'1'
	call	tx_w
	call tx_crlf
	return


help_pri:
	movlw	'F'
	call	tx_w
	movlw	'o'
	call	tx_w
	movlw	'r'
	call	tx_w
	movlw	' '
	call	tx_w
	movlw	'h'
	call	tx_w
	movlw	'e'
	call	tx_w
	movlw	'l'
	call	tx_w
	movlw	'p'
	call	tx_w
	movlw	' '
	call	tx_w
	movlw	's'
	call	tx_w
	movlw	'e'
	call	tx_w
	movlw	'e'
	call	tx_w
	movlw	' '
	call	tx_w
	movlw	'R'
	call	tx_w
	movlw	'E'
	call	tx_w
	movlw	'A'
	call	tx_w
	movlw	'D'
	call	tx_w
	movlw	'M'
	call	tx_w
	movlw	'E'
	call	tx_w
	call	tx_crlf
	return


eeprom_write:
	banksel INTCON
	bcf	INTCON,	GIE				; disable interrupts

	BANKSEL EEADR				;
	MOVF    eeprom_address, W	;
	MOVWF   EEADR				; Data Memory Address to write
	MOVF    eeprom_data, W		;
	MOVWF   EEDAT				; Data Memory Value to write
	BANKSEL EECON1				;
	BCF     EECON1, EEPGD		; Point to DATA memory
	BSF     EECON1, WREN		; Enable writes
	BCF     INTCON, GIE			; Disable INTs.
	BTFSC   INTCON, GIE			; SEE AN576
	GOTO    $-2
	MOVLW   0x55				;
	MOVWF   EECON2				; Write 55h
	MOVLW   0xAA				;
	MOVWF   EECON2				; Write AAh
	BSF     EECON1, WR			; Set WR bit to begin write
	BSF     INTCON, GIE			; Enable INTs.
eeprom_poll_write_complete_loop:
    banksel EECON1
    btfsc   EECON1, WR
    goto    eeprom_poll_write_complete_loop

	BCF     EECON1, WREN		; Disable writes
	BANKSEL 0					; Bank 0

	call	eeprom_verify

	return


eeprom_verify:					; data at EEDADR against eeprom_data
	call eeprom_read			; EEDATA in w

	banksel	0
	bcf	flags1,	EEPROM_WRITE_ERROR_FLAG

	movfw	eeprom_data

	banksel	EEDATA
	subwf	EEDATA,	W			; compare to requested
	btfss	STATUS,	Z			; skip if no error

	goto	eeprom_write_error

	banksel	0

	return


eeprom_write_error:
	call	eeprom_report

	banksel	0
	bsf	flags1,	EEPROM_WRITE_ERROR_FLAG

	return


eeprom_report:
; eeprom write result
	banksel	0
	movlw	'e'
	call	tx_w
	movlw	'e'
	call	tx_w
	movlw	'p'
	call	tx_w
	movlw	'r'
	call	tx_w
	movlw	'o'
	call	tx_w
	movlw	'm'
	call	tx_w
	movlw	' '
	call	tx_w
	movlw	'w'
	call	tx_w
	movlw	'r'
	call	tx_w
	movlw	'i'
	call	tx_w
	movlw	't'
	call	tx_w
	movlw	'e'
	call	tx_w
	movlw	' '
	call	tx_w
	movlw	'r'
	call	tx_w
	movlw	'e'
	call	tx_w
	movlw	's'
	call	tx_w
	movlw	'u'
	call	tx_w
	movlw	'l'
	call	tx_w
	movlw	't'
	call	tx_w
	movlw	' '
; address=nnn	
	call	tx_w
	movlw	'a'
	call	tx_w
	movlw	'd'
	call	tx_w
	movlw	'd'
	call	tx_w
	movlw	'r'
	call	tx_w
	movlw	'e'
	call	tx_w
	movlw	's'
	call	tx_w
	movlw	's'
	call	tx_w
	movlw	'='
	call	tx_w
	movfw	eeprom_address
	call	print_w_ascii_dec
	movlw	' '
	call	tx_w
; original=nnn
	movlw	'o'
	call	tx_w
	movlw	'r'
	call	tx_w
	movlw	'i'
	call	tx_w
	movlw	'g'
	call	tx_w
	movlw	'i'
	call	tx_w
	movlw	'n'
	call	tx_w
	movlw	'a'
	call	tx_w
	movlw	'l'
	call	tx_w
	movlw	'='
	call	tx_w
	movfw	eeprom_data
	call	print_w_ascii_dec
	movlw	' '
; read=nnn
	call	tx_w
	movlw	'r'
	call	tx_w
	movlw	'e'
	call	tx_w
	movlw	'a'
	call	tx_w
	movlw	'd'
	call	tx_w
	movlw	'='
	call	tx_w
; read eeprom	
	call	eeprom_read

	banksel	0
	call	print_w_ascii_dec	
	call	tx_crlf
	return

eeprom_read:					; address in eeprom_address, returns data in W
	banksel 0
	movfw	eeprom_address

	banksel	EEADR
	movwf	EEADR	

	banksel EECON1
	bcf	EECON1, EEPGD			; access data memory	
	bsf	EECON1,	RD

	banksel EEDATA
	movfw	EEDATA
; return data in w
	return

save_settings:
	banksel	0
	movlw	D'0'
; clock_calibration
	movwf	eeprom_address			; 0
	movfw	timer1_reload_l
	movwf	eeprom_data
	call	eeprom_write	

	banksel	0
	incf	eeprom_address			; 1
	movfw	pwm_pulse_width
	movwf	eeprom_data
	call	eeprom_write

	banksel	0

	return

load_settings:
; this will load settings from EEPROM.
; To see if any value was actually programmed in EEPROM, first EEPROM address 255 is read.
; if EEPROM address 255 reads 255, then nothing was programmed, defaults are then written to EEPROM,
; and the value 123 is written to EEPROM address 255 as a marker that it is programmed. 
	banksel	0

	movlw	D'255'
	movwf	eeprom_address			; 255
	call	eeprom_read				; returns data in w

	banksel	0
	xorlw	D'123'					; test for programmed
	btfsc	STATUS,	Z
	goto	eeprom_is_programmed	

; program default values in eeprom
	banksel	0

	movlw	D'175'
	movwf	timer1_reload_l

	movlw	D'128'
	movwf	pwm_pulse_width

	call	save_settings

; mark writtem
	banksel	0
	movlw	D'123'
	movwf	eeprom_data

	movlw	D'255'
	movwf	eeprom_address
	
	call	eeprom_write

	banksel	0

	return	

eeprom_is_programmed:
	banksel	0

	movlw	D'0'
; clock calibration
	movwf	eeprom_address			; 0
	call	eeprom_read				; returns data in w
	banksel	0
	movwf	timer1_reload_l

	incf	eeprom_address			; 1
	call	eeprom_read	
	banksel	0
	movwf	pwm_pulse_width

	banksel	0

	return

clock_calibration_pri:
; print timer1_reload_l
	banksel	0

	movfw	timer1_reload_l
	call	print_w_ascii_dec

	call	tx_crlf

	return


neg_A
	comf    ACCaLO, F       ; negate ACCa ( -ACCa -> ACCa )
	incf    ACCaLO, F
	btfsc	STATUS, Z
	decf    ACCaHI, F
	comf    ACCaHI, F
	retlw   0


; Double Precision Subtraction ( ACCb - ACCa -> ACCb )
D_sub
;	 call    neg_A           ; At first negate ACCa; Then add
; inline
	comf    ACCaLO, F       ; negate ACCa ( -ACCa -> ACCa )
	incf    ACCaLO, F
	btfsc	STATUS, Z
	decf    ACCaHI, F
	comf    ACCaHI, F

; Double Precision  Addition ( ACCb + ACCa -> ACCb )
D_add
	movf    ACCaLO, W
	addwf   ACCbLO, F		; add lsb
	btfsc   STATUS, C		; add in carry
	incf    ACCbHI, F
	movf    ACCaHI,	W
	addwf   ACCbHI, F		; add msb
	retlw   0


;       division macro
;
divMac  MACRO
	LOCAL   NOCHK
	LOCAL   NOGO
;
	bcf     STATUS, C
	rlf     ACCdLO, F
	rlf     ACCdHI, F
	rlf     ACCcLO, F
	rlf     ACCcHI, F
	movf    ACCaHI, W
	subwf   ACCcHI, W          ;check if a>c
	btfss   STATUS, Z
	goto    NOCHK
	movf    ACCaLO, W
	subwf   ACCcLO, W        ;if msb equal then check lsb
NOCHK
	btfss   STATUS,C        ;carry set if c>a
	goto    NOGO
	movf    ACCaLO, W        ;c-a into c
	subwf   ACCcLO, F
	btfss   STATUS, C
	decf    ACCcHI, F
	movf    ACCaHI, W
	subwf   ACCcHI, F
	bsf     STATUS, C        ;shift a 1 into b (result)
NOGO
	rlf     ACCbLO, F
	rlf     ACCbHI, F
;
	ENDM


;       Double Precision Divide ( 16/16 -> 16 )
;
;         ( ACCb/ACCa -> ACCb with remainder in ACCc ) : 16 bit output
; with Quotiont in ACCb (ACCbHI,ACCbLO) and Remainder in ACCc (ACCcHI,ACCcLO).
;
;   NOTE  :  Before calling this routine, the user should make sure that
;            the Numerator(ACCb) is greater than Denominator(ACCa). If
;            the case is not true, the user should scale either Numerator
;            or Denominator or both such that Numerator is greater than
;            the Denominator.
;
;
setup
	movlw	.16					; for 16 shifts
	movwf	temp
	movf	ACCbHI, W			; move ACCb to ACCd
	movwf	ACCdHI
	movf	ACCbLO, W
	movwf	ACCdLO
	clrf	ACCbHI
	clrf	ACCbLO
	retlw	0
;
D_divF
;
     IF   SIGNED
     CALL    S_SIGN
     ENDIF
;
	call    setup
	clrf    ACCcHI
	clrf    ACCcLO
;
; use the divMac macro 16 times
;
	divMac
	divMac
	divMac
	divMac
	divMac
	divMac
	divMac
	divMac
	divMac
	divMac
	divMac
	divMac
	divMac
	divMac
	divMac
	divMac
;
    IF    SIGNED
	btfss   sign,MSB        ; check sign if negative
	retlw   0
	goto    neg_B          ; negate ACCa ( -ACCa -> ACCa )
    ELSE
	retlw   0
    ENDIF

;  Assemble this section only if Signed Arithmetic Needed
;
     IF    SIGNED
;
S_SIGN  movf    ACCaHI,W
	xorwf   ACCbHI,W
	movwf   sign
	btfss   ACCbHI,MSB        ; if MSB set go & negate ACCb
	goto    chek_A
;
	comf    ACCbLO          ; negate ACCb
	incf    ACCbLO
	btfsc   STATUS,Z
	decf    ACCbHI
	comf    ACCbHI
;
chek_A  btfss   ACCaHI,MSB        ; if MSB set go & negate ACCa
	retlw   0
	goto    neg_A
;
     ENDIF
;


;               Double Precision Multiply ( 16x16 -> 32 )
;         ( ACCb*ACCa -> ACCb,ACCc ) : 32 bit output with high word
;  in ACCb ( ACCbHI,ACCbLO ) and low word in ACCc ( ACCcHI,ACCcLO ).
;
D_mpyS                           ;results in ACCb(16 msb's) and ACCc(16 lsb's)
;
     IF   SIGNED
     CALL    S_SIGN
     ENDIF
;
	call    setup
mloop   rrf     ACCdHI, F       ;rotate d right
	rrf     ACCdLO, F
	btfsc   STATUS,C    ;need to add?
	call    D_add
	rrf     ACCbHI, F
	rrf     ACCbLO, F
	rrf     ACCcHI, F
	rrf     ACCcLO, F
	decfsz  temp, F         ;loop until all bits checked
	goto    mloop
;
    IF    SIGNED
	btfss   sign,MSB
	retlw   0
	comf    ACCcLO, F       ; negate ACCa ( -ACCa -> ACCa )
	incf    ACCcLO, F
	btfsc   STATUS,Z
	decf    ACCcHI, F
	comf    ACCcHI, F
	btfsc   STATUS,Z
neg_B   comf    ACCbLO, F       ; negate ACCb
	incf    ACCbLO, F
	btfsc   STATUS,Z
	decf    ACCbHI, F
	comf    ACCbHI, F
	retlw   0
    ELSE
	retlw   0
    ENDIF


print_millivolts:
	banksel	0
	movfw	millivolt_h
	movwf	bin 

	movfw	millivolt_l
	movwf	bin+1

	call	print_16_ascii_dec

	banksel	0

	movlw	'm'
	call	tx_w
	movlw	'V'
	call	tx_w

	return	


print_adc:
	banksel	0
	movfw	ad_h
	movwf	bin 

	movfw	ad_l
	movwf	bin+1

	call	print_16_ascii_dec

	banksel	0

	return	


ad_to_millivolts:					; converts AD reading in ad_h and ad_l to millivolts in volts_h and volts_l.
; math:
; Vref = 5070 mV 	
; voltage = (AD / 1023) x Vref (range 0 to Vref)											use signed 16 bit
; voltage = AD * (5.07 /  1023) = AD * 4.956 mV is aprox  AD * 5 mV 	

; AD * 49 / 10

; Vref in milli volts
	movlw	VREF_H				; D'19'
	movwf	ACCaHI
	movlw	VREF_L				; D'206'				; Vref in mV,  5070 
	movwf	ACCaLO

	movfw	ad_h
	movwf	ACCbHI
	movfw	ad_l
	movwf	ACCbLO

	call	D_mpyS         		; Double Precision Multiply ( 16 x 16 -> 32 ( ACCb * ACCa -> ACCb, ACCc ) : 32 bit output with high word, results in ACCb(16 msb's) and ACCc(16 lsb's)

; AD * 5000 in ACCb - ACCc range 0 to 5115000 for 5V Vref (32 bits)
; now divide by 1023 to get mV

; if we divide by 1024 we can use simple shifts and throw low bits away.	
; sr 1 = :2, sr 2 = :4, sr 3 = :8, sr 4 = :16, sr 5 = :32, sr 6 = :64 , sr 7 = :128 , sr 8 = :256, sr 9 = :512, sh 10 = :1024
; 1024 makes 1 byte plus 2 bits.
;  result ACCbHI, ACCbLO, ACCcHI, ACCcLO
; after 1 byte shift:
; result ACCbHI, ACCbLO, ACCcHI
; we know never more then about 5000, this fits in 2 bytes, throw away ACCbHI.
; now 16 bits divide by 4.
	movfw	ACCbLO
	movwf	ACCbHI				; overwrites ACCbHI
	movfw	ACCcHI
	movwf	ACCbLO

; divid by 4
	movlw	D'0'
	movwf	ACCaHI
	movlw	D'4'
	movwf	ACCaLO		

	call	D_divF				; Double Precision Divide ( 16/16 -> 16 )  ( ACCb/ACCa -> ACCb with remainder in ACCc ) : 16 bit output with Quotiont in ACCb (ACCbHI,ACCbLO) and Remainder in ACCc (ACCcHI,ACCcLO).

; milli volts now in ACCb
	banksel	0

	movfw	ACCbHI
	movwf	bin 
	movwf	millivolt_h

	movfw	ACCbLO
	movwf	bin+1
	movwf	millivolt_l

	return


;	org D'2048'
print_time:
	movfw	hours
	call	print_w_ascii_dec
	
	movlw	':'
	call	tx_w

	movfw	minutes
	call	print_w_ascii_dec

	return


pwm_init:
	banksel	TMR2
	clrf	TMR2

	banksel	PR2
;	movlw	d'249'			; PWM Period
	movlw	0x65			; 8MHz 1x prescaler 8 bits resolution
	movwf	PR2

; init 50 % duty cycle
	banksel	0
;	movlw	D'128'			; Load Duty cycle into CCPR1L
	movlw	D'50'			; seems 0 (0V) to - 102 (+5V).
	movwf	pwm_pulse_width
	banksel	CCPR1L
	movwf	CCPR1L

; CCP1CON:	P1M1 P1M0  DC1B1 DC1B0  CCP1M3 CCP1M2 CCP1M1 CCP1M0
	movlw	b'00001100'
	banksel	CCP1CON
	movwf	CCP1CON			; Setup PWM mode

; set prescaler 1x
	banksel	T2CON
	clrf	T2CON
	bcf	T2CON,	T2CKPS0		; 00 = /1, 01 = /4, 1x = /16
	bcf	T2CON,	T2CKPS1

; timer 2 on
	bsf	T2CON,	TMR2ON

	banksel	0

	return


	end

