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

#include "termGlobal.h"

	/* 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,
			CursorInvisible	= FALSE;

	/* Cursor handling data. */

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

STATIC LONG		DestX,
			DestY,
			XSize,
			FontByColumn;

STATIC BYTE		CursorGhosted = FALSE;

	/* Backup style. */

STATIC UBYTE		StyleType = FS_NORMAL;

	/* Cursor backup data. */

STATIC struct CursorData	CursorBackup;
STATIC BYTE			CursorBackupValid;

	/* A couple of internally referenced variables. */

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

STATIC BYTE		PrintFormFeed	= FALSE,
			PrintFullScreen	= FALSE;

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

	/* UpdatePens(VOID):
	 *
	 *	Update colour and style.
	 */

VOID
UpdatePens()
{
	UWORD	ForePen,
		BackPen,
		Attrs,
		TextFlags = 0;

		// Convert the colours and the text attributes

	ForePen	= PenTable[ForegroundPen];
	BackPen	= PenTable[BackgroundPen];
	Attrs	= TextAttributeTable[Attributes];

		// Choose a sensible colour

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

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

		// Take care of the text attributes

	if(Attrs & ATTR_UNDERLINE)
		TextFlags |= FSF_UNDERLINED;

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

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

				ForePen = 3;

				break;

			case COLOUR_EIGHT:

				ForePen |= 8;

				break;

			case COLOUR_SIXTEEN:

				if(Screen && DepthMask > 15)
					ForePen |= 16;

				break;

			case COLOUR_MONO:

				ForePen = GetPenIndex(SafeTextPen);

				break;
		}
	}

		// Make sure that monochrome text renders properly

	if(Config -> ScreenConfig -> ColourMode == COLOUR_MONO)
	{
			// Out of bounds?

		if(ForePen > 1)
		{
				// If it's #7 then it's white, else it's black

			if(ForePen == 7)
				ForePen = GetPenIndex(SafeTextPen);
			else
				ForePen = 0;
		}

		if(BackPen > 1)
		{
			if(BackPen == 7)
				BackPen = GetPenIndex(SafeTextPen);
			else
				BackPen = 0;
		}

			// Oops... the text should be readable

		if(ForePen == BackPen)
		{
				// Inverse video?

			if(BackPen)
			{
				ForePen = 0;
				BackPen = GetPenIndex(SafeTextPen);
			}
			else
			{
				ForePen = GetPenIndex(SafeTextPen);
				BackPen = 0;
			}
		}
	}

	if(Attrs & ATTR_INVERSE)
	{
		UWORD Help;

		Help	= ForePen;
		ForePen	= BackPen;
		BackPen	= Help;
	}

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

		StyleType = TextFlags;
	}

	FgPen = MappedPens[0][ForePen];
	BgPen = MappedPens[0][BackPen];

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

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

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

	/* RethinkRasterLimit():
	 *
	 *	Take care of the extreme left column position
	 *	permitted.
	 */

VOID
RethinkRasterLimit()
{
	register LONG Y;

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

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

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

	/* EmulationSerWrite(STRPTR String,LONG Length):
	 *
	 *	Write text to the serial line.
	 */

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

	/* RethinkCursorPosition():
	 *
	 *	Calculate the new cursor position.
	 */

STATIC VOID
RethinkCursorPosition(VOID)
{
	if(CursorY != LastCursorY || CursorX != LastCursorX)
	{
		STATIC LONG X,Y;

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

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

			DestY = MUL_Y(Y);

			LastCursorY = CursorY;
		}

		LastCursorX = CursorX;

		if(CursorX > FontByColumn)
			X = FontByColumn;
		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;
			}
		}
	}
}

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

