/**
 ** driver32.h ---- the 16M color padded SVGA linear frame buffer driver
 **                 standard routines for 32H and 32L support
 **
 ** 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)
 ** Andrzej Lawa [FidoNet: Andrzej Lawa 2:480/19.77]
 **
 **/

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

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

/* #define FAR_ACCESS for video access routines */

#ifdef FAR_ACCESS
# define peek32          peek_l_f
# define poke32_xor      poke_l_f_xor
# define poke32_or       poke_l_f_or
# define poke32_and      poke_l_f_and
# define poke32          poke_l_f
# ifdef colfill_l_f
#  define colfill32_xor  colfill_l_f_xor
#  define colfill32_or   colfill_l_f_or
#  define colfill32_and  colfill_l_f_and
#  define colfill32      colfill_l_f
# endif /* defined colfill_l */
# ifdef repfill_l_f
#  define repfill32_xor  repfill_l_f_xor
#  define repfill32_or   repfill_l_f_or
#  define repfill32_and  repfill_l_f_and
#  define repfill32      repfill_l_f
# endif /* defined repfill_l */
# define SETFARSEL(sel)  setup_far_selector(sel)
# if defined(__GNUC__) && defined(__i386__)
#   define ASM_386_SEL  I386_GCC_FAR_SELECTOR
# endif /* GCC i386 */
#else /* defined FAR_ACCESS */
# define peek32          peek_l
# define poke32_xor      poke_l_xor
# define poke32_or       poke_l_or
# define poke32_and      poke_l_and
# define poke32          poke_l
# ifdef colfill_l
#  define colfill32_xor  colfill_l_xor
#  define colfill32_or   colfill_l_or
#  define colfill32_and  colfill_l_and
#  define colfill32      colfill_l
# endif /* defined colfill_l */
# ifdef repfill_l
#  define repfill32_xor  repfill_l_xor
#  define repfill32_or   repfill_l_or
#  define repfill32_and  repfill_l_and
#  define repfill32      repfill_l
# endif /* defined repfill_l */
# define SETFARSEL(sel)
#endif

#ifndef ASM_386_SEL
# define ASM_386_SEL
#endif

static INLINE
GrColor readpixel(GrFrame *c,int x,int y)
{
	char *pp;
	GRX_ENTER();
#ifdef FAR_ACCESS
	pp = &SCRN->gc_baseaddr[0][FOFS(x,y,SCRN->gc_lineoffset)];
	SETFARSEL(SCRN->gc_selector);
#else
/* problem with LFB_BY_NEAR_POINTER here? Does c always point to screen? */
	pp = &c->gf_baseaddr[0][FOFS(x,y,c->gf_lineoffset)];
#endif
	GRX_RETURN(PIX2COL(peek32(pp)));
}

static INLINE
void drawpixel(int x,int y,GrColor color)
{
	char *ptr;
	int op;
	GRX_ENTER();
	ptr  = &CURC->gc_baseaddr[0][FOFS(x,y,CURC->gc_lineoffset)];
	op   = C_OPER(color);
	color= COL2PIX(color);
	SETFARSEL(CURC->gc_selector);
	switch(op) {
	    case C_XOR: poke32_xor(ptr,color); break;
	    case C_OR:  poke32_or( ptr,color); break;
	    case C_AND: poke32_and(ptr,color); break;
	    default:    poke32(    ptr,color); break;
	}
	GRX_LEAVE();
}

#ifdef colfill32
static void drawvline(int x,int y,int h,GrColor color)
{
	unsigned lwdt;
	char *pp;
	int op;

	GRX_ENTER();
	lwdt = CURC->gc_lineoffset;
	pp   = &CURC->gc_baseaddr[0][FOFS(x,y,CURC->gc_lineoffset)];
	op   = C_OPER(color);
	color= COL2PIX(color);
	SETFARSEL(CURC->gc_selector);
	switch(op) {
	    case C_XOR: colfill32_xor(pp,lwdt,color,h); break;
	    case C_OR:  colfill32_or( pp,lwdt,color,h); break;
	    case C_AND: colfill32_and(pp,lwdt,color,h); break;
	    default:    colfill32(    pp,lwdt,color,h); break;
	}
	GRX_LEAVE();
}
#else
static
#include "fdrivers/generic/vline.c"
#endif

#ifdef repfill32
static void drawhline(int x,int y,int w,GrColor color)
{
	int op;
	char *pp;
	GR_repl cval;

	GRX_ENTER();
	pp   = &CURC->gc_baseaddr[0][FOFS(x,y,CURC->gc_lineoffset)];
	op   = C_OPER(color);
	color= COL2PIX(color);
	cval = freplicate_l(color);
	SETFARSEL(CURC->gc_selector);
	switch(op) {
	    case C_XOR: repfill32_xor(pp,cval,w); break;
	    case C_OR:  repfill32_or( pp,cval,w); break;
	    case C_AND: repfill32_and(pp,cval,w); break;
	    default:    repfill32(    pp,cval,w); break;
	}
	GRX_LEAVE();
}

