//////////////////////////////////////////////////////////////////////////////
// Copyright (c) Winnov Inc., 1993.  All rights reserved
// compile.c: "Video sampler" microcode compiler
//////////////////////////////////////////////////////////////////////////////

#include <windows.h>
#include "ucode.h"
#include "compile.h"

// vertical scale factor
#define VSCALE(val)	(((DWORD)val)<<16)
#define UNVSCALE(val)	(HIWORD(val))

//////////////////////////////////////////////////////////////////////////////

#define DO_DELAY    0
#define REVERSE_ORDER	0

//  These are used to initialize the error in the vertical decimation DDA.
#define DECIMATIONOFFSET_1_2	(VSCALE(4UL)>>3)  //  Standard value

static void
HDecimation(DWORD	dwVideoWidth,
	    DWORD	dwCaptureWidth,
	    LPDWORD	hrate,
	    LPDWORD	hclks)
    {
	DWORD	herr	= 32;
	DWORD	npx;

	*hclks = dwVideoWidth;
	*hrate = ((dwCaptureWidth * 64) / dwVideoWidth);
	if (*hrate < 2)
		*hrate = 2;
	npx = ((*hclks * *hrate) + herr) / 64;
	/*  Force the decimation ratio up if not enough pixels captured	*/
	while (npx < dwCaptureWidth)
	    {
		(*hrate)++;
		npx = ((*hclks * *hrate) + herr) / 64;
	    }
	/*  Force the number of clocks down if too many pixels captured	*/
	while (npx > dwCaptureWidth)
	    {
		(*hclks)--;
		npx = ((*hclks * *hrate) + herr) / 64;
	    }
	//  Attempt to work around WAVI video sampler bugs:
	//  Capture for the maximum number of clocks that take the number of
	//  pixels we want
	while (((((*hclks + 1) * *hrate) + herr) / 64) == npx)
		++(*hclks);
    }


