; s.iprom
;
; IP for the Beeb. Lovingly coded in hand-crafted assembler. Hmph.
; (c) 1995-1996 Philip Blundell.
;
; 12-Sep-95  - send_packet cheating removed
;            - arp added. Doesn't seem to work - not sure if this is Freenet's
;              fault or mine
;            - routing added
;            - BASIC loader program added
;            - made ip_checksum more general, removed icmp_checksum
;            - clock now 32 bits
;            - started implementing TCP
;            - fixed block_copy of >256 bytes
;
; 15-Dec-95  - fixed read_rx control-byte check
;            - implemented response to incoming ARP
;            - fixed outgoing ARP requests
;            - implemented handling of incoming ARP replies
;            - improved ARP/route table search code
;
; 17-Dec-95  - implemented TCP
;
; 27-Dec-95  - started to implement sideways ROM version
;
; 30-Dec-95  - add tcpack_buffer to fix overwrite problems in reject and ack
;            - more tx retries (10 not 2)
;            - randomise our port number
;            - try to fix the zero-window deadlock problem
;            - start implementing TELAPI
;
; 03-Jan-96  - add initflag
;            - implement vector claim & passon properly
;
; 11-Apr-96  - fix resets being sent with wrong port numbers (was killing
;              active connection when a spurious segment arrived)
;            - fix CNP code to return correct values
;
; 16-Apr-96  - queue up another frame to go out if we have buffered data
;              when an ACK arrives <doh!>
;            - don't re-open rxcb on soft break
;            - do initialisation stuff after claiming workspace, not on
;              auto-boot call
;	     - print numbers in decimal not hex
;	     - preserve more zero-page stuff more of the time
;	     - print our IP address on *HELP
;
; 17-Apr-96  - part implemented UDP and bootp transmission
;	     - can't really do broadcasts though, so we lose
;
; 01-Nov-96  - removed initflag, replaced with DEROUTE (to allow the
;              ROM to be put into hardware)

__DEBUG__	EQU	0
;
EVENTV		EQU	&30
temp		EQU	&A8
temp2		EQU	&AA
base		EQU	&AC
data		EQU	&AE
params		EQU	&70
tcp_frame	EQU	&72
temp3		EQU	&74
pltemp		EQU	&74
bccount		EQU	&76
trcount		EQU	&77
;
; Econet MTU=1280 bytes=&500.
scratch		EQU	&A040		; &40 bytes more temp space
pblk		EQU	&A080
trx_buf		EQU	&A0C0

tcprx_buffer	EQU	&A200		; 256 bytes TCP receive space
tcptx_buffer	EQU	&A100		; ditto, transmit

varbase		EQU	&B200
oldvector	EQU	varbase		; &980
rxhandle	EQU	varbase+2	; &982
ihl		EQU	varbase+3	; &983
clock		EQU	varbase+4	; &984
rstation	EQU	varbase+8	; &988
rlength		EQU	varbase+&A	; &98A
cpsrc		EQU	varbase+&C	; &98C
cpdst		EQU	varbase+&E	; &98E
cplen		EQU	varbase+&10	; &990
his_ip		EQU	varbase+&12	; &992
proto		EQU	varbase+&16	; &996
retry		EQU	varbase+&17	; &997
seg_seq		EQU	varbase+&18	; &998
seg_ack		EQU	varbase+&1C	; &99C
tcp_state	EQU	varbase+&20	; &9A0
tcp_flags	EQU	varbase+&21	; &9A1
locport		EQU	varbase+&22	; &9A2
remport		EQU	varbase+&24	; &9A4
tcp_peer	EQU	varbase+&26	; &9A6
snd_nxt		EQU	varbase+&2A	; &9AA
hisport		EQU	varbase+&2E	; &9AE
ourport		EQU	varbase+&30	; &9B0
tcp_retransmit	EQU	varbase+&32	; &9B2
tcp_timer	EQU	varbase+&34	; &9B4
rcv_nxt		EQU	varbase+&35	; &9B5
snd_una		EQU	varbase+&39	; &9B9
tcprx_in	EQU	varbase+&3D	; &9BD
tcprx_out	EQU	varbase+&3E	; &9BE
tcptx_in	EQU	varbase+&3F	; &9BF
tcptx_out	EQU	varbase+&40	; &9C0
tx_size		EQU	varbase+&41	; &9C1
my_ip		EQU	varbase+&42
rxbuffer_ptr	EQU	varbase+&46
txbuffer_ptr	EQU	varbase+&47
rxbuffer_end_ptr	EQU	varbase+&48
txbuffer_end_ptr	EQU	varbase+&49
blk_ptr		EQU	varbase+&4A
zwindow		EQU	varbase+&4B
old_insv	EQU	varbase+&4C
old_remv	EQU	varbase+&4F
old_cnpv	EQU	varbase+&52
old_eventv	EQU	varbase+&55
zpstore		EQU	varbase+&80
tx_flag		EQU	varbase+&8A
ack_timer	EQU	varbase+&8B
iac_flag	EQU	varbase+&8C
iac_cmd		EQU	varbase+&8D
lockflag	EQU	varbase+&8E
iac_x		EQU	varbase+&8F
data_size	EQU	varbase+&90
our_fin_flag	EQU	varbase+&92

STATE_CLOSED		EQU	0
STATE_SYN_SENT		EQU	1
STATE_ESTABLISHED	EQU	2
STATE_FIN		EQU	3

URG		EQU	32
ACK		EQU	16
PSH		EQU	8
RST		EQU	4
SYN		EQU	2
FIN		EQU	1

arp		EQU	varbase+&100	; space for 40x6-byte arp entries
route		EQU	varbase+&1F0	; space for 10x8-byte routes
tcp_buffer	EQU	varbase+&240	; space for 512 bytes TCP data
tcpack_buffer	EQU	varbase+&440	; space for TCP ack

		ORG	&8000

header		BRK			; we aren't a language
		DW	0
		JMP	service
		DFB	%10000010		;Service ROM in 6502 code
		DFB	>copyright-1
		DFB	14
ROMName		ASC	'TCP/IP'
namelen		EQU	*-ROMName
		BRK
		ASC	'v0.14 1996-04-16'
		BRK
copyright	ASC	'(C) Philip Blundell'
		BRK
		DW	0
		DW	0

service		CMP	#0
		BEQ	:noservice
		PHA
		TYA
		PHA
		LDA	temp
		PHA
		LDA	temp+1
		PHA
		LDA	temp2
		PHA
		LDA	temp2+1
		PHA
		TSX
		LDA	&106,X
		CMP	#2
		BEQ	:claimworkspace
		CMP	#9
		BEQ	:help
		CMP	#3
		BNE	:notbreak
		JMP	break
:notbreak	CMP	#4
		BNE	:notstarcmd
		JMP	starcmd
:notstarcmd
service_exit
		PLA
		STA	temp2+1
		PLA
		STA	temp2
		PLA
		STA	temp+1
		PLA
		STA	temp
		PLA
		TAY
		PLA
		LDX	&F4
:noservice	RTS
:claimworkspace
		LDX	&F4
		TYA
		STA	&DF0,X
		STY	rxbuffer_ptr
		INY
		INY
		STY	txbuffer_ptr
		INY
		INY
		STY	blk_ptr
		INY
		TSX
		TYA
		STA	&105,X
		JMP	service_exit

:help		LDA	(&F2),Y
		CMP	#&D
		BEQ	:shorthelp
		LDX	#0
		JSR	cmd_decode_setup
		JSR	cmd_decode
		JMP	service_exit

:shorthelp	JSR	prinline
		DB	13
		ASC	'Econet TCP/IP 0.14 '
		BRK
:init_ok	LDA	my_ip
;		CMP	#0
		BEQ	:no_addr_known
		PHA
		LDA	#'('
		JSR	OSWRCH
		PLA
		JSR	PrintDecimal
		LDA	#'.'
		JSR	OSWRCH
		LDA	my_ip+1
		JSR	PrintDecimal
		LDA	#'.'
		JSR	OSWRCH
		LDA	my_ip+2
		JSR	PrintDecimal
		LDA	#'.'
		JSR	OSWRCH
		LDA	my_ip+3
		JSR	PrintDecimal
		LDA	#')'
		JSR	OSWRCH
:no_addr_known
		JSR	prinline
		DB	13
		ASC	'  TCPIP'
		DB	13
		BRK
		JMP	service_exit

		BLOCK

cmd_decode_setup
		TYA
		CLC
		ADC	&F2
		STA	temp2
		LDA	&F3
		ADC	#0
		STA	temp2+1
		RTS

cmd_decode
		LDA	cmd_table,X
		STA	temp
		LDA	cmd_table+1,X
		STA	temp+1
		LDY	#0
:loop		LDA	(temp),Y
;		CMP	#0
		BEQ	:ok
		LDA	(temp2),Y
		CMP	#46
		BEQ	:ok
		AND	#&5F
		CMP	(temp),Y
		BNE	:bad
		INY
		JMP	:loop
:bad		INX
		INX
		INX
		INX
		LDA	#0
		RTS

:ok		LDA	cmd_table+2,X
		STA	temp
		LDA	cmd_table+3,X
		STA	temp+1
		JMP	(temp)

cmd_table	DW	tcpip_str
		DW	tcpip_help
		DW	arp_str
		DW	arpcmd
		DW	ipaddr_str
		DW	ipaddrcmd
		DW	telnet_str
		DW	telnetcmd
		DW	route_str
		DW	routecmd
		DW	deroute_str
		DW	deroutecmd
		DW	telapi_str
		DW	telapicmd

tcpip_str	ASC	'TCPIP'
		BRK
arp_str		ASC	'ARP'
		BRK
ipaddr_str	ASC	'IPADDR'
		BRK
telnet_str	ASC	'TELNET'
		BRK
route_str	ASC	'ROUTE'
		BRK
deroute_str	ASC	'DEROUTE'
		BRK
telapi_str	ASC	'TELAPI'
		BRK

tcpip_help	JSR	prinline
		DB	13
		STR	'TCP/IP 0.14'
		STR	'(C) 1996 Philip Blundell'
		STR	'  IPADDR <ip>'
		STR	'  DEROUTE'
		STR	'  ROUTE <target ip> <gateway ip>'
		STR	'  TELNET <ip> (<port>)'
		STR	'  TELAPI <ip> (<port>)'
		STR	'  ARP'
		BRK
		LDA	#&FF
		RTS

