;*
;*	$Filename: taglist.a $
;*	$Release: , V$
;*	$Revision: 1.13 $
;*	$Date: 1994/01/11 19:33:24 $
;*
;*  TagListLibrary is a shared library. It contains functions, optimzed for
;*  maximum speed, which manipulates taglists.
;*
;*	(C) Copyright 1993 Zeal-Computer
;*	All Rights Reserved
;*
;*	Please set tabsize to 8 when reading this document.
;*




;*
;*COPYRIGHT
;*  This  is the second release of the TagListLibrary documentation and is
;*  consistent with release 1.1 of the shared library taglist.library.
;*
;*  This  code  is  freely  redistributable  upon  the conditions that this
;*  notice  remains  intact  and that modified versions of this file not be
;*  distributed  in  any way. The author makes no warranty of any kind with
;*  respect to this product and explicitly disclaims any implied warranties
;*  of merchantability or fitness for any particular purpose.
;*
;*	Published by
;*		ZealComputer
;*		Vognporten 14, 213
;*		2620 Albertslund
;*		Denmark
;*




;*
;*INTRODUCTION
;*  Starting  with  KickStart  2.0:  Taglists  are  used to handle variable
;*  numbers  of  arguments  to  functions.  This  method is rather slow but
;*  highly  flexible  since you search out each argument independent of its
;*  location, and if it has been left out a default value may be specified.
;*
;*  KickStart 1.2/1.3 users can use the primitive functions in this library
;*  as a replacement for the Kickstart 2.0 ones. Both KickStart
;*  1.2/1.3 and KickStart 2.0 users can benefit from the 'taglist to
;*  structure conversion' functions. (KickStart 3.0 has the same functions, 
;*  but they are in no way better than those of taglist.library.)
;*
;*  If Commodore modify, or improve, their taglist specifications you won't
;*  need to rewrite or recompile your programs, updating the binary
;*  taglist.library file will suffice.
;*
;*  Please mail me if you think of any improvements to either the code, the
;*  documentation or both.
;*		
;*		Sam Hepworth
;*		Vognporten 14, 213
;*		2620 Albertslund
;*		Denmark
;*
;*		(shepherd@diku.dk)
;*




