;		93C57 EEPROM WRITER
; Read and Write data from/to 93C57 serial EEPROM
; Modified from 59C11 programmer Ver.2.2.
; Supports 16 bit mode ONLY. While 8 bit routines are there
; they are ignored.
; Adjust rate_9600, ee_delay and delay_1msec if Xtal freq.
; is other than 3.58MHz.
;
; Copyright 2001 Tickets, All rights reserved
;
; NOTE: This code is written in Paralax SPASM format.
;
	device	pic16f84,hs_osc,wdt_off,protect_off,pwrt_on
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; DEFINITIONS
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Version
version_major	=	'0'		; Major version
version_minor	=	'2'		; Minor version

; I/O Ports

; RS232C Ports

serial_rxd	=	PortA.0		; RS232C RXD (Output)
serial_txd	=	PortB.0		;        TXD (Input)

rate_9600	=	28		; 9600bps
rate_half	=	rate_9600/2	; Half bit

; EEPROM 93C57

ee_do		=	PortB.1		; DO (Input)
ee_di		=	PortA.1		; DI (Output)
ee_clk		=	PortA.2		; CLK (Output)
ee_cs		=	PortA.3		; CS (Output)
ee_rdy		=	PortB.2		; RDY/BSY (Input) H=Ready
ee_mode		=	PortB.3		; 93C57 mode (Input) L=8bit
					;                    H=16bit

					; Command construction(16bit)
					;	A: Address(A6-A0)
					;	X: Don't care
					; READ	10AAAAAAA
					; WRITE	01AAAAAAA
					; EWEN	0011XXXXX
					; EWDS	0000XXXXX
ee_rdcmd	=	11000000b	; Left 3 bits including start bit
ee_wrcmd	=	10100000b	; starts from left
ee_ewen		=	10000000b	; EEPROM write enable
dummy_ewen	=	11111111b	; First 2bits are part of EWEN command
ee_ewds		=	10000000b	; EEPROM write disable
dummy_ewds	=	00000000b	; First 2bits are part of EWDS command
ee_cmdcntr	=	3		; 3bits for command and start bit
					; EWEN/EWDS requires address field
					; as a part of command (FFh/00h)
ee_adrcntr8	=	8		; 8bits for address (byte config.)
ee_adrcntr16	=	7		; 7bits for address (double byte)
ee_datacntr	=	8		; 8bits for data
ee_maxadr8	=	00h		; Address limit for 8bit (100h)
ee_maxadr16	=	80h		; Address limit for 16bit

; ASCII Code

code_cr		=	0Dh		; CR code
code_lf		=	0Ah		; LF
code_esc	=	1Bh		; ESC
code_space	=	20h		; Space

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	org	0ch			; Top of available RAM

		; RS-232C

mode		ds	1		; EEPROM mode 0=8bit NZ=16bit
bit_cntr	ds	1		; Bit counter
xmt_byte	ds	1		; Transmit data
rcv_byte	ds	1		; Received data
delay_cntr	ds	1		; RS232C delay
delay_cnt2	ds	1		;
delay_cnt3	ds	1		;
msg_cntr	ds	1		; Message counter
conv_byte	ds	1		; Byte to be converted
hex_data	ds	1		; HEX input buffer
first_byte	ds	1		; 0=first byte
temp		ds	1		; Temporary buffer
str_pointer	ds	1		; Pointer for string data

		; 93C57

ee_address	ds	1		; Address
ee_adrcntr	ds	1		; 
ee_maxadr	ds	1		; Address limit 40h or 80h
ee_wdatah	ds	1		; Write data High byte
ee_wdatal	ds	1		; Write data Low byte
ee_rdatah	ds	1		; Read data High byte
ee_rdatal	ds	1		; Read data Low byte
ee_cntr		ds	1		; EEPROM data bit counter
ee_buff		ds	1		; Buffer

