/*
 *			D M - V G . C
 *
 *  MGED display manager for Vector General 3300 monochrome display
 *  system with 3DI UNIBUS interface.  This version only works with
 *  the BRL VAX UNIX VG driver.
 *
 *  Author -
 *	Michael John Muuss
 *  
 *  Source -
 *	SECAD/VLD Computing Consortium, Bldg 394
 *	The U. S. Army Ballistic Research Laboratory
 *	Aberdeen Proving Ground, Maryland  21005
 *  
 *  Copyright Notice -
 *	This software is Copyright (C) 1985 by the United States Army.
 *	All rights reserved.
 */
#ifndef lint
static char RCSid[] = "@(#)$Header: dm-vg.c,v 2.7 86/03/10 21:38:52 mike Exp $ (BRL)";
#endif

#include <fcntl.h>
#include "./machine.h"	/* special copy */
#include "../h/vmath.h"
#include "ged.h"
#include "dm.h"
#include "vgdev.h"			/* include VG specific definitions */
#include "solid.h"

extern void	perror(), sync();
extern char	*malloc();
extern unsigned	sleep();
extern long	lseek();
extern int	ioctl(), select();

typedef unsigned char u_char;
typedef short	displaylist_t;		/* VG displaylist */

/* Display Manager package interface */

#define VGBOUND	0.95	/* Max magnification */
int	Vg_open();
void	Vg_close();
int	Vg_input();
void	Vg_prolog(), Vg_epilog();
void	Vg_normal(), Vg_newrot();
void	Vg_update();
void	Vg_puts(), Vg_2d_line(), Vg_light();
int	Vg_object();
unsigned Vg_cvtvecs(), Vg_load();
void	Vg_statechange(), Vg_viewchange(), Vg_colorchange();
void	Vg_window(), Vg_debug();

struct dm dm_Vg = {
	Vg_open, Vg_close,
	Vg_input,
	Vg_prolog, Vg_epilog,
	Vg_normal, Vg_newrot,
	Vg_update,
	Vg_puts, Vg_2d_line,
	Vg_light,
	Vg_object,
	Vg_cvtvecs, Vg_load,
	Vg_statechange,
	Vg_viewchange,
	Vg_colorchange,
	Vg_window,
	Vg_debug,
	1,				/* has displaylist */
	VGBOUND,
	"vg", "Vector General 3300"
};

struct timeval	{			/* needed for select() */
	long	tv_sec;			/* seconds */
	long	tv_usec;		/* microseconds */
};

#define DEV_DISPLAYLIST "/dev/vg0"
#define DEV_SELREGS	"/dev/vgrs0"

static int	start_mar = BATODLA(0);

#define VGWORDS	(16*1024)	/* # words of VG displaylist */
#define STACKST	((VGWORDS-128)*sizeof(displaylist_t))
				/* BYTE addr of VG Stack start */

static displaylist_t	*zoombufp;	/* dyanmic buffer base of zoombuf */
static displaylist_t	*curzp;		/* "current" position in zoombuf */
static unsigned		zoombytes;	/* sizeof zoombuf */

static int	rvg;			/* raw VG DMA device file descr. */
static int	regb;			/* Buffered register dev fd */

static struct vgregb	regs;		/* Input area for vg regs */
static struct vgregb	oldregs;	/* Save area for prev register scan */
static void regdiff();

static int		vg_debug;
static int		vg_window[6];

extern struct device_values dm_values;	/* values read from devices */

/* Mapping from VG buttons to GED button functions */
static unsigned char bmap[32] = {
	BV_35_25,		/* Lower bank, rightmost button (#31) */
	BV_90_90,
	BE_O_ZSCALE,
	BE_O_YSCALE,
	BE_O_ZSCALE,
	0, 0,
	BE_S_ILLUMINATE,

	BV_RESET,
	BV_ADCURSOR,
	BE_MENU,
	BE_S_SCALE,
	BE_S_TRANS,
	BE_S_ROTATE,
	BE_S_EDIT,
	BV_SLICEMODE,		/* (#16) */

	BE_REJECT,		/* Upper 1/2 bank, rightmost button (#15) */
	BE_ACCEPT,
	BE_O_ROTATE,
	BE_O_XY,
	BE_O_Y,
	BE_O_X,
	BE_O_SCALE,
	BE_O_ILLUMINATE,

	BV_VSAVE,
	BV_VRESTORE,
	BV_REAR,
	BV_FRONT,
	BV_LEFT,
	BV_RIGHT,
	BV_BOTTOM,
	BV_TOP			/* (#0) */
};

