; 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	XProtocolCleanup
	XDEF	XProtocolSetup
	XDEF	XProtocolSend
	XDEF	XProtocolReceive
	XDEF	XProtocolHostMon
	XDEF	XProtocolUserMon
	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	10
LibName:
	dc.b	"xprzmodem.library",0
LibIDString:
	dc.b	"xprzmodem 2.10 (12 Feb 1991)",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
	dc.l	XProtocolSend
	dc.l	XProtocolReceive
	dc.l	XProtocolHostMon
	dc.l	XProtocolUserMon
	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



; Added glue to protect the comm program from having us munge its registers
; when it calls our code, due to bug which caused us to munge the A6
; register in an earlier version.  Since the XPR spec requires the arguments
; to be passed in the same registers that Lattice uses for function call
; register arguments, all we have to do is save the non-changeable registers
; on entry and restore them on exit.

XProtocolCleanup:
	movem.l	d2-d7/a2-a6,-(sp)
	jsr	@XProtocolCleanup
	movem.l	(sp)+,d2-d7/a2-a6
	rts

XProtocolSetup:
	movem.l	d2-d7/a2-a6,-(sp)
	jsr	@XProtocolSetup
	movem.l	(sp)+,d2-d7/a2-a6
	rts

XProtocolSend:
	movem.l	d2-d7/a2-a6,-(sp)
	jsr	@XProtocolSend
	movem.l	(sp)+,d2-d7/a2-a6
	rts

XProtocolReceive:
	movem.l	d2-d7/a2-a6,-(sp)
	jsr	@XProtocolReceive
	movem.l	(sp)+,d2-d7/a2-a6
	rts

XProtocolHostMon:
	movem.l	d2-d7/a2-a6,-(sp)
	jsr	@XProtocolHostMon
	movem.l	(sp)+,d2-d7/a2-a6
	rts

XProtocolUserMon:
	movem.l	d2-d7/a2-a6,-(sp)
	jsr	@XProtocolUserMon
	movem.l	(sp)+,d2-d7/a2-a6
	rts

LibEnd:
	END
