/** targa.c **/

#ifdef __TURBOC__
#	pragma	warn -par
#endif

#define TARGA_DATA

#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#ifndef XFRACT
#include	<conio.h>
#endif
#include	"targa.h"
#include	"fractint.h"
#include	"prototyp.h"

/*************	****************/

void	WriteTGA( int x, int y, int index );
int	ReadTGA ( int x, int y );
void	EndTGA	( void );
void	StartTGA( void );
void	ReopenTGA( void );

/*************	****************/

static unsigned _fastcall near Row16Calculate(unsigned,unsigned);
static void	_fastcall near PutPix16(int,int,int);
static unsigned _fastcall near GetPix16(int,int);
static unsigned _fastcall near Row32Calculate(unsigned,unsigned);
static void	_fastcall near PutPix32(int,int,int);
static unsigned _fastcall near GetPix32(int,int);
static void	_fastcall near DoFirstPixel(int,int,int);
static void _fastcall fatalerror(char far *);
static int  GetLine(int);
static void _fastcall near SetDispReg(int,int);
static int  VWait(void);
static void _fastcall SetVBorder(int,int);
static void _fastcall SetBorderColor(long);
static void _fastcall SetVertShift(int);
static void _fastcall SetInterlace(int);
static void _fastcall SetBlndReg(int);
static void _fastcall SetRGBorCV(int);
static void _fastcall SetVCRorCamera(int);
static void _fastcall SetMask(int);
static void _fastcall SetBlndReg(int);
static void _fastcall SetContrast(int);
static void _fastcall SetHue(int);
static void _fastcall SetSaturation(int);
static void _fastcall SetHBorder(int,int);
static void SetFixedRegisters(void);
static void _fastcall VCenterDisplay(int);
static void _fastcall SetOverscan(int);
static void _fastcall near TSetMode(int);
static int  GraphInit(void);
static void GraphEnd(void);

/*************	****************/

int	     xorTARGA;
unsigned far *tga16 = NULL;  /* [256] */
long	 far *tga32;	     /* [256] */
static int   last = 0;

/*************	****************/

static int	initialized;

/*************	****************/

static void	(near _fastcall *DoPixel) ( int x, int y, int index );
static void	(near _fastcall *PutPixel)( int x, int y, int index );
static unsigned (near _fastcall *GetPixel)( int x, int y );

/**************************************************************************/
#ifdef __BORLANDC__
#if(__BORLANDC__ > 2)
   #pragma warn -eff
#endif
#endif

static unsigned _fastcall near Row16Calculate( unsigned line, unsigned x1 )
{
	outp( DESTREG, (line >> 5) );
	return( ((line & 31) << 10) | (x1 << 1) ); /* calc the pixel offset */
}

/**************************************************************************/

static void _fastcall near PutPix16( int x, int y, int index )
{
unsigned far * ip;

	/**************/
	ip = MK_FP( MEMSEG, Row16Calculate( y, x ) );
	if( ! xorTARGA )
		*ip = tga16[index];
	else
		*ip = *ip ^ 0x7fff;
}

/**************************************************************************/

static unsigned _fastcall near GetPix16( int x, int y )
{
register unsigned pixel, index;
unsigned far * ip;
	/**************/
	ip = MK_FP( MEMSEG, Row16Calculate( y, x ) );
	pixel = *ip & 0x7FFF;
	if( pixel == tga16[last] ) return( last );
	for( index = 0; index < 256; index++ )
		if( pixel == tga16[index] ) {
			last = index;
			return( index );
		}
	return( 0 );
}

/**************************************************************************/

static unsigned _fastcall near Row32Calculate( unsigned line, unsigned x1 )
{
	outp( DESTREG, (line >> 4) );
	return ( ((line & 15) << 11) | (x1 << 2) ); /* calc the pixel offset */
}

/**************************************************************************/

