DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T c

⟦b279ed03c⟧ TextFile

    Length: 25913 (0x6539)
    Types: TextFile
    Names: »ckitio.c«

Derivation

└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
    └─⟦3da672b63⟧ »EurOpenD3/misc/kermit.tar.Z« 
        └─⟦126dd3ca0⟧ 
            └─⟦this⟧ »ckitio.c« 

TextFile

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* |_o_o|\\ Copyright (c) 1986 The Software Distillery.  All Rights Reserved */
/* |. o.| || This program may not be distributed without the permission of   */
/* | .  | || the authors.                                                    */
/* | o  | ||    Dave Baker     Ed Burnette  Stan Chow    Jay Denebeim        */
/* |  . |//     Gordon Keener  Jack Rouse   John Toebes  Doug Walker         */
/* ======          BBS:(919)-471-6436      VOICE:(919)-469-4210              */ 
/*                                                                           */
/* Contributed to Columbia University for inclusion in C-Kermit.             */
/* Permission is granted to any individual or institution to use, copy, or   */
/* redistribute this software so long as it is not sold for profit, provided */
/* this copyright notice is retained.                                        */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

char *ckxv = "Amiga tty I/O, 4D(005), 31 Jul 87";
 
/*  C K I T I O  --  Serial and Console I/O support for the Amiga */

/*
 Author: Jack Rouse, The Software Distillery
 Based on the CKUTIO.C module for Unix
*/

#include <stdio.h>		/* standard I/O stuff */
#undef NULL
#include "exec/types.h"
#include "exec/exec.h"
#include "devices/serial.h"
#include "devices/timer.h"
#include "libraries/dos.h"
#include "libraries/dosextens.h"
#define fh_Interact fh_Port
#define fh_Process fh_Type
#include "intuition/intuition.h"
#include "intuition/intuitionbase.h"
#define BREAKSIGS (SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_D)
#ifdef LAT310
#include "fcntl.h"
#include "signal.h"
#include "ios1.h"		/* defines ufbs structure */
#else
#include "lattice/ios1.h"	/* defines ufbs structure */
#endif

char *ckxsys = " Commodore Amiga";	/* system name */
\f


/* external declarations */
extern int speed, mdmtyp, parity, flow;

/* external definitions */
char *dftty = SERIALNAME;		/* serial device name */
int dfloc = 1;				/* serial line is external */
int dfprty = 0;				/* default parity is none */
int ttprty = 0;				/* parity in use */
int dfflow = 1;				/* default flow control is on */
int backgrd = 0;			/* default to foreground */
int ckxech = 0;				/* echo in case redirected stdin */

struct Process *CurProc;		/* current process */
struct CommandLineInterface *CurCLI;	/* current CLI info */
extern struct IntuitionBase *IntuitionBase;	/* ptr to Intuition lib */
 
/* static definitions */
static struct MsgPort *serport;		/* message port for serial comm */
static struct MsgPort *conport;		/* console packet port */
static struct timerequest *TimerIOB;	/* timer request */
static struct IOExtSer *ReadIOB;	/* serial input request */
static struct IOExtSer *WriteIOB;	/* serial output request */
static struct DosPacket *conpkt;	/* console I/O packet */
static WORD serialopen;			/* true iff serial device open */
static WORD timeropen;			/* true iff timer device open */
static WORD pendwrite;			/* true iff WriteIOB in use */
static WORD pendread;			/* true iff ReadIOB in use */
static WORD pendconsole;		/* true when console read pending */
static int queuedser;			/* serial pushback char or -1 */
static UBYTE serbufc;			/* char buffer for read ahead I/O */
#define NTTOQ 64			/* connect output queue size */
static char ttoq[NTTOQ];		/* connect output queue */
static int nttoq;			/* number of chars in ttoq */
static int pttoq;			/* next char to output in ttoq */
static int queuedcon;			/* contti pushback char or -1 */
static LONG intsigs;			/* signals for aborting serial I/O */
static int (*inthdlr)();		/* function to signal break to */
static BPTR rawcon;			/* file handle for RAW: window */
static BPTR saverr;                     /* saved stderr file handle */
static APTR savewindow;			/* saved process WindowPtr */
static APTR pushwindow;			/* pushed process WindowPtr */
static struct DateStamp prevtime;	/* saved time value */
\f


