; A TCP/IP + UDP + ICMP stack for an 8051 microcontroller
;   Copyright (C) 2003  Tim Hurman
;
;   This program is free software; you can redistribute it and/or modify
;   it under the terms of the GNU General Public License as published by
;   the Free Software Foundation; either version 2 of the License, or
;   (at your option) any later version.
;
;   This program is distributed in the hope that it will be useful,
;   but WITHOUT ANY WARRANTY; without even the implied warranty of
;   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;   GNU General Public License for more details.
;
;   You should have received a copy of the GNU General Public License
;   along with this program; if not, write to the Free Software
;   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
;


; serial io routines to the lowest level of the IP stack
; note this does not use interrupt routines
; uses onboard UART and timer 1
; timer 1 baud rate = ((2^SMOD)/32)*(Osc Freq/(12*256-TH1)))
; for 9600 TH1=0xfdh
; IntRAM starts at 0x30h

;
; SLIP definitions
;
	.equ	SLIPEND,0xc0h
	.equ	SLIPESC,0xdbh
	.equ	SLIPESCESC,0xddh
	.equ	SLIPESCEND,0xdch
	.equ	SLIPESC_R,0x05h
	.equ	SENDOK,0x00h	; is it ok to put the char from the buffer into the (bit addr)
	.equ	PKTRECV,0x06h	; packet reception in progress
	.equ	SLSEND1,0x07h	; packet send in progress
	.equ	SLSEND2,0x08h	; is it ok to send the next packet

;
; buffer defitions
;
	.equ	TxB_Start,0x30h ; start of Tx buffer
	.equ	TxB_End,0x36h	; end of Tx buffer + 1
	.equ	TxB_Tail,0x36h	; Tail of the Tx FIFO
	.equ	TxB_Head,0x37h	; Head of the Tx FIFO
	.equ	TxB_FULL,0x01h	; Tx buffer full flag
	.equ	TxB_EMPTY,0x02h	; Tx buffer empty flag

	.equ	RxB_Start,0x38h ; start of Tx buffer
	.equ	RxB_End,0x3eh	; end of Tx buffer + 1
	.equ	RxB_Tail,0x3eh	; Tail of the Tx FIFO
	.equ	RxB_Head,0x3fh	; Head of the Tx FIFO
	.equ	RxB_FULL,0x03h	; Tx buffer full flag
	.equ	RxB_EMPTY,0x04h	; Tx buffer empty flag

;
; IP layer definitions
;
	.equ	IPADDRB1,10d	; IP address byte 1
	.equ	IPADDRB2,0d	; IP address byte 2
	.equ	IPADDRB3,0d	; IP address byte 3
	.equ	IPADDRB4,2d	; IP address byte 4
	.equ	IPREPLEN,0x40h	; IP header len (in 32 bit words, reported)
	.equ	IPCALLEN,0x41h	; IP header len (in 32 bit words, calculated)
	.equ	IPTOTLEN1,0x42h	; the total length (reported) bits 0-7
	.equ	IPTOTLEN2,0x43h	; the total length (reported) bits 8-15
	.equ	IPSAB1,0x44h	; IP source address byte 1 - may move to tcp
	.equ	IPSAB2,0x45h	; IP source address byte 2
	.equ	IPSAB3,0x46h	; IP source address byte 3
	.equ	IPSAB4,0x47h	; IP source address byte 4
	.equ	IPREMLEN1,0x48h	; remaining packet length 1
	.equ	IPREMLEN2,0x49h	; remaining packet length 1
	.equ	IPIDMSB,0x4ah	; IP identification
	.equ	IPIDLSB,0x4bh	; IP identification
	.equ	CHECKSUMB,0x09h	; bit indicating where the checksum is done
	.equ	IPTOS,0x4ch	; Incoming TOS bits
	.equ	IPIPID1,0x4dh	; Incoming packet ID MSB
	.equ	IPIPID2,0x4eh	; Incoming packet ID LSB
	.equ	IPICHK1,0x4fh	; Incoming IP checksum MSB
	.equ	IPICHK2,0x50h	; Incoming IP checksum LSB
	.equ	IPHB1,0x51h	; header byte 1
	.equ	IPTTLB,0x052	; Incoming IP TTL
	.equ	IPDFF,0x0ah	; Incoming IP don't fragment flag

;
; UDP  + Transport layer defs
;
	.equ	SPORT1,0x53h	; source port (MSB)
	.equ	SPORT2,0x54h	; source port (LSB)
	.equ	DPORT1,0x55h	; dest port (MSB)
	.equ	DPORT2,0x56h	; dest port (LSB)
	.equ	CMDVAL,0x57h	; ON or OFF?
	.equ	TRANERR,0x58h	; transport error occourred?
	.equ	TRANCHK1,0x59h	; transport layer MSB checksum
	.equ	TRANCHK2,0x5ah	; transport layer LSB checksum
	.equ	RECCMD,0x0bh	; have byte 1 gone?

