	include <p16c84.inc>
; This program is a serial programmer for a 9366 serial EEPROM with a microwire
; interface. This program accepts Motorola SRecords via the serial interface
; and programs the EEPROM with the downloaded data. This program acceepts 3
; types of SRecords:
;
; S1 - program data
; S9 - release target reset and EEPROM interface so that target can execute.
; SR - Special: read and display the contents of the EEPROM

; This program was designed as a serial downloader for compiled tokens for my
; NPCI interpreter. For more information on NPCI, consult my PIC page at:
; 
; http://www.finitesite.com/d3jsys
;
; LICENSE: This software can be used, modified, and redistributed for personal
; non-commercial use only. Please contact the author at byron@cc.gatech.edu
; for any commercial usage of this software. 

d1	equ	.12
eecomm	equ	.12		; 
d2	equ	.13
eebits	equ	.13
count	equ	.14
type	equ	.15
copyhi	equ	.15
addrhi	equ	.16
addrlo	equ	.17
byte	equ	.18
copylo	equ	.18
eecount	equ	.19
count2	equ	.20
buf	equ	.28

; target board defines. Note a eedata2 is a combined data in and data out line
; for the EEPROM. The data out line must be tied to the data in line via a
; resistor. 1K should be fine.

cs	equ	.0
clk	equ	.1
eedata2	equ	.2
reset	equ	.3

start    
	movlw   .255
	movwf	PORTA   
	clrf	PORTB
	clrw
	tris    PORTB           ; Turn port B to all outputs
	movlw   0x11
	tris    PORTA           ; Turn port A to all except for RA4 and RA0

main	call	serin	
	xorlw	'S'
	btfss	STATUS,Z
	goto	main		; Loop forever

	call	serin
	movwf	type
	xorlw	'0'
	btfss	STATUS,Z
	goto	test1

	clrf	PORTB		; Reset everything.
	clrw	
	tris	PORTB		; Make port B all outputs again.

ok	movlw	'Y'
	call	serout
	goto	main

test1	movf	type,W
	xorlw	'1'
	btfss	STATUS,Z
	goto	test9

	call	getbyte
	movwf	count
	call	getbyte
	movwf	addrhi
	call	getbyte
	movwf	addrlo
	movlw	buf
	movwf	FSR
	movlw	.3
	subwf	count,W
	movwf	count2
	movwf	count
	incf	count,F		; Get the checksum too.

fillit	call	getbyte
	movwf	INDF
	incf	FSR,F
	decfsz	count,F
	goto	fillit
	
; At this point the buffer is full of bytes to write to the EEPROM and
; count2 has the count of the bytes. 
;
; Remember to do EWEN and EWDS before and after writing.

	call	ewen		; enable the EEPROM
	movlw	buf		; start of buffer
	movwf	FSR
wrloop	movf	INDF,W		; Get the next byte to write
	call	eewrite		; Write it.
	incfsz	addrlo,F	; Next address
	goto	wrcont		
	incf	addrhi,F	
wrcont	incf	FSR,F
	decfsz	count2,F	; Write all the bytes
	goto	wrloop

	call	ewds		; disable writing on the EEPROM
	clrf	PORTB		; Disable the EEPROM
	goto	ok

ewen
	movf	addrlo,w	; Need bit 7. so copy this
	movwf	copylo		; 
	movf	addrhi,w	; Need this too
	movwf	copyhi
	movlw	0xff
	movwf	addrlo
	movwf	addrhi
	movlw	0x90
	call	eeaddr		; Run the command and return
	movf	copylo,w	; Restore addresses
	movwf	addrlo		; 
	movf	copyhi,w	; 
	movwf	addrhi
	return	

	
ewds
	movf	addrlo,w	; Need bit 7. so copy this
	movwf	copylo		; 
	movf	addrhi,w	; Need this too
	movwf	copyhi
	clrf	addrlo
	clrf	addrhi
	movlw	0x80
	call	eeaddr		; Run the command and return
	movf	copylo,w	; restore address
	movwf	addrlo		; 
	movf	copyhi,w	; 
	movwf	addrhi
	return

test9	movf	type,W
	xorlw	'9'
	btfss	STATUS,Z
	goto	testR

; Normally would release reset so that program can start running.
; Be sure to make the CS, CLK, and DI bits (0-2) inputs before executing.

	movlw	'Y'		; Send a Y back...
	call	serout	

	movlw	.15		; 
	tris	PORTB		; Make CS, CLK, DI/DO, and reset inputs
				; This releases the reset on the target.

debug	btfss	PORTA,0		; See if a start bit from host
	goto	main		; Exit if start bit found
	btfss	PORTB,clk	; Check for clock HI.
	goto	debug		; Loop of no rising edge
	btfsc	PORTB,cs	; Debugging only when CS is not selected
	goto	debug		; Loop until then

