;----------------------------------------------------------------------------
;File name:	NETD_STX.S			Revision date:	1999.04.01
;Creator:	Ulf Ronald Andersson		Creation date:	1998.10.29
;(c)1998 by:	Ulf Ronald Andersson		All rights reserved
;Feedback to:	dlanor@oden.se			Released as FREEWARE
;----------------------------------------------------------------------------
	include	RA_TOS.I
	include	RA_JAR.I
	include	RA_XB.I
	include	RA_RAM.I
;
	include	sting\NETD.I
	include	sting\LAYER.I
	include	sting\TRANSPRT.I
;----------------------------------------------------------------------------
	output	.PRG
;----------------------------------------------------------------------------
M_YEAR	=	1999
M_MONTH	=	4
M_DAY	=	1
;
M_TITLE		MACRO
		dc.b	'NetDemon'
		ENDM
M_VERSION	MACRO
		dc.b	'01.04'
		ENDM
M_AUTHOR	MACRO
		dc.b	'Ronald Andersson'
		ENDM
;----------------------------------------------------------------------------
XB_cpu_mode	set	1	;never test mode or call Super in XB-macros
RAM_chunk	=	16384
ZERO_IP		=	0	;local IP code for listening connections
TOS_NORMAL	=	0	;normal TCP TOS code for TCP_open
;----------------------------------------------------------------------------
;Start of:	NetD_STX program
;----------------------------------------------------------------------------
	SECTION	TEXT
;----------------------------------------------------------------------------
text_start:
basepage	=	(text_start-$100)
my_cookie	=	basepage	;ND_base
;----------------------------------------------------------------------------
my_LongJump:
start:
	jmp	(start_1).l		;ND_jump
;----------------------------------------------------------------------------
;Start of:	Resident NetD_STX data (starting with tail of cookie struct)
;----------------------------------------------------------------------------
my_magic:
	dc.l	ND_XB_ID	;ND_magic	;Must equal ND_XB_ID
	dc.l	'_STX'		;ND_xmagic;Must equal '_STX'
	dc.l	REF_IF_VER	;ND_if_ver	;NetD interface version of NetD STX
	dc.w	((M_YEAR-1980)<<9)|(M_MONTH<<5)|M_DAY	;uint16 ND_date
	dc.l	my_version_s	;ND_version	;NetD STX version string in "xx.yy" format
	dc.l	my_author_s	;char_p ND_author	;-> name of author of NetD STX
;-------
	dc.l	my_init_APP	;ND_init_APP	;-> func called by NetD APP to log in to NetD STX
	dc.l	my_exit_APP	;ND_exit_APP	;-> func called by NetD APP to log out from NetD STX
	dc.l	my_exec_APP	;ND_exec_APP	;-> func called by NetD APP at GEM timer events
;-------
	dc.l	my_init_SRV	;ND_init_SRV	;-> func called by servers to log in to NetD STX
	dc.l	my_exit_SRV	;ND_exit_SRV	;-> func called by servers to log out from NetD STX
;-------
	dc.l	my_init_CON	;ND_init_CON	;-> func called by servers to reserve a CONN service
	dc.l	my_exit_CON	;ND_exit_CON	;-> func called by servers to release a CONN service
	dc.l	my_kill_CON	;ND_kill_CON	;-> func called by servers to kill an active CONN
	dc.l	my_send_CON	;ND_send_CON	;-> func optionally used by servers to send data
;-------
	dc.l	0		;ND_server_q	;-> server queue
;----------------------------------------------------------------------------
my_APP_p:	dc.l	0		;-> initialized NetD APP or is NULL
;----------------------------------------------------------------------------
sting_drivers:	dc.l	0	;DRV_LIST	*sting_drivers;
tpl:		dc.l	0	;TPL		*tpl;
stx:		dc.l	0	;STX		*stx;
;----------------------------------------------------------------------------
ND_layer:			;struct	LAYER
	dc.l	layer_name_s	;char_p	LAYER_name	;Name of layer
	dc.l	layer_version_s	;char_p	LAYER_version	;Version as xx.yy
	dc.l	0		;uint32	LAYER_flags	;Private data
	dc.w	((M_YEAR-1980)<<9)|(M_MONTH<<5)|M_DAY	;uint16	LAYER_date
	dc.l	layer_author_s	;char_p	LAYER_author	;Name of programmer
	dc.w	0		;int16	LAYER_stat_dropped	;dropped packets
	dc.l	0		;void_p	LAYER_next	;-> Next layer
basepage_p:
	dc.l	basepage	;void_p	LAYER_basepage	;-> basepage of self
;ND_layer struct end		;d_end	LAYER
;----------------------------------------------------------------------------
layer_name_s:
	M_TITLE
	dc.b	NUL
my_version_s:
layer_version_s:
	M_VERSION
	dc.b	NUL
my_author_s:
layer_author_s:
	M_AUTHOR
	dc.b	NUL
	EVEN
;----------------------------------------------------------------------------
;End of:	Resident NetD_STX data
;----------------------------------------------------------------------------
;Start of:	Resident NetD_STX routines
;----------------------------------------------------------------------------
;Start of:	NetD_STX functions called from NetD_APP
;----------------------------------------------------------------------------
;int16 my_init_APP (NetD_APP *app_base);
;
my_init_APP:			;func called by NetD APP to log in to NetD STX
	lv_init		sp	;use stack pointer to access arguments
	lv_arg.l	app_base
