***** THREAD ALLL REFERENCES TO SSDAS! *****
SURPRISE	EQU	SDOS+SDOS:SURPRISE
	ORG	$4003

***** KLUGES ARE ABOVE
::	SET	*
*	BUFFER CONTROL DEFINITION
*	NOTE: RING BUFFERS ARE 256 BYTES LONG, ORG'D TO PAGE BOUNDARIES

	ORG	0
SDNETBUF:EMPTY	RMB	2	POINTER TO NEXT BYTE TO TAKE FROM RING BUFFER
SDNETBUF:FILL	RMB	2	POINTER TO NEXT BYTE TO PLACE IN RING BUFFER
SDNETBUF:CBSIZE	EQU	*
	PAGE
	IFUND	LISTPACKETMANAGER
LISTPACKETMANAGER	EQU	1
	FIN

	IFUND	SOCKETMANAGER
SOCKETMANAGER	EQU	1
	FIN	SOCKETMANAGER

	IFUND	SDNETRETRYCOUNT	HOW MANY TIMES TO TRY BEFORE DECLARING DEAD NODE
SDNETRETRYCOUNT	EQU	100
	FIN

	IFUND	SDNETOURCPUNUMBER	IDENTITY OF THIS PROCESSOR ON THE NET
SDNETOURCPUNUMBER	EQU	1
	FIN

	IFUND	SDNETEOTFUSETIME	HOW LONG TO WAIT BEFORE GIVING UP END OF MESSAGE SEARCH
SDNETEOTFUSETIME	EQU	20	60THS OF A SECOND
	FIN

	IFUND	SDNETRETRYTIME	HOW LONG TO WAIT BEFORE RETRANSMITTING OUR MESSAGE
SDNETRETRYTIME	EQU	15	60THS OF A SECOND
	FIN

	IFUND	SDNETRESPONDTIME	HOW LONG TO WAIT BEFORE RESPONDING TO A MESSAGE JUST RECEIVED
SDNETRESPONDTIME	EQU	3
	FIN

	IFUND	SDNETXMTBUFOVFTHRESHOLD	NUMBER OF BUFFER BYTES INDICATING IMMINENT BUFFER FULL CONDITION
SDNETXMTBUFOVFTHRESHOLD	EQU	200
	FIN


	IFUND	SDNETXMITTHRESHOLD	HOW MANY DATA BYTES ARE NEEDED FOR SDNET PACKET TO BE EFFICIENT
SDNETXMITTHRESHOLD	EQU	100	= 5 * MESSAGE OVERHEAD IN BYTES
	FIN
	PAGE
*	SD NET NODE REPRESENTATIVE BLOCK

	ORG	0
SDNETNRB:XBUFLK	RMB	1	TRANSMIT BUFFER LOCK
SDNETNRB:TIMEQFLINK	RMB	2	FORWARD LINK TO NEXT NRB IN ORDER LIST
SDNETNRB:TIMEQBLINK	RMB	2	BACKWARD LINK TO PREVIOUS NRB IN ORDER LIST
SDNETNRB:TIMEQDELTA	RMB	1	DELAY DELTA FROM NRB(BLINK)
SDNETNRB:RCVBUF	RMB	SDNETBUF:CBSIZE	CONTROL BLOCK FOR RECEIVE BUFFER
SDNETNRB:XMTBUF	RMB	SDNETBUF:CBSIZE	CONTROL BLOCK FOR TRANSMIT BUFFER
SDNETNRB:NODE	RMB	1	NODE NUMBER BEING REPRESENTED
SDNETNRB:STATE	RMB	1	NODE STATE
SDNETNRB:RETRY	RMB	1	NUMBER OF TIMEOUTS TO CONCLUDE DEAD
SDNETNRB:XVPOS	RMB	2	TRANSMIT VIRTUAL POSITION (MODULO 2**16)
SDNETNRB:RVPOS	RMB	2	RECEIVED VIRTUAL POSITION (MODULO 2**16)
SDNETNRB:RCVDRDY	RMB	2	LAST RCVDREADY COUNT
SDNETNRB:GOOF	RMB	1	SET THIS TO HOLLER "GOOF!!!"
SDNETNRB:EPITAPH	RMB	1	raison de morte [sic]
SDNETNRB:WORK	RMB	5	work area for listener task
SDNETNRB:SOCKET	RMB	2	0 or pointer to socket being serviced
SDNETNRB:TCB	RMB	TCB:SIZE	LISTENER'S TCB
SDNETNRB:TCBSTACK	RMB	20+MINSTACK	STACK SPACE FOR LISTENER TASK
SDNETNRB:STACKBASE	RMB	1	BASE OF STACK FOR LISTENER TASK FOR THIS NRB
SDNETNRB:SIZE	EQU	*


	ORG	0
NODEEPITAPH:WASCRAZY	RMB	1	packet manager out of sync with remote
NODEEPITAPH:REMOTEDISCONNECT	RMB	1	remote sent QUIT
NODEEPITAPH:REMOTENORESPONSE	RMB	1	remote does not respond
NODEEPITAPH:LOCALDISCONNECT	RMB	1	local requested QUIT
NODEEPITAPH:LOCALKILLED	RMB	1	local requested KILL
	PAGE
*	NODE STATES

	ORG	0
NODESTATE:DEAD	RMB	1	REPRESENTED NODE IS DEAD
NODESTATE:AGREEQUIT	RMB	1	REPRESENTED NODE SENT "WANTQUIT"
NODESTATE:WANTQUIT	RMB	1	NEED TO DISCONNECT FROM REPRESENTED NODE
NODESTATE:INACTIVE	RMB	1	REPRESENTED NODE IS INACTIVE
NODESTATE:ACTIVATING	RMB	1	WE ARE TRYING TO ESTABLISH CONTACT WITH REPRESENTED NODE
NODESTATE:RESYNCHED	RMB	1	REPRESENTED NODE SENT "RESYNCH"
NODESTATE:ACTIVATED	RMB	1	REPRESENTED NODE SENT "RESYNCHED"
NODESTATE:CONNECTEDR	RMB	1	REPRESENTED NODE IS CONNECTED; WE EXPECT TO RECEIVE NEXT
NODESTATE:CONNECTEDS	RMB	1	REPRESENTED NODE IS CONNECTED; WE'RE EXPECTED TO SEND NEXT
NODESTATE:QUIT	RMB	1	WE MUST SEND "QUIT" MSG TO REPRESENTED NODE


*	SDLP CONTROL FIELD

SDLPCONTROL:NORMAL	EQU	0	NORMAL SDLP MESSAGE
SDLPCONTROL:RESYNCHREQ	EQU	1	RE-SYNCHRONIZE REQUEST
SDLPCONTROL:RESYNCHED	EQU	2	RE-SYNCHRONIZED RESPONSE
SDLPCONTROL:WANTQUIT	EQU	3	WANTS TO QUIT
SDLPCONTROL:AGREEQUIT	EQU	4	AGREES TO QUIT
SDLPCONTROL:QUIT	EQU	5	QUIT!
SDLPCONTROL:RESYNCHQUIT	EQU	6	RESYNCHRONIZE, HAVE DATA, AND QUIT
	PAGE
*	SOCKET MANAGER ERROR CODES

	ORG	1100
ERR:BADTYPE	RMB	1	BAD TYPE PASSED TO ALLOCATE
ERR:NOSOCKETS	RMB	1	NO MORE SOCKETS LEFT FOR ALLOCATE
ERR:BADNODE	RMB	1	INVALID NODE SPECIFIED TO ALLOCATE
ERR:LOCALCROAKED	RMB	1	LOCAL SOCKET CROAKED
ERR:REMOTECROAKED	RMB	1	REMOTE SOCKET CROAKED
ERR:BADSOCKETCAP	RMB	1	INVALID SOCKET CAPABILITY
ERR:BADSOCKETOPER	RMB	1	INVALID OPERATION FOR SOCKET
ERR:LOCALCLOSELOST	RMB	1	LOCAL CLOSE LOST RECEIVED DATA
ERR:REMOTECLOSELOST	RMB	1	REMOTE CLOSE LOST TRANSMITTED DATA
ERR:REMOTECLOSE	RMB	1	REMOTE CLOSED
ERR:UNKNOWNNODEEPITAPH	RMB	1	couldn't decipher node obituary
ERR:NOREASON	RMB	1	I got an error, BUT I don't know what it is!!
ERR:WASCRAZY	RMB	1	packet manager out of sync with remote
ERR:REMOTEDISCONNECT	RMB	1	remote sent QUIT
ERR:REMOTENORESPONSE	RMB	1	remote does not respond
ERR:LOCALDISCONNECT	RMB	1	local requested QUIT
ERR:LOCALKILLED	RMB	1	local requested KILL

	ORG	2
SKTMNRB	RMB	0	listener task: address of NRB
SKTMSOCKET	RMB	2	sender task: address of socket
SKTMPARM	RMB	2	SOCKET MANAGER WORKING CELL
SKTMMISC	RMB	2	DITTO
	PAGE
*	SOCKET DESCRIPTOR BLOCK DEFINITIONS

	ORG	0
SOCKET:LOCK	RMB	1	SOCKET LOCK
SOCKET:STATUSCHANGED	RMB	1	NON-ZERO GRABS SENDER TASK'S ATTENTION
SOCKET:NRB	RMB	2	PTR TO SOCKET'S NRB
SOCKET:RCVBUF	RMB	SDNETBUF:CBSIZE	CONTROL BLOCK FOR RECEIVE BUFFER
SOCKET:XMTBUF	RMB	SDNETBUF:CBSIZE	CONTROL BLOCK FOR TRANSMIT BUFFER
SOCKET:PROVXMTEMPTY	RMB	2	PROVISIONAL POINTER TO XMIT RISK BYTES
SOCKET:PROVXMTBASE	RMB	2	PROVISIONAL XMIT BASE (MODULO 2**16)
SOCKET:SOCKET	RMB	1	LOCAL SOCKET NUMBER
SOCKET:KEY	RMB	4	GENERATED KEY
SOCKET:TARGETNODE	RMB	1	TARGET NODE'S NUMBER
SOCKET:TARGETSOCKET	RMB	1	TARGET SOCKET NUMBER
SOCKET:TARGETKEY	RMB	4	TARGET SOCKET'S KEY
SOCKET:STATE	RMB	1	STATE OF LOCAL SOCKET
SOCKET:XMTBASE	RMB	2	INDEX UNVERIFIED TRANSMIT BYTE (MODULO 2**16)
SOCKET:RCVBASE	RMB	2	INDEX UNVERIFIED RECEIVE BYTE (MODULO 2**16)
SOCKET:XMTRISK	RMB	2	BYTES SENT WITHOUT ACK
SOCKET:RCVUNACKED	RMB	2	BYTES RECEIVED WITHOUT ACK
SOCKET:XMTRISKLIMIT	RMB	2	LIMIT ON BYTES TO SEND WITHOUT ACK
SOCKET:RCVUNACKEDLIMIT	RMB	2	LIMIT ON BYTES TO RECEIVE BEFORE ACKING
SOCKET:CONTTHRESH	RMB	2	turn off lost data when rcv buf lower than this
SOCKET:CROAKREASON	RMB	2	REASON FOR CROAKING
SOCKET:LOCALSTATUS	RMB	1	LOCAL SOCKET STATUS
SOCKET:OLDLOCALSTATUS	RMB	1	PREVIOUS LOCAL SOCKET STATUS
SOCKET:REMOTESTATUS	RMB	1	REMOTE SOCKET STATUS
SOCKET:HEADER	RMB	19	BUFFER FOR ASSEMBLING MESSAGE HEADER
SOCKET:TCB	RMB	TCB:SIZE	TCB FOR SENDER TASK
SOCKET:TCBSTACK	RMB	20+MINSTACK	STACK SPACE FOR SENDER TASK
SOCKET:STACKBASE	RMB	1	BASE OF STACK FOR SENDER TASK
SOCKET:SIZE	EQU	*
	PAGE
*	SOCKET STATES

	ORG	0
SOCKETSTATE:FREE	RMB	1	SOCKET AVAILABLE
SOCKETSTATE:ANSWER	RMB	1	SOCKET WAITING FOR FIRST MESSAGE
SOCKETSTATE:ASSIGNING	RMB	1	WAITING TO ASSIGN SOCKET TARGET
SOCKETSTATE:ASSIGNED	RMB	1	WAITING FOR ASSIGN ACKNOWLEDGEMENT
SOCKETSTATE:ORIGINATE	RMB	1	SOCKET WILL ORIGINATE CONNECTION
SOCKETSTATE:LINKING	RMB	1	ORIGINATING CONNECTION IN PROGRESS
SOCKETSTATE:OPEN	RMB	1	SOCKET CONNECTION ESTABLISHED
SOCKETSTATE:CROAKED	RMB	1	SOCKET CONNECTION CROAKED

*	SOCKET FUNCTIONS

	ORG	0
SOCKETFUNCTION:NORMALCONTINUATION	RMB	1
SOCKETFUNCTION:FIRSTMESSAGE	RMB	1
SOCKETFUNCTION:NOSUCHSOCKET	RMB	1
SOCKETFUNCTION:ASSIGNTARGETSOCKET	RMB	1
SOCKETFUNCTION:FIRSTMESSAGECONTINUATION	RMB	1
SOCKETFUNCTION:GOOFED	RMB	1
SKTMMAXFUNCTIONNUMBER	EQU	*-1

SOCKETSTATUS:CROAKED	EQU	%00000001	SOCKET CROAKED
SOCKETSTATUS:CLOSING	EQU	%00000010	SOCKET IS CLOSING
SOCKETSTATUS:CLOSED	EQU	%00000100	SOCKET IS CLOSED
SOCKETSTATUS:LOSTDATA	EQU	%00001000	RECEIVED DATA WAS LOST
SOCKETSTATUS:NEEDDATA	EQU	%00010000	RE-TRANSMIT REQUEST
SOCKETSTATUS:NODECROAKED	EQU	%00100000	NODE CROAKED

	ORG	::
	TITLE	SD NET I/O DRIVER
	PAGE	SENDER TASK
	TABS	8,16,24
	NAME	SENDER
	JMP	SKTMALLOCATE	allocate a socket
	JMP	SKTMTEST	test for number of bytes available
	JMP	SKTMREAD	read data
	JMP	SKTMWRITE	write data
	JMP	SKTMCLOSE	close socket
	JMP	SKTMQUITNODE	release node

SKTMSENDER	; SENDER TASK

;	See if status has changed, and if not, then go to sleep until it does

	TST	SOCKET:STATUSCHANGED,X
	BNE	SKTMCHECKOUTSTATUS	it did, it did!!
	STX	SKTMSOCKET	save socket address for later
	INX	second byte in socket just happens to be status-changed event
	JSR	SDOS+SDOS:WAITEVENT
	LDX	SKTMSOCKET

SKTMCHECKOUTSTATUS	; status has changed--find out what hit me
	JSR	RESOURCEALLOCATE	but first, grab socket and nrb
	LDX	SOCKET:NRB,X
	JSR	RESOURCEALLOCATE
	LDX	SKTMSOCKET
	LDAA	SOCKET:STATE,X	I don't want to handle crazy or useless sockets
	CMPA	#SOCKETSTATE:FREE
	BEQ	SKTMSLEEPJ
	CMPA	#SOCKETSTATE:CROAKED
	BEQ	SKTMSLEEPJ
	CMPA	#SOCKETSTATE:ANSWER	don't speak unless spoken to!!
	BEQ	SKTMSLEEPJ

	LDAA	SOCKET:REMOTESTATUS,X	give up ghost if remote has croaked
	BITA	#SOCKETSTATUS:NODECROAKED!SOCKETSTATUS:CROAKED
	BEQ	SKTMREMOTEALIVE	remote still alive
	LDD	SOCKET:CROAKREASON,X

SKTMCROAKSOCKET	; note obituary and pass problem off to someone else
	STAA	SOCKET:CROAKREASON,X
	STAB	SOCKET:CROAKREASON+1,X
	LDAA	#SOCKETSTATE:CROAKED
	STAA	SOCKET:STATE,X
	LDAA	#SOCKETSTATUS:CROAKED	let rest of world know through status
	ORAA	SOCKET:LOCALSTATUS,X
	STAA	SOCKET:LOCALSTATUS,X
SKTMSLEEPJ
	JMP	SKTMSLEEP
	PAGE
SKTMREMOTEALIVE	; if the remote is closed and local is closing, then...
	BITA	#SOCKETSTATUS:CLOSED	...there's no point in sticking around
	BEQ	SKTMREMOTENOTCLOSED	never mind
	LDAA	SOCKET:LOCALSTATUS,X
	BITA	#SOCKETSTATUS:CLOSING
	BEQ	SKTMLOCALNOTCLOSING	not closing
	JMP	SKTMSNUFFSOCKET	go off into a corner and die

SKTMLOCALNOTCLOSING	; tell local socket what happened to remote socket
	JSR	SKTMUNACKEDBYTES	see if anything in buffer
	STAA	TEMPA
	ORAB	TEMPA
	BNE	SKTMCLOSEDREMOTELOSTDATA	the dummy lost some of my data!
	LDAA	#ERR:REMOTECLOSE/256
	LDAB	#ERR:REMOTECLOSE&$FF
	BRA	SKTMCROAKSOCKET

SKTMCLOSEDREMOTELOSTDATA	; remote didn't get everything I sent so far
	LDAA	#ERR:REMOTECLOSELOST/256
	LDAB	#ERR:REMOTECLOSELOST&$FF
	BRA	SKTMCROAKSOCKET

SKTMREMOTENOTCLOSED	; remote's OK; am I OK?
	LDAA	SOCKET:LOCALSTATUS,X
	BITA	#SOCKETSTATUS:CLOSING	if local socket is closing, see if...
	BEQ	SKTMLOCALSTILLOPEN	...he can be closed
	BITA	#SOCKETSTATUS:CROAKED!SOCKETSTATUS:NODECROAKED
	BNE	SKTMLOCALISCLOSING
	JSR	SKTMUNACKEDBYTES
	STAA	TEMPA
	ORAB	TEMPA
	BEQ	SKTMLOCALISCLOSING	output buffer is empty--all bytes acked
	LDAA	SOCKET:REMOTESTATUS,X	give up if he's closing and I'm closing
	BITA	#SOCKETSTATUS:CLOSING
	BEQ	SKTMLOCALSTILLOPEN	no reason to quit--YET!
	PAGE
SKTMLOCALISCLOSING	; mark local socket closed and ream out buffers
	LDAA	SOCKET:LOCALSTATUS,X
	ANDA	#\SOCKETSTATUS:CLOSING	turn off closing bit
	ORAA	#SOCKETSTATUS:CLOSED	turn on closed bit
	STAA	SOCKET:LOCALSTATUS,X
	JSR	SKTMREAMINPUTOUTPUT	ream the buffers
	BRA	SKTMPREPARETOSEND	go send the updated status

SKTMLOCALSTILLOPEN	; local is still open, but see if he croaked
	LDAA	SOCKET:LOCALSTATUS,X
	BITA	#SOCKETSTATUS:CROAKED!SOCKETSTATUS:NODECROAKED
	BEQ	SKTMPREPARETOSEND	he's still alive
	JSR	SKTMREAMINPUTOUTPUT	ream the buffers before sending status
	LDAA	SOCKET:LOCALSTATUS,X	if local node croaked, say nothing
	BITA	#SOCKETSTATUS:NODECROAKED
	BEQ	SKTMPREPARETOSEND	B/ node didn't die
	LDD	SOCKET:CROAKREASON,X
	JMP	SKTMCROAKSOCKET

SKTMPREPARETOSEND	; get ready to send a message
	LDAA	SOCKET:LOCALSTATUS,X	see if status has changed since...
	EORA	SOCKET:OLDLOCALSTATUS,X	...last message was sent
	STAA	SKTMMISC	remember if status has changed
	BEQ	SKTMSTATUSHASNTCHANGED
	LDAA	SOCKET:LOCALSTATUS,X	never cry for more data
	ANDA	#\SOCKETSTATUS:NEEDDATA
	STAA	SOCKET:LOCALSTATUS,X
	STAA	SOCKET:OLDLOCALSTATUS,X
	LDAA	SOCKET:REMOTESTATUS,X	if remote is closing and unacked bytes...
	BITA	#SOCKETSTATUS:CLOSING	...then send status
	BEQ	SKTMCHECKUNACKEDLIMIT	B/ not closing--see if bytes to be acked
	LDD	SOCKET:RCVUNACKED,X	closing: ack any bytes received
	BNED	SKTMFORCESTATUS
	BRA	SKTMSTATUSHASNTCHANGED	B/ no unacked, so limit hasn't been reached

SKTMCHECKUNACKEDLIMIT	; remote not closing--see if time to ack a bundle of bytes
	LDD	SOCKET:RCVUNACKED,X
	CMPD	SOCKET:RCVUNACKEDLIMIT,X
	BCS	SKTMSTATUSHASNTCHANGED	B/ bundle isn't big enough
SKTMFORCESTATUS
	LDAA	#1
	STAA	SKTMMISC	force status out
	PAGE
SKTMSTATUSHASNTCHANGED	; now, see if I am allowed to send data
	JSR	SKTMSOCKETXMTBYTES	calculate L/(bytes to send),...
	STD	SKTMPARM	...(bytes to risk)
	LDD	SOCKET:XMTRISKLIMIT,X	calculate (bytes to risk) by subtracting...
	SUBD	SOCKET:XMTRISK,X	...bytes already risked from risk limit
	BCS	*	assert: bytes risked is never < 0!!
	CMPD	SKTMPARM	find the minimum of current limit and msg size
	BHI	SKTMSHC2	current limit greater than message size
	STD	SKTMPARM	reduce message size
SKTMSHC2
	LDAA	SKTMPARM	note whether there's data to send, or not
	ORAA	SKTMPARM+1
	STAA	SKTMMISC+1

	LDAA	SOCKET:REMOTESTATUS,X	send no data if remote is closing or...
	BITA	#SOCKETSTATUS:CLOSING!SOCKETSTATUS:LOSTDATA	...he lost data
	BEQ	SKTMSHC3	still OK to send data
	CLR	SKTMMISC+1	not OK to send data
	CLR	SKTMPARM
	CLR	SKTMPARM+1
SKTMSHC3
	LDAA	SKTMMISC	if no data or status to send, don't waste time
	ORAA	SKTMMISC+1
	BNE	SKTMSENDAMESSAGE
	JMP	SKTMSENDERDONE	scoot!
	PAGE
SKTMSENDAMESSAGE	; send a message appropriate to local state
	LDD	SKTMPARM	adjust transmit risk by size of message
	ADDD	SOCKET:XMTRISK,X
	STD	SOCKET:XMTRISK,X
	LDAA	SOCKET:STATE,X
	STAA	TEMPB	(do a double-whammy indexed load)
	LDAA	#SKTMFNTABLE/256
	STAA	TEMPA
	LDX	TEMPX
	LDAA	SKTMFNTABLE&$FF,X
	BMI	*	WACKO: shouldn't be sending a message in this state!
	PSHA	save message type for later
	ASLA	(prepare for another double-whammy operation)
	STAA	TEMPB
	LDAA	#SKTMFNADDRTABLE/256
	STAA	TEMPA
	LDX	TEMPX
	LDX	SKTMFNADDRTABLE&$FF,X
	JMP	0,X	send out a message tailored to socket state
	PAGE
SKTMFNTABLE	; INDEX BY SOCKET:STATE TO GET MESSAGE FUNCTION
	FCB	-1	(FREE) INVALID
	FCB	-1	(ANSWER) INVALID
	FCB	3	(ASSIGNING) ASSIGN SOCKET TARGET
	FCB	0	(ASSIGNED) NORMAL CONTINUATION
	FCB	1	(ORIGINATE) FIRST MESSAGE
	FCB	4	(LINKING) FIRST MESSAGE CONTINUATION
	FCB	0	(OPEN) NORMAL CONTINUATION
	FCB	-1	(CONNECTION CROAK) INVALID

SKTMFNADDRTABLE	; INDEX BY MESSAGE FUNCTION TO GET DRIVER ADDRESS
	FDB	SKTMNORMALCONTINUATION
	FDB	SKTMFIRSTMESSAGE
	FDB	0
	FDB	SKTMASSIGNSOCKETTARGET
	FDB	SKTMFIRSTMESSAGECONTINUATION
	PAGE
SKTMNORMALCONTINUATION	; OUTPUT A "NORMAL CONTINUATION" MESSAGE

;	+-------+-------+--------+--------------+----------+
;	| FUN=0 | TO SC | STATUS | FLOW CONTROL | XMT DATA |
;	+-------+-------+--------+--------------+----------+

	LDX	SKTMSOCKET
	PULB	OUTPUT FUNCTION CODE
	JSR	SKTMOUTPUTNRB
	JSR	SKTMTOSC
	JSR	SKTMOUTPUTSTATUS
	JSR	SKTMFLOWCONTROL
	JSR	SKTMOUTPUTXMTDATA
	JMP	SKTMSENDERDONE
	
SKTMFIRSTMESSAGE	; OUTPUT A "FIRST MESSAGE" MESSAGE

;	+-------+-------+--------+---------+-----------+----------+
;	| FUN=1 | TO SC | STATUS | FROM SC | XMT COUNT | XMT DATA |
;	+-------+-------+--------+---------+-----------+----------+

	LDX	SKTMSOCKET
	PULB	OUTPUT FUNCTION CODE
	JSR	SKTMOUTPUTNRB
	JSR	SKTMTOSC
	JSR	SKTMOUTPUTSTATUS
	JSR	SKTMFROMSC
	JSR	SKTMOUTPUTMSGBYTES
	JSR	SKTMOUTPUTXMTDATA

;	ADVANCE SOCKET STATE TO "LINKING"

	LDAA	#SOCKETSTATE:LINKING
	STAA	SOCKET:STATE,X
	JMP	SKTMSENDERDONE
	PAGE
SKTMASSIGNSOCKETTARGET	; OUTPUT AN "ASSIGN SOCKET TARGET" MESSAGE

;	+-------+-------+--------+---------+--------------+----------+
;	| FUN=3 | TO SC | STATUS | FROM SC | FLOW CONTROL | XMT DATA |
;	+-------+-------+--------+---------+--------------+----------+


	LDX	SKTMSOCKET
	PULB	OUTPUT FUNCTION CODE
	JSR	SKTMOUTPUTNRB
	JSR	SKTMTOSC
	JSR	SKTMOUTPUTSTATUS
	JSR	SKTMFROMSC
	JSR	SKTMFLOWCONTROL
	JSR	SKTMOUTPUTXMTDATA

;	ADVANCE SOCKET STATE TO "ASSIGNED"

	LDAA	#SOCKETSTATE:ASSIGNED
	STAA	SOCKET:STATE,X
	JMP	SKTMSENDERDONE
	PAGE
SKTMFIRSTMESSAGECONTINUATION	; OUTPUT A "FIRST MESSAGE CONTINUATION" MESSAGE

;	+-------+---------+--------+-----------+----------+----------+
;	| FUN=4 | FROM SC | STATUS | XMT COUNT | XMT BASE | XMT DATA |
;	+-------+---------+--------+-----------+----------+----------+

	LDX	SKTMSOCKET
	PULB	OUTPUT FUNCTION CODE
	JSR	SKTMOUTPUTNRB
	JSR	SKTMFROMSC
	JSR	SKTMOUTPUTSTATUS
	JSR	SKTMOUTPUTMSGBYTES
	JSR	SKTMOUTPUTXMTBASE
	JSR	SKTMOUTPUTXMTDATA

SKTMSENDERDONE	; the message has been sent--bug out!!
	LDX	#SDNETDESIREXMITLOCK	grab packet manager's attention
	JSR	RESOURCEALLOCATE
	LDX	SKTMSOCKET
	LDX	SOCKET:NRB,X
	STX	SDNETDESIREXMITNRBPTR
	LDX	#SDNETINTDESIREXMIT
	JSR	SDOS+SDOS:STARTIO
	LDX	SKTMSOCKET
	LDAA	SOCKET:LOCALSTATUS,X	if socket croaked, make him look it!
	BITA	SOCKETSTATUS:NODECROAKED!SOCKETSTATUS:CROAKED
	BEQ	SKTMSD1	he didn't croak
	LDAB	#SOCKETSTATE:CROAKED
	STAB	SOCKET:STATE,X
SKTMSD1
	BITA	#SOCKETSTATUS:CLOSED	free the socket, if he's now closed
	BEQ	SKTMSLEEP	not closed

SKTMSNUFFSOCKET	; put socket out of misery
	LDX	SOCKET:NRB,X	free up NRB, first
	JSR	RESOURCERELEASE
	JSR	SKTMZAPSOCKET	then reduce socket to primordial ooze
	LDX	SKTMSOCKET
	JMP	SKTMSENDER	hibernate until alive and needing service

SKTMSLEEP	; clear decks and wait for more action
	CLR	SOCKET:STATUSCHANGED,X
	LDX	SOCKET:NRB,X	free up nrb and socket
	JSR	RESOURCERELEASE
	LDX	SKTMSOCKET
	JSR	RESOURCERELEASE
	JMP	SKTMSENDER	sleep until more service needed
	PAGE
SKTMFROMSC	; OUTPUT LOCAL SOCKET CAPABILITY, AS BELOW

;	+-------+-----+
;	| SKT # | KEY |
;	+-------+-----+
;	   8      32             <-- BITS

	LDAB	SOCKET:SOCKET,X
	JSR	SKTMOUTPUTNRB
	LDAB	SOCKET:KEY,X
	JSR	SKTMOUTPUTNRB
	LDAB	SOCKET:KEY+1,X
	JSR	SKTMOUTPUTNRB
	LDAB	SOCKET:KEY+2,X
	JSR	SKTMOUTPUTNRB
	LDAB	SOCKET:KEY+3,X
	JMP	SKTMOUTPUTNRB

SKTMTOSC	; OUTPUT REMOTE SOCKET CAPABILITY, AS BELOW

;	+-------+-----+
;	| SKT # | KEY |
;	+-------+-----+
;	   8      32           <-- BITS

	LDAB	SOCKET:TARGETSOCKET,X
	JSR	SKTMOUTPUTNRB
	LDAB	SOCKET:TARGETKEY,X
	JSR	SKTMOUTPUTNRB
	LDAB	SOCKET:TARGETKEY+1,X
	JSR	SKTMOUTPUTNRB
	LDAB	SOCKET:TARGETKEY+2,X
	JSR	SKTMOUTPUTNRB
	LDAB	SOCKET:TARGETKEY+3,X
	JMP	SKTMOUTPUTNRB
	PAGE
SKTMFLOWCONTROL	; OUTPUT FLOW CONTROL, AS BELOW

;	+--------+---------+-----------+----------+
;	| RCD OK | RCV RDY | XMT COUNT | XMT BASE |
;	+--------+---------+-----------+----------+
;	    16       16          16         16           <--- BITS

;	PUT OUT RCD OK FIELD

	LDAB	SOCKET:RCVUNACKED,X
	JSR	SKTMOUTPUTNRB
	LDAB	SOCKET:RCVUNACKED+1,X
	JSR	SKTMOUTPUTNRB
	JSR	SKTMSOCKETRCVROOM
	PSHB
	TAB
	JSR	SKTMOUTPUTNRB
	PULB
	JSR	SKTMOUTPUTNRB
	JSR	SKTMOUTPUTMSGBYTES
	JSR	SKTMOUTPUTXMTBASE

;	SOCKET:TOTALUNACKED = 0

	CLR	SOCKET:RCVUNACKED,X
	CLR	SOCKET:RCVUNACKED+1,X
	RTS
	PAGE
SKTMOUTPUTXMTDATA	; SHOVEL DATA FROM SOCKET TO NRB
	LDAA	SOCKET:REMOTESTATUS,X	LOSE OUTPUT DATA IF REMOTE CLOSED
	BITA	#SOCKETSTATUS:CLOSED+SOCKETSTATUS:CLOSING
	BEQ	SKTMOUTPUTXMTDATA4	YOU LUCKY BASTARD!!  YOU'RE NOT CLOSED!
	CLR	SOCKET:PROVXMTEMPTY+1,X
	CLR	SOCKET:XMTBUF+SDNETBUF:FILL+1,X	WELL, THAT ZAPS THE...
	CLR	SOCKET:XMTBUF+SDNETBUF:EMPTY+1,X	...OUTPUT BUFFER
	CLR	SKTMPARM
	CLR	SKTMPARM+1
SKTMOUTPUTXMTDATA4
	LDAA	SKTMPARM
	ORAA	SKTMPARM+1
	BEQ	SKTMOUTPUTXMTDATA2
	JSR	SKTMINPUTSOCKET
	JSR	SKTMOUTPUTNRB
	INCD	SOCKET:PROVXMTBASE,X	maintain provisional xmt base
	DECD	SKTMPARM
	BRA	SKTMOUTPUTXMTDATA4

SKTMOUTPUTXMTDATA2
	RTS
	PAGE