break		JSR	prinline
		STR	'TCP/IP 0.14'
		BRK
		LDA	#253
		LDX	#0
		LDY	#255
		JSR	OSBYTE
		CPX	#0
		BEQ	:softbreak
		JSR	open_rx
		LDA	#0
		STA	my_ip
		STA	my_ip+1
		STA	my_ip+2
		STA	my_ip+3
		LDX	#239
:zlp		STA	arp,X
		DEX
		CPX	#&FF
		BNE	:zlp
:softbreak	LDA	#STATE_CLOSED
		STA	tcp_state
:init1		LDA	#0
		STA	zwindow
		STA	clock
		STA	clock+1
		STA	clock+2
		STA	clock+3
		STA	tcp_retransmit
		STA	tcp_retransmit+1
		STA	tcprx_in
		STA	tcprx_out
		STA	tcptx_in
		STA	tcptx_out
		STA	tx_flag
		STA	ack_timer
		STA	iac_flag
		STA	iac_cmd
		STA	lockflag
		STA	iac_x
		STA	data_size
		STA	data_size+1
		STA	our_fin_flag

		LDA	#10
		STA	tcp_timer

		LDA	#&A8
		LDX	#0
		LDY	#&FF
		JSR	OSBYTE
		STX	temp
		LDA	#&A9
		LDX	#0
		LDY	#&FF
		JSR	OSBYTE
		STX	temp+1

		LDA	&22A
		STA	old_insv
		LDA	&22B
		STA	old_insv+1
		LDA	&22C
		STA	old_remv
		LDA	&22D
		STA	old_remv+1
		LDA	&22E
		STA	old_cnpv
		LDA	&22F
		STA	old_cnpv+1

		LDA	&220
		CMP	#&30
		BNE	:eventv_notextended
		LDA	&221
		CMP	#&FF
		BNE	:eventv_notextended
		LDY	#&30
		LDA	(temp),Y
		STA	old_eventv
		INY
		LDA	(temp),Y
		STA	old_eventv+1
		INY
		LDA	(temp),Y
		STA	old_eventv+2
		JMP	:eventv_wasextended
:eventv_notextended
		LDA	&220
		STA	old_eventv
		LDA	&221
		STA	old_eventv+1
		LDA	#&FF
		STA	old_eventv+2
:eventv_wasextended

		PHP
		SEI

		LDA	#>vsync
		LDY	#&30
		STA	(temp),Y
		INY
		LDA	#<vsync
		STA	(temp),Y
		INY
		LDA	&F4
		STA	(temp),Y
		LDA	#&FF
		STA	&221
		LDA	#&30
		STA	&220

		LDY	#&3F
		LDA	#>insert
		STA	(temp),Y
		INY
		LDA	#<insert
		STA	(temp),Y
		INY
		LDA	&F4
		STA	(temp),Y
		LDA	#&FF
		STA	&22B
		LDA	#&3F
		STA	&22A

		LDY	#&42
		STY	&22C
		LDA	#&FF
		STA	&22D
		LDA	#>remove
		STA	(temp),Y
		INY
		LDA	#<remove
		STA	(temp),Y
		INY
		LDA	&F4
		STA	(temp),Y

		INY		; LDY #&45
		STY	&22E
		LDA	#&FF
		STA	&22F
		LDA	#>countpurge
		STA	(temp),Y
		INY
		LDA	#<countpurge
		STA	(temp),Y
		INY
		LDA	&F4
		STA	(temp),Y

		PLP
		LDA	#14
		LDX	#4
		JSR	OSBYTE
		JSR	OSNEWL
		JMP	service_exit

open_rx		LDA	blk_ptr
		STA	temp+1
		LDA	#0
		STA	temp
		LDY	#0
		TYA
		STA	(temp),Y
		LDY	#3
		STA	(temp),Y
		STA	(temp),Y
		LDY	#1
		LDA	#&7F
		STA	(temp),Y
		INY
		LDA	#&D2
		STA	(temp),Y
		LDA	rxbuffer_ptr
		LDY	#6
		STA	(temp),Y
		DEY
		LDA	#0
		STA	(temp),Y
		LDA	#&FF
		LDY	#7
		STA	(temp),Y
		INY
		STA	(temp),Y
		LDY	#11
		STA	(temp),Y
		INY
		STA	(temp),Y
		LDA	rxbuffer_ptr
		LDY	#10
		CLC
		ADC	#1
		STA	(temp),Y
		LDA	#&FF
		DEY
		STA	(temp),Y
		LDA	#&11
		LDX	#0
		LDY	blk_ptr
		JSR	OSWORD
		LDY	#0
		LDA	(temp),Y
;		CMP	#0
		BEQ	rxcb
		STA	rxhandle
		RTS

rxcb		BRK
		BRK
		ASC	'Rxcb?'
		BRK

evexit_j	JMP	evexit

eventcode	CMP	#4
		BEQ	vsync
		RTS
vsync		PHP
		PHA
		TXA
		PHA
		TYA
		PHA
		INC	clock
		LDA	clock
;		CMP	#0
	BLOCK
		BNE	:skip
		INC	clock+1
		LDA	clock+1
;		CMP	#0
		BNE	:skip
		INC	clock+2
		LDA	clock+2
;		CMP	#0
		BNE	:skip
		INC	clock+3
:skip
		LDA	lockflag
;		CMP	#0
		BNE	evexit_j
		INC	lockflag
		CLI
		LDA	temp
		PHA
		LDA	temp+1
		PHA
		LDA	temp2
		PHA
		LDA	temp2+1
		PHA
		LDA	tcp_frame
		STA	zpstore
		LDA	tcp_frame+1
		STA	zpstore+1
		LDA	base
		STA	zpstore+2
		LDA	base+1
		STA	zpstore+3
		LDA	data
		STA	zpstore+4
		LDA	data+1
		STA	zpstore+5
		LDA	params
		STA	zpstore+6
		LDA	params+1
		STA	zpstore+7
		LDA	temp3
		STA	zpstore+8
		LDA	temp3+1
		STA	zpstore+9
		; Every second, or if we get prodded:
		;
		;  + Check if there is a segment to be retransmitted
		;    if not, then
		;    + check if there is data in the transmit buffer
		;      if so, then
		;      + call tcp_transmit to build a segment and queue it
		;  + Check the Econet for received packets.  ACK any incoming
		;    data if we can.
		;  + Check if there is a segment to be retransmitted (including
		;    one we just generated). If so, transmit it.
		DEC	tcp_timer
		LDA	tcp_timer
		CMP	#&FF
		BNE	:go_read_rx
		LDA	#100
		STA	tcp_timer
		STA	tx_flag
;		LDA	my_ip
;		BNE	:okip
;		JSR	send_bootp
:okip		LDA	tcp_retransmit
;		CMP	#0
		BNE	:go_read_rx
		LDA	tcp_retransmit+1
;		CMP	#0
		BEQ	:no_frame_queued
		JMP	:go_read_rx
:no_frame_queued
		LDA	tcptx_in
		CMP	tcptx_out
		BEQ	:go_read_rx
		JSR	tcp_transmit
:go_read_rx
		LDX	rxhandle
	BLOCK
		LDA	#&33
		JSR	OSBYTE
		TXA
		BPL	:do_tcp_ack
		JSR	read_rx

:do_tcp_ack	LDA	ack_timer
;		CMP	#0			; no ack waiting
		BEQ	:do_tcp_tx
		DEC	ack_timer
		CMP	#1			; ack still waiting
		BNE	:do_tcp_tx
		; Fixme.  This shouldn't be needed.
		LDA	tcp_peer
		STA	his_ip
		LDA	tcp_peer+1
		STA	his_ip+1
		LDA	tcp_peer+2
		STA	his_ip+2
		LDA	tcp_peer+3
		STA	his_ip+3
		JSR	tcp_ack_send
:do_tcp_tx	LDA	tx_flag
;		CMP	#0
		BEQ	:evexit1
		LDA	#0
		STA	tx_flag
		LDA	tcp_retransmit+1
;		CMP	#0
		BNE	:tx_goahead
		LDA	tcp_retransmit
;		CMP	#0
		BEQ	:evexit1
:tx_goahead	LDA	tcp_retransmit+1
		STA	tcp_frame+1
		LDA	tcp_retransmit
		STA	tcp_frame
		LDA	tcp_peer
		STA	his_ip
		LDA	tcp_peer+1
		STA	his_ip+1
		LDA	tcp_peer+2
		STA	his_ip+2
		LDA	tcp_peer+3
		STA	his_ip+3
		JSR	tcp_send
:evexit1
		LDA	zpstore+9
		STA	temp3+1
		LDA	zpstore+8
		STA	temp3
		LDA	zpstore+7
		STA	params+1
		LDA	zpstore+6
		STA	params
		LDA	zpstore+5
		STA	data+1
		LDA	zpstore+4
		STA	data
		LDA	zpstore+3
		STA	base+1
		LDA	zpstore+2
		STA	base
		LDA	zpstore+1
		STA	tcp_frame+1
		LDA	zpstore
		STA	tcp_frame
		PLA
		STA	temp2+1
		PLA
		STA	temp2
		PLA
		STA	temp+1
		PLA
		STA	temp
		DEC	lockflag
evexit		PLA
		TAY
		PLA
		TAX
		PLA
		PLP
		RTS

read_rx
		LDA	blk_ptr
		STA	temp+1
		LDA	#0
		STA	temp
		LDA	rxhandle
		LDY	#0
		STA	(temp),Y
		LDA	#&11
		LDX	#0
		LDY	blk_ptr
		JSR	OSWORD
		LDY	#4
		LDA	(temp),Y
		STA	rstation+1
		DEY
		LDA	(temp),Y
		STA	rstation
		LDY	#9
		LDA	(temp),Y
		STA	rlength		; rbuf is page-aligned
		LDY	#10
		LDA	(temp),Y
		SEC
		LDY	#6
		SBC	(temp),Y
		STA	rlength+1
		LDY	#1
		LDA	(temp),Y
		CMP	#&81
		BNE	:not_dgram
		JMP	dgram
:not_dgram	CMP	#&A2
		BNE	:not_arp_reply
		JMP	arp_reply
:not_arp_reply
		CMP	#&A1
		BNE	:not_arp_request
		JMP	arp_request
