/*
**	termConsole.c
**
**	High-level terminal console routines
**
**	Copyright © 1990-1993 by Olaf `Olsen' Barthel & MXM
**		All Rights Reserved
*/

#include "termGlobal.h"

	/* Character set modes. */

enum	{	MODE_STANDARD,MODE_GFX };

	/* Hints for the data flow scanner. */

STATIC WORD	ScanStart,
		ScanEnd;

STATIC WORD	AttentionCount[SCAN_COUNT],
		AttentionLength[SCAN_COUNT],
		FlowCount;

	/* Temporary console working buffer. */

STATIC UBYTE	ConTempBuffer[512];

	/* Text output data. */

STATIC UBYTE	CurrentFontScale,FontScalingRequired;
STATIC WORD	LastPrintableColumn;

	/* ConTranslateSetup():
	 *
	 *	Set up for buffer translation.
	 */

STATIC VOID __inline
ConTranslateSetup(struct TranslationHandle *Handle,STRPTR SourceBuffer,LONG SourceLen,STRPTR DestinationBuffer,LONG DestinationLen,struct TranslationEntry **Table)
{
	Handle -> LocalBuffer		= NULL;
	Handle -> LocalLen		= 0;

	Handle -> SourceBuffer		= SourceBuffer;
	Handle -> SourceLen		= SourceLen;

	Handle -> DestinationBuffer	= DestinationBuffer;
	Handle -> DestinationLen	= DestinationLen;

	Handle -> Table			= Table;
}

	/* ConTranslateBuffer(struct TranslationHandle *Handle):
	 *
	 *	Translate buffer contents according to
	 *	translation table contents.
	 */

STATIC LONG __inline
ConTranslateBuffer(struct TranslationHandle *Handle)
{
	register STRPTR				 Data		= Handle -> DestinationBuffer;
	register LONG				 BytesWritten	= 0;
	register struct TranslationEntry	*Entry;

		/* Are we to return any translated data? */

	while(Handle -> LocalLen && BytesWritten < Handle -> DestinationLen)
	{
			/* Decrement number of bytes in buffer. */

		Handle -> LocalLen--;

			/* Return next character. */

		*Data++ = *Handle -> LocalBuffer++;

			/* Add another byte. */

		BytesWritten++;
	}

		/* Loop until done. */

	while(Handle -> SourceLen && BytesWritten < Handle -> DestinationLen)
	{
			/* Another byte eaten. */

		Handle -> SourceLen--;

			/* Get table entry. */

		if(Entry = Handle -> Table[*Handle -> SourceBuffer++])
		{
				/* Copy to local data area. */

			Handle -> LocalBuffer	= Entry -> String;
			Handle -> LocalLen	= Entry -> Len;

				/* Translate the data. */

			while(Handle -> LocalLen && BytesWritten < Handle -> DestinationLen)
			{
					/* Decrement number of bytes in buffer. */

				Handle -> LocalLen--;

					/* Return next character. */

				*Data++ = *Handle -> LocalBuffer++;

					/* Add another byte. */

				BytesWritten++;
			}
		}
	}

	return(BytesWritten);
}

	/* FlowFilter(register STRPTR Data,register LONG Size,UBYTE Mask):
	 *
	 *	Data flow filter.
	 */

STATIC VOID __regargs
FlowFilter(register STRPTR Data,register LONG Size,register UBYTE Mask)
{
	register UBYTE c;

		/* Run until done. */

	do
	{
		if(c = (*Data++ & Mask))
		{

				/* We already got a `CONNECT',
				 * Continue scanning the serial output
				 * data for the actual baud rate.
				 */

			if(BaudPending)
			{
				if(c < ' ')
				{
					while(BaudCount > 0 && BaudBuffer[BaudCount - 1] == ' ')
						BaudCount--;

					BaudBuffer[BaudCount] = 0;

					FlowInfo . Connect = TRUE;
					FlowInfo . Changed = TRUE;

					BaudPending = FALSE;

					DTERate = GetBaudRate(BaudBuffer);
				}
				else
				{
					if(BaudCount || c != ' ')
					{
						BaudBuffer[BaudCount++] = c;

						if(BaudCount == 79)
						{
							while(BaudCount > 0 && BaudBuffer[BaudCount - 1] == ' ')
								BaudCount--;

							BaudBuffer[BaudCount] = 0;

							FlowInfo . Connect = TRUE;
							FlowInfo . Changed = TRUE;

							BaudPending = FALSE;

							DTERate = GetBaudRate(BaudBuffer);
						}
					}
				}
			}
			else
			{
				register BYTE MatchMade;
				register WORD i;

				do
				{
					MatchMade = FALSE;

						/* Scan all ID strings for matches. */

					for(i = ScanStart ; i <= ScanEnd ; i++)
					{
							/* This sequence is a likely
							 * match.
							 */

						if(AttentionCount[i] == FlowCount)
						{
								/* Does the character
								 * fit into the sequence?
								 */

							if(c == AttentionBuffers[i][FlowCount] & Mask)
							{
								MatchMade = TRUE;

									/* Did we hit the
									 * last character
									 * in the sequence?
									 */

								if(++AttentionCount[i] == AttentionLength[i])
								{
										/* We've got a valid
										 * sequence, now look
										 * which flags to change.
										 */

									switch(i)
									{
											/* We got a `no carrier' message. */

										case SCAN_NOCARRIER:

											if(Config -> SerialConfig -> CheckCarrier)
											{
												WriteRequest -> IOSer . io_Command = SDCMD_QUERY;

												DoIO(WriteRequest);

													/* Is the carrier still present? */

												if(!(WriteRequest -> io_Status & CIAF_COMCD))
													break;
											}

											if(!FlowInfo . NoCarrier)
											{
												FlowInfo . NoCarrier	= TRUE;
												FlowInfo . Changed	= TRUE;
											}

											WasOnline = Online;

											Online = FALSE;

												/* Clear the password. */

											Password[0] = 0;
											UserName[0] = 0;

											CurrentBBSName[0]	= 0;
											CurrentBBSComment[0]	= 0;
											CurrentBBSNumber[0]	= 0;

											if(WasOnline)
											{
												StopCall(FALSE);

												if(CurrentPay && ChosenEntry)
													LogAction(LocaleString(MSG_TERMAUX_CARRIER_LOST_COST_TXT),CreateSum(CurrentPay,TRUE));
												else
													LogAction(LocaleString(MSG_TERMAUX_CARRIER_LOST_TXT));

												Say(LocaleString(MSG_TERMAUX_CARRIER_LOST_TXT));

												SetDialMenu(TRUE);
											}

											break;

											/* Got another call. */

										case SCAN_RING:

											if(!FlowInfo . Ring)
											{
												FlowInfo . Ring		= TRUE;
												FlowInfo . Changed	= TRUE;
											}

											break;

											/* Got a voice call. */

										case SCAN_VOICE:

											if(!FlowInfo . Voice)
											{
												FlowInfo . Voice	= TRUE;
												FlowInfo . Changed	= TRUE;
											}

											break;

											/* Got a connect message. */

										case SCAN_CONNECT:

												/* Are we to check the carrier signal? */

											if(Config -> SerialConfig -> CheckCarrier)
											{
												WriteRequest -> IOSer . io_Command = SDCMD_QUERY;

												DoIO(WriteRequest);

													/* Is the carrier signal present, i.e.
													 * was the `CONNECT' message valid?
													 */

												if(WriteRequest -> io_Status & CIAF_COMCD)
													break;
											}

											WasOnline = Online;

											if(!Online)
											{
												BaudBuffer[0]	= 0;

												BaudPending	= TRUE;
												BaudCount	= 0;
											}

											break;

											/* Got the ZModem inquiry sequence. */

										case SCAN_ZMODEM:

											if(!FlowInfo . ZModemUpload)
											{
												FlowInfo . ZModemUpload	= TRUE;
												FlowInfo . Changed	= TRUE;
											}

											break;

											/* Line is busy. */

										case SCAN_BUSY:

											if(!FlowInfo . Busy)
											{
												FlowInfo . Busy		= TRUE;
												FlowInfo . Changed	= TRUE;
											}

											break;

										case SCAN_NODIALTONE:

											if(!FlowInfo . NoDialTone)
											{
												FlowInfo . NoDialTone	= TRUE;
												FlowInfo . Changed	= TRUE;
											}

											break;
									}
								}
							}
						}
					}

					if(MatchMade)
						FlowCount++;
					else
					{
						if(FlowCount)
						{
							FlowCount = 0;

							memset(AttentionCount,0,sizeof(AttentionCount));
						}
						else
							break;
					}
				}
				while(!FlowCount);
			}
		}
	}
	while(--Size);
}

	/* StripSequence():
	 *
	 *	Strips a string from ESC and CSI introduced control
	 *	sequences.
	 */

