;----------------------------------------------------------------------------
;File name:	CEN_PLIP.S			Revision date:	1999.10.17
;Creator:	Ulf Ronald Andersson		Creation date:	1997.08.18
;(c)1997 by:	Ulf Ronald Andersson		All rights reserved
;Feedback to:	dlanor@oden.se			Released as FREEWARE
;----------------------------------------------------------------------------
	include	RA_TOS.I
	include	RA_JAR.I
	include	RA_XB.I
	include	RA_RAM.I
;
	include	sting\PORT.I
	include	sting\LAYER.I
	include	sting\TRANSPRT.I
;----------------------------------------------------------------------------
	output	.PRG
;----------------------------------------------------------------------------
XB_cpu_mode	set	1	;never test mode or call Super in XB-macros
USE_TIMER	=	0	;1 enables 'my_timer_func', 0 disables it.
USE_LOOPING	=	1	;1 enables multipacket sending, 0 disables it
;----------------------------------------------------------------------------
M_YEAR	=	1999
M_MONTH	=	10
M_DAY	=	17
;
M_TITLE	MACRO
	dc.b	'Centr. PLIP'
	ENDM
M_VERSION	MACRO
	dc.b	'01.19'
	ENDM
M_AUTHOR	MACRO
	dc.b	'Ronald Andersson'
	ENDM
;-------------------------------------
	struct	PLIP		;queue structure for raw datagram storage
	struc_p		PLIP_next	;-> next PLIP datagram
	uint16		PLIP_length	;length of raw datagram
	d_alias		PLIP_data	;data of varying size follows
	d_end	PLIP
;-------------------------------------
;	PLIP port constants
;-------------------------------------
MAX_mtu		=	4*Kb		;mtu may not be raised above this level
MAX_buffers	=	10		;max number of buffers used in plip_tx_q
;
MAX_portwork	=	10/5		;For high level STinG ports
MAX_delay	=	100/5		;tween-interrupt delays must be less
WATCH_max	=	64		;max number of loops per interrupt
WATCH_step	=	256/WATCH_max	;watch count step per interrupt loop
;
RAM_chunk	=	2*MAX_buffers*(MAX_mtu+sizeof_PLIP+2)
;-------------------------------------
; SLIP/PLIP character codes
;-------------------------------------
slip_ch_END	=	$C0	;indicates end of packet
slip_ch_ESC	=	$DB	;indicates byte stuffing
slip_ch_ESC_END	=	$DC	;ESC ESC_END means END data byte
slip_ch_ESC_ESC	=	$DD	;ESC ESC_ESC means ESC data byte
;----------------------------------------------------------------------------
;Start of:	STX program
;----------------------------------------------------------------------------
	SECTION	TEXT
;----------------------------------------------------------------------------
text_start:
basepage	=	(text_start-$100)
;----------------------------------------------------------------------------
start:
	bra	start_1
;----------------------------------------------------------------------------
;Start of:	Resident STX data
;----------------------------------------------------------------------------
my_port:
	dc.l	port_name_s	;prt_des_name
	dc.w	L_PAR_PTP	;prt_des_type
	dc.w	0		;prt_des_active		Activation flag
	dc.l	0		;prt_des_flags
	dc.l	0		;prt_des_ip_addr	IP_number
	dc.l	-1		;prt_des_sub_mask
	dc.w	MAX_mtu		;prt_des_mtu
	dc.w	MAX_mtu		;prt_des_max_mtu
	dc.l	0		;prt_des_stat_sd_data
	dc.l	0		;prt_des_send		->Tx queue
	dc.l	0		;prt_des_stat_rcv_data
	dc.l	0		;prt_des_receive	->Rx queue
	dc.w	0		;prt_des_stat_dropped
	dc.l	my_driver	;prt_des_driver		->driver struct
	dc.l	0		;prt_des_next		->next port
;------------------------------------
my_driver:
	dc.l	my_set_state	;drv_des_set_state
	dc.l	my_cntrl	;drv_des_cntrl
	dc.l	my_send		;drv_des_send
	dc.l	my_receive	;drv_des_receive
	dc.l	driver_name_s	;drv_des_name
	dc.l	version_s	;drv_des_version
	dc.w	((M_YEAR-1980)<<9)|(M_MONTH<<5)|M_DAY
	dc.l	author_s	;drv_des_author
	dc.l	0		;drv_des_next		->next driver
basepage_p:
	dc.l	basepage	;drv_des_basepage	->basepage of self
;------------------------------------
port_name_s:
driver_name_s:
	M_TITLE
	dc.b	NUL
	even
;------------------------------------
version_s:
	M_VERSION
	dc.b	NUL
	even
;------------------------------------
author_s:
	M_AUTHOR
	dc.b	NUL
	even
;------------------------------------
sting_drivers:	ds.l	1	;DRV_LIST	*sting_drivers;
tpl:		ds.l	1	;TPL		*tpl;
stx:		ds.l	1	;STX		*stx;
;------------------------------------
state_vector:	dc.l	cv_Dummy_State	;-> Interrupt state routine
;------------------------------------
state_t1:	dc.l	0	;long tick for state machine at work start
state_t2:	dc.l	0	;long tick for state machine after work
;
plip_open_f:	dc.w	0	;flags port open for traffic
;
plip_rx_q:	dc.l	0	;-> queue of raw plip datagrams received
recv_buf_p:	dc.l	0	;-> current raw plip datagram being received
recv_pos_p:	dc.l	0	;-> current receive position in datagram
recv_max_p:	dc.l	0	;-> receive position of datagram end
;
plip_tx_q_ct:	dc.w	0	;counts buffers in plip_tx_q
plip_tx_q:	dc.l	0	;-> queue of raw plip datagrams to send
send_buf_p:	dc.l	0	;-> current raw plip datagram to send
send_pos_p:	dc.l	0	;-> current send position in datagram
send_max_p:	dc.l	0	;-> send position of datagram end
;------------------------------------
plip_crc:	dc.w	0
plip_crc_ix:	dc.w	0
DOFF_char:	dc.w	0
;-------------------------------------
RX_ATT_pair:			;both bytes below as one word
RX_ATT_flag:	dc.b	$FF	;byte == 0 if RX_ATT_data valid
RX_ATT_data:	dc.b	$00	;byte == last received ATT data 
ref_perm_f:	dc.b	$00	;byte == $FF/$00 flagging permission to refuse reception (to send)
protect_f:	dc.b	$00
		EVEN
IO_progress_f:			;both bytes below, as one word
RX_progress_f:	dc.b	0
TX_progress_f:	dc.b	0
;------------------------------------
plip_regs:	ds.l	16
;------------------------------------
byte_watch:	dc.b	0
orig_imrb:	dc.b	0
orig_ierb:	dc.b	0
orig_aer:	dc.b	0
;------------------------------------
	IFNE	USE_LOOPING
port_time:	ds.w	1
	ENDC	;USE_LOOPING
;----------------------------------------------------------------------------
;End of:	Resident STX data
;----------------------------------------------------------------------------
;Start of:	Interrupt state machine macros
;----------------------------------------------------------------------------
def_state	MACRO	state
cv_\1:
	ENDM
;-------------------------------------
set_state	MACRO	state
	move.l	#cv_\1,state_vector
	ENDM
;-------------------------------------
loop_state	MACRO
	bra	state_loop
	ENDM
;-------------------------------------
goto_state	MACRO	state
	set_state	\1
	loop_state
	ENDM
;-------------------------------------
new_state	MACRO	state
	goto_state	\1
	def_state	\1
	ENDM
;-------------------------------------
pl_timeout	MACRO	ref_tick,limit,destination
	move.l		(_hz_200).w,d0
	sub.l		\1,d0
	cmp.l		\2,d0
	bhs.\0		\3
	ENDM
