/*
	SCCS id:	@(#) fb_ik.c	1.24
	Last edit: 	4/14/86 at 16:38:53	G S M
	Retrieved: 	6/12/86 at 10:52:38
	SCCS archive:	/vld/src/libfb/s.fb_ik.c

	Authors:	Mike J. Muuss
			Gary S. Moss
			U. S. Army Ballistic Research Laboratory
			Aberdeen Proving Ground
			Maryland 21005-5066
*/
#if ! defined( lint )
static
char	sccsTag[] = "@(#) fb_ik.c	1.24	last edit 4/14/86 at 16:38:53";
#endif
/*
 * This module is used when pre-setting the Ikonas FBC
 * (Frame Buffer Controller) to a known state.
 * The values for this table are derived from the
 * Ikonas-supplied program FBI, for compatability.
 * At present, three modes can be set:
 *	0 - LORES, 30 hz, non-interlaced
 *	1 - LORES, 60 hz, interlaced
 *	2 - HIRES, 30 hz, non-interlaced
 *
 * All that is provided is a prototype for the FBC registers;
 * the user is responsible for changing them (zooming, etc),
 * and writing them to the FBC.
 *
 * Written by Mike Muuss, BRL, 10/26/83, to enable "C" programs to
 * take their choice of frame-buffer operation, without requiring
 * manual intervention with FBI.
 *
 * The # of scan lines has been changed slightly, to permit the Dunn
 * camera to sync to the signals.  No image changes should be visible.
 *
 * Updated for new lseek() kernel interface and freedom from ADAGE
 * library by Mike Muuss, BRL, 01/07/84.
 */
#include <stdio.h>
#include <sys/types.h>
#if defined( BSD )
#include <sys/ioctl.h>
#else
#include <sys/_ioctl.h> /* GSM : _ needed for Sys V emulation.		*/
#endif
#include <fb.h>
#include "./fb_ik.h"

static struct ik_fbc ikfbc_setup[3] = {
    {
	0,	32,
	511,	511,
	0,	4067,
	0,	0,
	300,	560,
	0,	FBCH_PIXELCLOCK(45) | FBCH_DRIVEBPCK,
	0,	32
    }, {
	0,	68,
	511,	1023,
	0,	4063,
	0,	0,
	144,	1143,		/* was 144, 1167 */
	FBC_RS343 | FBC_NOINTERLACE, FBCH_PIXELCLOCK(18) | FBCH_DRIVEBPCK,
	0,	32
    }, {
	0,	64,
	1023,	1023,
	0,	4033,
	0,	0,
	144,	1144,		/* was 144, 1166 */
	FBC_HIRES | FBC_RS343, FBCH_PIXELCLOCK(19) | FBCH_DRIVEBPCK,
	0,	64
    }
};

struct	ik_fbc	ikfbcmem;	/* Global memory of current FBC state	*/

static Pixel	*background = (Pixel *) NULL;

/* Current values initialized in _ik_init().				*/
static int	x_origin, y_origin;
static int	x_zoom, y_zoom;
static int	x_window, y_window;

static long cursor[32] = {
#include "cursorbits.h"
};

/* Global Flags */
short	*_ikUBaddr;		/* Mapped-in Ikonas address */

void		_iksetbackground();

int
_ik_open()
{
#if defined( vax )
	if( ioctl( _fbfd, IKIOGETADDR, &_ikUBaddr ) < 0 )  {
		perror( "_ik_open ioctl(IKIOGETADDR)" );
	}
#endif
	return	0;
}

/*	_ i k _ i n i t ( )
 */
