#include <windows.h>
#include "host_def.h"
#include "insignia.h"
#include "vdm.h"

/*
 * ==========================================================================
 *	Name:		nt_det.c
 *	Author:		Jerry Sexton
 *	Derived From:
 *	Created On:	6th August 1992
 *	Purpose:	This module contains the code for the thread which
 *			detects transitions between windowed and full-screen.
 *
 *	(c)Copyright Insignia Solutions Ltd., 1992. All rights reserved.
 * ==========================================================================
 *
 * Modifications:
 *
 * Tim August 92. Full-screen and windowed transitions now switch between
 *     SoftPC video BIOS and host PC video BIOS.
 *
 */

/*
 * ==========================================================================
 * Other includes
 * ==========================================================================
 */
#include <stdlib.h>
#include <ntddvdeo.h>
#include "xt.h"
#include "cpu.h"
#include "gmi.h"
#include "gvi.h"
#include "ios.h"
#include "sas.h"
#include "gfx_upd.h"
#include "egacpu.h"
#include "egaports.h"
#include "egamode.h"
#include "egagraph.h"
#include "video.h"
#include "conapi.h"
#include "host_rrr.h"
#include "debug.h"
#include "error.h"
#include "config.h"
#include "idetect.h"
#include "nt_uis.h"
#include "nt_fulsc.h"
#include "nt_graph.h"
#include "nt_mouse.h"
#include "nt_thred.h"
#include "nt_reset.h"
#include "nt_eoi.h"
#include "nt_event.h"

/*
 * ==========================================================================
 * Macros
 * ==========================================================================
 */
#define SUSP_FAILURE            0xffffffff


//
// A bunch of imports
//
extern DISPLAY_MODE choose_mode[];



/*
 * ==========================================================================
 * Global data
 * ==========================================================================
 */

/* Size of video save block. */
GLOBAL DWORD stateLength;

/* Video save block pointer. */
GLOBAL PVIDEO_HARDWARE_STATE_HEADER videoState;
GLOBAL PVOID textState; // Tim Oct 92.

/* Name of the shared video block. */
GLOBAL WCHAR_STRING videoSection;
GLOBAL WCHAR_STRING textSection; // Tim Oct 92

/* Hand-shaking events. */
GLOBAL HANDLE hStartHardwareEvent;
GLOBAL HANDLE hEndHardwareEvent;

/* Flag to stop host_simulates on this thread executing timer tick code. */
GLOBAL BOOL NoTicks;

/*
** Tim Oct 92.
** New strategy for windowed graphics updates. A shared buffer with Console
** will remove need to copy the new data over, just pass a rectangle co-ord
** instead. But we still need to copy into the buffer.
*/
GLOBAL PBYTE *textBuffer;
GLOBAL COORD  textBufferSize;      // Dimensions of the shared buffer

GLOBAL BOOL Frozen256Packed = FALSE;  // use packed 256 mode paint routine




/*
 * ==========================================================================
 * Local data
 * ==========================================================================
 */

/* Variable that indicates if we are in a non-standard VGA mode. */
LOCAL BOOL inAFunnyMode = FALSE;
LOCAL BOOL ModeSetBatch = FALSE;

/* Storage for the frozen-window thread handle. */
LOCAL HANDLE freezeHandle = (HANDLE)0;



/*
 * ==========================================================================
 * Local function declarations
 * ==========================================================================
 */

#undef LOCAL
#define LOCAL

LOCAL VOID getCursorInfo(word *, half_word *, half_word *, half_word *);
LOCAL VOID setCursorInfo(word, half_word, half_word, half_word);
LOCAL VOID windowedToFullScreen(SHORT, BOOL);
LOCAL VOID fullScreenToWindowed(VOID);
LOCAL VOID syncHardwareToVGAEmulation(SHORT);
LOCAL VOID syncVGAEmulationToHardware(VOID);
LOCAL BOOL funnyMode(VOID);
LOCAL VOID freezeWindow(VOID);
#ifndef PROD
LOCAL VOID dumpBlock(VOID);
LOCAL VOID dumpPlanes(UTINY *, UTINY *, UTINY *, UTINY *);
#endif /* PROD */

/*
 * ==========================================================================
 * Global functions
 * ==========================================================================
 */

/*
** Tim Oct 92
** Centralised Console funx.
*/

GLOBAL VOID doNullRegister()
{
	DWORD dummylen;
        PVOID dummyptr;
        COORD dummycoord;

        if( !RegisterConsoleVDM( CONSOLE_UNREGISTER_VDM,
                                 NULL,
	                         NULL,
                                 NULL,
                                 0,
	                         &dummylen,
                                 &dummyptr,
	                         NULL,
	                         0,
	                         dummycoord,
                                 &dummyptr
	                       )
	  )
		ErrorExit();
#ifdef X86GFX
	sc.Registered = FALSE;
#endif

}

/*
*******************************************************************
** initTextSection()
*******************************************************************
** MIPS only: create a new section for windowed text.
** Tim Oct 92
*/
#ifndef MONITOR
GLOBAL VOID initTextSection(VOID)
{
    /*
     * Register start and end events with the console. These events are used
     * when gaining or losing control of the hardware.
     */
    hStartHardwareEvent = CreateEvent((LPSECURITY_ATTRIBUTES) NULL,
				      FALSE,
				      FALSE,
				      NULL);
    hEndHardwareEvent = CreateEvent((LPSECURITY_ATTRIBUTES) NULL,
				    FALSE,
				    FALSE,
				    NULL);
    if ((hStartHardwareEvent == NULL) || (hEndHardwareEvent == NULL))
	ErrorExit();
    /* Poll the event to try and get rid of any console queued sets 
     * This shouldn't be needed (or shouldn't work) but something along
     * those lines seems to be happening at the moment.
     */
    WaitForSingleObject(hStartHardwareEvent, 0);

    //
    // VideoSection size is determined by nt video driver
    // TextSectionSize is 80 * 50 * BytesPerCharacter
    //     on risc BytesPerCharacter is 4 (interleaved vga planes)
    //     on x86  BytesPerCharacter is 2 (only char\attr)
    //
    textBufferSize.X = 80;
    textBufferSize.Y = 50;

    if( !RegisterConsoleVDM( VDMForWOW ?
                                CONSOLE_REGISTER_WOW : CONSOLE_REGISTER_VDM,
                             hStartHardwareEvent,
	                     hEndHardwareEvent,
                             NULL,            // sectionname no longer used
                             0,               // sectionnamelen no longer used
	                     &stateLength,
                             (PVOID *) &videoState,
                             NULL,            // sectionname no longer used
                             0,               // sectionnamelen no longer used
	                     textBufferSize,
                             (PVOID *) &textBuffer
	                   )
    )
	ErrorExit();

} /* end initTextSection() */
#endif /* !MONITOR */

