/*
**	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. */

#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 "Rendezvous.h"

#include "hydracom.h"

	/* Serial buffer size. */

#define BUFFER_SIZE 8192

	/* 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_CONREAD	(1UL << ConsoleReadPort -> mp_SigBit)
#define SIG_TIMER	(1UL << TimePort -> mp_SigBit)
#define SIG_WINDOW	(1UL << LocalWindow -> UserPort -> mp_SigBit)

	/* A serial buffer structure. */

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

STATIC struct RendezvousData		*RendezvousData;
STATIC struct RendezvousSemaphore	*RendezvousSemaphore;

	/* Library bases. */

struct IntuitionBase	*IntuitionBase;
struct GfxBase		*GfxBase;
struct Library		*GadToolsBase,
			*UtilityBase,
			*TimerBase;

	/* Timer data. */

struct MsgPort		*TimePort;
struct timerequest	*TimeRequest;

	/* Serial data. */

struct MsgPort		*ReadPort,
			*WritePort;

struct SerialBuffer	*ThisBuffer,
			*NextBuffer,
			*ReadBuffer;

	/* Console data. */

struct Window		*FileWindow,
			*RemoteWindow,
			*LocalWindow,
			*LogWindow;

struct MsgPort		*ConsoleWritePort,
			*ConsoleReadPort;
struct IOStdReq		*ConsoleReadRequest;
UBYTE			 ConsoleChar;
BOOL			 ConsoleReady = FALSE,
			 WindowReady = FALSE;

struct IOStdReq		*FileRequest,
			*RemoteRequest,
			*LocalRequest,
			*LogRequest;

	/* DOS Data. */

struct Process		*ThisProcess;
APTR			 OldPtr;

struct AnchorPath	*Anchor;
BOOL			 AnchorUsed = FALSE;

	/* Screen data. */

struct Screen		*PublicScreen,
			*Screen;

	/* Menu data. */

APTR			 VisualInfo;
struct Menu		*Menu;

struct NewMenu MenuTemplate[] =
{
	{ NM_TITLE, "Project",			 0 ,	0,	0,	(APTR)0},
	{  NM_ITEM, "Toggle chat",		"C",	0,	0,	(APTR)Alt_C},
	{  NM_ITEM, NM_BARLABEL,		 0 ,	0,	0,	(APTR)0},
	{  NM_ITEM, "Hang up",			"H",	0,	0,	(APTR)Alt_H},
	{  NM_ITEM, "Toggle duplex",		"E",	0,	0,	(APTR)Alt_E},
	{  NM_ITEM, "Toggle 7 bits/8 bits",	"B",	0,	0,	(APTR)Alt_B},
	{  NM_ITEM, NM_BARLABEL,		 0 ,	0,	0,	(APTR)0},
	{  NM_ITEM, "Start upload",		"U",	0,	0,	(APTR)PgUp},
	{  NM_ITEM, "Start download",		"D",	0,	0,	(APTR)PgDn},
	{  NM_ITEM, NM_BARLABEL,		 0 ,	0,	0,	(APTR)0},
	{  NM_ITEM, "Exit HydraCom",		"Q",	0,	0,	(APTR)Alt_X},
	{ NM_END,   0,				 0 ,	0,	0,	(APTR)0}
};

	/* OpenConsole():
	 *
	 *	Open a console window.
	 */

