/*
**	termEmulation.c
**
**	Terminal emulation (parsing and processing) routines
**
**	Copyright © 1990-1994 by Olaf `Olsen' Barthel
**		All Rights Reserved
*/

#include "termGlobal.h"
#include "termEmulationProcess.h"

	/* One of the emulation callback routines. */

typedef VOID (*  EPTR)(STRPTR Buffer);

	/* This structure describes an ANSI control sequence. */

struct ControlCode
{
	UBYTE	FirstChar;
	STRPTR	Match;
	UBYTE	LastChar;

	BYTE	ExactSize;
	EPTR	Func;
};

	/* How many characters we will keep in the scan buffer. */

#define MAX_SCAN_SIZE	256

	/* Flag indicating whether the cursor has already been
	 * erased or not.
	 */

STATIC BYTE		CursorEnabled = FALSE;

	/* Cursor handling data. */

STATIC WORD		LastCursorX = -1,
			LastCursorY = -1;

STATIC LONG		DestX,
			DestY,
			XSize;

STATIC BYTE		CursorGhosted = FALSE;

	/* Backup style. */

STATIC UBYTE		StyleType = FS_NORMAL;

	/* Cursor backup data. */

STATIC struct CursorData CursorBackup;

	/* A couple of internally referenced variables. */

STATIC BYTE		CharsInBuffer	= 0,
			ScanStep	= 0;
STATIC UBYTE __far	SaveBuffer[MAX_SCAN_SIZE + 1];

	/* Character access tables. */

STATIC UBYTE __far Table0[256] =
{
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};

STATIC UBYTE __far Table1[256] =
{
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};

STATIC UBYTE __far Table2[256] =
{
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};

STATIC UBYTE __far Table3[256] =
{
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};

STATIC UBYTE __far Table4[256] =
{
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	1,1,1,1,1,1,1,1,1,1,0,0,0,1,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};

	/* This follows the control code information. */

struct ControlCode ANSICode[] =
{
	/* Single Character Sequences. */

	'D',	Table0,	 0 ,	1,	(EPTR)CursorScrollDown,
	'M',	Table0,	 0 ,	1,	(EPTR)CursorScrollUp,
	'E',	Table0,	 0 ,	1,	(EPTR)NextLine,
	'7',	Table0,	 0 ,	1,	(EPTR)SaveCursor,
	'8',	Table0,	 0 ,	1,	(EPTR)LoadCursor,
	'=',	Table0,	 0 ,	1,	(EPTR)NumericAppMode,
	'>',	Table0,	 0 ,	1,	(EPTR)NumericAppMode,
	'N',	Table0,	 0 ,	1,	(EPTR)Ignore,
	'O',	Table0,	 0 ,	1,	(EPTR)Ignore,
	'H',	Table0,	 0 ,	1,	(EPTR)SetTab,
	'Z',	Table0,	 0 ,	1,	(EPTR)RequestTerminal,
	'c',	Table0,	 0 ,	1,	(EPTR)Reset,
	'<',	Table0,	 0 ,	1,	(EPTR)Ignore,
	'~',	Table0,	 0 ,	1,	(EPTR)Ignore,
	'n',	Table0,	 0 ,	1,	(EPTR)Ignore,
	'}',	Table0,	 0 ,	1,	(EPTR)Ignore,
	'o',	Table0,	 0 ,	1,	(EPTR)Ignore,
	'|',	Table0,	 0 ,	1,	(EPTR)Ignore,

	/* Double Character Sequences. */

	'[',	Table0,	's',	2,	(EPTR)SaveCursor,
	'[',	Table0,	'u',	2,	(EPTR)LoadCursor,
	'(',	Table0,	'A',	2,	(EPTR)FontStuff,
	'(',	Table0,	'B',	2,	(EPTR)FontStuff,
	'(',	Table0,	'0',	2,	(EPTR)FontStuff,
	')',	Table0,	'A',	2,	(EPTR)FontStuff,
	')',	Table0,	'B',	2,	(EPTR)FontStuff,
	')',	Table0,	'0',	2,	(EPTR)FontStuff,
	'#',	Table0,	'3',	2,	(EPTR)ScaleFont,
	'#',	Table0,	'4',	2,	(EPTR)ScaleFont,
	'#',	Table0,	'5',	2,	(EPTR)ScaleFont,
	'#',	Table0,	'6',	2,	(EPTR)ScaleFont,
	'#',	Table0,	'8',	2,	(EPTR)AlignmentTest,
	' ',	Table0,	'F',	2,	(EPTR)Ignore,
	' ',	Table0,	'G',	2,	(EPTR)Ignore,

	/* Multiple Character Sequence. */

	'[',	Table3,	'i',	0,	(EPTR)PrinterController,

	'[',	Table3,	'n',	0,	(EPTR)RequestInformation,
	'[',	Table3,	'c',	0,	(EPTR)RequestTerminal,
	'[',	Table3,	'h',	0,	(EPTR)SetSomething,
	'[',	Table3,	'l',	0,	(EPTR)SetSomething,

	'[',	Table4,	'h',	0,	(EPTR)Ignore,

	'[',	Table1,	'A',	0,	(EPTR)MoveCursor,
	'[',	Table1,	'B',	0,	(EPTR)MoveCursor,
	'[',	Table1,	'C',	0,	(EPTR)MoveCursor,
	'[',	Table1,	'D',	0,	(EPTR)MoveCursor,
	'[',	Table1,	'G',	0,	(EPTR)MoveColumn,
	'[',	Table1,	'K',	0,	(EPTR)EraseLine,
	'[',	Table1,	'J',	0,	(EPTR)EraseScreen,
	'[',	Table1,	'P',	0,	(EPTR)EraseCharacters,
	'[',	Table1,	'@',	0,	(EPTR)InsertCharacters,
	'[',	Table1,	'L',	0,	(EPTR)InsertLine,
	'[',	Table1,	'M',	0,	(EPTR)ClearLine,
	'[',	Table1,	'g',	0,	(EPTR)SetTabs,
	'[',	Table1,	'q',	0,	(EPTR)Ignore,

	'[',	Table2,	'H',	0,	(EPTR)SetAbsolutePosition,
	'[',	Table2,	'f',	0,	(EPTR)SetAbsolutePosition,
	'[',	Table2,	'm',	0,	(EPTR)SetAttributes,
	'[',	Table2,	'y',	0,	(EPTR)Ignore,
	'[',	Table2,	'r',	0,	(EPTR)SetRegion,

	'[',	Table1,	'S',	0,	(EPTR)ScrollUp,
	'[',	Table1,	'T',	0,	(EPTR)ScrollDown
};

STATIC WORD NumCodes = sizeof(ANSICode) / sizeof(struct ControlCode);

	/* GetFontWidth():
	 *
	 *	Get the font width of the current line.
	 */

STATIC WORD
GetFontWidth(VOID)
{
	if(RasterAttr[CursorY] == SCALE_ATTR_NORMAL)
	{
		if(Config -> EmulationConfig -> FontScale == SCALE_HALF)
			return((WORD)(TextFontWidth / 2));
		else
			return(TextFontWidth);
	}
	else
	{
		if(Config -> EmulationConfig -> FontScale == SCALE_HALF)
			return(TextFontWidth);
		else
			return((WORD)(TextFontWidth * 2));
	}
}

	/* ColourValue(UWORD Colour):
	 *
	 *	Calculate the value of a given colour (brightness).
	 */

STATIC WORD __inline
ColourValue(UWORD Colour)
{
	BYTE	Red,Green,Blue;
	WORD	Sum;

	Red	=  Colour >> 8;
	Green	= (Colour >> 4) & 0xF;
	Blue	=  Colour       & 0xF;

	Sum = (Red + Green + Blue) / 3;

	return(Sum);
}

	/* ScrollRegion(WORD Direction):
	 *
	 *	Scroll the current scroll region up or down.
	 */

