/*
**	Command & Conquer Renegade(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	This program is free software: you can redistribute it and/or modify
**	it under the terms of the GNU General Public License as published by
**	the Free Software Foundation, either version 3 of the License, or
**	(at your option) any later version.
**
**	This program is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**	GNU General Public License for more details.
**
**	You should have received a copy of the GNU General Public License
**	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/* $Header$ */
/***********************************************************************************************
 ***              C O N F I D E N T I A L  ---  W E S T W O O D  S T U D I O S               ***
 ***********************************************************************************************
 *                                                                                             *
 *                 Project Name : NT Device Driver                                             *
 *                                                                                             *
 *                    File Name : MONODRVR.C                                                   *
 *                                                                                             *
 *                   Programmer : Joe L. Bostic                                                *
 *                                                                                             *
 *                   Start Date : 01/04/97                                                     *
 *                                                                                             *
 *                  Last Update : January 6, 1997 [JLB]                                        *
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 *   Display_Signon_Banner -- Display the signon banner at driver start.                       * 
 *   DriverEntry -- Offical driver start entry point.                                          * 
 *   Mono_Bring_To_Top -- Brings the mono page to the visible top of the display stack.        * 
 *   Mono_Claim_Resources -- Claims resources for the MGA driver.                              * 
 *   Mono_Clear_Screen -- Clears the monochrome screen & homes cursor.                         *
 *   Mono_Detect_MGA_Adapter -- Try to detec the MGA adapter.                                  *
 *   Mono_Dispatch -- Main IRP dispatch handler routine.                                       * 
 *   Mono_Fetch_Ptr -- Fetch a USER address space pointer to mono buffer.                      * 
 *   Mono_Fill_Attribute -- Fills rectangle with attribute specified.                          * 
 *   Mono_Get_Address_Ptr -- Converts a physical address into a usable pointer.                *
 *   Mono_Init_Buffer -- Initialize a mono buffer to known (unused) state.                     * 
 *   Mono_Pan -- Pan the mono screen over one column.                                          *
 *   Mono_Print -- Prints text (with formatting) to mono screen.                               *
 *   Mono_Print_Raw -- Print text (without processing) to mono screen.                         *
 *   Mono_Printf -- Print formatted text to the mono device.                                   *
 *   Mono_Scroll -- Scroll the mono screen up one line.                                        *
 *   Mono_Set_View_Pos -- Set the mono display ram view offset.                                *
 *   Mono_Unload -- Unloads the driver from the system.                                        * 
 *   Mono_Unreport_Resource_Usage -- Free reported hardware usage.                             * 
 *   Mono_Update_Cursor -- Updates the CRTC cursor to reflect current visible page.            * 
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

#include "ntddk.h"
#include "stdarg.h"
#include "stdio.h"
#include "monodrvr.h"


/*
**	Name constants.
*/
#define	DEVICE_NAME			L"\\Device\\Mono"			// Unicode internal device name.
#define	A_DEVICE_NAME		"\\Device\\Mono"			// ASCII internal device name.
#define	SYMBOLIC_NAME		L"\\DosDevices\\MONO"	// Unicode symbolic name.
#define	A_SYMBOLIC_NAME	"\\DosDevices\\MONO"		// ASCII symbolic name.
#define	RESOURCE_NAME		L"LOADED MONO DRIVER RESOURCES"	// Hardware registry name.

/*
**	MGA adapter hardware constants.
*/
#define	CRTC_REGISTER		0x3B4			// CRTC port address.
#define	CRTC_CONTROL		0x3B8			// CRTC control register address.
#define	MONO_MEMORY			0xB0000		// MGA video ram physical address.
#define	DEFAULT_ATTRIBUTE	0x07			// Normal character attribute (white on black).

/*
**	Mono dimension constants.
*/
#define	MONO_WIDTH			80				//	Number of characters wide each page.
#define	MONO_HEIGHT			25				// Number of lines tall each page.
#define	MONO_MEM_LENGTH	(0x10000-1)	// Number of mono video RAM bytes.
#define	MONO_PAGE_SIZE		(MONO_WIDTH * MONO_HEIGHT * sizeof(unsigned short))	// Page size (bytes)
#define	MONO_MAX_PAGES		((MONO_MEM_LENGTH+1) / MONO_PAGE_SIZE)		// Number of mono pages in video RAM.

/*
**	Helper pseudofunctions.
*/
#define	BUILD_MONO_CHAR(c, a)	((unsigned short)(((a) << 8) | ((c) & 0xFF)))
#define	PINDEX_TO_CELL(index)	((index) * MONO_WIDTH * MONO_HEIGHT)
#define	ARRAY_SIZE(x)				((int)(sizeof(x)/sizeof(x[0])))
#define	IS_FLAG_ON(x, flag)		((x->Flags & (1<<(flag))) != 0)
#define	FLAG_SET(x, flag)			x->Flags |= (1<<(flag))
#define	FLAG_CLEAR(x, flag)		x->Flags &= ~(1<<(flag))

/*
**	This controls the state information for each of the
**	display pages.
*/
typedef struct BuffControl
{
	/*
	**	This is the cursor position within the current window
	**	on the mono buffer.
	*/
	int XPos;
	int YPos;

	/*
	**	This specifies the dimensions of the current window
	**	on the mono display.
	*/
	int WinX;
	int WinY;
	int WinW;
	int WinH;

	/*
	**	This is the default attribute to use when displaying text.
	*/
	int Attribute;

	/*
	**	This is the logical address of the display page that this
	**	buffer represents. This pointer is in the address space
	**	of this driver, NOT of the owner of the file object. This
	**	pointer can and does change as the page stack changes.
	*/
	unsigned short * Buffer;

	/*
	**	This is a copy of a pointer to the page memory that is
	**	in mapped to the owning file object's address space. If this
	**	pointer is not NULL, then the page is locked (see LockCount).
	*/
	void * LockPtr;

	/*
	**	Records the number of times this mono page has been locked.
	**	If the page is locked, its position in the display stack cannot
	**	be changed.
	*/
	int LockCount;

	/*
	**	If this page has been allocated, then this flag will
	**	be true.
	*/
	int Allocated;

	/*
	**	These are the behavior flags for this buffer.
	*/
	long Flags;
} BuffControl;


/*
**	This stores the global information for the monochrome driver.
*/
typedef struct MonoGlobals
{
	/*
	**	Syncronizing object so that access to this device is
	**	serialized.
	*/
	KEVENT SyncEvent;

	/*
	**	Logical pointer to the CRTC registers.
	*/
	unsigned char * CRTCRegisters;

	/*
	**	Points to the first character of the MGA display
	**	memory.
	*/
	unsigned short * DisplayMemory;

	/*
	**	Each of the display pages is managed by this buffer
	**	control array.
	*/
	BuffControl Control[MONO_MAX_PAGES];

	/*
	**	This is the index of the currently visible display page. This
	**	means that the BuffControl indicated by this index has its
	**	Buffer pointer pointing to 0xB0000 physical memory.
	*/
	int CurrentVisible;

} MonoGlobals;


/*
**	Describes an MGA resource needed for this driver.
*/
typedef struct MonoResourceType
{
	unsigned long PhysicalAddress;
	unsigned long Length;
	unsigned long AddressSpace;
	unsigned long RangeSharable;
} MonoResourceType;