;*
;*BACKGROUND
;*  Taglists are used when a function receives many arguments of which some
;*  are  not  necessary.  The  function  will  then be designed to read its
;*  arguments from the taglist -- not from registers or the stack.
;*
;*  Each  argument  passed  via  a  taglist is a tagitem. Each of these are
;*  built  from  two longwords where the first longword is used to identify
;*  the second longword, which contains the value of the argument.
;*
;*  An example of a tagitem:
;*
;*		tagitem = TAGITEM {WA_Left,0};
;*	
;*  An  array  of tagitems forms a taglist. The previous example is however
;*  not  a  correct  taglist  because  it breaks a very important rule: All
;*  taglists  must  terminate  with  a  special  tagitem which contains the
;*  system  tag  "TAG_DONE"  and  has  no  argument  value.  Below are some
;*  examples of various taglists:
;*
;*		/* this is the only legal taglist with only one tagitem! */
;*		empty_taglist = TAGITEM {TAG_DONE,0};
;*
;*		/* this is a simple taglist with 5 tagitems of which 4 contain arguments */
;*		first_taglist = TAGITEM[]
;*		{
;*			WA_Left,0,WA_Top,11,
;*			WA_Width,640,WA_Height,200,
;*			TAG_DONE,0
;*		};
;*
;*		second_taglist_3 = TAGITEM[]
;*		{
;*			WA_Width,640,WA_Height,200,
;*			TAG_DONE,0
;*		};
;*		second_taglist_2 = TAGITEM[]
;*		{
;*			WA_Top,11
;*			TAG_MORE,(TAGDATA)&second_taglist_3
;*		};
;*		second_taglist = TAGITEM[]
;*		{
;*			WA_Left,0
;*			TAG_MORE,(TAGDATA)&second_taglist_2
;*		};
;*
;*		third_taglist = TAGITEM[]
;*		{
;*			TAG_SKIP,2,
;*			WA_Left,1,WA_Left,2,
;*			TAG_IGNORE,0,
;*			WA_Left,0,WA_Top,11,
;*			WA_Width,640,WA_Height,200,
;*			TAG_DONE,0
;*		};
;*	
;*	 An attempt to illustrate the previous taglist using ASCII graphics:
;*	
;*	   first_taglist
;*	  --------------
;*	 |WA_Left   0   |
;*	 |WA_Top    11  |
;*	 |WA_Width  640 |
;*	 |WA_Height 200 |
;*	 |TAG_DONE  0   |
;*	  --------------
;*	
;*	  second_taglist
;*	  --------------    second_taglist_2
;*	 |WA_Left   0   |    --------------    second_taglist_3
;*	 |TAG_MORE      |-->|WA_Top        |    --------------
;*	  --------------    |TAG_MORE      |-->|WA_Width      |
;*	                     --------------    |WA_Height     |
;*	                                       |TAG_DONE      |
;*	                                        --------------
;*	   third_taglist
;*	  --------------
;*	 |TAG_SKIP   2  |--+
;*	 |WA_Left    1  |  |
;*	 |WA_Left    2  |  |
;*	 |TAG_IGNORE 0  |<-+
;*	 |WA_Left    0  |
;*	 |WA_Top     11 |
;*	 |WA_Width   640|
;*	 |WA_Height  200|
;*	 |TAG_DONE   0  |
;*	  --------------
;*	
;*  4  system  tags  and  65535  user  tags  exists.  The  following  is  a
;*  description of these:
;*
;*	 TAG_DONE   Terminates taglist. ti_Data unused.
;*	
;*	 TAG_END    Synonym for TAG_DONE.
;*	
;*	 TAG_IGNORE Ignore this tagitem, not end of array.
;*	
;*	 TAG_MORE   ti_Data  is  pointer  to  another  taglist  note  that this
;*              tagitem terminates the current taglist.
;*
;*	 TAG_SKIP   Skips this and the next ti_Data tagitems. Remember that ALL
;*              tagitems are skipped, so beware not to skip any TAG_MORE or
;*              TAG_DONE tagitems.
;*
;*	 TAG_USER   Distinguishes  user  tags from system tags. When you create
;*              your  own tags you should always OR this value with the tag
;*              value.  User  tags  should use values in the range 1..65535
;*              (when  ORed  with  TAG_USER  the values will be in range of
;*              0x80000001..0x8000FFFF).
;*
;*
;*	Things to note about taglists
;*	ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ
;*  If a taglist contains two or more tagitems with equal ids then only the
;*  first tagitem is used. This way the tagitems of an existing taglist can
;*  be replaced simply by creating a new taglist contaning the new tagitems
;*  and a TAG_MORE linking it to the old taglist.
;*
;*  A  very clever program may be able to determine the value of additional
;*  tagitems with matching tagid's using the TL_NextTagItem() function.
;*
;*  A  program  should  never  modify  the contents of a tagitem created by
;*  another  program,  thus  if  a program must alter a taglist, one of the
;*  following methods should be used:
;*
;*	 · Use  TL_CopyTagList  to  make  a  copy  of  the  taglist.  Then  use
;*     TL_FindTagItem  to  get  a  pointer  to  the  tagitem to be changed.
;*     Finally write the new value to ti_Data.
;*
;*	 · Create  a  new taglist which contains the new tagitem and a TAG_MORE
;*     tagitem which links this taglist to the existing taglist.
;*
;*	 · Create  a  new  taglist which contains the new tagitem and merge the
;*     new and old taglists into another taglist using TL_MergeTagLists.
;*
;*
;*	BigO
;*	ŻŻŻŻ
;*  BigO  estimates  has  been used to express the time complexity for some
;*  functions.   Many programming books explain this notation and it is not
;*  discussed in this document.
;*

 include "macros/flow.i"
 include "macros/label.i"
 include "macros/stack.i"
 mRESET

 XREF _LVOAlert

 lDEFINE TYPE_MASK,$7f
 lDEFINE MISS_MASK,$ff80

 lDEFINE TAG_DONE,0
 lDEFINE TAG_INGORE,1
 lDEFINE TAG_MORE,2
 lDEFINE TAG_SKIP,3
 lDEFINE TAG_INIT,1
 lDEFINE TAG_USER,$80000000

 lDEFINE TL_BadTag,'BADT'
 lDEFINE TL_BadMapTag,'BADM'

 lSTRUCTURE TagMapItem,0
	lULONG tmi_Tag
	lUWORD tmi_Offset
	lUBYTE tmi_MissBits
	lUBYTE tmi_Type
	lULONG tmi_Default
 lENDSTRUCT tmi_SizeOf

; Bit 7 in tmi_Type is part of the MissBits! The function TL_MapTagList 
; returns this bit in bit 8.

 lSTRUCTURE TagItem,0
	lULONG ti_Tag
	lULONG ti_Data
 lENDSTRUCT ti_SizeOf




ERROR MACRO
	PEA.L \1
	BSR.W ShowAlert
	ENDM

 lLOCAL ShowAlert ; alert number on sp, registers unmodified
	sPUSHM.l d0-d1/d7/a0-a1/a6
	move.l (4).w,a6
	move.l 7*4(sp),d7
	jsr _LVOAlert(a6)
	sPOPM.l d0-d1/d7/a0-a1/a6
	move.l (sp)+,(sp)
	rts