VOID __regargs
ScrollRegion(WORD Direction)
{
	WORD RegionTop,RegionBottom,RegionLines;
	LONG Dir,MinY,MaxY;

	if(Direction < 0)
		Dir = -Direction;
	else
		Dir = Direction;

	if(RegionSet)
	{
		MinY 		= MUL_Y(Top);
		MaxY		= MUL_Y(Bottom + 1) - 1;

		RegionTop	= Top;
		RegionBottom	= Bottom + 1;
		RegionLines	= Bottom - Top + 1;
	}
	else
	{
		MinY		= 0;
		MaxY 		= MUL_Y(LastLine + 1) - 1;

		RegionTop	= 0;
		RegionBottom	= LastLine + 1;
		RegionLines	= LastLine + 1;
	}

	BackupRender();

	RasterScrollRegion(Direction,RegionTop,RegionBottom,RegionLines);

	if(Config -> EmulationConfig -> ScrollMode == SCROLL_JUMP || (TextFontHeight & 1))
	{
		if(Dir > RegionLines)
			ScrollLineRectFill(RPort,0,MinY,LastPixel,MaxY);
		else
		{
			if(Direction > 0)
				ScrollLineRaster(RPort,0,MUL_Y(Direction),0,MinY,LastPixel,MaxY,FALSE);
			else
				ScrollLineRaster(RPort,0,-MUL_Y(-Direction),0,MinY,LastPixel,MaxY,FALSE);
		}
	}
	else
	{
		if(Direction > 0)
			ScrollLineRaster(RPort,0,MUL_Y(Direction),0,MinY,LastPixel,MaxY,TRUE);
		else
			ScrollLineRaster(RPort,0,-MUL_Y(-Direction),0,MinY,LastPixel,MaxY,TRUE);
	}

	BackupRender();
}

	/* LastChar(STRPTR Buffer):
	 *
	 *	Return the last character in a string.
	 */

STATIC UBYTE __inline
LastChar(STRPTR Buffer)
{
	WORD Offset = 0;

	while(Buffer[Offset])
		Offset++;

	return(Buffer[Offset - 1]);
}

	/* ReadValue(STRPTR Buffer,BYTE *Value):
	 *
	 *	Parse a buffer for numbers and return a pointer
	 *	to the next buffer element to contain additional
	 *	information.
	 */

STATIC STRPTR __inline
ReadValue(STRPTR Buffer,WORD *Value)
{
	while(*Buffer && *Buffer != ';' && (*Buffer < '0' || *Buffer > '9'))
		Buffer++;

	if(*Buffer)
	{
		*Value = 0;

		while(*Buffer >= '0' && *Buffer <= '9')
			*Value = (*Value * 10) + (*Buffer++ - '0');
	}
	else
		*Value = -1;

	if(*Buffer == ';' || *Buffer == ' ')
		return(Buffer + 1);
	else
		return(NULL);
}

STATIC VOID __regargs
EmulationSerWrite(STRPTR String,LONG Length)
{
	if(SysBase -> ThisTask == SpecialQueue -> SigTask)
		SerWrite(String,Length);
	else
	{
		struct DataMsg *Msg;

		if(Length == -1)
			Length = strlen(String);

		if(Msg = (struct DataMsg *)CreateMsgItem(sizeof(struct DataMsg) + Length + 1))
		{
			Msg -> Type = DATAMSGTYPE_WRITE;
			Msg -> Data = (STRPTR)(Msg + 1);
			Msg -> Size = Length;

			CopyMem(String,Msg -> Data,Length + 1);

			PutMsgItem(SpecialQueue,(struct MsgItem *)Msg);
		}
	}
}

	/* DoCancel():
	 *
	 *	Cancel any currently scanned sequence.
	 */

BYTE 
DoCancel()
{
	InSequence	= FALSE;
	CharsInBuffer	= ScanStep = 0;

	return(FALSE);
}

	/* CSIFake():
	 *
	 *	This routine was added to support 8-bit control
	 *	sequences introduced by a CSI character.
	 */

VOID
CSIFake()
{
		/* Reset scanner */

	DoCancel();

		/* Perform as if ESC [ had been transmitted. */

	InSequence = ParseCode('[');
}

	/* ParseCode(UBYTE c):
	 *
	 *	Input:	A character to be passed through the ANSI code
	 *		parser.
	 *
	 *	Output:	FALSE if input characters did form a valid ANSI
	 *		control sequence or if input characters did not
	 *		form an ANSI control sequence at all.
	 *
	 *		TRUE if input characters did possibly introduce
	 *		a valid ANSI control sequence.
	 */

BYTE 
ParseCode(UBYTE c)
{
		/* ScanStep = 0:	This is the first character
		 *			to introduce a control sequence.
		 */

	if(!ScanStep)
	{
		register WORD i;

			/* Scan all available codes and try to find
			 * a match.
			 */

		for(i = 0 ; i < NumCodes ; i++)
		{
				/* This character may introduce a
				 * control sequence.
				 */

			if(ANSICode[i] . FirstChar == c)
			{
					/* If this is a single
					 * character control sequence
					 * call the approriate function
					 * and exit immediately.
					 */

				if(ANSICode[i] . ExactSize == 1)
				{
					if(Config -> TerminalConfig -> EmulationMode != EMULATION_ATOMIC)
					{
						SaveBuffer[CharsInBuffer++] = c;
						SaveBuffer[CharsInBuffer  ] = 0;

						(*ANSICode[i] . Func)(SaveBuffer);
					}

					CharsInBuffer = ScanStep = 0;

					return(FALSE);
				}
				else
				{
						/* The length of this control
						 * sequence is greater than
						 * a single character. Save
						 * the input character and
						 * return.
						 */

					ScanStep = i;

					SaveBuffer[CharsInBuffer++] = c;

					return(TRUE);
				}
			}
		}
	}
	else
	{
		if(CharsInBuffer < MAX_SCAN_SIZE)
		{
			register WORD i;

				/* Scan the remaining codes for a match. */

			for(i = ScanStep ; i < NumCodes ; i++)
			{
					/* This sequence begins with the
					 * same character the parser was
					 * initialized with, so let's take
					 * a look at it.
					 */

				if(ANSICode[i] . FirstChar == SaveBuffer[0])
				{
						/* This character is supposed to
						 * terminate the sequence, so exit.
						 */

					if(ANSICode[i] . LastChar == c)
					{
						if(Config -> TerminalConfig -> EmulationMode != EMULATION_ATOMIC)
						{
							SaveBuffer[CharsInBuffer++] = c;
							SaveBuffer[CharsInBuffer  ] = 0;

							(*ANSICode[i] . Func)(SaveBuffer);
						}

						CharsInBuffer = ScanStep = 0;

						return(FALSE);
					}
					else
					{
							/* If this character is part of
							 * a legal sequence store it
							 * and return.
							 */

						if(ANSICode[i] . Match[c])
						{
							ScanStep = i;

							SaveBuffer[CharsInBuffer++] = c;

							return(TRUE);
						}
					}
				}
			}
		}
	}

		/* Return failure. */

	CharsInBuffer = ScanStep = 0;

	return(FALSE);
}

	/* ToggleCursor():
	 *
	 *	(Re)draw the cursor image.
	 */

