***************************************************************************************
* Nudel-Info-Block (NIB) Routines						      *
***************************************************************************************

;=====================================================================================;
; NudelInfoBlock Subroutines.							      ;
;=====================================================================================;
;NIBR_ALLOCATE	; (De)Allocate memory
;NIBR_OPEN	; DOS/(Open|Close)
;NIBR_LOCK	; DOS/(Un)Lock
;NIBR_EXAMINE	; DOS/(Un)Examine (allocates FileInfoBlock)
;NIBR_NREAD	; Read a file into memory (creates and fills in a new NIB).
;	Include	ASM:Source/Routines/NIB.s


;=====================================================================================;
; NUDEL-INFO-BLOCK STRUCTURE							      ;
;=====================================================================================;
;See ASM:Include/Constants.i


***********************************************************************= 12-Aug-1995 =*
* Create a new NIB								      *
*=====================================================================================*
*  Inputs: Various NIB pointers.						      *
* Outputs: Various NIB pointers updated.					      *
* Active_NIB(a5) = Address of new NIB.						      *
*   Notes: Assume all registers apart from d0/a0 destroyed.			      *
*          Program will abort if allocation fails.				      *
***************************************************************************************
Create_NIB
	Moveq	#NIB_SizeOf,d0			Size of NIB structure.
	Lea	IDS_FileName(pc),a0		-._ Set pseudo-filename.
	Move.l	a0,NIB_NameAdrs+FakeNIB(a5)	-'  in fake NIB.
	Bsr	LeoAllocPooled			-.
	Tst.l	d0				 |- Get mem, quit on failure.
	Beq_ErrorE	ErrAct_AllocMem(pc),FakeNIB_Adrs(a5)
	Sub.l	d1,d1				-.
	Move.l	d0,a0				 |
	Move.l	d1,NIB_Next(a0)			 |
;;;;;;;	Move.l	d1,NIB_Previous(a0)		 |
	Move.l	d1,NIB_MemSize(a0)		 |_ Initialise
	Move.l	d1,NIB_MemAdrs(a0)		 |  the structure.
	Move.l	d1,NIB_NameAdrs(a0)		 |
	Move.l	d1,NIB_Handle(a0)		 |
	Move.l	d1,NIB_Lock(a0)			 |
	Move.l	d1,NIB_FIB(a0)			-'
	Move.l	Last_NIB(a5),NIB_Previous(a0)	Pointer to old last NIB.
	Bne.s	CNIB_S1				-._ If null, this
	Move.l	a0,Base_NIB(a5)			-'  is the base NIB.
	Bra.s	CNIB_S2
CNIB_S1	Move.l	NIB_Previous(a0),a1		-._ Update "next" pointer
	Move.l	a0,NIB_Next(a1)			-'  in previous NIB.
CNIB_S2	Move.l	a0,Last_NIB(a5)			This is now the last NIB.
	Move.l	a0,Active_NIB(a5)		Set Active NIB.
	RTS

IDS_FileName
	Dc.b	"internal data structure"
	Even


***********************************************************************= 11-Aug-1995 =*
* Delete ALL NIBs								      *
*=====================================================================================*
*   Notes: Assume all registers apart are destroyed.				      *
***************************************************************************************
NIBs_DeleteAll
	Bra.s	NIBs_DelStart
NIBs_DelMore
	Move.l	d0,Active_NIB(a5)		...make it "Active" for Deletion.
	Bsr.s	Delete_NIB			Delete this NIB, update Ptrs.
NIBs_DelStart
	Move.l	Base_NIB(a5),d0			Get current Base NIB...
	Bne.s	NIBs_DelMore			If all Freed, routine completed.
	RTS