#ifdef X86GFX

#ifdef SEPARATE_DETECT_THREAD
/***************************************************************************
 * Function:								   *
 *	CreateDetectThread						   *
 *									   *
 * Description: 							   *
 *	Creates the detection thread.					   *
 *									   *
 * Parameters:								   *
 *	None.								   *
 *									   *
 * Return value:							   *
 *	VOID								   *
 *									   *
 ***************************************************************************/
GLOBAL VOID CreateDetectThread(VOID)
{
    DWORD	 detectID;
    HANDLE	 detectHandle;


    /*
     *  If this codes is activated you must close the thread handle
     *  28-Feb-1993 Jonle
     */


    /* Create the detection thread. */
    detectHandle = CreateThread((LPSECURITY_ATTRIBUTES) NULL,
				DETECT_THREAD_SIZE,
				(LPTHREAD_START_ROUTINE) InitDetect,
				(LPVOID) NULL,
				(DWORD) 0,
				&detectID);
    if (detectHandle == -1)
	ErrorExit();
}
#endif /* SEPARATE_DETECT_THREAD */

/***************************************************************************
 * Function:								   *
 *	InitDetect							   *
 *									   *
 * Description: 							   *
 *	Does detection initialisation.					   *
 *									   *
 * Parameters:								   *
 *	None.								   *
 *									   *
 * Return value:							   *
 *	VOID								   *
 *									   *
 ***************************************************************************/
GLOBAL VOID InitDetect(VOID)
{
    DWORD	 flags;

    /*
     * Check to see if we are currently running windowed or full-screen, using
     * GetConsoleDisplayMode as sc.ScreenState is not set up yet. We have to do
     * this early to avoid conflict with ALT-ENTER handling.
     */
    if (!GetConsoleDisplayMode(&flags))
	ErrorExit();

    /*
     * Register start and end events with the console. These events are used
     * when gaining or losing control of the hardware.
     */
    hStartHardwareEvent = CreateEvent((LPSECURITY_ATTRIBUTES) NULL,
				      FALSE,
				      FALSE,
				      NULL);
    hEndHardwareEvent = CreateEvent((LPSECURITY_ATTRIBUTES) NULL,
				    FALSE,
				    FALSE,
				    NULL);
    if ((hStartHardwareEvent == NULL) || (hEndHardwareEvent == NULL))
	ErrorExit();

    /* Poll the event to try and get rid of any console queued sets 
     * This shouldn't be needed (or shouldn't work) but something along
     * those lines seems to be happening at the moment.
     */
    WaitForSingleObject(hStartHardwareEvent, 0);

    //
    // VideoSection size is determined by nt video driver
    // TextSectionSize is 80 * 50 * BytesPerCharacter
    //     on risc BytesPerCharacter is 4 (interleaved vga planes)
    //     on x86  BytesPerCharacter is 2 (only char\attr)
    // consrv handles the char to byte conversion
    //
    //
    textBufferSize.X = 80;
    textBufferSize.Y = 50;

    /*
     * Deallocate the regen area if we start up fullscreen. We have to do this
     * before we call RegisterConsoleVDM. Note that's right before the register
     * call to make sure no one tries to allocate any memory (eg create a 
     * section) that could nick bits of the video hole, causing bye-byes.
     */
    if (flags & CONSOLE_FULLSCREEN_HARDWARE)
        LoseRegenMemory();

    if( !RegisterConsoleVDM( VDMForWOW ?
                                CONSOLE_REGISTER_WOW : CONSOLE_REGISTER_VDM,
                             hStartHardwareEvent,
	                     hEndHardwareEvent,
                             NULL,            // sectionname no longer used
                             0,               // sectionnamelen no longer used
	                     &stateLength,
                             (PVOID *) &videoState,
                             NULL,            // sectionname no longer used
                             0,               // sectionnamelen no longer used
	                     textBufferSize,
                             (PVOID *) &textBuffer
	                   )
    )
        ErrorExit();
    RtlZeroMemory((BYTE *)videoState, sizeof(VIDEO_HARDWARE_STATE_HEADER));
    sc.Registered = TRUE;
    /* End of windowed text section creation */

#ifdef SEPARATE_DETECT_THREAD
    /* Go into hand-shaking loop. */
    while (WaitForSingleObject(hStartHardwareEvent, (DWORD) -1) == 0)
	DoHandShake();

    /* We have exited the loop so something funny must have happened. */
    ErrorExit();
#endif /* SEPARATE_DETECT_THREAD */
}

/***************************************************************************
 * Function:								   *
 *	DoHandShake							   *
 *									   *
 * Description: 							   *
 *	Does the hand-shaking with the console server.			   *
 *									   *
 * Parameters:								   *
 *	None.								   *
 *									   *
 * Return value:							   *
 *	VOID								   *
 *									   *
 ***************************************************************************/
GLOBAL VOID DoHandShake(VOID)
{
    /*
     * ICA in's and out's cause a critical section to be entered. As some
     * video register out's do ICA out's, we don't want the main thread to be
     * suspended while doing ICA in's or out's as this will cause a race
     * when the the switching code starts writing to the relevant video
     * registers. So wait until the main thread is out of the critical section
     * before suspending it.
     */
    host_ica_lock();

    /*
     * To stop video memory and registers being updated while we are in the
     * middle of hand-shaking, suspend the main thread, which is the one that
     * does the updates.
     */
    if(SuspendThread(MainThread) == SUSP_FAILURE)
        ErrorExit();


    if(!VDMForWOW && (SuspendThread(ThreadInfo.EventMgr.Handle)==SUSP_FAILURE))
        ErrorExit();

    NoTicks = TRUE;
    /*
     * We have the event telling us to switch so if we are windowed go
     * full-screen or if we full-screen go windowed.
     */
    if (sc.ScreenState == FULLSCREEN)
    {
	fullScreenToWindowed();
    }
    else
    {
	windowedToFullScreen(TEXT, BiosModeChange);

    }

    /* Now resume the main thread. */
    NoTicks = FALSE;

    if(!VDMForWOW && ResumeThread(ThreadInfo.EventMgr.Handle) == SUSP_FAILURE)
        ErrorExit();
    if(ResumeThread(MainThread) == SUSP_FAILURE)
	ErrorExit();

    /* Leave ICA critical section. */
    host_ica_unlock();
}

/***************************************************************************
 * Function:								   *
 *	GetDetectEvent							   *
 *									   *
 * Description: 							   *
 *	Returns the handle to the event which detects a fullscreen switch  *
 *	has occurred.							   *
 *									   *
 * Parameters:								   *
 *	None.								   *
 *									   *
 * Return value:							   *
 *	hStartHardwareEvent						   *
 *									   *
 ***************************************************************************/
GLOBAL HANDLE GetDetectEvent(VOID)
{
    return(hStartHardwareEvent);
}