quit_flag	ds	1		; Quit Process if non zero
error_flag	ds	1		; 0 if no error
skip_flag	ds	1		; Skip process if non zero
ignore_flag	ds	1		; Ignore input if non zero

	id	'9357'			; Product ID
	
	org	0
start
init
	mov	intcon,#00000000b	;Interrupt control
					; Disables all Interrupt

	mov	option,#00000000b
					;Option register
					; 7 Port-B pull up (L:ON)
					; 6 Interrupt trigger (L:H to L)
					; 5 RTCC source (L:Clock)
					; 4 RTCC trigger (L:H to L)
					; 3 Prescaler (L:RTCC)
					;2-0 Prescaler ratio (0=1:1)
	mov	!ra,#11110000b
					;A-Port
					; RA0 RXD (Output)
					; RA1 93C57 DI
					; RA2 93C57 CLK (L to H)
					; RA3 93C57 CS (Active H)
					
	mov	!rb,#11111111b
					;B-Port
					; RB0 TXD (Input)
					; RB1 93C57 DO
					; RB2 93C57 RDY (Active H)
					; RB3 93C57 ORG (L=8bit)
					
	setb	serial_rxd		; Init serial output

	clrb	ee_cs			; Disable 93C57
	call	delay_1msec
	
	call	disable_command		; Dummy command for init.
	call	send_address		; dummy address
	clrb	ee_cs
	
;	mov	ee_adrcntr,#ee_adrcntr8	; Set for 8bit
;	mov	ee_maxadr,#ee_maxadr8

	clr	mode			; Check mode input
	jnb	ee_mode,:eight		; Mode input is Low (8bit mode)
	inc	mode			; 16bit mode

	mov	ee_adrcntr,#ee_adrcntr16	; Set for 16bit
	mov	ee_maxadr,#ee_maxadr16

	goto	opening

:eight					; ERROR!!!�@Must be 16 bit mode
	call	disp_2crlf

	mov	w,#disp_error		; Display Error message
	call	disp_string
	mov	w,#disp_8bit
	call	disp_string

	call	disp_2crlf

	mov	w,#disp_modeerr
	call	disp_string

	call	disp_crlf
:loop					; It hangs in Mode Error
	goto	:loop

opening
	call	disp_crlf
	mov	w,#disp_open		; Greeting
	call	disp_string
	mov	w,#disp_version		; Display version number
	call	disp_string
	call	disp_crlf

	cje	mode,#0,:eight		; Select mode
	mov	w,#disp_16bit		; 16bit mode
	goto	:finish
:eight
	mov	w,#disp_8bit		; 8bit
:finish	call	disp_string
	call	disp_crlf

;---------------------------------------
; MAIN
;---------------------------------------
main
	call	cmd_decode		; Get command

	goto	main

;----------------------------------------
; MESSAGE STRINGS ( 0 terminates string )
;----------------------------------------
disp_str
	jmp	pc+w
str_base				; Base address
					; Strings should not be
					; Placed at address 0100h
					; or greater
str_open
	retw	'93C57 EEPROM Programmer  Ver.',0
str_version
	retw	version_major,'.',version_minor,0
str_8bit
	retw	'  [ 8bit mode ]',0
str_16bit
	retw	'  [ 16bit mode ]',0
str_error
	retw	'Error',0
str_command
	retw	'COMMAND (D,W): ',0

str_dump
	retw	'* MEMORY DUMP *',0
str_write
	retw	'<MEMORY WRITE>',0
str_address
	retw	'Start Address :00',0
str_adrerr
	retw	'Address Error',0
str_modeerr
	retw	'Set 16bit mode and Restart system',0

disp_open	=	str_open-str_base
disp_version	=	str_version-str_base
disp_8bit	=	str_8bit-str_base
disp_16bit	=	str_16bit-str_base
disp_error	=	str_error-str_base
disp_cmd	=	str_command-str_base
disp_dump	=	str_dump-str_base
disp_write	=	str_write-str_base
disp_address	=	str_address-str_base
disp_adrerr	=	str_adrerr-str_base
disp_modeerr	=	str_modeerr-str_base