/* maps from macros to lights (code 0 is reserved) */
static unsigned char invbmap[BV_MAXFUNC+1];

/**** Begin global display information, used by dm.c *******/
int	inten_offset;			/* Intensity offset */
int	inten_scale = 0x7FF0;		/* Intensity scale */
extern mat_t	model2view;		/* viewing rotation */
/**** End global display information ******/

static long	lights;			/* lights box values */


/*
 *			V G _ O P E N
 *
 * Fire up the display manager, and the display processor.
 *
 */
Vg_open()
{
	register int i,j;

	/* Protect ourselves. */
	sync();

	/* Get VG devices open */
	if( (rvg = open(DEV_DISPLAYLIST, O_RDWR)) < 0 )  {
		perror(DEV_DISPLAYLIST);
		return(1);		/* BAD */
	}

	/* This device can perform SELECTs */
	if( (regb = open( DEV_SELREGS, O_RDONLY)) < 0 )  {
		perror( DEV_SELREGS );
		return(1);			/* BAD */
	}

	(void)ioctl( rvg, VGIOHALT, (int *)0 ); /* Sanity: Halt display */

	/*  A fixed number is clearly wrong.  This probably ought to be
	 *  done more dynamicly....  Also, the error checking
	 *  should probably be improved!
	 */
#define NSOLID 500
#define VG_EXTRA_BYTES	1200	/* estimated faceplate space */
	zoombytes = (NSOLID * 9 * sizeof(displaylist_t)) + VG_EXTRA_BYTES;
	zoombufp = (displaylist_t *) malloc( zoombytes );
	if( zoombufp == (displaylist_t *)NULL )  {
		(void)printf("dm-vg: unable to malloc zoombuf for solids\n");
		return(1);		/* BAD */
	}

	/* For all possible button presses, build table of
	 * function to button/light mappings.
	 */
	for( i=0; i < 32; i++ )  {
		if( (j = bmap[i]) != 0 )
			invbmap[j] = i;		/* func j is on button i */
	}

	/* Free all non-control list memory */
	memfree( &(dmp->dmr_map), STACKST - zoombytes, (unsigned long)zoombytes );

	/* Done once here, for initialization purposes */
	Vg_prolog();
	Vg_normal();
	Vg_puts( "GED Display Monitor Initialized", -1000, 200, 2, 0 );
	Vg_epilog();
	Vg_update();		/* send displaylist to processor */

	/*
	 * Here we turn on the display processor.
	 */
	(void)ioctl( rvg, VGIOSTART, &start_mar );
	return(0);	/* OK */
}

/*
 *  			V G _ C L O S E
 *  
 *  Gracefully release the display.
 */
void
Vg_close()
{
	lights = 0;
	Vg_prolog();
	Vg_epilog();
	Vg_update();		/* Write, then wait 1 frame time ...*/

	/* can't really stop the display yet, so write a no-picture frame */
	curzp = zoombufp;
	Vg_epilog();
	Vg_update();

	(void)ioctl( rvg, VGIOHALT, (int *)0 ); /* Sanity: Halt display */

	(void)close( regb );
	(void)close( rvg );
	rvg = regb = -1;
}

/*
 *			V G _ P R O L O G
 *
 * Write the **constant length** control portion of the displaylist
 * (the prolog) on the front of zoombuf.
 * Sets static (local) pointer to first free control-list location.
 * There are global variables which are parameters to this routine.
 */