/*
 * ==========================================================================
 * Local functions
 * ==========================================================================
 */

/*
***************************************************************************
** getCursorInfo() - use BIOS funcs to get cursor position and other stuff
***************************************************************************
** The BIOS could be the SoftPC video BIOS or the host PC's real video BIOS.
** Cursor information needs to be communicated between the two BIOSes when
** a windowed/full-screen transition occurs.
** Tim July 92.
*/
LOCAL VOID getCursorInfo(word *type, half_word *column, half_word *row,
			 half_word *page)
{

    /* Get active page. */
    *page = sas_hw_at_no_check(vd_current_page);

    /* Get cursor position */
    *type = sas_w_at_no_check(VID_CURMOD);
    *column = sas_hw_at_no_check(current_cursor_col);
    *row = sas_hw_at_no_check(current_cursor_row);
}

/*
***************************************************************************
** setCursorInfo() - use BIOS funcs to set cursor position and other stuff
***************************************************************************
** The BIOS could be the SoftPC video BIOS or the host PC's real video BIOS.
** Cursor information needs to be communicated between the two BIOSes when
** a windowed/full-screen transition occurs.
** Tim July 92.
*/
LOCAL VOID setCursorInfo(word type, half_word column, half_word row, half_word page)
{

    /* Set active page. */
    sas_store_no_check(vd_current_page, page);

    /* Set cursor position. */
    sas_storew_no_check(VID_CURMOD, type);
    sas_store_no_check(current_cursor_col, column);
    sas_store_no_check(current_cursor_row, row);
}

/***************************************************************************
 * Function:								   *
 *	windowedToFullScreen						   *
 *									   *
 * Description: 							   *
 *	Called when the user or SoftPC requests that the console goes	   *
 *	fullscreen. It disables screen updates, synchronises the hardware  *
 *	to SoftPC's video planes and signals the console when it is	   *
 *	finished.							   *
 *									   *
 * Parameters:								   *
 *	dataType - the type of data stored in the video planes, set to     *
 *		   either TEXT or GRAPHICS.                                *
 *	biosModeChange - TRUE means call host BIOS to do mode change.	   *
 *									   *
 * Return value:							   *
 *	VOID								   *
 *									   *
 ***************************************************************************/
LOCAL VOID windowedToFullScreen(SHORT dataType, BOOL biosModeChange)
{
    word cursorType;
    half_word cursorCol, cursorRow, activePage;

    /* Disable the Idling system when Fullscreen as we cannot detect video
     * updates and thus would always idle.
     */
    IDLE_ctl(FALSE);

    /* Pass the current state of our VGA emulation to the hardware. */
    syncHardwareToVGAEmulation(dataType);

    /*
    ** A variable in K.SYS decides whether
    ** to call the host INT 10, or do a video BOP.
    ** Set the variable directly and subsequent INT 10's go to host
    ** video BIOS.
    */
    sas_store_no_check((int10_seg << 4) + useHostInt10, FULLSCREEN);

    /*
    ** Tim August 92. Transfer to host video BIOS.
    */
    getCursorInfo(&cursorType, &cursorCol, &cursorRow, &activePage);

    setCursorInfo(cursorType, cursorCol, cursorRow, activePage);

    /*
     * We only want to call the host bios to do a mode change if the current
     * screen switch is due to a bios mode change.
     */
    if (biosModeChange)
    {
	always_trace1("Host BIOS mode change to mode %x.",
		      sas_hw_at_no_check(vd_video_mode));

	/*
	** Tim August 92. Transfer to host video BIOS.
	*/
	getCursorInfo(&cursorType, &cursorCol, &cursorRow, &activePage);

	setCursorInfo(cursorType, cursorCol, cursorRow, activePage);
    }
}

/***************************************************************************
 * Function:								   *
 *	syncHardwareToVGAEmulation					   *
 *									   *
 * Description: 							   *
 *	Copies the contents of SoftPC's video registers and regen buffer   *
 *	to the real hardware on a transition to full-screen.		   *
 *									   *
 * Parameters:								   *
 *	dataType - the type of data stored in the video planes, set to     *
 *		   either TEXT or GRAPHICS.                                *
 *									   *
 * Return value:							   *
 *	VOID								   *
 *									   *
 ***************************************************************************/
