/**
 ** svga24.c ---- the 16M color Super VGA frame driver
 **
 ** Copyright (c) 1995 Csaba Biegl, 820 Stirrup Dr, Nashville, TN 37221
 ** [e-mail: csaba@vuse.vanderbilt.edu].
 **
 ** This file is part of the GRX graphics library.
 **
 ** The GRX graphics library is free software; you can redistribute it
 ** and/or modify it under some conditions; see the "copying.grx" file
 ** for details.
 **
 ** This library is distributed in the hope that it will be useful,
 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 **
 ** Contributions by: (See "doc/credits.doc" for details)
 ** Hartmut Schirmer (hsc@techfak.uni-kiel.de)
 **
 **/

#include "libgrx.h"
#include "grdriver.h"
#include "arith.h"
#include "mempeek.h"
#include "memfill.h"
#include "access24.h"

/* helper ... */
#define MULT3(x)     ( (x)+(x)+(x) )
#define REMAIN3(x)   ( (x) % 3 )

/* frame offset address calculation */
#define FOFS(x,y,lo) umuladd32((y),(lo),MULT3(x))


static INLINE
GrColor readpixel(GrFrame *c,int x,int y)
{
	GrColor cval;
	GR_int32u offs;
	int  bank;
	char *p;
	GRX_ENTER();
	cval = (GrColor)0;
	offs = FOFS(x,y,SCRN->gc_lineoffset);
	bank = BANKNUM(offs);
	p    = &SCRN->gc_baseaddr[0][BANKPOS(offs)];
	setup_far_selector(SCRN->gc_selector);
	CHKBANK(bank);
	switch (BANKLFT(offs)) {
	  case 1 : cval = peek_b_f(p);
		   ++bank;
		   SETBANK(bank);
		   p = SCRN->gc_baseaddr[0];
		   cval |= ((GR_int32u)peek_w_f(p)) << 8;
		   break;
	  case 2 : cval = peek_w_f(p);
		   ++bank;
		   SETBANK(bank);
		   p = SCRN->gc_baseaddr[0];
		   WR24BYTE(cval,2,peek_b_f(p));
		   break;
#ifdef PEEK_24_F_READS_ONE_MORE
	  case 3 : cval = peek_w_f(p);
		   WR24BYTE(cval,2,peek_b_f(p+2));
		   break;
#endif
	  default: cval = peek_24_f(p);
		   break;
	}
	GRX_RETURN(cval);
}

static INLINE
void drawpixel(int x,int y,GrColor color)
{
	GR_int32u offs;
	int bank;
	char *p;
	GRX_ENTER();
	offs = FOFS(x,y,CURC->gc_lineoffset);
	p    = &CURC->gc_baseaddr[0][BANKPOS(offs)];
	setup_far_selector(CURC->gc_selector);
	bank = BANKNUM(offs);
	CHKBANK(bank);
	switch(C_OPER(color)) {
	  case C_XOR:
	      switch (BANKLFT(offs)) {
		case 1:
		  poke_b_f_xor(p,(GR_int8u)color);
		  SETBANK(++bank);
		  p = SCRN->gc_baseaddr[0];
		  poke_b_f_xor(p,RD24BYTE(color,1));
		  poke_b_f_xor(p+1,RD24BYTE(color,2));
		  break;
		case 2:
		  poke_w_f_xor(p,(GR_int16u)color);
		  SETBANK(++bank);
		  p = SCRN->gc_baseaddr[0];
		  poke_b_f_xor(p,RD24BYTE(color,2));
		  break;
		default:
		  poke_24_f_xor(p,color);
		  break;
	      }
	      break;
	  case C_OR:
	      switch (BANKLFT(offs)) {
		case 1:
		  poke_b_f_or(p,(GR_int8u)color);
		  SETBANK(++bank);
		  p = SCRN->gc_baseaddr[0];
		  poke_b_f_or(p,RD24BYTE(color,1));
		  poke_b_f_or(p+1,RD24BYTE(color,2));
		  break;
		case 2:
		  poke_w_f_or(p,(GR_int16u)color);
		  SETBANK(++bank);
		  p = SCRN->gc_baseaddr[0];
		  poke_b_f_or(p,RD24BYTE(color,2));
		  break;
		default:
		  poke_24_f_or(p,color);
		  break;
	      }
	      break;
	  case C_AND:
	      switch (BANKLFT(offs)) {
		case 1:
		  poke_b_f_and(p,(GR_int8u)color);
		  SETBANK(++bank);
		  p = SCRN->gc_baseaddr[0];
		  poke_b_f_and(p,RD24BYTE(color,1));
		  poke_b_f_and(p+1,RD24BYTE(color,2));
		  break;
		case 2:
		  poke_w_f_and(p,(GR_int16u)color);
		  SETBANK(++bank);
		  p = SCRN->gc_baseaddr[0];
		  poke_b_f_and(p,RD24BYTE(color,2));
		  break;
		default:
		  poke_24_f_and(p,color);
		  break;
	      }
	      break;
	  default:
	      switch (BANKLFT(offs)) {
		case 1:
		  poke_b_f(p,(GR_int8u)color);
		  SETBANK(++bank);
		  p = SCRN->gc_baseaddr[0];
		  poke_b_f(p,RD24BYTE(color,1));
		  poke_b_f(p+1,RD24BYTE(color,2));
		  break;
		case 2:
		  poke_w_f(p,(GR_int16u)color);
		  SETBANK(++bank);
		  p = SCRN->gc_baseaddr[0];
		  poke_b_f(p,RD24BYTE(color,2));
		  break;
		default:
		  poke_24_f(p,color);
		  break;
	      }
	      break;
	}
	GRX_LEAVE();
}