int
_ik_init()
	{	register int	i;
		long	xbsval[34];
		long	wmask, shade;
		int	mode;
	x_zoom = y_zoom = 1;
	x_origin = y_origin = _fbsize / 2;
	x_window = y_window = 0;

	switch (_fbsize)
		{
	case 512:
		mode = 1;
		break;
	case 1024:
		mode = 2;
		break;
	default:
		(void) fprintf(stderr, "Bad fbsize (%d)\n", _fbsize);
		return	-1;
		}
	if( lseek( _fbfd, FBC*4L, 0 ) == -1 )
		{
		perror( "ik_init lseek" );
		return	-1;
		}
	if(	write(_fbfd,(char *)&ikfbc_setup[mode],sizeof(struct ik_fbc))
	    !=  sizeof(struct ik_fbc) )
		{
		perror( "ik_init write" );
		return	-1;
		}
	ikfbcmem = ikfbc_setup[mode];

	/* Build an identity for the crossbar switch */
	for( i=0; i < 34; i++ )
#ifndef pdp11
		xbsval[i] = (long)i;
#else
		xbsval[i] = (((long)i)<<16);	/* word swap.. */
#endif
	if( lseek( _fbfd, XBS*4L, 0 ) == -1 )
		{
		perror( "ik_init lseek" );
		return	-1;
		}
	if( write( _fbfd, (char *)xbsval, sizeof(xbsval) ) != sizeof(xbsval) )
		{
		perror( "ik_init write" );
		return	-1;
		}

	/* Initialize the LUVO crossbar switch, too */
#ifndef pdp11
	xbsval[0] = 0x24L;		/* 1:1 mapping, magic number */
#else
	xbsval[0] = (0x24L<<16);
#endif
	if( lseek( _fbfd, LUVOXBS*4L, 0 ) == -1 )
		{
		perror( "ik_init lseek" );
		return	-1;
		}
	if( write( _fbfd, (char *)xbsval, sizeof(long) ) != sizeof(long) )
		{
		perror( "ik_init write" );
		return-1;
		}

	/* Initialize the write mask and the shade register (GM64) */
	wmask = -1L;		/* all bits on */
	shade = 0L;		/* all bits off */
	/***** This is harder;  need to use special write mode,
	 * This is not just an address to write to... Sigh. ****/

	if( _iksetcursor( cursor ) == -1 )	/* dump in default cursor */
		return	-1;
	if( lseek( _fbfd, 0L, 0 ) == -1 )	/* To start of pixels */
		{
		perror( "ik_init lseek" );
		return	-1;
		}
	return	0;
	}

/*	_ i k c l e a r ( )
 */
_ikclear()
	{
	int mode = 1;

	/* If _ik_setbackground() was called with non-black color, must
		use DMAs to fill the frame buffer since there is no
		hardware support for this.
	 */
	if( background != (Pixel *) NULL )
		return	fast_dma_bg();
	if( _fbsize == 1024 )
		mode = 2;
	ikfbc_setup[mode].fbc_Lcontrol |= FBC_AUTOCLEAR;

	if( lseek( _fbfd, FBC*4L, 0 ) == -1 )
		{
		perror( "ikclear lseek" );
		return	-1;
		}
	if(	write( _fbfd, &ikfbc_setup[mode], sizeof(struct ik_fbc) )
	     != sizeof(struct ik_fbc)
		)
		{
		perror( "ikclear write" );
		return	-1;
		}

	sleep(1);	/* Give the FBC a chance to act */
	ikfbc_setup[mode].fbc_Lcontrol &= ~FBC_AUTOCLEAR;
	if( lseek( _fbfd, FBC*4L, 0 ) == -1 )
		{
		perror( "ikclear lseek" );
		return	-1;
		}
	if(	write( _fbfd, &ikfbc_setup[mode], sizeof(struct ik_fbc) )
	    !=	sizeof(struct ik_fbc)
		)
		{
		perror( "ikclear write" );
		return	-1;
		}
	return	0;
	}