SKTMOUTPUTMSGBYTES	; PUT OUT LENGTH OF DATA
	LDAB	SKTMPARM
	JSR	SKTMOUTPUTNRB
	LDAB	SKTMPARM+1
	JSR	SKTMOUTPUTNRB
	RTS

SKTMOUTPUTXMTBASE	; PUT OUT PROVISIONAL XMT BASE
	LDAB	SOCKET:PROVXMTBASE,X
	JSR	SKTMOUTPUTNRB
	LDAB	SOCKET:PROVXMTBASE+1,X
	JSR	SKTMOUTPUTNRB
	RTS

SKTMOUTPUTSTATUS
	LDAB	SOCKET:LOCALSTATUS,X
	JMP	SKTMOUTPUTNRB
	PAGE
SKTMOUTPUTNRB
	LDX	SOCKET:NRB,X	FRONT DOOR FOR NRB BUFFER STUFFER
	JSR	SDNETOUTPUTNRB
	LDX	SKTMSOCKET
	RTS

SKTMINPUTSOCKET	; GET A (B) = BYTE FROM SOCKET XMT BUFFER
	LDX	SOCKET:PROVXMTEMPTY,X
	LDAB	0,X
	LDX	SKTMSOCKET
	INC	SOCKET:PROVXMTEMPTY+1,X
	RTS

SKTMSOCKETXMTBYTES	; GET (A) & (B) = NUMBER OF UNSENT BYTES IN SOCKET XMT BUFFER
	LDAB	SOCKET:XMTBUF+SDNETBUF:FILL+1,X
	SUBB	SOCKET:PROVXMTEMPTY+1,X
	CLRA
	RTS

SKTMSOCKETRCVROOM	; GET (A) & (B) = FREE SPACE IN SOCKET RCV BUFFER
	LDAB	SOCKET:RCVBUF+SDNETBUF:EMPTY+1,X
	SUBB	SOCKET:RCVBUF+SDNETBUF:FILL+1,X
	DECB
	CLRA
	RTS
	PAGE	USER SUPPORT
	NAME	ALLOCATE
SKTMALLOCATE	; ALLOCATE A SOCKET

;	ON ENTRY (X) -> INPUT PARAMETER BLOCK, (A) & (B) -> OUTPUT PARAMETER
;	BLOCK.  ON SUCCESS EXIT (OKRTS), OUTPUT PARAMETER BLOCK IS FILLED.
;	ON FAIL EXIT (ERRORRTS), (X) = ERROR CODE.  (A), (B), (X) ARE VOLATILE!

;	        +-----------+------+-------+---------------+
;	(X) --> | TYPE CALL | NODE | CLASS |      KEY      |
;	        +-----------+------+-------+---------------+
;	             8         8       8          32                    <-- BITS

HERE	SET	*
	ORG	0
SKTMALLOCATETYPE	RMB	1
SKTMALLOCATENODE	RMB	1
SKTMALLOCATECLASS	RMB	1
SKTMALLOCATEKEY	RMB	4

;	              +-------------------+
;	(A) & (B) --> | SOCKET CAPABILITY |
;	              +-------------------+
;	                    40 BITS

	ORG	0
SKTMSCSOCKET	RMB	1
SKTMSCKEY	RMB	1
	ORG	HERE

	STX	SKTMPARM
	PSHB
	PSHA
	LDAA	SKTMALLOCATETYPE,X
	CMPA	#0
	BEQ	SKTMALLOCATEORIGINATE
	CMPA	#1
	BEQ	SKTMALLOCATEANSWER
	LDX	#ERR:BADTYPE
	JMP	SKTMALLOCATEERRORRTS
	PAGE
SKTMALLOCATEORIGINATE	; TYPE IS "ORIGINATE"
	LDAA	#SOCKETSTATE:ORIGINATE
	BRA	SKTMALLOCATECONTINUE

SKTMALLOCATEANSWER	; TYPE IS "ANSWER"
	LDAA	#SOCKETSTATE:ANSWER
SKTMALLOCATECONTINUE
	PSHA
	LDAA	SKTMALLOCATENODE,X
	PSHA	SAVE NODE NUMBER FOR LATER USE
	SUBA	#SDNETNODEMAX
	BCC	SKTMALLOCATEBADNODE

;	TYPE & NODE VALIDATED -- GET A FREE SOCKET (IF ANY) AND STUFF IT WITH GOODIES

	JSR	SKTMFINDFREESOCKET	RETURNS SOCKET ADDRESS IN SKTMSOCKET
	BCS	SKTMALLOCATENOSOCKETS
	LDX	SKTMPARM
	LDAA	SKTMALLOCATECLASS,X
	LDAB	SKTMALLOCATEKEY,X
	LDX	SKTMSOCKET
	STAA	SOCKET:TARGETSOCKET,X
	STAB	SOCKET:TARGETKEY,X
	LDX	SKTMPARM
	LDAA	SKTMALLOCATEKEY+1,X
	LDAB	SKTMALLOCATEKEY+2,X
	LDX	SKTMSOCKET
	STAA	SOCKET:TARGETKEY+1,X
	STAB	SOCKET:TARGETKEY+2,X
	LDX	SKTMPARM
	LDAA	SKTMALLOCATEKEY+3,X
	LDX	SKTMSOCKET
	STAA	SOCKET:TARGETKEY+3,X
	PULA	GET NODE NUMBER, STUFF IN SOCKET, CONVERT TO NRB ADDRESS,
	STAA	SOCKET:TARGETNODE,X	AND STUFF THAT IN SOCKET
	ASLA
	STAA	TEMPB	DO DOUBLE WHAMMY INDEXED LOAD
	LDAA	#SDNETNRBTABLE/256
	STAA	TEMPA
	LDX	TEMPX
	LDX	SDNETNRBTABLE&$FF,X
	STX	TEMPX
	LDX	SKTMSOCKET
	LDAA	TEMPA
	STAA	SOCKET:NRB,X
	LDAA	TEMPB
	STAA	SOCKET:NRB+1,X
	PULA	PLUG IN NEW STATE
	STAA	SOCKET:STATE,X
	LDAA	SOCKET:SOCKET,X	CONSTRUCT RETURN SOCKET CAPABILITY
	LDAB	SOCKET:KEY,X
	TSX
	LDX	0,X
	INS
	INS
	STX	SKTMPARM
	STAA	SKTMSCSOCKET,X
	STAB	SKTMSCKEY,X
	LDX	SKTMSOCKET
	LDAA	SOCKET:KEY+1,X
	LDAB	SOCKET:KEY+2,X
	LDX	SKTMPARM
	STAA	SKTMSCKEY+1,X
	STAB	SKTMSCKEY+2,X
	LDX	SKTMSOCKET
	LDAA	SOCKET:KEY+3,X
	LDX	SKTMPARM
	STAA	SKTMSCKEY+3,X
	LDX	SKTMSOCKET	LET GO OF SOCKET
	JSR	RESOURCERELEASE
	OKRTS
	PAGE
SKTMALLOCATENOSOCKETS	; FRESH OUT OF SOCKETS!!
	LDX	#ERR:NOSOCKETS
SKTMALLOCATEBADSN
	INS	FORGET NODE NUMBER
	INS	FORGET SOCKET STATE
SKTMALLOCATEERRORRTS
	INS	FORGET ADDRESS OF RESULT
	INS
	ERRORRTS

SKTMALLOCATEBADNODE	; FATAL ATTEMPT TO ALLOCATE TO A BAD NODE!!
	LDX	#ERR:BADNODE
	BRA	SKTMALLOCATEBADSN

SKTMREAMINPUTOUTPUT	; ream input and output buffers
	CLR	SOCKET:RCVBUF+SDNETBUF:FILL+1,X
	CLR	SOCKET:RCVBUF+SDNETBUF:EMPTY+1,X
	CLR	SOCKET:XMTBUF+SDNETBUF:FILL+1,X
	CLR	SOCKET:XMTBUF+SDNETBUF:EMPTY+1,X
	CLR	SOCKET:PROVXMTEMPTY+1,X
	RTS
	PAGE
SKTMFINDFREESOCKET	; FIND A FREE SOCKET
	LDX	#SOCKETTABLELOCK
	JSR	RESOURCEALLOCATE
	LDX	#SOCKETTABLE
	STX	SKTMSOCKETSLAB
	LDAA	#SDNETSOCKETMAX
	STAA	SKTMSOCKETCTR
SKTMFINDFREESOCKETLOOP
	JSR	SKTMGETNEXTFREESOCKET
	BCC	SKTMGOTFREESOCKET
	LDX	SKTMSOCKETSLAB
	INX
	INX
	STX	SKTMSOCKETSLAB
	DEC	SKTMSOCKETCTR
	BNE	SKTMFINDFREESOCKETLOOP
	LDX	#SOCKETTABLELOCK	LET GO OF SOCKET TABLE
	JSR	RESOURCERELEASE
	ERRORRTS

SKTMGOTFREESOCKET	; GOT A FREE SOCKET
	STX	SKTMSOCKETSLAB	SOCKET ADDRESS RETURNED BY GETNEXTFREESOCKET
	LDAA	#1
	STAA	SOCKET:LOCK,X
	LDAA	#SDNETSOCKETMAX
	STAA	SOCKET:STATE,X	TRASH THE SOCKET STATE
	SUBA	SKTMSOCKETCTR
	STAA	SOCKET:SOCKET,X
	BSR	SKTMREAMINPUTOUTPUT	ream input and output buffers
	LDAA	#SKTMESTXMTRISK/256
	STAA	SOCKET:XMTRISKLIMIT,X
	LDAA	#SKTMESTXMTRISK&$FF
	STAA	SOCKET:XMTRISKLIMIT+1,X
	LDAA	#SKTMESTRCVTHRESH/256
	STAA	SOCKET:RCVUNACKEDLIMIT,X
	LDAA	#SKTMESTRCVTHRESH&$FF
	STAA	SOCKET:RCVUNACKEDLIMIT+1,X
	CLR	SOCKET:RCVBASE,X	CLEAR TRANSMIT AND RECEIVE BASES
	CLR	SOCKET:RCVBASE+1,X
	CLR	SOCKET:RCVUNACKED,X
	CLR	SOCKET:RCVUNACKED+1,X
	CLR	SOCKET:XMTBASE,X
	CLR	SOCKET:XMTBASE+1,X
	CLR	SOCKET:LOCALSTATUS,X	CLEAR STATUS FIELD
	CLR	SOCKET:OLDLOCALSTATUS,X
	CLR	SOCKET:REMOTESTATUS,X
	CLR	SOCKET:CROAKREASON,X	CLEAR REASON FOR CROAKING
	CLR	SOCKET:CROAKREASON+1,X
	LDAA	#SKTMESTCONTTHRESH/256	SET CONTINUATION THRESHOLD
	LDAB	#SKTMESTCONTTHRESH&$FF
	STAA	SOCKET:CONTTHRESH,X
	STAB	SOCKET:CONTTHRESH+1,X
	JSR	SKTMRESETXMTBASE
	JSR	GENERATESOCKETKEY
	STX	TEMPX
	LDX	SKTMSOCKETSLAB
	STAA	SOCKET:KEY,X
	STAB	SOCKET:KEY+1,X
	LDAA	TEMPA
	STAA	SOCKET:KEY+2,X
	LDAA	TEMPB
	STAA	SOCKET:KEY+3,X
	STX	SKTMSOCKET
	JSR	RESOURCEALLOCATE	LATCH ONTO SOCKET
	LDX	#SOCKETTABLELOCK
	JSR	RESOURCERELEASE
	OKRTS
	PAGE
SKTMGETNEXTFREESOCKET	; STARE AT THE SOCKET AND SEE IF ITS STATE IS "FREE"
	LDX	SKTMSOCKETSLAB
	LDX	0,X
	LDAA	SOCKET:STATE,X
	CMPA	#SOCKETSTATE:FREE
	BNE	SKTMNOTTHISSOCKET
	OKRTS

SKTMNOTTHISSOCKET
	ERRORRTS

;	LOCKED BY SOCKETTABLELOCK

SKTMSOCKETSLAB	RMB	2	HOLDS ADDRESS OF NEWLY HATCHED SOCKET
SKTMSOCKETCTR	RMB	1	HOLDS COUNT OF SOCKETS TO PERUSE
	PAGE
	NAME	TEST
SKTMTEST	; TEST FOR NUMBER OF BYTES TO READ

;	ON ENTRY (X) -> SOCKET CAPABILITY
;	ON SUCCESS EXIT (A) & (B) = NUMBER OF BYTES AVAILABLE TO READ
;	ON FAIL EXIT (ERRORRTS), (X) = ERROR CODE.  (A), (B), (X) ARE VOLATILE!

;	        +---------------------+
;	(X) --> | @ SOCKET CAPABILITY |
;	        +---------------------+
;	              16 BITS


	JSR	SKTMCHECKSC	VALIDATE SOCKET CAPABILITY
	LDAA	SOCKET:STATE,X
	CMPA	#SOCKETSTATE:CROAKED
	BEQ	SKTMSEVERED
	CMPA	#SOCKETSTATE:ANSWER
	BEQ	SKTMTESTSTATEOK
	CMPA	#SOCKETSTATE:OPEN
	BEQ	SKTMTESTSTATEOK
	CMPA	#SOCKETSTATE:ASSIGNING
	BEQ	SKTMTESTSTATEOK
	CMPA	#SOCKETSTATE:ASSIGNED
	BEQ	SKTMTESTSTATEOK
SKTMBADSOCKETSTATE
	JSR	RESOURCERELEASE	LET GO OF SOCKET
	LDX	#ERR:BADSOCKETOPER
	ERRORRTS
	PAGE
SKTMTESTSTATEOK	; SOCKET STATE IS VALID WITH (X) -> SOCKET
	JSR	SKTMSOCKETRCVBYTES	RETURN BYTE COUNT IN A & B
SKTMRETURN2BYTES	; RETURN WITH (A) & (B) INTACT
	PSHA
	PSHB
	LDX	SKTMSOCKET	LET GO OF SOCKET
	JSR	RESOURCERELEASE
	PULB
	PULA
	OKRTS

SKTMSEVERED
	JSR	RESOURCERELEASE
	LDX	SOCKET:CROAKREASON,X	MAKE UP A REASON, UNLESS ONE EXISTS
	BNE	SKTMSEVEREDGOTREASON
	LDX	#ERR:NOREASON	gee!  I don't know what to say!
SKTMSEVEREDGOTREASON
	ERRORRTS
	PAGE
SKTMCHECKSC	; VALIDITY CHECK THE SOCKET CAPABILITY
	STX	SKTMPARM	SAVE PARAMETER LIST ADDRESS
	LDX	0,X	PICK UP ADDRESS OF SOCKET CAPABILITY
	LDAA	SKTMSCSOCKET,X
	ASLA
	STAA	TEMPB
	LDAA	#SOCKETTABLE/256
	STAA	TEMPA
	LDX	TEMPX
	LDX	SOCKETTABLE&$FF,X
	STX	SKTMSOCKET
	LDAA	SOCKET:KEY,X
	LDAB	SOCKET:KEY+1,X
	LDX	SOCKET:KEY+2,X
	STX	TEMPX
	LDX	SKTMPARM
	LDX	0,X	POINT AT SOCKET CAPABILITY, AGAIN
	EORA	SKTMSCKEY,X
	BNE	SKTMCHECKSCNOMATCH
	EORB	SKTMSCKEY+1,X
	BNE	SKTMCHECKSCNOMATCH
	LDAA	TEMPA
	EORA	SKTMSCKEY+2,X
	BNE	SKTMCHECKSCNOMATCH
	LDAA	TEMPB
	EORA	SKTMSCKEY+3,X
	BNE	SKTMCHECKSCNOMATCH
	LDX	SKTMSOCKET
	JSR	RESOURCEALLOCATE	LATCH ONTO SOCKET
	OKRTS

SKTMCHECKSCNOMATCH
	INS
	INS
	LDX	#ERR:BADSOCKETCAP
	ERRORRTS
	PAGE
SKTMSOCKETRCVBYTES	; GET INTO (A) & (B) SIZE OF DATA IN SOCKET RCV BUFFER
	LDAB	SOCKET:RCVBUF+SDNETBUF:FILL+1,X
	SUBB	SOCKET:RCVBUF+SDNETBUF:EMPTY+1,X
	CLRA
	RTS

SKTMSOCKETXMTROOM	; GET INTO (A) & (B) SIZE OF FREE SPACE IN SOCKET XMT BUFFER
	LDAB	SOCKET:XMTBUF+SDNETBUF:EMPTY+1,X
	SUBB	SOCKET:XMTBUF+SDNETBUF:FILL+1,X
	DECB
	CLRA
	RTS

SKTMZAPSOCKET	; PERFORM PREFRONTAL LOBOTOMY ON SOCKET
	LDX	#SOCKETTABLELOCK
	JSR	RESOURCEALLOCATE
	LDX	SKTMSOCKET
	CLR	SOCKET:NRB,X
	CLR	SOCKET:NRB+1,X
	CLR	SOCKET:STATE,X
	JSR	RESOURCERELEASE
	LDX	#SOCKETTABLELOCK
	JSR	RESOURCERELEASE
	RTS
	PAGE
	NAME	READ
SKTMREAD	; READ DATA FROM SOCKET RCV BUFFER

;	ON ENTRY (X) -> INPUT PARAMETER BLOCK
;	ON SUCCESS EXIT (OKRTS), (A) & (B) = BUFFER SPACE REMAINING
;	ON FAIL EXIT (ERRORRTS), (X) = ERROR CODE.

;	        +---------------------+----------+----------+
;	(X) --> | @ SOCKET CAPABILITY | @ BUFFER | # BUFFER |
;	        +---------------------+----------+----------+
;	                  16               16          16


HERE	SET	*
	ORG	0
SKTMSCADDR	RMB	2
SKTMBUFFERADDR	RMB	2
SKTMBUFFERSIZE	RMB	2
	ORG	HERE

	JSR	SKTMCHECKSC
	LDAA	SOCKET:STATE,X
	CMPA	#SOCKETSTATE:CROAKED
	BNE	SKTMR1	didn't croak--look further
	LDAA	SOCKET:REMOTESTATUS,X	passage permitted unless remote croaked
	BITA	#SOCKETSTATUS:NODECROAKED!SOCKETSTATUS:CROAKED
	BNE	SKTMSEVEREDJ	he did, he did!!
	BRA	SKTMREADSTATEOK	assume there's more data to read
				; I'm not sure I like this test.
SKTMR1
	LDAA	SOCKET:STATE,X
	CMPA	#SOCKETSTATE:ANSWER
	BEQ	SKTMREADSTATEOK
	CMPA	#SOCKETSTATE:OPEN
	BEQ	SKTMREADSTATEOK
	CMPA	#SOCKETSTATE:ASSIGNING
	BEQ	SKTMREADSTATEOK
	CMPA	#SOCKETSTATE:ASSIGNED
	BEQ	SKTMREADSTATEOK
	JMP	SKTMBADSOCKETSTATE

SKTMSEVEREDJ
	JMP	SKTMSEVERED
	PAGE
SKTMREADSTATEOK	; SOCKET STATE IS VALID WITH (X) -> SOCKET
	LDX	SKTMPARM
	LDX	SKTMBUFFERSIZE,X
	STX	SKTMMISC
	LDX	SKTMPARM
	LDX	SKTMBUFFERADDR,X
	STX	SKTMPARM
SKTMREADLOOP
	LDX	SKTMSOCKET
	LDAA	SKTMMISC	CHECK FOR ZERO COUNT
	ORAA	SKTMMISC+1
	BEQ	SKTMREADLOOPEXIT
	LDX	SKTMSOCKET
	JSR	SKTMGETFROMSOCKET	GET A BYTE FROM SOCKET RCV BUFFER
	BCS	SKTMREADLOOPEXITCL	END OF PIPE HIT!
	LDX	SKTMPARM	PUT IT IN THE USER'S BUFFER
	STAB	0,X
	INX
	STX	SKTMPARM
	TST	SKTMMISC+1	DECREMENT THE COUNT
	BNE	SKTMREADLOOP1
	DEC	SKTMMISC
SKTMREADLOOP1
	DEC	SKTMMISC+1
	BRA	SKTMREADLOOP
	PAGE
SKTMREADLOOPEXIT
	LDAA	SOCKET:LOCALSTATUS,X	IF CONTTHRESH EXCEEDED AND LOST DATA WAS...
	BITA	#SOCKETSTATUS:LOSTDATA	...SENT, SEND CONTINUE DATA
	BEQ	SKTMREADLOOPEXIT1
	JSR	SKTMSOCKETRCVBYTES
	SUBB	SOCKET:CONTTHRESH+1,X
	SBCA	SOCKET:CONTTHRESH,X
	BPL	SKTMREADLOOPEXIT1
	LDAA	SOCKET:LOCALSTATUS,X	turn off lost data and note status change
	ANDA	#\SOCKETSTATUS:LOSTDATA
	STAA	SOCKET:LOCALSTATUS,X
	LDAA	#1
	STAA	SOCKET:STATUSCHANGED,X
SKTMREADLOOPEXIT1
	JSR	RESOURCERELEASE
	LDAA	SKTMMISC
	LDAB	SKTMMISC+1
	OKRTS

SKTMREADLOOPEXITCL
	STX	SKTMPARM
	LDX	SKTMSOCKET
	JSR	RESOURCERELEASE
	LDX	SKTMPARM
	LDAA	SKTMMISC
	LDAB	SKTMMISC+1
	ERRORRTS

SKTMCROAKING
	INS	forget about return address
	INS
	JMP	SKTMSEVERED
	PAGE
SKTMGETFROMSOCKET	; GET A BYTE FROM SOCKET RCV BUFFER AND BLOCK IF NONE
	LDAA	SOCKET:RCVBUF+SDNETBUF:FILL+1,X
	CMPA	SOCKET:RCVBUF+SDNETBUF:EMPTY+1,X
	BNE	SKTMGETFROMSOCKETNOBLOCK
	LDAA	SOCKET:REMOTESTATUS,X	DON'T BLOCK IF REMOTE CLOSED
	BITA	#SOCKETSTATUS:CLOSED
	BEQ	SKTMGETNOTCLOSED
	LDX	#ERR:EOFHIT
	ERRORRTS

SKTMGETNOTCLOSED
	LDX	SKTMSOCKET
	LDAA	SOCKET:LOCALSTATUS,X	tell remote that I need more data
	ORAA	#SOCKETSTATUS:NEEDDATA
	STAA	SOCKET:LOCALSTATUS,X
	STAA	SOCKET:STATUSCHANGED,X
	LDX	SKTMSOCKET
	JSR	RESOURCERELEASE
	LDAA	#SKTMGETFROMSOCKETTEST/256
	LDAB	#SKTMGETFROMSOCKETTEST&$FF
	JSR	SDOS+SDOS:WAITCOND
	PSHA
	JSR	RESOURCEALLOCATE
	PULA
	CMPA	#2	CHECK REASON FOR UNBLOCKING
	BEQ	SKTMGETFROMSOCKETNOBLOCK	A CHARACTER IS AVAILABLE
	BRA	SKTMCROAKING

SKTMGETFROMSOCKETNOBLOCK
	LDX	SOCKET:RCVBUF+SDNETBUF:EMPTY,X
	LDAB	0,X
	LDX	SKTMSOCKET
	INC	SOCKET:RCVBUF+SDNETBUF:EMPTY+1,X
	OKRTS
	PAGE
SKTMGETFROMSOCKETTEST
	LDAA	SOCKET:RCVBUF+SDNETBUF:FILL+1,X
	SUBA	SOCKET:RCVBUF+SDNETBUF:EMPTY+1,X
	BNE	SKTMGETFROMSOCKETTESTWAKE2	CHARACTER IS AVAILABLE
	LDAA	SOCKET:LOCALSTATUS,X
	BITA	#SOCKETSTATUS:CROAKED
	BNE	SKTMGETFROMSOCKETTESTWAKE1
	CLRA	NOTHING GOING ON!
	RTS

SKTMGETFROMSOCKETTESTWAKE1
	LDAA	#1
	RTS

SKTMGETFROMSOCKETTESTWAKE2
	LDAA	#2
	RTS
	PAGE
	NAME	WRITE
SKTMWRITE	; WRITE DATA TO SOCKET XMT BUFFER (ALSO TO NRB XMT BUFFER)

;	ON ENTRY (X) -> INPUT PARAMETER BLOCK
;	ON FAIL EXIT (ERRORRTS), (X) = ERROR CODE.  (A), (B), (X) ARE VOLATILE!

;	        +---------------------+----------+----------+
;	(X) --> | @ SOCKET CAPABILITY | @ BUFFER | # BUFFER |
;	        +---------------------+----------+----------+
;	                  16               16          16

	JSR	SKTMCHECKSC
	LDAA	SOCKET:STATE,X
	CMPA	#SOCKETSTATE:CROAKED
	BEQ	SKTMSEVEREDJ1
	CMPA	#SOCKETSTATE:ORIGINATE
	BEQ	SKTMWRITESTATEOK
	CMPA	#SOCKETSTATE:LINKING
	BEQ	SKTMWRITESTATEOK
	CMPA	#SOCKETSTATE:OPEN
	BEQ	SKTMWRITESTATEOK
	JMP	SKTMBADSOCKETSTATE

SKTMSEVEREDJ1
	JMP	SKTMSEVERED
	PAGE
SKTMWRITESTATEOK	; SOCKET STATE IS VALID
	LDAA	SOCKET:REMOTESTATUS,X	SEE IF WRITE IS RATIONAL
	BITA	#SOCKETSTATUS:CLOSED+SOCKETSTATUS:CLOSING
	BEQ	SKTMWRITENOTCLOSED
	JSR	RESOURCERELEASE	OTHER SIDE IS CLOSED--BUG OUT
	LDX	#ERR:EOMHIT	END OF MEDIA!
	ERRORRTS

SKTMWRITENOTCLOSED
	LDX	SKTMPARM	SAVE THE BUFFER AND BUFFER SIZE
	LDX	SKTMBUFFERSIZE,X
	STX	SKTMMISC
	LDX	SKTMPARM
	LDX	SKTMBUFFERADDR,X
	STX	SKTMPARM
SKTMWRITELOOP
	LDAA	SKTMMISC
	ORAA	SKTMMISC+1
	BEQ	SKTMWRITELOOPEXIT	EXIT ON ZERO COUNT
	LDX	SKTMPARM	GET BYTE FROM USER BUFFER
	LDAB	0,X
	LDX	SKTMSOCKET	PUT IN SOCKET XMT BUFFER
	JSR	SKTMPUTTOSOCKET
	BCS	SKTMWRITELOOPEXIT	PAUSE IF NO MORE ROOM IN BUFFER
	LDX	SKTMPARM
	INX
	STX	SKTMPARM	BUMP POINTER AND DECREMENT COUNT
	TST	SKTMMISC+1
	BNE	SKTMWRITELOOP1
	DEC	SKTMMISC
SKTMWRITELOOP1
	DEC	SKTMMISC+1
	BRA	SKTMWRITELOOP
	PAGE
SKTMWRITELOOPEXIT
	LDX	SKTMSOCKET	FLAG A TRANSMIT REQUEST
	LDAA	#1
	STAA	SOCKET:STATUSCHANGED,X
	LDAA	SKTMMISC	SEE IF ANY MORE DATA IN USER BUFFER
	ORAA	SKTMMISC+1
	BEQ	SKTMWRITEDATAALLGONE
	LDX	SKTMSOCKET	DUMP SOCKET TO AVOID DEADLOCK DURING BLOCKING
	JSR	RESOURCERELEASE
	LDAA	#SKTMTESTSOCKETXMTBUFFERROOM/256
	LDAB	#SKTMTESTSOCKETXMTBUFFERROOM&$FF
	JSR	SDOS+SDOS:WAITCOND	BLOCK UNTIL MORE BUFFER SPACE
	PSHA
	JSR	RESOURCEALLOCATE	GET SOCKET BACK
	PULA
	CMPA	#2	CHECK FOR "MORE BUFFER SPACE" CONDITION
	BEQ	SKTMWRITELOOP	ANY OTHER CONDITION MEANS SOCKET IS EL SICKO
	JMP	SKTMCROAKING

SKTMWRITEDATAALLGONE	; ALL OF USER'S WRITE BUFFER WAS STUFFED IN SOCKET
	LDX	SKTMSOCKET
	JSR	RESOURCERELEASE	LET GO OF SOCKET
	OKRTS
	PAGE
SKTMPUTTOSOCKET	; PUT (B) = BYTE INTO SOCKET XMT BUFFER
	LDAA	SOCKET:XMTBUF+SDNETBUF:FILL+1,X
	SUBA	SOCKET:XMTBUF+SDNETBUF:EMPTY+1,X
	INCA
	BNE	SKTMPUTTOSOCKETGOTROOM
	ERRORRTS

SKTMPUTTOSOCKETGOTROOM
	LDX	SOCKET:XMTBUF+SDNETBUF:FILL,X
	STAB	0,X
	LDX	SKTMSOCKET
	INC	SOCKET:XMTBUF+SDNETBUF:FILL+1,X
	OKRTS

SKTMTESTSOCKETXMTBUFFERROOM
	LDAA	SOCKET:STATE,X
	CMPA	#SOCKETSTATE:CROAKED
	BEQ	SKTMTESTSOCKETXMTBUFFERWAKE1
	LDAA	SOCKET:XMTBUF+SDNETBUF:FILL+1,X
	SUBA	SOCKET:XMTBUF+SDNETBUF:EMPTY+1,X
	INCA
	BNE	SKTMTESTSOCKETXMTBUFFERWAKE2
	CLRA	NOTHING GOING ON
	RTS

SKTMTESTSOCKETXMTBUFFERWAKE1
	LDAA	#1
	RTS

SKTMTESTSOCKETXMTBUFFERWAKE2
	LDAA	#2
	RTS
	PAGE
	NAME	CLOSE
SKTMCLOSE	; CLOSE OUT A SOCKET

;	ON ENTRY (X) -> SOCKET CAPABILITY
;	ON FAIL EXIT (ERRORRTS), (X) = ERROR CODE.  (A), (B), (X) ARE VOLATILE!

;	        +---------------------+
;	(X) --> | @ SOCKET CAPABILITY |
;	        +---------------------+
;	              16 BITS

	JSR	SKTMCHECKSC
	LDX	#0	START OUT WITH NO ERRORS
	STX	SKTMPARM
	LDX	SKTMSOCKET
	LDAA	SOCKET:STATE,X
	CMPA	#SOCKETSTATE:FREE
	BEQ	SKTMBADCLOSE
	CMPA	#SOCKETSTATE:ORIGINATE
	BEQ	SKTMSHORTCLOSE
	CMPA	#SOCKETSTATE:ANSWER
	BEQ	SKTMSHORTCLOSE
	CMPA	#SOCKETSTATE:CROAKED
	BEQ	SKTMCROAKCLOSE
	LDX	SKTMSOCKET
	JSR	SKTMSOCKETRCVBYTES
	STAA	TEMPA
	ORAB	TEMPA
	BEQ	SKTMCLOSENOINPUT
	LDX	#ERR:LOCALCLOSELOST	UNREAD DATA EXISTS--BITCH ABOUT IT!!
	STX	SKTMPARM
	LDX	SKTMSOCKET
	PAGE
SKTMCLOSENOINPUT
	LDAA	SOCKET:LOCALSTATUS,X	tell sender we're closing and get out!
	ORAA	#SOCKETSTATUS:CLOSING
	STAA	SOCKET:LOCALSTATUS,X
	STAA	SOCKET:STATUSCHANGED,X
	JSR	RESOURCERELEASE
	BRA	SKTMCLOSEXIT

SKTMSHORTCLOSE
	JSR	SKTMZAPSOCKET	release socket
SKTMCLOSEXIT
	LDX	SKTMPARM
	BNE	SKTMCLOSEEXITERROR
	OKRTS

SKTMCLOSEEXITERROR
	ERRORRTS

SKTMBADCLOSE
	JSR	RESOURCERELEASE
	LDX	#ERR:BADSOCKETOPER
	ERRORRTS

SKTMCROAKCLOSE
	LDX	SOCKET:CROAKREASON,X
	STX	SKTMPARM
	BRA	SKTMSHORTCLOSE

SKTMUNACKEDBYTES	; GET NUMBER OF BYTES IN BUFFER BUT NOT ACKED
	LDAB	SOCKET:XMTBUF+SDNETBUF:FILL+1,X
	SUBB	SOCKET:XMTBUF+SDNETBUF:EMPTY+1,X
	CLRA
	RTS
	PAGE
	NAME	QUITNODE
SKTMQUITNODE	; DISCONNECT NODE REPRESENTATIVE

;	(A) = NUMBER OF NODE TO SHUT DOWN