static void _fastcall near PutPix32( int x, int y, int index )
{
long far * lp;
	lp = MK_FP( MEMSEG, Row32Calculate( y, x ) );
	if( ! xorTARGA )
		*lp = tga32[index];
	else
		*lp = *lp ^ 0x00FFFFFFL;
}

/**************************************************************************/

static unsigned _fastcall near GetPix32( int x, int y )
{
register int index;
long	pixel;
long	far * lp;

	lp = MK_FP( MEMSEG, Row32Calculate( y, x ) );
	pixel = *lp & 0x00FFFFFFL;
	if( pixel == tga32[last] ) return( last );
	for( index = 0; index < 256; index++ )
		if( pixel == tga32[index] ) {
			last = index;
			return( index );
		}
	return( 0 );
}

/**************************************************************************/

static void _fastcall near DoFirstPixel( int x, int y, int index )
{
int cnt;
	TSetMode( targa.mode | 1 );
	for( cnt = 0; cnt < targa.MaxBanks; cnt += 2 ) { /* erase */
		outp( DESTREG, cnt );
		outp( SRCREG,  cnt + 1 );
		erasesegment(targa.memloc,0);  /** general.asm **/
	}
	TSetMode( targa.mode & 0xFFFE );
	PutPixel = DoPixel;
	(*PutPixel)( x, y, index );
}

#ifdef __BORLANDC__
#if(__BORLANDC__ > 2)
   #pragma warn +eff
#endif
#endif

/***************************************************************************/

void WriteTGA( int x, int y, int index )
{
	OUTPORTB(MODEREG, targa.mode |= 1 );	/* TSetMode inline for speed */
	(*PutPixel)( x, sydots-y, index&0xFF ); /* fix origin to match EGA/VGA */
	OUTPORTB(MODEREG, targa.mode &= 0xFFFE );
}

/***************************************************************************/

int ReadTGA( int x, int y )
{
int val;
	OUTPORTB(MODEREG, targa.mode |= 1 );	/* TSetMode inline for speed */
	val = (*GetPixel)( x, sydots-y );
	OUTPORTB(MODEREG, targa.mode &= 0xFFFE );
	return( val );
}

/***************************************************************************/

void EndTGA( void )
{
	if( initialized ) {
		GraphEnd();
		initialized = 0;
	}
}

/***************************************************************************/

void StartTGA()
{
int	i;
/* 
   This overlayed data safe because used in this file, any only used for
   fatal error message! 
*/
static FCODE couldntfind[]={"Could not find Targa card"};
static FCODE noenvvar[]={"TARGA environment variable missing"};
static FCODE insuffmem[]={"Insufficient memory for Targa"};

	/****************/
	if( initialized ) return;
	initialized = 1;

	/****************/
	/* note that video.asm has already set the regualar video adapter */
	/* to text mode (ax in Targa table entries is 3);		  */
	/* that's necessary because TARGA can live at 0xA000, we DO NOT   */
	/* want to have an EGA/VGA in graphics mode!!			  */
	ReopenTGA(); /* clear text screen and display message */

	/****************/
	/*** look for and activate card ***/
	if ((i = GraphInit()) != 0)
		fatalerror((i == -1) ? couldntfind : noenvvar);

	VCenterDisplay( sydots + 1 );

	if (tga16 == NULL)
	    if ( (tga16 = (unsigned far *)farmemalloc(512L)) == NULL
	      || (tga32 = (long     far *)farmemalloc(1024L)) == NULL)
		fatalerror(insuffmem);

	SetTgaColors();

	if( targa.boardType == 16 ) {
		GetPixel = (unsigned (near _fastcall *)(int, int))GetPix16;
		DoPixel = PutPix16;
	}
	else {
		GetPixel = (unsigned (near _fastcall *)(int, int))GetPix32;
		DoPixel = PutPix32;
	}
	PutPixel = DoFirstPixel;	/* on first pixel --> erase */

	if( sydots == 482 ) SetOverscan( 1 );

	TSetMode( targa.mode & 0xFFFE );

	/****************/
	if (mapdacbox == NULL && SetColorPaletteName("default") != 0)
		exit( 1 ); /* stopmsg has already been issued */

}