static void drawblock(int x,int y,int w,int h,GrColor color)
{
	unsigned skip;
	int op;
	char *pp;
	GR_repl cval;

	GRX_ENTER();
	skip  = CURC->gc_lineoffset;
	pp    = &CURC->gc_baseaddr[0][FOFS(x,y,skip)];
	skip -= w<<2;
	op    = C_OPER(color);
	color = COL2PIX(color);
	cval  = freplicate_l(color);
	SETFARSEL(CURC->gc_selector);
	switch(op) {
	    case C_XOR: do { int ww = w;
			     repfill32_xor(pp,cval,ww);
			     pp += skip;
			} while(--h != 0);
			break;
	    case C_OR:  do { int ww = w;
			     repfill32_or(pp,cval,ww);
			     pp += skip;
			} while(--h != 0);
			break;
	    case C_AND: do { int ww = w;
			     repfill32_and(pp,cval,ww);
			     pp += skip;
			} while(--h != 0);
			break;
	    default:    do { int ww = w;
			     repfill32(pp,cval,ww);
			     pp += skip;
			} while(--h != 0);
			break;
	}
	GRX_LEAVE();
}
#else
static
#include "fdrivers/generic/hline.c"

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


#if defined(__GNUC__) && defined(__i386__)
static void drawline(int x,int y,int dx,int dy,GrColor color)
{
	struct {
	    int errsub;         /* subtract from error term */
	    int erradd;         /* add to error term when carry */
	    int offset1;        /* add to pointer if no carry on error term */
	    int offset2;        /* add to pointer if carry / banking dir */
	} lndata;
	int  op,npts,error,xstep;
	char *ptr;

	GRX_ENTER();
	op = C_OPER(color);
	color = COL2PIX(color);

#       ifdef __GNUC__
#       ifdef __i386__
#       define ASM_LINE1(OPC) asm volatile(""              \
	"   .align 2,0x90                             \n"  \
	"0: "#OPC"l %6,"ASM_386_SEL"(%0)              \n"  \
	"   subl %7,%2                                \n"  \
	"   jb   1f                                   \n"  \
	"   leal -4(%3),%3                            \n"  \
	"   decl %1                                   \n"  \
	"   jne  0b                                   \n"  \
	"   jmp  2f                                   \n"  \
	"   .align 2,0x90                             \n"  \
	"1: addl 4  + %7,%2                           \n"  \
	"   addl 12 + %7,%3                           \n"  \
	"   decl %1                                   \n"  \
	"   jne  0b                                   \n"  \
	"2: "                                              \
	 :  "=r" (ptr),         "=r" (npts), "=r" (error)  \
	 :  "0"  ((long)(ptr)), "1"  (npts), "2"  (error), \
	    "r"  ((long)(color)),      "o"  (lndata)       \
	)
#       define ASM_LINE2(OPC) asm volatile(""              \
	"   .align 2,0x90                             \n"  \
	"0: "#OPC"l %6,"ASM_386_SEL"(%0)              \n"  \
	"   subl %7,%2                                \n"  \
	"   jb   1f                                   \n"  \
	"   addl 8 + %7,%3                            \n"  \
	"   decl %1                                   \n"  \
	"   jne  0b                                   \n"  \
	"   jmp  2f                                   \n"  \
	"   .align 2,0x90                             \n"  \
	"1: addl 4  + %7,%2                           \n"  \
	"   addl 12 + %7,%3                           \n"  \
	"   decl %1                                   \n"  \
	"   jne  0b                                   \n"  \
	"2: "                                              \
	 :  "=r" (ptr),         "=r" (npts), "=r" (error)  \
	 :  "0"  ((long)(ptr)), "1"  (npts), "2"  (error), \
	    "r"  ((long)(color)),      "o"  (lndata)       \
	)
#       endif
#       endif

	if(dy < 0) {
	    y -= (dy = (-dy));
	    x -= (dx = (-dx));
	}
	if(dx < 0) {
	    xstep = (-4);
	    dx    = (-dx);
	} else
	    xstep = 4;

	ptr = &CURC->gc_baseaddr[0][FOFS(x,y,CURC->gc_lineoffset)];
	SETFARSEL(CURC->gc_selector);
	if(dx > dy) {
	    npts  = dx +  1;
	    error = dx >> 1;
	    lndata.errsub  = dy;
	    lndata.erradd  = dx;
	    lndata.offset1 = xstep;
	    lndata.offset2 = CURC->gc_lineoffset + xstep;
	    if(xstep < 0) {
		lndata.offset1 = 1;
		switch(op) {
		    case C_XOR: ASM_LINE1(xor); break;
		    case C_OR:  ASM_LINE1(or);  break;
		    case C_AND: ASM_LINE1(and); break;
		    default:    ASM_LINE1(mov); break;
		}
		goto done;
	    }
	}
	else {
	    npts  = dy +  1;
	    error = dy >> 1;
	    lndata.errsub  = dx;
	    lndata.erradd  = dy;
	    lndata.offset1 = CURC->gc_lineoffset;
	    lndata.offset2 = CURC->gc_lineoffset + xstep;
	}
	switch(op) {
	    case C_XOR: ASM_LINE2(xor); break;
	    case C_OR:  ASM_LINE2(or);  break;
	    case C_AND: ASM_LINE2(and); break;
	    default:    ASM_LINE2(mov); break;
	}
done:   GRX_LEAVE();
}
#else
static
#include "fdrivers/generic/line.c"
#endif

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
#ifdef FAR_ACCESS
	  _GrFrDrvPackedBitBltV2V_LFB(
#else
	  _GrFrDrvPackedBitBltR2R(
#endif
	    dst,(dx << 2),dy,
	    src,(sx << 2),sy,
	    (w << 2),h,
	    op
	);
	GRX_LEAVE();
}

#ifdef FAR_ACCESS

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_LFB(
	    dst,(dx << 2),dy,
	    src,(sx << 2),sy,
	    (w << 2),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_LFB(
	    dst,(dx << 2),dy,
	    src,(sx << 2),sy,
	    (w << 2),h,
	    op
	);
	GRX_LEAVE();
}
#endif /* FAR_ACCESS */

