/*
**	Amiga support module for HYDRA protocol sample implementation.
**
**	Written by	Olaf Barthel
**			Brabeckstrasse 35
**			D-30559 Hannover
**
**			eMail: olsen@sourcery.han.de
**
**	Freely distributable.
*/

	/* System includes. */

#define ReadPort (ior.IOSer.io_Message.mn_ReplyPort)
#define WritePort (iow.IOSer.io_Message.mn_ReplyPort)

#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/gadtools.h>
#include <proto/dos.h>
#include <proto/utility.h>

#include <intuition/intuitionbase.h>

#include <libraries/gadtools.h>

#include <graphics/gfxbase.h>

#include <utility/date.h>

#include <devices/conunit.h>
#include <devices/serial.h>
#include <devices/timer.h>

#include <hardware/cia.h>

#include <dos/dosextens.h>
#include <dos/filehandler.h>
#include <dos/dosasl.h>

#include <exec/memory.h>

	/* Correct a nasty bug in the prototypes. */

#define CheckIO foo21234

#include <clib/intuition_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/graphics_protos.h>
#include <clib/utility_protos.h>
#include <clib/timer_protos.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/macros.h>

	/* Get the CheckIO prototype right. */

#undef CheckIO

struct IORequest *CheckIO(struct IORequest *);

#include "hydracom.h"

	/* Serial buffer size. */

#define BUFFER_SIZE 8192

#define cprint(s) /**/

	/* A handy macro. */

#define ClrSignal(s)	SetSignal(0,s)

	/* Signal masks. */

#define SIG_SERREAD	(1UL << ReadPort -> mp_SigBit)
#define SIG_SERWRITE	(1UL << WritePort -> mp_SigBit)
#define SIG_TIMER	(1UL << TimePort -> mp_SigBit)


	/* A serial buffer structure. */

struct SerialBuffer
{
	struct IOExtSer	*SerialRequest;
	UBYTE		*SerialBuffer,
			*SerialIndex,
			*SerialTop;
	LONG		 SerialSize,
			 SerialFilled;
	BOOL		 IsClone,
			 IsBusy;
};

	/* Library bases. */

struct Library				*TimerBase;

	/* Timer data. */

struct MsgPort		*TimePort;
struct timerequest	*TimeRequest;

	/* Serial data. */

struct SerialBuffer	*ThisBuffer,
			*NextBuffer,
			*ReadBuffer;


	/* CloneSerialBuffer():
	 *
	 *	Clone a SerialBuffer structure.
	 */

extern struct IOExtSer ior, iow;

STATIC struct SerialBuffer * __regargs
CloneSerialBuffer(struct SerialBuffer *Source,struct MsgPort *MsgPort)
{
	struct SerialBuffer *Buffer;

	if(Buffer = (struct SerialBuffer *)AllocVec(sizeof(struct SerialBuffer) + Source -> SerialSize,MEMF_ANY | MEMF_PUBLIC))
	{
		Buffer -> SerialBuffer	= Buffer -> SerialIndex = (UBYTE *)(Buffer + 1);
		Buffer -> SerialFilled	= 0;
		Buffer -> SerialTop	= Buffer -> SerialBuffer + Source -> SerialSize;
		Buffer -> SerialSize	= Source -> SerialSize;
		Buffer -> IsClone	= TRUE;
		Buffer -> IsBusy	= FALSE;

		if(Buffer -> SerialRequest = (struct IOExtSer *)AllocVec(sizeof(struct IOExtSer),MEMF_ANY | MEMF_PUBLIC))
		{
			CopyMem(Source -> SerialRequest,Buffer -> SerialRequest,sizeof(struct IOExtSer));

			Buffer -> SerialRequest -> IOSer . io_Message . mn_ReplyPort = MsgPort;

			return(Buffer);
		}
		else
			cprint("Could not create serial request\n");

		FreeVec(Buffer);
	}
	else
		cprint("Could not create serial buffer\n");

	return(NULL);
}

	/* DeleteSerialBuffer():
	 *
	 *	Delete a SerialBuffer structure.
	 */