;
	move.l	app_base(sp),d0			;d0 -> NetD_APP struct (we hope...)
	cmp.l	#$8000,d0			;pointer too low ?
	blt	E_REFUSE_sub			;if so, go refuse service
	move.l	d0,a0				;a0 -> NetD_APP struct (we hope...)
	and.l	#$F0000001,d0			;pointer weird ?
	bne	E_REFUSE_sub			;if so, go refuse service
	cmp.l	#ND_XB_ID,ND_APP_magic(a0)	;main NetD magic correct ?
	bne	E_REFUSE_sub			;if not, go refuse service
	cmp.l	#'_APP',ND_APP_xmagic(a0)	;specific server magic correct ?
	bne	E_REFUSE_sub			;if not, go refuse service
	cmp.l	#REF_IF_VER,ND_SRV_if_ver(a0)	;correct interface version
	bne	E_REFUSE_sub			;if not, go refuse service
;-------
	move.l	a0,my_APP_p			;static my_APP_p -> NetD APP base
	moveq	#E_NORMAL,d0			;return E_NORMAL for success
	lv_exit	sp				;end sp scope for args & locals
	rts					;return to NetD APP
;----------------------------------------------------------------------------
;int16 my_exit_APP (NetD_APP *app_base);
;
my_exit_APP:			;func called by NetD APP to log out from NetD STX
	lv_init		sp	;use stack pointer to access arguments
	lv_arg.l	app_base
;
	move.l	app_base(sp),d0			;d0 = NetD_APP base of caller
	cmp.l	my_APP_p(pc),d0			;correct NetD APP pointer ?
	bne	E_REFUSE_sub			;if not, go refuse service
;-------
	clr.l	my_APP_p			;invalidate the APP pointer
.loop:
	lea	my_cookie+ND_server_q(pc),a0	;a0 ->-> server queue via cookie struct
	move.l	(a0),d0				;d0 -> first server in queue or is NULL
	ble.s	.exit				;exit at end of queue
	move.l	d0,a1				;a1 -> current server in queue
	move.l	ND_SRV_next(a1),(a0)		;link next server or NULL as new queue
	move.l	ND_SRV_warning(a1),d0		;d0 -> termination func of found server
	ble.s	.next				;skip call if pointer invalid
	move.l	d0,a0				;a0 -> termination func of found server
	move.l	my_APP_p(pc),-(sp)		;push -> NetD_APP as single arg
	jsr	(a0)				;call termination func of found server
	addq	#4,sp				;clean stack
.next:
	jmp	.loop				;loop back to inform all servers
;
.exit:
	moveq	#E_NORMAL,d0			;return success flag
	lv_exit	sp				;end sp scope for args & locals
	rts					;return to NetD APP
;----------------------------------------------------------------------------
;int16 my_exec_APP (NetD_APP *app_base);
;
my_exec_APP:			;func called by NetD APP at GEM timer events
	lv_init		sp	;use stack pointer to access arguments
	lv_arg.l	app_base
;-------
	move.l	app_base(sp),d0			;d0 = NetD_APP base of caller
	cmp.l	my_APP_p(pc),d0			;correct NetD APP pointer ?
	bne	E_REFUSE_sub			;if not, go refuse service
	movem.l	a3-a4,-(sp)			;push some registers
;-------
	move.l	my_cookie+ND_server_q(pc),d0	;d0 -> head of server queue, or is NULL
	ble.s	.done_turnon			;turnon is complete at end of server queue
.turnon_loop_1:
	move.l	d0,a4				;a4 -> NetD_SRV struct of server
.turnon_loop_2:
	protect_exec	(a4),pull_turnon_q	;pull -> NetD_CON or NULL from turnon queue
	tst.l	d0				;Is there any newly activated NetD_CON ?
	ble.s	.next_server_turnon		;skip to next server at end of turnon queue
	move.l	d0,a3				;a3 -> NetD_CON from turnon queue
	move.l	a3,-(sp)			;push -> newly activated NetD_CON as 2nd arg
	move.l	my_APP_p(pc),-(sp)		;push -> NetD_APP as 1st arg
	move.l	ND_SRV_connect(a4),a0		;a0 -> connection function of that server
	jsr	(a0)				;call connection function of the server
	addq	#8,sp				;clean stack
	cmp	#E_REFUSE,d0			;was connection refused ?
	bne.s	.activate_conn			;if not, go activate it normally
	move.l	a3,a0				;a0 -> refused NetD_CON
	bsr	release_CON_a0			;close connection and release RAM
	bra.s	.turnon_loop_2			;loop to process entire turnon queue
;-------
.activate_conn:
	protect_exec	(a3),push_active_q	;push -> NetD_CON onto active queue
	bra.s	.turnon_loop_2			;loop to process entire turnon queue
;-------
.next_server_turnon:
	move.l	ND_SRV_next(a4),d0		;d0 -> next server in server queue, or is NULL
	bgt.s	.turnon_loop_1			;loop back until whole server queue tested
.done_turnon:
	move.l	my_cookie+ND_server_q(pc),d0	;d0 -> head of server queue, or is NULL
	ble.s	.done_traffic			;traffic is complete at end of server queue