;----------------------------------------------------------------------------
;End of:	Interrupt state machine macros
;----------------------------------------------------------------------------
;-------------------------------------
;	PLIP port macros
;-------------------------------------
;macro pl_interrupt is used to enable system interrupts temporarily,
;mainly to allow the system timers to advance, but affects all interrupts
;allowed by the mask in given in the 'intmask' argument.
;
pl_interrupt	MACRO	intmask
	move		\1,sr		;reenable old interrupts
	or		#$0700,sr	;disable all interrupts
	ENDM	;pl_interrupt
;-------------------------------------
pl_strobe_LO	MACRO	temp
	move.b		#14,(hw_psgsel).w
	move.b		(hw_psgrd).w,\1
	bclr		#5,\1
	move.b		\1,(hw_psgwr).w
	ENDM	;pl_strobe_LO
;-------------------------------------
pl_strobe_HI	MACRO	temp
	move.b		#14,(hw_psgsel).w
	move.b		(hw_psgrd).w,\1
	bset		#5,\1
	move.b		\1,(hw_psgwr).w
	ENDM	;pl_strobe_HI
;-------------------------------------
pl_send_ACK	MACRO	temp1,temp2
	move.b		#14,(hw_psgsel).w	;select port A
	move.b		(hw_psgrd).w,\1		;get port A data
	move.b		\1,\2
	bclr		#5,\1
	bset		#5,\2
	move.b		\1,(hw_psgwr).w		;lower strobe
	move.b		\1,(hw_psgwr).w		;lower strobe
	move.b		\2,(hw_psgwr).w		;raise strobe
	move.b		\2,(hw_psgwr).w		;raise strobe
	ENDM	;pl_send_ACK
;-------------------------------------
pl_dir_in	MACRO	temp
	move.b		#7,(hw_psgsel).w
	move.b		(hw_psgrd).w,\1
	bclr		#7,\1
	move.b		\1,(hw_psgwr).w
	ENDM	;pl_dir_in
;-------------------------------------
pl_dir_out	MACRO	temp
	move.b		#7,(hw_psgsel).w
	move.b		(hw_psgrd).w,\1
	bset		#7,\1
	move.b		\1,(hw_psgwr).w
	ENDM	;pl_dir_out
;-------------------------------------
pl_recv_byte	MACRO	dest
	move.b		#15,(hw_psgsel).w	;select port B
	move.b		(hw_psgrd).w,\1		;dest.b = data from port B
	ENDM	;pl_recv_byte
;-------------------------------------
pl_send_byte	MACRO	data
	move.b		#15,(hw_psgsel).w	;select port B
	move.b		\1,(hw_psgwr).w		;write data byte to port B
	ENDM	;pl_send_byte
;-------------------------------------
pl_test_busy	MACRO
	btst		#0,(hw_gpip).w
	ENDM	;pl_test_busy
;-------------------------------------
pl_test_ACK	MACRO
	btst		#0,(hw_iprb).w
	ENDM	;pl_test_ACK
;-------------------------------------
pl_take_ACK	MACRO
	btst		#0,(hw_iprb).w
	beq.s		.done_\@
	move.b		#$FE,(hw_iprb).w
.done_\@:
	ENDM	;pl_take_ACK
;-------------------------------------
pl_clear_ACK	MACRO
	move.b		#$FE,(hw_iprb).w
	ENDM	;pl_clear_ACK
;-------------------------------------
pl_cli	MACRO
	bclr		#0,(hw_imrb).w		;disable 'busy' interrupt in MFP
	ENDM	;pl_cli
;-------------------------------------
pl_sti	MACRO
	bset		#0,(hw_imrb).w		;enable 'busy' interrupt in MFP
	ENDM	;pl_sti
;-------------------------------------
pl_abort_send	MACRO
	move.l		send_buf_p(pc),d0
	ble.s		.exit_abort_send
	move.l		d0,a0			;a0 -> aborted plip datagram
	move.l		plip_tx_q(pc),(a0)	;link datagram as root of queue
	move.l		a0,plip_tx_q		;and store it as queue root
	addq		#1,plip_tx_q_ct		;increment buffer count
	clr.l		send_buf_p
	addq		#1,prt_des_stat_dropped+my_port	;increment drop count
.exit_abort_send:
	pl_strobe_HI	d0			;normalize strobe
	pl_dir_in	d0			;set port for input
	sf		TX_progress_f
	set_state	Init_Recv
	ENDM	;pl_abort_send
;-------------------------------------
pl_abort_recv	MACRO
	tst.b		RX_progress_f		;are we in an early mode ?
	beq.s		.skip_dropping
	addq		#1,prt_des_stat_dropped+my_port	;increment drop count
.skip_dropping:
	pl_strobe_HI	d0			;normalize strobe
	pl_dir_in	d0			;set port for input
	sf		RX_progress_f
	set_state	Init_Recv
	ENDM	;pl_abort_recv
;----------------------------------------------------------------------------
;Start of:	Port driver functions
;----------------------------------------------------------------------------
;void	my_timer_func(void);
;
	IFNE	USE_TIMER
my_timer:
	rts
	ENDC	;USE_TIMER
;
;end of my_timer_func
;----------------------------------------------------------------------------
my_cntrl:
	link	a6,#0
	movem.l	d3-d5/a2-a5,-(sp)
	moveq	#E_PARAMETER,d0			;prep E_PARAMETER error for bad port arg
	lea	my_port(pc),a5			;a5 -> my_port
	cmpa.l	8(a6),a5			;port argument correct ?
	bne.s	.exit				;exit if port argument incorrect
	moveq	#E_FNAVAIL,d0			;prep E_FNAVAIL error code for unsupported opcodes
	move	16(a6),d2			;d2 = cntrl_port opcode
	lea	my_CTL_func_t(pc),a0		;a0 -> CTL function table
.seek_CTL_loop:				;loop start for seeking opcode in table
	move	(a0)+,d1			;d1 = CTL opcode from table
	ble.s	.exit				;exit with E_FNAVAIL if opcode unsupported
	move.l	(a0)+,a1			;a0 = CTL routine ptr from table
	cmp	d2,d1				;correct opcode now ?
	bne.s	.seek_CTL_loop			;loop back to seek correct opcode
.found_CTL:
	move.l	12(a6),d0			;d0 = long argument
	move.l	d0,a0				;a0 = long argument
	jsr	(a1)				;call a CTL subroutine
.exit:
	movem.l	(sp)+,d3-d5/a2-a5
	unlk	a6
	rts
;
def_CTL	MACRO	opcode,routine
	dc.w	\1
	dc.l	\2
	ENDM
;
my_CTL_func_t:
	def_CTL	CTL_GENERIC_SET_IP,my_SET_IP
	def_CTL	CTL_GENERIC_GET_IP,my_GET_IP
	def_CTL	CTL_GENERIC_SET_MTU,my_SET_MTU
	def_CTL	CTL_GENERIC_GET_MTU,my_GET_MTU
	def_CTL	CTL_GENERIC_GET_MMTU,my_GET_MMTU
	def_CTL	CTL_GENERIC_GET_TYPE,my_GET_TYPE
	def_CTL	CTL_GENERIC_GET_STAT,my_GET_STAT
	def_CTL	CTL_GENERIC_CLR_STAT,my_CLR_STAT
	dc.w	0
;
;end of my_cntrl
;----------------------------------------------------------------------------
;Start of:	CTL subroutines
;----------------------------------------------------------------------------
my_SET_IP:
	move.l	d0,prt_des_ip_addr(a5)
	moveq	#E_NORMAL,d0
	rts
;------------------------------------
my_GET_IP:
	move.l	prt_des_ip_addr(a5),(a0)
	moveq	#E_NORMAL,d0
	rts
