/* ** 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 . */ /* $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 : Command & Conquer * * * * File Name : MONO.C * * * * Programmer : Joe L. Bostic * * * * Start Date : 01/04/97 * * * * Last Update : January 5, 1997 [JLB] * * * *---------------------------------------------------------------------------------------------* * Functions: * * Mono_Clear_Screen -- Clears the monochrome screen & homes cursor. * * 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_Pan -- Pan the mono screen over one column. * * Mono_Detect_MGA_Adapter -- Try to detec the MGA adapter. * * Mono_Get_Address_Ptr -- Converts a physical address into a usable pointer. * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "ntddk.h" #include "stdarg.h" #include "stdio.h" #include "monopub.h" // // Indices into & count of the above array // #define MONO_VIDEO_BUFFER 0 #define MONO_CRTC_REG 1 #define MONO_MODE_CTL_REG 2 #define MONO_NUMBER_RESOURCE_ENTRIES 2 //#define MONO_NUMBER_RESOURCE_ENTRIES 3 // // Our user mode app will pass an initialized structure like this // down to the kernel mode driver // //typedef struct //{ // INTERFACE_TYPE InterfaceType; // Isa, Eisa, etc.... // ULONG BusNumber; // Bus number // PHYSICAL_ADDRESS BusAddress; // Bus-relative address // ULONG AddressSpace; // 0 is memory, 1 is I/O // ULONG Length; // Length of section to map // //} PHYSICAL_MEMORY_INFO, *PPHYSICAL_MEMORY_INFO; #define DEVICE_NAME L"\\Device\\Mono" #define A_DEVICE_NAME "\\Device\\Mono" #define SYMBOLIC_NAME L"\\DosDevices\\MONO" #define A_SYMBOLIC_NAME "\\DosDevices\\MONO" #define CRTC_REGISTER 0x3B4 #define MONO_MEMORY 0xB0000 #define MONO_MEM_LENGTH (0x10000-1) #define MAX_PAGES 16 #define MONO_WIDTH 80 #define MONO_HEIGHT 25 #define SIZE_OF_SCREEN (MONO_WIDTH * MONO_HEIGHT * sizeof(unsigned short)) #define BUILD_CHAR(c, a) ((unsigned short)(((a) << 8) | ((c) & 0xFF))) #define PINDEX_TO_CELL(index) ((index) * MONO_WIDTH * MONO_HEIGHT) /* ** This controls the state information for each of the ** display pages. */ typedef struct BuffControl { int XPos; int YPos; /* ** 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. */ 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. */ void * LockPtr; /* ** If this page has been allocated, then this flag will ** be true. */ int Allocated; } 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[MAX_PAGES]; /* ** This is the index of the currently visible display page. */ int CurrentVisible; } MonoGlobals; // // A structure decribing a particular device resource // typedef struct _MONO_RESOURCE { PHYSICAL_ADDRESS PhysicalAddress; unsigned long Length; unsigned long AddressSpace; unsigned long RangeSharable; } MONO_RESOURCE, *PMONO_RESOURCE; // // A global that we keep around for use by the MonoDbgPrint function // (when other drivers call MonoDbgPrint we need to be able to access // the info stored in the device extension). // MonoGlobals * GlobalDeviceExtension = NULL; // // A variable which determines the verboseness of the messages printed by // MonoDbgPrint. // unsigned long MonoDbgLevel = 3; // // Resources used by the monochrome video adapter // MONO_RESOURCE MonoResources[] = { { MONO_MEMORY, 0x00000000, // the video buffer MONO_MEM_LENGTH, // length 0, // in memory space 1 }, // shared with other drivers/devices (vga) { CRTC_REGISTER, 0x00000000, // the 6845 index & data regs 0x00000002, // length 1, // in I/O space 1 }, // shared with other drivers/devices (vga) { 0x000b03b8, 0x00000000, // mode control register 0x00000001, // length 1, // in I/O space 1 } // shared with other drivers/devices (vga) }; /* ** Each cell is constructed of the actual character that is displayed and the ** attribute to use. This character pair is located at every position on the ** display (80 x 25). Since this cell pair can be represented by a "short" ** integer, certain speed optimizations are taken in the monochrome drawing ** code. */ typedef struct CellType { unsigned char Character; // Character to display. unsigned char Attribute; // Attribute. } CellType; NTSTATUS MonoDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp); void MonoUnload(PDRIVER_OBJECT DriverObject); BOOLEAN pMonoReportResourceUsage(PDRIVER_OBJECT DriverObject, PMONO_RESOURCE MonoResources, unsigned long NumberOfResources); //NTSTATUS MapMemMapTheMemory(PDEVICE_OBJECT DeviceObject,PVOID IoBuffer, ULONG OutputBufferLength); void * Mono_Get_Address_Ptr(PHYSICAL_ADDRESS PhysicalAddress, unsigned long AddressSpace, unsigned long NumberOfBytes); void Mono_Set_View_Pos(MonoGlobals * device, int pos); NTSTATUS Mono_Detect_MGA_Adapter(void); void * Mono_Fetch_Ptr(PDEVICE_OBJECT DeviceObject); void Mono_Printf(BuffControl * control, char const * DbgMessage, ...); void Mono_Clear_Screen(BuffControl * control); void Mono_Print_Raw(BuffControl * control, unsigned char * string, unsigned long length); void Mono_Scroll(BuffControl * control); void Mono_Print(BuffControl * control, unsigned char * string, unsigned long length); void Mono_Bring_To_Top(MonoGlobals * device, int index); void Mono_Pan(BuffControl * control); void Display_Signon_Banner(BuffControl * control); void Mono_Update_Cursor(MonoGlobals * device); /*++ Routine Description: Installable driver initialization entry point. This entry point is called directly by the I/O system. Arguments: DriverObject - pointer to the driver object RegistryPath - pointer to a unicode string representing the path to driver-specific key in the registry Return Value: STATUS_SUCCESS if successful, STATUS_UNSUCCESSFUL otherwise --*/ NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { PDEVICE_OBJECT deviceObject = NULL; NTSTATUS ntStatus; UNICODE_STRING deviceNameUnicodeString; MonoGlobals * deviceExtension; BOOLEAN symbolicLinkCreated = FALSE; UNICODE_STRING deviceLinkUnicodeString; int j; // // Check to see if anybody else has claim the resources we want to // used. We don't want to be partying on someone else's hardware, // even when trying to locate our devicc. // if (!pMonoReportResourceUsage(DriverObject, MonoResources, MONO_NUMBER_RESOURCE_ENTRIES)) { return STATUS_UNSUCCESSFUL; } // // Try to locate a monochrome display adapter (MDA) // ntStatus = Mono_Detect_MGA_Adapter(); if (!NT_SUCCESS(ntStatus)) { ntStatus = STATUS_UNSUCCESSFUL; goto done_DriverEntry; } // // OK, we've claimed our resources & found our h/w, so create // a device and initialize stuff... // RtlInitUnicodeString(&deviceNameUnicodeString, DEVICE_NAME); // RtlInitUnicodeString(&deviceNameUnicodeString, L"\\Device\\Mono"); // // Create an EXCLUSIVE device, i.e. only 1 thread at a time can send // i/o requests. If opened as non-exclusive, then we would need to // implement a more robust synchronization scheme than the event // mechanism we utilize below. // ntStatus = IoCreateDevice(DriverObject, sizeof(MonoGlobals), &deviceNameUnicodeString, FILE_DEVICE_MONO, 0, FALSE, &deviceObject); if (NT_SUCCESS(ntStatus)) { deviceObject->Flags |= DO_BUFFERED_IO; GlobalDeviceExtension = deviceExtension = (MonoGlobals *) deviceObject->DeviceExtension; // // Initialize the dispatch event object. This allows us to // synchronize access to the h/w registers... // KeInitializeEvent(&deviceExtension->SyncEvent, SynchronizationEvent, TRUE); // // Map all the required resources, save the addresses // deviceExtension->DisplayMemory = (PUSHORT) Mono_Get_Address_Ptr(MonoResources[MONO_VIDEO_BUFFER].PhysicalAddress, MonoResources[MONO_VIDEO_BUFFER].AddressSpace, MonoResources[MONO_VIDEO_BUFFER].Length ); deviceExtension->CRTCRegisters = (unsigned char *) Mono_Get_Address_Ptr(MonoResources[MONO_CRTC_REG].PhysicalAddress, MonoResources[MONO_CRTC_REG].AddressSpace, MonoResources[MONO_CRTC_REG].Length ); // deviceExtension->ModeControlRegister = // (unsigned char *) Mono_Get_Address_Ptr (MonoResources[MONO_MODE_CTL_REG].PhysicalAddress, // MonoResources[MONO_MODE_CTL_REG].AddressSpace, // MonoResources[MONO_MODE_CTL_REG].Length // ); if (deviceExtension->DisplayMemory == NULL || deviceExtension->CRTCRegisters == NULL) { ntStatus = STATUS_UNSUCCESSFUL; goto done_DriverEntry; } // // Create a symbolic link that Win32 apps can specify to gain access // to this driver/device // RtlInitUnicodeString(&deviceLinkUnicodeString, SYMBOLIC_NAME); // RtlInitUnicodeString(&deviceLinkUnicodeString, L"\\DosDevices\\MONO"); ntStatus = IoCreateSymbolicLink(&deviceLinkUnicodeString, &deviceNameUnicodeString); if (!NT_SUCCESS(ntStatus)) { } else { symbolicLinkCreated = TRUE; } // // Create dispatch points for device control, create, close. // DriverObject->MajorFunction[IRP_MJ_CREATE] = MonoDispatch; DriverObject->MajorFunction[IRP_MJ_CLOSE] = MonoDispatch; DriverObject->MajorFunction[IRP_MJ_READ] = MonoDispatch; DriverObject->MajorFunction[IRP_MJ_WRITE] = MonoDispatch; DriverObject->MajorFunction[IRP_MJ_CLEANUP] = MonoDispatch; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MonoDispatch; DriverObject->DriverUnload = MonoUnload; // // Set the Start Address registers (indicate which part of // video buffer is displayed) to 0 // Mono_Set_View_Pos(deviceExtension, 0); deviceExtension->CurrentVisible = 0; for (j = 0; j < MAX_PAGES; j++) { deviceExtension->Control[j].Buffer = &deviceExtension->DisplayMemory[PINDEX_TO_CELL(j)]; deviceExtension->Control[j].XPos = 0; deviceExtension->Control[j].YPos = 0; deviceExtension->Control[j].Attribute = 0x07; deviceExtension->Control[j].LockPtr = NULL; deviceExtension->Control[j].Allocated = FALSE; } /* ** Display the signon banner. */ Display_Signon_Banner(&deviceExtension->Control[deviceExtension->CurrentVisible]); Mono_Update_Cursor(deviceExtension); } done_DriverEntry: if (!NT_SUCCESS(ntStatus)) { // // Something went wrong, so clean up // pMonoReportResourceUsage(DriverObject, NULL, 0); if (symbolicLinkCreated) { IoDeleteSymbolicLink(&deviceLinkUnicodeString); } if (deviceObject) { IoDeleteDevice(deviceObject); } GlobalDeviceExtension = NULL; } return ntStatus; } /*++ Routine Description: Process the IRPs sent to this device. Arguments: DeviceObject - pointer to a device object Irp - pointer to an I/O Request Packet Return Value: --*/ NTSTATUS MonoDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp) { PIO_STACK_LOCATION irpStack; MonoGlobals * deviceExtension; 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 */ deviceExtension = 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(&deviceExtension->SyncEvent, Executive, KernelMode, FALSE, NULL); /* ** Get a working pointer to the display page. */ if (currentindex == -1) { currentindex == deviceExtension->CurrentVisible; } control = &deviceExtension->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 < MAX_PAGES; j++) { if (!deviceExtension->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 = &deviceExtension->Control[avail]; newcon->XPos = 0; newcon->YPos = 0; newcon->Attribute = 0x07; newcon->Allocated = TRUE; Mono_Bring_To_Top(deviceExtension, avail); 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) { deviceExtension->Control[currentindex].Allocated = FALSE; fileobject->FsContext = NULL; } break; case IRP_MJ_CLEANUP: break; case IRP_MJ_DEVICE_CONTROL: ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode; switch (ioControlCode) { case IOCTL_MONO_BRING_TO_TOP: Mono_Bring_To_Top(deviceExtension, currentindex); break; case IOCTL_MONO_SCROLL: Mono_Scroll(control); break; case IOCTL_MONO_PAN: Mono_Pan(control); break; case IOCTL_MONO_SET_CURSOR: if (inputBufferLength == sizeof(int)*2) { control->XPos = *(int*)ioBuffer; control->YPos = *(((int*)ioBuffer) + 1); } break; case IOCTL_MONO_PRINT_RAW: Mono_Print_Raw(control, ioBuffer, inputBufferLength); break; case IOCTL_MONO_SET_POS: if (inputBufferLength == sizeof(int)) { Mono_Set_View_Pos(deviceExtension, *(int*)ioBuffer); } break; case IOCTL_MONO_CLEAR_SCREEN: Mono_Clear_Screen(control); break; case IOCTL_MONO_SET_ATTRIBUTE: control->Attribute = *(char *)ioBuffer; break; #ifdef NEVER case IOCTL_MONO_LOCK: if (deviceExtension->LockPtr != NULL) { *(void**)ioBuffer = deviceExtension->LockPtr; deviceExtension->LockCount++; } else { *(void**)ioBuffer = Mono_Fetch_Ptr(DeviceObject); if (*(void**)ioBuffer != NULL) { deviceExtension->LockPtr = *(void**)ioBuffer; deviceExtension->LockCount++; Irp->IoStatus.Information = sizeof(PVOID); } else { Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; } } break; case IOCTL_MONO_UNLOCK: if (deviceExtension->LockCount > 0) { deviceExtension->LockCount--; if (deviceExtension->LockCount == 0) { Irp->IoStatus.Status = ZwUnmapViewOfSection((HANDLE) -1, deviceExtension->LockPtr); deviceExtension->LockPtr = NULL; } } break; #endif 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 == deviceExtension->CurrentVisible) { Mono_Update_Cursor(deviceExtension); } KeSetEvent(&deviceExtension->SyncEvent, 0, FALSE); ntStatus = Irp->IoStatus.Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return(ntStatus); } /*++ Routine Description: Free all the allocated resources, etc. Arguments: DriverObject - pointer to a driver object Return Value: --*/ void MonoUnload(PDRIVER_OBJECT DriverObject) { // WCHAR deviceLinkBuffer[] = L"\\DosDevices\\MONO"; UNICODE_STRING deviceLinkUnicodeString; // // Unreport the resources // pMonoReportResourceUsage(DriverObject, NULL, 0); // // Delete the symbolic link // RtlInitUnicodeString(&deviceLinkUnicodeString, L"\\DosDevices\\MONO"); IoDeleteSymbolicLink(&deviceLinkUnicodeString); // // Delete the device object // 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: Returns with the NT status code of the check. STATUS_SUCCESS if the MGA adapter * * was found. * * * * WARNINGS: none * * * * HISTORY: * * 01/05/1997 JLB : Created. * *=============================================================================================*/ NTSTATUS Mono_Detect_MGA_Adapter(void) { PHYSICAL_ADDRESS physicalAddress; unsigned char * crtcRegisters; /* ** Get address to the CRTC registers. */ physicalAddress.LowPart = CRTC_REGISTER; physicalAddress.HighPart = 0; if ((crtcRegisters = Mono_Get_Address_Ptr(physicalAddress, 1, 2))) { /* ** Set the mono cursor position and see if it changed. If so, then ** presume that the MGA adapter is present. */ WRITE_PORT_UCHAR(crtcRegisters, 0x0F); WRITE_PORT_UCHAR(crtcRegisters+1, 0x55); if (READ_PORT_UCHAR(crtcRegisters+1) != 0x55) { return(STATUS_UNSUCCESSFUL); } WRITE_PORT_UCHAR(crtcRegisters+1, 0xAA); if (READ_PORT_UCHAR(crtcRegisters+1) != 0xAA) { return(STATUS_UNSUCCESSFUL); } WRITE_PORT_UCHAR(crtcRegisters+1, 0x00); } else { return(STATUS_UNSUCCESSFUL); } return(STATUS_SUCCESS); } /*********************************************************************************************** * 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 translatedAddress; void * usable_ptr = NULL; /* ** Translate a bus specific address into a logical system address. */ if (HalTranslateBusAddress(Isa, 0, address, &space, &translatedAddress)) { /* ** 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(translatedAddress, length, FALSE); } else { usable_ptr = (void *)translatedAddress.LowPart; } } return(usable_ptr); } // // NOTE: The following is commented out because some of the video miniport // drivers provided with the system (and in the NT DDK) regsister // their resources as exclusive; if these resources overlap with // the resources used by the mono driver, then this driver will fail // to load. This can be solved in one of two ways: by commenting // out the code below, and using the resources without reporting them // (not safe to do, but the easiest for this simple example); or, by // editing the source file(s) of the appropriate miniport driver // such that the VIDEO_ACCESS_RANGE.RangeSharable element is set to // TRUE, and rebuilding the driver. // // A real driver should *always* report it's resources. // /*++ Routine Description: Reports the resources used by a device. Arguments: DriverObject - pointer to a driver object MonoResources - pointer to an array of resource information, or NULL is unreporting resources for this driver NumberOfResources - number of entries in the resource array, or 0 if unreporting resources for this driver Return Value: TRUE if resources successfully report (and no conflicts), FALSE otherwise. --*/ BOOLEAN pMonoReportResourceUsage(PDRIVER_OBJECT DriverObject, PMONO_RESOURCE MonoResources, unsigned long NumberOfResources) { unsigned long sizeOfResourceList = 0; PCM_RESOURCE_LIST resourceList = NULL; PCM_PARTIAL_RESOURCE_DESCRIPTOR partial; unsigned long i; UNICODE_STRING className; BOOLEAN conflictDetected; if (NumberOfResources > 0) { // // Alloc enough memory to build a resource list & zero it out // sizeOfResourceList = sizeof(CM_RESOURCE_LIST) + (sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR)*(NumberOfResources - 1)); resourceList = ExAllocatePool(PagedPool, sizeOfResourceList); if (!resourceList) { return FALSE; } RtlZeroMemory(resourceList, sizeOfResourceList); // // Fill in the reosurce list // // NOTE: Assume Isa, bus # 0 // resourceList->Count = 1; resourceList->List[0].InterfaceType = Isa; resourceList->List[0].BusNumber = 0; resourceList->List[0].PartialResourceList.Count = NumberOfResources; partial = &resourceList->List[0].PartialResourceList.PartialDescriptors[0]; // // Account for the space used by the controller. // for (i = 0; i < NumberOfResources; i++) { if (MonoResources[i].AddressSpace) { partial->Type = CmResourceTypePort; partial->Flags = CM_RESOURCE_PORT_IO; partial->u.Port.Start = MonoResources[i].PhysicalAddress; partial->u.Port.Length = MonoResources[i].Length; } else { partial->Type = CmResourceTypeMemory; partial->Flags = CM_RESOURCE_MEMORY_READ_WRITE; partial->u.Memory.Start = MonoResources[i].PhysicalAddress; partial->u.Memory.Length = MonoResources[i].Length; } if (MonoResources[i].RangeSharable) { partial->ShareDisposition = CmResourceShareShared; } else { partial->ShareDisposition = CmResourceShareDeviceExclusive; } partial++; } } RtlInitUnicodeString(&className, L"LOADED MONO DRIVER RESOURCES"); IoReportResourceUsage(&className, DriverObject, resourceList, sizeOfResourceList, NULL, NULL, 0, FALSE, &conflictDetected); if (resourceList) { ExFreePool(resourceList); if (conflictDetected) { return FALSE; } else { return TRUE; } } return TRUE; } void Mono_Update_Cursor(MonoGlobals * device) { int pos; pos = device->Control[device->CurrentVisible].XPos + (device->Control[device->CurrentVisible].YPos * 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) { int x,y; unsigned char space = ' '; x = control->XPos; y = control->YPos; while (length > 0) { 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'; while (length > 0 && bchar == '\0') { bchar = *string++; length--; 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 not a valid formatting character, then clear out the break ** character variable so that processing will continue. */ default: blocklen++; bchar = '\0'; break; } } /* ** 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 pair. */ case '\n': x = 0; y++; if (y >= MONO_HEIGHT) { 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. */ 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; unsigned short blank = BUILD_CHAR(' ', control->Attribute); vidmem = control->Buffer; RtlMoveMemory(vidmem, vidmem+MONO_WIDTH, (MONO_HEIGHT-1) * MONO_WIDTH * sizeof(unsigned short)); /* ** Fill the bottom row with blanks. */ for (j = 0; j < MONO_WIDTH; j++) { *(vidmem + j + ((MONO_HEIGHT-1) * MONO_WIDTH)) = blank; } 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) { int j; unsigned short * vidmem; unsigned short blank = BUILD_CHAR(' ', control->Attribute); vidmem = control->Buffer; for (j = 0; j < MONO_HEIGHT; j++) { RtlMoveMemory(vidmem+j*MONO_WIDTH, vidmem+j*MONO_WIDTH+1, (MONO_WIDTH-1) * sizeof(unsigned short)); *(vidmem + j*MONO_WIDTH + MONO_WIDTH-1) = blank; } 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; int x,y; unsigned long i; vidmem = control->Buffer; x = control->XPos; y = control->YPos; for (i = 0; i < length; i++) { unsigned char letter; letter = *string++; *(vidmem + x + (y * MONO_WIDTH)) = BUILD_CHAR(letter, control->Attribute); x++; /* ** Don't allow the cursor to extend past the right margin. */ if (x >= MONO_WIDTH) { x = MONO_WIDTH-1; } } control->XPos = x; control->YPos = y; } } /*********************************************************************************************** * 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 i; unsigned short * vidptr = control->Buffer; unsigned short blank = BUILD_CHAR(' ', control->Attribute); for (i = 0; i < MONO_HEIGHT*MONO_WIDTH; i++) { *vidptr++ = blank; } 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[1024]; 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)); } } void Mono_Bring_To_Top(MonoGlobals * device, int index) { if (index == -1 || index == device->CurrentVisible) return; Mono_Set_View_Pos(device, index * 100); // Mono_Set_View_Pos(device, index * MONO_WIDTH * MONO_HEIGHT); device->CurrentVisible = index; Mono_Update_Cursor(device); } void Display_Signon_Banner(BuffControl * control) { Mono_Printf(control, "\fMono Display Driver [mono.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, " [%08X] IOCTL_MONO_PRINT_RAW(char * text, int length)\n", IOCTL_MONO_PRINT_RAW); Mono_Printf(control, " [%08X] IOCTL_MONO_CLEAR_SCREEN(void)\n", IOCTL_MONO_CLEAR_SCREEN); Mono_Printf(control, " [%08X] IOCTL_MONO_SET_CURSOR(int x, int y)\n", IOCTL_MONO_SET_CURSOR); Mono_Printf(control, " [%08X] IOCTL_MONO_SCROLL(void)\n", IOCTL_MONO_SCROLL); Mono_Printf(control, " [%08X] IOCTL_MONO_BRING_TO_TOP(void)\n", IOCTL_MONO_BRING_TO_TOP); Mono_Printf(control, " [%08X] IOCTL_MONO_SET_ATTRIBUTE(char attrib)\n", IOCTL_MONO_SET_ATTRIBUTE); Mono_Printf(control, " [%08X] IOCTL_MONO_PAN(void)\n", IOCTL_MONO_PAN); Mono_Printf(control, " [%08X] IOCTL_MONO_LOCK(void) [returns mono memory address]\n", IOCTL_MONO_LOCK); Mono_Printf(control, " [%08X] IOCTL_MONO_UNLOCK(void)\n", IOCTL_MONO_UNLOCK); } /*++ Routine Description: Given a physical address, maps this address into a user mode process's address space Arguments: DeviceObject - pointer to a device object IoBuffer - pointer to the I/O buffer InputBufferLength - input buffer length OutputBufferLength - output buffer length Return Value: STATUS_SUCCESS if sucessful, otherwise STATUS_UNSUCCESSFUL, STATUS_INSUFFICIENT_RESOURCES, (other STATUS_* as returned by kernel APIs) --*/ void * Mono_Fetch_Ptr(PDEVICE_OBJECT DeviceObject) { 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(§ion_handle, SECTION_ALL_ACCESS, &object_attribute); if (NT_SUCCESS(error)) { PHYSICAL_ADDRESS logical_base_address; PHYSICAL_ADDRESS physical_address; ULONG in_io_space = 0; physical_address.HighPart = 0; physical_address.LowPart = MONO_MEMORY; // // Translate the physical addresses. // if (HalTranslateBusAddress(Isa, 0, physical_address, &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); }