; 	****    ****
; Interrupt service routines

;-------------------------------------------------------------------------
tint
; With 14.7456 MHz crystal -> .2713 usec instruction cycles
; Timer prescalar is 1 so timer interrupts occur each 69.4444 usecs
; Thus there are three tints for each bit at 4800 bps
; Timermid ticks each 69.444 usec, rolls over each 17.777 msec
; Timerhi ticks each 17.777 msec, rolls over each 4.551 seconds
; Timerveryhi ticks each 4.551 seconds, rolls over each 19.411 minutes

	bcf	INTCON,T0IF
; Save w, status, FSR
	movwf   w_save     ; save w in w_save
	movf    STATUS,w    ; move the status into w
	movwf   s_save     ; save status in s_save
	movf	FSR,w
	movwf	f_save
; t=6
;----------------------------------------------------------------------------

; Grab the state of the serial input line
; It's best to grab it now because some of the following code is
; of duration which varies unpredictably
	movf	PORTA,w
	movwf	inputlatch

; Calculate the next cycle number (0 thru' 2)
; Cycle number is used for the 4.8 kb/s serial port
; There are three timer interrupts per bit at 4.8 kb/s
	incf	cyclenumber,w
	btfsc	cyclenumber,1	; skip unless this cycle is cycle2
	clrw
	movwf	nextcyclenumber
; t=12
;----------------------------------------------------------------------------
 #ifdef PWMreq
; There are 14400 timer interrupts per second.
; The sample rate for the A/D conversion was 7200 samples/second
; Thus the D/A must interpolate each second timer interrupt

PWM2x
; Select different paths for alternate interrupts
	btfss	timermid,0
	goto	PWM1

PWM0
; Fetch new byte from the buffer
; Set PWM to mean of last and new
; Save PWM new as PWMlastbyte

; Get the next D/A byte from the buffer
; bufferreadoffset is the offset of the last byte read by tint.
; bufferwriteoffset is the offset of the last byte written by i2c_gets.
; If bufferwriteoffset = bufferreadoffset-1, i2c_gets must wait.
; If bufferwriteoffset = bufferreadoffset then there's no new D/A value available
	movf	bufferwriteoffset,w
	xorwf	bufferreadoffset,w
	btfsc	_Z		; skip if there's a new value available
	goto	PWMend

; There's new D/A data, increment the buffer read offset
	incf	bufferreadoffset,f
	movlw	B'00111111'
	andwf	bufferreadoffset,f

; Point FSR at the new D/A value
	movlw	pwmbuffer
	addwf	bufferreadoffset,w
	movwf	FSR

; Average new and last PWM bytes
	movf	INDF,w
	addwf	PWMlastbyte,f
	rrf	PWMlastbyte,w
	movwf	PWMbyte
	movf	INDF,w
	movwf	PWMlastbyte
;	btfsc	PORTB,4	;tttttttttttttttttttttttttt simple option
	goto	PWMset
	
PWM1
; Set PWM to PWMlastbyte
	movf	PWMlastbyte,w
	movwf	PWMbyte
	goto	PWMset
;-----

PWMset
; Generate the new value for CCP1CON
	movlw	B'00001100'	; PWM mode
	btfsc	PWMbyte,1
	addlw	B'00100000'
	btfsc	PWMbyte,0
	addlw	B'00010000'
	movwf	CCP1CON

; Generate the new value for CCPR1L
	bcf	_C
	rrf	PWMbyte,f
	bcf	_C
	rrf	PWMbyte,w
	movwf	CCPR1L
; t=30
PWMend
 #endif
;----------------------------------------------------------------------------
 #ifdef	serialtransmitter
serialtx
; 4800 bps serial transmitter
; Useage:
;	- wait until serialtxbitcount is 0
;	- load serialtxbyte with the byte to send
;	- load serialtxbitcount with H'0B'

	movf	cyclenumber,f	; just set the flags
	btfss	_Z		; service serial tx each third tint
	goto	serialtxend

	movf	serialtxbitcount,w	
	btfsc	_Z
	goto	serialtxend

serialtx_setout
; Switch the serial output line now based on the last pass
; This avoids jitter from varying duration routes through subsequent code
	btfss	serialtxbit
	bcf	serialout
	btfsc	serialtxbit
	bsf	serialout

; serialtxbitcount is still in w
; 11 		=> 	send start bit
; 3 thru 10 	=> 	send data bit
; 2		=> 	send stop bit
; 1		=>	do nothing more
	xorlw	D'11'
	btfss	_Z	; skip if a start bit is required
	goto	serialtxcheckforstop

	bsf	serialtxbit
	goto	serialtxclup

serialtxcheckforstop
	xorlw	D'11' ^ D'2'
	btfss	_Z	; skip if a stop bit is required
	goto	serialtxcheckforidle

	bcf	serialtxbit
	goto	serialtxclup

serialtxcheckforidle
	xorlw	D'2' ^ D'1'
	btfsc	_Z	; skip unless idle
	goto	serialtxclup

serialtxdatabit
	rrf	serialtxbyte,f
	bcf	serialtxbit
	btfss	_C		; note inversion to give data bits correct sense
	bsf	serialtxbit

serialtxclup
	decf	serialtxbitcount,f

serialtxend
 #endif

;----------------------------------------------------------------------------
; Update timers
	incfsz	timermid,f
	goto	endtimerupdate
	incf	timerhi,f
	btfsc	_Z
	incf	timerveryhi,f
; 19 cycles (+ call delay of 3 cycles max)
endtimerupdate
;---------------------------------------------------------------------------

; Serial receivers
; There are three timer interrupts per 4800 bps bit period
; If not in the process of receiving a byte, the serial receivers
; look for a start bit each interrupt. If one is found then the start bit
; is rolled into the byte buffer next interrupt, and subsequent data 
; bits are rolled in each three interrupts thereafter. When the start
; bit has rolled out of the buffer, the receiver waits another three 
; interrupts for the arrival of the stop bit, then rearms to await a 
; new start bit.
;--------------------------------------------------------------------------
do_in0
; [restart cycle count]
; NMEA in0
; Test whether waiting for a start bit
; in0gettingbits is clear if waiting for a startbit
	btfsc	in0gettingbits
	goto	in0receiving

; NMEA in0 is waiting for a start bit
	btfss	inputlatch,in0line	; skip if start bit found
	goto	in0end		; no start bit - go check the next NMEA input

; NMEA in0 is waiting for a start bit and the first 1/3 of one has been found
	movlw	H'FF'
	movwf	in0byte		; so that 1 bits roll out until the 0 start bit
	movf	nextcyclenumber,w
	movwf	in0cyclenumber	; set cycle number to read in the bits
	bsf	in0gettingbits
	goto	in0end		; 11

in0receiving
; 3
; in0 is receiving either the start bit, a data bit or the stop bit
	movf	cyclenumber,w
	xorwf	in0cyclenumber,w
	btfss	_Z		; skip if its receiving this cycle
	goto	in0end		; do nothing if the wrong cycle

; 7
; in0 is receiving bits this cycle
; See if its just the stop bit, and if so cleanup and do next serial input
	btfss	in0gettingstopbit
	goto	in0loadbit

	bcf	in0gettingbits
	bcf	in0gettingstopbit
	goto	in0end

;10
in0loadbit
; Load the new bit into the byte buffer
; Note that this load flips the bit, so a start bit becomes a 0 bit 
; in in0byte, the data bits are correct sense, and a stop bit is a 1 bit.
	bcf	_C
	btfss	inputlatch,in0line
	bsf	_C
	rrf	in0byte,f	; rotate the new bit in

; Test whether the start bit has now rolled out, as that means byte done	
	btfsc	_C		; skip if the start bit has rolled out
	goto	in0end
;--------------------------------------------------------------------------
in0bytecomplete
;16
; All the data bits of an incoming byte have been received
; There's a new byte in in0byte, but the stop bit has yet to be received
	bsf	in0gettingstopbit
;-----------------------
;ttttttttttttttttttttttttttttttttttttttttttttttt
 #ifndef loading
	bsf	in0byteready
	goto	in0end
 #endif
;/tttttttttttttttttttttttttttttttttttttttttttttt
;--------------------------------------------------------------------------
 #ifdef	loading

; bufferwriteoffset is the offset of next location to write to in the buffer.
; Calculate the write address
	movlw	pwmbuffer
	addwf	bufferwriteoffset,w
	movwf	FSR

; Store the byte received via the serial port in the buffer
	movf	in0byte,w
	movwf	INDF

; Increment the address pointer
	incf	bufferwriteoffset,f
	movlw	B'00111111'
	andwf	bufferwriteoffset,f

; Test whether 64 bytes written
	btfss	_Z		; skip if 64 bytes written
	goto	in0end

; 64 bytes have been written
	bsf	pageready

	goto	in0end
 #endif
;--------------------------------------------------------------------------

in0checkforLF
; If were expecting a LF, check that it is one and reset sentence if not
	btfss	in0expectLF
	goto	in0checkforCR	;20

	movlw	H'0A'
	xorwf	in0byte,w
	btfss	_Z
	goto	in0resetsentence

; The sentence is probably OK
	bsf	in0databuffervalid
; Set timer for autotimeout of data
	movf	timerveryhi,w
	movwf	timelastheading
	goto	in0resetsentence

;-----------------------

;20
in0checkforCR
	btfss	in0expectCR
	goto	in0checkforCSlo	;23

	bcf	in0expectCR
	bsf	in0expectLF

	movlw	H'0D'
	xorwf	in0byte,w
	btfss	_Z
	goto	in0resetsentence

	bsf	in0expectLF
	goto	in0end

;-----------------------

;23
in0checkforCSlo
	btfss	in0expectCSlo
	goto	in0checkforCShi		;26

	bcf	in0expectCSlo
	bsf	in0expectCR
; It's the low byte of the checksum hex pair in the sentence
; The calculated checksum has already been xor'd with the binary
; equivalent of the high checksum hex char in the sentence.
; Now xor the binary equivalent of this low checksum hex char
; with the calculated checksum
	movf	in0byte,w
	addlw	D'208'		; initially assume the hexchar is 0-9
	btfsc	in0byte,6	; skip if the hexchar really is 0-9
	addlw	D'249'		; it was A-F so correct the binary value
	xorwf	in0checksum,w
	btfss	_Z		; skip if calc'd checksum = hex checksum
	goto	in0resetsentence

	goto	in0end

;-----------------------

;26
in0checkforCShi
	btfss	in0expectCShi
	goto	in0checkforasterisk	;29

	bcf	in0expectCShi

; It's the high byte of the checksum hex pair in the sentence
; xor the binary equivalent of this high checksum hex char
; with the calculated checksum
	movf	in0byte,w
	addlw	D'208'		; initially assume the hexchar is 0-9
	btfsc	in0byte,6	; skip if the hexchar really is 0-9
	addlw	D'249'		; it was A-F so correct the binary value
	movwf	tempi
	swapf	tempi,w
	xorwf	in0checksum,f

	bsf	in0expectCSlo
	goto	in0end

;-------------------------

;29
in0checkforasterisk
	movf	in0byte,w
	xorlw	'*'
	btfss	_Z
	goto	in0checkheaderOK	;34

	btfss	in0headerOK
	goto	in0resetsentence
	bsf	in0expectCShi
	goto	in0end

;------------------------

;34
in0checkheaderOK
; If the header is OK and the new byte hasn't been trapped by anything
; preceding, then this byte is data or is a field separator
	btfss	in0headerOK
	goto	in0checkheader	;37

; First update the checksum
	movf	in0byte,w
	xorwf	in0checksum,f

; This byte is data or a separator
; Only the first field is required from this sentence.
; It may be up to four bytes long (xxx.) where each x is generally a numeral

in0bufferdata
; Save only the first four bytes
	movf	in0databytecount,w
	andlw	B'11111100'
	btfss	_Z		; get 4 bytes max
	goto	in0end

; Clear in0databuffervalid so the talker routines know its being changed
	bcf	in0databuffervalid

; Set FSR to the address in the buffer to write the byte to
	movf	in0databytecount,w
	addlw	in0databuffer
	movwf	FSR

	movf	in0byte,w
	movwf	INDF

	incf	in0databytecount,f

	goto	in0end		;51

;------------------------

;37
in0checkheader

; This byte should be within the header
; Check to avoid the chance (error) of a call to outside the table
	movf	in0bytecount,w
	sublw	(in0headerlast-in0headerfirst)	; length of in0header table
			; leaves length of table - inbytecount
			; leaves carry set if byte within header table
	btfss	_C	; skip if this char should be in the header table
	goto	in0resetsentence

; _Z will be set if it's the last byte in the header
	btfsc	_Z
	bsf	in0headerOK

; Check that the byte received matches the header byte in the table
	movf	in0bytecount,w
	call	in0header	;46
;51
	xorwf	in0byte,w
	btfss	_Z
	goto	in0resetsentence

	movf	in0byte,w
	xorwf	in0checksum,f

	incf	in0bytecount,f
	goto	in0end		;59

;-------------------------------------------
in0resetsentence
	clrf	in0bytecount
	clrf	in0flags
	clrf	in0databytecount
; As the bytes are received, all the header, data and data separators are
; xor'd with the checksum. 
; The initial '$' should not be included in the checksum, so initialise
; the checksum to'$' to compensate.
	movlw	'$'
	movwf	in0checksum
;59 max
in0end
;--------------------------------------------------------------------------


;----------------------------------------------------------------------------
tintclup
; Clean up
	movf	nextcyclenumber,w
	movwf	cyclenumber

; Restore w, status and FSR
	movf	f_save,w
	movwf	FSR
	movf    s_save,w   ; get the saved status
	movwf   STATUS      ; restore the status
	swapf   w_save,f   ; swap nibbles in the saved w
	swapf   w_save,w   ; swap nibbles in the saved w, leaving result in w
	retfie	



