**
** ProNET.DEVICE  Version  2    ©1995 by Michael Krause
** Most parts totally recoded, only few parts left from V1
**
** 05-05-95	Started
** 08-05-95	v35.0
** 31-05-95	If the pending writes list is not empty before the MainLoop
**		Wait(), we will signal ourselves to call msgREAD! This is
**		because otherwise a timeout while sending would cause the
**		device to hang completely, if the other machine doesn't
**		send something! v35.1
** 05-06-95	Flags Argument for Driver-Init. v35.2
** 09-06-95	Getconfigstring uses MEMF_PUBLIC for Read Buffer. v35.3
** 15-06-95	PNDERR_DRIVERTROUBLE introduced. v35.4

VERSION		equ	35
REVISION	equ	4

DEBUG		equ	0

		include	"A:OSmacros.i"
		include	"exec/exec.i"
		include	"dos/dosextens.i"
		include	"A:ProNET/include/devices/pronet.i"
		include	"exec_lib.i"
		include	"dos_lib.i"

		moveq	#-1,d0
		rts

** -----------------------------------------------------------------------
**
**
**
**

**
** Library/Device Base Structures
**

**
**
**
**
** -----------------------------------------------------------------------

		STRUCTURE MyDev,LIB_SIZE	;Device Base
		APTR	pnd_SegList
		APTR	pnd_DosBase
		LABEL   pnd_SizeOf

		STRUCTURE MyUnit,0		;Unit Structure
		APTR	pndu_Next
		ULONG	pndu_UnitNr
		APTR	pndu_ProcessID
		APTR	pndu_MsgPort
		LABEL	pndu_SizeOf

		STRUCTURE PortMember,0		;One Port of a Unit
		APTR	pndp_Next
		UWORD	pndp_PortNr
		ULONG	pndp_MsgPort
		LABEL	pndp_SizeOf

PND_ADDPORT	equ	5
PND_REMPORT	equ	6

LibResident	dc.w	RTC_MATCHWORD
		dc.l	LibResident
		dc.l	EndResident
		dc.b	RTF_AUTOINIT
		dc.b	VERSION
		dc.b	NT_DEVICE
		dc.b	0
		dc.l	DevName
		dc.l	IDString
		dc.l	LibInitData

DevName		dc.b	"pronet.device",0
		dc.b	"$VER: "
IDString	dc.b	"pronet.device "
		dc.b	(VERSION/10)+48,(VERSION-((VERSION/10)*10))+48,".",REVISION+48
		dc.b	" (09-06-95)",13,10,0
		even

LibInitData	dc.l	pnd_SizeOf
		dc.l	FuncTab
		dc.l	DataTab
		dc.l	LibInitRout

FuncTab		dc.l	Open
		dc.l	Close
		dc.l	Expunge
		dc.l	ExtFunc
		dc.l	BeginIO
		dc.l	AbortIO
		dc.l	getconfigstr
		dc.l	freeconfigstr
		dc.l	-1

DataTab		INITBYTE   LN_TYPE,NT_DEVICE
		INITLONG   LN_NAME,DevName
		INITBYTE   LIB_FLAGS,LIBF_SUMUSED!LIBF_CHANGED
		INITWORD   LIB_VERSION,VERSION
		INITWORD   LIB_REVISION,REVISION
		INITLONG   LIB_IDSTRING,IDString
		dc.w	0

OurBase		dc.l	0

** -----------------------------------------------------------------------
**
**
**
**

**
** Library/Device Base Routines
**

**
**
**
**
** -----------------------------------------------------------------------

; -- Called once when the device was loaded
LibInitRout	movem.l	d1-d7/a0-a6,-(sp)
		move.l	d0,a5
		move.l	a5,OurBase
		move.l	a0,pnd_SegList(a5)

		lea	dosname(pc),a1
		moveq	#0,d0
		LIBCALL	OpenLibrary
		move.l	d0,pnd_DosBase(a5)

		move.l	a5,d0
		movem.l	(sp)+,d1-d7/a0-a6
		rts

; -- This piece is called by OpenDevice()
Open		movem.l	a2-a6/d2-d7,-(sp)
		addq.w	#1,LIB_OPENCNT(a6)
		move.l	d0,d6			;Unit
		move.l	a1,a2			;IORequest
		move.l	a6,a5			;DevBase

		move.b	#IOERR_OPENFAIL,IO_ERROR(a2)

		* Does the required Unit already exist?
		lea	unitlist(pc),a3
