/************************************************************************
*  a terminal program that has ascii and xmodem transfer capability
*
*  originally written by Michael Mounier
*
*  Modification History -  ( these mods by Steve Allen)
*   Feb 1986	- added Half-duplex and Echoplex.
*		- added timing to and fixed bugs in XMODEM receive.
*		- changed Ascii Send to Prompted Upload.
*		- added Command-Key sequences to menu.
*   March 86	- added function-keys.
*		- changed keyboard and screen io to console.device,
*		  which allows ansi standard vt-100 emulation.
*   April 86	- switched order of CheckKey() and CheckIO() in
*		  ModemRead(), to speed up Xmodem download.
*		- added AutoChop disable.
*		- added necessary code for PutFormat function in con.h
*		  buffered "printf()" (makes for speedy screen writes).
*   May 86	- made all local screen writes red.
*   June 86	- added xon/xoff control (default is OFF).
*		- expanded function-key file capabilities, adding info
*		  about prompt, baudrate, autochop, mode, xon/xoff.
*		- added code suggested by Larry Phillips/ICUG, to buffer
*		  serial input going to screen.
*		- adjusted Ascii capture to accept <TAB>.
*
* (following mods by Larry Phillips)
*   June 86	- Disabled printing of CTRL N.
*		- Fixed hang when cursor key pressed.
*		  NOTE: RKM states that cursor keys are <CSI> [A...D] ~
*		  Howver, the ~ (tilde) is not part of the cursor key
*		  sequence. Proper sequence is <CSI> [A...D]
*
************************************************************************/

/*  compiler directives to fetch the necessary header files */
#include <libraries/dos.h>
#include <devices/serial.h>
#include <fcntl.h>

#include <exec/types.h>
#include <stdio.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <exec/memory.h>
#include <exec/io.h>
#include <exec/devices.h>
#include <graphics/gfx.h>
#include <graphics/copper.h>
#include <graphics/text.h>
#include <graphics/gels.h>
#include <graphics/layers.h>
#include <graphics/gfxbase.h>
#include <graphics/regions.h>
#include <devices/keymap.h>
#include <hardware/blit.h>
#include <functions.h>


#define INTUITION_REV NULL
#define GRAPHICS_REV NULL

/* things for xmodem send and receive */
#define SECSIZ	 128		 /* This many bytes in an xmodem block */
#define BufSize  SECSIZ*2  /* We'll follow two blocks (for chop) */
#define SOH	 1	 /* Start of sector char */
#define EOT	 4	/* end of transmission char */
#define ACK	 6     /* acknowledge sector transmission */
#define NAK	 21   /* error in transmission detected */
#define CAN	 24
#define CR	 13
#define BELL	'G'&0x1f
#define CPM_EOF  26
#define CSI	 (UBYTE)0x9b	/* command sequence introducer */

	  /* things for function keys */
#define NUMKEYS 10		    /* number of function keys */
#define KEYSIZE 40		    /* maximum length of any one string */
#define BUFSIZE KEYSIZE * NUMKEYS   /* size of total buffer */
#define HEADER (UBYTE)0xaa		/* must be first byte of file */
#define NEWHEADER (UBYTE)0xab		/* first byte of expanded file */

/* The following sets the default baudrate, that is, the baudrate that
   Aterm6 comes up in. 0 = 300 baud, 1 = 1200, 2 = 2400, 3 = 4800,
   4 = 9600.
*/
#define STARTBAUD 0

UBYTE
    c,character,prompt,modebyte,
    bufr[BufSize], numstring[5],name[32],
    autochop=TRUE,baudnumber=STARTBAUD,xonoff=FALSE,
    *keybuf,*KeyOffset,
    oldflags;		/* used by xmodem's caller */
static int
    fd,
    timeout = FALSE,
    cancel,
    LOCAL, ECHO,
    waiting,
    OnKey=FALSE;

long baudrate[5] = { 300,1200,2400,4800,9600 };

UBYTE ModemRead();  /* C demands this non-integer declaration */
		    /* 'cause ModemRead is used before it is defined */

extern void PutFormat();		/* function found in "con.h"  */
struct IOStdReq *KeyPort;		/* see "con.h"	*/
extern struct IOStdReq *InitConsole();	/* see "con.h"	*/

/*   Intuition always wants to see these declarations */
struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;



/* my window structure */
struct NewWindow NewWindow = {
   0,0,640,200, /* left, top, width, height */
   0,1, 	/* detail, block pens */
   CLOSEWINDOW | MENUPICK,
   WINDOWCLOSE | SMART_REFRESH | ACTIVATE | WINDOWDEPTH | BORDERLESS,
   NULL,       /* firstgadget */
   NULL,       /* checkmark */
   "Aterm6.1", /* title */
   0,	       /* screen */
   NULL,       /* bitmap */
   0,0,        /* min x,y */
   640, 200,   /* max x,y */
   WBENCHSCREEN  /* type */
};

struct Window *mywindow;	     /* ptr to applications window */
struct IntuiMessage *NewMessage;    /* msg structure for GetMsg() */

/*****************************************************
*		      File Menu
*****************************************************/

/* define maximum number of menu items */
#define FILEMAX 5

/*   declare storage space for menu items and
 *   their associated IntuiText structures
 */
struct MenuItem FileItem[FILEMAX];
struct IntuiText FileText[FILEMAX];

