/* $Revision Header * Header built automatically - do not edit! *************
 *
 *	(C) Copyright 1990 by Olaf 'Olsen' Barthel & MXM
 *
 *	Name .....: ParseRoutines.c
 *	Created ..: Monday 21-Jan-91 20:12
 *	Revision .: 0
 *
 *	Date            Author          Comment
 *	=========       ========        ====================
 *	21-Jan-91       Olsen           Created this file!
 *
 * $Revision Header ********************************************************/

#include "TermGlobal.h"

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

STATIC BYTE CursorEnabled = FALSE;

	/* Global string buffer and backup style. */

STATIC UBYTE GlobalBuffer[20],StyleType = FS_NORMAL;

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

VOID
ClipBlitCursor(UBYTE DoClip,UBYTE DoMove)
{
	ULONG DestX,DestY,XSize;

	ULONG X,Y,lc;

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

		if(Config . RasterEnabled)
		{
			lc = (RasterAttr[Y] == SCALE_ATTR_NORMAL) ? LastColumn : LastColumn >> 1;

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

			if(Config . FontScale == SCALE_NORMAL)
			{
				if(RasterAttr[Y] == SCALE_ATTR_NORMAL)
				{
					DestX = X << 3;
					XSize = 8;
				}
				else
				{
					DestX = X << 4;
					XSize = 16;

					if(X > (LastColumn >> 1))
						X = LastColumn >> 1;
				}
			}
			else
			{
				if(RasterAttr[CursorY] == SCALE_ATTR_NORMAL)
				{
					DestX = X << 2;
					XSize = 4;
				}
				else
				{
					DestX = X << 3;
					XSize = 8;

					if(X > (LastColumn >> 1))
						X = LastColumn >> 1;
				}
			}
		}
		else
		{
			lc = (Config . FontScale == SCALE_NORMAL) ? LastColumn : LastColumn >> 1;

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

			if(Config . FontScale == SCALE_NORMAL)
			{
				DestX = X << 3;

				XSize = 8;
			}
			else
			{
				DestX = X << 2;

				XSize = 4;

				if(X > (LastColumn >> 1))
					X = LastColumn >> 1;
			}
		}

		DestY = Y << 3;

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

		if(DoClip)
			ClipBlit(RPort,0,0,RPort,DestX,DestY,XSize,8,0x50);
	}
}

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

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

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

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

	return(Sum);
}

	/* 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
ShiftChar(LONG Size)
{
	LONG Dx,xMin,yMin,xMax,yMax;

	xMax = Window -> Width - 1;

	yMin = CursorY << 3;
	yMax = yMin + 7;

	if(Config . RasterEnabled)
	{
		if(Config . FontScale == SCALE_NORMAL)
		{
			if(RasterAttr[CursorY] == SCALE_ATTR_NORMAL)
			{
				Dx = Size << 3;
				xMin = CursorX << 3;
			}
			else
			{
				Dx = Size << 4;
				xMin = CursorX << 4;
			}
		}
		else
		{
			if(RasterAttr[CursorY] == SCALE_ATTR_NORMAL)
			{
				Dx = Size << 2;
				xMin = CursorX << 2;
			}
			else
			{
				Dx = Size << 3;
				xMin = CursorX << 3;
			}
		}
	}
	else
	{
		Dx = Size << 3;
		xMin = CursorX << 3;
	}

	BackupRender();

	SetBPen(RPort,0);

	ScrollRaster(RPort,-Dx,0,xMin,yMin,xMax,yMax);

	BackupRender();
}

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

VOID
ScrollRegion(WORD Direction)
{
	WORD RegionTop, RegionBottom, RegionLines;
	LONG Dir,Dy,yMin,xMax,yMax;

	Dir = Direction < 0 ? -Direction : Direction;

	if(RegionSet)
	{
		yMin 		= Top * 8;
		yMax		= (Bottom + 1) * 8 - 1;

		RegionTop	= Top;
		RegionBottom	= Bottom;
		RegionLines	= Bottom - Top;
	}
	else
	{
		yMin		= 0;
		yMax 		= (LastLine + 1) * 8 - 1;

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

	xMax = Window -> Width - 1;

	BackupRender();

	SetBPen(RPort,0);

	if(Config . RasterEnabled)
		RasterScrollRegion(Direction,RegionTop,RegionBottom,RegionLines);

	if(Dir > RegionLines)
	{
		/* All that is needed is to delete the lines
		 * note: not too brilliant for smooth scroll
		 */

		RectFill(RPort,0,yMin,xMax,yMax);

	}
	else
	{
		if(Config . JumpScroll)
		{
			Dy = Direction * 8;

			ScrollRaster(RPort,0,Dy,0,yMin,xMax,yMax);
		}
		else
		{
			WORD i, j= Dir * 4;

			if(Direction < 0)
				Dy = -2;
			else
				Dy = 2;

			for(i = 0 ; i < j ; i++)
			{
				WaitTOF();

				ScrollRaster(RPort,0,Dy,0,yMin,xMax,yMax);
			}
		}
	}

	BackupRender();
}

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

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

	while(Buffer[Offset])
		Offset++;

	return(Buffer[Offset - 1]);
}

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