#if defined( vax )
/* Write 1 Ikonas pixel using PIO rather than DMA */
_ikwpixel(x, y, data)
int	x, y;
long	*data;
{
	register int i;
	register struct ikdevice *ikp = (struct ikdevice *)_ikUBaddr;

	i = 10000;
	while( i-- && !(ikp->ubcomreg & IKREADY) )  /* NULL */ 	;
	if( i == 0 )  {
		(void) fprintf(stderr,"IK READY stayed low\n" );
		return	-1;
	}

	if( _fbsize == 1024 )  {
		ikp->ikcomreg = IKPIX | IKWRITE | IKINCR | IKHIRES;
		ikp->ikloaddr = x;
		ikp->ikhiaddr = y;
	} else {
		ikp->ikcomreg = IKPIX | IKWRITE | IKINCR;
		ikp->ikloaddr = x<<1;
		ikp->ikhiaddr = y<<1;
	}
	ikp->ubcomreg = 1;			/* GO */
	ikp->datareg = (u_short)*data;
	ikp->datareg = (u_short)(*data>>16);
	if( ikp->ubcomreg & IKERROR )  {
		(void) fprintf(stderr,"IK ERROR bit on PIO\n" );
		return	-1;
	}
	return	0;	/* GSM : I guess this is a normal exit.		*/
}

/* Read 1 Ikonas pixel using PIO rather than DMA */
long
_ikrpixel(x, y, datap)
int	x, y;
long	*datap;
{
	register int i;
	register struct ikdevice *ikp = (struct ikdevice *)_ikUBaddr;
	register long data;

	i = 10000;
	while( i-- && !(ikp->ubcomreg & IKREADY) )  /* NULL */ 	;
	if( i == 0 )  {
		(void) fprintf( stderr, "IK READY stayed low (setup)\n" );
		return	-1;
	}

	if( _fbsize == 1024 )  {
		ikp->ikcomreg = IKPIX | IKINCR | IKHIRES;
		ikp->ikloaddr = x;
		ikp->ikhiaddr = y;
	} else {
		ikp->ikcomreg = IKPIX | IKINCR;
		ikp->ikloaddr = x<<1;
		ikp->ikhiaddr = y<<1;
	}
	ikp->ubcomreg = 1;			/* GO */

	i = 10000;
	while( i-- && !(ikp->ubcomreg & IKREADY) )  /* NULL */ 	;
	if( i == 0 )  {
		(void) fprintf( stderr, "IK READY stayed low (after)\n" );
		return	-1;
	}

	data = ikp->datareg;			/* low */
	data |= (((long)ikp->datareg)<<16);	/* high */
	if( ikp->ubcomreg & IKERROR )  {
		(void) fprintf( stderr, "IK ERROR bit on PIO\n" );
		return	-1;
	}
	*datap = data;
	return	0;
}
#endif vax

/*	_ i k z o o m ( )
	Update fbc_[xy]zoom registers in FBC
 */
_ikzoom( x, y )
register int	x, y;
	{
	/* Ikonas can only zoom to 1, 2, 4, 8 or 16 times actual size.	*/
	if( x < 2 )
		x = 1;
	else
	if( x < 4 )
		x = 2;
	else
	if( x < 8 )
		x = 4;
	else
	if( x < 16 )
		x = 8;
	else
		x = 16;
	if( y < 2 )
		y = 1;
	else
	if( y < 4 )
		y = 2;
	else
	if( y < 8 )
		y = 4;
	else
	if( y < 16 )
		y = 8;
	else
		y = 16;
	x_zoom = x;
	y_zoom = y;

	/* Ikonas zoom factor is actually (factor - 1)!			*/
	x--;
	y--;
#ifdef BROKEN
	/* From RDS 3000 Programming Reference Manual, June 1982, section
		5.3 Notes, page 5-12.
		In HIRES mode, horizontal zoom must be accomplished as follows:
		1.   To go from a ratio of 1:1 to 2:1 you must double the
			pixel clock rate, rather than use the zoom register.
		2.   Thereafter you can increment the zoom register, whicle
			leaving the pixel clock rate doubled.
	  ----------
	  Actually, life is not that simple...
	*/
	if( _fbsize == 1024 )
		{
		switch( x )
			{
		case 0 : /* 1:1 ratio, x & y zoom is 0, clock rate is 20 */
			ikfbcmem.fbc_Hcontrol = 
				FBCH_PIXELCLOCK(19) | FBCH_DRIVEBPCK;
			break;
		case 1 : /* 2:1 ratio, x & y zoom is 1, clock rate is 22 */
			ikfbcmem.fbc_Hcontrol = 
				FBCH_PIXELCLOCK(38) | FBCH_DRIVEBPCK;
			x--;
			break;
		case 3 : 
			ikfbcmem.fbc_Hcontrol = 
				FBCH_PIXELCLOCK(38) | FBCH_DRIVEBPCK;
			x--;
			break;
		case 7 : /* > 2:1 ratio, x lags 1 behind y, clock rate is 28 */
		case 15 :
			ikfbcmem.fbc_Hcontrol = 
				FBCH_PIXELCLOCK(38) | FBCH_DRIVEBPCK;
			x--;
			break;
		default :
			(void) fprintf( stderr, "Illegal zoom factor!\n" );
			return	-1;
			}
		if( lseek( _fbfd, FBCVC*4L, 0 ) == -1 )
			{
			perror( "ikzoom lseek" );
			return	-1;
			}
		if( write( _fbfd, &ikfbcmem.fbc_Lcontrol, 4 ) != 4 )
			{
			perror( "ikzoom write" );
			return	-1;
			}
		}
#endif
	ikfbcmem.fbc_xzoom = x;
	ikfbcmem.fbc_yzoom = y;
	if( lseek( _fbfd, FBCZOOM*4L, 0 ) == -1 )
		{
		perror( "ikzoom lseek" );
		return	-1;
		}
	if( write( _fbfd, &ikfbcmem.fbc_xzoom, 4 ) != 4 )
		{
		perror( "ikzoom write" );
		return	-1;
		}
	return	0;
	}