.traffic_loop:
	move.l	d0,a4				;a4 -> NetD_SRV struct of server
	move.l	ND_SRV_active_q(a4),d0	;d0 -> head of active connection queue, or is NULL
	ble.s	.next_server_traffic		;skip to next server if active queue is empty
	move.l	my_APP_p(pc),-(sp)		;push -> NetD_APP as single arg
	move.l	ND_SRV_traffic(a4),a0		;a0 -> traffic function of that server
	jsr	(a0)				;call traffic function of the server
	addq	#4,sp				;clean stack
.next_server_traffic:
	move.l	ND_SRV_next(a4),d0		;d0 -> next server in server queue, or is NULL
	bgt.s	.traffic_loop			;loop back until whole server queue tested
.done_traffic:
	movem.l	(sp)+,a3-a4			;pull some registers
	lv_exit	sp				;end sp scope for args & locals
	rts					;return to NetD APP
;----------------------------------------------------------------------------
;NetD_CON *pull_turnon_q(NetD_SRV *server);
;
pull_turnon_q:			;routine to pull an entry from turnon queue
	lv_init		sp			;use stack pointer to access arguments
	lv_arg.l	server
;-------
	move.l	server(sp),a0			;a0 -> NetD_SRV struct
	lea	ND_SRV_turnon_q(a0),a0	;a0 -> root -> activating NetD_CON, or root is NULL
	move.l	(a0),d0				;d0 -> activating NetD_CON, or is NULL
	ble.s	.exit				;if at end of queue, go return NULL in d0
	move.l	d0,a1				;a1 -> activated NetD_CON
	move.l	ND_CON_next(a1),(a0)		;unlink this NetD_CON from activation queue
.exit:						;here d0 -> activated NetD_CON, or is NULL
	lv_exit	sp				;end sp scope for args & locals
	rts					;return d0 to caller
;-------
;NB:  This routine may only be called via the STinG protect_exec function
;     so it is certain that it executes with fully disabled interrupts.
;----------------------------------------------------------------------------
;void push_active_q(NetD_CON conn);
;
push_active_q:			;routine to pull an entry from turnon queue
	lv_init		sp		;use stack pointer to access arguments
	lv_arg.l	conn
;-------
	move.l	conn(sp),a0			;a0 -> NetD_CON struct
	move.l	ND_CON_server(a0),a1		;a1 -> NetD_SRV struct
	lea	ND_SRV_active_q(a1),a1	;a1 -> queue for active connections
	move.l	(a1),ND_CON_next(a0)		;link NetD_CON to old queue
	move.l	a0,(a1)				;place NetD_CON first in queue
	lv_exit	sp				;end sp scope for args & locals
	rts					;return d0 to caller
;-------
;NB:  This routine may only be called via the STinG protect_exec function
;     so it is certain that it executes with fully disabled interrupts.
;----------------------------------------------------------------------------
;End of:	NetD_STX functions called from NetD_APP
;----------------------------------------------------------------------------
;Start of:	NetD_STX functions called from server TSR programs
;----------------------------------------------------------------------------
;int16 my_init_SRV (NetD_SRV *server);
;
my_init_SRV:			;func called by servers to log in to NetD STX
	lv_init		sp	;use stack pointer to access arguments
	lv_arg.l	server
;
	move.l	server(sp),d0			;d0 -> NetD_SRV struct (we hope...)
	cmp.l	#$8000,d0			;pointer too low ?
	blt	E_REFUSE_sub			;if so, go refuse service
	move.l	d0,a0				;a0 -> NetD_SRV struct (we hope...)
	and.l	#$F0000001,d0			;pointer weird ?
	bne	E_REFUSE_sub			;if so, go refuse service
	cmp.l	#ND_XB_ID,ND_SRV_magic(a0)	;main NetD magic correct ?
	bne	E_REFUSE_sub			;if not, go refuse service
	cmp.l	#'_SRV',ND_SRV_xmagic(a0)	;specific server magic correct ?
	bne	E_REFUSE_sub			;if not, go refuse service
	cmp.l	#REF_IF_VER,ND_SRV_if_ver(a0)	;correct interface version
	bne	E_REFUSE_sub			;if not, go refuse service
;-------
	tst.l	ND_SRV_next(a0)			;free of garbage server links ?
	bne	E_REFUSE_sub			;if not, go refuse service
	tst.l	ND_SRV_listen_q(a0)		;passive connection queue empty ?
	bne	E_REFUSE_sub			;if not, go refuse service
	tst.l	ND_SRV_active_q(a0)		;active connection queue empty ?
	bne	E_REFUSE_sub			;if not, go refuse service
;-------
	clr	ND_SRV_flags(a0)		;normalize the control flags
	lea	my_cookie+ND_server_q(pc),a1	;a1 ->-> server queue via NetD cookie
	move.l	(a1),ND_SRV_next(a0)		;link new server to old queue
	move.l	a0,(a1)				;place new server first in queue
	lv_exit	sp				;end sp scope for args & locals
E_NORMAL_sub:
NULL_sub:
	clr.l	d0				;result code = zero
	rts					;return to server
;----------------------------------------------------------------------------
;int16 my_exit_SRV (NetD_SRV *server);
;
my_exit_SRV:			;func called by servers to log out from NetD STX
	lv_init		sp	;use stack pointer to access arguments
	lv_arg.l	server