STATIC UBYTE *
ReadValue(UBYTE *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);
}

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

UBYTE *
Ignore()
{
	return(NULL);
}

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

UBYTE *
CursorScrollDown()
{
	ClearCursor();

	DownLine();

	SetCursor();

	return(NULL);
}

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

UBYTE *
CursorScrollUp()
{
	WORD Hit = 0;
	UBYTE InRegion = TRUE;

	ClearCursor();

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

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

		if(CursorY < 0)
			CursorY = 0;
	}

	SetCursor();

	return(NULL);
}

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

UBYTE *
NextLine()
{

	ClearCursor();

	CursorX = 0;

	DownLine();

	SetCursor();

	return(NULL);
}

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

UBYTE *
SaveCursor()
{
	CursorBackup . Charset		= Charset;
	CursorBackup . Attributes	= Attributes;
	CursorBackup . UseRegion	= UseRegion;
	CursorBackup . RegionSet	= RegionSet;
	CursorBackup . Top		= Top;
	CursorBackup . Bottom		= Bottom;
	CursorBackup . CursorX		= CursorX;
	CursorBackup . CursorY		= CursorY;

	return(NULL);
}

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

UBYTE *
FontStuff(UBYTE *Buffer)
{
	switch(LastChar(Buffer))
	{
		case 'A':
		case 'B':	if(Config . Font == FONT_IBM && IBM)
					CurrentFont = IBM;
				else
					CurrentFont = Topaz;

				SetFont(RPort,CurrentFont);

				break;

		default:	break;
	}

	return(NULL);
}

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

UBYTE *
LoadCursor()
{
	LONG TextFlags = FS_NORMAL;

	ClearCursor();

	if(CursorBackup . Attributes & ATTR_UNDERLINE)
		TextFlags |= FSF_UNDERLINED;

	if((CursorBackup . Attributes & ATTR_HIGHLIGHT) && Config . ColourMode != COLOUR_SIXTEEN)
		TextFlags |= FSF_BOLD;

	if(CursorBackup . Attributes & ATTR_BLINK)
	{
		if(Config . Emulation == EMULATION_ANSIVT100)
		{
			switch(Config . ColourMode)
			{
				case COLOUR_AMIGA:	if(CursorBackup . Attributes & ATTR_INVERSE)
								BgPen = 3;
							else
								FgPen = 3;

							break;

				case COLOUR_EIGHT:	if(CursorBackup . Attributes & ATTR_INVERSE)
								BgPen |= 8;
							else
								FgPen |= 8;

							break;

				case COLOUR_SIXTEEN:	break;
			}
		}
	}

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

		StyleType = TextFlags;
	}

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

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

	Attributes	= CursorBackup . Attributes;
	UseRegion	= CursorBackup . UseRegion;
	RegionSet	= CursorBackup . RegionSet;
	Top		= CursorBackup . Top;
	Bottom		= CursorBackup . Bottom;
	CursorX		= CursorBackup . CursorX;
	CursorY		= CursorBackup . CursorY;

	SetCursor();

	return(NULL);
}

