;*****************************************************************************************
; Copyright © 1999 Scenix Semiconductor, Inc. All rights reserved.
;
; Scenix Semiconductor, Inc. assumes no responsibility or liability for
; the use of this software.
; Scenix Semiconductor conveys no license, implicitly or otherwise, under
; any intellectual property rights.
; Information contained in this publication regarding TCP/IP stack, 
; and the like is intended through suggestion only and may
; be superseded by updates. Scenix Semiconductor makes no representation
; or warranties with respect to the accuracy or use of these information,
; or infringement of patents arising from such use or otherwise.
; Patent Pending.
;*****************************************************************************************
; 
; Filename: TCP.src
;
; Authors: 
;	   Chris Waters (SMTP, HTTP, TCP/IP, PPP, UART)	
;	   Abraham Si (POP3)		
;
; Revision:	1.0.4
;
; Part:		SX52BD 
; Freq:		50Mhz
; Compiled using Parallax SX-Key software v1.07 and SASM 1.40
; To compile this software using SXKEY search for the word 'SXKEY_CHANGE' and make the commented 
; changes.
;
; Date Written: Oct. 1999
;
; Last Revised: 
;	8 Nov 1999 - Added bank instruction to AppBytesToSend for HTTP. (CJW).
;	19 Nov 1999 - Integrated UDP demo code. Bug fixes. (CJW).
;	20 Jan 2000 - Changed device directive for 2.0 silicon. (CJW).
;	11 may 2000 - Updated to assemble correctly with SASM (version 1.44.6). Also verified with 
;         		  Parallax's software Rev. 1.19
;
; Program Description:                          
;		       HTTP server
;		       SMTP client, send email when threshold crossed if ADCDEMO is enabled;
;		                    else send email when connected and when PING'ed
;		       POP3 client, can retrieve up to 255 messages. Attachments are okay.
;		       received emails are not buffered, but just sent out immediately to 
;                      the debug port.                                             
;       
; Revision History: 1.00  initial public release
;
;*****************************************************************************************


;*****************************************************************************************
; 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
  irc_cal	IRC_SLOW
  freq		50_000_000
ENDIF

; set POP3DEMO & DEBUG =1 since the email will be pumped out from the debug port

;===============================================================================
; Options
;
; Use these defines to enable or disable different features in the code.
;===============================================================================

;DEBUG		; Set to enable the debugging information.
WIN32		; Set if the host is a Windows computer.
WIN98		; Set if the host is Windows 98.

UDP		; Enable UDP.
TCP		; Enable TCP.

;UDPDEMO	; Enable the UDP demonstration code. (Requires UDP.)

HTTPDEMO	; Enable the web server. (Requires TCP.)
JAVADEMO	; Enable the Java sprinkler demo. (Requires UDP.)

;SMTPDEMO	; Enable the SMTP client. (Requires TCP.)     
;POP3DEMO	; Enable the POP3 client (Requires TCP.)
;POP3DEBUG	; Send POP3 state information to the debug port.
;ADCDEMO	; Enable the A/D for the SMTP client demo.


IFDEF POP3DEMO
		id	'POP3  '		
ENDIF
IFDEF SMTPDEMO
		id	'SMTP  '
ENDIF
IFDEF HTTPDEMO
		id	'HTTP  '
ENDIF				
		reset	ResetVector                                           

;*****************************************************************************************
; Macros
;*****************************************************************************************

	;*********************************************************************************
	; Macro: _bank
	; Sets the bank appropriately for all revisions of SX.
	;
	; This is required since the bank instruction has only a 3-bit operand, it cannot
	; be used to access all 16 banks of the SX48/52. For this reason FSR.4 (for SX48/52BD/ES)
	; or FSR.7 (SX48/52bd production release) needs to be set appropriately, depending
	; on the bank address being accessed. This macro fixes this.
	;
	; So, instead of using the bank instruction to switch between banks, use _bank instead.
	; 
	;*********************************************************************************