;
; TCP defs (Byte 1 is always the MSB
;
	.equ	SYNB1,0x5fh	; Inital sequence number (loop) byte 1
	.equ	SYNB2,0x60h	; Inital sequence number (loop) byte 2
	.equ	SYNB3,0x61h	; Inital sequence number (loop) byte 3
	.equ	SYNB4,0x62h	; Inital sequence number (loop) byte 4
	.equ	ACKB1,0x63h	; Acknodegement number byte 1 (recv)
	.equ	ACKB2,0x64h	; Acknodegement number byte 2 (recv)
	.equ	ACKB3,0x65h	; Acknodegement number byte 3 (recv)
	.equ	ACKB4,0x66h	; Acknodegement number byte 4 (recv)
	.equ	TCPHDRL,0x67h	; TCP header length
	.equ	TMPSEQ1,0x72	; Received sequence number 1
	.equ	TMPSEQ2,0x73	; Received sequence number 2
	.equ	TMPSEQ3,0x74	; Received sequence number 3
	.equ	TMPSEQ4,0x75	; Received sequence number 4
	.equ	TCPURG,0x0ch	; TCP Urg flag (recv)
	.equ	TCPACK,0x0dh	; TCP Ack flag (recv)
	.equ	TCPPSH,0x0eh	; TCP Psh flag (recv)
	.equ	TCPRST,0x0fh	; TCP Rst flag (recv)
	.equ	TCPSYN,0x10h	; TCP Syn flag (recv)
	.equ	TCPFIN,0x11h	; TCP Fin flag (recv)
	.equ	TCPSURG,0x14h	; TCP Urg flag (send)
	.equ	TCPSACK,0x15h	; TCP Ack flag (send)
	.equ	TCPSPSH,0x16h	; TCP Psh flag (send)
	.equ	TCPSRST,0x17h	; TCP Rst flag (send)
	.equ	TCPSSYN,0x18h	; TCP Syn flag (send)
	.equ	TCPSFIN,0x19h	; TCP Fin flag (send)

	; to identify the incoming connection
	.equ	SEQNUM1,0x5bh	; sequence number byte 1 (local seq)
	.equ	SEQNUM2,0x5ch	; sequence number byte 2
	.equ	SEQNUM3,0x5dh	; sequence number byte 3
	.equ	SEQNUM4,0x5eh	; sequence number byte 4
	.equ	REMSEQ1,0x6eh	; Remote sequence number 1
	.equ	REMSEQ2,0x6fh	; Remote sequence number 2
	.equ	REMSEQ3,0x70h	; Remote sequence number 3
	.equ	REMSEQ4,0x71h	; Remote sequence number 4
	.equ	RSPORT1,0x68h	; recognised source port 1
	.equ	RSPORT2,0x69h	; recognised source port 2
	.equ	RIPNUM1,0x6ah	; recognised IP address 1
	.equ	RIPNUM2,0x6bh	; recognised IP address 1
	.equ	RIPNUM3,0x6ch	; recognised IP address 1
	.equ	RIPNUM4,0x6dh	; recognised IP address 1
	.equ	TCPCNS1,0x12h	; TCP connection state
	.equ	TCPCNS2,0x13h	; TCP connection state
	.equ	TCPCMTH,0x1ah	; Did the TCP connect data match?
	.equ	CLOSEW,0x1bh	; state CLOSE_WAIT?
	.equ	PKTACKR,0x1ch	; received a packet ack for this section?
	.equ	TRANPER,0x1dh	; TCP port error
	
	; Maximum segment size definition
	.equ	TCPMSS1,0x00h	; TCP MSS MSB
	.equ	TCPMSS2,0x10h	; TCP MSS LSB (16 bytes)

;
; Application layer defs
;
	.flag	LIGHT,P0.0	; P0.0, the light on/off bit
	.equ	ACTVP1,0x10h	; The port number to listen on
	.equ	ACTVP2,0x00h	; The port number to listen on

;
; main code
;
	.org 0000h
	ljmp	init		; goto init

;
; interrupt vectors
;
	; Timer 0
	.org 000bh
	sjmp	T0INT
	; serial
	.org 0023h
	sjmp	SERINT

;
; interrupt handlers
;
; Timer 0 overflow
;
T0INT:	push	acc
	clr	ea		; disable interrupts
	clr	tr0		; switch off the timer
	mov	th0,#0x4ch	; next reload val ((ffff-b3ff)+8, count up)
	mov	tl0,#0x08h
	setb	tr0		; run timer
	clr	tf0		; clear the overflow
	; add 6400 to ISNs
	clr	CY
	mov	a,SYNB3
	add	a,#0x31h		; add 3100h (12544d)
	mov	SYNB3,a
	jnb	CY,T0INTE	; if no carry, leave
	; carry to Byte 2
T0INTA:	clr	CY
	mov	a,SYNB2
	inc	a
	mov	SYNB2,a
	jnb	CY,T0INTE	; if no carry, leave
	; carry to byte 1
	clr	CY
	mov	a,SYNB1
	inc	a
	mov	SYNB1,a
	jnb	CY,T0INTE	; if no carry, leave
	; carry to byte 4
	clr	CY
	mov	a,SYNB4
	inc	a
	mov	SYNB4,a
	jnb	CY,T0INTE	; if no carry, leave
	;carry to byte 3
	clr	CY
	mov	a,SYNB3
	inc	a
	mov	SYNB3,a
	jb	CY,T0INTA	; this shouldn't happen, but!
T0INTE:	pop	acc
	setb	ea		; restart ints
	reti


;
; Serial port interrupt
;
SERINT:	push	psw		; save the psw
	push	acc		; save the accumulator
	push	0x00		; save r0;
	jnb	ri,SERtx	; if recv int is not set goto tx

;
; Serial Receive interrupt
;
SERrx:	clr	ri		; clean the interrupt
	mov	a,sbuf		; copy the sbuf into a

; was the char received valid or not?
	jnb	PKTRECV,Chk2	; if packet reception has not started, check for header

; check for a escaped char
Chk0:	jnb	SLIPESC_R,Chk1	; if SLIPESC_R == 0 goto Chk1
	clr	SLIPESC_R	; SLIPESC_R = 0
	cjne	a,#SLIPESCESC,SEEND	; if char != SLIPESCESC, goto SEEND
SEESC:	mov	a,#SLIPESC	; copy SLIPESC to a
	acall	RxBPS		; but data into the buffer
	sjmp	SERtx		; check ti
SEEND:	mov	a,#SLIPEND
	acall	RxBPS		; but data into the buffer
	sjmp	SERtx		; check ti

; check for a SLIPESC
Chk1:	cjne	a,#SLIPESC,Chk2	; did we get a SLIP esc char, if not goto Chk2
	setb	SLIPESC_R	; set SLIPESC_R
	sjmp	SERtx		; dont do anything else

; check for a 0xc0 char and toggle PKTRECV if needed
Chk2:	cjne	a,#SLIPEND,Chk3	; if char was not c0, goto SERtx
	jb	PKTRECV,TogRx	; if packet reception is happening, goto TogRx
	setb	PKTRECV	
	sjmp	SERtx		; goto SERtx
TogRx:	clr	PKTRECV		; turn of reception
	clr	ren		; disable receive
	sjmp	SERtx		; goto SERtx

; default, add char to buffer
Chk3:	jnb	PKTRECV,SERtx	; if we are not receiving a packet, drop the char
	acall	RxBPS		; but data into the buffer
	

; ################################################################

;
; Serial Transmit interrupt
;
SERtx:	jnb	ti,INTsEX	; now ti, then goto int exit
	clr	ti		; clear the interrupt


	jnb	SLSEND1,SCHK3	; if SLSEND1 != 1, goto SCHK3
	jb	SLSEND2,SSLEND	; if SLSEND2 == 1, send a SLIPEND char

	; ordinary send (state 1 0 )
TxCHK0:	jb	TxB_EMPTY,INTrTx	; if empty buf, leave setting SENDOK
	mov	r0,TxB_Tail	; peek at the tail
	mov	a,@r0		; load the contents
	cjne	a,#SLIPEND,TxCHK1	; if a != 0xc0 goto TxCHK1
	mov	sbuf,#SLIPESC	; send SLIPESC
	mov	@r0,#SLIPESCEND	; tail = SLIPESCEND
	sjmp	TxLve		; leave, not setting SENDOK

TxCHK1:	cjne	a,#SLIPESC,TxCHK2	; if a != 0xdb, goto TxCHK2
	mov	sbuf,#SLIPESC	; send SLIPESC
	mov	@r0,#SLIPESCESC	; tail = @r0,SLIPESCESC
	sjmp	TxLve		; leave, not setting SENDOK

TxCHK2:	acall	TxBGS		; get any data from the buffer
	mov	sbuf,a		; send the data from the buffer
	sjmp	TxLve		; leave, not setting SENDOK


	; packet over, send a slip END char (state 1 1)
SSLEND:	jnb	TxB_EMPTY,TxCHK0	; if buf ! empty, send data
	mov	sbuf,#SLIPEND	; else send a SLIPEND
	clr	SLSEND1		; goto state 0 1
	sjmp	TxLve		; leave, not setting SENDOK


SCHK3:	jb	SLSEND2,SSEND1	; if SLSEND2 == 1, send a SLIPEND char
	; nothing sent (state 0 0)
	mov	sbuf,#SLIPEND	; send SLIPEND
	setb	SLSEND1		; goto state 1 0
	sjmp	TxLve		; leave, not setting SENDOK

	; state 0 1 (packet over + trailer sent)
SSEND1:	clr	SLSEND2		; goto state 0 0
	sjmp	INTrTx		; leave, setting SENDOK

	; int leave routines
TxLve:	clr	SENDOK		; sending data, clear SENDOK
	pop	0x00
	pop	acc
	pop	psw
	reti			; interrupt over, return
INTrTx:	setb	SENDOK		; nothing to send, set the SENDOK flag
INTsEX:	pop	0x00
	pop	acc
	pop	psw
	reti			; interrupt over, return


;
; ------------------------------- Transmit section ------------------------------
;

;
; Transmit buffer Put
; 
; Transmit Buffer Put Start
TxBPS:	push	psw		; save some data ; don't save acc, we will return in it
	push	0x00h		; save r0 (can do due to use of only bank 0)
TxBBL:	jb	TxB_FULL,TxBBL	; block if the Tx buffer is full
	clr	ea		; turn off interrupts during this
	mov	r0,TxB_Head	; copy a pointer to the Head
	mov	@r0,a		; copy a to the head
	xch	a,r0		; swap a and r0
	inc	a		; add one ( a contains calc addr
	cjne	a,#TxB_End,TxBPW	; if a == Buffer End, wrap
	mov	a,#TxB_Start
TxBPW:	mov	TxB_Head,a	; copy back the head pointer
	cjne	a,TxB_Tail,TxBPQ	; if a == Buf_Tail, set flags and leave
	setb	TxB_FULL
TxBPQ:	clr	TxB_EMPTY	; tell TxBG there is new data
; begin tidying up
	mov	a,r0		; restore a
	jnb	SENDOK,TxBPE	; if SENDOK == 0, goto TxBPE
	setb	ti		; trigger a send
; Transmit Buffer Put End
TxBPE:	pop	0x00h		; restore r0 (can do due to use of only bank 0)
	pop	psw
	setb	ea		; turn on interrupt
	ret

;
; Transmit buffer Get (do notneed to diable ints as in int when called)
; 
; Transmit Buffer Get Start
TxBGS:	push	psw		; save some data ; don't save acc, we will return in it
	push	0x00h		; save r0 (can do due to use of only bank 0)
	jb	TxB_EMPTY,TxBGE	; leave if buffer is empty
	mov	r0,TxB_Tail	; copy the tail pointer
	mov	a,@r0		; copy the data out
	xch	a,r0		; exchange r0 and a
	inc	a		; increment the Tail pointer
	cjne	a,#TxB_End,TxBGW	; if a == Buffer End, wrap
	mov	a,#TxB_Start
TxBGW:	mov	TxB_Tail,a	; copy the tail pointer back
	cjne	a,TxB_Head,TxBGQ	; if a == Buffer Head, set flags and leave
	setb	TxB_EMPTY	; buffer empty
TxBGQ:	clr	TxB_FULL	; let put know there is more space
; Transmit Buffer Get End
	mov	a,r0		; restore origonal data
TxBGE:	pop	0x00h		; restore r0 (can do due to use of only bank 0)
        pop	psw
	ret


;
; ------------------------------- Receive section ------------------------------
;

;
; Receive buffer Put
; 
; Receive Buffer Put Start
RxBPS:	push	psw		; save some data ; don't save acc, we will return in it
	push	0x00h		; save r0 (can do due to use of only bank 0)
	;jb	RxB_FULL,RxBPE	; leave if the Rx buffer is full
	mov	r0,RxB_Head	; copy a pointer to the Head
	mov	@r0,a		; copy a to the head
	xch	a,r0		; swap a and r0
	inc	a		; add one (a contains calc addr)
	cjne	a,#RxB_End,RxBPW	; if a == Buffer End, wrap
	mov	a,#RxB_Start
RxBPW:	mov	RxB_Head,a	; copy back the head pointer
	cjne	a,RxB_Tail,RxBPQ1	; if a == Buf_Tail, set flags and leave
	clr	ren		; disable serial reception
	setb	RxB_FULL	; set buf_full flag
	sjmp	RxBPQ2

RxBPQ1:	inc	a
	cjne	a,#RxB_End,RxBPW1	; if a == Buffer End, wrap
	mov	a,#RxB_Start
RxBPW1:	cjne	a,RxB_Tail,RxBPQ2
	clr	ren
	setb	RxB_FULL

RxBPQ2:	clr	RxB_EMPTY	; tell RxBG there is new data
; Receive Buffer Put End
	mov	a,r0		; copy back the origonal data
RxBPE:	pop	0x00h		; restore r0 (can do due to use of only bank 0)
	pop	psw
	ret

;
; Receive buffer Get
; 
; Receive Buffer Get Start
RxBGS:	push	psw		; save some data ; don't save acc, we will return in it
	push	0x00h		; save r0 (can do due to use of only bank 0)
RxBBL:	jb	RxB_EMPTY,RxBBL	; block if buffer is empty
	clr	ea		; turn off interrupts during this
	mov	r0,RxB_Tail	; copy the tail pointer
	mov	a,@r0		; copy the data in
	xch	a,r0		; exchange r0 and a
	inc	a		; increment the Tail pointer
	cjne	a,#RxB_End,RxBGW	; if a == Buffer End, wrap
	mov	a,#RxB_Start
RxBGW:	mov	RxB_Tail,a	; copy the tail pointer back
	cjne	a,RxB_Head,RxBGQ	; if a == Buffer Head, set flags and leave
	setb	RxB_EMPTY	; buffer empty
RxBGQ:	clr	RxB_FULL	; let put know there is more space
	mov	a,r0		; restore origonal data
	jnb	PKTRECV,RxBGE	; if end of packet received, do not re-enable ren
	setb	ren		; and re-enable serial recption
; Receive Buffer Get End
RxBGE:	pop	0x00h		; restore r0 (can do due to use of only bank 0)
        pop	psw
	setb	ea		; turn on interrupts
	ret

;
; ***************************************************************************
;
; IP layer routines

; IP checksum calculation
; checksum - byte 1
IPCHKSM:	push	psw
	push	acc
	cjne	a,#0x00,IPCK1M	; if it is not zero, do stuff
	sjmp	IPCK1E		; if zero, exit
IPCK1M:	jb	CHECKSUMB,IPCK2M	; is CHECKSUMB is set, goto IPCK2M
	clr	CY
	add	a,r6		; checksum counting
	mov	r6,a		; copy back
	jnb	CY,IPCK1E	; if no carry, goto IPB1
	clr	CY
	mov	a,r7		; wrap round
	add	a,#0x01h
	mov	r7,a		; copy back
	jnb	CY,IPCK1E	; if no carry, goto IPB1
	clr	CY
	mov	a,r6
	add	a,#0x01h
	mov	r6,a		; copy back
IPCK1E:	cpl	CHECKSUMB
	pop	acc
	pop	psw
	ret
; checksum - byte 2
IPCK2M:	clr	CY
	add	a,r7		; checksum counting
	mov	r7,a		; copy back
	jnb	CY,IPCK2E	; if no carry, goto IPB1
	clr	CY
	mov	a,r6		; wrap round
	add	a,#0x01h
	mov	r6,a		; copy back
	jnb	CY,IPCK2E	; if no carry, goto IPB1
	clr	CY
	mov	a,r7
	add	a,#0x01h
	mov	r7,a		; copy back
IPCK2E:	cpl	CHECKSUMB
	pop	acc
	pop	psw
	ret

; generate the IP identification field
IPntID:	push	psw
	clr	CY
	inc	IPIDLSB
	jnb	CY,IPntIDE
	clr	CY
	inc	IPIDMSB
IPntIDE:	pop	psw
	ret

; generate the rest of the IP checksum ( may be probs as using 2 embeded
; function calls
	; the source IP
IPGCHK:	push	psw
	push	acc
	mov	a,#IPADDRB1
	acall	IPCHKSM
	mov	a,#IPADDRB2
	acall	IPCHKSM
	mov	a,#IPADDRB3
	acall	IPCHKSM
	mov	a,#IPADDRB4
	acall	IPCHKSM
	; the dest IP
	mov	a,IPSAB1
	acall	IPCHKSM
	mov	a,IPSAB2
	acall	IPCHKSM
	mov	a,IPSAB3
	acall	IPCHKSM
	mov	a,IPSAB4
	acall	IPCHKSM
	pop	acc
	pop	psw
	ret

; send the IP addresses
; again two levels of calls
IPsSDIP: push	acc
	mov	a,#IPADDRB1
	acall	TxBPS
	mov	a,#IPADDRB2
	acall	TxBPS
	mov	a,#IPADDRB3
	acall	TxBPS
	mov	a,#IPADDRB4
	acall	TxBPS
	; the dest IP
	mov	a,IPSAB1
	acall	TxBPS
	mov	a,IPSAB2
	acall	TxBPS
	mov	a,IPSAB3
	acall	TxBPS
	mov	a,IPSAB4
	acall	TxBPS
	pop	acc
	ret

;send the IP header, length 0f packet is stored in r2,r3
IPhTxS:	push	acc
	push	0x00h		; push r0
	push	0x02h		; push r2
	push	0x03h		; push r2
	clr	CHECKSUMB
	; send IP header
	; version + header len
	mov	a,#0x45h
	mov	r6,a
	acall	TxBPS
	; TOS
	mov	a,#0x00h
	mov	r7,a
	acall	TxBPS
	; calculate the correct length
	mov	a,r3		; copy in the LSB
	add	a,#0x14h		; add the IP header len
	mov	r3,a
	jnb	CY,IPhTxL	; if no carry, goto IPhTxL
	clr	CY
	inc	r3
	; length MSB
IPhTxL:	mov	a,r2
	acall	TxBPS
	acall	IPCHKSM
	; length	LSB
	mov	a,r3
	acall	TxBPS
	acall	IPCHKSM
	; 16 bit ID
	acall	IPntID
	mov	a,IPIDMSB
	acall	IPCHKSM
	acall	TxBPS
	mov	a,IPIDLSB
	acall	IPCHKSM
	acall	TxBPS
	; fragmentation
	mov	a,#0x00h
	acall	TxBPS
	acall	TxBPS
	; TTL
	mov	a,#0xffh
	acall	IPCHKSM
	acall	TxBPS
	; proto
	mov	a,r5
	acall	IPCHKSM
	acall	TxBPS
	; checksum - need to calc rest of header before sending
	mov	a,#IPADDRB1
	acall	IPCHKSM
	mov	a,#IPADDRB2
	acall	IPCHKSM
	mov	a,#IPADDRB3
	acall	IPCHKSM
	mov	a,#IPADDRB4
	acall	IPCHKSM
	; the dest IP
	mov	a,IPSAB1
	acall	IPCHKSM
	mov	a,IPSAB2
	acall	IPCHKSM
	mov	a,IPSAB3
	acall	IPCHKSM
	mov	a,IPSAB4
	acall	IPCHKSM
	; send the checksum
	mov	a,r6
	cpl	a
	acall	TxBPS
	mov	a,r7
	cpl	a
	acall	TxBPS
	; send the IP
	mov	a,#IPADDRB1
	acall	TxBPS
	mov	a,#IPADDRB2
	acall	TxBPS
	mov	a,#IPADDRB3
	acall	TxBPS
	mov	a,#IPADDRB4
	acall	TxBPS
	; the dest IP
	mov	a,IPSAB1
	acall	TxBPS
	mov	a,IPSAB2
	acall	TxBPS
	mov	a,IPSAB3
	acall	TxBPS
	mov	a,IPSAB4
	acall	TxBPS
	pop	0x03h
	pop	0x02h
	pop	0x00h
	pop	acc
	ret
	
	
	

;
;****************************************************************************
;

;
; Transport layer functions
;

; add a number to the Ack fields, pass value to add in r0?
TCPARSQ:	push	acc
TCPASq1:	clr	CY		; start byte 1
	mov	a,REMSEQ4
	add	a,r0
	mov	REMSEQ4,a
	jnb	CY,TCPASqE	; no carry, therefore leave
	clr	CY		; start byte 2
	mov	a,REMSEQ3
	add	a,#0x01
	mov	REMSEQ3,a
	jnb	CY,TCPASqE	; no carry, therefore leave
	clr	CY		; start byte 3
	mov	a,REMSEQ2
	add	a,#0x01
	mov	REMSEQ2,a
	jnb	CY,TCPASqE	; no carry, therefore leave
	clr	CY		; start byte 4
	mov	a,REMSEQ1
	add	a,#0x01
	mov	REMSEQ1,a
	jb	CY,TCPASq1	; carry, therefore goto start
TCPASqE:	pop	acc
	ret


; add a number to the Ack fields, pass value to add in r0?
TCPASyn:	push	acc
TCPASy1:	clr	CY		; start byte 1
	mov	a,SEQNUM4
	add	a,r0
	mov	SEQNUM4,a
	jnb	CY,TCPASyE	; no carry, therefore leave
	clr	CY		; start byte 2
	mov	a,SEQNUM3
	add	a,#0x01
	mov	SEQNUM3,a
	jnb	CY,TCPASyE	; no carry, therefore leave
	clr	CY		; start byte 3
	mov	a,SEQNUM2
	add	a,#0x01
	mov	SEQNUM2,a
	jnb	CY,TCPASyE	; no carry, therefore leave
	clr	CY		; start byte 4
	mov	a,SEQNUM1
	add	a,#0x01
	mov	SEQNUM1,a
	jb	CY,TCPASy1	; carry, therefore goto start
TCPASyE:	pop	acc
	ret


; grab the current sequence number and place it in the Sequence number field
; TCP Get Seuqence Number
TCPGSN:	push	acc
	mov	SEQNUM1,SYNB1
	mov	SEQNUM2,SYNB2
	mov	SEQNUM3,SYNB3
	mov	SEQNUM4,SYNB4
	pop	acc
	ret


; Main TCP reset, called when closing a connection
TCPMRST:	mov	RSPORT1,#0x00h	; TCP remote port
	mov	RSPORT2,#0x00h
	; TCP remote IP number
	mov	RIPNUM1,#0x00h
	mov	RIPNUM2,#0x00h
	mov	RIPNUM3,#0x00h
	mov	RIPNUM4,#0x00h
	; TCP sequence numbers
	mov	SEQNUM1,#0x00h
	mov	SEQNUM2,#0x00h
	mov	SEQNUM3,#0x00h
	mov	SEQNUM4,#0x00h
	; TCP remote sequence number
	mov	REMSEQ1,#0x00h
	mov	REMSEQ2,#0x00h
	mov	REMSEQ3,#0x00h
	mov	REMSEQ4,#0x00h
	; TCP connection state
	clr	TCPCNS1
	clr	TCPCNS2
	clr	CLOSEW
	clr	PKTACKR
	clr	TRANPER
	ret

; Send a TCP header + data
TCPHDRS:	push	acc
	push	0x00h
	push	0x01h
	; initalise the header checksum +
	; send the source port number
	clr	CHECKSUMB
	mov	a,DPORT1
	mov	r6,DPORT1
	acall	TxBPS
	mov	a,DPORT2
	mov	r7,DPORT2
	acall	TxBPS
	; send the dest port
	mov	a,RSPORT1
	acall	IPCHKSM
	acall	TxBPS
	mov	a,RSPORT2
	acall	IPCHKSM
	acall	TxBPS
	; send my sequence number
	mov	a,SEQNUM1
	acall	IPCHKSM
	acall	TxBPS
	mov	a,SEQNUM2
	acall	IPCHKSM
	acall	TxBPS
	mov	a,SEQNUM3
	acall	IPCHKSM
	acall	TxBPS
	mov	a,SEQNUM4
	acall	IPCHKSM
	acall	TxBPS
	; send an ack number
	mov	a,REMSEQ1
	acall	IPCHKSM
	acall	TxBPS
	mov	a,REMSEQ2
	acall	IPCHKSM
	acall	TxBPS
	mov	a,REMSEQ3
	acall	IPCHKSM
	acall	TxBPS
	mov	a,REMSEQ4
	acall	IPCHKSM
	acall	TxBPS
	; send the header length
	mov	a,r2
	swap	a
	acall	IPCHKSM
	acall	TxBPS
	; send the flags
	mov	a,#0x00h
	jnb	TCPSFIN,TCPTSyn	; if the Fin flag not set, goto TCPFSyn
	setb	acc.0
TCPTSyn:	jnb	TCPSSYN,TCPTRst	; if the Syn flag not set, goto TCPFRst
	setb	acc.1
TCPTRst:	jnb      TCPSRST,TCPTPsh	; if the Rst flag not set, goto TCPFPsh
	setb	acc.2
TCPTPsh:	jnb      TCPSPSH,TCPTAck	; if the Psh flag not set, goto TCPFAck
	setb	acc.3
TCPTAck:	jnb      TCPSACK,TCPTUrg	; if the Ack flag not set, goto TCPFUrg
	setb	acc.4
TCPTUrg:	jnb      TCPSURG,TCPTEND	; if the Urg flag not set, goto TCPFEND
	setb	acc.5

TCPTEND:	acall	IPCHKSM
	acall	TxBPS
	; send a window size, this may need a fix, don't know how
	; UNIX will respond to window size == MSS

	; if a TCP error occourred, send a window size of 0
	jb	TRANPER,TCPSWS0
	; normal
	mov	a,#TCPMSS1
	acall	IPCHKSM
	acall	TxBPS
	mov	a,#TCPMSS2
	acall	IPCHKSM
	acall	TxBPS
	sjmp	TCPSWS1
	; error send ws = 0
TCPSWS0:	mov	a,#0x00h
	acall	TxBPS
	acall	TxBPS
	
	; need to calc rest of packet (data + options) never send URG data
TCPSWS1:	mov	DPTR,#0x00ffh
	mov	a,r2
	clr	CY
	mov	b,#0x04
	mul	ab
	clr	CY
	add	a,r3		; this will not carry as data = small
	mov	r0,a		; backup for next bit

	; add sudo header to checksum
	acall	IPGCHK		; the IPs
	cpl	CHECKSUMB
	acall	IPCHKSM		; the length
	cpl	CHECKSUMB
	mov	a,#0x06h		; the protocol
	acall	IPCHKSM
	
	; calculate the packet len - header len = (data + options len)
	clr	CY
	mov	a,r0
	subb	a,#0x14h		; take 20 bytes from tcp header len
	mov	r0,a		; backup for next bit
	mov	r1,a		; backup for next bit
	; checksum data
TCPCCL1:	cjne	r0,#0x00h,TCPCCL2
	sjmp	TCPCCL3

TCPCCL2:	movx	a,@DPTR
	acall	IPCHKSM
	dec	DPL
	dec	r0
	sjmp	TCPCCL1

	
	
	; send checksum
TCPCCL3:	mov	a,r6
	cpl	a
	acall	TxBPS
	mov	a,r7
	cpl	a
	acall	TxBPS
	; send urgent pointer
	mov	a,#0x00h
	acall	TxBPS
	acall	TxBPS
	
	; send data
	mov	DPTR,#0x00ffh
TCPCCL4:	cjne	r1,#0x00h,TCPCCL5
	sjmp	TCPCCL6

TCPCCL5:	movx	a,@DPTR
	acall	TxBPS
	dec	DPL
	dec	r1
	sjmp	TCPCCL4

TCPCCL6:	pop	0x01
	pop	0x00
	pop	acc
	ret
	
;
; compare the incoming IP + port top the one stored for the connection
;
TCPCIPP:	push	acc
	clr	TCPCMTH
	; Compare source ports
	mov	a,RSPORT1
	cjne	a,SPORT1,TCPMERR
	mov	a,RSPORT2
	cjne	a,SPORT2,TCPMERR
	; Compare dest ports
	mov	a,DPORT1
	cjne	a,#ACTVP1,TCPMERR
	mov	a,DPORT2
	cjne	a,#ACTVP2,TCPMERR
	; compare IP addrs
	mov	a,IPSAB1
	cjne	a,RIPNUM1,TCPMERR
	mov	a,IPSAB2
	cjne	a,RIPNUM2,TCPMERR
	mov	a,IPSAB3
	cjne	a,RIPNUM3,TCPMERR
	mov	a,IPSAB4
	cjne	a,RIPNUM4,TCPMERR
	sjmp	TCPCIPE
TCPMERR:	setb	TCPCMTH		; match failure
TCPCIPE:	pop	acc
	ret

;
; compare sequence numbers ( seq number I send + ack num returned)
;
TCPCSYN:	push	acc
	clr	TCPCMTH
	; compare data
	mov	a,SEQNUM1
	cjne	a,ACKB1,TCPSERR
	mov	a,SEQNUM2
	cjne	a,ACKB2,TCPSERR
	mov	a,SEQNUM3
	cjne	a,ACKB3,TCPSERR
	mov	a,SEQNUM4
	cjne	a,ACKB4,TCPSERR
	sjmp	TCPCSYE
TCPSERR:	setb	TCPCMTH
TCPCSYE:	pop	acc
	ret


;
; compare sequence numbers ( seq number received + remote seq num)
;
TCPCRSY:	push	acc
	clr	TCPCMTH
	; compare data
	mov	a,TMPSEQ1
	cjne	a,REMSEQ1,TCPRSER
	mov	a,TMPSEQ2
	cjne	a,REMSEQ2,TCPRSER
	mov	a,TMPSEQ3
	cjne	a,REMSEQ3,TCPRSER
	mov	a,TMPSEQ4
	cjne	a,REMSEQ4,TCPRSER
	sjmp	TCPRSYE
TCPRSER:	setb	TCPCMTH
TCPRSYE:	pop	acc
	ret




;
;****************************************************************************
;

; Application layer function
; interpret byte
APPLINB:	push	acc
	mov	DPTR,#0x00ff

	cjne	a,#0x31h,APPLev1	; = '1'?
	setb	LIGHT
	sjmp	APPLev2
APPLev1:	cjne	a,#0x30h,APPLev3	; = '0'?
	clr	LIGHT

; send "OK"
APPLev2:	mov     r1,#0x02h       ; send "OK"
	mov     a,#'O'
	movx    @DPTR,a
	dec     DPL
	mov     a,#'K'
	movx    @DPTR,a
	sjmp    APPLend

APPLev3:	cjne    a,#0x53h,APPLinv ; = 'S'?
	mov     r1,#0x01h       ; send "ON/OFF"
	jb      LIGHT,APPLev4    ; if ON goto UDPev4
	; light OFF
	mov     r1,#0x03h
	mov     a,#'O'
	movx    @DPTR,a
	dec     DPL
	mov     a,#'F'
	movx    @DPTR,a
	dec     DPL
	mov     a,#'F'
	movx    @DPTR,a
	sjmp    APPLend

	; light ON
APPLev4:	mov     r1,#0x02h
	mov     a,#'O'
	movx    @DPTR,a
	dec     DPL
	mov     a,#'N'
	movx    @DPTR,a
	sjmp    APPLend

; send and error response
APPLinv:	mov     r1,#0x03h
	mov     a,#'E'
	movx    @DPTR,a
	dec     DPL
	mov     a,#'R'
	movx    @DPTR,a
	dec     DPL
	mov     a,#'R'
	movx    @DPTR,a

APPLend:	pop	acc
	ret

;
;****************************************************************************
;



;
; initalisation routines
;

init:	mov	scon,#0x40h	; 8 bit uart (set ti to start sending data, reception dis)
	mov	tmod,#0x21h	; T1(mode=2), T0(mode=1)
	mov	tcon,0x00h	; set tcon to 0, should be anyway tho.
	mov	th1,#0xfdh	; timer 1  to 9600 baud
	mov	th0,#0xff	; timer 0 MSB
	mov	tl0,#0xf0	; this will cause an interrupt pretty soon
	clr	LIGHT		; turn off the light
	mov	IPIDMSB,#0x00h
	mov	IPIDLSB,#0x00h
	acall	TCPMRST		; reset TCP info
	clr	CHECKSUMB
	setb	SENDOK		; setup SENDOK to be true as we are starting
	setb	tr1		; start timer 1
	setb	tr0		; start timer 0
	mov	ie,#0x92h	; enable interrupts, select serial + T0
;
; Initalise buffers
;
	setb	TxB_EMPTY
	setb	RxB_EMPTY
	mov	TxB_Tail,#TxB_Start	; copy the fifo start into all the vars
	mov	TxB_Head,#TxB_Start
	mov	RxB_Tail,#RxB_Start
	mov	RxB_Head,#RxB_Start
;
; setup SLIP
;
	clr	SLSEND1
	clr	SLSEND2
	sjmp	IPinit		; start doing things

;
; IP packet handling code
;

;
; wait whilst not processing the packet
;
IPnull:	jb	RxB_EMPTY,IPnul1 ; if buffer is empty, is the packet over
	acall	RxBGS		; empty buffer data
IPnul1:	jb	PKTRECV,IPnull	; look whilst receiving a packet


;
; initalise all settings + begin recption of a packet
;
IPinit:	mov	IPCALLEN,#0x00	; zero the calc len
	clr	CHECKSUMB
	mov	IPREMLEN1,#0x00
	mov	IPREMLEN2,#0x00
	mov	TRANERR,#0x00	; clear any transport errors
	setb	ren		; enable serial reception
PKTWte:	jnb	PKTRECV,PKTWte	; wait whilst no packet

; get a packet
IPst:	acall	RxBGS		; get the data (blocking)
	mov	r6,a		; checksum counting
	mov	IPHB1,a

; the IP version
	anl	a,#0b11110000	; get the version
	swap	a		; swap nibbles
	cjne	a,#0x04,IPnull	; if the version != 4 goto IPnull

; the IP header length
	mov	a,r6		; quick copy back the origonal data
	anl	a,#0b00001111	; mask for IP header length
	mov	IPREPLEN,a	; save the reported length

; the TOS bits (ignore)
	acall	RxBGS		; get the data (blocking)
	mov	r7,a		; checksum counting
	mov	IPTOS,a

; total length
; first byte
	acall	RxBGS		; get the data (blocking)
	acall	IPCHKSM		; checksum - byte 1
	mov	IPTOTLEN1,a	; save the total len (high bits)
; the second byte
IPB1:	acall	RxBGS		; get the data (blocking)
	acall	IPCHKSM		; checksum - byte 2
	inc	IPCALLEN	; increment the length by 1 word
	mov	IPTOTLEN2,a	; save the total len (low bits)


; 16 bit identification
IPID1:	acall	RxBGS		; get the data (blocking)
	mov	IPIPID1,a
	acall	IPCHKSM		; checksum - byte 1
; the second byte
IPID2:	acall	RxBGS		; get the data (blocking)
	mov	IPIPID2,a
	acall	IPCHKSM		; checksum - byte 2

; IP framgmentation, don't accept any fragments other than the first,
; may need to check TCP doesn't cause fragmentation in setup
; + adjust MTU approp
IPFR1:	acall	RxBGS		; get the data (blocking)
	acall	IPCHKSM		; checksum - byte 1
	jnb	acc.6,IPFR3
	setb	IPDFF
IPFR3:	anl	a,0b00011111	; get the low bits
	cjne	a,#0x00,IPnull	; if bad fragment, ignore packet
IPFR2:	acall	RxBGS		; get the data (blocking)
	cjne	a,#0x00,IPnull	; if bad fragment, ignore packet
	acall	IPCHKSM		; checksum - byte 2
	inc	IPCALLEN	; increment the length by 1 word

; TTL
IPTTL:	acall	RxBGS		; get the data (blocking)
	mov	IPTTLB,a
	acall	IPCHKSM		; checksum - byte 1

; protocol
IPPROT:	acall	RxBGS		; get the data (blocking)
	acall	IPCHKSM		; checksum - byte 2
	mov	r5,a		; copy the proto to r5 temporarly

; header checksum
	acall	RxBGS		; get the data (blocking)
	mov	IPICHK1,a
	acall	IPCHKSM		; checksum - byte 1
	acall	RxBGS		; get the data (blocking)
	mov	IPICHK2,a
	acall	IPCHKSM		; checksum - byte 2
	inc	IPCALLEN	; increment the length by 1 word

; Source IP address - move into loops
	acall	RxBGS		; get the data (blocking)
	acall	IPCHKSM		; checksum - byte 1
	mov	IPSAB1,a	; copy the source address
	acall	RxBGS		; get the data (blocking)
	acall	IPCHKSM		; checksum - byte 2
	mov	IPSAB2,a	; copy the source address
	acall	RxBGS		; get the data (blocking)
	acall	IPCHKSM		; checksum - byte 1
	mov	IPSAB3,a	; copy the source address
	acall	RxBGS		; get the data (blocking)
	acall	IPCHKSM		; checksum - byte 2
	mov	IPSAB4,a	; copy the source address
	inc	IPCALLEN	; increment the length by 1 word

; Destination IP address
	acall	RxBGS		; get the data (blocking)
	acall	IPCHKSM		; checksum - byte 1
	cjne	a,#IPADDRB1,IPDIPE	; compare the dest with my address
	acall	RxBGS		; get the data (blocking)
	acall	IPCHKSM		; checksum - byte 2
	cjne	a,#IPADDRB2,IPDIPE	; compare the dest with my address
	acall	RxBGS		; get the data (blocking)
	acall	IPCHKSM		; checksum - byte 1
	cjne	a,#IPADDRB3,IPDIPE	; compare the dest with my address
	acall	RxBGS		; get the data (blocking)
	acall	IPCHKSM		; checksum - byte 2
	cjne	a,#IPADDRB4,IPDIPE	; compare the dest with my address
	inc	IPCALLEN	; increment the length by 1 word
	sjmp	IPOPT		; jump to the IP options

IPDIPE:	ajmp	IPnull		; packet dead, leave it alone
	
; loop while the calc header len < reported header len, it ignore options
IPOPT:	mov	a,IPREPLEN	; move the reported length into a
	clr	CY		; precaution
	subb	a,IPCALLEN	; leave a number indicating no. of words left

IPOPTL:	cjne	a,#0x00,IPOTGC	; is calc = rep?
	sjmp	IPCCRC		; calc == rep
; loop whilst incorrect length
IPOTGC:	mov	r0,a		; calc != rep
	mov	DPTR,#0x00ff
IPOTGD:	acall	RxBGS		; get byte
	movx	@DPTR,a
	dec	DPL
	acall	IPCHKSM		; checksum - byte 1
	acall	RxBGS		; get byte
	movx	@DPTR,a
	dec	DPL
	acall	IPCHKSM		; checksum - byte 2
	acall	RxBGS		; get byte
	movx	@DPTR,a
	dec	DPL
	acall	IPCHKSM		; checksum - byte 1
	acall	RxBGS		; get byte
	movx	@DPTR,a
	dec	DPL
	acall	IPCHKSM		; checksum - byte 2
	djnz	r0,IPOTGD	; decrement len and go again if necc
	
; check the checksum calc
IPCCRC:	cjne	r6,#0xff,IPDIPE	; checksum error, ignore packet
	cjne	r7,#0xff,IPDIPE	; checksum error, ignore packet

; copy total len to remaining len
	mov	IPREMLEN1,IPTOTLEN1
	mov	IPREMLEN2,IPTOTLEN2
; change the IP total length into the remaining length
	mov	a,IPREPLEN	; copy the reported header len
	mov	b,#0x04		; going to mul by 4
; note do not need to check for overflow as 0b1111 * 4 < 255
	mul	ab		; multiply to get bytes
; result now in acc, subtract low order bits from total len + update
	mov	r0,a		; copy the header len into r0
IPRMLC:	mov	a,IPREMLEN2	; copy the LSBs into a
	clr	CY
	clr	OV
	subb	a,r0		; subtract the two
	mov	IPREMLEN2,a	; copy the remaining length back
	jnb	CY,IPPRS1	; no carry needed, goto next
; oops, you carried
	mov	a,IPREMLEN1	; copy the senior bits
	clr	CY
	clr	OV
	mov	r0,#0x01
	subb	a,r0		; subtract 1 from the MSBs
	mov	IPREMLEN1,a
	jb	CY,IPRMLC	; remove one from the LSBs if there is a carry
	
	
; make a decision on the protocol to reply with
IPPRS1:	cjne	r5,#0x06h,IPPRS2	; is it TCP?
	ljmp	TCPst		; TCP packet found, eval.
IPPRS2:	cjne	r5,#0x01h,IPPRS3	; Is it ICMP?
	sjmp	ICMPst		; ICMP message found, eval
IPPRS3:	cjne	r5,#0x11h,IPPRS4	; UDP packet
	ljmp	UDPst
IPPRS4:	ajmp	IPnull		; unknown, ignore packet




;***********************************************************************
; ICMP message evaluation
ICMPst:	acall	RxBGS
	
	; decide what message was sent
	cjne	a,#0x08h,ICMPn1	; not ICMP echo request, then goto ICMPn1
	; ICMP echo request received - goto ICMPers
	clr	CHECKSUMB
	sjmp	ICMPers

	; unknown message received
ICMPn1:	sjmp	ICMPn1		; loop here for a bit
	

; start ICMP echo reply code
; this is possibly the hardest thing to do,
; need to store all of the packet
; if the rmaining length of the packet is > 255, dispose, it's too big
;calculate the total length
ICMPers:	mov	a,IPREMLEN2
	mov	r1,IPREMLEN1
	clr	CY
	add	a,#20d
	mov	r0,a
	jnb	CY,ICMPer1	; no carry?, then goto ICMPer1
	clr	CY
	inc	r1
	; if the packet len > 255, dump it, have not got enough room
	cjne	r1,#0x00h,IPPRS2	; if length too long, dispose of packet
	; start sending the packet
ICMPer1:	mov	a,#0x45h
	acall	TxBPS
	mov	r6,a		; checksum calc
	mov	a,#0x00h
	acall	TxBPS
	mov	r7,a		; checksum calc
	; send the length
	mov	a,r1
	acall	IPCHKSM
	acall	TxBPS
	mov	a,r0
	acall	IPCHKSM
	acall	TxBPS
	;send the packet id
	acall	IPntID
	mov	a,IPIDMSB
	acall	IPCHKSM
	acall	TxBPS
	mov	a,IPIDLSB
	acall	IPCHKSM
	acall	TxBPS

	; receive some data and get it out of the way
	; do it here so as to fill the output buffer 
	acall	RxBGS
	acall	RxBGS
	acall	RxBGS
	; subtract 4 from the length, (type + code + checksum16)
	; note we will not be doing ICMP checksumming
	; as it is irrelevant in ping
	
	; now the id should be in the buffer

	;fragmentation stuff
	mov	a,#0x00h
	acall	TxBPS ; should not need checksum as will not do anything
	acall	TxBPS ; ditto
	; TTL
	mov	a,#0xffh
	acall	IPCHKSM
	acall	TxBPS
	; proto
	mov	a,#0x01h
	acall	IPCHKSM
	acall	TxBPS
	; generate checksum + send it
	acall	IPGCHK
	; send the checksum
	mov	a,r6
	cpl	a
	acall	TxBPS
	mov	a,r7
	cpl	a
	acall	TxBPS
	; send the ip 
	acall	IPsSDIP
	;------ END IP HEADER ------;
	; send ICMP echo
	mov	a,#0x00
	acall	TxBPS		; send type
	mov	r6,#0x00		; initalise checksum
	mov	r7,#0x00
	acall	TxBPS		; send code
	; can't send any more until checksum over
	; receive data
	mov	a,IPREMLEN2
	; subtract 4 from earlier comment
	clr	CY
	subb	a,#0x04
	mov	IPREMLEN2,a
	mov	r0,a
	mov	DPTR,#0x00ffh
	; receive singly - enhance to use fewer instrs
ICMPer2:	cjne	r0,#0x00h,ICMPer3
	sjmp	ICMPere
ICMPer3:	acall	RxBGS
	movx	@DPTR,a
	acall	IPCHKSM
	dec	DPL
	dec	r0
	sjmp	ICMPer2
ICMPere:	mov	a,r6	; send the checksum
	cpl	a
	acall	TxBPS
	mov	a,r7
	cpl	a
	acall	TxBPS
	; send the data
	mov	r0,IPREMLEN2
	mov	DPTR,#0x00ffh
ICMPer4:	cjne	r0,#0x00h,ICMPer5
	sjmp	ICMPerf
ICMPer5:	movx	a,@DPTR
	acall	TxBPS
	dec	DPL
	dec	r0
	sjmp	ICMPer4
ICMPerf:	ljmp	IPpkte



; send an ICMP port unreachable error
; ICMP error evaluation
ICMPeev:	mov	a,TRANERR
	cjne	a,#0x01,ICMPeew	; ICMP != port unr, goto next
	; ICMP port unreachable
	; setup type + code in regs
	mov	r0,#0x03h	; type
	mov	r1,#0x03h	; code
	sjmp	ICMP3ss		; send a type 3 message

ICMPeew:	ajmp	IPnull

; ** other ICMP codes (type 3 only I think) can jump in here
; work out the correct length for the packet (- IP header)
; = ICMP header + Incoming IP header + incoming transport layer header
ICMP3ss:	mov	r2,#0x00h
	; 4 bytes ICMP header + 4 bytes of 0 for type 3 ICMP errors
	; and 8 bytes of the transport header, only var is IP header
	mov	r3,#0x10h	; 4 + 4 + 8
	mov	a,IPHB1		; add IP header length
	anl	a,#0x0fh
	mov	b,#0x04h
	clr	CY
	mul	AB		; check no carry, should be imposs
	add	a,r3
	mov	r3,a
	jnb	CY,ICMPipe
	clr	CY
	inc	r2

	; set up the new protocol (ICMP)
ICMPipe:	mov	a,r5		; copy the proto
	mov	r5,#0x01h	; send ICMP
	acall	IPhTxS		; send IP header
	mov	r5,a		; copy back proto

	; send ICMP header
	clr	CHECKSUMB	; setup ICMP checksum
	; trick, the old IP header checksum will always be 0xFF 0xFF
	; most of the calculation is already done for us :)
	; only need to add ICMP header + transport header
	mov	r6,#0xff
	mov	r7,#0xff
	
	; send ICMP header
	mov	a,r0		; send type
	acall	IPCHKSM
	acall	TxBPS
	mov	a,r1		; send code
	acall	IPCHKSM
	acall	TxBPS

	; send the port numbers 
	mov	a,SPORT1
	acall	IPCHKSM
	mov	a,SPORT2
	acall	IPCHKSM
	mov	a,DPORT1
	acall	IPCHKSM
	mov	a,DPORT2
	acall	IPCHKSM

	; calc the rest of checksum ( first 8 bytes of transport header )
	cjne	r5,#0x11h,ICMPhc1
	; UDP
	; checksum the length
	mov	a,IPREMLEN1
	acall	IPCHKSM
	mov	a,IPREMLEN2
	acall	IPCHKSM
	; checksum the checksum
	mov	a,TRANCHK1
	acall	IPCHKSM
	mov	a,TRANCHK2
	acall	IPCHKSM

ICMPhc1:	cjne	r5,#0x06h,ICMPhce
	; TCP
	mov	a,REMSEQ1
	acall	IPCHKSM
	mov	a,REMSEQ2
	acall	IPCHKSM
	mov	a,REMSEQ3
	acall	IPCHKSM
	mov	a,REMSEQ4
	acall	IPCHKSM
	

ICMPhce:	mov	a,r6		; send checksum 0
	cpl	a
	acall	TxBPS
	mov	a,r7		; send checksum 1
	cpl	a
	acall	TxBPS
	mov	a,#0x00h		; send 4 0s as RFC says
	acall	TxBPS
	acall	TxBPS
	acall	TxBPS
	acall	TxBPS

	; send old IP header
	mov	a,IPHB1
	acall	TxBPS
	mov	a,IPTOS
	acall	TxBPS
	mov	a,IPTOTLEN1
	acall	TxBPS
	mov	a,IPTOTLEN2
	acall	TxBPS
	mov	a,IPIPID1
	acall	TxBPS
	mov	a,IPIPID2
	acall	TxBPS
	mov	a,#0x00h
	jnb	IPDFF,ICMPdnf
	setb	acc.6
ICMPdnf:	acall	TxBPS
	mov	a,#0x00
	acall	TxBPS
	mov	a,IPTTLB
	acall	TxBPS
	mov	a,r5
	acall	TxBPS
	mov	a,IPICHK1
	acall	TxBPS
	mov	a,IPICHK2
	acall	TxBPS
	; source IP addr
	mov     a,IPSAB1
	acall   TxBPS
	mov     a,IPSAB2
	acall   TxBPS
	mov     a,IPSAB3
	acall   TxBPS
	mov     a,IPSAB4
	acall   TxBPS
	; dest IP addr
	mov     a,#IPADDRB1
	acall   TxBPS
	mov     a,#IPADDRB2
	acall   TxBPS
	mov     a,#IPADDRB3
	lcall   TxBPS
	mov     a,#IPADDRB4
	lcall   TxBPS
	; send any options
	mov	a,IPHB1
	anl	a,#0x0fh
	mov	B,#0x04h
	mul	AB		; can never carry as 0x0f * 0x04 = 0x3c
	clr	CY
	subb	a,#0x14h		; subtract 20 bytes orig header
	mov	r0,a
	mov	DPTR,#0x00ff
ICMPsop:	cjne	r0,#0x00,ICMPso1	; send IP options
	sjmp	ICMPsoe
ICMPso1:	movx	a,@DPTR
	lcall	TxBPS
	dec	DPL
	dec	r0
	sjmp	ICMPsop

	; send 1st 8 bytes of transport header
ICMPsoe:	mov	a,SPORT1
	lcall	TxBPS
	mov	a,SPORT2
	lcall	TxBPS
	mov	a,DPORT1
	lcall	TxBPS
	mov	a,DPORT2
	lcall	TxBPS

	cjne	r5,#0x11h,ICMPhc2
	; UDP
	mov	a,IPREMLEN1
	lcall	TxBPS
	mov	a,IPREMLEN2
	lcall	TxBPS
	mov	a,TRANCHK1
	lcall	TxBPS
	mov	a,TRANCHK2
	lcall	TxBPS

ICMPhc2:	cjne	r5,#0x06h,ICMPhc3
	; TCP
	mov	a,REMSEQ1
	lcall	TxBPS
	mov	a,REMSEQ2
	lcall	TxBPS
	mov	a,REMSEQ3
	lcall	TxBPS
	mov	a,REMSEQ4
	lcall	TxBPS
	
	; end of packet
ICMPhc3:	ljmp	IPpkte


;***********************************************************************
; UDP packet evaluation
UDPdp:	ljmp	IPnull		; destroy the packet
	; UDP eval
UDPst:	clr	CHECKSUMB	; reset checksum calc
	lcall    RxBGS		; source port
	mov	r6,a
	mov	SPORT1,a
	lcall	RxBGS
	mov	r7,a
	mov	SPORT2,a
	; destination port
	lcall	RxBGS
	cjne	a,#ACTVP1,UDPpt1	; check port = active, if not send ICMP mess
	sjmp	UDPpt2
UDPpt1:	mov	TRANERR,#0x01h	; set port err

UDPpt2:	lcall	IPCHKSM
	mov	DPORT1,a
	lcall	RxBGS
	cjne	a,#ACTVP2,UDPpt3	; check port = active, if not send ICMP mess
	sjmp	UDPpt4
UDPpt3:	mov	TRANERR,#0x01h	; set port err

UDPpt4:	lcall	IPCHKSM
	mov	DPORT2,a
	; the length
	lcall	RxBGS
	lcall	IPCHKSM
	mov	IPREMLEN1,a
	lcall	RxBGS
	lcall	IPCHKSM
	mov	IPREMLEN2,a
	; rechecksum the two above for the checksum
	mov	a,IPREMLEN1
	lcall	IPCHKSM
	mov	a,IPREMLEN2
	lcall	IPCHKSM
	; receive the UDP checksum
UDPchkr:	lcall	RxBGS
	mov	TRANCHK1,a
	lcall	IPCHKSM
	lcall	RxBGS
	mov	TRANCHK2,a
	lcall	IPCHKSM
	; add sudo header to checksum fields
	; IP
	lcall	IPGCHK
	; the rest
	mov	a,#0x00		; dummy
	lcall	IPCHKSM
	mov	a,#0x11h		; proto
	lcall	IPCHKSM

; initalise Data Receive
UDPdr1:	clr	RECCMD
	mov	r0,IPREMLEN1	; load the MSBs
	mov	a,IPREMLEN2	; load the LSBs
	; calculate the remainain packet length
	clr	CY
	subb	a,#0x08h
	mov	r1,a
	jnb	CY,UDPdr2	; no carry?, then goto UDPdr2
	clr	CY
	dec	r0
	
	; check if LSBs = 0
UDPdr2:	cjne	r1,#0x00,UDPdr3
	; LSB = 0, dec MSB ( if not 0 already)
	cjne	r0,#0x00,UDPdr5
	sjmp	UDPdre
	; decrement MSBs
UDPdr5:	dec	r0
	sjmp	UDPdr3


	; read data
UDPdr3:	lcall	RxBGS
	lcall	IPCHKSM
	; interpret commands
	jb	RECCMD,UDPdr6
	mov	CMDVAL,a
	setb	RECCMD
	; end interpertation
UDPdr6:	dec	r1
	sjmp	UDPdr2

; check packet was valid, and whether a transport layer error occourred
UDPdre:	cjne	r6,#0xff,UDPdp1	; checksum not correct?, exit
	cjne	r7,#0xff,UDPdp1
	mov	a,TRANERR
	cjne	a,#0x00,UDPtle	; transport err, send ICMP
	sjmp	UDPev0

; jump out
UDPtle:	ljmp	ICMPeev		; transport layer error
UDPdp1:	ljmp	IPnull		; destroy the packet

; **************** evaluate the packet
; was a valid command sent?
UDPev0:	jnb	RECCMD,UDPinv
	mov	a,CMDVAL
	mov	DPTR,#0x00ffh
	; turn on?
	cjne	a,#0x31h,UDPev1	; = '1'?
	setb	LIGHT
	sjmp	UDPev2
UDPev1:	cjne	a,#0x30h,UDPev3	; = '0'?
	clr	LIGHT

UDPev2:	mov	r1,#0x02h	; send "OK"
	mov	a,#'O'
	movx	@DPTR,a
	dec	DPL
	mov	a,#'K'
	movx	@DPTR,a
	sjmp	UDPend
	; turn off?
	; status?
UDPev3:	cjne	a,#0x53h,UDPinv	; = 'S'?
	mov	r1,#0x01h	; send "ON/OFF"
	jb	LIGHT,UDPev4	; if ON goto UDPev4
	; light OFF
	mov	r1,#0x03h
	mov	a,#'O'
	movx	@DPTR,a
	dec	DPL
	mov	a,#'F'
	movx	@DPTR,a
	dec	DPL
	mov	a,#'F'
	movx	@DPTR,a
	sjmp	UDPend

	; light ON
UDPev4:	mov	r1,#0x02h
	mov	a,#'O'
	movx	@DPTR,a
	dec	DPL
	mov	a,#'N'
	movx	@DPTR,a
	sjmp	UDPend


; send and error response
UDPinv:	mov	r1,#0x03h
	mov	a,#'E'
	movx	@DPTR,a
	dec	DPL
	mov	a,#'R'
	movx	@DPTR,a
	dec	DPL
	mov	a,#'R'
	movx	@DPTR,a
	


UDPend:	mov	a,r1
	add	a,#0x08h
	mov	r2,#0x00h	; length MSB
	mov	r3,a		; length LSB
	lcall	IPhTxS
; intialise the checksum (with all sudo fields)
	mov	r6,#0x00h
	mov	r7,#0x11h
	setb	CHECKSUMB
	lcall	IPCHKSM
	lcall	IPGCHK
; send the UDP packet
	; src + dest ports
	mov	a,DPORT1
	lcall	IPCHKSM
	lcall	TxBPS
	mov	a,DPORT2
	lcall	IPCHKSM
	lcall	TxBPS
	mov	a,SPORT1
	lcall	IPCHKSM
	lcall	TxBPS
	mov	a,SPORT2
	lcall	IPCHKSM
	lcall	TxBPS
	; length
	mov	a,#0x00h
	cpl	CHECKSUMB
	lcall	TxBPS
	mov	a,r3
	lcall	IPCHKSM
	lcall	TxBPS
	; calc checksum
	mov	a,r1
	mov	r2,a
	mov	DPTR,#0x00ff
	; note, never going to be zero data
UDPchc:	movx	a,@DPTR
	lcall	IPCHKSM
	dec	DPL
	djnz	r1,UDPchc
	; send the checksum
	mov	a,r6
	cpl	a
	lcall	TxBPS
	mov	a,r7
	cpl	a
	lcall	TxBPS
	; send the data
	mov	DPTR,#0x00ff
UDPchc1:	movx	a,@DPTR
	lcall	TxBPS
	dec	DPL
	djnz	r2,UDPchc1
	ljmp	IPpkte


;***********************************************************************
; TCP packet evaluation
	; TCP eval
TCPst:	clr	CHECKSUMB	; reset checksum calc
	clr	TCPURG		; clear all the packet flags
	clr	TCPACK
	clr	TCPPSH
	clr	TCPRST
	clr	TCPSYN
	clr	TCPFIN
	; source port
	lcall    RxBGS
	mov	r6,a
	mov	SPORT1,a
	lcall	RxBGS
	mov	r7,a
	mov	SPORT2,a
	; destination port
	lcall	RxBGS
	lcall	IPCHKSM
	mov	DPORT1,a
	lcall	RxBGS
	lcall	IPCHKSM
	mov	DPORT2,a
	; Incoming Sequence number (store in Ack field)
	lcall	RxBGS
	lcall	IPCHKSM
	mov	TMPSEQ1,a
	lcall	RxBGS
	lcall	IPCHKSM
	mov	TMPSEQ2,a
	lcall	RxBGS
	lcall	IPCHKSM
	mov	TMPSEQ3,a
	lcall	RxBGS
	lcall	IPCHKSM
	mov	TMPSEQ4,a
	; Incoming Ack, for bytes previosly sent
	lcall	RxBGS
	lcall	IPCHKSM
	mov	ACKB1,a
	lcall	RxBGS
	lcall	IPCHKSM
	mov	ACKB2,a
	lcall	RxBGS
	lcall	IPCHKSM
	mov	ACKB3,a
	lcall	RxBGS
	lcall	IPCHKSM
	mov	ACKB4,a
	; read  + calculate the header length in bytes the Header length
	lcall	RxBGS
	lcall	IPCHKSM
	swap	a
	anl	a,#0x0fh
	clr	CY
	mov	b,#0x04h
	mul	ab
	mov	TCPHDRL,a
	; read the flags byte
	lcall	RxBGS
	lcall	IPCHKSM
	jnb	acc.0,TCPFSyn	; if the Fin flag not set, goto TCPFSyn
	setb	TCPFIN
TCPFSyn:	jnb	acc.1,TCPFRst	; if the Syn flag not set, goto TCPFRst
	setb	TCPSYN
TCPFRst:	jnb      acc.2,TCPFPsh	; if the Rst flag not set, goto TCPFPsh
	setb	TCPRST
TCPFPsh:	jnb      acc.3,TCPFAck	; if the Psh flag not set, goto TCPFAck
	setb	TCPPSH
TCPFAck:	jnb      acc.4,TCPFUrg	; if the Ack flag not set, goto TCPFUrg
	setb	TCPACK
TCPFUrg:	jnb      acc.5,TCPFEND	; if the Urg flag not set, goto TCPFEND
	setb	TCPURG
	; received the window size, can ignore as our data will never be 
	; near thier window size
TCPFEND:	lcall	RxBGS		; may reduce the next lot into one loop
	lcall	IPCHKSM
	lcall	RxBGS
	lcall	IPCHKSM
	; TCP checksum	
	lcall	RxBGS
	lcall	IPCHKSM
	lcall	RxBGS
	lcall	IPCHKSM
	; TCP urgent pointer
	lcall	RxBGS
	lcall	IPCHKSM
	lcall	RxBGS
	lcall	IPCHKSM
	; options - can avoid as my mss will always be smaller 
	mov	a,TCPHDRL
	clr	CY
	subb	a,#0x14
	mov	r0,a
	; read options
TCPOPTS:	cjne	r0,#0x00,TCPOPTR
	sjmp	TCPOPTE
TCPOPTR:	lcall	RxBGS		; read data
	lcall	IPCHKSM
	dec	r0
	sjmp	TCPOPTS

	; read data
TCPOPTE:	mov	DPTR,#0x00ffh
	mov	r0,IPREMLEN1
	clr	CY
	mov	a,IPREMLEN2
	subb	a,TCPHDRL
	mov	r1,a
	jnb	CY,TCPDATS
	clr	CY
	dec	r0
	; data read start
TCPDATS:	cjne	r1,#0x00h,TCPDRB	; r1 != 0, read data byte
	cjne	r0,#0x00h,TCPDMSB	; r0 != 0, decrement MSB
	sjmp	TCPDATE		; end of data, leave

TCPDMSB:	dec	r0
TCPDRB:	lcall	RxBGS
	lcall	IPCHKSM
	movx	@DPTR,a
	dec	r1
	dec	DPL
	sjmp	TCPDATS

	; apply sudo header
TCPDATE:	clr	CHECKSUMB	; clear CHECKSUMB, to apply
	lcall	IPGCHK
	cpl	CHECKSUMB
	mov	a,r5
	lcall	IPCHKSM
	mov	a,IPREMLEN1
	lcall	IPCHKSM
	mov	a,IPREMLEN2
	lcall	IPCHKSM
	
	; decide what is to be done with the packets
	cjne	r6,#0xff,TCPdp
	cjne	r7,#0xff,TCPdp

;************************************************
; is there a connection being/already established
	jb	TCPCNS1,TCPCEST
	sjmp	TCPNOCE
	; connection half open or open
TCPCEST:	jnb	TCPCNS2,TCPCHC1	; half est.?, then goto TCPCHCE
	ljmp	TCPCOES		; we are in the ESTABLISHED state


;jump a little longer
TCPCHC1:	ajmp	TCPCHCE
; leave 
TCPdp:	ljmp	IPnull		; destroy the packet
;************************************************

; tcp: no connection established, set one up if this is has the SYN flag
TCPNOCE:	jnb	TCPSYN,TCPdp
	; cool, lets go, set up options first (can do this as no data)
	mov	DPTR,#0x00ffh
	; send the MSS
	mov	a,#0x02h
	movx	@DPTR,a
	dec	DPL
	mov	a,#0x04h
	movx	@DPTR,a
	dec	DPL
	mov	a,#TCPMSS1
	movx	@DPTR,a
	dec	DPL
	mov	a,#TCPMSS2
	movx	@DPTR,a
	dec	DPL
	; send Window scale factor
	mov	a,#0x03h
	movx	@DPTR,a
	dec	DPL
	movx	@DPTR,a
	dec	DPL
	mov	a,#0x00h
	movx	@DPTR,a
	dec	DPL
	; send an options end
	movx	@DPTR,a

	; set up outgoing packet info
	clr	TCPSURG
	setb	TCPSACK
	clr	TCPSPSH
	clr	TCPSRST
	setb	TCPSSYN
	clr	TCPSFIN

	; setup the primary connection data
	setb	TCPCNS1		; move to half connected state
	mov	RSPORT1,SPORT1	; copy source port info
	mov	RSPORT2,SPORT2
	mov	RIPNUM1,IPSAB1	; copy source address
	mov	RIPNUM2,IPSAB2
	mov	RIPNUM3,IPSAB3
	mov	RIPNUM4,IPSAB4
	mov	REMSEQ1,TMPSEQ1	; copy remote sequence number
	mov	REMSEQ2,TMPSEQ2
	mov	REMSEQ3,TMPSEQ3
	mov	REMSEQ4,TMPSEQ4
	mov	r0,#0x01h
	lcall	TCPARSQ		; add 1 to the remote seq (for syn flag)
	lcall	TCPGSN		; grab a new sequence number

	; check that the remote host is connecting to the right port (DPORT)
	lcall	TCPCIPP
	jnb	TCPCMTH,TCPSSPK

	; oops port error, send a reset
	clr	TCPSSYN
	setb	TCPSRST
	setb	TRANPER
	mov	SEQNUM1,#0x00h
	mov	SEQNUM2,#0x00h
	mov	SEQNUM3,#0x00h
	mov	SEQNUM4,#0x00h
	
	; send the reset 
	mov	r2,#0x00h
	mov	r3,#0x14h
	lcall	IPhTxS
	
	; send the TCP header
	mov	r2,#0x05h	; the TCP header length
	mov	r3,#0x00h	; the TCP data length
	lcall	TCPHDRS		; send the TCP header

	; reset needed, do it and leave
	lcall	TCPMRST

	; leave
	sjmp	TCPSYNE
	
	; send the IP header, calc length first tho
	; 20 bytes TCP + 8 bytes TCP options
TCPSSPK:	mov	r2,#0x00h
	mov	r3,#0x1ch
	lcall	IPhTxS
	
	; send the TCP header
	mov	r2,#0x07h	; the TCP header length
	mov	r3,#0x00h	; the TCP data length
	lcall	TCPHDRS		; send the TCP header

	mov	r0,#0x01
	lcall	TCPASyn		; add one to the Sequence number

	; send final char
TCPSYNE:	ljmp	IPpkte



; connection half established, this should be an ack only
; process it and move to a full open connection.
TCPCHCE:	jnb	TCPSACK,TCPCMS1	; quit if no ack flag
	lcall	TCPCIPP		; compare IPs + portnumber
	jb	TCPCMTH,TCPCMS1	; leave + ditch packet

	; compare sequence numbers
TCPCMSN:	lcall	TCPCSYN
	jb	TCPCMTH,TCPCMS1	; leave + ditch packet

	; connection data ok, move to fully conencted state
TCPCMSO:	setb	TCPCNS2
TCPCMS1:	setb	PKTACKR		; say we can send a packet
	ajmp	TCPdp



; ESTABLISHED state handler. normal tx + rx
; need to check if Fin flag has been set, if so move states
TCPCOES:	jnb	CLOSEW,TCPCPKT	; are we in another state really?
	ajmp	TCPCWTE

	; check it was from the right hosts and port
TCPCPKT:	lcall	TCPCIPP
	jnb	TCPCMTH,TCPCFAK
TCPCOE1:	ajmp	TCPdp

	; packet ok, carry ok
	; if the ack bit was set, process the ack first
TCPCFAK:	jnb	TCPACK,TCPCFBT
	lcall	TCPCSYN
	jb	TCPCMTH,TCPCOE1	; if ack was bad, leave (ditch packet)
	setb	PKTACKR		; ack good, move on

	; if the fin bit was set, move states
TCPCFBT:	jnb	TCPFIN,TCPDATA	; fin not received, process data
	
	; FIN received
	; was the ack good?
	jnb	PKTACKR,TCPCOE1
	; was the data segment good?
	lcall	TCPCRSY
	jb	TCPCMTH,TCPCOE1	; if not then ditch packet

	; incoming data good, send ack
	; add one to the remote seq + send ack
	mov	r0,#0x01h
	lcall	TCPARSQ
	; set up outgoing packet info
	clr	TCPSURG
	setb	TCPSACK
	clr	TCPSPSH
	clr	TCPSRST
	clr	TCPSSYN
	clr	TCPSFIN
	; send the IP header, calc length first tho
	; 20 bytes TCP + 8 bytes TCP options
	mov	r2,#0x00h
	mov	r3,#0x14h
	lcall	IPhTxS
	
	; send the TCP header
	mov	r2,#0x05h	; the TCP header length
	mov	r3,#0x00h	; the TCP data length
	lcall	TCPHDRS		; send the TCP header

	; tidy up the network layer then send a FIN packet
	; esssentially do a IPpkte
	setb	SLSEND2
	jnb	SENDOK,TCPwait
	setb	ti
TCPwait:	jb	SLSEND1,TCPwait
	jb	SLSEND2,TCPwait
	
	; send the FIN response
	; set up outgoing packet info
	clr	TCPSURG
	setb	TCPSACK
	clr	TCPSPSH
	clr	TCPSRST
	clr	TCPSSYN
	setb	TCPSFIN
	; send the IP header, calc length first tho
	; 20 bytes TCP + 8 bytes TCP options
	mov	r2,#0x00h
	mov	r3,#0x14h
	lcall	IPhTxS
	
	; send the TCP header
	mov	r2,#0x05h	; the TCP header length
	mov	r3,#0x00h	; the TCP data length
	lcall	TCPHDRS		; send the TCP header


	; add one to the local sequence
	mov	r0,#0x01h
	lcall	TCPASyn

	; set the next bit, and wait for the final ack
	setb	CLOSEW

	; jump outta here
	sjmp	IPpkte


	; else deal with data
	; is it ok to send a packet (has the last ack been received?)
TCPDATA:	jnb	PKTACKR,TCPCOEE

	; data len must be < 16 as mss is only 16 :)
	mov	DPTR,#0x00ff
	; calc data length
	clr	CY
	mov	a,IPREMLEN2
	subb	a,TCPHDRL
	mov	r0,a
	lcall	TCPARSQ		; add data len to remote seq
	; can never carry as MSS = 16 (16+f+f=34=max packet len)
	cjne	r0,#0x00h,TCPDAT1
	;sjmp	TCPDAT2		;data zero, send ack and leave
	sjmp	TCPCOEE		;data zero, send ack and leave
	; rule, if no seq nums are used, no ack needs to be sent

	; there is data, so deal + then ack + return ans
TCPDAT1:	movx	a,@DPTR		; copy command in
	lcall	APPLINB		; application layer byte interpreter
				; data length left in r1

	; to start with, lets just ack it
	; send the FIN response
	; set up outgoing packet info
	clr	TCPSURG
	setb	TCPSACK
	setb	TCPSPSH
	clr	TCPSRST
	clr	TCPSSYN
	clr	TCPSFIN
	; send the IP header, calc length first tho
	; 20 bytes TCP + 8 bytes TCP options
	mov	r2,#0x00h
	mov	r3,#0x14h
	clr	CY
	mov	a,r1
	add	a,r3
	mov	r3,a
	lcall	IPhTxS
	
	; send the TCP header
	mov	r2,#0x05h	; the TCP header length
	mov	a,r1
	mov	r3,a		; the TCP data length
	lcall	TCPHDRS		; send the TCP header

	; add the data length to the local sequence
	mov	r0,a
	lcall	TCPASyn


	; tidy up and leave
	sjmp	IPpkte

TCPCOEE:	ajmp	TCPdp



; well, we are about to finally close the connection
TCPCWTE:	jnb	TCPSACK,TCPCWT0	; if the ack flag is not set, leave
	lcall	TCPCIPP		; compare IPs + portnumber
	jb	TCPCMTH,TCPCWT0	; if bad ack, leave

	lcall	TCPCSYN		; compare sequence numbers
	jb	TCPCMTH,TCPCWT0	; if bad seq, leave

	lcall	TCPMRST		; all ok, call a TCP reset
TCPCWT0:	ajmp	TCPdp
	


;#######################################################################
; organise sending the end
; need to check to see if PKTRECV is still active
IPpkte:	setb	SLSEND2		; flag end of packet near

	jnb	SENDOK,IPwait	; if not clear to send, waitfor IP
	setb	ti		; force an interrupt

IPwait:	jb	SLSEND1,IPwait	; wait for SLSEND1 to = 0;
	jb	SLSEND2,IPwait	; wait for SLSEND2 to = 0;
	ljmp	IPinit		; get the next byte



;
; An end marker for exiting the program
;
Exit:	sjmp	Exit
	.end