.searchunit	move.l	(a3),d0
		beq.s	.newunit
		move.l	d0,a3
		cmp.l	pndu_UnitNr(a3),d6
		bne.s	.searchunit
		bra.s	.unitok

		* No! So create a Unit!
.newunit	bsr	InitUnit
		tst.l	d0
		beq	.ende
		move.l	d0,a3

		* Yes! Add new port now! Unit-Structure in a3
.unitok		move.l	4.w,a6
		move.w	#PND_ADDPORT,IO_COMMAND(a2)
		move.l	a2,a1
		move.l	pndu_MsgPort(a3),a0
		LIBCALL	PutMsg
		move.l	a2,a1
		LIBCALL	WaitIO
		tst.b	d0
		bne.s	.ende

		move.l	a3,IO_UNIT(a2)

		* Device successfully opened.
		move.l	a5,a6
		bclr	#LIBB_DELEXP,LIB_FLAGS(a6)
		addq.w	#1,LIB_OPENCNT(a6)
		move.b	#NT_REPLYMSG,LN_TYPE(a2)
		clr.b	IO_ERROR(a2)

.ende		move.l	a5,a6
		subq.w	#1,LIB_OPENCNT(a6)
		move.b	IO_ERROR(a2),d0
		beq.s	.0
		move.l	#-1,IO_DEVICE(a2)
.0		movem.l	(sp)+,a2-a6/d2-d7
		rts

; -- Called by CloseDevice()
Close		movem.l	a2/a5,-(sp)
		move.l	a1,a2
		move.l	a6,a5

		move.l	4.w,a6
		move.l	IO_UNIT(a1),a0
		move.l	pndu_MsgPort(a0),a0
		move.w	#PND_REMPORT,IO_COMMAND(a1)
		LIBCALL	PutMsg
		move.l	a2,a1
		LIBCALL	WaitIO

		move.l	a2,a1
		move.l	a5,a6
		move.l	#-1,IO_DEVICE(a1)
		move.l	#-1,IO_UNIT(a1)

		subq.w	#1,LIB_OPENCNT(a6)
		moveq	#0,d0
		movem.l	(sp)+,a2/a5
		rts

; -- This routine was kept free by Commodore for about 10 years now ;)
ExtFunc		moveq	#0,d0
		rts

; -- Application wants an IORequest to be executed
BeginIO		move.l	a6,-(sp)
		move.b	#NT_MESSAGE,LN_TYPE(a1)
		bclr	#0,IO_FLAGS(a1)		;Clear IOF_QUICK
		clr.b	IO_ERROR(a1)
		move.l	IO_UNIT(a1),a0
		move.l	pndu_MsgPort(a0),a0
		move.l	4.w,a6
		LIBCALL	PutMsg
		moveq	#0,d0
		move.l	(sp)+,a6
		rts

; -- AbortIO is not supported
AbortIO		moveq	#0,d0			;Gar nix geht ab hier.
		rts

; -- Called e.g. when memory is low and the device is not used anymore.
Expunge		movem.l	d1-d2/a1-a6,-(sp)
		moveq	#0,d0
		bset	#LIBB_DELEXP,LIB_FLAGS(a6)	;we don't support
		movem.l	(sp)+,d1-d2/a1-a6		;expunging!
		rts

; -- Create new Unit
InitUnit	; a5 *DevBase
		; a2 *IORequest
		; d6 Unit
		; RETURNS d0 *Unit-Structure or NULL
		move.l	4.w,a6
		moveq	#pndu_SizeOf,d0
		moveq	#MEMF_PUBLIC,d1
		LIBCALL	AllocMem
		tst.l	d0
		beq.s	.ende
		move.l	d0,a3

		move.l	d6,pndu_UnitNr(a3)

		* First, create Unit process
		move.l	pnd_DosBase(a5),a6	;We will break the Forbid()
		move.l	#DevName,d1		;which EXEC made for us;
		moveq	#0,d2			;it's okay because we have
		move.l	#ProcessCode-4,d3	;all variables in registers,
		lsr.l	#2,d3			;so our routine can be run
		move.l	#4096,d4		;twice at one time without
		LIBCALL	CreateProc		;problems!
		move.l	d0,d7
		beq.s	.ende

		move.l	d7,pndu_ProcessID(a3)

		* Notify process about Unit number
		move.l	4.w,a6
		move.l	a2,a1
		move.l	a3,pnr_Data2(a1)
		move.b	IO_ERROR(a2),d2		;save it for the main routine
		move.l	d7,a0
		LIBCALL	PutMsg

		* Wait for Process to finish initialization
		move.l	a2,a1
		LIBCALL	WaitIO
		move.b	d2,IO_ERROR(a2)
		tst.b	d0
		bne.s	.error

		* Now add this Unit-Structure to our internal List!!
		LIBCALL	Forbid			;<< important!
		lea	unitlist(pc),a0
		move.l	(a0),(a3)
		move.l	a3,(a0)
		LIBCALL	Permit

		move.l	a3,d0
		rts