DWORD
CompileField(
	     DWORD	dwVideoLeft,
	     DWORD	dwVideoTop,
	     DWORD	dwVideoWidth,
	     DWORD	dwVideoHeight,
	     DWORD	dwCaptureWidth,
	     DWORD	dwCaptureHeight,
	     DWORD	dwInterruptLine,
	     BYTE	byInterruptInstruction,
         DWORD  dwDmaLine,
	     LPSTR	lpMicrocode,		// microcode buffer
	     BOOL	bEnableLineAvg,
	     int	nHSkip,
	     int	nVSkip,
	     int	nActive,
	     int	nReserved,		// bytes reserved for additional skips
         BOOL   bFlush,		// flush sampler pipeline for Huffman capture
	     DWORD	dwFlags)			// vertical decimation/averaging offset
{
	LPBYTE	 lpM = (LPBYTE)lpMicrocode;	// emit ucode here
	BOOL	 bInterrupted = FALSE;		// host interrupt instruction not emited yet
	BOOL	 bLineAvg = FALSE;		// line avg buffer is empty
	DWORD	 hclks, hrate;
	DWORD	 verr, vclks, vrate, vlines;
	DWORD	 dwLine;			// current scaled captured line number
	DWORD	 dwYCrop;			// #lines off the top
	DWORD	 dwXCrop;			// #clocks off the left
	WORD	 wXCropInstruction;		// ucode for x cropping
	WORD	 wXCaptureInstruction;		// ucode for decimation
	WORD	 wXCaptureInstruction2;		// ucode for decimation
	DWORD	 dwVideoMode;
	UINT	 nReservedOffset = 0;		// offset of reserved skips
	BOOL	 fFirstLineOfField = 1;

    if (!dwInterruptLine)
		dwInterruptLine = 1;	// capture at least one line

	// capture dimensions must not exceed video dimensions
	dwCaptureWidth = min(dwCaptureWidth, dwVideoWidth);
	dwCaptureHeight = min(dwCaptureHeight, dwVideoHeight);

	// capture width must be a multiple of 4 (for 4-1-1 YUV format)
	dwCaptureWidth &= ~3;

	// make sure capture size is not null
	if (!dwCaptureWidth || !dwCaptureHeight)
		return 0;

	HDecimation(dwVideoWidth, dwCaptureWidth, &hrate, &hclks);

	// can't capture raw with averaging above 2:1 ratio, or compressed with
	// averaging above 3:1
    if (!(dwFlags & COMPFLAG_HUFFMAN) && hrate > 32 ||
	 (dwFlags & COMPFLAG_HUFFMAN) && hrate > 19)
	bEnableLineAvg = 0;

	if (hclks > 959)
	    {
		wXCaptureInstruction = (WORD)(UI_DECIMATE_LOWPREC(hrate, hclks / 2));
		wXCaptureInstruction2 = (WORD)(UI_DECIMATE_LOWPREC(hrate, hclks - hclks / 2));
	    }
	else
	    {
		wXCaptureInstruction = (WORD)(UI_DECIMATE_LOWPREC(hrate,hclks));
		wXCaptureInstruction2 = 0;
	    }


	// scale vertically
	// if the capture height is slightly smaller than the video height,
	// change the video height to the capture height.  This will avoid
	// doing a line average in the middle, which screws things up.
	if ((dwCaptureHeight == (dwVideoHeight - 1)) ||
	    (dwCaptureHeight == dwVideoHeight))
	    {
		vrate = VSCALE(1L);
		dwLine = VSCALE(1L) >> 1;
		bLineAvg = FALSE;
	    }
	else
	    {
		verr = DECIMATIONOFFSET_1_2;
		vclks = dwVideoHeight;
		vrate = (VSCALE(dwCaptureHeight)) / ((DWORD)dwVideoHeight);
		vlines = UNVSCALE((vclks * vrate) + verr);
		// increase rate if not enough lines captured
		while (vlines < dwCaptureHeight)
		    {
			vrate++;
			vlines = UNVSCALE((vclks * vrate) + verr);
		    }
		// decrease the video height if too many lines captured
		while (vlines > dwCaptureHeight)
		    {
			vclks--;
			vlines = UNVSCALE((vclks * vrate) + verr);
		    }
	    dwVideoHeight = (WORD)vclks;	// are one and the same
		dwLine = verr;			// starting error term
		bLineAvg = FALSE;		// line avg buffer is empty
	    }

    //*lpDecimation = (vrate << 16) | hrate;

	// crop at the top
	dwYCrop = dwVideoTop + nVSkip;
	nReservedOffset = 0;
	if (nReserved)
	{// reserved skips
		//  Gotta have a WTHS before the NOPs
		*lpM++ = UI_WTHS;
		if (dwYCrop)
			--dwYCrop;
		nReservedOffset = lpM - lpMicrocode;
		while (nReserved--)
			*lpM++ = UI_NOP;
	}
	//  Gotta have WTHSs after the NOPs
	if (dwYCrop < 256)
		while (dwYCrop--)
			*lpM++ = UI_WTHS;	// skip cropped lines

	// crop to the left
	dwXCrop = dwVideoLeft + nHSkip; 
	// center the actual sample range within desired sample range
	if (hclks > dwVideoWidth)
		hclks = dwVideoWidth;
	dwXCrop += (dwVideoWidth - hclks) / 2;
	if (dwXCrop > 960)	// limit imposed by ucode instruction encoding
		dwXCrop = 960;
	wXCropInstruction = 0;
	dwXCrop = max(2, dwXCrop);
	if (dwXCrop)
		wXCropInstruction = UI_SKIP_LOWPREC(dwXCrop);

	dwVideoMode = 0; // track video mode

	while (UNVSCALE(dwLine) < dwCaptureHeight)
	{
		if (UNVSCALE(dwLine + vrate) > UNVSCALE(dwLine))
		{	// capture this line

			//  Set mode to average with previous line,
			//  or just capture as needed
			if (bEnableLineAvg)
			    {
				if (bLineAvg)
				    {	// average with previous line during capture
					// switch to video mode 2
					while (dwVideoMode != 2)
					    {
						*lpM++ = UI_SVM;		// increment video mode
						dwVideoMode = (dwVideoMode + 1) & 3;
					    }
				    }
				else
				    {	// line avg buffer is empty, make sure we capture in mode 0
					while (dwVideoMode != 0)
					    {
						*lpM++ = UI_SVM;
						dwVideoMode = (dwVideoMode + 1) & 3;
					    }
				    }
			    }
			//  Wait for hsync
			*lpM++ = UI_WTHS;
			//  Crop on left, also reset VP2 if line averaging
			if (dwVideoMode != 0)
			    {
				*lpM++ = UI_RVP2;
				*((LPWORD)lpM)++ = UI_SKIP_LOWPREC(dwXCrop - 1);
			    }
			else
			    {
				*((LPWORD)lpM)++ = UI_SKIP_LOWPREC(dwXCrop);
			    }
			//  Capture/avg
			*((LPWORD)lpM)++ = wXCaptureInstruction;
			if (wXCaptureInstruction2)
				*((LPWORD)lpM)++ = wXCaptureInstruction2;
			//  Reset average buffer full flag

#if DO_DELAY
	    *((LPWORD)lpM)++ = UI_SKIP_LOWPREC(16);
#endif

	        bLineAvg = FALSE;			// no averaged lines captured
	        fFirstLineOfField = 0;
		}
		else
		{	//  Either skip this line, or save in the average buffer
			if (bEnableLineAvg)
			{	//  Save it in average buffer, if it's empty
				if (bLineAvg)
					*lpM++ = UI_WTHS;	// skip it, avg buffer full
				else
				{	// not already averaged
					// switch to video mode 1
					while (dwVideoMode != 1)
					{
						*lpM++ = UI_SVM;		//
						dwVideoMode = (dwVideoMode + 1) & 3;
					}
					*lpM++ = UI_WTHS;	// wait for hsync
					*lpM++ = UI_RVP2;	// reload with vid ptr 1 to avg buf write
					*((LPWORD)lpM)++ = UI_SKIP_LOWPREC(dwXCrop - 1);	// crop on left
					*((LPWORD)lpM)++ = wXCaptureInstruction;	// capture to avg buf
					if (wXCaptureInstruction2)
						*((LPWORD)lpM)++ = wXCaptureInstruction2;

#if DO_DELAY
		            *((LPWORD)lpM)++ = (BYTE)UI_SKIP_LOWPREC(16);
#endif

					bLineAvg = TRUE;	// avg buffer valid
				}
			}
			else
			{	 // just skip it
				*lpM++ = UI_WTHS;		// wait for hsync
			}
		}
		dwLine += vrate;

	    //  Interrupt instruction
	    if (!dwInterruptLine--)
	    {
		    bInterrupted = TRUE;
		    if (byInterruptInstruction != UI_NOP)
		    {
			    *lpM++ = byInterruptInstruction;// interrupt host
			    if (dwFlags & COMPFLAG_AVPRO_INT)
			    {
				    *lpM++ = UI_SVS;
				    *lpM++ = UI_RVS;
			    }
		    }
	    }
	    //  DMA trigger instruction
	    if (!dwDmaLine--)
	    {
		    if (dwFlags & COMPFLAG_AVPRO_DMA)
		    {
			    *lpM++ = UI_SVS;
			    *lpM++ = UI_SVS;
			    *lpM++ = UI_RVS;
			    dwFlags &= ~COMPFLAG_AVPRO_DMA;
		    }
	    }
	}
	
    while (dwVideoMode != 0)
	{
		*lpM++ = UI_SVM;
		dwVideoMode = (dwVideoMode + 1) & 3;
	}

	// flush on-chip buffers for huffman capture
	if (bFlush)
		*((LPWORD)lpM)++ = UI_DECIMATE_LOWPREC(31,64);
    
	if (dwFlags & COMPFLAG_AVPRO_DMA)
	{//	Guarantee DMA is started
		*lpM++ = UI_SVS;
		*lpM++ = UI_SVS;
		*lpM++ = UI_RVS;
	}

	if (!bInterrupted && byInterruptInstruction != UI_NOP)
	{//	Guarantee there is an interrupt instruction
		while (dwInterruptLine--)
			*lpM++ = UI_WTHS;
		*lpM++ = byInterruptInstruction;	// interrupt host
		if (dwFlags & COMPFLAG_AVPRO_INT)
		{
			*lpM++ = UI_SVS;
			*lpM++ = UI_RVS;
		}
	}

    return (lpM - lpMicrocode) | (((DWORD)nReservedOffset)<<16);		// size in bytes
}