;
	move.l	server(sp),d0			;d0 -> NetD_SRV struct (we hope...)
	cmp.l	#$8000,d0			;pointer too low ?
	blt	E_REFUSE_sub			;if so, go refuse service
	move.l	d0,a0				;a0 -> NetD_SRV struct (we hope...)
	and.l	#$F0000001,d0			;pointer weird ?
	bne	E_REFUSE_sub			;if so, go refuse service
	cmp.l	#ND_XB_ID,ND_SRV_magic(a0)	;main NetD magic correct ?
	bne	E_REFUSE_sub			;if not, go refuse service
	cmp.l	#'_SRV',ND_SRV_xmagic(a0)	;specific server magic correct ?
	bne	E_REFUSE_sub			;if not, go refuse service
;-------
	lea	my_cookie+ND_server_q(pc),a1	;a1 ->-> server queue via NetD cookie
	move.l	(a1),d0				;d0 -> server queue or is NULL
	ble	E_REFUSE_sub			;if no matching server, go refuse service
.find_server_link:
	cmp.l	a0,d0				;found link to this server ?
	beq.s	.found_server_link		;if so, go deal with it
	move.l	d0,a1				;a1 -> NetD_SRV struct in queue
	lea	ND_SRV_next(a1),a1		;a1 ->-> next NetD_SRV struct or -> NULL
	move.l	(a1),d0				;d0 -> next NetD_SRV struct or is NULL
	bgt.s	.find_server_link		;loop back to search entire queue
	bra	E_REFUSE_sub			;if no matching server, go refuse service
;-------
.found_server_link:
	move.l	ND_SRV_next(a0),(a1)		;unlink this server from the queue
	move.l	a0,-(sp)			;push a0 -> found server struct
	lea	ND_SRV_listen_q(a0),a1	;a1 -> listening connection queue
	bsr.s	release_CON_queue_a1		;release listening connection queue
	move.l	(sp),a0				;revive a0 -> found server struct
	lea	ND_SRV_turnon_q(a0),a1	;a1 ->-> activating connection queue or is NULL
	bsr.s	release_CON_queue_a1		;release activating connection queue
	move.l	(sp)+,a0			;pull a0 -> found server struct
	lea	ND_SRV_active_q(a0),a1	;a1 ->-> active connection queue or is NULL
;-------
;Here we fall into the subroutine release_CON_queue_a1 to release active connection queue
;-------
release_CON_queue_a1:
	move.l	(a1),d0				;d0 -> first NetD_CON in queue or is NULL
	ble.s	.exit				;exit at end of queue
.loop:
	move.l	d0,a0				;a0 -> NetD_CON to be released
	move.l	ND_CON_next(a0),(a1)		;unlink this NetD_CON from the queue
	move.l	a1,-(sp)			;push a1 -> root ptr of queue
	bsr	release_CON_a0			;release that NetD_CON, closing connection
	move.l	(sp)+,a1			;pull a1 -> root ptr of queue
	move.l	(a1),d0				;d0 -> first NetD_CON in queue or is NULL
	bgt.s	.loop				;loop back until entire queue released
.exit:
	moveq	#E_NORMAL,d0			;result code = success
	lv_exit	sp				;end sp scope for args & locals
	rts					;return to server
;----------------------------------------------------------------------------
;NetD_CON *my_init_CON (NetD_SRV *srv, uint16,tcp_flag, uint16 port_num);
;
my_init_CON:			;func called by servers to reserve a CONN service
	lv_init		sp	;use stack pointer to access arguments
	lv_arg.l	server
	lv_arg.w	tcp_flag
	lv_arg.w	port_num
;
	move.l	server(sp),d0			;d0 -> NetD_SRV struct (we hope...)
	cmp.l	#$8000,d0			;pointer too low ?
	blt	NULL_sub			;if so, go refuse service
	move.l	d0,a0				;a0 -> NetD_SRV struct (we hope...)
	and.l	#$F0000001,d0			;pointer weird ?
	bne	NULL_sub			;if so, go refuse service
	cmp.l	#ND_XB_ID,ND_SRV_magic(a0)	;main NetD magic correct ?
	bne	NULL_sub			;if not, go refuse service
	cmp.l	#'_SRV',ND_SRV_xmagic(a0)	;specific server magic correct ?
	bne	NULL_sub			;if not, go refuse service
;-------
	move.l	tcp_flag(sp),d0			;d0 = tcp_flag<<16|port_num
	bsr.s	make_NetD_CON			;create primary NetD_CON
	bmi	NULL_sub			;refuse service if that failed
	move.l	d0,-(sp)			;push -> primary NetD_CON
	move.l	tcp_flag+4(sp),d0		;d0 = tcp_flag<<16|port_num NB:push above=>offs+4
	bsr.s	make_NetD_CON			;create secondary NetD_CON
	move.l	(sp)+,d0			;pull d0 -> primary NetD_CON
	lv_exit	sp				;end sp scope for args & locals
	rts					;return to server
;----------------------------------------------------------------------------
make_NetD_CON:				;here a0->server,  d0 = tcp_flag<<16|port_num
	movem.l	d0/a0,-(sp)			;push a0 -> server, port_num, tcp_flag
	R_alloc	#sizeof_NetD_CON		;allocate structure RAM
	bgt.s	.have_struct_RAM		;on success continue further below
	addq	#8,sp				;clean stack
	bra	E_REFUSE_sub			;go refuse service