void ReopenTGA()
{
static FCODE runningontarga[]={"Running On TrueVision TARGA Card"};
	helptitle();
	putstring(2,20,7,runningontarga);
	movecursor(6,0); /* in case of brutal exit */
}

static void _fastcall fatalerror(char far *msg)
{
static FCODE abortmsg[]={"...aborting!"};
	putstring(4,20,15,msg);
	putstring(5,20,15,abortmsg);
	movecursor(8,0);
	exit(1);
}



/***  the rest of this module used to be separate, in tgasubs.c,  ***/
/***  has now been merged into a single source			  ***/

/*******************************************************************/

static void _fastcall VCenterDisplay( int nLines )
{
int	lines;
int top, bottom;
long color;

	lines  = nLines >> 1;		/* half value of last line 0..x */
	top    = 140 - (lines >> 1);
	bottom = top + lines;
	SetVBorder( top, bottom );
	SetVertShift( 255 - lines );	/* skip lines we're not using */

	if( targa.boardType == 16 )
		color = (12 << 10) | (12 << 5) | 12;
	else
		color = ((long)80 << 16) | (80 << 8) | 80;
	SetBorderColor( color );
}


/*****************************************************************/

static void _fastcall near SetDispReg(int reg, int value)
{
	targa.DisplayRegister[reg] = value;

	TSetMode(targa.mode&MSK_REGWRITE);  /* select Index Register write */
	OUTPORTB(DRREG, reg);	/* select sync register */
	/*
	 *		Set Mask register to write value to
	 *		display register and to set Bit 9 in the DR
	 */
	TSetMode( ((targa.mode|(~MSK_REGWRITE))     /* turn on write bit */
				   & MSK_BIT9	   )	 /* turn off Bit 9 */
			   | ((value&0x0100)>>1)); /* set bit 9 for value */
	OUTPORTB(DRREG, value); /* select sync register */
 }

/*****************************************************************/

#define   WAITCOUNT  60000L

static int VWait()
{
int	rasterreg;
unsigned GiveUp;

	rasterreg = RASTERREG;

	/*
	 *	If beyond bottom of frame wait for next field
	 */
	GiveUp= WAITCOUNT;
	while ( (--GiveUp) && (GetLine(rasterreg) == 0) ) { }
	if (GiveUp) {
	   /*
	    *	   Wait for the bottom of the border
	    */
	   GiveUp= WAITCOUNT;
	   while ( (--GiveUp) && (GetLine(rasterreg) > 0) ) { }
	   }

	return ( ( GiveUp ) ? 0 : -1);
}


/*****************************************************************/

static void _fastcall SetVBorder(int top, int bottom)
{
	/* top border */
	if ( top < MIN_TOP ) top=MIN_TOP;
	SetDispReg(TOPBORDER,top);
	/* bottom border */
	if ( bottom > MAX_BOTTOM ) bottom=MAX_BOTTOM;
	SetDispReg(BOTTOMBORDER,bottom);

	SetDispReg(DR10,top);
	SetDispReg(DR11,bottom);
}


/*****************************************************************/

static void _fastcall SetRGBorCV(int type)
{
	/*  set the contrast level */
	targa.RGBorCV = type;
	targa.VCRCon = ( targa.VCRCon  & MSK_RGBORCV ) |
					(targa.RGBorCV<<SHF_RGBORCV) ;
	OUTPORTB(VCRCON, targa.VCRCon );
}

/*****************************************************************/

static void _fastcall SetVCRorCamera(int type)
{
	targa.VCRorCamera = type&1;
	targa.VCRCon = ( targa.VCRCon  & MSK_VCRORCAMERA ) |
					(targa.VCRorCamera<<SHF_VCRORCAMERA) ;
	OUTPORTB(VCRCON, targa.VCRCon );
}


/*****************************************************************/

