/**
 ** ram1.c ---- the mono system RAM 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.
 **
 **/

#include "libgrx.h"
#include "grdriver.h"
#include "allocate.h"
#include "arith.h"
#include "mempeek.h"
#include "memcopy.h"
#include "memfill.h"

#include "fdrivers/rblit_14.h"

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

static INLINE
GrColor readpixel(GrFrame *c,int x,int y)
{
	GR_int8u *ptr;
	GRX_ENTER();
	ptr = (GR_int8u *)&c->gf_baseaddr[0][FOFS(x,y,c->gf_lineoffset)];
	GRX_RETURN((GrColor)( (*ptr >> (7 - (x & 7)) ) & 1));
}

static INLINE
void drawpixel(int x,int y,GrColor color)
{
	GR_int8u *ptr;
	GR_int8u cval;

	GRX_ENTER();
	ptr = (GR_int8u *)&CURC->gc_baseaddr[0][FOFS(x,y,CURC->gc_lineoffset)];
	cval = (color & 1) << (7 - (x &= 7));
	switch(C_OPER(color)) {
	    case C_XOR: *ptr ^=  cval; break;
	    case C_OR:  *ptr |=  cval; break;
	    case C_AND: *ptr &= ~cval; break;
	    default:    *ptr  = (*ptr & (~0x80 >> x)) | cval; break;
	}
	GRX_LEAVE();
}

#define maskoper(d,op,s,msk,SF,DF) do {                       \
    unsigned char _c_ = peek_b##DF(d);                        \
    poke_b##DF((d), (_c_ & ~(msk)) | ((_c_ op (s)) & (msk))); \
  } while (0)
#define maskset(d,c,msk,DF) \
    poke_b##DF((d),(peek_b##DF(d) & ~(msk)) | ((c) & (msk)))

static void drawhline(int x,int y,int w,GrColor color) {
  int oper;

  GRX_ENTER();
  oper = C_OPER(color);
  color &= 1;
  if (!( !color && (oper==C_OR||oper==C_XOR)) && !(color && oper==C_AND) ) {
    GR_int8u lm = 0xff >> (x & 7);
    GR_int8u rm = 0xff << ((-(w + x)) & 7);
    GR_int8u *p = (GR_int8u *)&CURC->gc_baseaddr[0][FOFS(x,y,CURC->gc_lineoffset)];
    GR_repl cv = 0;
    if (color) cv = ~cv;
    w = ((x+w+7) >> 3) - (x >> 3);
    if (w==1) lm &= rm;
    if ( ((GR_int8u)(~lm)) ) {
	switch(oper) {
	  case C_XOR: maskoper(p,^,(GR_int8u)cv,lm,_set,_n); break;
	  case C_OR:  maskoper(p,|,(GR_int8u)cv,lm,_set,_n); break;
	  case C_AND: maskoper(p,&,(GR_int8u)cv,lm,_set,_n); break;
	  default:    maskset(p,(GR_int8u)cv,lm,_n);         break;
	}
	if (!(--w)) goto done;
	++p;
    }
    if ( ((GR_int8u)(~rm)) ) --w;
    if (w) {
	switch(oper) {
	  case C_XOR: repfill_b_xor(p,cv,w); break;
	  case C_OR:  repfill_b_or(p,cv,w);  break;
	  case C_AND: repfill_b_and(p,cv,w); break;
	  default:    repfill_b(p,cv,w);     break;
	}
    }
    if ( ((GR_int8u)(~rm)) ) {
	switch(oper) {
	  case C_XOR: maskoper(p,^,(GR_int8u)cv,rm,_set,_n); break;
	  case C_OR:  maskoper(p,|,(GR_int8u)cv,rm,_set,_n); break;
	  case C_AND: maskoper(p,&,(GR_int8u)cv,rm,_set,_n); break;
	  default:    maskset(p,(GR_int8u)cv,rm,_n);         break;
	}
    }
  }
done:
  GRX_LEAVE();
}

static void drawvline(int x,int y,int h,GrColor color)
{
	unsigned int lwdt, mask, oper;
	char *p;
	GRX_ENTER();
	oper = C_OPER(color);
	color &= 1;
	lwdt = CURC->gc_lineoffset;
	mask = 0x80 >> (x & 7);
	switch (oper) {
	  case C_XOR:
	      /* no need to xor anything with 0 */
	      if (color) {
		p = &CURC->gc_baseaddr[0][FOFS(x,y,lwdt)];
		colfill_b_xor(p,lwdt,mask,h);
	      }
	      break;
	  case C_OR:
	      /* no need to or anything with 0 */
	      if (color) {
	    do_OR:
		p = &CURC->gc_baseaddr[0][FOFS(x,y,lwdt)];
		colfill_b_or(p,lwdt,mask,h);
	      }
	      break;
	  case C_AND:
	      /* no need to and anything with 1 */
	      if (!color) {
	    do_AND:
		mask = ~mask;
		p = &CURC->gc_baseaddr[0][FOFS(x,y,lwdt)];
		colfill_b_and(p,lwdt,mask,h);
	      }
	      break;
	  default:
	      if (color) goto do_OR;
	      goto do_AND;
	}
	GRX_LEAVE();
}

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
#include "fdrivers/generic/bitblt.c"

static void bltr2r(GrFrame *dst,int dx,int dy,
		   GrFrame *src,int x,int y,int w,int h,
		   GrColor op)
{
    GRX_ENTER();
    _GR_rblit_14(dst,dx,dy,src,x,y,w,h,op,1,bitblt);
    GRX_LEAVE();
}


/* -------------------------------------------------------------------- */

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

/* -------------------------------------------------------------------- */

GrFrameDriver _GrFrameDriverRAM1 = {
    GR_frameRAM1,               /* frame mode */
    GR_frameUndef,              /* compatible RAM frame mode */
    FALSE,                      /* onscreen */
    4,                          /* scan line width alignment */
    1,                          /* number of planes */
    1,                          /* bits per pixel */
    16*1024L*1024L,             /* max plane size the code can handle */
    NULL,
    readpixel,
    drawpixel,
    drawline,
    drawhline,
    drawvline,
    drawblock,
    drawbitmap,
    drawpattern,
    bltr2r,
    NULL,
    NULL,
    getindexedscanline,
    _GrFrDrvGenericPutScanline
};