;-------
.have_struct_RAM:
	move.l	(sp)+,ND_CON_port_id(a0)	;pull to init protocol type and port number
	move.l	(sp)+,a1			;pull a1 -> owning server struct
	move.l	a1,ND_CON_server(a0)		;init -> owning server struct
	lea	ND_SRV_listen_q(a1),a1	;a1 ->-> listening queue
	movem.l	a0/a1,-(sp)			;push a1 -> listening queue, a0 -> NetD_CON
	tst	ND_CON_type(a0)		;TCP/UDP connection ?
	beq.s	.open_UDP_CON
.open_TCP_CON:
	TCP_open	#ZERO_IP,ND_CON_port(a0),#TOS_NORMAL,ND_CON_bsize(a0)
	bra.s		.done_open
;-------
.open_UDP_CON:
	UDP_open	#ZERO_IP,ND_CON_port(a0)
.done_open:
	movem.l	(sp),a0/a1			;revive a0 -> NetD_CON,  a1 -> listening queue
	move	d0,ND_CON_handle(a0)		;store handle produced by STinG
	CNgetinfo	d0			;get CIB ptr to d0
	movem.l	(sp)+,a0/a1			;pull a0 -> NetD_CON,  a1 -> listening queue
	move.l	d0,ND_CON_CIB_p(a0)		;store -> CIB of connection
	move.l	(a1),ND_CON_next(a0)		;link new NetD_CON to old queue
	move.l	a0,(a1)				;place new NetD_CON first in queue
	move.l	a0,d0				;result -> new NetD_CON
	movea.l	ND_CON_server(a0),a0		;restore entry a0->server
	rts					;return to caller
;----------------------------------------------------------------------------
;int16 my_exit_CON (NetD_SRV *srv, NetD_CON *conn);
;
my_exit_CON:			;func called by servers to release a CONN service
	lv_init		sp	;use stack pointer to access arguments
	lv_arg.l	server
	lv_arg.l	conn
;-------
	move.l	server(sp),d0			;d0 -> NetD_SRV struct (we hope...)
	cmp.l	#$8000,d0			;pointer too low ?
	blt	E_REFUSE_sub			;if so, go refuse service
	move.l	d0,a0				;a0 -> NetD_SRV struct (we hope...)
	and.l	#$F0000001,d0			;pointer weird ?
	bne	E_REFUSE_sub			;if so, go refuse service
	cmp.l	#ND_XB_ID,ND_SRV_magic(a0)	;main NetD magic correct ?
	bne	E_REFUSE_sub			;if not, go refuse service
	cmp.l	#'_SRV',ND_SRV_xmagic(a0)	;specific server magic correct ?
	bne	E_REFUSE_sub			;if not, go refuse service
;-------
	tas	ND_SRV_flags(a0)		;block server
	move.l	conn(sp),a1			;a1 -> NetD_CON of exited service
	move.l	ND_CON_port_id(a1),-(sp)	;push port type.number
	pea	ND_SRV_listen_q(a0)		;push -> listening queue root
	bsr.s	purge_port			;purge queue of matching NetD_CONs
;NB: here 8 extra bytes are on stack, which is why next instruction needs +8 offset.
	move.l	server+8(sp),a0			;a0 -> NetD_SRV struct
	addq	#4,sp				;pop queue root ptr off stack
	pea	ND_SRV_turnon_q(a0)		;push -> activation queue root
	bsr.s	purge_port			;purge queue of matching NetD_CONs
	addq	#8,sp				;clean stack
	move.l	server(sp),a0			;a0 -> NetD_SRV struct
	bclr	#7,ND_SRV_flags(a0)		;unblock server
	lv_exit	sp				;end sp scope for args & locals
	rts					;return to server
;----------------------------------------------------------------------------
;void	purge_port(NetD_CON **queue_pp, uint32 port_id);
;
purge_port:
	lv_init		sp		;use stack pointer to access arguments
	lv_arg.l	queue_pp	;->-> NetD_CON queue
	lv_arg.l	port_id		;port type.number
;-------
	move.l	queue_pp(sp),a1		;a1 ->-> NetD_CON queue
	move.l	(a1),d0			;d0 -> NetD_CON or is NULL
	ble.s	.exit			;exit at end of queue
.loop:
	move.l	d0,a0			;a0 -> NetD_CON in queue
	move.l	ND_CON_port_id(a0),d0	;d0 = port type.number of NetD_CON
	cmp.l	port_id(sp),d0		;is this what we look for ?
	bne.s	.pass_entry		;if not, pass this entry
	move.l	ND_CON_next(a0),(a1)	;unlink found entry from queue
	bsr.s	release_CON_a0		;close connection and release RAM
	move.l	queue_pp(sp),a1		;a1 ->-> queue
	bra.s	.next_entry		;go try next entry
;
.pass_entry:
	lea	ND_CON_next(a0),a1	;a1 ->-> next entry
	move.l	a1,queue_pp(sp)		;store this as queue for rest of purge
.next_entry:
	move.l	(a1),d0			;d0 -> next entry in queue or is NULL
	bgt.s	.loop			;loop back for another NetD_CON
.exit:
	lv_exit	sp				;end sp scope for args & locals
	rts					;return to server
;----------------------------------------------------------------------------
;int16 my_kill_CON (NetD_SRV *srv, NetD_CON *conn);
;
my_kill_CON:			;func called by servers to kill an active CONN
	move	#ND_SRV_active_q,d2		;d2 = struct offset for listening queue