static void drawhline(int x,int y,int w,GrColor color)
{
      char *p;
      int op, bank;
      unsigned int  w1, w2;
      GR_int32u offs;
      GR_int8u c2;

      GRX_ENTER();
      op   =  C_OPER(color);
      offs = FOFS(x,y,CURC->gc_lineoffset);
      p    = &CURC->gc_baseaddr[0][BANKPOS(offs)];
      setup_far_selector(CURC->gc_selector);

      c2 = RD24BYTE(color,2);
      bank = BANKNUM(offs);
      CHKBANK(bank);
      w = MULT3(w);
#     ifndef GRX_HAVE_FAST_REPFILL24
	if (c2 == RD24BYTE(color,0) && c2 == RD24BYTE(color,1) ) {
	   GR_repl cval = freplicate_b(c2);
	   w1 = BANKLFT(offs);
	   w2 = w - (w1 = umin(w,w1));
	   for (;;) {
	     switch(op) {
		 case C_XOR: repfill_b_f_xor(p,cval,w1); break;
		 case C_OR:  repfill_b_f_or( p,cval,w1); break;
		 case C_AND: repfill_b_f_and(p,cval,w1); break;
		 default:    repfill_b_f(    p,cval,w1); break;
	     }
	     if (!w2) break;
	     w1 = w2;
	     w2 = 0;
	     ++bank;
	     SETBANK(bank);
	     p = CURC->gc_baseaddr[0];
	   }
	   goto done;
	}
#     endif
      for (;;) {
	w1 = BANKLFT(offs);
	w1 = umin(w,w1);
	w -= w1;
	if (w1 <= 3) {
	  /* make sure we don't run in problems on first pixel */
	  w2 = w1;
	  switch (w1) {
	    case 1 : switch (op) {
		       case C_XOR: poke_b_f_xor(p,color); break;
		       case C_OR:  poke_b_f_or( p,color); break;
		       case C_AND: poke_b_f_and(p,color); break;
		       default:    poke_b_f_set(p,color); break;
		     }
		     break;
	    case 2 : switch (op) {
		       case C_XOR: poke_w_f_xor(p,color); break;
		       case C_OR:  poke_w_f_or( p,color); break;
		       case C_AND: poke_w_f_and(p,color); break;
		       default:    poke_w_f_set(p,color); break;
		     }
		     break;
	    case 3 : switch (op) {
		       case C_XOR: poke_24_f_xor(p,color); break;
		       case C_OR:  poke_24_f_or( p,color); break;
		       case C_AND: poke_24_f_and(p,color); break;
		       default:    poke_24_f_set(p,color); break;
		     }
		     goto next_bank;
	  }
	  goto complete;
	}
	if (w) w2 = REMAIN3(w1);
	else   w2 = 0;
	switch (op) {
	  case C_XOR: repfill_24_f_xor(p,color,w1); break;
	  case C_OR:  repfill_24_f_or( p,color,w1); break;
	  case C_AND: repfill_24_f_and(p,color,w1); break;
	  default:    repfill_24_f_set(p,color,w1); break;
	}
	if (w2) {
	  /* complete pixel on next bank */
  complete:
	  bank++;
	  SETBANK(bank);
	  p = CURC->gc_baseaddr[0];
	  switch (w2) {
	    case 1 : offs = 2;
		     switch (op) {
		       case C_XOR: poke_w_f_xor(p,color>>8); break;
		       case C_OR:  poke_w_f_or( p,color>>8); break;
		       case C_AND: poke_w_f_and(p,color>>8); break;
		       default:    poke_w_f_set(p,color>>8); break;
		     }
		     if ( !(w-=2) ) goto done;
		     p += 2;
		     break;
	    case 2 : offs = 1;
		     switch (op) {
		       case C_XOR: poke_b_f_xor(p,c2); break;
		       case C_OR:  poke_b_f_or( p,c2); break;
		       case C_AND: poke_b_f_and(p,c2); break;
		       default:    poke_b_f_set(p,c2); break;
		     }
		     if ( !(--w) ) goto done;
		     ++p;
		     break;
	  }
	  continue;
	}
  next_bank:
	if (!w) goto done;
	bank++;
	SETBANK(bank);
	p = CURC->gc_baseaddr[0];
	offs = 0;
      }
  done:
      GRX_LEAVE();
}