UBYTE *
ScaleFont(UBYTE *Buffer)
{
	WORD NewScale,Scale;
	WORD CursorXSave;
	UBYTE *RasterPtr;

	Scale = RasterAttr[CursorY];

	ClearCursor();

	if(Config . RasterEnabled)
		NewScale = Scale;
	else
		NewScale = Config . FontScale;

	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)
	{
		UWORD i;

		CursorXSave = CursorX;

		i = LastColumn + 1;

		if(NewScale != SCALE_ATTR_NORMAL)
			i >>= 1;

		if(Config . RasterEnabled)
		{
			RasterAttr[CursorY] = NewScale;

			RasterPtr = &Raster[CursorY * RasterWidth];

			if(((Config . FontScale == SCALE_NORMAL) && (NewScale == SCALE_ATTR_NORMAL)) || ((Config . FontScale == SCALE_HALF) && (NewScale == SCALE_ATTR_2X)))
			{
				Move(RPort,0,(CursorY << 3) + 6);
				Text(RPort,RasterPtr,i);
			}
			else
			{
				CursorX = 0;
				PrintScaled(RasterPtr,i,NewScale);
			}
		}

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

	SetCursor();

	return(NULL);
}

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

UBYTE *
SetTab()
{
	if(CursorX < 1024)
		TabStops[CursorX] = TRUE;

	return(NULL);
}

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

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

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

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

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

		default:	return(NULL);
	}
}

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

UBYTE *
Reset()
{
	WORD i;

	ClearCursor();

	memset(&TabStops[0],FALSE,1024);

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

	SetRast(RPort,0);

	if(Config . RasterEnabled)
		RasterEraseScreen(2);

	switch(Config . 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;
	}

	if(Config . Font == FONT_IBM && IBM)
		CurrentFont = IBM;
	else
		CurrentFont = Topaz;
	SetFont(RPort,CurrentFont);

	if(Config . EightyColumns)
		LastColumn = 79;
	else
		LastColumn = (Window -> Width >> 3) - 1;

	UseRegion		= FALSE;
	RegionSet		= FALSE;
	Config . AutoWrap	= TRUE;
	Config . NewLine	= FALSE;
	Config . InsertChar	= FALSE;
	Config . CursorApp	= FALSE;
	Config . NumApp		= FALSE;
	Config . FontScale	= SCALE_NORMAL;
	Config . JumpScroll	= TRUE;

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

	CursorBackup . Charset		= Charset;
	CursorBackup . Attributes	= Attributes;
	CursorBackup . UseRegion	= UseRegion;
	CursorBackup . RegionSet	= RegionSet;
	CursorBackup . Top		= Top;
	CursorBackup . Bottom		= Bottom;
	CursorBackup . CursorX		= CursorX;
	CursorBackup . CursorY		= CursorY;

	SetCursor();

	return(NULL);
}

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

UBYTE *
RequestInformation(UBYTE *Buffer)
{
	WORD Value;

	ReadValue(Buffer,&Value);

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

		case 5:		return("\033[0n");

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

				/* A VT200 command: request printer status.
				 * We will return `no printer connected'.
				 */

		case 15:	return("\033[?13n");

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

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

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

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

		default:	return(NULL);
	}
}

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

UBYTE *
SetSomething(UBYTE *Buffer)
{
	if(Buffer[1] == '?')
	{
		switch(Buffer[2])
		{
			case '1':	if(Buffer[3] == 'h')
						Config . CursorApp = TRUE;
					else
						Config . CursorApp = FALSE;

					return(NULL);

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

							if(Config . EightyColumns)
								LastColumn = 131;
							else
								LastColumn = (Window -> Width >> 2) - 1;
						}
					}
					else
					{
						if(Config . FontScale != SCALE_NORMAL)
						{
							Config . FontScale = SCALE_NORMAL;

							if(Config . EightyColumns)
								LastColumn = 79;
							else
								LastColumn = (Window -> Width >> 3) - 1;
						}
					}

					EraseScreen("2");

					ClearCursor();

					CursorX = 0;
					CursorY = 0;

					SetCursor();

					return(NULL);

			case '4':	if(Buffer[3] == 'h')
						Config . JumpScroll = FALSE;
					else
						Config . JumpScroll = TRUE;

					break;

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

					ResetCursor();

					return(NULL);

			case '7':	if(Buffer[3] == 'h')
						Config . AutoWrap = TRUE;
					else
						Config . AutoWrap = FALSE;

					return(NULL);

			case '9':	if(Buffer[3] == 'h')
					{
						if(!(Config . DisplayMode & LACE))
						{
							CopyMem(&Config,&PrivateConfig,sizeof(struct Configuration));

							Config . DisplayMode |= LACE;

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

							Config . DisplayMode &= ~LACE;

							ResetDisplay = TRUE;
						}
					}

					return(NULL);

			default:	return(NULL);
		}
	}
	else
	{
		if(Buffer[1] == '2' && Buffer[2] == '0')
		{
			if(Buffer[3] == 'h')
				Config . NewLine = TRUE;
			else
				Config . NewLine = FALSE;
		}
		else
		{
			if(Buffer[1] == '4')
			{
				if(Buffer[2] == 'h')
					Config . InsertChar = TRUE;
				else
					Config . InsertChar = FALSE;
			}
		}
	}

	return(NULL);
}

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