; At this point this is debugging info for us. Get 8 bits and ship up
; to the host.

dbgin	movlw	.8		; Number of bits to get
	movwf	d2

dbloop	btfss	PORTB,clk	; Wait for the clock to be HI
	goto 	dbloop

	btfsc	PORTB,eedata2	; Get the bit. xfer to carry
	goto	dbset
	bcf	STATUS,C
	goto	dbcont

dbset	bsf	STATUS,C
dbcont	rrf	d1,F		; Shift bit into destination

dbloop2	btfsc	PORTB,clk	; Wait for the clock to go low
	goto 	dbloop2

	decfsz	d2,F
	goto 	dbloop

	movf	d1,W		; Get the data
	call	serout		; and ship it off to the host
	goto	debug

testR	movf	type,W		; Read the contents of the EEPROM
	xorlw	'R'
	btfss	STATUS,Z
	goto	main

	call	getbyte		; Get the address to start reading
	movwf	addrhi
	call	getbyte
	andlw	0xf0		; Make sure on 16 byte boundry
	movwf	addrlo

	call	crlf		; Print a CR/FC combo.
	clrf	eecount		; Clear the count

rloop	movf	eecount,W		
	andlw	.15		; Test only low 4 bits
	xorlw	.0		; See if 0
	btfsc	STATUS,Z	; Skip address printing if not on 16 byte
	call	prtaddr		; Print the address	

	call	eeread		; read the EEPROM
	call	putbyte
	movlw	' '	
	call	serout

	incfsz	addrlo,F
	goto	rcont1
	incf	addrhi,F

rcont1	incfsz	eecount,F
	goto 	rloop

	call	crlf
	call	serin
	xorlw	'q'	
	btfsc	STATUS,Z
	goto 	main
	goto	rloop

ascbyte	andlw	.15
	addwf	PCL,F
	retlw	'0'
	retlw	'1'
	retlw	'2'
	retlw	'3'
	retlw	'4'
	retlw	'5'
	retlw	'6'
	retlw	'7'
	retlw	'8'
	retlw	'9'
	retlw	'A'
	retlw	'B'
	retlw	'C'
	retlw	'D'
	retlw	'E'
	retlw	'F'

; The eeaddr call expects the command (plus A8) in the top nybble of W
; The lower 8 address are retrieved from addrlo

eeread	movlw	0xc0		; Read commmand (SB 1 0)
	btfsc	addrhi,0	; Check A8
	iorlw	0x10		; Light A8 in the eecommand
	call	eeaddr		; Send out the address
	movlw	TRISB		; Get portb data direction
	movwf	FSR
	bsf	INDF,eedata2	; Make data bit incoming
	movlw	.8		; Number of bits to read
	movwf	eebits


eerloop	bsf	PORTB,clk	; Clock in the next bit
	nop
	btfsc	PORTB,eedata2	; Get the bit. xfer to carry
	goto	eerset
	bcf	STATUS,C
	goto	eercont

eerset	bsf	STATUS,C
eercont	bcf	PORTB,clk	; Bring the clock low.

	rlf	byte,F		; Shift bit into destination
	decfsz	eebits,F
	goto 	eerloop

	bcf	INDF,eedata2	; Make data outgoing again
	clrf	PORTB		; Make all bits low.
	movf	byte,W		; Get the result
	return			; And return it.

eewrite	movwf	byte		; store the byte to write.
	movlw	0xa0		; Read commmand (SB 0 1)
	btfsc	addrhi,0	; Check A8
	iorlw	0x10		; Light A8 in the eecommand
	call	eeaddr		; Send out the address
	movlw	.8		; Number of bits to write. Last bit is special
	movwf	eebits


eewloop	rlf	byte,F		; Put the next bit in the carry
	btfsc	STATUS,C	; Get the bit. xfer to carry
	goto	eewset
	bcf	PORTB,eedata2
	goto	eewcont

eewset	bsf	PORTB,eedata2
	nop
eewcont	bsf	PORTB,clk	; Clock in the next bit
	movlw	.1		; See if last bit
	xorwf	eebits,W
	btfss	STATUS,Z	; See if last bit was clocked in
	goto	eewcon2
	bcf	PORTB,cs	; Drop CS before dropping clock on last bit
	nop			; delay between successive port writes
eewcon2	bcf	PORTB,clk	; Bring the clock low.

	decfsz	eebits,F
	goto 	eewloop

	movlw	.4		; Get portb data direction
	tris	PORTB		; Make data an input

	bsf	PORTB,cs	; Turn on CS again to check status.
	nop