;	ERRORRTS (WITH (X) = CODE) IF NODE IS NOT REPRESENTED

	TAB
	SUBA	#SDNETNODEMAX
	BCC	SKTMQUITNODEBADNODE
	ASLB
	STAB	TEMPB
	LDAA	#SDNETNRBTABLE/256
	STAA	TEMPA
	LDX	TEMPX
	LDX	SDNETNRBTABLE&$FF,X
	STX	SKTMPARM
	LDX	#SDNETDESIREQUITLOCK
	JSR	RESOURCEALLOCATE
	LDX	SKTMPARM
	STX	SDNETDESIREQUITNRBPTR
	LDX	#SDNETINTDESIREQUIT
	JSR	SDOS+SDOS:STARTIO
	OKRTS

SKTMQUITNODEBADNODE
	LDX	#ERR:BADNODE
	ERRORRTS
	PAGE
	PAGE	Listener Task
	NAME	LISTENER
SKTMLISTENER

;	LISTENER TASK BLOCKS UNTIL EITHER THERE IS DATA IN ITS RECEIVE BUFFER,
;	ITS NODE HAS DIED, OR THE GOOF FLAG WAS SET AND THERE IS ROOM IN
;	THE NODE OUTPUT BUFFER.  IF IT HASN'T DIED IN ITS SLEEP, IT PEELS BYTES
;	OUT OF THE RECEIVE BUFFER, DISTRIBUTING THEM TO SOCKETS, AS
;	APPROPRIATE (OR FLUSHING THEM IF NO VALID SOCKET HAS BEEN NAMED).

	STX	SKTMNRB	remember this!!
	JSR	SKTMLISTENERBLOCKINGCONDITIONS
	BNE	SKTMLISTENERREADY
	LDAA	#SKTMLISTENERBLOCKINGCONDITIONS/256
	LDAB	#SKTMLISTENERBLOCKINGCONDITIONS&$FF
	JSR	SDOS+SDOS:WAITCOND
SKTMLISTENERREADY	; ASSERT: (X) -> NRB!!
	CLR	SDNETNRB:SOCKET,X	no socket is current
	CLR	SDNETNRB:SOCKET+1,X
	CMPA	#1
	BNE	SKTMNODENOTDEAD
	JMP	SKTMBURYNODE	NODE WOKE UP DEAD
SKTMNODENOTDEAD
	CMPA	#2
	BEQ	SKTMLISTENERGOTINPUT
	CMPA	#3
	BNE	SKTMLISTENERKILLNODE	HAVOC SHALL REIGN!!!
	JMP	SKTMLISTENERHANDLEGOOF
	PAGE
*	Wake up the listener task when any of the following occur:
*
*		Receive buffer not empty
*
*		Node is dead
*
*		NRB is marked GOOF and is unlocked and the transmit buffer
*		has room for at least one byte

SKTMLISTENERBLOCKINGCONDITIONS	; BLOCK LISTENER PENDING FOLLOWING CONDITIONS
	LDAB	SDNETNRB:STATE,X
	CMPB	#NODESTATE:DEAD
	BEQ	SKTMLISTENERBLOCKWAKE1
	LDAB	SDNETNRB:RCVBUF+SDNETBUF:FILL+1,X	CHECK FOR SOMETHING IN RECEIVE BUFFER
	SUBB	SDNETNRB:RCVBUF+SDNETBUF:EMPTY+1,X
	BNE	SKTMLISTENERBLOCKWAKE2
	LDAA	SDNETNRB:GOOF,X	SEE IF GOOFED, NRB UNLOCKED, AND XMT ROOM (WHEW!)
	BEQ	SKTMLISTENERBLOCKNOWAKE
	TST	SDNETNRB:XBUFLK,X
	BEQ	SKTMLISTENERBLOCKNOWAKE
	LDAA	SDNETNRB:XMTBUF+SDNETBUF:EMPTY+1,X
	SUBA	SDNETNRB:XMTBUF+SDNETBUF:FILL+1,X
	DECA
	BNE	SKTMLISTENERBLOCKWAKE3
SKTMLISTENERBLOCKNOWAKE
	CLRA
	RTS
SKTMLISTENERBLOCKWAKE1
	LDAA	#1
	RTS
SKTMLISTENERBLOCKWAKE2
	LDAA	#2
	RTS
SKTMLISTENERBLOCKWAKE3
	LDAA	#3
	RTS
	PAGE
SKTMLISTENERGOTINPUT
	JSR	SDNETINPUTNRB	GET MESSAGE FUNCTION AND PERFORM A "DO CASE"
	CMPB	#SKTMMAXFUNCTIONNUMBER
	BHI	SKTMLISTENERKILLNODE
	ASLB
	STAB	TEMPB
	LDAA	#SKTMLISTENERFUNCTIONTABLE/256
	STAA	TEMPA
	LDX	TEMPX
	LDX	SKTMLISTENERFUNCTIONTABLE&$FF,X
	JMP	0,X

SKTMLISTENERFUNCTIONTABLE	; INDEXED BY MESSAGE FUNCTION NUMBER
	FDB	SKTMLISTENERNORMALCONTINUATION
	FDB	SKTMLISTENERFIRSTMESSAGE
	FDB	SKTMLISTENERNOSUCHSOCKET
	FDB	SKTMLISTENERASSIGNTARGETSOCKET
	FDB	SKTMLISTENERFIRSTMSGCONT
	FDB	SKTMLISTENERGOOFED

SKTMLISTENERKILLNODE	; SOMETHING TERRIBLE HAPPENED--KILL THE NODE
	JSR	SKTMKILLNODE
	STX	TEMPX	TXD
	LDAA	TEMPA
	LDAB	TEMPB
	ADDB	#SDNETNRB:STACKBASE	ADDD	#SDNET:STACKBASE
	ADCA	#0
	STAA	TEMPA	TDS
	STAB	TEMPB
	LDS	TEMPX
	JMP	SKTMLISTENER
	PAGE
SKTMRESETXMTBASE	; RESET THE TRANSMIT BASE AND BUFFER POINTERS
	LDAB	SOCKET:XMTBUF+SDNETBUF:EMPTY+1,X	ASSERT: (X) -> SOCKET
	STAB	SOCKET:PROVXMTEMPTY+1,X
	LDAA	SOCKET:XMTBASE,X
	LDAB	SOCKET:XMTBASE+1,X
	STAA	SOCKET:PROVXMTBASE,X
	STAB	SOCKET:PROVXMTBASE+1,X
	CLR	SOCKET:XMTRISK,X
	CLR	SOCKET:XMTRISK+1,X
	RTS

SKTMRESETNRB	; RE-VITALIZE A DEAD NRB--ASSERT: (X) -> NRB!!
	CLR	SDNETNRB:RCVBUF+SDNETBUF:EMPTY+1,X
	CLR	SDNETNRB:RCVBUF+SDNETBUF:FILL+1,X
	CLR	SDNETNRB:XMTBUF+SDNETBUF:EMPTY+1,X
	CLR	SDNETNRB:XMTBUF+SDNETBUF:FILL+1,X
	CLR	SDNETNRB:XVPOS,X
	CLR	SDNETNRB:XVPOS+1,X
	CLR	SDNETNRB:RVPOS,X
	CLR	SDNETNRB:RVPOS+1,X
	CLR	SDNETNRB:GOOF,X
	CLR	SDNETNRB:XBUFLK,X
	INC	SDNETNRB:XBUFLK,X
	LDAA	#NODEEPITAPH:WASCRAZY	default epitaph: crazed!
	STAA	SDNETNRB:EPITAPH,X
	LDAA	#NODESTATE:INACTIVE
	STAA	SDNETNRB:STATE,X
	RTS

SKTMBURYNODE	; CLEAN UP MESS LEFT BY DYING NODE
	BSR	SKTMKILLSOCKETS	(X) -> NRB
	BSR	SKTMRESETNRB	(X) -> NRB
	JMP	SKTMLISTENER
	PAGE
SKTMLISTENERHANDLEGOOF	; ASSERT (X) -> NRB
	JSR	SKTMLISTENERBLOCKINGCONDITIONS	SEE IF CONDITIONS STILL OBTAIN
	CMPA	#3
	BNE	SKTMLISTENERJ	SOMETHING CHANGED!!
	JSR	RESOURCEALLOCATE	GRAB NRB
	LDAB	#SOCKETFUNCTION:GOOFED	ASSERT: LEN(FUNCTIONCODE) = 1!!
	JSR	SDNETOUTPUTNRB
	CLR	SDNETNRB:GOOF,X	AHHH...THAT FEELS BETTER!
	JSR	RESOURCERELEASE
SKTMLISTENERJ
	JMP	SKTMLISTENER
SKTMKILLNODE	; REQUEST THE NODE BE MARKED DEAD
	STX	TEMPX	SAVE NRB ADDRESS
	LDX	#SDNETDESIREKILLLOCK
	JSR	RESOURCEALLOCATE
	LDX	TEMPX
	STX	SDNETDESIREKILLNRBPTR
	LDX	#SDNETINTDESIREKILL
	JSR	SDOS+SDOS:STARTIO
	LDX	TEMPX
	RTS
	PAGE
SKTMINPUTXMTCOUNT	; INPUT XMT COUNT  (X) -> NRB
	JSR	SKTMINPUT2BYTESFROMNRB
	LDX	SDNETNRB:WORK,X
	STX	SKTMPARM
	LDX	SKTMNRB	XMT COUNT IS IN SKTMPARM
	RTS

SKTMFLUSHXMTDATA	; FLUSH SKTMPARM BYTES OF TRANSMIT DATA
	LDAA	SKTMPARM	SKTMPARM contains byte count
	ORAA	SKTMPARM+1
	BEQ	SKTMFLUSHXMTDATADONE
	JSR	SDNETINPUTNRB
	DECD	SKTMPARM
	BRA	SKTMFLUSHXMTDATA

SKTMFLUSHXMTDATADONE
	RTS

SKTMFLUSHFLOW	; FLUSH FLOW CONTROL INFO OF CURRENT MESSAGE IN NRB BUFFER
	JSR	SKTMINPUT2BYTESFROMNRB	MUNCH RCDOK
	JSR	SKTMINPUT2BYTESFROMNRB	MUNCH RCV RDY
	JSR	SKTMINPUTXMTCOUNT	MUNCH XMT COUNT
	JSR	SKTMINPUT2BYTESFROMNRB	MUNCH XMT BASE
	RTS
	PAGE
SKTMKILLSOCKETS; CROAK ALL SOCKETS POINTING TO NRB WHICH JUST DIED  (X) -> NRB
	LDX	SDNETNRB:SOCKET,X	(X) will be 0 if there is no socket
	BEQ	SKTMKILLSOCKETNOSOCKET
	JSR	RESOURCERELEASE	release socket
	LDX	SKTMNRB	forget about socket
	CLR	SDNETNRB:SOCKET,X
	CLR	SDNETNRB:SOCKET+1,X
SKTMKILLSOCKETNOSOCKET
	LDAA	#SDNETSOCKETMAX
	STAA	SKTMMISC
	LDX	#SOCKETTABLE	FIND ALL SOCKETS USING NRB AND CROAK THEM
	PAGE
SKTMKILLSOCKETLOOP
	STX	SKTMPARM
	LDX	0,X	POINT AT SOCKET
	LDX	SOCKET:NRB,X
	CPX	SKTMNRB
	BNE	SKTMKILLSOCKETGETNEXTSOCKET
	LDAA	SDNETNRB:EPITAPH,X	get obituary of node
	PSHA
	LDX	SKTMPARM	CROAK THIS SOCKET
	LDX	0,X	POINT AT SOCKET
	JSR	RESOURCEALLOCATE
	PULA	get epitaph
	CMPA	#NODEEPITAPH:WASCRAZY
	BNE	SKKSNOTCRAZY
	LDD	#ERR:WASCRAZY
	BRA	SKKSSETEPITAPH
SKKSNOTCRAZY
	CMPA	#NODEEPITAPH:REMOTEDISCONNECT
	BNE	SKKSNOTREMOTEDISCONNECT
	LDD	#ERR:REMOTEDISCONNECT
	BRA	SKKSSETEPITAPH
SKKSNOTREMOTEDISCONNECT
	CMPA	#NODEEPITAPH:REMOTENORESPONSE
	BNE	SKKSNOTREMOTENORESPONSE
	LDD	#ERR:REMOTENORESPONSE
	BRA	SKKSSETEPITAPH
SKKSNOTREMOTENORESPONSE
	CMPA	#NODEEPITAPH:LOCALDISCONNECT
	BNE	SKKSNOTLOCALDISCONNECT
	LDD	#ERR:LOCALDISCONNECT
	BRA	SKKSSETEPITAPH
SKKSNOTLOCALDISCONNECT
	CMPA	#NODEEPITAPH:LOCALKILLED
	BNE	SKKSNOTLOCALKILLED
	LDD	#ERR:LOCALKILLED
	BRA	SKKSSETEPITAPH
SKKSNOTLOCALKILLED
	LDD	#ERR:UNKNOWNNODEEPITAPH
SKKSSETEPITAPH
	STD	SOCKET:CROAKREASON,X
	LDAA	#SOCKETSTATUS:NODECROAKED
	ORAA	SOCKET:LOCALSTATUS,X
	STAA	SOCKET:LOCALSTATUS,X
	STAA	SOCKET:STATUSCHANGED,X	TELL SENDER ABOUT DEAD SOCKET
	JSR	RESOURCERELEASE
SKTMKILLSOCKETGETNEXTSOCKET
	LDX	SKTMPARM	BUMP SOCKET LIST POINTER
	INX
	INX
	DEC	SKTMMISC	KEEP COUNT
	BNE	SKTMKILLSOCKETLOOP
	LDX	SKTMNRB	restore (X) -> NRB
	RTS
	PAGE
