/*****************************************************************************
* Program to "tweak" the ZVG vector board.
* 
* Used to adjust internal ZVG registers used to time vector draws.
*
* Currently this program is very ugly in its use of the DOS screen, simply
* using "printf()" statements.  However it does fully show how to communicate
* with the ZVG using the 1284 commands available in ZVGPORT.C, and having
* this information available seems more important than a pretty screen.
*
* The screen will be cleaned up in future versions. -Zonn
*
* Version: 0.5
* Author:  Zonn Moore
* Created: 11/06/02
*
* History:
*
* (c) Copyright 2002, Zektor, LLC.  All Rights Reserved.
*****************************************************************************/
#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include	<conio.h>
#include	<dos.h>
#include	<bios.h>
#include	<time.h>

#include	"zstddef.h"
#include	"zvgport.h"
#include	"zvgcmds.h"
#include	"timer.h"

#define	MAX_FRAME_SIZE		16384

ZvgMon_s		Mon;
uchar			MonitorBfr[ZVG_MAX_BFRSZ];

// Buffer used to hold speed table information

uchar			Speeds[4];

// Setup file pointer for reading ZVG command file.

FILE	*FrameFile;
uchar	FrameBfr[MAX_FRAME_SIZE];

uint	EcpDMA;					// DMA channel (currently unused)
uint	EcpIRQ;					// IRQ address (currently unused)

/*****************************************************************************
* If something goes wrong just call this routine to return to DOS
*****************************************************************************/
void exitProg( void)
{
	if (FrameFile != NULL)
		fclose( FrameFile);

	tmrRestore();
	exit( 0);
}

/*****************************************************************************
* Print current monitor information.
*****************************************************************************/
void pMonitor( void)
{
	putchar( '\n');
	printf( "\nZ-Shift:             %3u", Mon.zShift);
	printf( "\nOvershoot:           %3u", Mon.oShoot);
	printf( "\nJumpFactor:          %3u", Mon.jumpFactor);
	printf( "\nPoint Intensity:     %3u", Mon.point_i);
	printf( "\nMin Color Intensity: %3u", Mon.min_i);
	printf( "\nMax Color Intensity: %3u", Mon.max_i);
	printf( "\nScale:               %3u", Mon.scale);
	printf( "\nSettling:            %3u", Mon.settle);
	putchar( '\n');
}