/*****************************************************************/
/*    The following function initializes the structure arrays	 */
/*   needed to provide the File menu topic.			 */
/*****************************************************************/
InitFileItems()
{
short n;

/* initialize each menu item and IntuiText with loop */
for( n=0; n<FILEMAX; n++ )
   {
   FileItem[n].NextItem = &FileItem[n]+1;
   FileItem[n].LeftEdge = 0;
   FileItem[n].TopEdge = 11 * n;
   FileItem[n].Width = 135 + COMMWIDTH;
   FileItem[n].Height = 11;
   FileItem[n].Flags = ITEMTEXT | ITEMENABLED | HIGHBOX | COMMSEQ;
   FileItem[n].MutualExclude = 0;
   FileItem[n].ItemFill = (APTR)&FileText[n];
   FileItem[n].SelectFill = NULL;
   FileItem[n].Command = 0;
   FileItem[n].SubItem = NULL;
   FileItem[n].NextSelect = 0;

   FileText[n].FrontPen = 0;
   FileText[n].BackPen = 1;
   FileText[n].DrawMode = JAM2;     /* render in fore and background */
   FileText[n].LeftEdge = 0;
   FileText[n].TopEdge = 1;
   FileText[n].ITextFont = NULL;
   FileText[n].NextText = NULL;
   }
FileItem[FILEMAX-1].NextItem = NULL;

/* initialize text for specific menu items */
FileText[0].IText = "Ascii Capture";
FileText[1].IText = "Ascii Transmit";
FileText[2].IText = "Xmodem Receive";
FileText[3].IText = "Xmodem Send";
FileText[4].IText = "Prompt";

FileItem[0].Command = 'c';
FileItem[1].Command = 't';
FileItem[2].Command = 'r';
FileItem[3].Command = 's';
FileItem[4].Command = 'p';

return( 0 );
}

/*****************************************************/
/*		  BaudRate  Menu		     */
/*****************************************************/

/* define maximum number of menu items */
#define RSMAX 5

/*   declare storage space for menu items and
 *   their associated IntuiText structures
 */
struct MenuItem RSItem[RSMAX];
struct IntuiText RSText[RSMAX];

/*****************************************************************/
/*    The following function initializes the structure arrays	 */
/*   needed to provide the BaudRate menu.	 		 */
/*****************************************************************/
InitRSItems()
{
short n;

/* initialize each menu item and IntuiText with loop */
for( n=0; n<RSMAX; n++ )
   {
   RSItem[n].NextItem = &RSItem[n]+1;
   RSItem[n].LeftEdge = 0;
   RSItem[n].TopEdge = 11 * n;
   RSItem[n].Width = 85;
   RSItem[n].Height = 11;
   RSItem[n].Flags = ITEMTEXT | ITEMENABLED | HIGHBOX | CHECKIT;
   RSItem[n].MutualExclude = (~(1 << n));
   RSItem[n].ItemFill = (APTR)&RSText[n];
   RSItem[n].SelectFill = NULL;
   RSItem[n].Command = 0;
   RSItem[n].SubItem = NULL;
   RSItem[n].NextSelect = 0;

   RSText[n].FrontPen = 0;
   RSText[n].BackPen = 1;
   RSText[n].DrawMode = JAM2;	  /* render in fore and background */
   RSText[n].LeftEdge = 0;
   RSText[n].TopEdge = 1;
   RSText[n].ITextFont = NULL;
   RSText[n].NextText = NULL;
   }
RSItem[RSMAX-1].NextItem = NULL;

/* check the baud item corresponding to STARTBAUD */
RSItem[STARTBAUD].Flags |= CHECKED;

/* initialize text for specific menu items */
RSText[0].IText = "   300";
RSText[1].IText = "   1200";
RSText[2].IText = "   2400";
RSText[3].IText = "   4800";
RSText[4].IText = "   9600";

}

/***************************************************
*		  Mode menu	    (feb.86)
****************************************************/
struct MenuItem ModeItem[3];
struct IntuiText Modetext[3];

initmode()
{
short n;

for ( n=0; n<3; n++)

{
 ModeItem[n].NextItem = &ModeItem[n+1];
 ModeItem[n].LeftEdge = 0;
 ModeItem[n].TopEdge = 11 * n;
 ModeItem[n].Width = 85 + COMMWIDTH;
 ModeItem[n].Height = 11;
 ModeItem[n].Flags = ITEMTEXT | ITEMENABLED | HIGHBOX | CHECKIT | COMMSEQ;
 ModeItem[n].MutualExclude = (~(1 << n));
 ModeItem[n].ItemFill = (APTR) &Modetext[n];
 ModeItem[n].SelectFill = NULL;
 ModeItem[n].Command = 0;
 ModeItem[n].SubItem = NULL;
 ModeItem[n].NextSelect = 0;

 Modetext[n].FrontPen = 0;
 Modetext[n].BackPen = 1;
 Modetext[n].DrawMode = JAM2;
 Modetext[n].LeftEdge = 0;
 Modetext[n].TopEdge = 1;
 Modetext[n].ITextFont = NULL;
 Modetext[n].NextText = NULL;
}

ModeItem[2].NextItem = NULL;

/* default is Terminal mode  (full duplex)  */
modebyte = 1;
ModeItem[1].Flags |= CHECKED;
LOCAL = 0;
ECHO = 0;

Modetext[0].IText = "    Half";
Modetext[1].IText = "    Full";
Modetext[2].IText = "    Echo";

ModeItem[0].Command = 'h';
ModeItem[1].Command = 'f';
ModeItem[2].Command = 'e';

}     /* end initmode */

/******************** Initialize Misc. Menu **********************/
#define FKMAX 5

struct MenuItem FKitem[FKMAX];
struct IntuiText FKtext[FKMAX];

InitMisc()
{
int x;
for (x=0;x<FKMAX;x++)
{
 FKitem[x].NextItem = &FKitem[x+1];
 FKitem[x].LeftEdge = 0;
 FKitem[x].TopEdge = 12 * x;
 FKitem[x].Width = 100 + COMMWIDTH;
 FKitem[x].Height = 12;
 FKitem[x].MutualExclude = NULL;
 FKitem[x].ItemFill = (APTR) &FKtext[x];
 FKitem[x].SelectFill = NULL;
 FKitem[x].Command = NULL;
 FKitem[x].SubItem = NULL;
 FKitem[x].NextSelect = NULL;

 FKtext[x].FrontPen = 0;
 FKtext[x].BackPen = 1;
 FKtext[x].DrawMode = JAM2;
 FKtext[x].LeftEdge = 0;
 FKtext[x].TopEdge = 1;
 FKtext[x].ITextFont = NULL;
 FKtext[x].NextText = NULL;
}
FKitem[FKMAX-1].NextItem = NULL;

FKitem[0].Flags = ITEMENABLED | HIGHCOMP | ITEMTEXT | COMMSEQ;
FKtext[0].IText = "F-Key Load";
FKitem[0].Command = 'k';

FKtext[1].IText = "F-Key Save";
FKitem[1].Flags = ITEMENABLED | HIGHCOMP | ITEMTEXT; /* no commseq on this */

FKtext[2].IText = "Define FKey";
FKitem[2].Command = 'd';
FKitem[2].Flags = ITEMENABLED | ITEMTEXT | HIGHCOMP | COMMSEQ;

FKtext[3].IText = "  AutoChop";
FKitem[3].Flags = ITEMENABLED | HIGHCOMP | ITEMTEXT | COMMSEQ | CHECKIT | CHECKED;
FKitem[3].Command = 'a';

FKtext[4].IText = "  Xon/Xoff";
FKitem[4].Flags = ITEMENABLED | ITEMTEXT | HIGHCOMP | COMMSEQ | CHECKIT ;
FKitem[4].Command = 'x';

}

