#include "mungal4.h"
#pragma hdrstop

#include "l4vb16.h"
#include "l4mfdview.h"
#include "l4plasmascreen.h"
#include "../munga/gaugrend.h"
#include "L4VIDEO.h"
#include "DXUtils.h"

#if defined(TRACE_SCREEN_COPY)
	static BitTrace Screen_Copy("Screen Copy");
	#define SET_SCREEN_COPY() Screen_Copy.Set()
	#define CLEAR_SCREEN_COPY() Screen_Copy.Clear()
#else
	#define SET_SCREEN_COPY()
	#define CLEAR_SCREEN_COPY()
#endif

#define BLIT_STATISTICS

#if defined(BLIT_STATISTICS)
	static int
		dirtyPixelCount,
		transferPixelCount,
		overflowPixelCount;
#endif

//#define DEBUG

#if defined(DEBUG)
	static Logical
		printFlag=True;
#	define Diag_on  printFlag=True
#	define Diag_off printFlag=False
#	define Diag_Tell(stuff) if(printFlag){std::cout << stuff;}
#else
#	define Diag_on
#	define Diag_off
#	define Diag_Tell(n)
#endif

//STUBBED: VIDEO RB 1/15/07
//extern "C" void
//	SVGASetMode(
//		int mode,
//		LWord pageFlipFcnpointer
//	);
//
//extern "C" void
//	SVGASetPage(
//		int page
//	);
//
//extern "C" int
//	SVGATransfer32(
//		int dest_offset,
//		Word *source_pointer,
//		LWord changed_bits
//	);
//
//extern "C" int
//	SVGATransfer32x(
//		int dest_offset,
//		Word *source_pointer,
//		LWord changed_bits
//	);
//
//extern "C" void
//	SVGASetSplitterClock(
//		Logical state
//	);
//
//extern "C" void
//	SVGAZeroPalette(
//		Word DAC_port
//	);
//
//extern "C" void
//	SVGAWriteFullPalette(
//		Byte *firstColorByte,
//		Word DAC_port
//	);
//
//extern "C" void
//	SVGAReadFullPalette(
//		Byte *firstColorByte,
//		Word DAC_port
//	);
//
//extern "C" void
//	SVGAWritePaletteMask(
//		Word DAC_port,
//		Byte new_mask
//	);
//
//extern "C" void
//	SVGAFunkyVideo(
//		Logical on_off
//	);


void SVGA16::BuildWindows(unsigned int width, unsigned int height, bool windowed, int *secondaryIndex, int *aux1Index, int *aux2Index)
{
	NUMGAUGEWINDOWS = 0;

	if (secondaryIndex != NULL)
	{
		NUMGAUGEWINDOWS++;
	}

	if (aux1Index != NULL)
	{
		NUMGAUGEWINDOWS++;
	}

	if (aux2Index != NULL)
	{
		NUMGAUGEWINDOWS++;
	}

	if (NUMGAUGEWINDOWS == 0)
	{
		return;
	}

	gaugeWindows = new HWND[NUMGAUGEWINDOWS];
	mDevice = new LPDIRECT3DDEVICE9[NUMGAUGEWINDOWS];
	mVertBuffers = new LPDIRECT3DVERTEXBUFFER9[NUMGAUGEWINDOWS];
	mSurfaces = new LPDIRECT3DTEXTURE9[NUMGAUGEWINDOWS * 2];
	mLockFlags = new DWORD[NUMGAUGEWINDOWS];
	mPresentParams = new D3DPRESENT_PARAMETERS[NUMGAUGEWINDOWS];
	mSurfaceRects = new RECT[NUMGAUGEWINDOWS];
	for (int j=0; j < NUMGAUGEWINDOWS; j++)
	{
		int desiredAdapter = -1;

		int adapterIndex = j;

		if (secondaryIndex != NULL && adapterIndex >= 0)
		{
			desiredAdapter = *secondaryIndex;
			adapterIndex--;
		}

		if (aux1Index != NULL && adapterIndex >= 0)
		{
			desiredAdapter = *aux1Index;
			adapterIndex--;
		}

		if (aux2Index != NULL && adapterIndex >= 0)
		{
			desiredAdapter = *aux2Index;
			adapterIndex--;
		}

		if (adapterIndex >= 0 || desiredAdapter < 0)
		{
			break;
		}

		RECT r;

		MONITORINFO info;
		info.cbSize = sizeof(MONITORINFO);
		GetMonitorInfo(gD3D->GetAdapterMonitor(desiredAdapter),&info);
		r = info.rcMonitor;

		mSurfaceRects[j].left = r.left;
		mSurfaceRects[j].top = r.top;
		mSurfaceRects[j].right = r.left + ((j==0 || aux2Index != NULL)?width:width*2);
		mSurfaceRects[j].bottom = r.top + height;

		RECT surfaceWindowRect = mSurfaceRects[j];
		AdjustWindowRectEx(&surfaceWindowRect, WS_BORDER, false, 0);

		gaugeWindows[j] = CreateWindowEx(0, L"MainWndClass", L"RPL4", WS_BORDER, surfaceWindowRect.left, surfaceWindowRect.top, surfaceWindowRect.right-surfaceWindowRect.left, surfaceWindowRect.bottom-surfaceWindowRect.top, (HWND)NULL, (HMENU)NULL, L4Application::GetAppInstance(), (LPVOID)NULL);
		if (gaugeWindows[j])
		{
			// In split-view mode the packed windows are only kept as D3D
			// device targets; the split windows are what the player sees.
			if (!splitViews)
			{
				ShowWindow(gaugeWindows[j], SW_SHOW);
			}

			memset(&mPresentParams[j], 0, sizeof(D3DPRESENT_PARAMETERS));

			mPresentParams[j].BackBufferCount = 1;
			mPresentParams[j].SwapEffect = D3DSWAPEFFECT_DISCARD;
			mPresentParams[j].hDeviceWindow = gaugeWindows[j];
			mPresentParams[j].Flags = 0;
			mPresentParams[j].FullScreen_RefreshRateInHz = (windowed)?D3DPRESENT_RATE_DEFAULT:60;
			mPresentParams[j].PresentationInterval = D3DPRESENT_RATE_DEFAULT;
			mPresentParams[j].BackBufferFormat = D3DFMT_R5G6B5;
			//pp.EnableAutoDepthStencil = TRUE;
			//pp.AutoDepthStencilFormat = D3DFMT_D24X8;
			mPresentParams[j].Windowed = windowed;
			if (!windowed)
			{
				mPresentParams[j].BackBufferWidth = mSurfaceRects[j].right - mSurfaceRects[j].left;
				mPresentParams[j].BackBufferHeight = mSurfaceRects[j].bottom - mSurfaceRects[j].top;
			}


			HRESULT hr = gD3D->CreateDevice(desiredAdapter, D3DDEVTYPE_HAL, ghWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &mPresentParams[j], &mDevice[j]);
			if (FAILED(hr))
			{
				DEBUG_STREAM<<"Couldn't create Direct3D device!"<<std::endl<<std::flush;
				PostQuitMessage(1);
			}

			D3DCAPS9 caps;
			DWORD usage = 0;
			D3DPOOL pool = D3DPOOL_MANAGED;
			hr = mDevice[j]->GetDeviceCaps(&caps);
				mLockFlags[j] = 0;
			
			hr = mDevice[j]->CreateTexture(mSurfaceRects[j].right - mSurfaceRects[j].left, height, 1, usage, D3DFMT_R5G6B5, D3DPOOL_SYSTEMMEM, &mSurfaces[j * 2], NULL);
			if (FAILED(hr))
			{
				DEBUG_STREAM << "Couldn't create dynamic texture for display " << j << "!" << std::endl << std::flush;
				PostQuitMessage(1);
			}

			hr = mDevice[j]->CreateTexture(mSurfaceRects[j].right - mSurfaceRects[j].left, height, 1, usage, D3DFMT_R5G6B5, D3DPOOL_DEFAULT, &mSurfaces[j * 2 + 1], NULL);
			if (FAILED(hr))
			{
				DEBUG_STREAM << "Couldn't create dynamic texture for display " << j << "!" << std::endl << std::flush;
				PostQuitMessage(1);
			}

			hr = mDevice[j]->CreateVertexBuffer(sizeof(float) * 6 * 4, NULL, D3DFVF_XYZRHW | D3DFVF_TEX1, D3DPOOL_MANAGED, &mVertBuffers[j], NULL);
			if (FAILED(hr))
			{
				DEBUG_STREAM << "Couldn't create vertex buffer for display " << j << "!" << std::endl << std::flush;
				PostQuitMessage(1);
			}

			float *buffer;
			hr = mVertBuffers[j]->Lock(0, 0, (void**)&buffer, 0);
			
			buffer[0] = 0.0f;	// top left, x
			buffer[1] = 0.0f;	// y
			buffer[2] = 0.0f;	// z
			buffer[3] = 1.0f;	// rhw
			buffer[4] = 0.0f;	// u
			buffer[5] = 0.0f;	// v

			buffer[6] = mSurfaceRects[j].right - mSurfaceRects[j].left;	// top right, x
			buffer[7] = 0.0f;	// y
			buffer[8] = 0.0f;	// z
			buffer[9] = 1.0f;	// rhw
			buffer[10] = 1.0f;	// u
			buffer[11] = 0.0f;	// v
			
			buffer[12] = mSurfaceRects[j].right - mSurfaceRects[j].left;	// bottom right, x
			buffer[13] = height;	// y
			buffer[14] = 0.0f;	// z
			buffer[15] = 1.0f;	// rhw
			buffer[16] = 1.0f;	// u
			buffer[17] = 1.0f,	// v
			
			buffer[18] = 0.0f;	// bottom left, x
			buffer[19] = height;	// y
			buffer[20] = 0.0f;	// z
			buffer[21] = 1.0f;	// rhw
			buffer[22] = 0.0f;	// u
			buffer[23] = 1.0f;	// v
			
			hr = mVertBuffers[j]->Unlock();

			mDevice[j]->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1);
			mDevice[j]->SetStreamSource(0, mVertBuffers[j], 0, sizeof(float) * 6);
			mDevice[j]->SetTexture(0, mSurfaces[j * 2 + 1]);

			mDevice[j]->Clear(0, NULL, D3DCLEAR_TARGET, 0xFF000000, 0.0f, 0);
			V( mDevice[j]->Present(NULL, NULL, NULL, NULL) );
			if (hr == D3DERR_DEVICELOST)
			{
				int bbCount = mPresentParams[j].BackBufferCount;
				int bbWidth = mPresentParams[j].BackBufferWidth;
				int bbHeight = mPresentParams[j].BackBufferHeight;

				mSurfaces[j * 2 + 1]->Release();
				V(mDevice[j]->Reset(&mPresentParams[j]));
				V(mDevice[j]->CreateTexture(mSurfaceRects[j].right - mSurfaceRects[j].left, height, 1, usage, D3DFMT_R5G6B5, D3DPOOL_DEFAULT, &mSurfaces[j * 2 + 1], NULL));

				mPresentParams[j].BackBufferCount = bbCount;
				mPresentParams[j].BackBufferWidth = bbWidth;
				mPresentParams[j].BackBufferHeight = bbHeight;
			}
		}
	}
}

	
//########################################################################
//############################# BitWrangler ##############################
//########################################################################
//
// A "bitwrangler" manages a group of bits by segregating them into two groups:
// "active", and "inactive".  Active bits are those specified within a given
// bit mask as "ones", and inactive bits are those specified as "zeros".
//
// Once initialized, the bitwrangler may be requested to increment either
// the active bit field or the inactive bit field: the "carry" is propagated
// from the least significant bit through the most significant bit, and if
// an overflow is generated it is returned as "False".
//
// Why in the world would anyone want such a bizarre object?
// It's used here to generate palettes and translation tables according
// to bit allocations for a GraphicsPort.  "Unused" colors are easily
// found and set to proper values.
//
class BitWrangler
{
public:
	BitWrangler(int bit_mask, int total_length);
	~BitWrangler()
		{}
	void	ResetActive();
	void	ResetInactive();
	Logical	IncrementActive();
	Logical	IncrementInactive();
	int	NumberOfActiveBits();
	int	NumberOfInactiveBits();

	int	Value;
protected:
	int	length;
	int	bitMask;
};

//
// Bitwrangler initialization
//
BitWrangler::BitWrangler(int bit_mask, int total_length)
{
	Verify(bit_mask != 0);

	bitMask = bit_mask;
	length = total_length;
	Value = 0;
	Check_Fpu();
}

//
// Reset all "active" bits to zero
//
void
	BitWrangler::ResetActive()
{
	Value &= ~ bitMask;
	Check_Fpu();
}

//
// Reset all "inactive" bits to zero
//
void
	BitWrangler::ResetInactive()
{
	Value &=  bitMask;
	Check_Fpu();
}

//
// Increment the "active" bit field, and return "false" if overflow
//
Logical
	BitWrangler::IncrementActive()
{
	int	single_bit;
	int	count(length);

	for(single_bit=1; count>0; --count, single_bit <<= 1)
	{
		//
		// If it's an active bit, process it
		//
		if (bitMask & single_bit)
		{
			//
			// Invert the bit
			//
			Value ^= single_bit;
			//
			// If the bit is now set, it was zero, so exit
			//
			if (Value & single_bit)
			{
				Check_Fpu();
				return True;
			}
		}
	}
	//
	// All the active bits are set, so return false
	//
	Check_Fpu();
	return False;
}

//
// Increment the "inactive" bit field, and return "false" if overflow
//
Logical
	BitWrangler::IncrementInactive()
{
	int	single_bit;
	int	count(length);
	int	inverse_mask(~bitMask);

	for(single_bit=1; count>0; --count, single_bit <<= 1)
	{
		//
		// If it's an inactive bit, process it
		//
		if (inverse_mask & single_bit)
		{
			//
			// Invert the bit
			//
			Value ^= single_bit;
			//
			// If the bit is now set, it was zero, so exit
			//
			if (Value & single_bit)
			{
				Check_Fpu();
				return True;
			}
		}
	}
	//
	// All the inactive bits are set, so return false
	//
	Check_Fpu();
	return False;
}

//
// Return the number of "active" bits
//
int
	BitWrangler::NumberOfActiveBits()
{
	int count(0), temp(bitMask);

	while(temp != 0)
	{
		++count;

		temp = temp & (temp-1);
	}

	Check_Fpu();
	return count;
}

//
// Return the number of "inactive" bits
//
int
	BitWrangler::NumberOfInactiveBits()
{
	Check_Fpu();
	return length-NumberOfActiveBits();
}

//########################################################################
//######################### Video16BitBuffered ###########################
//########################################################################
//
//NOTE: the application assumes that the origin is in the lower left
// corner of the display, and all parameters passed to these routines
// use the application's coordinate system.
//
// All of the methods here convert these values to the SCREEN
// coordinate system, with the origin in the UPPER LEFT corner of
// the display.
//

//===================================================================
// Creator
//===================================================================
Video16BitBuffered::Video16BitBuffered(int x, int y):
	GraphicsDisplay(x, y),
	pixelBuffer(x, y)
{
#	if defined(DEBUG)
		Tell(
			"Video16BitBuffered::Video16BitBuffered()\n"
		);
#	endif
	int
		i,
		changed_size;

	Verify(pixelBuffer.Data.MapPointer != NULL);

	height = y;
	width = x;
	changedBitWidth = (width >> 5);
	changed_size = height * changedBitWidth;

	maximumY = height-1;
	maximumX = width-1;
	//---------------------------------------------------------
	// Create 'changedLine' and 'changedBit' arrays
	//---------------------------------------------------------
	changedLine = new Byte[height];
	changedBit = new LWord[changed_size];

	if (changedLine == NULL || changedBit == NULL)
	{
#	if defined(DEBUG)
		Tell("INVALID!\n");
#	endif
		valid = False;
		return;
	}
	else
	{
		Register_Pointer(changedLine);
		Register_Pointer(changedBit);
		valid = True;
	}

#	if defined(DEBUG)
		Tell("changedLine=" << changedLine << "\n");
		Tell("changedBit=" << changedBit << "\n");
#	endif

	//---------------------------------------------------------
	// Clear the 'changedBit' array
	//---------------------------------------------------------
	memset(changedLine, 0, height);
	//---------------------------------------------------------
	// Clear the 'changedBit' array
	//---------------------------------------------------------
	LWord
		*bit_dest;

	for (i=changed_size,bit_dest=changedBit; i>0; --i,++bit_dest)
	{
		*bit_dest = (LWord) 0;
	}
	Check_Fpu();
}

//===================================================================
// Destructor
//===================================================================
Video16BitBuffered::~Video16BitBuffered()
{
	Check(this);

	if (changedLine != NULL)
	{
		Unregister_Pointer(changedLine);
		delete changedLine;
		changedLine = NULL;
	}

	if (changedBit != NULL)
	{
		Unregister_Pointer(changedBit);
		delete changedBit;
		changedBit = NULL;
	}
	Check_Fpu();
}


//===================================================================
// TestInstance
//===================================================================
Logical
	Video16BitBuffered::TestInstance() const
{
	return True;
}

//===================================================================
// ShowInstance
//===================================================================
void
	Video16BitBuffered::ShowInstance(
		char *indent
	)
{
	std::cout << indent << "Video16BitBuffered:\n";

	Check(this);

	char
		temp[80];

	Str_Copy(temp,indent, 80);
	Str_Cat(temp,"...", 80);

	std::cout << temp << "width      =" << width << "\n";
	std::cout << temp << "height     =" << height << "\n";
	std::cout << temp << "maximumX   =" << maximumX << "\n";
	std::cout << temp << "maximumY   =" << maximumY << "\n";
	std::cout << temp << "changedLine=" << changedLine << "\n";
	std::cout << temp << "changedBit =" << changedBit << "\n";

	pixelBuffer.ShowInstance(temp);
	GraphicsDisplay::ShowInstance(temp);
	Check_Fpu();
}


//===================================================================
// buildDestPointer
//===================================================================
void
	Video16BitBuffered::buildDestPointer(
		int screenX,
		int screenY,
		Word **pixel_pointer,
		LWord **changed_bit_pointer,
		LWord *changed_bit
	)
{
	Verify(valid);

	Verify(pixel_pointer != NULL);
	Verify(changed_bit_pointer != NULL);
	Verify(changed_bit != NULL);

	Verify(screenX >= 0);
	Verify(screenX < width);
	Verify(screenY >= 0);
	Verify(screenY < height);

	*pixel_pointer = pixelBuffer.Data.MapPointer +
		screenX +
		(screenY * width);
	Verify(*pixel_pointer >= pixelBuffer.Data.MapPointer);
	Verify(*pixel_pointer < &pixelBuffer.Data.MapPointer[height*width]);

	*changed_bit_pointer = changedBit +
		(screenX >> 5) +
		(screenY * changedBitWidth);
	Verify(*changed_bit_pointer >= changedBit);
	Verify(*changed_bit_pointer < &changedBit[height*changedBitWidth]);

	*changed_bit = 1L << (0x1F - (screenX & 0x1F));
	Verify(*changed_bit != 0L);

	Diag_Tell(
		"Video16BitBuffered::buildDestPointer(" << screenX <<
		", " << screenY << std::hex <<
		") = pix " << *pixel_pointer <<
		", cbp " << *changed_bit_pointer <<
		", cb " << *changed_bit << std::dec <<
		"\n"
	);
	Diag_Tell("changedBitWidth=" << changedBitWidth << "\n");

	Check_Fpu();
}

//===================================================================
// MarkChangedLines
//===================================================================
#if defined(BLIT_STATISTICS)
#	define SET_CHANGED(pointer, bits)  *pointer |= bits; ++dirtyPixelCount
#else
#	define SET_CHANGED(pointer, bits)  *pointer |= bits
#endif

#define LEFT_CHANGED(pointer, bits) \
	bits <<= 1; \
	if (bits == 0) { --pointer; bits=0x00000001L; }

#define RIGHT_CHANGED(pointer, bits) \
	bits >>= 1; \
	if (bits == 0) { ++pointer; bits=0x80000000L; }

#define UP_CHANGED(pointer, bits)   pointer -= changedBitWidth
#define DOWN_CHANGED(pointer, bits) pointer += changedBitWidth


#define LEFT_DEST(pointer) --pointer
#define RIGHT_DEST(pointer) ++pointer
#define UP_DEST(pointer) pointer -= width
#define DOWN_DEST(pointer) pointer += width

#define LEFT_SOURCE(pointer,map) --pointer
#define RIGHT_SOURCE(pointer,map) ++pointer
#define UP_SOURCE(pointer,map) pointer -= map->Data.Size.x
#define DOWN_SOURCE(pointer,map) pointer += map->Data.Size.x



#define LEFT_BITMAP(pointer,bits) \
	bits <<= 1; \
	if (bits == 0) { --pointer; bits=0x0001; }

#define RIGHT_BITMAP(pointer, bits) \
	bits >>= 1; \
	if (bits == 0) { ++pointer; bits=0x8000; }

#define UP_BITMAP(pointer,map) pointer -= map->Data.WidthInWords
#define DOWN_BITMAP(pointer,map) pointer += map->Data.WidthInWords


//
// Inputs are in DISPLAY COORDINATES, i.e., (0,0) in top left corner!
//
void
	Video16BitBuffered::MarkChangedLines(
		int start,
		int stop
	)
{
	Check(this);
	Diag_Tell(
		"Video16BitBuffered::MarkChangedLines(" << start <<
		", " << stop <<
		")\n"
	);

	if (start > stop)
	{
#		if defined(DEBUG)
			Tell("MarkChangedLines FLIPPING\n");
#		endif
		int	temp;

		temp = start;
		start = stop;
		stop = temp;
	}

	Verify(start >= 0);
	Verify(start <= maximumY);
	Verify(stop >= 0);
	Verify(stop <= maximumY);

	//------------------------------------------------
	// Set the flag for each changed line
	//------------------------------------------------
	Verify(stop >= start);
	memset(&changedLine[start], 1, stop-start+1);

	Check_Fpu();
}