*	Come here when the message function is NORMAL CONTINUATION.  This
*	function is used when communication is either firmly established
*	or on the verge thereof (receiver's state must be ASSIGNED or OPEN).

SKTMLISTENERNORMALCONTINUATION	; RECEIVED A "NORMAL CONTINUATION" MESSAGE
	JSR	SKTMVALIDATESOCKET
	BCS	SKTMLISTENERNORMALCONTINUATIONINVALID
	LDAA	SOCKET:STATE,X
	CMPA	#SOCKETSTATE:OPEN
	BEQ	SKTMLISTENERNORMALCONTINUATIONSTATEOK
	CMPA	#SOCKETSTATE:ASSIGNED
	BEQ	SKTMLISTENERNORMALCONTINUATIONASSIGNED
	BSR	SKTMLISTENERFLUSHNORMALCONTINUATION
SKTMBUGOUTTOKILLSOCKET	; COME HERE TO CROAK THE SOCKET--ASSERT: (X) -> NRB
	LDX	SDNETNRB:SOCKET,X	make sure we're pointing at socket
	LDD	#ERR:LOCALCROAKED
	STD	SOCKET:CROAKREASON,X
	LDAA	#SOCKETSTATUS:CROAKED
	ORAA	SOCKET:LOCALSTATUS,X
	STAA	SOCKET:LOCALSTATUS,X
	STAA	SOCKET:STATUSCHANGED,X	TELL SENDER TASK ABOUT CROAKED SOCKET
	BRA	SKTMBUGOUTTOLISTENER

SKTMLISTENERNORMALCONTINUATIONINVALID	; (X) -> NRB
	BSR	SKTMLISTENERFLUSHNORMALCONTINUATION
	JMP	SKTMLISTENER

SKTMLISTENERFLUSHNORMALCONTINUATION
	LDX	SKTMNRB
	JSR	SDNETINPUTNRB	FLUSH STATUS
	JSR	SKTMFLUSHFLOW
	JSR	SKTMFLUSHXMTDATA
	RTS
	PAGE
SKTMLISTENERNORMALCONTINUATIONASSIGNED
	LDAA	#SOCKETSTATE:OPEN	MAKE TRANSITION FROM "ASSIGNED" TO "OPEN"
	STAA	SOCKET:STATE,X
SKTMLISTENERNORMALCONTINUATIONSTATEOK
	JSR	SKTMINPUTSTATUS	GET REMOTE STATUS
	JSR	SKTMMUNCHFLOWDATA
SKTMBUGOUTTOLISTENER	; COME HERE WHEN MESSAGE PROCESSING COMPLETE
	LDAA	#1	alert sender task
	STAA	SOCKET:STATUSCHANGED,X
	JSR	RESOURCERELEASE	get rid of socket
	LDX	SKTMNRB
	JMP	SKTMLISTENER
	PAGE
*	Come here when the message function is FIRST MESSAGE.  This function
*	is the initial gambit in establishing a communications link.  The
*	receiver's state must be ANSWER.

SKTMLISTENERFIRSTMESSAGE	; RECEIVED A "FIRST MESSAGE" MESSAGE
	JSR	SKTMASSOCIATEVALIDATEFROMSC
	BCS	SKTMLISTENERFIRSTMESSAGEINVALID
	LDAA	SOCKET:STATE,X
	CMPA	#SOCKETSTATE:ANSWER
	BEQ	SKTMLISTENERFIRSTMESSAGESTATEOK
	LDX	SKTMNRB
	JSR	SDNETINPUTNRB	flush remote status
	JSR	SKTMINPUT5BYTESFROMNRB	flush remote socket capability
	JSR	SKTMINPUTXMTCOUNT	get count of data to flush,
	JSR	SKTMFLUSHXMTDATA	and flush it
	BRA	SKTMBUGOUTTOKILLSOCKET

SKTMLISTENERFIRSTMESSAGEINVALID	; ASSERT: (X) -> NRB!!
	JSR	SDNETINPUTNRB	FLUSH REMOTE STATUS
	JSR	RESOURCEALLOCATE	grab NRB to put response into
	TST	SDNETNRB:GOOF,X	SKIP THE FORMALITIES IF ALREADY GOOFED
	BNE	SKTMLISTENERALREADYGOOFED
	LDAA	SDNETNRB:XMTBUF+SDNETBUF:EMPTY+1,X
	SUBA	SDNETNRB:XMTBUF+SDNETBUF:FILL+1,X
	DECA
	SUBA	#6	"NO SUCH SOCKET" IS 6-BYTE MESSAGE
	BCC	SKTMLISTENERNOGOOF
	INC	SDNETNRB:GOOF,X	I GOOFED!!
SKTMLISTENERALREADYGOOFED
	JSR	SKTMINPUT5BYTESFROMNRB	get rid of remote capability
	BRA	SKTMLISTENERFIRSTINVALIDCONT

SKTMLISTENERNOGOOF
	LDAB	#SOCKETFUNCTION:NOSUCHSOCKET
	JSR	SDNETOUTPUTNRB
	JSR	SKTMCOPYSC
SKTMLISTENERFIRSTINVALIDCONT
	JSR	RESOURCERELEASE	don't need NRB anymore--get rid of it
	JSR	SKTMINPUTXMTCOUNT
	JSR	SKTMFLUSHXMTDATA
	JMP	SKTMLISTENER

SKTMCOPYSC	; COPY A SOCKET CAPABILITY FROM INPUT TO OUTPUT (X) -> NRB
	LDAA	#5
	STAA	SKTMMISC
SKTMCOPYSCLOOP
	JSR	SDNETINPUTNRB
	JSR	SDNETOUTPUTNRB
	DEC	SKTMMISC
	BNE	SKTMCOPYSCLOOP
	RTS
	PAGE
SKTMLISTENERFIRSTMESSAGESTATEOK
	JSR	SKTMINPUTSTATUS	SNARF UP REMOTE STATUS
	JSR	SKTMINPUTTARGETSC
	LDAA	#SOCKETSTATE:ASSIGNING
	STAA	SOCKET:STATE,X
	LDX	SKTMNRB
	JSR	SKTMINPUTXMTCOUNT
	LDX	SDNETNRB:SOCKET,X
	CLR	SOCKET:RCVBASE,X
	CLR	SOCKET:RCVBASE+1,X
	CLR	SOCKET:RCVUNACKED,X
	CLR	SOCKET:RCVUNACKED+1,X
	JSR	SKTMMUNCHDATA
	JMP	SKTMBUGOUTTOLISTENER
	PAGE
*	Come here when the message function is NO SUCH SOCKET.  This function
*	is the response of the sender when he receives a FIRST MESSAGE bearing
*	a socket name which he does not recognize.  Receipt of NO SUCH
*	SOCKET causes the sending socket to revert from LINKING to ORIGINATE
*	and to retry the transmission with another FIRST MESSAGE, hoping
*	that this time the target socket has his act together.

SKTMLISTENERNOSUCHSOCKET	; RECEIVED A "NO SUCH SOCKET" MESSAGE
	JSR	SKTMASSOCIATEVALIDATETOSC
	BCS	SKTMLISTENERNOSUCHSOCKETINVALID
	LDAA	SOCKET:STATE,X
	CMPA	#SOCKETSTATE:LINKING
	BEQ	SKTMLISTENERNOSUCHSOCKETSTATEOK
	LDX	SKTMNRB
	JMP	SKTMBUGOUTTOKILLSOCKET

SKTMLISTENERNOSUCHSOCKETINVALID	; ASSERT: (X) -> NRB
	JMP	SKTMLISTENER

SKTMLISTENERNOSUCHSOCKETSTATEOK
	BSR	SKTMLISTENERRESEND
	JMP	SKTMBUGOUTTOLISTENER

SKTMLISTENERRESEND
	LDAA	#SOCKETSTATE:ORIGINATE
	STAA	SOCKET:STATE,X
	JSR	SKTMRESETXMTBASE
	LDAA	#1	BANG SENDER ON NOGGIN
	STAA	SOCKET:STATUSCHANGED,X
	RTS
	PAGE
*	Come here when the message function is ASSIGN TARGET SOCKET. This
*	function is the response of the sender when he receives a FIRST
*	MESSAGE bearing a socket name which he recognizes.  Enclosed with
*	this function is the name of the sender socket, which completes the
*	communication link, advancing the receiver from LINKING to OPEN.

SKTMLISTENERASSIGNTARGETSOCKET	; RECEIVED AN "ASSIGN TARGET SOCKET" MESSAGE
	JSR	SKTMVALIDATESOCKET
	BCS	SKTMLISTENERASSIGNTARGETSOCKETINVALID
	LDAA	SOCKET:STATE,X
	CMPA	#SOCKETSTATE:LINKING
	BEQ	SKTMLISTENERASSIGNTARGETSOCKETSTATEOK
	BSR	SKTMLISTENERFLUSHASSIGNTARGETSOCKET
	JMP	SKTMBUGOUTTOKILLSOCKET

SKTMLISTENERASSIGNTARGETSOCKETINVALID
	BSR	SKTMLISTENERFLUSHASSIGNTARGETSOCKET
	JMP	SKTMLISTENER

SKTMLISTENERFLUSHASSIGNTARGETSOCKET
	LDX	SKTMNRB
	JSR	SDNETINPUTNRB	FLUSH THE REMOTE STATUS
	JSR	SKTMINPUT5BYTESFROMNRB
	JSR	SKTMFLUSHXMTDATA
	RTS

SKTMLISTENERASSIGNTARGETSOCKETSTATEOK
	JSR	SKTMINPUTSTATUS	GET REMOTE STATUS
	JSR	SKTMINPUTTARGETSC
	LDAA	#SOCKETSTATE:OPEN
	STAA	SOCKET:STATE,X
	JSR	SKTMMUNCHFLOWDATA
	JMP	SKTMBUGOUTTOLISTENER
	PAGE
*	Come here when the message function is FIRST MESSAGE CONTINUATION.
*	This function may be sent zero or more times following transmission
*	of a FIRST MESSAGE, until the total data count, over all FIRST
*	MESSAGE and FIRST MESSAGE CONTINUATION messages, equals the sender's
*	transmit risk threshold.  This function allows the initial data gambit
*	to be broken into several messages, sent asynchronously, before the
*	the communication link is firmly established.  The following is a
*	possible scenario (the receiving state must be ASSIGNED or ASSIGNING):
*
*		send FIRST MESSAGE with 20 bytes of data
*			[pause]
*		send FIRST MESSAGE CONTINUATION with 15 bytes of data
*			[pause]
*		send FIRST MESSAGE CONTINUATION with 5 bytes of data
*		receive NO SUCH SOCKET
*			[reset transmission pointers]
*		send FIRST MESSAGE with 40 bytes of data
*		receive ASSIGN TARGET SOCKET
*		send NORMAL CONTINUATION with data
*			.
*			.
*			.

SKTMLISTENERFIRSTMSGCONT	; RECEIVED A "FIRST MESSAGE CONTINUATION" MESSAGE
	JSR	SKTMASSOCIATEVALIDATEFROMSC
	BCS	SKTMLISTENERFIRSTMSGCONTINVALID
	LDAA	SOCKET:STATE,X
	CMPA	#SOCKETSTATE:ASSIGNING
	BEQ	SKTMLISTENERFIRSTMSGCONTSTATEOK
	CMPA	#SOCKETSTATE:ASSIGNED
	BEQ	SKTMLISTENERFIRSTMSGCONTSTATEOK
	BSR	SKTMLISTENERFLUSHFIRSTMSGCONT
	JMP	SKTMBUGOUTTOKILLSOCKET

SKTMLISTENERFIRSTMSGCONTINVALID
	BSR	SKTMLISTENERFLUSHFIRSTMSGCONT
SKTMLISTENERJ1
	JMP	SKTMLISTENER

SKTMLISTENERFLUSHFIRSTMSGCONT
	LDX	SKTMNRB
	JSR	SDNETINPUTNRB	FLUSH REMOTE STATUS
	JSR	SKTMINPUTXMTCOUNT
	JSR	SKTMINPUT2BYTESFROMNRB	FLUSH XMT BASE
	JSR	SKTMFLUSHXMTDATA
	RTS

SKTMLISTENERFIRSTMSGCONTSTATEOK
	JSR	SKTMINPUTSTATUS	GET REMOTE STATUS
	LDX	SKTMNRB
	JSR	SKTMINPUTXMTCOUNT
	JSR	SKTMSYNCMUNCHDATA
	JMP	SKTMBUGOUTTOLISTENER
	PAGE
*	Remote listener task wanted to say "no such socket" but didn't
*	have enough space in his NRB transmit buffer and he didn't care
*	to hang around waiting for it, so he sent over a one byte
*	"goofed" message, instead.  GOOFED will cause all sockets linked
*	to this NRB and in a LINKING state to resend their FIRST MESSAGE.
*
*	[NO SUCH SOCKET would have caused only the named socket to resend
*	its FIRST MESSAGE.]

SKTMLISTENERGOOFED	; OTHER NODE HOLLERED "GOOFED!!"
	LDAA	#SDNETSOCKETMAX	TRAIPSE THROUGH THE SOCKET LIST LOOKING...
	STAA	SKTMMISC	...FOR LIKELY VICTIMS
	LDX	#SOCKETTABLE
SKTMLISTENERGOOFEDLOOP
	STX	SKTMPARM
	LDX	0,X	SEE IF SOCKET MEETS CRITERIA
	LDX	SOCKET:NRB,X
	CPX	SKTMNRB
	BNE	SKTMRESENDNEXTSOCKET	SOCKET DOESN'T POINT TO THIS NRB
	LDX	SKTMPARM
	LDX	0,X
	LDAA	SOCKET:STATE,X
	CMPA	#SOCKETSTATE:LINKING	MUST BE LINKING
	BNE	SKTMRESENDNEXTSOCKET
	JSR	RESOURCEALLOCATE
	JSR	SKTMLISTENERRESEND	RESEND FIRST MESSAGE
	JSR	RESOURCERELEASE
SKTMRESENDNEXTSOCKET
	LDX	SKTMPARM
	INX
	INX
	DEC	SKTMMISC
	BNE	SKTMLISTENERGOOFEDLOOP
	LDX	SKTMNRB
	JMP	SKTMLISTENER
	PAGE
SKTMINPUTTARGETSC	; INPUT NAME OF REMOTE SOCKET
	LDX	SKTMNRB
	JSR	SKTMINPUT5BYTESFROMNRB
	LDD	SDNETNRB:WORK,X	copy over socket and key
	LDX	SDNETNRB:SOCKET,X
	STD	SOCKET:TARGETSOCKET,X
	LDX	SKTMNRB
	LDD	SDNETNRB:WORK+2,X
	LDX	SDNETNRB:SOCKET,X
	STD	SOCKET:TARGETSOCKET+2,X
	LDX	SKTMNRB
	LDAA	SDNETNRB:WORK+4,X
	LDX	SDNETNRB:SOCKET,X
	STAA	SOCKET:TARGETSOCKET+4,X
	RTS
	PAGE
SKTMINPUT5BYTESFROMNRB	; GET 5 BYTES FROM NRB RCV BUFFER
	LDAA	#5
	STAA	SKTMMISC
	STX	SKTMPARM
SKTMINPUTBYTESLOOP
	JSR	SDNETINPUTNRB
	LDX	SKTMPARM
	STAB	SDNETNRB:WORK,X
	INX
	STX	SKTMPARM
	LDX	SKTMNRB
	DEC	SKTMMISC
	BNE	SKTMINPUTBYTESLOOP
	RTS

SKTMINPUT2BYTESFROMNRB	; GET 2 BYTES FROM NRB BUFFER AND LEAVE THEM IN SKTMMISC
	JSR	SDNETINPUTNRB	(X) -> NRB
	STAB	SDNETNRB:WORK,X
	JSR	SDNETINPUTNRB
	STAB	SDNETNRB:WORK+1,X
	RTS

SKTMINPUTSTATUS	; GET REMOTE STATUS FROM NRB AND LEAVE IN SOCKET
	LDX	SKTMNRB
	JSR	SDNETINPUTNRB
	LDX	SDNETNRB:SOCKET,X
	STAB	SOCKET:REMOTESTATUS,X
	RTS
	PAGE
*	Compare 5 bytes for equal/not equal.  SKTMNRB points to the NRB
*	having the first string in its WORK field.  SKTMPARM points to the
*	socket having the second string in its SOCKET and KEY fields.  If
*	TARGETSOCKET and TARGETKEY are desired, instead, then SKTMPARM
*	must contain the socket pointer plus 6.

COMPARE5BYTES
	LDX	SKTMNRB
	LDD	SDNETNRB:WORK,X
	LDX	SKTMPARM
	CMPD	SOCKET:SOCKET,X
	BNE	CMP5NOMATCH
	LDX	SKTMNRB
	LDD	SDNETNRB:WORK+2,X
	LDX	SKTMPARM
	CMPD	SOCKET:SOCKET+2,X
	BNE	CMP5NOMATCH
	LDX	SKTMNRB
	LDAA	SDNETNRB:WORK+4,X
	LDX	SKTMPARM
	CMPA	SOCKET:SOCKET+4,X
	BNE	CMP5NOMATCH
	OKRTS

CMP5NOMATCH
	ERRORRTS
	PAGE
*	Input the next 5 bytes from the NRB.  Those bytes are a socket
*	capability.  The first byte of the capability is a socket number,
*	which must be valid.  The next four bytes are a key, which must
*	match the KEY field of the addressed socket.  If all criteria
*	are met, the socket is locked and return is made with carry
*	reset; otherwise, carry is set and the socket is not locked.

SKTMVALIDATESOCKET
	LDX	SKTMNRB
	JSR	SKTMINPUT5BYTESFROMNRB
	LDAA	SDNETNRB:WORK,X	get socket number
	CMPA	#SDNETSOCKETMAX	validate it
	BHI	SKTMVALIDATESOCKETINVALIDSOCKET
	ASLA		resolve socket number into socket pointer
	STAA	TEMPB
	LDAA	#SOCKETTABLE/256
	STAA	TEMPA
	LDX	TEMPX
	LDX	SOCKETTABLE&$FF,X
	STX	SKTMPARM
	LDX	SOCKET:NRB,X	see if this is me
	CPX	SKTMNRB
	BNE	SKTMVALIDATESOCKETINVALIDSOCKET
	BSR	COMPARE5BYTES
	BCS	SKTMVALIDATESOCKETINVALIDSOCKET
	LDX	SKTMNRB	hook socket to NRB and latch onto socket
	LDD	SKTMPARM
	STD	SDNETNRB:SOCKET,X
	LDX	SKTMPARM
	JSR	RESOURCEALLOCATE
	OKRTS

SKTMVALIDATESOCKETINVALIDSOCKET
	LDX	SKTMNRB
	ERRORRTS
	PAGE
*	Input the next 5 bytes from the NRB.  Those bytes are a socket
*	capability.  That capability is matched to the TARGETSOCKET and
*	TARGETKEY fields of all sockets referencing the current NRB, until a
*	match is found or the list of sockets is exhausted, in which
*	case return is made with carry set.  If a match is found,
*	the current NRB is hooked to the socket, the socket is locked,
*	and return is made with carry clear.

SKTMASSOCIATEVALIDATEFROMSC
	LDX	SKTMNRB
	JSR	SKTMINPUT5BYTESFROMNRB
	LDAA	#SDNETSOCKETMAX	number of sockets to examine
	STAA	SKTMMISC+1	(COMPARE5BYTES will use SKTMMISC+0)
	LDX	#SOCKETTABLE
	STX	TEMPX
SKTMASSOCVALIDATEFROMSCLOOP
	LDX	0,X
	LDX	SOCKET:NRB,X	see if this is me
	CPX	SKTMNRB
	BNE	SKTMASSOCVALIDATEFROMSOCKETINVALIDSOCKET
	LDX	TEMPX
	LDX	0,X
	LEAX	6,X	offset for TARGETSOCKET and TARGETKEY
	STX	SKTMPARM	STASH SUBJECT SOCKET ADDRESS
	JSR	COMPARE5BYTES	compare fields
	BCS	SKTMASSOCVALIDATEFROMSOCKETINVALIDSOCKET
	LDX	TEMPX
	LDX	0,X
	STX	TEMPX	SOCKET IS VALID
	LDX	SKTMNRB
	LDD	TEMPX
	STD	SDNETNRB:SOCKET,X
	LDX	TEMPX
	JSR	RESOURCEALLOCATE	LATCH ONTO SOCKET
	OKRTS

SKTMASSOCVALIDATEFROMSOCKETINVALIDSOCKET
	LDX	TEMPX
	INX
	INX
	STX	TEMPX
	DEC	SKTMMISC+1
	BNE	SKTMASSOCVALIDATEFROMSCLOOP
	LDX	SKTMNRB
	ERRORRTS
	PAGE
*	Input the next 5 bytes from the NRB.  Those bytes are a socket
*	capability.  That capability is matched to the SOCKET and
*	KEY fields of all sockets referencing the current NRB, until a
*	match is found or the list of sockets is exhausted, in which
*	case return is made with carry set.  If a match is found,
*	the current NRB is hooked to the socket, the socket is locked,
*	and return is made with carry clear.

SKTMASSOCIATEVALIDATETOSC
	LDX	SKTMNRB
	JSR	SKTMINPUT5BYTESFROMNRB
	LDAA	#SDNETSOCKETMAX	number of sockets to examine
	STAA	SKTMMISC+1	(COMPARE5BYTES will use SKTMMISC+0)
	LDX	#SOCKETTABLE
	STX	TEMPX
SKTMASSOCVALIDATETOSCLOOP
	LDX	0,X
	LDX	SOCKET:NRB,X	see if this is me
	CPX	SKTMNRB
	BNE	SKTMASSOCVALIDATETOSOCKETINVALIDSOCKET
	LDX	TEMPX
	LDX	0,X
	STX	SKTMPARM	STASH SUBJECT SOCKET ADDRESS
	JSR	COMPARE5BYTES	compare fields
	BCS	SKTMASSOCVALIDATETOSOCKETINVALIDSOCKET
	STX	TEMPX	SOCKET IS VALID
	LDX	SKTMNRB
	LDD	TEMPX
	STD	SDNETNRB:SOCKET,X
	LDX	TEMPX
	JSR	RESOURCEALLOCATE	LATCH ONTO SOCKET
	OKRTS

SKTMASSOCVALIDATETOSOCKETINVALIDSOCKET
	LDX	TEMPX
	INX
	INX
	STX	TEMPX
	DEC	SKTMMISC+1
	BNE	SKTMASSOCVALIDATETOSCLOOP
	LDX	SKTMNRB
	ERRORRTS
	PAGE
SKTMMUNCHDATA	; STUFF AS MUCH NRB INPUT AS WILL FIT INTO SOCKET RCV BUFFER
	LDAA	SOCKET:LOCALSTATUS,X	FLUSH MESSAGE IF THINGS...
	BITA	#\SOCKETSTATUS:NEEDDATA	...ARE NOT PERFECTLY NORMAL
	BNE	SKTMALLDONEMUNCHING	ASSERT: NO OTHER FLAGS EXIST
	JSR	SKTMSOCKETRCVROOM	; GET ROOM IN SOCKET RECEIVE BUFFER
	STAB	TEMPA
	ORAA	TEMPA
	BEQ	SKTMALLDONEMUNCHING	DONE IF NO MORE ROOM IN RCV BUFFER
	LDAA	SKTMPARM	(set up by SKTMINPUTXMTCOUNT)
	ORAA	SKTMPARM+1
	BEQ	SKTMALLDONEMUNCHING	DONE IF NO MORE MESSAGE
	LDX	SKTMNRB
	JSR	SDNETINPUTNRB
	DECD	SKTMPARM	GRAB BYTE AND DECREMENT REMAINING COUNT
	LDX	SDNETNRB:SOCKET,X
	STX	TEMPX
	LDX	SOCKET:RCVBUF+SDNETBUF:FILL,X	STUFF BYTE IN RCV BUFFER
	STAB	0,X
	LDX	TEMPX	ADJUST POINTER
	INC	SOCKET:RCVBUF+SDNETBUF:FILL+1,X
	INCD	SOCKET:RCVBASE,X
	INCD	SOCKET:RCVUNACKED,X
	BRA	SKTMMUNCHDATA

SKTMALLDONEMUNCHING	; DONE SHOVELING DATA FOR ONE OF TWO REASONS
	LDAA	SKTMPARM	SEE IF ANY INPUT LEFT
	ORAA	SKTMPARM+1
	BEQ	SKTMMUNCHDATANOINPUTLEFT	REASON 1: NO MORE INPUT
	LDX	SOCKET:NRB,X	WHAT'S LEFT IS REASON 2: NO MORE ROOM
	JSR	SKTMFLUSHXMTDATA	SOB..SNIFF..FLUSH REMAINING DATA
	LDX	SDNETNRB:SOCKET,X	restore socket base
	LDAA	#SOCKETSTATUS:LOSTDATA	MARK LOST DATA
	ORAA	SOCKET:LOCALSTATUS,X
	STAA	SOCKET:LOCALSTATUS,X
SKTMMUNCHDATANOINPUTLEFT	; ALL OF INPUT DATA FIT IN SOCKET BUFFER
	RTS
	PAGE
SKTMSYNCMUNCHDATA	; CHECK THAT INPUT XMT BASE MATCHES SOCKET RCV BASE (X) -> SOCKET
	LDX	SKTMNRB	get XMT base
	JSR	SKTMINPUT2BYTESFROMNRB
	LDD	SDNETNRB:WORK,X
	LDX	SDNETNRB:SOCKET,X
	CMPD	SOCKET:RCVBASE,X
	BNE	SKTMSYNCFLUSH
	JMP	SKTMMUNCHDATA	DATA IN SYNC--READ IT IN!

SKTMSYNCFLUSH
	LDX	SKTMNRB
	JSR	SKTMFLUSHXMTDATA
	LDX	SDNETNRB:SOCKET,X
	RTS
	PAGE
SKTMMUNCHFLOWDATA	; INPUT FLOW CONTROL AND PERFORM BOOKKEEPING CHORES
	LDX	SKTMNRB
	JSR	SKTMINPUT2BYTESFROMNRB	GO GET RCDOK
	LDD	SDNETNRB:WORK,X
	STD	SKTMMISC
	LDX	SDNETNRB:SOCKET,X
	LDAA	SKTMMISC+1	ASSERT: ACKED BYTES <= 255
	ADDA	SOCKET:XMTBUF+SDNETBUF:EMPTY+1,X	ADJUST EMPTY POINTER
	STAA	SOCKET:XMTBUF+SDNETBUF:EMPTY+1,X	BY AMOUNT ACKED
	LDD	SOCKET:XMTBASE,X	bump xmt base
	ADDD	SKTMMISC
	STD	SOCKET:XMTBASE,X
	LDD	SOCKET:XMTRISK,X	adjust xmt risk
	SUBD	SKTMMISC
	BCS	*	assert: acked bytes can't exceed risk!! (be graceful later)
	STD	SOCKET:XMTRISK,X
	LDAA	SOCKET:REMOTESTATUS,X
	BITA	#SOCKETSTATUS:LOSTDATA
	BEQ	SKTMMUNCHFLOWNOTLOSTDATA
	JSR	SKTMRESETXMTBASE
SKTMMUNCHFLOWNOTLOSTDATA
	LDX	SKTMNRB
	JSR	SKTMINPUT2BYTESFROMNRB	ABSORB RCD READY
	JSR	SKTMINPUTXMTCOUNT
	LDX	SDNETNRB:SOCKET,X
	JMP	SKTMSYNCMUNCHDATA	CHECK FOR VALID XMT BASE AND THEN INPUT DATA
	PAGE	SD's Line Protocol DRIVER
	LIST	LISTPACKETMANAGER
*
*	THIS IS THE PHYSICAL LINK DRIVER FOR "SDNET"
*	IT USES A SPECIALLY MODIFIED ACIA+RS232 DRIVER BOARD;
*	THE ACIA IS REPLACED WITH AND SSDA AND THE RS232 DRIVERS WITH TRISTATE BUFFERS
*	COLLISION DETECT IS PROVIDED
*	DATA TRANSFERS OCCUR AT A RATE OF 50K TO 100K BITS PER SECOND
*
	PAGE
	IFUND	SDNETTEST
SDNETTEST	EQU	0
	FIN
	IF	SDNETTEST
******* SDNET: DEVICE DRIVER ******
* DEFINITIONS FOR SDNET DCB DISPLACEMENTS
::	SET	*
	ORG	0
SDNETDONEFLAG	RMB	1
SDNETLASTERROR	RMB	2
SDNETNAME	RMB	2
SDNETNEXTDCB	RMB	2
	RMB	2
SDNETCOLCNT	RMB	1
	ORG	::
SDNETDCB	EQU	*
	FCB	1	DCB:DONEFLAG
	FDB	0	DCB:LASTERROR
	FDB	SDNETSTR	DCB:NAME
	FDB	NEXTDEVICEDCB	DCB:NEXT
	FDB	SDNETDRIVER	DCB:DRIVER

	FCB	0	COLUMN COUNT
SDNETSTR	FCC	"SDNET:"
	FCB	0

NEXTDEVICEDCB	SET	SDNETDCB
	PAGE
SDNETDRIVER	EQU	*
	FDB	SDNETOPEN	OPEN
	FDB	SDNETCLOSE
	FDB	SDNETREADA
	FDB	SDNETWRITEA	WRITEA
	FDB	SDNETREADB
	FDB	SDNETWRITEB
	FDB	SDNETCREATE
	FDB	ILLDEVICEOP	RENAME
	FDB	ILLDEVICEOP	DELETE
	FDB	ILLDEVICEOP	CONTROL
	FDB	SDNETSTATUS
	FDB	SDNETRESET
	FDB	SDNETPFRESTART

DVTYP.NETWRK	EQU	$FF

	PAGE
	IFUND	COUNT
COUNT	EQU	2
BUFFERPOINTER	EQU	4
	FIN
*
SDNETCREATE	EQU	*
SDNETOPEN	EQU	*	ASSERT: NODE IS DEAD, HERE
	CLR	SDNETDCB+DCB:LASTERROR
	CLR	SDNETDCB+DCB:LASTERROR+1
	LDX	#SDNETNRBS+1*SDNETNRB:SIZE	WHICH NRB TO INITZ
SDNETOPENNRB	; (X) SELECTS NRB TO OPEN
*	MAKE BUFFER POINTERS EQUAL
	LDAA	SDNETNRB:RCVBUF+SDNETBUF:FILL+1,X
	STAA	SDNETNRB:RCVBUF+SDNETBUF:EMPTY+1,X
	LDAA	SDNETNRB:XMTBUF+SDNETBUF:FILL+1,X
	STAA	SDNETNRB:XMTBUF+SDNETBUF:EMPTY+1,X
	LDAA	#NODESTATE:INACTIVE	REVIVE THE NODE
	STAA	SDNETNRB:STATE,X
	LDAA	#SDNETRETRYCOUNT
	STAA	SDNETNRB:RETRY,X
SDNETPFRESTART	OKRTS
*
SDNETCLOSE	EQU	*
	LDX	#SDNETNRBS+1*SDNETNRB:SIZE
	LDAA	#SDNETWAITEMPTYDEAD/256	WAIT FOR ALL DATA BYTES SENT
	LDAB	#SDNETWAITEMPTYDEAD&$FF
	JSR	SDOS+SDOS:WAITCOND
	JSR	SDNETWAITDEAD	SEE IF NRB IS DEAD
	BNE	SDNETPFRESTART	B/ HE'S DEAD, JUST LEAVE
	STX	SDNETDESIREQUITNRBPTR	KILL OFF OUR NRB
	CLR	SDNETDESIREQUITLOCK
	LDX	#SDNETINTDESIREQUIT
	JSR	SDOS+SDOS:STARTIO
	LDX	#SDNETNRBS+1*SDNETNRB:SIZE
	LDAA	#SDNETWAITDEAD/256
	LDAB	#SDNETWAITDEAD&$FF
	JSR	SDOS+SDOS:WAITCOND
	OKRTS

	PAGE
SDNETSTATUS	LDX	SDOS+SDOS:IOBLOCKPTR
	CMPA	#SC:GETEOF
	BEQ	SDNETGETEOF
	CMPA	#SC:GETCOL
	BEQ	SDNETGETCOL
	CMPA	#SC:GETTYPE
	BEQ	SDNETGETTYPE
	CMPA	#SC:GETPARAMS
	BEQ	SDNETGETPARAMS
	JMP	ILLDEVICEOP
*
SDNETGETEOF	JSR	SDOS+SDOS:CHECKRDLEN
	FDB	1
	LDX	SCBLK:RDBUF,X
	CLR	STATUS:EOFFLAG,X
	LDX	#SDNETNRBS+1*SDNETNRB:SIZE
	LDAA	SDNETNRB:RCVBUF+SDNETBUF:EMPTY+1,X
	SUBA	SDNETNRB:RCVBUF+SDNETBUF:FILL+1,X
	BNE	SDNETPFRESTART	B/ SOME STUFF REMAINS IN RCV BUFFER, NOT EOF YET
	JSR	SDNETWAITDEAD
	BEQ	SDNETOKRTS
	LDX	SDOS+SDOS:IOBLOCKPTR
	LDX	SCBLK:RDBUF,X
	INC	STATUS:EOFFLAG,X
	OKRTS
*
SDNETGETCOL	JSR	SDOS+SDOS:CHECKRDLEN
	FDB	1
	LDX	SCBLK:RDBUF,X
	LDAA	SDNETCOLCNT+SDNETDCB
	STAA	STATUS:COLUMN,X
	OKRTS
*
SDNETGETTYPE	JSR	SDOS+SDOS:CHECKRDLEN	CHECK ENOUGH ROOM FOR SDNET INFO
	FDB	1
	LDX	SCBLK:RDBUF,X	GO GET THE BUFFER POINTER
	LDAA	#DVTYP.NETWRK
	STAA	DVTYP:TYPE,X
SDNETOKRTS	OKRTS
	PAGE
SDNETGETPARAMS	EQU	*
	JSR	SDOS+SDOS:CHECKRDLEN	READ LINE PARAMETERS
	FDB	2
	LDX	SCBLK:RDBUF,X	GET POINTER TO READ-BACK BUFFER
	LDAA	#132
	LDAB	#0	; WHAT DO YOU THINK HERE?
	STAA	DVDAT:WIDTH,X
	STAB	DVDAT:DEPTH,X
	OKRTS

SDNETREADSETUP	EQU	*
	LDX	SDOS+SDOS:IOBLOCKPTR
	LDX	READB:BUFFERP,X
	STX	BUFFERPOINTER
	LDX	SDOS+SDOS:IOBLOCKPTR
	LDX	READB:MAXCOUNT,X
	STX	COUNT
	RTS

SDNETDEADCHECK
	PSHA		SAVE THE BYTE TO BE SENT
	LDX	#SDNETNRBS+1*SDNETNRB:SIZE
	JSR	SDNETWAITDEAD	SEE IF NRB IS SHOT
	PULA
	BNE	SDNETEOFERR	B/ YEP, GO DIE
	RTS

SKTMBURYNODE	INS		POP RETURN ADDRESS OFF STACK
	INS
	BSR	SDNETREADB2
	LDX	SCBLK:RPLEN,X
	BNE	SDNETOKRTS	B/ NOT END OF FILE YET
SDNETEOFERR	JSR	SDOS+SDOS:ERROR
	FDB	ERR:EOFHIT
	PAGE
SDNETREADB	EQU	*	READ BINARY
	CLR	SDNETDCB+SDNETCOLCNT
	BSR	SDNETREADSETUP
	BEQ	SDNETREADB2
SDNETREADBL	EQU	*
	LDX	#SDNETNRBS+1*SDNETNRB:SIZE
	JSR	SDNETINPUTNRB	GET A BYTE
	LDX	BUFFERPOINTER	STUFF BYTE INTO BUFFER
	STAB	0,X
	INX
	STX	BUFFERPOINTER
	LDX	COUNT	DOWN COUNT # BYTES LEFT
	DEX
	STX	COUNT
	BNE	SDNETREADBL
SDNETREADB2	EQU	*
	LDX	SDOS+SDOS:IOBLOCKPTR	COMPUTE LENGTH OF REPLY
	LDAA	BUFFERPOINTER
	LDAB	BUFFERPOINTER+1
	SUBB	READB:BUFFERP+1,X
	SBCA	READB:BUFFERP,X
	STAA	SCBLK:RPLEN,X	AND PLACE INTO SYSCALL BLOCK
	STAB	SCBLK:RPLEN+1,X
	OKRTS
	PAGE
SDNETREADA	EQU	*	READ ASCII
	BSR	SDNETREADSETUP
	BEQ	SDNETREADB2
SDNETREADA2	EQU	*	READ ASCII BYTE
	LDX	#SDNETNRBS+1*SDNETNRB:SIZE
	JSR	SDNETINPUTNRB
	INC	SDNETDCB+SDNETCOLCNT	ADJUST COLUMN COUNT
	CMPB	#ASCII:SPACE-1
	BHI	SDNETREADA4
	CLR	SDNETDCB+SDNETCOLCNT
SDNETREADA4	LDX	BUFFERPOINTER
	ANDB	#ASCII:MASK
	BEQ	SDNETREADA2	B/ NULL SUPPRESS
	STAB	0,X
	INX
	STX	BUFFERPOINTER
	LDX	COUNT
	DEX
	STX	COUNT
	BNE	SDNETREADA5
	JMP	SDNETREADB2

SDNETREADA5	EQU	*
	CMPA	#ASCII:CR
	BNE	SDNETREADA2
	LDX	SDOS+SDOS:IOBLOCKPTR
	LDAA	READA:LMFLAG,X
	BEQ	SDNETREADA2	B/ NOT LINE MODE
	JMP	SDNETREADB2
	PAGE
SDNETWRITEA	LDX	SDOS+SDOS:IOBLOCKPTR
	LDX	WRITEA:BUFFERP,X
	STX	BUFFERPOINTER
	LDX	SDOS+SDOS:IOBLOCKPTR
	LDX	WRITEA:COUNT,X
	STX	COUNT
	BEQ	SDNETWRITEA6
SDNETWRITEA1	LDX	BUFFERPOINTER
	LDAB	0,X
	INX
	STX	BUFFERPOINTER
	INC	SDNETDCB+SDNETCOLCNT
	CMPB	#ASCII:SPACE-1
	BHI	SDNETWRITEA4
	CLR	SDNETDCB+SDNETCOLCNT
SDNETWRITEA4	LDX	#SDNETNRBS+1*SDNETNRB:SIZE
	BSR	SDNETOUTPUTNRB
SDNETWRITEA5	LDX	COUNT
	DEX
	STX	COUNT
	BNE	SDNETWRITEA1
SDNETWRITEA6	JSR	SDNETDEADCHECK
	OKRTS
	PAGE
SDNETWRITEB	EQU	*
	CLR	SDNETCOLCNT+SDNETDCB
SDNETWRITE	LDX	SDOS+SDOS:IOBLOCKPTR
	LDX	WRITEA:BUFFERP,X
	STX	BUFFERPOINTER
	LDX	SDOS+SDOS:IOBLOCKPTR
	LDX	WRITEA:COUNT,X
	STX	COUNT
	BEQ	SDNETWRITEA6
SDNETWRITEBL	EQU	*
	LDX	BUFFERPOINTER
	LDAB	0,X
	INX
	STX	BUFFERPOINTER
	LDX	#SDNETNRBS+1*SDNETNRB:SIZE
	BSR	SDNETOUTPUTNRB
	LDX	COUNT
	DEX
	STX	COUNT
	BNE	SDNETWRITEBL
	JSR	SDNETDEADCHECK
	OKRTS

SDNETNRBTBLPTR	FDB	SDNETNRBTABLE

SDNETPOLL	;	POLL NRBS FOR ACTIVITY
	LDX	SDNETNRBTBLPTR	POINTER TO NEXT NRB TO TRY
	INX
	INX
	CPX	#SDNETNRBTABLE+SDNETNODEMAX*2
	BNE	SDNETPOLL1
	LDX	#SDNETNRBTABLE
SDNETPOLL1
	STX	SDNETNRBTBLPTR
	LDX	0,X	GET POINTER TO SDNETNRB
*	JMP	SDNETWAITBYTEORDEAD

	FIN	SDNETTEST
	PAGE
*
*	TASK WAKE-UP TEST ROUTINES
*
SDNETWAITBYTEORDEAD	; WAIT FOR ARRIVAL OF DATA OR DEATH OF NRB
	LDAA	SDNETNRB:RCVBUF+SDNETBUF:FILL+1,X
	SUBA	SDNETNRB:RCVBUF+SDNETBUF:EMPTY+1,X
	BEQ	SDNETWAITDEAD
	RTS

SDNETWAITEMPTYDEAD	; WAIT FOR ALL DATA BYTES SENT OR DEATH OF NRB
	LDAA	SDNETNRB:XMTBUF+SDNETBUF:EMPTY+1,X
	SUBA	SDNETNRB:XMTBUF+SDNETBUF:FILL+1,X
	TPA
	ANDA	#%100
	BEQ	SDNETWAITDEAD	B/ NOT EMPTY, GO CHECK FOR DEAD
	RTS

SDNETWAITROOMORDEAD	; WAIT FOR TRANSMIT BUFFER ROOM OR DEATH OF NRB
	LDAA	SDNETNRB:XMTBUF+SDNETBUF:EMPTY+1,X
	SUBA	SDNETNRB:XMTBUF+SDNETBUF:FILL+1,X
	DECA
	BNE	SDNETWAITRTS
SDNETWAITDEAD	; WAIT FOR NRB TO DIE
	LDAA	SDNETNRB:STATE,X
	SUBA	#NODESTATE:DEAD
	TPA
	ANDA	#%100	GIVE <>0 ANSWER IF NODE IS DEAD
SDNETWAITRTS	RTS
	PAGE
SDNETOUTPUTNRBWAIT	; NO ROOM IN TRANSMIT BUFFER, WAIT OUR TURN
	LDAA	SDNETNRB:STATE,X	IS NRB QUITE DEAD ?
	CMPA	#NODESTATE:DEAD	...?
	BEQ	SDNETWAITRTS	B/ YES, WE'RE ALL DONE!
	PSHB		SAVE OUR BYTE
	LDAA	#SDNETWAITROOMORDEAD/256
	LDAB	#SDNETWAITROOMORDEAD&$FF
	JSR	SDOS+SDOS:WAITCOND	(X) POINTS TO NRB HERE
	PULB
*
*	SDNETOUTPUTNRB -- PUT (B) INTO TRANSMIT BUFFER OF NRB (X)
*	"SKTMSOCKET" POINTS TO SOCKET WHICH SELECTS NRB
*	IF NRB IS DEAD, (B) IS PUT INTO THE BIT BUCKET
*	SDNETNRB:XBUFLK MUST BE "LOCKED" BEFORE CALLING THIS ROUTINE
*
*	PRESERVES (X)
*
SDNETOUTPUTNRB
	LDAA	SDNETNRB:XMTBUF+SDNETBUF:EMPTY+1,X
	SUBA	SDNETNRB:XMTBUF+SDNETBUF:FILL+1,X
	DECA
	BEQ	SDNETOUTPUTNRBWAIT	B/ NO ROOM AVAILABLE
	STX	TEMPX	SAVE NRB POINTER HERE, PLEEZ
	LDX	SDNETNRB:XMTBUF+SDNETBUF:FILL,X	PUT BYTE INTO TRANSMIT BUFFER
	STAB	0,X
	LDX	TEMPX	GET NRB POINTER BACK
	INC	SDNETNRB:XMTBUF+SDNETBUF:FILL+1,X	BUMP BUFFER FILL POINTER
	CPX	SDNETNRB:TIMEQFLINK,X	IS NRB ACTIVE ?
	BNE	SDNETOUTPUTNRB1	B/ YES
	LDX	#SDNETDESIREDELAYEDXMITLOCK	NRB IS IDLE --> NO DATA WAS IN BUFFER
	JSR	RESOURCEALLOCATE	I WANNA OWN "...DELAYEDXMITNRBPTR"
	LDX	TEMPX	NOW ITS MINE
	STX	SDNETDESIREDELAYEDXMITNRBPTR	CAUSE A TRANSMISSION...
	LDX	#SDNETINTDESIREDELAYEDXMIT	AFTER A SUITABLE DELAY
	BRA	SDNETOUTPUTNRBIO1	ASSERT: USES THIS PATH TO WAKE INACTIVE NRBS
	PAGE
SDNETOUTPUTNRB1	; NRB IS ACTIVE
	LDAA	SDNETNRB:STATE,X	DO WE EXPECT TO SEND NEXT ?
	CMPA	#NODESTATE:CONNECTEDS	...?
	BNE	SDNETWAITRTS	B/ NO, TIMEOUT OR RCVD MSG WILL CAUSE EVENTUAL XMIT
	LDAB	SDNETNRB:XMTBUF+SDNETBUF:FILL+1,X	YES, ITS OUR TURN TO SEND
	SUBB	SDNETNRB:XMTBUF+SDNETBUF:EMPTY+1,X	COMPUTE TOTAL TRANSMIT READY COUNT
	CLRA		(A,B):= TOTAL
	SUBB	SDNETXMITTHRESHOLDVALUE+1	> THRESHOLD ?
	SBCA	SDNETXMITTHRESHOLDVALUE
	BCS	SDNETWAITRTS	B/ NO, DON'T TRANSMIT YET
	LDAA	SDNETNRB:RCVDRDY,X	YES, DOES OTHER NODE HAVE ENOUGH ROOM ?
	LDAB	SDNETNRB:RCVDRDY+1,X	(=0 IF ACTIVATING)
	SUBB	#SDNETXMITTHRESHOLD&$FF
	SBCA	#SDNETXMITTHRESHOLD/256
	BCS	SDNETWAITRTS	B/ NO, DON'T TRANSMIT YET
SDNETOUTPUTNRBIO	; START I/O TO CAUSE TRANSMIT ON NRB(X)
	LDX	#SDNETDESIREXMITLOCK	I WANNA OWN "...DESIREXMITNRBPTR"
	JSR	RESOURCEALLOCATE
	LDX	TEMPX	WHEW! ITS A GOOD THING, THAT TEMPX
	STX	SDNETDESIREXMITNRBPTR	FORCE A XMIT
	LDX	#SDNETINTDESIREXMIT
SDNETOUTPUTNRBIO1	; START I/O AND THEN RESTORE (X)
	JSR	SDOS+SDOS:STARTIO
	LDX	TEMPX
	RTS

SDNETINPUTNRBWAIT	; NO DATA CURRENTLY AVAILABLE, WAIT FOR SOME
	LDAA	#SDNETWAITBYTEORDEAD/256
	LDAB	#SDNETWAITBYTEORDEAD&$FF
	JSR	SDOS+SDOS:WAITCOND	(X) POINTS AT NRB HERE
	LDAA	SDNETNRB:RCVBUF+SDNETBUF:FILL+1,X	SOME DATA IN THE BUFFER ?
	SUBA	SDNETNRB:RCVBUF+SDNETBUF:EMPTY+1,X	...?
	BNE	SDNETINPUTNRB1	B/ SOME DATA IS NOW AVAILABLE!
	JMP	SKTMBURYNODE	B/ NO INPUT RDY, AND WE WOKE --> MUST BE DEAD
	PAGE
*	SDNETINPUTNRB -- GET BYTE FROM NRB RECEIVE BUFFER TO (B)
*	(X) POINTS TO NRB ; PRESERVES (X)
*
SDNETINPUTNRB
	LDAA	SDNETNRB:RCVBUF+SDNETBUF:FILL+1,X	ANY DATA IN RECEIVE BUFFER ?
	SUBA	SDNETNRB:RCVBUF+SDNETBUF:EMPTY+1,X
	BEQ	SDNETINPUTNRBWAIT	B/ NO DATA AVAILABLE
SDNETINPUTNRB1
	STX	TEMPX	SAVE NRB POINTER
	LDX	SDNETNRB:RCVBUF+SDNETBUF:EMPTY,X	FETCH BYTE FROM RCV BUFFER
	LDAB	0,X
	LDX	TEMPX	GET POINTER TO NRB AGAIN
	INC	SDNETNRB:RCVBUF+SDNETBUF:EMPTY+1,X	BUMP RCV BUFFER EMPTY POINTER
	LDAA	SDNETNRB:STATE,X	ARE WE EXPECTED TO SEND A RESPONSE ?
	CMPA	#NODESTATE:CONNECTEDS	...?
	BNE	SDNETINPUTNRBRTS	B/ NO, DO NADA
	IF	SDNETXMITTHRESHOLD<255
	LDAA	SDNETNRB:RCVBUF+SDNETBUF:EMPTY+1,X	IS BUFFER SPACE ...
	SUBA	SDNETNRB:RCVBUF+SDNETBUF:FILL+1,X	...AVAILABLE CROSSING...
	CMPA	#SDNETXMITTHRESHOLD&$FF	...INTO EFFICIENT AREA?
	BNE	SDNETINPUTNRBRTS	B/ NO, DON'T SEND RESPONSE YET
	FIN
	PSHB		SAVE THE COLLECTED BYTE
	LDX	#SDNETDESIREXMITLOCK	I WANNA OWN "SDNETDESIREXMITNRBPTR"
	JSR	RESOURCEALLOCATE
	LDX	TEMPX	RCVDRDY > XMIT THRESHOLD, ...
	STX	SDNETDESIREXMITNRBPTR	FORCE TRANSMISSION
	LDX	#SDNETINTDESIREXMIT
	JSR	SDOS+SDOS:STARTIO
	LDX	TEMPX	AS PROMISED
	PULB		RETREIVE THE BYTE WE FETCHED
SDNETINPUTNRBRTS
	RTS
	PAGE
*	RESOURCEALLOCATE -- WAITS FOR OWNERSHIP OF RESOURCE (X)
*	(X) POINTS TO RESOURCE AVAILABLE COUNT
*	LIMIT OF 127 WAITING TASKS (ALL DOING DEC 0,X SIMULTANEOUSLY)

RESOURCEALLOCATEL	INC	0,X	RESTORE RESOURCE COUNT TO TRUE VALUE
	LDAA	#WAITFORRESOURCE/256	NOW WAIT FOR AVAILABLE RESOURCE
	LDAB	#WAITFORRESOURCE&$FF
	JSR	SDOS+SDOS:WAITCOND
RESOURCEALLOCATE	EQU	*
	DEC	0,X	ALLOCATE A RESOURCE UNIT
	BMI	RESOURCEALLOCATEL	B/ NONE AVAILABLE
	RTS

WAITFORRESOURCE	EQU	*	WAIT FOR RESOURCE COUNT >= 1
	LDAA	0,X
	BGT	WAITFORRESOURCE1	B/ TASK IS READY!
	CLRA		TASK IS NOT READY
WAITFORRESOURCE1	RTS
*
*	RESOURCERELEASE -- FREES RESOURCE (X)
*	(X) POINTS TO RESOURCE AVAILABLE COUNT
*
RESOURCERELEASE	EQU	*
	LDAA	0,X	IS RESOURCE COUNT = 0?
	BEQ	RESOURCERELEASE1	B/ YES, MAYBE SOMEONE IS WAITING
	INC	0,X	NO, MARK ANOTHER RESOURCE UNIT AS 'FREE'
	RTS

RESOURCERELEASE1	INC	0,X	MARK RESOURCE AS FREE
	LDAA	#READY/256	WAIT FOR A COMPLETED EVENT...
	LDAB	#READY&$FF	TO CAUSE RE-SCHEDULING
	JMP	SDOS+SDOS:WAITCOND

READY	LDAA	#1	THIS WOULD BE MORE EFFICIENT IF
	RTS		IT WAS AIMED AT "EXECUTING"
	PAGE
*
*	GENERATESOCKETKEY -- RETURNS 32 BIT RANDOM KEY IN (A,B,X)
*
GENERATESOCKETKEY
	LDX	#GENERATEDKEYBLOCK	LOCK THE KEY BLOCK SO TWO TASK DON'T SCREW IT UP
	BSR	RESOURCEALLOCATE
	ASL	4,X	SHIFT OLD KEY LEFT ONE BIT
	ROL	3,X
	ROL	2,X
	ROL	1,X
	LDAA	1,X	FETCH 1ST BYTE OF KEY
	BCC	GENERATESOCKETKEY1	B/ DON'T HAVE TO EXOR IN 32 BIT MODIFIER
	LDAB	4,X	EXOR IN 32 MODIFIER
	EORB	#$1	= [32,22,2,1]S (SEE "CODING FOR COMMUNICATION AND RANGING", PG. 80)
	STAB	4,X	= [32,10,30,31]M FOR MODULAR CONFIGURATION
	LDAB	3,X	= [31,9,29,30]M WHEN SHIFT REGISTER BITS ARE NUMBERED ORIGIN ZERO
	EORB	#(1##(9+1))/256
	STAB	3,X
	EORA	#1##(30+1-24)+1##(29+1-24)
	STAA	1,X
GENERATESOCKETKEY1
	LDAB	2,X
	PSHB		SAVE UPPER 16 BITS OF KEY
	PSHA
	LDAA	3,X
	LDAB	4,X
	PSHB
	PSHA
	BSR	RESOURCERELEASE	LET GO OF THE KEY BLOCK
	PULA
	PULB
	TSX
	LDX	0,X
	INS
	INS
	RTS

GENERATEDKEYBLOCK	; CONTAINS SEED TO GENERATE NEXT KEY WITH
	FCB	1	= LOCK
	FCB	$31,$41,$59,$82	NEED TO START WITH SOMETHING, DON'T I?
	PAGE
	IF	0
SDNETINBLKFROMNRBWAIT	; NO DATA CURRENTLY AVAILABLE, WAIT FOR SOME
	LDAA	SDNETNRB:STATE,X	ARE WE EXPECTED TO SEND A RESPONSE ?
	CMPA	#NODESTATE:CONNECTEDS	...?
	BNE	SDNETINPUTNRBRTS	B/ NO, DO NADA
** YES, CHECK: 1) WAS THERE TOO LITTLE SPACE READY TO RECEIVE BEFORE THIS?
*** 2) IS THERE NOW ENOUGH SPACE READY TO RECEIVE ?
	SUBB	#SDNETXMITTHRESHOLD&$FF	...?
	SBCA	#SDNETXMITTHRESHOLD/256
	BCC	SDNETINBLKFROMNRBX	B/ NO, DON'T SEND RESPONSE
	LDX	#SDNETDESIREXMITLOCK	I WANNA OWN "SDNETDESIREXMITNRBPTR"
	JSR	RESOURCEALLOCATE
	LDX	NRBPOINTER
	STX	SDNETDESIREXMITNRBPTR	FORCE TRANSMISSION
	LDX	#SDNETINTDESIREXMIT
	JSR	SDOS+SDOS:STARTIO
	LDAA	#SDNETWAITBYTEORDEAD/256
	LDAB	#SDNETWAITBYTEORDEAD&$FF
	JSR	SDOS+SDOS:WAITCOND	(X) POINTS AT NRB HERE
	LDAA	SDNETNRB:RCVBUF+SDNETBUF:FILL+1,X	SOME DATA IN THE BUFFER ?
	SUBA	SDNETNRB:RCVBUF+SDNETBUF:EMPTY+1,X	...?
	BNE	SDNETINBLKFROMNRBLOOP	B/ SOME DATA IS NOW AVAILABLE!
	JMP	SKTMBURYNODE	B/ NO INPUT RDY, AND WE WOKE --> MUST BE DEAD