:not_arp_request
	IF __DEBUG__
		JSR	prinline
		ASC	'Rcvd weird cb '
		BRK
		LDY	#1
		LDA	(temp),Y
		JSR	PrintHex1
	FI
		JMP	open_rx

arp_reply
	IF __DEBUG__
		JSR	prinline
		ASC	'ARP reply'
		BRK
	FI
		LDA	#0
		STA	tcp_timer
:find_empty
		TAX
		LDA	arp,X
;		CMP	#0
		BNE	:not_empty
		LDA	arp+1,X
;		CMP	#0
		BNE	:not_empty
		LDA	arp+2,X
;		CMP	#0
		BNE	:not_empty
		LDA	arp+3,X
;		CMP	#0
		BEQ	:is_empty
:not_empty	TXA
		CLC
		ADC	#6
		CMP	#60
		BNE	:find_empty
		JSR	prinline
		ASC	' (no slots free)'
		DB	13
		BRK
		JMP	open_rx
:is_empty	LDY	#5
		LDA	(temp),Y
		STA	data
		INY
		LDA	(temp),Y
		STA	data+1
		LDY	#0
		LDA	(data),Y
		STA	arp,X
		INY
		LDA	(data),Y
		STA	arp+1,X
		INY
		LDA	(data),Y
		STA	arp+2,X
		INY
		LDA	(data),Y
		STA	arp+3,X
		LDA	rstation
		STA	arp+4,X
		LDA	rstation+1
		STA	arp+5,X
		JMP	open_rx

dgram
	IF __DEBUG__
		JSR	prinline
		ASC	'rcvd '
		BRK
		LDX	rlength
		LDY	rlength+1
		JSR	PrintHex2
		JSR	prinline
		ASC	' bytes from '
		BRK
		LDA	rstation+1
		JSR	PrintHex1
		LDA	#46
		JSR	OSWRCH
		LDA	rstation
		JSR	PrintHex1
	FI
		LDY	#5
		LDA	(temp),Y
		STA	data
		INY
		LDA	(temp),Y
		STA	data+1
		JSR	IPDecapsulate
		JMP	open_rx

IPDecapsulate
	BLOCK
		LDY	#12
		LDA	(data),Y
		STA	his_ip
		INY
		LDA	(data),Y
		STA	his_ip+1
		INY
		LDA	(data),Y
		STA	his_ip+2
		INY
		LDA	(data),Y
		STA	his_ip+3
	IF __DEBUG__
		LDA	his_ip
		JSR	PrintHex1
		LDA	#46
		JSR	&FFEE
		LDA	his_ip+1
		JSR	PrintHex1
		LDA	#46
		JSR	&FFEE
		LDA	his_ip+2
		JSR	PrintHex1
		LDA	#46
		JSR	&FFEE
		LDA	his_ip+3
		JSR	PrintHex1
		JSR	prinline
		ASC	' -> '
		BRK
		LDY	#16
		LDA	(data),Y
		JSR	PrintHex1
		LDA	#46
		JSR	&FFEE
		INY
		LDA	(data),Y
		JSR	PrintHex1
		LDA	#46
		JSR	&FFEE
		INY
		LDA	(data),Y
		JSR	PrintHex1
		LDA	#46
		JSR	&FFEE
		INY
		LDA	(data),Y
		JSR	PrintHex1
		JSR	prinline
		ASC	' IHL='
		BRK
	FI
		LDY	#9
		LDA	(data),Y
		STA	proto
		LDY	#0
		LDA	(data),Y
		AND	#&F
		ASL	A
		ASL	A
		STA	ihl
	IF __DEBUG__
		JSR	PrintHex1
		JSR	prinline
		ASC	' proto='
		BRK
		LDA	proto
		JSR	PrintHex1
	FI
		LDA	rlength
		SEC
		SBC	ihl
		STA	rlength
		LDA	rlength+1
		SBC	#0
		STA	rlength+1
		LDA	data
		CLC
		ADC	ihl
		STA	data
		LDA	data+1
		ADC	#0
		STA	data+1
	IF __DEBUG__
		JSR	OSNEWL
	FI
		LDA	proto
		CMP	#1
		BNE	:noticmp
		JMP	icmp
:noticmp
		CMP	#6
		BNE	:nottcp
		JMP	tcp
:nottcp
		RTS

tcp	IF __DEBUG__
		JSR	prinline
		ASC	'TCP '
		BRK
	FI
		LDY	#0
		LDA	(data),Y
		STA	remport+1
		INY
		LDA	(data),Y
		STA	remport
		INY
		LDA	(data),Y
		STA	locport+1
		INY
		LDA	(data),Y
		STA	locport
		INY
		LDA	(data),Y
		STA	seg_seq+3
		INY
		LDA	(data),Y
		STA	seg_seq+2
		INY
		LDA	(data),Y
		STA	seg_seq+1
		INY
		LDA	(data),Y
		STA	seg_seq
		INY
		LDA	(data),Y
		STA	seg_ack+3
		INY
		LDA	(data),Y
		STA	seg_ack+2
		INY
		LDA	(data),Y
		STA	seg_ack+1
		INY
		LDA	(data),Y
		STA	seg_ack+0
		INY
		LDA	(data),Y
		LSR	A
		LSR	A
		STA	scratch		; TCP header size
		INY
		LDA	(data),Y
		STA	tcp_flags
		LDA	data
		CLC
		ADC	scratch
		STA	data
		LDA	data+1
		ADC	#0
		STA	data+1
		LDA	rlength
		SEC
		SBC	scratch
		STA	rlength
		LDA	rlength+1
		SBC	#0
		STA	rlength+1
	IF __DEBUG__
		LDX	remport
		LDY	remport+1
		JSR	PrintHex2
		JSR	prinline
		ASC	" -> "
		BRK
		LDX	locport
		LDY	locport+1
		JSR	PrintHex2
		LDA	#32
		JSR	OSWRCH
		LDX	seg_seq+2
		LDY	seg_seq+3
		JSR	PrintHex2
		LDX	seg_seq
		LDY	seg_seq+1
		JSR	PrintHex2
		LDA	#32
		JSR	OSWRCH
		LDX	seg_ack+2
		LDY	seg_ack+3
		JSR	PrintHex2
		LDX	seg_ack
		LDY	seg_ack+1
		JSR	PrintHex2
		LDA	#32
		JSR	OSWRCH
		LDA	tcp_flags
		JSR	PrintHex1
		JSR	OSNEWL
	FI
		LDA	tcp_state
		CMP	#STATE_CLOSED
		BEQ	reject
		LDA	locport
		CMP	ourport
		BNE	reject
		LDA	remport
		CMP	hisport
		BNE	reject
		JMP	:noreject

reject
	IF __DEBUG__
		JSR	prinline
		ASC	'[rejecting segment]'
		DB	13
		BRK
	FI
		LDA	tcp_flags
		BIT	#RST
		BNE	:notrst
		RTS
:notrst
		; use tcpack_buffer here
		LDX	#>tcpack_buffer
		LDY	#<tcpack_buffer
		STX	tcp_frame
		STY	tcp_frame+1
		LDY	#24
		LDA	#&50
		STA	(tcp_frame),Y
		LDY	#10
		LDA	#0
		STA	(tcp_frame),Y
		INY
		LDA	#20
		STA	(tcp_frame),Y
		; BIT	#ACK
		; BNE somewhere
		LDY	#23
		LDA	#0
		STA	(tcp_frame),Y
		DEY
		STA	(tcp_frame),Y
		DEY
		STA	(tcp_frame),Y
		DEY
		STA	(tcp_frame),Y
		LDA	#RST
		LDY	#25
		STA	(tcp_frame),Y
		LDA	#0
		INY
		STA	(tcp_frame),Y
		INY
		STA	(tcp_frame),Y
		LDY	#12
		LDA	locport+1
		STA	(tcp_frame),Y
		INY
		LDA	locport
		STA	(tcp_frame),Y
		INY
		LDA	remport+1
		STA	(tcp_frame),Y
		INY
		LDA	remport
		STA	(tcp_frame),Y
		JMP	tcp_send_port

:noreject
		LDA	tcp_state
		CMP	#STATE_SYN_SENT
		BEQ	syn_sent
		CMP	#STATE_ESTABLISHED
		BNE	:skip
		JMP	established
:skip		CMP	#STATE_FIN
		BNE	:skip1
		JMP	established
:skip1		RTS

syn_sent
	BLOCK
	IF __DEBUG__
		JSR	prinline
		ASC	'Processing SYN reply'
		DB	13
		BRK
	FI
		LDA	tcp_flags
		AND	#RST
;		CMP	#0
		BEQ	:conn_not_reset
		LDA	#STATE_CLOSED
		STA	tcp_state
		RTS
:conn_not_reset	LDA	tcp_flags
		BIT	#SYN
		BNE	:ok
		JMP	reject
:ok		LDA	seg_ack
		STA	snd_una
		LDA	seg_ack+1
		STA	snd_una+1
		LDA	seg_ack+2
		STA	snd_una+2
		LDA	seg_ack+3
		STA	snd_una+3
		LDA	#STATE_ESTABLISHED
		STA	tcp_state
		LDA	#0
		STA	tcp_retransmit
		STA	tcp_retransmit+1
		CLC
		LDA	seg_seq
		ADC	#1
		STA	rcv_nxt
		LDA	seg_seq+1
		ADC	#0
		STA	rcv_nxt+1
		LDA	seg_seq+2
		ADC	#0
		STA	rcv_nxt+2
		LDA	seg_seq+3
		ADC	#0
		STA	rcv_nxt+3
		JMP	tcp_ack

	BLOCK
established
	IF __DEBUG__
		JSR	prinline
		ASC	'Got TCP data len = '
		BRK
		LDX	rlength
		LDY	rlength+1
		JSR	PrintHex2
		JSR	OSNEWL
	FI
		LDA	tcp_flags
		AND	#ACK
;		CMP	#0
		BEQ	:not_ack
		JSR	ack_process
:not_ack	LDA	tcp_flags
		AND	#FIN
;		CMP	#0
		BEQ	:not_fin
		JSR	fin_process
:not_fin	LDA	tcp_flags
		AND	#RST
		BEQ	:not_rst
		LDA	#STATE_CLOSED
		STA	tcp_state
:not_rst	LDA	rlength
;		CMP	#0
		BNE	:nz
		LDA	rlength+1