//===================================================================
// DrawPoint
//===================================================================
void
	Video16BitBuffered::DrawPoint(
		int color,
		int bitmask,
		Enumeration operation,
		int x, int y
	)
{
	Check(this);
	Verify(x >= bounds.bottomLeft.x);
	Verify(x <= bounds.topRight.x);
	Verify(y >= bounds.bottomLeft.y);
	Verify(y <= bounds.topRight.y);

	Word
		*dest_pointer;
	LWord
		*changed_pointer;
	LWord
		changed_bit;
	//---------------------------------------------------------
	// If pixelbuffer is invalid, do nothing
	//---------------------------------------------------------
	if (!valid)
	{
		Check_Fpu();
		return;
	}
	//---------------------------------------------------------
	// Convert to screen co-ordinates
	//---------------------------------------------------------
	y = maximumY - y;

#	if defined(DEBUG)
		Tell("x=" << x << ", y=" << y << "\n");
#	endif

	Verify(x >= 0);
	Verify(x < width);
	Verify(y >= 0);
	Verify(y < height);
	//---------------------------------------------------------
	// Update changedLine array
	//---------------------------------------------------------
	MarkChangedLines(y, y);
	//---------------------------------------------------------
	// We really need inverse bitmap here
	//---------------------------------------------------------
	bitmask = ~bitmask;
	//---------------------------------------------------------
	// Create pointers
	//---------------------------------------------------------
	buildDestPointer(
		x, y,
		&dest_pointer,
		&changed_pointer,
		&changed_bit
	);
	//---------------------------------------------------------
	// Write the point
	//---------------------------------------------------------
	switch (operation)
	{
	case GraphicsDisplay::Replace:
		*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
		break;

	case GraphicsDisplay::And:
		*dest_pointer &= (Word) color;
		break;

	case GraphicsDisplay::Or:
		*dest_pointer |= (Word) color;
		break;

	case GraphicsDisplay::Xor:
		*dest_pointer ^= (Word) color;
		break;
	}
	SET_CHANGED(changed_pointer, changed_bit);
	Check_Fpu();
}

//===================================================================
// DrawLine
//===================================================================
void
	Video16BitBuffered::DrawLine(
		int color,
		int bitmask,
		Enumeration operation,
		int x1, int y1,
		int x2, int y2,
		Logical include_last_pixel
	)
{
#	if defined(DEBUG)
		Tell(
			"Video16BitBuffered::DrawLine(" << color <<
			", " << bitmask <<
			", " << operation <<
			", " << x1 << "," << y1 << "," << x2 << "," << y2 <<
			", " << include_last_pixel <<
			"\n"
		);
#	endif

	Check(this);

	Verify(x1 >= bounds.bottomLeft.x);
	Verify(x1 <= bounds.topRight.x);
	Verify(y1 >= bounds.bottomLeft.y);
	Verify(y1 <= bounds.topRight.y);

	Verify(x2 >= bounds.bottomLeft.x);
	Verify(x2 <= bounds.topRight.x);
	Verify(y2 >= bounds.bottomLeft.y);
	Verify(y2 <= bounds.topRight.y);

	enum
	{
		negative_delta_x = 1,
		negative_delta_y = 2,
		delta_y_greater = 0,
		delta_x_greater = 4
	};

	long
		length,
		rate,
		accumulator;

	int
		octant,
		delta_x,
		delta_y;

	Word
		*dest_pointer;
	LWord
		*changed_pointer;
	LWord
		changed_bit;

	//---------------------------------------------------------
	// If pixelbuffer is invalid, do nothing
	//---------------------------------------------------------
	if (!valid)
	{
		Check_Fpu();
		return;
	}
	//---------------------------------------------------------
	// Route to DrawPoint if single pixel
	//---------------------------------------------------------
	if (x1 == x2 && y1 == y2)
	{
		if (include_last_pixel)
		{
			DrawPoint(color, bitmask, operation, x1, y1);
		}
		return;
	}
	//---------------------------------------------------------
	// Convert to screen co-ordinates
	//---------------------------------------------------------
	y1 = maximumY - y1;
	y2 = maximumY - y2;
	//---------------------------------------------------------
	// Ensure that endpoints are legitimate
	//---------------------------------------------------------
	Verify(x1 >= 0);
	Verify(x1 < width);
	Verify(x2 >= 0);
	Verify(x2 < width);

	Verify(y1 >= 0);
	Verify(y1 < height);
	Verify(y2 >= 0);
	Verify(y2 < height);

#	if defined(DEBUG)
		Tell(
			"Transformed coordinates=" << x1 << "," << y1 <<
			"," << x2 << "," << y2 <<
			"\n"
		);
#	endif
	//---------------------------------------------------------
	// Update changedLine array
	//---------------------------------------------------------
	MarkChangedLines(y1, y2);
	//---------------------------------------------------------
	// We really need inverse bitmap here
	//---------------------------------------------------------
	bitmask = ~bitmask;
	//---------------------------------------------------------
	// Determine which octant to use
	//---------------------------------------------------------
	octant = 0;

	delta_x = x2 - x1;
	if (delta_x < 0)
	{
#		if defined(DEBUG)
			Tell("negative dx\n");
#		endif
		octant |= negative_delta_x;
		delta_x = - delta_x;
	}

	delta_y = y2 - y1;
	if (delta_y < 0)
	{
#		if defined(DEBUG)
			Tell("negative dy\n");
#		endif
		octant |= negative_delta_y;
		delta_y = - delta_y;
	}

	if (delta_x > delta_y)
	{
#		if defined(DEBUG)
			Tell("dx > dy\n");
#		endif
		octant |= delta_x_greater;
	}
#	if defined(DEBUG)
		Tell(
			"dx=" << delta_x <<
			", dy=" << delta_y <<
			", octant=" << octant <<
			", color=" << color <<
			"\n"
		);
#	endif
	//---------------------------------------------------------
	// Prepare to draw line
	//---------------------------------------------------------
	buildDestPointer(
		x1, y1,
		&dest_pointer,
		&changed_pointer,
		&changed_bit
	);

	accumulator = 1024L;			// preset accumulator to 1/2 full
	//---------------------------------------------------------
	// Draw the line!
	//---------------------------------------------------------
	switch (octant)
	{
	case delta_y_greater:
		//-------------------------------------
		// delta y greater
		// positive delta x
		// positive delta y
		//
		//     +
		//      \                             &
		//       \                            &
		//-------------------------------------
		length = delta_y;
		if (!include_last_pixel)
		{
			-- length;
		}

		Verify(delta_y > 0);
		rate = (delta_x*2048L)/delta_y;

#		if defined(DEBUG)
			Tell(
				"delta_y_greater, length=" << length <<
				", rate=" << rate <<
				"\n"
			);
#		endif
		Verify(rate >= 0L);
		Verify(rate <= 2048L);
		for ( ; length>0; --length)
		{
			switch (operation)
			{
			case GraphicsDisplay::Replace:
				*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
				break;

			case GraphicsDisplay::And:
				*dest_pointer &= (Word) color;
				break;

			case GraphicsDisplay::Or:
				*dest_pointer |= (Word) color;
				break;

			case GraphicsDisplay::Xor:
				*dest_pointer ^= (Word) color;
				break;
			}
#			if defined(DEBUG)
				Tell(
					length <<
					", accum=" << accumulator <<
					", dest_pointer=" << dest_pointer <<
					"\n"
				);
#			endif
			SET_CHANGED(changed_pointer, changed_bit);

			DOWN_DEST(dest_pointer);
			DOWN_CHANGED(changed_pointer, changed_bit);

			accumulator += rate;
			if (accumulator > 2048L)
			{
#				if defined(DEBUG)
					Tell("OVERFLOW\n");
#				endif
				accumulator -= 2048L;
				RIGHT_DEST(dest_pointer);
				RIGHT_CHANGED(changed_pointer, changed_bit);
			}
		}
		break;

	case delta_y_greater+negative_delta_x:
		//-------------------------------------
		// delta y greater
		// negative delta x
		// positive delta y
		//
		//     +
		//    /
		//   /
		//-------------------------------------
		length = delta_y;
		if (!include_last_pixel)
		{
			-- length;
		}
		rate = (delta_x*2048L)/delta_y;

#		if defined(DEBUG)
			Tell(
				"delta_y_greater+negative_delta_x, length=" << length <<
				", rate=" << rate <<
				"\n"
			);
#		endif
		Verify(rate >= 0L);
		Verify(rate <= 2048L);
		Verify(length >= 0);
		for ( ; length>0; --length)
		{
			switch (operation)
			{
			case GraphicsDisplay::Replace:
				*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
				break;

			case GraphicsDisplay::And:
				*dest_pointer &= (Word) color;
				break;

			case GraphicsDisplay::Or:
				*dest_pointer |= (Word) color;
				break;

			case GraphicsDisplay::Xor:
				*dest_pointer ^= (Word) color;
				break;
			}
#			if defined(DEBUG)
				Tell(
					length <<
					", accum=" << accumulator <<
					", dest_pointer=" << dest_pointer <<
					"\n"
				);
#			endif
			SET_CHANGED(changed_pointer, changed_bit);

			DOWN_DEST(dest_pointer);
			DOWN_CHANGED(changed_pointer, changed_bit);

			accumulator += rate;
			if (accumulator > 2048L)
			{
#				if defined(DEBUG)
					Tell("OVERFLOW\n");
#				endif
				accumulator -= 2048L;
				LEFT_DEST(dest_pointer);
				LEFT_CHANGED(changed_pointer, changed_bit);
			}
		}
		break;

	case delta_y_greater+negative_delta_y:
		//-------------------------------------
		// delta y greater
		// positive delta x
		// negative delta y
		//
		//     /
		//    /
		//   +
		//-------------------------------------
		length = delta_y;
		if (!include_last_pixel)
		{
			-- length;
		}
		rate = (delta_x*2048L)/delta_y;

#		if defined(DEBUG)
			Tell(
				"delta_y_greater+negative_delta_y, length=" << length <<
				", rate=" << rate <<
				"\n"
			);
#		endif
		Verify(rate >= 0L);
		Verify(rate <= 2048L);
		Verify(length >= 0);
		for ( ; length>0; --length)
		{
			switch (operation)
			{
			case GraphicsDisplay::Replace:
				*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
				break;

			case GraphicsDisplay::And:
				*dest_pointer &= (Word) color;
				break;

			case GraphicsDisplay::Or:
				*dest_pointer |= (Word) color;
				break;

			case GraphicsDisplay::Xor:
				*dest_pointer ^= (Word) color;
				break;
			}
#			if defined(DEBUG)
				Tell(
					length <<
					", accum=" << accumulator <<
					", dest_pointer=" << dest_pointer <<
					"\n"
				);
#			endif
			SET_CHANGED(changed_pointer, changed_bit);

			UP_DEST(dest_pointer);
			UP_CHANGED(changed_pointer, changed_bit);

			accumulator += rate;
			if (accumulator > 2048L)
			{
#				if defined(DEBUG)
					Tell("OVERFLOW\n");
#				endif
				accumulator -= 2048L;
				RIGHT_DEST(dest_pointer);
				RIGHT_CHANGED(changed_pointer, changed_bit);
			}
		}
		break;

	case delta_y_greater+negative_delta_x+negative_delta_y:
		//-------------------------------------
		// delta y greater
		// negative delta x
		// negative delta y
		//
		//  \											&
		//   \										&
		//   +
		//-------------------------------------
		length = delta_y;
		if (!include_last_pixel)
		{
			-- length;
		}
		rate = (delta_x*2048L)/delta_y;

#		if defined(DEBUG)
			Tell(
				"delta_y_greater+negative_delta_x+negative_delta_y, length=" <<
				length <<
				", rate=" << rate <<
				"\n"
			);
#		endif
		Verify(rate >= 0L);
		Verify(rate <= 2048L);
		Verify(length >= 0);
		for ( ; length>0; --length)
		{
			switch (operation)
			{
			case GraphicsDisplay::Replace:
				*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
				break;

			case GraphicsDisplay::And:
				*dest_pointer &= (Word) color;
				break;

			case GraphicsDisplay::Or:
				*dest_pointer |= (Word) color;
				break;

			case GraphicsDisplay::Xor:
				*dest_pointer ^= (Word) color;
				break;
			}
#			if defined(DEBUG)
				Tell(
					length <<
					", accum=" << accumulator <<
					", dest_pointer=" << dest_pointer <<
					"\n"
				);
#			endif
			SET_CHANGED(changed_pointer, changed_bit);

			UP_DEST(dest_pointer);
			UP_CHANGED(changed_pointer, changed_bit);

			accumulator += rate;
			if (accumulator > 2048L)
			{
#				if defined(DEBUG)
					Tell("OVERFLOW\n");
#				endif
				accumulator -= 2048L;
				LEFT_DEST(dest_pointer);
				LEFT_CHANGED(changed_pointer, changed_bit);
			}
		}
		break;

	case delta_x_greater:
		//-------------------------------------
		// delta x greater
		// positive delta x
		// positive delta y
		//
		//     +---___
		//
		//
		//-------------------------------------
		length = delta_x;
		if (!include_last_pixel)
		{
			-- length;
		}
		rate = (delta_y*2048L)/delta_x;

#		if defined(DEBUG)
			Tell(
				"delta_x_greater, length=" << length <<
				", rate=" << rate <<
				"\n"
			);
#		endif
		Verify(rate >= 0L);
		Verify(rate <= 2048L);
		Verify(length >= 0);
		for ( ; length>0; --length)
		{
			switch (operation)
			{
			case GraphicsDisplay::Replace:
				*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
				break;

			case GraphicsDisplay::And:
				*dest_pointer &= (Word) color;
				break;

			case GraphicsDisplay::Or:
				*dest_pointer |= (Word) color;
				break;

			case GraphicsDisplay::Xor:
				*dest_pointer ^= (Word) color;
				break;
			}
#			if defined(DEBUG)
				Tell(
					length <<
					", accum=" << accumulator <<
					", dest_pointer=" << dest_pointer <<
					"\n"
				);
#			endif
			SET_CHANGED(changed_pointer, changed_bit);

			RIGHT_DEST(dest_pointer);
			RIGHT_CHANGED(changed_pointer, changed_bit);

			accumulator += rate;
			if (accumulator > 2048L)
			{
#				if defined(DEBUG)
					Tell("OVERFLOW\n");
#				endif
				accumulator -= 2048L;
				DOWN_DEST(dest_pointer);
				DOWN_CHANGED(changed_pointer, changed_bit);
			}
		}
		break;

	case delta_x_greater+negative_delta_x:
		//-------------------------------------
		// delta x greater
		// negative delta x
		// positive delta y
		//
		// ___---+
		// 
		//
		//-------------------------------------
		length = delta_x;
		if (!include_last_pixel)
		{
			-- length;
		}
		rate = (delta_y*2048L)/delta_x;

#		if defined(DEBUG)
			Tell(
				"delta_x_greater+negative_delta_x, length=" << length <<
				", rate=" << rate <<
				"\n"
			);
#		endif
		Verify(rate >= 0L);
		Verify(rate <= 2048L);
		Verify(length >= 0);
		for ( ; length>0; --length)
		{
			switch (operation)
			{
			case GraphicsDisplay::Replace:
				*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
				break;

			case GraphicsDisplay::And:
				*dest_pointer &= (Word) color;
				break;

			case GraphicsDisplay::Or:
				*dest_pointer |= (Word) color;
				break;

			case GraphicsDisplay::Xor:
				*dest_pointer ^= (Word) color;
				break;
			}
#			if defined(DEBUG)
				Tell(
					length <<
					", accum=" << accumulator <<
					", dest_pointer=" << dest_pointer <<
					"\n"
				);
#			endif
			SET_CHANGED(changed_pointer, changed_bit);

			LEFT_DEST(dest_pointer);
			LEFT_CHANGED(changed_pointer, changed_bit);

			accumulator += rate;
			if (accumulator > 2048L)
			{
#				if defined(DEBUG)
					Tell("OVERFLOW\n");
#				endif
				accumulator -= 2048L;
				DOWN_DEST(dest_pointer);
				DOWN_CHANGED(changed_pointer, changed_bit);
			}
		}
		break;

	case delta_x_greater+negative_delta_y:
		//-------------------------------------
		// delta x greater
		// positive delta x
		// negative delta y
		//
		//       +___---
		// 
		//
		//-------------------------------------
		length = delta_x;
		if (!include_last_pixel)
		{
			-- length;
		}
		rate = (delta_y*2048L)/delta_x;

#		if defined(DEBUG)
			Tell(
				"delta_x_greater+negative_delta_y, length=" << length <<
				", rate=" << rate <<
				"\n"
			);
#		endif
		Verify(rate >= 0L);
		Verify(rate <= 2048L);
		Verify(length >= 0);
		for ( ; length>0; --length)
		{
			switch (operation)
			{
			case GraphicsDisplay::Replace:
				*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
				break;

			case GraphicsDisplay::And:
				*dest_pointer &= (Word) color;
				break;

			case GraphicsDisplay::Or:
				*dest_pointer |= (Word) color;
				break;

			case GraphicsDisplay::Xor:
				*dest_pointer ^= (Word) color;
				break;
			}
#			if defined(DEBUG)
				Tell(
					length <<
					", accum=" << accumulator <<
					", dest_pointer=" << dest_pointer <<
					"\n"
				);
#			endif
			SET_CHANGED(changed_pointer, changed_bit);

			RIGHT_DEST(dest_pointer);
			RIGHT_CHANGED(changed_pointer, changed_bit);

			accumulator += rate;
			if (accumulator > 2048L)
			{
#				if defined(DEBUG)
					Tell("OVERFLOW\n");
#				endif
				accumulator -= 2048L;
				UP_DEST(dest_pointer);
				UP_CHANGED(changed_pointer, changed_bit);
			}
		}
		break;

	case delta_x_greater+negative_delta_x+negative_delta_y:
		//-------------------------------------
		// delta x greater
		// negative delta x
		// negative delta y
		//
		// ---___+
		// 
		//
		//-------------------------------------
		length = delta_x;
		if (!include_last_pixel)
		{
			-- length;
		}
		rate = (delta_y*2048L)/delta_x;

#		if defined(DEBUG)
			Tell(
				"delta_x_greater+negative_delta_x+negative_delta_y, length=" <<
				length <<
				", rate=" << rate <<
				"\n"
			);
#		endif
		Verify(rate >= 0L);
		Verify(rate <= 2048L);
		Verify(length >= 0);
		for ( ; length>0; --length)
		{
			switch (operation)
			{
			case GraphicsDisplay::Replace:
				*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
				break;

			case GraphicsDisplay::And:
				*dest_pointer &= (Word) color;
				break;

			case GraphicsDisplay::Or:
				*dest_pointer |= (Word) color;
				break;

			case GraphicsDisplay::Xor:
				*dest_pointer ^= (Word) color;
				break;
			}
#			if defined(DEBUG)
				Tell(
					length <<
					", accum=" << accumulator <<
					", dest_pointer=" << dest_pointer <<
					"\n"
				);
#			endif
			SET_CHANGED(changed_pointer, changed_bit);

			LEFT_DEST(dest_pointer);
			LEFT_CHANGED(changed_pointer, changed_bit);

			accumulator += rate;
			if (accumulator > 2048L)
			{
#				if defined(DEBUG)
					Tell("OVERFLOW\n");
#				endif
				accumulator -= 2048L;
				UP_DEST(dest_pointer);
				UP_CHANGED(changed_pointer, changed_bit);
			}
		}
		break;
	}        
	Check_Fpu();
}