STATIC LONG __regargs
StripSequence(STRPTR Src,STRPTR Dst,LONG Length)
{
	STATIC BYTE	HasESC	= FALSE,
			HasCSI	= FALSE;
	LONG		Size	= 0;

	while(Length--)
	{
		if(HasESC)
		{
			if(*Src == '[')
			{
				HasESC = FALSE;
				HasCSI = TRUE;
			}
			else
			{
				if(*Src >= '0')
					HasESC = FALSE;
			}

			Src++;

			continue;
		}

		if(HasCSI)
		{
			if(*Src >= '@')
				HasCSI = FALSE;

			Src++;

			continue;
		}

		switch(*Src)
		{
			case CAN:
			case SUB:

				HasESC = HasCSI = FALSE;
				break;

			case '\r':

				break;

			case ESC:

				HasESC = TRUE;
				break;

			case CSI:

				HasCSI = TRUE;
				break;

			default:

				if(IsPrintable[*Src])
				{
					*Dst++ = *Src;

					Size++;
				}

				break;
		}

		Src++;
	}

	return(Size);
}

	/* ConOutputPrinter(STRPTR Buffer,LONG Size):
	 *
	 *	Raw text output routine, printer capture flavour.
	 */

STATIC VOID __regargs
ConOutputPrinter(STRPTR Buffer,LONG Size)
{
	if(FWrite(PrinterCapture,Buffer,Size,1) != 1)
	{
		BlockWindows();

		if(!MyEasyRequest(NULL,LocaleString(MSG_CONSOLE_ERROR_WRITING_TO_PRINTER_TXT),LocaleString(MSG_CONSOLE_IGNORE_CLOSE_PRINTER_TXT)))
			ClosePrinterCapture(TRUE);

		ReleaseWindows();
	}
}

	/* ConOutputCaptureGFX(STRPTR Buffer,LONG Size):
	 *
	 *	Raw text output routine, GFX text & capture flavour.
	 */

STATIC VOID __regargs
ConOutputCaptureGFX(STRPTR Buffer,LONG Size)
{
	register WORD Offset;

		/* Do we still have a character in the
		 * magnificient buffer?
		 */

	while(Size)
	{
			/* Cursor is positioned at
			 * the right hand side of the
			 * display. If auto-wrap is
			 * enabled, perform some
			 * kind of CR/LF, else leave
			 * the cursor where it is and
			 * quit the show.
			 */

		if(CursorX > LastPrintableColumn)
		{
				/* Wrap cursor. */

			if(Config -> EmulationConfig -> LineWrap)
			{
					/* Move to beginning of next line. */

				CursorX = 0;

				DownLine();

				Capture("\n",1);

				CurrentFontScale = SCALE_ATTR_NORMAL;

				FontScalingRequired = (Config -> EmulationConfig -> FontScale != SCALE_NORMAL);

				LastPrintableColumn = LastColumn;

					/* Reposition cursor, don't redraw it. */

				ClipBlitCursor(FALSE,TRUE);
			}
			else
			{
					/* Stop the cursor. */

				CursorX = LastPrintableColumn;

				Capture(Buffer,Size);

					/* Make it reappear. */

				ClipBlitCursor(FALSE,TRUE);

				return;
			}
		}

		if((Offset = LastPrintableColumn + 1 - CursorX) > Size)
			Offset = Size;

		if(Config -> EmulationConfig -> InsertMode)
		{
			RasterShiftChar(Offset);

			ScrollLineShiftChar(Offset);

			ShiftChar(Offset);
		}

		RasterPutString(Buffer,Offset);

		ScrollLinePutString(Offset);

		if(FontScalingRequired)
			PrintScaled(Buffer,Offset,CurrentFontScale);
		else
			GfxText(RPort,Buffer,Offset);

		Capture(Buffer,Offset);

		Buffer	+= Offset;

		Size	-= Offset;

		CursorX	+= Offset;
	}

	ClipBlitCursor(FALSE,TRUE);
}

	/* ConOutputCaptureNormal(STRPTR Buffer,LONG Size):
	 *
	 *	Raw text output routine, normal text & capture flavour.
	 */

STATIC VOID __regargs
ConOutputCaptureNormal(STRPTR Buffer,LONG Size)
{
	register WORD Offset;

	while(Size)
	{
		if(CursorX > LastPrintableColumn)
		{
			if(Config -> EmulationConfig -> LineWrap)
			{
				CursorX = 0;

				DownLine();

				Capture("\n",1);

				CurrentFontScale = SCALE_ATTR_NORMAL;

				FontScalingRequired = (Config -> EmulationConfig -> FontScale != SCALE_NORMAL);

				LastPrintableColumn = LastColumn;

				ClipBlitCursor(FALSE,TRUE);
			}
			else
			{
				CursorX = LastPrintableColumn;

				Capture(Buffer,Size);

				ClipBlitCursor(FALSE,TRUE);

				return;
			}
		}

		if((Offset = LastPrintableColumn + 1 - CursorX) > Size)
			Offset = Size;

		if(Config -> EmulationConfig -> InsertMode)
		{
			RasterShiftChar(Offset);

			ScrollLineShiftChar(Offset);

			ShiftChar(Offset);
		}

		RasterPutString(Buffer,Offset);

		ScrollLinePutString(Offset);

		if(FontScalingRequired)
			PrintScaled(Buffer,Offset,CurrentFontScale);
		else
			Text(RPort,Buffer,Offset);

		Capture(Buffer,Offset);

		Buffer	+= Offset;

		Size	-= Offset;

		CursorX	+= Offset;
	}

	ClipBlitCursor(FALSE,TRUE);
}

	/* ConOutputGFX(STRPTR Buffer,LONG Size):
	 *
	 *	Raw text output routine, GFX text flavour.
	 */