/* Exec routines */
APTR AllocMem();
LONG AllocSignal();
struct IORequest *CheckIO();
VOID CloseDevice(), CloseLibrary();
LONG DoIO();
struct MsgPort *FindPort();
struct Task *FindTask();
VOID FreeMem(), FreeSignal();
struct Message *GetMsg();
LONG OpenDevice();
struct Library *OpenLibrary();
VOID PutMsg(), ReplyMsg();
VOID SendIO();
LONG SetSignal();
VOID Signal();
LONG Wait();
LONG WaitIO();
struct Message *WaitPort();

/* Exec support */
struct IORequest *CreateExtIO();
VOID DeleteExtIO();
struct MsgPort *CreatePort();
VOID DeletePort();
struct Task *CreateTask();
VOID DeleteTask();

/* AmigaDOS routines */
VOID Delay();
BPTR Open();
VOID Close();
BPTR Input(), Output();
LONG Read();
LONG WaitForChar();
struct DateStamp *DateStamp();

/* AmigaDOS support (from ckiutl.c) */
struct DosPacket *CreatePacket();
VOID DeletePacket();

#ifdef LAT310
/* translate Unix file handle (0, 1, or 2) to AmigaDOS file handle */
#define DOSFH(n) fileno(&_iob[n])
/* translate Unix file handle (0, 1, or 2) to Lattice file handle */
#define FILENO(n) fileno(&_iob[n])
#else
/* Lattice runtime externals */
extern struct UFB _ufbs[];
extern int Enable_Abort;
#define DOSFH(n) (_ufbs[n].ufbfh)
#define FILENO(n) (n)
#endif
\f


/*
 * make note of a serial error and quit
 */
static Fail(msg)
char *msg;
{
	syscleanup();
	fprintf(stderr, msg);
	fprintf(stderr, "\n");
	exit(2);
}
/*
 * default interrupt handler
 */
static int defhdlr()
{
	printf("*** BREAK ***\n");
	doexit(1);
}
\f


/*
 *  sysinit -- Amiga specific initialization
 */
sysinit()
{
	register struct IOExtSer *iob;

	/* set current process info */
	CurProc = (struct Process *)FindTask((char *)NULL);
	CurCLI = (struct CommandLineInterface *)BADDR(CurProc->pr_CLI);
	backgrd = (CurCLI == NULL || CurCLI->cli_Background);
	savewindow = CurProc->pr_WindowPtr;

	/* default interrupts to exit handler */
	intsigs = BREAKSIGS;
	inthdlr = defhdlr;
#ifdef LAT310
	signal(SIGINT, SIG_IGN);
#else
	Enable_Abort = 0;
#endif

	/* allocate console ports and IO blocks */
	if ((conport = CreatePort((char *)NULL, 0L)) == NULL)
		Fail("no console MsgPort");
	if ((conpkt = CreatePacket()) == NULL)
		Fail("no console packet");

	/* allocate serial ports and IO blocks */
	if ((serport = CreatePort((char *)NULL, 0L)) == NULL)
		Fail("no serial MsgPort");
	iob = (struct IOExtSer *)CreateExtIO(serport,(LONG)sizeof(*iob));
	if ((WriteIOB = iob) == NULL) Fail("no WriteIOB");
	iob = (struct IOExtSer *)CreateExtIO(serport,(LONG)sizeof(*iob));
	if ((ReadIOB = iob) == NULL) Fail("no ReadIOB");

	/* open the timer device */
	TimerIOB = (struct timerequest *)
		   CreateExtIO(serport,(LONG)sizeof(*TimerIOB));
	if (TimerIOB == NULL) Fail("no TimerIOB");
	if (OpenDevice(TIMERNAME, (LONG)UNIT_VBLANK, TimerIOB, 0L) != 0)
		Fail("no timer device");
	timeropen = TRUE;

	/* open the Intuition library */
	if (!IntuitionBase &&
	    (IntuitionBase = (struct IntuitionBase *)
			     OpenLibrary("intuition.library", 0L) ) == NULL )
		Fail("can't open Intuition");

	/* open the serial device to get configuration */
	iob->io_SerFlags = SERF_SHARED;
	if (OpenDevice(SERIALNAME, 0L, iob, 0L) != 0)
		Fail("can't open serial.device");
	/* set parameters from system defaults */
	flow   = !(iob->io_SerFlags & SERF_XDISABLED);
	parity = !(iob->io_SerFlags & SERF_PARTY_ON) ? 0 :
		 (iob->io_SerFlags & SERF_PARTY_ODD) ? 'o' : 'e';
	speed  = iob->io_Baud;
	mdmtyp = (iob->io_SerFlags & SERF_7WIRE) != 0;

	CloseDevice(iob);
	serialopen = FALSE;
	return(0);
}
\f