STATIC VOID __regargs
DeleteSerialBuffer(struct SerialBuffer *Buffer)
{
	if(Buffer)
	{
		if(Buffer -> IsBusy)
		{
			if(!CheckIO(Buffer -> SerialRequest))
				AbortIO(Buffer -> SerialRequest);

			WaitIO(Buffer -> SerialRequest);
		}

		if(Buffer -> IsClone)
			FreeVec(Buffer -> SerialRequest);
		else
		{
			/*CloseDevice(Buffer -> SerialRequest);

			DeleteIORequest(Buffer -> SerialRequest);*/
		}

		FreeVec(Buffer);
	}
}

	/* CreateSerialBuffer():
	 *
	 *	Create a serial buffer structure.
	 */

STATIC struct SerialBuffer * __regargs
CreateSerialBuffer(LONG Size)
{
	struct SerialBuffer *Buffer;

	//message( 0, " ioflags %lx", ior->io_SerFlags );

	if(Buffer = (struct SerialBuffer *)AllocVec(sizeof(struct SerialBuffer) + Size,MEMF_ANY | MEMF_PUBLIC))
	{
		Buffer -> SerialBuffer	= Buffer -> SerialIndex = (UBYTE *)(Buffer + 1);
		Buffer -> SerialFilled	= 0;
		Buffer -> SerialTop	= Buffer -> SerialBuffer + Size;
		Buffer -> SerialSize	= Size;
		Buffer -> IsClone	= FALSE;
		Buffer -> IsBusy	= FALSE;

		Buffer -> SerialRequest = &ior;
		return( Buffer );
	}
	else
		cprint("Could not create serial buffer\n");

	return(NULL);
}

	/* OpenAll():
	 *
	 *	Allocate all the resources required.
	 */

STATIC BOOL
OpenAll( void )
{
	if(!(TimePort = CreateMsgPort()))
	{
		cprint("Could not create timer port\n");

		return(FALSE);
	}

	if(!(TimeRequest = (struct timerequest *)CreateIORequest(TimePort,sizeof(struct timerequest))))
	{
		cprint("Could not create timer request\n");

		return(FALSE);
	}

	if(OpenDevice(TIMERNAME,UNIT_VBLANK,TimeRequest,NULL))
	{
		cprint("Could not open timer\n");

		return(FALSE);
	}

	TimerBase = (struct Library *)TimeRequest -> tr_node . io_Device;

	if(!(ReadBuffer = CreateSerialBuffer(BUFFER_SIZE)))
		return(FALSE);

	if(!(ThisBuffer = CloneSerialBuffer(ReadBuffer,WritePort)))
		return(FALSE);

	if(!(NextBuffer = CloneSerialBuffer(ReadBuffer,WritePort)))
		return(FALSE);

	return(TRUE);
}

	/* CloseAll():
	 *
	 *	Close all the resources.
	 */

STATIC VOID
CloseAll(VOID)
{
	DeleteSerialBuffer(NextBuffer);
	DeleteSerialBuffer(ThisBuffer);
	DeleteSerialBuffer(ReadBuffer);

	if(TimeRequest)
	{
		if(TimeRequest -> tr_node . io_Device)
			CloseDevice(TimeRequest);

		DeleteIORequest(TimeRequest);
	}

	if(TimePort)
		DeleteMsgPort(TimePort);

}

	/* ConPutc():
	 *
	 *	Output a single character.
	 */

/*VOID __stdargs
ConPutc(struct IOStdReq *Request,UBYTE Char)
{
	Request -> io_Command	= CMD_WRITE;
	Request -> io_Data	= &Char;
	Request -> io_Length	= 1;

	DoIO(Request);
}*/