;*
;*NAME
;*	TL_NextTagItem - returns next tagitem in taglist
;*
;*SYNOPSIS
;*	TAGITEM * = TL_NextTagItem(TAGITEM *taglist);
;*	D0/A0/CC                   A0
;*
;*FUNCTION
;*  Returns pointer to next user tagitem in taglist.
;*
;*INPUT
;*	taglist		The taglist to search (may be NULL).
;*
;*RESULT
;*  tagitem     First  user  tagitem  encountered,  or  NULL if the taglist
;*              terminated.
;*
;*EXAMPLE
;*  This   example  shows  how  to  get  arguments  from  a  taglist  using
;*  TL_NextTagItem.  Please  note  the  use  of  "found"  to determine what
;*  tagitems have been found.
;*
;*	VOID
;*	function(TAGITEM *taglist)
;*	{
;*		ULONG found=NULL;
;*		TAGITEM *tagitem=taglist;
;*		
;*		for(;(found != 1|2|4|8) && tagitem; tagitem = TL_NextTagItem(tagitem))
;*		{
;*			switch(tagitem->ti_Tag)
;*			{
;*			  case TAG1 : if (found&1) break;
;*				/* ... */
;*				found |= 1; break;
;*			  case TAG2 : if (found&2) break;
;*				/* ... */
;*				found |= 2; break;
;*			  case TAG3 : if (found&4) break;
;*				/* ... */
;*				found |= 4; break;
;*			}
;*		}
;*	}
;*
;*NOTE
;*	This function has been implemented differently from 'utility.library'.
;*
;*BUGS
;*	Please report any!
;*
;*NOTE
;*  For  A6  to  contain  the  library  base  is  not a necessary when this
;*  function is called.
;*
;*TIME
;*  O(n)  where  'n' is the number of system tags encountered before a user tag
;*  is encountered.
;*

 lGLOBAL LibTL_NextTagItem
	move.l a0,d0				; d0 = a0;
	WNE.b						; if (d0!=0) {
		move.l (a0),d0			;  d0 = a0->ti_Tag;
		bpl.b 1$				;  if (d0>=0) goto command;
3$		addq.l #8,a0			;  a0 = a0+8;
6$		move.l (a0),d0			;  d0 = a0->ti_Tag;
		bpl.b 1$				;  if (d0>=0) goto command;
		move.l a0,d0			;  d0 = a0;
	WEND						; }
2$	rts							; return d0/a0/ccr;
1$	beq.b 2$					; if d0==0 goto TAG_DONE;
	subq.l #1,d0				; d0 = d0-1;
	beq.b 3$					; if d0==0 goto TAG_IGNORE;
	subq.l #1,d0				; d0 = d0-1;
	beq.b 4$					; if d0==0 goto TAG_MORE;
	subq.l #1,d0				; d0 = d0-1;
	beq.b 5$					; if d0==0 goto TAG_SKIP;
	ERROR TL_BadTag				;
	moveq.l #0,d0				; d0 = 0;
	move.l d0,a0				; a0 = 0;
	rts							; return d0/a0/ccr;
4$	move.l (4,a0),a0			; a0 = a0->ti_Data; // next taglist
	bra.b 6$					; goto loop;
5$	move.l (4,a0),d0			; d0 = a0->ti_Data; // tagitems to skip
	lsl.l #3,d0					; d0 = d0*8;
	lea (8,a0,d0.l),a0			; a0 = 8+a0+d0;
	bra.b 6$					; goto loop;




;*
;*NAME
;*	TL_GetTagData - searches tagitem in taglist and returns data
;*
;*SYNOPSIS
;*	ULONG value, TAGITEM *tagitem =
;*	D0           A0                  
;*
;*	TL_GetTagData(ULONG id, ULONG default, TAGITEM *taglist)
;*                D0        D1             A0						
;*FUNCTION
;*  Searches a given tagitem in a taglist.  If the tagitem does not exist
;*  default is returned, else the ti_Data is returned.
;*
;*INPUT
;*	id          User  tag  to be found. It is not possibile to search for a
;*              system tag.
;*
;*	deafult		Value returned if tag could not be found.
;*
;*	taglist		Taglist to search (may be NULL).
;*
;*RESULT
;*	value		Value of ti_Data, or 'default' if no tagitem was found.
;*
;*	tagitem		Pointer to tagitem found, or NULL if no tagitem was found.
;*
;*NOTE
;*  For  A6  to  contain  the  library  base  is  not a necessary when this
;*  function is called.
;*
;*TIME
;*  O(n) where 'n' is the number of tagitems to examine before the user tag
;*  is found.
;*

 lGLOBAL LibTL_GetTagData
	sPUSH.l d1					; "save default value on stack"
	bsr.b LibTL_FindTagItem		; if (d0/a0 = FindTagItem(tag, taglist))
	WNE.b						; {
		addq.l #4,a7			;   "remove default value from stack"
		move.l (4,a0),d0		;   d0 = a0->ti_Data;
		rts						;   return a0/d0/cc;
	WEND						; }
	sPOP.l d0					; return a0/d0/cc;
	rts