UBYTE *
NumericAppMode(UBYTE *Buffer)
{
	if(*Buffer == '=')
		Config . NumApp = TRUE;
	else
	{
		if(*Buffer == '>')
			Config . NumApp = FALSE;
	}

	return(NULL);
}

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

UBYTE *
MoveCursor(UBYTE *Buffer)
{
	WORD Value;
	WORD Hit,lc;
	UBYTE 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 . 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 . CursorWrap && InRegion)
						ScrollRegion(Value);
				}

				break;

		/*
		 *	Move cursor Right value columns
		 */

		case 'C':	CursorX += Value;

				if(CursorX > LastColumn)
				{
					if(Config . CursorWrap)
					{
						Value = CursorX / LastColumn;

						CursorX -= Value * LastColumn;

						goto ScrollDown;
					}
					else
						CursorX = LastColumn;
				}

				break;

		/*
		 *	Move cursor Left value columns
		 */

		case 'D':	CursorX -= Value;

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

						goto ScrollDown;
					}
					else
						CursorX = 0;
				}

				break;

		default:	break;
	}

	if(Config . RasterEnabled)
		lc = (RasterAttr[CursorY] == SCALE_ATTR_NORMAL) ? LastColumn : LastColumn >> 1;
	else
		lc = (Config . FontScale == SCALE_NORMAL) ? LastColumn : LastColumn >> 1;

	if(CursorX > lc)
		CursorX = lc;

	SetCursor();

	return(NULL);
}

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

UBYTE *
EraseLine(UBYTE *Buffer)
{
	WORD Value;

	ReadValue(Buffer,&Value);

	BackupRender();

	SetAPen(RPort,0);

	if(Config . RasterEnabled)
		RasterEraseLine(Value);

	ClearCursor();

	switch(Value)
	{
		case 1:	RectFill(RPort,0,CursorY * 8,(CursorX + 1) * 8 - 1,(CursorY + 1) * 8 - 1);
			break;

		case 2:	RectFill(RPort,0,CursorY * 8,Window -> Width - 1,(CursorY + 1) * 8 - 1);
			break;

		default:RectFill(RPort,CursorX * 8,CursorY * 8,Window -> Width - 1,(CursorY + 1) * 8 - 1);
			break;
	}

	DrawCursor();

	BackupRender();

	return(NULL);
}

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

UBYTE *
EraseScreen(UBYTE *Buffer)
{
	WORD Value;

	ClearCursor();

	ReadValue(Buffer,&Value);

	BackupRender();

	SetAPen(RPort,0);

	if(Config . RasterEnabled)
		RasterEraseScreen(Value);

	switch(Value)
	{
		case 1:	RectFill(RPort,0,0,Window -> Width - 1,(CursorY + 1) * 8 - 1);
			break;

		case 2:	RectFill(RPort,0,0,Window -> Width - 1,(LastLine + 1) * 8 - 1);
			break;

		default:RectFill(RPort,0,CursorY * 8,Window -> Width - 1,(LastLine + 1) * 8 - 1);
			break;
	}

	DrawCursor();

	BackupRender();

	return(NULL);
}

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

UBYTE *
EraseCharacters(UBYTE *Buffer)
{
	WORD Value;

	ReadValue(Buffer,&Value);

	BackupRender();

	SetBPen(RPort,0);

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

	if(Value > 0)
	{
		if(Config . RasterEnabled)
			RasterEraseCharacters(Value);

		ClearCursor();

		ScrollRaster(RPort,8 * Value,0,CursorX * 8,CursorY * 8,Window -> Width - 1,(LastLine + 1) * 8 - 1);

		DrawCursor();
	}

	BackupRender();

	return(NULL);
}

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

