#include "X.h"
#include "Xmd.h"

#include "misc.h"
#include "regionstr.h"
#include "gcstruct.h"
#include "windowstr.h"
#include "pixmapstr.h"
#include "scrnintstr.h"

#include "vga.h"
#include "../common/mouse.h"

#include "servermd.h"
#include "../../../os/msdos/msdos.h"

union place {
	u_char *ptr;
	struct {
		u_short x, y;
	} win;
};
static void _near vgaFillSolid(int, union place, Pixel, int, int, int, u_short);
static void _near vgaFillTiled(int, union place, int, int, int, int, GCPtr);
static void _near vgaFillStippled(int, union place, int, int, int, int, GCPtr, int);

/* SetSpans -- for each span copy pwidth[i] bits from psrc to pDrawable at
 * ppt[i] using the raster op from the GC.  If fSorted is TRUE, the scanlines
 * are in increasing Y order.
 * Source bit lines are server scanline padded so that they always begin
 * on a word boundary.
 */ 
void
vgaSetSpans(pDrawable, pGC, psrc, ppt, pwidth, nspans, fSorted, limits)
    DrawablePtr		pDrawable;
    GCPtr		pGC;
    unsigned char	*psrc;
    register DDXPointPtr ppt;
    int			*pwidth;
    int			nspans;
    Bool		fSorted;
    BoxPtr		limits;
{
    _segment		base;
    u_char _based(base) *pdstwin;
    u_char		*pdstBase;	/* start of dst bitmap */
    int 		widthDst;	/* width of bitmap in words */
    register RegBoxPtr pbox;
    RegBoxPtr pboxTest, pboxLast;
    register DDXPointPtr pptLast;
    int 		alu;
    RegionPtr 		prgnDst;
    int			xStart, xEnd;
    int			depth;

    alu = pGC->alu;
    prgnDst = ((vgaPrivGC *)(pGC->devPrivates[vgaGCPrivateIndex].ptr))->pCompositeClip;

    pptLast = ppt + nspans;

    if (pDrawable->type == DRAWABLE_WINDOW)
    {
	int x1, x2, y1, y2;

	/* quick check to avoid flashing cursor */
	if (!((WindowPtr)pDrawable)->viewable)
		return;
	base = vgapriv.fb;
	pdstwin = 0;
	widthDst = vgapriv.devKind;
	if (limits) {
		x1 = limits->x1;
		y1 = limits->y1;
		x2 = limits->x2;
		y2 = limits->y2;
	} else {
		/* Turn off cursor anywhere in this window.
		 * That should cover it. */
		x1 = pDrawable->x;
		y1 = pDrawable->y;
		x2 = pDrawable->x + pDrawable->width;
		y2 = pDrawable->y + pDrawable->height;
	}
#ifdef DIRECT_MOUSE
	_asm {
		push	si
		push	di
		mov	cx,x1
		mov	dx,y1
		mov	si,x2
		mov	di,y2
		mov	ax,16
		int	0x33
		pop	di
		pop	si
	}
	/*
	ConditionalOff(pDrawable->x, pDrawable->y,
	    pDrawable->x + pDrawable->width,
	    pDrawable->y + pDrawable->height);
	*/
#else
	if (CUR_OVERLAP(x1, y1, x2, y2))
		HideCursor();
#endif
	depth = 0;
    }
    else
    {
	depth = pDrawable->depth;
	pdstBase = ((PixmapPtr)pDrawable)->devPrivate.ptr;
	widthDst = (int)(((PixmapPtr)pDrawable)->devKind);
    }

    pbox =  REGION_RECTS(prgnDst);
    pboxLast = pbox + REGION_NUM_RECTS(prgnDst);

    if(fSorted)
    {
    /* scan lines sorted in ascending order. Because they are sorted, we
     * don't have to check each scanline against each clip box.  We can be
     * sure that this scanline only has to be clipped to boxes at or after the
     * beginning of this y-band 
     */
	pboxTest = pbox;
	while(ppt < pptLast)
	{
	    pbox = pboxTest;
	    while(pbox < pboxLast)
	    {
		if(pbox->y1 > ppt->y)
		{
		    /* scanline is before clip box */
		    break;
		}
		else if(pbox->y2 <= ppt->y)
		{
		    /* clip box is before scanline */
		    pboxTest = ++pbox;
		    continue;
		}
		else if(pbox->x1 > ppt->x + *pwidth) 
		{
		    /* clip box is to right of scanline */
		    break;
		}
		else if(pbox->x2 <= ppt->x)
		{
		    /* scanline is to right of clip box */
		    pbox++;
		    continue;
		}

		/* at least some of the scanline is in the current clip box */
		xStart = max(pbox->x1, ppt->x);
		xEnd = min(ppt->x + *pwidth, pbox->x2);
		if (!depth) {
		    vgaSetScanline(psrc + (xStart - ppt->x >> 1),
			(char far *)(pdstwin + widthDst * ppt->y + (xStart >> 3)),
			(xStart - ppt->x & 1) << 2, xStart & 7,
			xEnd-xStart, alu);
		} else if (depth == 1) {
		    movebits(psrc + (xStart - ppt->x >> 3),
			pdstBase + widthDst * ppt->y + (xStart >> 3),
			xStart - ppt->x & 7, xStart & 7, xEnd-xStart, alu);
		} else {
		    movebits(psrc + (xStart - ppt->x >> 1),
			(char far *)(pdstBase + widthDst * ppt->y + (xStart >> 1)),
			(xStart - ppt->x & 1) << 2, (xStart & 1) << 2,
			(xEnd-xStart)*depth, alu);
		}
		if(ppt->x + *pwidth <= pbox->x2)
		{
		    /* End of the line, as it were */
		    break;
		}
		else
		    pbox++;
	    }
	    /* We've tried this line against every box; it must be outside them
	     * all.  move on to the next point */
	    ppt++;
	    psrc += PixmapWidthInPadUnits(*pwidth, depth ? depth : 4);
	    pwidth++;
	}
    }
    else
    {
    /* scan lines not sorted. We must clip each line against all the boxes */
	while(ppt < pptLast)
	{
	    if(ppt->y >= 0)
	    {
		
		for(pbox = REGION_RECTS(prgnDst); pbox< pboxLast; pbox++)
		{
		    if(pbox->y1 > ppt->y)
		    {
			/* rest of clip region is above this scanline,
			 * skip it */
			break;
		    }
		    if(pbox->y2 <= ppt->y)
		    {
			/* clip box is below scanline */
			pbox++;
			break;
		    }
		    if(pbox->x1 <= ppt->x + *pwidth && pbox->x2 > ppt->x) {
			xStart = max(pbox->x1, ppt->x);
			xEnd = min(pbox->x2, ppt->x + *pwidth);
			if (!depth) {
			    vgaSetScanline(psrc + (xStart - ppt->x >> 1),
				(char far *)(pdstwin + widthDst * ppt->y +
				(xStart >> 3)),
				(xStart - ppt->x & 1) << 2, xStart & 7,
				xEnd-xStart, alu);
			} else if (depth == 1) {
			    movebits(psrc + (xStart - ppt->x >> 3),
				pdstBase + widthDst * ppt->y + (xStart >> 3),
				xStart - ppt->x & 7, xStart & 7,
				xEnd-xStart, alu);
			} else {
			    movebits(psrc + (xStart - ppt->x >> 1),
				(char far *)(pdstBase + widthDst * ppt->y +
				(xStart >> 1)),
				(xStart - ppt->x & 1) << 2, (xStart & 1) << 2,
				(xEnd-xStart)*depth, alu);
			}
		    }
		}
	    }
	    psrc += PixmapWidthInPadUnits(*pwidth, depth ? depth : 4);
	    ppt++;
	    pwidth++;
	}
    }
#ifdef DIRECT_MOUSE
    /* turn cursor back on */
    if (!depth) {
	    outpw(0x3c4, 0x0f02);	/* plane mask register */
	    ShowCursor();
    }
#endif
}