;------------------------------------
my_SET_MTU:
	clr.l	d1
	move	prt_des_max_mtu(a5),d1
	cmp.l	d1,d0
	bhi.s	E_PARAMETER_rts
	cmp	#48,d0
	bhs.s	accept_set_mtu
E_PARAMETER_rts:
	moveq	#E_PARAMETER,d0
	rts
;
accept_set_mtu:
	move	d0,prt_des_mtu(a5)
	moveq	#E_NORMAL,d0
	rts
;------------------------------------
my_GET_MTU:
	move	prt_des_mtu(a5),(a0)
	moveq	#E_NORMAL,d0
	rts
;------------------------------------
my_GET_MMTU:
	move	prt_des_max_mtu(a5),(a0)
	moveq	#E_NORMAL,d0
	rts
;------------------------------------
my_GET_TYPE:
	move	prt_des_type(a5),(a0)
	moveq	#E_NORMAL,d0
	rts
;------------------------------------
my_GET_STAT:
	clr.l	d0
	move	prt_des_stat_dropped(a5),d0
	move.l	d0,(a0)+
	move.l	prt_des_stat_sd_data(a5),(a0)+
	move.l	prt_des_stat_rcv_data(a5),(a0)+
	moveq	#E_NORMAL,d0
	rts
;------------------------------------
my_CLR_STAT:
	clr	prt_des_stat_dropped(a5)
	clr.l	prt_des_stat_sd_data(a5)
	clr.l	prt_des_stat_rcv_data(a5)
	moveq	#E_NORMAL,d0
	rts
;----------------------------------------------------------------------------
;End of:	CTL subroutines
;----------------------------------------------------------------------------
;void	my_send(PORT *port);
;
my_send:
	move.l		my_port+prt_des_send(pc),d0	;anything to send
	ble.s		.exit_direct			;exit if none to send
	cmpi.l		#my_port,4(sp)			;argument == my_port ?
	bne.s		.exit_direct			;exit if argument incorrect
	movem.l		d2-d3/a2-a5,-(sp)		;protect entry regs
	lea		my_port+prt_des_send(pc),a5	;a5 ->Tx queue ptr
	move.l		d0,d3				;d3 ->Tx queue
;
	IFNE	USE_LOOPING
	move		(_hz_200+2).w,port_time		;find work start time
	ENDC	;USE_LOOPING
.send_loop:
	move.l		d3,a4				;a4 -> Tx dgram
	move.l		IPDG_next(a4),(a5)		;unlink chain
	check_dgram_ttl	(a4)				;check TTL
	tst		d0
	bpl.s		.send_it			;continue if ok
	addq		#1,prt_des_stat_dropped-prt_des_send(a5)
	bra.s		.skip_it
;
.send_it:
	bsr		pass_send_pkt
	bgt.s		.count_sent_data
	move.l		(a5),IPDG_next(a4)	;relink to chain head
	move.l		a4,(a5)			;relink as chain head again
	bra.s		.exit
;
.count_sent_data:
	add.l		d0,prt_des_stat_sd_data-prt_des_send(a5)	;sent data
	bsr		retrigger_plip			;retrigger PLIP
.discard_datagram:
	IP_discard	(a4),#1				;discard datagram
.skip_it:
	IFNE	USE_LOOPING
	move.l		(a5),d3			;d3 = Tx queue, empty ?
	ble.s		.exit			;exit when queue empty
	move		(_hz_200+2).w,d0
	sub		port_time(pc),d0	;d0 = elapsed time
	cmp		#MAX_portwork,d0	;timeout ?
	blo		.send_loop		;loop back until 20 ms
	ENDC	;USE_LOOPING
.exit:
	movem.l		(sp)+,d2-d3/a2-a5		;restore entry regs
.exit_direct:
	bra		retrigger_plip			;retrigger PLIP
;
;end of my_send
;----------------------------------------------------------------------------
;void	my_receive(PORT *port);
;
my_receive:
	cmpi.l		#my_port,4(sp)		;argument == my_port ?
	bne		.exit_direct		;exit if argument incorrect
	move.l		plip_rx_q(pc),d0	;d0 -> raw Rx Queue, empty ?
	beq		.exit_direct		;exit directly if empty
	move		sr,-(sp)		;push sr
	movem.l		d2-d5/a2-a5,-(sp)	;push entry regs
	lea		my_port(pc),a5		;a5 -> my_port
;-------
	IFNE	USE_LOOPING
	move		(_hz_200+2).w,port_time		;find work start time
.transfer_loop:			;loop start to transfer raw dgrams to internal
	ENDC	;USE_LOOPING
	or		#$0700,sr		;disable interrupts
	move.l		plip_rx_q(pc),d0	;d0 -> raw Rx Queue, empty ?
	beq.s		.exit			;we're done if empty
	move.l		d0,a4			;a4 -> raw Rx queue
	move.l		(a4),plip_rx_q		;unlink first Rx datagram
	move		8*4(sp),sr		;reenable interrupts
;-------
	lea		PLIP_length(a4),a0	;a0 -> raw datagram length
	move		(a0)+,d0		;d0 = length,  a0 -> data
	clr.l		-(sp)			;reserve result space on stack
	lea		(sp),a1			;a1 -> result space
	bsr		make_IP_dgram		;create an internal datagram
	move.l		(sp)+,d0		;pop result to d0
	ble.s		.error			;drop packet on failure
;-------
	move.l		d0,a3			;a3 -> internal datagram
	set_dgram_ttl	(a3)			;init time-to-live data
;-------
	lea		prt_des_receive(a5),a0	;a0 = receive queue root -> first datagram
	bra.s		.store_test
;-------------------------------------
.store_loop:
	lea		IPDG_next(a1),a0	;a0 = link -> next datagram
.store_test:
	move.l		(a0),a1			;a1 -> next datagram or is NULL
	move.l		a1,d0			;test next datagram
	bne.s		.store_loop		;loop until queue end found
	move.l		a3,(a0)			;store new dgram at queue end
	clr.l		d0
	move		PLIP_length(a4),d0
	add.l		d0,prt_des_stat_rcv_data(a5)	;count received data
	bra.s		.release		;go release raw datagram
;-------------------------------------
.error:
	addq		#1,prt_des_stat_dropped(a5)	;increment drop count
.release:
	or		#$0700,sr		;disable interrupts
	R_free		(a4)			;release raw datagram buffer
	move		8*4(sp),sr		;reenable interrupts
	IFNE	USE_LOOPING
	move.l		plip_rx_q(pc),d3	;d3 -> Rx queue, empty ?
	ble.s		.exit			;exit when queue empty
	move		(_hz_200+2).w,d0
	sub		port_time(pc),d0	;d0 = elapsed time
	cmp		#MAX_portwork,d0	;timeout ?
	blo		.transfer_loop		;loop back until 20 ms
	ENDC	;USE_LOOPING
.exit:
	movem.l		(sp)+,d2-d5/a2-a5	;pull entry regs
	move		(sp)+,sr		;pull sr
.exit_direct:
	bra		retrigger_plip
;
;end of my_receive
;----------------------------------------------------------------------------
;uint16	my_set_state(PORT *port, uint16 state);
;
my_set_state:
	link		a6,#0
	movem.l		d2-d5/a2-a5,-(sp)
	lea		my_port(pc),a5		;a5 -> my_port
	clr.l		d0			;prep error flag
	cmpa.l		8(a6),a5		;argument correct ?
	bne.s		.exit			;exit if argument incorrect
	move		12(a6),d0		;d0 = new state
	beq.s		.passivate
.activate:
;
;Add port dependent activation code here
;This must include all initialization the port needs
;
	xbios		Supexec,pl_open(pc)