LOCAL VOID syncHardwareToVGAEmulation(SHORT dataType)
{
    ULONG    memLoc;
    UTINY   *regPtr,
            *egaPlanePtr,
	    *regenptr,
	    *fontptr,
	    *plane1Ptr,
	    *plane2Ptr,
	    *plane3Ptr,
	    *plane4Ptr;
    half_word dummy,
	     acModeControl,
	     acIndex,
	     index,
	     value,
	     rgb;
    USHORT   dacIndex;
    BOOL     monoMode;
    VIDEO_HARDWARE_STATE stateChange;
    DWORD bitmapLen = sizeof(VIDEO_HARDWARE_STATE);
    DWORD timo;

   /* If we timed out during switch (stress!!), the videoState buffer will
    * be removed by console. Check for this before accessing structure and
    * take error path down to rest of handshake which will time out and report
    * error cleanly.
    */
    try {
        videoState->ExtendedSequencerOffset = 0;
    } except(EXCEPTION_EXECUTE_HANDLER)
      {
          assert0(NO, "NTVDM:VideoState has valid pointer, but no memory at that address");
          goto syncHandshake;
      }
    /*
    ** If it's a text mode
    ** zero the extended fields in the shared saved/restore structure.
    ** Kipper, Tim Nov 92.
    */

    /* initialize the video state header if we haven't done it yet.
       if it is initialized, leave it alone.
    */
    if (videoState->Length == 0) {
	videoState->Length = STATELENGTH;
	videoState->BasicSequencerOffset = BASICSEQUENCEROFFSET;
	videoState->BasicCrtContOffset = BASICCRTCONTOFFSET;
	videoState->BasicGraphContOffset = BASICGRAPHCONTOFFSET;
	videoState->BasicAttribContOffset = BASICATTRIBCONTOFFSET;
	videoState->BasicDacOffset = BASICDACOFFSET;
	videoState->BasicLatchesOffset = BASICLATCHESOFFSET;
	videoState->PlaneLength = PLANELENGTH;
	videoState->Plane1Offset = PLANE1OFFSET;
	videoState->Plane2Offset = PLANE2OFFSET;
	videoState->Plane3Offset = PLANE3OFFSET;
	videoState->Plane4Offset = PLANE4OFFSET;
    }
    /* Save the current state of the attribute controller index register. */
    inb(EGA_AC_INDEX_DATA, &acIndex);

    /* Enable palette */
    acIndex |= 0x20;

    /*
     * Find out if we are running in mono mode as CRTC registers are different
     * if we are.
     */
    inb(EGA_IPSTAT1_REG, &dummy);
    outb(EGA_AC_INDEX_DATA, AC_MODE_CONTROL_REG);
    inb(EGA_AC_SECRET, &acModeControl);
    monoMode = acModeControl & DISPLAY_TYPE;

    /* Restore the state of the attribute controller index register. */
    inb(EGA_IPSTAT1_REG, &dummy);
    outb(EGA_AC_INDEX_DATA, acIndex);

    /*
     * Store values to be written to each of the real registers to synchronise
     * them to the current state of the registers in the VDD.
     */
    if (monoMode)
    {
    	/* Port 0x3b4 */
    	inb(0x3b4, (half_word *)&videoState->PortValue[0x4]);
    	/* Port 0x3b5 */
    	inb(0x3b5, (half_word *)&videoState->PortValue[0x5]);
    }

    /* Port 0x3c0 */
    videoState->PortValue[0x10] = acIndex;

    /* Port 0x3c1 */
    inb(EGA_AC_SECRET, (half_word *)&videoState->PortValue[0x11]);

    /* Port 0x3c2 */
    inb(VGA_MISC_READ_REG, (half_word *)&videoState->PortValue[0x12]);

    videoState->PortValue[0x13] = 0xff; /* Testing */

    /* Port 0x3c4 */
    inb(EGA_SEQ_INDEX, (half_word *)&videoState->PortValue[0x14]);

    /* Port 0x3c5 */
    inb(EGA_SEQ_DATA, (half_word *)&videoState->PortValue[0x15]);

    /* Port 0x3c6 */
    inb(VGA_DAC_MASK, (half_word *)&videoState->PortValue[0x16]);

    /* Port 0x3c7 */
    videoState->PortValue[0x17] = get_vga_DAC_rd_addr();

    /* Port 0x3c8 */
    inb(VGA_DAC_WADDR, (half_word *)&videoState->PortValue[0x18]);

    /* Port 0x3c9 */
    inb(VGA_DAC_DATA, (half_word *)&videoState->PortValue[0x19]);

    /* Port 0x3ce */
    inb(EGA_GC_INDEX, (half_word *)&videoState->PortValue[0x1e]);

    /* Port 0x3cf */
    inb(EGA_GC_DATA, (half_word *)&videoState->PortValue[0x1f]);

    if (!monoMode)
    {
    	/* Port 0x3d4 */
    	inb(EGA_CRTC_INDEX, (half_word *)&videoState->PortValue[0x24]);
    	/* Port 0x3d5 */
    	inb(EGA_CRTC_DATA, (half_word *)&videoState->PortValue[0x25]);
    }

    /* Port 0x3da */
    inb(VGA_FEAT_READ_REG, (half_word *)&videoState->PortValue[0x2a]);

    /* Store INDEX/DATA etc. register pairs. */

    /* Initialise `regPtr'. */
    regPtr =  GET_OFFSET(BasicSequencerOffset);

    /* Sequencer registers. */
    for (index = 0; index < NUM_SEQ_REGS; index++)
    {
	outb(EGA_SEQ_INDEX, index);
	inb(EGA_SEQ_DATA, &value);
        *regPtr++ = value;
    }

    /* CRTC registers. */
    regPtr = GET_OFFSET(BasicCrtContOffset);
    for (index = 0; index < NUM_CRTC_REGS; index++)
    {
	outb(EGA_CRTC_INDEX, index);
	inb(EGA_CRTC_DATA, &value);
        *regPtr++ = value;
    }

    /* Graphics controller registers. */
    regPtr = GET_OFFSET(BasicGraphContOffset);
    for (index = 0; index < NUM_GC_REGS; index++)
    {
	outb(EGA_GC_INDEX, index);
	inb(EGA_GC_DATA, &value);
        *regPtr++ = value;
    }

    /* Attribute controller registers. */
    regPtr = GET_OFFSET(BasicAttribContOffset);
    for (index = 0; index < NUM_AC_REGS; index++)
    {
	inb(EGA_IPSTAT1_REG, &dummy);	/* Reading 3DA sets 3C0 to index. */
	outb(EGA_AC_INDEX_DATA, index); /* Writing to 3C0 sets it to data. */
	inb(EGA_AC_SECRET, &value);
        *regPtr++ = value;
    }
    inb(EGA_IPSTAT1_REG, &dummy);	// re-enable video...
    outb(EGA_AC_INDEX_DATA, 0x20);

    /* DAC registers. */
    regPtr = GET_OFFSET(BasicDacOffset);
    outb(VGA_DAC_RADDR, (UTINY) 0);
    for (dacIndex = 0; dacIndex < NUM_DAC_REGS; dacIndex++)
    {

	/* Get 3 values for each port corresponding to red, green and blue. */
	for (rgb = 0; rgb < 3; rgb++)
	{
	    inb(VGA_DAC_DATA, &value);
	    *regPtr++ = value;
	}
    }

    /* Latches (which we always set to 0) */
    regPtr = GET_OFFSET(BasicLatchesOffset);
    *regPtr++ = 0;
    *regPtr++ = 0;
    *regPtr++ = 0;
    *regPtr++ = 0;

    if (!BiosModeChange) {
    /* if this windowed->fullscreen switch was because of video mode change
       do not change anything in the code buffer and the font because
       the ROM bios set mode will clear them anyway. If "not clear VRAM"
       bit was set(int 10h, ah = mode | 0x80), the application will take care
       the VRAM refreshing and restoring because if it doesn't the screen
       would look funnny as we just swtch mode from TEXT to GRAPHICS and the
       video planar chaining conditions are changed.
    */
	/* set up pointer to regen memory where the real data lies */
	regenptr = (UTINY *)0xb8000;

	/* and one to the fonts living in the base of the regen area */
	fontptr = (UTINY *)0xa0000;

	plane1Ptr = GET_OFFSET(Plane1Offset);
	plane2Ptr = GET_OFFSET(Plane2Offset);
	plane3Ptr = GET_OFFSET(Plane3Offset);
	plane4Ptr = GET_OFFSET(Plane4Offset);


// if we go to fullscreen graphics from text window then the regen contents
// is probably junk??? except when previous save... We can detect this 
// transition, so should we save time and just store blank planes???

	if (dataType == TEXT)
	{
		// Surprise of the week - the individual planes 0 & 1 actually appear
		// to be interleaved with 0's when dumped. Go with this for now, until
		// we can suss if that's correct or whether we're not programming up
		// the save and restore states properly.
		// Probably good on further thoughts as fontplane doesn't show same
		// interleave.
		//
		for (memLoc = 0; memLoc < (0xc0000 - 0xb8000); memLoc++)
		{
		    *plane1Ptr++ = *regenptr++;
		    *plane1Ptr++ = 0;		//char interleave
		    *plane2Ptr++ = *regenptr++;
		    *plane2Ptr++ = 0;		//attr interleave
		}
		for (memLoc = 0; memLoc < 0x4000; memLoc++)
		{
		    *plane3Ptr++ = *fontptr++;
		    *plane3Ptr++ = *fontptr++;
		    *plane3Ptr++ = *fontptr++;
		    *plane3Ptr++ = *fontptr++;
		}
	}
	else	//only true if restoring previous fullscreen graphics save
	{
	/*
	 * Get a copy of the video planes which are inter-leaved in one big
	 * plane - byte 0 = plane 0, byte 1 = plane 1, byte 2 = plane 2,
	 * byte 3 = plane 3, byte 4 = plane 0, etc.
	 */
		/* Set up a pointer to the video planes. */
		egaPlanePtr = EGA_planes;

		for (memLoc = 0; memLoc < videoState->PlaneLength; memLoc++)
		{
		    *plane1Ptr++ = *egaPlanePtr++;
		    *plane2Ptr++ = *egaPlanePtr++;
		    *plane3Ptr++ = *egaPlanePtr++;
		    *plane4Ptr++ = *egaPlanePtr++;
		}
	}
    }

    /* Now pass the data on to the hardware via the console. */
    stateChange.StateHeader = videoState;
    stateChange.StateLength = videoState->Plane4Offset +
						   videoState->PlaneLength;

#ifndef PROD
    dumpBlock();
#endif

   /* Transfer to this label only occurs if console has removed videostate */
syncHandshake:

    // do this here to ensure no surprises if get conflict with timer stuff
    sc.ScreenState = FULLSCREEN;

    /* make room for the real video memory */
    LoseRegenMemory();

    if (!SetEvent(hEndHardwareEvent))	// tell console memory's gone
	ErrorExit();

    // wait for console to tell us we can go on. Timeout after 60s
    timo = WaitForSingleObject(hStartHardwareEvent, 60000);

    if (timo != 0)		// 0 is 'signalled'
    {
#ifndef PROD
	if (timo == WAIT_TIMEOUT)
	    printf("NTVDM:Waiting for console to map frame buffer Timed Out\n");
#endif
        SetLastError(ERROR_SERVICE_REQUEST_TIMEOUT);
	ErrorExit();
    }
    // tell console it can go on.
    if (!SetEvent(hEndHardwareEvent))
	ErrorExit();

}