static
#include "fdrivers/generic/vline.c"

static
#include "fdrivers/generic/block.c"

static
#include "fdrivers/generic/line.c"

static
#include "fdrivers/generic/bitmap.c"

static
#include "fdrivers/generic/pattern.c"

static void bitblt(GrFrame *dst,int dx,int dy,GrFrame *src,int sx,int sy,int w,int h,GrColor op)
{
	GRX_ENTER();
	if(GrColorMode(op) == GrIMAGE) _GrFrDrvGenericBitBlt(
	    dst,dx,dy,
	    src,sx,sy,
	    w,h,
	    op
	);
	else _GrFrDrvPackedBitBltV2V(
	    dst,MULT3(dx),dy,
	    src,MULT3(sx),sy,
	    MULT3(w),h,
	    op
	);
	GRX_LEAVE();
}

#ifndef GRX_USE_RAM3x8

static void bltv2r(GrFrame *dst,int dx,int dy,GrFrame *src,int sx,int sy,int w,int h,GrColor op)
{
	GRX_ENTER();
	if(GrColorMode(op) == GrIMAGE) _GrFrDrvGenericBitBlt(
	    dst,dx,dy,
	    src,sx,sy,
	    w,h,
	    op
	);
	else _GrFrDrvPackedBitBltV2R(
	    dst,MULT3(dx),dy,
	    src,MULT3(sx),sy,
	    MULT3(w),h,
	    op
	);
	GRX_LEAVE();
}

static void bltr2v(GrFrame *dst,int dx,int dy,GrFrame *src,int sx,int sy,int w,int h,GrColor op)
{
	GRX_ENTER();
	if(GrColorMode(op) == GrIMAGE) _GrFrDrvGenericBitBlt(
	    dst,dx,dy,
	    src,sx,sy,
	    w,h,
	    op
	);
	else _GrFrDrvPackedBitBltR2V(
	    dst,MULT3(dx),dy,
	    src,MULT3(sx),sy,
	    MULT3(w),h,
	    op
	);
	GRX_LEAVE();
}

#endif /* !GRX_USE_RAM3x8 */

GrFrameDriver _GrFrameDriverSVGA24 = {
    GR_frameSVGA24,             /* frame mode */
#ifdef GRX_USE_RAM3x8
    GR_frameRAM3x8,             /* compatible RAM frame mode */
#else
    GR_frameRAM24,              /* compatible RAM frame mode */
#endif
    TRUE,                       /* onscreen */
    4,                          /* line width alignment */
    1,                          /* number of planes */
    24,                         /* bits per pixel */
    24*16*1024L*1024L,          /* max plane size the code can handle */
    NULL,
    readpixel,
    drawpixel,
    drawline,
    drawhline,
    drawvline,
    drawblock,
    drawbitmap,
    drawpattern,
    bitblt,
#ifdef GRX_USE_RAM3x8
    _GrFrDrvGenericBitBlt,
    _GrFrDrvGenericBitBlt,
#else
    bltv2r,
    bltr2v,
#endif
    _GrFrDrvGenericGetIndexedScanline,
    _GrFrDrvGenericPutScanline
};
