; E2File
;
; Upload a file structure into the EEPROM of the SX web-server. This code
; should be used in conjunction with the E2File.exe program on a PC.
; Updated may 11, 2000 to support both parallax and SASM (rev 1.44.6) assemblers.
; Copyright 1999 Celsius Research Ltd
;
;
;*****************************************************************************************
; Target SX
; Uncomment one of the following lines to choose the SX18AC, SX20AC, SX28AC, SX48BD/ES,
; SX48BD, SX52BD/ES or SX52BD. For SX48BD/ES and SX52BD/ES, uncomment both defines,
; SX48_52 and SX48_52_ES.
;*****************************************************************************************
;SX18_20
;SX28
SX48_52
;SX48_52_ES

;*****************************************************************************************
; Assembler Used
; Uncomment the following line if using the Parallax SX-Key assembler. SASM assembler
; enabled by default.
;*****************************************************************************************
;SX_Key

	;*********************************************************************************
	; Assembler directives:
	;	high speed external osc, turbo mode, 8-level stack, and extended option reg.
	;
	;	SX18/20/28 - 4 pages of program memory and 8 banks of RAM enabled by default.
	;	SX48/52 - 8 pages of program memory and 16 banks of RAM enabled by default.
	;                
	;*********************************************************************************

IFDEF SX_Key 				;SX-Key Directives
  IFDEF SX18_20				;SX18AC or SX20AC device directives for SX-Key
		device	SX18L,oschs2,turbo,stackx_optionx
  ENDIF
  IFDEF SX28				;SX28AC device directives for SX-Key		
		device	SX28L,oschs2,turbo,stackx_optionx
  ENDIF
  IFDEF SX48_52_ES			;SX48BD/ES or SX52BD/ES device directives for SX-Key
		device	oschs,turbo,stackx,optionx
  ELSE
    IFDEF SX48_52				;SX48/52/BD device directives for SX-Key
		device	oschs2
    ENDIF
  ENDIF
		freq	50_000_000
ELSE  					;SASM Directives
  IFDEF SX18_20				;SX18AC or SX20AC device directives for SASM
		device	SX18,oschs2,turbo,stackx,optionx
  ENDIF
  IFDEF SX28				;SX28AC device directives for SASM
		device	SX28,oschs2,turbo,stackx,optionx
  ENDIF
  IFDEF SX48_52_ES			;SX48BD/ES or SX52BD/ES device directives for SASM
		device	SX52,oschs,turbo,stackx,optionx
  ELSE
    IFDEF SX48_52			;SX48BD or SX52BD device directives for SASM
		device	SX52,oschs2 
    ENDIF
  ENDIF
ENDIF
		reset	ResetVector 
		ID	'EEPROG'

Temp		=	$0F

UartBank	= 	$10
UartStatus	=	UartBank + $0
UartTxStart	=	UartStatus.0
UartTxMode	=	UartStatus.1
UartRxAvail	=	UartStatus.2
UartRxMode	=	UartStatus.3
UartTxPin	=	rd.3			;Output - Idle High
UartRxPin	=	rd.0			;Input  - Idle High
Uart9600	=	24
Uart19200	=	12
Uart38400	=	6
Uart57600	=	4
Uart115200	=	2
UartSpeed	=	Uart57600
UartTxData	=	UartBank + $1
UartTxBits	=	UartBank + $2
UartTxBitCount	=	UartBank + $3
UartTxDivide	=	UartBank + $4
UartRxData	=	UartBank + $5
UartRxBits	=	UartBank + $6
UartRxBitCount	=	UartBank + $7
UartRxDivide	=	UartBank + $8

E2Bank		=	$30
E2DeviceRD	=	%10100001
E2DeviceWR	=	%10100000
E2FileSizeH	=	E2Bank + $0
E2FileSizeL	=	E2Bank + $1
E2FileChecksumH	=	E2Bank + $2
E2FileChecksumL	=	E2Bank + $3
E2AddrH		=	E2Bank + $4
E2AddrL		=	E2Bank + $5
E2DataBits	=	E2Bank + $6
E2BitCount	=	E2Bank + $7
E2Delay		=	E2Bank + $8
E2Port		=	re
E2SCLPin	=	E2Port.0
E2SDAPin	=	E2Port.1
E2SCLMask	=	%00000001
E2SDAMask	=	%00000010
E2SDAInDDR	=	%11111110
E2SDAOutDDR	=	%11111100
E2PortInit	=	%11111111
E2Size		=	32*1024