;*
;*NAME
;*	TL_FindTagData - search tagitem in taglist and return pointer to ti_Data
;*
;*SYNOPSIS
;*	ULONG *data = TL_FindTagData(ULONG id, TAGITEM *list)
;*	                             D0        A0
;*FUNCTION
;*  Find user tag and return a pointer to ti_Data.
;*
;*INPUT
;*	id		User tag to search.
;*	list	Taglist to search (may be NULL).
;*
;*RESULT
;*	data	Pointer to ti_Data of tagitem found, or NULL if no tagitem
;*			was found.
;*
;*NOTE
;*  For  A6  to  contain  the  library  base  is  not a necessary when this
;*  function is called.
;*

 lGLOBAL LibTL_FindTagData
	bsr.b LibTL_FindTagItem		; if (d0/a0 = FindTagItem(tag, taglist))
	WNE.b						; {
		addq.l #4,d0			;  d0 = &(a0->ti_Data);
		move.l d0,a0			;  a0 = d0;
	WEND						; }
	rts							; return d0/a0/cc;




;*
;*NAME
;*	TL_FindTagItem - search tagitem in taglist
;*
;*SYNOPSIS
;*	TAGITEM *item = TL_FindTagItem(ULONG id, TAGITEM *list)
;*	D0                             D0        A0
;*FUNCTION
;*	Search a given tagitem in a taglist.
;*
;*INPUT
;*	id		User tag to search.
;*	list	Taglist to search (may be NULL).
;*
;*RESULT
;*	item	Pointer to tagitem found, or NULL if no tagitem was found.
;*
;*NOTE
;*  For  A6  to  contain  the  library  base  is  not a necessary when this
;*  function is called.
;*

 lGLOBAL LibTL_FindTagItem
	move.l d0,d1				; d1 = d0;
	move.l a0,d0				; d0 = a0;
	bne.b 1$					; if (d0) goto 2$;
	rts							; return d0/a0/cc;
2$		addq.l #8,a0			; do { a0 = a0 + 8;
1$		move.l (a0),d0			;   d0 = a0->ti_Tag;
		bpl.b 3$				;   if (d0>=0) goto command;
	cmp.l d0,d1					; } while (d0!=d1);
	bne.b 2$					; 
	move.l a0,d0				; d0 = a0;
4$	rts							; return d0/a0/cc;
3$	beq.b 4$					; if (!d0) return d0; // TAG_DONE
	subq.l #1,d0				; d0 = d0-1;
	beq.b 2$					; if (!d0) goto loop; // TAG_IGNORE
	subq.l #1,d0				; d0 = d0-1;
	beq.b 5$					; if (!d0) goto TAG_MORE;
	subq.l #1,d0				; d0 = d0-1;
	beq.b 6$					; if (!d0) goto TAG_SKIP;
	ERROR TL_BadTag				; ShowAlert(TL_BadTag);
	moveq.l #0,d0				; d0 = 0;
	move.l d0,a0				; a0 = d0;
	rts							; return d0/a0/cc;
5$	move.l (4,a0),a0			; a0 = a0->ti_Data; // next taglist
	bra.b 1$					; goto loop;
6$	move.l (4,a0),d0			; d0 = a0->ti_Data;
	lsl.l #3,d0					; d0 = d0*8;
	lea (8,a0,d0.l),a0			; a0 = 8+a0+d0;
	bra.b 1$					; goto loop;




;*
;*NAME
;*	TL_MapTagList - convert taglist to structure
;*
;*SYNOPSIS
;*	ULONG missbits = 
;*	D0              
;*
;*	TL_MapTagList(TAGMAPITEM *map, VOID *struct, TAGITEM *list)
;*                A0               A1            A2
;*
;*FUNCTION
;*  This  routine copies tagvalues from a taglist into specified offsets in a
;*  structure.
;*
;*INPUT
;*	map      Tagmaplist used to convert taglist into structure.
;*
;*	struct   Memory  block  in which to write structure. Fill this memory block
;*           with zeros or some other values before you call this function.
;*
;*	list     Taglist to read values from.
;*
;*RESULT
;*	missbits Flags indicating tagitems not found.
;*
;*EXAMPLE
;*	VOID
;*	function(TAGITEM *taglist)
;*	{
;*		struct Color
;*		{
;*			UBYTE Red,
;*			UBYTE Green,
;*			UBYTE Blue
;*		} color;
;*		TAGMAPITEM map[] =
;*		{
;*			TAGMAP(CA_Red,Color,Red,0,INT,BYTE,DEFAULT,0),
;*			TAGMAP(CA_Green,Color,Green,0,INT,BYTE,DEFAULT,0),
;*			TAGMAP(CA_Blue,Color,Blue,0,INT,BYTE,DEFAULT,0),
;*			TAG_DONE
;*		};
;*	
;*		TL_MapTagList(map,color,taglist);
;*	
;*		/* use "color" in some code... */
;*	}
;*	
;*NOTE
;*  For  A6  to  contain  the  library  base  is  not a necessary when this
;*  function is called.
;*
;*TIME
;*  O(n*m)  where  "n"  is  the  number  of tagitems in list and "m" is the
;*  number of tagmapitems in map.
;*
;*BUGS
;*	Version 1.0 had the following bugs:
;*	 · It  was  not  possible  to say which tagitem would be mapped if more
;*     tagitems with equal ti_Tag existed. Now the first is allways mapped.
;*
;*   · TAG_SKIP would crash the machine.
;*
;*  Version 1.1
;*	 Please report any.
;*
;*SEE ALSO
;*	Include files "taglist.h" and "taglist.i" .

