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

#include "termGlobal.h"

	/* One of the emulation callback routines. */

typedef VOID (* __regargs EPTR)(STRPTR Buffer);

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

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

	BYTE	ExactSize;
	EPTR	Func;
};

#define NUM_CODES (sizeof(ANSICode) / sizeof(struct ControlCode))

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

	/* Global string buffer and backup style. */

STATIC UBYTE GlobalBuffer[40],StyleType = FS_NORMAL;

	/* Cursor backup data. */

STATIC struct CursorData CursorBackup;

	/* A couple of internally referenced variables. */

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

	/* Character access tables. */

STATIC UBYTE 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 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 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 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 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,	'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
};

	/* 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 __regargs
ParseCode(UBYTE c)
{
	register WORD i;

		/* ScanStep = 0:	This is the first character
		 *			to introduce a control sequence.
		 */

	if(!ScanStep)
	{
			/* Scan all available codes and try to find
			 * a match.
			 */

		for(i = 0 ; i < NUM_CODES ; 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)
		{
				/* Scan the remaining codes for a match. */

			for(i = ScanStep ; i < NUM_CODES ; 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);
}

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

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

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

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

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

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

	BackupRender();

	SetBPen(RPort,0);

	RasterScrollRegion(Direction,RegionTop,RegionBottom,RegionLines);

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

	BackupRender();
}

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

STATIC UBYTE __regargs
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 __regargs
ReadValue(STRPTR Buffer,WORD *Value)
{
	while((*Buffer < '0' || *Buffer > '9') && (*Buffer != ';') && *Buffer)
		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);
}

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

VOID __regargs
ClipBlitCursor(UBYTE DoClip,UBYTE DoMove)
{
	if(DoClip || DoMove)
	{
		STATIC WORD LastCursorX = -1,LastCursorY = -1;
		STATIC LONG DestX,DestY,XSize,X,Y,Column;

		if(CursorY != LastCursorY || CursorX != LastCursorX)
		{
			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 = Y * TextFontHeight;

				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 = X * TextFontWidth;
					XSize = TextFontWidth;
				}
				else
				{
					DestX = X * TextFontWidth * 2;
					XSize = TextFontWidth * 2;

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

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

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

		if(DoClip)
		{
			BYTE Depth = RPort -> BitMap -> Depth;

			if(Depth > 1 && !Config . ScreenConfig . FasterLayout)
			{
				STATIC	UBYTE	DepthMasks[5] = { 0,1,3,7,15 };
					UBYTE	Mask = RPort -> Mask;

				SetWrMsk(RPort,DepthMasks[Depth]);

				ClipBlit(RPort,0,0,RPort,DestX,DestY,XSize,TextFontHeight,0x50);

				SetWrMsk(RPort,Mask);
			}
			else
				ClipBlit(RPort,0,0,RPort,DestX,DestY,XSize,TextFontHeight,0x50);
		}
	}
}

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

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

		CursorEnabled = FALSE;
	}
}

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

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

		CursorEnabled = TRUE;
	}
}

	/* SetCursor():
	 *
	 *	Move the cursor to a given location.
	 */