VOID
ToggleCursor()
{
	ObtainTerminal();

	if(Config -> TerminalConfig -> EmulationMode != EMULATION_EXTERNAL)
	{
		UWORD	OldAPen	= ReadAPen(RPort),
			OldBPen	= ReadBPen(RPort),
			OldDrMd	= ReadDrMd(RPort),

			Left	= WindowLeft + DestX,
			Top	= WindowTop + DestY;

		if(Kick30)
		{
			SetABPenDrMd(RPort,DepthMask,OldBPen,JAM1 | COMPLEMENT);

			if(UseMasking)
			{
				UBYTE Mask = RPort -> Mask;

				SetWrMsk(RPort,DepthMask);

				RectFill(RPort,Left,Top,Left + XSize - 1,Top + TextFontHeight - 1);

				SetWrMsk(RPort,Mask);
			}
			else
				RectFill(RPort,Left,Top,Left + XSize - 1,Top + TextFontHeight - 1);

			SetABPenDrMd(RPort,OldAPen,OldBPen,OldDrMd);
		}
		else
		{
			SetAPen(RPort,DepthMask);
			SetDrMd(RPort,JAM1 | COMPLEMENT);

			if(UseMasking)
			{
				UBYTE Mask = RPort -> Mask;

				SetWrMsk(RPort,DepthMask);

				RectFill(RPort,Left,Top,Left + XSize - 1,Top + TextFontHeight - 1);

				SetWrMsk(RPort,Mask);
			}
			else
				RectFill(RPort,Left,Top,Left + XSize - 1,Top + TextFontHeight - 1);

			SetAPen(RPort,OldAPen);
			SetDrMd(RPort,OldDrMd);
		}
	}

	ReleaseTerminal();
}

	/* NormalCursor():
	 *
	 *	Enable normal (filled) cursor image.
	 */

VOID
NormalCursor()
{
	ObtainTerminal();

	if(CursorGhosted)
	{
		if(CursorEnabled)
		{
			SetAfPt(RPort,(UWORD *)&Crosshatch,1);

			ToggleCursor();

			SetAfPt(RPort,NULL,0);

			ToggleCursor();
		}

		CursorGhosted = FALSE;
	}

	ReleaseTerminal();
}

	/* GhostCursor():
	 *
	 *	Enable ghosted (checkered) cursor image.
	 */

VOID
GhostCursor()
{
	ObtainTerminal();

	if(!CursorGhosted)
	{
		if(CursorEnabled)
		{
			ToggleCursor();

			SetAfPt(RPort,(UWORD *)&Crosshatch,1);

			ToggleCursor();

			SetAfPt(RPort,NULL,0);
		}

		CursorGhosted = TRUE;
	}

	ReleaseTerminal();
}

	/* ClipBlitCursor(UBYTE DoClip,UBYTE DoMove):
	 *
	 *	Change the appearance of the cursor.
	 */

VOID __regargs
ClipBlitCursor(UBYTE DoClip,UBYTE DoMove)
{
	ObtainTerminal();

	if(DoClip || DoMove)
	{
		if(CursorY != LastCursorY || CursorX != LastCursorX)
		{
			STATIC LONG X,Y,Column;

			if(CursorY != LastCursorY)
			{
				if(CursorY > LastLine)
					Y = LastLine;
				else
				{
					if(CursorY < 0)
						Y = 0;
					else
						Y = CursorY;
				}

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

				DestY = MUL_Y(Y);

				LastCursorY = CursorY;
			}

			LastCursorX = CursorX;

			if(CursorX > Column)
				X = Column;
			else
			{
				if(CursorX < 0)
					X = 0;
				else
					X = CursorX;
			}

			if(Config -> EmulationConfig -> FontScale == SCALE_NORMAL)
			{
				if(RasterAttr[Y] == SCALE_ATTR_NORMAL)
				{
					DestX = MUL_X(X);
					XSize = TextFontWidth;
				}
				else
				{
					DestX = MUL_X(X) * 2;
					XSize = TextFontWidth * 2;

					if(X > ((LastColumn + 1) / 2) - 1)
						X = ((LastColumn + 1) / 2) - 1;
				}
			}
			else
			{
				if(RasterAttr[CursorY] == SCALE_ATTR_NORMAL)
				{
					DestX = MUL_X(X) / 2;
					XSize = TextFontWidth / 2;
				}
				else
				{
					DestX = MUL_X(X);
					XSize = TextFontWidth;

					if(X > ((LastColumn + 1) / 2) - 1)
						X = ((LastColumn + 1) / 2) - 1;
				}
			}
		}

		if(DoMove)
			Move(RPort,WindowLeft + DestX,WindowTop + DestY + TextFontBase);

		if(DoClip)
		{
			if(CursorGhosted)
			{
				SetAfPt(RPort,(UWORD *)&Crosshatch,1);

				ToggleCursor();

				SetAfPt(RPort,NULL,0);
			}
			else
				ToggleCursor();
		}
	}

	ReleaseTerminal();
}

	/* ClearCursor():
	 *
	 *	Clear the cursor image.
	 */

VOID
ClearCursor()
{
	ObtainTerminal();

	if(CursorEnabled)
	{
		ClipBlitCursor(TRUE,FALSE);

		CursorEnabled = FALSE;
	}

	ReleaseTerminal();
}

	/* DrawCursor():
	 *
	 *	Explicitely (re-)draw the cursor image.
	 */

VOID
DrawCursor()
{
	ObtainTerminal();

	if(!CursorEnabled)
	{
		ClipBlitCursor(TRUE,FALSE);

		CursorEnabled = TRUE;
	}

	ReleaseTerminal();
}

	/* BackupRender():
	 *
	 *	Save current draw modes, pen and position or restore
	 *	the data.
	 */

VOID
BackupRender()
{
	STATIC BYTE	Called = FALSE;
	STATIC UBYTE	DrMd,
			FgPen,
			BgPen;
	STATIC UWORD	OldX,OldY;
	STATIC UBYTE	Style;

	if(!Called)
	{
		DrMd	= ReadDrMd(RPort);
		FgPen	= ReadAPen(RPort);
		BgPen	= ReadBPen(RPort);

		OldX	= RPort -> cp_x - WindowLeft;
		OldY	= RPort -> cp_y - WindowTop;

		Style	= StyleType;

		Called	= TRUE;
	}
	else
	{
		if(ReadDrMd(RPort) != DrMd)
			SetDrMd(RPort,DrMd);

		if(ReadAPen(RPort) != FgPen)
			SetAPen(RPort,FgPen);

		if(ReadBPen(RPort) != BgPen)
			SetBPen(RPort,BgPen);

		Move(RPort,OldX + WindowLeft,OldY + WindowTop);

		if(Style != StyleType)
		{
			SetSoftStyle(RPort,Style,0xFF);

			StyleType = Style;
		}

		Called = FALSE;
	}
}

	/* ShiftChar(LONG Size):
	 *
	 *	Simulate character insertion at the current cursor
	 *	position by shifting the whole line Size times eight pixels
	 *	to the right.
	 */

VOID __regargs
ShiftChar(LONG Size)
{
	LONG DeltaX,MinX,MinY;

	MinY = MUL_Y(CursorY);

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

	if(MinX < WindowWidth)
	{
		BackupRender();

		ScrollLineRaster(RPort,-DeltaX,0,MinX,MinY,LastPixel,MinY + TextFontHeight - 1,FALSE);

		BackupRender();
	}
}

	/* Ignore():
	 *
	 *	Do nothing, return immediately.
	 */

VOID
Ignore()
{
}

	/* ScrollDown(STRPTR Buffer):
	 *
	 *	Scroll the current region down.
	 */

VOID
ScrollDown(STRPTR Buffer)
{
	WORD Value;

	ReadValue(Buffer,&Value);

	if(Value < 1)
		Value = 1;

	ScrollRegion(-Value);
}

	/* ScrollUp(STRPTR Buffer):
	 *
	 *	Scroll the current region up.
	 */

VOID
ScrollUp(STRPTR Buffer)
{
	WORD Value;

	ReadValue(Buffer,&Value);

	if(Value < 1)
		Value = 1;

	ScrollRegion(Value);
}

	/* CursorScrollDown():
	 *
	 *	Move cursor down and scroll region if necessary.
	 */