/* FillSpans -- for each span copy pwidth[i] bits from psrc to pDrawable at
 * ppt[i] using the raster op from the GC.  If fSorted is TRUE, the scanlines
 * are in increasing Y order.
 * Source bit lines are server scanline padded so that they always begin
 * on a word boundary.
 */ 
void
vgaFillSpans(pDrawable, pGC, nspans, ppt, pwidth, fSorted, limits)
    DrawablePtr		pDrawable;
    GCPtr		pGC;
    int			nspans;
    DDXPointPtr		ppt;
    int			*pwidth;
    Bool		fSorted;
    BoxPtr		limits;
{
    int 		widthDst;	/* width of bitmap in words */
    register RegBoxPtr pbox;
    RegBoxPtr pboxTest, pboxLast;
    DDXPointPtr pptLast;
    RegionPtr 		prgnDst;
    unsigned		xStart, xEnd;
    vgaPrivGC		*pPriv = (vgaPrivGC *)(pGC->devPrivates[vgaGCPrivateIndex].ptr);
    int			depth;
    int			xSrc, ySrc;
    int			alu = pGC->alu;
    Pixel		fgPixel = pGC->fgPixel;
    int			style = pGC->fillStyle;
    u_short		pmask = (u_short)pGC->planemask;
    union place place;
    u_char		*pdstBase;

    prgnDst = pPriv->pCompositeClip;
    pptLast = ppt + nspans;
    if (pDrawable->type == DRAWABLE_WINDOW)
    {
	int x1, x2, y1, y2;

	/* quick check to avoid flashing cursor */
	if (!((WindowPtr)pDrawable)->viewable)
		return;
	depth = 0;
	widthDst = vgapriv.devKind;
	xSrc = pDrawable->x;
	ySrc = pDrawable->y;
	if (limits) {
		x1 = limits->x1;
		y1 = limits->y1;
		x2 = limits->x2;
		y2 = limits->y2;
	} else {
		x1 = xSrc;
		y1 = ySrc;
		x2 = xSrc + pDrawable->width;
		y2 = ySrc + pDrawable->height;
	}
#ifdef DIRECT_MOUSE
	{
		_asm	push	si
		_asm	push	di
		_asm	mov	cx,x1
		_asm	mov	dx,y1
		_asm	mov	si,x2
		_asm	mov	di,y2
		_asm	mov	ax,16
		_asm	int	0x33
		_asm	pop	di
		_asm	pop	si
	}
	/*
	ConditionalOff(pDrawable->x, pDrawable->y,
	    pDrawable->x + ((WindowPtr)pDrawable)->clientWinSize.width,
	    pDrawable->y + ((WindowPtr)pDrawable)->clientWinSize.height);
	*/
#else
	if (CUR_OVERLAP(x1, y1, x2, y2))
		HideCursor();
#endif
    } else {
	depth = pDrawable->depth;
	pdstBase = ((PixmapPtr)pDrawable)->devPrivate.ptr;
	widthDst = (int)(((PixmapPtr)pDrawable)->devKind);
	xSrc = 0;
	ySrc = 0;
    }

    pbox =  REGION_RECTS(prgnDst);
    pboxLast = pbox + REGION_NUM_RECTS(prgnDst);

    if (fSorted)
    {
    /* scan lines sorted in ascending order. Because they are sorted, we
     * don't have to check each scanline against each clip box.  We can be
     * sure that this scanline only has to be clipped to boxes at or after the
     * beginning of this y-band 
     */
	pboxTest = pbox;
	while(ppt < pptLast)
	{
	    pbox = pboxTest;
	    while(pbox < pboxLast)
	    {
		if(pbox->y1 > ppt->y)
		{
		    /* scanline is before clip box */
		    break;
		}
		else if(pbox->y2 <= ppt->y)
		{
		    /* clip box is before scanline */
		    pboxTest = ++pbox;
		    continue;
		}
		else if(pbox->x1 > ppt->x + *pwidth) 
		{
		    /* clip box is to right of scanline */
		    break;
		}
		else if(pbox->x2 <= ppt->x)
		{
		    /* scanline is to right of clip box */
		    pbox++;
		    continue;
		}

		/* at least some of the scanline is in the current clip box */
		xStart = max(pbox->x1, ppt->x);
		xEnd = min(ppt->x + *pwidth, pbox->x2);
		if (!depth) {
			place.win.y = ppt->y;
		} else {
			place.ptr = pdstBase + ppt->y * widthDst;
		}

		if (style == FillSolid) {
		    vgaFillSolid(depth, place, fgPixel, xStart,
			xEnd-xStart, alu, pmask);
		} else {
		    int srcy = ppt->y - ySrc - pGC->patOrg.y;
		    int srcx = xStart - xSrc - pGC->patOrg.x;
		    if (style == FillTiled) {
			vgaFillTiled(depth, place, srcx, xStart, xEnd-xStart,
			    srcy, pGC);
		    } else {
			vgaFillStippled(depth, place, srcx, xStart,
			    xEnd-xStart, srcy, pGC, style);
		    }
		}
		if (ppt->x + *pwidth <= pbox->x2)
		{
		    /* End of the line, as it were */
		    break;
		}
		else
		    pbox++;
	    }
	    /* We've tried this line against every box; it must be outside them
	     * all.  move on to the next point */
	    ppt++;
	    pwidth++;
	}
    } else {
    /* scan lines not sorted. We must clip each line against all the boxes */
	pboxTest = pbox;
	while(ppt < pptLast)
	{
	    if(ppt->y >= 0)
	    {
		
		for(pbox = pboxTest; pbox< pboxLast; pbox++)
		{
		    if (pbox->y1 > ppt->y)
		    {
			/* rest of clip region is above this scanline,
			 * skip it */
			break;
		    }
//above this line is ok
		    if (pbox->y2 <= ppt->y)
		    {
			/* clip box is below scanline */
			continue;
		    }
		    if (pbox->x1 < ppt->x + *pwidth &&
		       pbox->x2 > ppt->x) {

			xStart = max(pbox->x1, ppt->x);
			xEnd = min(pbox->x2, ppt->x + *pwidth);
			if (!depth) {
			    place.win.y = ppt->y;
			} else {
			    place.ptr = pdstBase + ppt->y * widthDst;
			}
			if (style == FillSolid) {
		    	    vgaFillSolid(depth, place, fgPixel, xStart,
			    	xEnd-xStart, alu, pmask);
			} else {
//below this line is ok
                            int srcy = ppt->y - ySrc - pGC->patOrg.y;
			    int srcx = xStart - xSrc - pGC->patOrg.x;
			    if (style == FillTiled) {
				vgaFillTiled(depth, place, srcx, xStart,
				    xEnd-xStart, srcy, pGC);
			    } else {
				vgaFillStippled(depth, place, srcx, xStart,
				    xEnd-xStart, srcy, pGC, style);
			    }
			}
		    }
		}
	    }
	ppt++;
	pwidth++;
	}
    }
#ifdef DIRECT_MOUSE
    /* turn cursor back on */
    if (!depth) {
	outpw(0x3c4, 0x0f02);	/* plane mask register */
//above this line is ok
//	_asm˙˙˙˙
//	perhaps inline asm to turn cursor on was here before ..
	    ShowCursor();
    }
#endif
}