/*
 * syscleanup -- Amiga specific cleanup
 */
syscleanup()
{
	/* close everything */
	if (serialopen) CloseDevice(ReadIOB);
	if (timeropen) CloseDevice(TimerIOB);
	if (TimerIOB) DeleteExtIO(TimerIOB, (LONG)sizeof(*TimerIOB));
	if (WriteIOB) DeleteExtIO(WriteIOB, (LONG)sizeof(*WriteIOB));
	if (ReadIOB) DeleteExtIO(ReadIOB, (LONG)sizeof(*ReadIOB));
	if (serport) DeletePort(serport);
	if (conpkt) DeletePacket(conpkt);
	if (conport) DeletePort(conport);
	reqres();
	if (IntuitionBase)
	{
		CloseLibrary(IntuitionBase);
		IntuitionBase = NULL;
	}

	/* reset standard I/O */
	if (rawcon > 0)
	{
		/* restore Lattice AmigaDOS file handles */
		DOSFH(0) = Input();
		DOSFH(1) = Output();
		DOSFH(2) = saverr;
#ifdef LAT310
		close(rawcon);
#else
		Close(rawcon);
#endif
	}
}
\f


/*
 * reqoff -- turn requestors off
 *    When AmigaDOS encounters an error that user intervention can fix
 *    (like inserting the correct disk), it normally puts up a requestor.
 *    The following code disables requestors, causing an error to be
 *    returned instead.
 */
reqoff()
{
	pushwindow = CurProc->pr_WindowPtr;
	CurProc->pr_WindowPtr = (APTR)-1;
}
/*
 * reqpop -- restore requesters to action at last reqoff
 */
reqpop()
{
	CurProc->pr_WindowPtr = pushwindow;
}

/*
 * reqres -- restore requestors to startup action
 */
reqres()
{
	CurProc->pr_WindowPtr = savewindow;
}
\f


/*
 * KillIO -- terminate an I/O request
 */
static int KillIO(iob)
struct IORequest *iob;
{
	AbortIO(iob);
	return(WaitIO(iob));
}

/*
 * DoIOQuick -- DoIO with quick IO
 * This should not be used where waiting is expected since
 *    it cannot be interrupted.
 */
static int DoIOQuick(iob)
register struct IORequest *iob;
{
	register int D7Save;	/* V1.1 bug. IO sometimes trashes D7 */

	/* do I/O with quick option, wait around if necessary */
	iob->io_Flags = IOF_QUICK;
	if (BeginIO(iob) == 0 && !(iob->io_Flags & IOF_QUICK))
		WaitIO(iob);

	/* return the error, if any */
	return((int)iob->io_Error);
}
\f


/*
 * ttopen -- open the serial device
 *    If already open, returns 0 immediately.
 *    Otherwise, the ttname is compare to SERIALNAME and used to
 *    open the serial device, and, if the value of *lcl is < 0, it is
 *    reset to 1 indicating local mode.  Returns -1 on error.
 */
ttopen(ttname, lcl, modem)
char *ttname;
int *lcl;
int modem;
{
	register struct IOExtSer *iob = ReadIOB;

	if (serialopen) return(0);	/* ignore if already open */

	/* verify the serial name */
	if (strcmp(ttname, SERIALNAME) != 0) return(-1);

	/* set open modes */
	iob->io_SerFlags = (modem) ? (SERF_SHARED|SERF_7WIRE) : SERF_SHARED;

	/* open the serial device */
	if (OpenDevice(ttname, 0L, iob, 0L) != 0) return(-1);
	serialopen = TRUE;
	pendread = pendwrite = pendconsole = FALSE;
	queuedser = -1;

	/* fill in the fields of the other IO blocks */
	*WriteIOB = *iob;

	/* set local mode */
	if (*lcl == -1)	*lcl = 1; /* always local */
	return(0);
}
\f