//===================================================================
// DrawFilledRectangle
//===================================================================
void
	Video16BitBuffered::DrawFilledRectangle(
		int color,
		int bitmask,
		Enumeration operation,
		int x1, int y1,
		int x2, int y2
	)
{
#	if defined(DEBUG)
		Tell("Video16BitBuffered::DrawFilledRectangle(" <<
		color << ", " <<
		std::hex << bitmask << ", " << std::dec <<
		operation << ", " <<
		x1 << ", " <<
		y1 << ", " <<
		x2 << ", " <<
		y2 << ", " <<
		")\n");
#	endif
	Check(this);
	Verify(x1 >= bounds.bottomLeft.x);
	Verify(x1 <= bounds.topRight.x);
	Verify(y1 >= bounds.bottomLeft.y);
	Verify(y1 <= bounds.topRight.y);

	Verify(x2 >= bounds.bottomLeft.x);
	Verify(x2 <= bounds.topRight.x);
	Verify(y2 >= bounds.bottomLeft.y);
	Verify(y2 <= bounds.topRight.y);

	Word
		*dest_pointer;
	LWord
		*init_changed_pointer,
		*changed_pointer;
	LWord
		init_changed_bit,
		changed_bit;
	int
		x,
		dest_fixup,
		rect_width,
		rect_height;

	//---------------------------------------------------------
	// If pixelbuffer is invalid, do nothing
	//---------------------------------------------------------
	if (!valid)
	{
		Check_Fpu();
		return;
	}
	//---------------------------------------------------------
	// Convert to screen co-ordinates
	//---------------------------------------------------------
	y1 = maximumY - y1;
	y2 = maximumY - y2;
	//---------------------------------------------------------
	// Ensure that rectangle is properly specified
	//---------------------------------------------------------
	if (x2 < x1)
	{
		int temp;

		temp = x1;
		x1 = x2;
		x2 = temp;
	}

	if (y2 < y1)
	{
		int temp;

		temp = y1;
		y1 = y2;
		y2 = temp;
	}
	//---------------------------------------------------------
	// Verify that values are legitimate
	//---------------------------------------------------------
	Verify(pixelBuffer.Data.MapPointer != NULL);

	Verify(x1 >= 0);
	Verify(x1 <= x2);
	Verify(x2 < width);

	Verify(y1 >= 0);
	Verify(y1 <= y2);
	Verify(y2 < height);
	//---------------------------------------------------------
	// Update changedLine array
	//---------------------------------------------------------
	MarkChangedLines(y1, y2);
	//---------------------------------------------------------
	// We really need inverse bitmap here
	//---------------------------------------------------------
	bitmask = ~bitmask;
	//---------------------------------------------------------
	// Prepare to fill
	//---------------------------------------------------------
#	if defined(DEBUG)
		Tell(
			"x1=" << x1 << ", " << "y1=" << y1 << "\n" <<
			"x2=" << x2 << ", " << "y2=" << y2 << "\n" <<
			std::flush
		);
#	endif
	buildDestPointer(
		x1, y1,
		&dest_pointer,
		&init_changed_pointer,
		&init_changed_bit
	);

	rect_height = y2 - y1 + 1;
	rect_width  = x2 - x1 + 1;
	dest_fixup = width - rect_width;

#	if defined(DEBUG)
		Tell(
			"rect_height=" << rect_height << "\n" <<
			"rect_width=" << rect_width << "\n" <<
			"dest_fixup=" << dest_fixup << "\n" <<
			std::flush
		);
#	endif
	//---------------------------------------------------------
	// Fill the rectangle
	//---------------------------------------------------------
	switch(operation)
	{
	case GraphicsDisplay::Replace:
		for( ; rect_height>0; --rect_height)
		{
			changed_bit     = init_changed_bit;
			changed_pointer = init_changed_pointer;
			DOWN_CHANGED(init_changed_pointer, init_changed_bit);
			for(x=rect_width; x>0; --x)
			{
				*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
				dest_pointer++;
				SET_CHANGED(changed_pointer, changed_bit);
				RIGHT_CHANGED(changed_pointer, changed_bit);
			}
			dest_pointer += dest_fixup;
		}
		break;

	case GraphicsDisplay::And:
		for( ; rect_height>0; --rect_height)
		{
			changed_bit     = init_changed_bit;
			changed_pointer = init_changed_pointer;
			DOWN_CHANGED(init_changed_pointer, init_changed_bit);
			for(x=rect_width; x>0; --x)
			{
				*dest_pointer++ &= (Word) color;
				SET_CHANGED(changed_pointer, changed_bit);
				RIGHT_CHANGED(changed_pointer, changed_bit);
			}
			dest_pointer += dest_fixup;
		}
		break;

	case GraphicsDisplay::Or:
		for( ; rect_height>0; --rect_height)
		{
			changed_bit     = init_changed_bit;
			changed_pointer = init_changed_pointer;
			DOWN_CHANGED(init_changed_pointer, init_changed_bit);
			for(x=rect_width; x>0; --x)
			{
				*dest_pointer++ |= (Word) color;
				SET_CHANGED(changed_pointer, changed_bit);
				RIGHT_CHANGED(changed_pointer, changed_bit);
			}
			dest_pointer += dest_fixup;
		}
		break;

	case GraphicsDisplay::Xor:
		for( ; rect_height>0; --rect_height)
		{
			changed_bit     = init_changed_bit;
			changed_pointer = init_changed_pointer;
			DOWN_CHANGED(init_changed_pointer, init_changed_bit);
			for(x=rect_width; x>0; --x)
			{
				*dest_pointer++ ^= (Word) color;
				SET_CHANGED(changed_pointer, changed_bit);
				RIGHT_CHANGED(changed_pointer, changed_bit);
			}
			dest_pointer += dest_fixup;
		}
		break;
	}        
	Check_Fpu();
}


void
	Video16BitBuffered::DrawText(
		int /*color*/,
		int /*bitmask*/,
		Enumeration /*operation*/,
		Logical /*opaque*/,
		int /*rotation*/,
		Enumeration /*fontNumber*/,
		Logical /*vertical*/,
		GraphicsDisplay::Justification /*justification*/,
		Rectangle2D	*/*clippingRectanglepointer*/,
		char */*stringPointer*/
	)
{
#	if defined(DEBUG)
		Tell("Video16BitBuffered::DrawText()\n");
#	endif
	Check(this);
	//---------------------------------------------------------
	// If pixelbuffer is invalid, do nothing
	//---------------------------------------------------------
	Check_Fpu();
	if (!valid)
	{
		return;
	}
}

//===================================================================
// DrawBitMap
//===================================================================
void
	Video16BitBuffered::DrawBitMap(
		int color,
		int bitmask,
		Enumeration operation,
		int rotation,
		int x, int y,
		BitMap *bitmap,
		int sLeft, int sBottom, int sRight, int sTop
	)
{
#	if defined(DEBUG)
		Tell(
			"Video16BitBuffered::DrawBitMap(<" <<
			x << ", " <<
			y << ">,<" <<
			sLeft << ", " <<
			sBottom << ", " <<
			sRight << ", " <<
			sTop << ">" <<
			")\n"
		);
#	endif
	Check(this);
	Verify(x >= bounds.bottomLeft.x);
	Verify(x <= bounds.topRight.x);
	Verify(y >= bounds.bottomLeft.y);
	Verify(y <= bounds.topRight.y);

	int
		map_width,
		map_height,
		map_max_y,
		bits,
		bit_test,
		first_bit_test;

	Word
		*source_pointer_begin,
		*source_pointer,
		*dest_pointer_begin,
		*dest_pointer;

	LWord
		changed_bit_begin,
		changed_bit,
		*changed_pointer_begin,
		*changed_pointer;

	//---------------------------------------------------------
	// If pixelbuffer is invalid, do nothing
	//---------------------------------------------------------
	if (!valid)
	{
		Check_Fpu();
		return;
	}
	//---------------------------------------------------------
	// Ensure that source rectangle is properly specified
	//---------------------------------------------------------
	Verify(bitmap != NULL);

	Verify(sLeft >= 0);
	Verify(sLeft <= sRight);
	Verify(sRight < bitmap->Data.Size.x);

	Verify(sBottom >= 0);
	Verify(sBottom <= sTop);
	Verify(sTop < bitmap->Data.Size.y);
	//---------------------------------------------------------
	// Convert to screen co-ordinates
	//---------------------------------------------------------
	y = maximumY - y;

	map_max_y = bitmap->Data.Size.y-1; 
	sTop    = map_max_y - sTop;
	sBottom = map_max_y - sBottom;

	map_width = sRight - sLeft + 1;
	map_height = sBottom - sTop + 1;

#	if defined(DEBUG)
		Tell("x=" << x << ", y=" << y << "\n");
		Tell("sTop=" << sTop << ", sBottom=" << sBottom << "\n");
		Tell("map_width=" << map_width << ", map_height=" << map_height << "\n");
#	endif

	Verify(map_width > 0);
	Verify(map_width <= bitmap->Data.Size.x);
	Verify(map_height > 0);
	Verify(map_height <= bitmap->Data.Size.y);

	Verify(x >= 0);
	Verify(x < width);
	Verify(y >= 0);
	Verify(y < height);

	//---------------------------------------------------------
	// We really need inverse bitmap here
	//---------------------------------------------------------
	bitmask = ~bitmask;
	//---------------------------------------------------------
	// Prepare to draw bitmap
	//---------------------------------------------------------
	Verify(pixelBuffer.Data.MapPointer != NULL);

	source_pointer_begin =
		bitmap->Data.MapPointer +
		(sLeft >> 4) +
		(sBottom * bitmap->Data.WidthInWords);

	first_bit_test = 1 << (15-(sLeft & 15));

	buildDestPointer(
		x, y,
		&dest_pointer_begin,
		&changed_pointer_begin,
		&changed_bit_begin
	);

	switch (rotation)
	{
	default:
		//--------------------------------------------
		// transparent bitmap, zero degrees
		//   .-----.
		//   |     |
		//   |     |
		//   Y     |
		//   #X----.
		//--------------------------------------------
#		if defined(DEBUG)
			Tell(
				"zero: xp=" << (x+map_width-1) <<
				", yp=" << (y-map_height+1) <<
				"\n"
			);
#		endif
		Verify(x+map_width-1 >= 0);
		Verify(x+map_width-1 < width);
		Verify(y-map_height+1 >= 0);
		Verify(y-map_height+1 < height);

		MarkChangedLines(y-map_height+1, y);

		for(y=map_height; y>0; --y)
		{
			source_pointer = source_pointer_begin;
			changed_pointer = changed_pointer_begin;
			changed_bit = changed_bit_begin;
			dest_pointer = dest_pointer_begin;

			UP_BITMAP(source_pointer_begin, bitmap);
			UP_CHANGED(changed_pointer_begin, changed_bit_begin);
			UP_DEST(dest_pointer_begin);

			bit_test = first_bit_test;
			bits = *source_pointer++;
			for(x=map_width; x>0; --x,bit_test>>=1)
			{
				if (bit_test == 0)
				{
					bit_test = 0x8000;
					bits = *source_pointer++;
				}

				if (bits & bit_test)
				{
					switch (operation)
					{
					case GraphicsDisplay::Replace:
						*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
						break;

					case GraphicsDisplay::And:
						*dest_pointer &= (Word) color;
						break;

					case GraphicsDisplay::Or:
						*dest_pointer |= (Word) color;
						break;

					case GraphicsDisplay::Xor:
						*dest_pointer ^= (Word) color;
						break;
					}
					SET_CHANGED(changed_pointer, changed_bit);
				}
				RIGHT_CHANGED(changed_pointer, changed_bit);
				RIGHT_DEST(dest_pointer);
			}
		}
		break;

	case 90:
		//--------------------------------------------
		// Transparent bitmap, 90 degrees
		//   #Y----.
		//   X     |
		//   |     |
		//   |     |
		//   .-----.
		//--------------------------------------------
#		if defined(DEBUG)
			Tell("90 xp=" << (x+map_height) << ", yp=" << (y+map_width) << "\n");
#		endif
		Verify(x+map_height >= 0);
		Verify(x+map_height < width);
		Verify(y+map_width >= 0);
		Verify(y+map_width < height);

		MarkChangedLines(y, y+map_width);

		for(y=map_height; y>0; --y)
		{
			source_pointer = source_pointer_begin;
			changed_pointer = changed_pointer_begin;
			changed_bit = changed_bit_begin;
			dest_pointer = dest_pointer_begin;

			UP_BITMAP(source_pointer_begin, bitmap);
			RIGHT_CHANGED(changed_pointer_begin, changed_bit_begin);
			RIGHT_DEST(dest_pointer_begin);
															 
			bit_test = first_bit_test;
			bits = *source_pointer++;

			for(x=map_width; x>0; --x,bit_test>>=1)
			{
				if (bit_test == 0)
				{
					bit_test = 0x8000;
					bits = *source_pointer++;
				}

				if (bits & bit_test)
				{
					switch (operation)
					{
					case GraphicsDisplay::Replace:
						*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
						break;

					case GraphicsDisplay::And:
						*dest_pointer &= (Word) color;
						break;

					case GraphicsDisplay::Or:
						*dest_pointer |= (Word) color;
						break;

					case GraphicsDisplay::Xor:
						*dest_pointer ^= (Word) color;
						break;
					}
					SET_CHANGED(changed_pointer, changed_bit);
				}
				DOWN_CHANGED(changed_pointer, changed_bit);
				DOWN_DEST(dest_pointer);
			}
		}
		break;

	case 180:
		//--------------------------------------------
		// Transparent bitmap, 180 degrees
		//   .----X#
		//   |     Y
		//   |     |
		//   |     |
		//   .-----.
		//--------------------------------------------
#		if defined(DEBUG)
			Tell("xp=" << (x-map_width+1) << ", yp=" << (y+map_height) << "\n");
#		endif
		Verify(x-map_width+1 >= 0);
		Verify(x-map_width+1 < width);
		Verify(y+map_height >= 0);
		Verify(y+map_height < height);

		MarkChangedLines(y, y+map_height);

		for(y=map_height; y>0; --y)
		{
			source_pointer = source_pointer_begin;
			changed_pointer = changed_pointer_begin;
			changed_bit = changed_bit_begin;
			dest_pointer = dest_pointer_begin;

			UP_BITMAP(source_pointer_begin, bitmap);
			DOWN_CHANGED(changed_pointer_begin, changed_bit_begin);
			DOWN_DEST(dest_pointer_begin);

			bit_test = first_bit_test;
			bits = *source_pointer++;

			for(x=map_width; x>0; --x,bit_test>>=1)
			{
				if (bit_test == 0)
				{
					bit_test = 0x8000;
					bits = *source_pointer++;
				}

				if (bits & bit_test)
				{
					switch (operation)
					{
					case GraphicsDisplay::Replace:
						*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
						break;

					case GraphicsDisplay::And:
						*dest_pointer &= (Word) color;
						break;

					case GraphicsDisplay::Or:
						*dest_pointer |= (Word) color;
						break;

					case GraphicsDisplay::Xor:
						*dest_pointer ^= (Word) color;
						break;
					}
					SET_CHANGED(changed_pointer, changed_bit);
				}
				LEFT_CHANGED(changed_pointer, changed_bit);
				LEFT_DEST(dest_pointer);
			}
		}
		break;

	case 270:
		//--------------------------------------------
		// Transparent bitmap, 270 degrees
		//   .-----.
		//   |     |
		//   |     X
		//   |     |
		//   .--Y--#
		//--------------------------------------------
#		if defined(DEBUG)
			Tell("xp=" << (x-map_height) << ", yp=" << (y-(map_width-1)) << "\n");
#		endif

	if (x-map_height < 0)
	{
		Tell("x-map_height = " << (x - map_height) << "\n");
		return;
	}

		Verify(x-map_height >= 0);
		Verify(x-map_height < width);
		Verify(y-(map_width-1) >= 0);
		Verify(y-(map_width-1) < height);

		MarkChangedLines(y-(map_width-1), y);

		for(y=map_height; y>0; --y)
		{
			source_pointer = source_pointer_begin;
			changed_pointer = changed_pointer_begin;
			changed_bit = changed_bit_begin;
			dest_pointer = dest_pointer_begin;

			UP_BITMAP(source_pointer_begin, bitmap);
			LEFT_CHANGED(changed_pointer_begin, changed_bit_begin);
			LEFT_DEST(dest_pointer_begin);

			bit_test = first_bit_test;
			bits = *source_pointer++;

			for(x=map_width; x>0; --x,bit_test>>=1)
			{
				Verify(dest_pointer >= pixelBuffer.Data.MapPointer);
				Verify(dest_pointer < pixelBuffer.Data.MapPointer+
					(width*height));

				Verify(changed_pointer >= changedBit);
				Verify(changed_pointer < changedBit+(changedBitWidth*height));

				if (bit_test == 0)
				{
					bit_test = 0x8000;
					bits = *source_pointer++;
				}

				if (bits & bit_test)
				{
					switch (operation)
					{
					case GraphicsDisplay::Replace:
						*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
						break;

					case GraphicsDisplay::And:
						*dest_pointer &= (Word) color;
						break;

					case GraphicsDisplay::Or:
						*dest_pointer |= (Word) color;
						break;

					case GraphicsDisplay::Xor:
						*dest_pointer ^= (Word) color;
						break;
					}
					SET_CHANGED(changed_pointer, changed_bit);
				}
				UP_CHANGED(changed_pointer, changed_bit_begin);
				UP_DEST(dest_pointer);
			}
		}
		break;
	}
	Check_Fpu();
}

//===================================================================
// DrawBitMapOpaque
//===================================================================
void
	Video16BitBuffered::DrawBitMapOpaque(
		int foreground,
		int background,
		int bitmask,
		Enumeration operation,
		int rotation,
		int x, int y,
		BitMap *bitmap,
		int sLeft, int sBottom, int sRight, int sTop
	)
{
	Check(this);

Diag_on;
	Diag_Tell(
		"Video16BitBuffered::DrawBitMapOpaque(<" << x <<
		", " << y <<
		">,<" <<	sLeft <<
		", " << sBottom <<
		", " << sRight <<
		", " << sTop <<
		">)\n"
	);
	Verify(x >= bounds.bottomLeft.x);
	Verify(x <= bounds.topRight.x);
	Verify(y >= bounds.bottomLeft.y);
	Verify(y <= bounds.topRight.y);

	int
		map_width,
		map_height,
		map_max_y,
		bits,
		bit_test,
		first_bit_test;

	Word
		*source_pointer_begin,
		*source_pointer,
		*dest_pointer_begin,
		*dest_pointer;

	LWord
		changed_bit_begin,
		changed_bit,
		*changed_pointer_begin,
		*changed_pointer;

	Word
		color;

	//---------------------------------------------------------
	// If pixelbuffer is invalid, do nothing
	//---------------------------------------------------------
	if (!valid)
	{
		Check_Fpu();
		return;
	}
	//---------------------------------------------------------
	// Ensure that source rectangle is properly specified
	//---------------------------------------------------------
	Verify(bitmap != NULL);

	Verify(sLeft >= 0);
	Verify(sLeft <= sRight);
	Verify(sRight < bitmap->Data.Size.x);

	Verify(sBottom >= 0);
	Verify(sBottom <= sTop);
	Verify(sTop < bitmap->Data.Size.y);
	//---------------------------------------------------------
	// Convert to screen co-ordinates
	//---------------------------------------------------------
	y = maximumY - y;

	map_max_y = bitmap->Data.Size.y-1; 
	sTop    = map_max_y - sTop;
	sBottom = map_max_y - sBottom;

	map_width = sRight - sLeft + 1;
	map_height = sBottom - sTop + 1;

Diag_off;
	Diag_Tell("x=" << x << ", y=" << y << "\n");
	Diag_Tell("sTop=" << sTop << ", sBottom=" << sBottom << "\n");
	Diag_Tell(
		"map_width=" << map_width <<
		", map_height=" << map_height <<
		"\n"
	);
Diag_on;
					
	Verify(map_width > 0);
	Verify(map_width <= bitmap->Data.Size.x);
	Verify(map_height > 0);
	Verify(map_height <= bitmap->Data.Size.y);

	Verify(x >= 0);
	Verify(x < width);
	Verify(y >= 0);
	Verify(y < height);

	//---------------------------------------------------------
	// We really need inverse bitmap here
	//---------------------------------------------------------
	bitmask = ~bitmask;
	//---------------------------------------------------------
	// Prepare to draw bitmap
	//---------------------------------------------------------
	Verify(pixelBuffer.Data.MapPointer != NULL);

	source_pointer_begin =
		bitmap->Data.MapPointer +
		(sLeft >> 4) +
		(sBottom * bitmap->Data.WidthInWords);

	first_bit_test = 1 << (15-(sLeft & 15));

	buildDestPointer(
		x, y,
		&dest_pointer_begin,
		&changed_pointer_begin,
		&changed_bit_begin
	);

	//---------------------------------------------------------
	// The bitmap is always read left-to-right, bottom-to-top.
	// We write it to the screen in different directions
	// based on the rotation.
	//---------------------------------------------------------
	switch (rotation)
	{
	default:
		//--------------------------------------------
		// opaque bitmap, zero degrees
		//   .-----.
		//   |     |
		//   |     |
		//   Y     |
		//   #X----.
		//--------------------------------------------
#		if defined(DEBUG)
			Tell(
				"zero: xp=" << (x+map_width) <<
				", yp=" << (y-map_height+1) <<
				"\n"
			);
#		endif
		Verify(x+map_width >= 0);
		Verify(x+map_width <= width);				// HACK? <, or <=?
		Verify(y-map_height+1 >= 0);
		Verify(y-map_height+1 < height);

		MarkChangedLines(y-map_height+1, y);

		for(y=map_height; y>0; --y)
		{
			source_pointer = source_pointer_begin;
			changed_pointer = changed_pointer_begin;
			changed_bit = changed_bit_begin;
			dest_pointer = dest_pointer_begin;

			UP_BITMAP(source_pointer_begin, bitmap);
			UP_CHANGED(changed_pointer_begin, changed_bit_begin);
			UP_DEST(dest_pointer_begin);

			bit_test = first_bit_test;
			bits = *source_pointer++;

			for(x=map_width; x>0; --x,bit_test>>=1)
			{
				if (bit_test == 0)
				{
					bit_test = 0x8000;
					bits = *source_pointer++;
				}

				if (bits & bit_test)
				{
					color = (Word) foreground; 
				}
				else
				{
					color = (Word) background; 
				}
				{
					switch (operation)
					{
					case GraphicsDisplay::Replace:
						*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
						break;

					case GraphicsDisplay::And:
						*dest_pointer &= (Word) color;
						break;

					case GraphicsDisplay::Or:
						*dest_pointer |= (Word) color;
						break;

					case GraphicsDisplay::Xor:
						*dest_pointer ^= (Word) color;
						break;
					}
					SET_CHANGED(changed_pointer, changed_bit);
				}
				RIGHT_CHANGED(changed_pointer, changed_bit);
				RIGHT_DEST(dest_pointer);
			}
		}
		break;

	case 90:
		//--------------------------------------------
		// opaque bitmap, 90 degrees
		//   #--Y--.
		//   |     |
		//   X     |
		//   |     |
		//   .-----.
		//--------------------------------------------
#		if defined(DEBUG)
			Tell("90 xp=" << (x+map_height) << ", yp=" << (y+map_width) << "\n");
#		endif
		Verify(x+map_height >= 0);
		Verify(x+map_height < width);
		Verify(y+map_width >= 0);
		Verify(y+map_width < height);

		MarkChangedLines(y, y+map_width);

		for(y=map_height; y>0; --y)
		{
			source_pointer = source_pointer_begin;
			changed_pointer = changed_pointer_begin;
			changed_bit = changed_bit_begin;
			dest_pointer = dest_pointer_begin;

			UP_BITMAP(source_pointer_begin, bitmap);
			RIGHT_CHANGED(changed_pointer_begin, changed_bit_begin);
			RIGHT_DEST(dest_pointer_begin);
															 
			bit_test = first_bit_test;
			bits = *source_pointer++;

			for(x=map_width; x>0; --x,bit_test>>=1)
			{
				if (bit_test == 0)
				{
					bit_test = 0x8000;
					bits = *source_pointer++;
				}

				if (bits & bit_test)
				{
					color = (Word) foreground; 
				}
				else
				{
					color = (Word) background; 
				}
				{
					switch (operation)
					{
					case GraphicsDisplay::Replace:
						*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
						break;

					case GraphicsDisplay::And:
						*dest_pointer &= (Word) color;
						break;

					case GraphicsDisplay::Or:
						*dest_pointer |= (Word) color;
						break;

					case GraphicsDisplay::Xor:
						*dest_pointer ^= (Word) color;
						break;
					}
					SET_CHANGED(changed_pointer, changed_bit);
				}
				DOWN_CHANGED(changed_pointer, changed_bit);
				DOWN_DEST(dest_pointer);
			}
		}
		break;

	case 180:
		//--------------------------------------------
		// opaque bitmap, 180 degrees
		//   .----X#
		//   |     Y
		//   |     |
		//   |     |
		//   .-----.
		//--------------------------------------------
#		if defined(DEBUG)
			Tell("xp=" << (x-map_width+1) << ", yp=" << (y+map_height) << "\n");
#		endif
		Verify(x-map_width+1 >= 0);
		Verify(x-map_width+1 < width);
		Verify(y+map_height >= 0);
		Verify(y+map_height < height);

		MarkChangedLines(y, y+map_height);

		for(y=map_height; y>0; --y)
		{
			source_pointer = source_pointer_begin;
			changed_pointer = changed_pointer_begin;
			changed_bit = changed_bit_begin;
			dest_pointer = dest_pointer_begin;

			UP_BITMAP(source_pointer_begin, bitmap);
			DOWN_CHANGED(changed_pointer_begin, changed_bit_begin);
			DOWN_DEST(dest_pointer_begin);

			bit_test = first_bit_test;
			bits = *source_pointer++;

			for(x=map_width; x>0; --x,bit_test>>=1)
			{
				if (bit_test == 0)
				{
					bit_test = 0x8000;
					bits = *source_pointer++;
				}

				if (bits & bit_test)
				{
					color = (Word) foreground; 
				}
				else
				{
					color = (Word) background; 
				}
				{
					switch (operation)
					{
					case GraphicsDisplay::Replace:
						*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
						break;

					case GraphicsDisplay::And:
						*dest_pointer &= (Word) color;
						break;

					case GraphicsDisplay::Or:
						*dest_pointer |= (Word) color;
						break;

					case GraphicsDisplay::Xor:
						*dest_pointer ^= (Word) color;
						break;
					}
					SET_CHANGED(changed_pointer, changed_bit);
				}
				LEFT_CHANGED(changed_pointer, changed_bit);
				LEFT_DEST(dest_pointer);
			}
		}
		break;

	case 270:
		//--------------------------------------------
		// opaque bitmap, 270 degrees
		//   .-----.
		//   |     |
		//   |     X
		//   |     |
		//   .--Y--#
		//--------------------------------------------
#		if defined(DEBUG)
			Tell(
				"xp=" << (x-map_height+1) <<
				", yp=" << (y-(map_width-1)) <<
				"\n"
			);
#		endif

		Verify(x-map_height >= 0);
		Verify(x-map_height < width);
		Verify(y-(map_width-1) >= 0);
		Verify(y-(map_width-1) < height);

		Diag_Tell(
			"(y-(map_width-1))=" << (y-(map_width-1)) <<
			", y=" << y <<
			","
		);

		MarkChangedLines(y-(map_width-1), y);

		for(y=map_height; y>0; --y)
		{
			source_pointer = source_pointer_begin;
			changed_pointer = changed_pointer_begin;
			changed_bit = changed_bit_begin;
			dest_pointer = dest_pointer_begin;

			UP_BITMAP(source_pointer_begin, bitmap);
			LEFT_CHANGED(changed_pointer_begin, changed_bit_begin);
			LEFT_DEST(dest_pointer_begin);

			bit_test = first_bit_test;
			bits = *source_pointer++;

			for(x=map_width; x>0; --x,bit_test>>=1)
			{
				Verify(dest_pointer >= pixelBuffer.Data.MapPointer);
				Verify(dest_pointer < pixelBuffer.Data.MapPointer+
					(width*height));

				Verify(changed_pointer >= changedBit);
				Verify(changed_pointer < changedBit+(changedBitWidth*height));

				if (bit_test == 0)
				{
					bit_test = 0x8000;
					bits = *source_pointer++;
				}

				if (bits & bit_test)
				{
					color = (Word) foreground; 
				}
				else
				{
					color = (Word) background; 
				}
				{
					switch (operation)
					{
					case GraphicsDisplay::Replace:
						*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
						break;

					case GraphicsDisplay::And:
						*dest_pointer &= (Word) color;
						break;

					case GraphicsDisplay::Or:
						*dest_pointer |= (Word) color;
						break;

					case GraphicsDisplay::Xor:
						*dest_pointer ^= (Word) color;
						break;
					}
					SET_CHANGED(changed_pointer, changed_bit);
				}
				UP_CHANGED(changed_pointer, changed_bit_begin);
				UP_DEST(dest_pointer);
			}
		}
		Diag_Tell(
			"changed_pointer=" << changed_pointer << std::dec <<
			"\n"
		);

		break;
	}
	Check_Fpu();
Diag_off;
}