//////////////////////////////////////////////////////////////////////////////
// still scanned capture microcode compiler
// a single microcode template is used for all passes.
// Reserved NOPS are filled in with VSkips after each pass.
// capture is always YCH format
// line average is always permitted except for high resolution analog captures

long 
CompileStillField(
		  BYTE	bFieldInstruction,
		  BYTE	byInterruptInstruction,
		  LPSTR	lpMicrocode,
		  UINT	nReserved,
		  UINT	nVideoLeft,
		  UINT	nVideoTop,
		  UINT	nVideoWidth,
		  UINT	nVideoHeight,
		  UINT	nCaptureWidth,
		  UINT	nCaptureHeight,
		  UINT	nHSkip,
		  UINT	nVSkip,
		  UINT	nActiveWidth, 
		  BOOL	bAnalog,
		  DWORD	dwFlags)
{
	union tag_dwSize
	{
		DWORD	dw;
		WORD	w[2];
		WORD	wSize;
	}dwSize;

	LPBYTE	lpM = lpMicrocode;
	BOOL	bLineAvg = 1;

	//    bLineAvg = !bAnalog || nCaptureWidth < nVideoWidth / 2;

    // start new frame capture
    if (!(dwFlags & COMPFLAG_SYNCPOLARITY))
    {
	    *lpM++ = UI_WTVB;
	    *lpM++ = bFieldInstruction;
	    *lpM++ = UI_WTVB;
	    *lpM++ = UI_WTVA;
    }
    else
    {
	    *lpM++ = UI_WTVA;
	    *lpM++ = UI_WTVB;
	    *lpM++ = bFieldInstruction;
	    *lpM++ = UI_WTVB;
    }

	//  Delay after V Sync (this fixes the Crystal feathering problem)
	if (dwFlags & COMPFLAG_VSDELAY)
		*((LPWORD)lpM)++ = UI_SKIP_LOWPREC(200);

    dwSize.dw = CompileField (
	        nVideoLeft,
	        nVideoTop,
	        nVideoWidth,
	        nVideoHeight,
	        nCaptureWidth,
	        nCaptureHeight,
	        nCaptureHeight,		// interrupt line
	        byInterruptInstruction,	// interrupt instruction
	        0,			// dma trigger line
	        lpM,			// microcode buffer
	        bLineAvg,
	        nHSkip,
	        nVSkip,
	        nActiveWidth,
	        nReserved,		// bytes reserved for more skips
	        0,			// flush
	        dwFlags);

	dwSize.w[1] += lpM - lpMicrocode;	// offset reserved by starting offset
	lpM += dwSize.wSize;

	dwSize.wSize = (lpM - lpMicrocode);

	return dwSize.dw;	// ms word is offset of reserved skips
}