VOID
SetCursor()
{
	ClipBlitCursor(!CursorEnabled,TRUE);

	CursorEnabled = TRUE;
}

	/* 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	CpX,CpY;
	STATIC UBYTE	Style;

	if(!Called)
	{
		DrMd	= RPort -> DrawMode;
		FgPen	= RPort -> FgPen;
		BgPen	= RPort -> BgPen;

		CpX	= RPort -> cp_x;
		CpY	= RPort -> cp_y;

		Style	= StyleType;

		Called	= TRUE;
	}
	else
	{
		if(RPort -> DrawMode != DrMd)
			SetDrMd(RPort,DrMd);

		if(RPort -> FgPen != FgPen)
			SetAPen(RPort,FgPen);

		if(RPort -> BgPen != BgPen)
			SetBPen(RPort,BgPen);

		if(RPort -> cp_x != CpX || RPort -> cp_y != CpY)
			Move(RPort,CpX,CpY);

		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 = CursorY * TextFontHeight;

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

	if(MinX < Window -> Width)
	{
		BackupRender();

		SetBPen(RPort,0);

		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 __regargs
ScrollDown(STRPTR Buffer)
{
	WORD Value;

	ReadValue(Buffer,&Value);

	if(Value < 1)
		Value = 1;

	ClearCursor();

	ScrollRegion(-Value);

	DrawCursor();
}

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

VOID __regargs
ScrollUp(STRPTR Buffer)
{
	WORD Value;

	ReadValue(Buffer,&Value);

	if(Value < 1)
		Value = 1;

	ClearCursor();

	ScrollRegion(Value);

	DrawCursor();
}

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

VOID
CursorScrollDown()
{
	ClearCursor();

	DownLine();

	SetCursor();
}

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

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

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

	ClearCursor();

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

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

	SetCursor();
}

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

VOID
NextLine()
{
	ClearCursor();

	CursorX = 0;

	DownLine();

	SetCursor();
}

	/* 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		= RPort -> FgPen;
	CursorBackup . BgPen		= RPort -> BgPen;
	CursorBackup . CurrentFont	= CurrentFont;
}

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

VOID __regargs
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()
{
	ClearCursor();

	Charset		= CursorBackup . Charset;

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

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

		SetFont(RPort,CurrentFont);
	}

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

		StyleType = CursorBackup . Style;
	}

	if(RPort -> FgPen != CursorBackup . FgPen)
		SetAPen(RPort,CursorBackup . FgPen);

	if(RPort -> BgPen != CursorBackup . BgPen)
		SetBPen(RPort,CursorBackup . BgPen);

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

	SetCursor();
}

VOID __regargs
ScaleFont(STRPTR Buffer)
{
	WORD NewScale,Scale;

	Scale = RasterAttr[CursorY];

	ClearCursor();

	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;

		if(((Config . EmulationConfig . FontScale == SCALE_NORMAL) && (NewScale == SCALE_ATTR_NORMAL)) || ((Config . EmulationConfig . FontScale == SCALE_HALF) && (NewScale == SCALE_ATTR_2X)))
		{
			Move(RPort,0,CursorY * TextFontHeight + TextFontBase);
			Text(RPort,RasterPtr,RightMargin);
		}
		else
		{
			CursorX = 0;

			PrintScaled(RasterPtr,RightMargin,NewScale);
		}

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

	SetCursor();
}

VOID
AlignmentTest()
{
	STRPTR Buffer;

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

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

		EraseScreen("2");

		ClearCursor();

		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,0,(i * TextFontHeight) + 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,0,(i * TextFontHeight) + TextFontBase);
				Text(RPort,Buffer,LastColumn + 1);
			}
		}

		CursorX = CursorY = 0;

		SetCursor();

		FreeVec(Buffer);
	}
}

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

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

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

VOID __regargs
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");
				else
					EmulationSerWrite("\033[>1;10;0c");

				break;

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

		case 'Z':	EmulationSerWrite("\033[?1;0c");
				break;

		default:	break;
	}
}

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

VOID
Reset()
{
	WORD i;

	ClearCursor();

	memset(TabStops,FALSE,1024);

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

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

	Charset = 0;

	SetAPen(RPort,0);

	SetWrMsk(RPort,0xFF);

	RectFill(RPort,0,0,Window -> Width - 1,Window -> Height - 1);

	ScrollLineEraseScreen(2);

	RasterEraseScreen(2);

	switch(Config . ScreenConfig . ColourMode)
	{
		case COLOUR_EIGHT:	FgPen = 7;
					break;

		case COLOUR_SIXTEEN:	FgPen = 15;
					break;

		case COLOUR_AMIGA:
		default:		FgPen = 1;
					break;
	}

	BgPen = 0;

	if(RPort -> FgPen != FgPen)
		SetAPen(RPort,FgPen);

	if(RPort -> BgPen != BgPen)
		SetBPen(RPort,BgPen);

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

		StyleType = FS_NORMAL;
	}

	CurrentFont = TextFont;

	SetFont(RPort,CurrentFont);

	UseRegion		= FALSE;
	RegionSet		= FALSE;

	Config . EmulationConfig . CursorMode		= KEYMODE_STANDARD;
	Config . EmulationConfig . NumericMode		= KEYMODE_STANDARD;

	Config . EmulationConfig . NewLineMode		= FALSE;
	Config . EmulationConfig . InsertMode		= FALSE;

	Config . EmulationConfig . LineWrap		= TRUE;
	Config . EmulationConfig . CursorWrap		= FALSE;

	Config . EmulationConfig . FontScale		= SCALE_NORMAL;
	Config . EmulationConfig . ScrollMode		= SCROLL_JUMP;

	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;

	SetCursor();

	ScreenSizeStuff();
}

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

VOID __regargs
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 __regargs
RequestInformation(STRPTR Buffer)
{
	WORD Value;

	ReadValue(Buffer,&Value);

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

		case 5:		EmulationSerWrite("\033[0n");
				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(GlobalBuffer,"\033[%ld;%ldR",CursorY + 1,CursorX + 1);

				EmulationSerWrite(GlobalBuffer);

				break;

				/* A VT200 command: request printer status.
				 * We will return `the printer is ready'.
				 */

		case 15:	if(Config . EmulationConfig . PrinterEnabled)
					EmulationSerWrite("\033[?10n");
				else
					EmulationSerWrite("\033[?11n");

				break;

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

		case 25:	EmulationSerWrite("\033[?21n");
				break;

				/* Another VT200 command: request
				 * keyboard language. We will return
				 * `keyboard language unknown' - does
				 * anybody know when locale.library will
				 * be released?
				 */

		case 26:	EmulationSerWrite("\033[?27;0n");
				break;

		default:	break;
	}
}

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

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

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

				case '1':	if(Buffer[3] == 'h')
							Config . EmulationConfig . CursorMode = KEYMODE_APPLICATION;
						else
							Config . EmulationConfig . CursorMode = KEYMODE_STANDARD;

						break;

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

				case '3':	ClearCursor();

						if(Buffer[3] == 'h')
						{
							if(Config . EmulationConfig . FontScale != SCALE_HALF)
							{
								PrivateConfig = Config;

								Config . EmulationConfig . FontScale = SCALE_HALF;

								ScreenSizeStuff();
							}
						}
						else
						{
							if(Config . EmulationConfig . FontScale != SCALE_NORMAL)
							{
								PrivateConfig = Config;

								Config . EmulationConfig . FontScale = SCALE_NORMAL;

								ScreenSizeStuff();
							}
						}

						CursorX = CursorY = 0;

						SetCursor();

						EraseScreen("2");

						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))
								{
									CopyMem(&Config,&PrivateConfig,sizeof(struct Configuration));

									Config . ScreenConfig . DisplayMode |= LACE;

									ResetDisplay = TRUE;
								}
							}
						}
						else
						{
							if(Config . ScreenConfig . DisplayMode & LACE)
							{
								if(!ModeNotAvailable(Config . ScreenConfig . DisplayMode & ~LACE))
								{
									CopyMem(&Config,&PrivateConfig,sizeof(struct Configuration));

									Config . ScreenConfig . DisplayMode &= ~LACE;

									ResetDisplay = TRUE;
								}
							}
						}

						break;

				default:	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 __regargs