/***************************************************************************
 * Function:								   *
 *	fullScreenToWindowed						   *
 *									   *
 * Description: 							   *
 *	When hStartHardwareEvent is detected by the timer thread the user  *
 *	wants to go windowed. This function is then called to get the	   *
 *	current state of the hardware and send it to the VGA emulation.    *
 *									   *
 * Parameters:								   *
 *	None.								   *
 *									   *
 * Return value:							   *
 *	VOID								   *
 *									   *
 ***************************************************************************/

int BlockModeChange=0; /* Tim, when set stop nt_set_paint_routine() calling */
                       /* TextToGraphics() */

LOCAL VOID fullScreenToWindowed(VOID)
{

    BlockModeChange = 1; /* Temp. disable TextToGraphics calls in the */
                         /* following syncVGA... cos it chucks display */
                         /* back into full-screen */

    /* Pass the current state of the hardware to our VGA emulation. */
    syncVGAEmulationToHardware();

    /*
    ** Tim August 92. Switch to SoftPC video BIOS.
    */
    BlockModeChange = 0; /* Temp. disable cos it don't work! */

    /*
    ** Set the K.SYS variable which determines whether to use the host
    ** video BIOS or do a video BOP. Writing zero means use SoftPC BIOS.
    */
    sas_store_no_check((int10_seg << 4) + useHostInt10, (half_word)sc.ScreenState);

     /* Enable the Idling system when return to Windowed */
     /*	Only do the following stuff if we are really in windowed mode.
	this can happen: (fullscreen ->windowed(frozen) -> fullscreen) */
    if (sc.ScreenState != FULLSCREEN)  {
	/*
	 ** Force re-paint of windowed image.
	 */
	RtlFillMemory(&video_copy[0], 0x7fff, 0xff);

        IDLE_ctl(TRUE);
        IDLE_init();        /* and reset triggers */

	/*
	 * Clear the old pointer box that has been left befind from
	 * fullscreen
	 */

	 CleanUpMousePointer();

	 resetNowCur();	/* reset static vars holding cursor pos. */
    }
}	/* end of fullScreenToWindowed() */

/***************************************************************************
 * Function:								   *
 *	syncVGAEmulationToHardware					   *
 *									   *
 * Description: 							   *
 *	Copies the real hardware state to SoftPC's video registers and	   *
 *	regen buffer on a transition from full-screen to windowed,	   *
 *	freezing if we are currently running in a graphics mode.	   *
 *									   *
 * Parameters:								   *
 *	None.								   *
 *									   *
 * Return value:							   *
 *	VOID								   *
 *									   *
 ***************************************************************************/