;		CMP	#0
		BNE	:nz
		RTS			; Quietly drop zero segments

:nz		; Accept the segment if seq.seq <= rcv.nxt &&
		;    (seg.seq + length) > rcv.next
		LDA	seg_seq+3
		CMP	rcv_nxt+3
		BNE	:gds_check
		LDA	seg_seq+2
		CMP	rcv_nxt+2
		BNE	:gds_check
		LDA	seg_seq+1
		CMP	rcv_nxt+1
		BNE	:gds_check
		LDA	seg_seq
		CMP	rcv_nxt
		BNE	:gds_check
		; It's the segment we asked for.
		JMP	:yessame
:gds_check
		BCC	:okds
	IF __DEBUG__
		JSR	prinline
		ASC	'Bad segment'
		BRK
	FI
		JMP	tcp_ack			; Oh dear.  Ack the last one
						; again.

		; OK so far -- this segment is earlier than the one we wanted.
		; Calculate the difference between rcv.nxt and seg.seq.  If
		; it's less than rlength, we're in business.
:okds		SEC
		LDA	rcv_nxt
		SBC	seg_seq
		STA	scratch
		LDA	rcv_nxt+1
		SBC	seg_seq+1
		STA	scratch+1
		LDA	rcv_nxt+2
		SBC	seg_seq+2
		STA	scratch+2
		LDA	rcv_nxt+3
		SBC	seg_seq+3
		STA	scratch+3
		LDX	scratch+2
		LDY	scratch+3
	IF __DEBUG__
		JSR	prinline
		ASC	'DS '
		BRK
		JSR	PrintHex2
		LDX	scratch
		LDY	scratch+1
		JSR	PrintHex2
	FI
		LDA	scratch+3
;		CMP	#0
		BNE	:nogood
		LDA	scratch+2
;		CMP	#0
		BNE	:nogood
		LDA	scratch+1
;		CMP	#0
		BNE	:nogood
		CLC
		LDA	tcprx_out
		SBC	tcprx_in
		CMP	scratch
		BEQ	:nogood
		BCS	:okds2
:nogood
	IF __DEBUG__
		JSR	prinline
		ASC	'Bad seg'
		DB	13
		BRK
	FI
		JMP	tcp_ack

:okds2		LDA	rlength
		SEC
		SBC	scratch
		STA	rlength
		LDA	rlength+1
		SBC	#0
		STA	rlength+1
		LDA	data
		CLC
		ADC	scratch
		STA	data
		LDA	data+1
		ADC	#0
		STA	data+1
:yessame
	IF __DEBUG__
		JSR	prinline
		ASC	'out,in='
		BRK
		LDA	tcprx_out
		JSR	PrintHex1
		LDA	#32
		JSR	OSWRCH
		LDA	tcprx_in
		JSR	PrintHex1
		JSR	OSNEWL
	FI
		LDA	rlength+1
;		CMP	#0
		BNE	:toomuch
		CLC
		LDA	tcprx_out
		SBC	tcprx_in
		CMP	rlength
		BCS	:nottoomuch
		BEQ	:nottoomuch
		; At the moment we don't over-advertise the window, so this
		; should never happen.  This will change if Martyn gets his
		; evil way, though.  Even then we hope it would never happen,
		; but we have to do the best we can in the face of badness.
:toomuch
	IF __DEBUG__
		JSR	prinline
		ASC	'[Too much!]'
		BRK
	FI
		JMP	tcp_ack
:nottoomuch
		LDY	#&FF
		LDX	tcprx_in
:copy		INY
		CPY	rlength
		BEQ	:done
		LDA	(data),Y
		STA	tcprx_buffer,X
		INX
		JMP	:copy
:done		STX	tcprx_in
		CLC
		LDA	rcv_nxt
		ADC	rlength
		STA	rcv_nxt
		LDA	rcv_nxt+1
		ADC	rlength+1
		STA	rcv_nxt+1
		LDA	rcv_nxt+2
		ADC	#0
		STA	rcv_nxt+2
		BCC	:skip3
		INC	rcv_nxt+3
:skip3		LDA	our_fin_flag
;		CMP	#0
		BEQ	tcp_ack
		LDA	rcv_nxt
		SEC
		ADC	#0
		STA	rcv_nxt
		LDA	rcv_nxt+1
		ADC	#0
		STA	rcv_nxt+1
		LDA	rcv_nxt+2
		ADC	#0
		STA	rcv_nxt+2
		BCC	tcp_ack
		INC	rcv_nxt+3
		JMP	tcp_ack

tcp_ack
		; We hold back ACK frames by 1/10 second, in hopes that they
		; can be merged with some other frame.

		; If we've already delayed an ACK, send this one now
		LDA	ack_timer
;		CMP	#0
		BNE	tcp_ack_send

		; If there is a frame about to go out, add the ACK to it
		LDA	tx_flag
;		CMP	#0
		BNE	:fq

		; Set the timer and forget about it for now
		LDA	#6
		STA	ack_timer
		RTS

tcp_ack_send
		; If a segment is about to go out, piggyback the ACK onto it
		LDA	tx_flag
;		CMP	#0
		BEQ	:nfq

:fq
		LDA	tcp_retransmit
		STA	tcp_frame
		LDA	tcp_retransmit+1
		STA	tcp_frame+1
		JMP	:set_ack

		; We can't use tcp_buffer to generate ACK frames, as there
		; may be a data segment there waiting to be transmitted.
:nfq		LDX	#>tcpack_buffer
		LDY	#<tcpack_buffer
		STX	tcp_frame
		STY	tcp_frame+1
		LDY	#24
		LDA	#&50
		STA	(tcp_frame),Y
		LDY	#10
		LDA	#0
		STA	(tcp_frame),Y
		INY
		LDA	#20
		STA	(tcp_frame),Y
		LDA	#0
		PHA
		LDA	our_fin_flag
		CMP	#1
		BNE	:no_fin
		PLA
		ORA	#FIN
		PHA
		LDA	#2
		STA	our_fin_flag
:no_fin		PLA
		LDY	#25
		STA	(tcp_frame),Y
		INY
		LDA	#0
		STA	(tcp_frame),Y
		INY
		STA	(tcp_frame),Y
		LDA	snd_nxt+3
		LDY	#16
		STA	(tcp_frame),Y
		INY
		LDA	snd_nxt+2
		STA	(tcp_frame),Y
		INY
		LDA	snd_nxt+1
		STA	(tcp_frame),Y
		INY
		LDA	snd_nxt
		STA	(tcp_frame),Y
:set_ack	LDY	#25
		LDA	(tcp_frame),Y
		ORA	#ACK
		STA	(tcp_frame),Y
		LDY	#20
		LDA	rcv_nxt+3
		STA	(tcp_frame),Y
		INY
		LDA	rcv_nxt+2
		STA	(tcp_frame),Y
		INY
		LDA	rcv_nxt+1
		STA	(tcp_frame),Y
		INY
		LDA	rcv_nxt
		STA	(tcp_frame),Y
		CLC
		LDA	tcprx_out
		SBC	tcprx_in
		LDY	#27
		STA	(tcp_frame),Y       ; set window
		CMP	#0
		BNE	:notzerowindow
		LDA	#1
		STA	zwindow
:notzerowindow
		LDA	#0
		STA	ack_timer
		; If we created this frame, send it and delete it
		LDA	tx_flag
;		CMP	#0
		BNE	:rts
		JMP	tcp_send
:rts		RTS

tcp_send
	BLOCK
	IF __DEBUG__
		JSR	prinline
		ASC	'tcp_send: '
		BRK
	FI
		LDY	#12
		LDA	ourport+1
		STA	(tcp_frame),Y
		INY
		LDA	ourport
		STA	(tcp_frame),Y
		INY
		LDA	hisport+1
		STA	(tcp_frame),Y
		INY
		LDA	hisport
		STA	(tcp_frame),Y
tcp_send_port
		LDY	#0
		LDA	my_ip
		STA	(tcp_frame),Y
		INY
		LDA	my_ip+1
		STA	(tcp_frame),Y
		INY
		LDA	my_ip+2
		STA	(tcp_frame),Y
		INY
		LDA	my_ip+3
		STA	(tcp_frame),Y
		INY
		LDA	his_ip
		STA	pblk+6
		STA	(tcp_frame),Y
		INY
		LDA	his_ip+1
		STA	pblk+7
		STA	(tcp_frame),Y
		INY
		LDA	his_ip+2
		STA	pblk+8
		STA	(tcp_frame),Y
		INY
		LDA	his_ip+3
		STA	pblk+9
		STA	(tcp_frame),Y
		INY
		LDA	#0
		STA	(tcp_frame),Y
		INY
		LDA	#6		; proto
		STA	(tcp_frame),Y
		STA	pblk
		LDA	#0		; xsum
		LDY	#28
		STA	(tcp_frame),Y
		INY
		STA	(tcp_frame),Y
		LDY	#11
		LDA	(tcp_frame),Y
		CLC
		ADC	#12
		LDX	tcp_frame
		LDY	tcp_frame+1
		JSR	ip_checksum
		TYA
		LDY	#28
		STA	(tcp_frame),Y
		TXA
		INY
		STA	(tcp_frame),Y
		LDA	tcp_frame
		CLC
		ADC	#12		; strip pseudo header
		STA	pblk+2
		LDA	tcp_frame+1
		ADC	#0
		STA	pblk+3
		LDY	#10
		LDA	(tcp_frame),Y
		STA	pblk+5
		INY
		LDA	(tcp_frame),Y
		STA	pblk+4
		LDX	#>pblk
		LDY	#<pblk
		JMP	send_packet