;
	bra.s		.done
;
.passivate:
;
;Add port dependent passivation code here
;This must include release of any KRmalloc blocks etc
;
	xbios		Supexec,pl_close(pc)
;	
.done:
	moveq		#1,d0
.exit:
	movem.l		(sp)+,d2-d5/a2-a5
	unlk		a6
	rts
;
;end of my_set_state
;----------------------------------------------------------------------------
;End of:	Port driver functions
;----------------------------------------------------------------------------
;Start of:	Resident subroutines
;----------------------------------------------------------------------------
;Aregs:	a0-a3 are free,  a4->datagram
;Dregs  d0-d3 are free
;
pass_send_pkt:
	clr.l		d0				;d0 = NULL  (error)
	cmpi		#MAX_buffers,plip_tx_q_ct	;too many buffers ?
	bhs.s		.exit_direct			;exit if past limit
;
	moveq		#sizeof_IPHD,d3
	add		IPDG_opt_length(a4),d3
	add		IPDG_pkt_length(a4),d3	;d3 = PLIP buffer size
	moveq		#sizeof_PLIP,d0		;d0 = PLIP header size
	add		d3,d0			;d0 = total size of PLIP struct
	move		sr,-(sp)		;push int_mask
	ori		#$0700,sr		;disable int_mask
	R_alloc		d0			;allocate RA RAM
	move		(sp)+,sr		;pull int_mask
	tst.l		d0			;did we get any ?
	ble.s		.exit_direct		;exit if no RAM available
;
	move.l		d0,a2			;a2 -> PLIP buffer
	clr.l		(a2)			;init link to next buffer
	move		d3,PLIP_length(a2)	;set data length of this one
;
	lea		IPDG_hdr(a4),a0		;a0 -> IP header
	lea		PLIP_data(a2),a1	;a1 -> PLIP buffer
	moveq		#sizeof_IPHD,d0		;d0 =  standard IP header size
	buf_copy_l.w	a0,a1,d0		;copy header as longs
	move.l		IPDG_options(a4),a0
	move		IPDG_opt_length(a4),d0
	buf_copy_l.w	a0,a1,d0		;copy options as longs
	move.l		IPDG_pkt_data(a4),a0
	move		IPDG_pkt_length(a4),d0
	buf_copy_b.w	a0,a1,d0		;copy packet data as bytes
;
	move		sr,d2			;save interrupt mask in d2
	ori		#$0700,sr		;disable interrupts
;
	lea		plip_tx_q(pc),a0	;a0 -> send queue root ptr
	move.l		a0,d0			;d0 -> send queue root ptr
.store_loop:
	move.l		d0,a0			;a0 -> current chain ptr
	move.l		(a0),d0			;d0 -> next datagram or is NULL
	bne		.store_loop		;loop until queue end reached
	move.l		a2,(a0)			;append new buffer to queue end
	addq		#1,plip_tx_q_ct		;increment buffer count
	move		d2,sr			;restore interrupt mask
	move.l		d3,d0			;return PLIP packet length
.exit_direct:
	rts		;error <= zero (flagged) else => PLIP length
;
;ends	pass_send_pkt
;----------------------------------------------------------------------------
retrigger_plip:
	move		sr,-(sp)		;push old SR (for interrupt mask)
	or		#$0700,sr		;disable interrupts
	move.l		state_vector(pc),d0	;d0 -> current mode routine
	cmp.l		#cv_Init_Recv,d0	;are we in the initial mode ?
	bls.s		.try_more_sending	;try sending when resting
	pl_timeout.s	state_t1(pc),#MAX_delay,.timeout_1
	bra.s		.exit
;-------
.timeout_1:
	pl_timeout.s	state_t2(pc),#MAX_delay,.timeout_2
	bra.s		.exit
;-------
.timeout_2:
	bsr		abort_transfer		;abort old traffic at timeout
.try_more_sending:
	move		plip_tx_q_ct(pc),d0	;Is there anything to send ?
	ble.s		.exit
.send_stuff:
	pl_strobe_HI	d0			;normalize strobe
	move.l		(_hz_200).w,state_t1	;memorize start time
	pl_send_ACK	d0,d1			;send ACK as initial RTS
	set_state	Init_Send		;set initial sending state
.exit:
	move		(sp)+,sr		;restore entry interrupt mask
.exit_direct:
	rts					;return to caller
;
;ends	retrigger_plip
;----------------------------------------------------------------------------
;void  make_IP_dgram (uint8 *buffer, int16 buff_len, IP_DGRAM **dgram);
;
make_IP_dgram:
	movem.l		d3-d5/a3-a5,-(sp)
	move.l		a0,a4			;a4 is buffer
	move		d0,d5			;d5 is buff_len
	move.l		a1,a5			;a5 is dgram
	clr.l		(a5)			;*dgram = NULL
	cmp		#sizeof_IPHD,d5
	ble		.exit			;if (buff_len < sizeof (IP_HDR)) return;
;-------
	KRmalloc	#sizeof_IPDG
	tst.l		d0
	ble		.exit			;if ((temp = KRmalloc (sizeof (IP_DGRAM))) == NULL) return;
	move.l		d0,a3			;a3 is temp
	lea		IPDG_hdr(a3),a1		;dst_arg = & temp->hdr
	move		#sizeof_IPHD,d0		;len_arg = sizeof_IPHD
	buf_copy_l.w	a4,a1,d0		;memcpy (& temp->hdr, buffer, sizeof (IP_HDR)); buffer += sizeof (IP_HDR);
;-------
	cmp		IPDG_hdr+IPHD_length(a3),d5
	blo		.free_a3		;if (temp->hdr.length > buff_len
	move.b		IPDG_hdr+IPHD_verlen_f(a3),d0
	and.l		#amask_IPHD_f_hd_len,d0
	asl		#2,d0
	cmp		d5,d0
	bhi.s		.free_a3		;|| (temp->hdr.hd_len << 2) > buff_len
	move		d0,d3			;d3 = (temp->hdr.hd_len << 2)
	sub		#sizeof_IPHD,d0
	blt.s		.free_a3		;|| temp->hdr.hd_len < 5) {KRfree (temp); return;}
	move		d0,IPDG_opt_length(a3)
	KRmalloc	d0
	move.l		d0,IPDG_options(a3)	;temp->options  = KRmalloc (temp->opt_length = (temp->hdr.hd_len << 2) - sizeof (IP_HDR));
	clr.l		d0
	move		IPDG_hdr+IPHD_length(a3),d0
	sub		d3,d0
	move		d0,IPDG_pkt_length(a3)
	KRmalloc	d0
	move.l		d0,IPDG_pkt_data(a3)	;temp->pkt_data = KRmalloc (temp->pkt_length = temp->hdr.length - (temp->hdr.hd_len << 2));
	ble.s		.discard_a3
	move.l		d0,d3			;d3 = temp->pkt_data
	move.l		IPDG_options(a3),d0	;d0 = temp->options
	ble.s		.discard_a3		;if (temp->options == NULL || temp->pkt_data == NULL ) {IP_discard (temp, TRUE); return;}
	move.l		d0,a1			;a1 = temp->options
	move		IPDG_opt_length(a3),d0
	beq.s		.have_option
	buf_copy_l.w	a4,a1,d0		;memcpy (temp->options, buffer, temp->opt_length);
.have_option:
	move.l		d3,a1			;a1 = temp->pkt_data
	move		IPDG_pkt_length(a3),d0
	beq.s		.have_data
	buf_copy_b.w	a4,a1,d0		;memcpy (temp->pkt_data, buffer + temp->opt_length, temp->pkt_length);
.have_data:
	clr.l		IPDG_next(a3)
	move.l		a3,(a5)			;*dgram = temp;