VOID
CursorScrollDown()
{
	DownLine();

	ClipBlitCursor(FALSE,TRUE);
}

VOID
DownLine()
{
	UBYTE InRegion = TRUE;
	WORD  Hit      = LastLine;

	if(RegionSet)
	{
		if(CursorY <= Bottom)
			Hit = Bottom;
		else
			InRegion = FALSE;
	}

	if(CursorY == Hit)
	{
		if(InRegion)
			ScrollRegion(1);
	}
	else
	{
		CursorY++;

		if(CursorY > LastLine)
			CursorY = LastLine;

		ConFontScaleUpdate();
	}
}

	/* CursorScrollUp():
	 *
	 *	Move cursor up and scroll region if necessary.
	 */

VOID
CursorScrollUp()
{
	BYTE InRegion	= TRUE;
	WORD Hit	= 0;

	if(RegionSet)
	{
		if(CursorY >= Top)
			Hit = Top;
		else
			InRegion = FALSE;
	}

	if(CursorY == Hit)
	{
		if(InRegion)
			ScrollRegion(-1);
	}
	else
	{
		if(--CursorY < 0)
			CursorY = 0;

		ConFontScaleUpdate();
	}

	ClipBlitCursor(FALSE,TRUE);
}

	/* NextLine():
	 *
	 *	Do something like CR+LF.
	 */

VOID
NextLine()
{
	CursorX = 0;

	DownLine();

	ClipBlitCursor(FALSE,TRUE);
}

	/* SaveCursor():
	 *
	 *	Save cursor position and rendering attributes.
	 */

VOID
SaveCursor()
{
	CursorBackup . Charset		= Charset;
	CursorBackup . Attributes	= Attributes;
	CursorBackup . CursorX		= CursorX;
	CursorBackup . CursorY		= CursorY;
	CursorBackup . Style		= StyleType;
	CursorBackup . FgPen		= GetPenIndex(ReadAPen(RPort));
	CursorBackup . BgPen		= GetPenIndex(ReadBPen(RPort));
	CursorBackup . CurrentFont	= CurrentFont;
	CursorBackup . CharMode[0]	= CharMode[0];
	CursorBackup . CharMode[1]	= CharMode[1];
}

	/* FontStuff(STRPTR Buffer):
	 *
	 *	Set the drawing font (standard characters/line).
	 */

VOID 
FontStuff(STRPTR Buffer)
{
	BYTE Changed = FALSE;

	if(Buffer[0] == '(')
	{
		switch(LastChar(Buffer))
		{
			case 'A':
			case 'B':

				if(CharMode[0] != TABLE_ASCII && !Charset)
					Changed = TRUE;

				CharMode[0] = TABLE_ASCII;

				break;

			case '0':

				if(CharMode[0] != TABLE_GFX && !Charset)
					Changed = TRUE;

				CharMode[0] = TABLE_GFX;

				break;
		}
	}

	if(Buffer[0] == ')')
	{
		switch(LastChar(Buffer))
		{
			case 'A':
			case 'B':

				if(CharMode[1] != TABLE_ASCII && Charset == 1)
					Changed = TRUE;

				CharMode[1] = TABLE_ASCII;

				break;

			case '0':

				if(CharMode[1] != TABLE_GFX && Charset == 1)
					Changed = TRUE;

				CharMode[1] = TABLE_GFX;

				break;
		}
	}

	if(Changed)
	{
		BackupRender();

		if(Charset)
			DoShiftIn();
		else
			DoShiftOut();

		BackupRender();
	}
}

	/* LoadCursor():
	 *
	 *	Load cursor position and rendering attributes.
	 */

VOID
LoadCursor()
{
	Charset		= CursorBackup . Charset;

	CharMode[0]	= CursorBackup . CharMode[0];
	CharMode[1]	= CursorBackup . CharMode[1];

	if(CurrentFont != CursorBackup . CurrentFont)
	{
		CurrentFont = CursorBackup . CurrentFont;

		SetFont(RPort,CurrentFont);

		ConOutputUpdate();
	}

	if(StyleType != CursorBackup . Style)
	{
		SetSoftStyle(RPort,CursorBackup . Style,0xFF);

		StyleType = CursorBackup . Style;
	}

	if(ReadAPen(RPort) != MappedPens[0][CursorBackup . FgPen])
		SetAPen(RPort,MappedPens[0][CursorBackup . FgPen]);

	if(ReadBPen(RPort) != MappedPens[0][CursorBackup . BgPen])
		SetBPen(RPort,MappedPens[0][CursorBackup . BgPen]);

	Attributes	= CursorBackup . Attributes;
	CursorX		= CursorBackup . CursorX;
	CursorY		= CursorBackup . CursorY;

	ConFontScaleUpdate();

	ClipBlitCursor(FALSE,TRUE);
}

	/* ScaleFont(STRPTR Buffer):
	 *
	 *	Select a new font scale.
	 */

VOID 
ScaleFont(STRPTR Buffer)
{
	if(!Config -> EmulationConfig -> FontLocked)
	{
		WORD NewScale,Scale;

		Scale = RasterAttr[CursorY];

		NewScale = Scale;

		switch(LastChar(Buffer))
		{
			case '3':

				NewScale = SCALE_ATTR_TOP2X;

				break;

			case '4':

				NewScale = SCALE_ATTR_BOT2X;

				break;

			case '5':

				NewScale = SCALE_NORMAL;

				break;

			case '6':

				NewScale = SCALE_ATTR_2X;

				break;
		}

		if(Scale != NewScale)
		{
			UBYTE	*RasterPtr	= &Raster[CursorY * RasterWidth];
			WORD	 RightMargin	= LastColumn + 1,
				 CursorXSave	= CursorX;

			if(NewScale != SCALE_ATTR_NORMAL)
				RightMargin /= 2;

			RasterAttr[CursorY] = NewScale;

			ConFontScaleUpdate();

			if(((Config -> EmulationConfig -> FontScale == SCALE_NORMAL) && (NewScale == SCALE_ATTR_NORMAL)) || ((Config -> EmulationConfig -> FontScale == SCALE_HALF) && (NewScale == SCALE_ATTR_2X)))
			{
				Move(RPort,WindowLeft,WindowTop + MUL_Y(CursorY) + TextFontBase);
				Text(RPort,RasterPtr,RightMargin);
			}
			else
			{
				CursorX = 0;

				PrintScaled(RasterPtr,RightMargin,NewScale);
			}

			if(CursorXSave >= RightMargin)
				CursorX = RightMargin - 1;
			else
				CursorX = CursorXSave;
		}

		ClipBlitCursor(FALSE,TRUE);
	}
}

	/* AlignmentTest():
	 *
	 *	Perform screen alignment test, fill the screen with `E's.
	 */

VOID
AlignmentTest()
{
	STRPTR Buffer;

	if(Buffer = AllocVecPooled(LastColumn + 1,MEMF_ANY))
	{
		WORD i;

		memset(Buffer,'E',LastColumn + 1);

		EraseScreen("2");

		if(Config -> EmulationConfig -> FontScale == SCALE_HALF)
		{
			for(i = 0 ; i <= LastLine ; i++)
			{
				CursorX = 0;
				CursorY = i;

				RasterAttr[i] = SCALE_ATTR_NORMAL;

				RasterPutString(Buffer,LastColumn + 1);
				ScrollLinePutString(LastColumn + 1);

				Move(RPort,WindowLeft,WindowTop + MUL_Y(i) + TextFontBase);
				PrintScaled(Buffer,LastColumn + 1,SCALE_ATTR_NORMAL);
			}
		}
		else
		{
			for(i = 0 ; i <= LastLine ; i++)
			{
				CursorX = 0;
				CursorY = i;

				RasterAttr[i] = SCALE_ATTR_NORMAL;

				RasterPutString(Buffer,LastColumn + 1);
				ScrollLinePutString(LastColumn + 1);

				Move(RPort,WindowLeft,WindowTop + MUL_Y(i) + TextFontBase);
				Text(RPort,Buffer,LastColumn + 1);
			}
		}

		CursorX = CursorY = 0;

		ClipBlitCursor(FALSE,TRUE);

		FreeVecPooled(Buffer);

		ConFontScaleUpdate();
	}
}

	/* SetTab():
	 *
	 *	Set a tabulator stop at the current position.
	 */