NumericAppMode(STRPTR Buffer)
{
	if(*Buffer == '=')
		Config . EmulationConfig . NumericMode = TRUE;
	else
	{
		if(*Buffer == '>')
			Config . EmulationConfig . NumericMode = FALSE;
	}
}

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

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

	ReadValue(Buffer,&Value);

	if(Value < 1)
		Value = 1;

	ClearCursor();

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

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

				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;

		default:	break;
	}

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

	if(CursorX > LastCharPosition)
		CursorX = LastCharPosition;

	SetCursor();
}

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

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

	ReadValue(Buffer,&Value);

	BackupRender();

	SetAPen(RPort,0);

	ClearCursor();

	switch(Value)
	{
		case 1:	ScrollLineRectFill(RPort,0,CursorY * TextFontHeight,((CursorX + 1) * Width) - 1,(CursorY + 1) * TextFontHeight - 1);
			break;

		case 2:	ScrollLineRectFill(RPort,0,CursorY * TextFontHeight,LastPixel,(CursorY + 1) * TextFontHeight - 1);
			break;

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

	ScrollLineEraseLine(Value);

	DrawCursor();

	RasterEraseLine(Value);

	BackupRender();
}

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

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

	ClearCursor();

	ReadValue(Buffer,&Value);

	BackupRender();

	SetAPen(RPort,0);

	switch(Value)
	{
		case 1:	if(CursorY)
				ScrollLineRectFill(RPort,0,0,LastPixel,(CursorY * TextFontHeight) - 1);

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

			break;

		case 2:	ScrollLineRectFill(RPort,0,0,LastPixel,(LastLine + 1) * TextFontHeight - 1);
			break;

		default:ScrollLineRectFill(RPort,CursorX * Width,CursorY * TextFontHeight,LastPixel,(CursorY + 1) * TextFontHeight - 1);

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

			break;
	}

	ScrollLineEraseScreen(Value);

	RasterEraseScreen(Value);

	DrawCursor();

	BackupRender();
}

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

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

	ReadValue(Buffer,&Value);

	BackupRender();

	SetBPen(RPort,0);

	if(Value < 1)
		Value = 1;

	RasterEraseCharacters(Value);

	ScrollLineEraseCharacters(Value);

	ClearCursor();

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

	DrawCursor();

	BackupRender();
}

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

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

	ReadValue(Buffer,&Value);

	BackupRender();

	SetBPen(RPort,0);

	if(Value < 1)
		Value = 1;

	RasterShiftChar(Value);

	ScrollLineShiftChar(Value);

	ClearCursor();

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

	DrawCursor();

	BackupRender();
}

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

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

	ReadValue(Buffer,&Value);

	BackupRender();

	SetAPen(RPort,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);

	ClearCursor();

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

	DrawCursor();

	BackupRender();
}

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

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

	ReadValue(Buffer,&Value);

	BackupRender();

	SetAPen(RPort,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);

	ClearCursor();

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

	DrawCursor();

	BackupRender();
}

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