void
Vg_prolog()
{
	register displaylist_t *zp = zoombufp;
	static int idle;

	/*
	 * Display the idle pattern
	 */
	if( ( (idle <<= 1) & 0xFF ) == 0 )
		idle = 1;
	lights = (lights & ~0xFF) | idle;

	/* Delay 1/120 sec before redraw.  Allows DMAs through
	 * It is important this this instruction be located BEFORE
	 * any rotations, etc, are loaded, because displaylist execution
	 * will take place from here forwards, and pause here again next
	 * pass through.  Ideally, this should be the FIRST instruction
	 * in the displaylist.
	 */
	*zp++ = VGI_RHALT | VGI_TERMINATE;

	*zp++ = VGI_LD | VGR_SPR;
	*zp++ = BATODLA(STACKST) | VGI_TERMINATE;	/* VG Stack Pointer */

	*zp++ = VGI_LD | VGR_MCR;
	*zp++ = 0x00F0 | VGI_TERMINATE;	/* heads 1-4 on, no interrupts */

	*zp++ = VGI_LD | VGR_PSR;
	*zp++ = 077760;			/* psr */
	*zp++ = 0;			/* Name byte */
	*zp++ = 077760 | VGI_TERMINATE;	/* Coordinate scale reg */

	*zp++ = VGI_LD | VGR_PDXR;	/* Post X,Y displacements */
	*zp++ = 0;
	*zp++ = 0 | VGI_TERMINATE;

	*zp++ = VGI_LD | VGR_IOR;
	*zp++ = inten_offset & 0x7FF0;			/* IOR */
	*zp++ = (inten_scale & 0x7FF0) | VGI_TERMINATE;	/* ISR */

	/* Load lights display */
	*zp++ = VGI_LD | VGR_LTH;
	*zp++ = (lights>>16) & 0xFF00;
	*zp++ = ((lights>>8) & 0xFF00) | VGI_TERMINATE;
	*zp++ = VGI_LD | VGR_LT2H;
	*zp++ = (lights & 0xFF00);
	*zp++ = (lights<<8) | VGI_TERMINATE;

	/* A Point, in the Center of the Screen */
	*zp++ = 010064;				/* VGI_VA, PNT */
	*zp++ = VGI_SHOW( 0, VGI_V_LOAD, VGI_V_X );
	*zp++ = VGI_SHOW( 0, VGI_V_LOAD, VGI_V_Y );
	*zp++ = VGI_SHOW( 0, VGI_V_DRAW, VGI_V_Z );
	*zp++ = VGI_SHOW( 0, VGI_V_DT, VGI_V_Z );

	/* Draw the tracking cross */
	*zp++ = VGI_LD | VGR_DXR;
	*zp++ = (dm_values.dv_xpen<<4) & 0xFFF0;
	*zp++ = ((dm_values.dv_ypen<<4) & 0xFFF0) | VGI_TERMINATE;
	*zp++ = VGI_VA;
	*zp++ = VGI_SHOW( -75, VGI_V_LOAD, VGI_V_X );
	*zp++ = VGI_SHOW( 0, VGI_V_MOVE, VGI_V_Y );
	*zp++ = VGI_SHOW( 75, VGI_V_DRAW, VGI_V_X );
	*zp++ = VGI_SHOW( -75, VGI_V_DRAW, VGI_V_X );
	*zp++ = VGI_SHOW( 75, VGI_V_DRAW, VGI_V_X );
	*zp++ = VGI_SHOW( 0, VGI_V_LOAD, VGI_V_X );
	*zp++ = VGI_SHOW( 75, VGI_V_MOVE, VGI_V_Y );
	*zp++ = VGI_SHOW( -75, VGI_V_DRAW, VGI_V_Y);
	*zp++ = VGI_SHOW( 75, VGI_V_DRAW, VGI_V_Y );
	*zp++ = VGI_SHOW( -75, VGI_V_DT, VGI_V_Y );

	/* Initialize the Displacement registers to center of screen */
	*zp++ = VGI_LD | VGR_DXR;
	*zp++ = 0;			/* dx */
	*zp++ = 0;			/* dy */
	*zp++ = 0;			/* dz */

	*zp++ = VGI_V_CVT( model2view[0]);	/* r11r */
	*zp++ = VGI_V_CVT( model2view[1]);
	*zp++ = VGI_V_CVT( model2view[2]);

	*zp++ = VGI_V_CVT( model2view[4]);
	*zp++ = VGI_V_CVT( model2view[5]);
	*zp++ = VGI_V_CVT( model2view[6]);

	*zp++ = VGI_V_CVT( model2view[8]);
	*zp++ = VGI_V_CVT( model2view[9]);
	*zp++ = VGI_V_CVT( model2view[10]);	/* r33r */

	/* X,Y,Z window limits */
	*zp++ = (1 << 14);		/* WMCR - Window mode control (on) */
	*zp++ = (vg_window[0]<<3);			/* XHR */
	*zp++ = (-2048 << 3 );				/* XLR */
	*zp++ = ( 2047 << 3 );				/* YHR */
	*zp++ = (vg_window[3]<<3);			/* YLR */
	*zp++ = ( 2047 << 3 );				/* ZHR */
	*zp++ = (-2048 << 3 ) | VGI_TERMINATE;		/* ZLR */

	/* Indicate to the rest of the guys where dynamic part starts */
	curzp = zp;
}

