/*****************************************************************************
* VIRTRSTR.C - implementation of black and white virtual raster device.	     *
*									     *
* This program implements a virtual raster device of any size up to memory   *
* limit.								     *
*									     *
* Written by:  Gershon Elber			IBM PC Ver 1.0,	Jan. 1990    *
*****************************************************************************/

#include <stdio.h>
#include <string.h>
#include <math.h>

#ifdef __MSDOS__
#include <dos.h>
#include <alloc.h>
#else
char *malloc(unsigned int size);
#endif /* __MSDOS__ */

#include "virtrstr.h"

#define ABS(x)	((x) > 0 ? (x) : (-(x)))
#define INT_SWAP(x, y)	{ int TempI; TempI = x; x = y; y = TempI; }

/* Given an angle and a radius, uses the tables below to map the cos/sin: */
#define MAP_COS(Angle, Radius) ((int) ((CosTable[Angle] * \
				       ((long) Radius) + 16384L) >> 15));
#define MAP_SIN(Angle, Radius) ((int) ((SinTable[Angle] * \
				       ((long) Radius) + 16384L) >> 15));

/* Sines and cosines of integer angles in the range of 0 to 45 degrees,      */
/* multiplied by 32767 (2^15 - 1).					     */
static int SinTable[] = {
         0,   571,  1143,  1714,  2285,  2855,  3425,  3993,  4560,  5125,
      5689,  6252,  6812,  7370,  7927,  8480,  9031,  9580, 10125, 10667,
     11206, 11742, 12274, 12803, 13327, 13847, 14364, 14875, 15383, 15885,
     16383, 16876, 17363, 17846, 18323, 18794, 19259, 19719, 20173, 20620,
     21062, 21497, 21925, 22347, 22761, 23169
};

static int CosTable[] = {
     32767, 32762, 32747, 32722, 32687, 32642, 32587, 32522, 32448, 32363,
     32269, 32164, 32050, 31927, 31793, 31650, 31497, 31335, 31163, 30981,
     30790, 30590, 30381, 30162, 29934, 29696, 29450, 29195, 28931, 28658,
     28377, 28086, 27787, 27480, 27165, 26841, 26509, 26168, 25820, 25464,
     25100, 24729, 24350, 23964, 23570, 23169
};

/*****************************************************************************
* Driver Local Data Variables						     *
*****************************************************************************/
#ifdef __MSDOS__
static char far **FrameBuffer = NULL;	    /* Virtual screen is saved here. */
static char far *AllocBlock = NULL;
#else
static char **FrameBuffer = NULL;
static char *AllocBlock = NULL;
#endif /* __MSDOS__ */

static int FrameBufferX = 0, FrameBufferY = 0;
static int BytesPerLine = 0;

static TextOrientationType TextOrientation = TEXT_ORIENT_HORIZ;
static int TextScale = 1;
static TextHorizJustifyType TextXCenter = TEXT_X_CENTER;
static TextVertJustifyType TextYCenter = TEXT_Y_CENTER;

static int CursorX = 0, CursorY = 0;		 /* Current Cursor position. */

static int CrntColor = 1;		/* Default is to DRAW, 0 will ERASE. */