kill_CON_in_queue_d2:
	lv_init		sp	;use stack pointer to access arguments
	lv_arg.l	server
	lv_arg.l	conn
;-------
	move.l	server(sp),d0			;d0 -> NetD_SRV struct (we hope...)
	cmp.l	#$8000,d0			;pointer too low ?
	blt	E_REFUSE_sub			;if so, go refuse service
	move.l	d0,a0				;a0 -> NetD_SRV struct (we hope...)
	and.l	#$F0000001,d0			;pointer weird ?
	bne	E_REFUSE_sub			;if so, go refuse service
	cmp.l	#ND_XB_ID,ND_SRV_magic(a0)	;main NetD magic correct ?
	bne	E_REFUSE_sub			;if not, go refuse service
	cmp.l	#'_SRV',ND_SRV_xmagic(a0)	;specific server magic correct ?
	bne	E_REFUSE_sub			;if not, go refuse service
;-------
	lea	(a0,d2),a1			;a1 ->-> connection queue via server struct
	move.l	(a1),d0				;d0 -> connection queue or is NULL
	ble	E_REFUSE_sub			;if no match, go refuse service
	move.l	conn(sp),d2			;d2 -> NetD_CON struct (must be!)
.find_link:
	cmp.l	d0,d2				;found match ?
	beq.s	.found_link			;if so, go deal with it
	move.l	d0,a1				;a1 ->-> next NetD_CON struct in queue
;-------  NB: the line above relies on ND_CON_next having zero offset
	move.l	(a1),d0				;d0 -> next NetD_CON struct or is NULL
	bgt.s	.find_link			;loop back to find correct link
	bra	E_REFUSE_sub			;if no match, go refuse service
;-------
.found_link:
	move.l	d2,a0				;a0 -> found NetD_CON to kill
	move.l	ND_CON_next(a0),(a1)		;unlink the NetD_CON from queue
release_CON_a0:
	move.l	a0,-(sp)			;push a0 -> NetD_CON
	tst	ND_CON_type(a0)			;TCP/UDP connection ?
	beq.s	.close_UDP_CON
.close_TCP_CON:
	TCP_close	ND_CON_handle(a0),#1,NULL	;close TCP connection
	bra.s	.release_NetD_CON
;-------
.close_UDP_CON:
	UDP_close	ND_CON_handle(a0)	;close UDP connection
.release_NetD_CON:
	move.l	(sp)+,a0			;pull a0 -> NetD_CON
	move.l	d0,-(sp)			;push d0 = close_CON result code
	R_free	(a0)				;release the RAM block
	move.l	(sp)+,d0			;pull d0 = close_CON result code
	lv_exit	sp				;end sp scope for args & locals
	rts					;return to server
;----------------------------------------------------------------------------
;int16 my_send_CON (NetD_CON *conn, char *buff, int16 len);
;
my_send_CON:			;func called by servers to kill an active CONN
	lv_init		sp	;use stack pointer to access arguments
	lv_arg.l	conn	;-> NetD_CON for sending
	lv_arg.l	buff	;-> buffer holding data to send
	lv_arg.w	len	;length of data to send
;-------
	move.l	conn(sp),a0	;a0 -> NetD_CON
	move.l	buff(sp),a1	;a1 -> data buffer
	move	len(sp),d0	;d0 = data length
	tst	ND_CON_type(a0)	;TCP/UDP ?
	beq.s	.send_UDP
.send_TCP:
	TCP_send	ND_CON_handle(a0),(a1),d0
	bra.s		.exit
;-------
.send_UDP:
	UDP_send	ND_CON_handle(a0),(a1),d0
.exit:
	lv_exit	sp		;end sp scope for args & locals
	rts			;return to server
;----------------------------------------------------------------------------
;End of:	NetD_STX functions called from server TSR programs
;----------------------------------------------------------------------------
;Start of:	my_timer_func called from STinG kernel in system interrupt
;----------------------------------------------------------------------------
my_timer_func:
	move.l	my_cookie+ND_server_q(pc),d0	;d0 -> head of server queue, or is NULL
	ble.s	.exit_direct			;exit at end of server queue
	movem.l	a2-a4,-(sp)			;push entry registers
.loop_1:
	move.l	d0,a4				;a4 -> entry in server queue
	lea	ND_SRV_listen_q(a4),a2	;a2 -> root -> entry in listening queue, or root is NULL
	move.l	(a2),d0				;d0 -> entry in listening queue, or is NULL
	ble.s	.next_server			;skip to next server at end of listening queue
	tst	ND_SRV_flags(a4)		;test control flags
	bmi.s	.next_server			;don't interrupt blocked servers
.loop_2:
	move.l	d0,a3				;a3 -> NetD_CON in listening queue
	tst	ND_CON_handle(a3)		;is there a valid handle ?
	ble.s	.next_conn			;skip to next CONN on invalid handle
	move.l	ND_CON_CIB_p(a3),a0		;a0 -> CIB struct
	tst.l	CIB_rhost(a0)			;has a remote host been connected ?
	beq.s	.next_conn			;skip to next CONN if this one still listening
.activation:
	move.l	ND_CON_next(a3),(a2)		;unlink activated NetD_CON from listening queue
	lea	ND_SRV_turnon_q(a4),a0	;a0 ->-> active queue
	move.l	(a0),ND_CON_next(a3)		;link NetD_CON to old turnon queue
	move.l	a3,(a0)				;place NetD_CON first in turnon queue