/*
**	List of MGA resources needed by this driver.
*/
MonoResourceType MonoResources[2] =
{
	{MONO_MEMORY, MONO_MEM_LENGTH, 0, 1},		// MGA memory
	{CRTC_REGISTER, 2, 1, 1}						// CRTC register

#ifdef NEVER
	,{CRTC_CONTROL, 1, 1, 1}		// Mode control register.
#endif
};


/*
**	This is the only public entry point for this driver.
*/
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath);

/*
**	Internal routines for the driver.
*/
static BOOLEAN Mono_Claim_Resources(PDRIVER_OBJECT DriverObject);
static BOOLEAN Mono_Detect_MGA_Adapter(void);
static NTSTATUS Mono_Dispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp);
static void Mono_Fill_Attribute(BuffControl * control, int x, int y, int w, int h, int attrib);
static void * Mono_Fetch_Ptr(MonoGlobals * device, BuffControl * page);
static void * Mono_Get_Address_Ptr(PHYSICAL_ADDRESS PhysicalAddress, unsigned long AddressSpace, unsigned long NumberOfBytes);
static void Display_Signon_Banner(BuffControl * control);
static void Mono_Bring_To_Top(MonoGlobals * device, BuffControl * page);
static void Mono_Clear_Screen(BuffControl * control);
static void Mono_Free_Resources(PDRIVER_OBJECT DriverObject);
static void Mono_Init_Buffer(BuffControl * buffer);
static void Mono_Pan(BuffControl * control);
static void Mono_Print(BuffControl * control, unsigned char * string, unsigned long length);
static void Mono_Print_Raw(BuffControl * control, unsigned char * string, unsigned long length);
static void Mono_Printf(BuffControl * control, char const * DbgMessage, ...);
static void Mono_Scroll(BuffControl * control);
static void Mono_Set_View_Pos(MonoGlobals * device, int pos);
static void Mono_Unload(PDRIVER_OBJECT driver);
static void Mono_Update_Cursor(MonoGlobals * device);



/*********************************************************************************************** 
 * DriverEntry -- Offical driver start entry point.                                            * 
 *                                                                                             * 
 *    This routine is called by the system when the driver is first loaded. It is responsible  * 
 *    for initializing the driver state.                                                       * 
 *                                                                                             * 
 * INPUT:   driverobject   -- Pointer to the loaded driver object.                             * 
 *                                                                                             * 
 *          registry       -- Pointer to the registry string for this driver object.           * 
 *                                                                                             * 
 * OUTPUT:  Returns with the NTSTATUS of the driver initialization. If it is anything but      * 
 *          STATUS_SUCCESS, the the driver will not load.                                      * 
 *                                                                                             * 
 * WARNINGS:   none                                                                            * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   01/06/1997 JLB : Created.                                                                 * 
 *=============================================================================================*/
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
	NTSTATUS error_flag = STATUS_UNSUCCESSFUL;

	/*
	**	Check the registry to see if the MGA resources are free to be claimed (claim them
	**	if they are) and also check that the MGA hardware is present. Only if both
	**	are true can the driver be loaded.
	*/
	if (Mono_Claim_Resources(DriverObject) && Mono_Detect_MGA_Adapter())  {
		PDEVICE_OBJECT device_object = NULL;
		UNICODE_STRING device_name;

		/*
		**	Creates the device object.
		*/
		RtlInitUnicodeString(&device_name, DEVICE_NAME);
		error_flag = IoCreateDevice(DriverObject, sizeof(MonoGlobals), &device_name, FILE_DEVICE_MONO, 0, FALSE, &device_object);
		if (NT_SUCCESS(error_flag)) {
			MonoGlobals * mono_globals = (MonoGlobals *)device_object->DeviceExtension;

			/*
			**	This enables the WriteFile/ReadFile Win32 functions.
			*/
			device_object->Flags |= DO_BUFFERED_IO;

			/*
			** Initialize the dispatch event object. This allows us to
			** synchronize access to the h/w registers...
			*/
			KeInitializeEvent(&mono_globals->SyncEvent, SynchronizationEvent, TRUE);

			/*
			** Map all the required resources, save the addresses
			*/
			mono_globals->DisplayMemory = Mono_Get_Address_Ptr(RtlConvertUlongToLargeInteger(MONO_MEMORY), 0, MONO_MEM_LENGTH);
			mono_globals->CRTCRegisters =	Mono_Get_Address_Ptr(RtlConvertUlongToLargeInteger(CRTC_REGISTER), 1, 2);
			if (mono_globals->DisplayMemory != NULL && mono_globals->CRTCRegisters != NULL) {
				UNICODE_STRING link_name;

				/*
				** Create a symbolic link that Win32 apps can specify to gain access
				** to this driver/device
				*/
				RtlInitUnicodeString(&link_name, SYMBOLIC_NAME);
				error_flag = IoCreateSymbolicLink(&link_name, &device_name);
				if (NT_SUCCESS(error_flag)) {
					int index;

					/*
					** Create dispatch points for device control, create, close.
					*/
					DriverObject->MajorFunction[IRP_MJ_CREATE] = Mono_Dispatch;
					DriverObject->MajorFunction[IRP_MJ_CLOSE] = Mono_Dispatch;
					DriverObject->MajorFunction[IRP_MJ_READ] = Mono_Dispatch;
					DriverObject->MajorFunction[IRP_MJ_WRITE] = Mono_Dispatch;
					DriverObject->MajorFunction[IRP_MJ_CLEANUP] = Mono_Dispatch;
					DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = Mono_Dispatch;
					DriverObject->DriverUnload = Mono_Unload;

					/*
					** Set the Start Address registers (indicate which part of
					** video buffer is displayed) to 0
					*/
					Mono_Set_View_Pos(mono_globals, 0);

					mono_globals->CurrentVisible = 0;
					for (index = 0; index < MONO_MAX_PAGES; index++) {
						mono_globals->Control[index].Buffer = &mono_globals->DisplayMemory[PINDEX_TO_CELL(index)];
						Mono_Init_Buffer(&mono_globals->Control[index]);
					}

					/*
					**	Display the signon banner.
					*/
					Display_Signon_Banner(&mono_globals->Control[mono_globals->CurrentVisible]);
					Mono_Update_Cursor(mono_globals);
				}
			} else {
				error_flag = STATUS_UNSUCCESSFUL;
			}
		}
	} else {
		error_flag = STATUS_UNSUCCESSFUL;
	}

	/*
	**	If an error was detected during initialization, then unload the
	**	driver before returning the error flag to the system.
	*/
	if (!NT_SUCCESS(error_flag)) {
		Mono_Unload(DriverObject);
	}

	return(error_flag);
}


/*********************************************************************************************** 
 * Mono_Dispatch -- Main IRP dispatch handler routine.                                         * 
 *                                                                                             * 
 *    This routine is called whenever an IRP occurs. It is passed the driver object pointer    * 
 *    and the IRP info.                                                                        * 
 *                                                                                             * 
 * INPUT:   driver   -- Pointer to the device object.                                          * 
 *                                                                                             * 
 *          irp      -- Pointer to the IRP object.                                             * 
 *                                                                                             * 
 * OUTPUT:  Returns with the NTSTATUS of this IRP handler.                                     * 
 *                                                                                             * 
 * WARNINGS:   none                                                                            * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   01/06/1997 JLB : Created.                                                                 * 
 *=============================================================================================*/