;------------------------------------
; COMMAND DECODE
;------------------------------------
cmd_decode
	call	disp_crlf
	mov	w,#disp_cmd		; Command prompt
	call	disp_string
	
	call	get_chr			; Get string from 232C
	
	mov	w,rcv_byte
	call	disp_byte
	call	disp_2crlf
	
	cje	rcv_byte,#'D',:dump	; D
	cje	rcv_byte,#'d',:dump	; d
	cje	rcv_byte,#'W',:write	; W
	cje	rcv_byte,#'w',:write	; w

	mov	w,#disp_error		; Not a command
	call	disp_string
	call	disp_2crlf
	goto	cmd_decode		; Start it over again
	
:dump
	call	disp_crlf
	mov	w,#disp_dump
	call	disp_string
	call	disp_crlf
	call	mem_dump		; Memory dump
	call	disp_2crlf
	
	goto	:finish
	
:write
	call	disp_crlf
	mov	w,#disp_write
	call	disp_string
	call	disp_crlf

	call	set_address		; Set start address
	cjne	quit_flag,#0,:finish	; Quit if ESC
	cjne	skip_flag,#0,:finish	; Quit if CR

	call	write_data		; Modify memory
:finish
	ret

;------------------------------------
; GET HEX DATA FROM RS232C
;------------------------------------
get_hex					; Returns value in hex_data
	clr	hex_data
	clr	skip_flag
	clr	quit_flag
get_hex2
	clr	ignore_flag
	call	get_chr
	cjne	rcv_byte,#code_cr,:skip2	; Set skip_flag if CR
	inc	skip_flag
	goto	:finish
:skip2
	cjne	rcv_byte,#code_esc,:skip3	; ESC
	inc	quit_flag
	goto	:finish
:skip3
	call	check_hex
	cjne	ignore_flag,#0,get_hex2
	
	clc
	rl	hex_data		; Set high nibble
	rl	hex_data
	rl	hex_data
	rl	hex_data
	
:get_hex3
	clr	ignore_flag
	call	get_chr

	cje	rcv_byte,#code_cr,:get_hex3	; Ignore CR

	cjne	rcv_byte,#code_esc,:skip4	; Quit if ESC
	inc	quit_flag
	goto	:finish
:skip4
	call	check_hex
	cjne	ignore_flag,#0,:get_hex3
	cje	first_byte,#0,:finish	; Do not wait CR if first byte
:get_hex4
	call	get_chr			; Last input
	
	cjne	rcv_byte,#code_esc,:get_hex5	; ESC
	inc	quit_flag
	goto	:finish
:get_hex5
	cjne	rcv_byte,#code_cr,:get_hex4

	goto	:finish			; Everything done
:finish
	ret				; Data in hex_data

;-----------------
check_hex
	cjb	rcv_byte,#'0',:ignore	; Ignore illegal code
	cja	rcv_byte,#'9',:skip3
	mov	w,rcv_byte		; Echo back
	call	disp_byte
	mov	w,rcv_byte
	and	w,#0Fh			; Convert to binary
	goto	:go_on
:skip3
	cjb	rcv_byte,#'A',:ignore
	cjbe	rcv_byte,#'F',:skip4
	cjb	rcv_byte,#'a',:ignore
	cja	rcv_byte,#'f',:ignore
:skip4
	mov	w,rcv_byte		; Echo back
	call	disp_byte
	mov	w,rcv_byte
	and	w,#0Fh			; Convert to binary
	add	w,#9
	goto	:go_on
:ignore
	inc	ignore_flag		; Set ignore flag
:go_on
	or	hex_data,w		; Store data into hex_data

	ret
	