/*	_ i k w i n d o w ( )
	Set FBC window location to specified values so that <x,y> are
	at screen center.
 */
_ikwindow( x, y )
register int	x, y;
	{
	/* Window relative to image center.				*/
	x_window = x -= x_origin;
	y_window = y -= y_origin;

	if( _fbsize != 1024 )
		x *= 4;
	switch( x_zoom )
		{
	case 16 :
		if( _fbsize == 1024 )
			ikfbcmem.fbc_xwindow = 478 + x;	/* was 480 */
		else
			ikfbcmem.fbc_xwindow = 960 + x;
		break;
	case 8 :
		if( _fbsize == 1024 )
			ikfbcmem.fbc_xwindow = 446 + x;	/* was 447 */
		else
			ikfbcmem.fbc_xwindow = 896 + x;
		break;
	case 4 :
		if( _fbsize == 1024 )
			ikfbcmem.fbc_xwindow = 382 + x;	/* was 383 */
		else
			ikfbcmem.fbc_xwindow = 768 + x;
		break;
	case 2 :
		if( _fbsize == 1024 )
			ikfbcmem.fbc_xwindow = 256 + x;	/* was 254 */
		else
			ikfbcmem.fbc_xwindow = 511 + x;
		break;
	default :
		ikfbcmem.fbc_xwindow = x;
		break;
		}
	switch( y_zoom )
		{
	case 16 :
		if( _fbsize == 1024 )
			ikfbcmem.fbc_ywindow = 4573 + y; /* was 4574 */
		else
			ikfbcmem.fbc_ywindow = 4334 + y;
		break;
	case 8 :
		if( _fbsize == 1024 )
			ikfbcmem.fbc_ywindow = 4537 + y; /* was 4540 */
		else
			ikfbcmem.fbc_ywindow = 4316 + y;
		break;
	case 4 :
		if( _fbsize == 1024 )
			ikfbcmem.fbc_ywindow = 4465 + y; /* was 4472 */
		else
			ikfbcmem.fbc_ywindow = 4280 + y;
		break;
	case 2 :
		if( _fbsize == 1024 )
			ikfbcmem.fbc_ywindow = 4321 + y; /* was 4331 */
		else
			ikfbcmem.fbc_ywindow = 4208 + y;
		break;
	default :
		if( _fbsize == 1024 )
			ikfbcmem.fbc_ywindow = 4033 + y;
		else
			ikfbcmem.fbc_ywindow = 4064 + y;
		break;
		}
	if( lseek( _fbfd, FBCWL*4L, 0 ) == -1 )
		{
		perror( "ikwindow lseek" );
		return	-1;
		}

	if( write( _fbfd, &ikfbcmem.fbc_xwindow, 4 ) != 4 )
		{
		perror( "ikwindow write" );
		return	-1;
		}
	return	0;
	}