.error		move.l	4.w,a6			;Process will terminate
		move.l	a3,a1			;itself in case of an error
		moveq	#pndu_SizeOf,d0
		LIBCALL	FreeMem
.ende		moveq	#0,d0
		rts

unitlist	dc.l	0		;List of all Units loaded!

** -----------------------------------------------------------------------
**
**
**
**

**
** DeviceUnit Process Code
**

**
**
**
**
** -----------------------------------------------------------------------

		cnop	0,4

		STRUCTURE PrivateData,0
		APTR	priv_OurUnit
		STRUCT	priv_ConfigStringName,30
		APTR	priv_ConfigString
		STRUCT	priv_DriverName,30

		LABEL	priv_DriverData
		UBYTE	priv_ReadSignalBit
		UBYTE	priv_pad0
		APTR	priv_ReadQuery
		APTR	priv_Read
		APTR	priv_Write

		APTR	priv_MsgPort
		APTR	priv_MsgBackPort
		APTR	priv_PortList
		STRUCT	priv_PendingReads,12
		STRUCT	priv_PendingWrites,12
		LABEL	priv_SizeOf

ProcessCode	move.l	4.w,a6
		move.l	ThisTask(a6),a2
		lea	pr_MsgPort(a2),a2
		move.l	a2,a0
		LIBCALL	WaitPort
		move.l	a2,a0
		LIBCALL	GetMsg
		move.l	d0,a3

		move.b	#IOERR_OPENFAIL,IO_ERROR(a3)	;[a3] Startupmsg

		* Get memory for private data
		moveq	#priv_SizeOf,d0
		move.l	#MEMF_CLEAR,d1
		LIBCALL	AllocMem
		tst.l	d0
		beq	.ende
		move.l	d0,a5

		lea	priv_PendingReads(a5),a1
		NEWLIST	a1
		lea	priv_PendingWrites(a5),a1
		NEWLIST	a1

		* Get port for messages replied from the application
		bsr	CreatePort
		move.l	d0,priv_MsgBackPort(a5)
		beq.s	.noconf

		bsr	CreatePort
		move.l	d0,priv_MsgPort(a5)
		beq.s	.noport2

		move.l	pnr_Data2(a3),a0
		move.l	a0,priv_OurUnit(a5)
		move.l	priv_MsgPort(a5),pndu_MsgPort(a0)

		* Get configuration string for our unit
		move.l	pndu_UnitNr(a0),d0
		lea	confstr(pc),a1
		lea	priv_ConfigStringName(a5),a0
.l0		move.b	(a1)+,(a0)+
		bne.s	.l0
		subq.l	#1,a0
		bsr	long2dec
		move.b	#":",(a0)+
		clr.b	(a0)
		lea	priv_ConfigStringName(a5),a0
		bsr	getconfigstr
		move.l	d0,priv_ConfigString(a5)
		beq.s	.noconf

		move.b	#PNDERR_DRIVERTROUBLE,IO_ERROR(a3)
		bsr	StartDriver
		tst.l	d0
		beq.s	.noconf

		clr.b	IO_ERROR(a3)
		move.l	a3,a1
		LIBCALL	ReplyMsg

		bra	MainLoop

.noport2	move.l	priv_MsgBackPort(a5),a0
		bsr	DeletePort
.noconf		move.l	a5,a1
		moveq	#priv_SizeOf,d0
		LIBCALL	FreeMem
.ende		move.l	a3,a1
		LIBCALL	ReplyMsg
		rts

confstr		dc.b	"pronet-device-",0
		even