; ==============================================================================

bank52	MACRO	1
	bank	\1
	IF \1 & %10000000		;SX48BD and SX52BD (production release) bank instruction 
		setb	fsr.7		;modifies FSR bits 4,5 and 6. FSR.7 needs to be set by software.
	    ELSE
		clrb	fsr.7
	    ENDIF

	ENDM

; ==============================================================================
;  Interrupt Handler
; ==============================================================================
;  Description:
;      Interrupts every 217 cycles = 4.34us
; ==============================================================================

	mov	FSR, #UartBank			;2
UartTxIsr
	snb	UartTxMode			;1/2	Transmitting ?
	jmp	UartTxDataIsr			;3	Yes => UartTxDataIsr
	snb	UartTxStart			;1/2	Start transmit ?
	jmp	UartTxStartIsr			;3	Yes => UartTxStartIsr
UartTxRet
UartRxIsr
	snb	UartRxMode			;1/2	Receiving ?
	jmp	UartRxDataIsr			;3	Yes => UartRxDataIsr
	sb	UartRxPin			;1/2	Start bit ? (start bit = 0)
	jmp	UartRxStartIsr			;3	Yes => UartRxStartIsr
UartRxRet
	mov	w, #-217			;1	Interrupt in 217 cycles @ 50MHz
	retiw					;3	Total Isr Time = x cycles

UartTxDataIsr					;	Time = 4/15/18 cycles
	decsz	UartTxDivide			;1/2
	jmp	UartTxRet			;3	Isr Time = +n cycles from UartTxIsr
	movb	UartTxPin, UartTxBits.0		;4	Output next bit
	stc					;1	Stop bit (idle = 1)
	rr	UartTxBits			;1
	mov	UartTxDivide, #UartSpeed	;2	Apply baud rate
	decsz	UartTxBitCount			;1/2
	jmp	UartTxRet			;3	Isr Time = +n cycles from UartTxIsr
	clrb	UartTxMode			;1	Finished
	jmp	UartTxRet			;3	Isr Time = +n cycles from UartTxIsr

UartTxStartIsr
	mov	UartTxBits, UartTxData		;2	Load data for transmission
	clrb	UartTxPin			;1	Output start bit
	mov	UartTxDivide, #UartSpeed	;2	Apply UartSpeed
	mov	UartTxBitCount, #10		;2	Bit count = 8 data + 1 stop + 1 for stop bit to complete
	clrb	UartTxStart			;1	Clear start mode
	setb	UartTxMode			;1	Enter Tx mode
	jmp	UartTxRet			;3	Isr Time = +n cycles

UartRxDataIsr
	decsz	UartRxDivide			;1/2
	jmp	UartRxRet			;3	Isr Time = +n cycles
	decsz	UartRxBitCount			;1/2
	jmp	:Read				;3
:Stop	mov	UartRxData, UartRxBits		;2	Copy bits into UartData
	clrb	UartRxMode			;1	Finished
	setb	UartRxAvail			;1
	jmp	UartRxRet			;3	Isr Time = +n cycles
:Read	clc					;1	Determine bit
	snb	UartRxPin			;1/2	Determine bit
	stc					;1	Determine bit
	rr	UartRxBits			;1	Record bit
	mov	UartRxDivide, #UartSpeed	;2	Apply baud rate
	jmp	UartRxRet			;3	Isr Time = +n cycles

UartRxStartIsr
	mov	UartRxDivide, #UartSpeed / 2	;2	UartDivide = UartSpeed * 0.5
	mov	UartRxBitCount, #10		;2	Bit count = 10 (1/2 start, 8 data, 1 stop)
	setb	UartRxMode			;1	Enter rx mode
	jmp	UartRxRet			;3	Isr Time = +n cycles

; ==============================================================================
; ==============================================================================