NTSTATUS Mono_Dispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
	PIO_STACK_LOCATION irpStack;
	MonoGlobals *  mono_globals;
	void * ioBuffer;
	unsigned long inputBufferLength;
	unsigned long outputBufferLength;
	unsigned long ioControlCode;
	NTSTATUS ntStatus;
	FILE_OBJECT * fileobject = NULL;
	BuffControl * control = NULL;
	int currentindex = -1;

	/*
	**	Presume success in the command processing.
	*/
	Irp->IoStatus.Status      = STATUS_SUCCESS;
	Irp->IoStatus.Information = 0;		// Bytes read/written or other return value.

	/*
	** Get a pointer to the current location in the Irp. This is where
	**     the function codes and parameters are located.
	*/
	irpStack = IoGetCurrentIrpStackLocation(Irp);

	/*
	** Get a pointer to the device extension
	*/
	mono_globals = DeviceObject->DeviceExtension;

	/*
	**	Fetch the file object for this I/O request.
	*/
	fileobject = irpStack->FileObject;
	if (fileobject != NULL)  {
		currentindex = (long)fileobject->FsContext;
	}

	/*
	** Get the pointer to the input/output buffer and it's length
	*/
	ioBuffer           = Irp->AssociatedIrp.SystemBuffer;
	inputBufferLength  = irpStack->Parameters.DeviceIoControl.InputBufferLength;
	outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;

	/*
	** Synchronize execution of the dispatch routine by acquiring the device
	** event object. This ensures all request are serialized.
	*/
	KeWaitForSingleObject(&mono_globals->SyncEvent, Executive, KernelMode, FALSE, NULL);

	/*
	**	Get a working pointer to the display page.
	*/
	if (currentindex == -1) {
		currentindex == mono_globals->CurrentVisible;
	}
	control = &mono_globals->Control[currentindex];

	switch (irpStack->MajorFunction) {
		case IRP_MJ_READ:
			break;

		/*
		**	Process WriteFile request to display text.
		*/
		case IRP_MJ_WRITE:
			if (ioBuffer != NULL && irpStack->Parameters.Write.Length > 0)  {
				Mono_Print(control, ioBuffer, irpStack->Parameters.Write.Length);
				Irp->IoStatus.Information = irpStack->Parameters.Write.Length;
			}
			break;

		/*
		**	The create event is called when a file object is first created. At that
		**	time, designate a context buffer and inialize it.
		*/
		case IRP_MJ_CREATE: {
				int j;
				int avail = -1;

				/*
				**	Search for a free page to use.
				*/
				for (j = 0; j < MONO_MAX_PAGES; j++) {
					if (!mono_globals->Control[j].Allocated) {
						avail = j;
						break;
					}
				}

				/*
				**	If one is not found, then return with error.
				*/
				if (avail == -1) {
					Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
				} else {
					BuffControl * newcon = &mono_globals->Control[avail];
					Mono_Init_Buffer(newcon);
					newcon->Allocated = TRUE;
					Mono_Clear_Screen(newcon);
					Mono_Bring_To_Top(mono_globals, newcon);
					fileobject->FsContext = (void*)avail;
				}
			}
			break;

		/*
		**	Closing the file occurs when the last reference to the object has been
		**	severed.
		*/
		case IRP_MJ_CLOSE:
			if (currentindex != -1) {
				Mono_Init_Buffer(&mono_globals->Control[currentindex]);
				fileobject->FsContext = NULL;
			}
			break;

		/*
		**	This is called prior to closing the file.
		*/
		case IRP_MJ_CLEANUP:
			break;

		/*
		**	All the special I/O control functions route through this
		**	IRP message.
		*/
		case IRP_MJ_DEVICE_CONTROL:
			ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;

			switch (ioControlCode) {

				/*
				**	Set a driver flag.
				*/
				case IOCTL_MONO_SET_FLAG:
					if (inputBufferLength == sizeof(int))  {
						FLAG_SET(control, *(int*)ioBuffer);
					} else {
						Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
					}
					break;

				/*
				**	Clear a driver flag.
				*/
				case IOCTL_MONO_CLEAR_FLAG:
					if (inputBufferLength == sizeof(int))  {
						FLAG_CLEAR(control, *(int*)ioBuffer);
					} else {
						Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
					}
					break;

				/*
				**	Fill a region of the screen with a specific attribute.
				*/
				case IOCTL_MONO_FILL_ATTRIB:  
					if (inputBufferLength == sizeof(int)*5) {
						int x,y,w,h,a;

						x = *(((int*)ioBuffer) + 0);
						y = *(((int*)ioBuffer) + 1);
						w = *(((int*)ioBuffer) + 2);
						h = *(((int*)ioBuffer) + 3);
						a = *(((int*)ioBuffer) + 4);
						Mono_Fill_Attribute(control, x, y, w, h, a);
					} else {
						Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
					}
					break;

				/*
				**	Reset the sub-window back to full screen.
				*/
				case IOCTL_MONO_RESET_WINDOW:
					control->WinX = 0;
					control->WinY = 0;
					control->WinW = MONO_WIDTH;
					control->WinH = MONO_HEIGHT;
					control->XPos = 0;
					control->YPos = 0;
					break;

				/*
				**	Create a sub-window that restricts mono output to.
				*/
				case IOCTL_MONO_SET_WINDOW:
					if (inputBufferLength == sizeof(int)*4)  {
						control->WinX = *(((int*)ioBuffer) + 0);
						if (control->WinX < 0) control->WinX = 0;
						if (control->WinX >= MONO_WIDTH) control->WinX = MONO_WIDTH-1;

						control->WinY = *(((int*)ioBuffer) + 1);
						if (control->WinY < 0) control->WinY = 0;
						if (control->WinY >= MONO_HEIGHT) control->WinY = MONO_HEIGHT-1;
						
						control->WinW = *(((int*)ioBuffer) + 2);
						if (control->WinW < 1) control->WinW = 1;
						if (control->WinW > MONO_WIDTH-control->WinX) control->WinW = MONO_WIDTH-control->WinX;

						control->WinH = *(((int*)ioBuffer) + 3);
						if (control->WinH < 1) control->WinH = 1;
						if (control->WinH > MONO_HEIGHT-control->WinY) control->WinH = MONO_HEIGHT-control->WinY;
						
						control->XPos = 0;
						control->YPos = 0;
					} else {
						Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
					}
					break;

				/*
				**	Display the help screen (signon banner).
				*/
				case IOCTL_MONO_HELP_SCREEN:
					Display_Signon_Banner(control);
					break;

				/*
				**	Bring the mono page to the visible top, but only if it
				**	or the current visible top is not currently locked.
				*/
				case IOCTL_MONO_BRING_TO_TOP:
					Mono_Bring_To_Top(mono_globals, control);
					break;

				/*
				**	Scroll the screen up one row.
				*/
				case IOCTL_MONO_SCROLL:
					Mono_Scroll(control);
					break;

				/*
				**	Scroll the screen left one column.
				*/
				case IOCTL_MONO_PAN:
					Mono_Pan(control);
					break;

				/*
				**	Set the cursor position to the coordinates specified.
				*/
				case IOCTL_MONO_SET_CURSOR:
					if (inputBufferLength == sizeof(int)*2) {
						control->XPos = *(int*)ioBuffer;
						if (control->XPos < 0) control->XPos = 0;
						if (control->XPos >= control->WinW) control->XPos = control->WinW-1;

						control->YPos = *(((int*)ioBuffer) + 1);
						if (control->YPos < 0) control->YPos = 0;
						if (control->YPos >= control->WinH) control->YPos = control->WinH-1;
					} else {
						Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
					}
					break;

				/*
				**	Print text without any formatting processing.
				*/
				case IOCTL_MONO_PRINT_RAW:
					Mono_Print_Raw(control, ioBuffer, inputBufferLength);
					break;

				/*
				**	Clear the screen to blank (using the default attribute).
				*/
				case IOCTL_MONO_CLEAR_SCREEN:
					Mono_Clear_Screen(control);
					break;

				/*
				**	Set the default attribute to use for subsiquent output.
				*/
				case IOCTL_MONO_SET_ATTRIBUTE:
					control->Attribute = *(char *)ioBuffer;
					break;

				/*
				**	Lock the mono page and return a user-space mapped pointer to
				**	it.
				*/
				case IOCTL_MONO_LOCK:
					if (control->LockPtr == NULL)  {
						control->LockPtr = Mono_Fetch_Ptr(mono_globals, control);
					}
					if (control->LockPtr != NULL) {
						*(void**)ioBuffer = control->LockPtr;
						control->LockCount++;
						Irp->IoStatus.Information = sizeof(void *);
					} else {
						Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
					}
					break;

				/*
				**	Unlock the mono page.
				*/
				case IOCTL_MONO_UNLOCK:
					if (control->LockCount > 0) {
						control->LockCount--;

						if (control->LockCount == 0) {
							Irp->IoStatus.Status = ZwUnmapViewOfSection((HANDLE) -1, control->LockPtr);
							control->LockPtr = NULL;
						}
					}
					break;

				/*
				**	Unrecognized control code results in an error return value.
				*/
				default:
					Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
					break;
			}

			break;
	}

	/*
	**	Only if the context drawn to is the currently visible one, will the
	**	cursor position be updated.
	*/
	if (currentindex == mono_globals->CurrentVisible)  {
		Mono_Update_Cursor(mono_globals);
	}

	KeSetEvent(&mono_globals->SyncEvent, 0, FALSE);

	ntStatus = Irp->IoStatus.Status;

	IoCompleteRequest(Irp, IO_NO_INCREMENT);

	return(ntStatus);
}