// the return value is a dword
// hiword = offset of reserved bytes for changing starting capture line
// loword = size of microcode
// if bCaptureBoth fields is set, the loword is only the size of the
// microcode for one field, double it to get actual size.
// This allows the second set of reserved bytes to be located by
// summing the two words.

long CompileStillMicrocode (
    LPSTR lpMicrocode,
    BOOL bCaptureBothFields,
    BOOL bCapture2Fields,
    int  nFieldOffset,
    UINT nReserved,
    UINT nVideoLeft, 
    UINT nVideoTop,
    UINT nVideoWidth, 
    UINT nVideoHeight,
    UINT nCaptureWidth, 
    UINT nCaptureHeight,
    UINT nHSkip, 
    UINT nVSkip, 
    UINT nActiveWidth, 
    BOOL bAnalog, 
    DWORD dwFlags)
{
	union tag_dw
	{
		DWORD	dw;
		WORD	w[2];  // word 0 = size, word 1 = offset of reserved
		WORD	wSize;
	}dw;

	int	i;
    BYTE	byField1Instruction;
    BYTE	byField2Instruction;
	LPBYTE	lpM;

    if (dwFlags & COMPFLAG_FIELDORDER)
    {
	    byField1Instruction = UI_WTVF1;
	    byField2Instruction = UI_WTVF2;
    }
    else
    {
	    byField1Instruction = UI_WTVF2;
	    byField2Instruction = UI_WTVF1;
    }

    dw.dw = CompileStillField (
		byField1Instruction,		// field selection
		UI_NOP,				// no interrupt instruction
		lpMicrocode,
		nReserved + nFieldOffset,
		nVideoLeft, nVideoTop,
		nVideoWidth, nVideoHeight,
		nCaptureWidth, nCaptureHeight,
		nHSkip, nVSkip, nActiveWidth, bAnalog,
		dwFlags);
    CompileStillField (
		byField2Instruction,		// field selection
		UI_NOP,				// no interrupt
		lpMicrocode + dw.wSize,		// start of microcode
		nReserved,
		nVideoLeft, nVideoTop + nFieldOffset,
		nVideoWidth, nVideoHeight,
		nCaptureWidth, nCaptureHeight,
		nHSkip, nVSkip, nActiveWidth, bAnalog,
		dwFlags);

    if (!bCapture2Fields)
    {
	    CompileStillField (
		    byField1Instruction,		// field selection
		    UI_NOP,				// no interrupt this time
		    lpMicrocode + 2 * dw.wSize,	// start of microcode
		    nReserved + nFieldOffset,
		    nVideoLeft, nVideoTop,
		    nVideoWidth, nVideoHeight,
		    nCaptureWidth, nCaptureHeight,
		    nHSkip, nVSkip, nActiveWidth, bAnalog,
		    dwFlags);
	    CompileStillField (
		    byField2Instruction,		// field selection
		    UI_NOP,				// no interrupt this time
		    lpMicrocode + 3 * dw.wSize,	// start of microcode
		    nReserved,
		    nVideoLeft, nVideoTop + nFieldOffset,
		    nVideoWidth, nVideoHeight,
		    nCaptureWidth, nCaptureHeight,
		    nHSkip, nVSkip, nActiveWidth, bAnalog,
		    dwFlags);
	    dw.wSize <<= 1;
    }
	dw.wSize <<= 1;
    lpM = lpMicrocode + dw.wSize;

#define EXTRA_UCODE 18	// itr1 + svs + 16 hlts 
    *lpM++ = UI_ITR1;			// interrupt
    *lpM++ = UI_SVS;			// sw checks
    for (i = 0; i < EXTRA_UCODE - 2; i++)
	    *lpM++ = UI_HLT;		// fini

	dw.wSize += EXTRA_UCODE;
	return dw.dw;
}