;------ code creation macros ------------------------------------------------
mRE	MACRO
	move.\1	d2,(a1,d0.w)
	bra.b mgo
	ENDM
mBO	MACRO
	beq.b mde\1
	or.\1 d1,(a1,d0.w)
	bra.b mgo
	ENDM
mbo	MACRO
	bne.b mde\1
	or.\1 d1,(a1,d0.w)
	bra.b mgo
	ENDM
mDE	MACRO
	or.\1 d1,(a1,d0.w)
	bra.b mgo
	ENDM
mde	MACRO
mde\1
	not.\1 d1
	and.\1 d1,(a1,d0.w)
	bra.b mgo
	ENDM

;------ empty taglist -------------------------------------------------------
empty
	DC.L TAG_DONE

;------ code ----------------------------------------------------------------
mb1 mRE b,3
mw1 mRE w,2
ml1 mRE l,0
mb2 mBO b,3
mw2 mBO w,2
ml2 mBO l,0
mb3 mbo b,3
mw3 mbo w,2
ml3 mbo l,0
mb4 mde b,3
mw4 mde w,2
ml4 mde l,0
mb5 mDE b,3
mw5 mDE w,2
ml5 mDE l,0

;------ entry ---------------------------------------------------------------
 lGLOBAL LibTL_MapTagList
	sPUSHM.l d2-d5/a2-a3			; "save registers"
	moveq.l #0,d3					; d3 = 0;
	moveq.l #TYPE_MASK,d4			; d4 = TYPE_MASK;
	move.l a2,d5					; if (!(d5 = a2))
	WEQ.b							; {
		lea (empty,pc),a2			;   a2 = empty;
		move.l a2,d5				;   d5 = a2;
	WEND							; }
mgo	move.l (a0)+,d0					; d0 = a0->ti_Tag;
	bmi.b mlo

;------ handle command in tagmap --------------------------------------------
	WEQ.b							; if (d0==TAG_END) {
1$		move.w d3,d0				;   d0 = d3;
		lsr.w #8,d0					;   d0 = d0 / 256;
		WCS.b						;   if (d3&0x80) {
			bset.l #8,d0			;     d0 |= 0x100;
		WEND						;   }
		sPOPM.l d2-d5/a2-a3			;   "restore registers"
		rts							;   return d0/cc;
	WEND							; }
	subq.l #1,d0					; d0 = d0-1;
	beq.b mde						; if (!d0) goto mde; // TAG_INIT
	ERROR TL_BadMapTag				; "bad TagId encountered"
	moveq.l #0,d0					; d0 = 0;
	bra.b 1$						; goto 1$

;------ search loop ---------------------------------------------------------
									; do {
mlo		move.l (a2),d1				;   if ((d1=ti_Tag(a2))>-1)
		bpl.b mco					;     goto mco;
		addq.l #8,a2				;   a2=a2+8;
	cmp.l d0,d1						; } while (d0!=d1);
	bne.b mlo

;------ tag found in taglist ------------------------------------------------
	move.l (a0)+,d0					; d0=tmi_?(a0);
	move.l (a0)+,d1					; d1=tmi_(a0); a0=a0+8;
	and.w d4,d0						; d0 &= d4; // TYPE_MASK
	move.l (mj1,pc,d0.w),a3			; a3 = "address of type-specific function"
	swap.w d0						; d0 = d0<<16;
	move.l -(a2),d2					; d2 = (a2-8)->ti_Data;
	move.l d5,a2					; a2 = d5;
	jmp (a3)

;------ handle command in taglist -------------------------------------------
mco	WNE.b							; if (!d1) { // !TAG_DONE
		addq.l #8,a2				;   a2 = a2+8;
		subq.l #1,d1				;   d1 = d1-1;
		BEQ.b mlo					;   if (!d1) goto mlo; // TAG_IGNORE
		subq.l #1,d1				;   d1 = d1-1;
		WEQ.b						;   if (!d1) { // TAG_MORE
			move.l -(a2),a2			;     a2 = (a2-8)->ti_Data;
			bra.b mlo				;     goto mlo;
		WEND						;   }
		subq.l #1,d1				;   d1 = d1-1;
		WEQ.b						;   if (!d1) { // TAG_SKIP
			move.l -(a2),d1			;     d1 = (a2-8)->ti_Data;
			asl.l #3,d1				;     d1 *= 8;
			lea (4,a2,d1.l),a2		;     a2 = 4+a2+d1;
			bra.b mlo				;     goto mlo;
		WEND						;   }
		ERROR TL_BadTag				;   "bad TagId encountered"
		moveq.l #-1,d0				;	d0 = "what about missbits?"
		sPOPM.l d2-d5/a2-a3			;   "restore registers"
		rts							;   return d0/cc;		
	WEND							; }