/*********************************************************************************************** 
 * Mono_Unload -- Unloads the driver from the system.                                          * 
 *                                                                                             * 
 *    This will free any resources allocated by this driver, break any symbolic link, and      * 
 *    delete the driver object from the system. Effectively, this is the suicide function      * 
 *    for the MGA driver.                                                                      * 
 *                                                                                             * 
 * INPUT:   driver   -- Pointer to the driver object to delete.                                * 
 *                                                                                             * 
 * OUTPUT:  none                                                                               * 
 *                                                                                             * 
 * WARNINGS:   Don't use the driver after calling this function.                               * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   01/06/1997 JLB : Created.                                                                 * 
 *=============================================================================================*/
void Mono_Unload(PDRIVER_OBJECT DriverObject)
{
	UNICODE_STRING link_string;

	Mono_Free_Resources(DriverObject);

	RtlInitUnicodeString(&link_string, SYMBOLIC_NAME);
	IoDeleteSymbolicLink(&link_string);
	
	if (DriverObject->DeviceObject != NULL)  {
		IoDeleteDevice(DriverObject->DeviceObject);
	}
}


/***********************************************************************************************
 * Mono_Detect_MGA_Adapter -- Try to detec the MGA adapter.                                    *
 *                                                                                             *
 *    Tries to find the MGA adaptor by writing a few bytes to the CRTC register and checking   *
 *    if they return a corresponding value.                                                    *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  bool; Was the MGA card deteced?                                                    *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   01/05/1997 JLB : Created.                                                                 *
 *=============================================================================================*/
BOOLEAN Mono_Detect_MGA_Adapter(void)
{
	unsigned char * crtc_registerss;

	/*
	**	Get address to the CRTC registers.
	*/
	crtc_registerss = Mono_Get_Address_Ptr(RtlConvertUlongToLargeInteger(CRTC_REGISTER), 1, 2);
	if (crtc_registerss != NULL)  {

		/*
		**	Set the mono cursor position and see if it changed. If so, then
		**	presume that the MGA adapter is present.
		*/
		WRITE_PORT_UCHAR(crtc_registerss, 0x0F);
		WRITE_PORT_UCHAR(crtc_registerss+1, 0x55);
		if (READ_PORT_UCHAR(crtc_registerss+1) != 0x55) {
			return(FALSE);
		}
		WRITE_PORT_UCHAR(crtc_registerss+1, 0xAA);
		if (READ_PORT_UCHAR(crtc_registerss+1) != 0xAA) {
			return(FALSE);
		}

		WRITE_PORT_UCHAR(crtc_registerss+1, 0x00);
		return(TRUE);
	}

	return(FALSE);
}


/***********************************************************************************************
 * Mono_Get_Address_Ptr -- Converts a physical address into a usable pointer.                  *
 *                                                                                             *
 *    This routine will take the physical address specified and convert it into a pointer that *
 *    can be used to reference the address specified.                                          *
 *                                                                                             *
 * INPUT:   address  -- The physical address to convert.                                       *
 *                                                                                             *
 *          space    -- The address space that the specified address is in. 0 means memory,    *
 *                      1 means I/O space (ports).                                             *
 *                                                                                             *
 *          length   -- The length of the memory that will be referenced.                      *
 *                                                                                             *
 * OUTPUT:  Returns with a usable pointer to the memory specified or NULL if it could not be   *
 *          processed.                                                                         *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   01/05/1997 JLB : Created.                                                                 *
 *=============================================================================================*/
void * Mono_Get_Address_Ptr(PHYSICAL_ADDRESS address, unsigned long space, unsigned long length)
{
	PHYSICAL_ADDRESS logical_address;
	void * usable_ptr = NULL;

	/*
	**	Translate a bus specific address into a logical system address.
	*/
	if (HalTranslateBusAddress(Isa, 0, address, &space, &logical_address)) {

		/*
		**	If the space value is zero, then this idicates that a call to
		**	MmMapIoSpace is required. This is usually required for port addresses.
		*/
		if (space == 0) {
			usable_ptr = MmMapIoSpace(logical_address, length, FALSE);
		} else {
			usable_ptr = (void *)logical_address.LowPart;
		}
	}

	return(usable_ptr);
}


/*********************************************************************************************** 
 * Mono_Claim_Resources -- Claims resources for the MGA driver.                                * 
 *                                                                                             * 
 *    This routine will scan the registry in order to claim the hardware resources needed by   * 
 *    this MGA display driver. If any of the required resources have already been claimed      * 
 *    in an exclusive mode, then the driver should not load.                                   * 
 *                                                                                             * 
 * INPUT:   driver   -- Pointer to the driver object.                                          * 
 *                                                                                             * 
 * OUTPUT:  bool; Were the required resources claimed successfully?                            * 
 *                                                                                             * 
 * WARNINGS:   If the resources were not claimed, then the driver should not load.             * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   01/06/1997 JLB : Created.                                                                 * 
 *=============================================================================================*/