LOCAL VOID syncVGAEmulationToHardware(VOID)
{
    ULONG    memLoc,
	     StateFlags;
    UTINY   *regPtr,
	    *plane1Ptr,
	    *plane2Ptr,
	    *plane3Ptr,
	    *plane4Ptr,
	    *RegenPtr,
	     index,
	     dummy,
	     rgb;
    USHORT   dacIndex;
    DWORD bitmapLen = 0, timo;
    BOOL changing_to_graphics_mode = FALSE;

    /* Tell console we've got the hardware state. */
    if (!SetEvent(hEndHardwareEvent))
	ErrorExit();

    /* Wait for console to unmap memory. */
    timo = WaitForSingleObject(hStartHardwareEvent, 60000);

    if (timo != 0)		/* 0 is 'signalled' */
    {
#ifndef PROD
	if (timo == WAIT_TIMEOUT)
	    printf("NTVDM:Waiting for console to unmap frame buffer Timed Out\n");
#endif
        SetLastError(ERROR_SERVICE_REQUEST_TIMEOUT);
	ErrorExit();
    }

    /* Put some memory back into the regen area. */
    RegainRegenMemory();

    /* used to free console here - now must wait as may need to do gfx first */

    /* Store sequencer values */
    regPtr = GET_OFFSET(BasicSequencerOffset);
    for (index = 0; index < NUM_SEQ_REGS; index++)
    {
	outb(EGA_SEQ_INDEX, index);
	outb(EGA_SEQ_DATA, *regPtr++);
    }

    /* disable CRTC port locking */
    outb(EGA_CRTC_INDEX, 0x11);
    outb(EGA_CRTC_DATA, 0);

    /* Store CRTC values */
    regPtr = GET_OFFSET(BasicCrtContOffset);
    for (index = 0; index < NUM_CRTC_REGS; index++)
    {
	outb(EGA_CRTC_INDEX, index);
	outb(EGA_CRTC_DATA, *regPtr++);
    }


    /* Store graphics context values */
    regPtr = GET_OFFSET(BasicGraphContOffset);
    for (index = 0; index < NUM_GC_REGS; index++)
    {
	outb(EGA_GC_INDEX, index);
	outb(EGA_GC_DATA, *regPtr++);
    }


    /* Store attribute context values */
    regPtr = GET_OFFSET(BasicAttribContOffset);
    inb(EGA_IPSTAT1_REG, &dummy);	/* Reading 3DA sets 3C0 to index. */
    for (index = 0; index < NUM_AC_REGS; index++)
    {
	outb(EGA_AC_INDEX_DATA, index);
	outb(EGA_AC_INDEX_DATA, *regPtr++);
    }


    /* Store DAC values. */
    regPtr = GET_OFFSET(BasicDacOffset);
    outb(VGA_DAC_WADDR, (UTINY) 0);
    for (dacIndex = 0; dacIndex < NUM_DAC_REGS; dacIndex++)
    {
        for (rgb = 0; rgb < 3; rgb++)
	    outb(VGA_DAC_DATA, *regPtr++);
    }


    /* Store single value registers. */
    outb( (io_addr)0x3b4, (half_word)videoState->PortValue[0x3b4 - 0x3b0]); //Mono crtc ind
    outb( (io_addr)0x3ba, (half_word)videoState->PortValue[0x3ba - 0x3b0]); //Mono Feat
    outb( (io_addr)0x3c2, (half_word)videoState->PortValue[0x3c2 - 0x3b0]); //Misc Output
    outb( (io_addr)0x3c4, (half_word)videoState->PortValue[0x3c4 - 0x3b0]); //Seq Index
    outb( (io_addr)0x3c6, (half_word)videoState->PortValue[0x3c6 - 0x3b0]); //DAC mask
    outb( (io_addr)0x3c7, (half_word)videoState->PortValue[0x3c7 - 0x3b0]); //DAC read
    outb( (io_addr)0x3c8, (half_word)videoState->PortValue[0x3c8 - 0x3b0]); //DAC write
    outb( (io_addr)0x3ce, (half_word)videoState->PortValue[0x3ce - 0x3b0]); //GC Index
    outb( (io_addr)0x3d4, (half_word)videoState->PortValue[0x3d4 - 0x3b0]); //CRTC index

    /* Set up pointers to the planes in the video save block. */
    plane1Ptr = GET_OFFSET(Plane1Offset);
    plane2Ptr = GET_OFFSET(Plane2Offset);
    plane3Ptr = GET_OFFSET(Plane3Offset);
    plane4Ptr = GET_OFFSET(Plane4Offset);

#ifndef PROD
    dumpPlanes(plane1Ptr, plane2Ptr, plane3Ptr, plane4Ptr);
#endif /* PROD */

    /*
     * Here is where we need to start making decisions about what mode the above
     * has put us into as it effects what we do with the plane data - into regen
     * or into ega planes.
     */
     StateFlags = videoState->VGAStateFlags;

     ModeSetBatch = FALSE;

   /*
    * This actually indicates that the save/restore included all extended
    * registers which increases the chances of a mode not being what it
    * appears to be from the VGA registers. We need to tighten up the 'funny
    * mode' detection. (But not now - too much chance of things).
    *
    *  if (StateFlags & VIDEO_STATE_NON_STANDARD_VGA)
    *  {
    *      always_trace0("NTVDM:Non standard VGA - freeze state \n");
    *      ModeSetBatch = TRUE;
    *  }
    */

     if (StateFlags & VIDEO_STATE_UNEMULATED_VGA_STATE)
     {
	always_trace0("NTVDM:Unemulated VGA State - freeze\n");
	ModeSetBatch = TRUE;
     }

     if (StateFlags & VIDEO_STATE_PACKED_CHAIN4_MODE)
     {
        always_trace0("NTVDM:will need packed 256 colour paint\n");
        Frozen256Packed = TRUE;
     }
     else
        Frozen256Packed = FALSE;

    
  if (!ModeSetBatch)
  {
    (*choose_display_mode)();
    /* screen switching can happen when the BIOS is in the middle
       of set mode. The video driver only batches the protected registers(we
       will get VIDEO_STATE_UNEMULATED_VGA_STATE, which will set ModeSetBatch).
       When we are out of set mode batch and a screen switch happens,
       the choose_display_mode would choose a wrong mode(different what the
       the bios says) and the parameters setup in base code could be wrong
       (we calculate those parameters as it is in TEXT mode while we are in
       graphic mode.

       For example, the base code calculate the screen length as:

       screen length = offset_per_line * screen_height_resolution / font_height

       if the bios video mode is graphic mode 4(320 * 200), then
       font_height = 2
       screen_height_resolution = 200
       offset_per_line = 80
       the screen_lenght = 80 * 200 / 2 = 8000 bytes which means
       the screen has 8000 / 80 = 100 lines!!!!

       Treat it like we are in mode set batch process, so we go to iconized.
    */
    if (sc.ModeType == getModeType()) {

	/* Write data to video planes if we are in a graphics mode. */
	if (sc.ModeType == GRAPHICS)
	{

	/*
	 * 'choose_display_mode' calls 'host_set_paint_routine' and in so doing
	 * sets up 'sc.ModeType'. We now know we have a graphics mode so
	 * select the frozen paint routines and call 'host_set_paint_routine'
	 * again to get the required frozen routine.
	 */
	    select_frozen_routines();
	    host_set_paint_routine(choose_mode[get_munged_index()],
				       get_screen_height());
	}
	else /* TEXT */
	{
	/* Double check not race on graphics mode change */

	    if (sas_hw_at((int10_seg << 4) + changing_mode_flag) == 1)
	    {
		/* In middle of mode change - may actually be graphics any second */
		if ((sas_hw_at(vd_video_mode) > 3) && (sas_hw_at(vd_video_mode) != 7))
			changing_to_graphics_mode = TRUE;
	    }
	/* Now copy the data to the regen buffer. */
	    RegenPtr = (UTINY *)0xb8000;
	    for(memLoc = 0; memLoc < 0x4000; memLoc++) /* 16k of text data. */
	    {
		*RegenPtr++ = *plane1Ptr++;		/* char */
		plane1Ptr++;			/* skip interleave */
		*RegenPtr++ = *plane2Ptr++;		/* attr */
		plane2Ptr++;			/* skip interleave */
	    }

	/* Now the font. */
	    RegenPtr = (UTINY *)0xa0000;
	    for(memLoc = 0; memLoc < 0x4000; memLoc++) /* Up to 64k of font data. */
	    {
		*RegenPtr++ = *plane3Ptr++;
		*RegenPtr++ = *plane3Ptr++;
		*RegenPtr++ = *plane3Ptr++;
		*RegenPtr++ = *plane3Ptr++;
	    }
	}

	/* Re-enable vga attribute palette. */
	inb(EGA_IPSTAT1_REG, &dummy);	/* Reading 3DA sets 3C0 to index. */
	outb(EGA_AC_INDEX_DATA, 0x20);
    }
    else {
	ModeSetBatch = TRUE;
#ifndef PROD
	OutputDebugString("fullscreen->windowed switching in set mode\n");
#endif

    }

  }	/* modesetbatch */
    /*
     * If the state returned by the hardware is one we don't recognise iconify
     * the window. If, however, the hardware returns a graphics mode, the
     * current image will be displayed. In both cases the app will be frozen
     * until the user changes back to fullscreen.
     */
    if (ModeSetBatch || (inAFunnyMode = funnyMode()) || (sc.ModeType == GRAPHICS) || changing_to_graphics_mode)
    {

#ifndef PROD
	dumpBlock();
#endif /* PROD */

        /* if in middle of mode change, don't want to draw screen */
        if (changing_to_graphics_mode)
            inAFunnyMode = TRUE;

	/* Must do this before resize function. */
	sc.ScreenState = WINDOWED;

        /* Once we've done this, the VGA emulation is pushed into a graphics
         * mode. If we restart windowed, we must ensure it forces itself
         * back to a text mode for correct display & so correct screen buffer
         * is active. This will be cancelled if we return to a text window.
         */
        blocked_in_gfx_mode = TRUE;

        /*
         * freezewindow used to run in its own thread. Unfortunately, due to
         * console sync problems with video restore on XGA, this did unpleasant
         * things to the screen. Thus now this is has become a valid and *Only*
         * place in fullscreen switching where console permits us to make
         * console API calls.
         * I'm sorry, did you say 'Quack', Oh no, I see...
         */

        freezeWindow();

        /* Tell console we're done. */
        if (!SetEvent(hEndHardwareEvent))
	    ErrorExit();

	/* We block here until user switches us fullscreen again. */
	WaitForSingleObject(hStartHardwareEvent, INFINITE);

	/* Prevent updates which would cause hang. */
	sc.ScreenState = FULLSCREEN;

        savedScreenState = WINDOWED;   /* won't have been changed by timer fn */

        inAFunnyMode = TRUE;

	/* Put video section back as passed to us as we have not changed it. */
	LoseRegenMemory();

	/* Tell console memory's gone. */
	if (!SetEvent(hEndHardwareEvent))
	    ErrorExit();

	/* Wait for console to tell us we can go on. Timeout after 60s */
	timo = WaitForSingleObject(hStartHardwareEvent, 60000);

	if (timo != 0)		/* 0 is 'signalled' */
	{
#ifndef PROD
	    if (timo == WAIT_TIMEOUT)
		printf("NTVDM:Waiting for console to map frame buffer Timed Out\n");
#endif
            SetLastError(ERROR_SERVICE_REQUEST_TIMEOUT);
	    ErrorExit();
	}

        Frozen256Packed = FALSE;

        sas_connect_memory(0xb8000, 0xbffff, SAS_VIDEO);
	// tell console server it can go on
	if (!SetEvent(hEndHardwareEvent))
	    ErrorExit();
    }
    else /* TEXT */
    {
        /* Tell console we're done. */
        if (!SetEvent(hEndHardwareEvent))
	    ErrorExit();

	/* Set up screen-state variable. */
	sc.ScreenState = WINDOWED;

        blocked_in_gfx_mode = FALSE;   /* save restart mode switch */
#ifndef PROD
	/* Dump out a view of the state block as it might be useful. */
	dumpBlock();
#endif /* PROD */
    }

    do_new_cursor();    /* sync emulation about cursor state */
}