/*
 *			V G _ E P I L O G
 */
void
Vg_epilog()
{
	register displaylist_t *zp = curzp;

	/* Branch to beginning of "Mainline" displaylist */
	*zp++ = VGI_LD | VGR_MAR;
	*zp++ = BATODLA(0) | VGI_TERMINATE;
	*zp++ = VGI_NOP;
	curzp = zp;
}

/*
 *  			V G _ O B J E C T
 *  
 *  Set up for an object, transformed as indicated, and with an
 *  object center as specified.  The ratio of object to screen size
 *  is passed in as a convienience.
 *
 *  Returns 0 if object could be drawn, !0 if object was omitted.
 */
int
Vg_object( sp, mat, scale_down, illum_flag )
register struct solid *sp;
mat_t mat;
double scale_down;
{
	register displaylist_t *zp = curzp;
	static vect_t screen_pos;	/* Pos, in (rotated) view space */

	if( ((int)curzp) > (((int)zoombufp)+zoombytes-VG_EXTRA_BYTES) )  {
		return(0);	/* not drawn */
	}
	/* Compute view-space pos of solid's center */
	MAT4X3PNT( screen_pos, mat, sp->s_center );

	/*
	 * If the view co-ordinates of the center point fall
	 * outside the viewing window, discard this solid.
	 */
	if( screen_pos[X] >= VGBOUND || screen_pos[X] <= -VGBOUND )
		return(0);	/* BAD */
	if( screen_pos[Y] >= VGBOUND || screen_pos[Y] <= -VGBOUND )
		return(0);	/* BAD */
	if( screen_pos[Z] >= VGBOUND || screen_pos[Z] <= -VGBOUND )
		return(0);	/* BAD */

	/*
	 * Object will be displayed.
	 */
	*zp++ = VGI_LD | VGR_DXR;
	*zp++ = VGI_V_CVT( screen_pos[X] );
	*zp++ = VGI_V_CVT( screen_pos[Y] );
	*zp++ = VGI_V_CVT( screen_pos[Z] ) | VGI_TERMINATE;

	*zp++ = VGI_LD | VGR_CSR;
	*zp++ = VGI_V_CVT( scale_down ) | VGI_TERMINATE;
	*zp++ = VGI_STSM | VGR_MAR;
	*zp++ = VGI_LD | VGR_MAR;
	*zp++ = BATODLA( sp->s_addr) | VGI_TERMINATE;

	if( illum_flag )  {

		/* If this is the illuminated object, intensify */
		*zp++ = VGI_LD | VGR_IOR;	/* over-ride depth cue */
		*zp++ = 0x7FF0;
		*zp++ = 0x7FF0 | VGI_TERMINATE;

		*zp++ = VGI_STSM | VGR_MAR;
		*zp++ = VGI_LD | VGR_MAR;
		*zp++ = BATODLA( sp->s_addr) | VGI_TERMINATE;

		*zp++ = VGI_STSM | VGR_MAR;
		*zp++ = VGI_LD | VGR_MAR;
		*zp++ = BATODLA( sp->s_addr) | VGI_TERMINATE;

		*zp++ = VGI_STSM | VGR_MAR;
		*zp++ = VGI_LD | VGR_MAR;
		*zp++ = BATODLA( sp->s_addr) | VGI_TERMINATE;

		*zp++ = VGI_STSM | VGR_MAR;
		*zp++ = VGI_LD | VGR_MAR;
		*zp++ = BATODLA( sp->s_addr) | VGI_TERMINATE;

		/* restore depth cueing */
		*zp++ = VGI_LD | VGR_IOR;
		*zp++ = (inten_offset & 0xFFF0);
		*zp++ = (inten_scale & 0xFFF0) | VGI_TERMINATE;
	}

	curzp = zp;
	return(1);	/* OK */
}