/***************************************************/
/*		  Menu Definition		   */
/*						   */
/*	This section of code is where the simple   */
/*   menu definition goes.			   */
/***************************************************/

/* current number of available menu topics */
#define MAXMENU 4

/*   declaration of menu structure array for
 *   number of current topics.	Intuition
 *   will use the address of this array to
 *   set and clear the menus associated with
 *   the window.
 */
struct Menu menu[MAXMENU];

/**********************************************************************/
/*   The following function initializes the Menu structure array with */
/*  appropriate values for our simple menu strip.  Review the manual  */
/*  if you need to know what each value means.			      */
/**********************************************************************/
InitMenu()
{
menu[0].NextMenu = &menu[1];
menu[0].LeftEdge = 5;
menu[0].TopEdge = 0;
menu[0].Width = 50;
menu[0].Height = 10;
menu[0].Flags = MENUENABLED;
menu[0].MenuName = "File";	     /* text for menu-bar display */
menu[0].FirstItem = &FileItem[0];    /* pointer to first item in list */

menu[1].NextMenu = &menu[2];
menu[1].LeftEdge = 65;
menu[1].TopEdge = 0;
menu[1].Width = 85;
menu[1].Height = 10;
menu[1].Flags = MENUENABLED;
menu[1].MenuName = "BaudRate";	      /* text for menu-bar display */
menu[1].FirstItem = &RSItem[0];    /* pointer to first item in list */

menu[2].NextMenu = &menu[3];
menu[2].LeftEdge = 150;
menu[2].TopEdge = 0;
menu[2].Width = 60;
menu[2].Height = 10;
menu[2].Flags = MENUENABLED;
menu[2].MenuName = "Mode";
menu[2].FirstItem = &ModeItem[0];

menu[3].NextMenu = NULL;
menu[3].LeftEdge = 210;
menu[3].TopEdge = 0;
menu[3].Width = 50;
menu[3].Height = 10;
menu[3].Flags = MENUENABLED;
menu[3].MenuName = "Misc.";
menu[3].FirstItem = &FKitem[0];
}

/* declarations for the serial stuff */
struct IOExtSer *Read_Request;
UBYTE rs_in[2];
struct IOExtSer *Write_Request;
UBYTE rs_out[2];

/******************************************************/
/*		     Main Program		      */
/*						      */
/*	This is the main body of the program.	      */
/******************************************************/