*
*	SDNETINBLKFROMNRB -- READ BYTES FROM NRB BUFFER
*		ACCORDING TO SYSCALL BLOCK(X)
*		SYSCALL:PARAMS POINTS TO NRB
*
SDNETINBLKFROMNRB
	LDX	SYSCALLBLK:PARAMS,X	FETCH NRB SELECTOR
	STX	NRBPOINTER	= NRB TO DIDDLE
	LDAA	SYSCALLBLK:RDLEN,X	= # BYTES TO READ
	LDAB	SYSCALLBLK:RDLEN+1,X
	STAA	COUNT
	STAB	COUNT+1
	LDAA	SYSCALLBLK:RDBUF,X	= WHERE TO READ THEM TO
	LDAB	SYSCALLBLK:RDBUF+1,X
	STAA	TOPOINTER
	STAB	TOPOINTER+1
SDNETINBLKFROMNRBLOOP	; ASSUME WE ALREADY HAVE SOME BYTES
	LDAB	SDNETNRB:XMTBUF+SDNET:FILL+1,X	COMPUTE # BYTES AVAILABLE
	SUBB	SDNETNRB:RCVBUF+SDNET:EMPTY+1,X
	BEQ	SDNETINBLKFROMNRBWAIT	B/ NO DATA IS AVAILABLE
	BHI	SDNETINBLKFROMNRB0	B/ FILL > EMPTY
	LDAA	SDNETNRB:RCVBUF+SDNET:FILL+1,X	FILL <= EMPTY
	BEQ	SDNETINBLKFROMNRB0	B/ FILL IS EFFECTIVELY > EMPTY
	CLRB		COMPUTE # BYTES TO TOP OF BUFFER
	SUBB	SDNETNRB:RCVBUF+SDNET:EMPTY+1,X
SDNETINBLKFROMNRB0
	LDAA	COUNT		CAN WE PERFORM COPY IN SINGLE TRANSFER ?
	BEQ	SDNETINBLKFROMNRB3	B/ COUNT < 256
	CMPB	COUNT+1	COUNT < 256, IS COUNT <= # AVAILABLE ?
	BCC	SDNETINBLKFROMNRB2	B/ YES, GO DO SINGLE TRANSFER AND EXIT
	LDAB	COUNT+1	= # WE CAN TRANSFER THIS TIME AROUND
SDNETINBLKFROMNRB1	; MUST READ MULTIPLE CHUNKS, READ CHUNK FROM BUFFER
	PSHB		= # BYTES TO MOVE THIS TIME
	NEGB		SUBTRACT # BYTES WE'LL MOVE FROM COUNT
	LDAA	#-1	(SIGN EXTEND)
	ADDB	COUNT+1
	ADCA	COUNT
	STAA	COUNT
	STAB	COUNT+1
	LDX	SDNETNRB:RCVBUF+SDNET:EMPTY,X	= WHERE TO MOVE FROM
	PULB		= # BYTES TO MOVE
	CLRA
	JSR	BLOCKMOVE	GO MOVE SOME BYTES!
	STX	FROMPOINTER	= ADDR OF NEXT BYTE TO MOVE WHEN AVAILABLE
	LDX	NRBPOINTER
	LDAB	FROMPOINTER+1	= ADDR OF NEXT BYTE MOD 256
	STAB	SDNETNRB:RCVBUF+SDNET:EMPTY+1,X	UPDATE BUFFER EMPTY POINTER
	JMP	SDNETINBLKFROMNRBLOOP

SDNETINBLKFROMNRB2	; READ LAST CHUNK FROM BUFFER
	LDAA	COUNT	= # BYTES TO MOVE
	LDAB	COUNT+1
	LDX	SDNETNRB:RCVBUF+SDNET:EMPTY,X	= WHERE TO MOVE FROM
	JSR	BLOCKMOVE	GO MOVE SOME BYTES
	STX	FROMPTR	= ADDRESS OF NEXT BYTE TO MOVE WHEN IT IS AVAILABLE
	LDX	NRBPOINTER
	LDAB	FROMPTR+1	= ADDR OF NEXT BYTE, MOD 256
	STAB	SDNETNRB:RCVBUF+SDNET:EMPTY+1,X	UPDATE THE BUFFER EMPTY POINTER
	LDAA	SDNETNRB:STATE,X	ARE WE EXPECTED TO SEND A RESPONSE ?
	CMPA	#NODESTATE:CONNECTEDS	...?
	BNE	SDNETINBLKFROMNRBRTS	B/ NO, DO NADA
** YES, CHECK: 1) WAS THERE TOO LITTLE SPACE READY TO RECEIVE BEFORE THIS?
*** 2) IS THERE NOW ENOUGH SPACE READY TO RECEIVE ?
	SUBB	#SDNETXMITTHRESHOLD&$FF	...?
	SBCA	#SDNETXMITTHRESHOLD/256
	BCC	SDNETINBLKFROMNRBX	B/ NO, DON'T SEND RESPONSE
	LDX	#SDNETDESIREXMITLOCK	I WANNA OWN "SDNETDESIREXMITNRBPTR"
	JSR	RESOURCEALLOCATE
	LDX	NRBPOINTER
	STX	SDNETDESIREXMITNRBPTR	FORCE TRANSMISSION
	LDX	#SDNETINTDESIREXMIT
	JSR	SDOS+SDOS:STARTIO
SDNETINBLKFROMNRBRTS
	RTS
	PAGE
SDNETOUTBLKTONRBWAIT	; NO ROOM IN TRANSMIT BUFFER, WAIT OUR TURN
	LDAA	SDNETNRB:STATE,X	 IS NRB QUITE DEAD ?
	CMPA	#NODESTATE:DEAD	...?
	BEQ	SDNETOUTBLKTONRBRTS	B/ YES, WE CAN GET DONE REAL QUICK!
	LDD	#SDNETWAITROOMORDEAD
	JSR	SDOS+SDOS:WAITCOND	(X) POINTS TO NRB HERE!
	JMP	SDNETOUTBLKTONRBLOOP	GO COMPUTE FREE BUFFER SPACE WHEN WE WAKE
*
*	SDNETOUTBLKTONRB -- WRITE DATA BYTES TO NRB BUFFER
*	ACCORDING TO SYSCALL BLOCK(X)
*
SDNETOUTBLKTONRB
	LDD	SYSCALLBLK:PARAMS,X	FETCH NRB SELECTOR
	STD	NRBPOINTER = NRB TO DIDDLE
	LDD	SYSCALLBLK:WRLEN,X	# BYTES TO MOVE
	LDX	SYSCALLBLK:WRBUF,X	WHERE TO COPY FROM
	STX	FROMPOINTER
	STD	COUNT
	LDX	NRBPOINTER
SDNETOUTBLKTONRBLOOP
	LDAB	SDNETNRB:XMTBUF+SDNETBUF:EMPTY+1,X
	DECB
	CMPB	SDNETNRB:XMTBUF+SDNETBUF:FILL+1,X
	BHI	SDNETOUTBLKTONRB1	B/ EMPTY > FILL, USE AS CEILING
	CLRB		= ADDRESS OF LAST BYTE IN BUFFER
SDNETOUTBLKTONRB1	; COMPUTE SIZE OF CONTIGUOUS BUFFER CHUNK TO COPY TO
	SUBB	SDNETNRB:XMTBUF+SDNETBUF:FILL,X	= # BYTES AVAILABLE
	BEQ	SDNETOUTBLKTONRBWAIT	B/ NO ROOM, WAIT FOR SOME!
	LDAA	COUNT	SPACE AVAILABLE >= WRITE COUNT ?
	BNE	SDNETOUTBLKTONRB1A	B/ NOT A CHANCE...
	CMPB	COUNT+1	SPACE AVAILABLE >= WRITE COUNT ?
	BCC	SDNETOUTBLKTONRB2	B/ YES! GO DO AND QUIT
SDNETOUTBLKTONRB1A	; MUST WRITE MULTIPLE CHUNKS TO SATISFY REQUEST
	PSHB		= # BYTES TO WRITE TO BUFFER IN ONE CHUNK
	NEGB		= - # BYTES WE'LL WRITE THIS TIME
	LDAB	#-1	SIGN EXTEND (B)
	ADDD	COUNT	= # BYTES REMAINING AFTER WRITE CHUNK
	STD	COUNT
	LDX	SDNETNRB:XMTBUF+SDNETBUF:FILL,X	WHERE TO WRITE TO
	STX	TOPOINTER
	LDX	FROMPOINTER
	PULB		= # BYTES TO WRITE TO BUFFER IN ONE CHUNK
	CLRA
	JSR	BLOCKMOVE	GO MOVE A BLOCK!
	LDX	NRBPOINTER
	LDAB	TOPOINTER+1	REMEMBER, BUFFER IS CIRCULAR...
	STAB	SDNETNRB:XMTBUF+SDNETBUF:FILL+1,X	MAKE PTR GO MOD 256
	JMP	SDNETOUTBLKTONRBLOOP	GO COMPUTE NEXT CHUNK TO COPY

SDNETOUTBLKTONRB2	; WRITE LAST CHUNK TO BUFFER
	LDD	COUNT	= # BYTES TO MOVE
	LDX	SDNETNRB:XMTBUF+SDNETBUF:FILL,X	WHERE TO WRITE TO
	STX	TOPOINTER
	LDX	FROMPOINTER
	JSR	BLOCKMOVE	GO MOVE A BLOCK!
	LDX	NRBPOINTER
	LDAB	TOPOINTER+1	REMEMBER, BUFFER IS CIRCULAR
	STAB	SDNETNRB:XMTBUF+SDNETBUF:FILL+1,X	MAKE PTR GO MOD 256
	CPX	SDNETNRB:TIMEQFLINK,X	IS NRB ACTIVE ?
	BNE	SDNETOUTBLKTONRB3	B/ YES, GO CHECK THRESHOLD
	LDAB	SDNETNRB:XMTBUF+SDNETBUF:FILL+1,X	COMPUTE BUFFER BUSY COUNT
	SUBB	SDNETNRB:XMTBUF+SDNETBUF:EMPTY+1,X	(SHOULD BE > 0)
	CLRA		(A,B)= TOTAL
	SUBD	SDNETXMITTHRESHOLDVALUE+1	> THRESHOLD ?
	BCS	SDNETOUTBLKTONRBD	B/ NO, TRIGGER DELAYED TRANSMISSION
	LDD	SDNETNRB:RCVDRDY,X	DOES OTHER NODE HAVE ENOUGH ROOM ?
	SUBD	#SDNETXMITTHRESHHOLD
	BCC	SDNETOUTBLKTONRBSIO	B/ YES, GO START THE I/O
SDNETOUTBLKTONRBD	; NRB IS IDLE, FORCE TRANSMISSION AFTER A DELAY
	LDX	#SDNETDESIREDDELAYEDXMITLOCK	CAUSE A TRANSMISSION...
	JSR	RESOURCEALLOCATE	(I WANNA OWN THIS...)
	LDX	NRBPOINTER
	STX	SDNETDESIREDELAYEDXMITNRBPTR	(THIS IS WHO TO DELAY)
	LDX	#SDNETINTDESIREDEALYEDXMIT	AFTER DELAY TO ALLOW MORE BYTES TO CREEP IN
	JSR	SDOS+SDOS:STARTIO	ASSERT: USES THIS PATH TO WAKE INACTIVE NRBS
SDNETOUTBLKTONRB3	; NRB IS ACTIVE
	LDAA	SDNETNRB:STATE,X	DO WE EXPECT TO SEND NEXT ?
	CMPA	#NODESTATE:CONNECTEDS	...?
	BNE	SDNETOUTBLKTONRBRTS	B/ NO, TIMEOUT OR RCVD MSG WILL CAUSE EVENTUAL XMIT
	LDAB	SDNETNRB:XMTBUF+SDNETBUF:FILL+1,X	COMPUTE BUFFER BUSY COUNT
	SUBB	SDNETNRB:XMTBUF+SDNETBUF:EMPTY+1,X	(SHOULD BE > 0)
	CLRA		(A,B)= TOTAL
	SUBD	SDNETXMITTHRESHOLDVALUE+1	> THRESHOLD ?
	BCS	SDNETOUTBLKTONRBRTS	B/ NO, DON'T TRANSMIT YET
	LDD	SDNETNRB:RCVDRDY,X	DOES OTHER NODE HAVE ENOUGH ROOM ?
	SUBD	#SDNETXMITTHRESHHOLD
	BCS	SDNETOUTBLKTONRBRTS	B/ NO, DON'T TRANSMIT YET
SDNETOUTBLKTONRBSIO	; START NRB I/O, ***NOW***
	LDX	#SDNETDESIREXMITLOCK	I WANNA OWN "...DESIREXMITNRBPTR"
	JSR	RESOURCEALLOCATE
	LDX	NRBPOINTER	THIS IS PROPER NRB TO FIRE UP
	STX	SDNETDESRIEXMITNRBPTR	FORCE A TRANSMISSION
	LDX	#SDNETINTDESIREXMIT	TRIGGER THE I/O
	JMP	SDOS+SDOS:STARTIO
	BCC	SDNETOUTBLKTONRBSIO	B/ YES, GO START THE I/O
	FIN	0
	PAGE
*	SSDA DEFINITIONS
*
SDNETSYNCCODE	EQU	ASCII:SYN	ISN'T THIS THE USUAL CHOICE ?
SDNETEOTCODE	EQU	ASCII:EOT	END OF MESSAGE CHARACTER
	IF	CONRAC
SDNETSSDASTS	EQU	$FAE4	SSDA STATUS REGISTER
	FIN	CONRAC
SDNETSSDAC1	EQU	SDNETSSDASTS	SSDA C1 REGISTER ADDRESS
	IF	CONRAC
SDNETSSDAC2	EQU	SDNETSSDAC1+1	SSDA C2 REGISTER ADDRESS
	FIN	CONRAC
SDNETSSDAC3	EQU	SDNETSSDAC2	SSDA C3 REGISTER ADDRESS
SDNETSSDARCV	EQU	SDNETSSDAC2	SSDA RECEIVE FIFO REGISTER ADDRESS
SDNETSSDAXMT	EQU	SDNETSSDAC2	SSDA XMIT FIFO REGISTER ADDRESS
SDNETSSDASYNC	EQU	SDNETSSDAC2	SSDA SYNC REGISTER ADDRESS
	PAGE
*
*	SSDA BIT NAMES
*
SSDASTSIRQ	EQU	%10000000	"INTERRUPT REQUEST" BIT
SSDASTSPE	EQU	%01000000	"PARITY ERROR" BIT (NOT USED)
SSDASTSRXOV	EQU	%00100000	"RECEIVER OVERRUN" BIT
SSDASTSTUF	EQU	%00010000	"TRANSMITTER UNDERFLOW" BIT (NOT USED)
SSDASTSCTS	EQU	%00001000	"CLEAR TO SEND" (COLLISION DETECT) BIT
SSDSSTSDCD	EQU	%00000100	"DATA CARRIER DETECT" BIT (NOT USED)
SSDASTSTDRA	EQU	%00000010	"XMIT DATA REGISTER AVAILABLE" BIT
SSDASTSRDA	EQU	%00000001	"RECV DATA AVAILABLE" BIT

SSDAC1SELC2	EQU	%00000000	"SELECT C2" CODE
SSDAC1SELC3	EQU	%01000000	"SELECT C3" CODE
SSDAC1SELSYNC	EQU	%10000000	"SELECT SYNC REGISTER"
SSDAC1SELXMT	EQU	%11000000	"SELECT XMIT REGISTER"
SSDAC1RIE	EQU	%00100000	"RECEIVER INTERRUPT ENABLE" BIT
SSDAC1TIE	EQU	%00010000	"TRANMITTER INTERRUPT ENABLE" BIT
SSDAC1CLRSYNC	EQU	%00001000	"CLEAR SYNC" BIT
SSDAC1STRIP	EQU	%00000100	"STRIP SYNC" BIT
SSDAC1TXRS	EQU	%00000010	"TRANSMITTER RESET" BIT
SSDAC1RXRS	EQU	%00000001	"RECEIVER RESET" BIT

SSDAC2EIE	EQU	%10000000	"ERROR INTERRUPT ENABLE" BIT
SSDAC2TXSYNC	EQU	%01000000	"TRANSMIT SYNC ON TUF" BIT
SSDAC2WS8	EQU	%00011000	"SELECT WORD SIZE = 8 BITS, NO PARITY"
SSDAC21BYTE	EQU	%00000100	"SELECT 1 BYTE XFER MODE" BIT
SSDAC2DTR1	EQU	%00000010	"SET *DTR LOW" CODE
SSDAC2DTR0	EQU	%00000000	"SET *DTR HIGH" CODE

SSDAC3CTUF	EQU	%00001000	"CLEAR TRANSMITTER UNDERFLOW" BIT
SSDAC3CLRCTS	EQU	%00000100	"CLEAR *CTS LATCH" (RESETS COLLISION DETECTOR)
SSDAC31SYNC	EQU	%00000010	"SYNC ON SINGLE SYNC BYTE" BIT
SSDAC3EISYNC	EQU	%00000001	"SYNC ON EXTERNAL EVENT" BIT
	PAGE
SDNETNRBTABLE	RPT	SDNETNODEMAX	NODE REPRESENTATIVE POINTER TABLE
	FDB	SDNETNRBS+SDNETNRB:SIZE*(*-SDNETNRBTABLE)/2

SDNETNRBS	RPT	SDNETNRB:SIZE*SDNETNODEMAX	NODE REPRESENTATIVE BLOCKS
	FCB	0

SDNETNRBBUFFERS	ORG	*//256*256	RING BUFFERS FOR NRBS
	RMB	2*SDNETNODEMAX*256	256 BYTES PER BUFFER
SDNETSKTMBUFFERS	RMB	2*SDNETSOCKETMAX*256	256 BYTES PER BUFFER

*	SOCKET TABLE

SOCKETTABLELOCK	FCB	1	LOCK FOR ALLOCATING SOCKETS

SOCKETTABLE	RPT	SDNETSOCKETMAX
	FDB	SOCKETS+SOCKET:SIZE*(*-SOCKETTABLE)/2
SOCKETS	RPT	SOCKET:SIZE*SDNETSOCKETMAX
	FCB	0	INITIAL SOCKET STATE
	PAGE
::	SET	*
	ORG	SDNETNRBBUFFERS	RESET ROUTINES GO INTO BUFFERS
SDNETRESET	; DEVICE DRIVER LEVEL RESET ROUTINE
	JSR	SDNETSSDARESET	SO WE CAN RECEIVE MESSAGES EARLY ON
SDNETINITNRBS	; INITIALIZE NRBS, ASSUMING THEY HAVE BEEN ZEROED
	LDAA	#SDNETNODEMAX	= HOW MANY TO INITZ
	STAA	SDNETBUFCNT
	LDX	#SDNETNRBS	FIRST ONE TO INITIALIZE
	STX	SDNETNRBPTR
	LDX	#SDNETNRBBUFFERS	= ADDRESS OF FIRST AVAILABLE RING BUFFER
	STX	SDNETBUFPTR
SDNETINITNRBSL	; INITZ AN NRB
	JSR	SDNETLINKNRBTOSELF	LEAVING EVERYONE OUT OF THE TIMEQ INITIALLY
	LDX	SDNETNRBPTR	MAKE NRB POINT TO REST OF QUEUE
	JSR	SKTMRESETNRB
	LDAA	SDNETBUFPTR	SET UP XMIT BUFFER POINTERS
	LDAB	SDNETBUFPTR+1
	STAA	SDNETNRB:XMTBUF+SDNETBUF:FILL,X
	STAB	SDNETNRB:XMTBUF+SDNETBUF:FILL+1,X
	STAA	SDNETNRB:XMTBUF+SDNETBUF:EMPTY,X
	STAB	SDNETNRB:XMTBUF+SDNETBUF:EMPTY+1,X
	INC	SDNETBUFPTR	FIND NEXT RING BUFFER ADDRESS
	LDAA	SDNETBUFPTR
	STAA	SDNETNRB:RCVBUF+SDNETBUF:FILL,X
	STAB	SDNETNRB:RCVBUF+SDNETBUF:FILL+1,X
	STAA	SDNETNRB:RCVBUF+SDNETBUF:EMPTY,X
	STAB	SDNETNRB:RCVBUF+SDNETBUF:EMPTY+1,X
	INC	SDNETBUFPTR	FIND NEXT RING BUFFER ADDRESS FOR NEXT ROUND
	LDAA	#SDNETNODEMAX	STORE NODE NUMBER IN THIS NRB
	SUBA	SDNETBUFCNT
	STAA	SDNETNRB:NODE,X
	LDAA	#NODESTATE:DEAD	MARK NODE AS DEAD, INITIALLY
	STAA	SDNETNRB:STATE,X
	LDAA	#NODEEPITAPH:WASCRAZY	default epitaph: He t'were crazed
	STAA	SDNETNRB:EPITAPH,X
	LDAA	SDNETNRBPTR	INITIALIZE SOCKET MANAGER LISTENER TASK TCBS
	LDAB	SDNETNRBPTR+1	COMPUTE INITIAL STACK POINTER FOR EACH TASK
	ADDB	#(SDNETNRB:STACKBASE-CONTEXTBLOCK:SIZE)&$FF	LEAVE SPACE FOR CONTEXT BLOCK
	ADCA	#(SDNETNRB:STACKBASE-CONTEXTBLOCK:SIZE)/256
	STAA	SDNETNRB:TCB+TCB:STACK,X
	STAB	SDNETNRB:TCB+TCB:STACK+1,X
	LDX	SDNETNRB:TCB+TCB:STACK,X	SET PC, X AND CC REGS OF TASK
	LDAA	#SKTMLISTENER/256
	LDAB	#SKTMLISTENER&$FF
	STAA	REG:PC,X
	STAB	REG:PC+1,X
	LDAA	SDNETNRBPTR	(X) OF TCB MUST POINT TO NRB
	LDAB	SDNETNRBPTR+1
	STAA	REG:X,X
	STAB	REG:X+1,X
	CLR	REG:CC,X	ENABLE "I" BIT IN CC REGISTER OF TASK'S CONTEXT
	ADDB	#(SDNETNRB:SIZE+SDNETNRB:TCB)&$FF	FIND TCB ADDRESS IN NEXT NRB
	ADCA	#(SDNETNRB:SIZE+SDNETNRB:TCB)/256
	LDX	SDNETNRBPTR	MAKE THIS NRB'S TCB POINT TO THE NEXT
	STAA	SDNETNRB:TCB+TCB:LNK,X
	STAB	SDNETNRB:TCB+TCB:LNK+1,X
	SUBB	#SDNETNRB:TCB&$FF	FIND ADDRESS OF NEXT NRB
	SBCA	#SDNETNRB:TCB/256
	STAA	SDNETNRBPTR
	STAB	SDNETNRBPTR+1
	DEC	SDNETBUFCNT	MORE TO INITZ ?
	BNE	SDNETINITNRBSL	B/ YES
SKTMINITIALIZESOCKETS
	LDX	SKTMSOCKETPILE	STUFF BUFFER POINTERS INTO EACH SOCKET
	LDAB	SKTMBUFFERPILE
	STAB	SOCKET:XMTBUF+SDNETBUF:FILL,X
	STAB	SOCKET:XMTBUF+SDNETBUF:EMPTY,X
	STAB	SOCKET:PROVXMTEMPTY,X
	INC	SKTMBUFFERPILE
	LDAB	SKTMBUFFERPILE
	STAB	SOCKET:RCVBUF+SDNETBUF:FILL,X
	STAB	SOCKET:RCVBUF+SDNETBUF:EMPTY,X
	INC	SKTMBUFFERPILE
	LDAA	SKTMSOCKETPILE	INITIALIZE SENDER TASK TCB
	LDAB	SKTMSOCKETPILE+1
	ADDB	#(SOCKET:STACKBASE-CONTEXTBLOCK:SIZE)&$FF	SET UP STACK
	ADCA	#(SOCKET:STACKBASE-CONTEXTBLOCK:SIZE)/256
	LDX	SKTMSOCKETPILE
	STAA	SOCKET:TCB+TCB:STACK,X
	STAB	SOCKET:TCB+TCB:STACK+1,X
	LDX	SOCKET:TCB+TCB:STACK,X	SET UP INITIAL CONTEXT
	LDAA	#SKTMSENDER/256	INITIALIZE PC
	LDAB	#SKTMSENDER&$FF
	STAA	REG:PC,X
	STAB	REG:PC+1,X
	CLR	REG:CC,X	ALLOW INTERRUPTS
	LDAA	SKTMSOCKETPILE	(X) -> SOCKET
	LDAB	SKTMSOCKETPILE+1
	STAA	REG:X,X
	STAB	REG:X+1,X
	ADDB	#(SOCKET:SIZE+SOCKET:TCB)&$FF	POINT TCB AT NEXT SOCKET'S TCB
	ADCA	#(SOCKET:SIZE+SOCKET:TCB)/256
	LDX	SKTMSOCKETPILE
	STAA	SOCKET:TCB+TCB:LNK,X
	STAB	SOCKET:TCB+TCB:LNK+1,X
	SUBB	#SOCKET:TCB&$FF	POINT AT NEXT SOCKET
	SBCA	#SOCKET:TCB/256
	STAA	SKTMSOCKETPILE
	STAB	SKTMSOCKETPILE+1
	DEC	SKTMSOCKETCOUNT
	BNE	SKTMINITIALIZESOCKETS
	LDX	SDOS+SDOS:CONFIGURATION
	LDAA	CNFG:TASKQUEUE,X	MAKE TCB IN LAST SOCKET POINT...
	LDAB	CNFG:TASKQUEUE+1,X	...TO TASKQUEUE
	LDX	SOCKETTABLE+(SDNETSOCKETMAX-1)*2	FIND LAST SOCKET
	STAA	SOCKET:TCB+TCB:LNK,X
	STAB	SOCKET:TCB+TCB:LNK+1,X
	LDAA	#(SOCKETS+SOCKET:TCB)/256	MAKE TCB IN LAST NRB POINT...
	LDAB	#(SOCKETS+SOCKET:TCB)&$FF	...TO TCB IN FIRST SOCKET
	LDX	SDNETNRBTABLE+(SDNETNODEMAX-1)*2	FIND LAST NRB
	STAA	SDNETNRB:TCB+TCB:LNK,X
	STAB	SDNETNRB:TCB+TCB:LNK+1,X
	LDX	SDOS+SDOS:CONFIGURATION
	LDAA	#(SDNETNRBS+SDNETNRB:TCB)/256	MAKE CONFIGURATION TASKQ...
	LDAB	#(SDNETNRBS+SDNETNRB:TCB)&$FF
	STAA	CNFG:TASKQUEUE,X	...POINT TO TCB IN FIRST NRB
	STAB	CNFG:TASKQUEUE+1,X
	OKRTS

SKTMSOCKETCOUNT	FCB	SDNETSOCKETMAX
SKTMSOCKETPILE	FDB	SOCKETS
SKTMBUFFERPILE	FCB	SDNETSKTMBUFFERS/256
	IF	::>>*
	ORG	::
	FIN
	PAGE
SDNETINTSERVICE	EQU	*
	IF	WAVEMATE
	INC	SDOS+SDOS:STACKSWITCHED	COUNT 1 INTERRUPT LEVEL
	BNE	SDNETINTSERVICE1	B/ INTERRUPTED INTERRUPT ROUTINE
	LDX	SDOS+SDOS:CURRENTASK	SAVE STACK POINTER
	STS	TCB:STACK,X
	LDS	#INTERRUPTSTACKEND-1
SDNETINTSERVICE1	EQU	*
	FIN	WAVEMATE
	LDAA	SDNETSSDASTS	WAS IT AN SDNET INTERRUPT?
	BPL	SDNETINTSERVICE1A
	LDX	SDNETINTNEXT	GO TO SPECIFIED NEXT INTERRUPT ADDRESS
	JMP	0,X
SDNETINTSERVICE1A	JMP	SDOS+SDOS:RTI	FORGET THIS HAPPENED (GLITCH??)
*
	PAGE
*
*	TRANSMIT USING SDNET OVER SSDA-BASED "SDNET" PARTY LINE
*	SDNETNRB:NODE = TARGET COMPUTER
*	SDNETNRB:XVPOS = INDEX INTO VIRTUAL BYTE STREAM BEING SENT
*	SDNETNRB:RVPOS = INDEX IN VIRTUAL BYTE STREAM RECEIVED SO FAR
*
*	SDNETXINTCRCSEND -- PERFORM CRC ON (A) AND SEND VIA SSDA
*	CRC ROUTINE CODED INLINE FOR SHEER SPEED
*	POLYNOMIAL USED IS X^16+^12+X^5+X^0
*	USES TABLE DRIVEN VERSION TO PERFORM CRC COMPUTATION ON BYTE IN 1 STEP
*	SEE "SDNETCRCTABLE" FOR DETAILS
*
SDNETXINTCRCSEND	EQU	*	(A) = CHAR TO SEND
	LDAB	SDNETCOMPUTEDCRC	PICK UP CRC COMPUTED SO FAR
	ASLB
	BCS	SDNETXINTCRCSEND1	B/ USE SECOND HALF OF TABLE
	STAB	SDNETCRCTABLEADD1+1
	LDAB	SDNETCOMPUTEDCRC+1
	LDX	SDNETCRCTABLEADD1
	EORB	SDNETCRCTABLE&$FF,X
	EORA	(SDNETCRCTABLE&$FF)+1,X	SDNETCRCTABLE MUST BE ON EVEN BYTE BOUNDARY
	STAB	SDNETCOMPUTEDCRC	STORE THE UPDATED CRC
	STAA	SDNETCOMPUTEDCRC+1
	EORA	(SDNETCRCTABLE&$FF)+1,X	RESTORE ORIGINAL (A)
	LDAB	#SSDASTSTDRA	GET MASK FOR SSDA TRANSMIT DATA AVAILABLE
	CMPB	SDNETSSDASTS	IS FIFO SLOT AVAILABLE YET ?
	BNE	SDNETXINTCRCSEND2	B/ NO, GO WAIT FOR SLOT AVAILABLE