.exit:
	movem.l		(sp)+,d3-d5/a3-a5
	rts
;
.free_a3:
	KRfree		(a3)
	bra		.exit
;
.discard_a3:
	IP_discard	(a3),#1
	bra		.exit
;
;ends	make_IP_dgram
;----------------------------------------------------------------------------
;void  pl_open(void);
;
pl_open:
;-------
	tst.b		plip_open_f
	beq.s		.plip_closed
	bsr		pl_close
.plip_closed:
	bsr		release_buffers
;-------
	move.b		(hw_imrb).w,d3		;d3 = entry hw_imrb
	pl_cli					;mask reception interrupt
	move		sr,-(sp)		;push interrupt mask
	or		#$0700,sr		;disable interrupts
;-------
	pl_dir_in	d0			;set port for input
	pl_strobe_HI	d0			;normalize strobe
	pl_take_ACK				;clear pending ACK if present
;-------
	XB_remove	cent_busy_XB(pc),(iv_cenbusy).w	;unlink interrupt code
	bpl.s		.done_primary_open
	move.b		d3,orig_imrb		;save initial hw_imrb state
	move.b		(hw_ierb).w,orig_ierb	;save initial hw_ierb state
	move.b		(hw_aer).w,orig_aer	;save initial hw_aer state
.done_primary_open:
;-------
	XB_install	cent_busy_XB(pc),(iv_cenbusy).w	;link interrupt code
	bset		#0,(hw_ierb).w		;enable BUSY interrupt
	bclr		#0,(hw_aer).w		;set BUSY edge sense to falling
	pl_take_ACK				;ignore misfire at switch
	set_state	Init_Recv
	move		(sp)+,sr		;reenable interrupts
	pl_take_ACK				;clear pending ACK if present
	pl_sti					;unmask reception interrupt
	st		plip_open_f
	rts
;-------
;ends	pl_open
;----------------------------------------------------------------------------
;void  pl_close(void);
;
pl_close:
	move		sr,-(sp)		;push interrupt mask
	or		#$0700,sr		;disable interrupts
	set_state	Dummy_State
;-------
	move.b		(hw_imrb).w,d3		;d3 = entry hw_imrb
	pl_cli					;mask reception interrupt
	pl_dir_in	d0			;set port for input
	pl_strobe_HI	d0			;normalize strobe
	pl_take_ACK				;clear pending ACK if present
;
	XB_remove	cent_busy_XB(pc),(iv_cenbusy).w
	bmi.s		.done_primary_close
	move.b		orig_imrb(pc),d3	;d3 = original imrb
;
	btst		#0,orig_ierb(pc)
	beq.s		.clr_b0_ierb
.set_b0_ierb:
	bset		#0,(hw_ierb).w
	bra.s		.done_ierb
;
.clr_b0_ierb:
	bclr		#0,(hw_ierb).w
.done_ierb:
;-------
	btst		#0,orig_aer(pc)
	beq.s		.clr_b0_aer
.set_b0_aer:
	bset		#0,(hw_aer).w
	bra.s		.done_aer
;-------
.clr_b0_aer:
	bclr		#0,(hw_aer).w
.done_aer:
;-------
.done_primary_close:
	pl_take_ACK				;clear pending ACK if present
;-------
	btst		#0,d3
	beq.s		.clr_b0_imrb
.set_b0_imrb:
	bset		#0,(hw_imrb).w
	bra.s		.done_imrb
;
.clr_b0_imrb:
	bclr		#0,(hw_imrb).w
.done_imrb:
;-------
	sf		plip_open_f
	move		(sp)+,sr		;pull interrupt mask
release_buffers:
	move		sr,-(sp)		;push interrupt mask
	or		#$0700,sr		;disable interrupts
	pl_strobe_HI	d0			;normalize strobe
	pl_dir_in	d0			;set port for input
	move.l		recv_buf_p(pc),d0
	ble.s		.recv_buf_released
	R_free.i	d0
	clr.l		recv_buf_p
.recv_buf_released:
.release_rx_queue:
	pl_interrupt	(sp)			;poll interrupts
	move.l		plip_rx_q(pc),d0	;d0 -> raw datagram or is null
	beq.s		.rx_queue_released
	move.l		d0,a0			;a0 -> raw datagram
	move.l		(a0),plip_rx_q		;unlink this raw datagram
	R_free		(a0)			;release allocated buffer
	bra		.release_rx_queue
;
.rx_queue_released:
	move.l		send_buf_p(pc),d0
	ble.s		.send_buf_released
	R_free.i	send_buf_p(pc)
	clr.l		send_buf_p
.send_buf_released:
.release_tx_queue:
	pl_interrupt	(sp)			;poll interrupts
	move.l		plip_tx_q(pc),d0	;d0 -> raw datagram or is null
	beq.s		.tx_queue_released
	move.l		d0,a0			;a0 -> raw datagram
	move.l		(a0),plip_tx_q		;unlink this raw datagram
	R_free		(a0)			;release allocated buffer
	bra		.release_tx_queue
;
.tx_queue_released:
	clr		plip_tx_q_ct
	move		(sp)+,sr		;pull interrupt mask
	rts
;-------
;ends	pl_close
;----------------------------------------------------------------------------
allocate_recv_buf:
	move.l		recv_buf_p(pc),d0	;is there an old buffer ?
	bgt.s		.have_buffer		;keep old buffer if present
	R_alloc		#sizeof_PLIP+MAX_mtu+2	;allocate raw datagram buffer
	tst.l		d0			;did we get any ?
	ble.s		.exit			;exit on allocation failure
	move.l		d0,recv_buf_p
.have_buffer:
	move.l		d0,a0			;a0 -> receive buffer
	clr.l		(a0)			;clear its own queue link
	move		#MAX_mtu,d0
	move		d0,PLIP_length(a0)	;setup its max data length
	lea		PLIP_data(a0),a1
	move.l		a1,recv_pos_p		;init buffer position ptr
	lea		PLIP_data(a0,d0.w),a1	;a1 -> buffer end
	move.l		a1,recv_max_p		;init buffer end ptr
.exit:
	rts		;GT with d0 = a0 -> buf, or LE with d0 <= 0
;----------------------------------------------------------------------------
abort_transfer:
	clr		-(sp)			;push 0 as prel return value
	move		sr,-(sp)		;push SR (for interrupt mask)
	or		#$0700,sr		;disable interrupts
	move		IO_progress_f(pc),d0
	beq		.nothing_to_abort
	move.b		TX_progress_f(pc),d0
	beq.s		.done_TX_abort
	movem.l		d2/a2,-(sp)
	pl_abort_send
	movem.l		(sp)+,d2/a2
.done_TX_abort:
	move.b		RX_progress_f(pc),d0
	beq.s		.done_RX_abort
	movem.l		d2/a2,-(sp)
	pl_abort_recv
	movem.l		(sp)+,d2/a2
.done_RX_abort:
.nothing_to_abort:
	set_state	Init_Recv
	move		(sp)+,sr		;pull entry interrupt mask
	move		(sp)+,d0		;pull return value
	ext.l		d0			;make value long
	rts					;return to caller
;-------
;ends	abort_transfer
;----------------------------------------------------------------------------
;End of:	Resident subroutines
;----------------------------------------------------------------------------
;Start of:	Resident interrupt routines
;----------------------------------------------------------------------------
	XB_define	cent_busy_XB,'plip'	;XBRA header
	tas		protect_f
	bmi.s		protect_bomb
	or		#$0700,sr		;disable all interrupts
	movem.l		d0-d2/a0-a2,plip_regs
	move		(sp),-(sp)		;repush pre-exception SR
	clr		d0
	move.b		(sp),d0
	move.b		SR_table(pc,d0),(sp)	;patch it for polling SR
	move.l		(_hz_200).w,state_t1	;register time tick before work
	pl_take_ACK				;debounce ACK jitter
	bra		state_entry