static void _fastcall SetBorderColor(long color)
{
	targa.BorderColor = color;
	OUTPORTB(BORDER, (int)(0x0000ffffL&(color)));
	OUTPORTB((BORDER+2), (int)((color)>>16));
}

/*****************************************************************/

static void _fastcall SetMask(int mask)
{
	/* mask to valid values and output to mode register */
	targa.Mask = mask;
	OUTPORTB(MASKREG, mask);
}


/*****************************************************************/

static void _fastcall SetVertShift(int preshift)
{
	/*  set the Vertical Preshift count  level */
	targa.VertShift = preshift;
	OUTPORTB(VERTPAN, preshift);
}


/*****************************************************************/

static void _fastcall SetOverscan(int mode)
{
long tempColor;

	targa.ovrscnOn = mode;
	if ( mode == 0 ) {
			INPORTB(UNDERREG);  /*	select underscan mode */
			SetHBorder(	(DEF_LEFT+targa.xOffset),
						(DEF_RIGHT+targa.xOffset));
			SetDispReg(4,352);
			SetDispReg(5,1);
			SetBorderColor(targa.BorderColor);
	}
	else	{
			INPORTB(OVERREG);   /*	select overrscan mode */
			SetDispReg(0,64);   /*	Set four of the display registers */
			SetDispReg(1,363);  /*	to values required for Overscan */
			SetDispReg(4,363);
			SetDispReg(5,17);
			tempColor = targa.BorderColor;
			SetBorderColor(0L);
			targa.BorderColor = tempColor;
	}
}


/*****************************************************************/

static void _fastcall SetInterlace(int type)
{
	targa.InterlaceMode= type & MSK_INTERLACE;
	SetDispReg(INTREG, targa.InterlaceMode);
	/*
	 *	SET THE INTERLACE BIT TO MATCH THE INTERLACE MODE AND
	 *	SCREEN RESOLUTION -  SCREEN PAGE
	 */
	if ( ( targa.InterlaceMode >= 2 ) &&
		 ( targa.PageMode> 1 )	&&
		 ( (targa.PageMode&1) != 0 )  )
		TSetMode(targa.mode|(~MSK_IBIT) );
	else
		TSetMode(targa.mode& MSK_IBIT);
}


/*****************************************************************/

static void _fastcall SetBlndReg(int value)
{
	/*  set the Vertical Preshift count  level */
	if ( targa.boardType == 32 ) {
		targa.VCRCon = (targa.VCRCon&0xfe) | value;
		OUTPORTB(BLNDREG, value);
		}
}


/*****************************************************************/

static void _fastcall near TSetMode(int mode)
{
	/* mask to valid values and output to mode register */
	OUTPORTB(MODEREG, mode );
	targa.mode = mode;
}


/*****************************************************************/

static void _fastcall SetContrast(int level)
{
	/*  set the contrast level */
	targa.Contrast = level &((~MSK_CONTRAST)>>SHF_CONTRAST);
	targa.VCRCon = ( targa.VCRCon  & MSK_CONTRAST ) |
			(targa.Contrast<<SHF_CONTRAST) ;
	OUTPORTB(VCRCON, targa.VCRCon );
}


/*****************************************************************/

static void _fastcall SetHue(int level)
{
	/*  set the hue level -  Mask to valid value */
	targa.Hue = level&((~MSK_HUE)>>SHF_HUE);
	/* mask to valid range */
	targa.SatHue = (targa.SatHue&MSK_HUE) | (targa.Hue<<SHF_HUE);
	OUTPORTB(SATHUE, targa.SatHue );
}


/*****************************************************************/

static void _fastcall SetSaturation(int level)
{
	/*  set the saturation level */
	targa.Saturation= level&( (~MSK_SATURATION)>>SHF_SATURATION);
	targa.SatHue =	(targa.SatHue&MSK_SATURATION) |
			 (targa.Saturation<<SHF_SATURATION);
	OUTPORTB(SATHUE , targa.SatHue );
}