SDNETXINTCRCSENDSTAA
	STAA	SDNETSSDAXMT	YES, PLACE (A) INTO SSDA XMIT FIFO
	RTS		AND RETURN

SDNETXINTCRCPOPSTAA
	PULA		RESTORE THE DATA BYTE TO SEND
	STAA	SDNETSSDAXMT	GIVE IT TO THE SSDA FOR TRANSMISSION
	RTS		AND RETURN
	PAGE
SDNETXINTCRCSEND1
	STAB	SDNETCRCTABLEADD2+1
	LDAB	SDNETCOMPUTEDCRC+1
	LDX	SDNETCRCTABLEADD2
	EORB	SDNETCRCTABLE&$FF,X
	EORA	(SDNETCRCTABLE&$FF)+1,X
	STAB	SDNETCOMPUTEDCRC	SAVE UPDATED CRC
	STAA	SDNETCOMPUTEDCRC+1
	EORA	(SDNETCRCTABLE&$FF)+1,X	RESTORE (A) TO ORIGINAL VALUE
SDNETXINTWAITSEND		; WAIT FOR FREE FIFO SLOT AND THEN SEND (A)
	LDAB	#SSDASTSTDRA	WATCH FOR SSDA TRANSMIT DATA AVAILABLE
SDNETXINTCRCSEND2
***	NOW WATCH FOR TDRA UNTIL 250US. HAVE PASSED
	CMPB	SDNETSSDASTS	QUICK CHECK FOR READY IN CASE SSDA HAS FIFO ROOM
	BEQ	SDNETXINTCRCSENDSTAA	B/ FIFO HAS ROOM, GET OUT QUICK!
	PSHA		FIFO IS FULL, WE'LL HAVE TO WAIT A BIT
	LDAA	#(250*2-73)/14	MAX # TIMES AROUND WAIT LOOP
*	CYCLES TO HERE FROM SDNETXINTCRCSEND = 73
SDNETXINTCRCSEND3	; WAIT FOR FIFO SPACE LOOP
	CMPB	SDNETSSDASTS	FIFO ROOM SHOW UP YET ?
	BEQ	SDNETXINTCRCPOPSTAA	B/ YES, GO SEND THE DATA BYTE
	DECA		NO, DOWN COUNT DELAY
	BNE	SDNETXINTCRCSEND3	B/ WE'RE WILLING TO WAIT A LITTLE LONGER
****	SSDA XMIT TIMED OUT, MUST BE COLLISION (*CTS INHIBIT) !
	LDAA	SDNETSSDASTS	CHECK FOR *CTS KILLING THE TRANSMISSION
	BITA	#SSDASTSCTS	...?
	BEQ	*	B/ DRIVER FAILURE!
	INS		TIMED OUT, THROW DATA BYTE AWAY
	INS		ALSO FORGET ABOUT THE RETURN ADDRESS
	INS
	JSR	SDNETSSDARESET	SET SSDA INTO RECEIVE MODE
	LDX	SDNETCOLLISIONCOUNT	BUMP # COLLISIONS ENCOUNTERED
	INX
	STX	SDNETCOLLISIONCOUNT
	SEC		XMIT CONFLICT, COMPUTE NEW DELAY
	ROL	SDNETXDELAYMASK+1
	ROL	SDNETXDELAYMASK
	LDAA	SDOS+SDOS:CLOCK+1
	LDAB	SDOS+SDOS:CLOCK+2
	ORAB	#1	TO PREVENT DELAY FROM BEING ZERO, THEREBY CAUSING TIMEOUT
****** NOTE: ABOVE LINE IS INAPPROPRIATE, AND IS ONLY MEANT AS A TEMP PATCH
	ANDA	SDNETXDELAYMASK
	ANDB	SDNETXDELAYMASK+1
	BEQ	SDNETXINT0J	B/ RANDOM DELAY = 0, GO TRY TO TRANSMIT
	STAB	SDNETTIMEOUTFUSE	SET FUSE FOR COLLISION RESOLUTION
	LDX	#SDNETXINT0	SET UP TO RETRANSMIT IF BUS IS QUIET AFTER DELAY
	STX	SDNETTIMEOUTROUTINE
	BRA	SDNETINTDONE1	NOW LEAVE!

SDNETXINTMISSEDEOT	; TIMEOUT ON EOT SEARCH OCCURRED
	LDX	SDNETMISSEDEOTCOUNT	BUMP GOOF COUNT
	INX
	STX	SDNETMISSEDEOTCOUNT
SDNETXINT0J
	JMP	SDNETXINT0	GO TRY TO TRANSMIT AGAIN
	PAGE
SDNET60HZ	; GET HERE VIA INTERRUPT EVERY 1/60TH SECOND
	INC	CLOCKTICKS	= TIME THAT HAS PASSED
	LDAA	SDNETTIMEQHEAD+SDNETNRB:TIMEQDELTA	IS TIMEQ DELAY NON-ZERO?
	BEQ	SDNET60HZ1	B/ =0, NRB IS TRYING TO SEND
	DEC	SDNETTIMEQHEAD+SDNETNRB:TIMEQDELTA	YES, DOWN COUNT IT
	BEQ	SDNET60HZ2	B/ WENT TO ZERO, TIME TO TRY TO SEND
	BRA	SDNETINTDONE1	NOT ZERO, JUST GET OUT QUICK!

SDNET60HZ1	; NRB AT HEAD OF TIME Q IS TRYING TO SEND
	LDAA	SDNETTIMEOUTFUSE	COLLISION RESOLUTION OR EOT SEARCH ACTIVE ?
	BEQ	SDNETINTDONE1	B/ NO, JUST LEAVE
	DEC	SDNETTIMEOUTFUSE	YES, DOWN COUNT DELAY
	BNE	SDNETINTDONE1	B/ STILL NON-ZERO, JUST LEAVE
	LDX	SDNETTIMEQHEAD+SDNETNRB:TIMEQFLINK	WENT TO ZERO
	CPX	#SDNETTIMEQHEAD	IS TIMEQ EMPTY ?
	BEQ	SDNETINTDONE1	B/ YES, JUST LEAVE
	STX	SDNETNRBPTR	NO, SET NRB TO FIRST ENTRY IN TIMEQ
	LDX	SDNETTIMEOUTROUTINE	AND GO TO ERROR RECOVERY ROUTINE
	JMP	0,X

SDNETSIGNALSCHED	; TELL THE SCHEDULER THAT SOMETHING HAPPENED
	CLR	SURPRISE	OR SOME OTHER NONSENSE TO GET ITS ATTENTION
SDNETINTDONE	; COMMON EXIT POINT FOR SDNET INTERRUPT ROUTINES
	LDAA	SDNETTIMEQHEAD+SDNETNRB:TIMEQDELTA	IS THE NRB ACTIVE ?
	ORAA	SDNETTIMEOUTFUSE	AND ARE NO OTHER DELAYS PENDING ?
	BEQ	SDNET60HZ2	B/ YES
SDNETINTDONE1	; NO NET TRANSMISSIONS ARE REQUIRED RIGHT NOW
	LDAA	CLOCKTICKS	ANY TIME PASS ?
	BNE	SDNETINTDONE2	B/ YEP.
SDNETINTRTI
	JMP	SDOS+SDOS:RTI	NO, JUST GO 'WAY (WHEW!)

SDNETINTDONE2	; CLOCK HAS TICKED, TELL SDOS
	CLR	CLOCKTICKS	SO WE DON'T DO THIS AGAIN UNTIL NEEDED
	JMP	SDOS+SDOS:CLOCKTICKED
	PAGE
SDNET60HZ2	; NRB AT HEAD OF TIMEQ NEEDS TO SEND SOMETHING
	LDX	SDNETTIMEQHEAD+SDNETNRB:TIMEQFLINK	IS TIMEQ EMPTY ?
	CPX	#SDNETTIMEQHEAD	...?
	BEQ	SDNETINTDONE1	B/ YES, NOTHING TO DO!
	STX	SDNETNRBPTR	NO, USE FIRST NRB IN TIMEQ
SDNETXINTT	; TIMEOUT OCCURRED AFTER TRANSMISSION
	LDAA	SDNETNRB:STATE,X	IS A TIMEOUT EXPECTED HERE ?
	CMPA	#NODESTATE:CONNECTEDS	B/ YES
	BEQ	SDNETXINT0	B/ YES, JUST GO TRANSMIT
	LDX	SDNETTIMEDOUTCOUNT	NO, COUNT THE TIMEOUT AS AN ERROR
	INX
	STX	SDNETTIMEDOUTCOUNT
	BRA	SDNETXINT0	GO TRY TO TRANSMIT AGAIN

SDNETINTDESIREXMIT	; START I/O TO HERE TO FORCE TRANSMISSION OF A MESSAGE
	LDX	SDNETDESIREXMITNRBPTR	GET NRB FOR XMIT
	INC	SDNETDESIREXMITLOCK	AND RELEASE THE LOCK
	STX	SDNETNRBPTR
	LDAA	SDNETNRB:STATE,X	IS NRB STILL CONNECTED ?
	CMPA	#NODESTATE:CONNECTEDS	(IN FACT, ARE WE SPOS'D TO SEND NEXT ?)
	BEQ	SDNETINTDESIREXMIT0	B/ YES
	CMPA	#NODESTATE:CONNECTEDR	DO WE EXPECT TO RECEIVE NEXT ?
	BNE	SDNETINTRTI	B/ NO, LEAVE NRB ALONE
	CPX	SDNETNRB:TIMEQFLINK,X	IS NRB IDLE ?
	BNE	SDNETINTRTI	B/ NO, WAIT FOR OTHER NODE'S RESPONSE
	BRA	SDNETINTDESIREXMIT1	YES, CAUSE TRANSMISSION

SDNETINTDESIRERCV	; START I/O TO HERE IF DATA IS NEEDED BY A SOCKET
	LDX	SDNETDESIRERCVNRBPTR	THIS IS THE NODE THAT NEEDS A TWEAK
	INC	SDNETDESIRERCVLOCK	RELEASE THE LOCK
	STX	SDNETNRBPTR	IN CASE WE DECIDE TO DO SOMETHING
	LDAB	SDNETNRB:XMTBUF+SDNETBUF:FILL+1,X	ANY DATA TO SEND ?
	SUBB	SDNETNRB:XMTBUF+SDNETBUF:EMPTY+1,X	(COMPUTE XMTRDY)
	BEQ	SDNETINTRTI	B/ NO, LEAVE NODE ALONE
	LDAA	SDNETNRB:STATE,X	ARE WE SPOS'D TO SEND NEXT ?
	CMPA	#NODESTATE:CONNECTEDS	...?
	BNE	SDNETINTRTI	B/ NO, LEAVE NODE ALONE
SDNETINTDESIREXMIT0	; WE ARE SPOS'D TO SEND NEXT
	JSR	SDNETREMOVENRBFROMTIMEQ	SO WE DON'T INSERT IT TWICE
SDNETINTDESIREXMIT1
	CLRB		SET TIME DELAY = 0
	JSR	SDNETPUTNRBINTIMEQ	ADD THIS NRB TO TIMEQ
	LDAA	SDNETTIMEOUTFUSE	COLLISION RESOLUTION OR EOT SEARCH ACTIVE ?
	BNE	SDNETINTDONE	B/ YES, JUST LEAVE
	BRA	SDNETXINT0	NO, GO TRY TO TRANSMIT

SDNETRINTNORESPONSE
	LDAA	#NODEEPITAPH:REMOTENORESPONSE	epitaph: he OD'ed on sleep!
	STAA	SDNETNRB:EPITAPH,X
	JMP	SDNETRINTDEAD	SIGH... BACK TO LISTENING, FOLKS...
	PAGE
*	SDNET MESSAGE TRANSMIT ROUTINE
*	ASSERT: NRB(SDNETNRBPTR) IS AT FRONT OF TIMEQ
*
SDNETINTEOT		; DOUBLE EOT SYNC SEEN
	LDAA	SDNETSSDARCV	GET 3RD BYTE
	CMPA	#SDNETEOTCODE	IS IT ALSO AN EOT?
	BNE	SDNETXINT0A	B/ NO, CAN'T BE END OF MESSAGE!
SDNETXINT0	EQU	*	FALL INTO SYNC TO SEE IF BUS IS BUSY
*	ASSERT: SSDA WAS SET UP TO RECEIVE WHEN WE ARRIVED HERE!
	CLR	SDNETTIMEOUTFUSE	DISABLE TRANSMISSION DELAY
	LDX	SDNETNRBPTR
	DEC	SDNETNRB:RETRY,X	DOWN COUNT #TRIES WITH NO RESPONSE
	BEQ	SDNETRINTNORESPONSE
	LDAA	#SSDAC1TXRS!SSDAC1RXRS	RESET RECEIVER TO GET CLEAN SLATE
	STAA	SDNETSSDAC1
	LDAA	#SSDAC1SELC3!SSDAC1TXRS	HOLD THE TRANSMITTER RESET
	STAA	SDNETSSDAC1	BUT LET THE RECEIVER LOOSE
	LDAA	#SSDAC3CTUF!SSDAC3CLRCTS!SSDAC3EISYNC	ACKNOWLEDGE *CTS
	STAA	SDNETSSDAC3
	LDX	#(250*2)/8	= # CYCLES FOR 2MHZ COMPUTER TO WAIT 250US.
	DEX		WAIT FOR 2 BYTE TIMES
	BNE	*-1
	LDAA	SDNETSSDASTS	ANY TRAFFIC IN THE ETHER ?
	RORA		(ARE WE RECEIVING DATA ?)
	BCC	SDNETXINT0B	B/ NO, GRAB THE ETHER!!
	PAGE
SDNETXINT0A	EQU	*	YES, DELAY UNTIL TRAFFIC IS GONE
	LDX	SDNETETHERBUSYCOUNT	COUNT # TIMES THIS HAPPENS
	INX
	STX	SDNETETHERBUSYCOUNT
	LDAA	#SSDAC1SELC3!SSDAC1TXRS!SSDAC1RXRS
	STAA	SDNETSSDAC1	SWITCH BACK TO INTERNAL SYNC MODE
	LDAA	#SSDAC3CTUF!SSDAC3CLRCTS	USE 2SYNC MODE
	STAA	SDNETSSDAC3
	LDAA	#SSDAC1SELSYNC!SSDAC1CLRSYNC!SSDAC1TXRS!SSDAC1RXRS
	STAA	SDNETSSDAC1	SEL SYNC REG, DROP OUT OF SYNC
	LDAA	#SDNETEOTCODE
	STAA	SDNETSSDASYNC	SELECT THE SYNC CHARACTER TO WATCH FOR END OF MESSAGE
	LDAA	#SSDAC1RIE!SSDAC1TXRS
	STAA	SDNETSSDAC1	ENABLE RECEIVER TO INTERRUPT US WHEN 2 EOTS FOUND
	LDX	#SDNETINTEOT	WHERE TO GO WHEN 2 EOT SYNC CODES ARE HEARD
	STX	SDNETINTNEXT
	LDAA	#SDNETEOTFUSETIME	ALSO, MAKE SURE WE DON'T WAIT FOREVER
	STAA	SDNETTIMEOUTFUSE
	LDX	#SDNETXINTMISSEDEOT	WHERE TO GO WHEN TIMEOUT OCCURS
	STX	SDNETTIMEOUTROUTINE
	JMP	SDNETINTDONE1
	PAGE
SDNETXINT0B	EQU	*	ETHER APPEARS TO BE FREE
	LDAA	#SSDAC1SELXMT!SSDAC1TXRS!SSDAC1RXRS
	STAA	SDNETSSDAC1	SET UP TO PRE-LOAD XMIT FIFO
	LDAA	#$FF	PRELOAD FIFO WITH DUMMY BYTE...
	STAA	SDNETSSDAXMT	TO ALLOW CLOCK TO SETTLE WHEN FIRST GATED ONTO BUS
	LDAA	#SSDAC1SELC2!SSDAC1RXRS	ENABLE TRANSMIT
	STAA	SDNETSSDAC1	THIS ENABLES DUMMY BYTE TO START SHIFTING OUT
	LDAA	#SSDAC2TXSYNC!SSDAC2WS8!SSDAC21BYTE!SSDAC2DTR1
	STAA	SDNETSSDAC2	THIS GATES CLOCK AND DATA ONTO SDNET BUS
	LDAA	#SSDAC1SELC3!SSDAC1RXRS
	STAA	SDNETSSDAC1	NOW CLEAR CTS LATCH SO WE CAN HEAR COLLISIONS
	LDAA	#SSDAC3CTUF!SSDAC3CLRCTS	(OPERATE IN 2 SYNC MODE)
	STAA	SDNETSSDAC3	OPERATE IN TXSYNC MODE SO TUF TELLS US WE GOOFED!
	LDAA	#SSDAC1SELXMT!SSDAC1RXRS	SET UP TO STORE TO XMIT FIFO
	STAA	SDNETSSDAC1
	LDAA	#SDNETSYNCCODE	SEND "SYNC,SYNC,*SYNC"
	STAA	SDNETSSDAXMT	NOTE: SSDA CANNOT SEND EXTRA "SYNC" HERE!
	STAA	SDNETSSDAXMT
	COMA		(MAKE *SYNC)
	STAA	SDNETSSDAXMT	NOW FIFO SHOULD BE (VIRTUALLY) FULL
	LDX	#$FFFF	"ZERO" THE CRC BEFORE WE START
	STX	SDNETCOMPUTEDCRC
	LDX	SDNETNRBPTR	GET POINTER TO NODE REP. BLOCK
	LDAA	SDNETNRB:NODE,X	GRAB "TO" BYTE
	JSR	SDNETXINTCRCSEND	SEND THE "TO" BYTE
	LDAA	SDNETCPUIDCELL	SEND THE "ORIGINATOR" BYTE
	JSR	SDNETXINTCRCSEND
	LDX	SDNETNRBPTR	SEND THE CONTROL FIELD
	LDAA	SDNETNRB:NODE,X	IS THIS A BROADCAST ?
	BNE	SDNETXINT0F	B/ NO
	LDAA	#SDLPCONTROL:RESYNCHQUIT	YES, USE THIS CONTROL FIELD
	BRA	SDNETXINT0D

SDNETXINT0F	; NOT A BROADCAST, DECIDE WHAT TO SEND FOR CONTROL FIELD
	LDAA	SDNETNRB:STATE,X
	STAA	SDNETSTATECONTROL1+1	LOOK CONTROL FIELD UP...
	LDX	SDNETSTATECONTROL1	IN A "STATE-TO-CONTROL FIELD" XLATE TABLE
	LDAA	SDNETSTATECONTROL&$FF,X
SDNETXINT0D	; SEND (A) AS CONTROL FIELD
	JSR	SDNETXINTCRCSEND
	LDX	SDNETNRBPTR
	LDAA	SDNETNRB:RVPOS,X	SEND THE RECEIVED COUNT MOD 2**16
	JSR	SDNETXINTCRCSEND
	LDX	SDNETNRBPTR
	LDAA	SDNETNRB:RVPOS+1,X
	JSR	SDNETXINTCRCSEND
	LDX	SDNETNRBPTR	COMPUTE READY TO RECEIVE COUNT
	CLRA		= 255-(FILL-EMPTY)
	LDAB	SDNETNRB:RCVBUF+SDNETBUF:EMPTY+1,X
	SUBB	SDNETNRB:RCVBUF+SDNETBUF:FILL+1,X
	DECB		THIS MAGIC INCANTATION WORKS MODULO 256
	STAA	SDNETFREEBYTES
	STAB	SDNETFREEBYTES+1
	JSR	SDNETXINTCRCSEND	SEND READY TO RECEIVE COUNT
	LDAA	SDNETFREEBYTES+1
	JSR	SDNETXINTCRCSEND
	LDX	SDNETNRBPTR	GRAB TOTAL READY TO SEND COUNT
	LDAA	SDNETNRB:STATE,X	IS THIS A "RESYNCH" MESSAGE ?
	CMPA	#NODESTATE:ACTIVATING	...
	BNE	SDNETXINT0E	B/ NO
	LDX	#0	YES, TRANSMIT NO DATA BYTES
	BRA	SDNETXINT0G

SDNETXINT0E	; NOT A "RESYNCH" MESSAGE
	CLRA		= (FILL-EMPTY) MOD 256
	LDAB	SDNETNRB:XMTBUF+SDNETBUF:FILL+1,X
	SUBB	SDNETNRB:XMTBUF+SDNETBUF:EMPTY+1,X
	STAA	SDNETXCOUNT	USE THIS AS THE DEFAULT TRANSMIT COUNT
	STAB	SDNETXCOUNT+1
	SEC		MAKE RCVDRDY LOOK 1 LARGER THAN IT REALLY IS!
	SBCB	SDNETNRB:RCVDRDY+1,X	ASSUME WE'LL SEND WHOLE BUFFER FULL
	SBCA	SDNETNRB:RCVDRDY,X	IS TARGET NODE PREPARED FOR THIS MANY ?
	BCS	SDNETXINT0C	B/ YEP.
	LDX	SDNETNRB:RCVDRDY,X	NO, USE TARGET NODE'S COUNT AS LIMIT
	INX		FUDGE BY ONE, SO TARGET NODE WILL KNOW WE HAVE DATA
*			EVEN IF HE HAS ZERO ROOM
SDNETXINT0G	; (X) HAS NUMBER OF BYTES TO SEND
	STX	SDNETXCOUNT	SAVE TRANSMIT COUNT LIMIT
SDNETXINT0C	EQU	*
	LDAA	SDNETXCOUNT	SEND # BYTES BEING SENT
	JSR	SDNETXINTCRCSEND
	LDAA	SDNETXCOUNT+1
	JSR	SDNETXINTCRCSEND
	LDX	SDNETXCOUNT	SEND 0 BYTES ?
	BEQ	SDNETXINTCRC	B/ YES, CRC IS NEXT 
	LDX	SDNETNRBPTR
	LDAA	SDNETNRB:XVPOS,X	SEND OUT VIRTUAL POSITION OF 1ST TRANSMITTED DATA BYTE
	JSR	SDNETXINTCRCSEND
	LDX	SDNETNRBPTR
	LDAA	SDNETNRB:XVPOS+1,X
	JSR	SDNETXINTCRCSEND
	LDX	SDNETNRBPTR
	LDX	SDNETNRB:XMTBUF+SDNETBUF:EMPTY,X	POINTER TO 1ST BYTE TO TRANSMIT
	STX	SDNETBUFPTR	SAVE BUFFER SCAN POINTER
	PAGE
SDNETXINTDATA	; SEND NEXT DATA BYTE (<108 CYCLES/BYTE, MIN)
	LDAA	0,X	GET NEXT BYTE TO TRANSMIT
	JSR	SDNETXINTCRCSEND	AND GO SEND IT
	INC	SDNETBUFPTR+1	BUMP POINTER CIRCULARLY AROUND RING
	LDX	SDNETBUFPTR
	DEC	SDNETXCOUNT+1	DOWN COUNT # BYTES TO SEND
	BNE	SDNETXINTDATA	B/ SEND SOME MORE!
SDNETXINTCRC	EQU	*	NOW SEND THE CRC
	LDAA	SDNETCOMPUTEDCRC	GET FIRST CRC BYTE
	COMA		SEND IT IN COMPLEMENT FORM
	JSR	SDNETXINTWAITSEND	WAIT FOR FIFO SLOT TO SEND IT
	LDAA	SDNETCOMPUTEDCRC+1	GET 2ND CRC BYTE
	COMA		(SEND IN COMPLEMENT FORM)
	JSR	SDNETXINTWAITSEND	WAIT FOR FIFO SLOT TO SEND 2ND CRC BYTE
	LDAA	#SDNETEOTCODE	GET AN "END OF MESSAGE" CODE
	JSR	SDNETXINTWAITSEND	SEND IT
	LDAA	#SDNETEOTCODE	SEND 2ND "EOT" CODE
	JSR	SDNETXINTWAITSEND
	LDAA	#SDNETEOTCODE	SEND 3RD "EOT" CODE
	JSR	SDNETXINTWAITSEND
	LDAA	#$FF	NOW STUFF FILLER INTO FIFO 4 TIMES...
	JSR	SDNETXINTWAITSEND TO GAURANTEE THAT "EOM" CODES GET SENT
	LDAA	#$FF
	JSR	SDNETXINTWAITSEND
	LDAA	#$FF
	JSR	SDNETXINTWAITSEND
	LDAA	#$FF
	JSR	SDNETXINTWAITSEND
****	MESSAGE HAS BEEN COMPLETELY TRANSMITTED! ****
	LDAA	SDNETSSDASTS	OK, NOW CHECK FOR "TUF" SCREWUP
	BITA	#SSDASTSTUF
	BNE	*	OUCH, WE FUCKED UP SOMEWHERE
	LDX	SDNETXMITOKCOUNT	COUNT # TIMES WE SUCCEED IN TRANSMISSION
	INX
	STX	SDNETXMITOKCOUNT
	PAGE
	JSR	SDNETSSDARESET	GO KILL THE TRANSMIT ENABLE, SET UP TO RECEIVE
	LSR	SDNETXDELAYMASK	WE APPEAR TO HAVE SUCCESSFULLY TRANSMITTED A MESSAGE
	ROR	SDNETXDELAYMASK+1	SHORTEN TRANSMIT CONFLICT RESOLUTION WINDOW
	LDX	#SDNETRINTERRUPT	REMEMBER WHERE TO GO ON NEXT INTERRUPT
	STX	SDNETINTNEXT
	JSR	SDNETREMOVENRBFROMTIMEQ	TAKE NRB OFF THE TIME Q
	LDX	SDNETNRBPTR	IS THIS A BROADCAST MESSAGE ?
	LDAA	SDNETNRB:NODE,X	(IT IS IF NODE NUMBER = 0)
	BNE	SDNETXINT1	B/ NOT A BROADCAST MESSAGE, LEAVE NRB ALONE
*	BROADCAST MESSAGE, RELEASE THE BUFFER CONTENTS, LEAVE NRB OUT OF TIMEQ
	LDAA	SDNETNRB:XMTBUF+SDNETBUF:FILL+1,X
	STAA	SDNETNRB:XMTBUF+SDNETBUF:EMPTY+1,X
	JMP	SDNETSIGNALSCHED	AND WAKE UP BROADCAST TRANSMIT TASK
	PAGE
SDNETXINT1	; NON-BROADCAST MESSAGE JUST TRANSMITTED
	LDAA	SDNETNRB:STATE,X
	CMPA	#NODESTATE:QUIT	WERE WE JUST SPOS'D TO SEND "QUIT" ?
	BEQ	SDNETXINTQUIT	B/ YES, GO CLEAN UP
	CMPA	#NODESTATE:CONNECTEDS	ARE WE IN "SPOSD TO SEND" STATE ?
	BNE	SDNETXINT2B	B/ NO
	LDAA	#NODESTATE:CONNECTEDR	YES, FORCE STATE TO "SPOSD TO RECEIVE"
	STAA	SDNETNRB:STATE,X
SDNETXINT2B
	CMPA	#NODESTATE:CONNECTEDR	IN A "SPOSD TO RECEIVE NEXT" STATE ?
	BNE	SDNETXINT2A	B/ NO, MUST BE SPECIAL FUNCTION, FORCE A TIMEOUT
	LDAB	SDNETNRB:XMTBUF+SDNETBUF:FILL+1,X	DO WE HAVE DATA TO SEND?
	SUBB	SDNETNRB:XMTBUF+SDNETBUF:EMPTY+1,X	...?
	BEQ	SDNETXINT2C	B/ NO DATA TO SEND, DON'T SET TIMEOUT
SDNETXINT2A	; MUST SET A TIMEOUT
	LDAB	#SDNETRETRYTIME	SET TIME DELAY BEFORE RETRY
	JSR	SDNETPUTNRBINTIMEQ	PUT NRB IN TIMEQ TO BE PROCESSED LATER
SDNETXINT2C
	JMP	SDNETINTDONE	AND WAIT FOR A RESPONSE

SDNETXINTQUIT	; WE JUST FINISHED SENDING "QUIT"
	JMP	SDNETRINTDEAD	NOW FOR THE COUP DE GRACE
	PAGE
SDNETSSDARESET	EQU	*	RESET SDNET SSDA
	LDAA	#SSDAC1SELSYNC!SSDAC1CLRSYNC!SSDAC1TXRS!SSDAC1RXRS
	STAA	SDNETSSDAC1	SET SYNC CHARACTER TO RECOGNIZE MESSAGE START
	LDAA	#SDNETSYNCCODE
	STAA	SDNETSSDASYNC
	LDAA	#SSDAC1SELC2!SSDAC1CLRSYNC!SSDAC1TXRS!SSDAC1RXRS	RESET SSDA
	STAA	SDNETSSDAC1	SELECT "DON'T STRIP SYNC"
	LDAA	#SSDAC2TXSYNC!SSDAC2WS8!SSDAC21BYTE!SSDAC2DTR0
	STAA	SDNETSSDAC2	CHOOSE WORD SIZE = 8 BITS, 1 BYTE XMIT/RECEIVE
	LDAA	#SSDAC1SELC3!SSDAC1CLRSYNC!SSDAC1TXRS!SSDAC1RXRS
	STAA	SDNETSSDAC1	CLEAR TUF, *CTS LATCH, SELECT "2 SYNC" RECOGNITION
	LDAA	#SSDAC3CTUF!SSDAC3CLRCTS
	STAA	SDNETSSDAC3	ALSO FORCE INTERNAL SYNC MODE
	LDAA	#SSDAC1RIE!SSDAC1TXRS
	STAA	SDNETSSDAC1	FINALLY, ENABLE THE RECEIVER
	RTS

SDNETNRBPTR	RMB	2	POINTER TO NODE REPRESENTATIVE BLOCK
SDNETXCOUNT	RMB	2	# DATA BYTES TO SEND IN THIS MESSAGE

SDNETXDELAYMASK	FDB	0	QUADRATIC TRANSMIT CONFLICT RESOLUTION WINDOW MASK
*	SDNET SDLP MESSAGE FORMAT:
*
* --------------------------------------------------------------------------------
* !  TO  ! FROM ! CNTRL !  RCVDOK  ! RCVRDY ! XMTCNT ! XMTBASE ! ..DATA... ! CRC !
* --------------------------------------------------------------------------------
*    1B     1B     1B        2B        2B       2B       2B         nB        2B
*
*
*	TO = DESTINATION COMPUTER (:00 = "ALL" )
*	FROM = SOURCE COMPUTER (MUST BE <> 0)
*	CNTRL = CONTROL CODE (USED TO ESTABLISH/BREAK COMM LINK)
*	RCVDOK = POSITION IN VIRTUAL STREAM OF FIRST BYTE NOT RECEIVED (CORRECTLY) YET
*			MODULO 2^16
*	RCVRDY = SUGGESTED MAXIMUM XMTCNT FOR USE BY OTHER COMPUTER
*			USUALLY = SIZE OF "FROM" COMPUTER'S REMAINING RECEIVER BUFFER SPACE
*	XMTCNT = NUMBER OF ..DATA.. BYTES SENT IN THIS MESSAGE
*			MUST BE ACKNOWLEDGED BY ANOTHER CLOSE DATA LINK COMMAND
*	XMTBASE = POSITION OF FIRST ..DATA.. BYTE IN "TO" COMPUTER'S RECEIVE FILE
*			MODULO 2^16 (OMITTED IF XMTCNT = 0)
*	..DATA.. = STREAM OF DATA BYTES FOR "TO" COMPUTER
*			(MAX OF 2^16-1; OMITTED IF XMTCNT = 0 )
*	CRC = CRC OF MESSAGE USING CRC-CCITT AS DIVISOR POLYNOMIAL
*
SDNETRINTWAITWT	EQU	*	WAIT FOR RECV BYTE WITH TIMEOUT
	LDAA	#SSDASTSRDA	GET RECEIVER DATA READY MASK
****	WATCH FOR A MAXIMUM OF 250US WITH 2MHZ CPU ****
	BITA	SDNETSSDASTS	RECEIVE DATA ARRIVE ?
	BNE	SDNETRINTWAIT1	B/ YES, GO GET IT!
	LDAB	SDNETSSDASTS	CHECK FOR OVERRUN
	BITB	#SSDASTSRXOV	...?
	BNE	SDNETRINTOVRUN	B/ OVERRUN OCCURRED, WE MISSED SOMETHING
	LDAB	#(250*2-24)/(2+4+4+4)
SDNETRINTWAITWTL	DECB		DOWN COUNT # WAIT CYCLES
	BEQ	SDNETRINTTMO	B/ TIMED OUT
	BITA	SDNETSSDASTS
	BEQ	SDNETRINTWAITWTL	B/ DATA HAS NOT ARRIVED YET