/* for used with depth 1 pixmaps */

static char ReduceRop[16][2] = {
	{ RROP_BLACK,	RROP_BLACK },	/* GXclear */
	{ RROP_BLACK,	RROP_NOP },	/* GXand */
	{ RROP_BLACK,	RROP_INVERT },	/* GXandReverse */
	{ RROP_BLACK,	RROP_WHITE },	/* GXcopy */
	{ RROP_NOP,	RROP_BLACK },	/* GXandInverted */
	{ RROP_NOP,	RROP_NOP },	/* GXnoop */
	{ RROP_NOP,	RROP_INVERT },	/* GXxor */
	{ RROP_NOP,	RROP_WHITE },	/* GXor */
	{ RROP_INVERT,	RROP_BLACK },	/* GXnor */
//below this line is ok
	{ RROP_INVERT,	RROP_NOP },	/* GXequiv */
	{ RROP_INVERT,	RROP_INVERT },	/* GXinvert */
	{ RROP_INVERT,	RROP_WHITE },	/* GXorReverse */
	{ RROP_WHITE,	RROP_BLACK },	/* GXcopyInverted */
	{ RROP_WHITE,	RROP_NOP },	/* GXorInverted */
	{ RROP_WHITE,	RROP_INVERT },	/* GXnand */
	{ RROP_WHITE,	RROP_WHITE }	/* GXset */
};

