/*
**	patch.library
**
**	Copyright © 1993-1997 by Stefan Fuchs
**		Freely distributable.
*/

#ifndef _PATCH_INCLUDES_H
#include "patch_includes.h"
#endif

#define INSTR_JMP    0x4ef9
#define INSTR_JSR    0x4eb9


/****i* patch.library/ReturnD1 ***************************************
*
*   NAME
*        ReturnD1 -- Set register d1 to a specific value. 
*
*   SYNOPSIS
*        ReturnD1( value )
*
*        ReturnD1( ULONG );
*
*   FUNCTION
*        Set register d1 to a specific value.
*
*   INPUTS
*        value = value to be placed in register d1.
*
*   NOTES
*        Hopefully this function is not removed by the compiler.
*
******************************************************************************
*
*/
void ASMFUNC ReturnD1( REGD1 ULONG result GNUC_REGD1)
{
}


/****i* patch.library/GetNumberOfNodes ***************************************
*
*   NAME
*        GetNumberOfNodes -- Return the number of nodes in a List.
*
*   SYNOPSIS
*        Number = GetNumberOfNodes( list )
*
*        ULONG GetNumberOfNodes( struct List *);
*
*   FUNCTION
*        Return the number of nodes in an exec list.
*
*   INPUTS
*        list = pointer to a List structure
*
*   RESULT
*        Number = Number of nodes in the given exec list.
*
*   BUGS
*
******************************************************************************
*
*/

LONG GetNumberOfNodes(struct List *header)
{
LONG counter = - 2;

	do
	{
		counter++;
		header = (struct List *)header->lh_Head;
	}
	while(header);
	return(counter);
}


/****i* patch.library/ADeleteMyListNode ***************************************
*
*   NAME
*        ADeleteMyListNode -- Remove and deallocate node.
*
*   SYNOPSIS
*        ADeleteMyListNode( node )
*
*        ADeleteMyListNode( struct Node *);
*
*   FUNCTION
*        Remove and deallocate exec node. This node must have been created by one
*        of the ACreateMyListNode* () functions.
*
*   INPUTS
*        node = pointer to a node structure or NULL for no action
*
*   RESULT
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*        ACreateMyListNode(), AFreemem()
*
******************************************************************************
*
*/


void ADeleteMyListNode( struct Node *node)
{
	if(node)
	{
		Remove(node);
		AFreemem(node);
	}
}


/****i* patch.library/ACreateMyListNodeNamed ***************************************
*
*   NAME
*        ACreateMyListNodeNamed -- Create and add a named node.
*
*   SYNOPSIS
*        node = ACreateMyListNodeNamed( list, length, name )
*
*        struct Node *ACreateMyListNodeNamed( struct List *, ULONG, STRPTR );
*
*   FUNCTION
*        Create a node structure of the given size and name and add it to
*        the given list.
*        This node can be removed by a call to ADeleteMyListNode().
*
*   INPUTS
*        list = pointer to a list structure
*        length = size of node in bytes
*        name = name of the node to add or NULL, for an unnamed node
*
*   RESULT
*        node = pointer to the newly created node structure or NULL on failure
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*        ACreateMyListNode(), ADeleteMyListNode(), BAllocmem()
*
******************************************************************************
*
*/

struct Node *ACreateMyListNodeNamed(struct List *header, ULONG length, STRPTR string)
{
struct Node *pointer;
	if(string)
	{
		pointer = (struct Node *)BAllocmem( length + strlen( string) + 1, MEMF_CLEAR | MEMF_PUBLIC);
		if(pointer)
		{
			pointer->ln_Name = strcpy((char *) ((ULONG)pointer+length), string);
			AddTail( header, pointer);
		}
	}
	else
	{
		pointer = ACreateMyListNode( header, length);
	}
	return(pointer);
}


/****i* patch.library/ACreateMyListNode ***************************************
*
*   NAME
*        ACreateMyListNode -- Create and add a node.
*
*   SYNOPSIS
*        node = ACreateMyListNode( list, length )
*
*        struct Node *ACreateMyListNode( struct List *, ULONG);
*
*   FUNCTION
*        Create a node structure of the given size and add it to
*        the given list.
*        This node can be removed by a call to ADeleteMyListNode().
*
*   INPUTS
*        list = pointer to a list structure or NULL for no action
*        length = size of node in bytes
*
*   RESULT
*        node = pointer to the newly created node structure or NULL on failure
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*        ACreateMyListNode(), ADeleteMyListNode(), BAllocmem()
*
******************************************************************************
*
*/