send_bootp
		LDA	#>tcpack_buffer
		STA	tcp_frame
		LDA	#<tcpack_buffer
		STA	tcp_frame+1
		LDY	#12
		LDA	#0
		STA	(tcp_frame),Y
		INY
		INY
		STA	(tcp_frame),Y
		DEY
		LDA	#68
		STA	(tcp_frame),Y
		INY
		INY
		LDA	#67
		STA	(tcp_frame),Y
		LDA	#&34
		LDY	#11
		STA	(tcp_frame),Y
		LDY	#17
		STA	(tcp_frame),Y
		LDA	#1
		LDY	#10
		STA	(tcp_frame),Y
		LDA	#0
		LDY	#16
		STA	(tcp_frame),Y
		LDA	#255
		STA	his_ip
		STA	his_ip+1
		STA	his_ip+2
		STA	his_ip+3
		LDY	#20
		LDA	#1		; boot request
		STA	(tcp_frame),Y
		LDA	#80
		INY
		STA	(tcp_frame),Y
		INY
		LDA	#2
		STA	(tcp_frame),Y
		INY
		LDA	#0		; hops
		STA	(tcp_frame),Y
		INY
		STA	(tcp_frame),Y	; xid
		INY
		STA	(tcp_frame),Y
		INY
		STA	(tcp_frame),Y
		INY
		STA	(tcp_frame),Y
		INY
		STA	(tcp_frame),Y	; secs
		INY
		LDA	#1
		STA	(tcp_frame),Y


		; must have already initialised port numbers
udp_send
		LDY	#0
		LDA	my_ip
		STA	(tcp_frame),Y
		INY
		LDA	my_ip+1
		STA	(tcp_frame),Y
		INY
		LDA	my_ip+2
		STA	(tcp_frame),Y
		INY
		LDA	my_ip+3
		STA	(tcp_frame),Y
		INY
		LDA	his_ip
		STA	pblk+6
		STA	(tcp_frame),Y
		INY
		LDA	his_ip+1
		STA	pblk+7
		STA	(tcp_frame),Y
		INY
		LDA	his_ip+2
		STA	pblk+8
		STA	(tcp_frame),Y
		INY
		LDA	his_ip+3
		STA	pblk+9
		STA	(tcp_frame),Y
		INY
		LDA	#0
		STA	(tcp_frame),Y
		INY
		LDA	#17		; proto
		STA	(tcp_frame),Y
		STA	pblk
		LDA	#0		; xsum
		LDY	#18
		STA	(tcp_frame),Y
		INY
		STA	(tcp_frame),Y
		LDY	#11
		LDA	(tcp_frame),Y
		CLC
		ADC	#12
		LDX	tcp_frame
		LDY	tcp_frame+1
		JSR	ip_checksum
		TYA
		LDY	#18
		STA	(tcp_frame),Y
		TXA
		INY
		STA	(tcp_frame),Y
		LDA	tcp_frame
		CLC
		ADC	#12		; strip pseudo header
		STA	pblk+2
		LDA	tcp_frame+1
		ADC	#0
		STA	pblk+3
		LDY	#10
		LDA	(tcp_frame),Y
		STA	pblk+5
		INY
		LDA	(tcp_frame),Y
		STA	pblk+4
		LDX	#>pblk
		LDY	#<pblk
		JMP	send_packet

icmp
	IF __DEBUG__
		JSR	prinline
		ASC	'ICMP '
		BRK
	FI
		LDY	#0
		LDA	(data),Y
	IF __DEBUG__
		JSR	PrintHex1
	FI
		CMP	#8
		BNE	:notechoreq
		JMP	icmp_echoreq
:notechoreq
	IF __DEBUG__
		JSR	OSNEWL
	FI
		RTS

icmp_echoreq
	IF __DEBUG__
		JSR	prinline
		ASC	' Echo Request'
		BRK
		JSR	OSNEWL
	FI
		LDY	#0
		TYA
		STA	(data),Y	; mark as Echo Reply
		LDY	#2		; zero checksum
		STA	(data),Y
		INY
		STA	(data),Y
		LDX	data
		LDY	data+1
		LDA	rlength
		JSR	ip_checksum
		TYA
		LDY	#2
		STA	(data),Y
		TXA
		INY
		STA	(data),Y
		LDA	data
		STA	pblk+2
		LDA	data+1
		STA	pblk+3
		LDA	rlength
		STA	pblk+4
		LDA	rlength+1
		STA	pblk+5
		LDA	#1		; ICMP
		STA	pblk
		LDA	his_ip
		STA	pblk+6
		LDA	his_ip+1
		STA	pblk+7
		LDA	his_ip+2
		STA	pblk+8
		LDA	his_ip+3
		STA	pblk+9
		LDX	#>pblk
		LDY	#<pblk
		JMP	send_packet

send_packet
		STX	params
		STY	params+1
	IF __DEBUG__
		JSR	prinline
		ASC	'send_packet '
		BRK
	FI
		LDY	#4		; calc size of data+header
		LDA	(params),Y
		CLC
		ADC	#&14
		TAX
		PHA
		INY
		LDA	(params),Y
		ADC	#0
		TAY
		PHA
	IF __DEBUG__
		JSR	PrintHex2
	FI
		LDA	txbuffer_ptr
		STA	temp2+1
		LDA	#0
		STA	temp2
	IF __DEBUG__
		LDA	#32
		JSR	&FFEE
		JSR	PrintHex2
	FI
		LDA	#&45
		LDY	#0
		STA	(temp2),Y		; Vers IHL
		TYA
		INY
		STA	(temp2),Y		; TOS
		INY
		PLA
		STA	(temp2),Y		; Total size
		INY
		PLA
		STA	(temp2),Y
		INY
		LDA	clock
		STA	(temp2),Y		; ID
		INY
		LDA	clock+1
		STA	(temp2),Y
		INY
		LDA	#0
		STA	(temp2),Y		; Flags
		INY
		STA	(temp2),Y		; FragOff
		INY
		LDA	#64
		STA	(temp2),Y		; TTL
		LDY	#0
		LDA	(params),Y
		LDY	#9
		STA	(temp2),Y		; proto
		LDA	#0
		INY
		STA	(temp2),Y		; checksum
		INY
		STA	(temp2),Y
		LDY	#6
		LDA	(params),Y
		PHA
		INY
		LDA	(params),Y
		PHA
		INY
		LDA	(params),Y
		PHA
		INY
		LDA	(params),Y
		LDY	#19
		STA	(temp2),Y
		DEY
		PLA
		STA	(temp2),Y
		DEY
		PLA
		STA	(temp2),Y
		DEY
		PLA
		STA	(temp2),Y
		LDY	#12
		LDA	my_ip
		STA	(temp2),Y
		LDA	my_ip+1
		INY
		STA	(temp2),Y
		LDA	my_ip+2
		INY
		STA	(temp2),Y
		LDA	my_ip+3
		INY
		STA	(temp2),Y
		LDY	#20
		LDA	#0
		INY
		STA	(temp2),Y
		INY
		STA	(temp2),Y
		INY
		STA	(temp2),Y
		INY
		STA	(temp2),Y
		LDX	temp2
		LDY	temp2+1
		LDA	#&14
		JSR	ip_checksum
		TYA
		LDY	#10		; store the checksum
		STA	(temp2),Y
		INY
		TXA
		STA	(temp2),Y
		LDY	#2
		LDA	(params),Y
		STA	cpsrc
		INY
		LDA	(params),Y
		STA	cpsrc+1
		INY
		LDA	(params),Y
		STA	cplen
		INY
		LDA	(params),Y
		STA	cplen+1
		LDA	temp2
		CLC
		ADC	#&14
		STA	cpdst
		LDA	temp2+1
		ADC	#0
		STA	cpdst+1
		JSR	block_copy
		LDA	his_ip
		CMP	#&FF
		BNE	:not_bc
		LDA	his_ip+1
		CMP	#&FF
		BNE	:not_bc
		LDA	his_ip+2
		CMP	#&FF
		BNE	:not_bc
		LDA	his_ip+3
		CMP	#&FF
		BEQ	:is_broadcast
:not_bc		JSR	do_route		; check for specific route
		LDA	his_ip			; is destination on this
		CMP	my_ip			; subnet?
		BNE	:not_local
		LDA	his_ip+1
		CMP	my_ip+1
		BNE	:not_local
		LDA	his_ip+2
		CMP	my_ip+2
		BEQ	:is_local
:not_local	LDA	#0			; use the default route
		STA	his_ip
		STA	his_ip+1
		STA	his_ip+2
		STA	his_ip+3
		JSR	do_route
:is_local	LDA	his_ip+3
		CMP	#&FF
		BEQ	:is_broadcast
		JSR	do_arp			; look up his address
		LDA	rstation
;		CMP	#0			; valid arp entry?
		BNE	:send
		JMP	:dont_send
:send		LDA	#50
		STA	retry
		JMP	:try_tx
:is_broadcast	LDA	#240
		STA	rstation
		LDA	#0
		STA	rstation+1
		LDA	#&FE
		STA	bccount
:try_tx
		LDA	blk_ptr
		STA	temp+1
		LDA	#0
		STA	temp
		LDA	#&81
		LDY	#0
		STA	(temp),Y
		LDA	#&D2
		INY
		STA	(temp),Y
		LDA	rstation
		INY
		STA	(temp),Y
		LDA	rstation+1
		INY
		STA	(temp),Y
		LDA	temp2
		INY
		STA	(temp),Y
		LDA	temp2+1
		INY
		STA	(temp),Y
		LDA	#&FF
		INY
		STA	(temp),Y
		INY
		STA	(temp),Y
		LDY	#10
		STA	(temp),Y
		INY
		STA	(temp),Y
		LDY	#3
		LDA	(temp2),Y	; get total length LSB
		CLC
		LDY	#4
		ADC	(temp),Y
		LDY	#8
		STA	(temp),Y
		LDY	#2
		LDA	(temp2),Y	; MSB
		LDY	#5
		ADC	(temp),Y
		LDY	#9
		STA	(temp),Y
		LDA	#&10
		LDX	#0
		LDY	blk_ptr
		JSR	OSWORD
		LDA	his_ip+3
		CMP	#&FF
		BEQ	:next_bcast
:poll_tx	LDA	#&32
		JSR	OSBYTE
		TXA
		BMI	:poll_tx
		AND	#64
;		CMP	#0
		BEQ	:dont_send		; tx OK
		DEC	retry
		LDA	retry
;		CMP	#0
		BNE	:try_tx
		LDA	#'?'
		JSR	OSWRCH
:dont_send
	IF __DEBUG__
		JSR	OSNEWL
	FI
:rts		RTS

:next_bcast	INC	bccount
		LDA	bccount
		BNE	:go_try_tx
		LDA	#&FE
		STA	bccount
		INC	rstation
		LDA	rstation
		CMP	#&FF
		BEQ	:rts
:go_try_tx	JMP	:try_tx

do_route
	BLOCK
		LDA	#0