VOID
com_putblock(byte *s,word len)
{
	struct SerialBuffer *Swap = ThisBuffer;

	//message( 0, " Entering computblock: len %ld", len );

	if(ThisBuffer -> IsBusy)
		WaitIO(ThisBuffer -> SerialRequest);
	else
	{
		if(ThisBuffer -> SerialIndex > ThisBuffer -> SerialBuffer)
		{
			ThisBuffer -> SerialRequest -> IOSer . io_Command	= CMD_WRITE;
			ThisBuffer -> SerialRequest -> IOSer . io_Data		= ThisBuffer -> SerialBuffer;
			ThisBuffer -> SerialRequest -> IOSer . io_Length	= ThisBuffer -> SerialIndex - ThisBuffer -> SerialBuffer;

			//message( 0, " cpb: before flush (1doio)" );

			DoIO(ThisBuffer -> SerialRequest);
		}
	}

	//message( 0, " computblock: before bufferswap" );

	ThisBuffer -> IsBusy		= FALSE;
	ThisBuffer -> SerialIndex	= ThisBuffer -> SerialBuffer;

	ThisBuffer = NextBuffer;
	NextBuffer = Swap;

	if(ThisBuffer -> SerialIndex > ThisBuffer -> SerialBuffer)
	{
		ThisBuffer -> SerialRequest -> IOSer . io_Command	= CMD_WRITE;
		ThisBuffer -> SerialRequest -> IOSer . io_Data		= ThisBuffer -> SerialBuffer;
		ThisBuffer -> SerialRequest -> IOSer . io_Length	= ThisBuffer -> SerialIndex - ThisBuffer -> SerialBuffer;

		//message( 0, " cpb: before flush (2doio)" );

		DoIO(ThisBuffer -> SerialRequest);
	}

	ThisBuffer -> IsBusy		= FALSE;
	ThisBuffer -> SerialIndex	= ThisBuffer -> SerialBuffer;

	while(len > ( ( 2 * ThisBuffer -> SerialSize ) - 1 ) )
	{
		ThisBuffer -> SerialRequest -> IOSer . io_Command	= CMD_WRITE;
		ThisBuffer -> SerialRequest -> IOSer . io_Data		= s;
		ThisBuffer -> SerialRequest -> IOSer . io_Length	= ThisBuffer -> SerialSize;

		s	+= ThisBuffer -> SerialSize;
		len	-= ThisBuffer -> SerialSize;

		//message( 0, " cpb: writeloop len = %ld", len );

		DoIO(ThisBuffer -> SerialRequest);
	}

	CopyMem(s,ThisBuffer -> SerialBuffer,MIN(len,ThisBuffer -> SerialSize));

	ThisBuffer -> IsBusy					= TRUE;
	ThisBuffer -> SerialIndex				= ThisBuffer -> SerialBuffer + MIN(len,ThisBuffer -> SerialSize);
	ThisBuffer -> SerialRequest -> IOSer . io_Command	= CMD_WRITE;
	ThisBuffer -> SerialRequest -> IOSer . io_Data		= ThisBuffer -> SerialBuffer;
	ThisBuffer -> SerialRequest -> IOSer . io_Length	= MIN(len,ThisBuffer -> SerialSize);

	//message( 0, " cpb: qb1 len = %ld sx = %ld", len, ThisBuffer -> SerialRequest -> IOSer . io_Length );

	len	-= ThisBuffer -> SerialRequest -> IOSer . io_Length;
	s	+= ThisBuffer -> SerialRequest -> IOSer . io_Length;

	ClrSignal(SIG_SERWRITE);
	SendIO(ThisBuffer -> SerialRequest);

	if(len > 0)
	{
		CopyMem(s,NextBuffer -> SerialBuffer,len);

		NextBuffer -> SerialIndex = NextBuffer -> SerialBuffer + len;
	}

	//message( 0, " exit computblock", len );
	
}

	/* breakfunc():
	 *
	 *	Cleanup routine for SAS/C.
	 */

static int
breakfunc(void)
{
	CloseAll();

	return(1);
}

	/* sys_init(VOID):
	 *
	 *	Initialize this driver implementation.
	 */

VOID
sys_init(VOID)
{
	if(!OpenAll())
	{
		CloseAll();

		endprog(2);
	}

//	onbreak(breakfunc);
}

	/* sys_reset(VOID):
	 *
	 *	Perform cleanup for this driver implementation.
	 */

VOID
sys_reset(VOID)
{
	CloseAll();
}

	/* sys_idle(VOID):
	 *
	 *	This routine gets called when the system is idle.
	 *	That's a nice one. We are supposed to call the
	 *	system scheduler, etc.
	 */

extern struct Window *twindow;