;------------------------------------
; GET A CHARACTER FROM RS232C
;------------------------------------
get_chr
:start_bit
	snb	serial_txd		; Find start bit
	jmp	:start_bit
	
	call	start_delay		; Wai half bit
	jb	serial_txd,:start_bit	; Still there?
	
	mov	bit_cntr,#8		; Set bit counter
	clr	rcv_byte		; Clear data buffer

:receive
	call	bit_delay		; Wait one bit time
	movb	c,serial_txd		; Put data bit into carry
	rr	rcv_byte		; Rotate
	
	djnz	bit_cntr,:receive	; Received 8 bits?
	
	call	bit_delay		; Wai for stop bit
	
	ret

;------------------------------------
; DISPLAY DATA IN HEX VIA RS232C
;------------------------------------
disp_hex				; Display single byte in HEX
	mov	temp,conv_byte
	rr	temp
	rr	temp
	rr	temp
	rr	temp
	call	:disp_nbl
	mov	temp,conv_byte		; Low nibble
:disp_nbl
	and	temp,#0Fh
	cjae	temp,#0Ah,:alfa		; 0-9?
	or	temp,#30h		;  Yes, add 30h
:output
	mov	w,temp
	call	disp_byte
	ret
	
:alfa					; Convert to A-F
	sub	temp,#09h
	or	temp,#40h
	goto	:output
;----------------------------------------
; DISPLAY w CONTENTS VIA RS232C
;----------------------------------------
disp_byte				; Send one byte in W
	mov	xmt_byte,w
	mov	bit_cntr,#8
	
	clrb	serial_rxd		; Start bit
	call	bit_delay
	
:xmit	rr	xmt_byte		; Get next bit
	movb	serial_rxd,c
	call	bit_delay		; Bit rate
	djnz	bit_cntr,:xmit
	
	setb	serial_rxd		; Stop bit
	call	bit_delay
	call	bit_delay

	ret

;-----------------------------
; DISPLAY STRING
;-----------------------------
disp_string
	mov	msg_cntr,w
:loop
	mov	w,msg_cntr
	call	disp_str
	and	w,#0FFh
	jz	:end
	call	disp_byte
	inc	msg_cntr
	goto	:loop
:end
	ret
;-----------
disp_2crlf
	call	disp_crlf
;-----------
disp_crlf				; Send CR+LF to 232C
	mov	w,#code_cr
	call	disp_byte
	mov	w,#code_lf
	call	disp_byte
	ret
;-----------
disp_space				;Send single space to 2322C
	mov	w,#code_space
	call	disp_byte
	ret

;-----------------------------
; MEMORY DUMP
;-----------------------------
mem_dump
	clr	ee_address		; Reset address
:loop1
	call	disp_crlf
	mov	conv_byte,#0			; Display address
	call	disp_hex
	mov	conv_byte,ee_address
	call	disp_hex
:loop2
	call	disp_space
	call	get_eedata		; Read 93C57 data
	mov	conv_byte,ee_rdatah	; High byte (or data)
	call	disp_hex		; Value returns in W
	
	cje	mode,#0,:eight		; Skip if 8bit mode
	mov	conv_byte,ee_rdatal	; Low byte
	call	disp_hex
:eight
	inc	ee_address
	cje	ee_address,ee_maxadr,:finish	; End of dump
	
	mov	w,ee_address
	and	w,#00000111b		; Check last 3 bits of address
	jnz	:loop2			; Next data
	
	goto	:loop1			; Next line
	
:finish
	ret

;------------------------------
; SET MEMORY START ADDRESS
;------------------------------
set_address
	mov	w,#disp_address		; Start Addess prompt
	call	disp_string
	clr	quit_flag		; Clear flags
	clr	skip_flag
	clr	ignore_flag
	clr	error_flag

	call	get_hex			; Get start address

	cjne	quit_flag,#0,:finish	; Quit if ESC
	cjne	quit_flag,#0,:finish	; Quit if CR
	
	cjb	hex_data,ee_maxadr,:sadr1	; Check illegal address
	inc	error_flag
	call	disp_space
	mov	w,#disp_adrerr		; Address error message
	call	disp_string
	call	disp_2crlf

	goto	set_address