SDNETRINTWAIT1	EQU	*	RECEIVE DATA IS READY, GRAB IT
	LDAA	SDNETSSDARCV	PICK UP THE DATA BYTE
*	DO CRC STEP ON DATA BYTE IN (A)
*	POLYNOMIAL USED IS X^16+X^12+X^5+X^1
	LDAB	SDNETCOMPUTEDCRC	PICK UP CRC COMPUTED SO FAR
	ASLB
	BCS	SDNETRINTWAIT2	B/ USE SECOND HALF OF TABLE
	STAB	SDNETCRCTABLEADD1+1
	LDAB	SDNETCOMPUTEDCRC+1
	LDX	SDNETCRCTABLEADD1
	EORB	SDNETCRCTABLE&$FF,X
	EORA	(SDNETCRCTABLE&$FF)+1,X
	STAB	SDNETCOMPUTEDCRC	STORE THE UPDATED CRC
	STAA	SDNETCOMPUTEDCRC+1
	EORA	(SDNETCRCTABLE&$FF)+1,X
	RTS	68 CYCLES THRU HERE FROM SDNETRINTWAITWT IF BUFFER HAS DATA

SDNETRINTWAIT2
	STAB	SDNETCRCTABLEADD2+1
	LDAB	SDNETCOMPUTEDCRC+1
	LDX	SDNETCRCTABLEADD2
	EORB	SDNETCRCTABLE&$FF,X
	EORA	(SDNETCRCTABLE&$FF)+1,X
	STAB	SDNETCOMPUTEDCRC	SAVE UPDATED CRC
	STAA	SDNETCOMPUTEDCRC+1
	EORA	(SDNETCRCTABLE&$FF)+1,X
	RTS

SDNETRINTTMO	EQU	*	DID NOT RECEIVE DATA IN 250 US., TIMED OUT! ****
	INS		POP THE RETURN ADDRESS OFF THE STACK
	INS
	LDX	SDNETNOCLOCKCOUNT	BUMP NUMBER OF TIMES "TMO" OCCURS
	INX
	STX	SDNETNOCLOCKCOUNT
	BRA	SDNETINTENBRCV	IGNORE MESSAGE, WAIT FOR ANOTHER

SDNETRINTRTI	EQU	*	AND EXIT
	JMP	SDOS+SDOS:RTI
	PAGE
SDNETRINTOVRUN	EQU	*	OVERRUN OCCURRED, DATA LOST
	INS		CLEAR THE STACK
	INS
	LDX	SDNETOVRUNCOUNT	BUMP # OVERRUNS ENCOUNTERED
	INX
	STX	SDNETOVRUNCOUNT
	LDAA	SDNETRDTO	IS MESSAGE FOR US EXPLICITLY ?
	CMPA	SDNETCPUIDCELL	...?
	BEQ	SDNETINTENBRCV	B/ NO, DON'T CAUSE A COLLISION
	LDAA	#SSDAC1SELXMT!SSDAC1TXRS!SSDAC1RXRS	YES, CLEAR THE OVERRUN
	STAA	SDNETSSDAC1
	STAA	SDNETSSDAXMT	PRE-FILL FIFO WITH SOMETHING
	LDAA	#SSDAC1SELC2!SSDAC1RXRS
	STAA	SDNETSSDAC1	START SHIFTING OUT THE COLLISION-CAUSING BYTE
	LDAA	#SSDAC2TXSYNC!SSDAC2WS8!SSDAC21BYTE!SSDAC2DTR1
	STAA	SDNETSSDAC2	ENABLE TRANSMITTER AND CAUSE A COLLISION!
	LDAA	#SSDASTSTUF	WAIT FOR BYTE TO BE SENT
SDNETINTOVRUNL	BITA	SDNETSSDASTS	IS BYTE SENT YET ?
	BEQ	SDNETINTOVRUNL	B/ NO, WAIT SOME MORE
*	BRA	SDNETINTENBRCV	YES, NOW GO ENABLE THE RECEIVER

SDNETRINTSYNCERR	EQU	*	MISSED SYNC, IGNORE THE MESSAGE
SDNETINTENBRCV	; ENABLE THE RECEIVER
	JSR	SDNETSSDARESET	RESET THE SSDA AND ENABLE RECEIVE
	LDX	SDNETENBRCVCOUNT	BUMP NUMBER OF TIMES WE ENABLED THE RECEIVER
	INX
	STX	SDNETENBRCVCOUNT
	JMP	SDNETINTDONE
	PAGE
*	STRUCTURE OF THIS INTERRUPT ROUTINE:
*		<GET BYTE>
*		<DO SOME WORK>
*		<GET BYTE>
*		<DO SOME WORK>...
*	THIS TECHNIQUE SPREADS THE WORK OUT EVENLY
*	SO THERE ARE NO LONG DELAYS BETWEEN BYTES
*
SDNETRINT1	JSR	SDNETRINTWAITWT	WAIT FOR A BYTE
	STAA	SDNETRDFROM	SAVE THE NODE NUMBER OF THE NODE WE RECEIVED IT FROM
	LDX	SDNETNRBTABLE	GET POINTER TO NRB FOR NODE 0
	BRA	SDNETRINT1A	AND FALL BACK INTO MAIN LINE OF CODE

SDNETRINTERRUPT	EQU	*	WE GET HERE WHEN A DATA BYTE ARRIVES
	CLR	SDNETRDTO	ASSUME BROADCAST MESSAGE (USED BY "OVERRUN" CODE)
	JSR	SDNETRINTWAITWT	WAIT FOR FIRST BYTE OF MESSAGE
	CMPA	#\SDNETSYNCCODE	INVERTED SYNC ?
	BNE	SDNETRINTSYNCERR	B/ NO, WE SEEM TO BE OUT OF SYNC!
	LDX	#$FFFF	PROPER MESSAGE PREAMBLE SEEN
	STX	SDNETCOMPUTEDCRC	RESET CRC SO WE CAN COMPUTE FOR MESSAGE
	JSR	SDNETRINTWAITWT	WAIT FOR 'TO' BYTE
	STAA	SDNETRDTO	REMEMBER WHO THE MESSAGE WAS SENT TO
	BEQ	SDNETRINT1	B/ BROADCAST MESSAGE, KEEP IT.
	CMPA	SDNETCPUIDCELL	IS MESSAGE MEANT FOR ME ?
	BNE	SDNETRINTSYNCERR	B/ NO, SO IGNORE IT!
	JSR	SDNETRINTWAITWT	WAIT FOR "SENDER"S ID.
	CMPA	#SDNETNODEMAX	DO WE HAVE AN NRB FOR THIS NODE ?
	BHI	SDNETINTENBRCV	B/ NO, WE'LL JUST IGNORE THIS MESSAGE
	STAA	SDNETRDFROM	ELSE SAVE IT
	CLRB		FIND NODE REPRESENTATIVE BLOCK ADDRESS
	ASLA
	ROLB
	ADDA	#SDNETNRBTABLE&$FF
	ADCB	#SDNETNRBTABLE/256
	STAB	SDNETNRBPTR
	STAA	SDNETNRBPTR+1
	LDX	SDNETNRBPTR
	LDX	0,X
	PAGE
SDNETRINT1A	; RE-ENTRY POINT FOR BROADCAST MESSAGE RECEIVER CODE
	STX	SDNETNRBPTR
	LDAA	SDNETNRB:STATE,X	IF NODE REPRESENTATIVE IS MARKED AS 'DEAD',
	CMPA	#NODESTATE:DEAD	THEN IGNORE ANY MESSAGES FOR IT
	BEQ	SDNETINTENBRCV	B/ A ZOMBIE! EEEK!
	JSR	SDNETRINTWAITWT	WAIT FOR CONTROL BYTE
	STAA	SDNETRDCONTROL
	LDX	SDNETNRBPTR	COMPUTE READY TO RECEIVE COUNT
	CLRA		= 255-(FILL-EMPTY)
	LDAB	SDNETNRB:RCVBUF+SDNETBUF:EMPTY+1,X
	SUBB	SDNETNRB:RCVBUF+SDNETBUF:FILL+1,X
	DECB		THIS INCANTATION WORKS MODULO 256!
	STAA	SDNETFREEBYTES
	STAB	SDNETFREEBYTES+1
	JSR	SDNETRINTWAITWT	WAIT FOR ACKNOWLEDGED POSITION
	STAA	SDNETRDRVPOS
	JSR	SDNETRINTWAITWT
	STAA	SDNETRDRVPOS+1
	LDX	SDNETNRBPTR	SET TENTATIVE FILL POINTER...
	LDX	SDNETNRB:RCVBUF+SDNETBUF:FILL,X	TO FIRST FREE BYTE OF BUFFER CHAIN
	STX	SDNETBUFPTR
	JSR	SDNETRINTWAITWT	WAIT FOR OTHER COMPUTER'S AVAILABLE BUFFER SPACE
	STAA	SDNETRDREADYCOUNT
	JSR	SDNETRINTWAITWT
	STAA	SDNETRDREADYCOUNT+1
	JSR	SDNETRINTWAITWT	WAIT FOR # BYTES OTHER COMPUTER IS SENDING TO US
	STAA	SDNETRDXMITCOUNT
	JSR	SDNETRINTWAITWT
	STAA	SDNETRDXMITCOUNT+1
	LDX	SDNETRDXMITCOUNT	DID OTHER COMPUTER SEND US SOME DATA ?
	BEQ	SDNETRINTCRCJ	B/ NO, GO GET THE CRC!
	LDAA	SDNETRDTO	BROADCAST MESSAGE ?
	BNE	SDNETRINT1C	B/ NO, MUST BE NORMAL DATA MESSAGE
	LDAA	SDNETFREEBYTES	DECIDE IF WE CAN KEEP ENTIRE BROADCAST MESSAGE
	LDAB	SDNETFREEBYTES+1
	SUBB	SDNETRDXMITCOUNT+1
	SBCA	SDNETRDXMITCOUNT
	BCS	SDNETINTENBRCVJ	B/ CAN'T KEEP ENTIRE MESSAGE, "SOUR GRAPES..."
	JSR	SDNETRINTWAITWT	GET BASE OF OTHER COMPUTER'S TRANSMITTED DATA
	LDX	SDNETRDXMITCOUNT	SET PHASE 2 COUNT = ALL TRANSMITTED BYTES
	STX	SDNETPHASE2CNT
	JSR	SDNETRINTWAITWT	... (XMTBASE MUST BE ZERO, SO WE CAN IGNORE IT)
	LDX	#0	CAN KEEP ENTIRE MESSAGE, SET PHASE 3 COUNT TO 0
	STX	SDNETPHASE3CNT
	JMP	SDNETRINT4A	GO SAVE THE DATA BYTES

SDNETINTENBRCVJ	JMP	SDNETINTENBRCV

SDNETRINTELSEJ	JMP	SDNETRINTELSE

SDNETRINTCRCJ	JMP	SDNETRINTCRC
	PAGE
SDNETRINT1C	; MUST BE NORMAL DATA MESSAGE
	JSR	SDNETRINTWAITWT	YES, GET BASE OF OTHER COMPUTER'S TRANSMITTED DATA
	STAA	SDNETRDXMITBASE
	JSR	SDNETRINTWAITWT
	STAA	SDNETRDXMITBASE+1
*
*				...DATA... PART OF MESSAGE
*	--------------------------------------------------------------------
*	!  <-- PHASE 1 CNT --> ! <-- PHASE 2 CNT --> ! <-- PHASE 3 CNT --> !
*	--------------------------------------------------------------------
*	^                      ^                                           ^
*	!                      !                                           !
*	! <----------------------- SDNETRDXCNT --------------------------> !
*	!                      !
*	\                  SDNETNRB:RVPOS
*	  SDNETRDXMITBASE
*
*
*	IF SDNETRDXMITBASE <= SDNETNRB:RVPOS
*	AND SDNETRDXMITBASE+SDNETRDXCNT > SDNETNRB:RVPOS
*	THEN PHASE1CNT:=SDNETNRB:RVPOS-SDNETRDXMITBASE;
*	     PHASE2CNT:=MIN(availablebufferspace,
*                          SDNETRDXMITBASE+SDNETRDXCNT-SDNETNRB:RVPOS);
*	     PHASE3CNT:=SDNETRDXCNT-(PHASE1CNT+PHASE2CNT);
*	ELSE PHASE1CNT:=0;
*	     PHASE2CNT:=0;
*	     PHASE3CNT:=SDNETRDXCNT;
*	FI

	LDX	SDNETNRBPTR
	LDAA	SDNETNRB:RVPOS,X	IS SDNETRDXMITBASE <= SDNETNRB:RVPOS ?
	LDAB	SDNETNRB:RVPOS+1,X	I.E., 0 <= SDNETNRB:RVPOS - SDNETRDXMITBASE ?
	SUBB	SDNETRDXMITBASE+1	NOTE: -32768 <= ACTUALRVPOS-ACTUALXMITBASE <= 32767 !
*****?????? 32767 ?????
	SBCA	SDNETRDXMITBASE
	BMI	SDNETRINTELSEJ	B/ NO...
	STAA	SDNETPHASE1CNT	YES, SAVE PHASE1 SKIP COUNT
	STAB	SDNETPHASE1CNT+1
	LDAA	SDNETRDXMITCOUNT	COMPUTE MIN(...)
	LDAB	SDNETRDXMITCOUNT+1
	SUBB	SDNETPHASE1CNT+1
	SBCA	SDNETPHASE1CNT
	BMI	SDNETRINTELSEJ	B/ SDNETRDXBASE+SDNETRDXMITCOUNT < SDNETNRB:RVPOS
	STAA	SDNETPHASE2CNT	ASSUME SDNETRDXBASE+SDNETRDXMITCOUNT-SDNETNRB:RVPOS IS MIN
	STAB	SDNETPHASE2CNT+1
	SUBB	SDNETFREEBYTES+1
	SBCA	SDNETFREEBYTES
	BCS	SDNETRINT2	B/ ASSUMPTION WAS RIGHT
	LDX	SDNETFREEBYTES	OOPS, ASSUMPTION WAS WRONG
	STX	SDNETPHASE2CNT	USE availablebufferspace AS MIN
SDNETRINT2
	LDX	SDNETPHASE1CNT	SHOULD WE IGNORE ANY BYTES ?
	STX	SDNETPHASE3CNT	(FOR PHASE 3 COMPUTATION BELOW)
	BEQ	SDNETRINT4A	B/ NO
SDNETRINT3	EQU	*	IGNORE SDNETPHASE1CNT BYTES OF MESSAGE
	JSR	SDNETRINTWAITWT	WAIT FOR A BYTE TO IGNORE
	LDX	SDNETPHASE1CNT	DOWN COUNT # BYTES TO IGNORE
	DEX
	STX	SDNETPHASE1CNT
	BNE	SDNETRINT3	B/ MORE BYTES TO IGNORE
	PAGE
SDNETRINT4A	EQU	*	SAVE PHASE2CNT BYTES
	LDX	SDNETPHASE2CNT	ANY BYTES TO SAVE ?
	STX	SDNETPHASE2CNTSV	(SAVE THIS IN CASE CRC IS GOOD!)
	BEQ	SDNETRINT6A	B/ NO
	LDX	SDNETNRBPTR	GET BUFFER FILL POINTER
	LDX	SDNETNRB:RCVBUF+SDNETBUF:FILL,X
	STX	SDNETBUFPTR	SET UP FOR LOOP BODY
SDNETRINT4	; FILL BUFFER WITH RECEIVED DATA BYTES
	JSR	SDNETRINTWAITWT	GET A DATA BYTE
	LDX	SDNETBUFPTR	STORE INTO BUFFER, TENTATIVELY
	STAA	0,X
	INC	SDNETBUFPTR+1	BUMP BUFFER POINTER CIRCULARLY
	DEC	SDNETPHASE2CNT+1	DOWN COUNT # BYTES TO SAVE
	BNE	SDNETRINT4	B/ MORE TO SAVE
SDNETRINT6A	EQU	*	ANY BYTES TO IGNORE BEFORE CRC ARRIVES ?
	LDAA	SDNETRDXMITCOUNT	COMPUTE PHASE 3 (SKIP) COUNT
	LDAB	SDNETRDXMITCOUNT+1
	SUBB	SDNETPHASE3CNT+1
	SBCA	SDNETPHASE3CNT
	SUBB	SDNETPHASE2CNTSV+1
	SBCA	SDNETPHASE2CNTSV
	STAA	SDNETPHASE3CNT
	STAB	SDNETPHASE3CNT+1
	LDX	SDNETPHASE3CNT	IS IGNORE COUNT = 0 ?
	BEQ	SDNETRINTCRC	B/ YES, IGNORE ZERO BYTES
SDNETRINT6	EQU	*	IGNORE BYTES UNTIL CRC ARRIVES
	JSR	SDNETRINTWAITWT	GET YET ANOTHER BYTE
	LDX	SDNETPHASE3CNT
	DEX		DOWN COUNT # TO IGNORE
	STX	SDNETPHASE3CNT
	BNE	SDNETRINT6	B/ MORE TO IGNORE
	PAGE
SDNETRINTCRC	EQU	*	AT LAST! THE CRC WILL ARRIVE!
	LDX	SDNETCOMPUTEDCRC	GRAB CURRENT VALUE OF CRC
	STX	SDNETINTTEMPX	AND SAVE IT WHILE WE COLLECT CRC BYTES
	JSR	SDNETRINTWAITWT	WAIT FOR FIRST CRC BYTE
	ADDA	SDNETINTTEMPX	COMBINE WITH COMPUTED CRC ON MESSAGE BODY
	STAA	SDNETINTTEMPX
	JSR	SDNETRINTWAITWT	WAIT FOR 2ND CRC BYTE
	ADDA	SDNETINTTEMPX+1	COMBINE WITH COMPUTED CRC ON MESSAGE BODY
	STAA	SDNETINTTEMPX+1
	JSR	SDNETRINTWAITWT	EAT THE 3 TRAILING "EOT" CODES
	JSR	SDNETRINTWAITWT
	JSR	SDNETRINTWAITWT
	LDX	SDNETINTTEMPX	IS RECEIVED CRC CORRECT ?
	CPX	#$FFFF	(THIS MAGIC CONSTANT DEPENDS ON CONTENT OF TRAIL BYTES OF MESSAGE)
	BEQ	SDNETRINTMSGOK	B/ CRC MATCHES !
	LDX	SDNETBADCRCCOUNT	BUMP BAD CRC RCVD COUNTER
	INX
	STX	SDNETBADCRCCOUNT
SDNETINTENBRCVJ2
	JMP	SDNETINTENBRCV	NOTHING INTERESTING HAPPENED...

SDNETRINTELSE	EQU	*
	LDX	#0	SET SAVED COUNT TO ZERO
	STX	SDNETPHASE2CNTSV
	LDX	SDNETRDXMITCOUNT	USE TRANSMIT COUNT FOR PHASE 3
	STX	SDNETPHASE3CNT
	BRA	SDNETRINT6	GO IGNORE THE DATA BYTES

SDNETRINTCONFUSED	; UNTESTED
	JSR	SDNETREMOVENRBFROMTIMEQ	OTHER NODE IS CRAZED
	LDAA	SDNETRDCONTROL
	CMPA	#SDLPCONTROL:RESYNCHREQ
	BNE	SDNETRINTCONFNOTRESYNCH
	JMP	SDNETRINTDEAD	IF RESYNCH REQUEST, JUST KILL THIS NRB
SDNETRINTCONFNOTRESYNCH
	JMP	SDNETRINTSENDQUIT
	PAGE
SDNETRINTMSGOK	EQU	*	GOT THE MESSAGE OK!
	LDAA	SDNETSSDARCV	EAT POSSIBLE GARBAGE TRAILER BYTE
	LDX	#(250*2)/8	MAKE SURE THAT MESSAGE WE RECEIVED...
	DEX		IS NOT EMBEDDED IN A LARGER MESSAGE BODY BY MALICIOUS USER
	BNE	*-1	BY WAIT FOR 250 US, THEN VERIFYING THAT CLOCK IS GONE
	LDAA	SDNETSSDASTS	CHECK FOR PRESENCE OF CLOCK
	BITA	#SSDASTSRDA	BY SEEING IF ANY DATA ARRIVED IN THE WINDOW
	BNE	SDNETINTENBRCVJ2	B/ MESSAGE EMBEDDED IN MESSAGE, IGNORE IT!
	LDX	SDNETGOODCRCCOUNT	BUMP # GOOD MESSAGES WE HAVE RECEIVED
	INX
	STX	SDNETGOODCRCCOUNT
	LDX	SDNETNRBPTR
	LDAA	#SDNETRETRYCOUNT	WE GOT THROUGH!
	STAA	SDNETNRB:RETRY,X	SET RETRY COUNTER BACK TO MAXIMUM
*
*	IF SDNETRDRVPOS - SDNETNRB:XVPOS  > SDNETNRB:XMTRDY
*	OR SDNETRDRVPOS < SDNETNRB:XVPOS
*	THEN OTHER COMPUTER IS CONFUSED!!!
*	ELSE
*		LET DELTA:= THE DIFFERENCE
*		SDNETNRB:XMTBUF(EMPTY):=...(EMPTY)+DELTA (ACROSS BUFFER BOUNDARIES)
*		SDNETNRB:XVPOS:=SDNETRDXVPOS
*	FI
*	NOTE: IF OTHER NODE IS NOT CONFUSED,...
*		THEN SDNETRDVPOS >= SDNETNRB:XVPOS!
*
	LDAA	SDNETRDRVPOS	COMPUTE DELTA = RDRVPOS - XVPOS
	LDAB	SDNETRDRVPOS+1
	SUBB	SDNETNRB:XVPOS+1,X
	SBCA	SDNETNRB:XVPOS,X
	STAA	SDNETRDELTA	SAVE THE DELTA
	STAB	SDNETRDELTA+1
	CLRA		COMPUTE XMTRDY COUNT
	LDAB	SDNETNRB:XMTBUF+SDNETBUF:FILL+1,X	= FILL-EMPTY
	SUBB	SDNETNRB:XMTBUF+SDNETBUF:EMPTY+1,X
	SUBB	SDNETRDELTA+1	IF DELTA > XMTCNT THEN HE'S CONFUSED
	SBCA	SDNETRDELTA	(I.E. XMTCNT < AMOUNT ACKNOWLEDGED RCVD ?)
	BCS	SDNETRINTCONFUSED	B/ OTHER NODE IS DEFINITELY CONFUSED
	LDAB	SDNETNRB:XMTBUF+SDNETBUF:EMPTY+1,X	OK, ADVANCE TRANSMIT BUFFER POINTER
	ADDB	SDNETRDELTA+1
	STAB	SDNETNRB:XMTBUF+SDNETBUF:EMPTY+1,X
	LDAA	SDNETNRB:XVPOS,X	ADVANCE XVPOS BY DELTA
	LDAB	SDNETNRB:XVPOS+1,X
	ADDB	SDNETRDELTA+1
	ADCA	SDNETRDELTA
	STAA	SDNETNRB:XVPOS,X
	STAB	SDNETNRB:XVPOS+1,X
	LDAA	SDNETRDREADYCOUNT	SAVE THE RECEIVED READY COUNT
	LDAB	SDNETRDREADYCOUNT+1
	STAA	SDNETNRB:RCVDRDY,X
	STAB	SDNETNRB:RCVDRDY+1,X
	LDX	SDNETRDXMITCOUNT	ANY DATA RECEIVED ?
	BEQ	SDNETRINTMSGOK3	B/ NO
	LDX	SDNETNRBPTR	YES, AND WE RECEIVED IT CORRECTLY
	LDAB	SDNETNRB:RCVBUF+SDNETBUF:FILL+1,X
	ADDB	SDNETPHASE2CNTSV+1	BUMP # BYTES RECEIVED CORRECTLY
	STAB	SDNETNRB:RCVBUF+SDNETBUF:FILL+1,X
	LDAA	SDNETNRB:NODE,X	IS THIS THE BROADCAST RECEIVER NRB ?
	BEQ	SDNETRINTCASTRCVD	B/ YES, LEAVE :RVPOS ALONE
*	ALSO, LEAVE NRB ZERO IN TIMEQ IF IT IS ALREADY THERE
*	THIS ENSURES THAT IT GETS ITS CHANCE TO BROADCAST CORRECTLY
	LDAA	SDNETNRB:RVPOS,X	ADJUST POSITION IN RECEIVED VIRTUAL FILE
	LDAB	SDNETNRB:RVPOS+1,X
	ADDB	SDNETPHASE2CNTSV+1	= # BYTES RETAINED BY PHASE 2
	ADCA	SDNETPHASE2CNTSV	CARRY OUT IS LOST --> MODULO 2^16, AS SPEC'D
	STAA	SDNETNRB:RVPOS,X
	STAB	SDNETNRB:RVPOS+1,X
	PAGE
SDNETRINTMSGOK3
	CLR	SURPRISE	OR SOMETHING ELSE TO SIGNAL SCHEDULER HERE
	JSR	SDNETREMOVENRBFROMTIMEQ	IN CASE WE AREN'T GOING TO SET ANOTHER TIMEOUT
	LDAB	SDNETRDCONTROL	NOW BRANCH ON CONTROL MESSAGE TYPE
	LDAA	SDNETNRB:STATE,X
	CMPA	#NODESTATE:ACTIVATING	TRYING TO ACTIVATE THIS NODE ?
	BEQ	SDNETRINTACTIVATING	B/ YES
	CMPB	#SDLPCONTROL:RESYNCHQUIT-1	A BROADCAST STYLE CONTROL CODE ?
	BHI	SDNETRINTSENDQUIT	B/ NOT A VALID CONTROL CODE, BUT CRC IS RIGHT !?
	ASLB		DO TABLE JUMP ON CONTROL CODE
	STAB	SDNETCONTROL1+1
	LDX	SDNETCONTROL1
	LDAA	SDNETCONTROL&$FF,X
	LDAB	(SDNETCONTROL&$FF)+1,X
	PSHB
	PSHA
	LDX	SDNETNRBPTR	GRAB NRB POINTER AGAIN
	LDAA	SDNETNRB:STATE,X	FOR CONVENIENCE OF CALLED ROUTINE
	RTS		GO TO CONTROL MESSAGE HANDLER

SDNETRINTCASTRCVD	BRA	SDNETSIGNALENBRCV	GO WAKE UP BROADCAST LISTENER TASK
	PAGE
SDNETRINTACTIVATING	; WE ARE TRYING TO ACTIVATE THIS NRB
	CMPB	#SDLPCONTROL:RESYNCHREQ	DID WE GET A "RESYNCH" MESSAGE ?
	BEQ	SDNETRINTRESYNCH1	B/ THAT'S OK WITH US
	CMPB	#SDLPCONTROL:RESYNCHED	DID WE GET PROPER RESPONSE TO "RESYNCH" ?
	BNE	SDNETRINSTANT	B/ NO, GO SEND INSTANT RESYNCH AGAIN!
	LDAA	#NODESTATE:ACTIVATED	YES! SWITCH TO ACTIVATED
	BRA	SDNETRNEWSTATE

SDNETRINTRESYNCH	; RE-SYNCH REQUEST
	LDAB	SDNETRDXMITCOUNT	DID WE RECEIVE ANY DATA WITH "RESYNCH" ?
	ORAB	SDNETRDXMITCOUNT+1	...?
	BNE	SDNETRINTDEAD	B/ YES, THAT'S TOTAL GARBAGE...
	CMPA	#NODESTATE:INACTIVE	RESYNCH REQUEST LEGAL ONLY IF INACTIVE
	BCS	SDNETRINTDEAD	B/ NO SUCH LUCK, KILL THIS GUY.
	CMPA	#NODESTATE:RESYNCHED	ACTIVATING OR RESYNCHED STATE ?
	BHI	SDNETRINTDEAD	B/ NO, KILL OFF THE NRB
SDNETRINTRESYNCH1	; SWITCH TO "RESYNCHED" STATE
	LDAA	#NODESTATE:RESYNCHED	OK, SWITCH TO "RESYNCHED" STATE
SDNETRNEWSTATE	; SET NEW STATE TO (A) AND SEND INSTANT RESPONSE
	STAA	SDNETNRB:STATE,X
SDNETRINSTANT	; SEND RESPONSE INSTANTLY
	CLRB		SET TIME DELAY = 0
	JSR	SDNETPUTNRBINTIMEQ
	JMP	SDNETINTDONE

SDNETRINTRESYNCHED	; RE-SYNCHRONIZED MESSAGE WAS RECEIVED
	CMPA	#NODESTATE:ACTIVATED	THEN ITS OK...
	BNE	SDNETRINTSENDQUIT	B/ NOT LEGAL HERE
	BRA	SDNETRINTNORMAL1	WE DON'T NEED TO SEND INSTANT RESPONSE
	PAGE
SDNETRINTWANTQUIT	; "WANT TO QUIT" REQUEST RECEIVED
	CMPA	#NODESTATE:INACTIVE	IN THIS STATE, ITS NOT RIGHT TO GET "WANTQUIT"
	BEQ	SDNETRINTSENDQUIT	SO SEND A QUIT TO KILL NODE OFF
	LDAA	#NODESTATE:AGREEQUIT	GO TO "AGREE TO QUIT" STATE
	BRA	SDNETRNEWSTATE	GO SET STATE AND SEND INSTANT RESPONSE

SDNETRINTQUIT	; "QUIT!" MESSAGE RECEIVED
SDNETRINTDEAD	; OTHER NODE CROAKED OR WENT CRAZY, GIVE UP!
	JSR	SDNETREMOVENRBFROMTIMEQ	SO HE BECOMES VERY UNINTERESTING
	LDAA	#NODESTATE:DEAD	MARK THE NODE STATE APPROPRIATELY
	STAA	SDNETNRB:STATE,X
	CLR	SDNETNRB:XVPOS,X	ZERO THE TRANSMIT VIRTUAL POSITION
	CLR	SDNETNRB:XVPOS+1,X
	CLR	SDNETNRB:RVPOS,X	ZERO THE RECEIVE VIRTUAL POSITION
	CLR	SDNETNRB:RVPOS+1,X
	LDAA	#SDNETRETRYCOUNT	SET RETRY COUNTER TO MAX???
	STAA	SDNETNRB:RETRY,X
*	IT DOESN'T DO ANY GOOD TO KILL TRANSMIT BUFFER POINTERS,
*	SINCE USER/LISTENER TASK MIGHT BE FILLING TRANSMIT BUFFER AT THIS INSTANT
SDNETSIGNALENBRCV	; SIGNAL THE SCHEDULER AND ENABLE TO RECEIVE
	JSR	SDNETSSDARESET	ENABLE SSDA TO RECEIVE
	JMP	SDNETSIGNALSCHED	GO WAKE THE LISTENER TASK!
	PAGE
SDNETRINTNORMAL	; "NORMAL" MESSAGE RECEIVED
	CMPA	#NODESTATE:RESYNCHED-1	THIS IS OK...
*	THIS TEST ALSO INCLUDES ACTIVATED, CONNECTEDR AND CONNECTEDS
	BHI	SDNETRINTNORMAL1	GO DECIDE WHAT TO DO NEXT
	CMPA	#NODESTATE:WANTQUIT	MIGHT BE RESPONSE TO THIS IF MISSED
	BEQ	SDNETRINSTANT	B/ YES, GO SEND HIM OUR REQUEST AGAIN
SDNETRINTAGREEQUIT	; "AGREE TO QUIT" MESSAGE RECEIVED
SDNETRINTSENDQUIT	; CHANGE STATE TO QUIT AND SEND RESPONSE
	LDAA	#NODESTATE:QUIT	MARK THE NODE AS DEAD
	BRA	SDNETRNEWSTATE	AND GO SEND "QUIT!" RESPONSE (JUST ONCE!)

SDNETRINTNORMAL1	; DECIDE IF WE SHOULD RESPOND OR NOT
	LDAA	#NODESTATE:CONNECTEDR	ASSUME WE WILL RESPOND INSTANTLY
	STAA	SDNETNRB:STATE,X	(I.E., WE WILL XMIT AND THEN WAIT FOR MESSAGE)
	CLRA		COMPUTE TRANSMIT BUFFER READY TO (A,B)
	LDAB	SDNETNRB:XMTBUF+SDNETBUF:FILL+1,X	ANY DATA BYTES TO SEND ?
	SUBB	SDNETNRB:XMTBUF+SDNETBUF:EMPTY+1,X
	BEQ	SDNETRINTNORMAL3	B/ NO DATA TO SEND
	SUBB	SDNETXMITTHRESHOLDVALUE+1	ENOUGH BYTES TO SEND EFFICIENT MESSAGE ?
	SBCA	SDNETXMITTHRESHOLDVALUE	...?
	BCS	SDNETRINTNORMAL1B	B/ NOT ENOUGH TO SEND
	LDAA	SDNETNRB:RCVDRDY,X	ENOUGH TO SEND, DOES RECEIVER...
	LDAB	SDNETNRB:RCVDRDY+1,X	HAVE ENOUGH ROOM TO RECEIVE IT ALL?
	SUBB	#SDNETXMITTHRESHOLD&$FF
	SBCA	#SDNETXMITTHRESHOLD/256
	BCC	SDNETRINSTANT	B/ YES, SEND REPLY NOW!