eewbusy	btfss	PORTB,eedata2	; Get status
	goto	eewbusy		; loop until not busy

	clrw
	tris	PORTB
	nop
	clrf	PORTB		; Make all bits low.
	return			; And return 

eeaddr	bcf	PORTB,cs	; Turn the EEPROM on for one blip.
	nop
	nop
	bsf	PORTB,cs	; Turn the EEPROM on
	movwf	eecomm		; Save the command
	movlw	.4		; Number of bits to shift
	movwf	eebits		
	movlw	.2		; number of times to execute eealoop
	movwf	count

eealoop	rlf	eecomm,F	; Put the next bit in Carry

	btfsc	STATUS,C	; check the carry bit
	goto	eeaset1
	bcf	PORTB,eedata2
	goto	eeacon1

eeaset1	bsf	PORTB,eedata2
	nop
eeacon1	bsf	PORTB,clk	; Clock the bit into the EEPROM
	nop
	bcf	PORTB,clk	; Bring the clock low.

	decfsz	eebits,F
	goto	eealoop		; Do all 4 command bits

	decfsz	count,F		; Are we done?
	goto	eealow		; Nope. got to do low addresses
	return			; Yes. Yes we are done. CS remains raised.
	

eealow	movf	addrlo,W	; shift out the low part of the address
	movwf	eecomm		; Put in the command register
	movlw	.8		; Number of bits to shift
	movwf	eebits		
	goto	eealoop		; Shift out low address bits too.

crlf	movlw	.13		; CR
	call	serout
	movlw	.10		; LF
	goto	serout		; Print and return.

prtaddr	call	crlf
	movf	addrhi,W
	call	putbyte
	movf	addrlo,W
	call	putbyte
	movlw	':'
	call	serout
	movlw	' '
	goto	serout		; Print and return

serin	movlw	.8		; Number of bits to get
	movwf	d2

sbit	btfsc	PORTA,0		; Wait for a start bit
	goto	sbit
	call	halfdel		; delay half a bit
	btfsc	PORTA,0		; Make sure it wasn't a glitch
	goto	sbit

siloop	call	delay		; Wait for center of next cell
	btfsc	PORTA,0		; Get the bit. xfer to carry
	goto	siset
	bcf	STATUS,C
	goto	sicont

siset	bsf	STATUS,C
sicont	rrf	d1,F		; Shift bit into destination
	decfsz	d2,F
	goto 	siloop

	call	halfdel		; delay into stop bit. 
				; Have whole stop bit to do work.
	movf	d1,W		; Get return value
	return

serout	movwf	d1		; Save the character.
	movlw	.8		; 8 bits to transmit
	movwf	d2
	bcf	PORTA,3		; Send the start bit
	call	delay
soloop	rrf	d1,F		; Get the next bit into the carry
	btfss	STATUS,C	; check the carry bit
	goto	soclr
	bsf	PORTA,3		; Set bit to 1
	goto	socont
soclr	bcf	PORTA,3
socont	call 	delay
	decfsz	d2,1
	goto	soloop
	
	bsf	PORTA,3		; Stop bit
	call	delay
	return

delay	clrf	TMR0		; Start at timer 0
dloop	movlw	.192		; Comparing to 192
	andwf	TMR0,W		; Mask off lower bits
	xorlw	.192		; See if equal to 192	
	btfss	STATUS,Z	
	goto	dloop		; loop 'til then
	return			; That's all.

halfdel	clrf	TMR0		; Start at timer 0
hdloop	movlw	.97		; Comparing to 96
	andwf	TMR0,W		; Mask off lower bits
	xorlw	.97		; See if equal to 96	
	btfss	STATUS,Z	
	goto	hdloop		; loop 'til then
	return			; That's all.

putbyte	movwf	byte
	swapf	byte,W		; Get the high nybble
	call	ascbyte		; Get ascii value
	call	serout
	movf	byte,W		; Get low nybble
	call	ascbyte
	goto	serout

getbyte	call	serin		; Get top nybble
	call	convert		; Convert it to hex
	movwf	byte		; Save it.
	call	serin		; get the second nybble
	call	convert		; And convert it too
	swapf	byte,F		; Move the top nybble to proper position
	iorwf	byte,W		; Merge nybbles
	return			; All done.

convert	movwf	d1		; Save temp. I know it already there but I
				; Want this routine to function standalone.
	movlw	'0'		; value to subtract
	subwf	d1,F		; Remove offset
	movlw	.10		; See if larger than 9
	subwf	d1,W
	btfss	STATUS,C	; Positive. Must be A-F
	goto	condone
	movlw	.7		; Further offset removed
	subwf	d1,F
condone	movf	d1,W		; Put result in W
	return			; And begone	

	END