STATIC VOID
ToggleCursor(VOID)
{
	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;

				SetMask(RPort,DepthMask);

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

				SetMask(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;

				SetMask(RPort,DepthMask);

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

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

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

	/* RedrawCursor():
	 *
	 *	Change the appearance of the cursor.
	 */

STATIC VOID
RedrawCursor(VOID)
{
	ObtainSemaphore(&TerminalSemaphore);

	RethinkCursorPosition();

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

		ToggleCursor();

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

	ReleaseSemaphore(&TerminalSemaphore);
}

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

						/* Where to stop. */

					Arnie = ANSICode[i] . Terminator;

					return(TRUE);
				}
			}
		}
	}
	else
	{
		if(CharsInBuffer < MAX_SCAN_SIZE)
		{
			if(Arnie)
			{
				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(Arnie[c])
						{
							if(Config -> TerminalConfig -> EmulationMode != EMULATION_ATOMIC)
							{
								SaveBuffer[CharsInBuffer++] = c;
								SaveBuffer[CharsInBuffer  ] = 0;

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

							CharsInBuffer = ScanStep = 0;

							Arnie = NULL;

							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);
							}
						}
					}
				}
			}
			else
			{
				register WORD i;

				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;

	Arnie = NULL;

	return(FALSE);
}

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

VOID
NormalCursor()
{
	ObtainSemaphore(&TerminalSemaphore);

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

			ToggleCursor();

			SetAfPt(RPort,NULL,0);

			ToggleCursor();
		}

		CursorGhosted = FALSE;
	}

	ReleaseSemaphore(&TerminalSemaphore);
}

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

VOID
GhostCursor()
{
	ObtainSemaphore(&TerminalSemaphore);

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

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

			ToggleCursor();

			SetAfPt(RPort,NULL,0);
		}

		CursorGhosted = TRUE;
	}

	ReleaseSemaphore(&TerminalSemaphore);
}

	/* RepositionCursor():
	 *
	 *	Redraw the cursor at the new position.
	 */

VOID
RepositionCursor()
{
	ObtainSemaphore(&TerminalSemaphore);

	RethinkCursorPosition();

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

	ReleaseSemaphore(&TerminalSemaphore);
}

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

VOID
ClearCursor()
{
	ObtainSemaphore(&TerminalSemaphore);

	if(CursorEnabled && !CursorInvisible)
	{
		RedrawCursor();

		CursorEnabled = FALSE;
	}

	ReleaseSemaphore(&TerminalSemaphore);
}

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

VOID
DrawCursor()
{
	ObtainSemaphore(&TerminalSemaphore);

	if(!CursorEnabled && !CursorInvisible)
	{
		RedrawCursor();

		CursorEnabled = TRUE;
	}

	ReleaseSemaphore(&TerminalSemaphore);
}

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

	RepositionCursor();
}

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

	RepositionCursor();
}

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