_bank	macro	1
	NOEXPAND
	bank	\1

	IFDEF SX48_52
	  IFDEF SX48_52_ES
	    IF \1 & %00010000		;SX48BD/ES and SX52BD/ES (engineering sample) bank instruction
		setb	fsr.4		;modifies FSR bits 5,6 and 7. FSR.4 needs to be set by software.
	    ENDIF
	  ELSE
	    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
	  ENDIF
	ENDIF
	endm

	;*********************************************************************************
	; Macro: _mode
	; Sets the MODE register appropriately for all revisions of SX.
	;
	; This is required since the MODE (or MOV M,#) instruction has only a 4-bit operand. 
	; The SX18/20/28AC use only 4 bits of the MODE register, however the SX48/52BD have 
	; the added ability of reading or writing some of the MODE registers, and therefore use
	; 5-bits of the MODE register. The  MOV M,W instruction modifies all 8-bits of the 
	; MODE register, so this instruction must be used on the SX48/52BD to make sure the MODE
	; register is written with the correct value. This macro fixes this.
	;
	; So, instead of using the MODE or MOV M,# instructions to load the M register, use
	;  _mode instead.
	; 
	;*********************************************************************************
_mode	macro	1
	IFDEF SX48_52
		mov	w,#\1		;loads the M register correctly for the SX48BD and SX52BD
		mov	m,w
	ELSE
		mov	m,#\1		;loads the M register correctly for the SX18AC, SX20AC
					;and SX28AC
	ENDIF              
	endm



top	=	0
indent	=	1

IFDEF DEBUG 
; uncomment only if you have the debug monitor utility
	DEBUGP		MACRO	2
;		mov	DebugScratch1,w
;	IF	\2 = 1
;		mov	w,#(\1|$80)
;	ELSE
;		mov	w,#\1
;	ENDIF	
;		call	@DebugSendByte
;		mov	w,DebugScratch1
	ENDM

	DEBUGW		MACRO	2
;		mov	DebugScratch1,w
;	IF	\2 = 1
;		mov	w,#(\1|$80)
;	ELSE
;		mov	w,#\1
;	ENDIF	
;		call	@DebugSendByte
;		mov	w,DebugScratch1
;		call	@DebugSendByte
;		mov	w,DebugScratch1
	ENDM

ELSE
	DEBUGP	MACRO     2
	ENDM

	DEBUGW	MACRO     2
	ENDM
	
ENDIF

	POP3W   MACRO 
		mov	DebugScratch1,w		; save W register
		call	@DebugSendByte
		mov	w,DebugScratch1		; restore W 
		_bank	POP3Vars
	ENDM
	
IFDEF POP3DEBUG	
	POP3DW   MACRO 
		mov	DebugScratch1,w		; save W register
		call	@DebugSendByte
		mov	w,DebugScratch1		; restore W 
		_bank	POP3Vars
	ENDM
ELSE
	POP3DW	MACRO
	ENDM
ENDIF	

;-----------------------------------------------------------------------------
; UART ring macro
; Advance the pointer through the ring, wrapping around if necessary
; This could be more efficient for aligned and power of 2 ring sizes.
;-----------------------------------------------------------------------------

ringadv	MACRO	3		; Arguments ptr,base,size
	inc	\1		; Increment the pointer
	; Check for wrap around	
	mov	w,\1		; Load the pointer	
	xor	w,#(\2+\3)	; Check if ptr = base+size
	mov	w,#\2
	snb	status.2
	mov	\1,w		; Equal, set ptr to base
ENDM

;===============================================================================
; Constants
;===============================================================================

;-------------------------------------------------------------------------------
; Physical layer constants
;-------------------------------------------------------------------------------

; Ring buffer sizes
rx_ring_size	equ	7	; Size (in bytes) of the rx ring buffer.
tx_ring_size	equ	7	; Size of the tx ring buffer.

; N.B. In windows 95 the FIFO support must be turned off in the system control panel.

; PPP UART = 57600 baud
baud_bit	=	2                       
start_delay	=	4+2+1                
int_period	=	217            

; Port Assignment: Bit variables
rx_pin		EQU     rd.7 ; UART receive input
tx_pin		EQU     rd.6 ; UART transmit output
rts_pin		EQU	rd.5 ; UART RTS 
cts_pin		EQU	rd.1 ; UART CTS

;-------------------------------------------------------------------------------
; PPP constants
;-------------------------------------------------------------------------------

; PPP packet data format
PPPFlag		=	$7E
PPPEscape	= 	$7D
PPPXor		=	$20
PPPAddress	=	$FF
PPPControl	=	$03
PPPLCPPrefix	=	$C0
PPPLCP		=	$21
PPPIPCPPrefix	=	$80
PPPIPCP		=	$21
PPPIPPrefix	=	$00
PPPIP		=	$21

; PPP LCP codes
PPPConfigureRequest=	1
PPPConfigureAck	=	2
PPPConfigureNak	=	3
PPPConfigureReject=	4
PPPTerminateRequest=	5
PPPTerminateAck	=	6
PPPCodeReject	=	7
PPPProtocolReject=	8
PPPEchoRequest	=	9
PPPEchoReply	=	10
PPPDiscardRequest=	11

; PPP state machine states.
PPPStateInitial	=	0
PPPStateStarting=	1
PPPStateClosed	=	2
PPPStateStopped	=	3
PPPStateClosing	=	4
PPPStateStopping=	5
PPPStateReqSent	=	6
PPPStateAckRcvd	=	7
PPPStateAckSent	=	8
PPPStateOpened	=	9

; PPP receive packet state machine.
PPPStateFlag	=	0
PPPStateAddress	=	1
PPPStateControl	=	2	
PPPStateProto1	=	3
PPPStateProto2	=	4
PPPStateLCPCode	=	5
PPPStateLCPID	=	6
PPPStateLCPLen1	=	7
PPPStateLCPLen2	=	8
PPPStateData	=	9
PPPStateFCS1	=	10
PPPStateFCS2	=	11
PPPStateUnknownCode=	12

; PPP events.
PPP_NONE	=	0
PPP_RCA		=	2
PPP_RCN		=	3
PPP_RTR		=	4
PPP_RTA		=	5
PPP_RUC		=	6
PPP_RXJ_GOOD	=	7
PPP_RXJ_BAD	=	8
PPP_RXR		=	9
PPP_RCR_GOOD 	= 	10
PPP_RCR_BAD	=	11
PPP_TO_GOOD	=	12
PPP_TO_BAD	=	13
PPP_DATA	=	14

; PPP IPCP options
PPPAddressOption =	3

; Frame Check Sequence
PPPValidFCSh	= 	$F0	
PPPValidFCSl	= 	$B8

PPPRestartTimeout =	3	; Number of seconds before restart timer expires
PPPRestartExpire =	8	; 1/(PPPRestartTimeout * 1/(ClockRate) * 256 * 256 * int_period)
PPPRestartCountDefault	= 6	; Number of times to send configure-req before giving up.

;-------------------------------------------------------------------------------
; IP constants
;-------------------------------------------------------------------------------

IPVersion	=	4	; IP version number.
IPIHL		=	5	; Header length in number of 32 bit words. 
IPVIHL		=	(IPVersion<<4) | IPIHL
IPTOS		=	0	; Type of service. Equals zero for routine service.
IPFlagsField	=	0	; May fragment, last fragment.
IPFrag1		=	(IPFlagsField<<5)
IPFrag2		=	0	; No fragments so fragment offset equals zero.
IPTTL		=	15	; Time to live (number of hops packet allowed to travel).
	
IPProtocolICMP	=	1	; ICMP protocol.
IPProtocolUDP	=	17	; UDP protcol.
IPProtocolTCP	=	6	; TCP protocol.

ICMPEchoRequest	=	8	; ICMP echo request packet.
ICMPEchoReply	=	0	; ICMP echo reply packet.
ICMPEchoHL	=	8	; Length of an ICMP echo header.

IPAddress1	=	192	; IP address of the SX.
IPAddress2	=	168
IPAddress3	=	11
IPAddress4	=	1

UDPHLength	=	8	; Length of the UDP header.

;-------------------------------------------------------------------------------
; TCP constants
;-------------------------------------------------------------------------------

; TCP state machine states.
TCPStateClosed	=	0
TCPStateListen	=	1
TCPStateSynSent	=	2
TCPStateSynReceived =	3
TCPStateEstablished =	4
TCPStateFinWait1 =	5
TCPStateFinWait2 =	6
TCPStateCloseWait =	7
TCPStateClosing	=	8
TCPStateLastAck	=	9
TCPStateTimeWait = 	10

; Bit positions in the flag byte.
TCPFlagURG	=	5
TCPFlagACK	=	4
TCPFlagPSH	=	3
TCPFlagRST	=	2
TCPFlagSYN	=	1
TCPFlagFIN	=	0

; TCP Options
TCPOptionEnd	=	0
TCPOptionNOP	=	1
TCPOptionMSS	=	2	; Maximum segment size option.

TCPHeaderLength	=	5	; Normal TCP header length.
TCPOffsetMask	=	$F0

; This is the maximum number of data bytes we will accept at once.
TCPWindow	=	254

TCPRestartExpire =	16


CharCR		=	$0d	; Carriage return.
CharLF		=	$0a	; Linefeed.

;-------------------------------------------------------------------------------
; Java Demo constants
;-------------------------------------------------------------------------------

IFDEF JAVADEMO

sprinklerPort	=	7024
sprinklerZones	=	3	
sprinklerOn	=	1
sprinklerOff	=	0
sprinkler15	=	0
sprinkler30	=	1
sprinkler45	=	2
commandGet	=	10
commandSet	=	11
ENDIF

;-------------------------------------------------------------------------------
; HTTP constants
;-------------------------------------------------------------------------------

IFDEF HTTPDEMO

; Port number for HTTP server.
HTTPPorth	=	0
HTTPPortl	=	80

; States for parsing HTTP headers.
HTTPParseMethod	=	0
HTTPParseURI	=	1
HTTPParseVersion =	2
HTTPParseHeader	=	3
HTTPParseFinished =	4
HTTPParse1	=	5
HTTPParse2	=	6
HTTPParse3	=	7
HTTPParse4	=	8

HTTPMethodNone	=	0
HTTPMethodGET	=	'G'
HTTPMethodPOST	=	'P'
HTTPMethodHEAD	=	'H'

HTTP404Hash	=	$AA	; Hash of the URL /404.html

HTTPVarStart	=	'|'
ENDIF
;-------------------------------------------------------------------------------
; SMTP constants
;-------------------------------------------------------------------------------

IFDEF SMTPDEMO

SMTPPorth	=	0
SMTPPortl	=	25

; The state reflects the last message received from the SMTP server.
SMTPStateClosed	=	0
SMTPStateHello	=	1
SMTPStateHelloAck =	2
SMTPStateMail	=	3
SMTPStateMailAck =	4
SMTPStateRcpt	=	5
SMTPStateRcptAck =	6
SMTPStateData	=	7
SMTPStateDataAck =	8
SMTPStateMesg	=	9
SMTPStateMesgAck =	10
SMTPStateQuit	=	11
SMTPStateQuitAck =	12
SMTPStateFinished =	13

; IP address of the SMTP server.
SMTPAddress1	=	192	
SMTPAddress2	=	168
SMTPAddress3	=	11
SMTPAddress4	=	2

ENDIF


;----------------------------------------------;-------------------------------------------------------------------------------
; POP3 constants
;-------------------------------------------------------------------------------

IFDEF POP3DEMO

POP3Porth	=	0
POP3Portl	=	110

; The state reflects the last message received from the POP3 server.
POP3StateClosed	=	0
POP3StateUser	=	1
POP3StateUserAck =	2
POP3StatePass	=	3
POP3StatePassAck =	4
POP3StateStat	=	5
POP3StateStatAck =	6
POP3StateRetr	=	7
POP3StateRetrAck =	8
POP3StateMsg	 =	9
POP3StateDele	=	10
POP3StateDeleAck =	11
POP3StateQuit	=	12
POP3StateQuitAck =	13
POP3StateFinished =	14

POP3MsgSubStStart	=0	;start of Msg sub state
POP3MsgSubSt1CR		=1	; 1st CR
POP3MsgSubSt1LF		=2	; 1st LF detected
POP3MsgSubStDot		=3	; <CR><LF>. detected
POP3MsgSubSt2CR		=4	; <CR><LF>.<CR>

POP3MsgSubStDotDot	=6	; byte-stuffed termination octet, strip it off

; IP address of the POP3 server.
POP3Address1	=	192	
POP3Address2	=	168
POP3Address3	=	11
POP3Address4	=	2

ENDIF

;-------------------------------------------------------------------------------
; UDP Demo constants
;-------------------------------------------------------------------------------

IFDEF UDPDEMO
DemoPort	=	280	; Port number for SX demo.
DemoMemDump	=	$10	; Command to dump memory.
DemoMemSet	=	$20	; Command to set a memory location.
DemoMemGet	=	$30	; Command to get a memory location.
ENDIF

;-------------------------------------------------------------------------------
; Demo board constants
;-------------------------------------------------------------------------------

;StatusPort	=	re	; Port for the status LEDs. 
StatusPort	EQU	re	; Port for the status LEDs.
LED1		=	7
LED0		=	6
LEDUP		=	5	; Link Up LED.
LEDRx		=	4	; Traffic LED.
LEDTx		=	3	; Traffic LED.
LEDErr		=	2	; Error LED.


;===============================================================================
; Variables
;===============================================================================

	org	$0A
;-------------------------------------------------------------------------------
; Global variables
;-------------------------------------------------------------------------------


Scratch0	equ	0ah	; Scratch0 is preserved across SOME function calls.
Scratch1	equ	0bh	; Scratch1 is never preserved.
Scratch2	equ	0ch	; Scratch2   


IFDEF DEBUG
DebugScratch0	equ	0dh
DebugScratch1	equ	0eh
ENDIF

;-------------------------------------------------------------------------------
; PPP variables
;-------------------------------------------------------------------------------
	org	$10
PPPVars	=	$

PPPFlags	ds	1	; Flags for the PPP state machine.
inLCP		=	0	; 1 if the state machine is in LCP negotiation.
inIPCP		=	1	; 1 if the state machine is in IPCP negotiation.
inIP		=	2	; 1 if the IP layer is running.
timerRunning	=	3	; 1 if the restart timer is running.
linkUp		=	4	; 1 if the PPP link is up.
addressOption	=	5	; 1 if we are handling a pesky address option.
PPPState	ds	1	; The state of the PPP state machine. 
PPPRxState	ds	1	; State of the receive state machine.
PPPEvent	ds	1	; The last PPP event.
PPPDelayEvent	ds	1	; Temporarily hold a delayed event.
PPPProto1	ds	1	; The first byte of the received protocol
PPPProto2	ds	1	; The second byte of the received protocol
PPPIdentifier	ds	1	; The received identifier.
PPPLengthh	ds	1	; The high byte of the received length.
PPPLengthl	ds	1	; The low byte of the received length.
PPPTxFCSh	ds	1	; High byte of the Tx frame check sequence.
PPPTxFCSl	ds	1	; Low byte of the Tx frame check sequence.
PPPRxFCSh	ds	1	; High byte of the Rx frame check sequence.
PPPRxFCSl	ds	1	; Low byte of the Rx frame check sequence.
PPPFCSA		ds	1	; Temporary variable shared by both FCS routines.
PPPRestartCount	ds	1	; Restart count for PPP state machine.

	org	$20
IPVars	=	$

IPFlags		ds	1	; Flags used for IP Receive.
echoPacket	=	0	; Last packet was ICMP echo.
UDPPacket	=	1	; Current packet is UDP.
TCPPacket	=	2	; Current packet is TCP.
ICMPPacket	=	3	; ICMP packet. Will be refined to echoPacket when known.
anyPacket	=	4	; Any packet was received.
checksumBit	=	7	; Used while computing the checksum.
IPTxVars	=	$
IPProtocol	ds	1	; The protocol contained in the packet.
IPDestAddress1	ds	1	; IP destination address.
IPDestAddress2	ds	1
IPDestAddress3	ds	1
IPDestAddress4	ds	1
IPSrcAddress1	=	IPDestAddress1	; IP source address.
IPSrcAddress2	=	IPDestAddress2
IPSrcAddress3	=	IPDestAddress3
IPSrcAddress4	=	IPDestAddress4
IPLengthMSB	ds	1	; MSB of length of the packet in bytes.
IPLengthLSB	ds	1	; LSB of length of the packet in bytes.
IPChecksumMSB	ds	1	; High byte of the IP checksum.
IPChecksumLSB	ds	1	; Low byte of the IP checksum.
IPIDCounter	ds	1	; Counter for the identification field.
IPRxLengthMSB	ds	1	; Length of the packet in bytes.
IPRxLengthLSB	ds	1	; Length of the packet in bytes.

IFDEF TCP
TCPChecksumMSB	ds	1	; TCP Checksum. Must be in the IPVars bank.
TCPChecksumLSB	ds	1	; TCP Checksum.
ENDIF

;-------------------------------------------------------------------------------
; UDP variables
;-------------------------------------------------------------------------------

	org	$30

UDPVars		=	$

UDPSrcPortl	ds	1	; The source port.
UDPSrcPorth	ds	1	
UDPDestPortl	ds	1	; The destination port.
UDPDestPorth	ds	1
UDPLengthMSB	ds	1	; Temporary storage for the length.
UDPLengthLSB	ds	1
UDPRxLength	ds	1	; Length of a received UDP packet.


	org	$40
PPPTimer	=	$
PPPTimer1	ds	1	; Restart timer for the PPP state machine.
PPPTimer2	ds	1	; Restart timer for the PPP state machine.
PPPTimer3	ds	1	; Restart timer for the PPP state machine.

;-------------------------------------------------------------------------------
; TCP variables
;-------------------------------------------------------------------------------

IFDEF TCP

TCPVars		=	$
TCPState	ds	1	; State machine state.
TCPInfo		ds	1
TCPTMP_SEQ4	ds	1	; TMP.SEQ. 1=LSB, 4=MSB.
TCPTMP_SEQ3	ds	1	; Temporary information from the received packet.
TCPTMP_SEQ2	ds	1
TCPTMP_SEQ1	ds	1
TCPTMP_ACK4	ds	1	; TMP.ACK. 
TCPTMP_ACK3	ds	1	; Temporary information from the received packet.
TCPTMP_ACK2	ds	1
TCPTMP_ACK1	ds	1
TCPOutstanding	ds	1	; The number of unacknowledged bytes.
TCPRxFlags	ds	1	; Copy of the received flags field.

	org	$50
	
; The ordering of these variables is significant. It is the same as the TCP
; header. This simplifies the send-packet code.
TCB		=	$
TCPLocalPorth	ds	1	; The source port.
TCPLocalPortl	ds	1	
TCPRemotePorth	ds	1	; The destination port.
TCPRemotePortl	ds	1	
TCPSND_UNA4	ds	1	; SND.UNA. 1=LSB, 4=MSB.
TCPSND_UNA3	ds	1	; The oldest unacknowledged byte.
TCPSND_UNA2	ds	1
TCPSND_UNA1	ds	1
TCPRCV_NXT4	ds	1	; RCV.NXT. 1=LSB, 4=MSB.
TCPRCV_NXT3	ds	1	; The next byte to receive.
TCPRCV_NXT2	ds	1
TCPRCV_NXT1	ds	1
TCPOffset	ds	1	; Length of the TCP options.
TCPFlags	ds	1	; Flags field.
TCP_WND_MSB	ds	1	; The send window (MSB).
TCP_WND_LSB	ds	1	; the send window (LSB).
TCBEnd		=	$

ENDIF

;-------------------------------------------------------------------------------
; SMTP variables
;-------------------------------------------------------------------------------

	org	$60

IFDEF SMTPDEMO

SMTPVars	=	$
SMTPState	ds	1	; SMTP transaction state machine.
SMTPRxCount	ds	1	; Count of the number of bytes received in a packet.
SMTPTxCount	ds	1	; Count of the number of bytes sent in a packet.
SMTPCommand	ds	1	; Last SMTP command received.
SMTPEOL		ds	1	; Flag to indicate if EOL reached.
SMTPTxPointer	ds	1	; Pointer to the message to transmit.

ENDIF


;-------------------------------------------------------------------------------
; POP3 variables
;-------------------------------------------------------------------------------

	org	$60

IFDEF POP3DEMO

POP3Vars	=	$

POP3State	ds	1	; POP3 transaction state machine.
POP3MsgSubSt	ds	1	; substate of POP3 Msg state
POP3MsgSubStLast	ds	1	; the last substate since AckPacketOk
		
POP3RxCount	ds	1	; Count of the number of bytes received in a packet.
POP3TxCount	ds	1	; Count of the number of bytes sent in a packet.

; either +ok -err or +ok n (for stat command)
POP3Command1	ds	1	; Last POP3 command received.


POP3Command2	ds	1


POP3Command3	ds	1


POP3Command4	ds	1


POP3Command5	ds	1


POP3Command6	ds	1
POP3Command7	ds	1
POP3TxMsgNo	ds	1

POP3EOL		ds	1	; Flag to indicate if EOL reached.
POP3TxPointer	ds	1	; Pointer to the message to transmit.
POP3MsgEndFlag	ds	1	; message end =1, set when <LF>.<CR> is detected, actual change of state done in AppPacketOk

	org	$70	
POP3MoreVars	=	$
POP3RxMsgNo	ds	1	; reuse of memory, not use at the same time
	org	$75		; position at the 5 th byte, so commands like "RETR xxx" will be easily used
POP3TxMsgDigit1 ds	1
POP3TxMsgDigit2 ds	1
POP3TxMsgDigit3 ds	1
ENDIF


;-------------------------------------------------------------------------------
; HTTP variables
;-------------------------------------------------------------------------------

	org	$60

IFDEF HTTPDEMO

HTTPVars	=	$
HTTPParseState	ds	1	; State of the HTTP header parser.
HTTPURIHash	ds	1	; Hash of the current URI.
HTTPMethod	ds	1	; HTTP method.
HTTPDone	ds	1
HTTPLengthMSB	ds	1
HTTPLengthLSB	ds	1

E2Bank		=	$
E2DeviceRD	=	%10100001
E2DeviceWR	=	%10100000
E2FileSizeH	ds	1
E2FileSizeL	ds	1
E2FileChecksumH	ds	1
E2FileChecksumL	ds	1
E2AddrH		ds	1
E2AddrL		ds	1
E2DataBits	ds	1
E2BitCount	ds	1
E2Delay		ds	1

;E2Port		=	re    
E2Port		EQU	re

E2SCLPin	=	E2Port.0
E2SDAPin	=	E2Port.1


E2SCLMask	=	%00000001
E2SDAMask	=	%00000010
E2SDAInDDR	=	%00000010
E2SDAOutDDR	=	%00000000
E2PortInit	=	%00000011
E2Size		=	8192
ENDIF

;-------------------------------------------------------------------------------
; A/D Demo variables
;-------------------------------------------------------------------------------

IFDEF ADCDEMO
	org	$70

ADCVars		=	$
isrFlags	ds	1
adcComplete	ds	1
adcValue	ds	1
adcCount	ds	1
adcAcc		ds	1
adcTemp		ds	1
ADCWarning	ds	1
ADCTxCount	ds	1
ENDIF

;-------------------------------------------------------------------------------
; Java Demo variables
;-------------------------------------------------------------------------------

IFDEF JAVADEMO
	org	$80

JavaVars	=	$
lawnOn		ds	1
lawnTime	ds	1
pathOn		ds	1
pathTime	ds	1
flowerOn	ds	1
flowerTime	ds	1
JavaTxEnd	=	$
ENDIF

;-------------------------------------------------------------------------------
; Serial UART variables
;-------------------------------------------------------------------------------
	org     $E0             
serial          =       $       ; Serial UART bank
;
save_bank	ds	1
tx_high         ds      1	;hi byte to transmit
tx_low          ds      1       ;low byte to transmit
tx_count        ds      1       ;number of bits sent
tx_divide       ds      1       ;xmit timing (/16) counter
rx_count        ds      1       ;number of bits received
rx_divide       ds      1       ;receive timing counter
rx_byte         ds      1       ;buffer for incoming byte
flags		ds	1	; Flags
rx_flag         EQU     flags.0 ;signals when byte is received.
rx_over		EQU	1	;signals an overflow.
rx_ring_ip	ds	1	;receive ring in pointer
rx_ring_op	ds	1	;receive ring out pointer
rx_ring_cnt	ds	1	;receive ring contents count
tx_ring_ip	ds	1	;transmit ring in pointer
tx_ring_op	ds	1	;transmit ring out pointer
tx_ring_cnt	ds	1	;transmit ring contents count

	org	$F0		
uart_rx_ring	=	$	; UART ring buffers
uart_tx_ring	=	$
rx_ring		ds	rx_ring_size	;space for the rx and tx ring buffers
tx_ring		ds	tx_ring_size
uart_temp	ds	1	; Temporary byte for UART.
uart_temp_isr	ds	1	; Temporary byte for UART for use in ISR.

IFDEF DEBUG 
	org     $D0                     ;variables
debug_serial	=       $                       ;UART bank

debug_tx_high	ds      1                       ;hi byte to transmit
debug_tx_low	ds      1                       ;low byte to transmit
debug_tx_count	ds      1                       ;number of bits sent
debug_tx_divide	ds      1                       ;xmit timing (/16) counter
debug_rx_count	ds      1                       ;number of bits received
debug_rx_divide	ds      1                       ;receive timing counter
debug_rx_byte	ds      1                       ;buffer for incoming byte
debug_rx_flag	EQU     flags.6                 ;signals when byte is received
debug_save_bank	ds	1
debug_save_mode	ds	1			;Save the mode register

ENDIF

;===============================================================================
; Interrupt service routine
;===============================================================================

		org	$0	; The ISR starts at location 0.

	jmp	@SerialISR	; Use the UART VP

;===============================================================================
; Jump table
;===============================================================================


ResetVector	jmp	@_ResetVector

		org	$C00

PPPInit		jmp	@_PPPInit
PPPOpen		jmp	@_PPPOpen
PPPRxData	jmp	@_PPPRxData
PPPClose	jmp	@_PPPClose
PPPSendConfReq	jmp	@_PPPSendConfReq
PPPSendConfRej	jmp	@_PPPSendConfRej
PPPSendCodeRej	jmp	@_PPPSendCodeRej
PPPSendConfAck	jmp	@_PPPSendConfAck
PPPSendTermReq	jmp	@_PPPSendTermReq
PPPSendPacket	jmp	@_PPPSendPacket
PPPStartIPPacket jmp	@_PPPStartIPPacket
PPPClosePacket	jmp	@_PPPClosePacket
PPPSendPartialPacket	jmp	@_PPPSendPartialPacket
PPPReceive	jmp	@_PPPReceive
PPPTxFCSInit	jmp	@_PPPTxFCSInit
PPPTxFCSData	jmp	@_PPPTxFCSData
PPPRxFCSInit	jmp	@_PPPRxFCSInit
PPPRxFCSData	jmp	@_PPPRxFCSData
PPPCheckFCS	jmp	@_PPPCheckFCS

IPStartPacket	jmp	@_IPStartPacket
IPReceivePacket	jmp	@_IPReceivePacket
IPRxHeader	jmp	@_IPRxHeader
IPRxClosePacket	jmp	@_IPRxClosePacket
IPChecksum	jmp	@_IPChecksum

IFDEF UDP
UDPStartPacket	jmp	@_UDPStartPacket
UDPRxHeader	jmp	@_UDPRxHeader
ENDIF

IFDEF TCP
TCPActiveOpen	jmp	@_TCPActiveOpen
TCPPassiveOpen	jmp	@_TCPPassiveOpen
TCPClosePacket	jmp	@_TCPClosePacket
TCPTransmit	jmp	@_TCPTransmit
TCPRxHeader	jmp	@_TCPRxHeader
TCPSendHeader	jmp	@_TCPSendHeader
TCPSendSyn	jmp	@_TCPSendSyn
TCPSendSynAck	jmp	@_TCPSendSynAck
TCPSendAck	jmp	@_TCPSendAck
TCPAddRCV_NXT	jmp	@_TCPAddRCV_NXT
TCPAddSND_UNA	jmp	@_TCPAddSND_UNA
TCPInitChecksum	jmp	@_TCPInitChecksum
TCPTxByte	jmp	@_TCPTxByte
TCPProcessPacket jmp	@_TCPProcessPacket
TCPSendEmptyHeader jmp	@_TCPSendEmptyHeader
TCPClose	jmp	@_TCPClose
TCPChecksum	jmp	@_TCPChecksum
TCPAckUpdate	jmp	@_TCPAckUpdate

AppInit		jmp	@_AppInit
AppBytesToSend	jmp	@_AppBytesToSend
AppBytesAvailable	jmp	@_AppBytesAvailable
AppNak		jmp	@_AppNak
AppAck		jmp	@_AppAck
AppTxByte	jmp	@_AppTxByte
AppRxByte	jmp	@_AppRxByte
AppPacketOK	jmp	@_AppPacketOK
AppPacketBad	jmp	@_AppPacketBad
ENDIF

IPRxData
PhyRxByte	jmp	@_PhyRxByte
IPTxData
PhyTxByte	jmp	@_PhyTxByte
PhyTxByteNoFCS	jmp	@_PhyTxByteNoFCS
PhyNoTransTxByte jmp	@_PhyNoTransTxByte
PhyRxTest	jmp	@_PhyRxTest

ModemConnect	jmp	@_ModemConnect

GetByte		jmp	@_GetByte
SendByte	jmp	@_SendByte
SerialInit	jmp	@_SerialInit

IFDEF DEBUG
DebugSendByte	jmp	@_DebugSendByte
DebugSerialISR	jmp	@_DebugSerialISR
ENDIF
 
IFDEF POP3DEMO
get_tens	jmp	@_get_tens
get_hundreds	jmp	@_get_hundreds  
times_10	jmp	@_times_10
ENDIF

IFDEF ADCDEMO
ADCSendWarning	jmp	@_ADCSendWarning
ENDIF 
                 


;
; Subroutine - Get byte via serial port
;
_GetByte	_bank	serial
:wait		mov	w,rx_ring_cnt		; Get the number of bytes in the rx ring
		snz				; Is the receive ring empty?
		jmp	:wait			; Yes, block until not empty
		mov	w,rx_ring_op		; Load the ring out pointer
		mov	fsr,w
		mov	w,indf			; Get character from buffer
		mov	uart_temp,w		; Save character
		_bank	serial
		ringadv	rx_ring_op,rx_ring,rx_ring_size	; Advance ring pointer
		dec	rx_ring_cnt		; Decrement rx char count
		snz
		clrb	StatusPort.LEDRx
		snz				; Is the count zero?
		clrb	cts_pin			; Yes. Set the CTS pin to restart the DTE.
		_bank	uart_rx_ring
		mov	w,uart_temp		; Return byte in W
		retp				

;
; Subroutine - Send byte via serial port
;
_SendByte	_bank    uart_tx_ring
		setb	StatusPort.LEDTx	; Set the traffic LED.
		mov	uart_temp,w		; Move the byte to the ring bank.
		_bank	serial
:wait		csne	tx_ring_cnt,#tx_ring_size	; Compare to the ring size
		jmp	:wait			; No, block until there is room
		mov	w,tx_ring_ip		; Get buffer pointer
		mov	fsr,w
		mov	w,uart_temp
		mov	indf,w			; Save temp in the ring
		_bank	serial			; Ensure we are using the serial variables
		ringadv	tx_ring_ip,tx_ring,tx_ring_size	; Advance ring pointer
		inc	tx_ring_cnt		; Increment tx char count
		retp                            ;leave and fix page bits

_SerialInit
		mov	FSR,#$10                 ;reset all ram starting at 10h
:zero_ram       CLR     IND                     ;clear using indirect addressing
		IJNZ    FSR,:zero_ram           ;repeat until done
		mov      rd,#%01000100		; The both UARTs use port D.
		mov     !rd,#%10110001		; Set RD in/out directions.
		_bank	serial
		clr	rx_ring_cnt		; The receive ring is empty.
		mov	w,#rx_ring
		mov	rx_ring_ip,w		; Set the in and out pointers to the start of
		mov	rx_ring_op,w		; the receive ring.
		clr	tx_ring_cnt		; The transmit ring is empty.
		mov	w,#tx_ring
		mov	tx_ring_ip,w		; Set the in and out pointers to the start of
		mov	tx_ring_op,w		; the transmit ring.
		mov     !option,#%10011111      ; Enable rtcc interrupt.
		clrb	cts_pin			; Raise CTS to start the DTE.
		retp


;===============================================================================
; Debugging code. A second UART VP is used to send debugging messages to a 
; terminal.  
;===============================================================================

IFDEF DEBUG 
; If we are debugging then enable a second UART for transmitting debug info.

debug_rx_pin	EQU     rd.0                    ;UART receive input
debug_tx_pin	EQU     rd.3                    ;UART transmit output

; *** 57600 baud
debug_baud_bit		=	2		
debug_start_delay	=	4+2+1	
debug_int_period	= 	217		
				
;
; Subroutine - Send byte via serial port
;
_DebugSendByte	
	_bank	debug_serial	
:wait	test    debug_tx_count		; Wait for not busy
	jnz     :wait
	not     w			; Ready bits (inverse logic)
	mov     debug_tx_high,w		; Store data byte
	setb    debug_tx_low.7		; Set up start bit
	mov     debug_tx_count,#10	; 1 start + 8 data + 1 stop bit
	retp
;
; Serial ISR for the debug UART
;
_DebugSerialISR	
	_bank	debug_serial		; Switch to serial register bank
:transmit
	clrb    debug_tx_divide.debug_baud_bit	; Clear xmit timing count flag
	inc     debug_tx_divide		; Only execute the transmit routine
	stz				; Set zero flag for test
	snb     debug_tx_divide.debug_baud_bit	; Every 2^baud_bit interrupt
	test    debug_tx_count		; Are we sending?
	JZ      :rxdone			; If not, go to :receive
	clc				; Yes, ready stop bit
	rr      debug_tx_high		; And shift to next bit
	rr      debug_tx_low
	dec     debug_tx_count		; Decrement bit counter
	movb    debug_tx_pin,/debug_tx_low.6	; Output next bit
	retp
:rxdone	retp


ENDIF


                 
;===============================================================================
; PPP subroutines
;===============================================================================

	org	$4

;-------------------------------------------------------------------------------
; Subroutine: PPPOpen

; Subroutine: PPPRxData
;
; Open a PPP connection to the peer. This entire routine must fit into a page
; and not cross any page boundaries. It must also be in the first half of a page.
;
; The same state machine is used to negotiate both the LCP and NCP (IPCP)
; parameters. The flags: PPPFlags.inLCP and PPPFlags.inIPCP indicate the
; current type of negotiation.
;
; W on entry: -
; W on exit : z is set to 1 if a packet contained IP data, 0 otherwise.
; Variables : -
;-------------------------------------------------------------------------------

_PPPOpen
	DEBUGP	$0B,0
	_bank	PPPVars
	mov	PPPFlags,#(1<<inLCP)		; Initialize the state.
initMachine
	setb	StatusPort.LED0		; Turn on the negotiation LED.
	mov	PPPState,#PPPStateReqSent	; Reset the state machine.
	mov	PPPRxState,#PPPStateFlag	; Reset the Rx state machine.
	mov	PPPRestartCount,#PPPRestartCountDefault
	call	@PPPSendConfReq		; Send a configure-request
_PPPRxData
	DEBUGP	$0E,0
:eventLoop				; Wait for an event
	_bank	PPPVars
	mov	PPPEvent,#PPP_NONE	; Assume there will be no event.
	call	@PhyRxTest		; See if any bytes in receive buffer.
	sz	
	jmp	:pollTimer		; No bytes waiting. Check timer.
	call	@PPPReceive		; Process the received byte.
	test	PPPEvent		; Check for zero.
	sz				; Was the event non-zero?		
	jmp	:stateJump		; Yes. Process it.
:pollTimer
	sb	PPPFlags.inIP
	jmp	:cont
	clz
	retp
	; Check if the restart timer has expired.
:cont	sb	PPPFlags.timerRunning	; Is the restart timer running?
	jmp	:timerEnd		; No.
	_bank	PPPTimer
	csae	PPPTimer3,#PPPRestartExpire	; Has the restart timer expired?
	jmp	:timerEnd		; No.
	clr	PPPTimer1		; Yes. Initialise the restart timer.
	clr	PPPTimer2
	clr	PPPTimer3
	_bank	PPPVars
	test	PPPRestartCount		; Is the restart count zero?
	sz
	mov	w,#PPP_TO_GOOD	; Signal the event.
	snz
	mov	w,#PPP_TO_BAD	; Signal the event.
	mov	PPPEvent,w
	jmp	:stateJump

:timerEnd
	_bank	PPPVars
	; Call application specific short routine here.
	jmp	:eventLoop		; Continue polling.

:stateJump
	mov	w,PPPState
	DEBUGW	$0D,0
	_bank	PPPVars
	add	PC,PPPState		; Find the state in the jump table.
	jmp	:Initial
	jmp	:Starting
	jmp	:Closed
	jmp	:Stopped
	jmp	:Closing
	jmp	:Stopping
	jmp	:ReqSent
	jmp	:AckRcvd
	jmp	:AckSent
	jmp	:Opened
	; Not reached. The routine returns from one of the states.

; At this point W holds the latest event.
:Initial	jmp	:eventLoop
:Starting	jmp	:eventLoop
:Closed		jmp	:eventLoop
:Stopped	jmp	:eventLoop
:Closing	jmp	:eventLoop
:Stopping	jmp	:eventLoop

; State 6 ============================================================
:ReqSent	
	cje	PPPEvent,#PPP_RCR_BAD,:6ConfigureReject
	cje	PPPEvent,#PPP_RCR_GOOD,:6ConfigureReq
	cje	PPPEvent,#PPP_RCA,:6ConfigureAck
	cje	PPPEvent,#PPP_TO_GOOD,:6TimerGood
	cje	PPPEvent,#PPP_TO_BAD,:7TimerBad
	mov	w,PPPEvent
	DEBUGW	$04,0
	jmp	:eventLoop
:6ConfigureReject
	; We received a configure request with unacceptable options.
	call	@PPPSendConfRej		; Send a configure reject.
	jmp	:eventLoop		; Stay in this state.
:6ConfigureReq
	; We received an acceptable configure request.
	call	@PPPSendConfAck		; Send a configure acknowledge.
	mov	PPPState,#PPPStateAckSent	; Switch states.
	jmp	:eventLoop
:6ConfigureAck
	; We received a configure acknowledgement for our request.
	; ?? Initialize-Restart-Count
	mov	PPPState,#PPPStateAckRcvd	; Switch states.
	jmp	:eventLoop 
:6TimerGood
	; The timer expired. Send the configure request packet again.
	call	@PPPSendConfReq		; Retransmit the confReq
	jmp	:eventLoop	
:6TimerBad
	DEBUGP	$12,0			; No peer.
	retp				; Return unsuccessful

; State 7 ============================================================
:AckRcvd
	cje	PPPEvent,#PPP_RCR_BAD,:7ConfigureReject
	cje	PPPEvent,#PPP_RCR_GOOD,:7ConfigureReq
	cje	PPPEvent,#PPP_RCA,:7ConfigureAck
	cje	PPPEvent,#PPP_TO_GOOD,:7TimerGood
	cje	PPPEvent,#PPP_TO_BAD,:7TimerBad
	mov	w,PPPEvent
	DEBUGW	$04,0
	jmp	:eventLoop
:7ConfigureReject
	; We received a configure request with unacceptable options.
	call	@PPPSendConfRej		; Send a configure reject.
	jmp	:eventLoop		; Stay in this state.
:7ConfigureReq
	; We received an acceptable configure request.
	call	@PPPSendConfAck		; Send a configure acknowledge.
	mov	PPPState,#PPPStateOpened	; Switch states.
	jmp	:Opened			; Jump straight to the state.
:7ConfigureAck
	; We received a configure acknowledgement but there was 
	; no request outstanding. Indicates some sort of problem.
	jmp	:eventLoop 
:7TimerGood
	; The timer expired. Send the configure request packet again.
	call	@PPPSendConfReq		; Retransmit the confReq
	mov	PPPState,#PPPStateReqSent	; Switch states.
	jmp	:eventLoop		
:7TimerBad
	DEBUGP	$12,0			; No peer.
	retp				; Return unsuccessful

; State 8 ============================================================
:AckSent
	cje	PPPEvent,#PPP_RCR_BAD,:8ConfigureReject
	cje	PPPEvent,#PPP_RCR_GOOD,:8ConfigureReq
	cje	PPPEvent,#PPP_RCA,:8ConfigureAck
	cje	PPPEvent,#PPP_TO_GOOD,:8TimerGood
	cje	PPPEvent,#PPP_TO_BAD,:8TimerBad
	mov	w,PPPEvent
	DEBUGW	$04,0
	jmp	:eventLoop
:8ConfigureReject
	; We received a configure request with unacceptable options.
	call	@PPPSendConfRej		; Send a configure reject.
	mov	PPPState,#PPPStateReqSent	; Switch states.
	jmp	:eventLoop		
:8ConfigureReq
	; We received an acceptable configure request.
	call	@PPPSendConfAck		; Send a configure acknowledge.
	jmp	:eventLoop		; Stay in this state.
:8ConfigureAck
	; We received a configure acknowledgement. Negotiation is 
	; complete.
	mov	PPPState,#PPPStateOpened	; Switch states.
	jmp	:Opened 			;Jump straight to the state.
:8TimerGood
	; The timer expired. Send the configure request packet again.
	call	@PPPSendConfReq		; Retransmit the confReq
	jmp	:eventLoop	
:8TimerBad
	DEBUGP	$12,0			; No peer.
	retp				; Return unsuccessful
	
; State 9 ============================================================
:Opened		
	clrb	PPPFlags.timerRunning	; Stop the restart timer.
	sb	PPPFlags.inLCP	; Are we currently doing LCP negotiation?
	jmp	:IPCP	; No. Try IPCP.
	; The LCP layer is now up. Start the network layer.	
	clrb	PPPFlags.inLCP	; We are finished with LCP negotiation.
	setb	PPPFlags.inIPCP	; Start the IPCP negotiation.
	jmp	initMachine	; Restart the state machine with IPCP.
:IPCP	
	sb	PPPFlags.inIPCP	; Are we currently doing IPCP negotiation?
	jmp	:IP		; No. Try IP
	; PPP with IP is now up.
	DEBUGW	$05,0
	_bank	PPPVars
	setb	PPPFlags.linkUp
	setb	StatusPort.LEDUp	; Light up the link up LED.
	nop	
	nop
	clrb	StatusPort.LED0		; Turn of the negotiation LED.
	setb	PPPFlags.inIP
	clrb	PPPFlags.inIPCP
	retp			; The link is up, return successfully.
:IP
	sb	PPPFlags.inIP	; Is an IP connection open?
	jmp	:error	; No. Report an error.
	cje	PPPEvent,#PPP_DATA,:data	; Was the event IP data?
	cje	PPPEvent,#PPP_RTR,:TermReq	; Was the event a terminate request?
	clrb	z
	retp
:data	setb	z	
	retp			; Return.
:TermReq
	mov	PPPFlags,#0	; Signal the link is down.
	clrb	StatusPort.LEDUp
	clrb	z
	retp

:error	setb	StatusPort.LEDErr
	DEBUGP	$11,0
	jmp	:error	

;-------------------------------------------------------------------------------
; Subroutine: PPPInit
;
; Initialize the PPP layer.
;
; W on entry: -
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------

_PPPInit
	_bank	PPPVars
	mov	PPPState,#PPPStateInitial ; Reset the state machine.
	retp

;-------------------------------------------------------------------------------
; Subroutine: PPPSendConfReq 
;
; Send a configure-request packet to the peer. This packet contains the LCP
; options that we wish to negotiate.
;
; W on entry: -
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------
	
_PPPSendConfReq
	_bank	PPPTimer		
	mov	w,#0
	mov	PPPTimer1,w		; Initialise the restart timer.
	mov	PPPTimer2,w
	mov	PPPTimer3,w                                            
	_bank	PPPVars
	dec	PPPRestartCount		; Decrement the restart count.
	setb	PPPFlags.timerRunning	; Start the restart timer.
	snb	PPPFlags.inLCP			; Are we in LCP negotiation?
	mov	w, #_PPPConfReqPacketLCP&255	; Load the packet offset
	snb	PPPFlags.inIPCP			; Are we in IPCP negotiation?
	mov	w, #_PPPConfReqPacketIPCP&255	; Load the packet offset
	jmp	@PPPSendPacket	; Send the packet.
	; Return directly from PPPSendPacket

;-------------------------------------------------------------------------------
; Subroutine: PPPStartIPPacket
;
; Send an IP packet header.
;
; W on entry: -
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------
	
_PPPStartIPPacket
	mov	w, #_PPPIPPacket&255	; Load the packet offset
	jmp	@PPPSendPartialPacket	; Send the packet.
	; Return directly from PPPSendPacket


;-------------------------------------------------------------------------------
; Subroutine: PPPSendTermReq
;
; Send a terminate-request packet to the peer. This packet tells the peer we
; are closing the link.
;
; W on entry: -
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------
	
_PPPSendTermReq
	snb	PPPFlags.inLCP			; Are we in LCP negotiation?
	mov	w, #_PPPTermReqPacketLCP&255	; Load the packet offset
	snb	PPPFlags.inIPCP			; Are we in IPCP negotiation?
	mov	w, #_PPPTermReqPacketIPCP&255	; Load the packet offset
	jmp	@PPPSendPacket	; Send the packet.
	; Return directly from PPPSendPacket

;-------------------------------------------------------------------------------
; Subroutine: PPPSendPacket
;
; Send a canned packet. Scratch0 holds the address of the next byte. 
;
; W on entry: start address of the packet to send
; W on exit : -
; Variables : Scratch0, Scratch1
;-------------------------------------------------------------------------------
	
_PPPSendPacket
	call	@PPPSendPartialPacket
	jmp	@PPPClosePacket
	
;-------------------------------------------------------------------------------
; Subroutine: PPPXxFCSInit 
;
; Initialize the frame check sequence.
;
; W on entry: -
; W on exit : -
; Variables : PPPFCSh,PPPFCSl
;-------------------------------------------------------------------------------

_PPPRxFCSInit
	mov	w,#$ff			; Init FCS to $FFFF
	mov	PPPRxFCSh,w		
	mov	PPPRxFCSl,w	
	retp

_PPPTxFCSInit
	mov	w,#$ff			; Init FCS to $FFFF
	mov	PPPTxFCSh,w		
	mov	PPPTxFCSl,w	
	retp

;-------------------------------------------------------------------------------
; Subroutine: PPPCheckFCS
;
; Check that the FCS for a PPP frame is valid. The two FCS bytes must be the 
; next bytes to be received.
;
; W on entry: -
; W on exit : z is set to 1 if the frame is valid, 0 otherwise.
; Variables : -
;-------------------------------------------------------------------------------

_PPPCheckFCS
	call	@PhyRxByte	; Receive the FCS.
	call	@PhyRxByte
	_bank	PPPVars
	cse	PPPRxFCSh,#PPPRxFCSh	; Test the upper byte.
	jmp	:invalid
	cse	PPPRxFCSl,#PPPRxFCSl	; Test the lower byte.
	jmp	:invalid
	setb	z
	retp
:invalid
	clrb	z
	retp

;-------------------------------------------------------------------------------
; Subroutine: PPPFCSData
;
; Accumulate a byte for the FCS. The FCS is computed a byte at a time as the 
; data is transmitted.
;
; W is preserved by this routine.
;
; W on entry: data byte
; W on exit : data byte
; Variables : Scratch1,PPPFCSl,PPPFCSh,PPPFCSA
;-------------------------------------------------------------------------------

_PPPTxFCSData
	_bank	PPPVars
	mov	Scratch1,w			;Save W
	xor	PPPTxFCSl,w			;FCSl[=X] = FCSl xor w
	mov	w,<>PPPTxFCSl			;w = FCSl[32107654]
	and	w,#%11110000			;w = FCSl[3210oooo]
	xor	PPPTxFCSl,w			;FCSl = FCSl xor (FCSl shl 4)
						;Calculate A = FCSh
	mov	w,<>PPPTxFCSl			;w = FCSl[32107654]
	mov	PPPFCSA,w			;A = FCSl[32107654]
	mov	w,>>PPPFCSA			;w = FCSl[x3210765]
	and	w,#%00000111			;w = FCSl[ooooo765]
	xor	w,PPPTxFCSl			;w = FCSl xor (FCSl shr 5)
	mov	PPPFCSA,w			;store w into A = new FCSh value
						;Calculate new FCSl value
	rl	PPPTxFCSl	
	rl	PPPTxFCSl	
	mov	w,<<PPPTxFCSl			;w = FCSl[43210xxx]
	and	w,#%11111000			;w = FCSl[43210ooo]
	xor	w,PPPTxFCSh			;w = (FCSl shl 3) xor FCSh
	mov	PPPTxFCSl,w			;Store w into FCSl
	mov	w,<>PPPFCSA			;w = A[32107654]
	and	w,#%00001111			;w = A[oooo7654]
	xor	PPPTxFCSl,w			;FCSl = (FCSl shl 3) xor FCSh xor (A shr 4)
						;Store new FCSh value
	mov	w,PPPFCSA			;A holds FCSh value
	mov	PPPTxFCSh,w			;Store A in FCSh
	mov	w,Scratch1
	retp

_PPPRxFCSData
	mov	Scratch1,w			;Save w.
;	DEBUGW	$70,indent
	mov	w,Scratch1			;Restore w
	_bank	PPPVars
	xor	PPPRxFCSl,w			;FCSl[=X] = FCSl xor w
	mov	w,<>PPPRxFCSl			;w = FCSl[32107654]
	and	w,#%11110000			;w = FCSl[3210oooo]
	xor	PPPRxFCSl,w			;FCSl = FCSl xor (FCSl shl 4)
						;Calculate A = FCSh
	mov	w,<>PPPRxFCSl			;w = FCSl[32107654]
	mov	PPPFCSA,w			;A = FCSl[32107654]
	mov	w,>>PPPFCSA			;w = FCSl[x3210765]
	and	w,#%00000111			;w = FCSl[ooooo765]
	xor	w,PPPRxFCSl			;w = FCSl xor (FCSl shr 5)
	mov	PPPFCSA,w			;store w into A = new FCSh value
						;Calculate new FCSl value
	rl	PPPRxFCSl	
	rl	PPPRxFCSl	
	mov	w,<<PPPRxFCSl			;w = FCSl[43210xxx]
	and	w,#%11111000			;w = FCSl[43210ooo]
	xor	w,PPPRxFCSh			;w = (FCSl shl 3) xor FCSh
	mov	PPPRxFCSl,w			;Store w into FCSl

	mov	w,<>PPPFCSA			;w = A[32107654]
	and	w,#%00001111			;w = A[oooo7654]
	xor	PPPRxFCSl,w			;FCSl = (FCSl shl 3) xor FCSh xor (A shr 4)
						;Store new FCSh value
	mov	w,PPPFCSA			;A holds FCSh value
	mov	PPPRxFCSh,w			;Store A in FCSh
	mov	w,Scratch1			;Restore w
	retp

;-------------------------------------------------------------------------------
; Canned PPP packets. The packet is terminated with a word with $f in the high nibble.
;-------------------------------------------------------------------------------

_PPPCannedPackets	=	$
_PPPConfReqPacketLCP	dw	$FF, $03, $C0, $21, $01, $01, $00, $04 + $f00
_PPPCodeRejPacketLCP	dw	$FF, $03, $C0, $21, $07, $01 + $f00
_PPPConfRejPacketLCP	dw	$FF, $03, $C0, $21, $04 + $f00
_PPPConfAckPacketLCP	dw	$FF, $03, $C0, $21, $02 + $f00
_PPPTermReqPacketLCP	dw	$FF, $03, $C0, $21, $05, $01, $00, $04 + $f00

_PPPConfReqPacketIPCP	dw	$FF, $03, $80, $21, $01, $01, $00, $0A, $03, $06, IPAddress1, IPAddress2, IPAddress3, IPAddress4 + $f00
_PPPCodeRejPacketIPCP	dw	$FF, $03, $80, $21, $07, $01 + $f00
_PPPConfRejPacketIPCP	dw	$FF, $03, $80, $21, $04 + $f00
_PPPConfAckPacketIPCP	dw	$FF, $03, $80, $21, $02 + $f00
_PPPTermReqPacketIPCP	dw	$FF, $03, $80, $21, $05, $01, $00, $04 + $f00

_PPPIPPacket		dw	$FF, $03, $00, $21 + $f00



IFDEF POP3DEMO
_POP3CannedPackets	=	$
_POP3USER	dw	'USER eSX',CharCR,CharLF + $f00
_POP3PASS	dw	'PASS eSX',CharCR,CharLF + $f00
_POP3STAT	dw	'STAT',CharCR,CharLF + $f00

; the zero is added in the following 2 canned messages so that the AppBytesToSend routine has the correct count
_POP3RETR	dw	'RETR ',0,0,0,CharCR,CharLF + $f00
_POP3DELE	dw	'DELE ',0,0,0,CharCR,CharLF + $f00

_POP3QUIT	dw	'QUIT',CharCR,CharLF + $f00
_POP3NONE	dw	$f00

ENDIF
	


;-------------------------------------------------------------------------------
; Subroutine: PPPReceive
;
; Receive a packet a byte at a time using a state machine. If the received byte
; indicates an event then PPPEvent is set.
;
; W on entry: -
; W on exit : Received byte (when receiving data).
; Variables : SCRATCH0,PPPRxState,PPPEvent
;-------------------------------------------------------------------------------
	org	$200

_PPPReceive
	_bank serial
	sb 	flags.rx_over
	jmp	:go	
	DEBUGP	$13,0		; UART buffer overflow.
	_bank	PPPVars
	clrb	flags.rx_over

:go

	call	@PhyRxByte	; Receive a byte.
	mov	Scratch0,w	; Save the byte.
	DEBUGW	$03,1
	_bank	PPPVars
	mov	w,PPPRxState	; Load the receiver state.
	DEBUGW	$07,1	
	_bank	PPPVars
	jmp	@:tableStart
	
	; The jump table must not cross a 256-word boundary.
:tableStart
	jmp	PC+W		; Jump into the table.
	jmp	:Flag
	jmp	:Address
	jmp	:Control
	jmp	:Proto1
	jmp	:Proto2
	jmp	:LCPCode
	jmp	:LCPID
	jmp	:LCPLen1
	jmp	:LCPLen2
	jmp	:Data
	jmp	:FCS1
	jmp	:FCS2	

:Flag
	cse	SCRATCH0,#PPPFlag	; We expect the flag
	retp		; Didn't get it
	mov	PPPRxState,#PPPStateAddress; Goto the next state.
	call	@PPPRxFCSInit		; Initialize the FCS.
	DEBUGP	$0C,0
	_bank	PPPVars
	retp
:Address
	cje	SCRATCH0,#PPPAddress,:GotAddress	; We expect the address
	cje	SCRATCH0,#PPPFlag,:GotFlag	; Did we get a flag?
	jmp	:resetRx	; Didn't get it
:GotAddress
	mov	PPPRxState,#PPPStateControl	; Goto the next state.
:GotFlag					; Stay in this state.
	retp

:Control
	cse	SCRATCH0,#PPPControl	; We expect the address
	jmp	:resetRx	; Didn't get it
	mov	PPPRxState,#PPPStateProto1	; Goto the next state.
	retp
:Proto1
	mov	PPPProto1,SCRATCH0	; Save the first byte of the protocol.
	mov	PPPRxState,#PPPStateProto2
	retp

:Proto2
	cje	PPPProto1,#PPPLCPPrefix,:ProtoLCPPrefix
	cje	PPPProto1,#PPPIPCPPrefix,:ProtoIPCPPrefix
	cje	PPPProto1,#PPPIPPrefix,:ProtoIP
	; Unknown protocol.
	mov	PPPRxState,#PPPStateFlag	; Reset the receive state machine.
	; Silently discard the packet. The discard is done by restarting the state machine
	; and assuming it won't resynchronize until the next legitimate packet.
	retp
:ProtoLCPPrefix
	cje	SCRATCH0,#PPPLCP,:ProtoLCP
	;cje	...
	; Unknown protocol.
	mov	PPPRxState,#PPPStateFlag	; Reset the receive state machine.
	; Silently discard the packet.
	retp
:ProtoLCP	
	mov	PPPRxState,#PPPStateLCPCode	; Goto the next state
	retp
:ProtoIPCPPrefix
	cje	SCRATCH0,#PPPLCP,:ProtoIPCP
	;cje	...
	; Unknown protocol.
	mov	PPPRxState,#PPPStateFlag	; Reset the receive state machine.
	; Silently discard the packet.
	retp
:ProtoIPCP
	sb	PPPFlags.inIPCP		; Are we doing IPCP?
	jmp	:ResetRx		; No. Discard the packet.	
	mov	PPPRxState,#PPPStateLCPCode	; Goto the next state
	retp
:ProtoIPPrefix
	cje	SCRATCH0,#PPPIP,:ProtoIP
	; Unknown protocol.
	mov	PPPRxState,#PPPStateFlag	; Reset the receive state machine.
	; Silently discard the packet.
	retp

:ProtoIP
	mov	PPPEvent,#PPP_DATA
	mov	PPPRxState,#PPPStateFlag	; Restart the recevie state machine.
	retp

:LCPCode
	cje	SCRATCH0,#PPPConfigureRequest,:PPPConfigureRequest
	cje	SCRATCH0,#PPPConfigureAck,:PPPConfigureAck
	cje	SCRATCH0,#PPPTerminateRequest,:PPPTermReq
	mov	PPPDelayEvent,#PPP_RUC	; By default we assume it was an unknown code
	mov	PPPRxState,#PPPStateLCPID	; Goto the next state
	retp
:PPPConfigureRequest
	; We received a configure request. Since we don't accept any options we 
	; need to see if any options are included. For now assume it is bad.
	mov	PPPDelayEvent,#PPP_RCR_BAD	
	mov	PPPRxState,#PPPStateLCPID	; Goto the next state
	retp
:PPPConfigureAck
	; We received a configure acknowledge. Since we negotiate no options it 
	; should be empty. 
	mov	PPPDelayEvent,#PPP_RCA	
	mov	PPPRxState,#PPPStateLCPID	; Goto the next state
	retp
:PPPTermReq
	mov	PPPDelayEvent,#PPP_RTR	
	mov	PPPRxState,#PPPStateLCPID	; Goto the next state
	retp

:LCPID
	mov	PPPIdentifier,Scratch0		; Save the identifier
	mov	PPPRxState,#PPPStateLCPLen1	; Goto the next state
	retp
:LCPLen1
	mov	PPPLengthh,Scratch0		; Save the length
	mov	PPPRxState,#PPPStateLCPLen2	; Goto the next state
	retp
:LCPLen2
	mov	PPPLengthl,Scratch0		; Save the length
	mov	PPPRxState,#PPPStateData	; Goto the next state
	mov	PPPEvent,PPPDelayEvent		; Now send the delayed event.
	cje	PPPEvent,#PPP_RCR_BAD,:CheckLength ; Make sure the confReq is really unacceptable	
	retp
:CheckLength
	cse	PPPLengthl,#4		; Is the LSB of the length 4?			
	jmp	:unacc			; No, now check if the option is really unacceptable.
	test	PPPLengthh		; Is the MSB zero?
	sz
	retp				; No, we're done.
	mov	PPPEvent,#PPP_RCR_GOOD	; We received a length 4 confReq. It is good.
	retp

:unacc	
	; The option appears unacceptable. However... we must accept the IPCP Address option
	; from Windows 95 or else it complains that we can't do IP. So...
	sb	PPPFlags.inIPCP		; Are we doing IPCP?
	retp				; No, return.

; ## Assume the packet is good if we are in IPCP.
IFNDEF WIN98
	cse	PPPLengthl,#10		; Is the length 10?
	retp				; No, it can't be an address option only.
ENDIF

	mov	PPPEvent,#PPP_NONE	; Yes, Signal no event, we need to wait for the option.	
	setb	PPPFlags.addressOption	; Provisionally mark the event as good.
	retp

:Data	
	sb	PPPFlags.inIPCP		; Are we doing IPCP?
	jmp	:ResetRx		; No, gobble the packet.
	sb	PPPFlags.addressOption	; Are we processing a potential good request
	jmp	:ResetRx		; No
	csne	Scratch0,#PPPEscape	; Is it the escape character?
	retp					; Get more data.
	cse	Scratch0,#PPPAddressOption	; Is it the address option?
	jmp	:ResetRx			; No, discard the rest of the packet.
	mov	PPPEvent,#PPP_RCR_GOOD	; Signal a good conf-req.
	retp			
	
	; All of these cause the state machine to restart and gobble data up to	
	; the start of the next packet.
:FCS1
:FCS2

:ResetRx
	clrb	PPPFlags.addressOption
	mov	PPPRxState,#PPPStateFlag	; Restart the receive state machine.
	retp

;-------------------------------------------------------------------------------
; Subroutine: PPPClosePacket
;
; Close a packet by sending the FCS and flag character.
;
; W on entry: -
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------

_PPPClosePacket
	_bank	PPPVars
	mov	w,/PPPTxFCSl	; Send the complemented FCS LSB first.
	call	@PhyTxByteNoFCS	; Transmit it over the physical layer.
	_bank 	PPPVars
	mov	w,/PPPTxFCSh	
	call	@PhyTxByteNoFCS	
	mov	w,#PPPFlag	; Send the flag character
	call	@PhyNoTransTxByte	; Transmit without transparency.
	_bank	PPPVars
	retp
	
;-------------------------------------------------------------------------------
; Subroutine: PPPClose
;
; Close the open PPP connection.
;
; W on entry: -
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------

_PPPClose
	_bank	PPPVars
	call	@PPPSendTermReq		; Send the terminate request.
	clrb	PPPFlags.inIPCP		; Switch back to LCP mode.
	setb	PPPFlags.inLCP		
	call	@PPPSendTermReq		; Send the terminate request.
	; We don't bother waiting for a reply
	clrb	PPPFlags.linkUp
	DEBUGW	$06,0
	retp

;-------------------------------------------------------------------------------
; Subroutine: PPPSendCodeReject 
;
; Send a code-reject packet to the peer. The information field of the packet
; contains the received packet, starting at the information field, and not 
; including the FCS.
;
; W on entry: -
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------
	
_PPPSendCodeRej
	snb	PPPFlags.inLCP			; Are we in LCP negotiation?
	mov	w, #_PPPCodeRejPacketLCP&255	; Load the packet offset
	snb	PPPFlags.inIPCP			; Are we in IPCP negotiation?
	mov	w, #_PPPCodeRejPacketIPCP&255	; Load the packet offset
	call	@PPPSendPartialPacket	; Send the first part of the packet.
	_bank	PPPVars			; Set the bank
	clc
	mov	w,#4			; The extra header adds 4 to the length
	add	PPPLengthl,w		; Add the length
	snc				; Was there a carry?
	inc	PPPLengthh		; Yes, increment the high byte
	mov	w,PPPLengthh		; Load the length
	mov	w,PPPLengthh		; Load the length	
	call	@PhyTxByte
	mov	w,PPPLengthl		; Load the length
	call	@PhyTxByte

	; Send the received packet.
		
	jmp	@PPPClosePacket		; Send the FCS and flag character.

;-------------------------------------------------------------------------------
; Subroutine: PPPSendConfAck 
;
; Send a configure-acknowledge packet to the peer. The packet must contain
; the same identifier as the packet being acknowledged.
;
; W on entry: -
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------
	
_PPPSendConfAck
	DEBUGP	$10,0
	_bank	PPPVars
	snb	PPPFlags.inLCP			; Are we in LCP negotiation?
	mov	w, #_PPPConfAckPacketLCP&255	; Load the packet offset
	snb	PPPFlags.inIPCP			; Are we in IPCP negotiation?
	mov	w, #_PPPConfAckPacketIPCP&255	; Load the packet offset
	call	@PPPSendPartialPacket
	_bank	PPPVars			; Set the bank
	mov	w,PPPIdentifier		; Load the ID from the received packet.
	call	@PhyTxByte		; Transmit.
	_bank	PPPVars
	mov	w,PPPLengthh		; Load the length from the received packet.
	call	@PhyTxByte		; Transmit.
	_bank	PPPVars
	mov	w,PPPLengthl		; Load the ID from the received packet.
	call	@PhyTxByte		; Transmit.
	; Now use the length to count in the received bytes and retransmit them.
	; First subtract the header length from the length.
	_bank 	PPPVars
	mov	w,#4-1			; Load the header length.
	sub	PPPLengthl,w		; Subtract from the LSB
	sc
	dec	PPPLengthh
	mov	w,#0			; Load the MSB of the header length
	sub	PPPLengthh,w		; Subtract the MSB	
	; If this is an address option then send the option.
	sb	PPPFlags.addressOption	; Is it an address option?
	jmp	:midloop			; No.
	mov	w,#PPPAddressOption
	call	@PhyTxByte		; Send it
	_bank	PPPVars
	dec	PPPLengthl		; Decrement the length by 1.
	clrb	PPPFlags.addressOption	; Clear the address option flag.
	jmp	:midloop
	; Now loop for each received byte.
:loop	call	@PhyRxByte		; Receive a byte.
	call	@PhyTxByte		; Retransmit the byte.
	_bank 	PPPVars
:midloop dec	PPPLengthl		; Decrement the count.
	sz				; Is it zero?
	jmp	:loop			; No, loop again.
	test	PPPLengthh		; Is the MSB zero?	
	snz
	jmp	:done			; Yes, we're done.
	dec 	PPPLengthh		; No, decrement.
	dec	PPPLengthl
	jmp	:loop

:done	jmp	@PPPClosePacket		; Send the FCS and close the packet.
	; Return directly from PPPClosePacket

;-------------------------------------------------------------------------------
; Subroutine: PPPSendConfRej 
;
; Send a configure-reject packet. The rejected options fields are copied from 
; the received packet.
;
; The first portion of the packet, up to and including the Code, is canned 
; and sent as a string. The identifier and length are copied from the recieved
; packet. Then the options are copied from the received packet to the new packet.
;
; The remaineder of the received packet (FCS and flag) are discarded by the 
; next state.
;
; W on entry: -
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------
	
_PPPSendConfRej
	DEBUGP	$0F,0
	_bank	PPPVars
	snb	PPPFlags.inLCP			; Are we in LCP negotiation?
	mov	w, #_PPPConfRejPacketLCP&255	; Load the packet offset
	snb	PPPFlags.inIPCP			; Are we in IPCP negotiation?
	mov	w, #_PPPConfRejPacketIPCP&255	; Load the packet offset
	call	@PPPSendPartialPacket	; Send the first part of the packet.
	_bank	PPPVars			; Set the bank
	mov	w,PPPIdentifier		; Load the ID from the received packet.
	call	@PhyTxByte		; Transmit.
	_bank	PPPVars
	mov	w,PPPLengthh		; Load the length from the received packet.
	call	@PhyTxByte		; Transmit.
	_bank	PPPVars
	mov	w,PPPLengthl		; Load the ID from the received packet.
	call	@PhyTxByte		; Transmit.
	; Now use the length to count in the received bytes and retransmit them.
	; First subtract the header length from the length.
	_bank 	PPPVars
	mov	w,#4			; Load the header length minus 1.
	sub	PPPLengthl,w		; Subtract from the LSB
	sc
	dec	PPPLengthh
	mov	w,#0			; Load the MSB of the header length
	sub	PPPLengthh,w		; Subtract the MSB	
	; Now loop for each received byte.
:loop	call	@PhyRxByte		; Receive a byte.
	call	@PhyTxByte		; Retransmit the byte.
	_bank 	PPPVars
	dec	PPPLengthl		; Decrement the count.
	sz				; Is it zero?
	jmp	:loop			; No, loop again.
	test	PPPLengthh		; Is the MSB zero?	
	snz
	jmp	:done			; Yes, we're done.
	dec PPPLengthh			; No, decrement.
	dec PPPLengthl
	jmp	:loop
:done	jmp	@PPPClosePacket		; Send the FCS and flag character.

;-------------------------------------------------------------------------------
; Subroutine: PPPSendPartialPacket
;
; Send a canned packet without adding the frame check sequence. 
;
; W on entry: start address of the packet to send
; W on exit : -
; Variables : Scratch0, Scratch1
;-------------------------------------------------------------------------------
	
_PPPSendPartialPacket
	mov	Scratch0,w	; Save the start address.
	DEBUGW	$08,0
	mov	w,#PPPFlag	; Send the start flag
	call	@PhyNoTransTxByte	; Transmit without transparency.
	_bank 	PPPVars
	call	@PPPTxFCSInit	; Initialize the FCS

	mov	w,#0	
	mov	m,w	
:loop	mov	m, #(_PPPCannedPackets>>8)	; Load the mode register.
	mov	w,Scratch0	; Load the pointer
	iread			; Read the next byte.
	_bank	PPPVars
	call	@PhyTxByte	; Transmit it over the physical layer.
	_bank 	PPPVars
	mov	w,m		; Load the mode register.
	test	w
	sz			; If it is not zero then exit.
	jmp	:done		; We're done transmitting.
	inc	Scratch0	; Increment the pointer
	jmp	:loop		
:done
	retp

;-------------------------------------------------------------------------------
; Subroutine: ModemConnect
;
; Pretend that we are a modem so that Windows 95 Dialup Networking will talk to
; us. The strategy is simple:
;
; while there is input
;    if it starts with ATDT
;       send CONNECT
;       exit. PPP layer can start
;    else
;       send OK
;
; W on entry: -
; W on exit : -
; Variables : -
; Bank on exit : -
;-------------------------------------------------------------------------------

;	org	$f00

_ModemConnect
	call	@GetByte	; Load the first byte to prime the pump.
	mov	Scratch0,w	; Save the first byte.
	jmp	:loop
:ok	mov	w,#'O'			; Send OK.
	call	@SendByte
	mov	w,#'K'
	call	@SendByte
	mov	w,#13			; Send a carriage return	
	call	@SendByte 
:loop	mov	Scratch1,Scratch0	; Shift the bytes.
	call	@GetByte
	mov	Scratch0,w		; Save the second byte.
	cjne	Scratch1,#'A',:loop	; Did we get an 'A'?
	cjne	Scratch0,#'T',:loop	; Was it followed by a 'T'?
	call	@GetByte		; Get another byte

	mov	Scratch0,w
	cjne	Scratch0,#'D',:ok	; Did we get a 'D'?
	call	@GetByte		; Get another byte
	mov	Scratch0,w	
	cjne	Scratch0,#'T',:ok	; Did we get a 'T'?
	mov	w,#'C'			; Send CONNECT.
	call	@SendByte
	mov	w,#'O'	
	call	@SendByte
	mov	w,#'N'	
	call	@SendByte
	mov	w,#'N'		
	call	@SendByte
	mov	w,#'E'	
	call	@SendByte
	mov	w,#'C'	
	call	@SendByte
	mov	w,#'T'	
	call	@SendByte 
	mov	w,#13			; Send a carriage return	
	call	@SendByte 
	retp

  
IFDEF POP3DEMO
;-------------------------------------------------------------------------------
; Subroutine: AppInit
;
; Called once at startup to allow the application to initialise itself.
; 
; W on entry: -
; W on exit : -
;-------------------------------------------------------------------------------

_AppInit
	mov	w,#'I'
	POP3W

	_bank	POP3Vars
	mov	POP3State,#POP3StateClosed ; Start in the closed state.
	clr	POP3TxMsgNo
	clr 	POP3MsgEndFlag
	clr	POP3MsgSubSt		;start of Msg sub state
	clr	POP3MsgSubStLast
	clr	POP3RxCount
	clr	POP3TxCount
	mov	POP3TxPointer,#_POP3NONE
	clr	POP3EOL
	_bank	IPVars
	mov	IPDestAddress1,#POP3Address1	; POP3 server IP address.
	mov	IPDestAddress2,#POP3Address2
	mov	IPDestAddress3,#POP3Address3
	mov	IPDestAddress4,#POP3Address4
	; Randomly choose our own port.	
	_bank	PPPTimer
	mov	w,PPPTimer2		; Base it on a timer.
	_bank	TCB
	mov	TCPLocalPortl,w
	mov	TCPLocalPorth,#$36
	mov	TCPRemotePorth,#POP3Porth ; Set the port to connect to.
	mov	TCPRemotePortl,#POP3Portl
	jmp	@_TCPActiveOpen		; Connect to the remote TCP.
ENDIF

	
;===============================================================================
; IP subroutines
;===============================================================================

	org	$400

;-------------------------------------------------------------------------------
; Subroutine: IPStartPacket
;
; Start transmitting an IP packet. First a physical layer header is transmitted
; followed by the IP header. Information about the packet destination and length
; is read from the IPVars bank variables.
;
; Comments on the IP header checksum:
;
; The checksum is computed over the header fields only and is the 16 bit 
; complement of the 16 bit ones complement sum. The checksum field is set to
; zero to compute the checksum. To simplify the checksum calculation all fields
; that are known a priori are calculated below:
;
; Version/IHL/TOS              4500
; Total length                    ?
; ID                              ?
; Flags/Fragment offset        0000
; TTL/Protocol                    ?
; Checksum                     0000
; Source Address               IPAddress1<<8 | IPAddress2
; Source Address               IPAddress3<<8 | IPAddress4
; Dest Address                    ?
;
;
ip_cs1	=	$4500 + (IPAddress1<<8 | IPAddress2) + (IPAddress3<<8 | IPAddress4)
if ip_cs1 > $10000
	ip_cs2	=	ip_cs1 + 1 - $10000
else
	ip_cs2	=	ip_cs1
endif

;
; W on entry: -
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------

_IPStartPacket
	call	@PPPStartIPPacket		; Send a PPP IP packet header.
	_bank	IPVars
	mov	IPChecksumMSB,#(ip_cs2>>8)&$00ff	; Initialize the checksum.
	mov	IPChecksumLSB,#ip_cs2&$00ff
	; Increase the length by the header length.
	add	IPLengthLSB,#(IPIHL<<2)	; Add the header length times 4.
	snc
	inc	IPLengthMSB
	; Compute the checksum
	clrb	IPFlags.checksumBit
	mov	w,IPLengthMSB
	call	@IPChecksum
	mov	w,IPLengthLSB
	call	@IPChecksum
	mov	w,#0
	call	@IPChecksum
	mov	w,IPIDCounter
	call	@IPChecksum
	mov	w,#IPTTL
	call	@IPChecksum
	mov	w,IPProtocol
	call	@IPChecksum
	mov	w,IPDestAddress1
	call	@IPChecksum
	mov	w,IPDestAddress2
	call	@IPChecksum
	mov	w,IPDestAddress3
	call	@IPChecksum
	mov	w,IPDestAddress4
	call	@IPChecksum
	not	IPChecksumMSB
	not	IPChecksumLSB
	mov	w,#IPVIHL
	call	@PhyTxByte
	mov	w,#IPTOS
	call	@PhyTxByte
	_bank	IPVars
	mov	w,IPLengthMSB	; Send the MSB of the length
	call	@PhyTxByte	
	_bank	IPVars
	mov	w,IPLengthLSB	; Send the LSB of the length
	call	@PhyTxByte
	mov	w,#$00		; The identifier.
	call	@PhyTxByte
	_bank	IPVars	
	mov	w,IPIDCounter	; Load the counter before incrementing.
	inc	IPIDCounter	; Increment the counter.
	call	@PhyTxByte		
	mov	w,#IPFrag1	
	call	@PhyTxByte
	mov	w,#IPFrag2	
	call	@PhyTxByte
	mov	w,#IPTTL
	call	@PhyTxByte
	_bank	IPVars
	mov	w,IPProtocol	
	call	@PhyTxByte
	_bank	IPVars
	mov	w,IPChecksumMSB
	call	@PhyTxByte
	_bank	IPVars
	mov	w,IPChecksumLSB
	call	@PhyTxByte
	mov	w,#IPAddress1
	call	@PhyTxByte	
	mov	w,#IPAddress2
	call	@PhyTxByte
	mov	w,#IPAddress3
	call	@PhyTxByte
	mov	w,#IPAddress4
	call	@PhyTxByte
	_bank	IPVars
	mov	w,IPDestAddress1
	call	@PhyTxByte	
	_bank	IPVars
	mov	w,IPDestAddress2
	call	@PhyTxByte
	_bank	IPVars
	mov	w,IPDestAddress3
	call	@PhyTxByte
	_bank	IPVars
	mov	w,IPDestAddress4
	call	@PhyTxByte
	; There are no options.
	retp

;-------------------------------------------------------------------------------
; Subroutine: IPChecksum
;
; Accumulate the IP checksum.
;
; W on entry: The value of the byte to accumulate. 
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------

_IPChecksum
	_bank	IPVars
	sb	IPFlags.checksumBit	; Are we processing an MSB?
	jmp	:MSB		; Yes
	add	IPChecksumLSB,w	; Add it to the checksum 
	sc			; Was there a carry?
	jmp	:done
	inc	IPChecksumMSB	; Yes
	snz
	inc	IPChecksumLSB
	jmp	:done
:MSB	add	IPChecksumMSB,w	; Add it to the checksum 
	sc			; Was there a carry?
	jmp	:done
	inc	IPChecksumLSB	; Yes. This time it is added to the LSB.
	snz
	inc	IPChecksumMSB
:done	xor	IPFlags,#(1<<checksumBit) ; Flip the checksum bit.
	retp


;-------------------------------------------------------------------------------
; Subroutine: IPRxHeader
;
; Receive an IP packet header. If the header is not valid then the Z flag is set.
;
; W on entry: -
; W on exit : Z is set to 1 if the packet is invalid, 0 otherwise.
; Variables : -
;-------------------------------------------------------------------------------

_IPRxHeader
	_bank	IPVars
	call	@PhyRxByte	; Receive a byte.
	xor	w,#((IPVersion<<4)|IPIHL) ; We only accept packets with a HL of 5.
	sz
	jmp	:Invalid
	call	@PhyRxByte	; Ignore the type of service
	call	@PhyRxByte	
	mov	IPRxLengthMSB,w	; Save the packet length.
	call	@PhyRxByte
	mov	IPRxLengthLSB,w	; Save the packet length.
	call	@PhyRxByte	; Ignore the ID.
	call	@PhyRxByte	
	call	@PhyRxByte	
	and	w,#$20		; Are any fragment bits set?
	sz
	jmp	:Invalid	; Yes, discard the packet.
	call	@PhyRxByte
	test	w		; Is the fragment offset set?
	sz
	jmp	:Invalid	; Yes, discard the packet.	
	call	@PhyRxByte	; Ignore the TTL.
	call	@PhyRxByte	; Receive the protocol.
	_bank	IPVars
	mov	SCRATCH0,w
	csne	SCRATCH0,#IPProtocolICMP
	setb	IPFlags.ICMPPacket
	csne	SCRATCH0,#IPProtocolTCP
	setb	IPFlags.TCPPacket
	csne	SCRATCH0,#IPProtocolUDP
	setb	IPFlags.UDPPacket
	call	@PhyRxByte	; Live dangerously, Ignore the header checksum.	
	call	@PhyRxByte	
IFDEF	TCP
	; If a TCP connection is open then drop packets from any other IP address.
	_bank	TCPVars
	cse	TCPState,#TCPStateEstablished
	jmp	:notTCP
:inTCP
	_bank	IPVars
	call	@PhyRxByte
	xor	w,IPSrcAddress1 ; Verify that the source address is the same as the existing.
	sz
	jmp	:Invalid
	call	@PhyRxByte
	xor	w,IPSrcAddress2
	sz
	jmp	:Invalid
	call	@PhyRxByte
	xor	w,IPSrcAddress3
	sz
	jmp	:Invalid
	call	@PhyRxByte
	xor	w,IPSrcAddress4
	sz
	jmp	:Invalid	

	jmp	:ipAddressOK
ENDIF
:notTCP	_bank	IPVars
	call	@PhyRxByte
	mov	IPSrcAddress1,w	; Save the source address.
	call	@PhyRxByte
	mov	IPSrcAddress2,w	; Save the source address.
	call	@PhyRxByte
	mov	IPSrcAddress3,w	; Save the source address.
	call	@PhyRxByte
	mov	IPSrcAddress4,w	; Save the source address.
:ipAddressOK
	call	@PhyRxByte
	xor	w,#IPAddress1 	; Verify that the packet is for us.
	sz
	jmp	:Invalid
	call	@PhyRxByte
	xor	w,#IPAddress2 	; Verify that the packet is for us.
	sz
	jmp	:Invalid
	call	@PhyRxByte
	xor	w,#IPAddress3 	; Verify that the packet is for us.
	sz
	jmp	:Invalid
	call	@PhyRxByte
	xor	w,#IPAddress4 	; Verify that the packet is for us.
	sz
	jmp	:Invalid
	sub	IPRxLengthLSB,#(IPIHL<<2) ; Subtract the IP header length.
	sc
	dec	IPRxLengthMSB
	; That's the whole header.
	_bank	IPVars
	setb	IPFlags.anyPacket
	clrb	z
	retp			; Return successful.
:Invalid
	setb	z
	retp			; Return unsuccessful.

;-------------------------------------------------------------------------------
; Subroutine: IPReceivePacket
;
; Receive the next IP packet. This routine DOES NOT block if there is no packet 
; to receive. Bits are set in IPFlags acording to the packet type received. 
; The following actions are taken:
;
;    1. ICMP echo packet - send response, set echo flag (just for info purposes).
;    2. UDP - receive UDP header, set UDP flag.
;    3. Any other packet type - set Unknown flag.
;
; If the received packet is a UDP packet then the application can call IPRxData
; to receive the packet contents.
;
; After the packet has been processed by the application it should call IPCleanUp
; to ensure that any of the incoming data not used by the application is read from
; the physical layer.
;
; !!!! Actually this routine might block if an LCP packet is being received and
; it is not followed by an IP packet. !!!!
;
;
; W on entry: -
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------

_IPReceivePacket	
	_bank	IPVars
	clr	IPFlags		; Initialise the flags.
	call	@PhyRxTest	; Test if there is a byte in the physical layer.
	sz			; Is a byte waiting?
	retp			; No, return.
	call	@PPPRxData	; Read the PPP header.
	sz			; Was IP data received.
	retp			; No, just return.
	call	@IPRxHeader	; Receive the IP header.
	_bank	IPVars
	snb	IPFlags.ICMPPacket	; Is it an ICMP packet?
	jmp	:ICMP			; No, return.
	sz			; Is the packet valid?
	retp			; Yes. Return.
	jmp	_IPRxClosePacket ; No, Gobble it up. Yum, yum.

:ICMP	; Check if it is an echo request.
	call	@PhyRxByte
	xor	w,#ICMPEchoRequest	
	sz			; Is it an echo request?
	jmp	_IPRxClosePacket	; No, gobble the packet.
	; Send the ICMP echo reply packet.
	call	@PhyRxByte	; Ignore the Code field.
	_bank	IPVars
	mov	IPLengthMSB,IPRxLengthMSB
	mov	IPLengthLSB,IPRxLengthLSB	; Set the length to the received length.
	mov	IPProtocol,#IPProtocolICMP	; Set the protocol type to ICMP.
	mov	IPDestAddress1,IPSrcAddress1	; Copy the address.
	mov	IPDestAddress2,IPSrcAddress2	; !!!! This will only work if 
	mov	IPDestAddress3,IPSrcAddress3	; IPRxVars and IPVars are in the
	mov	IPDestAddress4,IPSrcAddress4	; same bank !!!!
	call	@IPStartPacket	; Send the IP packet header.
	mov	w,#ICMPEchoReply		; Send the ICMP type.
	call	@IPTxData
	mov	w,#0				; Send the code.
	call	@IPTxData
	call	@PhyRxByte	; Load the checksum.
	mov	IPChecksumMSB,w
	call	@PhyRxByte
	mov	IPChecksumLSB,w
	add	IPChecksumMSB,#8
	snc
	inc	IPChecksumLSB
	mov	w,IPChecksumMSB
	call	@PhyTxByte
	mov	w,IPChecksumLSB
	call	@PhyTxByte
	call	@PhyRxByte
	call	@PhyTxByte			; Send identifier.
	call	@PhyRxByte
	call	@PhyTxByte		
	call	@PhyRxByte
	call	@PhyTxByte			; Send sequence number.
	call	@PhyRxByte
	call	@PhyTxByte		
	; Send the data from the echo request.
	sub	IPLengthLSB,#(IPIHL<<2) + ICMPEchoHL
:data	call	@PhyRxByte
	call	@PhyTxByte
	decsz	IPLengthLSB
	jmp	:data

	call	@PPPClosePacket	; Close the echo reply packet.
	_bank	IPVars
	setb	IPFlags.echoPacket
	retp	

_IPRxClosePacket	; Ignore a packet by receiving everything up to the closing flag.
	_bank	IPVars	; Reset the flags.
	clr	IPFlags
	DEBUGP	$09,0
:loop	call	@PhyRxByte
	DEBUGW	$03,1	
	xor	w,#PPPFlag
	sz
	jmp	:loop
	; Since we just consumed a flag, move to the next PPP state to avoid missing
	; the next packet.
	_bank	PPPVars
	mov	PPPRxState,#PPPStateAddress
	call	@PPPRxFCSInit		; Initialize the FCS.
	retp

IFDEF UDP

;-------------------------------------------------------------------------------
; Subroutine: UDPStartPacket
;
; Transmit an IP header followed by a UDP header.
;
; W on entry: -
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------

_UDPStartPacket
	_bank	IPVars
	add	IPLengthLSB,#UDPHLength	; Increment the length by the UDP header size.
	snc
	inc	IPLengthMSB
	mov	IPProtocol,#IPProtocolUDP	; Set the protocol type to UDP.
	mov	Scratch0,IPLengthMSB
	mov	w,IPLengthLSB
	_bank	UDPVars
	mov	UDPLengthLSB,w		; Save the length for later.
	mov	UDPLengthMSB,Scratch0
	call	@IPStartPacket
	_bank	UDPVars
	mov	w,UDPSrcPorth		; Load the source port.
	call	@PhyTxByte
	mov	w,UDPSrcPortl	
	call	@PhyTxByte
	mov	w,UDPDestPorth		; Load the destination port.
	call	@PhyTxByte
	mov	w,UDPDestPortl	
	call	@PhyTxByte
	mov	w,UDPLengthMSB		; Reload the saved lengh.
	call	@PhyTxByte
	mov	w,UDPLengthLSB		; Reload the saved lengh.
	call	@PhyTxByte
	mov	w,#0			; Send zero for the checksum
	call	@PhyTxByte
	mov	w,#0		
	call	@PhyTxByte
	retp

;-------------------------------------------------------------------------------
; Subroutine: UDPRxHeader
;
; Receive a UDP header. To make it easy to reply to a packet the source
; port of the received packet is copied into UDPDestPort and the destination
; port is copied into UDPSrcPort.
;
; W on entry: -
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------

_UDPRxHeader
	call	@PhyRxByte	; Get the source port.	
	_bank	UDPVars
	mov	UDPDestPorth,w
	call	@PhyRxByte	
	mov	UDPDestPortl,w
	call	@PhyRxByte	; Get the destination port.	
	mov	UDPSrcPorth,w
	call	@PhyRxByte	
	mov	UDPSrcPortl,w
	call	@PhyRxByte	; Ignore the high byte of the length.	
	call	@PhyRxByte	; Get the low byte of the length.
	mov	UDPRxLength,w
	sub	UDPRxLength,#UDPHLength	; Subtract the length of the header.
	call	@PhyRxByte	; Ignore the checksum.
	call	@PhyRxByte
	retp
ENDIF

;===============================================================================
; TCP subroutines
;===============================================================================

;-------------------------------------------------------------------------------
; TCP helper macros.
;
; Macros to assist with manipulation and comparison of 32 bit values.
; Made more complex by the fact that values might be in different banks.
;
; Specify the LSB of each argument.
; Must be in the bank of A on entry.
; Bank might be changed on exit.
;-------------------------------------------------------------------------------

; A == B
; A <= B
; A + constant
; A = B

; Compare32 A,B	(returns with z bit set if A==B)

TCPCompare32 MACRO	2	
; SXKEY_CHANGE : Change the '==' in the next line to '='	
	IF \1>>4 == \2>>4 	; Are they in the same bank?
		mov	w,\1
		xor	w,\2
		sz
		jmp	:cdone
		mov	w,\1-1
		xor	w,\2-1
		sz
		jmp	:cdone
		mov	w,\1-2
		xor	w,\2-2
		sz
		jmp	:cdone
		mov	w,\1-3
		xor	w,\2-3  
	ELSE
		mov	w,\1	
	;	_bank	\2
			bank	\2

		IFDEF SX48_52
		  IFDEF SX48_52_ES
		    IF \2 & %00010000		;SX48BD/ES and SX52BD/ES (engineering sample) bank instruction
			setb	fsr.4		;modifies FSR bits 5,6 and 7. FSR.4 needs to be set by software.
		    ENDIF
		  ELSE
		    IF \2 & %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
		  ENDIF
		ENDIF
  
		xor	w,\2
		sz
		jmp	:cdone
	;	_bank	\1
		bank	\1

			IFDEF SX48_52
		  IFDEF SX48_52_ES
		    IF \1 & %00010000		;SX48BD/ES and SX52BD/ES (engineering sample) bank instruction
			setb	fsr.4		;modifies FSR bits 5,6 and 7. FSR.4 needs to be set by software.
		    ENDIF
		  ELSE
		    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
		  ENDIF
		ENDIF

		mov	w,\1-1
	;	_bank	\2
		bank	\2

		IFDEF SX48_52
		  IFDEF SX48_52_ES
		    IF \2 & %00010000		;SX48BD/ES and SX52BD/ES (engineering sample) bank instruction
			setb	fsr.4		;modifies FSR bits 5,6 and 7. FSR.4 needs to be set by software.
		    ENDIF
		  ELSE
		    IF \2 & %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
		  ENDIF
		ENDIF

		xor	w,\2-1
		sz
		jmp	:cdone
	;	_bank	\1
		bank	\1

		IFDEF SX48_52
		  IFDEF SX48_52_ES
		    IF \1 & %00010000		;SX48BD/ES and SX52BD/ES (engineering sample) bank instruction
			setb	fsr.4		;modifies FSR bits 5,6 and 7. FSR.4 needs to be set by software.
		    ENDIF
		  ELSE
		    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
		  ENDIF
		ENDIF

		mov	w,\1-2
	;	_bank	\2
			bank	\2

		IFDEF SX48_52
		  IFDEF SX48_52_ES
		    IF \2 & %00010000		;SX48BD/ES and SX52BD/ES (engineering sample) bank instruction
			setb	fsr.4		;modifies FSR bits 5,6 and 7. FSR.4 needs to be set by software.
		    ENDIF
		  ELSE
		    IF \2 & %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
		  ENDIF
		ENDIF

		xor	w,\2-2
		sz
		jmp	:cdone
	;	_bank	\1
		bank	\1

		IFDEF SX48_52
		  IFDEF SX48_52_ES
		    IF \1 & %00010000		;SX48BD/ES and SX52BD/ES (engineering sample) bank instruction
			setb	fsr.4		;modifies FSR bits 5,6 and 7. FSR.4 needs to be set by software.
		    ENDIF
		  ELSE
		    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
		  ENDIF
		ENDIF

		mov	w,\1-3
	;	_bank	\2
		bank	\2

		IFDEF SX48_52
		  IFDEF SX48_52_ES
		    IF \2 & %00010000		;SX48BD/ES and SX52BD/ES (engineering sample) bank instruction
			setb	fsr.4		;modifies FSR bits 5,6 and 7. FSR.4 needs to be set by software.
		    ENDIF
		  ELSE
		    IF \2 & %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
		  ENDIF
		ENDIF

		xor	w,\2-3	
	ENDIF
:cdone
ENDM

IFDEF TCP

;-------------------------------------------------------------------------------
; Subroutine: TCPPassiveOpen
;
; Do a passive open. I.e. listen for connections on a given port.
;
; W on entry: -
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------

_TCPPassiveOpen
	_bank	TCPVars
	mov	TCPState,#TCPStateListen
	clr	TCPOutstanding
	retp

;-------------------------------------------------------------------------------
; Subroutine: TCPActiveOpen
;
; Do a active open. I.e. initiate a connect to a remote TCP.
;
; W on entry: -
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------

_TCPActiveOpen
	_bank	TCB		
	mov	TCPFlags,#(1<<TCPFlagSYN)	; Make a SYN packet.	
	call	@TCPSendSyn
	_bank	TCPVars
	clr	TCPOutstanding
	mov	TCPState,#TCPStateSynSent
	retp

;-------------------------------------------------------------------------------
; Subroutine: TCPClose
;
; Force the current connection to close.
;
; W on entry: -
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------

_TCPClose
	_bank	TCPVars
	mov	TCPState,#TCPStateFinWait1
	clr	TCPOutstanding
	_bank	TCB
	mov	TCPFlags,#(1<<TCPFlagFIN)|(1<<TCPFlagACK) ; A FIN packet.
	call	@TCPSendEmptyHeader	; Send the header.
	call	@PPPClosePacket		; Close the packet.
	retp

;-------------------------------------------------------------------------------
; Subroutine: TCPRxHeader
;
; Receive a TCP header. 
;
; W on entry: -
; W on exit : Z is set to 1 if the packet is invalid, 0 otherwise.
; Variables : -
;-------------------------------------------------------------------------------

	org	$600

_TCPRxHeader
	; Check port and refuse packet if not for current connection.
	DEBUGP	$02,top		; TCP receive.
	_bank	TCPVars
	mov	w,TCPState
	DEBUGW	$14,top		; TCP state.
	_bank	TCPVars
	cjne	TCPState,#TCPStateListen,:CheckSrc
:readSrc			; Read the source port.
	_bank	TCB
	call	@PhyRxByte	; Get the source port.	
	mov	TCPRemotePorth,w
	call	@PhyRxByte	
	mov	TCPRemotePortl,w
	jmp	:checkDest
:checkSrc			; Check the source port is for the current connection.
	_bank	TCB
	call	@PhyRxByte	
	xor	w,TCPRemotePorth
	sz			; Is the high byte the same?
	jmp	:ignorePacket	; No, ignore the packet.
	call	@PhyRxByte	; Get the low byte.
	xor	w,TCPRemotePortl
	sz			; Is the low byte the same?
	jmp	:ignorePacket	; No, ignore the packet.
:checkDest
	call	@PhyRxByte	; Check the destination port matches our port.	
	xor	w,TCPLocalPorth
	sz			; Is the high byte the same?
	jmp	:ignorePacket	; No, ignore the packet.
	call	@PhyRxByte	; Get the low byte.
	xor	w,TCPLocalPortl
	sz			; Is the low byte the same?
	jmp	:ignorePacket	; No, ignore the packet.

	_bank	TCPVars
	call	@PhyRxByte	; Receive the sequence number
	mov	TCPTMP_SEQ4,w	; Save the sequence number in the TMP variable.
	call	@PhyRxByte	
	mov	TCPTMP_SEQ3,w
	call	@PhyRxByte	
	mov	TCPTMP_SEQ2,w
	call	@PhyRxByte	
	mov	TCPTMP_SEQ1,w	
	call	@PhyRxByte	; Receive the acknowledgement
	mov	TCPTMP_ACK4,w
	call	@PhyRxByte
	mov	TCPTMP_ACK3,w
	call	@PhyRxByte
	mov	TCPTMP_ACK2,w
	call	@PhyRxByte
	mov	TCPTMP_ACK1,w
	call	@PhyRxByte	; Receive the data offset. Used to skip the options.
	and	w,#TCPOffsetMask ; Mask out the offset.
	_bank	TCB
	mov	TCPOffset,w
	clc
	rr	TCPOffset	; Shift right to get the number of bytes.
	rr	TCPOffset
	mov	w,TCPOffset
	_bank	IPVars		; Decrease the total packet length
	sub	IPRxLengthLSB,w
	sc
	dec	IPRxLengthMSB
	_bank	TCB
	sub	TCPOffset,#(TCPHeaderLength<<2)-4 ; Subtract the standard header length (less the checksum and URG pointer.)
	call	@PhyRxByte	; Receive the flags.
	DEBUGW	$15,indent
	_bank	TCPVars
	mov	TCPRxFlags,w	; Take a copy of the flags.
	_bank	TCB
	mov	TCPFlags,w
	call	@PhyRxByte
	mov	TCP_WND_MSB,w	; Receive the window.
	call	@PhyRxByte
	mov	TCP_WND_LSB,w
	;	Skip over the options.
:optionLoop
	call	@PhyRxByte
	decsz	TCPOffset
	jmp	:optionLoop
	clz
	retp

:ignorePacket			; Ignore the rest of the packet.
	DEBUGP	$16,1
	call	@IPRxClosePacket
	; ## Need to send a reset in response to this packet.
	; ## Unfortunately sending a packet would overwrite the TCB.
	; ## Need to create a second TCB?
	setb	z
	retp

;-------------------------------------------------------------------------------
; Subroutine: TCPSendEmptyHeader,TCPSendHeader
;
; Send a TCP Header. Send empty header puts the checksum in the header so that
; a packet doesn't need to contain any data for a stuffed packet.
;
; W on entry: -
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------

_TCPSendEmptyHeader
	_bank	IPVars
	clr	IPLengthMSB
	clr	IPLengthLSB
	clr	TCPChecksumLSB	
	clr	TCPChecksumMSB
	clrb	IPFlags.checksumBit	; Indicate next checksum bit is an MSB.
_TCPSendHeader
	_bank	TCB
	mov	TCPOffset,#(TCPHeaderLength<<4)
	mov	TCP_WND_MSB,#(TCPWindow&$ff00)>>8
	mov	TCP_WND_LSB,#(TCPWindow&$00ff)
	_bank	IPVars
	clrb	IPFlags.checksumBit	; Indicate next checksum bit is an MSB.
	add	IPLengthLSB,#(TCPHeaderLength<<2) ; Add in the length of a TCP header.
	snc
	inc	IPLengthMSB
	mov	IPProtocol,#IPProtocolTCP	; Set the protocol type to TCP.
	call	@TCPInitChecksum
	call	@IPStartPacket		; Send the IP header.
	mov	Scratch0,#TCB		; Send the TCB fields.
:loop	mov	FSR,Scratch0
	mov	w,INDF			; Load the value.
	call	@TCPTxByte		; Transmit
	inc	Scratch0		
	cse	Scratch0,#TCBEnd	; Is the loop finished.
	jmp	:loop
	_bank	IPVars
	mov	w,TCPChecksumMSB
	not 	w
	call	@PhyTxByte		; Transmit the MSB of the checksum.
	mov	w,TCPChecksumLSB
	not	w
	call	@PhyTxByte		; Transmit the LSB of the checksum.
	mov	w,#0
	call	@PhyTxByte		; Transmit the urgent pointer.
	mov	w,#0
	call	@PhyTxByte
	retp

;-------------------------------------------------------------------------------
; Subroutine: TCPClosePacket
;
; Send the TCP checksum byte stuff and close the PPP packet.
;
; W on entry: -
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------

_TCPClosePacket = _PPPClosePacket

;-------------------------------------------------------------------------------
; Subroutine: TCPProcessPacket
;
; Process a received TCP packet. This function implements the TCP state machine.
;
; W on entry: -
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------

_TCPProcessPacket
	call	@TCPRxHeader		; Receive the header.
	snz				; Is the packet OK?
	retp				; No, return.
	_bank	TCPVars	
	cje	TCPState,#TCPStateClosed,:CLOSED	; Check the special states.
	cje	TCPState,#TCPStateListen,:LISTEN
	cje	TCPState,#TCPStateSynSent,:SYNSENT
	cje	TCPState,#TCPStateFinWait1,:FINWAIT1
	cje	TCPState,#TCPStateFinWait2,:FINWAIT2
	cje	TCPState,#TCPStateLastAck,:LASTACK
	; Otherwise...
	setb	StatusPort.LED1
	TCPCompare32	TCPTMP_SEQ1,TCPRCV_NXT1	; Check that RCV.NXT == SEG.SEQ
	sz					; Are they equal?
	jmp	@_TCPSendAck			; No, Send an ACK and drop packet.
	_bank	TCB
	; check the flags.
	snb	TCPFlags.TCPFlagRST	; Is the reset flag set?
	jmp	:gotoClosed		; Yes. Drop packet and close the connection.
	snb	TCPFlags.TCPFlagSYN	; Is the SYN bit set?
	jmp	@_TCPSendReset		; Yes. Drop the packet and send a reset.
	sb	TCPFlags.TCPFlagACK	; Is the ACK bit set?
	jmp	@_IPRxClosePacket	; No. Drop the packet.		

	; We only accept ACKs of complete packets. Assume the ACK is for our last packet.

	_bank	TCPVars
	mov	TCPState,#TCPStateEstablished	; Switch to the established state.

	; ## If we were in Syn-receieved then need to send an Ack.
	test	TCPOutstanding
	snz
	jmp	:noneOutstanding
	call	@AppAck			; Tell the application it was ACKed.
	_bank	TCPVars
	clr	TCPOutstanding		; There should be no data outstanding now.

:noneOutstanding
	; Check that the length is less than the window.
	; We always advertise a window < 256
	_bank	IPVars
	test	IPRxLengthMSB
	snz
	jmp	:winOK
;	DEBUGP	$1D,top
	jmp	@_TCPSendAck		; Window > 256

:winOK
	; Does the packet contain data? (Determine this from the length.)
	_bank	IPVars
	mov	IPRxLengthMSB,IPRxLengthLSB	; Save the length.
	test	IPRxLengthLSB
	snz
	jmp	:noData
	mov	w,IPRxLengthLSB
	call	@AppBytesAvailable
;	DEBUGP	$1A,top			; Report payload start.
:processData
	call	@PhyRxByte		;  Receive a byte.
;	DEBUGW	$19,indent		; Report that byte received.
	call	@AppRxByte		; Process the byte.

	_bank	IPVars			; Check whether to loop again.
	decsz	IPRxLengthLSB
	jmp	:processData
	inc	IPRxLengthLSB
:dataDone
;	DEBUGP	$1B,top			; Report payload end.
:noData
; ##  Check the PPP FCS
;	call	@PPPCheckFCS
;	sz				; Is the FCS OK?
;	jmp	:badFCS			; No. Send a debug message.

	call	@TCPAckUpdate
	; Send an ACK packet.
	_bank	IPVars
	test	IPRxLengthLSB		; Was Data received?
	snz
	jmp	:checkFIN		; No. It was an Ack packet. Just return.
	call	@AppPacketOK		; Indicate the packet was OK to the application.

	_bank	TCB
	mov	w,TCPFlags
	DEBUGW	$15,top		; TCP flags.

	_bank	TCPVars
	snb	TCPRxFlags.TCPFlagFIN	; Is the FIN bit set?
	jmp	:doClose		; Yes. Close the connection.
	jmp	@_TCPSendAck

:checkFIN
	_bank	TCPVars
	sb	TCPRxFlags.TCPFlagFIN	; Is the FIN bit set?
	retp				; No, just return.
:doClose
	DEBUGP	$28,top
	mov	w,#1			; Ack the fin.
	call	@TCPAddRCV_NXT
	_bank	TCPVars
	mov	TCPState,#TCPStateLastAck	; Change state.	
	jmp	@_TCPSendFin

:badFCS	call	@AppPacketBad
	DEBUGP	$18,top
	jmp	@_IPRxClosePacket	; Don't acknowledge the packet.

:gotoClosed
	DEBUGP	$1C,top
	_bank	TCPVars
	mov	TCPState,#TCPStateClosed	; Go to the closed state.
	jmp	@_IPRxClosePacket	; Ignore the incoming packet.

:FINWAIT1
	_bank	TCPVars
	mov	TCPState,#TCPStateFinWait2	; Continue closing.
	retp

:FINWAIT2
	_bank	TCPVars
	mov	TCPState,#TCPStateClosed	; Go to the closed state.
	mov	w,#1
	call	@TCPAddRCV_NXT			; ACK the FIN.
	jmp	@_TCPSendAck	
:LASTACK
	; Ignore the packet
	; ## Should check the packet is actually an ACK.
	mov	TCPState,#TCPStateClosed	; Go to the closed state.
	retp
:CLOSED
	jmp	@_TCPSendReset		; We shouldn't receive packets while closed.
:LISTEN
	call	@IPRxClosePacket	; Ignore the rest of the incoming packet.
	_bank	TCB
	snb	TCPFlags.TCPFlagRST	; Check for an RST
	retp				; Ignore a packet with RST.
	snb	TCPFlags.TCPFlagACK	; Check for an ACK
	jmp	@_TCPSendReset		; Bad ACK, send a RST.
	_bank	TCPVars			; Make RCV.NXT = SEG.SEQ+1
	mov	w,TCPTMP_SEQ4
	_bank	TCB
	mov	TCPRCV_NXT4,w
	_bank	TCPVars
	mov	w,TCPTMP_SEQ3
	_bank	TCB
	mov	TCPRCV_NXT3,w
	_bank	TCPVars
	mov	w,TCPTMP_SEQ2
	_bank	TCB
	mov	TCPRCV_NXT2,w
	_bank	TCPVars
	mov	w,TCPTMP_SEQ1
	_bank	TCB
	mov	TCPRCV_NXT1,w
	_bank	TCPVars
	mov	w,TCPTMP_SEQ3		; Initialise the initial segment sequence #.
	call	@TCPSendSynAck
	_bank	TCPVars
	mov	TCPState,#TCPStateSynReceived	; Change state.
	retp

:SYNSENT
	; Is the packet an Ack?
	_bank	TCB
	sb	TCPFlags.TCPFlagACK	; Is the ACK bit set?
	jmp	:noAck
	snb	TCPFlags.TCPFlagRST	; Is the reset bit set?
	jmp	:reset
	sb	TCPFlags.TCPFlagSYN	; Is the SYN bit set?
	jmp	:noAck			; No, Ignore the rest of the incoming packet.
		
	; ## Check that SND.UNA <= SEG.ACK <= SND.NXT

	DEBUGP	$20,top

	_bank	TCPVars
	mov	TCPState,#TCPStateEstablished	; The connection is now estabished.
	_bank	IPVars	
	clr	IPRxLengthLSB		; Set the length to one.
	mov	IPRxLengthMSB,#1
	call	@TCPAckUpdate
	jmp	@_TCPSendAck

:reset	DEBUGP	$1E,top
	mov	TCPState,#TCPStateClosed	; Close the TCP.
	jmp	@_IPRxClosePacket	; Ignore the rest of the incoming packet.

:noAck	; The peer wants us to raise the precedence. We can't.
	; We are not happy about not being Acked. Send a Reset.
	DEBUGP	$1F,top
	jmp	@_TCPSendReset	

_TCPAckUpdate
	_bank	TCPVars			; Set SND.UNA = SEG.ACK.
	mov	w,TCPTMP_ACK4
	_bank	TCB
	mov	TCPSND_UNA4,w
	_bank	TCPVars
	mov	w,TCPTMP_ACK3
	_bank	TCB
	mov	TCPSND_UNA3,w
	_bank	TCPVars
	mov	w,TCPTMP_ACK2
	_bank	TCB
	mov	TCPSND_UNA2,w
	_bank	TCPVars
	mov	w,TCPTMP_ACK1
	_bank	TCB
	mov	TCPSND_UNA1,w
					
	_bank	TCPVars			; Set RCV.NXT = SEG.SEQ
	mov	w,TCPTMP_SEQ4
	_bank	TCB
	mov	TCPRCV_NXT4,w
	_bank	TCPVars
	mov	w,TCPTMP_SEQ3
	_bank	TCB
	mov	TCPRCV_NXT3,w
	_bank	TCPVars
	mov	w,TCPTMP_SEQ2
	_bank	TCB
	mov	TCPRCV_NXT2,w
	_bank	TCPVars
	mov	w,TCPTMP_SEQ1
	_bank	TCB
	mov	TCPRCV_NXT1,w
	_bank	IPVars
	mov	w,IPRxLengthMSB
	jmp	@_TCPAddRCV_NXT		; Add the length of the received packet to the ACK.

;-------------------------------------------------------------------------------
; Subroutine: TCPTransmit
;
; See if the application has any data to transmit. If there are no outstanding
; packets and the application has data, then transmit a packet.
;
; W on entry: -
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------

	org	$800

_TCPTransmit
	_bank	TCPVars
	cjae	TCPState,#TCPStateSynSent,:ok	
	clrb	StatusPort.LED1
	retp
:ok	test	TCPOutstanding		
	sz			; Are there any bytes unacknowledged?
	jmp	:timeout	; Yes. Check for timeout.
	cjae	TCPState,#TCPStateFinWait1,:fintimeout	; Check for FIN timeout.
:retransmit
	call	@AppBytesToSend	; No. Ask the application if it wants to transmit.
	test	w
	snz			; Does the application wish to transmit?
	retp			; No. We're done here. Return.
	_bank	TCPVars
	mov	TCPOutstanding,w ; Save the number of bytes to transmit.
	_bank	IPVars
	; Compute the checksum over the data.
	mov	IPLengthLSB,w	
	clr	TCPChecksumMSB	; Initialize the checksum.
	clr	TCPChecksumLSB
	clrb	IPFlags.checksumBit	; Indicate the next bit is an MSB.
	DEBUGW	$26,top
:csloop	call	@AppTxByte
	DEBUGW	$2A,indent
	call	@TCPChecksum	; Accumulate the checksum
	_bank	IPVars
	decsz	IPLengthLSB
	jmp	:csloop
	call	@AppNak		; Reset the application.
	; Start a TCP packet.	
	_bank	TCPVars
	mov	w,TCPOutstanding
	_bank	IPVars
	mov	IPLengthLSB,w	
	clr	IPLengthMSB
	_bank	TCB	
	mov	TCPFlags,#(1<<TCPFlagPSH)|(1<<TCPFlagACK)
	call	@TCPSendHeader	; Send the header.
	_bank	TCPVars
	mov	w,TCPOutstanding
	_bank	IPVars
	mov	IPLengthLSB,w	
:dataloop			; Send the packet data.
	call	@AppTxByte
	call	@PhyTxByte	; Send the byte.
	_bank	IPVars
	decsz	IPLengthLSB
	jmp	:dataloop
	call	@TCPClosePacket	; End the packet.
	_bank	PPPTimer	; Initialise the restart timer.
	clr	PPPTimer1		
	clr	PPPTimer2
	clr	PPPTimer3
	retp
:fintimeout
	_bank	PPPTimer
	csae	PPPTimer3,#TCPRestartExpire	; Has the restart timer expired?
	jmp	:retransmit			
	clr	PPPTimer1		; Yes. Initialise the restart timer.
	clr	PPPTimer2
	clr	PPPTimer3
	jmp	@_TCPSendFin
:timeout
	_bank	PPPTimer
	csae	PPPTimer3,#TCPRestartExpire	; Has the restart timer expired?
	retp				; No
	clr	PPPTimer1		; Yes. Initialise the restart timer.
	clr	PPPTimer2
	clr	PPPTimer3
	DEBUGP	$24,top
	call	@AppNak			; Tell the application.
	jmp	:retransmit		; Transmit the packet again.

;-------------------------------------------------------------------------------
; Subroutine: TCPSendSyn
;
; Send an ACK packet with <SEQ=SND.NXT><ACK=RCV.NXT><CTL=SYN>
;
; W on entry: A random value to initialise the ISS.
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------

_TCPSendSynAck
	_bank	TCB	
	mov	TCPFlags,#(1<<TCPFlagSYN)|(1<<TCPFlagACK)	
	jmp	ISS
_TCPSendSyn
	DEBUGP	$22,top
	_bank	TCB	
	mov	TCPFlags,#(1<<TCPFlagSYN)
ISS	mov	TCPSND_UNA1,w		; packet to increase randomness.
	mov	TCPSND_UNA2,rtcc	
	mov	TCPSND_UNA3,rtcc
	mov	TCPSND_UNA4,rtcc
	mov	w,#1			; Add 1 to RCV.NXT
	call	@TCPAddRCV_NXT
	call	@TCPSendEmptyHeader	; Send the header.
	call	@PPPClosePacket		; Close the packet.
	mov	w,#1			; Add 1 to SND.NXT
	jmp	@_TCPAddSND_UNA

;-------------------------------------------------------------------------------
; Subroutine: TCPSendAck
;
; Send an ACK packet with <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK>
;
; W on entry: -
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------

_TCPSendAck
	DEBUGP	$23,top
	; Copy the acknowledgement number.
	_bank	TCB
	mov	TCPFlags,#(1<<TCPFlagACK) ; An ACK packet.
	call	@TCPSendEmptyHeader	; Send the header.
	call	@PPPClosePacket		; Close the packet.
	jmp	@_IPRxClosePacket	; Ignore the rest of the incoming packet.

;-------------------------------------------------------------------------------
; Subroutine: TCPSendFin
;
; Send a FIN packet
;
; W on entry: -
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------

_TCPSendFin
	DEBUGP	$29,top
	_bank	TCB
	mov	TCPFlags,#(1<<TCPFlagFIN)|(1<<TCPFlagACK) ; A FIN packet.
	call	@TCPSendEmptyHeader	; Send the header.
	call	@PPPClosePacket		; Close the packet.
	jmp	@_IPRxClosePacket	; Ignore the rest of the incoming packet.

;-------------------------------------------------------------------------------
; Subroutine: TCPSendReset
;
; Send a reset packet with <SEQ=SEG.ACK><CTL=RST>
;
; W on entry: -
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------

_TCPSendReset
	DEBUGP	$21,top
	; Copy the acknowledgement number.
	_bank	TCB
	mov	TCPFlags,#(1<<TCPFlagRST)	; A reset packet.
	_bank	TCPVars
	mov	w,TCPTMP_ACK4
	_bank	TCB
	mov	TCPSND_UNA4,w
	_bank	TCPVars
	mov	w,TCPTMP_ACK3
	_bank	TCB
	mov	TCPSND_UNA3,w
	_bank	TCPVars
	mov	w,TCPTMP_ACK2
	_bank	TCB
	mov	TCPSND_UNA2,w
	_bank	TCPVars
	mov	w,TCPTMP_ACK1
	_bank	TCB
	mov	TCPSND_UNA1,w
	call	@TCPSendEmptyHeader	; Send the header.
	call	@PPPClosePacket		; Close the packet.
	jmp	@_IPRxClosePacket	; Ignore the rest of the incoming packet.

;-------------------------------------------------------------------------------
; Subroutine: TCPInitChecksum
;
; Initialize the TCP checksum with the pseudo header fields.
;
; W on entry: - 
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------
ENDIF

IFDEF TCP

_TCPInitChecksum
	_bank	IPVars
	mov	w,#IPAddress1
	call	@TCPChecksum
	mov	w,#IPAddress2
	call	@TCPChecksum
	mov	w,#IPAddress3
	call	@TCPChecksum
	mov	w,#IPAddress4
	call	@TCPChecksum
	mov	w,#0
	call	@TCPChecksum	
	mov	w,#IPProtocolTCP
	call	@TCPChecksum	
	mov	w,IPDestAddress1
	call	@TCPChecksum
	mov	w,IPDestAddress2
	call	@TCPChecksum
	mov	w,IPDestAddress3
	call	@TCPChecksum
	mov	w,IPDestAddress4
	call	@TCPChecksum
	mov	w,IPLengthMSB
	call	@TCPChecksum
	mov	w,IPLengthLSB
	call	@TCPChecksum
	retp

;-------------------------------------------------------------------------------
; Subroutine: TCPChecksum
;
; Accumulate the TCP checksum.
;
; W on entry: The value of the byte to accumulate. 
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------

_TCPChecksum
	_bank	IPVars
	sb	IPFlags.checksumBit	; Are we processing an MSB?
	jmp	:MSB		; Yes
	add	TCPChecksumLSB,w	; Add it to the checksum 
	sc			; Was there a carry?
	jmp	:done
	inc	TCPChecksumMSB	; Yes
	snz
	inc	TCPChecksumLSB
	jmp	:done
:MSB	add	TCPChecksumMSB,w	; Add it to the checksum 
	sc			; Was there a carry?
	jmp	:done
	inc	TCPChecksumLSB	; Yes. This time it is added to the LSB.
	snz
	inc	TCPChecksumMSB
:done	xor	IPFlags,#(1<<checksumBit) ; Flip the checksum bit.
	retp

;-------------------------------------------------------------------------------
; Subroutine: TCPTxByte
;
; Transmit a TCP byte accumulating the checksum each time.
;
; W on entry: The value of the byte to send. 
; W on exit : -
; Variables : -
;-------------------------------------------------------------------------------

_TCPTxByte
	mov	Scratch1,w	; Save the byte.
	call	@TCPChecksum
	mov	w,Scratch1	; Retrieve the saved byte.
	call	@PhyTxByte
	retp

;===============================================================================
; 32-bit math subroutines
;===============================================================================

;-------------------------------------------------------------------------------
; Subroutine: TCPAddRCV_NXT, TCPAddSND_NXT
;
; Add a constant to RCV.NXT or SND.NXT
;
; W on entry: The number to add.
; W on exit : -
; Variables : -
; Bank on exit : -
;-------------------------------------------------------------------------------

_TCPAddRCV_NXT
	_bank	TCB
	add	TCPRCV_NXT1,w
	sc
	retp
	incsz	TCPRCV_NXT2
	retp
	incsz	TCPRCV_NXT3
	retp
	incsz	TCPRCV_NXT4
	retp

_TCPAddSND_UNA
	_bank	TCB
	add	TCPSND_UNA1,w
	sc
	retp
	incsz	TCPSND_UNA2
	retp
	incsz	TCPSND_UNA3
	retp
	incsz	TCPSND_UNA4
	retp

ENDIF

IFDEF POP3DEMO
_get_tens
	clr	Scratch2		;result
:2d_again
	sub	Scratch1,#10
	sc				; nc=below 100
	jmp	:2d_next_digit
	inc 	Scratch2
	jmp	:2d_again
:2d_next_digit
	add	Scratch1,#10		; restore from underflow
	add	Scratch2,#'0'
	retp
_get_hundreds
	clr	Scratch2		;result
:3d_again
	sub	Scratch1,#100
	sc				; nc=below 100
	jmp	:3d_next_digit
	inc 	Scratch2
	jmp	:3d_again
:3d_next_digit
	add	Scratch1,#100		; restore from underflow
	add	Scratch2,#'0'
	retp
_times_10
	clc
	rl	scratch1				
	mov	scratch2,scratch1			; store *2
	rl	scratch1
	rl	scratch1
	add	scratch1,scratch2
	retp
	
ENDIF
	org	$A00


;===============================================================================
; TCP HTTP Demo Functions
;===============================================================================

IFDEF HTTPDEMO

;-------------------------------------------------------------------------------
; EEPROM Routines
;
; These must be in the first half of a page.
;-------------------------------------------------------------------------------

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
	nop					;3 cycles needed before change DDR
	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
	nop
	nop
	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
	nop					;3 cycles needed before change DDR
	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
	nop
	nop
	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
	_bank	E2Bank
	mov	w,#$1f
	mov	m,w
	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
	_bank	E2Bank
	jmp	E2Write

E2WriteComplete					;Complete write process
	_bank	E2Bank
	jmp	E2Stop

E2ReadStart					;Returns Z=true for ack
	_bank	E2Bank
	mov	w,#$1f
	mov	m,w
	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
	_bank	E2Bank
	jmp	E2ReadAck			;Read byte

E2ReadComplete					;Returns W=data
	DEBUGP	$62,top
	_bank	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
	_bank	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 piror to reaching end

E2ReadFile					;Returns Z=More (=false for last byte)
	mov	w,#$1f
	mov	m,w
	_bank	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

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

;-------------------------------------------------------------------------------
; Subroutine: AppInit
;
; Called once at startup to allow the application to initialise itself.
; 
; W on entry: -
; W on exit : -
;-------------------------------------------------------------------------------

_AppInit
	DEBUGP	$50,top
	_bank	TCB
	mov	TCPLocalPortl,#HTTPPortl
	mov	TCPLocalPorth,#HTTPPorth
	_bank	HTTPVars
	clr	HTTPParseState		; Initialise all variables.
	clr	HTTPURIHash
	clr	HTTPDone
	clr	HTTPLengthMSB
	clr	HTTPLengthLSB
	jmp	@_TCPPassiveOpen	; Connect to the remote TCP.

;-------------------------------------------------------------------------------
; Subroutine: AppBytesToSend
;
; Called before transmitting a TCP packet to see if the application has any
; data it wishes to send.
;
; The way that data is segmented is a little unorthodox. The maximum transmit
; packet size is 256 bytes. The first packet contains length%256 bytes. Each
; subsequent packet contains 256 bytes until all data is sent.
; 
; W on entry: -
; W on exit : The number of bytes the application wishes to transmit.
;-------------------------------------------------------------------------------

_AppBytesToSend
	_bank	HTTPVars
	clr	E2FileSizeH

	test	HTTPLengthMSB
	snz
	jmp	:msbzero
	mov	w,#$ff
	mov	E2FileSizeL,w
	retp
:msbzero
	mov	E2FileSizeL,HTTPLengthLSB
	test	E2FileSizeL
	sz
	retp
	sb	HTTPDone.0
	retp
	clr	HTTPDone
	_bank	TCPVars
	cse	TCPState,#TCPStateEstablished
	retp
	jmp	@_TCPClose

;-------------------------------------------------------------------------------
; Subroutine: AppPacketOK
;
; The last packet received passed the CRC check. At this point the packet data
; may be acted upon. The application should not transmit a reply packet, but
; should wait for AppBytesToSend to be called.
; 
; W on entry: -
; W on exit : -
;-------------------------------------------------------------------------------

_AppPacketOK	
	DEBUGP	$57,top
	_bank	HTTPVars
	csae	HTTPParseState,#HTTPParse4	;  Check that the parse state is correct.
	retp
	; Read the pointer from the FAT.
	mov	w,HTTPParseState
	DEBUGW	$65,indent
	_bank	HTTPVars	
	clr	HTTPParseState
;	mov	w,#$2a		; Hardcoded hash of /index.html
	mov	w,HTTPURIHash
	DEBUGW	$64,indent
	mov	Scratch0,w
:retry	call	@E2OpenFile
	sz			; z indicates file found.		
	jmp	:404

	mov	HTTPLengthMSB,E2FileSizeH
	mov	HTTPLengthLSB,E2FileSizeL
	add	E2AddrL,#4	; Skip over length and checksum next time.
	snc
	inc	E2AddrH
	retp

:404	;Indicate 404 error.
	mov	w,Scratch0
	DEBUGW	$60,top
	mov	Scratch0,#HTTP404Hash	; Hash of the 404 page
	jmp	:retry
	retp

;-------------------------------------------------------------------------------
; Subroutine: AppPacketBad
;
; The last packet received did not pass the CRC check. The recieve counters
; should be reset and any actions undone since the packet will be received again.
; 
; W on entry: -
; W on exit : -
;-------------------------------------------------------------------------------

_AppPacketBad
	_bank	HTTPVars
	clr	HTTPParseState
	retp

;-------------------------------------------------------------------------------
; Subroutine: AppBytesAvailable
;
; Indicator to the application that a packet has been received and that AppRxByte
; is about to be called 'w' times.
; 
; W on entry: The number of bytes received 
; W on exit : -
;-------------------------------------------------------------------------------

_AppBytesAvailable
	_bank	HTTPVars
	clr	HTTPDone
	retp

;-------------------------------------------------------------------------------
; Subroutine: AppTxByte
;
; This routine is called once for each byte the application has says it wishes 
; to transmit. The application must be prepared to transmit the same sequence
; of bytes again if it receives the AppNak call.
; 
; W on entry: -
; W on exit : The bext byte to transmit.
;-------------------------------------------------------------------------------

_AppTxByte
	_bank	HTTPVars
	call	@E2ReadFile
	retp

;-------------------------------------------------------------------------------
; Subroutine: AppRxByte
;
; Called once for each byte received in a packet. The application should not 
; take any irreversible action until either AppPacketOK or AppPacketBad are
; called to indicate the packet CRC was OK. If the CRC is not OK then the same
; sequence of bytes will be received again when the packet is retransmitted by
; the remote host.
; 
; W on entry: The byte received
; W on exit : -
;-------------------------------------------------------------------------------

_AppRxByte
	_bank	HTTPVars
	mov	Scratch0,w	; Save the received byte.
	mov	w,HTTPParseState
	DEBUGW	$65,indent
	_bank	HTTPVars
	cje	Scratch0,#' ',:nextState
	cje	HTTPParseState,#HTTPParseMethod,:method
	cje	HTTPParseState,#HTTPParseURI,:uri
	jmp	:findEnd
:method	mov	HTTPMethod,Scratch0	; Save the method.
	retp
:uri	add	HTTPURIHash,Scratch0	; Add the byte to the hash
	retp
:nextState	
	csae	HTTPParseState,#HTTPParseVersion
	inc	HTTPParseState
:reset	cjb	HTTPParseState,#HTTPParseVersion,:done
	mov	HTTPParseState,#HTTPParse1
:done	retp
:findEnd
	cja	Scratch0,#CharCR,:reset
	inc	HTTPParseState
	retp


;-------------------------------------------------------------------------------
; Subroutine: AppNak
;
; The last packet transmitted was rejected by the remote host, or not 
; acknowledged. It needs to be retransmitted. This is a signal to the application
; to reset its transmit counters.
; 
; W on entry: -
; W on exit : -
;-------------------------------------------------------------------------------

_AppNak
	DEBUGP	$53,top
	_bank	TCPVars
	mov	w,TCPOutstanding
	_bank	HTTPVars
	mov	E2FileSizeL,w
	call	E2ReadStart			;Prepare to read
	snz					;Ack ?
	retp
	DEBUGP	$61,top
	retp

;-------------------------------------------------------------------------------
; Subroutine: AppAck
;
; The last packet transmitted was acknowledged by the remote host. It will not
; need to be retransmitted.
; 
; W on entry: -
; W on exit : -
;-------------------------------------------------------------------------------

_AppAck
	DEBUGP	$54,top	
	_bank	TCPVars
	mov	w,TCPOutstanding
	_bank	HTTPVars
	add	E2AddrL,w
	snc
	inc	E2AddrH
	sub	HTTPLengthLSB,w
	sc
	dec	HTTPLengthMSB
	test	HTTPLengthLSB		; Check if the whole document has been sent.
	sz
	jmp	:continue
	test	HTTPLengthMSB
	sz
	jmp	:continue
	setb	HTTPDone.0		; Signal that we are done.	
	retp

:continue
	call	E2ReadStart		; Prepare to read.
	retp

ENDIF

;===============================================================================
; TCP SMTP Demo Functions
;===============================================================================

IFDEF SMTPDEMO

;-------------------------------------------------------------------------------
; Subroutine: AppPacketOK
;
; The last packet received passed the CRC check. At this point the packet data
; may be acted upon. The application should not transmit a reply packet, but
; should wait for AppBytesToSend to be called.
; 
; W on entry: -
; W on exit : -
;-------------------------------------------------------------------------------



_SMTPCannedPackets	=	$
_SMTPHELO	dw	'HELO scenix.com',CharCR,CharLF + $f00
_SMTPMAIL	dw	'MAIL FROM:<dummy@demo.sx>',CharCR,CharLF + $f00
_SMTPRCPT	dw	'RCPT TO:<joe@demo.sx>',CharCR,CharLF + $f00
_SMTPDATA	dw	'DATA',CharCR,CharLF + $f00
_SMTPMESG	dw	'From: SX',CharCR,CharLF
		dw	'To: Joe',CharCR,CharLF
		dw	'Subject: Warning!',CharCR,CharLF,CharCR,CharLF
		dw	'Over-temperature condition in router.',CharCR,CharLF,'.',CharCR,CharLF + $f00
_SMTPQUIT	dw	'QUIT',CharCR,CharLF + $f00
_SMTPNONE	dw	$f00


_AppPacketOK
	DEBUGP	$57,top
	_bank	SMTPVars
	mov	w,SMTPState
	DEBUGW	$31,top		; SMTP state.
	_bank	SMTPVars
	; If the high nibble of the command is not 2 or 3 then an error occured. Abort.
	mov	Scratch0,SMTPCommand
	and	Scratch0,#%00100000
	sz
	jmp	:continue	
	; An error occurred. Close the TCP connection.
	mov	w,SMTPCommand
	DEBUGW	$30,top
	jmp	@_TCPClose	
:continue
	inc	SMTPState	; Increment the state to indicate the next step is possible.

	csne	SMTPState,#SMTPStateQuit
	test	SMTPState

	; Load the pointer for the next state.
	mov	Scratch0,SMTPState
	clc		
	rl	Scratch0	; Multiply the state by 2.
	mov	w,Scratch0
	jmp	pc+w		; Jump based on the SMTP state.
	mov	w,#_SMTPNONE
	jmp	:done
	mov	w,#_SMTPHELO
	jmp	:done
	mov	w,#_SMTPNONE
	jmp	:done
	mov	w,#_SMTPMAIL
	jmp	:done
	mov	w,#_SMTPNONE
	jmp	:done
	mov	w,#_SMTPRCPT
	jmp	:done
	mov	w,#_SMTPNONE
	jmp	:done
	mov	w,#_SMTPDATA
	jmp	:done
	mov	w,#_SMTPNONE
	jmp	:done
	mov	w,#_SMTPMESG
	jmp	:done
	mov	w,#_SMTPNONE
	jmp	:done
	mov	w,#_SMTPQUIT
	jmp	:done
:done	mov	SMTPTxPointer,w
	clr	SMTPRxCount
	clr	SMTPTxCount	; Reset the count.
	mov	w,SMTPState
	DEBUGW	$31,top		; SMTP state.
	retp

;-------------------------------------------------------------------------------
; Subroutine: AppInit
;
; Called once at startup to allow the application to initialise itself.
; 
; W on entry: -
; W on exit : -
;-------------------------------------------------------------------------------

_AppInit
	DEBUGP	$50,top
	_bank	SMTPVars
	mov	SMTPState,#SMTPStateClosed ; Start in the closed state.
	clr	SMTPRxCount
	clr	SMTPTxCount
	mov	SMTPTxPointer,#_SMTPNONE
	clr	SMTPEOL
	_bank	IPVars
	mov	IPDestAddress1,#SMTPAddress1	; SMTP server IP address.
	mov	IPDestAddress2,#SMTPAddress2
	mov	IPDestAddress3,#SMTPAddress3
	mov	IPDestAddress4,#SMTPAddress4
	; Randomly choose our own port.	
	_bank	PPPTimer
	mov	w,PPPTimer2		; Base it on a timer.
	_bank	TCB
	mov	TCPLocalPortl,w
	mov	TCPLocalPorth,#$36
	mov	TCPRemotePorth,#SMTPPorth ; Set the port to connect to.
	mov	TCPRemotePortl,#SMTPPortl
	jmp	@_TCPActiveOpen		; Connect to the remote TCP.

;-------------------------------------------------------------------------------
; Subroutine: AppBytesToSend
;
; Called before transmitting a TCP packet to see if the application has any
; data it wishes to send.
; 
; W on entry: -
; W on exit : The number of bytes the application wishes to transmit.
;-------------------------------------------------------------------------------

_AppBytesToSend
	; Compute the number of bytes we have to transmit.
	clr	Scratch1
	_bank	SMTPVars

	csne	SMTPState,#SMTPStateMesgAck
	test	SMTPState

	cjne	SMTPTxPointer,#_SMTPNONE,:count
	mov	w,#0
	retp
:count	mov	Scratch0,SMTPTxPointer	; Save the start address.
	mov	w,#0		
	mov	m,w	
:loop	mov	m, #(_SMTPCannedPackets>>8)	; Load the mode register.
	mov	w,Scratch0	; Load the pointer
	iread			; Read the next byte.
	inc	Scratch1
	mov	w,m		; Load the mode register.
	test	w
	sz			; If it is not zero then exit.
	jmp	:done		; We're done counting.
	inc	Scratch0	; Increment the pointer
	jmp	:loop		
:done	_bank	SMTPVars
	mov	w,SMTPState
	DEBUGW	$31,top
	mov	w,Scratch1
	retp

;-------------------------------------------------------------------------------
; Subroutine: AppBytesAvailable
;
; Indicator to the application that a packet has been received and that AppRxByte
; is about to be called 'w' times.
; 
; W on entry: The number of bytes received 
; W on exit : -
;-------------------------------------------------------------------------------

_AppBytesAvailable
	DEBUGW	$52,top
	retp

;-------------------------------------------------------------------------------
; Subroutine: AppTxByte
;
; This routine is called once for each byte the application has says it wishes 
; to transmit. The application must be prepared to transmit the same sequence
; of bytes again if it receives the AppNak call.
; 
; W on entry: -
; W on exit : The bext byte to transmit.
;-------------------------------------------------------------------------------

_AppTxByte
	DEBUGP	$55,indent
	_bank	SMTPVars
	mov	w,#0		
	mov	m,w	
	mov	m, #(_SMTPCannedPackets>>8)	; Load the mode register.
	mov	w,SMTPTxPointer	; Load start address.
	add	w,SMTPTxCount	; Offset by the current count.
	iread			; Read the next byte.
	inc	SMTPTxCount
	retp

;-------------------------------------------------------------------------------
; Subroutine: AppRxByte
;
; Called once for each byte received in a packet. The application should not 
; take any irreversible action until either AppPacketOK or AppPacketBad are
; called to indicate the packet CRC was OK. If the CRC is not OK then the same
; sequence of bytes will be received again when the packet is retransmitted by
; the remote host.
; 
; W on entry: The byte received
; W on exit : -
;-------------------------------------------------------------------------------

_AppRxByte
	DEBUGP	$56,indent
	mov	Scratch2,w		; Save the received byte.
	_bank	SMTPVars
	mov	w,SMTPRxCount	
	DEBUGW	$32,indent
	_bank	SMTPVars
	mov	w,SMTPCommand	
	DEBUGW	$33,indent

	; Process the recieved byte.
	_bank	SMTPVars
	cjae	SMTPRxCount,#2,:processByte
	; The first two bytes received indicate the status
	sub	Scratch2,#'0'		; Turn the character into a number.
	test	SMTPRxCount
	sz
	jmp	:one
	mov	w,<>Scratch2		; Move the first part of the command into the high nibble.
	mov	SMTPCommand,w		; Save the command.
	jmp	:done
:one	or	SMTPCommand,Scratch2	; Move the second part of the command into the low nibble.
	jmp	:done
:processByte
	; If the byte is a linefeed the command is over.
	cje	Scratch2,#CharLF,:linefeed
:done	inc	SMTPRxCount
	test	SMTPRxCount		; Watch out for the count wrapping around.
	mov	w,#2
	snz
	mov	SMTPRxCount,w
	retp
:linefeed
	inc	SMTPEOL			; Indicate a linefeed was detected.

	retp

;-------------------------------------------------------------------------------
; Subroutine: AppNak
;
; The last packet transmitted was rejected by the remote host, or not 
; acknowledged. It needs to be retransmitted. This is a signal to the application
; to reset its transmit counters.
; 
; W on entry: -
; W on exit : -
;-------------------------------------------------------------------------------

_AppNak
	DEBUGP	$53,top
	_bank	SMTPVars
	clr	SMTPTxCount
	retp

;-------------------------------------------------------------------------------
; Subroutine: AppAck
;
; The last packet transmitted was acknowledged by the remote host. It will not
; need to be retransmitted.
; 
; W on entry: -
; W on exit : -
;-------------------------------------------------------------------------------

_AppAck
	DEBUGP	$54,top
	_bank	SMTPVars
	inc	SMTPState
	mov	SMTPTxPointer,#_SMTPNONE
	retp

;-------------------------------------------------------------------------------
; Subroutine: AppPacketBad
;
; The last packet received did not pass the CRC check. The recieve counters
; should be reset and any actions undone since the packet will be received again.
; 
; W on entry: -
; W on exit : -
;-------------------------------------------------------------------------------

_AppPacketBad
	DEBUGP	$58,top
	_bank	SMTPVars
	clr	SMTPRxCount
	retp

ENDIF

;===============================================================================
; TCP POP3 Demo Functions
;===============================================================================

IFDEF POP3DEMO

;-------------------------------------------------------------------------------
; Subroutine: AppPacketOK
;
; The last packet received passed the CRC check. At this point the packet data
; may be acted upon. The application should not transmit a reply packet, but
; should wait for AppBytesToSend to be called.
; 
; W on entry: -
; W on exit : -
;-------------------------------------------------------------------------------
_AppPacketOK
	DEBUGP	$57,top
	_bank	POP3Vars
	mov	w,POP3State
	DEBUGW	$31,top		; POP3 state.
	_bank	POP3Vars

	; if the response is not a '+' then error occurs (except when receiving message body)
	csne	POP3State,#POP3StateMsg
	jmp	:continue
	csne	POP3Command1,#'+'
	jmp	:continue

;	mov	Scratch0,POP3Command
;	and	Scratch0,#%00100000
;	sz
;	jmp	:continue	

	; An error occurred. Close the TCP connection.
	mov	w,POP3Command1
	DEBUGW	$30,top
	jmp	@_TCPClose	
; state diagram
;	        Sx		POP3 server
; closed (0)--> send Username (1)
;		+ok <-------  UserAck (2)		
;		send Password (3)
;		+ok <-------  PassAck (4)
;		send Stat (5)
;		+ok num_msg <- StatAck (6)
;loop num_msg times in the following states with i=1 to num_msg
;		send Retr i (7)
;		+ok	    <- RetrAck (8)
;		multi-line msg (transmit to UART) <- (9)
;		terminated by . <-
;		send dele i (10)
;		+ok	    <- deleAck (11)
;		goto state 7
;		send quit (12)
;		+ok	    <- quitack (13)
;		finished (14)

:continue
;	inc	POP3State	; Increment the state to indicate the next step is possible.

;	csne	POP3State,#POP3StateQuit
;	test	POP3State

	; Load the pointer for the current state.
	mov	Scratch0,POP3State
	clc		
	rl	Scratch0	; Multiply the state by 2.
	mov	w,Scratch0
	jmp	pc+w		; Jump based on the POP3 state.

; st 0->1
	mov	w,#_POP3USER	; current state= closed, received +ok
	jmp	:inc_state	; inc the state, next state= send user name (1)
; st 1->2
	mov	w,#_POP3NONE	; Username acknowledged here, dummy state
	jmp	:inc_state	; will never visit here, since the ACK of user will inc state in AppAck routine

;recevied +ok after userAck
; st 2->3
	mov	w,#_POP3PASS
	jmp	:inc_state
; st 3->4
	mov	w,#_POP3NONE	; password acknowledged here, dummy state
	jmp	:inc_state	; will never visit here, since the ACK of password will inc state in AppAck routine
; st 4->5
;recevied +ok after PassAck

	mov	w,#_POP3STAT	; send STAT request
	jmp	:inc_state
; st 5->6
	mov	w,#_POP3NONE	; stat acknowledged here, dummy state
	jmp	:inc_state	; will never visit here, since the ACK of password will inc state in AppAck routine

;recevied +ok N M here after StatAck, where N is number of messages, M is the number of bytes
; limit to 9 messages only, thus only '+ok n' will be stored
; st 6->7 or 6->12
	mov	w,#_POP3RETR
	jmp	:stat_decide
; st 7->8	
	mov	w,#_POP3NONE	; Retr acknowledged here, dummy state
	jmp	:inc_state	; will never visit here, since the ACK of RETR will inc state in AppAck routine
; st 8->9, +ok, then multiple line message 
	mov	w,#_POP3NONE
	jmp	:inc_state	; nothing to send, just change state and wait for . to end, switch to dele state
; st 9 = message state, received packet okay, if msg end flag is set then, change to dele state
	mov	w,#_POP3NONE
	jmp	:msg_decide
; st 10 = delete state
	mov	w,#_POP3NONE
	jmp	:done
; st 11 = delete ack, +ok, retrieve next message or quit if no more
	mov	w,#_POP3RETR
	jmp	:deleAck_decide
; st 12 quit
	mov	w,#_POP3NONE
	jmp	:done
; st 13 quit ack, +ok received
	mov	w,#_POP3NONE
	jmp	:done
;st 14 finished
	jmp	:done
:deleAck_decide
	_bank	POP3MoreVars
	mov	w,POP3RxMsgNo

	_bank	POP3Vars
	mov	w,POP3TxMsgNo-w
	snz
	jmp	:quit_state
	
	inc	POP3TxMsgNo	

	; prepare the digits for the next retr command

	mov	Scratch1,POP3TxMsgNo
	_bank	POP3MoreVars

	clr	POP3TxMsgDigit1
	clr	POP3TxMsgDigit2
	clr	POP3TxMsgDigit3

	cjb	Scratch1,#10,:single_digit
	cjb	Scratch1,#100,:double_digit
	
	; 3 digits
	call	@get_hundreds		;quotient in scratch2,remainder in scratch1

	mov	POP3TxMsgDigit1,Scratch2
	
	call	@get_tens
	mov	POP3TxMsgDigit2,Scratch2
	
	add	Scratch1,#'0'
	mov	POP3TxMsgDigit3,Scratch1

	jmp	:retrieve
:double_digit
	call	@get_tens
	mov	POP3TxMsgDigit1,Scratch2
	
	add	Scratch1,#'0'
	mov	POP3TxMsgDigit2,Scratch1

	jmp	:retrieve
	
:single_digit
	add	Scratch1,#'0'
	mov	POP3TxMsgDigit1,Scratch1

:retrieve
	_bank	POP3Vars
	; go to retr state
	mov	POP3State,#POP3StateRetr
	mov	w,#_POP3RETR
	jmp	:done
	
:msg_decide
	sb	POP3MsgEndFlag.1
	jmp	:packet_done 			; not yet end, remain in message state for multiple line message
	
	; message ended with a .
	; change to dele state
	clr	POP3MsgEndFlag		; prepare for next message
	clr	POP3MsgSubSt
	mov	w,#_POP3DELE
	jmp	:inc_state

:stat_decide
	; if number of message is 0, then next state is quit
	; else retr message 1 to n, where n is in POP3Command5
	
	csne	POP3Command5,#'0'
	jmp	:quit_state			; 0 messages, next state=quit
	; message no. <>0, next state = retr
	; determine no. of messages to retrieve
	
	mov	scratch1,w			; POP3Command5-w is in w now
	cje	POP3Command6,#' ', :store_msg_no		; single digit?
	
	sub	POP3Command6,#'0'
	;multiply the previous digit by 10=digit*8+*2
	call	@times_10				; scratch1 x 10
	add	scratch1,POP3Command6

	cje	POP3Command7,#' ', :store_msg_no		; double digit?
	
	sub	POP3Command7,#'0'
	;multiply the previous digit by 10=digit*8+*2
	call	@times_10				; scratch1 x 10
	add	scratch1,POP3Command7
	
:store_msg_no
	_bank	POP3MoreVars
	mov	POP3RxMsgNo,scratch1			; =total no. of messages to retrieve
	mov	POP3TxMsgDigit1,#'1'
	clr	POP3TxMsgDigit2
	clr	POP3TxMsgDigit3

	_bank	POP3Vars
	mov	POP3TxMsgNo,#1	

	clr	POP3MsgEndFlag
	mov	w,#_POP3RETR
	
	; limit will be check in AppTxByte
	jmp	:inc_state			; next stat=retrieve
:packet_done
	mov	POP3TxPointer,w
	mov	POP3MsgSubStLast,POP3MsgSubSt	; store last sub state
	jmp	:done2
:quit_state
		
	mov	POP3State,#POP3StateQuit	; zero messages, next state=quit
	mov	w,#_POP3QUIT
	jmp	:done
:inc_state
	inc	POP3State	; Increment the state to indicate the next step is possible.
:done
	mov	POP3TxPointer,w
:done2
	clr	POP3RxCount
	clr	POP3TxCount	; Reset the count.
;	clr	POP3MsgSubSt	;start of Msg sub state
IFDEF POP3DEBUG
	mov	w,POP3State
	mov	DebugScratch1,w
	add	DebugScratch1,#'0'
	mov	w,DebugScratch1
	call	@DebugSendByte
ENDIF		
	retp

;-------------------------------------------------------------------------------
; Subroutine: AppRxByte
;
; Called once for each byte received in a packet. The application should not 
; take any irreversible action until either AppPacketOK or AppPacketBad are
; called to indicate the packet CRC was OK. If the CRC is not OK then the same
; sequence of bytes will be received again when the packet is retransmitted by
; the remote host.
; 
; W on entry: The byte received
; W on exit : -
;-------------------------------------------------------------------------------
_AppRxByte
;	POP3DW
	mov	Scratch2,w		; Save the received byte.
	_bank	POP3Vars
	mov	w,POP3RxCount	
	DEBUGW	$32,indent
	_bank	POP3Vars
	cje	POP3State,#POP3StateMsg,:message_state	; if message state, scan for <LF>.<CR> combination
	cjae	POP3RxCount,#7,:processByte
	; The first byte received indicate the status +=ok -=error or +ok n (no. of messages)
	mov	fsr,#POP3Command1
	add	fsr,POP3RxCount
	mov	ind,Scratch2
	jmp	:done
:processByte
	; skip until end of response
	; If the byte is a linefeed the command is over.
	cje	Scratch2,#CharLF,:linefeed
:done	inc	POP3RxCount
	test	POP3RxCount		; Watch out for the count wrapping around.
	mov	w,#5
	snz
	mov	POP3RxCount,w
	
	retp
:linefeed
	inc	POP3EOL			; Indicate a linefeed was detected.
	
	retp
:message_state
	cjne	POP3RxCount,#0,:skip_loading
	mov	POP3MsgSubSt,POP3MsgSubStLast
:skip_loading	
	mov	w,POP3MsgSubSt
	jmp	pc+w
	jmp	:Start
	jmp	:CR_detected
	jmp	:CR_LF_detected
	jmp	:CR_LF_dot_detected
	jmp	:CR_LF_dot_CR_detected
	jmp	:m_start
:Start
	cjne	Scratch2,#CharCR,:m_done
:m_next_state
	inc	POP3MsgSubSt		; 1 CR detected
	jmp	:m_done
:CR_detected
	cjne	Scratch2,#CharLF,:m_start
	jmp	:m_next_state
:CR_LF_detected
	cje	Scratch2,#'.',:m_next_state_no_show
	cjne	Scratch2,#CharCR,:m_start
;<CR><LF><CR> detected
	mov	POP3MsgSubSt,#POP3MsgSubSt1CR	
	jmp	:m_done
:CR_LF_dot_detected
	cje	Scratch2,#'.',:byte_stuffed	; byte-stuff . , display it
	cjne	Scratch2,#CharCR,:m_start

	jmp	:m_next_state_no_show
:CR_LF_dot_CR_detected
	cjne	Scratch2,#CharLF,:m_start
	setb	POP3MsgEndFlag.1	; LF+.+CR
	jmp	:m_ok
:m_next_state_no_show
	inc	POP3MsgSubSt
	jmp	:m_ok

:m_done	
	;send data to debug port from this line
	mov	w,Scratch2
	POP3W
:m_ok
	inc	POP3RxCount
	mov	Scratch1,POP3MsgSubSt
	add	Scratch1,#'!'
	mov	w,Scratch1
	POP3DW

	retp
:byte_stuffed
:m_start				; change to start state
	clr	POP3MsgSubSt
	jmp	:m_done   
;-------------------------------------------------------------------------------
; Subroutine: AppBytesToSend
;
; Called before transmitting a TCP packet to see if the application has any
; data it wishes to send.
; 
; W on entry: -
; W on exit : The number of bytes the application wishes to transmit.
;-------------------------------------------------------------------------------

_AppBytesToSend
	; Compute the number of bytes we have to transmit.
	clr	Scratch1
	_bank	POP3Vars

	cjne	POP3TxPointer,#_POP3NONE,:count
	mov	w,#0
	retp
:count	mov	Scratch0,POP3TxPointer	; Save the start address.
	mov	w,#0		; ## Compensate for SX52 mode bug.
	mov	m,w		; ##
:loop	mov	m, #(_POP3CannedPackets>>8)	; Load the mode register.
	mov	w,Scratch0	; Load the pointer
	iread			; Read the next byte.
	
	inc	Scratch1
	test	w
	snz
	jmp	:check

	mov	w,m		; Load the mode register.
	test	w
	sz			; If it is not zero then exit.
	jmp	:done		; We're done counting.
	inc	Scratch0	; Increment the pointer
	jmp	:loop		
:done	_bank	POP3Vars
	mov	w,POP3State
	DEBUGW	$31,top
	mov	w,Scratch1
	retp
:check	

;	d1 d2 d3   -> +2
;	d1 d2 0	   -> +1
;	d1 0  0    -> no change

	_bank	POP3MoreVars
	mov	w,POP3TxMsgDigit2
	test	w
	snz
	jmp	:no_change

	inc	Scratch1

	mov	w,POP3TxMsgDigit3
	test	w
	snz	
	jmp	:no_change
	inc	Scratch1

:no_change
	_bank  POP3Vars
;compensate for 0d,0a
	inc	Scratch1
	inc	Scratch1
	jmp	:done
	
;-------------------------------------------------------------------------------
; Subroutine: AppBytesAvailable
;
; Indicator to the application that a packet has been received and that AppRxByte
; is about to be called 'w' times.
; 
; W on entry: The number of bytes received 
; W on exit : -
;-------------------------------------------------------------------------------

_AppBytesAvailable
	DEBUGW	$52,top
	retp

;-------------------------------------------------------------------------------
; Subroutine: AppTxByte
;
; This routine is called once for each byte the application has says it wishes 
; to transmit. The application must be prepared to transmit the same sequence
; of bytes again if it receives the AppNak call.
; 
; W on entry: -
; W on exit : The bext byte to transmit.
;-------------------------------------------------------------------------------
; due to the fact that retr and dele has a variable, all message in those 2 states will be transmitted differently
; in the way as follows:
; if it is zero, replaced with the content of POP3TxMsgNo
_AppTxByte

	_bank	POP3Vars
	mov	w,#0		; ## Compensate for SX52 mode bug.
	mov	m,w		; ##
	mov	m, #(_POP3CannedPackets>>8)	; Load the mode register.
	mov	w,POP3TxPointer	; Load start address.
	add	w,POP3TxCount	; Offset by the current count.
	iread			; Read the next byte.
	test	w
	sz			; =0 then, replace with POP3TxMsgNo
	jmp	:done
;replace
	mov	Scratch2,POP3TxCount
	_bank  POP3MoreVars
	mov	fsr,#POP3MoreVars
	mov	w,Scratch2
	add	fsr,w
	mov	w,ind
	test	w
	sz
	jmp	:to_done
	;skip to <CR><LF>
	_bank 	POP3Vars
	mov	POP3TxCount,#8	; move pointer to <CR>
	mov	w,#CharCR
:to_done
	_bank  POP3Vars
:done
	POP3DW
	inc	POP3TxCount
	
	retp
ENDIF	
;===============================================================================
; Application code
;===============================================================================

	org	$d00

_ResetVector
	mov	!option,#%11001000	; Disable RTCC rollover.
	mov	StatusPort,#0	       ; Initialize status LEDs.
;	mov	!StatusPort,#0		; Set RA in/out directions for status LEDs.
   	mov	!re,#0		; Set RA in/out directions for status LEDs.

	call	@SerialInit		; Initialize the UART.
	call	@PPPInit		; Initialize the PPP layer.

IFDEF HTTPDEMO
	mov	re, #E2PortInit
	mov	!re, #E2SDAInDDR
	_bank	E2Bank
	clr	E2AddrH
	clr	E2AddrL
ENDIF

IFDEF ADCDEMO
	clr	rb			;Ensure rb output pins startup low
	mov	!rb, #%01000000
	_bank	ADCVars		
	clr	adcCount
	clr	adcAcc
	clr	adcwarning
ENDIF

IFDEF JAVADEMO
	_bank	JavaVars
	mov	lawnOn,#sprinklerOn
	mov	pathTime,#sprinkler45
ENDIF

	; Application code starts here:

	; Start the PPP connection
:Start
IFDEF WIN32
	call	@ModemConnect		; Pretend we are a modem.
ENDIF
	call	@PPPOpen
	sb	PPPFlags.linkUp		; Is the link up?
	jmp	:Start			; No. Try opening it again.


	; Initialise the TCP
IFDEF TCP
	_bank	TCPVars
	clr	TCPOutstanding	
	mov	TCPState,#TCPStateClosed
	
ENDIF

IFDEF SMTPDEMO
	_bank	SMTPVars
	mov	SMTPState,#SMTPStateClosed
	mov	SMTPTxPointer,#_SMTPNONE
ENDIF
        

IFDEF POP3DEMO
	_bank	POP3Vars
	mov	POP3State,#POP3StateClosed
	mov	POP3TxPointer,#_POP3NONE
ENDIF

IFDEF HTTPDEMO 
	call	@AppInit
ENDIF
IFDEF POP3DEMO
	call	@AppInit
ENDIF

:loop
IFDEF HTTPDEMO
	_bank	TCPVars
	csne	TCPState,#TCPStateClosed	
	call	@AppInit		; Ensure we continue to listen.
ENDIF

IFDEF TCP
	call	@TCPTransmit
ENDIF
	call	@IPReceivePacket
	_bank	IPVars
IFDEF TCP
	snb	IPFlags.TCPPacket	; Is the packet TCP?
	jmp	:TCPRx			; Yes, process it.
ENDIF
IFDEF SMTPDEMO
	IFNDEF ADCDEMO
		snb	IPFlags.ICMPPacket	; Is the packet ICMP?
		jmp	:SendMail		; If we get pinged, send an email message.
	ENDIF
ENDIF
IFDEF JAVADEMO
	snb	IPFlags.UDPPacket	; Is the packet UDP?
	jmp	:UDPRx		; Yes, process it.
ENDIF
IFDEF UDPDEMO
	snb	IPFlags.UDPPacket	; Is the packet UDP?
	jmp	:UDPRx		; Yes, process it.
ENDIF

IFDEF ADCDEMO
	_bank	ADCVars
	csa	adcValue, #256/7 * 4
	clr	ADCWarning

	csbe	adcValue, #256/7 * 4
	call	@ADCSendWarning

ENDIF

	_bank	IPVars
	snb	IPFlags.anyPacket	; Was a packet received?
	call	@IPRxClosePacket	; Yes, make sure it is cleaned up.

	_bank	PPPVars
	snb	PPPFlags.linkUp		; Check the link is still up.
	jmp	:loop
	jmp	:Start			; If the link is reset then start again.

	call	@PPPClose		; Close the link.
:done	jmp	:done


IFDEF	TCP
:TCPRx
	call	@TCPProcessPacket	; Handle a TCP packet.
ENDIF
	jmp	:loop

IFDEF SMTPDEMO
:SendMail
	call	@AppInit
	jmp	:loop
ENDIF

IFDEF UDPDEMO
:UDPRx	call	@UDPRxHeader		; Receive the UDP header.
	_bank	UDPVars
	cse	UDPSrcPorth,#(DemoPort&$ff00)>>8	; Check the port number.
	jmp	:gobble
	cse	UDPSrcPortl,#DemoPort&$00ff	
	jmp	:gobble
	; OK the packet is for the right port.
	call	@IPRxData
	mov	Scratch0,w
	cje	Scratch0,#DemoMemDump,:memdump	; Is the command a memory dump?	
	cje	Scratch0,#DemoMemSet,:set	; Is the command a set memory?	
	cje	Scratch0,#DemoMemGet,:get	; Is the command a get memory?	

:gobble	DEBUGP	$80,0
	call	@IPRxClosePacket
	jmp	:loop

:memdump	; Dump all of the memory in a 192 byte structure.
	_bank	IPVars
	clr	IPLengthMSB
	mov	IPLengthLSB,#192
	mov	IPDestAddress1,IPSrcAddress1	; Copy the address of the sender
	mov	IPDestAddress2,IPSrcAddress2
	mov	IPDestAddress3,IPSrcAddress3
	mov	IPDestAddress4,IPSrcAddress4
	call	@UDPStartPacket			; Start the reply packet.
	clr 	fsr                    
:all_ram 
	sb	fsr.4                  
	setb    fsr.3
	mov	Scratch0,fsr	; Save the FSR                
	mov	w,ind                   
	call	@IPTxData	
	mov	fsr,Scratch0	; Restore the FSR
	ijnz	fsr,:all_ram   
	call	@PPPClosePacket    
	jmp	:gobble

:set	; Read the next byte to get an address and set that address to 
	; the following byte.
	call	@IPRxData
	mov	Scratch0,w	; Save the address.
	call	@IPRxData	
	mov	Scratch1,w	; Save the data.
	mov	FSR,Scratch0	; Use indirect addressing.
	mov	IND,Scratch1	; Set the register.
	jmp	:gobble

:get	; The next byte contains an address. Reply with a packet containing
	; the byte at that address.
	_bank	IPVars
	clr	IPLengthMSB
	mov	IPLengthLSB,#1
	mov	IPDestAddress1,IPSrcAddress1	; Copy the address of the sender
	mov	IPDestAddress2,IPSrcAddress2
	mov	IPDestAddress3,IPSrcAddress3
	mov	IPDestAddress4,IPSrcAddress4

	call	@UDPStartPacket	; Start the reply packet.
	call	@IPRxData		; Read the address.
	mov	FSR,w			; Use indirect addressing.
	mov	w,IND			; Load the byte.
	call	@IPTxData		; Transmit it.
	call	@PPPClosePacket	; Finish the packet.
	jmp	:gobble 
ENDIF

IFDEF JAVADEMO
:UDPRx	call	@UDPRxHeader		; Receive the UDP header.
	_bank	UDPVars
	cse	UDPSrcPorth,#(sprinklerPort&$ff00)>>8	; Check the port number.
	jmp	:gobble
	cse	UDPSrcPortl,#sprinklerPort&$00ff	
	jmp	:gobble
	call	@PhyRxByte		; Receive the command.
	mov	Scratch0,w
	cje	Scratch0,#commandGet,:commandGet
	cje	Scratch0,#commandSet,:commandSet
	; Invalid command.
:gobble	call	@IPRxClosePacket
	jmp	:loop
:commandGet
	; Send a reply packet.
	_bank	IPVars
	mov	IPLengthLSB,#2+(sprinklerZones*2)
	clr	IPLengthMSB
	mov	IPDestAddress1,IPSrcAddress1	; Copy the address of the sender
	mov	IPDestAddress2,IPSrcAddress2
	mov	IPDestAddress3,IPSrcAddress3
	mov	IPDestAddress4,IPSrcAddress4
	call	@UDPStartPacket			; Start the reply packet.
	mov	w,#commandGet
	call	@PhyTxByte
	mov	w,#2+(sprinklerZones*2)
	call	@PhyTxByte
	mov	Scratch0,#JavaVars
:gloop	mov	fsr,scratch0
	mov	w,indf
	call	@PhyTxByte
	inc	Scratch0
	cjbe	Scratch0,#JavaTxEnd,:gloop
	call	@PPPClosePacket
	jmp	:loop
:commandSet
	call	@PhyRxByte	 	; Ignore the number of zones.
	mov	Scratch0,#JavaVars
:sloop	call	@PhyRxByte
	mov	Scratch1,w
	mov	fsr,scratch0
	mov	indf,Scratch1
	inc	Scratch0
	cjbe	Scratch0,#JavaTxEnd,:sloop
	jmp	:loop
ENDIF


IFDEF ADCDEMO
_ADCSendWarning
	snb	ADCWarning.0
	retp
	setb	ADCWarning.0
	jmp	@_AppInit
ENDIF


;===============================================================================
; Physical layer routines
;===============================================================================

;-------------------------------------------------------------------------------
; Subroutine: PhyRxByte
;
; Receive a byte from the physical layer. This routine blocks until a byte is 
; available. To prevent blocking, call PhyRxTest to see if there are any bytes
; already available in the receive buffer. Any transparency added by the peer
; is removed here.
;
; W on entry: -
; W on exit : the received byte
; Variables : Scratch1
; Bank on exit : serial
;-------------------------------------------------------------------------------

_IPRxData
_PhyRxByte
	mov	w,fsr
	_bank	serial
	mov	save_bank,w
	call	@GetByte	; Call the UART VP
	mov	Scratch2,w
	cjne	Scratch2,#PPPEscape,:done
	call	@GetByte	; Get the escaped byte
	xor	w,#PPPXor
	mov	Scratch2,w	
:done	call	@PPPRxFCSData
	mov	w,Scratch2
	DEBUGW	$03,1
	_bank	serial	
	mov	fsr,save_bank
	mov	w,Scratch2
	retp

;-------------------------------------------------------------------------------
; Subroutine: PhyTxByte
; Subroutine: PhyNoTransTxByte
;
; Send a byte to the physical layer. If the transmit buffer is full then this 
; routine will block until space is available.
;
; PhyTxByte adds transparency while PhyNoTransTxByte doesn't. Every byte between
; the start and stop flag sequences should be processed for transparency.
; By default all characters less than $20 as well as the control escape ($7D)
; and the flag sequence ($7E) is replaced by the control escape followed by the
; original character xored with $20.
;
; W on entry: The byte to transmit
; W on exit : -
; Variables : -
; Bank on exit : serial
;-------------------------------------------------------------------------------

_IPTxData
_PhyTxByte
	mov	Scratch1,w
	mov	w,fsr
	_bank	serial
	mov	save_bank,w
	mov	w,Scratch1
	call	@PPPTxFCSData
_PhyTxByteNoFCS
	DEBUGW	$0A,1
	mov	Scratch1,w			; Save w
	cje	Scratch1, #PPPFlag, :trans	; Compare to the flag sequence
	cje	Scratch1, #PPPEscape,:trans	; Compare to the control escape
	cjb	Scratch1, #$20, :trans		; Check if < $20
	mov	w,Scratch1			; Reload byte to transmit
	call	@SendByte			; No transparency issues
	_bank	serial
	mov	fsr,save_bank
	retp
:trans	mov	w, #PPPEscape		; Transmit the control escape

	call	@SendByte
	mov	w,Scratch1		; Reload byte to transmit
	xor	w,#PPPXor		; Xor it.
_PhyNoTransTxByte
	call	@SendByte	; Call the UART VP
	_bank	serial
	mov	fsr,save_bank
	retp

;-------------------------------------------------------------------------------
; Subroutine: PhyRxTest
;
; Test if there is at least one byte available in the physical layer receive 
; buffer.
;
; W on entry: -
; W on exit : Z is set to 1 if byte available, 0 otherwise
; Variables : -
; Bank on exit : serial
;-------------------------------------------------------------------------------

_PhyRxTest
	mov	w,fsr
	_bank	serial	; Switch to the UART bank.
	mov	save_bank,w
	clz
	test	rx_ring_cnt	; Test if the ring count is zero
	snz
	jmp	:not_set
	mov	fsr,save_bank
	setb	z
	retp
:not_set
	mov	fsr,save_bank
	clrb	z
	retp	              
IFDEF POP3DEMO	
;-------------------------------------------------------------------------------
; Subroutine: AppNak
;
; The last packet transmitted was rejected by the remote host, or not 
; acknowledged. It needs to be retransmitted. This is a signal to the application
; to reset its transmit counters.
; 
; W on entry: -
; W on exit : -
;-------------------------------------------------------------------------------

_AppNak

	_bank	POP3Vars
	clr	POP3TxCount

	mov	w,#'n'
	POP3DW
	retp

;-------------------------------------------------------------------------------
; Subroutine: AppAck
;
; The last packet transmitted was acknowledged by the remote host. It will not
; need to be retransmitted.
; 
; W on entry: -
; W on exit : -
;-------------------------------------------------------------------------------

_AppAck

	_bank	POP3Vars
	inc	POP3State
	mov	POP3TxPointer,#_POP3NONE
	
	mov	w,#'a'
	POP3DW

	
	retp

;-------------------------------------------------------------------------------
; Subroutine: AppPacketBad
;
; The last packet received did not pass the CRC check. The recieve counters
; should be reset and any actions undone since the packet will be received again.
; 
; W on entry: -
; W on exit : -
;-------------------------------------------------------------------------------

_AppPacketBad
	DEBUGP	$58,top
	_bank	POP3Vars
	clr	POP3RxCount
	mov	POP3MsgSubSt,POP3MsgSubStLast
	mov	w,#'b'
	POP3DW
	retp

ENDIF



;===============================================================================
; UART virtual peripheral code to provide the physical layer
;===============================================================================

	org	$e00

SerialISR
IFDEF ADCDEMO
	_bank	ADCVars			;switch to ISR bank
	mov	w, rb			;Read port b
	mov	adcTemp, w
	and	w, #%01111111		;clear bit 7
	sb	adcTemp.6		;Bit 6 low ?
	or	w, #%10000000		;Yes => set bit 7
	mov	rb, w			;update cap. discharge pins
	snb	adcTemp.6		;check if adc0 triggered?
	incsz	adcAcc			;if so, increment accumulator
	inc	adcAcc			; and prevent overflowing
	dec	adcAcc			; by skipping second 'INC'
	inc	adcCount		;adjust adc0 timing count
	jnz	:End			;  not done, jump ahead
	mov	adcValue, adcAcc	;samples ready, update adc0
	clr	adcAcc			; reset adc0 accumulator
	setb	adcComplete		;Indicate data

	mov	w, #%11000000
	and	rb, w
	csb	adcValue, #256/7 * 1
	setb	rb.0
	csb	adcValue, #256/7 * 2
	setb	rb.1
	csb	adcValue, #256/7 * 3
	setb	rb.2
	csb	adcValue, #256/7 * 4
	setb	rb.3
	csb	adcValue, #256/7 * 5
	setb	rb.4
	csb	adcValue, #256/7 * 6
	setb	rb.5
:End
ENDIF

		_bank    serial                  ;switch to serial register bank
:transmit       clrb    tx_divide.baud_bit      ;clear xmit timing count flag
		inc     tx_divide               ;only execute the transmit routine
		stz                             ;set zero flag for test
		sb	tx_divide.baud_bit      ;every 2^baud_bit interrupt
		jmp	:receive		;not a transmit cycle
		test    tx_count                ;are we sending?
		sz		
		jmp	:txbit			;yes, send next bit
		mov	w,tx_ring_cnt		;is tx ring empty?
		snz
		jmp	:receive                ;yes, go to :receive
:txring		mov	w,tx_ring_op		;move one character from the ring to the
		mov	fsr,w			; transmitter using indirect addressing
		mov	w,indf
		_bank	serial			;switch back to the uart bank
		not     w                       ;ready bits (inverse logic)
		mov     tx_high,w               ; store data byte
		setb    tx_low.7                ; set up start bit
		mov     tx_count,#10            ;1 start + 8 data + 1 stop bit
		dec	tx_ring_cnt		;decrement tx ring byte count
		snz
		clrb	StatusPort.LEDTx
		ringadv	tx_ring_op,tx_ring,tx_ring_size	;advance ring pointer
:txbit		clc                             ;ready stop bit
		rr      tx_high                 ; and shift to next bit
		rr      tx_low                  ;
		dec     tx_count                ;decrement bit counter
		movb    tx_pin,/tx_low.6        ;output next bit
:receive        movb    c,rx_pin                ;get current rx bit
		test    rx_count                ;currently receiving byte?
		sz
		jmp     :rxbit                  ;if so, jump ahead
		mov     w,#9                    ;in case start, ready 9 bits
		sc                              ;skip ahead if not start bit
		mov     rx_count,w              ;it is, so renew bit count
		mov     rx_divide,#start_delay  ;ready 1.5 bit periods
:rxbit          decsz   rx_divide		;middle of next bit?
		jmp	:rxdone
       		setb    rx_divide.baud_bit      ;yes, ready 1 bit period
		dec     rx_count                ;last bit?
		sz                              ;if not
		rr      rx_byte                 ;  then save bit
		sz                             	;and skip to end
		jmp	:rxdone
		mov	w,rx_ring_cnt		; Is the receive buffer already full?
		xor	w,#rx_ring_size		; Compare with the buffer size
		sz
		jmp	:rx_ok			; Not full
		setb	flags.rx_over		; Signal receive buffer overflow
		jmp	:rxdone			; Return to the interrupt handler
:rx_ok		mov	w,rx_byte		; Move the received byte to the ring bank.
		_bank	uart_rx_ring			
		mov	uart_temp_isr,w
		_bank	serial
		mov	w,rx_ring_ip		; Store character in receive buffer
		mov	fsr,w			; Set indirect address
		mov	w,uart_temp_isr		; temp must be in same bank as rx ring.
		mov	indf,w			; Store the received byte
		_bank	serial			; Restore the bank
		setb	StatusPort.LEDRx	; There is traffic.
		ringadv	rx_ring_ip,rx_ring,rx_ring_size
		inc	rx_ring_cnt		; Increment the ring buffer count
		csne	rx_ring_cnt,#(rx_ring_size-2)	; Is the ring nearly full?
		setb	cts_pin			; Yes. Drop CTS to stop the DTE.
:rxdone
IFDEF DEBUG 
		call	@DebugSerialISR		; Call the debug ISR.
ENDIF
		; Update the timer.
		_bank	PPPTimer	
		incsz	PPPTimer1
		jmp	:timerDone
		incsz	PPPTimer2
		jmp	:timerDone
		inc	PPPTimer3
:timerDone
		mov     w,#-int_period&255          ;interrupt every 'int_period' clocks
:end_int	retiw                           ;exit interrupt