; -- Load the interface driver and initialize it.
StartDriver	; RETURNS d0 NULL on failure!
		movem.l	a2/a6,-(sp)
		lea	priv_DriverName(a5),a1
		move.l	a1,d1
		lea	drivername(pc),a0
.l0		move.b	(a0)+,(a1)+
		bne.s	.l0
		subq.l	#1,a1
		move.l	priv_ConfigString(a5),a0
.l1		move.b	(a0)+,d0
		move.b	d0,(a1)+
		cmp.b	#" ",d0
		bhi.s	.l1
		clr.b	-(a1)
		move.l	a0,d6

		move.l	OurBase(pc),a6		;naahh why should I open
		move.l	pnd_DosBase(a6),a6	;it again??
		LIBCALL	LoadSeg
		add.l	d0,d0
		add.l	d0,d0
		beq.s	.error
		addq.l	#4,d0
		move.l	d0,a2

		* And now, Ladies & Gentlemen :))
		lea	priv_DriverData(a5),a0
		move.l	d6,a1		;ConfString after Driver-ID
		move.l	#"RST!",d0
		moveq	#0,d1
		jsr	(a2)
		tst.l	d0
		beq.s	.error

		moveq	#-1,d0
		bra.s	.ende

.error		moveq	#0,d0
.ende		movem.l	(sp)+,a2/a6
		rts

drivername	dc.b	"DEVS:ProNET/",0
		even


******************
; -- The Main Loop
******************

MainLoop	move.l	priv_MsgBackPort(a5),a0
		move.b	MP_SIGBIT(a0),d2
		move.b	priv_ReadSignalBit(a5),d3
		move.l	priv_MsgPort(a5),a0
		move.b	MP_SIGBIT(a0),d4
		moveq	#0,d5
		bset	d2,d5
		bset	d3,d5
		bset	d4,d5			;[d5] Signalmask to wait for
		movem.l	a6/d2-d5,-(sp)

Wait4Msg	movem.l	(sp),a6/d2-d5
		bsr	CheckWrites
		move.l	d5,d0
		LIBCALL	Wait
		movem.l	d0/d3/d4,-(sp)
		btst	d2,d0
		bne	doMSGBACK
wait1		movem.l	(sp),d0/d3/d4
		btst	d3,d0
		bne	doREAD
wait2		movem.l	(sp)+,d0/d3/d4
		btst	d4,d0
		bne	doMSG
		bra	Wait4Msg

; -- New Message at our MsgPort
doMSG		move.l	priv_MsgPort(a5),a0
		LIBCALL	GetMsg
		tst.l	d0
		beq	Wait4Msg		;no more messages
		move.l	d0,a2
		move.w	IO_COMMAND(a2),d0

		cmp.w	#PND_WRITE,d0
		beq	doWRITE
		cmp.w	#PND_ADDPORT,d0
		beq	doADDPORT
		cmp.w	#PND_REMPORT,d0
		beq	doREMPORT

		move.b	#IOERR_NOCMD,IO_ERROR(a2)
		move.l	a2,a1
		LIBCALL	ReplyMsg
		bra	doMSG

; -- Add a new port to this unit
doADDPORT	move.w	pnr_NetSourcePort(a2),d1
		cmp.w	#-1,d1
		beq	addport_getconf
		cmp.w	#-2,d1
		beq	addport_getnext

		move.b	#IOERR_OPENFAIL,IO_ERROR(a2)

		* See if port already exists, portnumber in d1 now
		lea	priv_PortList(a5),a0
.loop0		move.l	(a0),d0
		beq.s	.ok
		move.l	d0,a0
		cmp.w	pndp_PortNr(a0),d1
		bne.s	.loop0
		* Sorry
		move.b	#PNDERR_PORTEXISTS,IO_ERROR(a2)
		bra.s	addport_ende

		* No, add it!
.ok		moveq	#pndp_SizeOf,d0
		moveq	#0,d1
		LIBCALL	AllocMem
		tst.l	d0
		beq.s	addport_ende

		move.l	d0,a4
		move.w	pnr_NetSourcePort(a2),pndp_PortNr(a4)
		move.l	pnr_MsgPort(a2),pndp_MsgPort(a4)
		lea	priv_PortList(a5),a0
		move.l	(a0),(a4)
		move.l	a4,(a0)

		bsr	TryPendingReads

		clr.b	IO_ERROR(a2)		;Okay, port added