VOID
SetTab()
{
	if(CursorX < TabStopMax)
		TabStops[CursorX] = TRUE;
}

	/* RequestTerminal(STRPTR Buffer):
	 *
	 *	Return the current terminal position.
	 */

VOID 
RequestTerminal(STRPTR Buffer)
{
	switch(Buffer[0])
	{
			/* Make ourselves known as a VT200
			 * terminal.
			 */

		case '[':

			if(Buffer[1] != '>')
				EmulationSerWrite("\033[?62;1;2;6;7;8;9c",-1);
			else
				EmulationSerWrite("\033[>1;10;0c",-1);

			break;

			/* This is an old status request type,
			 * we will return the standard `I am a
			 * VT101' sequence.
			 */

		case 'Z':

			EmulationSerWrite("\033[?1;0c",-1);
			break;
	}
}

	/* SoftReset():
	 *
	 *	Plain and simple: reset the text rendering colours, style and the
	 *	font being used. This works similar to the Reset() call which
	 *	also clears the screen.
	 */

VOID
SoftReset()
{
	ObtainTerminal();

		/* Are we running on an external emulation? */

	if(XEmulatorBase && Config -> TerminalConfig -> EmulationMode == EMULATION_EXTERNAL)
	{
		XEmulatorResetTextStyles(XEM_IO);
		XEmulatorResetCharset(XEM_IO);
	}
	else
	{
			/* Reset the text rendering styles. */

		SetAttributes("0m");

		if(!Config -> EmulationConfig -> FontLocked)
			Config -> EmulationConfig -> FontScale = SCALE_NORMAL;

		ConFontScaleUpdate();

			/* Reset the text rendering colours. */

		FgPen = GetPenIndex(SafeTextPen);
		BgPen = 0;

		if(ReadAPen(RPort) != MappedPens[0][FgPen])
			SetAPen(RPort,MappedPens[0][FgPen]);

		if(ReadBPen(RPort) != MappedPens[0][BgPen])
			SetBPen(RPort,MappedPens[0][BgPen]);

		SetWrMsk(RPort,DepthMask);

		CurrentFont = TextFont;

		SetFont(RPort,CurrentFont);

		ConOutputUpdate();
	}

	ReleaseTerminal();
}

	/* Reset():
	 *
	 *	Reset terminal to initial state.
	 */

VOID
Reset()
{
	LONG	MaxColumns,MaxLines,
		Columns,Lines,
		i;

	ObtainTerminal();

	CursorEnabled = FALSE;

	if(Window -> Flags & WFLG_WINDOWACTIVE)
		CursorGhosted = FALSE;
	else
		CursorGhosted = TRUE;

		/* Determine window inner dimensions and top/left edge offsets. */

	WindowLeft	= Window -> BorderLeft;
	WindowTop	= Window -> BorderTop;

	WindowWidth	= Window -> Width - (Window -> BorderLeft + Window -> BorderRight);
	WindowHeight	= Window -> Height - (Window -> BorderTop + Window -> BorderBottom);

	MaxColumns	= WindowWidth / TextFontWidth;
	MaxLines	= WindowHeight / TextFontHeight;

		/* Set up the new screen width. */

	if(Config -> TerminalConfig -> NumColumns < 20)
		Columns = MaxColumns;
	else
		Columns = Config -> TerminalConfig -> NumColumns;

		/* Set up the new screen height. */

	if(Config -> TerminalConfig -> NumLines < 20)
		Lines = MaxLines;
	else
		Lines = Config -> TerminalConfig -> NumLines;

		/* More columns than we will be able to display? */

	if(Columns > MaxColumns)
		Columns = MaxColumns;

		/* More lines than we will be able to display? */

	if(Lines > MaxLines)
		Lines = MaxLines;

		/* Set up the central data. */

	LastColumn	= Columns - 1;
	LastLine	= Lines - 1;
	LastPixel	= MUL_X(Columns) - 1;

	memset(TabStops,FALSE,TabStopMax);

	for(i = 8 ; i < TabStopMax ; i += 8)
		TabStops[i] = TRUE;

	CharMode[0] = TABLE_ASCII;
	CharMode[1] = TABLE_GFX;

	Charset = 0;

	SetAPen(RPort,MappedPens[0][0]);

	SetWrMsk(RPort,DepthMask);

	RectFill(RPort,WindowLeft,WindowTop,WindowLeft + WindowWidth - 1,WindowTop + WindowHeight - 1);

	ScrollLineEraseScreen(2);

	RasterEraseScreen(2);

	FgPen = GetPenIndex(SafeTextPen);
	BgPen = 0;

	if(ReadAPen(RPort) != MappedPens[0][FgPen])
		SetAPen(RPort,MappedPens[0][FgPen]);

	if(ReadBPen(RPort) != MappedPens[0][BgPen])
		SetBPen(RPort,MappedPens[0][BgPen]);

	if(StyleType != FS_NORMAL)
	{
		SetSoftStyle(RPort,FS_NORMAL,0xFF);

		StyleType = FS_NORMAL;
	}

	CurrentFont = TextFont;

	SetFont(RPort,CurrentFont);

	ConOutputUpdate();

	UseRegion = FALSE;
	RegionSet = FALSE;

	if(!Config -> EmulationConfig -> CursorLocked)
		Config -> EmulationConfig -> CursorMode = KEYMODE_STANDARD;

	if(!Config -> EmulationConfig -> KeysLocked)
		Config -> EmulationConfig -> NumericMode = KEYMODE_STANDARD;

	Config -> EmulationConfig -> NewLineMode	= FALSE;
	Config -> EmulationConfig -> InsertMode		= FALSE;

	Config -> EmulationConfig -> LineWrap		= TRUE;
	Config -> EmulationConfig -> CursorWrap		= FALSE;

	Config -> EmulationConfig -> ScrollMode		= SCROLL_JUMP;

	if(!Config -> EmulationConfig -> FontLocked)
		Config -> EmulationConfig -> FontScale = SCALE_NORMAL;

	Attributes	= 0;
	Top		= 0;
	Bottom		= LastLine;
	CursorX		= 0;
	CursorY		= 0;

	CursorBackup . Charset		= Charset;
	CursorBackup . Attributes	= Attributes;
	CursorBackup . CursorX		= CursorX;
	CursorBackup . CursorY		= CursorY;
	CursorBackup . Style		= StyleType;
	CursorBackup . FgPen		= FgPen;
	CursorBackup . BgPen		= BgPen;
	CursorBackup . CurrentFont	= CurrentFont;
	CursorBackup . CharMode[0]	= CharMode[0];
	CursorBackup . CharMode[1]	= CharMode[1];

	ConFontScaleUpdate();

	ClipBlitCursor(FALSE,TRUE);

	ReleaseTerminal();
}

	/* PrinterController(STRPTR Buffer):
	 *
	 *	Controls various screen dump and capture functions.
	 */

VOID 
PrinterController(STRPTR Buffer)
{
	if(Config -> EmulationConfig -> PrinterEnabled)
	{
		switch(Buffer[1])
		{
			case 'i':
			case '0':

				if(RegionSet)
					PrintRegion(Top,Bottom + 1);
				else
					PrintRegion(0,LastLine + 1);

				break;

			case '5':

				OpenPrinterCapture(TRUE);
				break;

			case '4':

				ClosePrinterCapture(FALSE);
				break;
		}
	}
}

	/* RequestInformation(STRPTR Buffer):
	 *
	 *	Request miscellaneous information (state & cursor position).
	 */