:loop
		TAX
		LDA	route,X
		CMP	his_ip
		BNE	:no
		LDA	route+1,X
		CMP	his_ip+1
		BNE	:no
		LDA	route+2,X
		CMP	his_ip+2
		BNE	:no
		LDA	route+3,X
		CMP	his_ip+3
		BNE	:no
	; Got a match
		LDA	route+4,X
		STA	his_ip
		LDA	route+5,X
		STA	his_ip+1
		LDA	route+6,X
		STA	his_ip+2
		LDA	route+7,X
		STA	his_ip+3
		RTS
:no		; terminator?
		LDA	route,X
		CMP	#&FF
		BNE	:not_targeted
		LDA	route+1,X
		CMP	#&FF
		BNE	:not_targeted
		LDA	route+2,X
		CMP	#&FF
		BNE	:not_targeted
		LDA	route+3,X
		CMP	#&FF
		BNE	:not_targeted
		RTS
:not_targeted	TXA
		CLC
		ADC	#8		; go round for another try
		JMP	:loop

do_arp
	BLOCK
		LDA	#0
:loop		TAX
		LDA	arp,X
		CMP	his_ip
		BNE	:no
		LDA	arp+1,X
		CMP	his_ip+1
		BNE	:no
		LDA	arp+2,X
		CMP	his_ip+2
		BNE	:no
		LDA	arp+3,X
		CMP	his_ip+3
		BNE	:no
		LDA	arp+4,X
		STA	rstation
		LDA	arp+5,X
		STA	rstation+1
		RTS
:no		TXA
		CLC
		ADC	#6
		CMP	#60		; Finished yet?
		BNE	:loop
		LDA	#0		; He's not there.
		STA	rstation
		STA	rstation+1
		LDA	blk_ptr
		STA	temp+1
		LDA	#0
		STA	temp
		LDA	his_ip
		LDY	#8
		STA	(temp),Y
		LDA	his_ip+1
		INY
		STA	(temp),Y
		LDA	his_ip+2
		INY
		STA	(temp),Y
		LDA	his_ip+3
		INY
		STA	(temp),Y
		LDA	my_ip
		LDY	#4
		STA	(temp),Y
		LDA	my_ip+1
		INY
		STA	(temp),Y
		LDA	my_ip+2
		INY
		STA	(temp),Y
		LDA	my_ip+3
		INY
		STA	(temp),Y
		LDY	#0
		LDA	#&A1
		STA	(temp),Y
		LDA	#&D2
		INY
		STA	(temp),Y
		LDA	#&FF
		INY
		STA	(temp),Y
		INY
		STA	(temp),Y
		LDA	#&10
		LDX	#0
		LDY	blk_ptr
		JMP	OSWORD

ip_checksum
	BLOCK
	; entry YX point to block. A=count
	; exit YX contain checksum
		STA	scratch+3
		LDA	temp2
		PHA
		LDA	temp2+1
		PHA
		STX	temp2
		STY	temp2+1
		LDY	#0
		STY	scratch
		STY	scratch+1
		STY	scratch+2
:loop
		LDA	(temp2),Y
		PHA
		INY
		CPY	scratch+3
		CLC
		BEQ	:end
		LDA	(temp2),Y
		ADC	scratch
		STA	scratch
:end		PLA
		ADC	scratch+1
		STA	scratch+1
		LDA	scratch+2
		ADC	#0
		STA	scratch+2
		CPY	scratch+3
		BEQ	:loop2
		INY
		CPY	scratch+3
		BNE	:loop
:loop2		LDA	scratch+2
;		CMP	#0
		BEQ	:skip
		CLC
		ADC	scratch
		STA	scratch
		LDA	scratch+1
		ADC	#0
		STA	scratch+1
		LDA	#0
		ADC	#0
		STA	scratch+2
		JMP	:loop2
:skip
		LDA	scratch
		EOR	#&FF
		TAX
		LDA	scratch+1
		EOR	#&FF
		TAY
		PLA
		STA	temp2+1
		PLA
		STA	temp2
		RTS

PrintHex2
		PHA
		TYA
		JSR	PrintHex1
		TXA
		JSR	PrintHex1
		PLA
		RTS

PrintHex1
	BLOCK
		PHA
		AND	#240
		LSR	A
		LSR	A
		LSR	A
		LSR	A
		JSR	:HexNibble
		PLA
		PHA
		AND	#15
		JSR	:HexNibble
		PLA
		RTS
:HexNibble
		BLOCK
		PHA
		CMP	#10
		BCC	:nibble.dec
		CLC
		ADC	#'A'-10
		JSR	OSWRCH
		PLA
		RTS

:nibble.dec
		ADC	#'0'
		JSR	OSWRCH
		PLA
		RTS

prinline
	BLOCK
		PLA
		CLC
		ADC	#1
		STA	pltemp
		PLA
		ADC	#0
		STA	pltemp+1
		TYA
		PHA
		LDY	#0
:loop
		LDA	(pltemp),Y
		BEQ	:done
		JSR	OSASCI
		INC	pltemp
		BNE	:loop
		INC	pltemp+1
		BNE	:loop
:done
		PLA
		TAY
		INC	pltemp
		BNE	:skip
		INC	pltemp+1
:skip		JMP	(pltemp)

error
	BLOCK
		PLA
		CLC
		ADC	#1
		STA	pltemp
		PLA
		ADC	#0
		STA	pltemp+1
		TYA
		PHA
		LDY	#0
		STY	&100
		LDA	(pltemp),Y
		STA	&101
		INY
		LDX	#2
:loop
		LDA	(pltemp),Y
		STA	&100,X
;		CMP	#0
		BEQ	:done
		INX
		INC	pltemp
		BNE	:loop
		INC	pltemp+1
		BNE	:loop
:done
		PLA
		TAY
		INC	pltemp
		BNE	:skip
		INC	pltemp+1
:skip		JMP	&100


block_copy
	BLOCK
		LDA	temp
		PHA
		LDA	temp+1
		PHA
		LDA	temp2
		PHA
		LDA	temp2+1
		PHA
		LDA	cpsrc
		STA	temp
		LDA	cpsrc+1
		STA	temp+1
		LDA	cpdst
		STA	temp2
		LDA	cpdst+1
		STA	temp2+1
		LDY	#0
:loop		DEC	cplen
		LDA	cplen
		CMP	#&FF
		BNE	:skip
		DEC	cplen+1
		LDA	cplen+1
		CMP	#&FF
		BEQ	:done
:skip		LDA	(temp),Y
		STA	(temp2),Y
		INY
		CPY	#0
		BNE	:loop
		INC	temp+1
		INC	temp2+1
		JMP	:loop
:done		PLA
		STA	temp2+1
		PLA
		STA	temp2
		PLA
		STA	temp+1
		PLA
		STA	temp
		RTS

arp_request
	BLOCK
	IF __DEBUG__
		JSR	prinline
		ASC	'ARP request '
		BRK
	FI
		LDA	blk_ptr
		STA	temp+1
		LDA	#0
		STA	temp
		LDY	#5
		LDA	(temp),Y
		STA	data
		INY
		LDA	(temp),Y
		STA	data+1
		LDY	#0
		LDA	(data),Y
		STA	his_ip
		INY
		LDA	(data),Y
		STA	his_ip+1
		INY
		LDA	(data),Y
		STA	his_ip+2
		INY
		LDA	(data),Y
		STA	his_ip+3
		LDY	#4
		LDA	(data),Y
		CMP	my_ip
		BNE	:not_me
		INY
		LDA	(data),Y
		CMP	my_ip+1
		BNE	:not_me
		INY
		LDA	(data),Y
		CMP	my_ip+2
		BNE	:not_me
		INY
		LDA	(data),Y
		CMP	my_ip+3
		BEQ	:is_me
:not_me
	IF __DEBUG__
		JSR	prinline
		ASC	' (ignored)'
		DB	13
		BRK
	FI
		JMP	open_rx

:is_me
	IF __DEBUG__
		JSR	prinline
		ASC	' (erk!)'
		DB	13
		BRK
	FI
		LDA	blk_ptr
		STA	temp+1
		LDA	#0
		STA	temp
		LDY	#&20
		LDA	my_ip
		STA	(temp),Y
		LDA	my_ip+1
		INY
		STA	(temp),Y
		LDA	my_ip+2
		INY
		STA	(temp),Y
		LDA	my_ip+3
		INY
		STA	(temp),Y
		LDA	his_ip
		INY
		STA	(temp),Y
		LDA	his_ip+1
		INY
		STA	(temp),Y
		LDA	his_ip+2
		INY
		STA	(temp),Y
		LDA	his_ip+3
		INY
		STA	(temp),Y
		LDA	#&A2
		LDY	#0
		STA	(temp),Y
		LDA	#&D2
		INY
		STA	(temp),Y
		LDA	rstation
		INY
		STA	(temp),Y
		LDA	rstation+1
		INY
		STA	(temp),Y
		LDA	#&20
		INY
		STA	(temp),Y
		CLC
		ADC	#8
		LDY	#8
		STA	(temp),Y
		LDA	blk_ptr
		LDY	#5
		STA	(temp),Y
		LDY	#9
		STA	(temp),Y
		LDA	#&FF
		LDY	#6
		STA	(temp),Y
		INY
		STA	(temp),Y
		LDY	#10
		STA	(temp),Y
		INY
		STA	(temp),Y
		LDA	#&10
		LDX	#0
		LDY	blk_ptr
		JSR	OSWORD
		JMP	open_rx

	BLOCK
:syntax		JSR	error
		BRK
		ASC	'Syntax: *TELNET <ip address> (<port>)'
		BRK

telapicmd
	; Open a TCP connection to some remote host
		LDA	tcp_frame
		PHA
		LDA	tcp_frame+1
		PHA
:loop		LDA	(temp2),Y
		CMP	#&D
		BEQ	:syntax
		CMP	#32
		BNE	:go1
		INY
		JMP	:loop
:go1		JSR	getNumber
		STA	tcp_peer
		LDA	(temp2),Y
		CMP	#'.'
		BNE	:syntax
		INY
		JSR	getNumber
		STA	tcp_peer+1
		LDA	(temp2),Y
		CMP	#'.'
		BNE	:syntax
		INY
		JSR	getNumber
		STA	tcp_peer+2
		LDA	(temp2),Y
		CMP	#'.'
		BNE	:syntax
		INY
		JSR	getNumber
		STA	tcp_peer+3
		LDA	#23
		STA	hisport
		LDA	(temp2),Y
		CMP	#&D
		BEQ	:cr
		CMP	#' '
		BEQ	:oksyntax
		JMP	:syntax
