
static char rcsid[] = "grab.c";


#include "grab.h"
#include "misc.h"

#include <sys/ioccom.h>			     /* gotta load first so we can fix defs. */


#ifdef __STDC__				     /* sys/ioccom.h is really brain-damaged */
					     /* and has very non-ANSI leanings */
#undef  _IO				     /* (it relies on macro replacement  */
#undef  _IOR				     /* within a character constant) */
#undef  _IORN
#undef  _IOW
#undef  _IOWN
#undef  _IOWR
#undef  _IOWRN
					     /* these are ANSI. Yea! */
#define _IO(x,y)      (_IOC_VOID|((*(#x))<<8)|y)
#define _IOR(x,y,t)   (_IOC_OUT|((sizeof(t)&_IOCPARM_MASK)<<16)|((*(#x))<<8)|y)
#define _IORN(x,y,t)  (_IOC_OUT|(((t)&_IOCPARM_MASK)<<16)|((*(#x))<<8)|y)
#define _IOW(x,y,t)   (_IOC_IN|((sizeof(t)&_IOCPARM_MASK)<<16)|((*(#x))<<8)|y)
#define _IOWN(x,y,t)  (_IOC_IN|(((t)&_IOCPARM_MASK)<<16)|((*(#x))<<8)|y)
#define _IOWR(x,y,t)  (_IOC_INOUT|((sizeof(t)&_IOCPARM_MASK)<<16)|((*(#x))<<8)|y)
#define _IOWRN(x,y,t) (_IOC_INOUT|(((t)&_IOCPARM_MASK)<<16)|((*(#x))<<8)|y)
#endif


#include "vfc_lib.h"
/*#include "vfc_ioctls.h"*/
/*#include <sys/types.h>*/
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>

#define FULL_WIDTH    VFC_YUV_WIDTH	     /* 720 */
#define FULL_HEIGHT   VFC_NTSC_HEIGHT	     /* 480 */
#define VBL 7200			     /* number of samples before image data */


local void do_grab_ultra_general(GrayImage im,
                                  volatile u_int *frame1, volatile u_int *frame2,
                                  int skipx, int skipy,
                                  int src_x, int src_y,
                                  int dst_x, int dst_y,
                                  int width, int height);
local void do_grab_general(GrayImage im, volatile u_int *frame1, volatile u_int *frame2,
			    register int skipx, register int skipy);
local void do_grab_1x1(GrayImage im, volatile u_int *frame1, volatile u_int *frame2);
local void do_grab_2x2(GrayImage im, volatile u_int *frame1, volatile u_int *frame2);
local void do_grab_3x3(GrayImage im, volatile u_int *frame1, volatile u_int *frame2);
local void do_grab_4x4(GrayImage im, volatile u_int *frame1, volatile u_int *frame2);
local void do_grab_8x8(GrayImage im, volatile u_int *frame1, volatile u_int *frame2);




local struct {
  boolean connected;                         /* connected ?? */
  int fvid;                                  /* file descriptor */
  int vprt;                                  /* port (1 .. 3) */
  volatile u_int *f1, *f2;                   /* frame pointers */
  int status[4];                             /* use [1..3] - index by port */
} VideoPixInfo = { connected: FALSE };



export inline int grabVideoPixWidth(int skipx)
{
  return (FULL_WIDTH + skipx - 1)/skipx;
}

export inline int grabVideoPixHeight(int skipy)
{
  return (FULL_HEIGHT + skipy - 1)/skipy;
}