;-------
	move.l	a4,a0				;a0 -> server
	move.l	ND_CON_port_id(a3),d0		;d0 = type<<16|port
	bsr	make_NetD_CON			;make a new listening port NetD_CON
	bra.s	.same_link			;after unlinking, reuse same listening link
;
.next_conn:
	lea	ND_CON_next(a3),a2		;a2 -> link -> next NetD_CON, or link is NULL
.same_link:
	move.l	(a2),d0				;d0 -> next NetD_CON or is NULL
	bgt.s	.loop_2				;loop back until whole listening queue tested
.next_server:
	move.l	ND_SRV_next(a4),d0		;d0 -> next server in server queue, or is NULL
	bgt.s	.loop_1				;loop back until whole server queue tested
	movem.l	(sp)+,a2-a4			;pull entry registers
.exit_direct:				
	rts					;return to STinG kernel
;----------------------------------------------------------------------------
;End of:	my_timer_func called from STinG kernel in system interrupt
;----------------------------------------------------------------------------
;Start of:	Diverse resident subroutines used by NetD_STX
;----------------------------------------------------------------------------
E_REFUSE_sub:
	moveq	#E_REFUSE,d0
	rts
;----------------------------------------------------------------------------
;End of:	Diverse resident subroutines used by NetD_STX
;----------------------------------------------------------------------------
;Start of:	Resident library code used by NetD_STX
;----------------------------------------------------------------------------
;	Resident library function code will be expanded here
;
	make		JAR_links
	_uniref		RAM_own
	_uniref		RAM_add
	make		RAM_links
;----------------------------------------------------------------------------
resident_end:
;all beyond that point will be released in going resident
resident_size	=	resident_end-text_start+$100
;----------------------------------------------------------------------------
;End of:	Resident NetD_STX routines
;----------------------------------------------------------------------------
;Start of:	Non-resident NetD_STX initialization code
;----------------------------------------------------------------------------
start_1:
	move.l		a0,d0
	sne		d7
	bne.s		.have_basepage
	move.l		4(sp),d0
.have_basepage:
	move.l		d0,a5
	lea		mystack(pc),sp
	move.l		a5,basepage_p
	tst.b		d7
	bne		.ACC_launch
	gemdos		Mshrink,#0,(a5),#initial_size
	lea		bp_arglen(a5),a0
	lea		STinG_Load_s(pc),a1
	str_comp	a0,a1
	bne		.bad_launch
;
	gemdos		Super,0.w
	move.l		d0,d4
;
	moveq		#-1,d3				;d3 = flag cookie conflict
	eval_cookie	#ND_XB_ID			;test for own cookie
	bpl.s		.done_cookies
	clr.l		d3				;d3 = flag STiK cookie error
	eval_cookie	#"STiK"
	bmi.s		.done_cookies
	move.l		d0,d3				;d3 = d0 -> DRV_LIST structure
.done_cookies:
	gemdos		Super|_ind,d4
;
	move.l		d3,sting_drivers		;sting_drivers -> DRV_LIST structure
	blt		.cookie_conflict
	beq		.STiK_not_found
	move.l		d3,a3				;a3 -> DRV_LIST structure
	lea		DRV_LIST_magic(a3),a0
	lea		STiKmagic_s(pc),a1
	moveq		#10-1,d0
.strcmp_loop:					;loop to test STiKmagic of DRV_LIST
	cmpm.b		(a0)+,(a1)+
	dbne		d0,.strcmp_loop
	bne		.STiK_not_valid
;
	move.l		DRV_LIST_get_dftab(a3),a0	;a0 -> get_dftab function
	pea		TRANSPORT_DRIVER_s		;-(sp) = "TRANSPORT_TCPIP"
	jsr		(a0)				;call get_dftab
	addq		#4,sp
	move.l		d0,tpl				;store pointer in 'tpl'
	ble		.driver_not_valid
;
	move.l		DRV_LIST_get_dftab(a3),a0	;a0 -> get_dftab function
	pea		MODULE_DRIVER_s			;-(sp) = "MODULE_LAYER"
	jsr		(a0)				;call get_dftab
	addq		#4,sp
	move.l		d0,stx				;store pointer in 'stx'
	ble		.layer_not_valid
.install:
	link		a6,#-4
	query_chains	0.w,0.w,-4(a6)
	move.l		-4(a6),a4			;a4->layers
	unlk		a6
;
	move.l		a4,d0
	beq		.bad_layer_list
.layer_loop:
	move.l		d0,a4
	move.l		LAYER_next(a4),d0
	bne		.layer_loop
;
;NB: The code above gives a4 a value -> the end of the layer queue.
;    This is used to install ND_layer further below.
;
	gemdos		Super,0.w
	move.l		d0,d4
	lea		my_cookie(pc),a0
	make_cookie	#ND_XB_ID,a0
	move.l		d0,d3
	gemdos		Super|_ind,d4
	move.l		d3,d0
	bmi.s		.cookie_impossible
;
	TIMER_call	my_timer_func(pc),#HNDLR_SET
	tst		d0
	beq.s		.timer_failure