main()
{
ULONG class,waitmask;
USHORT code,menunum,itemnum;
int KeepGoing,capture,send;
UBYTE c;
FILE *tranr,*trans;

IntuitionBase = OpenLibrary("intuition.library", INTUITION_REV);
if( IntuitionBase == NULL )
   {
   puts("can't open intuition\n");
   exit(TRUE);
   }

GfxBase = OpenLibrary("graphics.library",GRAPHICS_REV);
if( GfxBase == NULL )
   {
   puts("can't open graphics library\n");
   exit(TRUE);
   }


if(( mywindow = OpenWindow(&NewWindow) ) == NULL)
   {
   puts("can't open window\n");
   exit(TRUE);
   }

if(( KeyPort = InitConsole(mywindow) ) == FALSE)
   { puts("Can't open console\n");
     goto q7;
    }

Read_Request = (struct IOExtSer *)AllocMem((long)sizeof(*Read_Request),
  (long)MEMF_PUBLIC|MEMF_CLEAR);
if (Read_Request == NULL) goto q6;

Read_Request->io_SerFlags = SERF_SHARED | SERF_XDISABLED;
Read_Request->io_CtlChar = 0x11130501;
Read_Request->IOSer.io_Message.mn_ReplyPort = CreatePort("Read_RS",NULL);
if(OpenDevice(SERIALNAME,NULL,Read_Request,NULL))
   {
   puts("Can't open Read device\n");
   goto q4;
   }

Read_Request->io_Baud = baudrate[STARTBAUD];
Read_Request->io_ReadLen = 8;
Read_Request->io_WriteLen = 8;
Read_Request->IOSer.io_Command = SDCMD_SETPARAMS;
DoIO(Read_Request);
Read_Request->IOSer.io_Command = CMD_READ;
Read_Request->IOSer.io_Length = 1;
Read_Request->IOSer.io_Data = &rs_in[0];


Write_Request = (struct IOExtSer *)AllocMem((long)sizeof(*Write_Request),
  (long)MEMF_PUBLIC|MEMF_CLEAR);
if (Write_Request == NULL) goto q3;

Write_Request->io_SerFlags = SERF_SHARED | SERF_XDISABLED;
Write_Request->io_CtlChar = 0x11130501;
Write_Request->IOSer.io_Message.mn_ReplyPort = CreatePort("Write_RS",NULL);
if(OpenDevice(SERIALNAME,NULL,Write_Request,NULL))
   {
   puts("Can't open Write device\n");
   goto q1;
   }
Write_Request->io_Baud = baudrate[STARTBAUD];
Write_Request->io_ReadLen = 8;
Write_Request->io_WriteLen = 8;
Write_Request->IOSer.io_Command = SDCMD_SETPARAMS;
DoIO(Write_Request);

Write_Request->IOSer.io_Command = CMD_WRITE;
Write_Request->IOSer.io_Length = 1;
Write_Request->IOSer.io_Data =	&rs_out[0];

if (( keybuf= AllocMem((long)BUFSIZE,
     (long)MEMF_PUBLIC | MEMF_CLEAR))== NULL)
 { puts("Can't get memory for function keys\n");
   CloseDevice(Write_Request);
q1:   DeletePort(Write_Request->IOSer.io_Message.mn_ReplyPort);
q2:   FreeMem(Write_Request,(long)sizeof(*Write_Request));
q3:   CloseDevice(Read_Request);
q4:   DeletePort(Read_Request->IOSer.io_Message.mn_ReplyPort);
q5:   FreeMem(Read_Request,(long)sizeof(*Read_Request));
q6:   CloseConsole();
q7:   CloseWindow( mywindow );
      CloseLibrary(GfxBase);
      CloseLibrary(IntuitionBase);
   exit(TRUE);
  }


InitFileItems();
InitRSItems();
initmode();
InitMisc();
InitMenu();
SetMenuStrip(mywindow,&menu[0]);

capture=FALSE;
waiting=send=FALSE;
prompt = 0;

BeginIO(Read_Request);

PutChar(12);	/* clears the screen */

 waitmask = (1L << Read_Request->IOSer.io_Message.mn_ReplyPort->mp_SigBit)
 | ( 1L << mywindow->UserPort->mp_SigBit)
 | (1L << KeyPort->io_Message.mn_ReplyPort->mp_SigBit);

if (LoadKeys("init.key"))	    /* did the default f-keys load? */
	format(PutFormat,"\x9b33mFunction Keys Loaded...\n\x9bm\xff");
else
if (LoadKeys("s:init.key"))	    /* did the default f-keys load? */
	format(PutFormat,"\x9b33mFunction Keys Loaded...\n\x9bm\xff");

KeepGoing = TRUE;
while( KeepGoing )
     {

     if ((send | OnKey) == 0)
     Wait(waitmask);

     if (send)
       {

	 if (waiting == FALSE)
	  {  int c;
	     if ((c=getc(trans)) != EOF)
	      { if (c == '\n')
		 { sendchar(CR);  /* switch in carriage return	*/
		   if (prompt) waiting=TRUE;
		  }
		else sendchar(c);
	       }
	    else
	      {
		fclose(trans);
		PutString("\x9b33m\nFile Sent\n\x9bm");
		send=waiting=FALSE;
	       }
	   }	 /* end (if not waiting) */
	 }	 /* end (if send) */

     if (OnKey)  /* if doing function-key  (Mar.86) */
       {
    if ((c=*KeyOffset++)== 0) /* then end of this F-key string */
	     OnKey=FALSE;
	 else sendchar(c);
	}


   while (c=CheckKey())
   {			  /*  User has touched the keyboard */
     if (c == CSI)
      { character = GetKey();
	if ((character < 'A') || (character > 'D'))
	while ((c=GetKey()) != '~')
	      ;

	if (character == '?')
	 {
	   format(PutFormat,"\x9b33mAMIGA Term ATERM6.1\n\n");	/* in red */

	   format(PutFormat,"\nPrompt character is \xff");
	   if (prompt == 0) PutChar('0');
	    else keychar(prompt);
	   PutString("\n\n");
	   DoContents();		/* display function keys */
	   PutString("\x9bm");		/* turn off red */
	  }
	 else if ((character >= 0x30) && (character < 0x3a))
	  { KeyOffset = keybuf+(KEYSIZE*(character-'0'));
	    OnKey = TRUE;
	   }
	}    /* end if CSI */
      else
       {
	 rs_out[0] = c;
	 DoIO(Write_Request);
	 if (LOCAL) PutChar(c=='\r'?'\n':c);
	}
    }	   /* end while CheckKey() */

     if(CheckIO(Read_Request))
      { do
       {
	WaitIO(Read_Request);
	c=rs_in[0] & 0x7f;
	BeginIO(Read_Request);
	if ((c != '\n') && (c != 14))	/* don't print LF or CTRL N */
	PutFormat(c=='\r'?'\n':c);	/* convert CR into LF */
	if (ECHO)
	 {
	  rs_out[0] = c;       /* must do this way: sendchar() also */
	  DoIO(Write_Request); /* PutChar()s if LOCAL, and calling sendchar() */
	  }		       /* would do a double-PutChar() in EchoPlex */

	if (waiting) if (c==prompt) waiting = FALSE;
	if (capture)
	    if (c > 31 && c < 127 || c == '\n' || c== 9)
				   /* trash them mangy ctl chars */
		putc(c , tranr);
	} while (CheckIO(Read_Request));
	 PutFormat((char)0xff);		/* squirt out all received chars */
       }

     while( NewMessage=(struct IntuiMessage *)GetMsg(mywindow->UserPort) )
	  {
	  class = NewMessage->Class;
	  code = NewMessage->Code;
	  ReplyMsg( NewMessage );
	  switch( class )
		{
		case CLOSEWINDOW:
		   KeepGoing = FALSE;
		break;

		case MENUPICK:
		   if ( code != MENUNULL )
		       {
		       menunum = MENUNUM( code );
		       itemnum = ITEMNUM( code );
		       switch( menunum )
			     {
			     case 0:
				switch( itemnum )
				      {
				      case 0:
					 if (capture)
					  {
					     capture=FALSE;
					     fclose(tranr);
					     PutString("\x9b33m\nEnd File Capture\n\x9bm");
					    }
					 else
					   {
					     if (GetLine("\x9b33m\nAscii Capture: ",name))
					      {
						if ((tranr=fopen(name,"w")) == 0)
						    {
						      capture=FALSE;
						      PutString("\nCan't Open File\n\x9bm");
						      break;
						     }
						 capture=TRUE;
						 PutString("\x9bm");
						}
					      else PutString("\nAborted\n\x9bm");
					     }
				      break;
				      case 1:
					 if (send)
					     { 
					     send=waiting=FALSE;
					     fclose(trans);
					     PutString("\n\x9b33mFile Send Cancelled\n\x9bm");
					     }
					 else
					   if (GetLine("\x9b33m\nAscii Send: ",name))
					      {
						if ((trans=fopen(name,"r")) == 0)
						 { send=FALSE;
						   PutString("\nCan't Open File\n\x9bm");
						   break;
						  }
						send=TRUE;
						waiting=FALSE;
						PutString("\x9bm");
					       }
					    else PutString("\nAborted\n\x9bm");
				      break;
				      case 2:
					 if (GetLine("\x9b33m\nXmodem Receive: ",name))
					  {

					 /* disable ^Q/^S trapping */
					    AbortIO(Read_Request);
					    oldflags = Read_Request->io_SerFlags;
					    Read_Request->io_SerFlags |= SERF_XDISABLED;
					    Read_Request->IOSer.io_Command = SDCMD_SETPARAMS;
					    DoIO(Read_Request);
					    Read_Request->IOSer.io_Command = CMD_READ;
					    BeginIO(Read_Request);

					   XMODEM_Read_File(name);

					 /* Restore old flags */
					    AbortIO(Read_Request);
					    Read_Request->io_SerFlags = oldflags;
					    Read_Request->IOSer.io_Command = SDCMD_SETPARAMS;
					    DoIO(Read_Request);
					    Read_Request->IOSer.io_Command = CMD_READ;
					    BeginIO(Read_Request);
					   DisplayBeep(NULL);
					    }
					    else
					   { PutString("\nAborted\n");
					    }
					  PutString("\x9bm");
				      break;
				      case 3:
				       if (GetLine("\x9b33m\nXmodem Send: ",name))
					{ if (XMODEM_Send_File(name))
					     {
					     PutString("\nFile Sent\n\x9bm");
					     DisplayBeep(NULL);
					     }
					   else
					     {
					     close(fd);
					     PutString("\nXmodem Send Failed\n\x9bm");
					     DisplayBeep(NULL);
					     }
					 }
				       else PutString("\nAborted\n\x9bm");
				      break;
				      case 4:	   /* new prompt */
					 PutString("\x9b33mNew Prompt Character: ");
					 if ((c = GetKey()) == '0')
					  prompt = 0;
					  else prompt = c;
					 keychar(c);
					 PutString("\n\x9bm");
				       break;
				      }
			     break;

			     case 1:		/* New Baud Rate */
				AbortIO(Read_Request);
				Read_Request->io_Baud = baudrate[itemnum];
				baudnumber = itemnum;

				Read_Request->IOSer.io_Command = SDCMD_SETPARAMS;
				DoIO(Read_Request);
				Read_Request->IOSer.io_Command = CMD_READ;
				BeginIO(Read_Request);
			     break;
			    case 2:		   /*  "Mode" */
			      modebyte = itemnum;
			      switch( itemnum)
			      {
			       case 0:	/* Half Duplex */
				ECHO = 0;
				LOCAL = 1;
				break;
			       case 1:	/* Full Duplex */
				ECHO = 0;
				LOCAL = 0;
				break;
			       case 2:	/* EchoPlex  */
				ECHO = 1;
				LOCAL = 1;
			       }
			     break;

			     case 3:	/* misc. menu selection */
			     switch (itemnum)
			      { case 0: /* FKey load */
			       if (GetLine("\x9b33mFunction-Key file name: ",name))
				{ if (LoadKeys(name))
				  PutString("Loaded\n\x9bm");
				  else PutString("Couldn't load that file\n\x9bm");
				 } else PutString(" aborted.\n\x9bm");
				 break;


				case 1:		/* save a F-Key file */
				 if (GetLine("\x9b33mFunction-Key Save Filename: ",name))
				  { if (SaveFKeys(name))
				     format(PutFormat,"File saved\n\x9bm\xff");
				    else format(PutFormat,"Unable to save that file.\n\x9bm\xff");
				   }
				 else format(PutFormat,"aborted...\n\x9bm\xff");
				 break;


				case 2:		/* define a F-Key string */
				 DefineString();
				 break;


				case 3:       /* switch autochop on or off */ 
				 autochop = autochop ? FALSE : TRUE;
				 /* Here's a kluge to set and reset checkmark */
				 ClearMenuStrip(mywindow);

				 if (autochop)
				   FKitem[3].Flags |= CHECKED;
				 else
				   FKitem[3].Flags &= (~CHECKED);

				 SetMenuStrip(mywindow,&menu[0]);
				 break;


				case 4:		/* Xon/Xoff switcheroo	*/
				 xonoff = xonoff ? FALSE : TRUE;
				AbortIO(Read_Request);
				if (xonoff)
				 Read_Request->io_SerFlags &= ~(SERF_XDISABLED);
				else
				 Read_Request->io_SerFlags |= SERF_XDISABLED;
				Read_Request->IOSer.io_Command = SDCMD_SETPARAMS;
				DoIO(Read_Request);
				Read_Request->IOSer.io_Command = CMD_READ;
				BeginIO(Read_Request);

				 ClearMenuStrip(mywindow);
				 if (xonoff)
				  FKitem[4].Flags |= CHECKED;
				 else
				  FKitem[4].Flags &= ~(CHECKED);
				 SetMenuStrip(mywindow,&menu[0]);
				 break;

			       }   /*  end switch (itemnum) */ 
			     } /* end of switch ( menunum ) */
		       }    /*	end of if ( not null ) */
		}   /* end of switch (class) */
	  }   /* end of while ( newmessage )*/
     }	/* end while ( keepgoing ) */

/*   It must be time to quit, so we have to clean
*   up and exit.
*/
       
CloseDevice(Read_Request);
DeletePort(Read_Request->IOSer.io_Message.mn_ReplyPort);
FreeMem(Read_Request,(long)sizeof(*Read_Request));
FreeMem(keybuf,(long)BUFSIZE);
CloseDevice(Write_Request);
DeletePort(Write_Request->IOSer.io_Message.mn_ReplyPort);
FreeMem(Write_Request,(long)sizeof(*Write_Request));
ClearMenuStrip( mywindow );
CloseWindow( mywindow );
CloseConsole();
CloseLibrary(GfxBase);
CloseLibrary(IntuitionBase);
exit();
} /* end of main */