VOID
NextLine()
{
	CursorX = 0;

	DownLine();

	RepositionCursor();
}

	/* 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		= ForegroundPen;
	CursorBackup . BgPen		= BackgroundPen;
	CursorBackup . CurrentFont	= CurrentFont;
	CursorBackup . CharMode[0]	= CharMode[0];
	CursorBackup . CharMode[1]	= CharMode[1];

	CursorBackupValid = TRUE;
}

	/* 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();
	}

	ForegroundPen	= CursorBackup . FgPen;
	BackgroundPen	= CursorBackup . BgPen;
	Attributes	= CursorBackup . Attributes;
	CursorX		= CursorBackup . CursorX;
	CursorY		= CursorBackup . CursorY;

	UpdatePens();

	ConFontScaleUpdate();

	RepositionCursor();
}

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

			RethinkRasterLimit();

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

		RepositionCursor();
	}
}

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

		RethinkRasterLimit();

		RepositionCursor();

		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()
{
	ObtainSemaphore(&TerminalSemaphore);

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

		ForegroundPen = GetPenIndex(SafeTextPen);
		BackgroundPen = PenTable[0];

		UpdatePens();

		SetMask(RPort,DepthMask);

		CurrentFont = TextFont;

		SetFont(RPort,CurrentFont);

		ConOutputUpdate();

		CursorBackupValid = FALSE;
	}

	ReleaseSemaphore(&TerminalSemaphore);
}

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

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

	ObtainSemaphore(&TerminalSemaphore);

	CursorEnabled = CursorInvisible = FALSE;

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

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

	UpdateTerminalLimits();

	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][PenTable[0]]);

	SetMask(RPort,DepthMask);

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

	ScrollLineEraseScreen(2);

	RasterEraseScreen(2);

	if(!Config -> EmulationConfig -> LockColour)
	{
		ForegroundPen = GetPenIndex(SafeTextPen);
		BackgroundPen = 0;
	}

	if(StyleType != FS_NORMAL && !Config -> EmulationConfig -> LockStyle)
		StyleType = FS_NORMAL;

	UpdatePens();

	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 -> ScrollMode		= SCROLL_JUMP;

	if(!Config -> EmulationConfig -> LockWrapping)
		Config -> EmulationConfig -> LineWrap = TRUE;

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

	if(!Config -> EmulationConfig -> LockStyle)
		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		= ForegroundPen;
	CursorBackup . BgPen		= BackgroundPen;
	CursorBackup . CurrentFont	= CurrentFont;
	CursorBackup . CharMode[0]	= CharMode[0];
	CursorBackup . CharMode[1]	= CharMode[1];

	CursorBackupValid = FALSE;

	ConFontScaleUpdate();

	RepositionCursor();

	ReleaseSemaphore(&TerminalSemaphore);
}

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

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

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

				break;

			case '5':	// Turn on printer controller mode

				OpenPrinterCapture(TRUE);
				break;

			case '4':	// Turn off printer controller mode

				ClosePrinterCapture(FALSE);
				break;

			case '?':

				if(Buffer[2] == '1')	// Print current line
					PrintRegion(CursorY,CursorY + 1,FALSE);

				if(Buffer[2] == '5')	// Turn on auto print mode
					OpenPrinterCapture(FALSE);

				if(Buffer[2] == '4')	// Turn off auto print mode
					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)
{
	WORD	Value;
	UBYTE	Last;
	BOOLEAN	TurnOn;

	ReadValue(Buffer,&Value);
	Last = LastChar(Buffer);

	if(Buffer[1] == '?')
	{
		switch(Value)
		{
				/* Set cursor keys applications mode. */

			case 1:		// DECCKM

				if(!Config -> EmulationConfig -> CursorLocked)
				{
					if(Last == 'h')
						Config -> EmulationConfig -> CursorMode = KEYMODE_APPLICATION;

					if(Last == 'l')
						Config -> EmulationConfig -> CursorMode = KEYMODE_STANDARD;
				}

				break;

				/* ANSI/VT52 mode */

			case 2:		// DECANM

				break;

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

			case 3:		// DECCOLM

				if(!Config -> EmulationConfig -> FontLocked)
				{
					if(CursorEnabled)
					{
						ClearCursor();

						TurnOn = TRUE;
					}
					else
						TurnOn = FALSE;

					if(Last == 'h')
					{
						if(Config -> EmulationConfig -> FontScale != SCALE_HALF)
						{
							CursorX = CursorY = 0;

							RepositionCursor();

							BackupRender();

							SetAPen(RPort,MappedPens[0][PenTable[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();
						}
					}

					if(Last == 'l')
					{
						if(Config -> EmulationConfig -> FontScale != SCALE_NORMAL)
						{
							CursorX = CursorY = 0;

							RepositionCursor();

							BackupRender();

							SetAPen(RPort,MappedPens[0][PenTable[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:		// DECSCLM

				if(Last == 'h')
					Config -> EmulationConfig -> ScrollMode = SCROLL_SMOOTH;

				if(Last == 'l')
					Config -> EmulationConfig -> ScrollMode = SCROLL_JUMP;

				break;

				/* Reverse/normal screen. */

			case 5:		// DECSCNM

				break;

				/* Turn region on or off. */

			case 6:

				if(Last == 'h')
					UseRegion = TRUE;

				if(Last == 'l')
					UseRegion = FALSE;

				break;

				/* Turn character wrapping on or off. */

			case 7:		// DECAWM

				if(!Config -> EmulationConfig -> LockWrapping)
				{
					if(Last == 'h')
						Config -> EmulationConfig -> LineWrap = TRUE;

					if(Last == 'l')
						Config -> EmulationConfig -> LineWrap = FALSE;
				}

				break;

				/* Turn auto repeat on or off. */

			case 8:		// DECARM

				break;

				/* Set 240/480 line mode. */

			case 9:

				break;

				/* Print form feed after `print screen command'. */

			case 18:	// DECPFF

				if(Last == 'h')
					PrintFormFeed = TRUE;

				if(Last == 'l')
					PrintFormFeed = FALSE;

				break;

				/* Print full screen or just region. */

			case 19:	// DECPEX

				if(Last == 'h')
					PrintFullScreen = TRUE;

				if(Last == 'l')
					PrintFullScreen = FALSE;

				break;

				/* Text cursor enable. */

			case 25:	// DECTCEM

				TurnOn = CursorEnabled;

				ClearCursor();

				if(Last == 'h')
					CursorInvisible = FALSE;

				if(Last == 'l')
					CursorInvisible = TRUE;

				if(TurnOn)
					DrawCursor();

				break;

				/* National/multinational character set. */

			case 42:	// DECNRCM

				break;
		}
	}
	else
	{
		switch(Value)
		{
				/* Keyboard action unlocked/locked. */

			case 2:		// KAM

				break;

				/* Insertion/replacement. */

			case 4:		// IRM

				if(Last == 'h')
					Config -> EmulationConfig -> InsertMode = TRUE;

				if(Last == 'l')
					Config -> EmulationConfig -> InsertMode = FALSE;

				break;

				/* Echo on/off. */

			case 12:	// SRM

				if(Last == 'h')
					Config -> SerialConfig -> Duplex = DUPLEX_FULL;

				if(Last == 'l')
					Config -> SerialConfig -> Duplex = DUPLEX_HALF;

				break;

				/* Line feed/new line. */

			case 20:	// LNM

				if(Last == 'h')
					Config -> EmulationConfig -> NewLineMode = TRUE;

				if(Last == 'l')
					Config -> EmulationConfig -> NewLineMode = FALSE;

				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;

	RepositionCursor();
}

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

	RepositionCursor();
}

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

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

	if(*Buffer == '?')
		Buffer++;

	ReadValue(Buffer,&Value);

	BackupRender();

	SetAPen(RPort,MappedPens[0][PenTable[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();

	if(*Buffer == '?')
		Buffer++;

	ReadValue(Buffer,&Value);

	BackupRender();

	SetAPen(RPort,MappedPens[0][PenTable[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();

	if(*Buffer == '?')
		Buffer++;

	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][PenTable[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][PenTable[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);

	CursorY = 0;
	CursorX = 0;

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

		if(Value)
			Value--;

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

	RepositionCursor();
}

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

VOID
SetTopPosition(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();

	RepositionCursor();
}

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

VOID
SetAttributes(STRPTR Buffer)
{
	WORD Value;

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

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

		switch(Value)
		{
			case 0:

				if(!Config -> EmulationConfig -> LockStyle)
				{
					ForegroundPen = GetPenIndex(SafeTextPen);
					BackgroundPen = 0;

					Attributes = 0;
				}

				break;

			case 1:

				if(!Config -> EmulationConfig -> LockStyle)
					Attributes |= ATTR_HIGHLIGHT;

				break;

			case 4:

				if(!Config -> EmulationConfig -> LockStyle)
					Attributes |= ATTR_UNDERLINE;

				break;

			case 5:

				if(!Config -> EmulationConfig -> LockStyle)
					Attributes |= ATTR_BLINK;

				break;

			case 7:

				if(!Config -> EmulationConfig -> LockStyle)
					Attributes |= ATTR_INVERSE;

				break;

			case 22:

				if(!Config -> EmulationConfig -> LockStyle)
					Attributes &= ~ATTR_HIGHLIGHT;

				break;

			case 24:

				if(!Config -> EmulationConfig -> LockStyle)
					Attributes &= ~ATTR_UNDERLINE;

				break;

			case 25:

				if(!Config -> EmulationConfig -> LockStyle)
					Attributes &= ~ATTR_BLINK;

				break;

			case 27:

				if(!Config -> EmulationConfig -> LockStyle)
					Attributes &= ~ATTR_INVERSE;

				break;

			default:

				if(!Config -> EmulationConfig -> LockColour)
				{
					if(Value >= 30)
					{
						if(Value <= 37)
							ForegroundPen = Value - 30;
						else
						{
							if(Value >= 40 && Value <= 47)
								BackgroundPen = Value - 40;
						}
					}
				}

				break;
		}
	}
	while(Buffer);

	UpdatePens();

	RepositionCursor();
}

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

	RepositionCursor();
}