addport_ende	move.l	a2,a1
		LIBCALL	ReplyMsg
		bra	doMSG

addport_getconf	move.l	pnr_Data1(a2),a0
		bsr	getconfigstr
		move.l	d0,d3
		beq.s	.nostring
		move.l	d0,a0
		bsr	dec2long
		move.w	d1,pnr_NetSourcePort(a2)
		move.l	d3,a0
		bsr	freeconfigstr
		bra	doADDPORT
.nostring	move.b	#PNDERR_BADCONFIG,IO_ERROR(a2)
		bra.s	addport_ende

addport_getnext	moveq	#1,d1
		lea	priv_PortList(a5),a1
.next		move.l	a1,a0
.loop0		move.l	(a0),d0
		beq.s	.notfound
		move.l	d0,a0
		cmp.w	pndp_PortNr(a0),d1
		bne.s	.loop0
		addq.w	#1,d1
		bra.s	.next
.notfound	move.w	d1,pnr_NetSourcePort(a2)
		bra	doADDPORT

; -- Remove current port
doREMPORT	move.w	pnr_NetSourcePort(a2),d2
		lea	priv_PortList(a5),a1
.find		move.l	a1,a3
		move.l	(a1),d0
		beq.s	.notfound
		move.l	d0,a1
		cmp.w	pndp_PortNr(a1),d2
		bne.s	.find
		move.l	(a1),d2
		moveq	#pndp_SizeOf,d0
		LIBCALL	FreeMem
		move.l	d2,(a3)		;Teil aus der Liste entfernt !!
		move.l	a2,a1
		LIBCALL	ReplyMsg
.notfound	bra	doMSG

; -- Do a WRITE command
doWRITE		move.l	a2,a1
		lea	priv_PendingWrites(a5),a0
		LIBCALL	AddTail
		bsr	TryPendingWrites
		bra	doMSG			;kurz und schmerzlos :-)

; -- A ProNET-Message was replied
doMSGBACK	move.l	priv_MsgBackPort(a5),a0
		LIBCALL	GetMsg
		tst.l	d0
		beq	wait1
		move.l	d0,a1			;we don't need it any more.
		moveq	#0,d0
		move.w	MN_LENGTH(a1),d0
		add.l	#MN_SIZE,d0
		LIBCALL	FreeMem
		bra.s	doMSGBACK

; -- A new message from another Amiga is ready
doREAD		move.l	priv_ReadQuery(a5),a0	;Do external driver magic..
		jsr	(a0)

		* d0.w length (if NULL then exit!!!)
		* d1 destination port
		* d2 source port
		ext.l	d0
		beq.s	.ende
		move.w	d1,d7
		move.w	d2,d5
		move.l	d0,d6
		addq.w	#2,d6	;include Source Port word

		add.l	#MN_SIZE+2,d0
		move.l	d0,d3
.tryagain	moveq	#MEMF_PUBLIC,d1		;This part is the one and
		LIBCALL	AllocMem		;only thing in this device
		tst.l	d0			;which I don't like, but
		bne.s	.memok			;it's the only solution!
		move.l	OurBase(pc),a6
		move.l	pnd_DosBase(a6),a6
		moveq	#50,d1
		LIBCALL	Delay
		move.l	4.w,a6
		move.l	d3,d0
		bra.s	.tryagain

		* Now get the data and build a Message
.memok		move.l	d0,a3
		lea	MN_SIZE+2(a3),a0
		move.l	priv_Read(a5),a1
		jsr	(a1)
		move.w	d5,MN_SIZE(a3)
		move.w	d6,MN_LENGTH(a3)
		move.w	d7,LN_NAME(a3)
		move.l	priv_MsgBackPort(a5),MN_REPLYPORT(a3)
		move.b	#NT_MESSAGE,LN_TYPE(a3)

		lea	priv_PendingReads(a5),a0
		move.l	a3,a1
		LIBCALL	AddTail

.ende		bsr	TryPendingReads
		bsr	TryPendingWrites
		bra	wait2

***
***
***

; -- Look at the history, 31-05-95 for reasons why we do this!
CheckWrites	lea	priv_PendingWrites(a5),a0
		TSTLIST	a0
		beq.s	.okay
		move.b	priv_ReadSignalBit(a5),d0
		moveq	#0,d1
		bset	d0,d1
		moveq	#-1,d0
		LIBCALL	SetSignal