struct Node *ACreateMyListNode(struct List *header, ULONG length)
{
struct Node *pointer = 0;
	if(header)
	{
		pointer = (struct Node *)BAllocmem( length, MEMF_CLEAR | MEMF_PUBLIC);
		if(pointer) AddTail( header, pointer);
	}
	return(pointer);
}


/****i* patch.library/ACreateMyListNodeNamedPri ***************************************
*
*   NAME
*        ACreateMyListNodeNamedPri -- Create and add a named node with priority.
*
*   SYNOPSIS
*        node = ACreateMyListNodeNamedPri( list, length, name, priority )
*
*        struct Node *ACreateMyListNodeNamedPri( struct List *, ULONG, STRPTR, BYTE );
*
*   FUNCTION
*        Create a node structure of the given size and name and add it to
*        the given list with the given priority.
*        This node can be removed by a call to ADeleteMyListNode().
*
*   INPUTS
*        list = pointer to a list structure
*        length = size of node in bytes
*        name = name of the node to add or NULL, for an unnamed node
*        priority = position at which the node is to be added to the list
*
*   RESULT
*        node = pointer to the newly created node structure or NULL on failure
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*        ACreateMyListNode(), ADeleteMyListNode(), BAllocmem(), EnqueueLIFO()
*
******************************************************************************
*
*/

struct Node *ACreateMyListNodeNamedPri(struct List *header, ULONG length, STRPTR string, BYTE pri)
{
struct Node *pointer;
	if(string)
	{
		pointer = (struct Node *)BAllocmem( length + strlen( string) + 1, MEMF_CLEAR | MEMF_PUBLIC);
		if(pointer)
		{
			pointer->ln_Name = strcpy((char *) ((ULONG)pointer+length), string);
			pointer->ln_Pri = pri;
			EnqueueLIFO( header, pointer);
		}
	}
	else
	{
		pointer = ACreateMyListNodePri( header, length, pri);
	}
	return(pointer);
}


/****i* patch.library/ACreateMyListNodePri ***************************************
*
*   NAME
*        ACreateMyListNodePri -- Create and add a node with priority.
*
*   SYNOPSIS
*        node = ACreateMyListNodePri( list, length, priority )
*
*        struct Node *ACreateMyListNodePri( struct List *, ULONG, BYTE );
*
*   FUNCTION
*        Create a node structure of the given size and add it to
*        the given list with the given priority.
*        This node can be removed by a call to ADeleteMyListNode().
*
*   INPUTS
*        list = pointer to a list structure or NULL for no action
*        length = size of node in bytes
*        priority = position at which the node is to be added to the list
*
*   RESULT
*        node = pointer to the newly created node structure or NULL on failure
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*        ACreateMyListNode(), ADeleteMyListNode(), BAllocmem(), EnqueueLIFO()
*
******************************************************************************
*
*/

struct Node *ACreateMyListNodePri(struct List *header, ULONG length, BYTE pri)
{
struct Node *pointer = 0;
	if(header)
	{
		pointer = (struct Node *)BAllocmem( length, MEMF_CLEAR | MEMF_PUBLIC);
		if(pointer) 
		{
			pointer->ln_Pri = pri;
			EnqueueLIFO( header, pointer);
		}
	}
	return(pointer);
}


/****i* patch.library/AAllocmem ***************************************
*
*   NAME
*        AAllocmem -- Allocate memory from the memory pool remembering
*                     its size.
*
*   SYNOPSIS
*        memory = AAllocmem( size, attributes, pool )
*
*        APTR AAllocmem( ULONG, ULONG, APTR );
*
*   FUNCTION
*        Allocate memory from the global or (if it exists) from a local
*        memory pool and remember its size.
*        Deallocate this memory with AFreemem().
*
*   INPUTS
*        size = number of bytes to allocate
*        attributes = memory attributes (only required, if pool is NULL)
*        pool = pointer to a memorypool as returned by CreatePool() or NULL, if
*               memory should be allocated from the global memory.
*
*   RESULT
*        memory = pointer to newly allocated memory block or NULL on failure
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*        Allocmem(), AllocVec(), AFreemem(), CreatePool()
*
******************************************************************************
*
*/


APTR AAllocmem( ULONG size, ULONG attr, APTR pool)
{
ULONG *memory;

	size += 8;
	if( pool)
	{
		memory = LibAllocPooled( pool, size);
	}
	else
	{
		memory = AllocMem( size, attr);
	}

	if (memory)
	{
		*memory++ = size;
		*memory++ = (ULONG)pool;
	}
	return( memory);
}


/****i* patch.library/AFreemem ***************************************
*
*   NAME
*        AFreemem -- Free memory allocated from the memory pool.
*
*   SYNOPSIS
*        AFreemem( memory )
*
*        AFreemem( APTR );
*
*   FUNCTION
*        Free memory from the global or from a local
*        memory pool.
*        This memory must have been allocated with AAllocmem().
*
*   INPUTS
*        memory = pointer to memoryblock
*
*   RESULT
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*        AAllocmem() 
*
******************************************************************************
*
*/

void AFreemem( APTR memory)
{
ULONG *mem;
	if( memory)
	{
		mem = (ULONG *)memory - 2;
		if (mem[1])
			LibFreePooled((APTR) mem[1], mem, mem[0]);
		else
			FreeMem( mem, mem[0]);
	}
}


/****i* patch.library/MyDelay ***************************************
*
*   NAME
*        MyDelay -- Delay a task for a number of ticks.
*
*   SYNOPSIS
*        MyDelay( ticks )
*
*        MyDelay( ULONG );
*
*   FUNCTION
*        Put a task a number of ticks (1/50 seconds) to sleep.
*
*   INPUTS
*        ticks = number of ticks (1/50 seconds) to wait
*
*   RESULT
*
*   NOTES
*        This function has been written as it is unclear wether
*        dos.library/Delay() may be called from tasks or not.
*
*   BUGS
*        Number of ticks is restricted to about a minute
*
*   SEE ALSO
*        dos.library/Delay()
*
******************************************************************************
*
*/

void MyDelay( LONG time)
{
struct MsgPort *msgport;
struct IORequest *iorequest;

	if( msgport = CreateMsgPort())
	{
		if( iorequest = CreateExtIO( msgport, sizeof(struct timerequest)))
		{
			if( !OpenDevice("timer.device", UNIT_VBLANK, iorequest, 0L))
			{
				((struct timerequest *)iorequest)->tr_time.tv_secs = 0;
				((struct timerequest *)iorequest)->tr_time.tv_micro = time * 20000;
				((struct timerequest *)iorequest)->tr_node.io_Command = TR_ADDREQUEST;

				DoIO( iorequest);

				CloseDevice( iorequest);
			}
			DeleteExtIO( iorequest);
		}
		DeleteMsgPort(msgport);
	}
}

/****i* patch.library/FindNameNoCase ***************************************
*
*   NAME
*        FindNameNoCase -- Search an exec-list not case-sensitive.
*
*   SYNOPSIS
*        node = FindNameNoCase( list, string )
*
*        struct Node *FindNameNoCase( struct List *, STRPTR );
*
*   FUNCTION
*        Search an exec-list not case-sensitive.
*
*   INPUTS
*        list = pointer to List structure to search or NULL for no action
*        string = C-String or NULL for no action.
*
*   RESULT
*        node = pointer to node structure or NULL, if not found
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*        exec.library/FindName()
*
******************************************************************************
*
*/

struct Node *FindNameNoCase( struct List *header, STRPTR name)
{
struct Node *pointer = NULL;

	if( (header) && (name))
	{
		for( pointer = header->lh_Head;
		     pointer->ln_Succ;
		     pointer = pointer->ln_Succ)
		{
			if (stricmp( pointer->ln_Name, name) == 0L)
				break;
		}
	}
	return( pointer);
}


/****i* patch.library/FindType ***************************************
*
*   NAME
*        FindType -- Search an exec-list for a node of the given type.
*
*   SYNOPSIS
*        node = FindType( list, type )
*
*        struct Node *FindType( struct List *, UBYTE );
*
*   FUNCTION
*        Search an exec-list fo a node of the given type.
*
*   INPUTS
*        list = pointer to List structure to search
*        type = Type of node to search for.
*
*   RESULT
*        node = pointer to node structure or NULL, if not found
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
******************************************************************************
*
*/


struct Node *FindType( struct List *header,  UBYTE type)
{
struct Node *pointer;
	for (pointer = (struct Node *)header->lh_Head;
	     pointer->ln_Succ;
	     pointer = (struct Node *)pointer->ln_Succ)
	{
		if( pointer->ln_Type == type) return( pointer);
	}
	return( NULL);
}