/*************************************************************/

/*** UNUSED
static void _fastcall SetPageMode(int pageMode)
{
	pageMode &= 0x07;
	targa.PageMode = pageMode;
	VWait();
	TSetMode( (targa.mode)&(MSK_RES) |((pageMode<<SHF_RES)&(~MSK_RES)) ) ;
	if ( ( targa.DisplayRegister[20] >= 2 ) &&
		 ( pageMode> 1 )  &&
		 ( (pageMode&1) != 0 )	  )	TSetMode(targa.mode|(~MSK_IBIT) );
	else
		TSetMode(targa.mode& MSK_IBIT);
}
***/


/*****************************************************************/

static void _fastcall SetHBorder(int left, int right)
{
	SetDispReg(LEFTBORDER, left);	/* set horizontal left border */
	SetDispReg(RIGHTBORDER,right);	/* set horizontal right border */
/*
 *					Set DR 8 and 9 since they
 *					default to tracking DR0 and DR 1
 */
	SetDispReg(DR8,left);
	SetDispReg(DR9,left);
}


/*****************************************************************/

/*** UNUSED
static void _fastcall SetGenlock(int OnOrOff)
{
	TSetMode( (targa.mode)&(MSK_GENLOCK)
			|((OnOrOff<<SHF_GENLOCK)&(~MSK_GENLOCK)) );
}
***/


/*****************************************************************/
/* was asm, TC fast enough on AT */

static int GetLine( int port )
{
int cnt;
int val1, val2;

	val1 = INPORTB( port );
	for( cnt = 0; cnt < 20; cnt++ ) {
		val2 = INPORTB( port );
		if( val1 == val2 )
			break;
		val1 = val2;
	}
	return( val1 );
}


/**********************************************************************
			  TINIT
**********************************************************************/