VOID
sys_idle(VOID)
{
	ULONG Signals;

	if( ( ReadBuffer -> SerialFilled <= 0 ) && ( !ReadBuffer -> IsBusy ) )
	{
		ReadBuffer -> IsBusy					= TRUE;
		ReadBuffer -> SerialIndex				= ReadBuffer -> SerialBuffer;
		ReadBuffer -> SerialRequest -> IOSer . io_Command	= CMD_READ;
		ReadBuffer -> SerialRequest -> IOSer . io_Data		= ReadBuffer -> SerialBuffer;
		ReadBuffer -> SerialRequest -> IOSer . io_Length	= 1;

		ClrSignal(SIG_SERREAD);
		SendIO(ReadBuffer -> SerialRequest);
	}

	TimeRequest -> tr_node . io_Command	= TR_ADDREQUEST;
	TimeRequest -> tr_time . tv_secs	= 1;
	TimeRequest -> tr_time . tv_micro	= 0;

	ClrSignal(SIG_TIMER);
	SendIO(TimeRequest);

	//message( 0, " Before Wait() in sys_idle t %lx, r %lx, w %lx", SIG_TIMER, SIG_SERREAD, SIG_SERWRITE );
	Signals = Wait(SIG_SERREAD | SIG_SERWRITE | SIG_TIMER | (1L<<twindow->UserPort->mp_SigBit ));
	//message( 0, " After Wait() in sys_idle %lx", Signals );

	if(!(Signals & SIG_TIMER))
	{
		if(!CheckIO(TimeRequest))
			AbortIO(TimeRequest);
	}

	WaitIO(TimeRequest);

	if(Signals & SIG_SERREAD)
	{
		WaitIO(ReadBuffer -> SerialRequest);

		ReadBuffer -> IsBusy		= FALSE;
		ReadBuffer -> SerialFilled	= ReadBuffer -> SerialRequest -> IOSer . io_Actual;
		ReadBuffer -> SerialIndex	= ReadBuffer -> SerialBuffer;

		ReadBuffer -> SerialRequest -> IOSer . io_Command = SDCMD_QUERY;
		DoIO(ReadBuffer -> SerialRequest);

		if(ReadBuffer -> SerialRequest -> IOSer . io_Actual)
		{
			LONG Size = ReadBuffer -> SerialSize - ReadBuffer -> SerialFilled;

			if(Size > 0)
			{
				if(Size > ReadBuffer -> SerialRequest -> IOSer . io_Actual)
					Size = ReadBuffer -> SerialRequest -> IOSer . io_Actual;

				ReadBuffer -> SerialRequest -> IOSer . io_Command	= CMD_READ;
				ReadBuffer -> SerialRequest -> IOSer . io_Data		= ReadBuffer -> SerialBuffer + ReadBuffer -> SerialFilled;
				ReadBuffer -> SerialRequest -> IOSer . io_Length	= Size;

				DoIO(ReadBuffer -> SerialRequest);

				ReadBuffer -> SerialFilled += ReadBuffer -> SerialRequest -> IOSer . io_Actual;
			}
		}
	}

	if(Signals & SIG_SERWRITE)
	{
		struct SerialBuffer *Swap = ThisBuffer;

		WaitIO(ThisBuffer -> SerialRequest);

		ThisBuffer -> IsBusy		= FALSE;
		ThisBuffer -> SerialIndex	= ThisBuffer -> SerialBuffer;

		ThisBuffer = NextBuffer;
		NextBuffer = Swap;
	}

	//message( 0, " sys_idle: rbsf %ld", ReadBuffer->SerialFilled );

}

	/* com_outfull(VOID):
	 *
	 *	Return number of bytes still to be transferred.
	 */

int
com_outfull(VOID)
{
	return(ThisBuffer -> SerialIndex - ThisBuffer -> SerialBuffer + NextBuffer -> SerialIndex - NextBuffer -> SerialBuffer);
}

	/* carrier(VOID):
	 *
	 *	Return current carrier status.
	 */

int
carrier(VOID)
{
	if(nocarrier)
		return(1);
	else
	{
		if(!ThisBuffer -> IsBusy)
		{
			ThisBuffer -> SerialRequest -> IOSer . io_Command = SDCMD_QUERY;

			DoIO(ThisBuffer -> SerialRequest);

			if(ThisBuffer -> SerialRequest -> io_Status & CIAF_COMCD)
				return(0);
			else
				return(1);
		}
		else
		{
			NextBuffer -> SerialRequest -> IOSer . io_Command = SDCMD_QUERY;

			DoIO(NextBuffer -> SerialRequest);

			if(NextBuffer -> SerialRequest -> io_Status & CIAF_COMCD)
				return(0);
			else
				return(1);
		}
	}
}

	/* com_flush(VOID):
	 *
	 *	Make sure all pending data gets written.
	 */