/***************************************************************************
 * Function:								   *
 *	funnyMode							   *
 *									   *
 * Description: 							   *
 *	Detects whether the state of the video hardware returned when	   *
 *	switching from fullscreen is one that our VGA emulation		   *
 *	understands.							   *
 *									   *
 * Parameters:								   *
 *	None.								   *
 *									   *
 * Return value:							   *
 *	TRUE if it is a funny state, otherwise FALSE.			   *
 *									   *
 ***************************************************************************/
LOCAL BOOL funnyMode(VOID)
{

    /*
     * If the screen is of a higher resolution than 640 x 480 we have a
     * non-standard VGA mode.
     */
    if ((get_bytes_per_line() > 80) || (get_screen_height() > 480)){
	return( FALSE ); /* Tim, don't like it, see what happens other way! */
	/* return(TRUE); */
    }

    /*
     * If 'nt_set_paint_routine' was called with 'mode' set to one of the
     * "funny" values e.g. TEXT_40_FUN we assume that the mode the hardware
     * is currently in is not compatible with the VGA emulation.
     */
    if (FunnyPaintMode){
	return(TRUE);
    }

    /* We have a standard VGA mode. */
    return(FALSE);
}

/***************************************************************************
 * Function:								   *
 *	freezeWindow							   *
 *									   *
 * Description: 							   *
 *	This function is the entry point for the temporary thread which	   *
 *	does console calls when the main thread is frozen on a fullscreen  *
 *	to windowed transition.						   *
 *									   *
 * Parameters:								   *
 *	None.								   *
 *									   *
 * Return value:							   *
 *	VOID								   *
 *									   *
 ***************************************************************************/
LOCAL VOID freezeWindow(VOID)
{

    DWORD Dummy;

    /* Add -FROZEN to the window title. */
    FreezeWinTitle();

    /* Turn off any active sounds (eg flight simulator engine noise) */
    LazyBeep(0, 0);

    /* Iconify if we are in a funny mode, otherwise paint the screen. */
    if (ModeSetBatch || inAFunnyMode)
	VDMConsoleOperation(VDM_HIDE_WINDOW, &Dummy);
    else
    {


	/* Set the screen size. */
	graphicsResize();

        //
        // Remove the Hide Mouse Pointer message from the
        // system menu so the user cannot apply this option
        // the screen is frozen.
        // Andy!

        MouseDetachMenuItem(TRUE);

	/*
 	 * Set up the palette as DAC registers may have changed and we
 	 * won't get any more timer ticks after this one until we
 	 * unfreeze (the palette is not set up until 2 timer ticks after
	 * 'choose_display_mode' has been called).
 	 */
	set_the_vlt();

	/*
	 * Full window graphics paint - relies on paint routines to check
	 * for memory overflow.
	 */
	VGLOBS->dirty_flag = (ULONG) 0xffffffff;
	(*update_alg.calc_update)();
    }
    /* Unblock frozen-window thread creation. */
    freezeHandle = 0;
}

#ifndef PROD

/***************************************************************************
 * Function:								   *
 *	dumpBlock							   *
 *									   *
 * Description: 							   *
 *	Dumps the contents of the video state block.			   *
 *									   *
 * Parameters:								   *
 *	None.								   *
 *									   *
 * Return value:							   *
 *	VOID								   *
 *									   *
 ***************************************************************************/