BOOLEAN Mono_Claim_Resources(PDRIVER_OBJECT driver)
{
	unsigned long resource_list_size = 0;
	PCM_RESOURCE_LIST resource_list = NULL;

	/*
	**	Allocate a resource descriptor list. This must be allocated because it is a variable
	**	size structure and the CM_RESOURCE_LIST is typedef'd as single entry lists (which
	**	is insufficient for our purposes).
	*/
	resource_list_size = sizeof(CM_RESOURCE_LIST) + (sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) * (ARRAY_SIZE(MonoResources) - 1));
	resource_list = ExAllocatePool(PagedPool, resource_list_size);
	if (resource_list != NULL) {
		UNICODE_STRING class_name;
		PCM_PARTIAL_RESOURCE_DESCRIPTOR partial_resource_list;
		BOOLEAN conflict_flag;
		unsigned long index;
	
		RtlZeroMemory(resource_list, resource_list_size);

		/*
		**	There is one resource list (although multiple resources). They are
		**	all located on the Isa bus number 0.
		*/
		resource_list->Count = 1;
		resource_list->List[0].InterfaceType = Isa;
		resource_list->List[0].BusNumber     = 0;
		resource_list->List[0].PartialResourceList.Count = ARRAY_SIZE(MonoResources);

		/*
		**	Fill in the resrouce list with the resources required by the MGA
		**	device driver.
		*/
		partial_resource_list = &resource_list->List[0].PartialResourceList.PartialDescriptors[0];
		for (index = 0; index < ARRAY_SIZE(MonoResources); index++) {
			if (MonoResources[index].AddressSpace) {
				partial_resource_list->Type          = CmResourceTypePort;
				partial_resource_list->Flags         = CM_RESOURCE_PORT_IO;
				partial_resource_list->u.Port.Start  = RtlConvertUlongToLargeInteger(MonoResources[index].PhysicalAddress);
				partial_resource_list->u.Port.Length = MonoResources[index].Length;
			} else {
				partial_resource_list->Type            = CmResourceTypeMemory;
				partial_resource_list->Flags           = CM_RESOURCE_MEMORY_READ_WRITE;
				partial_resource_list->u.Memory.Start  = RtlConvertUlongToLargeInteger(MonoResources[index].PhysicalAddress);
				partial_resource_list->u.Memory.Length = MonoResources[index].Length;
			}

			if (MonoResources[index].RangeSharable) {
				partial_resource_list->ShareDisposition = CmResourceShareShared;
			} else {
				partial_resource_list->ShareDisposition = CmResourceShareDeviceExclusive;
			}

			partial_resource_list++;
		}

		/*
		**	Now that the resource list has been filled in, try to request the resources
		**	from the system. It does this by comparing the requested resources with those
		**	that have been claimed in the registry.
		*/
		RtlInitUnicodeString(&class_name, RESOURCE_NAME);
		IoReportResourceUsage(&class_name, driver, resource_list, resource_list_size, NULL, NULL, 0, FALSE, &conflict_flag);

		ExFreePool(resource_list);
		
		return(conflict_flag == FALSE);
	}
	return(FALSE);
}


/*********************************************************************************************** 
 * Mono_Free_Resources -- Free reported hardware usage.                                        * 
 *                                                                                             * 
 *    This routine will clear the registry of the reported hardware usage by the mono          * 
 *    display driver. It should be called only when the driver is being unloaded.              * 
 *                                                                                             * 
 * INPUT:   driver   -- Pointer to the driver object.                                          * 
 *                                                                                             * 
 * OUTPUT:  none                                                                               * 
 *                                                                                             * 
 * WARNINGS:   none                                                                            * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   01/06/1997 JLB : Created.                                                                 * 
 *=============================================================================================*/
void Mono_Free_Resources(PDRIVER_OBJECT driver)
{
	UNICODE_STRING class_name;
	BOOLEAN found_conflict;
	CM_RESOURCE_LIST resource_list =  {0};

	RtlInitUnicodeString(&class_name, RESOURCE_NAME);
	IoReportResourceUsage(&class_name, driver, &resource_list, sizeof(resource_list), NULL, NULL, 0, FALSE, &found_conflict);
}



/*********************************************************************************************** 
 * Mono_Update_Cursor -- Updates the CRTC cursor to reflect current visible page.              * 
 *                                                                                             * 
 *    Use this routine to update the display cursor to match the recorded cursor position      * 
 *    for the currently visible page.                                                          * 
 *                                                                                             * 
 * INPUT:   device   -- Pointer to the mono driver globals.                                    * 
 *                                                                                             * 
 * OUTPUT:  none                                                                               * 
 *                                                                                             * 
 * WARNINGS:   none                                                                            * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   01/06/1997 JLB : Created.                                                                 * 
 *=============================================================================================*/
void Mono_Update_Cursor(MonoGlobals * device)
{
	int pos;
	BuffControl * control = &device->Control[device->CurrentVisible];

	pos = control->XPos + control->WinX + ((control->YPos+control->WinY) * MONO_WIDTH);

	WRITE_PORT_UCHAR(device->CRTCRegisters, 0x0E);
	WRITE_PORT_UCHAR(device->CRTCRegisters+1, (unsigned char)(pos>>8));
	WRITE_PORT_UCHAR(device->CRTCRegisters, 0x0F);
	WRITE_PORT_UCHAR(device->CRTCRegisters+1, (unsigned char)(pos & 0xFF));
}


/***********************************************************************************************
 * Mono_Print -- Prints text (with formatting) to mono screen.                                 *
 *                                                                                             *
 *    This routine will take the text specified and print it to the monochrome screen. If      *
 *    there are any formatting characters present, then they will be processed accordingly.    *
 *                                                                                             *
 * INPUT:   control  -- Poitner to the buffer to print to.                                     *
 *                                                                                             *
 *          string   -- Pointer to the string to print.                                        *
 *                                                                                             *
 *          length   -- The length of the string to print.                                     *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   01/04/1997 JLB : Created.                                                                 *
 *=============================================================================================*/