/*
 *			V G _ N O R M A L
 *
 * Restore the display processor to a normal mode of operation
 * (ie, not scaled, rotated, displaced, etc).
 * Turns off windowing.
 */
void
Vg_normal()
{
	register displaylist_t *zp = curzp;

	*zp++ = VGI_LD | VGR_CSR;
	*zp++ = 077760;		/* VGI_V_CVT( 1.0 ) */

	*zp++ = 0;		/* dxr, dyr, dzr */
	*zp++ = 0;
	*zp++ = 0;

	*zp++ = 077760;		/* r11r, etc */
	*zp++ = 0;
	*zp++ = 0;

	*zp++ = 0;
	*zp++ = 077760;
	*zp++ = 0;

	*zp++ = 0;
	*zp++ = 0;
	*zp++ = 077760;
	*zp++ = 0 | VGI_TERMINATE;	/* WMCR -- turn off window control */

	*zp++ = VGI_LD | VGR_IOR;	/* Full brightness */
	*zp++ = 077760;
	*zp++ = 077760 | VGI_TERMINATE;

	curzp = zp;
}

/*
 *			V G _ N E W R O T
 *
 * Switch to a new rotation matrix
 */
void
Vg_newrot( rotp )
register mat_t rotp;
{
	register displaylist_t *zp = curzp;

	*zp++ = VGI_LD | VGR_R11R;
	*zp++ = VGI_V_CVT( rotp[0] );
	*zp++ = VGI_V_CVT( rotp[1] );
	*zp++ = VGI_V_CVT( rotp[2] );

	*zp++ = VGI_V_CVT( rotp[4] );
	*zp++ = VGI_V_CVT( rotp[5] );
	*zp++ = VGI_V_CVT( rotp[6] );

	*zp++ = VGI_V_CVT( rotp[8] );
	*zp++ = VGI_V_CVT( rotp[9] );
	*zp++ = VGI_V_CVT( rotp[10] ) | VGI_TERMINATE;

	curzp = zp;
}
/*
 *			V G _ U P D A T E
 *
 * Transmit accumulated displaylist to the display processor.
 */
void
Vg_update()
{

	if( ((int)curzp) > (((int)zoombufp)+zoombytes) )  {
		(void)printf("Vg_update: zoombuf overflow!\n");
	}
	(void)lseek( rvg, 0L, 0 );
	if( (unsigned)write( rvg, (char *)zoombufp, zoombytes )
							!= zoombytes )  {
		perror("Vg_update");
		release();		/* detach this display */
	}
}

/*
 *			V G _ P U T S
 *
 * Output a string into the displaylist.
 * The starting position of the beam is as specified.
 */
/* ARGSUSED */
void
Vg_puts( str, x, y, size, color )
register char *str;
{
	register displaylist_t *zp = curzp;
	register char *ccp;

	*zp++ = VGI_LD | VGR_XR;
	*zp++ = VGI_SHOW( x, 0, 0 );
	*zp++ = VGI_SHOW( y, 0, 0 );
	*zp++ = VGI_SHOW( 0, 0, 0 ) | VGI_TERMINATE;	/* Z co-ordinate */
	switch( size ) {
	case 0:
	default:
		*zp++ = VGI_CH | VGI_CH_BSWAP | VGI_CHS0;
		break;
	case 1:
		*zp++ = VGI_CH | VGI_CH_BSWAP | VGI_CHS1;
		break;
	case 2:
		*zp++ = VGI_CH | VGI_CH_BSWAP | VGI_CHS2;
		break;
	case 3:
		*zp++ = VGI_CH | VGI_CH_BSWAP | VGI_CHS3;
		break;
	}
	ccp = (char *) zp;

	while( *ccp = *str++ )
		ccp++;

	*ccp++ = VGI_CH_END;
	if( (int)ccp & 01 )
		*ccp++ = VGI_CH_END;

	curzp = (displaylist_t *) ccp;
}