***********************************************************************= 12-Aug-1995 =*
* Delete a NIB									      *
*=====================================================================================*
*  Inputs: Active_NIB(a5) = Pointer to NIB to delete.				      *
* Outputs: Various NIB pointers updated.					      *
*          Active_NIB(a5) = NULL						      *
*   Notes: Assume all registers apart are destroyed.				      *
*          If extra memory was allocated for the filename, _you_ must be free it.     *
*          (You should allocate this mem as a NIB and thus have it auto-freed)        *
***************************************************************************************
Delete_NIB
	Tst.l	Active_NIB(a5)			-.
	Bne.s	Delete_NIB_Skip			 |- No NIB? Skip routine.
	RTS					-'
Delete_NIB_Skip
	IFD	NIBR_ALLOCATE			-.
	Bsr	Deallocate_NIB			 |- Free memory if allocated.
	ENDC					-'
	IFD	NIBR_OPEN			-.
	Bsr	Close_NIB			 |- Close if openned.
	ENDC					-'
	IFD	NIBR_LOCK			-.
	Bsr	UnLock_NIB			 |- Unlock if locked.
	ENDC					-'
	IFD	NIBR_EXAMINE			-.
	Bsr	UnExamine_NIB			 |- Free FileInfoBlock if allocated.
	ENDC					-'
	Move.l	Active_NIB(a5),a1
	Move.l	#0,Active_NIB(a5)		No active NIB anymore.

	Move.l	NIB_Previous(a1),a0		a0 = Previous NIB
;;;;;;;						a1 = This NIB
	Move.l	NIB_Next(a1),a2			a2 = Next NIB

Delete_NIB_DeLink
	Tst.l	NIB_Previous(a1)		A previous NIB?
	Beq.s	Delete_NIB_NoPrev		If not, jump
Delete_NIB_Prev
	Tst.l	NIB_Next(a1)			A next NIB?
	Beq.s	Delete_NIB_Prev_NoNext		If not, jump

Delete_NIB_Prev_Next
	Move.l	a2,NIB_Next(a0)
	Move.l	a0,NIB_Previous(a2)
	Bra.s	Delete_NIB_DeLinked

Delete_NIB_Prev_NoNext
	Move.l	a0,Last_NIB(a5)
	Move.l	a2,NIB_Next(a0)			(a2 = 0)
	Bra.s	Delete_NIB_DeLinked


Delete_NIB_NoPrev
	Tst.l	NIB_Next(a1)			A next NIB?
	Beq.s	Delete_NIB_NoPrev_NoNext	If not, jump

Delete_NIB_NoPrev_Next
	Move.l	a2,Base_NIB(a5)
	Move.l	a0,NIB_Previous(a2)		(a0 = 0)
	Bra.s	Delete_NIB_DeLinked

Delete_NIB_NoPrev_NoNext
	Move.l	a0,Base_NIB(a5)			(a0 = 0)
	Move.l	a0,Last_NIB(a5)			(a0 = 0)
;;;;;;;	Bra.s	Delete_NIB_DeLinked
Delete_NIB_DeLinked

;;;;;;;	Move.l	a1,a1				-.
	Moveq	#NIB_SizeOf,d0			 |- Free the NIB itself.
	Bra	LeoFreePooled			-'
;;;;;;;	RTS for us.


	IFD	NIBR_ALLOCATE
***********************************************************************= 22-Jul-1995 =*
* Allocate for a NIB								      *
*=====================================================================================*
*  Inputs: Active_NIB(a5)/NIB_MemSize = Size of memory to be allocated.		      *
* Outputs: Active_NIB(a5)/NIB_MemAdrs = Address of allocated memory.		      *
*   Notes: Assume all registers apart are destroyed.				      *
*          Program will abort if allocation fails.				      *
***************************************************************************************
Allocate_NIB
	Move.l	Active_NIB(a5),a0
	Tst.l	NIB_MemAdrs(a0)			-._ Internal error if NIB already
	Bne	Internal			-'  has memory allocated.
	Move.l	NIB_MemSize(a0),d0		Size of allocation to d0.
;;;;;;;	Tst.l	d0				-._ If zero bytes specified,
	Beq	Internal			-'  internal error.
	Bsr	LeoAllocPooled			-.
	Tst.l	d0				 |- Get mem, quit on failure.
	Beq_ErrorE	ErrAct_AllocMem(pc),Active_NIB(a5)
	Move.l	Active_NIB(a5),a0
	Move.l	d0,NIB_MemAdrs(a0)		Store address in NIB.
	RTS