STATIC VOID __regargs
ConOutputGFX(STRPTR Buffer,LONG Size)
{
	register WORD Offset;

		/* Do we still have a character in the
		 * magnificient buffer?
		 */

	while(Size)
	{
			/* Cursor is positioned at
			 * the right hand side of the
			 * display. If auto-wrap is
			 * enabled, perform some
			 * kind of CR/LF, else leave
			 * the cursor where it is and
			 * quit the show.
			 */

		if(CursorX > LastPrintableColumn)
		{
				/* Wrap cursor. */

			if(Config -> EmulationConfig -> LineWrap)
			{
					/* Move to beginning of next line. */

				CursorX = 0;

				DownLine();

				CurrentFontScale = SCALE_ATTR_NORMAL;

				FontScalingRequired = (Config -> EmulationConfig -> FontScale != SCALE_NORMAL);

				LastPrintableColumn = LastColumn;

					/* Reposition cursor, don't redraw it. */

				ClipBlitCursor(FALSE,TRUE);
			}
			else
			{
					/* Stop the cursor. */

				CursorX = LastPrintableColumn;

					/* Make it reappear. */

				ClipBlitCursor(FALSE,TRUE);

				return;
			}
		}

		if((Offset = LastPrintableColumn + 1 - CursorX) > Size)
			Offset = Size;

		if(Config -> EmulationConfig -> InsertMode)
		{
			RasterShiftChar(Offset);

			ScrollLineShiftChar(Offset);

			ShiftChar(Offset);
		}

		RasterPutString(Buffer,Offset);

		ScrollLinePutString(Offset);

		if(FontScalingRequired)
			PrintScaled(Buffer,Offset,CurrentFontScale);
		else
			GfxText(RPort,Buffer,Offset);

		Buffer	+= Offset;

		Size	-= Offset;

		CursorX	+= Offset;
	}

	ClipBlitCursor(FALSE,TRUE);
}

	/* ConOutputNormal(STRPTR Buffer,LONG Size):
	 *
	 *	Raw text output routine, normal text flavour.
	 */

STATIC VOID __regargs
ConOutputNormal(STRPTR Buffer,LONG Size)
{
	register WORD Offset;

	while(Size)
	{
		if(CursorX > LastPrintableColumn)
		{
			if(Config -> EmulationConfig -> LineWrap)
			{
				CursorX = 0;

				DownLine();

				CurrentFontScale = SCALE_ATTR_NORMAL;

				FontScalingRequired = (Config -> EmulationConfig -> FontScale != SCALE_NORMAL);

				LastPrintableColumn = LastColumn;

				ClipBlitCursor(FALSE,TRUE);
			}
			else
			{
				CursorX = LastPrintableColumn;

				ClipBlitCursor(FALSE,TRUE);

				return;
			}
		}

		if((Offset = LastPrintableColumn + 1 - CursorX) > Size)
			Offset = Size;

		if(Config -> EmulationConfig -> InsertMode)
		{
			RasterShiftChar(Offset);

			ScrollLineShiftChar(Offset);

			ShiftChar(Offset);
		}

		RasterPutString(Buffer,Offset);

		ScrollLinePutString(Offset);

		if(FontScalingRequired)
			PrintScaled(Buffer,Offset,CurrentFontScale);
		else
			Text(RPort,Buffer,Offset);

		Buffer	+= Offset;

		Size	-= Offset;

		CursorX	+= Offset;
	}

	ClipBlitCursor(FALSE,TRUE);
}

	/* ConOutputUpdate():
	 *
	 *	Choose the right raw text output routine for the job.
	 */

VOID
ConOutputUpdate()
{
	if(ControllerActive)
		ConOutput = ConOutputPrinter;
	else
	{
		if((Config -> CaptureConfig -> CaptureFilterMode && FileCapture) || PrinterCapture)
		{
			if(CurrentFont == GFX)
				ConOutput = ConOutputCaptureGFX;
			else
				ConOutput = ConOutputCaptureNormal;
		}
		else
		{
			if(BufferFrozen)
			{
				if(CurrentFont == GFX)
					ConOutput = ConOutputGFX;
				else
					ConOutput = ConOutputNormal;
			}
			else
			{
				if(CurrentFont == GFX)
					ConOutput = ConOutputCaptureGFX;
				else
					ConOutput = ConOutputCaptureNormal;
			}
		}
	}

	if(ReceiveTable)
		ConDump = ConTransWrite;
	else
		ConDump = ConOutput;
}

	/* ConFontScaleUpdate():
	 *
	 *	Choose the right font scale for the job.
	 */

VOID
ConFontScaleUpdate()
{
	CurrentFontScale = RasterAttr[CursorY];

	if(Config -> EmulationConfig -> FontScale == SCALE_HALF)
	{
		FontScalingRequired	= (CurrentFontScale != SCALE_ATTR_2X);
		LastPrintableColumn	= LastColumn;
	}
	else
	{
		if(FontScalingRequired = (CurrentFontScale != SCALE_ATTR_NORMAL))
			LastPrintableColumn = ((LastColumn + 1) / 2) - 1;
		else
			LastPrintableColumn = LastColumn;
	}
}

	/* ConTransWrite(STRPTR Buffer,LONG Size):
	 *
	 *	Frontend for ConWrite(), including character translation.
	 */

VOID __regargs
ConTransWrite(STRPTR Buffer,LONG Size)
{
	struct TranslationHandle	Handle;
	UBYTE				LocalBuffer[256];

		/* Set up for translation. */

	ConTranslateSetup(&Handle,Buffer,Size,LocalBuffer,256,ReceiveTable);

		/* Process and output the data. */

	while(Size = ConTranslateBuffer(&Handle))
		(* ConOutput)(LocalBuffer,Size);
}

	/* ConProcessDataTransExternal(register STRPTR String,register LONG Size):
	 *
	 *	Process data, external emulation including translation flavour.
	 */

VOID __regargs
ConProcessDataTransExternal(register STRPTR String,register LONG Size)
{
	struct TranslationHandle Handle;

		/* Set up for translation. */

	ConTranslateSetup(&Handle,String,Size,ConTempBuffer,256,ReceiveTable);

	if(StripBuffer)
	{
		register LONG Len;

			/* Process and output the data. */

		while(Size = ConTranslateBuffer(&Handle))
		{
			XEmulatorWrite(XEM_IO,ConTempBuffer,Size);

			XEM_HostData . Source		= ConTempBuffer;
			XEM_HostData . Destination	= StripBuffer;

			if(Len = XEmulatorHostMon(XEM_IO,&XEM_HostData,Size))
				Capture(StripBuffer,Len);
		}
	}
	else
	{
			/* Process and output the data. */

		while(Size = ConTranslateBuffer(&Handle))
			XEmulatorWrite(XEM_IO,ConTempBuffer,Size);
	}
}

	/* ConProcessDataExternal(register STRPTR String,register LONG Size):
	 *
	 *	Process data, external emulation flavour.
	 */