void 
UpdateStillMicrocodeField(LPSTR	lpMicrocode,
			  UINT	nReservedOffset,
			  UINT	nCount,
			  BYTE	bVal)
{
	lpMicrocode += nReservedOffset;
	while (nCount--)
		*(lpMicrocode++) = bVal;
}

LRESULT 
UpdateStillMicrocode(LPSTR	lpMicrocode,		// microcode
		     UINT	nMicrocodeSize,	// size of microcode
		     BOOL	b2Fields,
		     UINT	nReservedOffset,	// offset
		     UINT	nCount,		// size
		     UINT	nCommand)		// command
{
	BYTE	bVal;

	switch (nCommand)
	{
	    case INSERT_NOPS:	bVal = UI_NOP;	break;
	    case INSERT_VSKIPS:	bVal = UI_WTHS; break;
	}

	if (b2Fields)
	{
		UpdateStillMicrocodeField(
			lpMicrocode, 
			nReservedOffset, nCount, bVal);
		UpdateStillMicrocodeField(
			lpMicrocode + ((nMicrocodeSize-EXTRA_UCODE) / 2),
			nReservedOffset, nCount, bVal);
	}
	else
	{
		UpdateStillMicrocodeField(
			lpMicrocode, 
			nReservedOffset, nCount, bVal);
		UpdateStillMicrocodeField(
			lpMicrocode + ((nMicrocodeSize-EXTRA_UCODE) / 4),
			nReservedOffset, nCount, bVal);
		UpdateStillMicrocodeField(
			lpMicrocode + ((nMicrocodeSize-EXTRA_UCODE) / 2),
			nReservedOffset, nCount, bVal);
		UpdateStillMicrocodeField(
			lpMicrocode + (3 * (nMicrocodeSize-EXTRA_UCODE) / 4),
			nReservedOffset, nCount, bVal);
	}

	return 0;
}