.okay		rts

***
***
***

; -- Try to send off all pending messages
TryPendingReads	move.l	a2,-(sp)
		lea	priv_PendingReads(a5),a3
		move.l	LH_HEAD(a3),a2
		addq.l	#4,a3
.looop		cmp.l	a2,a3
		bne.s	.doit
		move.l	(sp)+,a2
		rts

.doit		move.l	a2,a4
		move.l	LN_SUCC(a2),a2

		move.w	LN_NAME(a4),d1
		lea	priv_PortList(a5),a0
.find		move.l	(a0),d0
		beq.s	.looop
		move.l	d0,a0
		cmp.w	pndp_PortNr(a0),d1
		bne.s	.find
		move.l	pndp_MsgPort(a0),d5
		move.l	a4,a1
		LIBCALL	Remove
		move.l	d5,a0
		move.l	a4,a1
		LIBCALL	PutMsg
		bra.s	.looop

; -- Try to send off all pending write requests
TryPendingWrites
		move.l	a2,-(sp)
		lea	priv_PendingWrites(a5),a3
		move.l	LH_HEAD(a3),a2
		addq.l	#4,a3
.looop		cmp.l	a2,a3
		bne.s	.doit
.busy		move.l	(sp)+,a2
		rts

.doit		move.l	a2,a4
		move.l	LN_SUCC(a2),a2

		move.l	pnr_Data1(a4),a0
		move.l	pnr_Length1(a4),d0
		addq.l	#1,d0			;round up to word boundary
		bclr	#0,d0
		move.l	pnr_Data2(a4),a1
		move.l	pnr_Length2(a4),d1
		addq.l	#1,d1			;dito
		bclr	#0,d1
		move.w	pnr_NetDestPort(a4),d2
		move.w	pnr_NetSourcePort(a4),d3
		move.l	priv_Write(a5),a6
		jsr	(a6)			;Again some driver magic..
		move.l	4.w,a6
		tst.w	d0
		bne.s	.busy

		* IORequest done, reply it!
		move.l	a4,a1
		LIBCALL	Remove
		move.l	a4,a1
		LIBCALL	ReplyMsg
		bra.s	.looop

** -----------------------------------------------------------------------
**
**
**
**

**
** Routines for handling the configuration file
**

**
**
**
**
** -----------------------------------------------------------------------

; -- Obtain configuration string
getconfigstr	; a0 *line-identifier (e.g. 'HD2:') with ':'
		; RETURNS d0 *line-string or NULL if it doesn't exist
		movem.l	d2-d7/a2-a6,-(sp)
		move.l	a0,a2
		moveq	#0,d4			;RC

		move.l	4.w,a6
		move.l	#$104,d0
		moveq	#0,d1
		LIBCALL	AllocMem
		move.l	d0,d6			;Allocate FIB
		beq	.error
		move.l	OurBase(pc),a5
		move.l	pnd_DosBase(a5),a5
		move.l	a5,a6
		move.l	#configname,d1
		moveq	#ACCESS_READ,d2
		LIBCALL	Lock
		move.l	d0,d7
		beq	.nolock.mem
		move.l	d7,d1
		move.l	d6,d2
		LIBCALL	Examine
		move.l	d7,d1
		LIBCALL	UnLock			;So we've got the file length

		move.l	4.w,a6
		move.l	d6,a0
		move.l	fib_Size(a0),d0
		addq.l	#1,d0
		move.l	d0,d5			;[d5] configmemsize
		move.l	#MEMF_PUBLIC^MEMF_CLEAR,d1
		LIBCALL	AllocMem
		tst.l	d0
		beq	.nolock.mem
		move.l	d0,a3			;[a3] configmem
		move.l	a3,a4

		move.l	a5,a6			;Read file
		move.l	#configname,d1
		move.l	#MODE_OLDFILE,d2
		LIBCALL	Open
		move.l	d0,d7
		beq.s	.nofile
		move.l	d7,d1
		move.l	a3,d2
		move.l	d5,d3
		subq.l	#1,d3
		LIBCALL	Read
		move.l	d0,d2
		move.l	d7,d1
		LIBCALL	Close
		cmp.l	d2,d3
		bne.s	.nofile