VOID __regargs
ConProcessDataExternal(register STRPTR String,register LONG Size)
{
	XEmulatorWrite(XEM_IO,String,Size);

		/* Build another string to contain
		 * the pure ASCII contents, i.e.
		 * not including any ESC control
		 * sequences.
		 */

	if(StripBuffer)
	{
		register LONG Len;

		XEM_HostData . Source		= String;
		XEM_HostData . Destination	= StripBuffer;

		if(Len = XEmulatorHostMon(XEM_IO,&XEM_HostData,Size))
			Capture(StripBuffer,Len);
	}
}

	/* ConProcessData7(register STRPTR String,register LONG Size):
	 *
	 *	Process data, 7 bit flavour.
	 */

VOID __regargs
ConProcessData7(register STRPTR String,register LONG Size)
{
	register LONG	Len = 0;
	register UBYTE	c;

		/* If still parsing a sequence,
		 * continue with it.
		 */

	if(InSequence)
	{
		while(Size--)
		{
			c = *String++ & 0x7F;

			if(!(*AbortTable[c])(c))
			{
				InSequence = FALSE;

				break;
			}
		}
	}

		/* Check which font we are in, if other than Topaz
		 * the only invalid char is a Null (0) which will
		 * display as a space if let to continue.
		 */

	if(Config -> TerminalConfig -> FontMode == FONT_STANDARD)
	{
		while(Size-- > 0)
		{
			if(IsPrintable[c = *String++ & 0x7F])
			{
					/* This character is associated with a
					 * special function (bell, xon, xoff, etc.).
					 */

				if(SpecialTable[c])
				{
					if(Len)
					{
						(*ConDump)(ConTempBuffer,Len);

						Len = 0;
					}

						/* Does this character start
						 * a control sequence?
						 */

					if(InSequence = (*SpecialTable[c])(c))
					{
						while(Size-- > 0)
						{
							c = *String++ & 0x7F;

							if(!(*AbortTable[c])(c))
							{
								InSequence = FALSE;

								break;
							}
						}
					}
				}
				else
				{
						/* Put the character into the buffer
						 * and flush it if necessary.
						 */

					ConTempBuffer[Len] = c;

					if(Len++ == 512)
					{
						(*ConDump)(ConTempBuffer,Len);

						Len = 0;
					}
				}
			}
		}
	}
	else
	{
		while(Size-- > 0)
		{
			if(c = (*String++ & 0x7F))
			{
					/* This character is associated with a
					 * special function (bell, xon, xoff, etc.).
					 */

				if(SpecialTable[c])
				{
					if(Len)
					{
						(*ConDump)(ConTempBuffer,Len);

						Len = 0;
					}

					if(InSequence = (*SpecialTable[c])(c))
					{
						while(Size-- > 0)
						{
							c = *String++ & 0x7F;

							if(!(*AbortTable[c])(c))
							{
								InSequence = FALSE;

								break;
							}
						}
					}
				}
				else
				{
						/* Put the character into the buffer
						 * and flush it if necessary.
						 */

					ConTempBuffer[Len] = c;

					if(Len++ == 512)
					{
						(*ConDump)(ConTempBuffer,Len);

						Len = 0;
					}
				}
			}
		}
	}

	if(Len)
		(*ConDump)(ConTempBuffer,Len);
}

	/* ConProcessData8(register STRPTR String,register LONG Size):
	 *
	 *	Process data, 8 bit flavour.
	 */

VOID __regargs
ConProcessData8(register STRPTR String,register LONG Size)
{
	register LONG	Len = 0;
	register UBYTE	c;

	if(InSequence)
	{
		while(Size--)
		{
			c = *String++;

			if(!(*AbortTable[c])(c))
			{
				InSequence = FALSE;

				break;
			}
		}
	}

	if(Config -> TerminalConfig -> FontMode == FONT_STANDARD)
	{
		while(Size-- > 0)
		{
			if(IsPrintable[c = *String++])
			{
				if(SpecialTable[c])
				{
					if(Len)
					{
						(*ConDump)(ConTempBuffer,Len);

						Len = 0;
					}

					if(InSequence = (*SpecialTable[c])(c))
					{
						while(Size-- > 0)
						{
							c = *String++;

							if(!(*AbortTable[c])(c))
							{
								InSequence = FALSE;

								break;
							}
						}
					}
				}
				else
				{
					ConTempBuffer[Len] = c;

					if(Len++ == 512)
					{
						(*ConDump)(ConTempBuffer,Len);

						Len = 0;
					}
				}
			}
		}
	}
	else
	{
		while(Size-- > 0)
		{
			if(c = *String++)
			{
				if(SpecialTable[c])
				{
					if(Len)
					{
						(*ConDump)(ConTempBuffer,Len);

						Len = 0;
					}

					if(InSequence = (*SpecialTable[c])(c))
					{
						while(Size-- > 0)
						{
							c = *String++;

							if(!(*AbortTable[c])(c))
							{
								InSequence = FALSE;

								break;
							}
						}
					}
				}
				else
				{
					ConTempBuffer[Len] = c;

					if(Len++ == 512)
					{
						(*ConDump)(ConTempBuffer,Len);

						Len = 0;
					}
				}
			}
		}
	}

	if(Len)
		(*ConDump)(ConTempBuffer,Len);
}

	/* ConProcessDataHex7(register STRPTR String,register LONG Size):
	 *
	 *	Process data, hex mode flavour, seven bits.
	 */

VOID __regargs
ConProcessDataHex7(register STRPTR String,register LONG Size)
{
	UBYTE	DummyBuffer[40],c;
	LONG	Fit,Spaces,Current;

		/* How many characters will fit into the line? */

	Fit	= (LastColumn + 1) / 4;

		/* Which position are we currently in? */

	Current	= CursorX / 3;

		/* Weird cursor position? */

	if(Current >= Fit || (CursorX % 3))
	{
		ConProcessData7("\r\n",2);

		Current = 0;
	}

		/* Check the font type. */

	if(Config -> TerminalConfig -> FontMode == FONT_STANDARD)
	{
		while(Size-- > 0)
		{
			c = (*String++) & 0x7F;

			Spaces = (Fit - Current) * 3 + Current - 3;

			if(c > ' ' && c < 127)
				SPrintf(DummyBuffer,"%02lx \33[%ldC%lc\33[%ldD",c,Spaces,c,Spaces + 1);
			else
				SPrintf(DummyBuffer,"%02lx \33[%ldC.\33[%ldD",c,Spaces,Spaces + 1);

			ConProcessData7(DummyBuffer,strlen(DummyBuffer));

			if(Current++ == Fit)
			{
				Current = 0;

				ConProcessData7("\r\n",2);
			}
		}
	}
	else
	{
		while(Size-- > 0)
		{
			c = (*String++) & 0x7F;

			Spaces = (Fit - Current) * 3 + Current - 3;

			if(c && c != ' ')
				SPrintf(DummyBuffer,"%02lx \33[%ldC%lc\33[%ldD",c,Spaces,c,Spaces + 1);
			else
				SPrintf(DummyBuffer,"%02lx \33[%ldC.\33[%ldD",c,Spaces,Spaces + 1);

			ConProcessData7(DummyBuffer,strlen(DummyBuffer));

			if(Current++ == Fit)
			{
				Current = 0;

				ConProcessData7("\r\n",2);
			}
		}
	}
}

	/* ConProcessDataHex8(register STRPTR String,register LONG Size):
	 *
	 *	Process data, hex mode flavour, eight bits.
	 */