UartGetByte
	bank52	UartBank
:Loop	sb	UartRxAvail
	jmp	:Loop
	mov	w, UartRxData
	clrb	UartRxAvail
	retp

UartSendByte
	bank52	UartBank
:Loop1	snb	UartTxStart
	jmp	:Loop1
:Loop2	snb	UartTxMode
	jmp	:Loop2
	mov	UartTxData, w
	setb	UartTxStart
	retp

; ==============================================================================

ResetVector
	bank52	UartBank
	clr	UartStatus
	mov	w, #$1F
	mov	m, w
	mov	rd, #$FF
	mov	!rd, #%11110111
	mov	re, #E2PortInit
	mov	!re, #E2SDAInDDR
	mov	w, #%10011111			;Enable RTCC int
	mov	!option, w			;Enable RTCC int

	bank52	E2Bank
	clr	E2AddrH
	clr	E2AddrL

Main	call	@UartGetByte
	mov	Temp, w
	csne	Temp, #'R'
	jmp	ReadFile
	csne	Temp, #'W'
	jmp	WriteBlock
	csne	Temp, #'?'
	jmp	Hello
	jmp	Main

ReadFile
	call	@UartGetByte
	call	@E2OpenFile
	sz					;File open ? (z=true)
	jmp	:Error				;No => indicate error
	mov	w, #'['				;indicate open
	call	@UartSendByte			;send indication
:Loop	call	@E2ReadFile			;get data byte
	call	@UartSendByte
	snz					;More bytes ? (z=true)
	jmp	:Loop				;Yes => read next byte
	mov	w, #']'				;indicate complete
	call	@UartSendByte			;send indication
	jmp	Main
:Error	mov	w, #'!'				;indicate error
	call	@UartSendByte			;send indication
	jmp	Main

WriteBlock
	call	@UartGetByte
	bank52	E2Bank
	mov	E2AddrH, w			;store address high
	call	@UartGetByte
	bank52	E2Bank
	and	w, #%11100000			;ensure address low = multiple of 32
	mov	E2AddrL, w			;store address low
	call	@E2WriteStart			;open stream
	sz					;opened ?
	jmp	:Error				;No => error
	mov	E2AddrL, #32			;Count 32 bytes
	mov	w, #'['				;indicate ready
	call	@UartSendByte			;send indication
:Loop	call	@UartGetByte			;get data byte
	call	@E2WriteData			;store data
	decsz	E2AddrL
	jmp	:Loop
	call	@E2WriteComplete		;finish write transaction
	mov	w, #']'				;indicate complete
	call	@UartSendByte			;send indication
	jmp	Main
:Error	mov	w, #'!'				;indicate error
	call	@UartSendByte			;send indication
	jmp	Main

Hello
	mov	w, #'#'				;Reply
	call	@UartSendByte			;send indication
	mov	w, #E2Size / 256		;indicate size
	call	@UartSendByte			;send indication
	jmp	Main

; ==============================================================================

	org	$200

E2Start						;SCL=high, SDA=in
	mov	w, #E2SDAOutDDR			;Prepare to make SDA an output
	clrb	E2SDAPin			;When output SDA will be low
	mov	!E2Port, w			;Make SDA an output => low
	jmp	E2DelaySCLHigh			;Delay & return

E2Stop						;SCL=high, SDA=in/out
	clrb	E2SCLPin			;Make SCL go low => can change data
	mov	w, #E2SDAOutDDR			;Prepare to make SDA an output
	clrb	E2SDAPin			;Prepare to output SDA low
	mov	!E2Port, w			;Make SDA an output => low
	call	E2DelaySCLLow			;Delay
	setb	E2SCLPin			;Make SCL go high
	call	E2DelaySCLHigh			;Delay
	mov	w, #E2SDAInDDR			;Prepare to make SDA go high => stop bit
	mov	!E2Port, w			;Release SDA => stop bit
	call	E2DelaySCLHigh			;Delay
	clz					;Indicate stream closed
	retp

