please dont rip this site

Scenix Lib IO OSI3 Tcpip ISXV1_0_4.SRC

; *****************************************************************************************
; 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


file: /Techref/scenix/lib/io/osi3/tcpip/iSXv1_0_4.src, 153KB, , updated: 2005/8/19 17:13, local time: 2024/11/30 03:55,
TOP NEW HELP FIND: 
18.226.104.30:LOG IN

 ©2024 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions?
Please DO link to this page! Digg it! / MAKE!

<A HREF="http://techref.massmind.org/Techref/scenix/lib/io/osi3/tcpip/iSXv1_0_4.src"> scenix lib io osi3 tcpip iSXv1_0_4</A>

Did you find what you needed?

 

Welcome to massmind.org!

 

Welcome to techref.massmind.org!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  .