void Mono_Print(BuffControl * control, unsigned char * string, unsigned long length)
{
	if (control != NULL)  {
		unsigned char space = ' ';
		int original_x = control->XPos;

		while (length > 0) {
			int x,y;
			unsigned char * blockstart;
			int blocklen;
			unsigned char bchar;		// Breaking character.

			/*
			**	Scan for a block of contiguous non-formatting characters to print.
			*/
			blockstart = string;
			blocklen = 0;
			bchar = '\0';
			x = control->XPos;
			while (length > 0 && bchar == '\0') {
				bchar = *string++;
				length--;

				/*
				**	Detect if this character is special and requires
				**	formatting control rather than simple displaying.
				*/
				switch (bchar) {
					case '\b':		// Backspace
					case '\a':		// Audible bell
					case '\t':		// Horizontal tab
					case '\f':		// Formfeed
					case '\n':		// Linefeed
					case '\v':		// Vertical tab
						break;

					/*
					**	If a <CR><LF> pair is detected, then treat it
					**	just as if it were a '\n'. This is for backward
					**	compatibility. If it is just a '\r', then it 
					**	functions similarly to the '\n' code.
					*/
					case '\r':
						if (length > 0 && *string == '\n')  {
							length--;
							string++;
							bchar = '\n';
						}
						break;

					/*
					**	If not a valid formatting character, then clear out the break
					**	character variable so that processing will continue.
					*/
					default:
						blocklen++;
						bchar = '\0';
						break;
				}
				
				/*
				**	If wrapping at right margin is desired, then check for 
				**	this condition and force a line break if margin is reached.
				*/
				if (bchar == '\0' && IS_FLAG_ON(control, MONOFLAG_WRAP))  {
					x++;
					if (x >= control->WinW)  {
						bchar = '\n';
					}
				}
			}

			/*
			**	If a block has been scanned, then print it at this time.
			*/
			if (blocklen > 0) {
				Mono_Print_Raw(control, blockstart, blocklen);
			}

			/*
			**	Conveniently get the cursor position into working variables.
			**	This helps because many of the formatting characters change
			**	the cursor position.
			*/
			x = control->XPos;
			y = control->YPos;

			/*
			**	If a formatting character was found, then process it at this
			**	time.
			*/
			switch (bchar) {
				/*
				**	Backspace moves the cursor back one space (erasing what was there).
				*/
				case '\b':
					if (x > 0) {
						x--;
						control->XPos = x;
						Mono_Print_Raw(control, &space, 1);
						control->XPos = x;
					}
					break;

				/*
				**	Formfeed -- clears the screen.
				*/
				case '\f':
					Mono_Clear_Screen(control);
					break;

				/*
				**	Linefeed -- in the tradition of C, this actually translates
				**	into a functional <CR><LF> pair.
				*/
				case '\n':
					x = 0;
					y++;
					if (y >= control->WinH) {
						Mono_Scroll(control);
						y--;
					}
					control->XPos = x;
					control->YPos = y;
					break;

				/*
				**	A <CR> will move the cursor down one row and back to the column
				**	position that the print started at.
				*/
				case '\r':
					x = original_x;
					y++;
					if (y >= control->WinH) {
						Mono_Scroll(control);
						y--;
					}
					control->XPos = x;
					control->YPos = y;
					break;

				/*
				**	Horizontal tab
				*/
				case '\t':
					Mono_Print_Raw(control, &space, 1);
					break;

				/*
				**	Unrecognized formatting character -- ignore it.
				*/
				default:
					break;
			}
		}
	}
}


/***********************************************************************************************
 * Mono_Scroll -- Scroll the mono screen up one line.                                          *
 *                                                                                             *
 *    Use this routine to scroll the mono screen up one line. The bottom line will be filled   *
 *    with blanks. The cursor position is moved up one line as well.                           *
 *                                                                                             *
 * INPUT:   control  -- Pointer to the buffer to scroll.                                       *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   01/04/1997 JLB : Created.                                                                 *
 *=============================================================================================*/
void Mono_Scroll(BuffControl * control)
{
	if (control != NULL)  {
		int j;
		unsigned short * vidmem = control->Buffer;
		unsigned short blank = BUILD_MONO_CHAR(' ', control->Attribute);
		unsigned short * lineptr = vidmem + (control->WinY*MONO_WIDTH) + control->WinX;

		/*
		**	Move every line of the sub-window.
		*/
		for (j = 0; j < control->WinH-1; j++) {
			RtlCopyMemory(lineptr, lineptr+MONO_WIDTH, control->WinW * sizeof(unsigned short));
			lineptr += MONO_WIDTH;
		}

		/*
		**	Fill the bottom row with blanks.
		*/
		for (j = 0; j < control->WinW; j++) {
			*lineptr++ = blank;
		}

		/*
		**	The cursor position moves with the scroll, but limited to the
		**	edge of the window.
		*/
		control->YPos -= 1;
		if (control->YPos < 0)  {
			control->YPos = 0;
		}
	}
}


/***********************************************************************************************
 * Mono_Pan -- Pan the mono screen over one column.                                            *
 *                                                                                             *
 *    This will scroll (horizontally) the monochrome screen. The new column will be filled     *
 *    with blank spaces.                                                                       *
 *                                                                                             *
 * INPUT:   control  -- Pointer to the buffer to pan.                                          *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   01/04/1997 JLB : Created.                                                                 *
 *=============================================================================================*/
void Mono_Pan(BuffControl * control)
{
	if (control != NULL)  {
		unsigned short * vidmem = control->Buffer;
		unsigned short blank = BUILD_MONO_CHAR(' ', control->Attribute);
		int j;

		/*
		**	Move the video memory over by one column.
		*/
		for (j = 0; j < control->WinH; j++) {
			unsigned short * linestart = vidmem + ((control->WinY+j)*MONO_WIDTH) + control->WinX;
			RtlMoveMemory(linestart, linestart+1, (control->WinW-1) * sizeof(unsigned short));
			*(linestart+control->WinW-1) = blank;
		}

		/*
		**	The cursor position moves as well, but limited to the window
		**	margin.
		*/
		control->XPos -= 1;
		if (control->XPos < 0)  {
			control->XPos = 0;
		}
	}
}


/***********************************************************************************************
 * Mono_Print_Raw -- Print text (without processing) to mono screen.                           *
 *                                                                                             *
 *    This is a low level routine that will not process the characters submitted, but merely   *
 *    draw them to the monochrome screen. The output will not wrap at right margin nor will    *
 *    it scroll the screen when it tries to print past the bottom right character position.    *
 *    This routine can be used to output characters that have a visual image even though they  *
 *    line up with formatting control characters.                                              *
 *                                                                                             *
 * INPUT:   control  -- Pointer to the buffer to print to.                                     *
 *                                                                                             *
 *          string   -- Pointer to the string to output.                                       *
 *                                                                                             *
 *          length   -- The length of the string in characters.                                *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   01/04/1997 JLB : Created.                                                                 *
 *=============================================================================================*/
void Mono_Print_Raw(BuffControl * control, unsigned char * string, unsigned long length)
{
	if (control != NULL)  {
		unsigned short * vidmem = control->Buffer + (control->XPos+control->WinX) + ((control->YPos+control->WinY)*MONO_WIDTH);
		int x = control->XPos;
		
		while (length > 0) {
			*vidmem++ = BUILD_MONO_CHAR(*string++, control->Attribute);
			x++;
			length--;

			/*
			**	Don't allow the cursor to extend past the right margin.
			*/
			if (x >= control->WinW) {
				x--;
				vidmem--;
			}
		}
		control->XPos = x;
	}
}


/***********************************************************************************************
 * Mono_Clear_Screen -- Clears the monochrome screen & homes cursor.                           *
 *                                                                                             *
 *    This will erase the monochrome screen and move the output cursor to the upper left       *
 *    corner.                                                                                  *
 *                                                                                             *
 * INPUT:   control  -- Pointer to the buffer to clear.                                        *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   01/04/1997 JLB : Created.                                                                 *
 *=============================================================================================*/