/*
 * StartTimer -- start a timeout
 */
static VOID StartTimer(secs, micro)
LONG secs, micro;
{
	TimerIOB->tr_node.io_Command = TR_ADDREQUEST;
	TimerIOB->tr_time.tv_secs  = secs;
	TimerIOB->tr_time.tv_micro = micro;
	SendIO(TimerIOB);
}

/*
 * SerialWait -- wait for serial I/O to terminate
 *    return I/O error
 */
static int SerialWait(iob, timeout)
register struct IOExtSer *iob;
int timeout;
{
	register int D7Save;		/* save register D7 */
	register LONG sigs;
	register struct timerequest *timer = TimerIOB;
	register LONG waitsigs;

	/* set up timeout if necessary */
	if (timeout > 0) StartTimer((LONG)timeout, 0L);

	/* wait for completion, timeout, or interrupt */
	sigs = 0;
	waitsigs = (1L << serport->mp_SigBit) | intsigs;
	for (;;)
	{
		if (sigs & intsigs)
		{	/* interrupted */
			if (timeout > 0) KillIO(timer);
			KillIO(iob);
			testint(sigs);
			return(-1);
		}
		if (CheckIO(iob))
		{
			if (timeout > 0) KillIO(timer);
			return(WaitIO(iob));
		}
		if (timeout > 0 && CheckIO(timer))
		{
			KillIO(iob);
			WaitIO(timer);
			/* restart if XOFF'ed */
			iob->IOSer.io_Command = CMD_START;
			DoIOQuick(iob);
			return(-1);
		}
		sigs = Wait(waitsigs);
	}
}

/*
 * TerminateRead -- wait for queued read to finish
 */
static int TerminateRead()
{
	if (!pendread) return(0);
	if (WaitIO(ReadIOB) == 0) queuedser = serbufc;
	pendread = FALSE;
	return((int)ReadIOB->IOSer.io_Error);
}

/*
 * TerminateWrite -- ensure WriteIOB is ready for reuse
 */
static int TerminateWrite(timeout)
int timeout;
{
	testint(0L);
	if (!pendwrite) return(0);
	pendwrite = FALSE;
	return(SerialWait(WriteIOB, timeout));
}
\f


/*
 * SerialReset -- terminate pending serial and console I/O
 */ 
static VOID SerialReset()
{
	if (pendread)
	{
		AbortIO(ReadIOB); /* should work even if read finished */
		TerminateRead();
	}

	if (pendconsole)
	{	/* this does not happen normally */
		WaitPort(conport);
		GetMsg(conport);
		pendconsole = FALSE;
	}

	if (pendwrite)
		TerminateWrite(1);
}

/*
 * ttres -- reset serial device
 */
ttres()
{
	if (!serialopen) return(-1);

	/* reset everything */
	SerialReset();
	ReadIOB->IOSer.io_Command = CMD_RESET;
	return(DoIOQuick(ReadIOB) ? -1 : 0);
}

/*
 * ttclos -- close the serial device
 */
ttclos()
{
	if (!serialopen) return(0);
	if (ttres() < 0) return(-1);
	CloseDevice(ReadIOB);
	serialopen = FALSE;
	return(0);
}

/*
 * tthang -- hang up phone line
 *    Drops DTR by closing serial.device
 */
tthang()
{	return((serialopen) ? ttclos() : -1); }
\f


/*
 * ttpkt -- set serial device up for packet transmission
 *    sets serial parameters
 */
