; Latticelib.a: Amiga shared runtime library shell for Lattice 'C' 5.02+
; By Rick Huebner, 28 October 1989.  Released to the Public Domain.
; Derived from sample.library.asm in ROM Kernal Reference Manual and other
; sources.

; Provides RomTag and other magic thingies needed to make a set of
; C routines into a library.  Should work with most any set of routines;
; just substitute in the proper external labels.  This file should be the
; first one given in the link list, so that the RomTag struct can be quickly
; & easily found by the system when the library is opened.

; 5.04 note: I just got done trying to use the new libent and libinit modules
; provided with Lattice C 5.04, and ran into a few problems.  I'm probably
; not using them quite right (the update docs are pretty sketchy), but I'm
; not going to waste any more time on them.  I'd already written and tested
; this stuff before 5.04 came, and it works well.  If it ain't broke...

	INCLUDE	"exec/types.i"
	INCLUDE	"exec/libraries.i"
	INCLUDE	"exec/lists.i"
	INCLUDE	"exec/initializers.i"
	INCLUDE	"exec/resident.i"

	; Things we define for external reference (for easier debuggging)
	XDEF	LibStart
	XDEF	RomTag
	XDEF	LibName
	XDEF	LibIDString
	XDEF	LibInit
	XDEF	LibOpen
	XDEF	LibClose
	XDEF	LibExpunge
	XDEF	LibExtFunc
	XDEF	LibEnd

	; Our library base structure description
	STRUCTURE XPRBase,LIB_SIZE
	ULONG	xb_SegList
	LABEL	XPRBase_SIZEOF


	SECTION	text,code

	; Things we need others to define
	XREF	@XProtocolCleanup
	XREF	@XProtocolSetup
	XREF	@XProtocolSend
	XREF	@XProtocolReceive
	XREF	@XProtocolHostMon
	XREF	@XProtocolUserMon
	XREF	__BSSBAS
	XREF	__BSSLEN
	XREF	_AbsExecBase
	XREF	_LVOOpenLibrary
	XREF	_LVOCloseLibrary
	XREF	_LVORemove
	XREF	_LVOFreeMem


	; Supposed start of executable code.  This is where execution
	; will start if anybody's silly enough to try and run the library
	; from the command line.  Just return the highest error code we
	; conveniently can to tell them they screwed up.
LibStart:
	moveq	#$7F,d0
	rts

	; Where the magic begins.  OpenLibrary actually looks through the
	; library file contents trying to find this table by locating the
	; magic RTC_MATCHWORD value followed by its own address.  This
	; table then tells OpenLibrary where to find other things it needs.
	; This needs to be close to the front of your library file to cut
	; down on the amount of searching OpenLibrary does; that's why
	; this object file should be first in the link list.
RomTag:	dc.w	RTC_MATCHWORD	; Magic value to search for to find table
	dc.l	RomTag		; Address of matchword; the two together prevent accidental matches
	dc.l	LibEnd		; Address of end of library handler code
	dc.b	RTF_AUTOINIT	; Request system to automatically initialize our library
	dc.b	VERSION		; Version number of our library (defined below)
	dc.b	NT_LIBRARY	; Node type = Library
	dc.b	0		; Node priority = 0 (normal)
	dc.l	LibName		; Name of this library file, for debugging info
	dc.l	LibIDString	; More debugging info
	dc.l	inittable	; Initialization table used by RTF_AUTOINIT

VERSION	 EQU	2
REVISION EQU	0
LibName:
	dc.b	"xprzmodem.library",0
LibIDString:
	dc.b	"xprzmodem 2.0 (28 Oct 1989)",13,10,0

	ds.w	0

inittable:
	dc.l	XPRBase_SIZEOF	; Size of our library base struct
	dc.l	functable	; Where our function addresses are
	dc.l	datatable	; Initialization info for our library base struct
	dc.l	LibInit		; Library initialization routine address

functable:
	dc.l	LibOpen			; Addresses of all library functions
	dc.l	LibClose		; First 4 MUST be provided, in this order
	dc.l	LibExpunge
	dc.l	LibExtFunc
	dc.l	@XProtocolCleanup	; The meat of the library.
	dc.l	@XProtocolSetup		; Since the XPR spec requires the args
	dc.l	@XProtocolSend		; to be passed in the same registers that
	dc.l	@XProtocolReceive	; Lattice uses for -rr, we can just let the
	dc.l	@XProtocolHostMon	; calling program jump straight into the
	dc.l	@XProtocolUserMon	; C routines, with no glue code required.
	dc.l	-1

	; Things for the system to automatically initialize for us via RTF_AUTOINIT request