:oksyntax
		INY
		JSR	getNumber
		STA	hisport
:cr		LDA	tcp_state
		CMP	#STATE_CLOSED
		BEQ	:okstate
		JSR	error
		BRK
		ASC	"Connection already open"
		BRK
:okstate	LDA	#0
		STA	our_fin_flag
		LDA	#0
		STA	hisport+1
		LDA	tcp_timer
		STA	ourport
	IF __DEBUG__
		JSR	prinline
		ASC	'TCP open connection to '
		BRK
		LDA	tcp_peer
		JSR	PrintHex1
		LDA	#46
		JSR	OSWRCH
		LDA	tcp_peer+1
		JSR	PrintHex1
		LDA	#46
		JSR	OSWRCH
		LDA	tcp_peer+2
		JSR	PrintHex1
		LDA	#46
		JSR	OSWRCH
		LDA	tcp_peer+3
		JSR	PrintHex1
		LDA	#'/'
		JSR	OSWRCH
		LDA	hisport+1
		JSR	PrintHex1
		LDA	hisport
		JSR	PrintHex1
		JSR	OSNEWL
	FI
		LDA	#0
		STA	iac_flag
		LDA	#31
		STA	ourport+1
		JSR	lock_rx
		LDX	#>tcp_buffer
		LDY	#<tcp_buffer
		STX	tcp_frame
		STY	tcp_frame+1
		LDY	#24
		LDA	#&50
		STA	(tcp_frame),Y
		LDY	#10
		LDA	#0
		STA	(tcp_frame),Y
		INY
		LDA	#20
		STA	(tcp_frame),Y
		LDA	#SYN
		LDY	#25
		STA	(tcp_frame),Y
		INY
		LDA	#0
		STA	(tcp_frame),Y
		INY
		STA	(tcp_frame),Y
		LDA	snd_nxt+3
		LDY	#16
		STA	(tcp_frame),Y
		INY
		LDA	snd_nxt+2
		STA	(tcp_frame),Y
		INY
		LDA	snd_nxt+1
		STA	(tcp_frame),Y
		INY
		LDA	snd_nxt
		STA	(tcp_frame),Y
		JSR	tcp_queue
		LDA	#STATE_SYN_SENT
		STA	tcp_state
		JSR	unlock_rx
		JSR	prinline
		ASC	'Trying...'
		BRK
:poll
		LDA	tcp_state
		CMP	#STATE_ESTABLISHED
		BEQ	:go
		CMP	#STATE_CLOSED
		BEQ	:fail
		LDA	#126
		JSR	OSBYTE
		CPX	#255
		BNE	:poll
		LDA	#STATE_CLOSED
		STA	tcp_state
		LDA	#0
		STA	tcp_retransmit
		STA	tcp_retransmit+1
		PLA
		STA	tcp_frame+1
		PLA
		STA	tcp_frame
		LDA	#&FF
		RTS
:fail		JSR	error
		BRK
		ASC	"Connection failed"
		BRK
:go		JSR	prinline
		DB	13
		ASC	'Connected.'
		DB	13
		BRK
		PLA
		STA	tcp_frame+1
		PLA
		STA	tcp_frame
		LDA	#&FF
		RTS

	BLOCK
insert		CPX	#2
		BEQ	:insserial
		JMP	(old_insv)

:insserial	JMP	tcp_write

	BLOCK
remove		CPX	#1
		BEQ	:remserial
		JMP	(old_remv)

:remserial	LDX	tcprx_out
		CPX	tcprx_in
		BEQ	:empty
		LDA	tcprx_buffer,X
		INX
		STX	tcprx_out
		CPX	tcprx_in
		BNE	:nozwcheck
		JSR	:zwcheck
:nozwcheck	LDY	iac_flag
		CPY	#1
		BEQ	:iac1
		CPY	#2
		BEQ	:iac2
		CMP	#255
		BNE	:iac0
		LDA	#1
		STA	iac_flag
		JMP	:remserial		; try again
:iac0		TAY

		CLC
		RTS

:iac1		STA	iac_cmd
		INC	iac_flag
		JMP	:remserial

:iac2		PHA
		LDA	#0
		STA	iac_flag
		LDA	iac_cmd
		CMP	#253
		BEQ	:iac_do
		PLA
		JMP	:remserial

:iac_do		LDA	#255
		JSR	tcp_write
		LDA	#252
		JSR	tcp_write
		PLA
		JSR	tcp_write
		JMP	:remserial

:zwcheck	PHA
		LDA	zwindow
;		CMP	#0
		BEQ	:rts
		LDA	#2
		STA	ack_timer
		DEC	zwindow		; zwindow=0
:rts		PLA
		RTS

:empty		JSR	:zwcheck
		SEC
		RTS

	BLOCK
countpurge	CPX	#1
		BEQ	:countserial
		CPX	#2
		BEQ	:countserialout
		JMP	(old_cnpv)

:countserial	BVS	:flush
		PHP
		PHP
		CLC
		LDA	tcprx_out
		SBC	tcprx_in
		PLP
		BCS	:counted
:countminus
		EOR	#255
:counted	TAX
		LDY	#0
		PLP
:flush		RTS

:countserialout
		BVS	:flush
		PHP
		PHP
		CLC
		LDA	tcptx_out
		SBC	tcptx_in
		PLP
		BCS	:counted
		JMP	:countminus

telnetcmd
	BLOCK
		JSR	telapicmd
:mainloop	LDX	#>trx_buf
		LDY	#<trx_buf
		LDA	#32
		JSR	tcp_read
		TAY
		LDX	#0
:loop		CPY	#32
		BEQ	:done
		LDA	iac_flag
		CMP	#1
		BEQ	:iac1j
		CMP	#2
		BEQ	:iac2j
		LDA	trx_buf,X
		CMP	#&FF
		BNE	:not_iac
		LDA	#1
		STA	iac_flag
		JMP	:next
:iac1j		JMP	:iac1
:iac2j		JMP	:iac2
:not_iac
		JSR	OSWRCH
:next		INX
		INY
		JMP	:loop
:done		LDA	#145
		LDX	#0
		LDY	#0
		JSR	OSBYTE
		BCS	:nokey
		TYA
:normalkey	JSR	tcp_write
:nokey		LDA	tcp_state
		CMP	#STATE_ESTABLISHED
		BEQ	:mainloop
		JMP	:finished

:iac1		LDA	trx_buf,X
		STA	iac_cmd
		LDA	#2
		STA	iac_flag
		JMP	:next

:iac2
		LDA	trx_buf,X
		PHA
		LDA	#0
		STA	iac_flag
		LDA	iac_cmd
		CMP	#253
		BEQ	:iac_do
		PLA
		JMP	:next

:iac_do		STX	iac_x
		LDA	#&FF
		JSR	tcp_write
		LDA	#252
		JSR	tcp_write
		PLA
		JSR	tcp_write
		LDX	iac_x
		JMP	:next

:finished	JSR	prinline
		ASC	"Connection closed by remote host"
		DB	13
		BRK
		JSR	lock_rx
		LDA	#STATE_CLOSED
		STA	tcp_state
		LDA	#0
		STA	tcp_frame
		STA	tcp_frame+1
		STA	tcp_retransmit
		STA	tcp_retransmit+1
		JSR	unlock_rx
		LDA	#&FF
		RTS

tcp_queue
	BLOCK
		LDY	#11
		LDA	(tcp_frame),Y
		SEC
		SBC	#20		; minus sizeof(tcp_header)
		STA	data_size
		DEY
		LDA	(tcp_frame),Y
		SBC	#0
		STA	data_size+1
		LDY	#25
		LDA	(tcp_frame),Y
		AND	#(SYN+FIN)
;		CMP	#0
		BEQ	:skip
		CLC
		LDA	data_size
		ADC	#1
		STA	data_size
		BCC	:skip
		INC	data_size+1
:skip
		CLC
		LDA	snd_nxt
		ADC	data_size
		STA	snd_nxt
		LDA	snd_nxt+1
		ADC	data_size+1
		STA	snd_nxt+1
		LDA	snd_nxt+2
		ADC	#0
		STA	snd_nxt+2
		BCC	:skip1
		INC	snd_nxt+3
:skip1
		; if we're in the established state, we can transmit an ack
		; here.
		LDY	#20
		LDA	rcv_nxt+3
		STA	(tcp_frame),Y
		INY
		LDA	rcv_nxt+2
		STA	(tcp_frame),Y
		INY
		LDA	rcv_nxt+1
		STA	(tcp_frame),Y
		INY
		LDA	rcv_nxt
		STA	(tcp_frame),Y
		INY
		LDA	tcp_state
		CMP	#STATE_ESTABLISHED
		BNE	:not_established
		INY
		LDA	(tcp_frame),Y
		ORA	#ACK
		STA	(tcp_frame),Y
		LDA	#0			; it's gone now
		STA	ack_timer
:not_established
		LDA	tcp_frame
		STA	tcp_retransmit
		LDA	tcp_frame+1
		STA	tcp_retransmit+1
;		LDA	#0
;		STA	tcp_timer
		LDA	#1
		STA	tx_flag
		RTS

tcp_close	RTS

; A: character to write
tcp_write
	BLOCK
		LDX	tcptx_in
		INX
		CPX	tcptx_out
		BEQ	:noroom
		DEX
		STA	tcptx_buffer,X
		INX
		STX	tcptx_in
		JSR	lock_rx
		; See if we already have a data frame queued up to go
		LDA	tcp_retransmit
;		CMP	#0
		BNE	:rts
		LDA	tcp_retransmit+1
;		CMP	#0
		BNE	:rts
		; No, so kick the TCP code
		LDA	#0
		STA	tcp_timer
:rts		JSR	unlock_rx
		CLC
		RTS

:noroom		SEC
		RTS

; A max# of characters to return
; X,Y point to buffer
tcp_read
		STA	trcount
		STX	temp3
		STY	temp3+1
		LDY	#0
:read_data	LDX	tcprx_out
		CPX	tcprx_in
		BEQ	:read_done
		LDA	tcprx_buffer,X
		STA	(temp3),Y
		INY
		INC	tcprx_out
		DEC	trcount
		LDA	trcount