/********************************************************************/
/*	     terminal-mode character send			    */
/********************************************************************/

sendchar(ch)
  UBYTE ch;
  {
    rs_out[0] = ch;
    DoIO(Write_Request);
    if (LOCAL)
    if(ch!='\n')
    PutChar(ch=='\r'?'\n':ch);	/* if Half-Duplex or Echo-plex */
   }

/**************************************************************/
/* send char and read char functions for the xmodem function */
/************************************************************/

/*    purge() is called whenever an error is detected.
     Its function is to flush the input buffer, and wait
     until the sender has finished sending a block.
     This ensures that the sender will see the NAK
     sent by the error routine.
*/

void purge(purgetime)
    int purgetime;	/* number of ticks @ 50 per second */
   {

    do	ModemRead(purgetime);
    while (timeout == FALSE);	    /* Wait for the line to clear */
   }



     /* ModemSend() is simply sendchar() without local echo
      */

void ModemSend(c)
    UBYTE c;
    { rs_out[0] = c;
      DoIO(Write_Request);
     }

/*     ModemRead()  gets a character from the serial port within a
      specified number of ticks (tick = 1/50 second). DateStamp()
      returns a 3-byte integer date consisting of day, minute, ticks.
      ModemRead() will function correctly as long as the date is set
      and a day boundary isn't crossed.
*/


    UBYTE
ModemRead(MAXTICKS)
   int MAXTICKS;	      /* number of ticks @ 50 per second */
  {
   static long startime[3], endtime[3];
   UBYTE c;
 
   timeout = FALSE;
   DateStamp(startime);
   do {

       if (CheckIO(Read_Request))
	{ WaitIO(Read_Request);
	  c = rs_in[0];
	  BeginIO(Read_Request);
	  return c;		/* Character came in on time */
	 }
       if ((c=CheckKey()) == 0x1b)  /* ESC cancels xmodem tranxfer */
	     { cancel = TRUE;
	       return (0);  /* This will tend to force an error        */
	      } 	    /* when <esc> pressed 'midst block receive */
       DateStamp(endtime);
       endtime[2] -= startime[2];
	if ( endtime[1] -= startime[1] ) /* did minutes count change? */
	endtime[2] += endtime[1] * 3000; /* number of ticks in minute */
      }
   while (endtime[2] < MAXTICKS);
     /* else character not received on time */
   timeout = TRUE;
   return (0);
  }



/*   CloseEmUp() closes a receive file, ending Xmodem receive.
     It also performs a 'chop' function, testing for
     zero, 0xff, and CP/M_EOF (ctrol-z).
     To implement the chop function, a two-block buffer
     is maintained, with the basic pointer being bufr[offset],
     with offset toggled 0,SECSIZ,0, etc.
     During file receive, writes lag reads by one block: e.g.,
     when block # 3 is received, block # 2 is written.
     If block # 3 is the final block, CloseEmUp() will
     chop any invalid characters, write the block, and
     close the file.
*/

CloseEmUp(fd,bufr,offset,ourblock)

int fd,offset, ourblock;
UBYTE *bufr;

{
   int i,x;
   ULONG size=0;
   UBYTE c,numstring[7];

 offset = offset ? 0 : SECSIZ ;  /* toggle offset */

 if (ourblock)			   /* skip if no data at all */
  {
    i = SECSIZ - 1;
    if (autochop)
     {
       c = bufr[offset + i];	   /* c gets last char in block */
       if ( (c == 0) || (c == CPM_EOF) || (c == 0xff) )
	{
	  while (bufr[ offset+(--i) ] == c)  /* Chop those useless bytes! */
		   ;
	 }
      }
    else PutString("Chop Disabled: ");

    if ((c = write(fd, bufr+offset, ++i)) != i)
       PutString("Error writing final block\n");
    x = SECSIZ - i;
    format(PutFormat,"%d bytes chopped from final block\n",&x);

    size=(--ourblock)*(long)SECSIZ;
    size += i;
    format(PutFormat,"File size: %ld\n\xff",&size);
   }

 close(fd);
}


/**************************************/
/* xmodem send and receive functions */
/************************************/

	    /* These time constants are for benefit of
	       CompuServe and other packet-switching networks
	       that must be allowed more "slack time"
	       due to inherent delays in system.
	     */

#define Blocktime 20*50 /* Wait 20 seconds for start of block */
#define Chartime   5*50 /* Wait 5 seconds for chars when getting block */


XMODEM_Read_File(file)

char *file;

{
int ourblock, errors, checksum, offset, x,i;
UBYTE c,blockin;

if ( (fd = creat(file,0)) == -1)
   { PutString("Can't open file\n");
     return FALSE;
    }
else PutString("Receiving file \n");

	 /* Input file now open */

offset = ourblock = errors = cancel = 0;


ModemSend(NAK);
			/*  MAIN LOOP STARTS HERE  */
loop:

c = ModemRead(Blocktime);

   if (cancel)
   { PutString("Xmodem cancelled\n");
     close(fd);
     purge(Chartime);
     ModemSend(CAN);
     return(FALSE);
    }

   if (timeout)
   { format(PutFormat,"timed out waiting for block start\n");
     goto error;
    }

 if(c == CAN)	 /*Sender just quit*/
   { purge(Chartime);
     PutString("Sender aborted transfer\n");
     CloseEmUp(fd,bufr,offset,ourblock);
     return(FALSE);
    }
 if (c == EOT)	 /*Sender is finished */
   {
     CloseEmUp(fd,bufr,offset,ourblock);   /* chop and write last block */
     PutString("Transfer Completed\n");
     ModemSend(ACK);
     return(TRUE);
    }
 if (c != SOH)
   { purge(Chartime);
     format(PutFormat,"not a valid header\n");
     goto error;
    }
      /*Sender has a block for us */
      /* block receive follows here */
blockin = ModemRead(Chartime);	/* get block number */
   if (timeout)
   { purge(Chartime);
     format(PutFormat,"Didn't get block# in time\n");
     goto error;
    }
c = ModemRead(Chartime);       /* get 1/blocknumber */
   if (timeout)
   { purge(Chartime);
     format(PutFormat,"Timed out waiting for 1/block#\n");
     goto error;
    }

if ( ( (UBYTE)~c) != blockin)  /* block # got lunched */
   { purge(Chartime);
     format(PutFormat,"block complement didn't match\n");
     goto error;
    }
	    /* Here comes a block of data  */

checksum = 0;

for (i = 0; i < SECSIZ; i++ )
 {
   c = ModemRead(Chartime);
     if (timeout)
      { purge(Chartime);
	format(PutFormat,"Timed out during block\n");
	goto error;
       }
   bufr[offset+i] = c;
   checksum += c;
  }

	    /* Here comes the checksum */
c = ModemRead(Chartime);
   if (timeout)
   { purge(Chartime);
     format(PutFormat,"Timed out waiting for checksum\n");
     goto error;
    }
if (c != (checksum & 0xff) ) /* checksums don't match */
  {
   purge(Chartime);
   format(PutFormat,"checksum error\n");
   goto error;
   }

      /* Block has been received OK: Now check back on blocknumber */

switch ( (UBYTE) (blockin - (ourblock & 0xff)) )
{
 case (0):	 /* Duplicate block */
  {
   format(PutFormat,"\nDuplicate block # %d\n\xff",&ourblock);
   errors = 0;
   ModemSend(ACK);
   goto loop;
  }
 case (1):	/* New block */
  {
   ourblock++;		 /* Update our block count */
   format(PutFormat,"\x0bReceived block # %d	 \n\xff",&ourblock);

   offset = offset ? 0 : SECSIZ;		  /* toggle offset */
   if (ourblock != 1)
   {						  /* Write it out */
     if ( (x = (write(fd, bufr+offset, SECSIZ)) ) != SECSIZ)
       { close(fd);
	 PutString("Error writing to file.\nTransfer aborted.\n");
	 ModemSend(CAN);
	 return(FALSE);
	}
    }
   errors = 0;
   ModemSend(ACK);
   goto loop;
  }
default:
    break;     /* fall through to error: */
}
purge(Chartime);
format(PutFormat,"Block numbers didn't agree\n");

		  /* ERROR ROUTINE STARTS HERE */
error:

if (cancel)
   { format(PutFormat,"XModem cancelled\n\xff");
     close(fd);
     ModemSend(CAN);
     return(FALSE);
    }

 errors++;

 if (errors == 10) /* then abort */
   {
     format(PutFormat," 10 Errors. Transfer cancelled.\n\xff");
     close(fd);
     ModemSend(CAN);
     return(FALSE);
    }
 format(PutFormat,"Error # %d\n\xff",&errors);
 ModemSend(NAK);
 goto loop;

} /* end of XMODEM_Read_File()	*/


#define WaitTime 20*50	/* Wait this long for NAKs (20 seconds)*/

XMODEM_Send_File(file)
    char *file;
{
    int i, x, j,
	 size,
	 errors=0, blocknum=1;
    char numb[10];
    UBYTE sectnum=1,c,checksum;
    cancel=FALSE;
    if ((fd = open(file, O_RDONLY)) == -1)
      {
	 PutString("Can't Open Send File\n");
	 ModemSend(CAN);
	 return FALSE;
	}
    else
	PutString("Sending File\n");

       for (errors=0;errors<10;errors++)
	{ c=ModemRead(WaitTime);
	  if (cancel)
	   { PutString("Aborted\n");
	     return(FALSE);
	    }
	  if (timeout)
	   { PutString("...nothing yet...\n");
	    }
	  else if (c == CAN)
	   { PutString("Receiver has cancelled.\n");
	     return(FALSE);
	    }
	  else if (c != NAK)
	   { PutString("Got something, but not NAK\n");
	    }
	  else break;	     /* got first NAK */
	 }
	   if (errors == 10)
	    { PutString("Quitting after 10 errors\n");
	      ModemSend(CAN);
	      return(FALSE);
	     }
   XS0:    /* get a block into the buffer */

#asm
      move.l   #_bufr,a0      ; zero out the memory buffer
      move.w   #31,d0	      ; set up for 128 bytes
loop  clr.l    (a0)+
      dbf      d0,loop
#endasm

      size = read(fd,bufr,SECSIZ);
      if (size == 0)
       { ModemSend(EOT);
	 close(fd);
	 return(TRUE);
	}
      if (size == -1)
       { PutString("Error reading file\n");
	 close(fd);
	 ModemSend(CAN);
	 return(FALSE);
	}
      errors = 0;
   XS1: 	 /* send a block */

	 ModemSend(SOH);
	 ModemSend(sectnum);
	 ModemSend(~sectnum);
	 checksum = 0;

	 for (j=0;j<SECSIZ;j++)
	  {
	     ModemSend(bufr[j]);
	     checksum += bufr[j];
	    }
	 ModemSend(checksum);

	 c = ModemRead(WaitTime);
	 if (cancel)
	   { ModemSend(CAN);
	     PutString("Aborted.\n");
	     return(FALSE);
	    }
	 if (c==CAN)
	   { PutString("Receiver cancelled.\n");
	     return(FALSE);
	    }
	 if ((timeout) || (c != ACK))
	  { errors++;
	    format(PutFormat,"Error # %d\n\xff",&errors);
	    if (errors == 10)
	     { ModemSend(CAN);
	       return(FALSE);
	      }
	    goto XS1;	    /*	try again  */
	   }
		/* following line does reverse linefeed, erase to eol */
	    format(PutFormat,"\x0b\x9bKBlock %d sent\n\xff",&blocknum);
	    sectnum++;
	    blocknum++;
	    goto XS0;
 }		      /* end XMODEM_send */

/*********************************************************************
**	  Some routines concerned with the function keys	    **
*********************************************************************/
	       
/************* Attempt to load a Function-Key file ******************/