VOID __regargs
ConProcessDataHex8(register STRPTR String,register LONG Size)
{
	UBYTE	DummyBuffer[40],c;
	LONG	Fit,Spaces,Current;

		/* How many characters will fit into the line? */

	Fit	= (LastColumn + 1) / 4;

		/* Which position are we currently in? */

	Current	= CursorX / 3;

		/* Weird cursor position? */

	if(Current >= Fit || (CursorX % 3))
	{
		ConProcessData8("\r\n",2);

		Current = 0;
	}

		/* Check the font type. */

	if(Config -> TerminalConfig -> FontMode == FONT_STANDARD)
	{
		while(Size-- > 0)
		{
			c = *String++;

			Spaces = (Fit - Current) * 3 + Current - 3;

			if((c > ' ' && c < 127) || c > 160)
				SPrintf(DummyBuffer,"%02lx \33[%ldC%lc\33[%ldD",c,Spaces,c,Spaces + 1);
			else
				SPrintf(DummyBuffer,"%02lx \33[%ldC.\33[%ldD",c,Spaces,Spaces + 1);

			ConProcessData8(DummyBuffer,strlen(DummyBuffer));

			if(Current++ == Fit)
			{
				Current = 0;

				ConProcessData8("\r\n",2);
			}
		}
	}
	else
	{
		while(Size-- > 0)
		{
			c = *String++;

			Spaces = (Fit - Current) * 3 + Current - 3;

			if(c && c != ' ')
				SPrintf(DummyBuffer,"%02lx \33[%ldC%lc\33[%ldD",c,Spaces,c,Spaces + 1);
			else
				SPrintf(DummyBuffer,"%02lx \33[%ldC.\33[%ldD",c,Spaces,Spaces + 1);

			ConProcessData8(DummyBuffer,strlen(DummyBuffer));

			if(Current++ == Fit)
			{
				Current = 0;

				ConProcessData8("\r\n",2);
			}
		}
	}
}

	/* ConTransferHost(STRPTR Buffer,LONG Len):
	 *
	 *	Process data read from the serial line,
	 *	special XPR flavour.
	 */

VOID __regargs
ConTransferHost(STRPTR Buffer,LONG Len)
{
	Len = XProtocolHostMon(XprIO,Buffer,Len,Config -> SerialConfig -> SerialBufferSize);

	if(TransferWindow)
		TransferCleanup();

	if(Len)
		ConProcess(Buffer,Len);
}

	/* ConsoleCommand(STRPTR String):
	 *
	 *	Just like SerialCommand(), but addresses the local
	 *	terminal side.
	 */

VOID __regargs
ConsoleCommand(STRPTR String)
{
	STATIC UBYTE LocalBuffer[256];

	LONG	Count = 0,i,Len = strlen(String);

	BYTE	GotControl	= FALSE,
		GotEscape	= FALSE;

		/* Scan the string. */

	for(i = 0 ; i < Len ; i++)
	{
			/* We are looking for plain characters
			 * and the control ('\') and escape
			 * ('^') characters.
			 */

		if(!GotControl && !GotEscape)
		{
				/* Got a control character,
				 * the next byte will probably be
				 * a command sequence.
				 */

			if(String[i] == '\\')
			{
				GotControl = TRUE;

				continue;
			}

				/* Got an escape character,
				 * the next byte will be some
				 * kind of control character
				 * (such as XON, XOF, bell, etc.).
				 */

			if(String[i] == '^')
			{
				GotEscape = TRUE;

				continue;
			}

				/* This tells us to wait another
				 * second before continuing with
				 * the scanning.
				 */

			if(String[i] == '~')
				continue;

				/* Stuff the character into the
				 * buffer.
				 */

			LocalBuffer[Count++] = String[i];
		}
		else
		{
				/* Convert the character to a control
				 * style character (^C, etc.).
				 */

			if(GotEscape)
			{
				if(ToUpper(String[i]) >= 'A' && ToUpper(String[i]) <= '_')
					LocalBuffer[Count++] = ToUpper(String[i]) - '@';
				else
					LocalBuffer[Count++] = String[i];

				GotEscape = FALSE;
			}

				/* The next character represents a command. */

			if(GotControl)
			{
				switch(ToUpper(String[i]))
				{
						/* Translate code. */

					case '*':

						i++;

						while(i < Len && String[i] == ' ')
							i++;

						if(i < Len)
						{
							UBYTE DummyBuffer[5],j = 0,Char;

							if(String[i] >= '0' && String[i] <= '9')
							{
								while(j < 3 && i < Len)
								{
									Char = String[i++];

									if(Char >= '0' && Char <= '9')
										DummyBuffer[j++] = Char;
									else
									{
										i--;

										break;
									}
								}
							}
							else
							{
								while(j < 4 && i < Len)
								{
									Char = ToLower(String[i++]);

									if((Char >= '0' && Char <= '9') || (Char >= 'a' && Char <= 'z'))
										DummyBuffer[j++] = Char;
									else
									{
										i--;

										break;
									}
								}
							}

							DummyBuffer[j] = 0;

							LocalBuffer[Count++] = NameToCode(DummyBuffer);
						}

						i--;

						break;

						/* Execute an AmigaDOS command. */

					case 'D':

						return;

						/* Execute an ARexx command. */

					case 'A':

						return;

						/* Add the control character ('\'). */

					case '\\':

						LocalBuffer[Count++] = '\\';
						break;

						/* This is a backspace. */

					case 'B':

						LocalBuffer[Count++] = '\b';
						break;

						/* This is a form feed. */

					case 'F':

						LocalBuffer[Count++] = '\f';
						break;

						/* This is a line feed. */

					case 'N':

						LocalBuffer[Count++] = '\n';
						break;

						/* Send the current password. */

					case 'P':

						break;

						/* This is a carriage return. */

					case 'R':

						LocalBuffer[Count++] = '\r';
						break;

						/* This is a tab. */

					case 'T':

						LocalBuffer[Count++] = '\t';
						break;

						/* Send the current user name. */

					case 'U':

						break;

						/* Send a break across the serial line. */

					case 'X':

						break;

						/* Feed the contents of the
						 * clipboard into the input
						 * stream.
						 */

					case 'I':

						if(Count)
							ConProcess(LocalBuffer,Count);

						Count = LoadClip(LocalBuffer,256);

						break;

						/* Send a string to the clipboard. */

					case 'G':

						if(String[i + 1])
							SaveClip(&String[i + 1],strlen(&String[i + 1]));

						return;

						/* Produce the escape character. */

					case 'E':

						LocalBuffer[Count++] = ESC;
						break;

						/* Call a menu item. */

					case 'C':

						break;

						/* Stuff the character into the buffer. */

					default:

						LocalBuffer[Count++] = String[i];
						break;
				}

				GotControl = FALSE;
			}
		}

			/* If the buffer is full, release it. */

		if(Count == 256)
		{
			ConProcess(LocalBuffer,Count);

			Count = 0;
		}
	}

	if(Count)
		ConProcess(LocalBuffer,Count);
}

	/* ConProcess(STRPTR String,LONG Size):
	 *
	 *	Process the contents of a string to be sent to the
	 *	console window.
	 */