.loop		move.l	a2,a0			;Search string
		move.l	a3,a1
		bsr.s	.comparestr
		tst.w	d0
		bne.s	.next

.search0	move.b	(a1)+,d0		;Skip spaces until first
		beq.s	.nofile			;character
		cmp.b	#10,d0
		beq.s	.nofile
		cmp.b	#32,d0
		bls.s	.search0

		move.l	a1,d2			;Get length of string
		subq.l	#1,d2
		moveq	#-5,d0
.search1	move.b	(a1)+,d1
		beq.s	.searchok
		cmp.b	#10,d1
		beq.s	.searchok
		dbra	d0,.search1

.searchok	neg.l	d0			;Extract string
		move.l	d0,d3
		move.l	4.w,a6
		moveq	#MEMF_PUBLIC,d1
		LIBCALL	AllocMem
		tst.l	d0
		beq.s	.nofile
		move.l	d0,a0
		move.l	d3,(a0)+
		move.l	a0,d4
		move.l	d2,a1
.copyl0		move.b	(a1)+,d1
		move.b	d1,(a0)+
		beq.s	.copyok
		cmp.b	#10,d1
		bne.s	.copyl0
.copyok		clr.b	-(a0)
		bra.s	.nofile

.next		bsr.s	.nextline
		tst.w	d0
		beq.s	.loop

.nofile		move.l	4.w,a6
		move.l	a4,a1
		move.l	d5,d0
		LIBCALL	FreeMem
.nolock.mem	move.l	4.w,a6
		move.l	d6,a1
		move.l	#$104,d0
		LIBCALL	FreeMem

.error		move.l	d4,d0
.ende		movem.l	(sp)+,d2-d7/a2-a6
		rts

.comparestr	moveq	#0,d0
.compl0		move.b	(a0)+,d1
		move.b	(a1)+,d2
		bclr	#5,d1
		bclr	#5,d2
		cmp.b	d1,d2
		bne.s	.comperr
		tst.b	d1
		beq.s	.compok
		bra.s	.compl0
.comperr	moveq	#-1,d0
.compok		rts

.nextline	moveq	#0,d0
		move.b	(a3)+,d1
		beq.s	.nextlerr
		cmp.b	#10,d1
		beq.s	.nextlok
		bra.s	.nextline
.nextlerr	moveq	#-1,d0		;end of file
.nextlok	rts

; -- Dispose configuration string
freeconfigstr	; a0 *line-string got by 'getconfigstr'
		move.l	a6,-(sp)
		move.l	-(a0),d0
		move.l	a0,a1
		move.l	4.w,a6
		LIBCALL	FreeMem
		move.l	(sp)+,a6
		rts

configname	dc.b	"DEVS:ProNET.config",0
dosname		dc.b	"dos.library",0
		even


** -----------------------------------------------------------------------
**
**
**
**

**
** Useful routines
**

**
**
**
**
** -----------------------------------------------------------------------

dec2long	; konvertiert Dezimalstring ab (a0) zu Longword in D1 !!
		moveq	#0,d1
.loop		moveq	#0,d0
		move.b	(a0)+,d0
		sub.b	#"0",d0
		cmp.b	#9,d0
		bhi.s	.oki
		move.l	d1,d2
		lsl.l	#3,d1
		add.l	d2,d1
		add.l	d2,d1
		add.l	d0,d1
		bra.s	.loop
.oki		rts

long2dec	;Converts Long in d0 to Dec-String in (a0)+
		tst.l	d0
		bne.s	.0
		move.b	#"0",(a0)+
		rts
.0		lea	long2asciitab(pc),a1
		moveq	#9,d1
		moveq	#0,d6
.1		move.l	(a1)+,d2
		move.l	d0,d3
		moveq	#-1,d4
.2		move.l	d3,d5
		sub.l	d2,d3
		cmp.l	d5,d3
		dbhi	d4,.2
		add.l	d2,d3
		move.l	d3,d0
		not.w	d4
		bne.s	.3
		tst.w	d6
		beq.s	.4
.3		st	d6
		add.b	#"0",d4
		move.b	d4,(a0)+
.4		dbra	d1,.1
		rts
long2asciitab	dc.l	1000000000,100000000,10000000,1000000,100000
		dc.l	10000,1000,100,10,1

		include	"A:ProNET/source/devio.s"

EndResident:
