/*
 *  bufcnt.h from ObjectProDSP 0.2
 *  Copyright (C) 1994 1995, Mountain Math Software. All rights reserved.
 *  
 *  This file is part of ObjectProDSP, a tool for Digital Signal
 *  Processing design, development and implementation. It is free
 *  software provided you use and distribute it under the terms of
 *  version 2 of the GNU General Public License as published
 *  by the Free Software Foundation. You may NOT distribute it or
 *  works derived from it or code that it generates under ANY
 *  OTHER terms.  In particular NONE of the ObjectProDSP system is
 *  licensed for use under the GNU General Public LIBRARY License.
 *  Mountain Math Software plans to offer a commercial version of
 *  ObjectProDSP for a fee. That version will allow redistribution
 *  of generated code under standard commercial terms.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of version 2 of the GNU General
 *  Public License along with this program. See file COPYING. If not
 *  or if you wish information on commercial versions and licensing
 *  write Mountain Math Software, P. O. Box 2124, Saratoga, CA 95070,
 *  USA, or send us e-mail at: support@mtnmath.com.
 *  
 *  You may also obtain the GNU General Public License by writing the
 *  Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
 *  USA.  However if you received a copy of this program without the
 *  file COPYING or without a copyright notice attached to all text
 *  files, libraries and executables please inform Mountain Math Software.
 *  
 *  ObjectProDSP is a trademark of Mountain Math Software.
 */
/*  bufcnt.h   */
/*  Copyright 1990 Mountain Math Software  */
/*  All Rights Reserved                    */
#include "ObjProDSP/portable.h"
#include "ObjProGen/errcode.h"
#include "ObjProArith/hrdarth.h"

class TargetNode ;
class ostream ;
class Buffer ;

/*
 *	The structures defined in this header file address three methods
 *	for controlling kernel loop execution:
 *
 *		1. A single counter that gives the number of loop
 *		iterations. There must be this amount of space
 *		or data available in each buffer. Note this implies
 *		that the buffer sizes must be multiples of the data `chunk'
 *		size and there must not be overlap in any input channel.
 *		The chunk size is the product of the element size
 *		and the block size. 
 *
 *		2. Separate counters for each input and output buffer.
 *		Loop execution ends when the available contiguous space
 *		or input data is exhausted on ANY channel. Buffers are
 *		checked to see if more data is available at top of buffer.
 *
 *		3. The same as method 2 with the addition of a fixed
 *		upper limit on the number of executions. This limit
 *		is translated to a limit on the number of words to
 *		process for each channel. (Note there may be left over
 *		words between succesive executions that must be accounted
 *		for.)
 *
 *	The macros in this file serve two purposes:
 *
 *		1. They expand into the code for execution in
 *		interactive DSP++.
 *		
 *		2. They are key words for generating more efficient and
 *		less dynamic code for kernel execution on a target
 *		processor.
 */


struct KernelInterface {
	int32*& Count ;
	MachWord**& Point ;
	KernelInterface(int*& count, MachWord**& point):
		Count(count), Point(point){}
	void Allocate(int Size);
};

class NodeBufferInterface ;

class BufferInterface {
/*
 * friend InputBufferInterface ;
 * friend OutputBufferInterface ;
 * friend InLimBufferInterface ;
 * friend OutLimBufferInterface ;
 */
friend NodeBufferInterface ;
	int32& Count ;
	int32 Save ; // used for chunk size or to save initial count value
	const MachWord*& Point ;
	class Buffer& TheBuffer ;
public:
	BufferInterface(KernelInterface& kernel_interface, Buffer& buffer,
		int Chan);
	const MachWord * GetPoint() const {return Point;}
	int GetCount() const {return Count;}
	int32 SetupLoop(TargetNode& TheNode, int Chan);
	void CheckCount();
	virtual int32 GetWordLimit(TargetNode& node, int Chan);
	virtual int32 SetLimits(TargetNode& node, int Chan); 
	virtual int32 LoopInitialization();
	virtual int EndTest();
	// virtual ErrCode LoopCleanUp(ErrCode ErrIn);
};

class InputBufferInterface : public BufferInterface {
	int Channel ;
public:
	InputBufferInterface(NodeBufferInterface& Intfc,
		KernelInterface& kernel_intfc, int InChan);
	int32 GetWordLimit(TargetNode& node, int Chan);
	virtual int32 LoopInitialization();
	int EndTest();
	// ErrCode LoopCleanUp(ErrCode ErrIn);
};

class OutputBufferInterface : public BufferInterface {
public:
	OutputBufferInterface(NodeBufferInterface& Intfc,
		KernelInterface& kernel_intfc, int OutChan);
	int32 GetWordLimit(TargetNode& node, int Chan);
	virtual int32 LoopInitialization();
	int EndTest();
	// virtual ErrCode LoopCleanUp(ErrCode ErrIn);
};

class InLimBufferInterface : public InputBufferInterface {
	int32 WordLimit ;
	int32 WordLeftover ;
public:
	InLimBufferInterface(NodeBufferInterface& node_interface,
		KernelInterface& kernel_interface, int chan);
	int32 LoopInitialization();
	int32 SetLimits(TargetNode& node, int chan); 
	// ErrCode LoopCleanUp(ErrCode ErrIn);
};
	
class OutLimBufferInterface : public OutputBufferInterface {
	int32 WordLimit ;
	int32 WordLeftover ;
public:
	OutLimBufferInterface(NodeBufferInterface& node_interface,
		KernelInterface& kernel_interface, int chan);
	int32 LoopInitialization();
	int32 SetLimits(TargetNode& node, int chan); 
	// ErrCode LoopCleanUp(ErrCode ErrIn);
};

class NodeBufferInterface {
	TargetNode& TheNode;
	ErrCode ErrorState ;
	InputBufferInterface ** TheInputChannels ;
	OutputBufferInterface ** TheOutputChannels ;
public:
	NodeBufferInterface(TargetNode& the_node, KernelInterface& kernel_in,
		KernelInterface& kernel_out);
	~NodeBufferInterface() ;
	TargetNode& GetTheNode() const { return TheNode; }
	int32 LoopInitialization();
	int32 Reset() ;
	int32 EndTest();
	ErrCode LoopCleanUp(ErrCode err_in);
	ErrCode GetErrorState() const {return ErrorState; }
	void SetErrorState(ErrCode NewState);
};


#define BEGIN_LOOP_BASE(InDeclare,InAccess,OutDeclare,OutAccess) \
	static NodeBufferInterface * LoopControl = 0 ; \
	InDeclare ; \
	OutDeclare ; \
	if (!LoopControl) LoopControl = \
		new NodeBufferInterface(*this, \
		KernelInterface(InAccess), \
		KernelInterface(OutAccess)); \
	if (!LoopControl->Reset()) goto LoopExit ; \
	for (;;) {

#define EXIT_LOOP(Code) {LoopControl->SetErrorState(Code); goto LoopExit;}

#define IN_DECLARE_FIXED(in) \
	static int32 InCount[in] ; \
	static MachWord* InPoint[in] ;

#define OUT_DECLARE_FIXED(out) \
	static int32 OutCount[out] ; \
	static MachWord* OutPoint[out] ;

#define IN_DECLARE \
	static int32* InCount = 0 ; \
	static MachWord** InPoint = 0 ;

#define OUT_DECLARE \
	static int32* OutCount = 0 ; \
	static MachWord** OutPoint = 0 ;

#define IN_ACCESS InCount, InPoint

#define OUT_ACCESS OutCount, OutPoint

#define NULL_DEFINE

#define BEGIN_OUTPUT_LOOP_FIXED(size) BEGIN_LOOP_BASE(\
	NULL_DEFINE, NULL_DEFINE, OUT_DECLARE_FIXED(size), OUT_ACCESS_FIXED()

#define BEGIN_OUTPUT_LOOP() BEGIN_LOOP_BASE(\
	NULL_DEFINE, NULL_DEFINE, OUT_DECLARE, OUT_ACCESS)

#define BEGIN_INPUT_LOOP() BEGIN_LOOP_BASE( \
	IN_DECLARE, IN_ACCESS, NULL_DEFINE, NULL_DEFINE)

#define BEGIN_INPUT_LOOP_FIXED(size) BEGIN_LOOP_BASE( \
	IN_DECLARE(size), IN_ACCESS, NULL_DEFINE, NULL_DEFINE)

#define BEGIN_LOOP() BEGIN_LOOP_BASE(LoopType, \
	IN_DECLARE, IN_ACCESS, OUT_DECLARE, OUT_ACCESS)

#define BEGIN_LOOP_FIXED(in_size,out_size) BEGIN_LOOP_BASE(LoopType, \
	IN_DECLARE(in_size), IN_ACCESS, OUT_DECLARE(out_size), OUT_ACCESS)

#define NEXT_IN(input,channel) input = *(InPoint[channel]++) 

#define NEXT_IN_TEST(input,channel) { \
	if (!InCount[channel]--) if (!LoopControl->EndTest()) goto LoopExit ; \
	NEXT_IN(input,channel) ; \
	}

#define NEXT_OUT(output,channel) { \
	*(OutPoint[channel]++) = output ; \
	}

#define NEXT_OUT_TEST(output,channel) { \
	NEXT_OUT(output,channel) ; \
	if (!--OutCount[channel]) if (!LoopControl->EndTest()) goto LoopExit ; \
	} 

#define END_LOOP(err) \
	} \
LoopExit: \
	err = LoopControl->LoopCleanUp(err);