VOID __regargs
ConProcess(register STRPTR String,register LONG Size)
{
		/* If the capture filter happens to be disabled, write the
		 * raw data.
		 */

	if(!Config -> CaptureConfig -> CaptureFilterMode)
		CaptureToFile(String,Size);

		/* Feed the flow filter. */

	if(UseFlow)
	{
		if(Config -> SerialConfig -> StripBit8)
			FlowFilter(String,Size,0x7F);
		else
			FlowFilter(String,Size,0xFF);
	}

		/* In quiet mode no characters are echoed to the
		 * console window, they are just passed through
		 * the data flow filter. Usually, this mode is
		 * enabled by the dial panel.
		 */

	if(!Quiet)
		(*ConProcessData)(String,Size);
}

	/* ConWrites(STRPTR String,...):
	 *
	 *	Output a string to the console.
	 */

VOID __stdargs
ConWrites(STRPTR String,...)
{
	va_list	VarArgs;

	if(Marking)
		DropMarker();

	ClearCursor();

	va_start(VarArgs,String);
	VSPrintf(SharedBuffer,String,VarArgs);
	va_end(VarArgs);

	ConProcess(SharedBuffer,strlen(SharedBuffer));

	DrawCursor();
}

	/* FlowInit():
	 *
	 *	Set up the data flow parser. The parser scans the serial
	 *	output data for more or less interesting modem output
	 *	(carrier lost, connect, etc.).
	 */

VOID
FlowInit(BYTE FullReset)
{
	WORD i;

		/* Set up `NO CARRIER' message. */

	if(Config -> ModemConfig -> NoCarrier[0])
		SPrintf(AttentionBuffers[SCAN_NOCARRIER],"%s\r",Config -> ModemConfig -> NoCarrier);
	else
		AttentionBuffers[SCAN_NOCARRIER][0] = 0;

		/* Set up ZRQINIT message. */

	strcpy(AttentionBuffers[SCAN_ZMODEM],"*\030B01");

		/* Set up `CONNECT' message. */

	strcpy(AttentionBuffers[SCAN_CONNECT],Config -> ModemConfig -> Connect);

		/* Set up `VOICE' message. */

	if(Config -> ModemConfig -> Voice[0])
		SPrintf(AttentionBuffers[SCAN_VOICE],"%s\r",Config -> ModemConfig -> Voice);
	else
		AttentionBuffers[SCAN_VOICE][0] = 0;

		/* Set up `RING' message. */

	if(Config -> ModemConfig -> Ring[0])
		SPrintf(AttentionBuffers[SCAN_RING],"%s\r",Config -> ModemConfig -> Ring);
	else
		AttentionBuffers[SCAN_RING][0] = 0;

		/* Set up `BUSY' message. */

	if(Config -> ModemConfig -> Busy[0])
		SPrintf(AttentionBuffers[SCAN_BUSY],"%s\r",Config -> ModemConfig -> Busy);
	else
		AttentionBuffers[SCAN_BUSY][0] = 0;

		/* Set up `NO DIALTONE' message. */

	if(Config -> ModemConfig -> NoDialTone[0])
		SPrintf(AttentionBuffers[SCAN_NODIALTONE],"%s\r",Config -> ModemConfig -> NoDialTone);
	else
		AttentionBuffers[SCAN_NODIALTONE][0] = 0;

		/* Set up `OK' message. */

	if(Config -> ModemConfig -> Ok[0])
		SPrintf(AttentionBuffers[SCAN_OK],"%s\r",Config -> ModemConfig -> Ok);
	else
		AttentionBuffers[SCAN_OK][0] = 0;

		/* Set up `ERROR' message. */

	if(Config -> ModemConfig -> Error[0])
		SPrintf(AttentionBuffers[SCAN_ERROR],"%s\r",Config -> ModemConfig -> Error);
	else
		AttentionBuffers[SCAN_ERROR][0] = 0;

		/* Reset match counter. */

	FlowCount = 0;

		/* Reset indices. */

	memset(AttentionCount,0,sizeof(AttentionCount));

		/* Determine lengths. */

	for(i = 0 ; i < SCAN_COUNT ; i++)
		AttentionLength[i] = strlen(AttentionBuffers[i]);

	if(FullReset)
	{
			/* No, we are not yet looking for a baud rate. */

		BaudPending = FALSE;

			/* Reset the flags. */

		memset(&FlowInfo,FALSE,sizeof(struct FlowInfo));
	}

		/* Is it safe to disable the flow filter? */

	if(!FullCheck && Online && Config -> SerialConfig -> CheckCarrier && !Config -> MiscConfig -> AutoUpload)
		UseFlow = FALSE;
	else
		UseFlow = TRUE;

		/* Full data check is a lot slower than looking for
		 * just a single sequence (such as the `CONNECT'
		 * below). This mode is reserved for the dial panel.
		 */

	if(FullCheck)
	{
		ScanStart	= 0;
		ScanEnd		= SCAN_COUNT - 1;
	}
	else
	{
		if(Online)
		{
			ScanStart = SCAN_NOCARRIER;

			if(UsesZModem)
				ScanEnd = SCAN_ZMODEM;
			else
				ScanEnd = SCAN_NOCARRIER;
		}
		else
		{
			if(UsesZModem)
				ScanStart = SCAN_ZMODEM;
			else
				ScanStart = SCAN_CONNECT;

			ScanEnd = SCAN_RING;
		}
	}
}

	/* GfxText(struct RastPort *RPort,STRPTR Buffer,LONG Length):
	 *
	 *	Text output, if necessary switching from gfx font
	 *	to current default font.
	 */

VOID __regargs
GfxText(struct RastPort *RPort,STRPTR Buffer,LONG Length)
{
	BYTE TextMode;
	LONG SameMode;

		/* Determine current text rendering mode. */

	if(GfxTable[Buffer[0]] == MODE_GFX)
		TextMode = MODE_GFX;
	else
	{
		TextMode = MODE_STANDARD;

		SetFont(RPort,TextFont);
	}

		/* Reset number of characters in common mode. */

	SameMode = 0;

		/* Scan until all input is processed. */

	FOREVER
	{
			/* Scan for characters in the current mode. */

		while(GfxTable[Buffer[SameMode]] == TextMode && SameMode < Length)
			SameMode++;

			/* Output the text found. */

		Text(RPort,Buffer,SameMode);

			/* Decrement number of remaining bytes. */

		Length -= SameMode;

			/* Anything left? */

		if(Length)
		{
				/* Skip to next character. */

			Buffer		+= SameMode;
			SameMode	 = 0;

				/* Change text output mode. */

			if(TextMode == MODE_GFX)
			{
				SetFont(RPort,TextFont);

				TextMode = MODE_STANDARD;
			}
			else
			{
				SetFont(RPort,GFX);

				TextMode = MODE_GFX;
			}
		}
		else
			break;
	}

		/* Reset font type. */

	if(TextMode == MODE_STANDARD)
		SetFont(RPort,GFX);
}

	/* ConvertKey():
	 *
	 *	The actual key conversion routine.
	 */