UBYTE *
InsertLine(UBYTE *Buffer)
{
	WORD Value;

	ReadValue(Buffer,&Value);

	BackupRender();

	SetAPen(RPort,0);

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

	if(Value > 0)
	{
		if(Config . RasterEnabled)
			RasterInsertLine(Value);

		ClearCursor();

		ScrollRaster(RPort,0,-8 * Value,0,CursorY * 8,Window -> Width - 1,(LastLine + 1) * 8 - 1);

		DrawCursor();
	}

	BackupRender();

	return(NULL);
}

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

UBYTE *
ClearLine(UBYTE *Buffer)
{
	WORD Value;

	ReadValue(Buffer,&Value);

	BackupRender();

	SetAPen(RPort,0);

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

	if(Value > 0)
	{
		if(Config . RasterEnabled)
			RasterClearLine(Value);

		ClearCursor();

		ScrollRaster(RPort,0,Value * 8,0,CursorY * 8,Window -> Width - 1,(LastLine + 1) * 8 - 1);

		DrawCursor();
	}

	BackupRender();

	return(NULL);
}

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

UBYTE *
SetTabs(UBYTE *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[0],FALSE,1024);

			break;

		default:break;
	}

	return(NULL);
}

	/* SetPosition(UBYTE *Buffer):
	 *
	 *	Move the cursor to a given location on the display.
	 */

UBYTE *
SetPosition(UBYTE *Buffer)
{
	WORD Value;

	Buffer = ReadValue(Buffer,&Value);

	ClearCursor();

	CursorX = 0;
	CursorY = 0;

	if(Value == -1)
	{
		if(UseRegion)
			CursorY = Top;
	}
	else
	{
			/* Our raster origin is 0/0 instead of 1/1. */

		if(Value)
			Value--;

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

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

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

		}

			/* Truncate illegal positions (this took almost
			 * two months to fix!).
			 */

		if(CursorX > LastColumn)
			CursorX = LastColumn;

		if(RegionSet)
		{
			if(CursorY > Bottom)
				CursorY = Bottom;
		}
		else
		{
			if(CursorY > LastLine)
				CursorY = LastLine;
		}
	}

	SetCursor();

	return(NULL);
}

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

UBYTE *
SetAttributes(UBYTE *Buffer)
{
	LONG TextFlags = FS_NORMAL;
	WORD Value;

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

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

		switch(Value)
		{
			case 0:
				if((Attributes & ATTR_HIGHLIGHT) && Config . ColourMode == COLOUR_SIXTEEN)
				{
					if(Attributes & ATTR_INVERSE)
						BgPen &= ~8;
					else
						FgPen &= ~8;
				}

				if(Attributes & ATTR_BLINK)
				{
					switch(Config . ColourMode)
					{
						case COLOUR_AMIGA:
						case COLOUR_MONO:	if(Attributes & ATTR_INVERSE)
										BgPen = 1;
									else
										FgPen = 1;

									break;

						case COLOUR_EIGHT:	if(Attributes & ATTR_INVERSE)
										BgPen &= ~8;
									else
										FgPen &= ~8;

									break;

						default:		break;
					}
				}

				if(Attributes & ATTR_INVERSE)
				{
					BYTE Help;

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

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

	if(Attributes & ATTR_UNDERLINE)
		TextFlags |= FSF_UNDERLINED;

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

	if(Attributes & ATTR_BLINK)
	{
		if(Config . Emulation == EMULATION_ANSIVT100)
		{
			switch(Config . 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 . ColourMode == COLOUR_MONO)
	{
		if(ColourValue(FgPen) < ColourValue(BgPen))
		{
			FgPen = 0;
			BgPen = 1;
		}
		else
		{
			FgPen = 1;
			BgPen = 0;
		}
	}

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

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

	return(NULL);
}

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

UBYTE *
SetRegion(UBYTE *Buffer)
{
	WORD Value;

	WORD NewTop = 0;
	WORD NewBottom = LastLine;

	Buffer = ReadValue(Buffer,&Value);

	if(Value != -1)
	{
		NewTop = Value;

		if(Value)
			NewTop--;

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

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

	if(NewTop < NewBottom)
	{
		RegionSet = TRUE;

		if(NewTop == 0 && (NewBottom == LastLine || NewBottom == 24))
			RegionSet = FALSE;

		Top	= NewTop;
		Bottom	= NewBottom;

		ResetCursor();
	}

	return(NULL);
}

/*
 *	Reset cursor to top of screen
 */

VOID
ResetCursor()
{
	ClearCursor();

	CursorX	= 0;

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

	SetCursor();
}