VOID 
RequestInformation(STRPTR Buffer)
{
	UBYTE	LocalBuffer[40];
	WORD	Value;

	ReadValue(Buffer,&Value);

	switch(Value)
	{
			/* Terminal status report, return code
			 * for `no malfunction'.
			 */

		case 5:

			EmulationSerWrite("\033[0n",-1);
			break;

			/* The origin is placed at 0/0 and the first
			 * cursor position is 1/1. We'll have to add
			 * 1 to our internal positions since our
			 * universe has been shifted one field to the
			 * left top corner.
			 */

		case 6:

			SPrintf(LocalBuffer,"\033[%ld;%ldR",CursorY + 1,CursorX + 1);

			EmulationSerWrite(LocalBuffer,-1);

			break;

			/* A VT200 command: request printer status.
			 * We will return `the printer is ready' in
			 * case the printer control commands are
			 * enabled, else return `no printer connected'.
			 */

		case 15:

			if(Config -> EmulationConfig -> PrinterEnabled)
				EmulationSerWrite("\033[?10n",-1);
			else
				EmulationSerWrite("\033[?11n",-1);

			break;

			/* VT200 command: request user defined
			 * key status. We will return `user
			 * defined keys are locked'.
			 */

		case 25:

			EmulationSerWrite("\033[?21n",-1);
			break;

			/* Another VT200 command: request
			 * keyboard language. We will return
			 * `keyboard language unknown'.
			 */

		case 26:

			EmulationSerWrite("\033[?27;0n",-1);
			break;
	}
}

	/* SetSomething(STRPTR Buffer):
	 *
	 *	Set a terminal option.
	 */

VOID 
SetSomething(STRPTR Buffer)
{
	switch(Buffer[1])
	{
		case '?':

			switch(Buffer[2])
			{
					/* Set cursor keys applications mode. */

				case '1':

					if(!Config -> EmulationConfig -> CursorLocked)
					{
						if(Buffer[3] == 'h')
							Config -> EmulationConfig -> CursorMode = KEYMODE_APPLICATION;
						else
							Config -> EmulationConfig -> CursorMode = KEYMODE_STANDARD;
					}

					break;

					/* Set line length (132 or 80). */

				case '3':

					if(!Config -> EmulationConfig -> FontLocked)
					{
						BOOLEAN TurnOn;

						if(CursorEnabled)
						{
							ClearCursor();

							TurnOn = TRUE;
						}
						else
							TurnOn = FALSE;

						if(Buffer[3] == 'h')
						{
							if(Config -> EmulationConfig -> FontScale != SCALE_HALF)
							{
								CursorX = CursorY = 0;

								ClipBlitCursor(FALSE,TRUE);

								BackupRender();

								SetAPen(RPort,MappedPens[0][0]);

								ScrollLineRectFill(RPort,0,0,LastPixel,MUL_Y(LastLine + 1) - 1);

								ScrollLineEraseScreen(2);

								RasterEraseScreen(2);

								SaveConfig(Config,PrivateConfig);

								Config -> EmulationConfig -> FontScale = SCALE_HALF;

								BackupRender();

								ScreenSizeStuff();
							}
						}
						else
						{
							if(Config -> EmulationConfig -> FontScale != SCALE_NORMAL)
							{
								CursorX = CursorY = 0;

								ClipBlitCursor(FALSE,TRUE);

								BackupRender();

								SetAPen(RPort,MappedPens[0][0]);

								ScrollLineRectFill(RPort,0,0,LastPixel,MUL_Y(LastLine + 1) - 1);

								ScrollLineEraseScreen(2);

								RasterEraseScreen(2);

								SaveConfig(Config,PrivateConfig);

								Config -> EmulationConfig -> FontScale = SCALE_NORMAL;

								BackupRender();

								ScreenSizeStuff();
							}
						}

						if(TurnOn)
							DrawCursor();
					}

					break;

					/* Set scroll mode (jump or smooth). */

				case '4':

					if(Buffer[3] == 'h')
						Config -> EmulationConfig -> ScrollMode = SCROLL_SMOOTH;
					else
						Config -> EmulationConfig -> ScrollMode = SCROLL_JUMP;

					break;

					/* Turn region on or off. */

				case '6':

					if(Buffer[3] == 'h')
						UseRegion = TRUE;
					else
						UseRegion = FALSE;

					ResetCursor();

					break;

					/* Turn character wrapping on or off. */

				case '7':

					if(Buffer[3] == 'h')
						Config -> EmulationConfig -> LineWrap = TRUE;
					else
						Config -> EmulationConfig -> LineWrap = FALSE;

					break;

					/* Set interlaced mode. */

				case '9':

					if(Buffer[3] == 'h')
					{
						if(!(Config -> ScreenConfig -> DisplayMode & LACE))
						{
							if(!ModeNotAvailable(Config -> ScreenConfig -> DisplayMode | LACE))
							{
								SaveConfig(Config,PrivateConfig);

								Config -> ScreenConfig -> DisplayMode |= LACE;

								ResetDisplay = TRUE;
							}
						}
					}
					else
					{
						if(Config -> ScreenConfig -> DisplayMode & LACE)
						{
							if(!ModeNotAvailable(Config -> ScreenConfig -> DisplayMode & ~LACE))
							{
								SaveConfig(Config,PrivateConfig);

								Config -> ScreenConfig -> DisplayMode &= ~LACE;

								ResetDisplay = TRUE;
							}
						}
					}

					break;
			}

			break;

		case '2':

				/* Set newline mode. */

			if(Buffer[2] == '0')
			{
				if(Buffer[3] == 'h')
					Config -> EmulationConfig -> NewLineMode = TRUE;
				else
					Config -> EmulationConfig -> NewLineMode = FALSE;
			}

			break;

		case '4':

				/* Set insert mode. */

			if(Buffer[2] == 'h')
				Config -> EmulationConfig -> InsertMode = TRUE;
			else
				Config -> EmulationConfig -> InsertMode = FALSE;

			break;

		case '1':

				/* Print region or screen. */

			if(Buffer[2] == '9')
			{
				if(Buffer[3] == 'l')
				{
					if(RegionSet)
						PrintRegion(Top,Bottom + 1);
					else
						PrintRegion(0,LastLine + 1);
				}

				if(Buffer[3] == 'h')
					PrintRegion(0,LastLine + 1);
			}

			break;

		default:

			break;
	}
}

	/* NumericAppMode(STRPTR Buffer):
	 *
	 *	Set the numeric pad applications mode.
	 */

VOID 
NumericAppMode(STRPTR Buffer)
{
	if(!Config -> EmulationConfig -> KeysLocked)
	{
		if(*Buffer == '=')
			Config -> EmulationConfig -> NumericMode = KEYMODE_APPLICATION;
		else
		{
			if(*Buffer == '>')
				Config -> EmulationConfig -> NumericMode = KEYMODE_STANDARD;
		}
	}
}

	/* MoveCursor(STRPTR Buffer):
	 *
	 *	Move the cursor in some direction and stop at
	 *	top/bottom/margin if necessary.
	 */