void Mono_Clear_Screen(BuffControl * control)
{
	if (control != NULL)  {
		int yindex;
		unsigned short blank = BUILD_MONO_CHAR(' ', control->Attribute);

		unsigned short * rowptr = control->Buffer + (control->WinY * MONO_WIDTH) + control->WinX;
		for (yindex = 0; yindex < control->WinH; yindex++) {
			int xindex;
			unsigned short * lineptr = rowptr;
			for (xindex = 0; xindex < control->WinW; xindex++) {
				*lineptr++ = blank;
			}
			rowptr += MONO_WIDTH;
		}

		control->XPos = 0;
		control->YPos = 0;
	}
}


/***********************************************************************************************
 * Mono_Printf -- Print formatted text to the mono device.                                     *
 *                                                                                             *
 *    Prints formatted text to the monochrome device specified.                                *
 *                                                                                             *
 * INPUT:   control  -- Pointer to the buffer to print to.                                     *
 *                                                                                             *
 *          text     -- Pointer to the text to display.                                        *
 *                                                                                             *
 *          ...      -- Any optional parameters needed by the string.                          *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   The final formatted string is limited to 1024 characters long.                  *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   01/04/1997 JLB : Created.                                                                 *
 *=============================================================================================*/
void Mono_Printf(BuffControl * control, char const * text, ...)
{
	if (control != NULL)  {
		char buf[MONO_WIDTH*MONO_HEIGHT + 1];

		va_list ap;
		va_start(ap, text);
		vsprintf(buf, text, ap);
		Mono_Print(control, buf, strlen(buf));
		va_end(ap);
	}
}


/***********************************************************************************************
 * Mono_Set_View_Pos -- Set the mono display ram view offset.                                  *
 *                                                                                             *
 *    Set the CRTC register for the monochrome display to the offset specified. The card       *
 *    defaults to viewing offset 0 (i.e. $B0000 absolute RAM address).                         *
 *                                                                                             *
 * INPUT:   device   -- The monochrome device global data.                                     *
 *                                                                                             *
 *          pos      -- The offset position to set the view to.                                *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   01/04/1997 JLB : Created.                                                                 *
 *=============================================================================================*/
void Mono_Set_View_Pos(MonoGlobals * device, int pos)
{
	if (device != NULL)  {
		WRITE_PORT_UCHAR(device->CRTCRegisters, 0x0C);
		WRITE_PORT_UCHAR(device->CRTCRegisters+1, (unsigned char)(pos >> 8));
		WRITE_PORT_UCHAR(device->CRTCRegisters, 0x0D);
		WRITE_PORT_UCHAR(device->CRTCRegisters+1, (unsigned char)(pos & 0xFF));
	}
}


/*********************************************************************************************** 
 * Mono_Bring_To_Top -- Brings the mono page to the visible top of the display stack.          * 
 *                                                                                             * 
 *    This routine will take the page specified and bring it to the top of the display stack.  * 
 *    Effectively, this makes it visible.                                                      * 
 *                                                                                             * 
 * INPUT:   device   -- Pointer to the mono device globals.                                    * 
 *                                                                                             * 
 *          page     -- Pointer to the page that is to be displayed.                           * 
 *                                                                                             * 
 * OUTPUT:  none                                                                               * 
 *                                                                                             * 
 * WARNINGS:   If either the visible page or the one specified is locked, then no action       * 
 *             can be performed.                                                               * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   01/06/1997 JLB : Created.                                                                 * 
 *=============================================================================================*/
void Mono_Bring_To_Top(MonoGlobals * device, BuffControl * page)
{
	BuffControl * visible_page = &device->Control[device->CurrentVisible];

	if (page == NULL || page->Buffer == visible_page->Buffer) return;

	/*
	**	If either the one trying to be brought to the front or the
	**	one already displayed is locked, then the display stack
	**	cannot be modified.
	*/
	if (page->LockCount > 0 || visible_page->LockCount > 0) return;

	/*
	**	Swap the contents of the page buffers and then swap the page buffer
	**	pointers.
	*/ 
	{
		unsigned short temp[MONO_WIDTH*MONO_HEIGHT];
		void * tempptr;
		int tempindex;
	
		RtlCopyMemory(temp, page->Buffer, MONO_PAGE_SIZE);
		RtlCopyMemory(page->Buffer, visible_page->Buffer, MONO_PAGE_SIZE);
		RtlCopyMemory(visible_page->Buffer, temp, MONO_PAGE_SIZE);

		tempptr = page->Buffer;
		page->Buffer = visible_page->Buffer;
		visible_page->Buffer = tempptr;
	}

	device->CurrentVisible = (page - &device->Control[0]);
	Mono_Update_Cursor(device);
}


/*********************************************************************************************** 
 * Display_Signon_Banner -- Display the signon banner at driver start.                         * 
 *                                                                                             * 
 *    This displays the signon banner that the driver displays when it first starts.           * 
 *                                                                                             * 
 * INPUT:   control  -- Pointer to the buffer that the signon banner will be displayed to.     * 
 *                                                                                             * 
 * OUTPUT:  none                                                                               * 
 *                                                                                             * 
 * WARNINGS:   none                                                                            * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   01/06/1997 JLB : Created.                                                                 * 
 *=============================================================================================*/
void Display_Signon_Banner(BuffControl * control)
{
	Mono_Printf(control,
		"\fMono Display Driver [monodrvr.sys] -- NT Kernel Mode Device Driver\n"
		"Version: 1.0  -- (" __DATE__ "  " __TIME__ ")\n"
		"Device Name: " A_DEVICE_NAME "\n"
		"Symbolic Link: " A_SYMBOLIC_NAME " (\"\\\\.\\MONO\")\n\n");

	Mono_Printf(control, "Supporting IOCTL Services:\n");
	Mono_Printf(control, "--------------------------\n");
	Mono_Printf(control, " 0x%08X IOCTL_MONO_HELP_SCREEN\n", IOCTL_MONO_HELP_SCREEN);
	Mono_Printf(control, " 0x%08X IOCTL_MONO_CLEAR_SCREEN\n", IOCTL_MONO_CLEAR_SCREEN);
	Mono_Printf(control, " 0x%08X IOCTL_MONO_PRINT_RAW(char * text, int length)\n", IOCTL_MONO_PRINT_RAW);
	Mono_Printf(control, " 0x%08X IOCTL_MONO_SET_CURSOR(int x, int y)\n", IOCTL_MONO_SET_CURSOR);
	Mono_Printf(control, " 0x%08X IOCTL_MONO_SCROLL\n", IOCTL_MONO_SCROLL);
	Mono_Printf(control, " 0x%08X IOCTL_MONO_BRING_TO_TOP\n", IOCTL_MONO_BRING_TO_TOP);
	Mono_Printf(control, " 0x%08X IOCTL_MONO_SET_ATTRIBUTE(char attrib)\n", IOCTL_MONO_SET_ATTRIBUTE);
	Mono_Printf(control, " 0x%08X IOCTL_MONO_PAN\n", IOCTL_MONO_PAN);
	Mono_Printf(control, " 0x%08X IOCTL_MONO_LOCK [returns mono memory address]\n", IOCTL_MONO_LOCK);
	Mono_Printf(control, " 0x%08X IOCTL_MONO_UNLOCK\n", IOCTL_MONO_UNLOCK);
	Mono_Printf(control, " 0x%08X IOCTL_MONO_SET_WINDOW(int x, int y, int w, int h)\n", IOCTL_MONO_SET_WINDOW);
	Mono_Printf(control, " 0x%08X IOCTL_MONO_RESET_WINDOW\n", IOCTL_MONO_RESET_WINDOW);
	Mono_Printf(control, " 0x%08X IOCTL_MONO_SET_FLAG(int flag)\n", IOCTL_MONO_SET_FLAG);
	Mono_Printf(control, " 0x%08X IOCTL_MONO_CLEAR_FLAG(int flag)\n", IOCTL_MONO_CLEAR_FLAG);
	Mono_Printf(control, " 0x%08X IOCTL_MONO_FILL_ATTRIB(int x, int y, int w, int h, int attrib)\n", IOCTL_MONO_FILL_ATTRIB);
}