/*	_ i k c u r s o r ( )
	Place cursor at image coordinates x and y.
	IMPORTANT : Ikonas cursor addressing is in screen space.
 */
_ikcursor( mode, x, y )
int	mode, x, y;
	{	register int	x_cursor_offset, y_cursor_offset;
	/* Map image coordinates to screen space.			*/
	if( _fbsize == 1024 )
		{
		switch( x_zoom )
			{
		case 16 :
			x_cursor_offset = 9;
			y_cursor_offset = 39;
			break;
		case 8 :
			x_cursor_offset = -4;
			y_cursor_offset = 43;
			break;
		case 4 :
			x_cursor_offset = -10;
			y_cursor_offset = 44;
			break;
		case 2 :
			x_cursor_offset = -17;
			y_cursor_offset = 45;
			break;
		case 1 :
			x_cursor_offset = -16;
			y_cursor_offset = 46;
			break;
			}
		}
	else
		{
		x_cursor_offset = X_CURSOR_OFFSET;
		y_cursor_offset = Y_CURSOR_OFFSET;
		}
	x = x_origin + ((x - x_origin) - x_window)*x_zoom + x_cursor_offset;
	y = y_origin + ((y - y_origin) - y_window)*y_zoom + y_cursor_offset;

	if (mode)
		ikfbcmem.fbc_Lcontrol |= FBC_CURSOR;
	else
		ikfbcmem.fbc_Lcontrol &= ~FBC_CURSOR;
	ikfbcmem.fbc_xcursor = x&01777;
	ikfbcmem.fbc_ycursor = y&01777;

	if( lseek( _fbfd, FBCVC*4L, 0 ) == -1 )
		{
		perror( "_ikcursor lseek" );
		return	-1;
		}
	if( write( _fbfd, &ikfbcmem.fbc_Lcontrol, 8 ) != 8 )
		{
		perror( "_ikcursor write" );
		return	-1;
		}
	return	0;
	}

/*	_ i k s c u r s o r ( )
	Place cursor at SCREEN coordinates x and y.
 */
_ikscursor( mode, x, y )
int	mode, x, y;
	{
	if( _fbsize == 1024 && y_zoom == 1 )
		y += 30;
	if (mode)
		ikfbcmem.fbc_Lcontrol |= FBC_CURSOR;
	else
		ikfbcmem.fbc_Lcontrol &= ~FBC_CURSOR;
	ikfbcmem.fbc_xcursor = x&01777;
	ikfbcmem.fbc_ycursor = y&01777;

	if( lseek( _fbfd, FBCVC*4L, 0 ) == -1 )
		{
		perror( "_ikcursor lseek" );
		return	-1;
		}
	if( write( _fbfd, &ikfbcmem.fbc_Lcontrol, 8 ) != 8 )
		{
		perror( "_ikcursor write" );
		return	-1;
		}
	return	0;
	}

/*	_ i k s e t c u r s o r ( )
 */
_iksetcursor(bitmap)
long	*bitmap;
	{
	register int i;
	long	cursorarray[256];

#ifdef pdp11
	for(i = 0; i < 256; i++)
		cursorarray[i] = ((bitmap[i/8]>>((i%8)*4))&017L)<<16;
#else
	for (i = 0; i < 256; i++)
		cursorarray[i] = (bitmap[i/8]>>((i%8)*4))&017L;
#endif
	if( lseek( _fbfd, FBCCD*4L, 0 ) == -1 )
		{
		perror( "iksetcursor lseek" );
		return	-1;
		}
	if( write( _fbfd, cursorarray, 1024 ) != 1024 )
		{
		perror( "iksetcursor write" );
		return	-1;
		}
	return	0;
	}

/*	_ i k _ p o k e _ m a p ( )
 	GSM : write one color map entry.
	Page selects the color map; 0, 1, 2, or  3.
	Offset indexes into the map.
 */