//===================================================================
// DrawPixelMap8
//===================================================================
void
	Video16BitBuffered::DrawPixelMap8(
		int *translation_table,
		int bitmask,
		Enumeration operation,
		Logical opaque,
		int rotation,
		int x, int y,
		PixelMap8	*pixelmap,
		int sLeft, int sBottom, int sRight, int sTop
	)
{
#	if defined(DEBUG)
		Tell(
			"Video16BitBuffered::DrawPixelMap8(<" <<
			x << ", " <<
			y << ">,<" <<
			sLeft << ", " <<
			sBottom << ", " <<
			sRight << ", " <<
			sTop << ">" <<
			")\n"
		);
#	endif
	Check(this);
	Verify(x >= bounds.bottomLeft.x);
	Verify(x <= bounds.topRight.x);
	Verify(y >= bounds.bottomLeft.y);
	Verify(y <= bounds.topRight.y);

	int
		map_width,
		map_height,
		map_max_y;

	Byte
		source_data,
		*source_pointer_begin,
		*source_pointer;

	Word
		*dest_pointer_begin,
		*dest_pointer;

	LWord
		changed_bit_begin,
		changed_bit,
		*changed_pointer_begin,
		*changed_pointer;

	Word
		color;
	//---------------------------------------------------------
	// If pixelbuffer is invalid, do nothing
	//---------------------------------------------------------
	if (!valid)
	{
		Check_Fpu();
		return;
	}
	//---------------------------------------------------------
	// Ensure that source rectangle is properly specified
	//---------------------------------------------------------
	Verify(pixelmap != NULL);

	Verify(sLeft >= 0);
	Verify(sLeft <= sRight);
	Verify(sRight < pixelmap->Data.Size.x);

	Verify(sBottom >= 0);
	Verify(sBottom <= sTop);
	Verify(sTop < pixelmap->Data.Size.y);
	//---------------------------------------------------------
	// Convert to screen co-ordinates
	//---------------------------------------------------------
	y = maximumY - y;

	map_max_y = pixelmap->Data.Size.y-1; 
	sTop    = map_max_y - sTop;
	sBottom = map_max_y - sBottom;

	map_width = sRight - sLeft + 1;
	map_height = sBottom - sTop + 1;

#	if defined(DEBUG)
		Tell("x=" << x << ", y=" << y << "\n");
		Tell("sTop=" << sTop << ", sBottom=" << sBottom << "\n");
		Tell("map_width=" << map_width << ", map_height=" << map_height << "\n");
#	endif

	Verify(map_width > 0);
	Verify(map_width <= pixelmap->Data.Size.x);
	Verify(map_height > 0);
	Verify(map_height <= pixelmap->Data.Size.y);

	Verify(x >= 0);
	Verify(x < width);
	Verify(y >= 0);
	Verify(y < height);

	//---------------------------------------------------------
	// We really need inverse bitmap here
	//---------------------------------------------------------
	bitmask = ~bitmask;
	//---------------------------------------------------------
	// Prepare to draw bitmap
	//---------------------------------------------------------
	Verify(pixelBuffer.Data.MapPointer != NULL);

	source_pointer_begin =
		pixelmap->Data.MapPointer +
		sLeft +
		(sBottom * pixelmap->Data.Size.x);

	buildDestPointer(
		x, y,
		&dest_pointer_begin,
		&changed_pointer_begin,
		&changed_bit_begin
	);

	if (opaque)
	{
		switch (rotation)
		{
		default:
			//--------------------------------------------
			// Opaque pixelmap, zero degrees
			//   .-----.
			//   |     |
			//   |     |
			//   Y     |
			//   #X----.
			//--------------------------------------------
#			if defined(DEBUG)
				Tell(
					"Opaque zero: xp=" << (x+map_width) <<
					", yp=" << (y-map_height+1) <<
					"\n"
				);
#			endif
			Verify(x+map_width >= 0);
			Verify(x+map_width < width);
			Verify(y-map_height+1 >= 0);
			Verify(y-map_height+1 < height);

			MarkChangedLines(y-map_height+1, y);

			for(y=map_height; y>0; --y)
			{
				source_pointer = source_pointer_begin;
				changed_pointer = changed_pointer_begin;
				changed_bit = changed_bit_begin;
				dest_pointer = dest_pointer_begin;

				UP_SOURCE(source_pointer_begin, pixelmap);
				UP_CHANGED(changed_pointer_begin, changed_bit_begin);
				UP_DEST(dest_pointer_begin);

				for(x=map_width; x>0; --x)
				{
					source_data = *source_pointer++;		//SOURCE_RIGHT
					{
						color = (Word) translation_table[source_data];
						switch(operation)
						{
						case GraphicsDisplay::Replace:
							*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
							break;

						case GraphicsDisplay::And:
							*dest_pointer &= color;
							break;

						case GraphicsDisplay::Or:
							*dest_pointer |= color;
							break;

						case GraphicsDisplay::Xor:
							*dest_pointer ^= color;
							break;
						}
						SET_CHANGED(changed_pointer, changed_bit);
					}
					RIGHT_CHANGED(changed_pointer, changed_bit);
					RIGHT_DEST(dest_pointer);
				}
			}
			break;

		case 90:
			//--------------------------------------------
			// Opaque pixelmap, 90 degrees
			//   #Y----.
			//   X     |
			//   |     |
			//   |     |
			//   .-----.
			//--------------------------------------------
#			if defined(DEBUG)
				Tell(
					"Opaque 90: xp=" << (x+map_height) <<
					", yp=" << (y+map_width) <<
					"\n"
				);
#			endif
			Verify(x+map_height >= 0);
			Verify(x+map_height < width);
			Verify(y+map_width >= 0);
			Verify(y+map_width < height);

			MarkChangedLines(y, y+map_width);

			for(y=map_height; y>0; --y)
			{
				source_pointer = source_pointer_begin;
				changed_pointer = changed_pointer_begin;
				changed_bit = changed_bit_begin;
				dest_pointer = dest_pointer_begin;

				UP_SOURCE(source_pointer_begin, pixelmap);
				RIGHT_CHANGED(changed_pointer_begin, changed_bit_begin);
				RIGHT_DEST(dest_pointer_begin);
															 
				for(x=map_width; x>0; --x)
				{
					source_data = *source_pointer++;		//SOURCE_RIGHT
					{
						color = (Word) translation_table[source_data];
						switch(operation)
						{
						case GraphicsDisplay::Replace:
							*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
							break;

						case GraphicsDisplay::And:
							*dest_pointer &= color;
							break;

						case GraphicsDisplay::Or:
							*dest_pointer |= color;
							break;

						case GraphicsDisplay::Xor:
							*dest_pointer ^= color;
							break;
						}
						SET_CHANGED(changed_pointer, changed_bit);
					}
					DOWN_CHANGED(changed_pointer, changed_bit);
					DOWN_DEST(dest_pointer);
				}
			}
			break;

		case 180:
			//--------------------------------------------
			// Opaque pixelmap, 180 degrees
			//   .----X#
			//   |     Y
			//   |     |
			//   |     |
			//   .-----.
			//--------------------------------------------
#			if defined(DEBUG)
				Tell(
					"Opaque 180: xp=" << (x-map_width+1) <<
					", yp=" << (y+map_height) <<
					"\n"
				);
#			endif
			Verify(x-map_width+1 >= 0);
			Verify(x-map_width+1 < width);
			Verify(y+map_height >= 0);
			Verify(y+map_height < height);

			MarkChangedLines(y, y+map_height);

			for(y=map_height; y>0; --y)
			{
				source_pointer = source_pointer_begin;
				changed_pointer = changed_pointer_begin;
				changed_bit = changed_bit_begin;
				dest_pointer = dest_pointer_begin;

				UP_SOURCE(source_pointer_begin, pixelmap);
				DOWN_CHANGED(changed_pointer_begin, changed_bit_begin);
				DOWN_DEST(dest_pointer_begin);

				for(x=map_width; x>0; --x)
				{
					source_data = *source_pointer++;		//SOURCE_RIGHT
					{
						color = (Word) translation_table[source_data];
						switch(operation)
						{
						case GraphicsDisplay::Replace:
							*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
							break;

						case GraphicsDisplay::And:
							*dest_pointer &= color;
							break;

						case GraphicsDisplay::Or:
							*dest_pointer |= color;
							break;

						case GraphicsDisplay::Xor:
							*dest_pointer ^= color;
							break;
						}
						SET_CHANGED(changed_pointer, changed_bit);
					}
					LEFT_CHANGED(changed_pointer, changed_bit);
					LEFT_DEST(dest_pointer);
				}
			}
			break;

		case 270:
			//--------------------------------------------
			// Opaque pixelmap, 270 degrees
			//   .-----.
			//   |     |
			//   |     X
			//   |     |
			//   .--Y--#
			//--------------------------------------------
#			if defined(DEBUG)
				Tell("Opaque 270: x=" << x << ", y=" << y << "\n");
				Tell(
						"xp="   << (x-map_height) <<
						", yp=" << (y-(map_width-1)) <<
						"\n"
					);
#			endif
			Verify(x-map_height-1 >= 0);
			Verify(x-map_height-1 < width);
			Verify(y-(map_width-1) >= 0);
			Verify(y-(map_width-1) < height);

			MarkChangedLines(y-(map_width-1), y);

			for(y=map_height ; y>0; --y)
			{
				source_pointer = source_pointer_begin;
				changed_pointer = changed_pointer_begin;
				changed_bit = changed_bit_begin;
				dest_pointer = dest_pointer_begin;

				UP_SOURCE(source_pointer_begin, pixelmap);
				LEFT_CHANGED(changed_pointer_begin, changed_bit_begin);
				LEFT_DEST(dest_pointer_begin);

				for(x=map_width; x>0; --x)
				{
					Verify(dest_pointer >= pixelBuffer.Data.MapPointer);
					Verify(dest_pointer < pixelBuffer.Data.MapPointer+
						(width*height));

					Verify(changed_pointer >= changedBit);
					Verify(changed_pointer < changedBit+(changedBitWidth*height));

					source_data = *source_pointer++;		//SOURCE_RIGHT
					{
						color = (Word) translation_table[source_data];
						switch(operation)
						{
						case GraphicsDisplay::Replace:
							*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
							break;

						case GraphicsDisplay::And:
							*dest_pointer &= color;
							break;

						case GraphicsDisplay::Or:
							*dest_pointer |= color;
							break;

						case GraphicsDisplay::Xor:
							*dest_pointer ^= color;
							break;
						}
						SET_CHANGED(changed_pointer, changed_bit);
					}
					UP_CHANGED(changed_pointer, changed_bit_begin);
					UP_DEST(dest_pointer);
				}
			}
			break;
		}
	}
	else
	{
		switch (rotation)
		{
		default:
			//--------------------------------------------
			// Transparent pixelmap, zero degrees
			//   .-----.
			//   |     |
			//   |     |
			//   Y     |
			//   #X----.
			//--------------------------------------------
#			if defined(DEBUG)
				Tell(
					"transparent zero: xp=" << (x+map_width) <<
					", yp=" << (y-map_height+1) <<
					"\n"
				);
#			endif
			Verify(x+map_width >= 0);
			Verify(x+map_width < width);
			Verify(y-map_height+1 >= 0);
			Verify(y-map_height+1 < height);

			MarkChangedLines(y-map_height+1, y);

			for(y=map_height; y>0; --y)
			{
				source_pointer = source_pointer_begin;
				changed_pointer = changed_pointer_begin;
				changed_bit = changed_bit_begin;
				dest_pointer = dest_pointer_begin;

				UP_SOURCE(source_pointer_begin, pixelmap);
				UP_CHANGED(changed_pointer_begin, changed_bit_begin);
				UP_DEST(dest_pointer_begin);

				for(x=map_width; x>0; --x)
				{
					source_data = *source_pointer++;		//SOURCE_RIGHT
					if (source_data)
					{
						color = (Word) translation_table[source_data];
						switch(operation)
						{
						case GraphicsDisplay::Replace:
							*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
							break;

						case GraphicsDisplay::And:
							*dest_pointer &= color;
							break;

						case GraphicsDisplay::Or:
							*dest_pointer |= color;
							break;

						case GraphicsDisplay::Xor:
							*dest_pointer ^= color;
							break;
						}
						SET_CHANGED(changed_pointer, changed_bit);
					}
					RIGHT_CHANGED(changed_pointer, changed_bit);
					RIGHT_DEST(dest_pointer);
				}
			}
			break;

		case 90:
			//--------------------------------------------
			// Transparent pixelmap, 90 degrees
			//   #Y----.
			//   X     |
			//   |     |
			//   |     |
			//   .-----.
			//--------------------------------------------
#			if defined(DEBUG)
				Tell(
					"transparent 90: xp=" << (x+map_height) <<
					", yp=" << (y+map_width) <<
					"\n"
				);
#			endif
			Verify(x+map_height >= 0);
			Verify(x+map_height < width);
			Verify(y+map_width >= 0);
			Verify(y+map_width < height);

			MarkChangedLines(y, y+map_width);

			for(y=map_height; y>0; --y)
			{
				source_pointer = source_pointer_begin;
				changed_pointer = changed_pointer_begin;
				changed_bit = changed_bit_begin;
				dest_pointer = dest_pointer_begin;

				UP_SOURCE(source_pointer_begin, pixelmap);
				RIGHT_CHANGED(changed_pointer_begin, changed_bit_begin);
				RIGHT_DEST(dest_pointer_begin);
															 
				for(x=map_width; x>0; --x)
				{
					source_data = *source_pointer++;		//SOURCE_RIGHT
					if (source_data)
					{
						color = (Word) translation_table[source_data];
						switch(operation)
						{
						case GraphicsDisplay::Replace:
							*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
							break;

						case GraphicsDisplay::And:
							*dest_pointer &= color;
							break;

						case GraphicsDisplay::Or:
							*dest_pointer |= color;
							break;

						case GraphicsDisplay::Xor:
							*dest_pointer ^= color;
							break;
						}
						SET_CHANGED(changed_pointer, changed_bit);
					}
					DOWN_CHANGED(changed_pointer, changed_bit);
					DOWN_DEST(dest_pointer);
				}
			}
			break;

		case 180:
			//--------------------------------------------
			// Transparent pixelmap, 180 degrees
			//   .----X#
			//   |     Y
			//   |     |
			//   |     |
			//   .-----.
			//--------------------------------------------
#			if defined(DEBUG)
				Tell(
					"transparent 180: xp=" << (x-map_width+1) <<
					", yp=" << (y+map_height) <<
					"\n"
				);
#			endif
			Verify(x-map_width+1 >= 0);
			Verify(x-map_width+1 < width);
			Verify(y+map_height >= 0);
			Verify(y+map_height < height);

			MarkChangedLines(y, y+map_height);

			for(y=map_height; y>0; --y)
			{
				source_pointer = source_pointer_begin;
				changed_pointer = changed_pointer_begin;
				changed_bit = changed_bit_begin;
				dest_pointer = dest_pointer_begin;

				UP_SOURCE(source_pointer_begin, pixelmap);
				DOWN_CHANGED(changed_pointer_begin, changed_bit_begin);
				DOWN_DEST(dest_pointer_begin);

				for(x=map_width; x>0; --x)
				{
					source_data = *source_pointer++;		//SOURCE_RIGHT
					if (source_data)
					{
						color = (Word) translation_table[source_data];
						switch(operation)
						{
						case GraphicsDisplay::Replace:
							*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
							break;

						case GraphicsDisplay::And:
							*dest_pointer &= color;
							break;

						case GraphicsDisplay::Or:
							*dest_pointer |= color;
							break;

						case GraphicsDisplay::Xor:
							*dest_pointer ^= color;
							break;
						}
						SET_CHANGED(changed_pointer, changed_bit);
					}
					LEFT_CHANGED(changed_pointer, changed_bit);
					LEFT_DEST(dest_pointer);
				}
			}
			break;

		case 270:
			//--------------------------------------------
			// Transparent pixelmap, 270 degrees
			//   .-----.
			//   |     |
			//   |     X
			//   |     |
			//   .--Y--#
			//--------------------------------------------
#			if defined(DEBUG)
				Tell("transparent 270: x=" << x << ", y=" << y << "\n");
				Tell(
						"xp="   << (x-map_height) <<
						", yp=" << (y-(map_width-1)) <<
						"\n"
					);
#			endif
			Verify(x-(map_height-1) >= 0);
			Verify(x-(map_height-1) < width);
			Verify(y-(map_width-1) >= 0);
			Verify(y-(map_width-1) < height);

			MarkChangedLines(y-(map_width-1), y);

			for(y=map_height ; y>0; --y)
			{
				source_pointer = source_pointer_begin;
				changed_pointer = changed_pointer_begin;
				changed_bit = changed_bit_begin;
				dest_pointer = dest_pointer_begin;

				UP_SOURCE(source_pointer_begin, pixelmap);
				LEFT_CHANGED(changed_pointer_begin, changed_bit_begin);
				LEFT_DEST(dest_pointer_begin);

				for(x=map_width; x>0; --x)
				{
					Verify(dest_pointer >= pixelBuffer.Data.MapPointer);
					Verify(dest_pointer < pixelBuffer.Data.MapPointer+
						(width*height));

					Verify(changed_pointer >= changedBit);
					Verify(changed_pointer < changedBit+(changedBitWidth*height));

					source_data = *source_pointer++;		//SOURCE_RIGHT
					if (source_data)
					{
						color = (Word) translation_table[source_data];
						switch(operation)
						{
						case GraphicsDisplay::Replace:
							*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
							break;

						case GraphicsDisplay::And:
							*dest_pointer &= color;
							break;

						case GraphicsDisplay::Or:
							*dest_pointer |= color;
							break;

						case GraphicsDisplay::Xor:
							*dest_pointer ^= color;
							break;
						}
						SET_CHANGED(changed_pointer, changed_bit);
					}
					UP_CHANGED(changed_pointer, changed_bit_begin);
					UP_DEST(dest_pointer);
				}
			}
			break;
		}
	}
	Check_Fpu();
}