/*********************************************************************************************** 
 * Mono_Fetch_Ptr -- Fetch a USER address space pointer to mono buffer.                        * 
 *                                                                                             * 
 *    This routine will create a user address space pointer (one that the ring 3 program can   * 
 *    use).                                                                                    * 
 *                                                                                             * 
 * INPUT:   device   -- Pointer to the mono device globals.                                    * 
 *                                                                                             * 
 *          page     -- Pointer to the page that a pointer will be generated for.              * 
 *                                                                                             * 
 * OUTPUT:  Returns with a user pointer to the monochrome card memory that is represented      * 
 *          by the page specified.                                                             * 
 *                                                                                             * 
 * WARNINGS:   This effectively locks the buffer so that it cannot be swapped.                 * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   01/06/1997 JLB : Created.                                                                 * 
 *=============================================================================================*/
void * Mono_Fetch_Ptr(MonoGlobals * device, BuffControl * page)
{
	UNICODE_STRING     memory_unicode_string;
	OBJECT_ATTRIBUTES  object_attribute;
	HANDLE             section_handle  = NULL;
	NTSTATUS           error;
	void * retval = NULL;

	/*
	**	Try to open the PhysicalMemory section. This will be needed when mapping the
	**	physical memory 'file' to an accessable address range. First create a physical
	**	memory object and then convert that object into a handle value.
	*/
	RtlInitUnicodeString(&memory_unicode_string, L"\\Device\\PhysicalMemory");
	InitializeObjectAttributes(&object_attribute, &memory_unicode_string, OBJ_CASE_INSENSITIVE, (HANDLE)NULL, (PSECURITY_DESCRIPTOR)NULL);
	error = ZwOpenSection(&section_handle, SECTION_ALL_ACCESS, &object_attribute);
	if (NT_SUCCESS(error)) {
		PHYSICAL_ADDRESS logical_base_address;
		ULONG in_io_space = 0;

		//
		// Translate the physical addresses.
		//
		if (HalTranslateBusAddress(Isa, 0, RtlConvertUlongToLargeInteger(MONO_MEMORY + (page->Buffer - device->DisplayMemory) * sizeof(unsigned short)), &in_io_space, &logical_base_address)) {
			PVOID virtual_address = NULL;
			ULONG viewlength = MONO_MEM_LENGTH;
			PHYSICAL_ADDRESS view_base = logical_base_address;

			//
			// Map the section
			//
			error = ZwMapViewOfSection(section_handle, (HANDLE) -1, &virtual_address, 0L, viewlength, &view_base, &viewlength, ViewShare, 0, PAGE_READWRITE | PAGE_NOCACHE);
			if (NT_SUCCESS(error)) {

				//
				// Mapping the section above rounded the physical address down to the
				// nearest 64 K boundary. Now return a virtual address that sits where
				// we want by adding in the offset from the beginning of the section.
				//
				(ULONG) virtual_address += (ULONG)logical_base_address.LowPart - (ULONG)view_base.LowPart;
				retval = virtual_address;
			}
		}

		/*
		**	Close the zone handle before exiting.
		*/
		ZwClose(section_handle);
	}
	return(retval);
}


/*********************************************************************************************** 
 * Mono_Init_Buffer -- Initialize a mono buffer to known (unused) state.                       * 
 *                                                                                             * 
 *    This is equivalent to a constructor for the BuffControl object. It resets all values     * 
 *    that it can to a known state.                                                            * 
 *                                                                                             * 
 * INPUT:   buffer   -- Pointer to the buffer object to initialize.                            * 
 *                                                                                             * 
 * OUTPUT:  none                                                                               * 
 *                                                                                             * 
 * WARNINGS:   The internal MGA RAM pointer is not reset by this routine. This value is        * 
 *             managed at the driver level, not the buffer object level.                       * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   01/06/1997 JLB : Created.                                                                 * 
 *=============================================================================================*/
void Mono_Init_Buffer(BuffControl * buffer)
{
	buffer->XPos = 0;
	buffer->YPos = 0;
	buffer->Attribute = DEFAULT_ATTRIBUTE;
	buffer->LockPtr = NULL;
	buffer->LockCount = 0;
	buffer->Allocated = FALSE;
	buffer->WinX = 0;
	buffer->WinY = 0;
	buffer->WinW = MONO_WIDTH;
	buffer->WinH = MONO_HEIGHT;
	buffer->Flags = 0;
}	


/*********************************************************************************************** 
 * Mono_Fill_Attribute -- Fills rectangle with attribute specified.                            * 
 *                                                                                             * 
 *    This routine will set the attribute for the region specified. It can be used to          * 
 *    hightlight or otherwise perform some cheesy effects without altering the text that       * 
 *    is already present.                                                                      * 
 *                                                                                             * 
 * INPUT:   x,y,w,h  -- Coordinates and dimensions of the rectangle to alter.                  * 
 *                                                                                             * 
 *          attrib   -- The attribute to set for the rectangle specified.                      * 
 *                                                                                             * 
 * OUTPUT:  none                                                                               * 
 *                                                                                             * 
 * WARNINGS:   It is possible to set an attribute such that the text 'disappears'.             * 
 *                                                                                             * 
 * HISTORY:                                                                                    * 
 *   01/06/1997 JLB : Created.                                                                 * 
 *=============================================================================================*/
void Mono_Fill_Attribute(BuffControl * control, int x, int y, int w, int h, int attrib)
{
	if (control != NULL)  {
		int yindex;
		unsigned short blank = BUILD_MONO_CHAR(' ', control->Attribute);
		unsigned short * rowptr;

		/*
		**	Clip the parameters to legal values.
		*/
		if (x < 0) x = 0;
		if (x >= control->WinW) x = control->WinW-1;
		if (y < 0) y = 0;
		if (y >= control->WinH) y = control->WinH-1;
		if (w < 1) w = 1;
		if (w > control->WinW-x) w = control->WinW-x;
		if (h < 1) h = 1;
		if (h > control->WinH-y) h = control->WinH-y;

		rowptr = control->Buffer + ((control->WinY + y) * MONO_WIDTH) + control->WinX + x;
		for (yindex = 0; yindex < h; yindex++) {
			int xindex;
			unsigned short * lineptr = rowptr;
			for (xindex = 0; xindex < w; xindex++) {
				*lineptr = BUILD_MONO_CHAR(*lineptr, attrib);
				lineptr++;
			}
			rowptr += MONO_WIDTH;
		}
	}
}	