;-------------------------------------
protect_bomb:
	illegal
	bra.s		protect_bomb
;-------------------------------------
SR_table:
	rept	8
	dc.b	$24,$24,$24,$24,$24,$25,$26,$27,$24,$24,$24,$24,$24,$25,$26,$27
	endr
	rept	8
	dc.b	$A4,$A4,$A4,$A4,$A4,$A5,$A6,$A7,$A4,$A4,$A4,$A4,$A4,$A5,$A6,$A7
	endr
;-------------------------------------
state_entry:
	pl_strobe_HI	d0			;normalize strobe
	pl_dir_in	d0			;normalize port direction
	pl_test_busy				;ACK or ATT ?
	sne		RX_ATT_flag		;flag normal ACK presence
	pl_recv_byte	RX_ATT_data		;store RX_ATT_data
.state_jump:
	move		RX_ATT_pair(pc),d0
	movea.l		state_vector(pc),a0	;a0 -> state routine
	jmp		(a0)			;jump to state routine
;-------------------------------------
state_loop:
	move.l		(_hz_200).w,state_t2	;register time tick after work
	addq.b		#WATCH_step,byte_watch	;count loops
	bcs.s		state_exit		;exit anyway when watch overflows
	pl_test_ACK				;new interrupt pending ?
	beq.s		state_exit		;else just exit
	pl_clear_ACK
	pl_take_ACK				;debounce ACK jitter
	pl_interrupt	(sp)			;poll interrupts
	bra		state_entry
;-------
state_exit:
	sf		protect_f
	addq		#2,sp			;pop polling sr off stack
	movem.l		plip_regs(pc),d0-d2/a0-a2
	move.b		#$FE,(hw_isrb).w	;flag service to hardware
	rte					;return from exception
;----------------------------------------------------------------------------
	def_state	Dummy_State	;-------;This state does nothing,
	loop_state			;-------;and remains until forced.
;----------------------------------------------------------------------------
	def_state	Init_Recv	;-------
	bpl.s		state_exit		;unwanted CTS => ignore it
	bsr		allocate_recv_buf	;ensure buffer allocation
	ble.s		state_exit		;no RAM => ignore RTS
	pl_strobe_LO	d0			;strobe low as initial CTS
	new_state	Send_RTS	;-------
	bpl		back_Init_Recv		;unwanted CTS => Init_Recv
	pl_send_ACK	d0,d1			;send ACK as final CTS
	new_state	Recv_First	;-------
	bpl.s		back_Init_Recv		;unwanted CTS => Init_Recv
	cmp.b		#slip_ch_END,d0		;END code ?
	beq.s		back_Init_Recv		;initial END code => Init_Recv
	move.l		recv_pos_p(pc),a0	;a0 -> IP packet position
	cmpa.l		recv_max_p(pc),a0	;is it too high ?
	bhs.s		back_Init_Recv		;if too high, abort reception
	pl_send_ACK	d1,d2			;send ACK as data receipt
	cmp.b		#slip_ch_ESC,d0		;ESC code ?
	beq.s		go_Recv_First_ESC	;go handle ESC when needed
	move.b		d0,(a0)+		;store IP packet byte
	move.l		a0,recv_pos_p		;update position pointer
	st		RX_progress_f		;note RX in progress
	goto_state	Recv_Loop	;-------
;-------------------------------------
back_Init_Recv:
	goto_state	Init_Recv	;-------
;-------------------------------------
go_Recv_First_ESC:
	new_state	Recv_First_ESC	;-------
	bpl		back_Init_Recv		;unwanted CTS => Init_Recv
	cmp.b		#slip_ch_ESC_END,d0	;is this ESC_END
	bne.s		Recv_First_ESC_not_END	;go handle other ESCs than END
	pl_send_ACK	d1,d2			;send ACK as data receipt
	move.l		recv_pos_p(pc),a0	;a0 -> IP packet position
	move.b		#slip_ch_END,(a0)+	;store END as IP packet byte
	move.l		a0,recv_pos_p		;update position pointer
	st		RX_progress_f		;note RX in progress
	goto_state	Recv_Loop	;-------
;-------------------------------------
Recv_First_ESC_not_END:
	cmp.b		#slip_ch_ESC_ESC,d0	;is this ESC_ESC
	bne		back_Init_Recv		;unknown code => Init_Recv
	pl_send_ACK	d1,d2			;send ACK as data receipt
	move.l		recv_pos_p(pc),a0	;a0 -> IP packet position
	move.b		#slip_ch_ESC,(a0)+	;store IP packet byte
	move.l		a0,recv_pos_p		;update position pointer
	st		RX_progress_f		;note RX in progress
	new_state	Recv_Loop	;-------
	bpl.s		abort_Recv_Loop		;unwanted CTS => abort
	cmp.b		#slip_ch_END,d0		;END code ?
	beq		Recv_Loop_END		;END code => finish packet
	move.l		recv_pos_p(pc),a0	;a0 -> IP packet position
	cmpa.l		recv_max_p(pc),a0	;is it too high ?
	bhs.s		abort_Recv_Loop		;if too high, abort reception
	pl_send_ACK	d1,d2			;send ACK as data receipt
	cmp.b		#slip_ch_ESC,d0		;ESC code ?
	beq.s		go_Recv_Loop_ESC	;go handle ESC when needed
	move.b		d0,(a0)+		;store IP packet byte
	move.l		a0,recv_pos_p		;update position pointer
	loop_state			;-------
;-------------------------------------
abort_Recv_Loop:
	pl_abort_recv
	goto_state	Init_Recv	;-------
;-------------------------------------
go_Recv_Loop_ESC:
	new_state	Recv_Loop_ESC	;-------
	bpl		abort_Recv_Loop		;unwanted CTS => abort
	cmp.b		#slip_ch_ESC_END,d0	;is this ESC_END
	bne.s		Recv_Loop_ESC_not_END	;go handle other ESCs than END
	pl_send_ACK	d1,d2			;send ACK as data receipt
	move.l		recv_pos_p(pc),a0	;a0 -> IP packet position
	move.b		#slip_ch_END,(a0)+	;store END as IP packet byte
	move.l		a0,recv_pos_p		;update position pointer
	goto_state	Recv_Loop	;-------
;-------------------------------------
Recv_Loop_ESC_not_END:
	cmp.b		#slip_ch_ESC_ESC,d0	;is this ESC_ESC
	bne		abort_Recv_Loop		;unknown code => abort
	pl_send_ACK	d1,d2			;send ACK as data receipt
	move.l		recv_pos_p(pc),a0	;a0 -> IP packet position
	move.b		#slip_ch_ESC,(a0)+	;store IP packet byte
	move.l		a0,recv_pos_p		;update position pointer
	goto_state	Recv_Loop	;-------
;-------------------------------------
Recv_Loop_END:
	pl_send_ACK	d1,d2			;send ACK as data receipt
	move.l		recv_buf_p(pc),a0	;a0 -> recv buffer struct
	move.l		recv_pos_p(pc),d0	;d0 -> end of received data
	lea		PLIP_data(a0),a1	;a1 -> start of received data
	sub.l		a1,d0			;d0 = received data length
	move		d0,PLIP_length(a0)	;store real data length
	lea		plip_rx_q(pc),a0	;a0 -> receive queue root ptr
	move.l		a0,d0			;d0 -> receive queue root ptr