STATIC BOOL __inline
OpenConsole(struct Screen *Screen,LONG Top,LONG Height,STRPTR Title,BOOL Resize,struct Window **WindowPtr,struct IOStdReq **ConsolePtr)
{
	struct Window *Window;

	if(Window = OpenWindowTags(NULL,
		WA_Left,		0,
		WA_Top,			Top,
		WA_Width,		Screen -> Width,
		WA_Height,		Height,
		WA_Title,		Title,
		WA_SimpleRefresh,	TRUE,
		WA_DepthGadget,		TRUE,
		WA_DragBar,		TRUE,
		WA_SizeGadget,		Resize,
		WA_SizeBRight,		TRUE,
		WA_CustomScreen,	Screen,
		WA_RMBTrap,		TRUE,
		WA_NewLookMenus,	TRUE,
	TAG_DONE))
	{
		struct IOStdReq *ConsoleRequest;

		if(Window -> RPort -> Font -> tf_Flags & FPF_PROPORTIONAL)
			SetFont(Window -> RPort,GfxBase -> DefaultFont);

		if(ConsoleRequest = (struct IOStdReq *)CreateIORequest(ConsoleWritePort,sizeof(struct IOStdReq)))
		{
			ConsoleRequest -> io_Data = Window;

			if(!OpenDevice("console.device",CONU_CHARMAP,ConsoleRequest,CONFLAG_DEFAULT))
			{
				WindowLimits(Window,Window -> BorderLeft + 10 * Window -> RPort -> Font -> tf_XSize * 10 + Window -> BorderRight,Window -> BorderTop + 2 * Window -> RPort -> Font -> tf_YSize + Window -> BorderBottom,Screen -> Width,Screen -> Height);

					/* Turn off the cursor. */

				ConPrintf(ConsoleRequest,"\033[0 p");

				*WindowPtr	= Window;
				*ConsolePtr	= ConsoleRequest;

				return(TRUE);
			}

			DeleteIORequest(ConsoleRequest);
		}

		CloseWindow(Window);
	}

	return(FALSE);
}

	/* CloseConsole():
	 *
	 *	Close a console window.
	 */