ttpkt(baud, flow)
int baud, flow;
{
	extern UBYTE eol;
	register struct IOExtSer *iob = ReadIOB;
	int speed;

	if (!serialopen || pendread) return(-1);

	/* terminate any pending writes */
	TerminateWrite(1);

	/* fill in parameters */
	iob->io_CtlChar = 0x11130000;
	if (baud >= 0 && (speed = ttsspd(baud)) >= 0) iob->io_Baud = speed;
	setmem(&iob->io_TermArray, sizeof(struct IOTArray), eol);
	iob->io_ReadLen = iob->io_WriteLen = 8;
	iob->io_StopBits = 1;
	if (flow)
		iob->io_SerFlags &= ~SERF_XDISABLED;
	else
		iob->io_SerFlags |= SERF_XDISABLED;
	/* if no flow and high baud rate, RAD_BOOGIE is appropriate */
	if (!flow && iob->io_Baud > 19200)
		iob->io_SerFlags |= SERF_RAD_BOOGIE;
	else
		iob->io_SerFlags &= ~SERF_RAD_BOOGIE;
	iob->io_SerFlags &= ~(SERF_EOFMODE|SERF_PARTY_ON|SERF_PARTY_ODD);

	/* set the parameters */
	iob->IOSer.io_Command = SDCMD_SETPARAMS;
	if (DoIOQuick(iob) != 0) return(-1);
	return(ttflui());
}

/*
 * ttvt -- set up serial device for connect mode
 */
ttvt(baud, flow)
int baud, flow;
{	return(ttpkt(baud, flow)); }

/*
 * ttsspd -- verify baud rate
 */
int ttsspd(speed)
{
    if (speed < 110 || speed > 292000) return(-1);
    return(max(112, speed));
}

/*
 * ttflui -- flush serial device input buffer
 */
ttflui()
{
	if (!serialopen || pendread) return(-1);
	queuedser = -1;
	ReadIOB->IOSer.io_Command = CMD_CLEAR;
	return(DoIOQuick(ReadIOB) ? -1 : 0);
}
\f



static struct IntuiText BodyText = {
	-1, -1, 0, 4, 4, NULL, "Interrupt Requested", NULL
};

static struct IntuiText ContinueText = {
	-1, -1, 0, 4, 4, NULL, "Continue", NULL
};

static struct IntuiText AbortText = {
	-1, -1, 0, 4, 4, NULL, "Exit C-Kermit", NULL
};

/*
 * test for and catch interrupt
 */
testint(sigs)
LONG sigs;
{
	int (*catch)();

	/* test for and reset caught interrupt signals */
	if (((sigs | SetSignal(0L, (LONG)BREAKSIGS)) & intsigs) && inthdlr)
	{
		if (AutoRequest((struct Window *)NULL,
				&BodyText,
				&ContinueText,
				&AbortText,
				0L, 0L, 260L, 55L) )
			return;
		catch = inthdlr;
		inthdlr = NULL;
		intsigs = 0;
		(*catch)();
	}
}

/*
 * conint -- set console interrupt handler
 */
conint(newhdlr)
int (*newhdlr)();
{
	testint(0L);			/* handle any pending interrupts */
	inthdlr = newhdlr;		/* set the new handler */
	intsigs = BREAKSIGS;		/* note signal caught */
}

/*
 * connoi -- disable interrupt trapping
 */
connoi()
{
	inthdlr = NULL;			/* disable interrupts */
	intsigs = 0;			/* note signal ignored */
	testint(0L);			/* ignore pending interrupts */
}
\f


/*
 * ttchk -- return number of chars immediately available from serial device
 */ 
ttchk()
{
	register struct IOExtSer *read = ReadIOB;

	if (!serialopen) return(-1);
	testint(0L);
	if (pendread && !CheckIO(read)) return(0);
	if (TerminateRead() != 0) return(-1);
	read->IOSer.io_Command = SDCMD_QUERY;
	return((DoIOQuick(read) == 0)
			? ((queuedser >= 0) + (int)read->IOSer.io_Actual)
			: -1);
}

/*
 * ttxin -- get n characters from serial device
 */
ttxin(n, buf)
int n;
char *buf;
{	return(ttinl(buf, n, 0, -1)); }

/*
 * ttinc -- read character from serial line
 */
ttinc(timeout)
int timeout;
{
	UBYTE ch;
 
	return((ttinl(&ch, 1, timeout, -1) > 0) ? (int)ch : -1);
}

/*
 * ttol -- write n chars to serial device, assumes <= 256 characters
 */
ttol(buf, n)
char *buf;
int n;
{
	register int D7Save;
	register struct IOExtSer *write = WriteIOB;
	static char outbuf[256];	/* safe place for output characters */

	if (!serialopen) return(-1);
	if (TerminateWrite(0) != 0) return(-1);
	pendwrite = TRUE;
	movmem(buf, outbuf, n);
	write->IOSer.io_Command = CMD_WRITE;
	write->IOSer.io_Data    = (APTR)outbuf;
	write->IOSer.io_Length  = n;
	SendIO(write);
	return(n);
}