.store_loop:
	move.l		d0,a0			;a0 -> current chain ptr
	move.l		(a0),d0			;d0 -> next datagram or is NULL
	bne		.store_loop		;loop until queue end reached
	move.l		recv_buf_p(pc),(a0)	;store new dgram at queue end
	clr.l		recv_buf_p		;erase buffer pointer
	sf		RX_progress_f		;flag RX not progressing
	st		ref_perm_f		;we may refuse opponent now
	goto_state	Init_Recv	;-------;back to Init_Recv again
;----------------------------------------------------------------------------
	def_state	Init_Send	;-------
	bmi		back_Init_Recv		;refused RTS => Init_Recv
	repeat		25,<nop>
	pl_test_busy				;BUSY lost now ?
	bne		back_Init_Recv		;too short CTS => Init_Recv
	pl_take_ACK				;extra ACK ?
	bne		back_Init_Recv		;abort on extra ACK
;-------
;Here we have initial CTS from opponent, and can complete our RTS.
;-------
	pl_send_ACK	d0,d1			;send ACK as RTS completion
;But before sending any actual data, we must first init some stuff
;Now, before sending any actual data, we must first init some stuff
	move.l		plip_tx_q(pc),d0	;d0 -> next buffer in plip_tx_q
	ble		back_Init_Recv		;abort if no buffer left
	move.l		d0,send_buf_p		;send_buf_p -> that buffer
	move.l		d0,a0			;a0 -> the buffer found
	move.l		(a0),plip_tx_q		;unlink it from plip_tx_q
	subq		#1,plip_tx_q_ct		;decrement buffer count
	move		PLIP_length(a0),d0	;d0 = data length of packet
	lea		PLIP_data(a0),a0	;a0 -> data start in packet
	move.l		a0,send_pos_p		;send_pos_p -> data start
	lea		(a0,d0.w),a0		;a0 -> data end in packet
	move.l		a0,send_max_p		;send_max_p -> data end
	st		TX_progress_f		;note TX in progress
	new_state	Send_Loop	;-------
	bpl.s		abort_Send_Loop		;unwanted CTS => abort
	move.l		send_pos_p(pc),a0	;a0 -> next character to send
	cmpa.l		send_max_p(pc),a0	;is it at or beyond packet end
	bhs		send_finish		;go handle packet end
	move.b		(a0),d0			;d0 = IP byte for crc calc
	move.b		(a0)+,d0		;d0 = IP byte to send
	move.l		a0,send_pos_p		;update pointer
	cmp.b		#slip_ch_ESC,d0		;is this ESC ?
	beq		go_Send_ESC		;go send ESC encoded
	cmp.b		#slip_ch_END,d0		;is this END ?
	beq		go_Send_END		;go send END encoded
	pl_dir_out	d1
	pl_send_byte	d0			;send PLIP byte
	pl_send_ACK	d1,d2			;send ACK as data strobe
	loop_state			;-------
;-------------------------------------
abort_Send_Loop:
	pl_abort_send
	goto_state	Init_Recv	;-------
;-------------------------------------
go_Send_ESC:
	pl_dir_out	d1
	pl_send_byte	#slip_ch_ESC		;send ESC prefix byte
	pl_send_ACK	d1,d2			;send ACK as data strobe
	new_state	Send_ESC	;-------
	bpl		abort_Send_Loop		;unwanted CTS => abort
	pl_dir_out	d1
	pl_send_byte	#slip_ch_ESC_ESC	;send ESC_ESC suffix
	pl_send_ACK	d1,d2			;send ACK as data strobe
	goto_state	Send_Loop	;-------
;-------------------------------------
go_Send_END:
	pl_dir_out	d1
	pl_send_byte	#slip_ch_ESC		;send ESC prefix byte
	pl_send_ACK	d1,d2			;send ACK as data strobe
	new_state	Send_END	;-------
	bpl		abort_Send_Loop		;unwanted CTS => abort
	pl_dir_out	d1
	pl_send_byte	#slip_ch_ESC_END	;send ESC_END suffix
	pl_send_ACK	d1,d2			;send ACK as data strobe
	goto_state	Send_Loop	;-------
;-------------------------------------
send_finish:
	pl_dir_out	d1
	pl_send_byte	#slip_ch_END		;send END code
	pl_send_ACK	d1,d2			;send ACK as data strobe
	new_state	Post_Send	;-------
	bpl		abort_Send_Loop		;unwanted CTS => abort
;-------
;Here the packet has been fully transferred, so we just need to clean up
;-------
	sf		ref_perm_f		;we may not refuse opponent now
	R_free.i	send_buf_p(pc)
	clr.l		send_buf_p
	sf		TX_progress_f		;flag TX not progressing
	move		plip_tx_q_ct(pc),d0	;any more to send ?
	beq		back_Init_Recv		;else go try reception
	pl_send_ACK	d1,d2			;send ACK as initial RTS
	goto_state	Init_Send	;-------
;----------------------------------------------------------------------------
;End of:	cent_busy_XB
;----------------------------------------------------------------------------
;Since TOS can leave garbage in iv_cenbusy at boot, here is a dumb routine
;to reside there when CEN_PLIP is turned 'off'.  It will only 'eat' all the
;incoming 'busy' pulses without responding in any way.
;
dumb_cenbusy:
	bclr		#0,(hw_imrb).w
	move.b		#$FE,(hw_isrb).w		;flag service to hardware
	rte
;----------------------------------------------------------------------------
;End of:	Resident interrupt routines
;----------------------------------------------------------------------------
;	Resident library function code will be expanded here
;
	make		JAR_links
	_uniref		RAM_own
	make		RAM_links
;----------------------------------------------------------------------------
;all beyond this point will be reused for RAM buffers in going resident
;----------------------------------------------------------------------------
;Start of:	STX Non-resident initialization code with tests
;----------------------------------------------------------------------------
RAM_start:		;this will be RAM buffer start
start_1:
	move.l		a0,d0
	sne		d7
	bne.s		.have_basepage
	move.l		4(sp),d0
.have_basepage:
	move.l		d0,a5
	lea		mystack(pc),sp
	move.l		a5,basepage_p
	tst.b		d7
	bne		.ACC_launch
	gemdos		Mshrink,#0,(a5),#initial_size
	lea		bp_arglen(a5),a0
	lea		STinG_Load_s(pc),a1
	str_comp	a0,a1
	bne		.bad_launch
;
	gemdos		Super,0.w
	move.l		d0,d4
;
	bsr		ensure_iv_cenbusy
;
	eval_cookie	#"STiK"
	move.l		d0,d3				;d3 = d0 -> DRV_LIST structure
	gemdos		Super|_ind,d4
;
	move.l		d3,sting_drivers		;sting_drivers -> DRV_LIST structure
	ble		.STiK_not_found
	move.l		d3,a3				;a3 -> DRV_LIST structure
	lea		DRV_LIST_magic(a3),a0
	lea		STiKmagic_s(pc),a1
	moveq		#10-1,d0
.strcmp_loop:					;loop to test STiKmagic of DRV_LIST
	cmpm.b		(a0)+,(a1)+
	dbne		d0,.strcmp_loop
	bne		.STiK_not_valid
;
	move.l		DRV_LIST_get_dftab(a3),a0	;a0 -> get_dftab function
	pea		TRANSPORT_DRIVER_s		;-(sp) = "TRANSPORT_TCPIP"
	jsr		(a0)				;call get_dftab
	addq		#4,sp
	move.l		d0,tpl				;store pointer in 'tpl'
	ble		.driver_not_valid
;
	move.l		DRV_LIST_get_dftab(a3),a0	;a0 -> get_dftab function
	pea		MODULE_DRIVER_s			;-(sp) = "MODULE_LAYER"
	jsr		(a0)				;call get_dftab
	addq		#4,sp
	move.l		d0,stx				;store pointer in 'tpl'
	ble		.layer_not_valid