int main( int argc, char *argv[])
{
	uint	err, readLen;
	ulong	lPort;
	char	*dmy, cc;

	uint	frameSize, pBfr;

	fputs( "\nZVGTWEAK - Fine tune the ZVG vector generator. Version 0.5", stdout);
	fputs( "\n(c) Copyright 2002, Zektor, LLC.  All rights reserved.\n", stdout);

	if (argc < 3)
	{	fputs( "\nUse: ZVGTWEAK frameFile port\n", stdout);
		fputs( "\nWhere:", stdout);
		fputs( "\n   frameFile = Filename of ZVG frame file.", stdout);
		fputs( "\n   port      = Address of ECP compatible parallel port (in hex).\n", stdout);
		exit( 0);
	}

	FrameFile = NULL;

	// Setup the ZVG subsytem, this routine initializes the timers and calibrates any timing
	// needed by the the zvgTalk routines.

	zvgInit();

	err = errOk;

	EcpDMA = 0;
	EcpIRQ = 0;

	// Read address of the ZVG port from the commandline.  This will be replaced by
	// an autodetect function.  By running through all available ports, and requesting
	// a device ID from each port, I will be able to find the ZVG automatically. -Zonn
	//
	// But for now the port address must be given on the command line...

	lPort = strtoul( argv[2], &dmy, 16);

	// Range check port address

	if (lPort > 0x3FF)
		err = errNotEcp;

	// Verify that the port is an ECP port

	if (!err)
		err = zvgDetectECP( (unsigned int)lPort, &EcpDMA, &EcpIRQ);

	if (err)
	{	zvgError( err);
		exit( 0);
	}

	// Uncomment to see what the ECP claims its DMA and IRQ are.  This
	// is inevitably wrong, since almost no ECP chipsets support this mode.

//	fprintf( stdout, "\nDMA = %u, IRQ = %u\n", EcpDMA, EcpIRQ);

	// Use the 1284 Request ID command to read the version, and error
	// status of the ZVG.  Future code will parse this to return more
	// human readable version numbers, for now, just dump to the
	// screen what we get back from the ZVG. -Zonn

	err = zvgGetDeviceID( MonitorBfr, ZVG_MAX_BFRSZ, &readLen);

	if (err)
		zvgError( err);

	// Print Request ID info

	else
		fprintf( stdout, "\n%s\n", MonitorBfr);

	// Read in a pre-encoded ZVG commmand file.  This file will be dumped
	// to the ZVG over and over, and is used as the test screen.

	FrameFile = fopen( argv[1], "rb");

	if (FrameFile == NULL)
	{	fprintf( stdout, "\nError opening file: \"%s\".\n", argv[1]);
		exit( 0);
	}

	// Read file, get size of frame

	frameSize = fread( FrameBfr, 1, MAX_FRAME_SIZE, FrameFile);

	// Close the file, no longer needed

	fclose( FrameFile);

	// Setup port.  Ecp mode, No DMA, no interrupts
	// Later versions will enable DMA and IRQs. -Zonn

	if ((err = zvgSetEcpMode()) != errOk)
	{	zvgError( err);
		exitProg();
	}

	// snycronize the internal variables of ZVGTWEAK with those inside the ZVG

	if ((err = zvgReadMonitorInfo( &Mon)) != errOk)
	{	zvgError( err);
		exitProg();
	}

	// print the current value of the ZVG settings

	pMonitor();

	if ((err = zvgReadSpeedInfo( Speeds)) != errOk)
	{	zvgError( err);
		exitProg();
	}

	// Print the current value of the speed tables used by the ZVG

	printf( "\nSpeed tables: %uus, %uus, %uus, %uus\n", Speeds[0], Speeds[1], Speeds[2], Speeds[3]);

	// Dump frame over and over to the ZVG while looking for key presses

	tmrSetFrameRate( 40);			// set frame rate to 40 frames per second

	pBfr = 0;
	err = errOk;

	while (err == errOk)
	{
		// send block of data to ZVG, exit loop when end of buffer is reached, or
		// some kind of error returned.

		while (err == errOk && pBfr != frameSize)
			err = zvgEcpPutc( FrameBfr[pBfr++]);

		// End of frame? Wrap back to start

		if (pBfr == frameSize)
			pBfr = 0;

		// if end of buffer, (or ZVG just too busy right now), check for a key press

		if (kbhit())
		{
			// read key

			cc = getch();

			// I have arbitrarily picked a bunch of keys to adjust ZVG parameters.
			// This will be replaced with an on screen display, allowing the
			// user to tab to the desired value and use the up/down (left/right?)
			// arrows to adjust these parameters. -Zonn

			switch (cc)
			{
				// Lower the min monitor intensity

			case ';':
				if (Mon.min_i > 0)
				{	Mon.min_i--;
					zvgEcpPutc( zcMIN_I);
					zvgEcpPutc( Mon.min_i);
					printf( "\nMin Intensity=%u", Mon.min_i);
				}
				break;  

				// Raise the min monitor intensity

			case '\'':
				if (Mon.min_i != 255)
				{	Mon.min_i++;
					zvgEcpPutc( zcMIN_I);
					zvgEcpPutc( Mon.min_i);
					printf( "\nMin Intensity=%u", Mon.min_i);
				}
				break;  

				// Lower the max monitor intensity

			case ':':
				if (Mon.max_i > 0)
				{	Mon.max_i--;
					zvgEcpPutc( zcMAX_I);
					zvgEcpPutc( Mon.max_i);
					printf( "\nMax Intensity=%u", Mon.max_i);
				}
				break;  

				// Raise the max monitor intensity

			case '"':
				if (Mon.max_i != 255)
				{	Mon.max_i++;
					zvgEcpPutc( zcMAX_I);
					zvgEcpPutc( Mon.max_i);
					printf( "\nMax Intensity=%u", Mon.max_i);
				}
				break;  

				// Move the Z-shift towards the start of vector
				// This has the effect of brightening the starting point
				// of a vector, and at the same time dimming the ending point

			case '[':
													
				if (Mon.zShift > 0)
				{	Mon.zShift--;
					zvgEcpPutc( zcZSHIFT);
					zvgEcpPutc( Mon.zShift);
					printf( "\nZ-Shift=%u", Mon.zShift);
				}
				break;  

				// Move the Z-shift away from the start of vector

			case ']':
				if (Mon.zShift != 255)
				{	Mon.zShift++;
					zvgEcpPutc( zcZSHIFT);
					zvgEcpPutc( Mon.zShift);
					printf( "\nZ-Shift=%u", Mon.zShift);
				}
				break;

				// Remove length from the Z-overshoot.  This effectively dims the
				// ending point of the vector, and has no effect on the starting
				// point.
  
			case '{':
				if (Mon.oShoot > 0)
				{	Mon.oShoot--;
					zvgEcpPutc( zcOSHOOT);
					zvgEcpPutc( Mon.oShoot);
					printf( "\nOvershoot=%u", Mon.oShoot);
				}
				break;  

				// Add length to the Z-overshoot.  This effectively brightens the
				// ending point of the vector, and has no effect on the starting
				// point.
  
			case '}':
				if (Mon.oShoot != 255)
				{	Mon.oShoot++;
					zvgEcpPutc( zcOSHOOT);
					zvgEcpPutc( Mon.oShoot);
					printf( "\nOvershoot=%u", Mon.oShoot);
				}
				break;

				// Lower the jump factor.  When a new vector is jumped to (its
				// starting point is not shared with the ending point of the previous
				// vector), this value is multiplied with the distance of the
				// jump to determine how long to wait for the trace to make it to
				// the new destination. Lower values of the jump factor will increase
				// the Frames per Second rate, but eventually the monitor will not
				// be able to keep up

			case '_':
				if (Mon.jumpFactor > 0)
				{	Mon.jumpFactor--;
					zvgEcpPutc( zcJUMP);
					zvgEcpPutc( Mon.jumpFactor);
					printf( "\nJumpFactor=%u", Mon.jumpFactor);
				}
				break;  

				// Raise the jump factor.

			case '+':
				if (Mon.jumpFactor != 255)
				{	Mon.jumpFactor++;
					zvgEcpPutc( zcJUMP);
					zvgEcpPutc( Mon.jumpFactor);
					printf( "\nJumpFactor=%u", Mon.jumpFactor);
				}
				break;

				// Lower the amount of time the ZVG waits while a POINT is being
				// drawn.  The longer the trace is left in one spot, the brighter
				// the point will appear.

			case ',':
				if (Mon.point_i > 0)
				{	Mon.point_i--;
					zvgEcpPutc( zcPOINT_I);
					zvgEcpPutc( Mon.point_i);
					printf( "\nPoint Intensity=%u", Mon.point_i);
				}
				break;  

				// Raise the amount of time the ZVG waitw while a POINT is being
				// drawn.

			case '.':
				if (Mon.point_i != 255)
				{	Mon.point_i++;
					zvgEcpPutc( zcPOINT_I);
					zvgEcpPutc( Mon.point_i);
					printf( "\nPoint Intensity=%u", Mon.point_i);
				}
				break;

				// Lower the minimum settling time.  After the Jump factor time
				// is calculated, it is checked against this value for a minimum
				// settling time. Changes in the firmware have pretty much obsoleted
				// this command, and it should probably just be left at one.
				// (Internally the ZVG treats a 0 as a 1)

			case '<':
				if (Mon.settle > 1)
				{	Mon.settle--;
					zvgEcpPutc( zcSETTLE);
					zvgEcpPutc( Mon.settle);
					printf( "\nSettling=%u", Mon.settle);
				}
				break;  

				// Raise the minimum settling time.

			case '>':
				if (Mon.settle != 255)
				{	Mon.settle++;
					zvgEcpPutc( zcSETTLE);
					zvgEcpPutc( Mon.settle);
					printf( "\nSettling=%u", Mon.settle);
				}
				break;

				// This decreases the screen size. This is done in hardware on the
				// ZVG and no resolution is lost by changing the screen size.

				// This Vectrex monitor is so sensitive, the only way to write to
				// it is to lower this value, along with removing the 2x jumpers
				// on the ZVG.

			case '-':
				if (Mon.scale > 0)
				{	Mon.scale--;
					zvgEcpPutc( zcSCALE);
					zvgEcpPutc( Mon.scale);
					printf( "\nScale=%u", Mon.scale);
				}
				break;  

				// This increases the screen size.

			case '=':
				if (Mon.scale != 255)
				{	Mon.scale++;
					zvgEcpPutc( zcSCALE);
					zvgEcpPutc( Mon.scale);
					printf( "\nScale=%u", Mon.scale);
				}
				break;

				// Save the current value in the ZVG, to the ZVG's EEPROM.
				// This allow the parameters to survive a power failure.
				// FYI: There are separate EEPROM locations for each table
				// speed.

			case 'S':
				zvgEcpPutc( zcSAVE_EE);
				fputs( "\nCurrent values saved to EEPROM.", stdout);
				break;

				// Reload the values from the EEPROM, incase you've screwed
				// something up and want to get back to where you were.

			case 'L':
				zvgEcpPutc( zcLOAD_EE);
				fputs( "\nMonitor values re-loaded from EEPROM.", stdout);
				break;

				// Reset the values to the factor default, incase you've *really*
				// screwed something up!  If the values save in EEPROM are
				// screwed up you will have to save these values back to EEPROM
				// if you don't want them reappearing.

			case 'R':
				zvgEcpPutc( zcRESET_MON);
				fputs( "\nMonitor values reset to factory defaults.", stdout);
				break;

				// just dump the parameters ZVGTWEAK is using.

			case 'v':
			case 'V':
				pMonitor();
				break;

				// re-read the information from the ZVG, if for some reason we've
				// got out of sync.

			case 'q':
			case 'Q':
				zvgReadMonitorInfo( &Mon);
				pMonitor();
				break;	
			}

			if (cc == 0x1B)
				break;							// leave loop
		}

		// wait for start of next frame

		tmrWaitFrame();
	}

	if (err)
		zvgError( err);

	// return to the compatiblity (SPP) mode

	zvgSetSppMode();
	exitProg();
	return (0);
}