/*
 *			V G _ 2 D _ G O T O
 *
 */
void
Vg_2d_line( x1, y1, x2, y2, dashed )
int x1, y1;
int x2, y2;
int dashed;
{
	register displaylist_t *zp = curzp;

	if( dashed )
		*zp++ = VGI_VA | VA_DASH;
	else
		*zp++ = VGI_VA;

	*zp++ = VGI_SHOW( x1, VGI_V_LOAD, VGI_V_X );
	*zp++ = VGI_SHOW( y1, VGI_V_MOVE, VGI_V_Y );
	*zp++ = VGI_SHOW( x2, VGI_V_LOAD, VGI_V_X );
	*zp++ = VGI_SHOW( y2, VGI_V_DT,   VGI_V_Y );

	curzp = zp;
}

/*
 *			V G _ I N P U T
 *
 * Execution must suspend in this routine until a significant event
 * has occured on either the command stream, or a device event has
 * occured, unless "noblock" is set.
 *
 * The GED "generic input" structure is filled in.
 *
 * Returns:
 *	0 if no command waiting to be read,
 *	1 if command is waiting to be read.
 */
Vg_input( cmd_fd, noblock )
{
	static long readfds;
	static struct timeval timeout;
	register int i;
	register long l;

	/*
	 * Check for input on the keyboard or on the polled registers.
	 *
	 * Suspend execution until either
	 *  1)  User types a full line
	 *  2)  A change in peripheral status occurs
	 *  3)  The timelimit on SELECT has expired
	 *
	 * If a RATE operation is in progress (zoom, rotate, slew)
	 * in which the peripherals (rate setting) may not be changed,
	 * but we still have to update the display,
	 * do not suspend execution.
	 */
	if( noblock )
		timeout.tv_sec = 0;
	else
		timeout.tv_sec = 30;
	timeout.tv_usec = 0;

	readfds = (1<<cmd_fd) | (1<<regb);	/* STDIN + REGs */
	(void)select( 32, &readfds, 0L, 0L, &timeout );

	/* Read ALL the registers (of interest) via
	 * the buffered interface.
	 */
	oldregs = regs;
	(void)lseek( regb, 0L, 0 );
	(void)read( regb, (char *)&regs, sizeof regs );
	if( vg_debug )
		regdiff( &oldregs, &regs );

	/*
	 * Build device interface structure for GED
	 */
	inten_offset = regs.vg_d0;			/* For depth cueing */

	dm_values.dv_buttonpress = 0;
	l = (((long)regs.vg_fs1) << 16) | (regs.vg_fs2 & 0xFFFF);
	for( i=0; i < 32; i++ )  if( l & (1L << i) )  {
		dm_values.dv_buttonpress = bmap[i];
		break;
	}

	/*	debounce		 */
	if( l != 0 )  {
		struct vgregb tempregs;
		i = 400;	/* counter */
		do {
			(void)lseek( regb, 0L, 0 );
			(void)read( regb, (char *)&tempregs, sizeof tempregs );
			l = (((long)tempregs.vg_fs1) << 16) |
				(tempregs.vg_fs2 & 0xFFFF);
			if( i-- <= 0 )  {
				(void)printf("Button(s) Stuck: x%lx\n", l );
				(void)sleep(1);
				i = 40;
			}
		} while( l != 0 );
	}
	
	dm_values.dv_xjoy = -vglimit( regs.vg_jy>>4 ) / 2047.0;
	dm_values.dv_yjoy = vglimit( regs.vg_jx>>4 ) / 2047.0;
	dm_values.dv_zjoy = vglimit( regs.vg_jz>>4 ) / 2047.0;

	if( regs.vg_tix & VGR_T_NEAR )  {
		/* Pen is near the tablet */
		dm_values.dv_xpen = regs.vg_tix>>4;	/* will sign extend */
		dm_values.dv_ypen = regs.vg_tiy>>4;
		dm_values.dv_penpress = regs.vg_tix & VGR_T_PRESS;
	}  else  {
		dm_values.dv_xpen = 0;
		dm_values.dv_ypen = 0;
		dm_values.dv_penpress = 0;
	}

	/* Debounce the pen */
	if( regs.vg_tix & VGR_T_PRESS )  {
		i = 500;
		do {
			(void)lseek( regb, 0L, 0 );
			(void)read( regb, (char *)&regs, sizeof regs );
			if( i-- < 0 )  {
				(void)printf("Data Tablet still Pressed\n");
				(void)sleep(1);
				i = 40;
			}
		} while( regs.vg_tix & VGR_T_PRESS );
	}

	/* Zoom */
	dm_values.dv_zoom = vglimit( regs.vg_d4>>4 ) / 2048.0;

	/* Slew */
	dm_values.dv_xslew = vglimit(regs.vg_d1>>4) / 2048.0;
	dm_values.dv_yslew = vglimit(regs.vg_d2>>4) / 2048.0;

	/* Angle/Distance cursor */
	if(
		oldregs.vg_d5 != regs.vg_d5  ||
		oldregs.vg_d6 != regs.vg_d6  ||
		oldregs.vg_d7 != regs.vg_d7  ||
		oldregs.vg_d8 != regs.vg_d8  ||
		oldregs.vg_d9 != regs.vg_d9
	)  {
		dm_values.dv_xadc = regs.vg_d5>>4;
		dm_values.dv_yadc = regs.vg_d6>>4;
		dm_values.dv_1adc = regs.vg_d7>>4;
		dm_values.dv_2adc = regs.vg_d8>>4;
		dm_values.dv_distadc = regs.vg_d9>>4;
		dm_values.dv_flagadc = 1;
	}  else
		dm_values.dv_flagadc = 0;

	if( readfds & (1<<cmd_fd) )
		return(1);		/* command awaits */
	else
		return(0);		/* just peripheral stuff */
}