VOID
com_flush(VOID)
{
	if(ThisBuffer -> IsBusy)
	{
		WaitIO(ThisBuffer -> SerialRequest);

		ThisBuffer -> IsBusy		= FALSE;
		ThisBuffer -> SerialIndex	= ThisBuffer -> SerialBuffer;
	}

	if(NextBuffer -> SerialIndex > NextBuffer -> SerialBuffer)
	{
		NextBuffer -> SerialRequest -> IOSer . io_Command	= CMD_WRITE;
		NextBuffer -> SerialRequest -> IOSer . io_Data		= NextBuffer -> SerialBuffer;
		NextBuffer -> SerialRequest -> IOSer . io_Length	= NextBuffer -> SerialIndex - NextBuffer -> SerialBuffer;

		DoIO(NextBuffer -> SerialRequest);

		NextBuffer -> SerialIndex = NextBuffer -> SerialBuffer;
	}
}

	/* com_putbyte(byte c):
	 *
	 *	Transmit a single byte, queueing it if necessary.
	 */

VOID
com_putbyte(byte c)
{
	//message( 0, " XX com_putbyte; isbusy %ld, thisbuffer %lx", ThisBuffer->IsBusy, ThisBuffer );

	if(ThisBuffer -> IsBusy)
	{
		if(NextBuffer -> SerialIndex + 1 >= NextBuffer -> SerialTop)
		{
			struct SerialBuffer *Swap = ThisBuffer;

			WaitIO(ThisBuffer -> SerialRequest);

			ThisBuffer -> IsBusy		= FALSE;
			ThisBuffer -> SerialIndex	= ThisBuffer -> SerialBuffer;

			ThisBuffer = NextBuffer;
			NextBuffer = Swap;

			ThisBuffer -> IsBusy					= TRUE;
			ThisBuffer -> SerialRequest -> IOSer . io_Command	= CMD_WRITE;
			ThisBuffer -> SerialRequest -> IOSer . io_Data		= ThisBuffer -> SerialBuffer;
			ThisBuffer -> SerialRequest -> IOSer . io_Length	= ThisBuffer -> SerialIndex - ThisBuffer -> SerialBuffer;

			ClrSignal(SIG_SERWRITE);
			SendIO(ThisBuffer -> SerialRequest);
		}

		*NextBuffer -> SerialIndex++ = c;
	}
	else
	{
		if(ThisBuffer -> SerialIndex + 1 < ThisBuffer -> SerialTop)
		{
			*ThisBuffer -> SerialIndex++ = c;

			ThisBuffer -> IsBusy					= TRUE;
			ThisBuffer -> SerialRequest -> IOSer . io_Command	= CMD_WRITE;
			ThisBuffer -> SerialRequest -> IOSer . io_Data		= ThisBuffer -> SerialBuffer;

// BUG: Länge Falsch berechnet
//			ThisBuffer -> SerialRequest -> IOSer . io_Length	= 1;

			ThisBuffer -> SerialRequest -> IOSer . io_Length	= ThisBuffer -> SerialIndex - ThisBuffer -> SerialBuffer;

			ClrSignal(SIG_SERWRITE);
			SendIO(ThisBuffer -> SerialRequest);
		}
		else
		{
			ThisBuffer -> IsBusy					= TRUE;
			ThisBuffer -> SerialRequest -> IOSer . io_Command	= CMD_WRITE;
			ThisBuffer -> SerialRequest -> IOSer . io_Data		= ThisBuffer -> SerialBuffer;
			ThisBuffer -> SerialRequest -> IOSer . io_Length	= ThisBuffer -> SerialIndex - ThisBuffer -> SerialBuffer;

			ClrSignal(SIG_SERWRITE);
			SendIO(ThisBuffer -> SerialRequest);

			*NextBuffer -> SerialIndex++ = c;
		}
	}
}

	/* com_purge(VOID):
	 *
	 *	Clear the read/write buffers.
	 */