/*
 * ttoc -- output single character to serial device
 */
ttoc(c)
char c;
{	return(ttol(&c, 1)); }
\f


/*
 * ttinl -- read from serial device, possibly with timeout and eol character
 *    reads up to n characters, returning the number of characters read
 *    if eol >= 0, reading the eol character will terminate read
 *    if timeout > 0, terminates read if timeout elapses
 *    returns -1 on error, such as timeout or interrupt
 */
ttinl(buf, n, timeout, eol)
register char *buf;
int n, m;
int timeout;				/* timeout in seconds or <= 0 */
int eol;				/* end of line character */
{
	register int D7Save;
	register struct IOExtSer *read = ReadIOB;
	register int count;

	testint(0L);
 	if (!serialopen || pendread || n <= 0) return(-1);

	m = (ttprty ? 0177 : 0377);	/* parity stripping mask */

	/* handle pushback */
	if (queuedser >= 0)
	{
		*buf = queuedser;
		queuedser = -1;
		if ((*buf & 0177) == eol || n == 1) return(1);
		++buf;
		--n;
		count = 1;
	}
	else
		count = 0;

	/* set up line terminator */
	if (eol >= 0)
	{
		/* set up line terminator */
/*** Watch out -- need to recognize 7-bit eol, even if it comes in with ***/
/*** parity bit on.  It's not obvious to me how to change this code to  ***/
/*** do that!   - Frank, C-Kermit 4E ***/

		if (eol != *(UBYTE *)&read->io_TermArray)
		{
			setmem(&read->io_TermArray,
			       sizeof(struct IOTArray), eol);
			read->IOSer.io_Command = SDCMD_SETPARAMS;
			DoIOQuick(read);
		}
		read->io_SerFlags |= SERF_EOFMODE;
	}
	else
		read->io_SerFlags &= ~SERF_EOFMODE;

	/* set up the read */
	read->IOSer.io_Command = CMD_READ;
	read->IOSer.io_Data    = (APTR)buf;
	read->IOSer.io_Length  = n;

	/* perform read quickly if possible */
	read->IOSer.io_Flags = IOF_QUICK;
	BeginIO(read);
	if (read->IOSer.io_Flags & IOF_QUICK)
	{
		read->IOSer.io_Flags = 0;
		return ((read->IOSer.io_Error == 0)
			? (count + (int)read->IOSer.io_Actual)
			: -1);
	}

/*** Need code somewhere here to strip parity if ttprty != 0 ***/

	/* wait for read to complete */
	return ((SerialWait(read, timeout) != 0)
		? -1
		: count + (int)read->IOSer.io_Actual );
}
\f


/*
 * Sleeper -- perform an interruptible timeout
 */
static Sleeper(secs, micro)
LONG secs, micro;
{
	register int D7Save;
	register LONG sigs;
	register LONG waitsigs;
	register struct timerequest *timer = TimerIOB;

	if (!timeropen) return(-1);
	StartTimer(secs, micro);
	sigs = 0;
	waitsigs = (1L << serport->mp_SigBit) | intsigs;
	for (;;)
	{
		if (CheckIO(timer))
		{
			WaitIO(timer);
			return(0);
		}
		if (sigs & intsigs)
		{
			KillIO(timer);
			testint(sigs);
			return(-1);
		}
		sigs = Wait(waitsigs);
	}
}

/*
 * sleep -- wait n seconds
 */
sleep(n)
int n;
{	return(Sleeper((LONG)n, 0L)); }

/*
 * msleep -- wait n milliseconds
 */
msleep(m)
int m;
{	return(Sleeper((LONG)(m / 1000), (m % 1000) * 1000L)); } 

\f


/*
 * rtimer -- reset elapsed time
 */
rtimer()
{	DateStamp(&prevtime); }

/*
 * gtimer -- get currently elapsed time in seconds
 */