STATIC VOID __inline
CloseConsole(struct Window **WindowPtr,struct IOStdReq **ConsolePtr)
{
	if(*ConsolePtr)
	{
		CloseDevice(*ConsolePtr);

		DeleteIORequest(*ConsolePtr);

		*ConsolePtr = NULL;
	}

	if(*WindowPtr)
	{
		CloseWindow(*WindowPtr);

		*WindowPtr = NULL;
	}
}

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

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(STRPTR Device,LONG Unit,LONG Size,struct MsgPort *MsgPort)
{
	struct SerialBuffer *Buffer;

	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;

		if(Buffer -> SerialRequest = (struct IOExtSer *)CreateIORequest(MsgPort,sizeof(struct IOExtSer)))
		{

		Buffer -> SerialRequest -> io_SerFlags = SERF_SHARED|SERF_XDISABLED|SERF_RAD_BOOGIE;

			if(!OpenDevice(Device,Unit,Buffer->SerialRequest,(ULONG)0L))
				{
				return(Buffer);
				} else {
				cprint("Could not open \"%s\", unit %d\n",Device,Unit);
				DeleteIORequest(Buffer -> SerialRequest);
				}

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

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

	return(NULL);
}

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

STATIC BOOL
OpenAll(STRPTR Device,LONG Unit)
{
	LONG Top,Lines,BorderSize,FontSize,ExtraLines,RemainingLines,TotalHeight;
	UWORD Pens = (UWORD)~0;

	if(!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",37)))
	{
		cprint("Could not open intuition.library v37\n");

		return(FALSE);
	}

	if(!(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",37)))
	{
		cprint("Could not open graphics.library v37\n");

		return(FALSE);
	}

	if(!(GadToolsBase = OpenLibrary("gadtools.library",37)))
	{
		cprint("Could not open gadtools.library v37\n");

		return(FALSE);
	}

	if(!(UtilityBase = OpenLibrary("utility.library",37)))
	{
		cprint("Could not open utility.library v37\n");

		return(FALSE);
	}

	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(!(Anchor = (struct AnchorPath *)AllocVec(sizeof(struct AnchorPath) + 512,MEMF_ANY | MEMF_CLEAR)))
	{
		cprint("Could not allocate pattern matching buffe\n");

		return(FALSE);
	}

	Anchor -> ap_Strlen = 512;

	if(!(ReadPort = CreateMsgPort()))
	{
		cprint("Could not create serial read port\n");

		return(FALSE);
	}

	if(!(WritePort = CreateMsgPort()))
	{
		cprint("Could not create serial write port\n");

		return(FALSE);
	}

	Forbid();

	if(RendezvousSemaphore = (struct RendezvousSemaphore *)FindSemaphore(Device))
	{
		ObtainSemaphore(RendezvousSemaphore);

		if(!(RendezvousData = (*RendezvousSemaphore -> rs_Login)(ReadPort,WritePort,NULL)))
		{
			Permit();

			ReleaseSemaphore(RendezvousSemaphore);

			RendezvousSemaphore = NULL;

			cprint("Could not link to `term'\n");

			return(FALSE);
		}
	}

	Permit();

	if(!(ConsoleReadPort = CreateMsgPort()))
	{
		cprint("Could not create console read port\n");

		return(FALSE);
	}

	if(!(ConsoleWritePort = CreateMsgPort()))
	{
		cprint("Could not create console write port\n");

		return(FALSE);
	}

	if(!(ConsoleReadRequest = (struct IOStdReq *)AllocVec(sizeof(struct IOStdReq),MEMF_ANY | MEMF_PUBLIC | MEMF_CLEAR)))
	{
		cprint("Could not create console read request\n");

		return(FALSE);
	}

	if(!RendezvousData)
	{
		if(!(ReadBuffer = CreateSerialBuffer(Device,Unit,BUFFER_SIZE,ReadPort)))
			return(FALSE);
	}
	else
	{
		if(ReadBuffer = (struct SerialBuffer *)AllocVec(sizeof(struct SerialBuffer) + BUFFER_SIZE,MEMF_ANY | MEMF_PUBLIC))
		{
			ReadBuffer -> SerialBuffer	= ReadBuffer -> SerialIndex = (UBYTE *)(ReadBuffer + 1);
			ReadBuffer -> SerialFilled	= 0;
			ReadBuffer -> SerialTop		= ReadBuffer -> SerialBuffer + BUFFER_SIZE;
			ReadBuffer -> SerialSize	= BUFFER_SIZE;
			ReadBuffer -> IsClone		= TRUE;
			ReadBuffer -> IsBusy		= FALSE;
			ReadBuffer -> SerialRequest	= &RendezvousData -> rd_ReadRequest;
		}
		else
		{
			cprint("Could not create serial ReadBuffer\n");

			return(FALSE);
		}
	}

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

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

	if(!(PublicScreen = LockPubScreen(NULL)))
	{
		cprint("Could not find default public screen\n");

		return(FALSE);
	}

	if(RendezvousData)
	{
		if(!(Screen = RendezvousData -> rd_Screen))
			Screen = PublicScreen;
	}
	else
	{
		if(!(Screen = OpenScreenTags(NULL,
			SA_DisplayID,	GetVPModeID(&PublicScreen -> ViewPort),
			SA_Overscan,	OSCAN_TEXT,
			SA_Depth,	2,
			SA_Title,	PRGNAME " " VERSION " " HC_OS " Amiga rev 2, ported by Olaf `Olsen' Barthel",
			SA_Behind,	TRUE,
			SA_SysFont,	1,
			SA_Pens,	&Pens,
		TAG_DONE)))
		{
			cprint("Could not open screen\n");

			return(FALSE);
		}
	}

	if(!(VisualInfo = GetVisualInfo(Screen,TAG_DONE)))
	{
		cprint("Could not obtain screen visual info\n");

		return(FALSE);
	}

	if(!(Menu = CreateMenus(MenuTemplate,TAG_DONE)))
	{
		cprint("Could not create menus\n");

		return(FALSE);
	}

	if(!LayoutMenus(Menu,VisualInfo,
		GTMN_TextAttr,		Screen -> Font,
		GTMN_NewLookMenus,	TRUE,
	TAG_DONE))
	{
		cprint("Could not layout menus\n");

		return(FALSE);
	}

	Top		= Screen -> BarHeight + 1;
	BorderSize	= Screen -> WBorTop + Screen -> Font -> ta_YSize + 1 + Screen -> WBorBottom;
	FontSize	= GfxBase -> DefaultFont -> tf_YSize;
	TotalHeight	= Screen -> Height - Top;
	Lines		= (TotalHeight - 3 - 4 * BorderSize) / FontSize;
	ExtraLines	= Lines > 23 ? (Lines - 23) / 3 : 0;
	RemainingLines	= Lines > 23 + ExtraLines * 3 ? Lines - (23 + ExtraLines * 3) : 0;

	if(!OpenConsole(Screen,Top,BorderSize + (6 + ExtraLines) * FontSize,"Log",TRUE,&LogWindow,&LogRequest))
	{
		cprint("Could not open console window #1\n");

		return(FALSE);
	}

	TotalHeight	-= LogWindow -> Height + 1;
	Top		+= LogWindow -> Height + 1;

	if(!OpenConsole(Screen,Top,BorderSize + 3 * FontSize,"File",FALSE,&FileWindow,&FileRequest))
	{
		cprint("Could not open console window #2\n");

		return(FALSE);
	}

	TotalHeight	-= FileWindow -> Height + 1;
	Top		+= FileWindow -> Height + 1;

	if(!OpenConsole(Screen,Top,BorderSize + (8 + RemainingLines) * FontSize,"Remote",TRUE,&RemoteWindow,&RemoteRequest))
	{
		cprint("Could not open console window #3\n");

		return(FALSE);
	}

	TotalHeight	-= RemoteWindow -> Height + 1;
	Top		+= RemoteWindow -> Height + 1;

	if(!OpenConsole(Screen,Top,TotalHeight,"Local (Press Amiga+X to start/end chat mode)",TRUE,&LocalWindow,&LocalRequest))
	{
		cprint("Could not open console window #4\n");

		return(FALSE);
	}

	SetMenuStrip(LocalWindow,Menu);

	if(!ModifyIDCMP(LocalWindow,IDCMP_MENUPICK))
	{
		cprint("Could not modify IDCMP flags\n");

		return(FALSE);
	}

	LocalWindow -> Flags &= ~WFLG_RMBTRAP;

	CopyMem(LocalRequest,ConsoleReadRequest,sizeof(struct IOStdReq));

	ConsoleReadRequest -> io_Message . mn_ReplyPort = ConsoleReadPort;

		/* Turn the cursors back on. */

	ConPrintf(LocalRequest,"\33[ p");
	ConPrintf(RemoteRequest,"\33[ p");

	ConsoleReadRequest -> io_Command	= CMD_READ;
	ConsoleReadRequest -> io_Data		= &ConsoleChar;
	ConsoleReadRequest -> io_Length		= 1;

	ClrSignal(SIG_CONREAD);
	SendIO(ConsoleReadRequest);

	ThisProcess = (struct Process *)FindTask(NULL);

	OldPtr = ThisProcess -> pr_WindowPtr;

	ThisProcess -> pr_WindowPtr = (APTR)LocalWindow;

	ScreenToFront(Screen);

	ActivateWindow(LocalWindow);

	UnlockPubScreen(NULL,PublicScreen);

	PublicScreen = NULL;

	return(TRUE);
}

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

STATIC VOID
CloseAll(VOID)
{
	if(LocalWindow)
		ClearMenuStrip(LocalWindow);

	if(Menu)
		FreeMenus(Menu);

	if(VisualInfo)
		FreeVisualInfo(VisualInfo);

	if(AnchorUsed)
		MatchEnd(Anchor);

	if(Anchor)
		FreeVec(Anchor);

	if(ThisProcess)
		ThisProcess -> pr_WindowPtr = OldPtr;

	if(ConsoleReadRequest)
	{
		if(ConsoleReadRequest -> io_Device)
		{
			if(!CheckIO(ConsoleReadRequest))
				AbortIO(ConsoleReadRequest);

			WaitIO(ConsoleReadRequest);
		}

		FreeVec(ConsoleReadRequest);
	}

	CloseConsole(&LocalWindow,&LocalRequest);
	CloseConsole(&RemoteWindow,&RemoteRequest);
	CloseConsole(&FileWindow,&FileRequest);
	CloseConsole(&LogWindow,&LogRequest);

	if(!RendezvousData && Screen)
		CloseScreen(Screen);

	if(PublicScreen)
		UnlockPubScreen(NULL,PublicScreen);

	DeleteSerialBuffer(NextBuffer);
	DeleteSerialBuffer(ThisBuffer);

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

			WaitIO(ReadBuffer -> SerialRequest);
		}

		FreeVec(ReadBuffer);
	}
	else
		DeleteSerialBuffer(ReadBuffer);

	if(ConsoleWritePort)
		DeleteMsgPort(ConsoleWritePort);

	if(ConsoleReadPort)
		DeleteMsgPort(ConsoleReadPort);

	if(WritePort)
		DeleteMsgPort(WritePort);

	if(ReadPort)
		DeleteMsgPort(ReadPort);

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

		DeleteIORequest(TimeRequest);
	}

	if(TimePort)
		DeleteMsgPort(TimePort);

	if(UtilityBase)
		CloseLibrary(UtilityBase);

	if(GadToolsBase)
		CloseLibrary(GadToolsBase);

	if(GfxBase)
		CloseLibrary(GfxBase);

	if(IntuitionBase)
		CloseLibrary(IntuitionBase);

	if(RendezvousData)
	{
		(*RendezvousSemaphore -> rs_Logoff)(RendezvousData);

		RendezvousData = NULL;
	}

	if(RendezvousSemaphore)
	{
		ReleaseSemaphore(RendezvousSemaphore);

		RendezvousSemaphore = NULL;
	}
}

	/* 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);
}

	/* ConPuts():
	 *
	 *	Output a string.
	 */

VOID
ConPuts(struct IOStdReq *Request,STRPTR String)
{
	Request -> io_Command	= CMD_WRITE;
	Request -> io_Data	= String;
	Request -> io_Length	= strlen(String);

	DoIO(Request);
}

	/* ConPrintf():
	 *
	 *	Formatted console output.
	 */

VOID __stdargs
ConPrintf(struct IOStdReq *Request,STRPTR Format,...)
{
	STATIC UBYTE Buffer[512];

	va_list VarArgs;

	va_start(VarArgs,Format);
	vsprintf(Buffer,Format,VarArgs);
	va_end(VarArgs);

	Request -> io_Command	= CMD_WRITE;
	Request -> io_Data	= Buffer;
	Request -> io_Length	= strlen(Buffer);

	DoIO(Request);
}

	/* ConMove():
	 *
	 *	Move the cursor to a new position.
	 */

VOID
ConMove(struct IOStdReq *Request,LONG x,LONG y)
{
	ConPrintf(Request,"\33[%ld;%ldH",y,x);
}

	/* ConClear():
	 *
	 *	Clear the console window.
	 */

VOID
ConClear(struct IOStdReq *Request)
{
	struct ConUnit *ConUnit = (struct ConUnit *)Request -> io_Device;
	LONG x,y;

	x = ConUnit -> cu_XCCP;
	y = ConUnit -> cu_YCCP;

	ConPrintf(Request,"\f\33[%ld;%ldH",y,x);
}

	/* ConGetKey():
	 *
	 *	Read a character from a console window.
	 */

int
ConGetKey()
{
	if(ConsoleReady)
	{
		int Result = ConsoleChar;

		ConsoleReady = FALSE;

		ConsoleReadRequest -> io_Command	= CMD_READ;
		ConsoleReadRequest -> io_Data		= &ConsoleChar;
		ConsoleReadRequest -> io_Length		= 1;

		ClrSignal(SIG_CONREAD);
		SendIO(ConsoleReadRequest);

		return(Result);
	}
	else
	{
		int Result = 0;

		if(WindowReady)
		{
			struct IntuiMessage *IntuiMessage;
			ULONG MsgClass;
			UWORD MsgCode;

			while(IntuiMessage = (struct IntuiMessage *)GetMsg(LocalWindow -> UserPort))
			{
				MsgClass	= IntuiMessage -> Class;
				MsgCode		= IntuiMessage -> Code;

				ReplyMsg(IntuiMessage);

				if(MsgClass == IDCMP_MENUPICK)
				{
					struct MenuItem *Item;

					while(MsgCode != MENUNULL)
					{
						if(Item = ItemAddress(Menu,MsgCode))
						{
							if(MENU_USERDATA(Item))
							{
								if(!Result)
									Result = (int)MENU_USERDATA(Item);
							}

							MsgCode = Item -> NextSelect;
						}
						else
							break;
					}
				}
			}

			WindowReady = FALSE;
		}

		return(Result);
	}
}

	/* ConScanKey():
	 *
	 *	Check for a keyboard event.
	 */

int
ConScanKey()
{
	if(ConsoleReady || WindowReady)
		return(1);
	else
	{
		int Result = 0;

		if(CheckSignal(SIG_WINDOW))
		{
			WindowReady = TRUE;

			Result = 1;
		}

		if(CheckIO(ConsoleReadRequest))
		{
			if(!WaitIO(ConsoleReadRequest))
			{
				ConsoleReady = TRUE;

				return(1);
			}
			else
			{
				ConsoleReadRequest -> io_Command	= CMD_READ;
				ConsoleReadRequest -> io_Data		= &ConsoleChar;
				ConsoleReadRequest -> io_Length		= 1;

				ClrSignal(SIG_CONREAD);
				SendIO(ConsoleReadRequest);
			}
		}

		return(Result);
	}

	return(0);
}

	/* dtr_out(byte flag):
	 *
	 *	If flag == 0 -> drop DTR signal, else set it.
	 */

VOID
dtr_out(byte flag)
{
	if(!flag && !RendezvousData)
	{
		if(ThisBuffer -> IsBusy)
		{
			WaitIO(ThisBuffer -> SerialRequest);

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

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

			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;
				}
			}
		}

		CloseDevice(ReadBuffer -> SerialRequest);

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

		DoIO(TimeRequest);

		if(OpenDevice(device,port,ReadBuffer -> SerialRequest,NULL))
		{
			CloseAll();

			exit(10);
		}
		else
		{
			ReadBuffer -> SerialRequest -> io_Baud		= ThisBuffer -> SerialRequest -> io_Baud;
			ReadBuffer -> SerialRequest -> io_ReadLen	= ThisBuffer -> SerialRequest -> io_ReadLen;
			ReadBuffer -> SerialRequest -> io_WriteLen	= ThisBuffer -> SerialRequest -> io_WriteLen;
			ReadBuffer -> SerialRequest -> io_SerFlags	= ThisBuffer -> SerialRequest -> io_SerFlags;

			ReadBuffer -> SerialRequest -> IOSer . io_Command = SDCMD_SETPARAMS;

			DoIO(ReadBuffer -> SerialRequest);

			CopyMem(ReadBuffer -> SerialRequest,ThisBuffer -> SerialRequest,sizeof(struct IOExtSer));

			ThisBuffer -> SerialRequest -> IOSer . io_Message . mn_ReplyPort = WritePort;

			CopyMem(ReadBuffer -> SerialRequest,NextBuffer -> SerialRequest,sizeof(struct IOExtSer));

			NextBuffer -> SerialRequest -> IOSer . io_Message . mn_ReplyPort = WritePort;
		}
	}
}

	/* com_flow(byte flags):
	 *
	 *	The bit mask `flags' determines the style(s) of
	 *	handshaking:
	 *
	 *	if (flags & 9) -> enable xON/xOFF software handshaking,
	 *	if (flags & 2) -> enable RTS/CTS hardware handshaking
	 */