SDNETRINTNORMAL1B	; "SOMETHING TO SEND" EXCUSE IS NOT GOOD ENOUGH YET
	LDX	SDNETRDXMITCOUNT	DID OTHER NODE SEND US SOME DATA ?
	BEQ	SDNETINTNORMAL2	B/ NO, TAKE OUR TIME ABOUT RESPONDING
SDNETRINTNORMAL1A
	LDX	SDNETNRBPTR	YES, DO WE HAVE ENOUGH RECEIVER BUFFER ROOM...
	CLRA		= 256-(FILL-EMPTY)
	LDAB	SDNETNRB:RCVBUF+SDNETBUF:EMPTY+1,X
	SUBB	SDNETNRB:RCVBUF+SDNETBUF:FILL+1,X
	DECB
	SUBB	#SDNETXMITTHRESHOLD&$FF	FOR OTHER NODE TO SEND EFFICIENT MESSAGE ?
	SBCA	#SDNETXMITTHRESHOLD/256
	BCC	SDNETRINSTANT	B/ YES, SEND OUR RESPONSE NOW!
SDNETINTNORMAL2	; RESPOND TO OTHER NODE'S TRANSMISSION AFTER SUITABLE DELAY
	LDX	SDNETNRBPTR	SINCE (X) IS GARBAGE HERE
	LDAA	#NODESTATE:CONNECTEDS	REMEMBER WE ARE "SPOSD TO SEND" NEXT
	STAA	SDNETNRB:STATE,X
	LDAB	#SDNETRESPONDTIME	DELAY A LITTLE TO LET TRANSMIT BUFFER FILL
	JSR	SDNETPUTNRBINTIMEQ	SHORTENING "RESPONDTIME"...
SDNETINTNORMAL2A
	JSR	SDNETSSDARESET	ENABLE THE RECEIVER
SDNETINTDONEJ
	JMP	SDNETINTDONE	INCREASES RESPONSIVENESS

SDNETRINTNORMAL3	; NOTHING TO SEND
	LDX	SDNETRDXMITCOUNT	DID OTHER NODE SEND US DATA ?
	BEQ	SDNETINTNORMAL2A	B/ NO, DON'T SEND A RESPONSE
	BRA	SDNETRINTNORMAL1A	YES, GO SEE IF ENOUGH RECEIVER ROOM
	PAGE
SDNETINTDESIREKILL	; SOCKET MANAGER WANTS TO KILL AN NRB
	LDX	SDNETDESIREKILLNRBPTR	AND THIS IS THE VIRGIN TO BE SACRIFICED
	LDAA	#NODEEPITAPH:LOCALKILLED	epitaph: he committed suicide
	STAA	SDNETNRB:EPITAPH,X
	INC	SDNETDESIREKILLLOCK	RELEASE THE LOCK ON THE VIRGIN
SDNETRINTDEADJ1
	BRA	SDNETRINTDEAD	AND GO MARK HER AS DEAD

SDNETINTDESIREQUIT	; SOCKET MANAGER WANTS TO DISCONNECT FROM AN NRB
	LDX	SDNETDESIREQUITNRBPTR	AND THIS IS THE GUY
	LDAA	#NODEEPITAPH:LOCALDISCONNECT	epitaph: he politely said "go away"
	STAA	SDNETNRB:EPITAPH,X
	INC	SDNETDESIREQUITLOCK	LET GO THE LOCK
	STX	SDNETNRBPTR	SAVE THE NRB BASE
	JSR	SDNETREMOVENRBFROMTIMEQ	TAKE NRB OUT OF WHERE HE IS
	LDAA	SDNETNRB:STATE,X
	CMPA	#NODESTATE:CONNECTEDR	STILL CONNECTED ?
	BEQ	SDNETINTDESIREQUIT1	B/ YEP.
	CMPA	#NODESTATE:CONNECTEDS	...?
	BNE	SDNETRINTDEADJ1	B/ NO, KILL IT OFF QUICK!
SDNETINTDESIREQUIT1
	LDAA	#NODESTATE:WANTQUIT	YES, MARK HIM AS "WANT QUIT"
	JMP	SDNETRNEWSTATE	GO SEND THE REQUEST

SDNETINTDESIREDELAYEDXMIT	; SOCKET MANAGER WANTS TO TRANSMIT DATA...
*	BUT DOESN'T HAVE ENOUGH DATA TO JUSTIFY IT RIGHT NOW
*	SO WE'LL LIGHT A FUSE, AND SEND IT LATER WHEN FUSE GOES OFF,
*	OR MORE DATA ARRIVES
	LDX	SDNETDESIREDELAYEDXMITNRBPTR	GET NRB SELECTED
	INC	SDNETDESIREDELAYEDXMITLOCK	FREE THE LOCK
	STX	SDNETNRBPTR
	CPX	SDNETNRB:TIMEQFLINK,X	IS NRB STILL IDLE ?
	BNE	SDNETINTDONEJ	B/ NO, IGNORE STARTIO REQUEST
	LDAA	SDNETNRB:STATE,X	DO WE EXPECT TO RECEIVE NEXT ?
	CMPA	#NODESTATE:CONNECTEDR	...?
	BEQ	SDNETINTNORMAL2	YES, PLACE NRB IN TIMEQ FOR LATER TRANSMISSION
	CMPA	#NODESTATE:INACTIVE	IS THIS NRB CURRENTLY ASLEEP ?
	BNE	SDNETINTDONEJ	B/ NO, SOCKET NOT IN STATE IN WHICH START IO ISSUED, SO IGNORE THE STARTIO
**** WHAT IF BROADCAST??? SET RCVDRDY = MAX???
	LDAA	#NODESTATE:ACTIVATING	MUST BE TIME TO WAKE THIS GUY UP
	JMP	SDNETRNEWSTATE	GO SET STATE AND TRANSMIT

SDNETCONTROL1	FDB	SDNETCONTROL&$FF00
*	BRANCH TABLE USED TO BRANCH ON RECEIVED MESSAGE TYPE
SDNETCONTROL	FDB	SDNETRINTNORMAL	SDLPCONTROL:NORMAL
	FDB	SDNETRINTRESYNCH	SDLPCONTROL:RESYNCH
	FDB	SDNETRINTRESYNCHED	SDLPCONTROL:RESYNCHED
	FDB	SDNETRINTWANTQUIT	SDLPCONTROL:WANTQUIT
	FDB	SDNETRINTAGREEQUIT	SDLPCONTROL:AGREEQUIT
	FDB	SDNETRINTQUIT	SDLPCONTROL:QUIT
	PAGE
*	SDNETREMOVENRBFROMTIMEQ -- REMOVES NRB SELECTED BY SDNETNRBPTR...
*	FROM THE TIME PROCESSING QUEUE
*	PRESERVES SDNETNRBPTR
*
SDNETREMOVENRBFROMTIMEQ
	LDX	SDNETTIMEQHEAD+SDNETNRB:TIMEQFLINK	FIND FIRST NRB IN QUEUE
	LDAA	SDNETTIMEQHEAD+SDNETNRB:TIMEQDELTA	SET HIS DELAY...
	STAA	SDNETNRB:TIMEQDELTA,X	TO REMAINING DELAY BEFORE TIMEOUT
	LDX	SDNETNRBPTR	THIS IS THE GUY TO PULL OUT OF THE QUEUE
	LDAA	SDNETNRB:TIMEQDELTA,X	GRAB HIS DELAY
	PSHA		AND SAVE IT SO WE CAN ADD TO NEXT NRB'S DELAY
	LDAA	SDNETNRB:TIMEQFLINK,X	FIND NRB THAT FOLLOWS ONE TO BE REMOVED
	LDAB	SDNETNRB:TIMEQFLINK+1,X
	LDX	SDNETNRB:TIMEQBLINK,X	FIND NRB PRECEDING ONE TO BE REMOVED
	STAA	SDNETNRB:TIMEQFLINK,X	MAKE PRECEDING NRB POINT TO NEXT NRB
	STAB	SDNETNRB:TIMEQFLINK+1,X
	STX	SDNETINTTEMPX	SAVE PRECEDING NRB'S ADDRESS
	LDX	SDNETNRB:TIMEQFLINK,X	GRAB ADDRESS OF FOLLOWING NRB
	PULA		GET DELAY FROM NRB BEING REMOVED
	ADDA	SDNETNRB:TIMEQDELTA,X	ADD TO DELAY OF FOLLOWING NRB
	STAA	SDNETNRB:TIMEQDELTA,X	AND UPDATE FOLLOWING NRB DELAY
	LDAA	SDNETINTTEMPX	MAKE FOLLOWING NRB...
	LDAB	SDNETINTTEMPX+1	POINT BACKWARDS TO PRECEDING NRB
	STAA	SDNETNRB:TIMEQBLINK,X
	STAB	SDNETNRB:TIMEQBLINK+1,X
	LDX	SDNETTIMEQHEAD+SDNETNRB:TIMEQFLINK	SET TIME DELAY...
	LDAA	SDNETNRB:TIMEQDELTA,X	TO DELTA IN FIRST NRB OF TIMEQ
	STAA	SDNETTIMEQHEAD+SDNETNRB:TIMEQDELTA
SDNETLINKNRBTOSELF	; ENTRY POINT USED BY INITZ ROUTINES
	LDX	SDNETNRBPTR	NOW MAKE REMOVED NRB POINT TO SELF
	LDAA	SDNETNRBPTR
	LDAB	SDNETNRBPTR+1
	STAA	SDNETNRB:TIMEQFLINK,X
	STAB	SDNETNRB:TIMEQFLINK+1,X
	STAA	SDNETNRB:TIMEQBLINK,X
	STAB	SDNETNRB:TIMEQBLINK+1,X
	RTS		AND EXIT
	PAGE
*
*	SDNETPUTNRBINTIMEQ -- PLACES NRB SELECTED BY SDNETNRBPTR...
*	INTO TIME PROCESSING QUEUE, ACCORDING TO (B)
*	NRB SELECTED BY SDNETNRBPTR MUST NOT BE IN TIME QUEUE ALREADY
*	SDNETNRBPTR IS SET TO FIRST NRB IN Q WHEN DONE
*
SDNETPUTNRBINTIMEQ
	LDX	SDNETTIMEQHEAD+SDNETNRB:TIMEQFLINK	WHIZ DOWN QUEUE LOOKING FOR PROPER PLACE
	LDAA	SDNETTIMEQHEAD+SDNETNRB:TIMEQDELTA	BUT FIRST, PLACE REMAINING TIME...
	STAA	SDNETNRB:TIMEQDELTA,X	INTO FIRST BLOCK ON LIST
	CPX	#SDNETTIMEQHEAD	IS QUEUE EMPTY ?
	BEQ	SDNETPUTNRBINQ2	B/ Q IS EMPTY, PUT NEW GUY IN (ANYWHERE!)
SDNETPUTNRBINQ1	; CHECK TO SEE IF THIS IS PROPER INSERT POINT
	SUBB	SDNETNRB:TIMEQDELTA,X	DECREASE TOTAL DELAY BY DELTA
	BCS	SDNETPUTNRBINQ3	B/ WENT NEGATIVE, THIS IS INSERT POINT!
	LDX	SDNETNRB:TIMEQFLINK,X	STILL POSITIVE, ON TO NEXT NRB IN Q
	CPX	#SDNETTIMEQHEAD	AT END OF Q ?
	BNE	SDNETPUTNRBINQ1	B/ NOPE, GO CHECK AGAIN
SDNETPUTNRBINQ2	; INSERT NEW NRB IN Q AT (X)
	PSHB		SAVE COMPUTED DELTA
	LDAA	SDNETNRB:TIMEQBLINK,X	FIND NRB PREVIOUS TO INSERT POINT
	LDAB	SDNETNRB:TIMEQBLINK+1,X
	STX	SDNETINTTEMPX	SAVE INSERT POINT ADDRESS
	LDX	SDNETNRBPTR	MAKE INSERTED NODE POINT BACKWARD TO PREV NRB
	STAA	SDNETNRB:TIMEQBLINK,X
	STAB	SDNETNRB:TIMEQBLINK+1,X
	LDAA	SDNETINTTEMPX	MAKE INSERTED NODE POINT TO INSERT POINT NRB
	LDAB	SDNETINTTEMPX+1
	STAA	SDNETNRB:TIMEQFLINK,X
	STAB	SDNETNRB:TIMEQFLINK+1,X
	PULB		GRAB COMPUTED DELTA
	STAB	SDNETNRB:TIMEQDELTA,X	AND SAVE IT IN INSERTED NRB
	LDX	SDNETNRB:TIMEQBLINK,X	MOVE BACK TO PREV NRB
	LDAA	SDNETNRBPTR	MAKE PREV NRB POINT TO NEWLY INSERTED NRB
	LDAB	SDNETNRBPTR+1
	STAA	SDNETNRB:TIMEQFLINK,X
	STAB	SDNETNRB:TIMEQFLINK+1,X
	LDX	SDNETINTTEMPX	MAKE INSERT POINT BACK LINK =...
	STAA	SDNETNRB:TIMEQBLINK,X	NEWLY INSERTED NODE
	STAB	SDNETNRB:TIMEQBLINK+1,X
	LDX	SDNETTIMEQHEAD+SDNETNRB:TIMEQFLINK	GET POINTER TO FIRST NRB ON Q
	STX	SDNETNRBPTR	THIS IS THE MOST INTERESTING GUY IN THE Q
	LDAA	SDNETNRB:TIMEQDELTA,X	GET REMAINING DELAY
	STAA	SDNETTIMEQHEAD+SDNETNRB:TIMEQDELTA	AND SAVE IT AS NEXT TIMEOUT
	RTS		WHEW! ALL DONE

SDNETPUTNRBINQ3	; THIS IS INSERT POINT, ADJUST DELTA IN INSERT POINT NRB
	TBA		SAVE NEGATIVE DIFFERENCE
	ADDB	SDNETNRB:TIMEQDELTA,X	COMPUTE INSERTED NRB'S ACTUAL DELTA
	NEGA		= INSERT POINT NRB'S NEW DELTA
	STAA	SDNETNRB:TIMEQDELTA,X
	BRA	SDNETPUTNRBINQ2	GO INSERT NEW NRB
	PAGE
SDNETCPUIDCELL	FCB	SDNETOURCPUNUMBER
SDNETFREEBYTES	FDB	0	NUMBER OF FREE BYTES TO RECEIVE INTO
SDNETRDELTA	FDB	0	= SDNETRDVPOS-SDNETNRB:XVPOS
SDNETDESIREDELAYEDXMITNRBPTR	FDB	0	POINTER TO NRB
SDNETDESIREDELAYEDXMITLOCK	FCB	1	LOCK FOR SDNETDESIREDELAYEDXMITNRBPTR
SDNETDESIREXMITNRBPTR	FDB	0	POINTER TO NRB
SDNETDESIREXMITLOCK	FCB	1	LOCK FOR SDNETDESIREXMITNRBPTR
SDNETDESIRERCVNRBPTR	FDB	0	POINTER TO NRB
SDNETDESIRERCVLOCK	FCB	1	LOCK FOR SDNETDESIRERCVNRBPTR
SDNETDESIREQUITNRBPTR	FDB	0	POINT TO NRB
SDNETDESIREQUITLOCK	FCB	1	LOCK FOR SDNETDESIREQUITNRBPTR
SDNETDESIREKILLNRBPTR	FDB	0	POINTER TO NRB
SDNETDESIREKILLLOCK	FCB	1	LOCK FOR SDNETDESIREKILLNRBPTR
SDNETBUFCNT	FCB	0	BUFFER BYTE COUNT; CONTROLS SMALL LOOPS
SDNETINTNEXT	FDB	SDNETRINTERRUPT	NEXT SSDA INTERRUPT AIMED HERE
SDNETCOMPUTEDCRC	FDB	0	COMPUTED CRC, JUST LIKE IT SEZ
SDNETBUFPTR	FDB	0	TENTATIVE RECEIVER BUFFER FILL POINTER
SDNETINTTEMPX	FDB	0	TEMPORARY SAVE FOR X REGISTER

SDNETRDTO	RMB	1	WHO MESSAGE WAS SENT TO
SDNETRDFROM	FCB	0	WHO WE RECEIVED THE MESSAGE FROM
SDNETRDCONTROL	FCB	SDLPCONTROL:NORMAL	CONTROL BYTE SENT BY OTHER COMPUTER
SDNETRDRVPOS	RMB	2	VIRTUAL POSITION OF OUR FILE THAT OTHER COMPUTER HAS
SDNETRDREADYCOUNT	RMB	2	AVAILABLE BUFFER SPACE THAT OTHER COMPUTER HAS
SDNETRDXMITCOUNT	RMB	2	# DATA BYTES SENT BY OTHER COMPUTER IN THIS MESSAGE
SDNETRDXMITBASE	RMB	2	VIRTUAL ADDRESS MOD 2**16 OF FIRST DATA BYTE IN MESSAGE
SDNETPHASE1CNT	RMB	2	NUMBER OF DATA BYTES TO IGNORE
SDNETPHASE2CNT	RMB	2	NUMBER OF DATA BYTES TO PLACE INTO RECEIVER BUFFER
SDNETPHASE3CNT	RMB	2	NUMBER OF DATA BYTES TO IGNORE
SDNETPHASE2CNTSV	RMB	2	COPY OF SDNETPHASE2CNT

SDNETTIMEOUTFUSE	FCB	0	FUSE FOR COLLISION RESOLUTION AND EOT SEARCH
SDNETTIMEOUTROUTINE	FDB	0	POINTS TO TIMEOUT HANDLER
CLOCKTICKS	FCB	0	60THS OF A SECOND NOT YET TOLD TO SDOS
	PAGE
*
*	STATISTICS ABOUT LINK
*
SDNETENBRCVCOUNT	FDB	0	# TIMES WE ENABLED RECIEVER TO HEAR NEW MESSAGE
SDNETBADCRCCOUNT	FDB	0	# MSGS WITH BAD CRC RECEIVED
SDNETGOODCRCCOUNT	FDB	0	# MSGS WITH GOOD CRC RECEIVED
SDNETOVRUNCOUNT	FDB	0	# MSGS FOR WHICH WE WEREN'T FAST ENOUGH TO GET ALL THE BYTES
SDNETMISSEDEOTCOUNT	FDB	0	# TIMES WE SEARCHED FOR EOT AND TIMED OUT
SDNETNOCLOCKCOUNT	FDB	0	# TIMES RECEIVED MESSAGE STOPPED IN MIDDLE
SDNETCOLLISIONCOUNT	FDB	0	# TIMES THAT A COLLISION OCCURRED ON TRANSMIT
SDNETXMITOKCOUNT	FDB	0	# TIMES WE SUCCESSFULLY SENT A COMPLETE MESSAGE
SDNETETHERBUSYCOUNT	FDB	0	# TIMES ETHER WAS BUSY WHEN WE WANTED TO TRANSMIT
SDNETTIMEDOUTCOUNT	FDB	0	# TIMES WE TIMED OUT EXPECTING A RESPONSE

SDNETTIMEQHEAD1	FDB	SDNETTIMEQHEAD	SDNETNRB:TIMEQFLINK
	FDB	SDNETTIMEQHEAD	SDNETNRB:TIMEQBLINK
	FCB	0	SDNETNRB:TIMEQDELTA
SDNETTIMEQHEAD	EQU	SDNETTIMEQHEAD1-SDNETNRB:TIMEQFLINK

SDNETSTATECONTROL1	FDB	SDNETSTATECONTROL&$FF00	USED TO INDEX SDNETSTATECONTROL
*
*	TRANSLATE TABLE USED TO CONVERT SDNETNRB:STATE INTO SDLPCONTROL:XXXX
*
SDNETSTATECONTROL
	FCB	SDLPCONTROL:QUIT	NODESTATE:DEAD
	FCB	SDLPCONTROL:AGREEQUIT	NODESTATE:AGREEQUIT
	FCB	SDLPCONTROL:WANTQUIT	NODESTATE:WANTQUIT
	FCB	SDLPCONTROL:RESYNCHED	NODESTATE:INACTIVE
	FCB	SDLPCONTROL:RESYNCHREQ	NODESTATE:ACTIVATING
	FCB	SDLPCONTROL:RESYNCHED	NODESTATE:RESYNCHED
	FCB	SDLPCONTROL:NORMAL	NODESTATE:ACTIVATED
	FCB	SDLPCONTROL:NORMAL	NODESTATE:CONNECTEDR
	FCB	SDLPCONTROL:NORMAL	NODESTATE:CONNECTEDS
	FCB	SDLPCONTROL:QUIT	NODESTATE:QUIT

SDNETXMITTHRESHOLDVALUE	; CELL HOLDING MIN(SDNETXMTBUFOVFTHRESHOLD,SDNETXMITTHRESHOLD)
	IF	SDNETXMTBUFOVFTHRESHOLD>>SDNETXMITTHRESHOLD
	FDB	SDNETXMITTHRESHOLD	ENOUGH BYTES FOR "EFFICIENT" MESSAGE
	ELSE
	FDB	SDNETXMTBUFOVFTHRESHOLD
	FIN

	LIST	1

SDNETPATCH	RPT	50
	FCB	0

	LIST	LISTPACKETMANAGER
	PAGE	CRC TABLE
*****************************************************************
*
*	THIS IS THE PROGRAM USED TO COMPUTE THE CRC TABLE
*	THE BASIC IDEA IS THAT THE CRC COMPUTATION IS DONE A BYTE AT A TIME,
*	INSTEAD OF THE TRADITIONAL BIT AT A TIME APPROACH
*	THIS PROGRAM COMPUTES A CRC VALUE TO BE "XOR"ED FOR EACH...
*	OF THE 256 POSSIBLE PATTERNS THE NEXT BYTE OF THE DIVIDEND CAN TAKE
*
*	DIM I,J,WORD,CARRY,CRCDIVISOR/:1021/,TEMP$(5),FILE$(80)
*	INPUT "COMPUTE CRC CONSTANTS INTO FILE: " FILE$
*	CREATE #1,FILE$
*	FOR I=0 TO 255
*		WORD=I**8
*		FOR J=1 TO 8
*			CARRY = WORD & :8000
*			WORD = ( WORD & :7FFF ) **1
*			IF CARRY
*			THEN	WORD = WORD XOR CRCDIVISOR
*		NEXT J
*	TEMP$= HEX$(WORD)
*	PRINT #1,'	FDB	$';TEMP$(2,4);
*	TEMP$ = HEX$(I)
*	PRINT #1,'	:';TEMP$(4,2);I
*	NEXT I
*	STOP
*	END

	IF	*&1	ENSURE THAT CRC TABLE IS ON EVEN ADDRESS
	NOP
	FIN	*&1

SDNETCRCTABLEADD1	FDB	SDNETCRCTABLE
SDNETCRCTABLEADD2	FDB	SDNETCRCTABLE+$100

SDNETCRCTABLE	EQU	*
	FDB	$0000	:00 0
	FDB	$1021	:01 1
	FDB	$2042	:02 2
	FDB	$3063	:03 3
	FDB	$4084	:04 4
	FDB	$50A5	:05 5
	FDB	$60C6	:06 6
	FDB	$70E7	:07 7
	FDB	$8108	:08 8
	FDB	$9129	:09 9
	FDB	$A14A	:0A 10
	FDB	$B16B	:0B 11
	FDB	$C18C	:0C 12
	FDB	$D1AD	:0D 13
	FDB	$E1CE	:0E 14
	FDB	$F1EF	:0F 15
	FDB	$1231	:10 16
	FDB	$0210	:11 17
	FDB	$3273	:12 18
	FDB	$2252	:13 19
	FDB	$52B5	:14 20
	FDB	$4294	:15 21
	FDB	$72F7	:16 22
	FDB	$62D6	:17 23
	FDB	$9339	:18 24
	FDB	$8318	:19 25
	FDB	$B37B	:1A 26
	FDB	$A35A	:1B 27
	FDB	$D3BD	:1C 28
	FDB	$C39C	:1D 29
	FDB	$F3FF	:1E 30
	FDB	$E3DE	:1F 31
	FDB	$2462	:20 32
	FDB	$3443	:21 33
	FDB	$0420	:22 34
	FDB	$1401	:23 35
	FDB	$64E6	:24 36
	FDB	$74C7	:25 37
	FDB	$44A4	:26 38
	FDB	$5485	:27 39
	FDB	$A56A	:28 40
	FDB	$B54B	:29 41
	FDB	$8528	:2A 42
	FDB	$9509	:2B 43
	FDB	$E5EE	:2C 44
	FDB	$F5CF	:2D 45
	FDB	$C5AC	:2E 46
	FDB	$D58D	:2F 47
	FDB	$3653	:30 48
	FDB	$2672	:31 49
	FDB	$1611	:32 50
	FDB	$0630	:33 51
	FDB	$76D7	:34 52
	FDB	$66F6	:35 53
	FDB	$5695	:36 54
	FDB	$46B4	:37 55
	FDB	$B75B	:38 56
	FDB	$A77A	:39 57
	FDB	$9719	:3A 58
	FDB	$8738	:3B 59
	FDB	$F7DF	:3C 60
	FDB	$E7FE	:3D 61
	FDB	$D79D	:3E 62
	FDB	$C7BC	:3F 63
	FDB	$48C4	:40 64
	FDB	$58E5	:41 65
	FDB	$6886	:42 66
	FDB	$78A7	:43 67
	FDB	$0840	:44 68
	FDB	$1861	:45 69
	FDB	$2802	:46 70
	FDB	$3823	:47 71
	FDB	$C9CC	:48 72
	FDB	$D9ED	:49 73
	FDB	$E98E	:4A 74
	FDB	$F9AF	:4B 75
	FDB	$8948	:4C 76
	FDB	$9969	:4D 77
	FDB	$A90A	:4E 78
	FDB	$B92B	:4F 79
	FDB	$5AF5	:50 80
	FDB	$4AD4	:51 81
	FDB	$7AB7	:52 82
	FDB	$6A96	:53 83
	FDB	$1A71	:54 84
	FDB	$0A50	:55 85
	FDB	$3A33	:56 86
	FDB	$2A12	:57 87
	FDB	$DBFD	:58 88
	FDB	$CBDC	:59 89
	FDB	$FBBF	:5A 90
	FDB	$EB9E	:5B 91
	FDB	$9B79	:5C 92
	FDB	$8B58	:5D 93
	FDB	$BB3B	:5E 94
	FDB	$AB1A	:5F 95
	FDB	$6CA6	:60 96
	FDB	$7C87	:61 97
	FDB	$4CE4	:62 98
	FDB	$5CC5	:63 99
	FDB	$2C22	:64 100
	FDB	$3C03	:65 101
	FDB	$0C60	:66 102
	FDB	$1C41	:67 103
	FDB	$EDAE	:68 104
	FDB	$FD8F	:69 105
	FDB	$CDEC	:6A 106
	FDB	$DDCD	:6B 107
	FDB	$AD2A	:6C 108
	FDB	$BD0B	:6D 109
	FDB	$8D68	:6E 110
	FDB	$9D49	:6F 111
	FDB	$7E97	:70 112
	FDB	$6EB6	:71 113
	FDB	$5ED5	:72 114
	FDB	$4EF4	:73 115
	FDB	$3E13	:74 116
	FDB	$2E32	:75 117
	FDB	$1E51	:76 118
	FDB	$0E70	:77 119
	FDB	$FF9F	:78 120
	FDB	$EFBE	:79 121
	FDB	$DFDD	:7A 122
	FDB	$CFFC	:7B 123
	FDB	$BF1B	:7C 124
	FDB	$AF3A	:7D 125
	FDB	$9F59	:7E 126
	FDB	$8F78	:7F 127
	FDB	$9188	:80 128
	FDB	$81A9	:81 129
	FDB	$B1CA	:82 130
	FDB	$A1EB	:83 131
	FDB	$D10C	:84 132
	FDB	$C12D	:85 133
	FDB	$F14E	:86 134
	FDB	$E16F	:87 135
	FDB	$1080	:88 136
	FDB	$00A1	:89 137
	FDB	$30C2	:8A 138
	FDB	$20E3	:8B 139
	FDB	$5004	:8C 140
	FDB	$4025	:8D 141
	FDB	$7046	:8E 142
	FDB	$6067	:8F 143
	FDB	$83B9	:90 144
	FDB	$9398	:91 145
	FDB	$A3FB	:92 146
	FDB	$B3DA	:93 147
	FDB	$C33D	:94 148
	FDB	$D31C	:95 149
	FDB	$E37F	:96 150
	FDB	$F35E	:97 151
	FDB	$02B1	:98 152
	FDB	$1290	:99 153
	FDB	$22F3	:9A 154
	FDB	$32D2	:9B 155
	FDB	$4235	:9C 156
	FDB	$5214	:9D 157
	FDB	$6277	:9E 158
	FDB	$7256	:9F 159
	FDB	$B5EA	:A0 160
	FDB	$A5CB	:A1 161
	FDB	$95A8	:A2 162
	FDB	$8589	:A3 163
	FDB	$F56E	:A4 164
	FDB	$E54F	:A5 165
	FDB	$D52C	:A6 166
	FDB	$C50D	:A7 167
	FDB	$34E2	:A8 168
	FDB	$24C3	:A9 169
	FDB	$14A0	:AA 170
	FDB	$0481	:AB 171
	FDB	$7466	:AC 172
	FDB	$6447	:AD 173
	FDB	$5424	:AE 174
	FDB	$4405	:AF 175
	FDB	$A7DB	:B0 176
	FDB	$B7FA	:B1 177
	FDB	$8799	:B2 178
	FDB	$97B8	:B3 179
	FDB	$E75F	:B4 180
	FDB	$F77E	:B5 181
	FDB	$C71D	:B6 182
	FDB	$D73C	:B7 183
	FDB	$26D3	:B8 184
	FDB	$36F2	:B9 185
	FDB	$0691	:BA 186
	FDB	$16B0	:BB 187
	FDB	$6657	:BC 188
	FDB	$7676	:BD 189
	FDB	$4615	:BE 190
	FDB	$5634	:BF 191
	FDB	$D94C	:C0 192
	FDB	$C96D	:C1 193
	FDB	$F90E	:C2 194
	FDB	$E92F	:C3 195
	FDB	$99C8	:C4 196
	FDB	$89E9	:C5 197
	FDB	$B98A	:C6 198
	FDB	$A9AB	:C7 199
	FDB	$5844	:C8 200
	FDB	$4865	:C9 201
	FDB	$7806	:CA 202
	FDB	$6827	:CB 203
	FDB	$18C0	:CC 204
	FDB	$08E1	:CD 205
	FDB	$3882	:CE 206
	FDB	$28A3	:CF 207
	FDB	$CB7D	:D0 208
	FDB	$DB5C	:D1 209
	FDB	$EB3F	:D2 210
	FDB	$FB1E	:D3 211
	FDB	$8BF9	:D4 212
	FDB	$9BD8	:D5 213
	FDB	$ABBB	:D6 214
	FDB	$BB9A	:D7 215
	FDB	$4A75	:D8 216
	FDB	$5A54	:D9 217
	FDB	$6A37	:DA 218
	FDB	$7A16	:DB 219
	FDB	$0AF1	:DC 220
	FDB	$1AD0	:DD 221
	FDB	$2AB3	:DE 222
	FDB	$3A92	:DF 223
	FDB	$FD2E	:E0 224
	FDB	$ED0F	:E1 225
	FDB	$DD6C	:E2 226
	FDB	$CD4D	:E3 227
	FDB	$BDAA	:E4 228
	FDB	$AD8B	:E5 229
	FDB	$9DE8	:E6 230
	FDB	$8DC9	:E7 231
	FDB	$7C26	:E8 232
	FDB	$6C07	:E9 233
	FDB	$5C64	:EA 234
	FDB	$4C45	:EB 235
	FDB	$3CA2	:EC 236
	FDB	$2C83	:ED 237
	FDB	$1CE0	:EE 238
	FDB	$0CC1	:EF 239
	FDB	$EF1F	:F0 240
	FDB	$FF3E	:F1 241
	FDB	$CF5D	:F2 242
	FDB	$DF7C	:F3 243
	FDB	$AF9B	:F4 244
	FDB	$BFBA	:F5 245
	FDB	$8FD9	:F6 246
	FDB	$9FF8	:F7 247
	FDB	$6E17	:F8 248
	FDB	$7E36	:F9 249
	FDB	$4E55	:FA 250
	FDB	$5E74	:FB 251
	FDB	$2E93	:FC 252
	FDB	$3EB2	:FD 253
	FDB	$0ED1	:FE 254
	FDB	$1EF0	:FF 255
	LIST	1
	END		; SDNET DRIVER