export void grabDisconnectVideoPix(void)
{
  if (VideoPixInfo.connected) {
    close(VideoPixInfo.fvid);
  }

  VideoPixInfo.connected = FALSE;
}

  
export int grabConnectToVideoPix(int port)
{
  int status;
  caddr_t base;
  int fvid;
  int vprt;
  

  if (port > 3  || port < 1)  {              /* use any port */
    port = 0;
  }
  
  if (!VideoPixInfo.connected) {
    for (vprt = 1; vprt <= 3; vprt++) {
      VideoPixInfo.status[vprt] = NO_LOCK;   /* no signal present */
    }
    
    if ((fvid = open("/dev/vfc0", O_RDONLY)) == -1) { 
      return 0;                              /* can't open it */
    }

    VideoPixInfo.fvid = fvid;                /* set file descriptor */
  }
  else {
    fvid = VideoPixInfo.fvid;
    vprt = VideoPixInfo.vprt;
  }

  if (!VideoPixInfo.connected  ||  (port != 0  &&  port != vprt)) {
    if (port == 0) {                         /* asked for any port */
      for (vprt = 1; vprt <= 3; vprt++) {
	ioctl(fvid, VFCPORTCHG, &vprt);      /* set port */

        if ((status = VideoPixInfo.status[vprt]) != NO_LOCK) {
          break;                             /* assume still have signal */
        }
        else {                               /* must check for signal */
          if (ioctl(fvid, VFCGVID, &status) == -1) { /* error */
            return 0;
          }

          if (status != NO_LOCK) {           /* have signal */
            break;
          }
        }
      }
    }
    else {				     /* specific port requested */
      vprt = port;
      ioctl(fvid, VFCPORTCHG, &vprt);

      if ((status = VideoPixInfo.status[vprt]) == NO_LOCK  &&
          ioctl(fvid, VFCGVID, &status) == -1) {
        return 0;                            /* error checking status */
      }
    }
  
    if (status == NO_LOCK) {		     /* don't have a signal? */
      return 0;
    }
  
    VideoPixInfo.connected    = TRUE;        /* Yes! */
    VideoPixInfo.vprt         = vprt;        /* save port # of connection */
    VideoPixInfo.status[vprt] = status;      /* save last status on this port */
    
    base = mmap(0, 0x8000, PROT_READ, MAP_SHARED, fvid, 0);
    VideoPixInfo.f1 = (u_int *) (void *) (base + 0x5000); /* (void *) gets rid of */
    VideoPixInfo.f2 = (u_int *) (void *) (base + 0x7000); /* alignment warnings */
  }

  return vprt;
}


export GrayImage grabVideoPix_(kwArgDecl(VideoPixKW))
{
  kwDefaultN(im, into, (GrayImage) NULL);
  int skipx = kwSpecified(down_x) ? kwGetVal(down_x)
                                  : kwSpecified(downsample) ? kwGetVal(downsample)
                                                            : 1;
  int skipy = kwSpecified(down_y) ? kwGetVal(down_y)
                                  : kwSpecified(downsample) ? kwGetVal(downsample)
                                                            : 1;
  kwDefault(src_x,  0);
  kwDefault(src_y,  0);
  kwDefault(dst_x,  (im != (GrayImage) NULL) ? imGetXBase(im) : 0);
  kwDefault(dst_y,  (im != (GrayImage) NULL) ? imGetYBase(im) : 0);
  kwDefault(width,  grabVideoPixWidth(skipx)  - src_x);
  kwDefault(height, grabVideoPixHeight(skipy) - src_y);
  kwDefault(port,   0);

  int cmd = CAPTRCMD;
  int fvid;


  if (width <= 0  ||  height <= 0  ||        /* bad args */
      skipx <= 0  ||  skipy  <= 0 ) { 
    return (GrayImage) NULL;
  }
  
  if (im == (GrayImage) NULL) {              /* need to allocate an image */
    im = imNewOffset(IMAGE_GRAY, width, height, dst_x, dst_y);
  }

  if (im == (GrayImage) NULL) {              /* couldn't allocate image */
    return (GrayImage) NULL;
  }
  
  if (grabConnectToVideoPix(port) == 0) {    /* couldn't connect */
    return (GrayImage) NULL;
  }

  fvid = VideoPixInfo.fvid;
  cmd  = MEMPRST;
  ioctl(fvid, VFCSCTRL, &cmd);
  cmd  = CAPTRCMD;
  if (ioctl(fvid, VFCSCTRL, &cmd) == -1) {   /* couldn't capture */
    return (GrayImage) NULL;
  }


  if (imGetXBase(im) == dst_x    &&          /* a simple case??? */
      imGetYBase(im) == dst_y    &&
      src_x  == 0                &&
      src_y  == 0                &&
      width  == imGetWidth(im)   &&
      height == imGetHeight(im)  &&
      width  == grabVideoPixWidth(skipx)  &&
      height == grabVideoPixHeight(skipy))  {
    if      (skipx == skipy && skipy == 1) {
      do_grab_1x1(im, VideoPixInfo.f1, VideoPixInfo.f2);
    }
    else if (skipx == skipy && skipy == 2) {
      do_grab_2x2(im, VideoPixInfo.f1, VideoPixInfo.f2);
    }
    else if (skipx == skipy && skipy == 3) {
      do_grab_3x3(im, VideoPixInfo.f1, VideoPixInfo.f2);
    }
    else if (skipx == skipy && skipy == 4) {
      do_grab_4x4(im, VideoPixInfo.f1, VideoPixInfo.f2);
    }
    else if (skipx == skipy && skipy == 8) {
      do_grab_8x8(im, VideoPixInfo.f1, VideoPixInfo.f2);
    }
    else {
      do_grab_general(im, VideoPixInfo.f1, VideoPixInfo.f2, skipx - 1, skipy - 1);
    }
  }
  else {
    do_grab_ultra_general(im, VideoPixInfo.f1, VideoPixInfo.f2, skipx - 1, skipy - 1,
                          src_x, src_y, dst_x, dst_y, width, height);
  }
  
  return im;
}