.install:
	link		a6,#-8
	query_chains	-8(a6),-4(a6),0.w
	movem.l		-8(a6),a3/a4			;a3->ports  a4->drivers
	unlk		a6
;
	move.l		a3,d0
	beq.s		.bad_port_list
.port_loop:
	move.l		d0,a3
	move.l		prt_des_next(a3),d0
	bne		.port_loop
;
	move.l		a4,d0
	beq.s		.bad_driver_list
.driver_loop:
	move.l		d0,a4
	move.l		drv_des_next(a4),d0
	bne		.driver_loop
;
	IFNE	USE_TIMER
	TIMER_call	my_timer_func(pc),#HNDLR_SET
	tst		d0
	beq		.timer_failure
	ENDC	;USE_TIMER
;
.final_install:
	move.l		#my_port,prt_des_next(a3)
	move.l		#my_driver,drv_des_next(a4)
;-------
	gemdos		Super,0.w
	move.l		d0,d4
	RAM_own		#1		;setup current bp as RAM owner
	RAM_set		RAM_start(pc),#RAM_chunk
	gemdos		Super|_ind,d4
;-------
	gemdos		Ptermres,#resident_size,#0
;-------------------------------------
.ACC_launch:
	lea		ACC_launch_s(pc),a0
	bsr		report_error
.loop:
	bra		.loop
;-------------------------------------
.bad_launch:
	lea		bad_launch_s(pc),a0
	bra.s		.error_exit
;-------------------------------------
.bad_port_list:
	lea		bad_port_list_s(pc),a0
	bra.s		.error_exit
;-------------------------------------
.bad_driver_list:
	lea		bad_driver_list_s(pc),a0
	bra.s		.error_exit
;-------------------------------------
	IFNE	USE_TIMER
.timer_failure:
	lea		timer_failure_s(pc),a0
	bra		.error_exit
	ENDC	;USE_TIMER
;-------------------------------------
.STiK_not_found:
	lea		STiK_not_found_s,a0
	bra.s		.error_exit
;-------------------------------------
.STiK_not_valid:
	lea		STiK_not_valid_s,a0
	bra.s		.error_exit
;-------------------------------------
.driver_not_valid:
	lea		driver_not_valid_s,a0
	bra.s		.error_exit
;-------------------------------------
.layer_not_valid:
	lea		layer_not_valid_s(pc),a0
.error_exit:
	bsr.s		report_error
	gemdos		Pterm,#E_ERROR
;-------------------------------------
ensure_iv_cenbusy:
	move		sr,d2				;d2 = entry SR
	or		#$0700,sr			;disable interrupts
	move.l		(ev_buserr).w,a1		;save ev_buserr
	move.l		(ev_adrerr).w,a2		;save ev_adrerr
	move.l		#bad_iv_cenbusy,(ev_buserr).w	;patch ev_adrerr
	move.l		#bad_iv_cenbusy,(ev_adrerr).w	;patch ev_adrerr
	move.l		(iv_cenbusy).w,a0		;a0 = suspect pointer
	move.l		sp,d1				;d1 = entry SP
	move.l		(a0),d0				;test pointer ?
good_iv_cenbusy:
	moveq		#E_OK,d0		;flag good pointer
	bra.s		know_iv_cenbusy		;go to common restore code
;
bad_iv_cenbusy:		;bus error or address error leads here !!!
	move.l		d1,SP			;SP = entry SP  (pop big frame)
	moveq		#E_ERROR,d0		;flag bad pointer
know_iv_cenbusy:
	move.l		a1,(ev_buserr).w	;restore ev_buserr
	move.l		a2,(ev_adrerr).w	;restore ev_adrerr
	move		d2,sr			;SR = entry SR
	tst.l		d0			;remember test result
	beq.s		.exit			;just exit if pointer is good
.patch_vector:
	move.l		#dumb_cenbusy,(iv_cenbusy).w	;patch iv_cenbusy
.exit:
	rts
;-------------------------------------
report_error:
	move.l		a0,-(sp)
	lea		error_title_s(pc),a0
	bsr.s		Cconws_sub
	move.l		(sp)+,a0
	bsr.s		Cconws_sub
	lea		error_tail_s(pc),a0
Cconws_sub:
	gemdos		Cconws,(a0)
	rts
;----------------------------------------------------------------------------
;End of:	STX Non-resident initialization code with tests
;----------------------------------------------------------------------------
;	Non-resident library function code will be expanded here
;
	make	JAR_links
	make	RAM_links
;----------------------------------------------------------------------------
text_limit:
text_size	= text_limit-text_start
	SECTION	DATA
data_start:
;----------------------------------------------------------------------------
STinG_Load_s:
	dc.b	10,'STinG_Load',NUL
STiKmagic_s:
	dc.b	'STiKmagic',NUL
TRANSPORT_DRIVER_s:
	dc.b	'TRANSPORT_TCPIP',NUL
MODULE_DRIVER_s:
	dc.b	'MODULE_LAYER',NUL
;
ACC_launch_s:
	dc.b	'This non-ACC, was launched as an ACC,',CR,LF
	dc.b	'so now you must reset the computer !',CR,LF
	dc.b	'I am looping forever to avoid damage',CR,LF
	dc.b	'that could occur if I try to exit !',CR,LF
	dc.b	NUL
;
bad_launch_s:	
	dc.b	'This STX should only be launched by',CR,LF
	dc.b	'STinG, or another TCP/IP stack with',CR,LF
	dc.b	'a compatible module interface !',CR,LF
	dc.b	NUL
;
bad_port_list_s:
	dc.b	'The list chain of STinG ports was',CR,LF
	dc.b	'not found...!',CR,LF
	dc.b	NUL
;
bad_driver_list_s:
	dc.b	'The list chain of STinG drivers was',CR,LF
	dc.b	'not found...!',CR,LF
	dc.b	NUL
;
	IFNE	USE_TIMER
timer_failure_s:
	dc.b	'STinG TIMER_call function failed !',CR,LF
	dc.b	NUL
	ENDC	;USE_TIMER
;
STiK_not_found_s:
	dc.b	'There is no STiK cookie in the jar !',CR,LF
	dc.b	NUL
;
STiK_not_valid_s:
	dc.b	'The STiK cookie data is corrupt !',CR,LF
	dc.b	NUL
;
driver_not_valid_s:
	dc.b	'The main STinG driver is not valid !',CR,LF
	dc.b	NUL
;
layer_not_valid_s:
	dc.b	'The STinG module layer is not valid !',CR,LF
	dc.b	NUL
;
error_title_s:
	dc.b	BEL,CR,LF
	dc.b	'------------'
	M_TITLE
	dc.b	' '
	M_VERSION
	dc.b	'------------',CR,LF
	dc.b	NUL
;
error_tail_s:
	dc.b	BEL,CR,LF,NUL
	EVEN
;----------------------------------------------------------------------------
data_limit:
data_size	=	data_limit-data_start
	SECTION	BSS
bss_start:
;----------------------------------------------------------------------------
		ds.l	200		;subroutine stack >= 100 longs
mystack:	ds.l	1		;top of subroutine stack
;----------------------------------------------------------------------------
used_bss_end:
used_bss_size	=	used_bss_end-bss_start
used_RAM_size	=	(used_bss_size+data_size+(text_limit-RAM_start))
pad_RAM_size	=	RAM_chunk-used_RAM_size+8
		ds.b	pad_RAM_size
;----------------------------------------------------------------------------
bss_limit:
bss_size	=	bss_limit-bss_start
;----------------------------------------------------------------------------
initial_size	=	text_size+data_size+bss_size+$100
resident_size	=	initial_size
;----------------------------------------------------------------------------
		END
;----------------------------------------------------------------------------
;End of file:	CEN_PLIP.S
;----------------------------------------------------------------------------