VOID 
MoveCursor(STRPTR Buffer)
{
	WORD Value,Hit,LastCharPosition;
	BYTE InRegion = TRUE;

	ReadValue(Buffer,&Value);

	if(Value < 1)
		Value = 1;

	switch(LastChar(Buffer))
	{
			/* Move cursor Up value lines */

		case 'A':

ScrollUp:		Hit = 0;

			if(RegionSet)
			{
				if(CursorY >= Top)
					Hit = Top;
				else
					InRegion = FALSE;
			}

			CursorY -= Value;

			if(CursorY < Hit)
			{
				Value = CursorY - Hit;

				CursorY = Hit;

				if(Config -> EmulationConfig -> CursorWrap && InRegion)
					ScrollRegion(Value);
			}

			ConFontScaleUpdate();

			break;

			/* Move cursor Down value lines */

		case 'B':

ScrollDown:		Hit = LastLine;

			if(RegionSet)
			{
				if(CursorY <= Bottom)
					Hit = Bottom;
				else
					InRegion = FALSE;
			}

			CursorY += Value;

			if(CursorY > Hit)
			{
				Value = CursorY - Hit;

				CursorY = Hit;

				if(Config -> EmulationConfig -> CursorWrap && InRegion)
					ScrollRegion(Value);
			}

			ConFontScaleUpdate();

			break;

			/* Move cursor Right value columns */

		case 'C':

			CursorX += Value;

			if(CursorX > LastColumn)
			{
				if(Config -> EmulationConfig -> CursorWrap)
				{
					Value	 = CursorX / (LastColumn + 1);

					CursorX	-= Value * (LastColumn + 1);

					goto ScrollDown;
				}
				else
					CursorX = LastColumn;
			}

			break;

			/* Move cursor Left value columns */

		case 'D':

			CursorX -= Value;

			if(CursorX < 0)
			{
				if(Config -> EmulationConfig -> CursorWrap)
				{
					Value	 = CursorX / (LastColumn + 1);
					CursorX	-= Value * (LastColumn + 1);
					Value	 = -Value;

					goto ScrollDown;
				}
				else
					CursorX = 0;
			}

			break;
	}

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

	if(CursorX > LastCharPosition)
		CursorX = LastCharPosition;

	ClipBlitCursor(FALSE,TRUE);
}

	/* MoveColumn(STRPTR Buffer):
	 *
	 *	Move the cursor to a certain column.
	 */

VOID 
MoveColumn(STRPTR Buffer)
{
	WORD Value,LastCharPosition;

	ReadValue(Buffer,&Value);

	if(Value < 1)
		Value = 1;

	CursorX = Value - 1;

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

	if(CursorX > LastColumn)
		CursorX = LastColumn;

	if(CursorX > LastCharPosition)
		CursorX = LastCharPosition;

	ClipBlitCursor(FALSE,TRUE);
}

	/* EraseLine(STRPTR Buffer):
	 *
	 *	Erase a line on the display.
	 */

VOID 
EraseLine(STRPTR Buffer)
{
	WORD Value,Width = GetFontWidth();

	ReadValue(Buffer,&Value);

	BackupRender();

	SetAPen(RPort,MappedPens[0][0]);

	switch(Value)
	{
		case 1:

			ScrollLineRectFill(RPort,0,MUL_Y(CursorY),((CursorX + 1) * Width) - 1,MUL_Y(CursorY + 1) - 1);
			break;

		case 2:

			ScrollLineRectFill(RPort,0,MUL_Y(CursorY),LastPixel,MUL_Y(CursorY + 1) - 1);
			break;

		default:

			ScrollLineRectFill(RPort,CursorX * Width,MUL_Y(CursorY),LastPixel,MUL_Y(CursorY + 1) - 1);
			break;
	}

	ScrollLineEraseLine(Value);

	RasterEraseLine(Value);

	BackupRender();
}

	/* EraseScreen(STRPTR Buffer):
	 *
	 *	Erase parts of the screen.
	 */

VOID 
EraseScreen(STRPTR Buffer)
{
	WORD Value,Width = GetFontWidth();

	ReadValue(Buffer,&Value);

	BackupRender();

	SetAPen(RPort,MappedPens[0][0]);

	switch(Value)
	{
		case 1:

			if(CursorY)
				ScrollLineRectFill(RPort,0,0,LastPixel,MUL_Y(CursorY) - 1);

			ScrollLineRectFill(RPort,0,MUL_Y(TextFontHeight),((CursorX + 1) * Width) - 1,MUL_Y(CursorY + 1) - 1);

			break;

		case 2:

			ScrollLineRectFill(RPort,0,0,LastPixel,MUL_Y(LastLine + 1) - 1);

			if(Config -> EmulationConfig -> CLSResetsCursor)
				CursorX = CursorY = 0;

			break;

		default:

			ScrollLineRectFill(RPort,CursorX * Width,MUL_Y(CursorY),LastPixel,MUL_Y(CursorY + 1) - 1);

			if(CursorY != LastLine)
				ScrollLineRectFill(RPort,0,MUL_Y(CursorY + 1),LastPixel,MUL_Y(LastLine + 1) - 1);

			break;
	}

	ScrollLineEraseScreen(Value);

	RasterEraseScreen(Value);

	BackupRender();
}

	/* EraseCharacters(STRPTR Buffer):
	 *
	 *	Erase a number of characters.
	 */

VOID 
EraseCharacters(STRPTR Buffer)
{
	WORD Value,Width = GetFontWidth();

	ReadValue(Buffer,&Value);

	BackupRender();

	if(Value < 1)
		Value = 1;

	RasterEraseCharacters(Value);

	ScrollLineEraseCharacters(Value);

	ScrollLineRaster(RPort,Value * Width,0,CursorX * Width,MUL_Y(CursorY),LastPixel,MUL_Y(CursorY + 1) - 1,FALSE);

	BackupRender();
}

	/* InsertCharacters(STRPTR Buffer):
	 *
	 *	Insert a number of characters.
	 */

VOID 
InsertCharacters(STRPTR Buffer)
{
	WORD Value,Width = GetFontWidth();

	ReadValue(Buffer,&Value);

	BackupRender();

	if(Value < 1)
		Value = 1;

	RasterShiftChar(Value);

	ScrollLineShiftChar(Value);

	ScrollLineRaster(RPort,-Value * Width,0,CursorX * Width,MUL_Y(CursorY),LastPixel,MUL_Y(CursorY + 1) - 1,FALSE);

	BackupRender();
}

	/* InsertLine(STRPTR Buffer):
	 *
	 *	Insert a number of lines and scroll the rest of the
	 *	display down.
	 */

VOID 
InsertLine(STRPTR Buffer)
{
	WORD Value,RegionBottom,RegionTop,TheTop = CursorY;

	ReadValue(Buffer,&Value);

	BackupRender();

	SetAPen(RPort,MappedPens[0][0]);

	if(Value < 1)
		Value = 1;

	if(RegionSet)
	{
		RegionTop	= Top;
		RegionBottom	= Bottom + 1;
	}
	else
	{
		RegionTop	= 0;
		RegionBottom	= LastLine + 1;
	}

	if(TheTop < RegionTop)
		TheTop = RegionTop;

	RasterInsertLine(Value,TheTop);

	ScrollLineRaster(RPort,0,-MUL_Y(Value),0,MUL_Y(TheTop),LastPixel,MUL_Y(RegionBottom) - 1,FALSE);

	BackupRender();
}

	/* ClearLine(STRPTR Buffer):
	 *
	 *	Clear a number of lines and scroll up the ones below it.
	 */

VOID 
ClearLine(STRPTR Buffer)
{
	WORD Value,RegionBottom,RegionTop,TheTop = CursorY;

	ReadValue(Buffer,&Value);

	BackupRender();

	SetAPen(RPort,MappedPens[0][0]);

	if(Value < 1)
		Value = 1;

	if(RegionSet)
	{
		RegionTop	= Top;
		RegionBottom	= Bottom + 1;
	}
	else
	{
		RegionTop	= 0;
		RegionBottom	= LastLine + 1;
	}

	if(TheTop < RegionTop)
		TheTop = RegionTop;

	RasterClearLine(Value,TheTop);

	ScrollLineRaster(RPort,0,MUL_Y(Value),0,MUL_Y(TheTop),LastPixel,MUL_Y(RegionBottom) - 1,FALSE);

	BackupRender();
}

	/* SetTabs(STRPTR Buffer):
	 *
	 *	Set the current tab stops.
	 */

