/*	CGA.c -  Low level drivers for the CGA board
*/


#include "lib.h"
#include "vgr.h"

int cga_init();
int cga_write_row();
int cga_clear();
int cga_clr_point();
int cga_xor_point();
int cga_get_point();
int cga_set_point();
int cga_mode();
int cga_null();
int cga_palette(); /* not tested! */
int cga_movmem();
int cga_peekb();
int cga_pokeb();

static int mode;
static int (*cga_func[])() = {
	cga_init,       cga_clear,     cga_set_point, cga_clr_point,
	cga_xor_point,  cga_get_point, cga_write_row, cga_null,
	cga_null,       cga_mode,      cga_movmem, cga_peekb, cga_pokeb,
	cga_null,       cga_palette    };


static unsigned char far * far *cga_column;


static int cga_null()
{
	return ERROR;
}


int cga_peekb( p )
unsigned char far *p;
{
#asm
	les   bx, dword ptr 4[bp]
	mov   dx, 03dah
x1:	in    al, dx
	test  al, 1
	jnz   x1
x2:	in    al, dx
	test  al, 1
	jz    x2
	mov   al, es:byte ptr [bx]
	sub ah,ah
#endasm
}


int cga_pokeb( p, b )
unsigned char far *p;
unsigned char b;
{
#asm
	mov   ah,byte ptr 8[bp]
	les   bx,dword ptr 4[bp]
	mov   dx, 03dah
y1:	in    al, dx
	test  al, 1
	jnz   y1
y2:	in    al, dx
	test  al, 1
	jz    y2
	mov   es:byte ptr [bx],ah
#endasm
}


/*	WARNING: cga_movmem() doesn't check for overlapping blocks!!!!
*/

int cga_movmem( s, d, n )
unsigned char far *s, *d;
int n;
{
	for ( ; n--; s++, d++ )
	{
#asm
	les   bx, dword ptr 4[bp]
	mov   ah, es:byte ptr [bx]
	les   bx, dword ptr 8[bp]
	mov   dx, 03dah
z1:	in    al, dx
	test  al, 1
	jnz   z1
z2:	in    al, dx
	test  al, 1
	jz    z2
	mov   es:byte ptr [bx],ah
#endasm
	};
}

int cga_init()
{
	void *malloc();
	int row;

	if ( !cga_column )
	   cga_column = CASTUCFPP malloc( sizeof CASTUCFP * 200 );

	if ( !cga_column )
	   return ERROR;

	for ( row = 0; row < 200; row++ )
	   cga_column[row] = CASTUCFP BASE_CGA + ((uint)row >> 1)*80l + (row & 1)*0x2000;

	VGR_NBPL = VGR_NCOLORS = 
	VGR_HRES = VGR_VRES = 0;   /* must set mode first! */

	movmem( cga_func, vgr_func, sizeof(vgr_func) );
	return OK;
}


/*	Warning: NOT TESTED
*/
int cga_palette( bg, fg )
int fg, bg;
{
	if ( !mode )
	   return OK;  /* no meaning if not in 320x200x4 mode */

	outportb( 0x3d9, ((uint)(fg & 0x07)<<5) | (bg & 0x0f) );
	return OK;
}


int cga_write_row( row, prow, nbytes )
register unsigned int nbytes;
unsigned char far *prow;
int row;
{
	cga_movmem( prow, cga_column[row], nbytes );
}


int cga_clear()
{
	int i;

	for ( i=0; i < 0x4000; i++ )
	   cga_pokeb( CASTUCFP (BASE_CGA + (long)i), 0 );
}


int cga_clr_point( x, y )
unsigned int x, y;
{
	unsigned char far *p;

	if ( mode )
	{  p = cga_column[y] + (x>>2);
	   cga_pokeb( p, cga_peekb(p) & ~(0xc0 >> ((x & 0x03) << 1)) );
	} else 
	{  p = cga_column[y] + (x>>3);
	   cga_pokeb( p, cga_peekb(p) & ~(0x80 >> (x & 0x07)) );
	};
}


int cga_xor_point( x, y, color )
unsigned int x, y, color;
{
	cga_set_point( x, y, cga_get_point( x, y ) ^ color );
}


int cga_get_point( x, y )
unsigned int x, y;
{
	uint i;

	if ( mode )
	{  i = (unsigned int)(x & 0x03) << 1;
	   return (uint)(cga_peekb(cga_column[y]+(x>>2))&(0xc0>>i)) >> (6-i);
	} else 
	   return !!( cga_peekb( cga_column[y] + (x>>3) ) 
	                                   & (0x80 >> (x & 0x07)));
}


int cga_set_point( x, y, color )
unsigned int x, y, color;
{
	unsigned char far *p;
	unsigned char i;

	if ( !mode )
	{  p = cga_column[y] + (x>>3);
	   return cga_pokeb( p, cga_peekb(p) | (0x80 >> (x & 0x07)) );
	};

	p = cga_column[y] + (x>>2);
	i = (x & 3) << 1;
	cga_pokeb( p, cga_peekb(p) & ~(0xc0 >> i) );
	cga_pokeb( p, cga_peekb(p) | ((color & 0x03) << (6-i)) );
}


int cga_mode( m )
int m;
{
	if ( m == MODE_TEXT0 )
	   return vgr_mode(3);

	if ( m == MODE_APA3 )   /* 320x200x4 */
	{  VGR_HRES = 320;
	   VGR_VRES = 200;
	   VGR_NCOLORS = 4;
	   VGR_NBPL =  80;
	   mode = 1;
	   return vgr_mode(4);
	};

	if ( m == MODE_APA2 )   /* 640x200x2 */
	{  VGR_HRES = 640;
	   VGR_VRES = 200;
	   VGR_NCOLORS = 2;
	   VGR_NBPL =  80;
	   mode = 0;
	   return vgr_mode(6);
	};

	return ERROR;  /* mode not currently supported */
}