datatable:
	INITBYTE	LN_TYPE,NT_LIBRARY
	INITLONG	LN_NAME,LibName
	INITBYTE	LIB_FLAGS,LIBF_SUMUSED | LIBF_CHANGED
	INITWORD	LIB_VERSION,VERSION
	INITWORD	LIB_REVISION,REVISION
	INITLONG	LIB_IDSTRING,LibIDString
	dc.l	0


	; System interface routines.  Exec has performed Forbid() before
	; getting here, so do this stuff quick and get the hell out.


	; Routine which is executed by the system as part of first OpenLibrary
	; call, when the library is first loaded into RAM from disk.
	; D0 = library base pointer, A0 = segment list pointer.  Must return
        ; nonzero in D0 if initialization worked, or zero if it failed.
LibInit:
	move.l	d0,-(sp)		; Save D0 for return code
	move.l	d0,a1			; A1 = library base
	move.l	a0,xb_SegList(a1)	; Save seglist addr for future expunge
	lea	__BSSBAS,a0		; Initialize BSS memory to 0s
	move.l	#__BSSLEN,d0
	moveq	#0,d1
	bra.s	clr_lp
clr_bss	move.l	d1,(a0)+
clr_lp	dbf	d0,clr_bss
	move.l	(sp)+,d0		; Return nonzero = success
	rts

	; Open the library.  Executed by system as part of OpenLibrary call,
	; after loading the library into RAM and calling LibInit if necessary.
	; D0 = version, A6 = library base.  Return nonzero if open worked,
	; or zero if open failed for some reason.
LibOpen:
	addq.w	#1,LIB_OPENCNT(a6)		; Increment open count
	bclr	#LIBB_DELEXP,LIB_FLAGS(a6)	; Clear delayed expunge flag
	move.l	a6,d0				; Return nonzero = success
	rts

	; Close the library.  Executed by system as part of CloseLibrary call.
	; A6 = library base.  Return seglist address if a delayed expunge
	; was performed, else return 0 if library is still loaded.
LibClose:
	moveq	#0,d0				; Init return value = 0
	subq.w	#1,LIB_OPENCNT(a6)		; Decrement open count
	bne.s	dontexpunge			; If still open by others, can't expunge
	btst	#LIBB_DELEXP,LIB_FLAGS(a6)	; Was an expunge requested while we were open?
	beq.s	dontexpunge
	bsr.s	LibExpunge			; If so, do it now
dontexpunge:
	rts

	; Expunge the library (remove it from RAM).  Executed by system
	; memory allocation routine when memory gets tight.  A6 = library
	; base.  Return seglist address if library was closed and we were
	; able to expunge, else return 0 if library is still loaded.
LibExpunge:
	movem.l	d2/a5/a6,-(sp)
	move.l	a6,a5
	move.l	_AbsExecBase,a6			; Get ExecBase for future use
	tst.w	LIB_OPENCNT(a5)			; Is library open?
	beq.s	doexpunge
	bset	#LIBB_DELEXP,LIB_FLAGS(a5)	; If so, set delayed expunge flag
	moveq	#0,d0				; and return 0
	bra.s	expungedone
doexpunge:
	move.l	xb_SegList(a5),d2		; Save seglist address in D2
	move.l	a5,a1				; A1 = library base address
	jsr	_LVORemove(a6)			; Unlink library node from system library list
	moveq	#0,d0				; Compute total size of library node
	move.w	LIB_NEGSIZE(a5),d0		;   D0 = bytes used before base
	move.l	a5,a1
	sub.l	d0,a1				;   A1 = base - negsize
	add.w	LIB_POSSIZE(a5),d0		;   D0 = possize + negsize
	jsr	_LVOFreeMem(a6)			; Free library node memory
	move.l	d2,d0				; Return segment list address
expungedone:
	movem.l	(sp)+,d2/a5/a6
	rts

	; "Extra" function (room for future growth?).  We don't need it,
	; so dummy it out.
LibExtFunc:
	moveq	#0,d0
	rts

LibEnd:
	END