VOID 
SetTabs(STRPTR Buffer)
{
	WORD Value;

	ReadValue(Buffer,&Value);

	if(Value < 1)
		Value = 0;

	switch(Value)
	{
		case 0:

			if(CursorX < TabStopMax)
				TabStops[CursorX] = FALSE;

			break;

		case 3:

			memset(TabStops,FALSE,TabStopMax);

			break;

		default:

			break;
	}
}

	/* SetAbsolutePosition(STRPTR Buffer):
	 *
	 *	Move the cursor to a given location on the display,
	 *	this routine ignores the current scroll region
	 *	settings.
	 */

VOID 
SetAbsolutePosition(STRPTR Buffer)
{
	WORD Value;

	Buffer = ReadValue(Buffer,&Value);

	if(UseRegion && RegionSet)
		CursorY = Top;
	else
		CursorY = 0;

	CursorX = 0;

	if(Value != -1)
	{
			/* Our raster origin is 0/0 instead of 1/1. */

		if(Value)
			Value--;

		if(UseRegion && RegionSet)
			CursorY = Top + Value;
		else
			CursorY = Value;

		if(Buffer)
		{
			ReadValue(Buffer,&Value);

			if(Value > 0)
				CursorX = Value - 1;
			else
				CursorX = 0;
		}

			/* Truncate illegal positions. */

		if(CursorX > LastColumn)
			CursorX = LastColumn;

		if(CursorY > LastLine)
			CursorY = LastLine;
	}

	ConFontScaleUpdate();

	ClipBlitCursor(FALSE,TRUE);
}

	/* SetAttributes(STRPTR Buffer):
	 *
	 *	Set the current display rendering attributes.
	 */

VOID 
SetAttributes(STRPTR Buffer)
{
	LONG TextFlags = FS_NORMAL;
	WORD Value;

	do
	{
		Buffer = ReadValue(Buffer,&Value);

		if(Value == -1)
			Value = 0;

		switch(Value)
		{
			case 0:

				FgPen = GetPenIndex(SafeTextPen);
				BgPen = 0;

				Attributes = 0;

				break;

			case 1:

				Attributes |= ATTR_HIGHLIGHT;

				break;

			case 4:

				Attributes |= ATTR_UNDERLINE;

				break;

			case 5:

				Attributes |= ATTR_BLINK;

				break;

			case 7:

				if(!(Attributes & ATTR_INVERSE))
				{
					BYTE Help;

					Help	= FgPen;
					FgPen	= BgPen;
					BgPen	= Help;
				}

				Attributes |= ATTR_INVERSE;

				break;

			default:

				if(Value >= 30)
				{
					if(Value <= 37)
					{
						if(Attributes & ATTR_INVERSE)
							BgPen = Value - 30;
						else
							FgPen = Value - 30;
					}
					else
					{
						if(Value >= 40 && Value <= 47)
						{
							if(Attributes & ATTR_INVERSE)
								FgPen = Value - 40;
							else
								BgPen = Value - 40;
						}
					}
				}

				break;
		}
	}
	while(Buffer);

		/* Make sure that the text rendered will be
		 * visible by wrapping the colour if
		 * necessary.
		 */

	if(FgPen > DepthMask && !(FgPen & DepthMask))
		FgPen = GetPenIndex(SafeTextPen);

	if(BgPen > DepthMask && !(BgPen & DepthMask))
		BgPen = GetPenIndex(SafeTextPen);

	if(Attributes & ATTR_UNDERLINE)
		TextFlags |= FSF_UNDERLINED;

	if(Attributes & ATTR_HIGHLIGHT)
	{
		if(Config -> ScreenConfig -> ColourMode == COLOUR_SIXTEEN)
		{
			if(Attributes & ATTR_INVERSE)
				BgPen |= 8;
			else
				FgPen |= 8;
		}
		else
			TextFlags |= FSF_BOLD;
	}

	if(Attributes & ATTR_BLINK)
	{
		if(Config -> TerminalConfig -> EmulationMode == EMULATION_ANSIVT100)
		{
			switch(Config -> ScreenConfig -> ColourMode)
			{
				case COLOUR_AMIGA:

					if(Attributes & ATTR_INVERSE)
						BgPen = 3;
					else
						FgPen = 3;

					break;

				case COLOUR_EIGHT:

					if(Attributes & ATTR_INVERSE)
						BgPen |= 8;
					else
						FgPen |= 8;

					break;

				case COLOUR_SIXTEEN:

					if(Screen)
					{
						if(Screen -> RastPort . BitMap -> Depth > 4)
						{
							if(Attributes & ATTR_INVERSE)
								BgPen |= 16;
							else
								FgPen |= 16;
						}
					}

					break;

				case COLOUR_MONO:

					if(Attributes & ATTR_INVERSE)
						BgPen = GetPenIndex(SafeTextPen);
					else
						FgPen = GetPenIndex(SafeTextPen);

					break;
			}
		}
	}

	if(TextFlags != StyleType)
	{
		SetSoftStyle(RPort,TextFlags,0xFF);

		StyleType = TextFlags;
	}

	if(Config -> ScreenConfig -> ColourMode == COLOUR_MONO)
	{
		if(ColourValue(MappedPens[0][FgPen]) < ColourValue(MappedPens[0][BgPen]))
		{
			if(Attributes & ATTR_INVERSE)
			{
				FgPen = GetPenIndex(SafeTextPen);
				BgPen = 0;
			}
			else
			{
				FgPen = 0;
				BgPen = GetPenIndex(SafeTextPen);
			}
		}
		else
		{
			if(Attributes & ATTR_INVERSE)
			{
				FgPen = 0;
				BgPen = GetPenIndex(SafeTextPen);
			}
			else
			{
				FgPen = GetPenIndex(SafeTextPen);
				BgPen = 0;
			}
		}
	}

	if(MappedPens[0][FgPen] != ReadAPen(RPort))
		SetAPen(RPort,MappedPens[0][FgPen]);

	if(MappedPens[0][BgPen] != ReadBPen(RPort))
		SetBPen(RPort,MappedPens[0][BgPen]);

	ClipBlitCursor(FALSE,TRUE);
}

	/* SetRegion(STRPTR Buffer):
	 *
	 *	Set the current scroll region top and bottom.
	 */

VOID 
SetRegion(STRPTR Buffer)
{
	WORD NewTop,Value,NewBottom = LastLine;

	Buffer = ReadValue(Buffer,&Value);

	if(!Value)
		Value = 1;

	if(Value > 0)
	{
		if(Buffer)
		{
			NewTop = Value - 1;

			ReadValue(Buffer,&Value);

			if(Value > 0)
				NewBottom = Value - 1;

			if(NewBottom > LastLine)
				NewBottom = LastLine;

			if(NewTop > LastLine)
				NewTop = LastLine;
		}
		else
		{
			NewTop		= 0;
			NewBottom	= LastLine;
		}
	}
	else
	{
		NewTop		= 0;
		NewBottom	= LastLine;
	}

	if(NewTop < NewBottom)
	{
		if(NewTop != 0 || NewBottom != LastLine)
		{
			Top	= NewTop;
			Bottom	= NewBottom;

			RegionSet = TRUE;
		}
		else
			UseRegion = RegionSet = FALSE;

		ResetCursor();
	}
	else
		RegionSet = FALSE;
}

	/* ResetCursor():
	 *
	 *	Reset cursor to top of screen.
	 */

VOID
ResetCursor()
{
	CursorX	= 0;

	if(UseRegion && RegionSet)
		CursorY = Top;
	else
		CursorY	= 0;

	ConFontScaleUpdate();

	ClipBlitCursor(FALSE,TRUE);
}