/*
 *			V G L I M I T
 *
 * VG noise limiter
 */
static
vglimit(i)
register int i;
{
#define NOISE	100		/* DtoA Noise cutoff, needed for joysticks. */

	if( i > NOISE )
		return( i-NOISE );
	if( i < -NOISE )
		return( i+NOISE );
	return(0);
}


static char	*regnames[] = {
	"fs1",
	"fs2",
	"vg_tix",
	"vg_tiy",
	"vg_jx",
	"vg_jy",
	"vg_jz",
	"vg_d0",
	"vg_d1",
	"vg_d2",
	"vg_d3",
	"vg_d4",
	"vg_d5",
	"vg_d6",
	"vg_d7",
	"vg_d8",
	"vg_d9",
	(char *)NULL
};

/*
 *			R E G D I F F
 *
 * Toggled by "X" command.
 * Prints out the differences in the VG device registers from the
 * last iteration to this one, symbolically.
 */
static void
regdiff( oldp, newp )
register short	*oldp;			/* really (struct vgregb *) */
register short	*newp;
{
	register char **regnamp = &regnames[0];

	while( *regnamp != (char *)NULL )  {
		if( *oldp ^ *newp )  {
			(void)printf("%s: %4x %4x\n",
				*regnamp,
				*oldp & 0xFFFF,
				*newp & 0xFFFF );
		}
		regnamp++;
		oldp++;
		newp++;
	}
	(void)printf("----\n");
}

/*
 *			V G _ L I G H T
 */
void
Vg_light( cmd, func )
int cmd;
int func;			/*  BE_ or BV_ function */
{

	switch( cmd )  {

	case LIGHT_RESET:
		lights = 0;
		return;

	case LIGHT_ON:
		lights |= 1L << invbmap[func];
		return;

	case LIGHT_OFF:
		lights &= ~(1L << invbmap[func]);
		return;
	}
}