/****i* patch.library/GetListNodeHeader ***************************************
*
*   NAME
*        GetListNodeHeader -- Get a List structure to a given node.
*
*   SYNOPSIS
*        list = GetListNodeHeader( node )
*
*        struct List *FindType( struct Node * );
*
*   FUNCTION
*        Get a pointer to the List structure of a given node.
*
*   INPUTS
*        node = pointer to a Node structure
*
*   RESULT
*        list = pointer to List structure
*
*   NOTES
*        The node MUST belong to a List or everything might happen.
*
*   BUGS
*
*   SEE ALSO
*
******************************************************************************
*
*/

struct List *GetListNodeHeader( struct Node *pointer)
{
	for(;(pointer->ln_Succ);pointer = pointer->ln_Succ);
	pointer = (struct Node *)(((ULONG *)pointer) - 1);
	return( (struct List *)pointer);
}


/****i* patch.library/EnqueueLIFO ***************************************
*
*   NAME
*        EnqueueLIFO -- Insert nodes in front of the first node
*                       with the same priority
*
*   SYNOPSIS
*        EnqueueLIFO( list, node )
*
*        EnqueueLIFO( struct List *, struct Node * );
*
*   FUNCTION
*        Insert a node in front of the first node with the same priority
*        (Last in, First out - style)
*
*   INPUTS
*        list = pointer to List structure or NULL for no action
*        node = pointer to Node structure or NULL for no action
*
*   RESULT
*
*   NOTES
*       exec.library/Enqueue() works in FIFO - style.
*
*   BUGS
*
*   SEE ALSO
*       exec.library/Enqueue()
*
******************************************************************************
*
*/


void EnqueueLIFO( struct List *header, struct Node *newnode)
{
struct Node *pointer;
	if( (header) && (newnode))
	{
		for (pointer = (struct Node *)header->lh_Head;
		     pointer->ln_Succ;
	  	     pointer = (struct Node *)pointer->ln_Succ)
		{
			if( pointer->ln_Succ->ln_Succ == NULL) break;
			if( newnode->ln_Pri >= pointer->ln_Succ->ln_Pri) break;
		}
		if( header == (struct List *)pointer)
		{
			AddHead( header, newnode);
		}
		else
		{
			Insert( header, newnode, pointer);
		}
	}
}

/****i* patch.library/SetJMPInstr ***************************************
*
*   NAME
*        SetJMPInstr -- Modify a jump instruction
*
*   SYNOPSIS
*        SetJMPInstr( memory, destination )
*
*        SetJMPInstr( UBYTE *, APTR );
*
*   FUNCTION
*        Modify a (68K) jump instruction at a given location in memory to
*        point to a new adress (destination).
*
*   INPUTS
*        memory = memory location, where to place the instruction or
*                 NULL for no action
*        destination = memory location, to which the jump instruction is set
*
*   RESULT
*
*   NOTES
*        On processors with cache this function must be enclosed by 
*        disable(), FlushCache(), Enable().
*        This function has been introduced to make patch.library
*        portable.
*
*   BUGS
*
*   SEE ALSO
*
******************************************************************************
*
*/


void SetJMPInstr( UBYTE *memptr, APTR destination)
{
	if( memptr)
	{
		*((UWORD *)memptr) = INSTR_JMP;
		memptr += 2;
		*((ULONG *)memptr) = (ULONG)destination;
	}
}
/****i* patch.library/SetJSRInstr ***************************************
*
*   NAME
*        SetJSRInstr -- Modify a jump-subroutine instruction
*
*   SYNOPSIS
*        SetJSRInstr( memory, destination )
*
*        SetJSRInstr( UBYTE *, APTR );
*
*   FUNCTION
*        Modify a (68K) jump-subroutine instruction at a given location
*        in memory to point to a new adress (destination).
*
*   INPUTS
*        memory = memory location, where to place the instruction or
*                 NULL for no action
*        destination = memory location, to which the jsr instruction is set
*
*   RESULT
*
*   NOTES
*        On processors with cache this function must be enclosed by 
*        disable(), FlushCache(), Enable().
*        This function has been introduced to make patch.library
*        portable.
*
*   BUGS
*
*   SEE ALSO
*
******************************************************************************
*
*/


void SetJSRInstr( UBYTE *memptr, APTR destination)
{
	if( memptr)
	{
		*((UWORD *)memptr) = INSTR_JSR;
		memptr += 2;
		*((ULONG *)memptr) = (ULONG)destination;
	}
}