gtimer()
{
	int x;
	struct DateStamp curtime;

	DateStamp(&curtime);
	x = ((curtime.ds_Days   - prevtime.ds_Days  ) * 1440 +
	     (curtime.ds_Minute - prevtime.ds_Minute) ) * 60 +
	     (curtime.ds_Tick   - prevtime.ds_Tick  ) / 50;
	return((x < 0) ? 0 : x );
}

/*
 * ztime -- format current date and time into string
 */
ztime(s)
char **s;
{
   /*
    * The following date code taken from a USENET article by
    *    Tomas Rokicki(rokicki@Navajo.ARPA)
    */
   static char *months[] = { NULL,
      "January","February","March","April","May","June",
      "July","August","September","October","November","December"};
   static char buf[32];

   long n ;
   int m, d, y ;
   struct DateStamp datetime;

   DateStamp(&datetime);

   n = datetime.ds_Days - 2251 ;
   y = (4 * n + 3) / 1461 ;
   n -= 1461 * y / 4 ;
   y += 1984 ;
   m = (5 * n + 2) / 153 ;
   d = n - (153 * m + 2) / 5 + 1 ;
   m += 3 ;
   if (m > 12) {
      y++ ;
      m -= 12 ;
   }
   sprintf(buf, "%02d:%02d:%02d %s %d, %d",
           datetime.ds_Minute / 60, datetime.ds_Minute % 60,
	   datetime.ds_Tick / 50, months[m], d, y) ;
   *s = buf;
}
\f


/*
 * congm -- save console modes
 */
congm()
{
	if (!saverr) saverr = DOSFH(2);
	return(0);
}

/*
 * CreateWindow -- create window and jam it into standard I/O
 */
int CreateWindow(esc)
{
	if (rawcon > 0) return(0);
	congm();

#ifdef LAT310
	if ((rawcon = open("RAW:0/0/640/200/Kermit", O_RDWR)) <= 0)
		return(-1);
#else
	if ((rawcon = Open("RAW:0/0/640/200/Kermit", (LONG)MODE_NEWFILE)) == 0)
		return(-1);
#endif
	DOSFH(0) = DOSFH(1) = DOSFH(2) = rawcon;

	/* if we create a window, don't abort on errors or echo */
	backgrd = FALSE;
	ckxech = 1;
	return(0);
}

/*
 * concb -- put console in single character wakeup mode
 */
concb(esc)
{
	if (rawcon) return(0);
	if (CurCLI && CurProc->pr_CIS != CurCLI->cli_StandardInput)
		return(0);
	return(CreateWindow(esc));
}

/*
 * conbin -- put console in raw mode
 */
conbin(esc)
{
	if (rawcon) return(0);
	if (CurCLI && CurProc->pr_CIS != CurCLI->cli_StandardInput)
		return(isatty(0) ? 0 : -1);
	return(CreateWindow(esc));
}
 
/*
 * conres -- restore console
 *    we actually restore in syscleanup()
 */
conres()
{	return(0); }
\f


/*
 * conoc -- output character to console
 */
conoc(c)
{
	putchar(c);
	fflush(stdout);
	testint(0L);
}

/*
 * conxo -- output x chars to console
 */ 
conxo(n, buf)
char *buf;
int n;
{
	fflush(stdout);
	write(FILENO(1), buf, n);
	testint(0L);
}

/*
 * conol -- output line to console
 */
conol(l)
char *l;
{
	fputs(l, stdout);
	fflush(stdout);
	testint(0L);
}

/*
 * conola -- output line array to console
 */
conola(l)
char **l;
{
	for (; **l; ++l) conol(*l);
}

/*
 * conoll -- output line with CRLF
 */
conoll(l) char *l;
{
    conol(l);
    conxo(2, "\r\n");
}
\f


/*
 * conchk -- returns nonzero if characters available from console
 */
conchk()
{
	fflush(stdout);
	testint(0L);
	return(WaitForChar(DOSFH(0), 0L) != 0);
}

/*
 * coninc -- get input character from console
 */
coninc(timeout)
int timeout;
{
	UBYTE ch;

	fflush(stdout);
	testint(0L);
	if (timeout > 0 && !WaitForChar(DOSFH(0), timeout * 1000000L))
		return(-1);
	if (read(FILENO(0), &ch, 1) < 1) return(-1);
	testint(0L);
	return((int)ch);
}
\f