int dumpit = 0;
LOCAL VOID dumpBlock(VOID)
{
    USHORT i,
	   dacIndex;
    UTINY *regPtr,
	   index,
	   rgb;

    if (dumpit == 0) return;

    /* Dump out single value registers. */
    printf("\nSingle value registers:\n");
    for (i = 0; i < 0x30; i++)
	printf("\tPort %#x = %#x\n", i, videoState->PortValue[i]);

    /* Dump sequencer values */
    regPtr = GET_OFFSET(BasicSequencerOffset);
    printf("Sequencer registers: (addr %#x)\n",regPtr);
    for (index = 0; index < NUM_SEQ_REGS; index++)
    {
	printf(" %#x = %#x\t", index, *regPtr++);
    }
    printf("\n");

    /* Dump CRTC values */
    regPtr = GET_OFFSET(BasicCrtContOffset);
    printf("CRTC registers: (addr %#x)\n",regPtr);
    for (index = 0; index < NUM_CRTC_REGS; index++)
    {
	printf(" %#x = %#x\t", index, *regPtr++);
    }
    printf("\n");

    /* Dump graphics context values */
    regPtr = GET_OFFSET(BasicGraphContOffset);
    printf("Graphics context registers: (addr %#x)\n",regPtr);
    for (index = 0; index < NUM_GC_REGS; index++)
    {
	printf(" %#x = %#x\t", index, *regPtr++);
    }
    printf("\n");

    /* Dump attribute context values */
    regPtr = GET_OFFSET(BasicAttribContOffset);
    printf("Attribute context registers: (addr %#x)\n",regPtr);
    for (index = 0; index < NUM_AC_REGS; index++)
    {
	printf(" %#x = %#x\t", index, *regPtr++);
    }
    printf("\n");

    /* Dump DACs. First few only otherwise too slow & console times out! */
    regPtr = GET_OFFSET(BasicDacOffset);
    printf("DAC registers:\n");
    for (dacIndex = 0; dacIndex < NUM_DAC_REGS/8; dacIndex++)
    {
	printf("Ind:%#02x:  ", dacIndex);
	for (rgb = 0; rgb < 3; rgb++)
	{
	    printf("R:%#02x G:%#02x B:%#02x\t", *regPtr++, *regPtr++, *regPtr++);
	}
	if ((dacIndex % 4) == 0) printf("\n");
    }
}

int doPlaneDump = 0;
LOCAL VOID dumpPlanes(UTINY *plane1Ptr, UTINY *plane2Ptr, UTINY *plane3Ptr,
		      UTINY *plane4Ptr)
{
    HANDLE	outFile;
    char	planeBuffer[256],
		*bufptr;
    DWORD	i,
		j,
		k,
		plane,
		nBytes,
		bytesWritten;
    UTINY	*planes[4];
    FAST UTINY	*tempPlanePtr;

    if (doPlaneDump)
    {

	/* Dump out plane(s). */
	outFile = CreateFile("PLANE",
			     GENERIC_WRITE,
			     (DWORD) 0,
			     (LPSECURITY_ATTRIBUTES) NULL,
			     CREATE_ALWAYS,
			     FILE_ATTRIBUTE_NORMAL,
			     (HANDLE) NULL);
	if (outFile == INVALID_HANDLE_VALUE)
	    ErrorExit();
	planes[0] = plane1Ptr;
	planes[1] = plane2Ptr;
	planes[2] = plane3Ptr;
	planes[3] = plane4Ptr;
	for (plane = 0; plane < 4; plane++)
	{
	    tempPlanePtr = planes[plane];
	    sprintf(planeBuffer, "Plane %d\n", plane);
	    strcat(planeBuffer, "-------\n");
	    if (!WriteFile(outFile,
			   planeBuffer,
			   strlen(planeBuffer),
			   &bytesWritten,
			   (LPOVERLAPPED) NULL))
		ErrorExit();
	    for (i = 0; i < 0x10000; i += 0x10)
	    {
		sprintf(planeBuffer, "%04x\t", i);
		bufptr = planeBuffer + strlen(planeBuffer);
		for (j = 0; j < 2; j++)
		{
		    for (k = 0; k < 8; k++)
		    {
			LOCAL char numTab[] =
			{
			    '0', '1', '2', '3', '4', '5', '6', '7',
			    '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
			};
			FAST UTINY temp;

			temp = *tempPlanePtr++;
			*bufptr++ = numTab[(temp >> 4) & 0xf];
			*bufptr++ = numTab[temp & 0xf];
			*bufptr++ = ' ';
		    }
		    if (j == 0)
		    {
			*bufptr++ = '-';
			*bufptr++ = ' ';
		    }
		}
		*bufptr++ = '\n';
		*bufptr++ = '\0';
		nBytes = strlen(planeBuffer);
		if (!WriteFile(outFile,
			       planeBuffer,
			       nBytes,
			       &bytesWritten,
			       (LPOVERLAPPED) NULL))
		    ErrorExit();
	    }
	    if (!WriteFile(outFile,
			   "\n",
			   1,
			   &bytesWritten,
			   (LPOVERLAPPED) NULL))
		ErrorExit();
	}
	CloseHandle(outFile);
    }
}

#endif /* PROD */
#endif /* X86GFX */

#ifdef PLANEDUMPER
extern half_word *vidpl16;
void planedumper()
{
    char filen[50];
    half_word outs[100];
    HANDLE pfh;
    int loop, curoff;
    char *format = "0123456789abcdef";
    half_word *pl, ch;

    printf("planedumper for plane %d\n", *vidpl16 - 1);
    strcpy(filen, "plane ");
    filen[5] = '0' + *vidpl16 - 1;
    pfh = CreateFile(filen,
		     GENERIC_WRITE,
		     (DWORD) 0,
		     (LPSECURITY_ATTRIBUTES) NULL,
		     CREATE_ALWAYS,
		     FILE_ATTRIBUTE_NORMAL,
		     (HANDLE) NULL);
    if (pfh == INVALID_HANDLE_VALUE)
    {
         printf("Can't create file %s\n", filen);
         return;
    }

    pl = (half_word *)0xa0000;

    curoff = 0;
    for(loop = 0; loop < 64*1024; loop++)
    {
       ch = *pl++;
       outs[curoff++] = *(format + (ch >> 4));
       outs[curoff++] = *(format + (ch & 0xf));
       outs[curoff++] = ' ';

       if (curoff == 78)
       {
           outs[curoff] = '\n';
           
           WriteFile(pfh, outs, 80, &curoff, (LPOVERLAPPED) NULL);
           curoff = 0;
       }
    }
    outs[curoff] = '\n';
           
    WriteFile(pfh, outs, curoff, &curoff, (LPOVERLAPPED) NULL);

    CloseHandle(pfh);
}
#endif