//===================================================================
// DrawPixelMap8SingleColor
//===================================================================
void
	Video16BitBuffered::DrawPixelMap8SingleColor(
		int color,
		int bitmask,
		Enumeration operation,
		int rotation,
		int x, int y,
		PixelMap8	*pixelmap,
		int sLeft, int sBottom, int sRight, int sTop
	)
{
#	if defined(DEBUG)
		Tell(
			"Video16BitBuffered::DrawPixelMap8SingleColor(<" <<
			x << ", " <<
			y << ">,<" <<
			sLeft << ", " <<
			sBottom << ", " <<
			sRight << ", " <<
			sTop << ">" <<
			")\n"
		);
#	endif
	Check(this);
	Verify(x >= bounds.bottomLeft.x);
	Verify(x <= bounds.topRight.x);
	Verify(y >= bounds.bottomLeft.y);
	Verify(y <= bounds.topRight.y);

	int
		map_width,
		map_height,
		map_max_y;

	Byte
		source_data,
		*source_pointer_begin,
		*source_pointer;

	Word
		*dest_pointer_begin,
		*dest_pointer;

	LWord
		changed_bit_begin,
		changed_bit,
		*changed_pointer_begin,
		*changed_pointer;

	//---------------------------------------------------------
	// If pixelbuffer is invalid, do nothing
	//---------------------------------------------------------
	if (!valid)
	{
		Check_Fpu();
		return;
	}
	//---------------------------------------------------------
	// Ensure that source rectangle is properly specified
	//---------------------------------------------------------
	Verify(pixelmap != NULL);

	Verify(sLeft >= 0);
	Verify(sLeft <= sRight);
	Verify(sRight < pixelmap->Data.Size.x);

	Verify(sBottom >= 0);
	Verify(sBottom <= sTop);
	Verify(sTop < pixelmap->Data.Size.y);
	//---------------------------------------------------------
	// Convert to screen co-ordinates
	//---------------------------------------------------------
	y = maximumY - y;

	map_max_y = pixelmap->Data.Size.y-1; 
	sTop    = map_max_y - sTop;
	sBottom = map_max_y - sBottom;

	map_width = sRight - sLeft + 1;
	map_height = sBottom - sTop + 1;

#	if defined(DEBUG)
		Tell("x=" << x << ", y=" << y << "\n");
		Tell("sTop=" << sTop << ", sBottom=" << sBottom << "\n");
		Tell("map_width=" << map_width << ", map_height=" << map_height << "\n");
#	endif

	Verify(map_width > 0);
	Verify(map_width <= pixelmap->Data.Size.x);
	Verify(map_height > 0);
	Verify(map_height <= pixelmap->Data.Size.y);


	Verify(x >= 0);
	Verify(x < width);
	Verify(y >= 0);
	Verify(y < height);

	//---------------------------------------------------------
	// We really need inverse bitmap here
	//---------------------------------------------------------
	bitmask = ~bitmask;
	//---------------------------------------------------------
	// Prepare to draw bitmap
	//---------------------------------------------------------
	Verify(pixelBuffer.Data.MapPointer != NULL);

	source_pointer_begin =
		pixelmap->Data.MapPointer +
		sLeft +
		(sBottom * pixelmap->Data.Size.x);

	buildDestPointer(
		x, y,
		&dest_pointer_begin,
		&changed_pointer_begin,
		&changed_bit_begin
	);

	{
		switch (rotation)
		{
		default:
			//--------------------------------------------
			// Single-color pixelmap, zero degrees
			//   .-----.
			//   |     |
			//   |     |
			//   Y     |
			//   #X----.
			//--------------------------------------------
#			if defined(DEBUG)
				Tell(
					"Single-color zero: xp=" << (x+map_width) <<
					", yp=" << (y-map_height+1) <<
					"\n"
				);
#			endif
			Verify(x+map_width >= 0);
			Verify(x+map_width < width);
			Verify(y-map_height+1 >= 0);
			Verify(y-map_height+1 < height);

			MarkChangedLines(y-map_height+1, y);

			for(y=map_height; y>0; --y)
			{
				source_pointer = source_pointer_begin;
				changed_pointer = changed_pointer_begin;
				changed_bit = changed_bit_begin;
				dest_pointer = dest_pointer_begin;

				UP_SOURCE(source_pointer_begin, pixelmap);
				UP_CHANGED(changed_pointer_begin, changed_bit_begin);
				UP_DEST(dest_pointer_begin);

				for(x=map_width; x>0; --x)
				{
					source_data = *source_pointer++;		//SOURCE_RIGHT
					if (source_data)
					{
						switch(operation)
						{
						case GraphicsDisplay::Replace:
							*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
							break;

						case GraphicsDisplay::And:
							*dest_pointer &= (Word) color;
							break;

						case GraphicsDisplay::Or:
							*dest_pointer |= (Word) color;
							break;

						case GraphicsDisplay::Xor:
							*dest_pointer ^= (Word) color;
							break;
						}
						SET_CHANGED(changed_pointer, changed_bit);
					}
					RIGHT_CHANGED(changed_pointer, changed_bit);
					RIGHT_DEST(dest_pointer);
				}
			}
			break;

		case 90:
			//--------------------------------------------
			// Single-color pixelmap, 90 degrees
			//   #Y----.
			//   X     |
			//   |     |
			//   |     |
			//   .-----.
			//--------------------------------------------
#			if defined(DEBUG)
				Tell(
					"Single-color 90: xp=" << (x+map_height) <<
					", yp=" << (y+map_width) <<
					"\n"
				);
#			endif
			Verify(x+map_height >= 0);
			Verify(x+map_height < width);
			Verify(y+map_width >= 0);
			Verify(y+map_width < height);

			MarkChangedLines(y, y+map_width);

			for(y=map_height; y>0; --y)
			{
				source_pointer = source_pointer_begin;
				changed_pointer = changed_pointer_begin;
				changed_bit = changed_bit_begin;
				dest_pointer = dest_pointer_begin;

				UP_SOURCE(source_pointer_begin, pixelmap);
				RIGHT_CHANGED(changed_pointer_begin, changed_bit_begin);
				RIGHT_DEST(dest_pointer_begin);
															 
				for(x=map_width; x>0; --x)
				{
					source_data = *source_pointer++;		//SOURCE_RIGHT
					if (source_data)
					{
						switch(operation)
						{
						case GraphicsDisplay::Replace:
							*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
							break;

						case GraphicsDisplay::And:
							*dest_pointer &= (Word) color;
							break;

						case GraphicsDisplay::Or:
							*dest_pointer |= (Word) color;
							break;

						case GraphicsDisplay::Xor:
							*dest_pointer ^= (Word) color;
							break;
						}
						SET_CHANGED(changed_pointer, changed_bit);
					}
					DOWN_CHANGED(changed_pointer, changed_bit);
					DOWN_DEST(dest_pointer);
				}
			}
			break;

		case 180:
			//--------------------------------------------
			// Single-color pixelmap, 180 degrees
			//   .----X#
			//   |     Y
			//   |     |
			//   |     |
			//   .-----.
			//--------------------------------------------
#			if defined(DEBUG)
				Tell(
					"Single-color 180: xp=" << (x-map_width+1) <<
					", yp=" << (y+map_height) <<
					"\n"
				);
#			endif
			Verify(x-map_width+1 >= 0);
			Verify(x-map_width+1 < width);
			Verify(y+map_height >= 0);
			Verify(y+map_height < height);

			MarkChangedLines(y, y+map_height);

			for(y=map_height; y>0; --y)
			{
				source_pointer = source_pointer_begin;
				changed_pointer = changed_pointer_begin;
				changed_bit = changed_bit_begin;
				dest_pointer = dest_pointer_begin;

				UP_SOURCE(source_pointer_begin, pixelmap);
				DOWN_CHANGED(changed_pointer_begin, changed_bit_begin);
				DOWN_DEST(dest_pointer_begin);

				for(x=map_width; x>0; --x)
				{
					source_data = *source_pointer++;		//SOURCE_RIGHT
					if (source_data)
					{
						switch(operation)
						{
						case GraphicsDisplay::Replace:
							*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
							break;

						case GraphicsDisplay::And:
							*dest_pointer &= (Word) color;
							break;

						case GraphicsDisplay::Or:
							*dest_pointer |= (Word) color;
							break;

						case GraphicsDisplay::Xor:
							*dest_pointer ^= (Word) color;
							break;
						}
						SET_CHANGED(changed_pointer, changed_bit);
					}
					LEFT_CHANGED(changed_pointer, changed_bit);
					LEFT_DEST(dest_pointer);
				}
			}
			break;

		case 270:
			//--------------------------------------------
			// Single-color pixelmap, 270 degrees
			//   .-----.
			//   |     |
			//   |     X
			//   |     |
			//   .--Y--#
			//--------------------------------------------
#			if defined(DEBUG)
				Tell("Single-color 270: x=" << x << ", y=" << y << "\n");
				Tell(
						"xp="   << (x-map_height) <<
						", yp=" << (y-map_width) <<
						"\n"
					);
#			endif
			Verify(x-map_height >= 0);
			Verify(x-map_height < width);
			Verify(y-map_width >= 0);
			Verify(y-map_width < height);

			MarkChangedLines(y-map_width, y);

			for(y=map_height ; y>0; --y)
			{
				source_pointer = source_pointer_begin;
				changed_pointer = changed_pointer_begin;
				changed_bit = changed_bit_begin;
				dest_pointer = dest_pointer_begin;

				UP_SOURCE(source_pointer_begin, pixelmap);
				LEFT_CHANGED(changed_pointer_begin, changed_bit_begin);
				LEFT_DEST(dest_pointer_begin);

				for(x=map_width; x>0; --x)
				{
					Verify(dest_pointer >= pixelBuffer.Data.MapPointer);
					Verify(dest_pointer < pixelBuffer.Data.MapPointer+
						(width*height));

					Verify(changed_pointer >= changedBit);
					Verify(changed_pointer < changedBit+(changedBitWidth*height));

					source_data = *source_pointer++;		//SOURCE_RIGHT
					if (source_data)
					{
						switch(operation)
						{
						case GraphicsDisplay::Replace:
							*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
							break;

						case GraphicsDisplay::And:
							*dest_pointer &= (Word) color;
							break;

						case GraphicsDisplay::Or:
							*dest_pointer |= (Word) color;
							break;

						case GraphicsDisplay::Xor:
							*dest_pointer ^= (Word) color;
							break;
						}
						SET_CHANGED(changed_pointer, changed_bit);
					}
					UP_CHANGED(changed_pointer, changed_bit_begin);
					UP_DEST(dest_pointer);
				}
			}
			break;
		}
	}
	Check_Fpu();
}

//########################################################################
//############################ SVGA640x480x16 ############################
//########################################################################

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Single-window cockpit: the viewscreen child erases true black
// (SS_BLACKRECT paints the system window-frame gray on modern Windows).
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static WNDPROC gViewscreenBaseProc = NULL;

static LRESULT CALLBACK
	ViewscreenBlackProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	if (message == WM_ERASEBKGND)
	{
		RECT client;
		GetClientRect(hwnd, &client);
		FillRect((HDC) wParam, &client, (HBRUSH) GetStockObject(BLACK_BRUSH));
		return 1;
	}
	return CallWindowProcA(gViewscreenBaseProc, hwnd, message, wParam, lParam);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// The cockpit shell: re-fit the canvas whenever the window changes size
// (maximise, restore, drag). The game window's own proc handles the
// rest, so chain to it.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static WNDPROC gCockpitBaseProc = NULL;

static LRESULT CALLBACK
	CockpitShellProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	if (message == WM_SIZE && wParam != SIZE_MINIMIZED)
	{
		SVGA16 *cockpit = SVGA16::GetCockpit();
		if (cockpit != NULL)
		{
			cockpit->LayoutCockpit(LOWORD(lParam), HIWORD(lParam));
		}
	}
	return CallWindowProcA(gCockpitBaseProc, hwnd, message, wParam, lParam);
}