LoadKeys(keyfile)    /* returns (BOOL) success	*/

   char *keyfile;  /* the filename */
 {
      FILE *fp;
      UBYTE c;
      int i;
   if ((fp=fopen(keyfile,"r")) == NULL)
     return(FALSE);
   if ((c=fgetc(fp)) != HEADER)  /* gotta be a valid key file! */
    {
     if (c != NEWHEADER)
     { fclose(fp);
       return(FALSE);
      }
   else		/* it's a new, improved info file! */
    {
      prompt = fgetc(fp);	/* get prompt character */
      baudnumber = fgetc(fp);
      modebyte = fgetc(fp);		/* half, full, or echo	*/
      autochop = fgetc(fp);
      xonoff = fgetc(fp);

		/* First fix up the serial ports */

      AbortIO(Read_Request);
      if(xonoff==0)
       Read_Request->io_SerFlags |= SERF_XDISABLED;
      else
	Read_Request->io_SerFlags &= ~(SERF_XDISABLED);

	Read_Request->io_Baud = baudrate[baudnumber];
      Read_Request->IOSer.io_Command = SDCMD_SETPARAMS;
      DoIO(Read_Request);
      Read_Request->IOSer.io_Command = CMD_READ;
      BeginIO(Read_Request);
      switch (modebyte)
       {
        case 0:	/* Half Duplex */
	 ECHO = 0;
	 LOCAL = 1;
	 break;
	case 1:	/* Full Duplex */
	 ECHO = 0;
	 LOCAL = 0;
	 break;
	case 2:	/* EchoPlex  */
	 ECHO = 1;
	 LOCAL = 1;
	}
		/* Now try to fix up the menus */
	ClearMenuStrip(mywindow);
	FKitem[3].Flags &= (~CHECKED);		/* set up autochop menu	*/
	if (autochop)
	  FKitem[3].Flags |= CHECKED;

	for (i=0;i<3;i++)
	 ModeItem[i].Flags &= ~(CHECKED);	/* un-check mode menu	*/
	ModeItem[modebyte].Flags |= CHECKED;	/* now check right mode	*/

	for (i=0;i<RSMAX;i++)
	 RSItem[i].Flags &= ~(CHECKED);		/* un-check baudrate	*/
	RSItem[baudnumber].Flags |= CHECKED;	/* now check right baud	*/

	FKitem[4].Flags &= ~(CHECKED);		/* set up xon-xoff menu	*/
	if (xonoff)
	 FKitem[4].Flags |= CHECKED;
	
	SetMenuStrip(mywindow,&menu[0]);			/* all done: turn menu on */
     }
    }			/* here begins the standard f-key file load */
   for (i=0;i<BUFSIZE;i++)
    {
      if ((c=fgetc(fp)) == EOF)
       { fclose(fp);
	 return(FALSE);
	} 
      else keybuf[i] = c;
     }
    fclose(fp);
    return(TRUE);    /* ahh, success! */
  }
/**************Save Function-Key file *****************/

SaveFKeys(filename)	/* returns (BOOL) success */
 char *filename;

{
  FILE *fp;
  int x,i;

  if((fp=fopen(filename,"w")) == NULL)
    return(FALSE);

  if((x=putc(NEWHEADER,fp)) == EOF)
   goto savequit;

  if((x=putc(prompt,fp)) == EOF)
   goto savequit;
   
  if((x=putc(baudnumber,fp)) == EOF)
   goto savequit;

  if((x=putc(modebyte,fp)) == EOF)
   goto savequit;

  if((x=putc(autochop,fp)) == EOF)
   goto savequit;

  if((x=putc(xonoff,fp)) == EOF)
   goto savequit;

  for (i=0;i<BUFSIZE;i++)
   { if ((x=putc(keybuf[i],fp)) == EOF)  /* write Function-Keys */
    goto savequit;
    }
  fclose(fp);
  return (TRUE);   /* File successfully saved */

  savequit: format(PutFormat,"Error writing file\n\xff");
  fclose(fp);
  return (FALSE);
 }


/************ Display Function-Key contents **********/

DoContents()

 {
   int x;
   UBYTE c;
  for (x=0;x<NUMKEYS;x++)
  {
    KeyOffset = keybuf+(KEYSIZE * x);
    PrintKeyNum(x);
    while ( c = *KeyOffset++ )
      keychar(c);
    PutChar('\n');
   }
 }

/************ Print the function-key number **************/
/*		format example: F7:			*/

PrintKeyNum(keynum)
    int keynum;
 {
   keynum++;
   PutChar('F');
  if (keynum == 10)
   PutString("10: ");
  else
   { PutChar(keynum + '0');
     PutString(":  ");
    }
  }

/*********** Print a character from function-key string ************/

keychar(key)
   UBYTE key;
 {
   if (key < 32) /* control character? */
    {
      switch (key)
      { case 0:
	PutChar('0');
	break;
	case 8:
	PutString("<BS>");
	break;
	case 9:
	PutString("<TAB>");
	break;
       case 10:
	PutString("<NL>");
	break;
       case 13:
	PutString("<CR>");
	break;
       case 27:
	PutString("<ESC>");
	break;
       default:
	PutChar('^');
	PutChar(key + 0x40);
       } 
      }
   else PutChar(key);
  }

/*******	Define a function-key string	*********/

 DefineString()
{
 UBYTE c, character;
 int keynum, count, offset, i;

  format(PutFormat,"\x9b33mFunction-Key String Definition:\n\n\xff");
  if ((c=GetKey()) != CSI)
   { format(PutFormat,"aborted...\n\n\x9bm\xff");
     return;
    }
  else
   {
     character=GetKey();  /* get the meat of the sequence */
     while ((c=GetKey()) != '~')   /* wait for sequence to end */
         ;
    }
      if (!( (character >= 0x30) && (character < 0x3a) )) /* isdigit? */
      {
	format(PutFormat,"aborted...\n\n\x9bm\xff");
	return;
       }
       else
	{
	   keynum = character-'0';
	   PrintKeyNum(keynum);
	   offset = KEYSIZE * keynum; /* find correct spot in buffer */
	   i=0;
	  while ((character = GetKey()) != CSI)		/* get the string */
	   { if (i<(KEYSIZE-1))
	    {
	      keybuf[offset+ (i++) ] = character;	/* store it	  */
	      keychar(character);  			/* display it, too */
	     }
	    }
	   while ((c=GetKey()) != '~' )	/* skip over rest of F-key	*/
	   	;
	   while (i<KEYSIZE)
	   {
	      keybuf[offset+i] = 0;	/* zero out the rest of		*/
	      i++;			/* that string's buffer		*/
	    }
	   PutString("\n\n");
	   PutString("\x9bm");
	  }	/* end else */
 }	/* end DefineString();	*/