:sadr1
	mov	ee_address,hex_data	; Set address
:finish
	ret

;------------------------------
; WRITE DATA INTO EEPROM
;------------------------------
write_data
	call	disp_2crlf
	mov	conv_byte,#0		; Display address
	call	disp_hex
	mov	conv_byte,ee_address
	call	disp_hex
	call	disp_space
	
	call	get_eedata		; Display current content
	mov	conv_byte,ee_rdatah
	call	disp_hex

	cje	mode,#0,:eight		; Skip next byte if 8bit mode
	mov	conv_byte,ee_rdatal
	call	disp_hex
:eight
	call	disp_space
	
	clr	first_byte		; First Byte
	call	get_hex			; Fetch data from 232C
	cjne	quit_flag,#0,:finish	; Quit if ESC
	cjne	skip_flag,#0,:go_on	; Skip to next address if CR

	mov	ee_wdatah,hex_data	; Store High byte of address
	
	cje	mode,#0,:eight2		; Skip if 8 bit mode
	inc	first_byte		; Second byte
	call	get_hex
	cjne	quit_flag,#0,:finish	; Quit if ESC

	mov	ee_wdatal,hex_data
:eight2
	cjne	quit_flag,#0,:finish	; If ESC quit
	cjne	skip_flag,#0,:go_on	; CR without data

	call	enable_command		; Set EEPROM to write enable
	call	enable_command2		; Send Dummy data
;	mov	ee_address,#dummy_ewen	; Dummy address data for EWEN 
;	call	send_address		; First 2bits are 11b
	clrb	ee_cs

	call	put_eedata		; Write into EEPROM

	call	delay_2msec
	clrb	ee_cs			; CS to L
	call	delay_2msec

	call	disable_command		; Write disable
	call	disable_command2	; Send Dummy data
;	mov	ee_address,#dummy_ewds	; Dummy address data for EWDS
;	call	send_address		; First 2bits are 00b
	clrb	ee_cs

	cje	error_flag,#0,:go_on	; No error
	call	disp_crlf
	mov	w,#disp_error		; Error occured
	call	disp_string
	call	disp_2crlf
	goto	:finish
:go_on
	inc	ee_address		; Show next address
	cjne	ee_address,ee_maxadr,write_data
:finish
	ret
	
;-----------------------------
; PUT DATA INTO EEPROM 93C57
;-----------------------------
put_eedata				; Address in ee_address
					; Data in ee_outdata
	clr	error_flag
	
	call	write_command		; Send write command to EEPROM
	call	send_address		; Set address
	call	send_data		; Send data
	
	ret

;-----------------------------
; GET DATA FROM EEPROM 93C57
;-----------------------------
get_eedata				; Address in ee_address
					; Data into ee_rdata
	clr	ee_rdatah
	clr	ee_rdatal
	
	call	read_command		; Send read command
	call	send_address		; Set address
	call	read_eedata		; Read data
	
	call	ee_delay		; CS to L
	clrb	ee_cs
	call	ee_delay
	
	ret
;---------------------------------
; SEND COMMAND, ADDRESS TO EEPROM
; * Multi entry, Single exit *
;---------------------------------
; * READ COMMAND
read_command
	clc
	clrb	ee_cs			; CS to L
	call	ee_delay
	setb	ee_cs			; CS to H
	
	mov	ee_cntr,#ee_cmdcntr	; 3bits for this command
	mov	ee_buff,#ee_rdcmd
	goto	ee_sendbit
; * WRITE ENABLE EEPROM
enable_command				; Write Enable mode
	clc
	clrb	ee_cs
	call	ee_delay
	setb	ee_cs
	
	mov	ee_cntr,#ee_cmdcntr	; 3bits for this command
					; First 2 bit of address should be 11b
	mov	ee_buff,#ee_ewen	; EWEN command
	goto	ee_sendbit