.final_install:
	gemdos		Super,0.w
	move.l		d0,d4
	RAM_own		#1		;setup current bp as RAM owner
	RAM_add		#RAM_chunk	;get some RAM
	gemdos		Super|_ind,d4
	lea		ND_layer(pc),a0
	move.l		a0,LAYER_next(a4)
	gemdos		Ptermres,#resident_size,#0
;-------------------------------------
.ACC_launch:
	lea		ACC_launch_s(pc),a0
	bsr.s		report_error
.loop:
	bra		.loop
;-------------------------------------
.bad_launch:
	lea		bad_launch_s(pc),a0
	bra.s		.error_exit
;-------------------------------------
.cookie_conflict:
	lea	cookie_conflict_s(pc),a0
	bra.s	.error_exit
;-------------------------------------
.cookie_impossible:
	lea	cookie_impossible_s(pc),a0
	bra.s	.error_exit
;-------------------------------------
.bad_layer_list:
	lea		bad_layer_list_s(pc),a0
	bra.s		.error_exit
;-------------------------------------
.timer_failure:
	lea		timer_failure_s(pc),a0
	bra.s		.error_exit
;-------------------------------------
.STiK_not_found:
	lea		STiK_not_found_s,a0
	bra.s		.error_exit
;-------------------------------------
.STiK_not_valid:
	lea		STiK_not_valid_s,a0
	bra.s		.error_exit
;-------------------------------------
.driver_not_valid:
	lea		driver_not_valid_s,a0
	bra.s		.error_exit
;-------------------------------------
.layer_not_valid:
	lea		layer_not_valid_s(pc),a0
.error_exit:
	bsr.s		report_error
	gemdos		Pterm,#E_ERROR
;-------------------------------------
report_error:
	move.l		a0,-(sp)
	lea		error_title_s(pc),a0
	bsr.s		Cconws_sub
	move.l		(sp)+,a0
	bsr.s		Cconws_sub
	lea		error_tail_s(pc),a0
Cconws_sub:
	gemdos		Cconws,(a0)
	rts
;----------------------------------------------------------------------------
;End of:	Non-resident NetD_STX initialization code with tests
;----------------------------------------------------------------------------
;Start of:	Non-resident library code used by NetD_STX
;----------------------------------------------------------------------------
	make	JAR_links
;----------------------------------------------------------------------------
text_limit:
text_size	= text_limit-text_start
	SECTION	DATA
data_start:
;----------------------------------------------------------------------------
STinG_Load_s:
	dc.b	10,'STinG_Load',NUL
STiKmagic_s:
	dc.b	'STiKmagic',NUL
TRANSPORT_DRIVER_s:
	dc.b	'TRANSPORT_TCPIP',NUL
MODULE_DRIVER_s:
	dc.b	'MODULE_LAYER',NUL
;
ACC_launch_s:
	dc.b	'This non-ACC, was launched as an ACC,',CR,LF
	dc.b	'so now you must reset the computer !',CR,LF
	dc.b	'I am looping forever to avoid damage',CR,LF
	dc.b	'that could occur if I try to exit !',CR,LF
	dc.b	NUL
;
bad_launch_s:	
	dc.b	'This STX should only be launched by',CR,LF
	dc.b	'STinG, or another TCP/IP stack with',CR,LF
	dc.b	'a compatible module interface !',CR,LF
	dc.b	NUL
;
	EVEN
cookie_conflict_s:
	dc.l	ND_XB_ID
	dc.b	' cookie already exists !',CR,LF
	dc.b	NUL
;
	EVEN
cookie_impossible_s:
	dc.l	ND_XB_ID
	dc.b	' cookie can not be installed !',CR,LF
	dc.b	NUL
;
bad_layer_list_s:
	dc.b	'The list chain of STinG layers was',CR,LF
	dc.b	'not found...!',CR,LF
	dc.b	NUL
;
timer_failure_s:
	dc.b	'STinG TIMER_call function failed !',CR,LF
	dc.b	NUL
;
STiK_not_found_s:
	dc.b	'There is no STiK cookie in the jar !',CR,LF
	dc.b	NUL
;
STiK_not_valid_s:
	dc.b	'The STiK cookie data is corrupt !',CR,LF
	dc.b	NUL
;
driver_not_valid_s:
	dc.b	'The main STinG driver is not valid !',CR,LF
	dc.b	NUL
;
layer_not_valid_s:
	dc.b	'The STinG module layer is not valid !',CR,LF
	dc.b	NUL
;
error_title_s:
	dc.b	BEL,CR,LF
	dc.b	'------------'
	M_TITLE
	dc.b	' '
	M_VERSION
	dc.b	'------------',CR,LF
	dc.b	NUL
;
	EVEN
error_tail_s:
	dc.l	ND_XB_ID
	dc.b	'installation aborted.',CR,LF
	dc.b	BEL,CR,LF,NUL
	EVEN
;----------------------------------------------------------------------------
data_limit:
data_size	=	data_limit-data_start
	SECTION	BSS
bss_start:
;----------------------------------------------------------------------------
		ds.l	200		;subroutine stack >= 100 longs
mystack:	ds.l	1		;top of subroutine stack
;----------------------------------------------------------------------------
bss_limit:
bss_size	=	bss_limit-bss_start
;----------------------------------------------------------------------------
initial_size	=	text_size+data_size+bss_size+$100
;----------------------------------------------------------------------------
		END
;----------------------------------------------------------------------------
;End of file:	NETD_STX.S
;----------------------------------------------------------------------------