//////////////////////////////////////////////////////////////////////////////////

DWORD CompileMicrocode (
    BOOL bBothFields,
    DWORD dwVideoLeft,
    DWORD dwVideoTop,
    DWORD dwVideoWidth,
    DWORD dwVideoHeight,
    DWORD dwCaptureWidth,
    DWORD dwCaptureHeight,
    DWORD dwInterruptLine,
    DWORD dwDmaLine,
    LPSTR lpMicrocode,		// microcode buffer
    BOOL bEnableLineAvg,
    int  nHSkip,
    int  nVSkip,
    int  nActive,
    DWORD dwFlags)
{
	LPBYTE	lpM;
	int	i;
    BYTE	byInterruptInstruction	= UI_ITR1;
    BYTE	byEofInterruptInstruction	= UI_ITR2;
    BYTE	byFieldInstruction	= UI_WTVF1;
    DWORD	dwCompFlags		= dwFlags;

    if (bBothFields && dwInterruptLine > dwVideoHeight)// interrupt in 2nd field
	    byInterruptInstruction = UI_NOP;
    if (bBothFields && (dwFlags & COMPFLAG_FIELDORDER) ||
	!bBothFields && (dwFlags & COMPFLAG_PREFERREDFIELD))
    	byFieldInstruction = UI_WTVF2;
    if (bBothFields && dwDmaLine > dwVideoHeight)// DMA trigger in 2nd field
	    dwCompFlags &= ~(COMPFLAG_AVPRO_DMA);
    if (bBothFields)
	dwCaptureHeight /= 2;

	lpM = lpMicrocode;

    //  Use status bit to detect if sampler is running
    if (!(dwFlags & (COMPFLAG_AVPRO_DMA | COMPFLAG_AVPRO_INT)))
	    *lpM++ = UI_RVS;

    // start new frame capture
    if (!(dwFlags & COMPFLAG_SYNCPOLARITY))
    {
	    *lpM++ = UI_WTVB;
	    *lpM++ = byFieldInstruction;
	    *lpM++ = UI_WTVB;
	    *lpM++ = UI_WTVA;
    }
    else
    {
	    *lpM++ = UI_WTVA;
	    *lpM++ = UI_WTVB;
	    *lpM++ = byFieldInstruction;
	    *lpM++ = UI_WTVB;
    }
    //  Delay after V Sync (this fixes the Crystal feathering problem)
    if (dwFlags & COMPFLAG_VSDELAY)
	    *((LPWORD)lpM)++ = UI_SKIP_LOWPREC(200);

    //  Bug fix: Not enough microcode space (needs 5K) to capture
    //  two-field PAL with line averaging
    if (bBothFields && dwVideoHeight > 240 && dwCaptureHeight > 120)
	bBothFields = FALSE;
    
    lpM += CompileField (
	dwVideoLeft,
	dwVideoTop,
	dwVideoWidth,
	dwVideoHeight,
	dwCaptureWidth,
	dwCaptureHeight,
	dwInterruptLine,
	byInterruptInstruction,
	dwDmaLine,
	lpM,
	bEnableLineAvg,
	nHSkip,
	nVSkip,
	nActive,
	0,
	(dwCompFlags & COMPFLAG_HUFFMAN) && !bBothFields, // flush
	dwCompFlags);

	if (bBothFields)
	{
		if (byInterruptInstruction == UI_NOP)
			byInterruptInstruction = UI_ITR1;
		else
			byInterruptInstruction = UI_NOP;

	    if (byFieldInstruction == UI_WTVF1)
		    byFieldInstruction = UI_WTVF2;
	    else
		    byFieldInstruction = UI_WTVF1;

	    if ((dwFlags & COMPFLAG_AVPRO_DMA) &&
	        dwDmaLine > dwVideoHeight)
		    dwCompFlags |= (COMPFLAG_AVPRO_DMA);// DMA trigger in 2nd field
	    else
		    dwCompFlags &= ~(COMPFLAG_AVPRO_DMA);// DMA trigger in 1st field
	    

	    if (!(dwFlags & COMPFLAG_SYNCPOLARITY))
	    {
	        *lpM++ = UI_WTVB;
	        *lpM++ = byFieldInstruction;
	        *lpM++ = UI_WTVB;
	        *lpM++ = UI_WTVA;
	    }
	    else
	    {
	        *lpM++ = UI_WTVA;
	        *lpM++ = UI_WTVB;
	        *lpM++ = byFieldInstruction;
	        *lpM++ = UI_WTVB;
	    }
	    //  Delay after V Sync (this fixes the Crystal feathering problem)
	    if (dwFlags & COMPFLAG_VSDELAY)
		    *((LPWORD)lpM)++ = UI_SKIP_LOWPREC(200);

	    lpM += CompileField (
	        dwVideoLeft,
	        dwVideoTop,
	        dwVideoWidth,
	        dwVideoHeight,
	        dwCaptureWidth,
	        dwCaptureHeight,
	        dwInterruptLine - dwVideoHeight,
	        byInterruptInstruction,
	        dwDmaLine - dwVideoHeight,		// dma trigger line
	        lpM,
	        bEnableLineAvg,
	        nHSkip,
	        nVSkip,
	        nActive,
	        0,
	        (dwCompFlags & COMPFLAG_HUFFMAN),	// flush
	        dwCompFlags);
	}

    //  End-of-frame interrupt
    if (dwFlags & COMPFLAG_EOFINT)
    {
	*lpM++ = byEofInterruptInstruction;
	if (dwFlags & COMPFLAG_AVPRO_INT)
	{
		*lpM++ = UI_SVS;
		*lpM++ = UI_RVS;
	}
    }

    //  Software checks to detect when readout was too fast
    //  Use status bit to detect if sampler is running
    if (!(dwFlags & (COMPFLAG_AVPRO_DMA | COMPFLAG_AVPRO_INT)))
	*lpM++ = UI_SVS;

    *lpM++ = UI_HLT;				// fini

    //  Fill to next 16-byte boundary
    i = 15 - (lpM - lpMicrocode - 1) % 16;
    for (; i; --i)
	*lpM++ = UI_HLT;
	
    //  Marker microcode (at 16-byte boundary following end of main code)
    if (dwFlags & COMPFLAG_MARKERCODE)
    {
	*lpM++ = UI_SVS;
	*lpM++ = UI_SVS;
	*lpM++ = UI_SVS;
	*lpM++ = UI_SVS;
	*lpM++ = UI_RVS;
	*((LPWORD)lpM)++ = UI_DECIMATE_LOWPREC(32,600);
	*((LPWORD)lpM)++ = UI_DECIMATE_LOWPREC(32,600);
	*lpM++ = UI_SVS;// s/w polls for this
	*lpM++ = UI_HLT;
    }

    return (lpM - lpMicrocode);
}