;------ tag not found in taglist --------------------------------------------
mde	move.l (a0)+,d0					; d0=tmi_?(a0);
	move.l (a0)+,d1					; d1=tmi_?(a0);a0=a0+8;
	or.w d0,d3						; d3|=d0; /* set missbits */
	and.w d4,d0						;
	move.l (mj2,pc,d0.w),a3
	swap.w d0
	move.l d1,d2
	move.l d5,a2
	jmp (a3)

;------ tagitem found ------------------------------------------------------
 CNOP 0,4
mj1
 DC.L mb1,mw1,ml1	; NORMAL	NODEFAULT	copy value into struct
 DC.L mb2,mw2,ml2	; BOOL		NODEFAULT	or value into struct
 DC.L mb3,mw3,ml3	; bool		NODEFAULT	and ~value into struct
 DC.L mb1,mw1,ml1	; NORMAL	DEFAULT		copy value into struct
 DC.L mb2,mw2,ml2	; BOOL		DEFAULT		or value into struct
 DC.L mb3,mw3,ml3	; bool		DEFAULT		and ~value into struct
 DC.L mb2,mw2,ml2	; BOOL		default		or value into struct
 DC.L mb3,mw3,ml3	; bool		default		and ~value into struct

;------ tagitem could not be found ------------------------------------------
mj2
 DC.L mgo,mgo,mgo	; NORMAL	NODEFAULT	skip
 DC.L mgo,mgo,mgo	; BOOL		NODEFAULT	skip
 DC.L mgo,mgo,mgo	; bool		NODEFAULT	skip
 DC.L mb1,mw1,ml1	; NORMAL	DEFAULT		copy default into struct
 DC.L mb5,mw5,ml5	; BOOL		DEFAULT		or default into struct
 DC.L mb4,mw4,ml4	; bool		DEFAULT		and ~default into struct
 DC.L mb4,mw4,ml4	; BOOL		default		and ~default into struct
 DC.L mb5,mw5,ml5	; bool		default		or default into struct









;*
;*NAME
;*	TL_BuildTagList - convert structure to taglist
;*
;*SYNOPSIS
;*  TAGITEM *list =
;*	D0/CC
;*
;*	TL_BuildTagList(TAGAMPITEM *map, APTR struct, TAGITEM *list)
;*	                A0               A1           A2
;*FUNCTION
;*	This routine converts a structure into a taglist.
;*
;*INPUT
;*	map		Tagmaplist used to copy values from structure into taglist.
;*
;*	struct	Pointer to structure.
;*
;*	list	Taglist to copy values into (may be NULL).
;*
;*RESULT
;*	list	Same as input "list"
;*
;*TIME
;*	O(n) where "n" is the number of tagmapitems.
;*
;*NOTE
;*  For  A6  to  contain  the  library  base  is  not a necessary when this
;*  function is called.
;*
;*BUGS
;*	Please report.
;*

;------ code creation macros ------------------------------------------------
bRE	MACRO
	move.\1	(a1,d0.w),d2
	bra.b bgo
	ENDM
bBO	MACRO
	move.\1 (a1,d0.w),d2
	and.\1 d1,d2
	cmp.\1 d1,d2
	bne.b bgo
	moveq.l #1,d2
	bra.b bgo
	ENDM
bbo	MACRO
	move.\1 (a1,d0.w),d2
	not.\1 d2
	and.\1 d1,d2
	cmp.\1 d1,d2
	bne.b bgo
	moveq.l #1,d2
	bra.b bgo
	ENDM

;------ code ----------------------------------------------------------------
bb1 bRE b,3
bw1 bRE w,2
bl1 bRE l,0
bb2 bBO b,3
bw2 bBO w,2
bl2 bBO l,0
bb3 bbo b,3
bw3 bbo w,2
bl3 bbo l,0

;------ entry ---------------------------------------------------------------
 lGLOBAL LibTL_BuildTagList
	sPUSHM.l a2-a3/d2-d3			; "push registers;
	move.l a2,d0					; if (!a2)
	beq.b bex						;   goto exit;
	moveq.l #TYPE_MASK,d3			; d3 = TYPE_MASK;
	bra.b bg1