enable_command2				; Dummy data to complete EWEN
	mov	ee_cntr,#ee_adrcntr16
	mov	ee_buff,#dummy_ewen
	goto	ee_sendbit

; * WRITE DISABLE EEPEOM
disable_command				; Write Disable mode
	clc
	clrb	ee_cs
	call	ee_delay
	setb	ee_cs
	
	mov	ee_cntr,#ee_cmdcntr	; 3bits for this command
					; First 2 bit of address should be 00b
	mov	ee_buff,#ee_ewds	; EWDS command
	goto	ee_sendbit
disable_command2			; Dummy data to complete EWDS
	mov	ee_cntr,#ee_adrcntr16
	mov	ee_buff,#dummy_ewds
	goto	ee_sendbit
	
; * WRITE COMMAND
write_command
	clc
	clrb	ee_cs			; CS to L
	call	ee_delay
	setb	ee_cs			; CS to H
	
	mov	ee_cntr,#ee_cmdcntr	; 3 bits for this command
	mov	ee_buff,#ee_wrcmd
	goto	ee_sendbit

; * SEND READ/WRITE ADDRESS
send_address				; Assuming CS is kept H
	mov	ee_cntr,ee_adrcntr	; Length of address (8 or 7)
	mov	ee_buff,ee_address
	cje	mode,#0,:eight
	rl	ee_buff			; 16bit mode
:eight
	goto	ee_sendbit

; * SEND WRITE DATA
send_data				; Assuming CS is kept H
	mov	ee_cntr,#ee_datacntr	; 8bits for data
	mov	ee_buff,ee_wdatah	; Data to write
	call	ee_sendbit
	
	cje	mode,#0,ee_sendfinish
	mov	ee_buff,ee_wdatal
	goto	ee_sendbit
;-------------
ee_sendbit
	jnb	ee_rdy,ee_sendbit	; Wait for READY status
	rl	ee_buff			; Set data into Carry
	movb	ee_di,c			; Output data
	call	ee_delay
	setb	ee_clk			; Clock to H
	call	ee_delay
	clrb	ee_clk			; Clock to L
	call	ee_delay
	djnz	ee_cntr,ee_sendbit
	
	call	ee_delay
ee_sendfinish
	ret				; Be care, CS is still H

;-----------------------------
; READ DATA FROM EEPROM
;  Assuming address is set
;-----------------------------
read_eedata				; Assuming CS is kept H
	mov	ee_cntr,#ee_datacntr	; 8bits for data
	call	:loop1			; Read first byte
	mov	ee_rdatah,ee_rdatal	; Read next data if 16bit mode

	cje	mode,#0,:finish
	mov	ee_cntr,#ee_datacntr	; 8bits for data

:loop1
	setb	ee_clk			; Clock to H
	call	ee_delay
	movb	c,ee_do			; Get data
	rl	ee_rdatal		; Set data
	clrb	ee_clk			; Clock to L
	call	ee_delay
	
	djnz	ee_cntr,:loop1
	
:finish
	ret

;-----------------------------
; DELAY ROUTINES
;-----------------------------
ee_delay				; EEPROM delay
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	ret
bit_delay
	mov	delay_cntr,#rate_9600	; Bit duration
:loop	djnz	delay_cntr,:loop
	ret
;-----------
start_delay
	mov	delay_cntr,#rate_half	; Half bit duration
:loop	djnz	delay_cntr,:loop
	ret
;-----------
delay_2msec				; 2msec delay
	call	delay_1msec
delay_1msec				; 1msec delay
	mov	delay_cntr,#0FFh	; Parameter for 3.58MHz
:loop	nop
	nop
	nop
	nop
	djnz	delay_cntr,:loop
	ret

end