VOID
com_flow(byte flags)
{
	if(ThisBuffer -> IsBusy)
	{
		WaitIO(ThisBuffer -> SerialRequest);

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

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

		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(flags & 2)
		ReadBuffer -> SerialRequest -> io_SerFlags |= SERF_7WIRE;
	else
		ReadBuffer -> SerialRequest -> io_SerFlags &= ~SERF_7WIRE;

	if(flags & 9)
		ReadBuffer -> SerialRequest -> io_SerFlags &= ~SERF_XDISABLED;
	else
		ReadBuffer -> SerialRequest -> io_SerFlags |= SERF_XDISABLED;
		ReadBuffer -> SerialRequest -> io_SerFlags = SERF_SHARED;

	DoIO(ReadBuffer -> SerialRequest);

	ThisBuffer -> SerialRequest -> io_SerFlags = ReadBuffer -> SerialRequest -> io_SerFlags;
	NextBuffer -> SerialRequest -> io_SerFlags = ReadBuffer -> SerialRequest -> io_SerFlags;
}

	/* com_setspeed(word speed):
	 *
	 *	Set the transfer speed (in bits/second).
	 */

VOID
com_setspeed(word speed)
{
	if(ThisBuffer -> IsBusy)
	{
		WaitIO(ThisBuffer -> SerialRequest);

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

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

		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;
			}
		}
	}

	ReadBuffer -> SerialRequest -> io_Baud = speed;

	if(parity)
	{
		ReadBuffer -> SerialRequest -> io_ReadLen = ReadBuffer -> SerialRequest -> io_WriteLen = 7;
		ReadBuffer -> SerialRequest -> io_SerFlags |= SERF_PARTY_ON;
	}
	else
	{
		ReadBuffer -> SerialRequest -> io_ReadLen = ReadBuffer -> SerialRequest -> io_WriteLen = 8;
		ReadBuffer -> SerialRequest -> io_SerFlags &= ~SERF_PARTY_ON;
	}

	ReadBuffer -> SerialRequest -> IOSer . io_Command = SDCMD_SETPARAMS;

	DoIO(ReadBuffer -> SerialRequest);

	ThisBuffer -> SerialRequest -> io_Baud		= ReadBuffer -> SerialRequest -> io_Baud;
	ThisBuffer -> SerialRequest -> io_ReadLen	= ReadBuffer -> SerialRequest -> io_ReadLen;
	ThisBuffer -> SerialRequest -> io_WriteLen	= ReadBuffer -> SerialRequest -> io_WriteLen;
	ThisBuffer -> SerialRequest -> io_SerFlags	= ReadBuffer -> SerialRequest -> io_SerFlags;

	NextBuffer -> SerialRequest -> io_Baud		= ReadBuffer -> SerialRequest -> io_Baud;
	NextBuffer -> SerialRequest -> io_ReadLen	= ReadBuffer -> SerialRequest -> io_ReadLen;
	NextBuffer -> SerialRequest -> io_WriteLen	= ReadBuffer -> SerialRequest -> io_WriteLen;
	NextBuffer -> SerialRequest -> io_SerFlags	= ReadBuffer -> SerialRequest -> io_SerFlags;
}

	/* com_putblock(byte *s,word len):
	 *
	 *	Send a data block asynchronously.
	 */

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

	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;

			DoIO(ThisBuffer -> SerialRequest);
		}
	}

	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;

		DoIO(ThisBuffer -> SerialRequest);
	}

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

	while(len > 2 * ThisBuffer -> SerialSize)
	{
		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;

		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);

	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;
	}
}

	/* 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(device,port))
	{
		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.
	 */

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);

	Signals = Wait(SIG_SERREAD | SIG_SERWRITE | SIG_CONREAD | SIG_WINDOW | SIG_TIMER);

	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;
	}

	if(Signals & SIG_CONREAD)
	{
		WaitIO(ConsoleReadRequest);

		ConsoleReady = TRUE;
	}

	if(Signals & SIG_WINDOW)
		WindowReady = TRUE;
}

	/* 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)
{
	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;
			ThisBuffer -> SerialRequest -> IOSer . io_Length	= 1;

			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))
				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)
				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)
					return(EOF);
				else
				{
					ReadBuffer -> SerialFilled	= ReadBuffer -> SerialRequest -> IOSer . io_Actual;
					ReadBuffer -> SerialIndex	= ReadBuffer -> SerialBuffer;
				}
			}
		}
	}

	Result = *ReadBuffer -> SerialIndex++;

	ReadBuffer -> SerialFilled--;

	if(ReadBuffer -> SerialFilled <= 0)
	{
		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);
}

	/* ffirst(char *FileSpec):
	 *
	 *	Return name of first file matching the given specs.
	 */

char *
ffirst(char *filespec)
{
	AnchorUsed = TRUE;

	if(MatchFirst(filespec,Anchor))
		return(NULL);
	else
		return((char *)Anchor -> ap_Buf);
}

	/* fnext(VOID):
	 *
	 *	Return name of next file matching the given specs.
	 */

char *
fnext(VOID)
{
	AnchorUsed = TRUE;

	if(MatchNext(Anchor))
		return(NULL);
	else
		return((char *)Anchor -> ap_Buf);
}