VOID
com_purge(VOID)
{
	if(ThisBuffer -> IsBusy)
	{
		if(!CheckIO(ThisBuffer -> SerialRequest))
			AbortIO(ThisBuffer -> SerialRequest);

		WaitIO(ThisBuffer -> SerialRequest);
	}

	ThisBuffer -> IsBusy		= FALSE;
	ThisBuffer -> SerialIndex	= ThisBuffer -> SerialBuffer;

	NextBuffer -> IsBusy		= FALSE;
	NextBuffer -> SerialIndex	= NextBuffer -> SerialBuffer;

	if(ReadBuffer -> IsBusy)
	{
		if(!CheckIO(ReadBuffer -> SerialRequest))
			AbortIO(ReadBuffer -> SerialRequest);

		WaitIO(ReadBuffer -> SerialRequest);
	}

	ReadBuffer -> IsBusy		= FALSE;
	ReadBuffer -> SerialIndex	= ReadBuffer -> SerialBuffer;
	ReadBuffer -> SerialFilled	= 0;

	ThisBuffer -> SerialRequest -> IOSer . io_Command = CMD_CLEAR;
	DoIO(ThisBuffer -> SerialRequest);
}

	/* com_dump(VOID):
	 *
	 *	Wait for asynchronous write request to terminate.
	 */

VOID
com_dump(VOID)
{
	com_flush();
}

	/* com_getbyte(VOID):
	 *
	 *	Read a single byte from the serial line. If not available,
	 *	return EOF.
	 */

int
com_getbyte(VOID)
{
	int Result;

	if(ReadBuffer -> SerialFilled <= 0)
	{
		if(ReadBuffer -> IsBusy)
		{
			if(!CheckIO(ReadBuffer -> SerialRequest))
			{
				//message( 0, " CGB: returning EOF" );
				return(EOF);
			}
			else
				WaitIO(ReadBuffer -> SerialRequest);

			ReadBuffer -> IsBusy		= FALSE;
			ReadBuffer -> SerialFilled	= ReadBuffer -> SerialRequest -> IOSer . io_Actual;
			ReadBuffer -> SerialIndex	= ReadBuffer -> SerialBuffer;

			ReadBuffer -> SerialRequest -> IOSer . io_Command = SDCMD_QUERY;
			DoIO(ReadBuffer -> SerialRequest);

			if(ReadBuffer -> SerialRequest -> IOSer . io_Actual)
			{
				LONG Size = ReadBuffer -> SerialSize - ReadBuffer -> SerialFilled;

				if(Size > 0)
				{
					if(Size > ReadBuffer -> SerialRequest -> IOSer . io_Actual)
						Size = ReadBuffer -> SerialRequest -> IOSer . io_Actual;

					ReadBuffer -> SerialRequest -> IOSer . io_Command	= CMD_READ;
					ReadBuffer -> SerialRequest -> IOSer . io_Data		= ReadBuffer -> SerialBuffer + ReadBuffer -> SerialFilled;
					ReadBuffer -> SerialRequest -> IOSer . io_Length	= Size;

					DoIO(ReadBuffer -> SerialRequest);

					ReadBuffer -> SerialFilled += ReadBuffer -> SerialRequest -> IOSer . io_Actual;
				}
			}
		}
		else
		{
			ReadBuffer -> SerialRequest -> IOSer . io_Command = SDCMD_QUERY;
			DoIO(ReadBuffer -> SerialRequest);

			if(!ReadBuffer -> SerialRequest -> IOSer . io_Actual)
			{
				//message( 0, " CGB: isbusy = FALSE, query = 0, EOF" );
				return(EOF);
			}
			else
			{
				ReadBuffer -> SerialRequest -> IOSer . io_Command	= CMD_READ;
				ReadBuffer -> SerialRequest -> IOSer . io_Data		= ReadBuffer -> SerialBuffer;
				ReadBuffer -> SerialRequest -> IOSer . io_Length	= MIN(ReadBuffer -> SerialRequest -> IOSer . io_Actual,ReadBuffer -> SerialSize);

				DoIO(ReadBuffer -> SerialRequest);

				if(!ReadBuffer -> SerialRequest -> IOSer . io_Actual)
				{
					//message( 0, " CGB: EOF after valid query!!" );
					return(EOF);
				}
				else
				{
					ReadBuffer -> SerialFilled	= ReadBuffer -> SerialRequest -> IOSer . io_Actual;
					ReadBuffer -> SerialIndex	= ReadBuffer -> SerialBuffer;
				}
			}
		}
	}

/*	if( !ReadBuffer->SerialFilled )
		message( 0, " CGB: ERROR unexpected serialfilled = " );

	if( ReadBuffer->IsBusy )
		message( 0, " CGB: ERROR readbuffer->isbusy " );*/

	Result = *ReadBuffer -> SerialIndex++;
	ReadBuffer -> SerialFilled--;

	if(ReadBuffer -> SerialFilled <= 0)
	{
		//message( 0, " CGB: queueing new rr" );

		ReadBuffer -> IsBusy					= TRUE;
		ReadBuffer -> SerialIndex				= ReadBuffer -> SerialBuffer;

		ReadBuffer -> SerialRequest -> IOSer . io_Command	= CMD_READ;
		ReadBuffer -> SerialRequest -> IOSer . io_Data		= ReadBuffer -> SerialBuffer;
		ReadBuffer -> SerialRequest -> IOSer . io_Length	= 1;

		ClrSignal(SIG_SERREAD);
		SendIO(ReadBuffer -> SerialRequest);
	}

	return(Result);
}

	/* setstamp(STRPTR Name,LONG Time):
	 *
	 *	Set time/date of a file.
	 */

