mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
336 lines
8.7 KiB
336 lines
8.7 KiB
/*
|
|
* Copyright (c) 1993 Digital Equipment Corporation
|
|
*
|
|
* Module Name: offscrn.c
|
|
*
|
|
* This module contains routines to manage off-screen memory in the TGA
|
|
* frame buffer. It was stolen from FFBPIXMAP.C.
|
|
*
|
|
* Next-fit storage allocation mechanism
|
|
*
|
|
* Algorithm stolen from Knuth V1, p 437,
|
|
* with suggested modifications from Exercise 2.5.6.
|
|
*
|
|
* free list is kept in tga address order
|
|
* ppdev->pFreeList is a pointer to first free element
|
|
* ppdev->pRover is a pointer into free list
|
|
* ppdev->pAllocated points to list of allocated elements
|
|
*
|
|
* History:
|
|
*
|
|
* 29-Nov-1993 Barry Tannenbaum
|
|
* Original adaptation
|
|
*
|
|
* 08-Jun-1994 Bob Seitsinger
|
|
* Modify the initial values for the low-order 4k of the frame buffer
|
|
* to reserve the first 8 bytes for offscreen blit copies. Remember that
|
|
* blits scr->scr code 'may' have to adjust the destination address down
|
|
* by 8 bytes (quadword alignment) if the source alignment adjustment is
|
|
* greater than the destination alignment adjustment - which will most
|
|
* likely be the case if the destination is the very first frame buffer
|
|
* address.
|
|
*
|
|
* 14-Jul-1994 Bob Seitsinger
|
|
* Add code in vTgaOffScreenInit to handle 24 plane boards.
|
|
*
|
|
*/
|
|
|
|
#include "driver.h"
|
|
|
|
/*
|
|
* EnQueue
|
|
*
|
|
* This routine adds a node to a doubly linked list after a specified node.
|
|
* If the previous node is NULL, the node is simply points to itself.
|
|
*/
|
|
|
|
static __inline void EnQueue (OffScreen *prev, OffScreen *p)
|
|
{
|
|
if (NULL == prev)
|
|
{
|
|
p->prev = p;
|
|
p->next = p;
|
|
}
|
|
else
|
|
{
|
|
p->next = prev->next;
|
|
prev->next = p;
|
|
p->prev = prev;
|
|
p->next->prev = p;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* DeQueue
|
|
*
|
|
* This routine removes a node from a doubly linked list and updates a pointer
|
|
* to the start of the list.
|
|
*/
|
|
|
|
static __inline OffScreen *DeQueue (OffScreen *p, OffScreen **ptr)
|
|
{
|
|
if (*ptr == p) // Update the pointer
|
|
{
|
|
if (p->next == p)
|
|
*ptr = NULL;
|
|
else
|
|
*ptr = p->next;
|
|
}
|
|
|
|
p->prev->next = p->next; // Remove the element
|
|
p->next->prev = p->prev;
|
|
|
|
return p;
|
|
}
|
|
|
|
/*
|
|
* vTgaOffScreenInit
|
|
*
|
|
* This routine initializes the off-screen memory management routines
|
|
*/
|
|
|
|
VOID vTgaOffScreenInit (PPDEV ppdev)
|
|
{
|
|
OffScreen *p;
|
|
|
|
// Allocate block for chunk of memory before the start of on-screen memory
|
|
|
|
p = EngAllocMem (FL_ZERO_MEMORY, sizeof(OffScreen), ALLOC_TAG);
|
|
if (NULL == p)
|
|
return;
|
|
|
|
// We need to 'reserve' the low-order 8 bytes of the frame buffer for
|
|
// blit copy mode copies, as well as the high-order 8 bytes for the
|
|
// 'on-screen' portion of the frame buffer.
|
|
//
|
|
// Additionally, 24-plane systems need to reserve the first 2k of
|
|
// the frame buffer. This is used by NT for cursors.
|
|
|
|
if (8 == ppdev->ulBitCount)
|
|
{
|
|
p->addr = ppdev->pjVideoMemory + 8;
|
|
p->bytes = (ppdev->pjFrameBuffer - ppdev->pjVideoMemory) - 16;
|
|
}
|
|
else
|
|
{
|
|
p->addr = ppdev->pjVideoMemory + (2048 + 8);
|
|
p->bytes = (ppdev->pjFrameBuffer - ppdev->pjVideoMemory) - (2048 + 16);
|
|
}
|
|
|
|
DISPDBG ((1, "TGA.DLL!vTgaOffScreenInit - pjVideoMemory [%x], pjFrameBuffer [%x]\n",
|
|
ppdev->pjVideoMemory, ppdev->pjFrameBuffer));
|
|
DISPDBG ((1, "TGA.DLL!vTgaOffScreenInit - ulFrameBufferLen [%x], lScreenStride [%x], cyScreen [%x]\n",
|
|
ppdev->ulFrameBufferLen, ppdev->lScreenStride, ppdev->cyScreen));
|
|
DISPDBG ((1, "TGA.DLL!vTgaOffScreenInit - p: addr [%x], bytes [0x%x]\n",
|
|
p->addr, p->bytes));
|
|
|
|
ppdev->pFreeList = ppdev->pRover = p;
|
|
EnQueue (NULL, p);
|
|
|
|
// Allocate block for chunk of memory after on-screen memory
|
|
|
|
p = EngAllocMem (FL_ZERO_MEMORY, sizeof(OffScreen), ALLOC_TAG);
|
|
if (NULL == p)
|
|
return;
|
|
|
|
p->addr = ppdev->pjFrameBuffer + (ppdev->lScreenStride * ppdev->cyScreen);
|
|
p->bytes = ppdev->ulFrameBufferLen - (ppdev->lScreenStride * ppdev->cyScreen);
|
|
|
|
DISPDBG ((1, "TGA.DLL!vTgaOffScreenInit - p: addr [%x], bytes [0x%x]\n",
|
|
p->addr, p->bytes));
|
|
|
|
EnQueue (ppdev->pRover, p);
|
|
}
|
|
|
|
/*
|
|
* pTgaOffScreenMalloc
|
|
*
|
|
* This routine allocates off-screen memory. If a suitable chunk can't be
|
|
* allocated a null pointer is returned. Otherwise, a pointer to an OffScreen
|
|
* data structure is returned
|
|
*/
|
|
|
|
OffScreen *pTgaOffScreenMalloc (PPDEV ppdev, ULONG bytes, ULONG priority)
|
|
{
|
|
OffScreen *p, *q, *start;
|
|
|
|
// If there's nothing available, bail out now. pRover is invalid if
|
|
// there's nothing on the free list
|
|
|
|
if (NULL == ppdev->pFreeList)
|
|
return NULL;
|
|
|
|
start = p = ppdev->pRover->next;
|
|
|
|
// Search for a block large enough. If we wrap back to the beginning,
|
|
// bail out
|
|
|
|
while (p->bytes < bytes)
|
|
{
|
|
p = p->next;
|
|
if (p == start)
|
|
return NULL;
|
|
}
|
|
|
|
// If we found a chunk of exactly the right size, simply remove the chunk
|
|
// from the free list and return the pointer to the OffScreen
|
|
|
|
if (p->bytes == bytes)
|
|
{
|
|
ppdev->pRover = p->prev;
|
|
DeQueue (p, &ppdev->pFreeList);
|
|
EnQueue (ppdev->pAllocated, p);
|
|
if (NULL == ppdev->pAllocated)
|
|
ppdev->pAllocated = p;
|
|
return p;
|
|
}
|
|
|
|
// We have to split a chunk. Create a new OffScreen element to return and
|
|
// adjust the fragment we're leaving behind appropriately
|
|
|
|
ppdev->pRover = q = p;
|
|
p = EngAllocMem (FL_ZERO_MEMORY, sizeof(OffScreen), ALLOC_TAG);
|
|
|
|
EnQueue (ppdev->pAllocated, p);
|
|
if (NULL == ppdev->pAllocated)
|
|
ppdev->pAllocated = p;
|
|
|
|
p->addr = q->addr;
|
|
p->bytes = bytes;
|
|
|
|
q->addr += bytes;
|
|
q->bytes -= bytes;
|
|
|
|
return p;
|
|
}
|
|
|
|
/*
|
|
* vTgaOffScreenFree
|
|
*
|
|
* This routine returns a block of memory to the free list
|
|
*/
|
|
|
|
VOID vTgaOffScreenFree (PPDEV ppdev, OffScreen *returned)
|
|
{
|
|
OffScreen *p, *q;
|
|
|
|
// Make sure the the element is not locked
|
|
|
|
if (returned->locked)
|
|
{
|
|
DISPDBG ((0, "vTgaOffScreenFree - Attempt to free locked block of memory\n"));
|
|
return;
|
|
}
|
|
|
|
// Remove the OffScreen element from the list of allocated elements
|
|
|
|
DeQueue (returned, &ppdev->pAllocated);
|
|
|
|
// If the free list is empty, simply add this element
|
|
|
|
if (NULL == ppdev->pFreeList)
|
|
{
|
|
EnQueue (NULL, returned);
|
|
ppdev->pFreeList = ppdev->pRover = returned;
|
|
return;
|
|
}
|
|
|
|
// Search for the right place. The list is kept sorted by address in the
|
|
// TGA framebuffer
|
|
|
|
p = ppdev->pFreeList;
|
|
while (p->addr < returned->addr)
|
|
{
|
|
p = p->next;
|
|
if (p == ppdev->pFreeList)
|
|
break;
|
|
}
|
|
q = p->prev;
|
|
|
|
// Free memory goes somewhere between q->addr and p->addr.
|
|
// It may be immediately after q->addr, or immediately before p->addr,
|
|
// or both. Merge abutting blocks appropriately.
|
|
|
|
// New free space immediately before p->addr?
|
|
|
|
if (p->addr == returned->addr + returned->bytes)
|
|
{
|
|
if (q->addr + q->bytes == returned->addr) // Free space also abuts q,
|
|
{ // merge all three spaces
|
|
q->bytes += returned->bytes + p->bytes;
|
|
q->next = p->next;
|
|
DeQueue (p, &ppdev->pFreeList);
|
|
EngFreeMem (p);
|
|
EngFreeMem (returned);
|
|
}
|
|
else
|
|
{ // Just merge free space with p
|
|
p->bytes += returned->bytes;
|
|
p->addr -= returned->bytes;
|
|
EngFreeMem (returned);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (q->addr + q->bytes == returned->addr)
|
|
{ // new block abuts q, merge
|
|
q->bytes += returned->bytes;
|
|
EngFreeMem (returned);
|
|
}
|
|
else
|
|
{ // does not abut either q or p
|
|
EnQueue (q, returned);
|
|
if (returned->addr < ppdev->pFreeList->addr)
|
|
ppdev->pFreeList = returned;
|
|
}
|
|
}
|
|
|
|
ppdev->pRover = q; // Start search here next time
|
|
}
|
|
|
|
// Free all the memory for the offscreen data structures
|
|
//
|
|
// This works for a linked list that is standard (i.e.
|
|
// does not curl back itself) or circular (i.e. does curl
|
|
// back on itself)
|
|
|
|
//static
|
|
VOID vTgaOffScreenFreeAll_ (OffScreen *ptr)
|
|
{
|
|
|
|
OffScreen *p, *q;
|
|
|
|
p = q = ptr;
|
|
|
|
while (NULL != p)
|
|
{
|
|
p = p->next;
|
|
|
|
EngFreeMem (q);
|
|
|
|
// We've come back to the beginning so no more
|
|
|
|
if (p == ptr)
|
|
break;
|
|
|
|
q = p;
|
|
}
|
|
|
|
ptr = NULL;
|
|
|
|
}
|
|
|
|
VOID vTgaOffScreenFreeAll (PPDEV ppdev)
|
|
{
|
|
|
|
// Free up the data structure memory for the Free list
|
|
|
|
vTgaOffScreenFreeAll_ (ppdev->pFreeList);
|
|
|
|
// Free up the data structure memory for the Allocated list
|
|
|
|
vTgaOffScreenFreeAll_ (ppdev->pAllocated);
|
|
|
|
ppdev->pRover = NULL;
|
|
|
|
}
|