static int GraphInit()
{
int i;
int bottom, top;
char *envptr;
unsigned switches, got_switches;

	memset( &targa, 0, sizeof(targa) );

	targa.boardType = TYPE_16;		/* default to T16 */
	targa.xOffset = 0;  targa.yOffset = 0;	/* default to no offset */
	targa.LinesPerField= DEF_ROWS/2;	/*  number of lines per field */
	targa.AlwaysGenLock = DEF_GENLOCK;	/* default to genlock off */
	targa.PageMode = 0;
	targa.InterlaceMode = DEF_INT;		/* Defalut:  Interlace Mode 0 */
	targa.Contrast= DEF_CONTRAST;
	targa.Saturation = DEF_SATURATION;
	targa.Hue = DEF_HUE;
	targa.RGBorCV = CV;			/* default to Composite video */
	targa.VCRorCamera = CAMERA;
	targa.PanXOrig = 0; targa.PanYOrig = 0;
	targa.PageUpper= 0xffff;		/* set the bank flags to illega& values */
	targa.PageLower= 0xffff;		/* so that they will be set the first time */
	targa.ovrscnAvail = 0;			/* Assume no Overscan option */
	targa.ovrscnOn = 0;

	if ((envptr = getenv("TARGA")) == NULL)
	   return(-2);
	switches = got_switches = 0;
	while (*envptr) {
	   if (*envptr != ' ') ++got_switches;
	   if (*envptr >= '2' && *envptr <= '8')
	      switches |= (1 << ('8' - *envptr));
	   ++envptr;
	   }
	if (got_switches == 0) { /* all blanks, use default */
	   targa.memloc = (signed int)0xA000;
	   targa.iobase = 0x220;
	   }
	else {
	   targa.memloc = 0x8000 + ((switches & 0x70) << 8);
	   targa.iobase = 0x200  + ((switches & 0x0f) << 4);
	   }

	if ((envptr = getenv("TARGASET")) != NULL) {
	   for(;;) { /* parse next parameter */
	      while (*envptr == ' ' || *envptr == ',') ++envptr;
	      if (*envptr == 0) break;
	      if (*envptr >= 'a' && *envptr <= 'z') *envptr -= ('a'-'A');
	      i = atoi(envptr+1);
	      switch (*envptr) {
		 case 'T':
		    if (i == 16) targa.boardType = TYPE_16;
		    if (i == 24) targa.boardType = TYPE_24;
		    if (i == 32) targa.boardType = TYPE_32;
		    break;
		 /* case 'E' not done, meaning not clear */
		 case 'X':
		    targa.xOffset = i;
		    break;
		 case 'Y':
		    targa.yOffset = i;
		    break;
		 case 'I':
		    targa.InterlaceMode = i;
		    break;
		 /* case 'N' not done, I don't know how to handle it */
		 case 'R':
		    targa.RGBorCV = RGB;
		    break;
		 case 'B':
		    targa.VCRorCamera = CAMERA;
		    break;
		 case 'V':
		    targa.VCRorCamera = VCR;
		    break;
		 case 'G':
		    targa.AlwaysGenLock = 1;
		    break;
		 case 'C':
		    targa.Contrast = i * 31 / 100;
		    break;
		 case 'S':
		    targa.Saturation = i * 7 / 100;
		    break;
		 case 'H':
		    targa.Hue = i * 31 / 100;
		    break;
		 /* note: 'A' and 'O' defined but apply only to type M8 */
		 /* case 'P' not handled cause I don't know how */
		 }
	      while (*(++envptr) >= '0' && *envptr <= '9') { }
	      }
	   }

	if ( targa.boardType == TYPE_16 ) {
		targa.MaxBanks = 16;
		targa.BytesPerPixel = 2;
	}
	if ( targa.boardType == TYPE_24 ) {
		targa.MaxBanks = 32;
		targa.BytesPerPixel = 3;
	}
	if ( targa.boardType == TYPE_32 ) {
		targa.MaxBanks = 32;
		targa.BytesPerPixel = 4;
	}

	/****** Compute # of rows per 32K bank	********/
	targa.RowsPerBank = 512/(targa.MaxBanks);
	targa.AddressShift =  targa.MaxBanks>>4;

	/*	if initializing CVA:  set these before we quit	*/
	SetSaturation(targa.Saturation);
	SetHue(targa.Hue);
	SetContrast( targa.Contrast);

	/*	Set Genlock bit if always genlocked */
	/*	Set before flipping and jerking screen */
	TSetMode( (targa.AlwaysGenLock<<SHF_GENLOCK) | DEF_MODE);

	SetBlndReg(0);		/*  disable blend mode on TARGA 32 */

	SetInterlace(targa.InterlaceMode);
	SetFixedRegisters();
	SetOverscan( 0 );

	top    = 140 - (targa.LinesPerField / 2);
	bottom = top + targa.LinesPerField;
	SetVBorder(top,bottom);
	SetVertShift(256-targa.LinesPerField);

	SetMask(DEF_MASK);
	SetRGBorCV (targa.RGBorCV );
	SetVCRorCamera(targa.VCRorCamera);

	/*	See if the raster register is working correctly and
		return error flag if its not	     */
	return (( VWait() == -1) ? -1 : 0);

}


/**************************************************************/

static void GraphEnd()
{
	TSetMode( (targa.mode)&MSK_MSEL );     /*  disable memory */
}


/**************************************************************/
/* Set the registers which have required values */

#define FIXED_REGS	10

static int FixedRegs[] = {
	DR6,DR7,DR12,DR13,DR14,DR15,DR16,DR17,DR18,DR19
};

static int FixedValue[] = {
	DEF_DR6,DEF_DR7,DEF_DR12,DEF_DR13,DEF_DR14,
	DEF_DR15,DEF_DR16,DEF_DR17,DEF_DR18,DEF_DR19
};

static void SetFixedRegisters()
{
int reg;

	for ( reg=0; reg<FIXED_REGS; reg++)
		SetDispReg(FixedRegs[reg],FixedValue[reg]);
}