SVGA16 *SVGA16::activeCockpit = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Fit the 1920x1080 cockpit canvas into the given client area: one
// uniform scale (so the canvas never stretches - a wider desktop just
// letterboxes), centred, with every pane re-scaled and re-placed.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
	SVGA16::LayoutCockpit(int client_w, int client_h)
{
	Check_Pointer(this);

	if (!splitViews || client_w <= 0 || client_h <= 0)
	{
		return;
	}

	const int canvas_w = 1920;
	const int canvas_h = 1080;

	int fit_w = (client_w * 100) / canvas_w;
	int fit_h = (client_h * 100) / canvas_h;
	int scale = (fit_w < fit_h) ? fit_w : fit_h;
	if (scale < 25)	scale = 25;

	int view_w = (canvas_w * scale) / 100;
	int view_h = (canvas_h * scale) / 100;

	// centre the canvas: the leftover is black on the long axis
	int origin_x = (client_w - view_w) / 2;
	int origin_y = (client_h - view_h) / 2;

	//---------------------------------------------------------------
	// The viewscreen fills the canvas; the 3D presents into it and
	// D3D stretches the back buffer to fit, so -res only decides how
	// sharp the scene is, not how big the cockpit is.
	//---------------------------------------------------------------
	if (cockpitViewscreen != NULL)
	{
		SetWindowPos(cockpitViewscreen, NULL,
			origin_x, origin_y, view_w, view_h,
			SWP_NOZORDER | SWP_NOACTIVATE);
	}

	int top_glass_w = (320 * scale) / 100;
	int top_glass_h = (240 * scale) / 100;
	// the radar reads best big: 1.35x the compact glass
	int map_glass_w = (324 * scale) / 100;
	int map_glass_h = (432 * scale) / 100;

	for (int view = 0; view < SplitViewCount; ++view)
	{
		if (splitView[view] == NULL)
		{
			continue;
		}
		if (view == SplitMap)
		{
			splitView[view]->Resize(map_glass_w, map_glass_h);
		}
		else
		{
			splitView[view]->Resize(top_glass_w, top_glass_h);
		}
	}

	//---------------------------------------------------------------
	// Top row edge to edge across the canvas, bottom cluster on the
	// bottom edge - all relative to the centred canvas origin.
	//---------------------------------------------------------------
	if (splitView[SplitMFDUpperLeft] != NULL)
	{
		splitView[SplitMFDUpperLeft]->SetPosition(origin_x, origin_y);
	}
	if (splitView[SplitMFDUpperCenter] != NULL)
	{
		splitView[SplitMFDUpperCenter]->SetPosition(
			origin_x + (view_w - splitView[SplitMFDUpperCenter]->ClientWidth()) / 2,
			origin_y);
	}
	if (splitView[SplitMFDUpperRight] != NULL)
	{
		splitView[SplitMFDUpperRight]->SetPosition(
			origin_x + view_w - splitView[SplitMFDUpperRight]->ClientWidth(),
			origin_y);
	}
	if (splitView[SplitMFDLowerLeft] != NULL)
	{
		splitView[SplitMFDLowerLeft]->SetPosition(
			origin_x,
			origin_y + view_h - splitView[SplitMFDLowerLeft]->ClientHeight());
	}
	if (splitView[SplitMFDLowerRight] != NULL)
	{
		splitView[SplitMFDLowerRight]->SetPosition(
			origin_x + view_w - splitView[SplitMFDLowerRight]->ClientWidth(),
			origin_y + view_h - splitView[SplitMFDLowerRight]->ClientHeight());
	}
	if (splitView[SplitMap] != NULL)
	{
		splitView[SplitMap]->SetPosition(
			origin_x + (view_w - splitView[SplitMap]->ClientWidth()) / 2,
			origin_y + view_h - splitView[SplitMap]->ClientHeight());
	}

	// The panes go OVER the main display (pod bezel occlusion): pin
	// the viewscreen to the bottom of the sibling z-order so the 3D
	// present clips around every pane.
	if (cockpitViewscreen != NULL)
	{
		SetWindowPos(cockpitViewscreen, HWND_BOTTOM, 0, 0, 0, 0,
			SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
	}

	DEBUG_STREAM << "SVGA16: cockpit fitted " << view_w << "x" << view_h
		<< " at " << scale << "% in a " << client_w << "x" << client_h
		<< " client\n" << std::flush;
}

SVGA16::SVGA16(
	int mode,
	int init_width,
	int init_height,
	int mem_page_size,
	int mem_granularity,
	int bytes_per_line,
	int page_function_pointer,
	int special_interface,
   bool windowed,
   int *secondaryIndex,
   int *aux1Index,
   int *aux2Index
):Video16BitBuffered(init_width, init_height)
{
	//------------------------------------------------------------------
	// Split-view mode: decide before BuildWindows so the packed gauge
	// windows can stay hidden.
	//------------------------------------------------------------------
	splitViews = False;
	cockpitViewscreen = NULL;
	Logical explodedViews = False;
	{
		const char *split_string = getenv("L4MFDSPLIT");
		if (split_string != NULL && atoi(split_string) != 0)
		{
			splitViews = True;
			// L4MFDSPLIT=2+ : exploded diagnostic view - each display
			// in its own native-resolution desktop window instead of
			// the composited glass cockpit.
			if (atoi(split_string) >= 2)
			{
				explodedViews = True;
			}
		}
	}
	for (int view = 0; view < SplitViewCount; ++view)
	{
		splitView[view] = NULL;
	}

   BuildWindows(init_width,init_height,windowed, secondaryIndex, aux1Index, aux2Index);

	if (splitViews && explodedViews)
	{
		//---------------------------------------------------------------
		// Exploded diagnostic view: every display in its own full-size
		// desktop window (MFDs native 640x480, map 480x640 rotated),
		// decoded exactly as the pod's VDB split them from the single
		// gauge canvas - no cockpit compositing, no downscale. Each MFD
		// can be read and screenshotted at full resolution, matching the
		// emulator's per-channel reference windows. Laid out in the pod
		// arrangement (top row / bottom row) across the work area; the
		// windows are draggable (dragging a title bar briefly pauses the
		// game's single-per-frame message pump, as expected).
		//---------------------------------------------------------------
		RECT work;
		work.left = 0; work.top = 0; work.right = 1920; work.bottom = 1080;
		SystemParametersInfoA(SPI_GETWORKAREA, 0, &work, 0);
		int work_w = work.right - work.left;
		int work_h = work.bottom - work.top;

		int mid_x    = (work_w - init_width) / 2;   if (mid_x < 0)    mid_x = 0;
		int right_x  =  work_w - init_width;         if (right_x < 0)  right_x = 0;
		int bottom_y =  work_h - init_height;        if (bottom_y < 0) bottom_y = 0;

		splitView[SplitMFDUpperLeft] = new MFDSplitView(
			"MFD upper left", init_width, init_height,
			init_width, init_height, work.left, work.top,
			MFDSplitView::NoButtons, 0, 0, 0);
		splitView[SplitMFDUpperCenter] = new MFDSplitView(
			"MFD upper center", init_width, init_height,
			init_width, init_height, work.left + mid_x, work.top,
			MFDSplitView::NoButtons, 0, 0, 0);
		splitView[SplitMFDUpperRight] = new MFDSplitView(
			"MFD upper right", init_width, init_height,
			init_width, init_height, work.left + right_x, work.top,
			MFDSplitView::NoButtons, 0, 0, 0);
		splitView[SplitMFDLowerLeft] = new MFDSplitView(
			"MFD lower left", init_width, init_height,
			init_width, init_height, work.left, work.top + bottom_y,
			MFDSplitView::NoButtons, 0, 0, 0);
		splitView[SplitMFDLowerRight] = new MFDSplitView(
			"MFD lower right", init_width, init_height,
			init_width, init_height, work.left + right_x, work.top + bottom_y,
			MFDSplitView::NoButtons, 0, 0, 0);
		// map is portrait-mounted: source rotated to 480x640
		splitView[SplitMap] = new MFDSplitView(
			"Map", init_height, init_width,
			init_height, init_width, work.left + mid_x, work.top + bottom_y,
			MFDSplitView::NoButtons, 0, 0, 0);
	}
	else if (splitViews)
	{
		//---------------------------------------------------------------
		// The 1920x1080 internal cockpit canvas.
		//
		//   [ MFD UL 640 ][ MFD UC 640 ][ MFD UR 640 ]   <- native glass,
		//                                                   exactly 1920
		//              [ viewscreen 800x600, centered ]
		//   [ MFD LL ]      [ Map ]         [ MFD LR ]   <- bottom edge
		//
		// The panes layer OVER the viewscreen like the pod's bezels -
		// the MFDs partially occlude the main display by design, just
		// as in the original cockpit. The whole canvas scales down
		// uniformly on monitors smaller than 1920x1080.
		//---------------------------------------------------------------
		const int canvas_w = 1920;
		const int canvas_h = 1080;

		HWND cockpit = ApplicationManager::GetCurrentManager()->GetHWnd();

		int chrome_w = 16, chrome_h = 40;
		if (cockpit != NULL)
		{
			RECT outer, inner;
			GetWindowRect(cockpit, &outer);
			GetClientRect(cockpit, &inner);
			chrome_w = (outer.right - outer.left) - inner.right;
			chrome_h = (outer.bottom - outer.top) - inner.bottom;
		}

		RECT work;
		work.left = 0; work.top = 0; work.right = 1920; work.bottom = 1080;
		SystemParametersInfoA(SPI_GETWORKAREA, 0, &work, 0);
		int avail_w = (work.right - work.left) - chrome_w;
		int avail_h = (work.bottom - work.top) - chrome_h;

		//-----------------------------------------------------------
		// One uniform scale, up or down: the canvas keeps its 16:9
		// shape whatever the monitor is, so a wider-than-16:9 desktop
		// letterboxes instead of stretching. Scaling UP is free
		// quality on the MFDs - their glass is a downscale of a
		// native 640x480 channel until about 200%.
		//-----------------------------------------------------------
		int fit_w = (avail_w * 100) / canvas_w;
		int fit_h = (avail_h * 100) / canvas_h;
		int scale_num = (fit_w < fit_h) ? fit_w : fit_h;
		if (scale_num < 25)	scale_num = 25;

		DEBUG_STREAM << "SVGA16: cockpit canvas at " << scale_num
			<< "% for the " << (work.right - work.left) << "x"
			<< (work.bottom - work.top) << " work area\n" << std::flush;

#define COCKPIT_CANVAS(v)	(((v) * scale_num) / 100)

		int client_w = COCKPIT_CANVAS(canvas_w);
		int client_h = COCKPIT_CANVAS(canvas_h);

		//---------------------------------------------------------------
		// Grow the main window into the cockpit shell
		//---------------------------------------------------------------
		if (cockpit != NULL)
		{
			SetWindowLongPtrA(cockpit, GWL_STYLE,
				GetWindowLongPtrA(cockpit, GWL_STYLE) | WS_CLIPCHILDREN);

			SetWindowPos(cockpit, NULL, work.left, work.top,
				client_w + chrome_w, client_h + chrome_h,
				SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
		}

		//---------------------------------------------------------------
		// The viewscreen: created FIRST so every pane sits above it in
		// the z-order, and clipped by those siblings so the 3D present
		// never draws over them. STATIC hit-tests transparent, so mouse
		// input over the 3D view reaches the game window as before.
		// Fills the whole canvas - launch with -res 1920 1080 for
		// native 1:1 pixels.
		//---------------------------------------------------------------
		int view_w = client_w;
		int view_h = client_h;

		if (cockpit != NULL)
		{
			// plain STATIC (SS_BLACKRECT actually paints the system
			// window-frame GRAY on modern Windows), subclassed below to
			// erase true black
			cockpitViewscreen = CreateWindowExA(
				0, "STATIC", "",
				WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS,
				0, 0,
				view_w, view_h,
				cockpit, NULL, GetModuleHandleA(NULL), NULL);
			if (cockpitViewscreen != NULL)
			{
				gViewscreenBaseProc = (WNDPROC) SetWindowLongPtrA(
					cockpitViewscreen, GWLP_WNDPROC,
					(LONG_PTR) ViewscreenBlackProc);
				gMainPresentWindow = cockpitViewscreen;
			}
		}

		//---------------------------------------------------------------
		// Button banks per display (addresses per vRIO CockpitLayout,
		// bank-to-display placement per the pod cockpit):
		//   upper left MFD 0x2F.. / upper center 0x27.. / upper right
		//   0x37.. / lower left 0x0F.. / lower right 0x07.. and the map
		//   flanked by Secondary 0x10-0x15 / Screen 0x18-0x1D.
		//---------------------------------------------------------------
		// all five MFDs at the compact size, corners + top center
		int top_glass_w = COCKPIT_CANVAS(320);
		int top_glass_h = COCKPIT_CANVAS(240);
		int bot_glass_w = COCKPIT_CANVAS(320);
		int bot_glass_h = COCKPIT_CANVAS(240);
		// the radar reads best big: 1.35x the compact glass
		// (1.5x proved a touch too large in playtest)
		int map_glass_w = COCKPIT_CANVAS(324);
		int map_glass_h = COCKPIT_CANVAS(432);

		splitView[SplitMFDUpperLeft] = new MFDSplitView(
			"MFD upper left", init_width, init_height,
			top_glass_w, top_glass_h, 0, 0,
			MFDSplitView::MFDStrips, 0x2F, 0, cockpit);
		splitView[SplitMFDUpperCenter] = new MFDSplitView(
			"MFD upper center", init_width, init_height,
			top_glass_w, top_glass_h, 0, 0,
			MFDSplitView::MFDStrips, 0x27, 0, cockpit);
		splitView[SplitMFDUpperRight] = new MFDSplitView(
			"MFD upper right", init_width, init_height,
			top_glass_w, top_glass_h, 0, 0,
			MFDSplitView::MFDStrips, 0x37, 0, cockpit);
		splitView[SplitMFDLowerLeft] = new MFDSplitView(
			"MFD lower left", init_width, init_height,
			bot_glass_w, bot_glass_h, 0, 0,
			MFDSplitView::MFDStrips, 0x0F, 0, cockpit);
		splitView[SplitMFDLowerRight] = new MFDSplitView(
			"MFD lower right", init_width, init_height,
			bot_glass_w, bot_glass_h, 0, 0,
			MFDSplitView::MFDStrips, 0x07, 0, cockpit);
		// map is portrait: source rotated 90 degrees clockwise
		splitView[SplitMap] = new MFDSplitView(
			"Map", init_height, init_width,
			map_glass_w, map_glass_h, 0, 0,
			MFDSplitView::SideColumns, 0x10, 0x18, cockpit);

		//---------------------------------------------------------------
		// Everything above built the panes at a starting size; the
		// layout itself lives in LayoutCockpit so a resized window can
		// re-run it. Track the instance for the WM_SIZE hook below.
		//---------------------------------------------------------------
		activeCockpit = this;

		if (cockpit != NULL)
		{
			RECT inner;
			GetClientRect(cockpit, &inner);
			LayoutCockpit(inner.right, inner.bottom);

			// catch maximise / restore / drag-resize and re-fit
			gCockpitBaseProc = (WNDPROC) SetWindowLongPtrA(
				cockpit, GWLP_WNDPROC, (LONG_PTR) CockpitShellProc);
		}
		else
		{
			LayoutCockpit(client_w, client_h);
		}

		// the plasma glass sits out for now
		PlasmaScreen::Hide();

#undef COCKPIT_CANVAS
	}
//STUBBED: VIDEO RB 1/15/07
#	if defined(DEBUG)
		Tell("SVGA16::SVGA16()\n");
#	endif

	pageSize     = mem_page_size * 1024;
   Verify(mem_granularity > 0);
	pageDelta    = mem_page_size/mem_granularity;
	widthInBytes = bytes_per_line;
	pageFcnPtr   = page_function_pointer;
	specialInterface = special_interface;		// currently unused

	currentPageNumber = -1;

	//
	// Set video mode
	//
	//SVGASetMode(mode, pageFcnPtr);
	//
	// Set VWE video splitter clock divider
	//
	//SVGASetSplitterClock(True);

	//------------------------------------------------------------------------
	// Initialize palettes
	//------------------------------------------------------------------------
	FadeToWhite(0.0);
	//------------------------------------------------------------------------
	// Initialize palette data
	//------------------------------------------------------------------------
	// The +2 causes Adam's std::decoded Palette addresses to "match up"
	// with the address definitions used by the standard VGA palette port.
	// Adam's port std::decoder design uses:
	//     xx00 = write address port
	//     xx01 = data port
	//     xx10 = pixel mask port
	//     xx11 = read address port
	// The standard VGA/SVGA ports are:
	//     03C6 = ......0110 = xx10 = pixel mask port
	//     03C7 = ......0111 = xx11 = read address port
	//     03C8 = ......1000 = xx00 = write address port
	//     03C9 = ......1001 = xx01 = data port
	// Adam's ports are:
	//     0300 = secondary palette
	//     0308 = auxiliary palette 1
	//     0310 = auxiliary palette 2
	// By adding 2 to "Adam's" port assignments, they match with a VGA:
	//     0302/030A/0312 = xx10 = pixel mask port
	//     0303/030B/0313 = xx11 = read address port
	//     0304/030C/0314 = xx00 = write address port
	//     0305/030D/0315 = xx01 = data port

	// See L4VB16.hpp, class SVGA16 for an enumeration of palettes
	// matching this table.

	static Word port_addr[PaletteCount] = {
		0x3C6,	// NativePalette
		0x300+2,	// SecondaryPalette
		0x308+2,	// AuxiliaryPalette1
		0x310+2	//	AuxiliaryPalette2
	};

	SVGA16Palette
		*palette_pointer = &palette[0];
	for(int i=0; i<PaletteCount; ++i,++palette_pointer)
	{
		int j;

		palette_pointer->hardwarePort = port_addr[i];
		palette_pointer->modified = False;
		palette_pointer->flashAccumulator = 0.0;
		palette_pointer->flashRate = 0.0;
		palette_pointer->previousMaskState = -1;

		for(j=0; j<SVGA16Palette::maskStates; ++j)
		{
			palette_pointer->mask[j] = 0xFF;
		}

		/*if (port_addr[i] != 0x3C6)
		{
			SVGAZeroPalette(port_addr[i]);
		}*/
	}
	//---------------------------------------------------------
	// Prepare update values
	//---------------------------------------------------------
	currentPageNumber = -1;

	ResetUpdatePosition();

#	if defined(BLIT_STATISTICS)
		dirtyPixelCount = 0;
		transferPixelCount = 0;
		overflowPixelCount = 0;
#	endif
	//---------------------------------------------------------
	// Clear the display
	//---------------------------------------------------------
#	if defined(DEBUG)
		Tell("Valid, drawing rectangle-\n" << std::flush);
#	endif
	DrawFilledRectangle(
		0,
		0xFFFF,
		GraphicsDisplay::Replace,
		0, 0,
		maximumX, maximumY
	);
#	if defined(DEBUG)
		Tell("About to update-\n" << std::flush);
#	endif
	Update(False);
	Check_Fpu();

   mDisplayToUpdate = 0;
}

SVGA16::~SVGA16()
{
   for (int i=0; i < NUMGAUGEWINDOWS; i++)
   {
      if (mSurfaces[i * 2] != NULL)
      {
         mSurfaces[i * 2]->Release();
         mSurfaces[i * 2] = NULL;
      }
	  if (mSurfaces[i * 2 + 1] != NULL)
	  {
		  mSurfaces[i * 2 + 1]->Release();
		  mSurfaces[i * 2 + 1] = NULL;
	  }

      if (mDevice[i] != NULL)
      {
         mDevice[i]->Release();
         mSurfaces[i] = NULL;
      }

      DestroyWindow(gaugeWindows[i]);
   }

//STUBBED: VIDEO RB 1/15/07
#	if defined(DEBUG)
		Tell("SVGA16::~SVGA16()\n");
#	endif
	Check(this);
	//---------------------------------------------------------
	// Eventually wait for fade (if any) to complete??
	//---------------------------------------------------------

	//---------------------------------------------------------
	// Set video mode
	//---------------------------------------------------------
	//SVGASetMode(3, 0);
#	if defined(BLIT_STATISTICS)
		double
			ratio;

		if (transferPixelCount == 0)
		{
			ratio = (double) 0;
		}
		else
		{
			ratio = ((double) dirtyPixelCount) / transferPixelCount;
		}
		
		//------------------------------------------------------
		// Print statistics
		//------------------------------------------------------
		DEBUG_STREAM <<
			"SVGA16::~SVGA16: pixel management statistics ------------" <<
			"\nNumber of dirty pixels      =" << dirtyPixelCount << 
			"\nNumber of transferred pixels=" << transferPixelCount <<
			"\nTimes overflowed            =" << overflowPixelCount <<
			"\nRatio =" << ratio <<
			"\n-------------------------------------------------------\n";
#	endif
	//---------------------------------------------------------
	// Set VWE video splitter clock divider
	//---------------------------------------------------------
	//SVGASetSplitterClock(False);

	for (int view = 0; view < SplitViewCount; ++view)
	{
		delete splitView[view];
		splitView[view] = NULL;
	}
	if (cockpitViewscreen != NULL)
	{
		if (gMainPresentWindow == cockpitViewscreen)
		{
			gMainPresentWindow = NULL;
		}
		DestroyWindow(cockpitViewscreen);
		cockpitViewscreen = NULL;
	}

	Check_Fpu();
}

Logical
	SVGA16::TestInstance() const
{
	return Video16BitBuffered::TestInstance(); 
}

void
	SVGA16::ShowInstance(char *indent)
{
	std::cout << indent << "SVGA16:\n";

	char
		temp[80];

	Str_Copy(temp,indent, 80);
	Str_Cat(temp,"...", 80);

	std::cout << temp << "SVGA16:\n";
	Video16BitBuffered::ShowInstance(temp);
	Check_Fpu();
}

int GetShiftAmount(DWORD mask)
{
	int amount = -1;
	while (mask)
	{
		mask >>= 1;
		amount++;
	}
	return amount;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Render one MFD's bit-slice of the shared canvas into its split-view
// window as a green screen. The mask arrives in the duplicated
// (mask | mask<<16) form the packed path uses.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void SVGA16::FillSplitMFD(
	SplitViewID view,
	DWORD mask,
	int shift
)
{
	if (splitView[view] == NULL)
	{
		return;
	}

	Word mask16 = (Word)(mask & 0xFFFF);
	Word max_value = (Word)(mask16 >> shift);
	if (max_value == 0)
	{
		return;
	}

	Word *source = pixelBuffer.Data.MapPointer;
	unsigned long *dest = splitView[view]->Pixels();
	int count = pixelBuffer.Data.Size.x * pixelBuffer.Data.Size.y;

	for (int i = 0; i < count; ++i)
	{
		unsigned long value = (unsigned long)((*source++ & mask16) >> shift);
		unsigned long green = (value * 255ul) / max_value;
		*dest++ = green << 8;
	}

	splitView[view]->Repaint();
}

Logical SVGA16::Update(Logical forceAll)
{
	HRESULT hr;
	
	GaugeRenderer *renderer = application->GetGaugeRenderer();
	if (!valid || renderer == NULL)
	{
		CLEAR_SCREEN_COPY();
		return False;		// Do no more!
	}

	if (++mDisplayToUpdate >= NUMGAUGEWINDOWS)
		mDisplayToUpdate = 0;

	//Top MFD's
	L4GraphicsPort *UL = static_cast<L4GraphicsPort*>(renderer->GetGraphicsPort("auxUL2"));
	L4GraphicsPort *Cent = static_cast<L4GraphicsPort*>(renderer->GetGraphicsPort("auxC"));
	L4GraphicsPort *UR = static_cast<L4GraphicsPort*>(renderer->GetGraphicsPort("auxUR2"));
	//Bottom MFD's
	L4GraphicsPort *LL = static_cast<L4GraphicsPort*>(renderer->GetGraphicsPort("auxLL"));
	L4GraphicsPort *LR = static_cast<L4GraphicsPort*>(renderer->GetGraphicsPort("auxLR"));
	//Secondary
	L4GraphicsPort *secPort = static_cast<L4GraphicsPort*>(renderer->GetGraphicsPort("sec"));

	DWORD ulMask = 0, ucMask = 0, urMask = 0, llMask = 0, lrMask = 0;
	int ulMask_sh = 0, ucMask_sh = 0, urMask_sh = 0, llMask_sh = 0, lrMask_sh = 0;
	int secMask = 0;
	SVGA16Palette *secPalette = NULL;
	
	if (this->mDisplayToUpdate == 1 || this->mDisplayToUpdate == 2)
	{
		//Drawing MFD's... make sure we have MFDs to draw
		if (UL != NULL && Cent != NULL && UR != NULL && LL != NULL && LR != NULL)
		{
			ulMask = UL->GetBitMask();
			ulMask_sh = GetShiftAmount(ulMask);
			ulMask |= (ulMask << 16);
		
			ucMask = Cent->GetBitMask();
			ucMask_sh = GetShiftAmount(ucMask);
			ucMask |= (ucMask << 16);

			urMask = UR->GetBitMask();
			urMask_sh = GetShiftAmount(urMask);
			urMask |= (urMask << 16);

			llMask = LL->GetBitMask();
			llMask_sh = GetShiftAmount(llMask);
			llMask |= (llMask << 16);

			lrMask = LR->GetBitMask();
			lrMask_sh = GetShiftAmount(lrMask);
			lrMask |= (lrMask << 16);
		} else
		{
			//No MFDs to draw, break out early
			return False;
		}
	} else
	{
		//Drawing secondary, make sure we got secondary
		if (secPort != NULL)
		{
			secMask = secPort->GetBitMask();
			secPalette = &((SVGA16 *) secPort->graphicsDisplay)->palette[secPort->paletteID];
		} else
		{
			//No secondary, skip
			return False;
		}
	}

	//------------------------------------------------------------------
	// Split views: render this window's displays into their own desktop
	// windows, straight from the shared canvas (CPU-side, no D3D).
	//------------------------------------------------------------------
	if (splitViews)
	{
		if (mDisplayToUpdate == 0)
		{
			if (splitView[SplitMap] != NULL)
			{
				// Map is portrait-mounted: rotate 90 degrees clockwise.
				// dest(x,y) = source(row = srcH-1-x, col = y)
				Word *source_base = pixelBuffer.Data.MapPointer;
				unsigned long *dest = splitView[SplitMap]->Pixels();
				int src_w = pixelBuffer.Data.Size.x;
				int src_h = pixelBuffer.Data.Size.y;

				for (int dy = 0; dy < src_w; ++dy)
				{
					for (int dx = 0; dx < src_h; ++dx)
					{
						Word pixel = source_base[(src_h - 1 - dx) * src_w + dy];
						PaletteTriplet *entry =
							&secPalette->paletteData.Color[pixel & secMask];
						*dest++ = ((unsigned long) entry->Red << 16) |
								  ((unsigned long) entry->Green << 8) |
								  ((unsigned long) entry->Blue);
					}
				}
				splitView[SplitMap]->Repaint();
			}
		}
		else if (mDisplayToUpdate == 1)
		{
			FillSplitMFD(SplitMFDUpperLeft,   ulMask, ulMask_sh);
			FillSplitMFD(SplitMFDUpperCenter, ucMask, ucMask_sh);
			FillSplitMFD(SplitMFDUpperRight,  urMask, urMask_sh);
			if (NUMGAUGEWINDOWS < 3)
			{
				// spanning mode: the lower MFDs ride this window too
				FillSplitMFD(SplitMFDLowerLeft,  llMask, llMask_sh);
				FillSplitMFD(SplitMFDLowerRight, lrMask, lrMask_sh);
			}
		}
		else
		{
			FillSplitMFD(SplitMFDLowerLeft,  llMask, llMask_sh);
			FillSplitMFD(SplitMFDLowerRight, lrMask, lrMask_sh);
		}
	}

	Word *data = pixelBuffer.Data.MapPointer;

	D3DLOCKED_RECT rect;
	if (FAILED(mSurfaces[mDisplayToUpdate * 2]->LockRect(0, &rect, NULL, mLockFlags[mDisplayToUpdate])))
	{
		DEBUG_STREAM << "Failed to lock texture for display " << mDisplayToUpdate << "!" << std::endl << std::flush;
	}
	else
	{
		switch(mDisplayToUpdate)
		{
			case 0:
				{
					//Secondary
					Word *source = data;
					Word *dest = (Word*)rect.pBits;

					int postRowIncrement = (rect.Pitch / 2) - pixelBuffer.Data.Size.x;
					for (int y = 0; y < pixelBuffer.Data.Size.y; y++)
					{
						for (int x = 0; x < pixelBuffer.Data.Size.x; x++)
						{
							PaletteTriplet *paletteEntry = &(secPalette->paletteData.Color[*source & secMask]);

							*dest = ((paletteEntry->Red >> 3) << 11) |
									((paletteEntry->Green >> 2) << 5) |
									((paletteEntry->Blue >> 3));

							dest++;
							source++;
						}

						dest += postRowIncrement;
					}
				}
				break;
			case 2:
				{
					//HACK - set the upper mask to be the lower mask (so that we draw the lower MFDs to the window)
					//and then drop through and draw like the upper stuff
					ulMask = llMask;
					ucMask = lrMask;
					urMask = 0;
					ulMask_sh = llMask_sh;
					ucMask_sh = lrMask_sh;
					urMask_sh = 0;
				}
			case 1:
				{
					DWORD *sourceData = (DWORD*)data;
					DWORD *destData = (DWORD*)rect.pBits;

					int leftWidth = pixelBuffer.Data.Size.x/2;

					int postRowIncrement = (rect.Pitch / sizeof(DWORD)) - leftWidth;
					for (int y = 0; y < pixelBuffer.Data.Size.y; y++)
					{
						for (int x=0; x < leftWidth; x++)
						{
							*destData = (((*sourceData & ulMask) >> ulMask_sh) * 0xF800) |
										(((*sourceData & ucMask) >> ucMask_sh) * 0x07E0) |
										(((*sourceData & urMask) >> urMask_sh) * 0x001F);

							if (NUMGAUGEWINDOWS < 3)
							{
								//Only draw if we are in spanning mode
								*(destData + leftWidth) = (((*sourceData & llMask) >> llMask_sh) * 0xF800) |
															(((*sourceData & lrMask) >> lrMask_sh) * 0x07E0);
							}
							sourceData++;
							destData++;
						}

						destData += postRowIncrement;
					}

					break;
				}
		}

		V( mSurfaces[mDisplayToUpdate * 2]->UnlockRect(0) );

		V( mDevice[mDisplayToUpdate]->UpdateTexture(mSurfaces[mDisplayToUpdate * 2], mSurfaces[mDisplayToUpdate * 2 + 1]) );
	}

	V( mDevice[mDisplayToUpdate]->BeginScene() );
	V( mDevice[mDisplayToUpdate]->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1) );
	V( mDevice[mDisplayToUpdate]->SetStreamSource(0, mVertBuffers[mDisplayToUpdate], 0, sizeof(float) * 6) );
	V( mDevice[mDisplayToUpdate]->SetTexture(0, mSurfaces[mDisplayToUpdate * 2 + 1]) );
	V( mDevice[mDisplayToUpdate]->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2) );
	V( mDevice[mDisplayToUpdate]->EndScene() );

	V( mDevice[mDisplayToUpdate]->Present(NULL, NULL, NULL, NULL) );
	if (hr == D3DERR_DEVICELOST)
	{
		int bbCount = mPresentParams[mDisplayToUpdate].BackBufferCount;
		int bbWidth = mPresentParams[mDisplayToUpdate].BackBufferWidth;
		int bbHeight = mPresentParams[mDisplayToUpdate].BackBufferHeight;

		mSurfaces[mDisplayToUpdate * 2 + 1]->Release();
		V(mDevice[mDisplayToUpdate]->Reset(&mPresentParams[mDisplayToUpdate]));
		hr = mDevice[mDisplayToUpdate]->CreateTexture(mSurfaceRects[mDisplayToUpdate].right - mSurfaceRects[mDisplayToUpdate].left, height, 1, 0, D3DFMT_R5G6B5, D3DPOOL_DEFAULT, &mSurfaces[mDisplayToUpdate * 2 + 1], NULL);


		mPresentParams[mDisplayToUpdate].BackBufferCount = bbCount;
		mPresentParams[mDisplayToUpdate].BackBufferWidth = bbWidth;
		mPresentParams[mDisplayToUpdate].BackBufferHeight = bbHeight;
	}

//	Time end = Now();

//	if (end.ticks - start.ticks > 100)
//		end = start;

	return False;			// True == 'more to do'
}