local void do_grab_ultra_general(GrayImage im,
                                  volatile u_int *frame1, volatile u_int *frame2,
                                  int skipx, int skipy,
                                  int src_x, int src_y,
                                  int dst_x, int dst_y,
                                  int width, int height)
{
  int x, y, xx, yy;
  int wastex, wastey;
  int xxmax = MIN(imGetXBase(im) + imGetWidth(im),  dst_x + width);
  int yymax = MIN(imGetYBase(im) + imGetHeight(im), dst_y + height);
  register volatile u_int *fr = frame1;      /* start with frame2 (really!) */
  register u_int trash;


  for (x = VBL; x; x--) {		     /* trash lossage */
    trash = *frame1;
    trash = *frame2;
  }

  wastey = 0;
  for (yy = dst_y, y = 0; y < FULL_HEIGHT; y++) {
    fr = (fr == frame2) ? frame1 : frame2;   /* alternate frames */

    if (wastey  ||  y < src_y  ||  yy >= yymax) {
      wastey -= (wastey > 0);
      for (x = FULL_WIDTH; x; x--)
	trash = *fr;
    }
    else {
      wastey = skipy;
      wastex = 0;
      for (xx = dst_x, x = 0; x < FULL_WIDTH; x++) {
	if (wastex  ||  x < src_x  ||  xx >= xxmax) {
          wastex -= (wastex > 0);
	  trash = *fr;
	}
	else {
	  wastex = skipx;
	  imRef(im, xx, yy) = (uchar) (*fr >> 24);
          xx++;
	}
      }
      yy++;
    }
  }
}

local void do_grab_general(GrayImage im, volatile u_int *frame1, volatile u_int *frame2,
			    register int skipx, register int skipy)
{
  register uchar *scan = imGetStore(im);
  register uchar *end  = scan + imGetStoreLen(im);
  register volatile u_int *fr   = frame1;    /* start with frame2 (really!) */
  register u_int trash;
  register int i, wastex, wastey;


  for (i = VBL; i > 0; i--) {		     /* trash lossage */
    trash = *frame1;
    trash = *frame2;
  }

  wastey = 0;
  while (scan < end) {
    fr = (fr == frame2) ? frame1 : frame2;   /* alternate frames */

    if (wastey) {
      wastey--;
      for (i = FULL_WIDTH; i; i--)
	trash = *fr;
    }
    else {
      wastey = skipy;
      for (wastex = 0, i = FULL_WIDTH; i; i--) {
	if (wastex) {
	  wastex--;
	  trash = *fr;
	}
	else {
	  wastex = skipx;
	  *scan++ = (uchar) (*fr >> 24);
	}
      }
    }
  }
}