***********************************************************************= 22-Jul-1995 =*
* Deallocate for a NIB								      *
*=====================================================================================*
*  Inputs: Active_NIB(a5)/NIB_MemSize = Size of memory to be allocated.		      *
*          Active_NIB(a5)/NIB_MemAdrs = Address of allocated memory.		      *
* Outputs: Active_NIB(a5)/NIB_MemSize = Null.					      *
*          Active_NIB(a5)/NIB_MemAdrs = Null.					      *
*   Notes: Assume all registers apart are destroyed.				      *
***************************************************************************************
Deallocate_NIB
	Move.l	Active_NIB(a5),a0
	Move.l	NIB_MemSize(a0),d0		-.
;;;;;;;	Tst.l	d0				 |
	Beq.s	Deallocate_NIB_Skip		 |_ Skip if memory
	Move.l	NIB_MemAdrs(a0),d1		 |  not allocated.
;;;;;;;	Tst.l	d1				 |
	Beq.s	Deallocate_NIB_Skip		-'
	Move.l	d1,a1
	Bra	LeoFreePooled			Free memory.
;;;;;;;	RTS for us.
Deallocate_NIB_Skip
	RTS
	ENDC


	IFD	NIBR_OPEN
***********************************************************************= 26-Sep-1995 =*
* Open for a NIB								      *
*=====================================================================================*
*  Inputs: Active_NIB(a5)/NIB_NameAdrs = Address of null-term filename to open.	      *
*          d2 = #Mode_Old or #Mode_New						      *
*          d7 = Temp storage.
* Outputs: Active_NIB(a5)/NIB_Handle = Handle of opened file.			      *
*          d0 = Null if failure.						      *
*   Notes: Assume all registers apart are destroyed.				      *
*          _You_ must handle error (d0 = #0) yourself.				      *
***************************************************************************************
Open_NIB
	Move.w	#3,d7			Maximum number of 3 retries.
	Move.l	Active_NIB(a5),a0	-.
	Tst.l	NIB_Handle(a0)		 |- Internal error if NIB already open.
	Bne	Internal		-'
Open_NIB_Retry
	Move.l	Active_NIB(a5),a0
	Move.l	NIB_NameAdrs(a0),d1	-.
;;;;;;;	Tst.l	d1			 |- Error if no filename to open.
	Beq	Internal		-'
;;;;;;;	Move.l	d1,d1			NameAdrs into d1.
;;;;;;;	Move.l	d2,d2			Mode into d2.
	Movem.l	d2/d7,-(SP)		Preserve mode & nš retries.
	N_CallDOS	Open		Open file.
	Movem.l	(SP)+,d2/d7		Restore mode & nš retries.

;;;;;;;	Note: The only error handled is File-in-use, the calling routine MUST CHECK d0.
	Tst.l	d0			Check for error
	Bne.s	Open_NIB_InUseSkip	-._ If max retries done,
	DBra	d7,Open_NIB_InUse	-'  pass it through.
Open_NIB_InUse_MaxTries
	Moveq	#0,d0
Open_NIB_InUseSkip
	Move.l	Active_NIB(a5),a0
	Move.l	d0,NIB_Handle(a0)	Store filehandle.
	RTS
;-------------------------------------------------------------------------------------;
Open_NIB_InUse
	Movem.l	d2/d7,-(SP)		Preserve mode & nš retries.
	N_CallDOS	IoErr		Get error return code into d0
	Movem.l	(SP)+,d2/d7		Restore mode & nš retries.
	Cmpi.l	#ERROR_OBJECT_IN_USE,d0	-._ If it's any error other than "in use",
	Bne.s	Open_NIB_InUse_MaxTries	-'  drop it through to the calling routine.
	Movem.l	d2/d7,-(SP)		Preserve mode & nš retries.
	Move.l	#250,d1			-._ Wait
	N_CallDOS	Delay		-'  5 Secs.
	Movem.l	(SP)+,d2/d7		Restore mode & nš retries.
	Bra.s	Open_NIB_Retry

***********************************************************************= 22-Jul-1995 =*
* Close for a NIB								      *
*=====================================================================================*
*  Inputs: Active_NIB(a5)/NIB_Handle = Handle of opened file.			      *
* Outputs: Active_NIB(a5)/NIB_Handle = Null.					      *
*   Notes: Assume all registers are destroyed.					      *
*          Errors are handled automatically (jump to error routine).		      *
***************************************************************************************
Close_NIB
	Move.l	Active_NIB(a5),a0		a0 = active NIB.
	Move.l	NIB_Handle(a0),d1		Handle to d1.
	Move.l	#0,NIB_Handle(a0)		Mark as closed (MUST be before ErrorE).
	Tst.l	d1				-._ Skip if handle
	Beq.s	Close_NIB_Skip			-'   not open.
	N_CallDOS	Close			Close the file.
	Tst.l	d0				-._ If error, it's fatal.
	Beq_ErrorE	ErrAct_Close(pc),Active_NIB(a5)
Close_NIB_Skip
	RTS
ErrAct_Close
	Dc.b	"Could not close ",0
	Even
	ENDC


	IFD	NIBR_LOCK
***********************************************************************= 26-Sep-1995 =*
* Lock for a NIB								      *
*=====================================================================================*
*  Inputs: Active_NIB(a5)/NIB_NameAdrs = Address of null-term filename to lock.	      *
*          d2 = #ACCESS_READ, or #ACCESS_WRITE					      *
*          d7 = Temp storage.
* Outputs: Active_NIB(a5)/NIB_Lock = Lock of locked object.			      *
*          d0 = Null if failure.						      *
*   Notes: Assume all registers are destroyed.					      *
*          _You_ must handle error (d0 = #0) yourself.				      *
***************************************************************************************
Lock_NIB
	Move.w	#3,d7			Maximum number of 3 retries.
	Move.l	Active_NIB(a5),a0	-.
	Tst.l	NIB_Lock(a0)		 |- Internal error if NIB already locked.
	Bne	Internal		-'
Lock_NIB_Retry
	Move.l	Active_NIB(a5),a0
	Move.l	NIB_NameAdrs(a0),d1		-.
;;;;;;;	Tst.l	d1				 |- Error if no filename to lock.
	Beq	Internal			-'
;;;;;;;	Move.l	d1,d1				NameAdrs into d1.
;;;;;;;	Move.l	d2,d2				Mode into d2.
	Movem.l	d2/d7,-(SP)		Preserve mode & nš retries.
	N_CallDOS	Lock			Lock the object.
	Movem.l	(SP)+,d2/d7		Restore mode & nš retries.

;;;;;;;	Note: The only error handled is File-in-use, the calling routine MUST CHECK d0.
	Tst.l	d0			Check for error
	Bne.s	Lock_NIB_InUseSkip	-._ If max retries done,
	DBra	d7,Lock_NIB_InUse	-'  pass it through.
Lock_NIB_InUse_MaxTries
	Moveq	#0,d0
Lock_NIB_InUseSkip
	Move.l	Active_NIB(a5),a0
	Move.l	d0,NIB_Lock(a0)		Store lock.
	RTS

Lock_NIB_InUse
	Movem.l	d2/d7,-(SP)		Preserve mode & nš retries.
	N_CallDOS	IoErr		Get error return code into d0
	Movem.l	(SP)+,d2/d7		Restore mode & nš retries.
	Cmpi.l	#ERROR_OBJECT_IN_USE,d0	-._ If it's any error other than "in use",
	Bne.s	Lock_NIB_InUse_MaxTries	-'  drop it through to the calling routine.
	Movem.l	d2/d7,-(SP)		Preserve mode & nš retries.
	Move.l	#250,d1			-._ Wait
	N_CallDOS	Delay		-'  5 Secs.
	Movem.l	(SP)+,d2/d7		Restore mode & nš retries.
	Bra.s	Lock_NIB_Retry

ErrAct_Lock
	Dc.b	"Could not lock ",0
	Even

***********************************************************************= 12-Aug-1995 =*
* UnLock for a NIB								      *
*=====================================================================================*
*  Inputs: Active_NIB(a5)/NIB_Lock = Lock of locked object.			      *
* Outputs: Active_NIB(a5)/NIB_Lock = Null.					      *
*   Notes: Assume all registers are destroyed.					      *
***************************************************************************************
UnLock_NIB
	Move.l	Active_NIB(a5),a0		a0 = active NIB.
	Move.l	NIB_Lock(a0),d1			Lock to d1
	Move.l	#0,NIB_Lock(a0)			Mark as unlocked (MUST be before ErrE).
	Tst.l	d1				-._ Skip if object
	Beq.s	UnLock_NIB_Skip			-'  not locked.
	N_CallDOS	UnLock			UnLock the object.
UnLock_NIB_Skip
	RTS
	ENDC


	IFD	NIBR_EXAMINE
***********************************************************************= 26-Sep-1995 =*
* Examine for a NIB								      *
*=====================================================================================*
*  Inputs: Active_NIB(a5)/NIB_Lock = Lock of locked object.			      *
* Outputs: Active_NIB(a5)/NIB_FIB = Pointer to filled in FileInfoBlock.		      *
*   Notes: Assume all registers are destroyed.					      *
*	 : Errors are handled automatically by this routine.			      *
***************************************************************************************
Examine_NIB
	Move.l	Active_NIB(a5),a0	-.
	Tst.l	NIB_FIB(a0)		 |- Internal error if NIB already examined.
	Bne	Internal		-'

	Lea	IDS_FileName(pc),a0		-._ Set pseudo-filename in fake NIB
	Move.l	a0,NIB_NameAdrs+FakeNIB(a5)	-'  to "internal data structure".

	Moveq	#DOS_FIB,d1		Want to Allocate a FileInfoBlock.
	Moveq	#0,d2			No tags.
	N_CallDOS	AllocDosObject	Allocate it.
	Move.l	Active_NIB(a5),a0	-._ Store pointer to
	Move.l	d0,NIB_FIB(a0)		-'  the FileInfoBlock.
	Beq_ErrorE	ErrAct_AllocMem(pc),FakeNIB_Adrs(a5)

;;;;;;;	Move.l	Active_NIB(a5),a0
	Move.l	NIB_Lock(a0),d1		File lock for Examine()
	Move.l	d0,d2			FileInfoBlock for Examine()
	N_CallDOS	Examine		Examine the file.
	Tst.l	d0
	Beq_ErrorE	ErrAct_Examine(pc),Active_NIB(a5)
	RTS

ErrAct_Examine
	Dc.b	"Could not examine ",0
	Even

***********************************************************************= 26-Sep-1995 =*
* UnExamine for a NIB (Frees the FileInfoBlock)					      *
*=====================================================================================*
*  Inputs: Active_NIB(a5)/NIB_FIB = Allocated FileInfoBlock.			      *
* Outputs: Active_NIB(a5)/NIB_FIB = NULL (FileInfoBlock is freed).		      *
*   Notes: Assume all registers are destroyed.					      *
***************************************************************************************
UnExamine_NIB
	Move.l	Active_NIB(a5),a0	-.
	Move.l	NIB_FIB(a0),d2		 |_ Skip routine if FIB
	Bne.s	UnExamine_NIB_DoIt	 |  hasn't been allocated.
	RTS				-'
UnExamine_NIB_DoIt
	Moveq	#DOS_FIB,d1		Want to Free a FileInfoBlock.
;;;;;;;	Move.l	d2,d2			Pointer to FIB to be freed.
	N_JumpDOS	FreeDosObject	Free it.
;;;;;;;	RTS for us.
	ENDC


	IFD	NIBR_NREAD
***********************************************************************= 26-Sep-1995 =*
* NRead_NIB: Reads a file into memory, creating and filling in a new NIB.	      *
*=====================================================================================*
*  Inputs: a1 - Pointer to filename to read into memory.			      *
* Outputs: d0 - Boolean success.						      *
*	   On failure ONLY: The given filename is put into FakeNIB for you.	      *
*	   Active_NIB(a5) - Pointer to the new NIB for the file.		      *
*	   Active_NIB(a5)/NIB_FIB - Pointer to filled-in FileInfoBlock for file.      *
*	   Active_NIB(a5)/NIB_MemSize - Size of memory allocated (equal to filesize)  *
*	   Active_NIB(a5)/NIB_MemAdrs - Pointer to memory containing the file.	      *
*   Notes: Failure will only occur if a DOS routine fails, so Ioerr()'s result is     *
*	   valid after calling this routine.					      *
*	 : When the file is finished with, you can use Delete_NIB as "UnRead_NIB".    *
*	 : The file will NOT be locked or open after the routine is done.	      *
*	 : If the file is empty the program will output an error message and abort.   *
*	 : Assume all registers destroyed.					      *
***************************************************************************************
NRead_NIB
	Move.l	a1,-(SP)		Preserve pointer to filename.
	Bsr	Create_NIB		Create a new NIB for this file.
;;;;;;;	(Errors handled automatically)
	Move.l	(SP)+,a1		Restore pointer to filename.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
	Move.l	Active_NIB(a5),a0	-._ Put pointer to filename
	Move.l	a1,NIB_NameAdrs(a0)	-'  into the NIB structure.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
	Moveq	#ACCESS_READ,d2		-._ Lock() the file
	Bsr	Lock_NIB		-'  with READ mode.
	Tst.l	d0			-._ If it failed,
	Beq.s	NRead_NIB_Failure	-'  abort the routine.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
	Bsr	Examine_NIB		Allocate and fill-in the FileInfoBlock
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
	Move.l	Active_NIB(a5),a0		-.
	Move.l	NIB_FIB(a0),a1			 |- Want to allocate enough space to
	Move.l	fib_Size(a1),NIB_MemSize(a0)	-'  load the entire file in to memory.
	Beq_ErrorE	ErrAct_EmptyFile(pc),Active_NIB(a5)
;;;;;;;	(The Finish routine will delete the NIB, there's no need to worry about it).
	Bsr	Allocate_NIB		Allocate the memory for the file.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
	Move.l	#MODE_OLDFILE,d2	-._ Open the file
	Bsr	Open_NIB		-'  (must already exit).
	Tst.l	d0			-._ If it failed,
	Beq.s	NRead_NIB_Failure	-'  abort the routine.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
	Bsr	UnLock_NIB		Don't need the lock anymore, so UnLock file.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
	Move.l	Active_NIB(a5),a0
	Move.l	NIB_Handle(a0),d1	Handle to read from,
	Move.l	NIB_MemAdrs(a0),d2	Memory to read into,
	Move.l	NIB_MemSize(a0),d3	Amount to read (filesize and memory size).
	N_CallDOS	Read		Read the file into memory.
	Cmpi.l	#-1,d0			(Read returns -1 for error!)
	Beq.s	NRead_NIB_Failure
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
	Bsr	Close_NIB		Close the file.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
	Moveq	#1,d0			Indicate success.
	RTS
;-------------------------------------------------------------------------------------;
NRead_NIB_Failure
	Move.l	Active_NIB(a5),a0	Put filename into FakeNIB automatically.
	Move.l	NIB_NameAdrs(a0),NIB_NameAdrs+FakeNIB(a5)
	Bsr	Delete_NIB		Delete the NIB.
	Moveq	#0,d0			Indicate failure.
	RTS
;-------------------------------------------------------------------------------------;
ErrAct_Read
	Dc.b	"Could not read ",0
ErrAct_EmptyFile
	Dc.b	"File is empty: ",0
	Even
	ENDC