/*
 * ttsndb -- send a BREAK
 *    flushes queued and active output
 */
ttsndb()
{
	register int D7Save;

	if (!serialopen) return(-1);
	/* flush queued output */
	TerminateWrite(1);
	nttoq = 0;
	pendwrite = TRUE;
	WriteIOB->IOSer.io_Command = SDCMD_BREAK;
	WriteIOB->io_SerFlags &= ~SERF_QUEUEDBRK;
	SendIO(WriteIOB);
	return(0);
}

/*
 * ttocq -- write char to serial device, queueing if necessary
 *    returns -2 on overrun, -1 on serial error
 *    use only in connect mode
 */
ttocq(c)
char c;
{
	register int i;

	if (!serialopen) return(-1);
	if (pendwrite && CheckIO(WriteIOB))
	{
		pendwrite = FALSE;
		if (WaitIO(WriteIOB) != 0) return(-1);
	}
	if (pendwrite)
	{
		if (nttoq >= NTTOQ) return(-2);		/* overrun */
		ttoq[(pttoq + nttoq++) % NTTOQ] = c;
	}
	else if (nttoq == 0)
		return(ttoc(c));
	else
	{
		i = ttoc(ttoq[pttoq]);
		ttoq[(pttoq + nttoq) % NTTOQ] = c;
		pttoq = (pttoq + 1) % NTTOQ;
		if (i < 0) return(-1);
	}
	return(1);
}

/*
 * ttonq -- returns number of characters in serial output queue
 */
int ttonq()
{	return(nttoq); }
\f


/*
 * conttb -- prepare for contti() usage
 */
conttb()
{
	/* flush queued input and output */
	queuedcon = -1;
	pttoq = nttoq = 0;
}

/*
 * contte -- end contti() usage
 *    this can be called after a tthang, it which case ttres will already
 *    have done this cleanup
 */
contte()
{
	/* clear any pending ^C, ^D interrupts */
	testint(0L);

	/* terminate any pending I/O */
	if (serialopen) SerialReset();
}

/*
 * contti -- wait for console or tty input
 *    returns next console input or -1 when serial input available
 */
int contti()
{
	register int D7Save;
	register int i;
	register LONG waitsigs;
	register struct DosPacket *pkt = conpkt;
	register struct IOExtSer *read = ReadIOB;
	static UBYTE conchar;
	BPTR dosfh = DOSFH(0);
	struct FileHandle *fh = (struct FileHandle *)BADDR(dosfh);

	if (queuedcon >= 0)
	{
		conchar = queuedcon;
		queuedcon = -1;
		return((int)conchar);
	}

	if (!pendconsole)
	{	/* start a console read */
		pkt->dp_Port = conport;
		pkt->dp_Type = ACTION_READ;
		pkt->dp_Arg1 = (LONG)dosfh;
		pkt->dp_Arg2 = (LONG)&conchar;
		pkt->dp_Arg3 = 1;
		PutMsg(fh->fh_Process, pkt->dp_Link);
		pendconsole = TRUE;
	}

	if (queuedser < 0 && !pendread)
	{	/* start a serial read */
		read->IOSer.io_Command = CMD_READ;
		read->IOSer.io_Data    = (APTR)&serbufc;
		read->IOSer.io_Length  = 1;
		SendIO(read);
		pendread = TRUE;
	}

	waitsigs = (1L << serport->mp_SigBit) | (1L << conport->mp_SigBit);
	for (;;)
	{
		if (pendwrite && CheckIO(WriteIOB))
		{
			pendwrite = FALSE;
			if (nttoq > 0)
			{
				i = ttoc(ttoq[pttoq]);
				pttoq = (pttoq + 1) % NTTOQ;
				--nttoq;
				if (i < 0) return(-1);
			}
		}

		/* give the console first chance */
		if (GetMsg(conport))
		{
			pendconsole = FALSE;
			if (pkt->dp_Res1 != 1) return(-1);
			/* translate CSI to ESC [ */
			if (conchar == 0x9B)
			{	conchar = 0x1B; queuedcon = '['; }
			return((int)conchar);
		}

		if (queuedser >= 0) return(-2);

		if (CheckIO(read))
			return((TerminateRead() == 0) ? -2 : -1);

		Wait(waitsigs);
	}
}