static void _near
vgaFillSolid(depth, place, pixel, doff, n, alu, pmask)
int depth;
union place place;
Pixel pixel;
int doff, n, alu;
u_short pmask;
{
    unsigned mask;
    u_char fgPixel = (u_char)pixel;
if ((pmask & 0xf) != 0xf)
printf("depth=%d, pmask=%x\n", depth, pmask);

    if (!depth) {
	int i;
	_segment base = vgapriv.fb;
	u_char _based(base) *pdst =
	    (u_char _based(base) *)(place.win.y * vgapriv.devKind);

	i = GXtoEGA[alu];
	if (i >= 0) {
		alu = i & EGA_OP;
		switch (i & EGA_PREOP) {
		    case EGA_ZERO:
			fgPixel = 0;
			break;
		    case EGA_ONE:
			fgPixel = 15;
			break;
		    case EGA_INVERT:
			fgPixel = ~fgPixel;
			break;
		}
				/* set-reset register */
		outpw(0x3ce, (u_short)(fgPixel & 0xf) << 8);
		outpw(0x3ce, 0xf01); /* enable set-reset register */
		outpw(0x3ce, alu << 11 | 3); /* op / shift register */
		/* Set to write mode 0 and read mode 0 */
		outpw(0x3ce, 0x0005);
		pdst += doff >> 3;
		doff &= 7;
		if (doff) {
		    mask = 0xff >> doff;
		    n -= 8 - doff;
		    if (n < 0) {
			mask &= 0xff << -n;
			n = 0;
		    }
		    outpw(0x3ce, mask << 8 | 8);
		    /*
		    mask = *pdst;
		    *pdst++ = mask;
		    */
		    {
			_asm	mov	es,base
			_asm	mov	bx,pdst
			_asm	mov	al,es:[bx]
			_asm	mov	es:[bx],al
		    }
		    pdst++;
		}
		doff = n >> 3;
		if (doff) {
		    outpw(0x3ce, 0xff08);
		    {
			_asm	mov	cx,doff
			_asm	mov	es,base
			_asm	mov	di,pdst
			_asm	_L1:
			_asm	mov	al,es:[di]
			_asm	stosb
			_asm	loop	_L1
			_asm	mov	pdst,di
		    }
		    /*
		    while (doff--) {
			    mask = *pdst;
			    *pdst++ = 0;
		    }
		    */
		}
		n &= 7;
		if (n) {
		    outpw(0x3ce, (u_char)~(0xff >> n) << 8 | 8);
		    /*
		    mask = *pdst;
		    *pdst = 0;
		    */
		    {
			_asm	mov	es,base
			_asm	mov	bx,pdst
			_asm	mov	al,es:[bx]
			_asm	mov	es:[bx],al
		    }
		}
		outpw(0x3ce, 0x0001); /* set-reset register */
		outpw(0x3ce, 0x0003); /* op / shift register */
		outpw(0x3ce, 0x0f08); /* mask register */
	} else {
		printf("vgaFillSolid: HELP.  alu=%d\n", alu);
		return;
	}
    } else if (depth == 1) {
	int rrop;
	u_char *pdst = place.ptr + (doff >> 3);
	u_char mask;

	rrop = ReduceRop[alu][fgPixel ? 1 : 0];
	if (rrop == RROP_NOP)
	    return;
	doff &= 7;
	if (doff) {
	    mask = (u_char)(0xff >> doff);
	    doff = 8 - doff;
	    if (n < doff) {
		mask &= (u_char)(0xff << (doff - n));
		n = 0;
	    } else
		n -= doff;
	    if (rrop == RROP_WHITE)
		*pdst |= mask;
	    else if (rrop == RROP_BLACK)
		*pdst &= ~mask;
	    else
		*pdst ^= mask;
	    pdst++;
	}
	doff = n >> 3;
	if (doff) {
	    if (rrop == RROP_WHITE) {
		memset(pdst, 0xff, doff);
		pdst += doff;
	    } else if (rrop == RROP_BLACK) {
		memset(pdst, 0, doff);
		pdst += doff;
	    } else while (doff--) {
		*pdst = (u_char)~*pdst;
		pdst++;
	    }
	}
	n &= 7;
	if (n) {
	    mask = (u_char)(0xff >> n);
	    if (rrop == RROP_WHITE)
		*pdst |= ~mask;
	    else if (rrop == RROP_BLACK)
		*pdst &= mask;
	    else
		*pdst ^= ~mask;
	}
    } else {
	u_char *pdst = place.ptr + (doff >> 1);
	u_char src = (u_char)(((u_char)fgPixel & 0xf) | ((u_char)fgPixel << 4));

	/* depth = 4 into a pixmap */
	doff &= 1;
	if (doff) {
	    n--;
	    movebits(&src, pdst, 0, 4, 4, alu);
	    doff = 0;
	}
	while (n > 1) {
	    movebits(&src, pdst, 0, 0, 8, alu);
	    n -= 2;
	}
	if (n)
	    movebits(&src, pdst, 0, 0, 4, alu);
    }
}