local void do_grab_1x1(GrayImage im, volatile u_int *frame1, volatile u_int *frame2)
{
  register uchar *scan = imGetStore(im);
  register uchar *end  = scan + imGetStoreLen(im);
  register volatile u_int *fr = frame1;
  register u_int trash;
  register int i;
  
  assert(VBL%40 == 0);			     /* make sure unrolling OK */
  assert(FULL_WIDTH%40 == 0);
  
  for (i = VBL/40; i > 0; i--) {	     /* trash lossage, unroll 40 times */
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
  }

  while (scan < end) {
    fr = (fr == frame2) ? frame1 : frame2;   /* alternate frames */

    for (i = FULL_WIDTH/40; i; i--) {	     /* unroll 40 times */
      *scan++ = (uchar) (*fr >> 24);      *scan++ = (uchar) (*fr >> 24);
      *scan++ = (uchar) (*fr >> 24);      *scan++ = (uchar) (*fr >> 24);
      *scan++ = (uchar) (*fr >> 24);      *scan++ = (uchar) (*fr >> 24);
      *scan++ = (uchar) (*fr >> 24);      *scan++ = (uchar) (*fr >> 24);
      *scan++ = (uchar) (*fr >> 24);      *scan++ = (uchar) (*fr >> 24);
      *scan++ = (uchar) (*fr >> 24);      *scan++ = (uchar) (*fr >> 24);
      *scan++ = (uchar) (*fr >> 24);      *scan++ = (uchar) (*fr >> 24);
      *scan++ = (uchar) (*fr >> 24);      *scan++ = (uchar) (*fr >> 24);
      *scan++ = (uchar) (*fr >> 24);      *scan++ = (uchar) (*fr >> 24);
      *scan++ = (uchar) (*fr >> 24);      *scan++ = (uchar) (*fr >> 24);
      *scan++ = (uchar) (*fr >> 24);      *scan++ = (uchar) (*fr >> 24);
      *scan++ = (uchar) (*fr >> 24);      *scan++ = (uchar) (*fr >> 24);
      *scan++ = (uchar) (*fr >> 24);      *scan++ = (uchar) (*fr >> 24);
      *scan++ = (uchar) (*fr >> 24);      *scan++ = (uchar) (*fr >> 24);
      *scan++ = (uchar) (*fr >> 24);      *scan++ = (uchar) (*fr >> 24);
      *scan++ = (uchar) (*fr >> 24);      *scan++ = (uchar) (*fr >> 24);
      *scan++ = (uchar) (*fr >> 24);      *scan++ = (uchar) (*fr >> 24);
      *scan++ = (uchar) (*fr >> 24);      *scan++ = (uchar) (*fr >> 24);
      *scan++ = (uchar) (*fr >> 24);      *scan++ = (uchar) (*fr >> 24);
      *scan++ = (uchar) (*fr >> 24);      *scan++ = (uchar) (*fr >> 24);
    }
  }
}