E2WriteToRead					;SCL=high, SDA=in
	clrb	E2SCLPin			;SCL goes low to allow data change
	call	E2DelaySCLLow			;Delay
	setb	E2SCLPin			;Return SCL high
	call	E2DelaySCLHigh			;Delay
	jmp	E2Start

E2Write						;SCL=high, SDA=in
	mov	E2DataBits, w			;Store data to be sent
	mov	E2BitCount, #8			;Send 8 bits
:Bit	clrb	E2SCLPin			;SCL goes low to allow data change
	rl	E2DataBits			;C = bit to send (MSB first)
	mov	w, #E2SDAOutDDR			;Guess bit is a 0
	snc					;Should bit be a 1 ?
	mov	w, #E2SDAInDDR			;Yes => Change to 1
	clrb	E2SDAPin			;SDA low in case of an ouput
	mov	!E2Port, w			;Apply SDA DDR
	call	E2DelaySCLLow			;Delay
	setb	E2SCLPin			;Return SCL high to allow E2 to read data
	call	E2DelaySCLHigh			;Delay
	decsz	E2BitCount			;More bits to send ?
	jmp	:Bit				;Yes => send next bit
	mov	w, #E2SDAInDDR			;Prepare to make SDA go high
	mov	!E2Port, w			;Release SDA pin for ack
	clrb	E2SCLPin			;SCL goes low for ack
	call	E2DelaySCLLow			;Delay
	setb	E2SCLPin			;Return SCL high to read ack
	call	E2DelaySCLHigh			;Delay
	stz					;Assume a 0
	snb	E2SDAPin			;Is the data a 1 ?
	clz					;Yes => change to a 1
	retp

E2ReadAck					;SCL should be high
	mov	E2BitCount, #8			;Get 8 bits
:Bit	clrb	E2SCLPin			;SCL goes low to allow data change
	mov	w, #E2SDAInDDR			;Prepare to make SDA go high
	mov	!E2Port, w			;Release SDA pin to allow data read
	call	E2DelaySCLLow			;Delay
	setb	E2SCLPin			;Return SCL high to allow E2 to read data
	clc					;Assume a 0
	snb	E2SDAPin			;Is the data a 1 ?
	stc					;Yes => change to a 1
	rl	E2DataBits			;Store new bit (MSB first)
	call	E2DelaySCLHigh			;Delay
	decsz	E2BitCount			;More bits to come ?
	jmp	:Bit				;Yes => get next bit
	clrb	E2SCLPin			;SCL goes low to allow data change for ack
	mov	w, #E2SDAOutDDR			;Prepare to make SDA go low for ack
	clrb	E2SDAPin			;SDA low when output
	mov	!E2Port, w			;Force SDA pin low
	call	E2DelaySCLLow			;Delay
	setb	E2SCLPin			;Return SCL high. Note SDA still low
	call	E2DelaySCLHigh			;Delay
	mov	w, E2DataBits			;Data to be returned
	stz					;Indicate ack
	retp

E2ReadNotAck					;SCL should be high
	mov	E2BitCount, #8			;Get 8 bits of data
:Bit	clrb	E2SCLPin			;SCL goes low to allow data change
	mov	w, #E2SDAInDDR			;Prepare to make SDA go high
	mov	!E2Port, w			;Release SDA pin to allow data read
	call	E2DelaySCLLow			;Delay
	setb	E2SCLPin			;Return SCL high to allow E2 to read data
	clc					;Assume a 0
	snb	E2SDAPin			;Is the data a 1 ?
	stc					;Yes => change to a 1
	rl	E2DataBits			;Store new bit (MSB first)
	call	E2DelaySCLHigh			;Delay
	decsz	E2BitCount			;More bits to come ?
	jmp	:Bit				;Yes => get next bit
	clrb	E2SCLPin			;SCL goes low to allow data change for ack
	call	E2DelaySCLLow			;Leave SDA high for not ack, Delay
	setb	E2SCLPin			;Return SCL high.
	call	E2DelaySCLHigh			;Delay
	mov	w, E2DataBits			;Data to be returned
	clz					;Indicate not ack
	retp

E2DelaySCLLow					;1300ns minimum = 65 instructions
	mov	E2Delay, #15			;Loop 15 times (4 cycles per loop + 8 for overhead)