/*
 * Copy one scan line of a tile into a drawable, which may be another
 * pixmap or a window.
 */
static void _near
vgaFillTiled(depth, place, soff, doff, n, srcy, pGC)
int depth;
union place place;	/* where to put it */
int soff, doff, n, srcy;
GCPtr pGC;
{
    PixmapPtr pixmap = pGC->tile.pixmap;
    int alu = pGC->alu;
    u_char *psrc;
    int width = pixmap->drawable.width;
    int i;

    soff += width;
    soff %= width;
    srcy += pixmap->drawable.height;
    srcy %= pixmap->drawable.height;

    psrc = (u_char *)pixmap->devPrivate.ptr + pixmap->devKind * srcy;

    if (!depth) {
	/* Destination is VGA screen, depth is 4. */
	_segment base = vgapriv.fb;
	u_char _based(base) *pdst =
	    (u_char _based(base) *)(place.win.y * vgapriv.devKind);

	pdst += doff >> 3;
	doff &= 7;
	if (soff) {
		i = min((unsigned)n, pixmap->drawable.width-soff);
		vgaSetScanline(psrc + (soff >> 1), (char *)pdst,
			(soff & 1) << 2, doff, i, alu);
		n -= i;
		doff += i;
		pdst += doff >> 3;
		doff &= 7;
	}
	while (n) {
		i = min((unsigned)n, pixmap->drawable.width);
		vgaSetScanline(psrc, (char *)pdst, 0, doff, i, alu);
		n -= i;
		doff += i;
		pdst += doff >> 3;
		doff &= 7;
	}
    } else if (depth == 1) {		/* Bitmap */
	u_char *pdst = place.ptr + (doff >> 3);
	int i;

	doff &= 7;
	if (soff) {
		i = min(n, width - soff);
		movebits(psrc + (soff >> 3), pdst, soff & 7, doff, i, alu);
		n -= i;
		doff += i;
		pdst += doff >> 3;
		doff &= 7;
	}
	while (n) {
		i = min(n, width);
		movebits(psrc, pdst, 0, doff, i, alu);
		n -= i;
		doff += i;
		pdst += doff >> 3;
		doff &= 7;
	}
    } else {			/* Pixmap */
	u_char *pdst = place.ptr + (doff >> 1);

	doff &= 1;
	if (soff) {
		i = min((unsigned)n, pixmap->drawable.width-soff);
		movebits(psrc + (soff >> 1), pdst,
			(soff & 1) << 2, doff*4, i*4, alu);
		n -= i;
		doff += i;
		pdst += doff >> 1;
		doff &= 1;
	}
	while (n) {
		i = min((unsigned)n, pixmap->drawable.width);
		movebits(psrc, pdst, 0, doff*4, i*4, alu);
		n -= i;
		doff += i;
		pdst += doff >> 1;
		doff &= 1;
	}
    }
}