local void do_grab_2x2(GrayImage im, volatile u_int *frame1, volatile u_int *frame2)
{
  register uchar *scan = imGetStore(im);
  register uchar *end  = scan + imGetStoreLen(im);
  register volatile u_int *fr = frame2;
  register u_int trash;
  register int i;
  
  assert(VBL%40 == 0);			     /* make sure unrolling OK */
  assert(FULL_WIDTH%(2*40) == 0);
  
  for (i = VBL/40; i > 0; i--) {  /* trash lossage, fr=frame2 only; unroll 40 times */
    trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
    trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
    trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
    trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
    trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
    trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
    trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
    trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
  }

  while (scan < end) {			     /* unroll 40 times */
    *scan++ = (uchar)(*fr>>24); trash = *fr;    *scan++ = (uchar)(*fr>>24); trash = *fr;
    *scan++ = (uchar)(*fr>>24); trash = *fr;    *scan++ = (uchar)(*fr>>24); trash = *fr;
    *scan++ = (uchar)(*fr>>24); trash = *fr;    *scan++ = (uchar)(*fr>>24); trash = *fr;
    *scan++ = (uchar)(*fr>>24); trash = *fr;    *scan++ = (uchar)(*fr>>24); trash = *fr;
    *scan++ = (uchar)(*fr>>24); trash = *fr;    *scan++ = (uchar)(*fr>>24); trash = *fr;
    *scan++ = (uchar)(*fr>>24); trash = *fr;    *scan++ = (uchar)(*fr>>24); trash = *fr;
    *scan++ = (uchar)(*fr>>24); trash = *fr;    *scan++ = (uchar)(*fr>>24); trash = *fr;
    *scan++ = (uchar)(*fr>>24); trash = *fr;    *scan++ = (uchar)(*fr>>24); trash = *fr;
    *scan++ = (uchar)(*fr>>24); trash = *fr;    *scan++ = (uchar)(*fr>>24); trash = *fr;
    *scan++ = (uchar)(*fr>>24); trash = *fr;    *scan++ = (uchar)(*fr>>24); trash = *fr;
    *scan++ = (uchar)(*fr>>24); trash = *fr;    *scan++ = (uchar)(*fr>>24); trash = *fr;
    *scan++ = (uchar)(*fr>>24); trash = *fr;    *scan++ = (uchar)(*fr>>24); trash = *fr;
    *scan++ = (uchar)(*fr>>24); trash = *fr;    *scan++ = (uchar)(*fr>>24); trash = *fr;
    *scan++ = (uchar)(*fr>>24); trash = *fr;    *scan++ = (uchar)(*fr>>24); trash = *fr;
    *scan++ = (uchar)(*fr>>24); trash = *fr;    *scan++ = (uchar)(*fr>>24); trash = *fr;
    *scan++ = (uchar)(*fr>>24); trash = *fr;    *scan++ = (uchar)(*fr>>24); trash = *fr;
    *scan++ = (uchar)(*fr>>24); trash = *fr;    *scan++ = (uchar)(*fr>>24); trash = *fr;
    *scan++ = (uchar)(*fr>>24); trash = *fr;    *scan++ = (uchar)(*fr>>24); trash = *fr;
    *scan++ = (uchar)(*fr>>24); trash = *fr;    *scan++ = (uchar)(*fr>>24); trash = *fr;
    *scan++ = (uchar)(*fr>>24); trash = *fr;    *scan++ = (uchar)(*fr>>24); trash = *fr;
  }
}

local void do_grab_3x3(GrayImage im, volatile u_int *frame1, volatile u_int *frame2)
{
  register uchar *scan = imGetStore(im);
  register uchar *end  = scan + imGetStoreLen(im);
  register volatile u_int *fr = frame1;
  register u_int trash;
  register int i;

  assert(VBL%40 == 0);			     /* make sure unrolling OK */
  assert(FULL_WIDTH%(3*40) == 0);
  assert(FULL_WIDTH%40 == 0);
  
  for (i = VBL/40; i > 0; i--) {	     /* trash lossage, unroll 40 */
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
    trash = *frame1; trash = *frame2;    trash = *frame1; trash = *frame2;
  }

  while (scan < end) {
    fr = (fr == frame2) ? frame1 : frame2;   /* alternate frames */

    for (i = FULL_WIDTH/(3*40); i; i--) {    /* unroll 40 */
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr;
    }

    for (i = FULL_WIDTH/40; i; i--) {	     /* kill two scan lines, unroll 40 */
      trash = *frame1; trash = *frame2;      trash = *frame1; trash = *frame2;
      trash = *frame1; trash = *frame2;      trash = *frame1; trash = *frame2;
      trash = *frame1; trash = *frame2;      trash = *frame1; trash = *frame2;
      trash = *frame1; trash = *frame2;      trash = *frame1; trash = *frame2;
      trash = *frame1; trash = *frame2;      trash = *frame1; trash = *frame2;
      trash = *frame1; trash = *frame2;      trash = *frame1; trash = *frame2;
      trash = *frame1; trash = *frame2;      trash = *frame1; trash = *frame2;
      trash = *frame1; trash = *frame2;      trash = *frame1; trash = *frame2;
      trash = *frame1; trash = *frame2;      trash = *frame1; trash = *frame2;
      trash = *frame1; trash = *frame2;      trash = *frame1; trash = *frame2;
      trash = *frame1; trash = *frame2;      trash = *frame1; trash = *frame2;
      trash = *frame1; trash = *frame2;      trash = *frame1; trash = *frame2;
      trash = *frame1; trash = *frame2;      trash = *frame1; trash = *frame2;
      trash = *frame1; trash = *frame2;      trash = *frame1; trash = *frame2;
      trash = *frame1; trash = *frame2;      trash = *frame1; trash = *frame2;
      trash = *frame1; trash = *frame2;      trash = *frame1; trash = *frame2;
      trash = *frame1; trash = *frame2;      trash = *frame1; trash = *frame2;
      trash = *frame1; trash = *frame2;      trash = *frame1; trash = *frame2;
      trash = *frame1; trash = *frame2;      trash = *frame1; trash = *frame2;
      trash = *frame1; trash = *frame2;      trash = *frame1; trash = *frame2;
    }
  }
}