;		CMP	#0
		BNE	:read_data
		; read all requested data
		RTS
:read_done	; buffer is empty now
		PHA
		LDA	zwindow
;		CMP	#0
		BEQ	:notzerowindow
		LDA	#2
		STA	ack_timer
		DEC	zwindow		; zwindow=0
:notzerowindow
		PLA
		RTS

lock_rx
	IF __DEBUG__
		JSR	prinline
		ASC	'[lock]'
		BRK
	FI
		LDA	#1
		STA	lockflag
		RTS

unlock_rx
	IF __DEBUG__
		JSR	prinline
		ASC	'[unlock]'
		BRK
	FI
		LDA	#0
		STA	lockflag
		RTS

tcp_transmit
	BLOCK
		SEC
		LDA	tcptx_in
		SBC	tcptx_out
		CMP	#80		; max tx at once
		BCC	:skip
		LDA	#80
:skip		STA	tx_size
		CLC
		ADC	#32			; plus TCP header
		STA	scratch
		LDY	#<tcp_buffer
		LDX	#>tcp_buffer
		STX	tcp_frame
		STY	tcp_frame+1
		LDY	#32
		LDX	tcptx_out
:copy		LDA	tcptx_buffer,X
		STA	(tcp_frame),Y
		INX
		INY
		CPY	scratch
		BNE	:copy
		STX	tcptx_out
		LDY	#24
		LDA	#&50
		STA	(tcp_frame),Y
		LDY	#10
		LDA	#0
		STA	(tcp_frame),Y
		INY
		LDA	scratch
		SEC
		SBC	#12
		STA	(tcp_frame),Y
		LDA	#0
		LDY	#25
		STA	(tcp_frame),Y
		INY
		LDA	#0
		STA	(tcp_frame),Y
		CLC
		LDA	tcprx_out
		SBC	tcprx_in
		LDY	#27
		STA	(tcp_frame),Y       ; set window
		CMP	#0
		BNE	:notzerowindow
		LDA	#1
		STA	zwindow
:notzerowindow
		LDA	snd_nxt+3
		LDY	#16
		STA	(tcp_frame),Y
		INY
		LDA	snd_nxt+2
		STA	(tcp_frame),Y
		INY
		LDA	snd_nxt+1
		STA	(tcp_frame),Y
		INY
		LDA	snd_nxt
		STA	(tcp_frame),Y
		JMP	tcp_queue

	BLOCK
ack_process
		LDA	seg_ack
		STA	snd_una
		LDA	seg_ack+1
		STA	snd_una+1
		LDA	seg_ack+2
		STA	snd_una+2
		LDA	seg_ack+3
		STA	snd_una+3
		LDA	tcp_retransmit
		STA	tcp_frame
;		CMP	#0
		BNE	:skip
		LDA	tcp_retransmit+1
;		CMP	#0
		BEQ	:no_frame_queued
:skip		LDA	tcp_retransmit+1
		STA	tcp_frame+1
		LDY	#19
		LDA	(tcp_frame),Y
		CLC
		ADC	tx_size
		STA	scratch
		DEY
		LDA	(tcp_frame),Y
		ADC	#0
		STA	scratch+1
		DEY
		LDA	(tcp_frame),Y
		ADC	#0
		STA	scratch+2
		DEY
		LDA	(tcp_frame),Y
		ADC	#0
		STA	scratch+3
		CMP	snd_una+3
		BNE	:diff
		LDA	scratch+2
		CMP	snd_una+2
		BNE	:diff
		LDA	scratch+1
		CMP	snd_una+1
		BNE	:diff
		LDA	scratch
		CMP	snd_una
		BNE	:diff
:ok		LDA	#0
		STA	tcp_retransmit
		STA	tcp_retransmit+1
		STA	tx_size
		STA	tcp_frame
		STA	tcp_frame+1
	IF __DEBUG__
		JSR	prinline
		ASC	'ACK: dequeued frame'
		DB	13
		BRK
	FI
:no_frame_queued
		; We may be able to queue some more data up for transmission
		LDA	tcptx_in
		CMP	tcptx_out
		BEQ	:tx_empty
		JMP	tcp_transmit
:tx_empty	RTS

:diff		BCS	:no_frame_queued
		JMP	reject

fin_process	LDA	#1
		STA	our_fin_flag
		LDA	#STATE_FIN
		STA	tcp_state
		RTS

	BLOCK
arpcmd
		LDX	#0
:arploop	TXA
		PHA
		LDA	arp+4,X
;		CMP	#0
		BEQ	:arpskip
		LDA	arp,X
		JSR	PrintDecimal
		LDA	#'.'
		JSR	OSWRCH
		PLA
		PHA
		TAX
		LDA	arp+1,X
		JSR	PrintDecimal
		LDA	#'.'
		JSR	OSWRCH
		PLA
		PHA
		TAX
		LDA	arp+2,X
		JSR	PrintDecimal
		LDA	#'.'
		JSR	OSWRCH
		PLA
		PHA
		TAX
		LDA	arp+3,X
		JSR	PrintDecimal
		JSR	prinline
		ASC	' at '
		BRK
		PLA
		PHA
		TAX
		LDA	arp+5,X
		JSR	PrintDecimal
		LDA	#'.'
		JSR	OSWRCH
		PLA
		PHA
		TAX
		LDA	arp+4,X
		JSR	PrintDecimal
		JSR	OSNEWL
:arpskip	PLA
		CLC
		ADC	#6
		TAX
		CMP	#240
		BNE	:arploop
		LDA	#&FF
		RTS

starcmd
	BLOCK
		JSR	cmd_decode_setup
		LDX	#4
:cmdlp		JSR	cmd_decode
		CMP	#&FF
		BEQ	:ok
		CPX	#28
		BNE	:cmdlp
		JMP	service_exit
:ok		TSX
		LDA	#0
		STA	&106,X
		JMP	service_exit

ipaddrcmd
	BLOCK
:loop		LDA	(temp2),Y
		CMP	#&D
		BEQ	:syntax
		CMP	#32
		BNE	:go
		INY
		JMP	:loop
:syntax		JSR	error
		BRK
		ASC	'Syntax: *IPADDR <ip address>'
		BRK
:go		JSR	getNumber
		STA	my_ip
		LDA	(temp2),Y
		CMP	#'.'
		BNE	:syntax
		INY
		JSR	getNumber
		STA	my_ip+1
		LDA	(temp2),Y
		CMP	#'.'
		BNE	:syntax
		INY
		JSR	getNumber
		STA	my_ip+2
		LDA	(temp2),Y
		CMP	#'.'
		BNE	:syntax
		INY
		JSR	getNumber
		STA	my_ip+3
		LDA	#&FF
		RTS

		; read a number in decimal from (temp2),Y
		; return: result in A, Y points to next character, X corrupt
getNumber
	BLOCK
		LDA	scratch
		PHA
		LDA	#0
		STA	scratch
:next		LDA	(temp2),Y
		CMP	#'0'
		BCC	:notdigit
		CMP	#58		; '9'+1
		BCS	:notdigit
		SEC
		SBC	#'0'		; get the digit
		PHA
		LDX	#0
		LDA	scratch
:multloop	CLC
		ADC	scratch
		INX
		CPX	#9
		BNE	:multloop
		STA	scratch
		PLA
		CLC
		ADC	scratch
		STA	scratch
		INY
		JMP	:next
:notdigit	LDA	scratch
		TAX
		PLA
		STA	scratch
		TXA
		RTS

deroutecmd
	BLOCK
		LDA	#&FF
		TAX
:loop		INX
		STA	route,X
		CPX	#3
		BNE	:loop
		RTS

routecmd
	BLOCK
		LDA	#0
		STA	scratch+1		; route entry
:loop		LDA	(temp2),Y
		CMP	#&D
		BEQ	:syntax
		CMP	#32
		BNE	:go
		INY
		JMP	:loop
:syntax		JSR	error
		BRK
		ASC	'Syntax: *ROUTE <dest> <gw addr>'
		BRK
:go		LDA	(temp2),Y
		CMP	#42
		BNE	:notDefault
		JMP	:isDefault
:notDefault	JSR	getNumber
		LDX	scratch+1
		STA	route,X
		LDA	(temp2),Y
		CMP	#'.'
		BNE	:syntax
		INY
		JSR	getNumber
		LDX	scratch+1
		STA	route+1,X
		LDA	(temp2),Y
		CMP	#'.'
		BNE	:syntax
		INY
		JSR	getNumber
		LDX	scratch+1
		STA	route+2,X
		LDA	(temp2),Y
		CMP	#'.'
		BNE	:syntax
		INY
		JSR	getNumber
		LDX	scratch+1
		STA	route+3,X
:part2		LDA	(temp2),Y
		CMP	#' '
		BNE	:syntax
		INY
		JSR	getNumber
		LDX	scratch+1
		STA	route+4,X
		LDA	(temp2),Y
		CMP	#'.'
		BNE	:syntax1
		INY
		JSR	getNumber
		LDX	scratch+1
		STA	route+5,X
		LDA	(temp2),Y
		CMP	#'.'
		BNE	:syntax1
		INY
		JSR	getNumber
		LDX	scratch+1
		STA	route+6,X
		LDA	(temp2),Y
		CMP	#'.'
		BNE	:syntax1
		INY
		JSR	getNumber
		LDX	scratch+1
		STA	route+7,X
		LDA	#&FF
		STA	route+8,X
		STA	route+9,X
		STA	route+10,X
		STA	route+11,X
		RTS

:syntax1	JMP	:syntax

:isDefault
		LDX	scratch+1
		LDA	#0
		STA	route,X
		STA	route+1,X
		STA	route+2,X
		STA	route+3,X
		INY
		JMP	:part2

PrintDecimal
		LDX	#0
	BLOCK				; do 100s
:loop		CMP	#&64
		BCC	:ping
		SBC	#&64
		INX
		JMP	:loop
:ping		PHA
		TXA
		BEQ	:skip
		ADC	#'0'
		JSR	OSWRCH
:skip		PLA
		LDX	#0
	BLOCK				; do 10s
:loop		CMP	#&A
		BCC	:ping
		SBC	#&A
		INX
		JMP	:loop
:ping		PHA
		TXA
		BEQ	:skip
		ADC	#'0'
		JSR	OSWRCH
:skip		PLA
		ADC	#'0'
		JMP	OSWRCH