STATIC LONG __inline
ConvertKey(ULONG Qualifier,UWORD Code,APTR IAddress,STRPTR Buffer,LONG Len)
{
	LONG Actual;

		/* Fill in the defaults. */

	FakeInputEvent -> ie_Code		= Code;
	FakeInputEvent -> ie_Qualifier		= Qualifier;
	FakeInputEvent -> ie_position . ie_addr	= *((APTR *)IAddress);

		/* Clear the buffer (sortof). */

	Buffer[0] = 0;

		/* Convert the codes. */

	if((Actual = RawKeyConvert(FakeInputEvent,Buffer,Len,KeyMap)) > 0)
		return(Actual);
	else
		return(0);
}

	/* KeyConvert(struct IntuiMessage *Massage,STRPTR Buffer):
	 *
	 *	Convert a raw key information according to the
	 *	current keymap settings.
	 */

UBYTE __regargs
KeyConvert(struct IntuiMessage *Massage,STRPTR Buffer,LONG *Len)
{
	if(Buffer)
		Buffer[0] = 0;

	if(Len)
		*Len = 0;

		/* Key was pressed, not released. */

	if(Massage -> Class == IDCMP_RAWKEY && !(Massage -> Code & IECODE_UP_PREFIX))
	{
			/* These are the sequences mapped to special
			 * control keys (cursor keys, function keys,
			 * the help key).
			 */

		STATIC struct { STRPTR RawCode; UBYTE Result; } ConversionTable[15] =
		{
			(STRPTR)"A",	CUP,
			(STRPTR)"B",	CDN,
			(STRPTR)"C",	CFW,
			(STRPTR)"D",	CBK,

			(STRPTR)"?~",	HLP,

			(STRPTR)"0~",	FN1,
			(STRPTR)"1~",	FN2,
			(STRPTR)"2~",	FN3,
			(STRPTR)"3~",	FN4,
			(STRPTR)"4~",	FN5,
			(STRPTR)"5~",	FN6,
			(STRPTR)"6~",	FN7,
			(STRPTR)"7~",	FN8,
			(STRPTR)"8~",	FN9,
			(STRPTR)"9~",	F10
		};

		STATIC UBYTE SeqLens[15] = { 1,1,1,1,2,2,2,2,2,2,2,2,2,2,2 };

		UBYTE	ConvertBuffer[257];
		ULONG	Qualifier;
		LONG	Actual;

			/* If it's a function or cursor key, clear the qualifier. */

		if(Massage -> Code >= CURSOR_UP_CODE && Massage -> Code <= F10_CODE)
			Qualifier = NULL;
		else
		{
			Qualifier = Massage -> Qualifier;

				/* Does it have a numeric keypad qualifier set? */

			if((Qualifier & (IEQUALIFIER_NUMERICPAD | IEQUALIFIER_CONTROL)) == (IEQUALIFIER_NUMERICPAD | IEQUALIFIER_CONTROL))
			{
					/* Look at the vanilla result. */

				if(ConvertKey(Qualifier & ~(IEQUALIFIER_CONTROL | IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT | IEQUALIFIER_LALT | IEQUALIFIER_RALT),Massage -> Code,Massage -> IAddress,ConvertBuffer,1))
				{
					STRPTR String;

						/* Take a look at the results. */

					switch(ConvertBuffer[0])
					{
						case '[':

							String = "\033OP";
							break;

						case ']':

							String = "\033OQ";
							break;

						case '/':

							String = "\033OR";
							break;

						case '*':

							String = "\033OS";
							break;

						default:

							String = NULL;
							break;
					}

						/* Return the PFx key code. */

					if(String)
					{
						if(Buffer)
							memcpy(Buffer,String,3);

						if(Len)
							*Len = 3;

						return('\033');
					}
				}
			}

				/* Does it have a shift qualifier set? */

			if(Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
			{
					/* Do the conversion... */

				if(ConvertKey(Qualifier & ~(IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT),Massage -> Code,Massage -> IAddress,ConvertBuffer,1))
				{
						/* Did it produce a tab key? If so, transfer
						 * Esc tab instead.
						 */

					if(ConvertBuffer[0] == '\t')
					{
						if(Len)
							*Len = 2;

						if(Buffer)
							memcpy(Buffer,"\033\t",2);

						return('\033');
					}
				}
			}

				/* Does it have the control qualifier set? */

			if(Qualifier & IEQUALIFIER_CONTROL)
			{
					/* Do the conversion... */

				if(ConvertKey(Qualifier & ~IEQUALIFIER_CONTROL,Massage -> Code,Massage -> IAddress,ConvertBuffer,1))
				{
						/* Did it produce a space or an `at' sign? */

					if(ConvertBuffer[0] == ' ' || ConvertBuffer[0] == '@')
					{
							/* If so, produce a nul instead. */

						if(Len)
							*Len = 1;

						if(Buffer)
							Buffer[0] = 0;

						return(0);
					}
				}
			}
		}

			/* Do the final conversion... */

		if(Actual = ConvertKey(Qualifier,Massage -> Code,Massage -> IAddress,ConvertBuffer,256))
		{
				/* Did we get anything in return? */

			if(ConvertBuffer[0])
			{
					/* Are we to swap the backspace and
					 * delete keys?
					 */

				if(Config -> EmulationConfig -> SwapBSDelete)
				{
					register WORD i;

					for(i = 0 ; i < Actual ; i++)
					{
						if(ConvertBuffer[i] == BKS)
							ConvertBuffer[i] = DEL;
						else
						{
							if(ConvertBuffer[i] == DEL)
								ConvertBuffer[i] = BKS;
						}
					}
				}

					/* Translated sequence starts
					 * with a CSI, let's have a look
					 * at the associated control
					 * key.
					 */

				if(ConvertBuffer[0] == CSI)
				{
					register WORD i;

					for(i = 0 ; i < sizeof(SeqLens) ; i++)
					{
							/* Does it match? */

						if(!memcmp(&ConvertBuffer[1],ConversionTable[i] . RawCode,SeqLens[i]))
						{
								/* Store the length. */

							if(Len)
								*Len = 1;

								/* Store the result. */

							if(Buffer)
							{
								Buffer[0] = ConversionTable[i] . Result;
								Buffer[1] = 0;
							}

							return(ConversionTable[i] . Result);
						}
					}
				}

					/* Store the number of characters converted. */

				if(Len)
					*Len = Actual;

					/* Store the converted characters. */

				if(Buffer)
					memcpy(Buffer,ConvertBuffer,Actual);

				return(ConvertBuffer[0]);
			}
		}
	}

	return(0);
}

	/* DoBackspace():
	 *
	 *	Special function: perform backspace.
	 */

BYTE __regargs
DoBackspace()
{
	Capture("\b",1);

	if(CursorX)
	{
		CursorX--;

			/* If destructive, shift the remaining line
			 * one character to the right.
			 */

		if(Config -> EmulationConfig -> DestructiveBackspace)
		{
			WORD DeltaX,MinX;

			BackupRender();

			RasterEraseCharacters(1);

			if(Config -> EmulationConfig -> FontScale == SCALE_NORMAL)
			{
				if(RasterAttr[CursorY] == SCALE_ATTR_NORMAL)
				{
					DeltaX	= TextFontWidth;
					MinX	= MUL_X(CursorX);
				}
				else
				{
					DeltaX	= TextFontWidth / 2;
					MinX	= MUL_X(CursorX) / 2;
				}
			}
			else
			{
				if(RasterAttr[CursorY] == SCALE_ATTR_NORMAL)
				{
					DeltaX	= TextFontWidth / 2;
					MinX	= MUL_X(CursorX) / 2;
				}
				else
				{
					DeltaX	= TextFontWidth;
					MinX	= MUL_X(CursorX);
				}
			}

			ScrollLineEraseCharacters(1);

			ScrollLineRaster(RPort,DeltaX,0,MinX,MUL_Y(CursorY),LastPixel,MUL_Y(CursorY + 1) - 1,FALSE);

			BackupRender();
		}

		ClipBlitCursor(FALSE,TRUE);
	}

	return(FALSE);
}

	/* DoBeep():
	 *
	 *	The real interface to the beep routine.
	 */

BYTE __regargs
DoBeep()
{
		/* Handle the visual part. */

	if(Config -> TerminalConfig -> BellMode != BELL_AUDIBLE)
	{
		if(StatusProcess)
			Signal(StatusProcess,SIG_BELL);
	}

		/* Let it beep. */

	if(Config -> TerminalConfig -> BellMode == BELL_AUDIBLE || Config -> TerminalConfig -> BellMode == BELL_BOTH)
		SoundPlay(SOUND_BELL);

		/* Capture the bell. */

	if(Config -> CaptureConfig -> CaptureFilterMode == FILTER_NONE)
		Capture("\a",1);

	return(FALSE);
}

	/* DoxON():
	 *
	 *	Perform XON (stop data flow).
	 */

BYTE __regargs
DoxON()
{
	if(Config -> SerialConfig -> xONxOFF)
		Status = STATUS_HOLDING;

	return(FALSE);
}

	/* DoLF():
	 *
	 *	Special function: perform line feed.
	 */

BYTE __regargs
DoLF()
{
	if(Config -> TerminalConfig -> ReceiveLF == LF_ASLFCR)
	{
		CursorX = 0;

		DownLine();

		ClipBlitCursor(FALSE,TRUE);

		Capture("\n",1);
	}
	else
	{
		if(Config -> TerminalConfig -> ReceiveLF == LF_ASLF)
		{
			DownLine();

			ClipBlitCursor(FALSE,TRUE);
		}
	}

	return(FALSE);
}

	/* DoShiftIn():
	 *
	 *	Special function: Shift into graphics mode
	 */

BYTE __regargs
DoShiftIn()
{
	if(CharMode[1] == TABLE_GFX && GFX)
		CurrentFont = GFX;

	if(CharMode[1] == TABLE_ASCII)
		CurrentFont = TextFont;

	SetFont(RPort,CurrentFont);

	ConOutputUpdate();

	Charset = 1;

	return(FALSE);
}

	/* DoShiftOut():
	 *
	 *	Special function: Shift out of graphics mode
	 */

BYTE __regargs
DoShiftOut()
{
	if(CharMode[0] == TABLE_GFX && GFX)
		CurrentFont = GFX;

	if(CharMode[0] == TABLE_ASCII)
		CurrentFont = TextFont;

	SetFont(RPort,CurrentFont);

	ConOutputUpdate();

	Charset = 0;

	return(FALSE);
}

	/* DoCR_LF():
	 *
	 *	Special function: perform carriage return and line feed.
	 */

BYTE __regargs
DoCR_LF()
{
	CursorX = 0;

	DownLine();

	ClipBlitCursor(FALSE,TRUE);

	Capture("\n",1);

	return(FALSE);
}

	/* DoFF():
	 *
	 *	Special function: perform form feed.
	 */

BYTE __regargs
DoFF()
{
	if(Config -> EmulationConfig -> NewLineMode)
	{
		CursorX = 0;

		DoCR_LF();
	}
	else
	{
		EraseScreen("2");

		CursorX = CursorY = 0;

		ClipBlitCursor(FALSE,TRUE);

		ConFontScaleUpdate();

		Capture("\n",1);
	}

	return(FALSE);
}

	/* DoLF_FF_VT():
	 *
	 *	Special function: handle line feed, form feed and vertical
	 *	tab.
	 */

BYTE __regargs
DoLF_FF_VT()
{
	if(Config -> EmulationConfig -> NewLineMode)
		DoCR_LF();
	else
		DoLF();

	return(FALSE);
}

	/* DoCR():
	 *
	 *	Special function: handle carriage return.
	 */

BYTE __regargs
DoCR()
{
	if(Config -> EmulationConfig -> NewLineMode || Config -> TerminalConfig -> ReceiveCR == CR_ASCRLF)
		DoCR_LF();
	else
	{
		if(Config -> TerminalConfig -> ReceiveCR == CR_ASCR)
		{
			CursorX = 0;

			ClipBlitCursor(FALSE,TRUE);

			Capture("\n",1);
		}
	}

	return(FALSE);
}

	/* DoTab():
	 *
	 *	Special function: handle tab, move cursor to next
	 *	tab stop.
	 */

BYTE __regargs
DoTab()
{
	WORD Column;

	if(RasterAttr[CursorY] == SCALE_ATTR_NORMAL)
		Column = LastColumn;
	else
		Column = ((LastColumn + 1) / 2) - 1;

	if(Config -> EmulationConfig -> LineWrap)
	{
		if(CursorX >= LastColumn)
		{
			CursorX = 0;

			DownLine();
		}
		else
		{
			while(CursorX < Column)
			{
				CursorX++;

				if(TabStops[CursorX])
					break;
			}
		}
	}
	else
	{
		while(CursorX < Column)
		{
			CursorX++;

			if(TabStops[CursorX])
				break;
		}
	}

	ClipBlitCursor(FALSE,TRUE);

	Capture("\t",1);

	return(FALSE);
}

	/* DoEnq():
	 *
	 *	Special function: send answerback message.
	 */

BYTE __regargs
DoEnq()
{
	if(Config -> EmulationConfig -> AnswerBack[0])
		SerialCommand(Config -> EmulationConfig -> AnswerBack);

	return(FALSE);
}

	/* DoEsc():
	 *
	 *	Start new control sequence.
	 */

BYTE __regargs
DoEsc()
{
	if(Config -> TerminalConfig -> EmulationMode == EMULATION_TTY)
		(* ConDump)("^",1);

	return(TRUE);
}

	/* DoCsi():
	 *
	 *	Start new control sequence.
	 */

BYTE __regargs
DoCsi()
{
	if(Config -> TerminalConfig -> EmulationMode == EMULATION_TTY)
		(* ConDump)("^[",2);

	ParseCode('[');

	return(TRUE);
}

	/* DoNewEsc(UBYTE Char):
	 *
	 *	Start new control sequence.
	 */

BYTE __regargs
DoNewEsc(UBYTE Char)
{
	if(Config -> TerminalConfig -> EmulationMode == EMULATION_TTY)
		(* ConDump)("^",1);

	DoCancel();

	return(TRUE);
}

	/* DoNewCsi(UBYTE Char):
	 *
	 *	Start new control sequence.
	 */

BYTE __regargs
DoNewCsi(UBYTE Char)
{
	if(Config -> TerminalConfig -> EmulationMode == EMULATION_TTY)
		(* ConDump)("^[",2);

	DoCancel();

	return(ParseCode('['));
}