void SVGA16::Refresh()
{
	HRESULT hr;
	V( mDevice[mDisplayToUpdate]->Clear(0, NULL, D3DCLEAR_TARGET, 0xFF000000, 1.0f, 0) );

	V( mDevice[mDisplayToUpdate]->BeginScene() );

	V( mDevice[mDisplayToUpdate]->EndScene() );

	V( mDevice[mDisplayToUpdate]->Present(NULL, NULL, NULL, NULL) );
	if (hr == D3DERR_DEVICELOST)
	{
		int bbCount = mPresentParams[mDisplayToUpdate].BackBufferCount;
		int bbWidth = mPresentParams[mDisplayToUpdate].BackBufferWidth;
		int bbHeight = mPresentParams[mDisplayToUpdate].BackBufferHeight;

		mSurfaces[mDisplayToUpdate * 2 + 1]->Release();
		V(mDevice[mDisplayToUpdate]->Reset(&mPresentParams[mDisplayToUpdate]));
		hr = mDevice[mDisplayToUpdate]->CreateTexture(mSurfaceRects[mDisplayToUpdate].right - mSurfaceRects[mDisplayToUpdate].left, height, 1, 0, D3DFMT_R5G6B5, D3DPOOL_DEFAULT, &mSurfaces[mDisplayToUpdate * 2 + 1], NULL);

		V( mDevice[mDisplayToUpdate]->Present(NULL, NULL, NULL, NULL));

		mPresentParams[mDisplayToUpdate].BackBufferCount = bbCount;
		mPresentParams[mDisplayToUpdate].BackBufferWidth = bbWidth;
		mPresentParams[mDisplayToUpdate].BackBufferHeight = bbHeight;
	}

	mDisplayToUpdate++;

	if (mDisplayToUpdate >= NUMGAUGEWINDOWS)
	{
		mDisplayToUpdate = 0;
	}
}
//STUBBED: VIDEO RB 1/15/07
//	SET_SCREEN_COPY();
//	Diag_Tell("SVGA16::Update(" << forceAll << ")\n");
//	Check(this);
//
//	//---------------------------------------------------------
//	// If pixelbuffer is invalid, do nothing
//	//---------------------------------------------------------
//	if (!valid)
//	{
//		CLEAR_SCREEN_COPY();
//		return False;		// Do no more!
//	}
//	//---------------------------------------------------------
//	// Mark all lines as changed if commanded
//	//---------------------------------------------------------
//	if (forceAll)
//	{
//		memset(changedLine, 1, height);
//	}
//
//	int
//		previous_line_number = lineNumber;
//
//	long
//		dest_size,
//		dest_line_size;
//
//	//---------------------------------------------------------
//	// Calculate sizes based on specialInterface
//	//---------------------------------------------------------
//	if (specialInterface)
//	{
//		dest_size = 32L;					// BYTE offset!
//		dest_line_size = width;
//	}
//	else
//	{
//		dest_size = 32L << 1;			// WORD offset!
//		dest_line_size = width << 1;
//	}
//	//---------------------------------------------------------
//	// Process lines for as long as we can
//	//---------------------------------------------------------
//#if 0		// Let's process just a few lines for background loop processing...
//	while (Get_Frame_Percent_Used() < .65f)
//#endif
//	{
//		//---------------------------------------------------------
//		// Do a couple of lines before checking time again
//		//---------------------------------------------------------
//		for(int line_limit=15; line_limit > 0; )
//		{
//			//---------------------------------------------------------
//			// Check for changed line, clear the flag
//			//---------------------------------------------------------
//			Verify(changedLinePointer < &changedLine[height]);
//
//			int
//				changed_line = *changedLinePointer;
//			*changedLinePointer++ = 0;
//
//			if (changed_line)
//			{
//				Diag_Tell(height << ":");
//				//---------------------------------------------------------
//				// Pixels on this line have been changed
//				//---------------------------------------------------------
//				--line_limit;
//				//---------------------------------------------------------
//				// Check 'dirty' LWords: if set, scan for changed pixels
//				//---------------------------------------------------------
//				for(int x=0; x<changedBitWidth; ++x)
//				{
//					//---------------------------------------------------------
//					// Check for changed pixels in LWord, clear flag
//					//---------------------------------------------------------
//					Verify(
//						changedBitPointer < &changedBit[height*changedBitWidth]
//					);
//
//					LWord
//						changed_bits = *changedBitPointer;
//					*changedBitPointer++ = 0L;
//
//					if (changed_bits)
//					{
//						Diag_Tell( (changed_bits & 0xFFFF0000L) ? "#" : "." );
//						Diag_Tell( (changed_bits & 0x0000FFFFL) ? "#" : "." );
//						//---------------------------------------------------------
//						// Changed pixels!  Make sure display page is correct
//						//---------------------------------------------------------
//						if (currentPageNumber != nextPageNumber)
//						{
//							SVGASetPage(nextPageNumber);
//							currentPageNumber = nextPageNumber;
//						}
//						//---------------------------------------------------------
//						// Then transfer changed pixels
//						//---------------------------------------------------------
//						if (specialInterface)
//						{
//#							if defined(BLIT_STATISTICS)
//								transferPixelCount +=
//#							endif
//							SVGATransfer32x(destOffset, sourcePointer, changed_bits);
//						}
//						else
//						{
//#							if defined(BLIT_STATISTICS)
//								transferPixelCount +=
//#							endif
//							SVGATransfer32(destOffset, sourcePointer, changed_bits);
//						}
//					}
//#					if defined(DEBUG)
//					else
//					{
//						//---------------------------------------------------------
//						// No pixels in this LWord, move to next one
//						//---------------------------------------------------------
//						Tell("--");
//					}
//#					endif
//					//---------------------------------------------------------
//					// Update remaining pixel count
//					//---------------------------------------------------------
//					sourcePointer += 32;
//					destOffset += dest_size;
//
//					if (destOffset >= pageSize)
//					{
//						destOffset -= pageSize;
//						nextPageNumber += pageDelta;
//					}
//				}
//				Diag_Tell("\n");
//			}
//			else
//			{
//				//---------------------------------------------------------
//				// Line has not changed, skip over it
//				//---------------------------------------------------------
//#if defined(CHECK_FOR_DIRT)
//				Logical
//					dirty_flag = False;
//
//				for(int x=0; x<changedBitWidth; ++x)
//				{
//					//---------------------------------------------------------
//					// Check for changed pixels in LWord
//					//---------------------------------------------------------
//					Verify(
//						changedBitPointer < &changedBit[height*changedBitWidth]
//					);
//
//					LWord
//						changed_bits = *changedBitPointer;
//					*changedBitPointer++ = 0L;
//
//					if (changed_bits)
//					{
//						dirty_flag = True;
//					}
//					//---------------------------------------------------------
//					// Update remaining pixel count
//					//---------------------------------------------------------
//					sourcePointer += 32;
//					destOffset += dest_size;
//
//					if (destOffset >= pageSize)
//					{
//						destOffset -= pageSize;
//						nextPageNumber += pageDelta;
//					}
//				}
//				if (dirty_flag)
//				{
//					Tell(
//						"SVGA16::Update: Unexpected dirty bits in line " <<
//						lineNumber <<
//						"\n!"
//					);
//				}
//#else
//				changedBitPointer += changedBitWidth;
//				sourcePointer += width;
//				destOffset += dest_line_size;
//
//				if (destOffset >= pageSize)
//				{
//					destOffset -= pageSize;
//					nextPageNumber += pageDelta;
//				}
//#endif
//			}
//
//#			if defined(BLIT_STATISTICS)
//				//---------------------------------
//				// Keep values within a sane range
//				//---------------------------------
//				if (
//						(transferPixelCount > 0x10000000)
//						||
//						(dirtyPixelCount > 0x10000000)
//					)
//				{       
//					transferPixelCount >>= 1;
//					dirtyPixelCount >>= 1;
//					++overflowPixelCount;
//				}
//#			endif
//			//---------------------------------------------------------
//			// Check for wrap
//			//---------------------------------------------------------
//			if (++lineNumber >= height)
//			{
//				ResetUpdatePosition();
//			}
//			//---------------------------------------------------------
//			// If back to beginning, exit loop
//			//---------------------------------------------------------
//			if (lineNumber == previous_line_number)
//			{
//				Check_Fpu();
//				CLEAR_SCREEN_COPY();
//				return False;		// All done
//			}
//		}
//	}
//	Check_Fpu();
//	CLEAR_SCREEN_COPY();


void
	SVGA16::ResetUpdatePosition()
{
	lineNumber = 0;

	sourcePointer = pixelBuffer.Data.MapPointer;

	changedLinePointer = changedLine;
	changedBitPointer = changedBit;

	nextPageNumber = 0;
	destOffset = 0L;
}

void
	SVGA16::FadeToPalettes(Scalar fade_time)
{
	Check(this);

	previousFadeTime = Now();
	paletteFadeState = fadeToColor;

	if(Small_Enough(fade_time))
	{
		fadeUnitsPerSecond = 0.0;
		fadeAlpha = 1.0;
	}
	else
	{
		fadeUnitsPerSecond = (1.0/fade_time);
	}
	Check_Fpu();
}

void
	SVGA16::FadeToWhite(Scalar fade_time)
{
	Check(this);

	previousFadeTime = Now();
	paletteFadeState = fadeToWhite;

	if(Small_Enough(fade_time))
	{
		fadeUnitsPerSecond = 0.0;
		fadeAlpha = 0.0;
	}
	else
	{
		fadeUnitsPerSecond = (1.0/fade_time);
	}
	Check_Fpu();
}

void
	generateFade(
		SVGA16Palette *source,
		Scalar	alpha
	)
{
//STUBBED: VIDEO RB 1/15/07
	Check_Pointer(source);

	if (alpha < 0.0)
	{
		alpha = 0.0;
	}
	else if (alpha > 1.0)
	{
		alpha = 1.0;
	}

	Palette8
		temp;
//	Scalar
//		inverse_alpha = (1.0 - alpha) * 255.0;
	int
		i;

	for(i=0; i<256; ++i)
	{
		temp.Color[i].Red = (Byte)
//			(inverse_alpha + (source->paletteData.Color[i].Red   * alpha));
			((source->paletteData.Color[i].Red   * alpha));
		temp.Color[i].Green = (Byte)
//			(inverse_alpha + (source->paletteData.Color[i].Green * alpha));
			((source->paletteData.Color[i].Green * alpha));
		temp.Color[i].Blue = (Byte)
//			(inverse_alpha + (source->paletteData.Color[i].Blue  * alpha));
			((source->paletteData.Color[i].Blue  * alpha));
	}

//	SVGAWriteFullPalette(		// in L4SVGA16.ASM
//		&temp.Color[0].Red,
//		source->hardwarePort
//	);
	Check_Fpu();
}

void
	SVGA16::FlashPalette(
		int palette_number,
		Scalar rate,
		unsigned char *stateList
	)
{
	Check(this);
	Verify(palette_number >= 0);
	Verify(palette_number < PaletteCount);

	SVGA16Palette
		*palette_pointer = &palette[palette_number];

	//-------------------------------------------------
	// Adjust rate so value becomes "cycles per second"
	//-------------------------------------------------
	palette_pointer->flashRate = rate * (Scalar) SVGA16Palette::maskStates;
	//-------------------------------------------------
	// Copy the mask values
	//-------------------------------------------------
	for (int i=0; i<SVGA16Palette::maskStates; ++i)
	{
		palette_pointer->mask[i] = *stateList++;
	}
	Check_Fpu();
}

void
	SVGA16::UnflashPalette(
		int palette_number
	)
{
	//STUBBED: VIDEO RB 1/15/07
	//Check(this);
	//Verify(palette_number >= 0);
	//Verify(palette_number < PaletteCount);

	//SVGA16Palette
	//	*palette_pointer = &palette[palette_number];

	//palette_pointer->flashRate = 0.0;
	//palette_pointer->flashAccumulator = 0.0;
	//palette_pointer->previousMaskState = -1;
	//SVGAWritePaletteMask(palette_pointer->hardwarePort, 0xFF);

	//Check_Fpu();
}

void
	SVGA16::UpdatePalette()
{
	//STUBBED: VIDEO RB 1/15/07
	Check(this);

	int
		i,
		mask_state;
	SVGA16Palette
		*palette_pointer;

	//--------------------------------------------------------
	// Update time values
	//--------------------------------------------------------
	Time
		right_now = Now();
	Scalar
		delta_t = (Scalar) (right_now - previousFadeTime);

	previousFadeTime = right_now;
	if (delta_t <= 0.0)
	{
		return;
	}
	//--------------------------------------------------------
	// Set palette masks
	//--------------------------------------------------------
	palette_pointer = &palette[0];
	for(i=0; i<PaletteCount; ++i,++palette_pointer)
	{
		if (palette_pointer->flashRate != 0.0)
		{
			palette_pointer->flashAccumulator +=
				palette_pointer->flashRate * delta_t;

			while (palette_pointer->flashAccumulator >=
				(Scalar) SVGA16Palette::maskStates)
			{
				palette_pointer->flashAccumulator -=
					(Scalar) SVGA16Palette::maskStates;
			}

			mask_state = (int) palette_pointer->flashAccumulator;

			if (mask_state != palette_pointer->previousMaskState)
			{
				palette_pointer->previousMaskState = mask_state;

	//			SVGAWritePaletteMask(
	//				palette_pointer->hardwarePort,
	//				palette_pointer->mask[mask_state]
	//			);
			}
		}
	}
	//--------------------------------------------------------
	// Fade palettes
	//--------------------------------------------------------
	palette_pointer = &palette[0];
	for(i=0; i<PaletteCount; ++i,++palette_pointer)
	{
		//--------------------------------------------------------
		// Only the secondary palette (i==1) is allowed to fade!
		// All other palettes are merely copied.
		//--------------------------------------------------------
		if ((i != 1) || (paletteFadeState == staticPalette))
		{
			if (palette_pointer->modified)
			{
				palette_pointer->modified = False;

	//			SVGAWriteFullPalette(
	//				&palette_pointer->paletteData.Color[0].Red,
	//				palette_pointer->hardwarePort
	//			);
			}
		}
		else
		{
			//--------------------------------------------------------
			// Discard 'modified' flag (we use the local values for
			// fade in/out, and in the 'white' state we don't care,
			// because we'll have to fade back in anyway)
			//--------------------------------------------------------
			palette_pointer->modified = False;
			//--------------------------------------------------------
			// Perform fade (or stay white)
			//--------------------------------------------------------
			switch(paletteFadeState)
			{
			case fadeToWhite:
				fadeAlpha -= (delta_t * fadeUnitsPerSecond);
				generateFade(palette_pointer, fadeAlpha);

				if (fadeAlpha <= 0.0)
				{
					fadeAlpha = 0.0;
					paletteFadeState = whitePalette;
				}
				break;

			case whitePalette:
				break;

			case fadeToColor:
				fadeAlpha += (delta_t * fadeUnitsPerSecond);
				generateFade(palette_pointer, fadeAlpha);

				if (fadeAlpha >= 1.0)
				{
					fadeAlpha = 1.0;
					paletteFadeState = staticPalette;
				}
				break;
			}
		}
	}
	Check_Fpu();
}

void
	SVGA16::FunkyVideo(Logical on_off)
{
	//STUBBED: VIDEO RB 1/15/07
	//Check(this);
	//SVGAFunkyVideo(on_off);
	//Check_Fpu();
}

//########################################################################
//########################### L4GraphicsPort #############################
//########################################################################

L4GraphicsPort::L4GraphicsPort(
	Video16BitBuffered *graphics_display,
	const char *name,
	int rotation,
	int bit_mask,
	SVGA16::PaletteID palette_ID,
	L4GraphicsPort::ChannelEnableID channel_enable
):GraphicsPort(graphics_display, name)
{
	int
		bit_test;
	//
	// Save the base rotation value
	//
	baseRotation = rotation;
	//
	// Save the paletteID and channel enable
	//
	paletteID = palette_ID;
	channelEnable = channel_enable;
	//
	// Save the bitMask
	//
	bitMask = bit_mask;
	Verify (bitMask != 0);
	//
	// Count the number of active bits in the bitMask
	//
	for(bit_test=0x8000,numberOfBits=0; bit_test!=0; bit_test>>=1)
	{
		if (bit_test & bitMask)
		{
			++numberOfBits;
		}
	}
	//
	// Initialize conversion constants
	//
	maximumX = bounds.topRight.x - bounds.bottomLeft.x;
	maximumY = bounds.topRight.y - bounds.bottomLeft.y;
	//
	// Overwrite the GraphicsPort::bounds data with rotated values
	// for GraphView's benefit
	//
	switch(baseRotation)
	{
	default:
		// do nothing
		break;

	case 90:
	case 270:
		if (graphics_display != NULL)
		{
			Check(graphics_display);
			bounds.bottomLeft.x = graphics_display->bounds.bottomLeft.y;
			bounds.bottomLeft.y = graphics_display->bounds.bottomLeft.x;
			bounds.topRight.x   = graphics_display->bounds.topRight.y;
			bounds.topRight.y   = graphics_display->bounds.topRight.x;
		}
		break;
	}

	for(int j=0; j<256; ++j)
	{
		myColor[j] = NULL;
	}

	Check_Fpu();
}

L4GraphicsPort::~L4GraphicsPort()
{
	Check(this);
	Check_Fpu();
}

Logical
	L4GraphicsPort::TestInstance() const
{
	return True;
}

void
	L4GraphicsPort::ShowInstance(char *indent)
{
	Check(this);

	std::cout << indent << "L4GraphicsPort:\n";

	char
		temp[80];

	Str_Copy(temp,indent, 80);
	Str_Cat(temp,"...", 80);

	std::cout << temp << "bitMask      =" << bitMask << "\n";
	std::cout << temp << "baseRotation =" << baseRotation << "\n";
	std::cout << temp << "maximumX     =" << maximumX << "\n";
	std::cout << temp << "maximumY     =" << maximumY << "\n";
	std::cout << temp << "bounds       =" << bounds << "\n";
	std::cout << std::flush;
	GraphicsPort::ShowInstance(temp);
	Check_Fpu();
}

void
	L4GraphicsPort::SetColor(
		PaletteTriplet *source_triplet,
		int color_index
	)
{
	Check(this);
	Check_Pointer(source_triplet);
   Verify(color_index >= 0);
	Verify(color_index < 256);

	switch(channelEnable)
	{
	case L4GraphicsPort::DirectColor:
	case L4GraphicsPort::BlankColor:
		break; 
	default:
		BuildSecondaryColor(source_triplet, color_index);
		break;
	}        
	Check_Fpu();
}

void
	L4GraphicsPort::SetSecondaryPalette(
		Palette8 *source_palette
	)
{
	Check(this);
	Check(source_palette);

	if (graphicsDisplay == NULL)
	{
		return;
	}

	switch(channelEnable)
	{
	case L4GraphicsPort::DirectColor:
		BuildDirectTranslation(source_palette);
		break;
	case L4GraphicsPort::BlankColor:
		BlankPalette();
		BuildSecondaryTranslation();
		break; 
	default:
		BuildSecondaryPalette(source_palette);
		BuildSecondaryTranslation();
		break;
	}        
	Check_Fpu();
}