:Loop	decsz	E2Delay				;Delay complete ?
	jmp	:Loop				;No => loop again
	retp

E2DelaySCLHigh					;600ns minimum = 30 instructions
	mov	E2Delay, #6			;Loop 6 times (4 cycles per loop + 8 for overhead)
:Loop	decsz	E2Delay				;Delay complete ?
	jmp	:Loop				;No => loop again
	retp

; ==============================================================================

;  Mode must be DDR

E2WriteStart					;Returns Z=true for ready
	bank52	E2Bank
	call	E2Start				;Send start bit
	mov	w, #E2DeviceWR
	call	E2Write				;Output device code
	sz					;Ack ?
	jmp	E2Stop				;No => not ready, send stop bit + return z=false
	mov	w, E2AddrH
	call	E2Write				;Output AddrH
	mov	w, E2AddrL
	call	E2Write				;Output AddrL
	sz					;Ack ?
	jmp	E2Stop				;No => not ready, send stop bit + return z=false
	retp

E2WriteData					;W = data, returns Z=true for ack
	bank52	E2Bank
	jmp	E2Write

E2WriteComplete					;Complete write process
	bank52	E2Bank
	jmp	E2Stop

E2ReadStart					;Returns Z=true for ack
	bank52	E2Bank
	call	E2Start				;Send start bit
	mov	w, #E2DeviceWR
	call	E2Write				;Output device code
	sz					;Ack ?
	jmp	E2Stop				;No => not ready, send stop bit + return z=false
	mov	w, E2AddrH
	call	E2Write				;Output AddrH
	mov	w, E2AddrL
	call	E2Write				;Output AddrL
	call	E2WriteToRead			;Send start bit
	mov	w, #E2DeviceRD
	call	E2Write				;Output 
	sz					;Ack ?
	jmp	E2Stop				;No => not ready, send stop bit + return z=false
	retp

E2ReadData					;Returns W=data
	bank52	E2Bank
	jmp	E2ReadAck			;Read byte

E2ReadComplete					;Returns W=data
	bank52	E2Bank
	call	E2ReadNotAck			;Read byte
	call	E2Stop				;Send stop bit
	mov	w, E2DataBits			;Data to be returned
	retp

; ==============================================================================

E2OpenFile					;w = file reference, returns z=true for open
	bank52	E2Bank
	mov	E2AddrL, w			;AddrL = reference
	clr	E2AddrH				;AddrH = 0
	clc					;Prepare to multiply by 2
	rl	E2AddrL				;Multiply by 2
	rl	E2AddrH				;Multiply by 2
	call	E2ReadStart			;Prepare to read
	sz					;Ack ?
	retp					;No => return error (z=false)
	call	E2ReadData			;Read MSB of address
	mov	E2AddrH, w			;AddrH = MSB
	call	E2ReadComplete			;Read LSB of address
	mov	E2AddrL, w			;AddrL = LSB
	call	E2ReadStart			;Prepare to read
	sz					;Ack ?
	retp					;No => return error (z=false)
	call	E2ReadData			;Read 1st byte
	mov	E2FileSizeH, w			;1st byte = FilesizeH
	call	E2ReadData			;Read 2nd byte
	mov	E2FileSizeL, w			;2nd byte = FilesizeL
	call	E2ReadData			;Read 3rd byte
	mov	E2FileChecksumH, w		;3rd byte = ChecksumH
	call	E2ReadData			;Read 4th byte
	mov	E2FileChecksumL, w		;4th byte = ChecksumL
	retp					;Return ready to read 1st real data byte

E2CloseFile	=	E2ReadComplete		;Should not be used unless closing file prior to reaching end

E2ReadFile					;Returns Z=More (=false for last byte)
	bank52	E2Bank
	decsz	E2FilesizeL			;More ?
	jmp	E2ReadData			;Yes => read data
	test	E2FilesizeH			;No  => check high
	snz					;More ?
	jmp	E2ReadComplete			;No  => read last byte and close file
	dec	E2FilesizeH			;Yes => decrement counter
	jmp	E2ReadData			;       read data

; ==============================================================================