_ik_poke_map( cp, page, offset )
register Pixel	*cp;
long	page, offset;
	{
	long	lp;

	lp = RGB10( cp->red<<2, cp->green<<2, cp->blue<<2 );
	lseek( _fbfd, (LUVO + page*256 + offset)*4L, 0);
	if( write( _fbfd, (char *) &lp, 4 ) != 4 )
		{
		perror( "_ik_poke_map write" );
		return	0;
		}
	return	1;
	}

/*	_ i k _ w m a p ( )
	GSM : added explicit returns, -1 for error, 0 otherwise.
 */
_ik_wmap(cp)
register ColorMap *cp;
	{
	long cmap[1024];
	register long *lp = cmap;
	register int i;

	/* Note that RGB10(r,g,b) flips to cmap order (b,g,r).		*/
	if( cp == (ColorMap *) NULL )
		{
		for( i=0; i < 256; i++ )
			*lp++ = RGB10( i<<2, i<<2, i<<2 );
		}  
	else
		{
		for( i=0; i < 256; i++ )
			*lp++ = RGB10( cp->cm_red[i]<<2,
				       cp->cm_green[i]<<2,
				       cp->cm_blue[i]<<2 );
		}

#ifdef pdp11
	/* 16-bit-word-in-long flipping for PDP's */
	for( i=0; i < 256; i++ )
		{
		register struct twiddle
			{
			short rhs;
			short lhs;
			}  *cmp = &cmap[i];
		register short temp;
		temp = cmp->rhs;
		cmp->rhs = cmp->lhs;
		cmp->lhs = temp;
		}
#endif
	/*
	 * Replicate first copy of color map onto second copy,
	 * and also do the "overlay" portion too.
	 * TODO:  Load inverse map into "overlay" (for cursor),
	 * and load standard film map into second map.
	 */
	for( i=0; i < 256; i++ )
		{
		cmap[i+512] = cmap[i];
		cmap[i+512+256] = cmap[i+256] = ~cmap[i];
		}
	if( lseek( _fbfd, LUVO*4L, 0) == -1 )
		{
		perror( "_ik_wmap lseek" );
		return	-1;
		}
	if( write( _fbfd, cmap, 1024*4 ) != 1024*4 )
		{
		perror( "_ik_wmap write" );
		return	-1;
		}
	return	0;
	}

/*	_ i k _ r m a p ( )
	GSM : added explicit returns, -1 for error, 0 otherwise.
 */
_ik_rmap(cp)
register ColorMap *cp;
	{
	register int i;
	long cmap[1024];

	if( lseek( _fbfd, LUVO*4L, 0) == -1 )
		{
		perror( "ik_rmap lseek" );
		return	-1;
		}
	if( read( _fbfd, cmap, 1024*4 ) != 1024*4 )
		{
		perror( "ik_rmap read" );
		return	-1;
		}
#ifndef pdp11
	for( i=0; i < 256; i++ )
		{
		cp->cm_red[i] = (cmap[i]>>(2+0))  & 0xFF;
		cp->cm_green[i] = (cmap[i]>>(4+8))  & 0xFF;
		cp->cm_blue[i] = (cmap[i]>>(6+16)) & 0xFF;
		}
#else
	for( i=0; i < 256; i++ )
		{
		register struct twiddle
			{
			short rhs;
			short lhs;
			}  *cmp = &cmap[i];
		cp->cm_red[i] = (cmp->rhs>>(2+0))  & 0xFF;
		cp->cm_green[i] = ((cmp->lhs<<4)&0xF0) | ((cmp->rhs>>12)&0xF);
		cp->cm_blue[i] = (cmp->lhs>>(6+0)) & 0xFF;
		}
#endif
	return	0;
	}

void
_ik_setbackground (pp)
Pixel	*pp;
	{
	if( pp->red == 0 && pp->green == 0 && pp->blue == 0 )
		/* Take advantage of AUTOCLEAR in _ikclear().		*/
		background = (Pixel *) NULL;
	else
		background = pp;
	return;
	}