void
	L4GraphicsPort::SetAuxiliaryPalette()
{
	Check(this);

	if (graphicsDisplay == NULL)
	{
		return;
	}

	switch(channelEnable)
	{
	case L4GraphicsPort::DirectColor:
		//---------------------------------------------------------------
		// Set translation table for direct color mode
		//---------------------------------------------------------------
		{
			Palette8
				monochrome_palette;

			PaletteTriplet
				black,
				white;

			black.Red   = 0;
			black.Green = 0;
			black.Blue  = 0;

			white.Red   = 255;
			white.Green = 255;
			white.Blue  = 255;

			monochrome_palette.BuildColorRange(0,255,black, white);

			BuildDirectTranslation(&monochrome_palette);
		}
		break;

	case L4GraphicsPort::BlankColor:
		BlankPalette();
		BuildAuxiliaryTranslation();
		break;

	default:
		BuildAuxiliaryPalette();
		BuildAuxiliaryTranslation();
		break;
	}
	Check_Fpu();
}


void
	L4GraphicsPort::DrawPoint(
		int color,
		Enumeration operation,
		int x, int y
	)
{
	Check(this);

	if (graphicsDisplay == NULL)
	{
		Check_Fpu();
		return;
	}

	Check(graphicsDisplay);

	int
		xp, yp;

	convertPortPair(&xp, &yp, x, y);

	graphicsDisplay->
		DrawPoint(
			translationTable[color],
			bitMask,
			operation,
			xp+bounds.bottomLeft.x, yp+bounds.bottomLeft.y
		);
	Check_Fpu();
}

void
	L4GraphicsPort::DrawLine(
		int color,
		Enumeration operation,
		int x1, int y1,
		int x2, int y2,
		Logical include_last_pixel
	)
{
	Check(this);

	if (graphicsDisplay == NULL)
	{
		Check_Fpu();
		return;
	}

	Check(graphicsDisplay);

	int
		x1p, y1p,
		x2p, y2p;

	convertPortPair(&x1p, &y1p, x1, y1);
	convertPortPair(&x2p, &y2p, x2, y2);

	graphicsDisplay->
		DrawLine(
			translationTable[color],
			bitMask,
			operation,
			x1p+bounds.bottomLeft.x, y1p+bounds.bottomLeft.y,
			x2p+bounds.bottomLeft.x, y2p+bounds.bottomLeft.y,
         include_last_pixel
		);
	Check_Fpu();
}

void
	L4GraphicsPort::DrawFilledRectangle(
		int color,
		Enumeration operation,
		int x1, int y1,
		int x2, int y2
	)
{
	Check(this);

	if (graphicsDisplay == NULL)
	{
		Check_Fpu();
		return;
	}

	Check(graphicsDisplay);

	int
		x1p, y1p,
		x2p, y2p;

	convertPortPair(&x1p, &y1p, x1, y1);
	convertPortPair(&x2p, &y2p, x2, y2);

	graphicsDisplay->
		DrawFilledRectangle(
			translationTable[color],
			bitMask,
			operation,
			x1p+bounds.bottomLeft.x, y1p+bounds.bottomLeft.y,
			x2p+bounds.bottomLeft.x, y2p+bounds.bottomLeft.y
		);
	Check_Fpu();
}

void
	L4GraphicsPort::DrawText(
		int /*color*/,
		Enumeration /*operation*/,
		Logical /*opaque*/,
		int /*rotation*/,
		Enumeration /*fontNumber*/,
		Logical /*vertical*/,
		GraphicsDisplay::Justification /*justification*/,
		Rectangle2D	*/*clippingRectanglepointer*/,
		char */*stringPointer*/
	)
{
}

void
	L4GraphicsPort::DrawBitMap(
		int color,
		Enumeration operation,
		int rotation,
		int x, int y,
		BitMap *bitmap,
		int sLeft, int sBottom, int sRight, int sTop
	)
{
	Check(this);

	if (graphicsDisplay == NULL)
	{
		Check_Fpu();
		return;
	}

	Check(graphicsDisplay);

	if (bitmap == NULL)
	{
		Check_Fpu();
		return;
	}

	if (bitmap->Data.MapPointer == NULL)
	{
		Check_Fpu();
		return;
	}

	Check(bitmap);

	int
		xp, yp;

	convertPortPair(&xp, &yp, x, y);

	rotation += baseRotation;
	while (rotation <    0) { rotation += 360; }
	while (rotation >= 360) { rotation -= 360; }

	graphicsDisplay->
		DrawBitMap(
			translationTable[color],
			bitMask,
			operation,
			rotation,
			xp+bounds.bottomLeft.x, yp+bounds.bottomLeft.y,
			bitmap,
			sLeft, sBottom, sRight, sTop
		);
	Check_Fpu();
}

void
	L4GraphicsPort::DrawBitMapOpaque(
		int color,
		int background,
		Enumeration operation,
		int rotation,
		int x, int y,
		BitMap *bitmap,
		int sLeft, int sBottom, int sRight, int sTop
	)
{
	Check(this);

	if (graphicsDisplay == NULL)
	{
		Check_Fpu();
		return;
	}

	Check(graphicsDisplay);

	if (bitmap == NULL)
	{
		Check_Fpu();
		return;
	}

	if (bitmap->Data.MapPointer == NULL)
	{
		Check_Fpu();
		return;
	}

	Check(bitmap);

	int
		xp, yp;

	convertPortPair(&xp, &yp, x, y);

	rotation += baseRotation;
	while (rotation <    0) { rotation += 360; }
	while (rotation >= 360) { rotation -= 360; }

	graphicsDisplay->
		DrawBitMapOpaque(
			translationTable[color],
			translationTable[background],
			bitMask,
			operation,
			rotation,
			xp+bounds.bottomLeft.x, yp+bounds.bottomLeft.y,
			bitmap,
			sLeft, sBottom, sRight, sTop
		);
}

void
	L4GraphicsPort::DrawPixelMap8(
		Enumeration operation,
		Logical opaque,
		int rotation,
		int x, int y,
		PixelMap8 *pixelmap,
		int sLeft, int sBottom, int sRight, int sTop
	)
{
	Check(this);

	if (graphicsDisplay == NULL)
	{
		Check_Fpu();
		return;
	}

	Check(graphicsDisplay);

	if (pixelmap == NULL)
	{
		Check_Fpu();
		return;
	}

	if (pixelmap->Data.MapPointer == NULL)
	{
		Check_Fpu();
		return;
	}

	Check(pixelmap);

	int
		xp, yp;

	convertPortPair(&xp, &yp, x, y);

	rotation += baseRotation;
	while (rotation <    0) { rotation += 360; }
	while (rotation >= 360) { rotation -= 360; }

	graphicsDisplay->
		DrawPixelMap8(
			translationTable,
			bitMask,
			operation,
			opaque,
			rotation,
			xp+bounds.bottomLeft.x, yp+bounds.bottomLeft.y,
			pixelmap,
			sLeft, sBottom, sRight, sTop
		);
	Check_Fpu();
}

void
	L4GraphicsPort::DrawPixelMap8SingleColor(
		int color,
		Enumeration operation,
		int rotation,
		int x, int y,
		PixelMap8 *pixelmap,
		int sLeft, int sBottom, int sRight, int sTop
	)
{
	Check(this);

	if (graphicsDisplay == NULL)
	{
		Check_Fpu();
		return;
	}

	Check(graphicsDisplay);

	if (pixelmap == NULL)
	{
		Check_Fpu();
		return;
	}

	if (pixelmap->Data.MapPointer == NULL)
	{
		Check_Fpu();
		return;
	}

	Check(pixelmap);

	int
		xp, yp;

	convertPortPair(&xp, &yp, x, y);

	rotation += baseRotation;
	while (rotation <    0) { rotation += 360; }
	while (rotation >= 360) { rotation -= 360; }

	graphicsDisplay->
		DrawPixelMap8SingleColor(
			translationTable[color],
			bitMask,
			operation,
			rotation,
			xp+bounds.bottomLeft.x, yp+bounds.bottomLeft.y,
			pixelmap,
			sLeft, sBottom, sRight, sTop
		);
	Check_Fpu();
}

void
	L4GraphicsPort::convertPortPair(
		int *xp,
		int *yp,
		int x,
		int y
	)
{
	switch(baseRotation)
	{
	default:
		*xp = x;
		*yp = y;
		break;

	case 90:
		*xp = y;
		*yp = maximumY - x;
		break;

	case 180:
		*xp = maximumX - x;
		*yp = maximumY - y;
		break;

	case 270:
		*xp = maximumX - y;
		*yp = x;
		break;
	}
	Check_Fpu();
}

//
//---------------------------------------------------------------------------
// This method generates a translation table specifically for writing
// to a "direct" color mode display, in which colors are represented
// as 5 red bits, 6 green bits, and 5 blue bits in a display word.
//---------------------------------------------------------------------------
//
void
	L4GraphicsPort::BuildDirectTranslation(
		Palette8 *source_palette
	)
{
	PaletteTriplet
		*source_data(source_palette->Color);

	int
		*dest(translationTable);

	int
		red_bits,
		green_bits,
		blue_bits,
		i;

	//
	// Force to all bits (just to be sure)
	//
	bitMask = 0xFFFF;

	for(i=256; i>0; --i,++source_data)
	{
		red_bits   = source_data->Red >> 3;
		green_bits = source_data->Green >> 2;
		blue_bits  = source_data->Blue >> 3;

		*dest++ = (red_bits << 11)|(green_bits << 5) | blue_bits;
	}
	Check_Fpu();
}

//
//---------------------------------------------------------------------------
// This method combines palettes from multiple L4GraphicsPorts.
// If several ports have palettes mapped onto the same RGB display, then
// the most recently built palette has precedence over the previous one(s).
//
// Palettes are assumed to always have 256 colors.
//---------------------------------------------------------------------------
//
void
	L4GraphicsPort::BuildSecondaryPalette(
		Palette8 *source_palette
	)
{
	Verify (bitMask != 0);

	if (graphicsDisplay == NULL)
	{
		return;
	}
	Check(graphicsDisplay);
	Check(source_palette);


	int
		byte_mask(bitMask & 0xFF);
	Verify (byte_mask != 0);

	BitWrangler
		wrangler(byte_mask, 8);

	PaletteTriplet
		*source_triplet,
		*destination_triplet;

	SVGA16Palette
		*svga_palette(&((SVGA16 *) graphicsDisplay)->palette[paletteID]);

	source_triplet = &source_palette->Color[0];

	//-------------------------------------------
	// Clear the 'owned color' flags
	//-------------------------------------------
	for(int j=0; j<256; ++j)
	{
		myColor[wrangler.Value] = 0;
	}

	//
	// If any of the ...TransparentZero modes are used,
	// leave color zero undefined for this bit group by
	// skipping over it
	//
	if (channelEnable & 0x80)
	{
		++source_triplet;
		wrangler.IncrementActive();
	}

	//
	// Fill in the color table for this palette set
	//
	do
	{
		//-------------------------------------------
		// Write all destination colors
		//-------------------------------------------
		do
		{
			//-------------------------------------------
			// Set the 'owned color' flag
			//-------------------------------------------
			myColor[wrangler.Value] = 1;
			//-------------------------------------------
			// Get destination pointer
			//-------------------------------------------
			destination_triplet =
				&svga_palette->paletteData.Color[wrangler.Value];
			//-------------------------------------------
			// Copy color data
			//-------------------------------------------
			switch(channelEnable)
			{
			case RedChannel:
			case RedChannelTransparentZero:
				destination_triplet->Red = source_triplet->Red;
				break;

			case GreenChannel:
			case GreenChannelTransparentZero:
				destination_triplet->Green = source_triplet->Green;
				break;

			case BlueChannel:
			case BlueChannelTransparentZero:
				destination_triplet->Blue = source_triplet->Blue;
				break;

			case AllChannels:
			case AllChannelsTransparentZero:
				*destination_triplet = *source_triplet;
				break;
			}
		}
		while (wrangler.IncrementInactive());
		//-------------------------------------------
		// Bump the source pointer
		//-------------------------------------------
		++source_triplet;
	}
	while (wrangler.IncrementActive());

	svga_palette->paletteData.Valid = True;
	svga_palette->modified = True;

	Check_Fpu();
}

//
//---------------------------------------------------------------------------
// This method combines palettes from multiple L4GraphicsPorts.
// If several ports have palettes mapped onto the same RGB display, then
// the most recently built palette has precedence over the previous one(s).
//
// Palettes are assumed to always have 256 colors.
//---------------------------------------------------------------------------
//
void
	L4GraphicsPort::BuildSecondaryColor(
		PaletteTriplet *source_triplet,
		int dest_color_number
	)
{
	Verify (bitMask != 0);

	if (graphicsDisplay == NULL)
	{
		return;
	}
	Check(graphicsDisplay);
	Check_Pointer(source_triplet);

	int
		byte_mask(bitMask & 0xFF);
	Verify (byte_mask != 0);

	BitWrangler
		wrangler(byte_mask, 8);

	int
		destination_color = 0;

	PaletteTriplet
		*destination_triplet;

	SVGA16Palette
		*svga_palette(&((SVGA16 *) graphicsDisplay)->palette[paletteID]);

	//-------------------------------------------
	// If any of the ...TransparentZero modes are used,
	// leave color zero undefined for this bit group by
	// skipping over it
	//-------------------------------------------
	if (channelEnable & 0x80)
	{
		++destination_color;
		wrangler.IncrementActive();
	}

	//-------------------------------------------
	// Set the color value
	//-------------------------------------------
	do
	{
		//-------------------------------------------
		// Write all destination colors
		//-------------------------------------------
		if (destination_color == dest_color_number)
		{ 
			//-------------------------------------------
			// Write all destination colors
			//-------------------------------------------
			do
			{
				//-------------------------------------------
				// Do we own this color? Skip if unowned
				//-------------------------------------------
				if (myColor[wrangler.Value])
				{
					//-------------------------------------------
					// Copy color data
					//-------------------------------------------
					destination_triplet =
						&svga_palette->paletteData.Color[wrangler.Value];

					switch(channelEnable)
					{
					case RedChannel:
					case RedChannelTransparentZero:
						destination_triplet->Red = source_triplet->Red;
						break;

					case GreenChannel:
					case GreenChannelTransparentZero:
						destination_triplet->Green = source_triplet->Green;
						break;

					case BlueChannel:
					case BlueChannelTransparentZero:
						destination_triplet->Blue = source_triplet->Blue;
						break;

					case AllChannels:
					case AllChannelsTransparentZero:
						*destination_triplet = *source_triplet;
						break;
					}
				}
			}
			while (wrangler.IncrementInactive());
			//-------------------------------------------
			// Only doing one color, so exit the loop
			//-------------------------------------------
			break;
		}
		//-------------------------------------------
		// Bump the color counter
		//-------------------------------------------
		++destination_color;
	}
	while (wrangler.IncrementActive());

	svga_palette->paletteData.Valid = True;
	svga_palette->modified = True;

	Check_Fpu();
}

//
//---------------------------------------------------------------------------
// This method combines palettes from multiple L4GraphicsPorts.
// If several ports have palettes mapped on top of another, then
// the most recently built palette has precedence over the previous one(s).
//---------------------------------------------------------------------------
//
void
	L4GraphicsPort::BuildAuxiliaryPalette()
{
	Verify (bitMask != 0);

	if (graphicsDisplay == NULL)
	{
		return;
	}
	Check(graphicsDisplay);


	int
		byte_mask((bitMask >> 8) & 0xFF);

	Verify (byte_mask != 0);
	BitWrangler
		wrangler(byte_mask, 8);

	int
		rate,
		accumulator;

	Byte
		color_value;

	PaletteTriplet
		*destination_triplet;

	SVGA16Palette
		*svga_palette(&((SVGA16 *) graphicsDisplay)->palette[paletteID]);

	Verify(((1<<wrangler.NumberOfActiveBits())-1) > 0);
	rate = (255<<5)/((1<<wrangler.NumberOfActiveBits())-1); 
	accumulator = 0;

	//
	//-----------------------------------------------------------------------
	// Generate palette
	//-----------------------------------------------------------------------
	//

	// If any of the ...TransparentZero modes are used,
	// leave color zero undefined for this bit group by
	// skipping over it
	if (channelEnable & 0x80)
	{
		accumulator += rate;
		wrangler.IncrementActive();
	}

	do
	{
		color_value = (Byte) (accumulator >> 5);
		accumulator += rate;

		do
		{
			destination_triplet = &svga_palette->paletteData.
				Color[wrangler.Value];

			switch(channelEnable)
			{
			case RedChannel:
			case RedChannelTransparentZero:
				destination_triplet->Red = color_value;
				break;

			case GreenChannel:
			case GreenChannelTransparentZero:
				destination_triplet->Green = color_value;
				break;

			case BlueChannel:
			case BlueChannelTransparentZero:
				destination_triplet->Blue = color_value;
				break;

			case AllChannels:
			case AllChannelsTransparentZero:
				destination_triplet->Red   = color_value;
				destination_triplet->Green = color_value;
				destination_triplet->Blue  = color_value;
				break;
			}
		}
		while (wrangler.IncrementInactive());
	}
	while (wrangler.IncrementActive());

	svga_palette->paletteData.Valid = True;
	svga_palette->modified = True;

#	if defined(TESTPALETTE)
		std::cout << "L4GraphicsPort::BuildAuxiliaryPalette for port " <<
			std::hex << svga_palette->hardwarePort << "\n";
		for(int i=0; i<256; ++i)
		{
			if ((i & 0x03) == 0)
			{
				std::cout << std::dec << "\n" << i << ":" << std::hex;
			}
			std::cout << svga_palette->paletteData.Color[i] << " ";
		}
		std::cout << "\n";
		{
			Palette8
				temp;

			SVGAReadFullPalette(
				&temp.Color[0].Red,
				svga_palette->hardwarePort
			);

			std::cout << "L4GraphicsPort::BuildAuxiliaryPalette, read back\n";
			for(int i=0; i<256; ++i)
			{
				if ((i & 0x03) == 0)
				{
					std::cout << std::dec << "\n" << i << ":" << std::hex;
				}
				std::cout << temp.Color[i] << " ";
			}
			std::cout << "\n";
		}
#	endif
	Check_Fpu();
}

//
//---------------------------------------------------------------------------
// This sets 'owned' color values in a palette to zero.
//---------------------------------------------------------------------------
//
void
	L4GraphicsPort::BlankPalette()
{
	Verify (bitMask != 0);

	if (graphicsDisplay == NULL)
	{
		return;
	}
	Check(graphicsDisplay);

	int
		byte_mask((bitMask >> 8) & 0xFF);

	Verify (byte_mask != 0);
	BitWrangler
		wrangler(byte_mask, 8);

	Byte
		color_value;

	PaletteTriplet
		*destination_triplet;

	SVGA16Palette
		*svga_palette(&((SVGA16 *) graphicsDisplay)->palette[paletteID]);

	Verify(((1<<wrangler.NumberOfActiveBits())-1) > 0);

	color_value = 0;
	do
	{
		do
		{
			destination_triplet = &svga_palette->paletteData.
				Color[wrangler.Value];

			switch(channelEnable)
			{
			case RedChannel:
			case RedChannelTransparentZero:
				destination_triplet->Red = color_value;
				break;

			case GreenChannel:
			case GreenChannelTransparentZero:
				destination_triplet->Green = color_value;
				break;

			case BlueChannel:
			case BlueChannelTransparentZero:
				destination_triplet->Blue = color_value;
				break;

			case AllChannels:
			case AllChannelsTransparentZero:
				destination_triplet->Red   = color_value;
				destination_triplet->Green = color_value;
				destination_triplet->Blue  = color_value;
				break;
			}
		}
		while (wrangler.IncrementInactive());
	}
	while (wrangler.IncrementActive());

	svga_palette->paletteData.Valid = True;
	svga_palette->modified = True;

	Check_Fpu();
}

//
//---------------------------------------------------------------------------
// This method generates a translation table for the secondary display,
// using a previously set palette and channel.
//
// Palettes are assumed to always have 256 colors.
//---------------------------------------------------------------------------
//
void
	L4GraphicsPort::BuildSecondaryTranslation()
{
	Verify((bitMask & 0xFF) != 0);

	BitWrangler
		wrangler(bitMask & 0xFF, 8);

	int
		*destination(translationTable);

	do
	{
		*destination++ = wrangler.Value;
	}
	while (wrangler.IncrementActive());
	Check_Fpu();
}


//
//---------------------------------------------------------------------------
// This method generates a translation table for the auxiliary displays.
//
// Palettes are assumed to always have 256 colors (0=black, 255=white).
//---------------------------------------------------------------------------
//
void
	L4GraphicsPort::BuildAuxiliaryTranslation()
{
	Verify(numberOfBits != 0);
	Verify(numberOfBits <= 8);

	int
		byte_mask((bitMask >> 8) & 0xFF);
	Verify(byte_mask != 0);

	BitWrangler
		wrangler(byte_mask, 8);
	int
		*destination(translationTable);
	int
		currentValue;

	//
	// Generate lookup table
	//
	do
	{
		currentValue = wrangler.Value << 8;

		do
		{
			*destination++ = currentValue;
		}
		while (wrangler.IncrementInactive());
	}
	while (wrangler.IncrementActive());

#	if defined(TESTPALETTE)
		std::cout << "L4GraphicsPort::BuildAuxiliaryTranslation\n";
		for(int i=0; i<256; ++i)
		{
			if ((i & 0x07) == 0)
			{
				std::cout << "\n" << std::dec << i << ":" << std::hex;
			}
			std::cout << translationTable[i] << " ";
		}
		std::cout << "\n";
#	endif
	Check_Fpu();
}