/*****************************************************************************
* Ascii 8 by 8 regular font - only first 128 characters are supported.	     *
*****************************************************************************/
static unsigned char AsciiTable[][8] = {
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* Ascii 0  */
    { 0x3c, 0x42, 0xa5, 0x81, 0xbd, 0x42, 0x3c, 0x00 }, /* Ascii 1  */
    { 0x3c, 0x7e, 0xdb, 0xff, 0xc3, 0x7e, 0x3c, 0x00 }, /* Ascii 2  */
    { 0x00, 0xee, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00 }, /* Ascii 3  */
    { 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00 }, /* Ascii 4  */
    { 0x00, 0x3c, 0x18, 0xff, 0xff, 0x08, 0x18, 0x00 }, /* Ascii 5  */
    { 0x10, 0x38, 0x7c, 0xfe, 0xfe, 0x10, 0x38, 0x00 }, /* Ascii 6  */
    { 0x00, 0x00, 0x18, 0x3c, 0x18, 0x00, 0x00, 0x00 }, /* Ascii 7  */
    { 0xff, 0xff, 0xe7, 0xc3, 0xe7, 0xff, 0xff, 0xff }, /* Ascii 8  */
    { 0x00, 0x3c, 0x42, 0x81, 0x81, 0x42, 0x3c, 0x00 }, /* Ascii 9  */
    { 0xff, 0xc3, 0xbd, 0x7e, 0x7e, 0xbd, 0xc3, 0xff }, /* Ascii 10 */
    { 0x1f, 0x07, 0x0d, 0x7c, 0xc6, 0xc6, 0x7c, 0x00 }, /* Ascii 11 */
    { 0x00, 0x7e, 0xc3, 0xc3, 0x7e, 0x18, 0x7e, 0x18 }, /* Ascii 12 */
    { 0x04, 0x06, 0x07, 0x04, 0x04, 0xfc, 0xf8, 0x00 }, /* Ascii 13 */
    { 0x0c, 0x0a, 0x0d, 0x0b, 0xf9, 0xf9, 0x1f, 0x1f }, /* Ascii 14 */
    { 0x00, 0x92, 0x7c, 0x44, 0xc6, 0x7c, 0x92, 0x00 }, /* Ascii 15 */
    { 0x00, 0x00, 0x60, 0x78, 0x7e, 0x78, 0x60, 0x00 }, /* Ascii 16 */
    { 0x00, 0x00, 0x06, 0x1e, 0x7e, 0x1e, 0x06, 0x00 }, /* Ascii 17 */
    { 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x18 }, /* Ascii 18 */
    { 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00 }, /* Ascii 19 */
    { 0xff, 0xb6, 0x76, 0x36, 0x36, 0x36, 0x36, 0x00 }, /* Ascii 20 */
    { 0x7e, 0xc1, 0xdc, 0x22, 0x22, 0x1f, 0x83, 0x7e }, /* Ascii 21 */
    { 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x00, 0x00, 0x00 }, /* Ascii 22 */
    { 0x18, 0x7e, 0x18, 0x18, 0x7e, 0x18, 0x00, 0xff }, /* Ascii 23 */
    { 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00 }, /* Ascii 24 */
    { 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x00 }, /* Ascii 25 */
    { 0x00, 0x04, 0x06, 0xff, 0x06, 0x04, 0x00, 0x00 }, /* Ascii 26 */
    { 0x00, 0x20, 0x60, 0xff, 0x60, 0x20, 0x00, 0x00 }, /* Ascii 27 */
    { 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xff, 0x00 }, /* Ascii 28 */
    { 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00 }, /* Ascii 29 */
    { 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x00, 0x00 }, /* Ascii 30 */
    { 0x00, 0x00, 0x00, 0xfe, 0x7c, 0x38, 0x10, 0x00 }, /* Ascii 31 */
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /*   */
    { 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x00 }, /* ! */
    { 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* " */
    { 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00 }, /* # */
    { 0x10, 0x7c, 0xd2, 0x7c, 0x86, 0x7c, 0x10, 0x00 }, /* $ */
    { 0xf0, 0x96, 0xfc, 0x18, 0x3e, 0x72, 0xde, 0x00 }, /* % */
    { 0x30, 0x48, 0x30, 0x78, 0xce, 0xcc, 0x78, 0x00 }, /* & */
    { 0x0c, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* ' */
    { 0x10, 0x60, 0xc0, 0xc0, 0xc0, 0x60, 0x10, 0x00 }, /* ( */
    { 0x10, 0x0c, 0x06, 0x06, 0x06, 0x0c, 0x10, 0x00 }, /* ) */
    { 0x00, 0x54, 0x38, 0xfe, 0x38, 0x54, 0x00, 0x00 }, /* * */
    { 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00 }, /* + */
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x70 }, /* , */
    { 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00 }, /* - */
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00 }, /* . */
    { 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x00 }, /* / */
    { 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00 }, /* 0x */
    { 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x3c, 0x00 }, /* 1 */
    { 0x7c, 0xc6, 0x06, 0x0c, 0x30, 0x60, 0xfe, 0x00 }, /* 2 */
    { 0x7c, 0xc6, 0x06, 0x3c, 0x06, 0xc6, 0x7c, 0x00 }, /* 3 */
    { 0x0e, 0x1e, 0x36, 0x66, 0xfe, 0x06, 0x06, 0x00 }, /* 4 */
    { 0xfe, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0xfc, 0x00 }, /* 5 */
    { 0x7c, 0xc6, 0xc0, 0xfc, 0xc6, 0xc6, 0x7c, 0x00 }, /* 6 */
    { 0xfe, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x60, 0x00 }, /* 7 */
    { 0x7c, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0x7c, 0x00 }, /* 8 */
    { 0x7c, 0xc6, 0xc6, 0x7e, 0x06, 0xc6, 0x7c, 0x00 }, /* 9 */
    { 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00 }, /* : */
    { 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x20, 0x00 }, /* }, */
    { 0x00, 0x1c, 0x30, 0x60, 0x30, 0x1c, 0x00, 0x00 }, /* < */
    { 0x00, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00 }, /* = */
    { 0x00, 0x70, 0x18, 0x0c, 0x18, 0x70, 0x00, 0x00 }, /* > */
    { 0x7c, 0xc6, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00 }, /* ? */
    { 0x7c, 0x82, 0x9a, 0xaa, 0xaa, 0x9e, 0x7c, 0x00 }, /* @ */
    { 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00 }, /* A */
    { 0xfc, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xfc, 0x00 }, /* B */
    { 0x7c, 0xc6, 0xc6, 0xc0, 0xc0, 0xc6, 0x7c, 0x00 }, /* C */
    { 0xf8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0xf8, 0x00 }, /* D */
    { 0xfe, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xfe, 0x00 }, /* E */
    { 0xfe, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0x00 }, /* F */
    { 0x7c, 0xc6, 0xc0, 0xce, 0xc6, 0xc6, 0x7e, 0x00 }, /* G */
    { 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00 }, /* H */
    { 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00 }, /* I */
    { 0x1e, 0x06, 0x06, 0x06, 0xc6, 0xc6, 0x7c, 0x00 }, /* J */
    { 0xc6, 0xcc, 0xd8, 0xf0, 0xd8, 0xcc, 0xc6, 0x00 }, /* K */
    { 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0x00 }, /* L */
    { 0xc6, 0xee, 0xfe, 0xd6, 0xc6, 0xc6, 0xc6, 0x00 }, /* M */
    { 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00 }, /* N */
    { 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00 }, /* O */
    { 0xfc, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, 0xc0, 0x00 }, /* P */
    { 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x06 }, /* Q */
    { 0xfc, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xc6, 0x00 }, /* R */
    { 0x78, 0xcc, 0x60, 0x30, 0x18, 0xcc, 0x78, 0x00 }, /* S */
    { 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00 }, /* T */
    { 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00 }, /* U */
    { 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00 }, /* V */
    { 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00 }, /* W */
    { 0xc6, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0xc6, 0x00 }, /* X */
    { 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00 }, /* Y */
    { 0xfe, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xfe, 0x00 }, /* Z */
    { 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00 }, /* [ */
    { 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x00 }, /* \ */
    { 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00 }, /* ] */
    { 0x00, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00 }, /* ^ */
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff }, /* _ */
    { 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* ` */
    { 0x00, 0x00, 0x7c, 0x06, 0x7e, 0xc6, 0x7e, 0x00 }, /* a */
    { 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xe6, 0xdc, 0x00 }, /* b */
    { 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0x7e, 0x00 }, /* c */
    { 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xce, 0x76, 0x00 }, /* d */
    { 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0x7e, 0x00 }, /* e */
    { 0x1e, 0x30, 0x7c, 0x30, 0x30, 0x30, 0x30, 0x00 }, /* f */
    { 0x00, 0x00, 0x7e, 0xc6, 0xce, 0x76, 0x06, 0x7c }, /* g */
    { 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x00 }, /*  */
    { 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00 }, /* i */
    { 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0xf0 }, /* j */
    { 0xc0, 0xc0, 0xcc, 0xd8, 0xf0, 0xd8, 0xcc, 0x00 }, /* k */
    { 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00 }, /* l */
    { 0x00, 0x00, 0xcc, 0xfe, 0xd6, 0xc6, 0xc6, 0x00 }, /* m */
    { 0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x00 }, /* n */
    { 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00 }, /* o */
    { 0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xe6, 0xdc, 0xc0 }, /* p */
    { 0x00, 0x00, 0x7e, 0xc6, 0xc6, 0xce, 0x76, 0x06 }, /* q */
    { 0x00, 0x00, 0x6e, 0x70, 0x60, 0x60, 0x60, 0x00 }, /* r */
    { 0x00, 0x00, 0x7c, 0xc0, 0x7c, 0x06, 0xfc, 0x00 }, /* s */
    { 0x30, 0x30, 0x7c, 0x30, 0x30, 0x30, 0x1c, 0x00 }, /* t */
    { 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x00 }, /* u */
    { 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00 }, /* v */
    { 0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xfe, 0x6c, 0x00 }, /* w */
    { 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00 }, /* x */
    { 0x00, 0x00, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0x7c }, /* y */
    { 0x00, 0x00, 0xfc, 0x18, 0x30, 0x60, 0xfc, 0x00 }, /* z */
    { 0x0e, 0x18, 0x18, 0x70, 0x18, 0x18, 0x0e, 0x00 }, /* { */
    { 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00 }, /* | */
    { 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00 }, /* } */
    { 0x00, 0x00, 0x70, 0x9a, 0x0e, 0x00, 0x00, 0x00 }, /* ~ */
    { 0x00, 0x00, 0x18, 0x3c, 0x66, 0xff, 0x00, 0x00 } /* Ascii 127 */
};

static void VirtArcAux(int XCenter, int YCenter, int Radius, int Octant,
						int StartAngle, int EndAngle);
static void VirtArcAuxX(int XCenter, int YCenter, int Radius,
			int XStart, int YStart, int XEnd, int YEnd);
static void VirtArcAuxY(int XCenter, int YCenter, int Radius,
			int XStart, int YStart, int XEnd, int YEnd);

/*****************************************************************************
* Virtual driver init routine - allocate memory and clear it. If allocation  *
* is succesful return TRUE, FALSE otherwise.				     *
*****************************************************************************/
int VirtInit(int x, int y)
{
    int LineOffset, Segment, i;

    FrameBufferX = x;
    FrameBufferY = ((y + 7) >> 3) << 3 ;

    /*   The offset from one line to next in paragraphs: first convert to    */
    /* bytes by dividing by 8 and then to paragraphs by dividing by 16.      */
    /* Finally fix the offset if division clipped off remainder...	     */
    LineOffset = FrameBufferY >> 7;
    while ((LineOffset << 7) < FrameBufferY)
	LineOffset++;
    BytesPerLine = LineOffset << 4;   /* So how many bytes per line we have? */

    /* Allocate a block of FrameBufferX pointer (for each line) and one big  */
    /* block for the whole frame buffer, which will be split to lines.       */
#ifdef __MSDOS__
    if ((FrameBuffer = (char far **) farmalloc(FrameBufferX *
					       sizeof(char far *))) == NULL)
	return FALSE;
    if ((AllocBlock = farmalloc(((long) FrameBufferX) *
				((long) LineOffset) * 16)) == NULL)
    {
	farfree((char far *) FrameBuffer);
	return FALSE;
    }
#else
    if ((FrameBuffer = (char **) malloc(FrameBufferX *
					      sizeof(char *))) == NULL)
	return FALSE;
    if ((AllocBlock = (char *) malloc(FrameBufferX * BytesPerLine)) == NULL)
    {
	free((char *) FrameBuffer);
	return FALSE;
    }
#endif /* __MSDOS__ */

    /*   The allocated block is split here to lines so we can access the     */
    /* screen faster, and mainly easier.				     */
#ifdef __MSDOS__
    Segment = FP_SEG(AllocBlock);

    for (i = 0; i < FrameBufferX; i++)
	FrameBuffer[i] = MK_FP(Segment + LineOffset * i, 0x0008);
#else
    for (i = 0; i < FrameBufferX; i++)
	FrameBuffer[i] = AllocBlock + BytesPerLine * i;
#endif /* __MSDOS__ */

    VirtClear();
    return TRUE;
}

/*****************************************************************************
* Clear all bytes in the virtual raster array to zero.			     *
*****************************************************************************/
void VirtClear(void)
{
    int i;

    for (i = 0; i < FrameBufferX; i++)
	memset(FrameBuffer[i], 0, BytesPerLine);
}

/*****************************************************************************
* Free memory allocated.						     *
*****************************************************************************/
void VirtClose(void)
{
#ifdef __MSDOS__
    farfree(AllocBlock);
    farfree((char far **) FrameBuffer);
#else
    free((char *) AllocBlock);
    free((char *) FrameBuffer);
#endif /* __MSDOS__ */

    FrameBuffer = NULL;
}

/*****************************************************************************
* Set Cursor current position.						     *
*****************************************************************************/
void VirtMoveTo(int x, int y)
{
    CursorX = x;
    CursorY = y;
}

/*****************************************************************************
* Set Cursor current position.						     *
*****************************************************************************/
void VirtMoveRelTo(int x, int y)
{
    CursorX += x;
    CursorY += y;
}

/*****************************************************************************
* Draw line from current Cursor position to the given point.		     *
* Note the Cursor position is updated to the given point.		     *
*****************************************************************************/
void VirtLineTo(int x, int y)
{
    VirtLine(CursorX, CursorY, x, y);

    CursorX = x;
    CursorY = y;
}

/*****************************************************************************
* Draw line from current Cursor position to the given relative point.	     *
* Note the Cursor position is updated to the given point.		     *
*****************************************************************************/
void VirtLineRelTo(int x, int y)
{
    VirtLine(CursorX, CursorY, CursorX + x, CursorY + y);

    CursorX += x;
    CursorY += y;
}

/*****************************************************************************
* Draw a line between the two given points.				     *
* Note the Cursor position is NOT updated in this routine.		     *
* Implement a simple DDA between the two points.			     *
*****************************************************************************/
void VirtLine(int x1, int y1, int x2, int y2)
{
    int Accumulate,
	x = x1,
	y = y1,
	Dx = ABS(x1 - x2),
	Dy = ABS(y1 - y2),
	XStep = x2 > x1 ? 1 : -1,
	YStep = y2 > y1 ? 1 : -1;

    VirtSetPixel(x, y);

    if (Dx > Dy) {
	/* Dx is modified by 1 in each step. */
	Accumulate = Dx / 2;
	do {
	    Accumulate -= Dy;
	    if (Accumulate < 0) {
		y += YStep;
		Accumulate += Dx;
	    }
	    x += XStep;
	    VirtSetPixel(x, y);
	}
	while (x != x2);
    }
    else {
	/* Dy is modified by 1 in each step. */
	Accumulate = Dy / 2;
	do {
	    Accumulate -= Dx;
	    if (Accumulate < 0) {
		x += XStep;
		Accumulate += Dy;
	    }
	    y += YStep;
	    VirtSetPixel(x, y);
	}
	while (y != y2);
    }
}

/*****************************************************************************
* Draw a filled box.							     *
*****************************************************************************/
void VirtBar(int x1, int y1, int x2, int y2)
{
    int i;

    if (x1 < x2)
	for (i = x1; i <= x2; i++)
	    VirtLine(i, y1, i, y2);
    else
	for (i = x2; i <= x1; i++)
	    VirtLine(i, y1, i, y2);
}

/*****************************************************************************
* Draw a circle, given the center and the radius.			     *
*****************************************************************************/
void VirtCirc(int x, int y, int r)
{
    int i;

    for (i=0; i<8; i++)
	VirtArcAux(x, y, r, i, 0, 45);
}

/*****************************************************************************
* Draw an arc, given the center, radius, and angles.			     *
* Note arc are supported to 45 degrees boundaries only, and rounded otherwise*
*****************************************************************************/
void VirtArc(int x, int y, int r, int Angle1, int Angle2)
{
    int Angle, Angle1Mod, Angle2Mod;

    if (Angle1 > Angle2) {
	Angle = Angle1;
	Angle1 = Angle2;
	Angle2 = Angle;
    }

    Angle1Mod = Angle1 % 45;
    Angle2Mod = Angle2 % 45;
    Angle1 = Angle1 / 45;
    Angle2 = Angle2 / 45;

    while (Angle1 < 0) Angle1 += 8;
    while (Angle2 < 0) Angle2 += 8;
    while (Angle1 >= 8) Angle1 -= 8;
    while (Angle2 >= 16) Angle2 -= 8;

    while (Angle1 < Angle2) {
	VirtArcAux(x, y, r, Angle1++ % 8, Angle1Mod, 45);
	Angle1Mod = 0;
    }
    if (Angle2Mod > 0) VirtArcAux(x, y, r, Angle2 % 8, 0, Angle2Mod);

}

/*****************************************************************************
* Draw an arc, given the center, radius, and angles.			     *
* This aux routine draw on one octant as specified by Octant (0..7).	     *
* StartAngle and EndAngles are in [0..45] and are relative to Octant base.   *
*****************************************************************************/
static void VirtArcAux(int XCenter, int YCenter, int Radius, int Octant,
						int StartAngle, int EndAngle)
{
    int XStart, YStart, XEnd, YEnd;
    switch (Octant) {
	case 0:
	    XStart = MAP_COS(StartAngle, Radius);
	    YStart = MAP_SIN(StartAngle, Radius);
	    XEnd = MAP_COS(EndAngle, Radius);
	    YEnd = MAP_SIN(EndAngle, Radius);
	    break;
	case 1:
	    XStart = MAP_SIN(45 - EndAngle, Radius);
	    YStart = MAP_COS(45 - EndAngle, Radius);
	    XEnd = MAP_SIN(45 - StartAngle, Radius);
	    YEnd = MAP_COS(45 - StartAngle, Radius);
	    break;
	case 2:
	    XStart = -MAP_SIN(StartAngle, Radius);
	    YStart = MAP_COS(StartAngle, Radius);
	    XEnd = -MAP_SIN(EndAngle, Radius);
	    YEnd = MAP_COS(EndAngle, Radius);
	    break;
	case 3:
	    XStart = -MAP_COS(45 - EndAngle, Radius);
	    YStart = MAP_SIN(45 - EndAngle, Radius);
	    XEnd = -MAP_COS(45 - StartAngle, Radius);
	    YEnd = MAP_SIN(45 - StartAngle, Radius);
	    break;
	case 4:
	    XStart = -MAP_COS(StartAngle, Radius);
	    YStart = -MAP_SIN(StartAngle, Radius);
	    XEnd = -MAP_COS(EndAngle, Radius);
	    YEnd = -MAP_SIN(EndAngle, Radius);
	    break;
	case 5:
	    XStart = -MAP_SIN(45 - EndAngle, Radius);
	    YStart = -MAP_COS(45 - EndAngle, Radius);
	    XEnd = -MAP_SIN(45 - StartAngle, Radius);
	    YEnd = -MAP_COS(45 - StartAngle, Radius);
	    break;
	case 6:
	    XStart = MAP_SIN(StartAngle, Radius);
	    YStart = -MAP_COS(StartAngle, Radius);
	    XEnd = MAP_SIN(EndAngle, Radius);
	    YEnd = -MAP_COS(EndAngle, Radius);
	    break;
	case 7:
	    XStart = MAP_COS(45 - EndAngle, Radius);
	    YStart = -MAP_SIN(45 - EndAngle, Radius);
	    XEnd = MAP_COS(45 - StartAngle, Radius);
	    YEnd = -MAP_SIN(45 - StartAngle, Radius);
	    break;
    }

    if (Octant == 0 || Octant == 3 || Octant == 4 || Octant == 7)
	VirtArcAuxX(XCenter, YCenter, Radius, XStart, YStart, XEnd, YEnd);
    else
	VirtArcAuxY(XCenter, YCenter, Radius, XStart, YStart, XEnd, YEnd);
}

/*****************************************************************************
* Draw an arc, given the center, radius, XStart, YStart and XEnd, YEnd.      *
* X/YStart and X/YEnd are given relative to a circle centered at the origin. *
* This aux routine draw one arc in upto one octant (45 degrees at most),     *
* in which ABS(Y) <= ABS(X).						     *
* See Foley and Van Dam, pp 445, for basic idea.			     *
*****************************************************************************/
static void VirtArcAuxX(int XCenter, int YCenter, int Radius,
			int XStart, int YStart, int XEnd, int YEnd)
{
    int d = 3 - (Radius << 1),
	x = XStart, y = YStart,
	XStep = XStart < 0 ? 1 : -1,
	YStep = YEnd > 0 ? 1 : -1;

    while (ABS(x) >= ABS(XEnd)) {
	VirtSetPixel(XCenter + x, YCenter + y);
	if (d < 0)
	    d += (ABS(y) << 2) + 6;
	else {
	    d += ((ABS(y) - ABS(x)) << 2) + 10;
	    x += XStep;
	}
	y += YStep;
    }
}

/*****************************************************************************
* Draw an arc, given the center, radius, XStart, YStart and XEnd, YEnd.      *
* X/YStart and X/YEnd are given relative to a circle centered at the origin. *
* This aux routine draw one arc in upto one octant (45 degrees at most),     *
* in which ABS(Y) >= ABS(X).						     *
* See Foley and Van Dam, pp 445, for basic idea.			     *
*****************************************************************************/
static void VirtArcAuxY(int XCenter, int YCenter, int Radius,
			int XStart, int YStart, int XEnd, int YEnd)
{
    int d = 3 - (Radius << 1),
	x = XStart, y = YStart,
	XStep = XEnd > 0 ? 1 : -1,
	YStep = YStart < 0 ? 1 : -1;

    while (ABS(y) >= ABS(YEnd)) {
	VirtSetPixel(XCenter + x, YCenter + y);
	if (d < 0)
	    d += (ABS(x) << 2) + 6;
	else {
	    d += ((ABS(x) - ABS(y)) << 2) + 10;
	    y += YStep;
	}
	x += XStep;
    }
}

/*****************************************************************************
* Set current Color to be used in drawing.				     *
*****************************************************************************/
void VirtSetColor(int Color)
{
    if (Color > 1) return;
    CrntColor = Color;
}

/*****************************************************************************
* Set text format to be drawn - orientation (horizontal/vertical) and	     *
* centering around the Cursor position in X and Y axes.			     *
*****************************************************************************/
void VirtTextFormat(TextOrientationType Orient, int Scale,
		    TextHorizJustifyType XCenter, TextVertJustifyType YCenter)
{
    TextOrientation = Orient;
    TextScale = Scale;
    TextXCenter = XCenter;
    TextYCenter = YCenter;
}

/*****************************************************************************
* Draw the given NULL terminated text in the current Cursor position and     *
* with the format as specified by VirtTextFormat.			     *
*****************************************************************************/
void VirtText(char *Str)
{
    char c, Byte;
    int i, j, k, s, Mask, CharPosX, CharPosY, Len = strlen(Str);

    switch (TextOrientation) {
	case TEXT_ORIENT_HORIZ:
	    switch (TextXCenter) {
		case TEXT_X_LEFT:
		    CharPosX = CursorX;
		    break;
		case TEXT_X_CENTER:
		    /* Half string width == Len * 4 * TextScale pixels. */
		    CharPosX = CursorX - Len * 4 * TextScale;
		    break;
		case TEXT_X_RIGHT:
		    /* Full string width == Len * 8 * TextScale pixels. */
		    CharPosX = CursorX - Len * 8 * TextScale;
		    break;
	    }
	    switch (TextYCenter) {
		case TEXT_Y_TOP:
		    CharPosY = CursorY;
		    break;
		case TEXT_Y_CENTER:
		    CharPosY = CursorY - 4 * TextScale;
		    break;
		case TEXT_Y_BOTTOM:
		    CharPosY = CursorY - 8 * TextScale;
		    break;
	    }
	    for (i = 0; i < Len; i++, CharPosX += 8 * TextScale) {
		c = Str[i];
		for (j = 0; j < 8 * TextScale; j++) {
		    Byte = AsciiTable[c][j / TextScale];
		    for (k = 0, Mask = 128; k < 8; k++, Mask >>= 1)
			if (Byte & Mask)
                            for (s = TextScale - 1; s >= 0; s--)
			        VirtSetPixel(CharPosX + k * TextScale + s,
			    		     CharPosY + j);
		}
	    }
	    break;
	case TEXT_ORIENT_VERT:
	    switch (TextXCenter) {
		case TEXT_X_LEFT:
		    CharPosX = CursorX;
		    break;
		case TEXT_X_CENTER:
		    CharPosX = CursorX - 4 * TextScale;
		    break;
		case TEXT_X_RIGHT:
		    CharPosX = CursorX - 8 * TextScale;
		    break;
	    }
	    switch (TextYCenter) {
		case TEXT_Y_TOP:
		    CharPosY = CursorY;
		    break;
		case TEXT_Y_CENTER:
		    /* Half string width == Len * 4 * TextScale pixels. */
		    CharPosY = CursorY - Len * 4 * TextScale;
		    break;
		case TEXT_Y_BOTTOM:
		    /* Full string height == Len * 8 * TextScale pixels. */
		    CharPosY = CursorY - Len * 8 * TextScale;
		    break;
	    }
	    for (i = 0; i < Len; i++, CharPosY += 8 * TextScale) {
		c = Str[Len - i - 1];
		for (j = 0; j < 8 * TextScale; j++) {
		    Byte = AsciiTable[c][j / TextScale];
		    for (k = 0, Mask = 1; k < 8; k++, Mask <<= 1)
			if (Byte & Mask)
                            for (s = TextScale - 1; s >= 0; s--)
				VirtSetPixel(CharPosX + j,
					     CharPosY + k * TextScale + s);
		}
	    }
	    break;
    }
}

/****************************************************************************
* Routine to return text width in pixels.				    *
****************************************************************************/
int VirtTextWidth(char *Str)
{
    return strlen(Str) * 8 * TextScale;
}

/****************************************************************************
* Routine to return text height in pixels.				    *
****************************************************************************/
int VirtTextHeight(char *Str)
{
    return 8 * TextScale;
}

/*****************************************************************************
* Get the value of the pixel at the given XY location.			     *
*****************************************************************************/
int VirtGetPixel(int x, int y)
{
    if (x >= FrameBufferX || y >= FrameBufferY)
        return 0;
    else
	return FrameBuffer[x][y >> 3] & (1 << (y & 0x07)) ? 1 : 0;
}

/*****************************************************************************
* Get the value of the pixel at the given XY location, to the CrntColor.     *
*****************************************************************************/
void VirtSetPixel(int x, int y)
{
    if (x <= 0 || y <= 0 || x >= FrameBufferX || y >= FrameBufferY) return;

    switch (CrntColor) {
	case 0:
	    FrameBuffer[x][y >> 3] &= ~(CrntColor << (y & 0x07));
	    break;
	case 1:
	    FrameBuffer[x][y >> 3] |= (CrntColor << (y & 0x07));
	    break;
    }
}

/*****************************************************************************
* Get a block of pixels from the virtual raster device, into Buffer.	     *
*****************************************************************************/
void VirtGetBlock(int x1, int y1, int x2, int y2, char *Buffer)
{
    int i, j, k, Count, Data, Bit;

    if (x1 > x2) {
	i = x1;
	x1 = x2;
	x2 = i;
    }
    if (y1 > y2) {
	i = y1;
	y1 = y2;
	y2 = i;
    }

    k = Data = Count = 0;
    for (i = x1; i <= x2; i++)
	for (j = y1; j <= y2; j++) {
	    Bit = VirtGetPixel(i, j);
	    Data = (Data << 1) + Bit;
	    if (++Count == 8) {
		Buffer[k++] = Data;
		Data = Count = 0;
	    }
	}

    if (Count != 0) {
	Data <<= (8 - Count);
	Buffer[k++] = Data;
    }
}