local void do_grab_8x8(GrayImage im, volatile u_int *frame1, volatile u_int *frame2)
{
  register uchar *scan = imGetStore(im);
  register uchar *end  = scan + imGetStoreLen(im);
  register volatile u_int *fr = frame2;
  register u_int trash;
  register int i;

  assert(VBL%40 == 0);			     /* make sure unrolling OK */
  assert(FULL_WIDTH%(8*15) == 0);
  assert(FULL_WIDTH%40 == 0);

  for (i = VBL/40; i > 0; i--) {	     /* trash lossage, frame2 only; unroll 40 */
    trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
    trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
    trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
    trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
    trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
    trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
    trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
    trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
  }

  while (scan < end) {
    for (i = FULL_WIDTH/(8*15); i; i--) {    /* unroll 15 times */
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr;  trash = *fr;                  trash = *fr;  trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr;  trash = *fr;                  trash = *fr;  trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr;  trash = *fr;                  trash = *fr;  trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr;  trash = *fr;                  trash = *fr;  trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr;  trash = *fr;                  trash = *fr;  trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr;  trash = *fr;                  trash = *fr;  trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr;  trash = *fr;                  trash = *fr;  trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr;  trash = *fr;                  trash = *fr;  trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr;  trash = *fr;                  trash = *fr;  trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr;  trash = *fr;                  trash = *fr;  trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr;  trash = *fr;                  trash = *fr;  trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr;  trash = *fr;                  trash = *fr;  trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr;  trash = *fr;                  trash = *fr;  trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr;  trash = *fr;                  trash = *fr;  trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr;  trash = *fr;                  trash = *fr;  trash = *fr;
    }

    for (i = 3*FULL_WIDTH/40; i; i--) {	     /* kill three even scan lines; unroll 40 */
      trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
      trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
      trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
      trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
      trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
      trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
      trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
      trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
    }
  }
}

local void do_grab_4x4(GrayImage im, volatile u_int *frame1, volatile u_int *frame2)
{
  register uchar *scan = imGetStore(im);
  register uchar *end  = scan + imGetStoreLen(im);
  register volatile u_int *fr = frame2;
  register u_int trash;
  register int i;

  assert(VBL%40 == 0);			     /* make sure unrolling OK */
  assert(FULL_WIDTH%(4*30) == 0);
  assert(FULL_WIDTH%40 == 0);

  for (i = VBL/40; i > 0; i--) {	     /* trash lossage, frame2 only; unroll 40 */
    trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
    trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
    trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
    trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
    trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
    trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
    trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
    trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
  }

  while (scan < end) {
    for (i = FULL_WIDTH/(4*30); i; i--) {    /* unroll 30 times */
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
      trash = *fr; *scan++ = (uchar) (*fr >> 24); trash = *fr; trash = *fr;
    }

    for (i = FULL_WIDTH/40; i; i--) {	     /* kill one even scan line; unroll 40 */
      trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
      trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
      trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
      trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
      trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
      trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
      trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
      trash = *fr; trash = *fr; trash = *fr; trash = *fr; trash = *fr;
    }
  }
}



/* Local Variables: */
/* truncate-lines: t */
/* End: */