VOID __regargs
SetTabs(STRPTR Buffer)
{
	WORD Value;

	ReadValue(Buffer,&Value);

	if(Value < 1)
		Value = 0;

	switch(Value)
	{
		case 0:	if(CursorX < 1024)
				TabStops[CursorX] = FALSE;

			break;

		case 3:	memset(TabStops,FALSE,1024);

			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 __regargs
SetAbsolutePosition(STRPTR Buffer)
{
	WORD Value;

	Buffer = ReadValue(Buffer,&Value);

	ClearCursor();

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

	SetCursor();
}

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

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

	ClearCursor();

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

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

		switch(Value)
		{
			case 0:	switch(Config . ScreenConfig . ColourMode)
				{
					case COLOUR_AMIGA:
					case COLOUR_MONO:	FgPen = 1;
								break;

					case COLOUR_EIGHT:	FgPen = 7;
								break;

					case COLOUR_SIXTEEN:	FgPen = 15;
								break;
				}

				BgPen = 0;

				if(RPort -> FgPen != FgPen)
					SetAPen(RPort,FgPen);

				if(RPort -> BgPen != BgPen)
					SetBPen(RPort,BgPen);

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

	Mask = (1 << Screen -> RastPort . BitMap -> Depth) - 1;

	if(FgPen > Mask && !(FgPen & Mask))
		FgPen = 1;

	if(BgPen > Mask && !(BgPen & Mask))
		BgPen = 1;

	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:	break;

				case COLOUR_MONO:	if(Attributes & ATTR_INVERSE)
								BgPen = 1;
							else
								FgPen = 1;

							break;
			}
		}
	}

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

		StyleType = TextFlags;
	}

	if(Config . ScreenConfig . ColourMode == COLOUR_MONO)
	{
		if((FgPen & 1) == (BgPen & 1))
		{
			if(ColourValue(FgPen) < ColourValue(BgPen))
			{
				if(Attributes & ATTR_INVERSE)
				{
					FgPen = 1;
					BgPen = 0;
				}
				else
				{
					FgPen = 0;
					BgPen = 1;
				}
			}
			else
			{
				if(Attributes & ATTR_INVERSE)
				{
					FgPen = 0;
					BgPen = 1;
				}
				else
				{
					FgPen = 1;
					BgPen = 0;
				}
			}
		}
	}

	if(FgPen != RPort -> FgPen)
		SetAPen(RPort,FgPen);

	if(BgPen != RPort -> BgPen)
		SetBPen(RPort,BgPen);

	SetCursor();
}

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

VOID __regargs
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()
{
	ClearCursor();

	CursorX	= 0;

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

	SetCursor();
}