/*
 * pixmap is of depth 1.
 */
static void _near
vgaFillStippled(depth, place, soff, doff, n, srcy, pGC, style)
int depth;
union place place;
int soff, doff, n, srcy;
GCPtr pGC;
int style;
{
    PixmapPtr bitmap = pGC->stipple;
    int alu = pGC->alu;
    u_char *psrc;
    int width = bitmap->drawable.width;
    u_char fgPixel = (u_char)pGC->fgPixel;
    u_char bgPixel = (u_char)pGC->bgPixel;

    soff += width;
    soff %= width;
    srcy += bitmap->drawable.height;
    srcy %= bitmap->drawable.height;

    psrc = (u_char *)bitmap->devPrivate.ptr + srcy * bitmap->devKind;

    if (depth == 1) {		/* Bitmap */
	int fgRrop, bgRrop;
	u_char *pdst = place.ptr + (doff >> 3);

	doff &= 7;
	fgRrop = ReduceRop[alu][fgPixel ? 1 : 0];
	bgRrop = style == FillOpaqueStippled ?
	    ReduceRop[alu][bgPixel ? 1 : 0] : RROP_NOP;
	if (fgRrop == bgRrop) {
	    vgaFillSolid(depth, place, fgPixel, doff, n, alu, 0);
	} else {
	    int i;

	    if (style == FillOpaqueStippled) {
		if (bgPixel)
			alu = InverseAlu[alu];
	    } else {
		alu = fgRrop == RROP_WHITE  ? GXor :
			 fgRrop == RROP_BLACK  ? GXandInverted :
			 fgRrop == RROP_INVERT ? GXxor :
			 GXnoop;
	    }
	    if (soff) {
		i = min(n, width - soff);
		movebits(psrc + (soff >> 3), pdst, soff & 7, doff, i, alu);
		n -= i;
		doff += i;
		pdst += (doff >> 3);
		doff &= 7;
	    }
	    while (n) {
		i = min(n, width);
		movebits(psrc, pdst, 0, doff, i, alu);
		n -= i;
		doff += i;
		pdst += (doff >> 3);
		doff &= 7;
	    }
	}
    } else if (!depth) {	/* Window */
	_segment base = vgapriv.fb;
	u_char _based(base) *pdst =
	    (u_char _based(base) *)(place.win.y * vgapriv.devKind);
	u_char *pSrc = psrc + (soff >> 3);
	int saven, savedoff, savesoff;
	int i;

	savedoff = doff;
	pdst += doff >> 3;
	doff &= 7;
	i = GXtoEGA[alu];
	if (i < 0) {
printf("vgaFillStippled: can't handle alu %d\n", alu);
		return;
	}
	alu = i & EGA_OP;
	switch (i & EGA_PREOP) {
	    case EGA_ZERO:
		fgPixel = 0;
		bgPixel = 0;
		break;
	    case EGA_ONE:
		fgPixel = 15;
		bgPixel = 15;
		break;
	    case EGA_INVERT:
		fgPixel = (u_char)~fgPixel;
		bgPixel = (u_char)~bgPixel;
		break;
	}
	/* Do fg pixel first */
	outpw(0x3ce, (u_short)(fgPixel & 0xf) << 8);
	outpw(0x3ce, 0xf01); /* enable set-reset register */
	outpw(0x3ce, 0x0305);	/* write mode 3 */
	saven = n;
	savesoff = soff;
	while (n) {
		i = min(n, width-soff);
		soff &= 7;
		egacopybits(pSrc, pdst, soff, doff, i, alu);
		soff = 0;
		n -= i;
		doff += i;
		pdst += doff >> 3;
		doff &= 7;
		pSrc = psrc;
	}
	if (style == FillOpaqueStippled) {
	    /* Now do background */
	    outpw(0x3ce, (u_short)(bgPixel & 0xf) << 8);
	    n = saven;
	    soff = savesoff;
	    doff = savedoff;
	    pSrc = psrc + (soff >> 3);
	    pdst = (u_char _based(base) *)(place.win.y * vgapriv.devKind);
	    pdst += doff >> 3;
	    while (n) {
		i = min(n, width-soff);
		soff &= 7;
		egacopyinvert(pSrc, pdst, soff, doff, i, alu);
		soff = 0;
		n -= i;
		doff += i;
		pdst += doff >> 3;
		doff &= 7;
		pSrc = psrc;
	    }
	}
    } else {			/* Pixmap */
	/* Dest depth is 4.  Source is Stipple. */
	unsigned int smask = 0x80 >> soff;
	unsigned char dst, src;
	u_char *pdst = place.ptr + (doff >> 1);

	pdst += doff >> 1;
	doff &= 1;
	while (n--) {
	    dst = doff ? (u_char)(*pdst & 0xf) : (u_char)(*pdst >> 4);
	    src = (u_char)(*psrc & smask);
	    if (src || style == FillOpaqueStippled) {
		src = src ? fgPixel : bgPixel;
	    } else {
		if (doff) {
		    doff = 0;
		    pdst++;
		} else {
		    doff = 1;
		}
		smask >>= 1;
		if (!smask) {
		    smask = 0x80;
		    psrc++;
		}
		continue;
	    }
	    switch (alu) {
		case GXclear:
		    dst = 0;
		    break;
		case GXand:
		    dst &= src;
		    break;
		case GXandReverse:
		    dst = (u_char)(src & ~dst);
		    break;
		case GXcopy:
		    dst = src;
		    break;
		case GXandInverted:
		    dst &= ~src;
		    break;
		case GXxor:
		    dst ^= src;
		    break;
		case GXor:
		    dst |= src;
		    break;
		case GXnor:
		    dst = (u_char)(~src & ~dst);
		    break;
		case GXequiv:
		    dst ^= ~src;
		    break;
		case GXinvert:
		    dst = (u_char)~dst;
		    break;
		case GXorReverse:
		    dst = (u_char)(src | ~dst);
		    break;
		case GXcopyInverted:
		    dst = (u_char)~src;
		    break;
		case GXorInverted:
		    dst |= ~src;
		    break;
		case GXnand:
		    dst &= src;
		    break;
		case GXset:
		    dst = 0xf;
		    break;
	    }
	    if (doff) {
		*pdst = (u_char)(*pdst & 0xf0 | dst);
		doff = 0;
		pdst++;
	    } else {
		*pdst = (u_char)(*pdst & 0xf | dst << 4);
		doff = 1;
	    }
	    smask >>= 1;
	    if (!smask) {
		smask = 0x80;
		psrc++;
	    }
	}
    }
}