bgo	move.l d2,(a2)+
bg1	move.l (a0)+,d0					; if ((d0=a0->tmi_Tag)<0)
	WMI.b							; {
		move.l d0,(a2)+				;   a2->ti_Tag=d0
		move.l (a0)+,d0				;   d0 = (a0->tmi_Offset<<16)|(a0->tmi_MissBits<<8)|(a0->tmi_Type);
		move.l (a0)+,d1				;   d1 = a0->tmi_Default
		moveq.l #0,d2				;   d2 = 0; // see macros
		and.w d3,d0					;   d0 &= TYPE_MASK;
		move.l (bj1,pc,d0.w),a3		;   a3 = *((VOID **)(bj1+pc+d0.w));
		swap.w d0					;   d0 = d0<<16 // offset in structure
		jmp (a3)					;   (*a3); // call type-specific function
	WEND							; }
	WEQ.b							; if (!d0) { // TAG_DONE
1$		clr.l (a2)+					;  a2->ti_Tag=0;
		clr.l (a2)+					;  a2->ti_Data=0;
		sPOPM.l a2-a3/d2-d3			;  "pop registers";
		move.l a2,d0				;  d0 = a2;
		rts							;  return d0/cc;
	WEND							; }
	subq.l #1,d0					; d0 = d0-1;
	WEQ.b							; if (!d0) { // TAG_INIT
		move.l (a0)+,(a2)+			;   a2->ti_Tag = (ULONG *)(&a0->ti_Offset);
		move.l (a0)+,(a2)+			;   a2->ti_Data = a0->ti_Default;
		bra.b bg1					;   goto loop;
	WEND							; }
	ERROR TL_BadMapTag				; "bad TagMapId encountered"
	bra.b 1$						; goto exit;
bex	sPOPM.l a2-a3/d2-d3				;  "pop registers";
	rts								;  return d0/cc;


;------ get tagitem from structure -----------------------------------------
 CNOP 0,4
bj1
 DC.L bb1,bw1,bl1	; NORMAL	NODEFAULT	copy value into ti_Data
 DC.L bb2,bw2,bl2	; BOOL		NODEFAULT	copy boolean into ti_Data
 DC.L bb3,bw3,bl3	; bool		NODEFAULT	copy ~boolean into ti_Data
 DC.L bb1,bw1,bl1	; NORMAL	DEFAULT		copy value into ti_Data
 DC.L bb2,bw2,bl2	; BOOL		DEFAULT		copy boolean into ti_Data
 DC.L bb3,bw3,bl3	; bool		DEFAULT		copy ~booelan into ti_Data
 DC.L bb2,bw2,bl2	; BOOL		default		or value into struct
 DC.L bb3,bw3,bl3	; bool		default		and ~value into struct




;*
;*NAME
;*	TL_SizeTagList - search tagitem in taglist
;*
;*SYNOPSIS
;*	ULONG size = TL_SizeTagList(TAGITEM *list)
;*	D0/CC                       A0
;*
;*FUNCTION
;*	Return size of taglist. Only user tags are included in the size, thus
;*	extra "space" for the TAG_DONE must be added...
;*
;*NOTE
;*  For  A6  to  contain  the  library  base  is  not a necessary when this
;*  function is called.
;*

 lGLOBAL LibTL_SizeTagList
	moveq.l #0,d0				; d0 = 0;
	move.l a0,d1				; d1 = a0;
	bne.b 1$					; if (d1) goto 2$;
	rts							; return d0/cc;
2$		addq.l #8,d0			; d0 = d0 + 8;
3$		addq.l #8,a0			; a0 = a0 + 8;
1$		move.l (a0),d1			;   d0 = a0->ti_Tag;
	BMI.b 2$					; if (d1<0) goto loop; // TAG_USER
	WEQ.b						; if (!d1) {
		rts						;   return d0/cc;
	WEND						; }
	subq.l #1,d1				; d1 = d1-1;
	BEQ.b 3$					; if (!d1) goto loop; // TAG_IGNORE
	subq.l #1,d1				; d1 = d1-1;
	WEQ.b						; if (!d1) { // TAG_MORE
		move.l (4,a0),a0		;   a0 = a0->ti_Data; // next taglist
		bra.b 1$				;   goto loop;
	WEND						; }
	subq.l #1,d1				; d1 = d1-1;
	WEQ.b						; if (!d1) { // TAG_SKIP
		move.l (4,a0),d1		;   d1 = a0->ti_Data;
		lsl.l #3,d1				;   d1 = d1*8;
		lea (8,a0,d1.l),a0		;   a0 = 8+a0+d1;
		bra.b 1$				;   goto loop;
	WEND						; }
	ERROR TL_BadTag				; "bad TagId encountered"
	tst.l d0					;
	rts							; return d0/cc;