VOID
setstamp(char *Name,long Time)
{
        struct tm *t;
	struct ClockData ClockData;
	ULONG Seconds;
	struct DateStamp Date;

        t = localtime(&Time);

	ClockData . sec		= t -> tm_sec;
	ClockData . min		= t -> tm_min;
	ClockData . hour	= t -> tm_hour;
	ClockData . mday	= t -> tm_mday;
	ClockData . month	= t -> tm_mon;
	ClockData . year	= t -> tm_year + 1900;

	Seconds = Date2Amiga(&ClockData);

	Date . ds_Days		= Seconds / (24 * 60 * 60);
	Date . ds_Minute	= (Seconds % (24 * 60 * 60)) / 60;
	Date . ds_Tick		= (Seconds % 60) * TICKS_PER_SECOND;

	SetFileDate(Name,&Date);
}

	/* freespace(STRPTR DrivePath):
	 *
	 *	Get free disk space for specified drive.
	 */

long
freespace(char *DrivePath)
{
	struct DevProc	*DevProc = GetDeviceProc(DrivePath,NULL);
	struct DosList	*DosList;
	BOOL		 GoodDevice = FALSE;
	LONG		 Size = (LONG)((ULONG)~0 >> 2);

	if(DosList = LockDosList(LDF_DEVICES | LDF_READ))
	{
		while(DosList = NextDosEntry(DosList,LDF_DEVICES))
		{
			if(DosList -> dol_Task == DevProc -> dvp_Port)
			{
				struct FileSysStartupMsg *FSSM = (struct FileSysStartupMsg *)BADDR(DosList -> dol_misc . dol_handler . dol_Startup);

				if(TypeOfMem(FSSM))
				{
					struct DosEnvec *DosEnvec = (struct DosEnvec *)BADDR(FSSM -> fssm_Environ);
					STRPTR Name = (STRPTR)BADDR(FSSM -> fssm_Device);

					if(TypeOfMem(DosEnvec) && TypeOfMem(Name))
					{
						if(Name[0] > 0 && !Name[(WORD)Name[0] + 1])
						{
							struct IOStdReq __aligned IORequest;

							if(!OpenDevice(Name + 1,FSSM -> fssm_Unit,&IORequest,FSSM -> fssm_Unit))
							{
								CloseDevice(&IORequest);

								if(DosEnvec -> de_TableSize > 0 && DosEnvec -> de_LowCyl <= DosEnvec -> de_HighCyl)
									GoodDevice = TRUE;
							}
						}
					}
				}
			}
		}

		UnLockDosList(LDF_DEVICES | LDF_READ);
	}

	FreeDeviceProc(DevProc);

	if(GoodDevice)
	{
		struct InfoData *InfoData;

		if(InfoData = (struct InfoData *)AllocVec(sizeof(struct InfoData),MEMF_ANY | MEMF_PUBLIC))
		{
			UBYTE NewName[256],*Index;
			BPTR FileLock;

			memcpy(NewName,DrivePath,255);

			NewName[255] = 0;

			Index = PathPart(NewName);

			*Index = 0;

			FileLock = Lock(NewName,ACCESS_READ);

			if(FileLock)
			{
				if(Info(FileLock,InfoData))
					Size = InfoData -> id_BytesPerBlock * (InfoData -> id_NumBlocks - InfoData -> id_NumBlocksUsed);

				UnLock(FileLock);
			}

			FreeVec(InfoData);
		}
	}

	return(Size);
}