#define BUFSIZE	5000
static displaylist_t	storage[BUFSIZE];	/* Displaylist  area */

/*
 * Coordinate conversion macros.
 *
 * The co-ordinate system extends from +full scale to
 * -full scale, as the object extends from xmin to xmax.
 * Hence the total co-ordinate distance is 2*full scale.
 *
 * Note that these macros draw the object FULL SCALE.
 * (Ie to occupy the full screen).  dozoom() depends on this.
 *
 *	delta * full scale / (scale/2)
 * gives delta * full scale * 2 / scale
 *
 * 4094 = 2047 * 2
 */
#define XCVT(x)		( (int) (((x) - sp->s_center[0]) * factor ))
#define YCVT(y)		( (int) (((y) - sp->s_center[1]) * factor ))
#define ZCVT(z)		( (int) (((z) - sp->s_center[2]) * factor ))

/*
 * Calculates displaylist memory requirement
 */
unsigned
Vg_cvtvecs( sp )
register struct solid *sp;
{
	static int curx, cury, curz;	/* Current x, y, z beam coordinate */
	unsigned count;
	register displaylist_t	*output;
	register int temp;
	register int outflag = 0;
	register struct veclist *vp;
	static float factor;
	int nvec;

	factor = 4094.0 / sp->s_size;	/* Conversion to -4094 .. +4094 */
	curx = cury = curz = 32767;	/* Undrawable point */
	output = &storage[0];

	/* Draw prototype with VECTOR ABSOLUTE */
	if( sp->s_soldash )
		*output++ = VGI_VA | VA_DOTTED;
	else
		*output++ = VGI_VA;

	nvec = sp->s_vlen;
	for( vp = sp->s_vlist; nvec-- > 0; vp++ )  {
		outflag = 0;
		if ( (temp = XCVT( vp->vl_pnt[0] )) != curx ) {
			*output++ = VGI_SHOW( temp, VGI_V_LOAD, VGI_V_X );
			curx = temp;
			++outflag;
		}
		if ( (temp = YCVT( vp->vl_pnt[1] )) != cury ) {
			*output++ = VGI_SHOW( temp, VGI_V_LOAD, VGI_V_Y );
			cury = temp;
			++outflag;
		}
		if ( (temp = ZCVT( vp->vl_pnt[2] )) != curz ) {
			*output++ = VGI_SHOW( temp, 000000, VGI_V_Z );
			curz = temp;
			++outflag;
		}
		if ( outflag )
			output[-1] = (output[-1] & VGI_V_OPMASK) |
			     ((vp->vl_pen==PEN_UP) ? VGI_V_MOVE : VGI_V_DRAW);
		if( output >= &storage[BUFSIZE-4] )  {
			(void)printf("Vg_cvtvecs:  Displaylist truncated\n");
			break;
		}
	}
	/* Finish off the display subroutine */
	*output++ = VGI_SHOW( curz, VGI_V_DT, VGI_V_Z );	/* end draw */
	*output++ = VGI_RTS;			/* ret to control portion */

	count = (output - &storage[0]) * sizeof (displaylist_t);
	return( count );
}

/*
 * Loads displaylist from storage[]
 */
unsigned
Vg_load( addr, count )
unsigned addr, count;
{
	if( addr ==  0 ) {
		(void)printf("Vg_load: addr=0\n");
		return( 0 );		/* FLAG:  error */
	}
	if(  lseek( rvg, (long)addr, 0 ) < 0L ||
	     ((unsigned)write( rvg, (char *)&storage[0], count )) != count ) {
		(void)printf("Vg_load:  Displaylist Write error\n");
		return( 0 );		/* FLAG:  error */
	}
	return( count );
}

void
Vg_statechange()
{
}

void
Vg_viewchange()
{
}

void
Vg_colorchange()
{
}

void
Vg_debug(lvl)
{
	vg_debug = lvl;
}

void
Vg_window(w)
int w[];
{
	register int i;
	for( i=0; i<6; i++ )
		vg_window[i] = w[i];
}