;*
;*NAME
;*	TL_JoinTagLists - search tagitem in taglist
;*
;*SYNOPSIS
;*	TAGITEM *dest = TL_JoinTagLists(TAGITEM *a, TAGITEM *b, TAGITEM *dest)
;*	D0/CC                           A0          A1          A2
;*
;*FUNCTION
;*	Join two taglists into one, removing all system tags except TAG_DONE.
;*
;*INPUT
;*	a       First  taglist to read tagitems from (may be NULL). Tagitems in
;*          this taglist are copied before tagitems in taglist b.
;*
;*	b		Second taglist to read tagitems from (may be NULL).
;*
;*	dest	Taglist to store tagitems in (may be NULL). This can be the
;*			the same taglist as taglist a.
;*
;*RESULT
;*	dest	Equal to input "dest".
;*
;*EXAMPLE
;*	/* clone one taglist */
;*	if (clone=TL_JoinTagLists(source,NULL,AllocVec(TL_SizeTagList(source)+8)))
;*	{
;*	    FreeVec(clone);
;*	}
;*	
;*	/* join two taglists */
;*	if (joined=TL_JoinTagLists(a,b,AllocVec(TL_SizeTagList(a)+TL_SizeTagList(b)+8)))
;*	{
;*	    FreeVec(joined);
;*	}
;*	
;*	/* merge three taglists */
;*	if (joined=TL_JoinTagLists(a,b,AllocVec(TL_SizeTagList(a)+TL_SizeTagList(b)+TL_SizeTagList(c)+8)))
;*	{
;*	    TL_JoinTagLists(joined,c,joined);
;*		FreeVec(joined);
;*	}
;*
;*	/* now you know how to join four taglists */
;*
;*TIME
;*	O(n+m) where "n" is the number of tagitems in taglist a and "m" is the
;*	number of tagitems in taglist b.
;*
;*NOTE
;*  For  A6  to  contain  the  library  base  is  not a necessary when this
;*  function is called.
;*
;*BUGS
;*	Please report any.
;*

 lGLOBAL LibTL_JoinTagLists
	sPUSH.l a2						; "save register"
	WNE.b							; if (a2) {
		move.l a0,d0				;  d0 = a0;
		WNE.b						;  if (d0) {
1$			move.l (a0)+,d0			;   d0 = a0->ti_Tag; a0 += 4;
			WMI.b					;   if (d0<0) {
				move.l d0,(a2)+		;    a2->ti_Tag = d0;
				move.l (a0)+,(a2)+	;    a2->ti_Data = a0->ti_Data;
				bra.b 1$			;    goto 1$;
			WEND					;   }
			WHI.b					;   if (d0>0) {
				move.l (a0)+,d1		;    d1 = a0->ti_Data;
				subq.l #1,d0		;    d0 -= 1;
				BEQ.b 1$			;    if (!d0) goto 1$; // TAG_IGNORE
				subq.l #1,d0		;    d0 -= 1;
				WEQ.b				;    if (!d0) { // TAG_MORE
					move.l d1,a0	;     a0 = d1;
					bra.b 1$		;     goto 1$;
				WEND				;    }
				subq.l #1,d0		;    d0 -= 1;
				WEQ.b				;    if (!d0) { // TAG_MORE
					lsl.l #3,d1		;     d1 *= 8;
					add.l d1,a0		;     a0 += d1;
					bra.b 1$		;     goto 1$;
				WEND				;    }
				ERROR TL_BadTag		;    "bad TagId encountered"
			WEND					;   }
		WEND						;  }
		move.l a1,d0                ;  d0 = a1;
		WNE.b                       ;  if (d0) {
2$			move.l (a1)+,d0         ;   d0 = a1->ti_Tag; a1 += 4;
			WMI.b                   ;   if (d0<0) {
				move.l d0,(a2)+     ;    a2->ti_Tag = d0;
				move.l (a1)+,(a2)+  ;    a2->ti_Data = a1->ti_Data;
				bra.b 2$            ;    goto 2$;
			WEND                    ;   }
			WHI.b                   ;   if (d0>0) {
				move.l (a1)+,d1     ;    d1 = a1->ti_Data;
				subq.l #1,d0        ;    d0 -= 1;
				BEQ.b 2$            ;    if (!d0) goto 2$; // TAG_IGNORE
				subq.l #1,d0        ;    d0 -= 1;
				WEQ.b               ;    if (!d0) { // TAG_MORE
					move.l d1,a1    ;     a1 = d1;
					bra.b 2$        ;     goto 2$;
				WEND                ;    }
				subq.l #1,d0        ;    d0 -= 1;
				WEQ.b               ;    if (!d0) { // TAG_MORE
					lsl.l #3,d1     ;     d1 *= 8;
					add.l d1,a1     ;     a1 += d1;
					bra.b 2$        ;     goto 2$;
				WEND                ;    }
				ERROR TL_BadTag     ;    "bad TagId encountered"
				moveq.l #0,d0       ;    d0 = 0;
			WEND                    ;   }
		WEND						;  }
		move.l d0,(a2)+				;  a2->ti_Tag = 0; a2+=4;
		move.l d0,(a2)				;  a2->ti_Data = 0;
	WEND							; }
	sPOP.l a2						; "restore registers"
	move.l a2,d0					; d0 = a2;
	rts								; return d0;
